* enhance ai menu

* remove api prefix from mcp
This commit is contained in:
Philipinho
2026-03-02 03:31:52 +00:00
parent cf43e2b4fe
commit ee6b98edaa
7 changed files with 21 additions and 14 deletions
@@ -614,6 +614,7 @@
"AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.": "AI is only available in the Docmost enterprise edition. Contact sales@docmost.com.",
"AI & MCP": "AI & MCP",
"AI": "AI",
"AI settings": "AI settings",
"MCP": "MCP",
"Model Context Protocol (MCP)": "Model Context Protocol (MCP)",
"Enable the MCP server to allow AI assistants and tools to interact with your workspace content.": "Enable the MCP server to allow AI assistants and tools to interact with your workspace content.",
@@ -113,7 +113,7 @@ const groupedData: DataGroup[] = [
showDisabledInNonEE: true,
},
{
label: "AI",
label: "AI settings",
icon: IconSparkles,
path: "/settings/ai",
isAdmin: true,
@@ -1,5 +1,5 @@
import { Editor } from "@tiptap/react";
import { ActionIcon, TextInput, Tooltip } from "@mantine/core";
import { ActionIcon, TextInput } from "@mantine/core";
import { useDebouncedCallback, useMediaQuery } from "@mantine/hooks";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
@@ -14,7 +14,7 @@ import { ResultPreview } from "./result-preview.tsx";
import classes from "./ai-menu.module.css";
import { marked } from "marked";
import { DOMSerializer } from "@tiptap/pm/model";
import { htmlToMarkdown } from "@docmost/editor-ext";
import { copyToClipboard, htmlToMarkdown } from "@docmost/editor-ext";
import { useLocation } from "react-router-dom";
interface EditorAiMenuProps {
@@ -110,6 +110,7 @@ const EditorAiMenu = ({ editor }: EditorAiMenuProps): JSX.Element | null => {
setOutput((output) => output + chunk.content);
},
onComplete: () => {
setPrompt("");
setIsLoading(false);
setActiveCommandSet("result");
},
@@ -146,12 +147,17 @@ const EditorAiMenu = ({ editor }: EditorAiMenuProps): JSX.Element | null => {
}
const html = (marked.parse(output) as string).trim();
// Strip <p> wrapper for single-paragraph output to preserve inline context
const content =
const isSingleParagraph =
html.startsWith("<p>") &&
html.endsWith("</p>") &&
html.lastIndexOf("<p>") === 0
? html.slice(3, -4)
html.lastIndexOf("<p>") === 0;
// Strip <p> wrapper for single-paragraph output to preserve inline context,
// then decode HTML entities via DOMParser since TipTap would otherwise
// treat the tagless string as plain text and insert entities literally.
const content = isSingleParagraph
? new DOMParser().parseFromString(html.slice(3, -4), "text/html")
.body.innerHTML
: html;
chain.insertContent(content).run();
@@ -169,7 +175,7 @@ const EditorAiMenu = ({ editor }: EditorAiMenuProps): JSX.Element | null => {
return setShowAiMenu(false);
}
if (item.id === "result-copy") {
navigator.clipboard.writeText(output);
copyToClipboard(output);
return setShowAiMenu(false);
}
@@ -27,7 +27,7 @@ export default function McpSettings() {
const [checked, setChecked] = useState(workspace?.settings?.ai?.mcp);
const hasAccess = useIsCloudEE();
const mcpUrl = `${getAppUrl()}/api/mcp`;
const mcpUrl = `${getAppUrl()}/mcp`;
const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.currentTarget.checked;
+2 -2
View File
@@ -37,9 +37,9 @@ export default function AiSettings() {
return (
<>
<Helmet>
<title>AI - {getAppName()}</title>
<title>AI settings - {getAppName()}</title>
</Helmet>
<SettingsTitle title={t("AI")} />
<SettingsTitle title={t("AI settings")} />
<Tabs color="dark" value={activeTab} onChange={handleTabChange}>
<Tabs.List>
@@ -77,7 +77,7 @@ export default function UserApiKeys() {
<Text size="sm" mt={4}>
{t("MCP server URL:")}{" "}
<Text size="sm" fw={500} span ff="monospace">
{`${getAppUrl()}/api/mcp`}
{`${getAppUrl()}/mcp`}
</Text>
</Text>
</Alert>
+1 -1
View File
@@ -36,7 +36,7 @@ async function bootstrap() {
app.useLogger(app.get(PinoLogger));
app.setGlobalPrefix('api', {
exclude: ['robots.txt', 'share/:shareId/p/:pageSlug'],
exclude: ['robots.txt', 'share/:shareId/p/:pageSlug', 'mcp'],
});
const reflector = app.get(Reflector);