Loading editor/html/css/editor.css +6 −0 Changes for editor/html/css/editor.css: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -801,6 +801,12 @@ dialog.ai-panel::backdrop { align-self: flex-start; border-bottom-left-radius: 2px; } .ai-msg-status { color: var(--text-secondary); font-size: 11px; font-style: italic; margin-top: 4px; } .ai-msg.assistant .ai-msg-actions { display: flex; gap: 6px; margin-top: 6px; } Loading editor/html/js/editor.js +19 −2 Changes for editor/html/js/editor.js: 19 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -1649,7 +1649,12 @@ currentAiRequest = null; statusEl.remove(); var result = resp.result || ''; addAssistantMessage(result); if (resp.applied) { Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); } addAssistantMessage(result, false, !!resp.applied, resp.tool_calls || 0); resetSendBtn(); }).catch(function(err) { currentAiRequest = null; Loading Loading @@ -1680,7 +1685,7 @@ return div; } function addAssistantMessage(text, noSave) { function addAssistantMessage(text, noSave, applied, toolCalls) { var div = document.createElement('div'); div.className = 'ai-msg assistant'; Loading @@ -1688,6 +1693,14 @@ content.textContent = text; div.appendChild(content); if (applied) { var appliedStatus = document.createElement('div'); appliedStatus.className = 'ai-msg-status'; appliedStatus.textContent = '\u2713 ' + (toolCalls || 0) + ' ' + I18n.t('I18N_AI_APPLIED', '\u00C4nderung(en) direkt am Dokument angewendet'); div.appendChild(appliedStatus); } var actions = document.createElement('div'); actions.className = 'ai-msg-actions'; Loading @@ -1701,6 +1714,9 @@ }); actions.appendChild(copyBtn); // The model already applied its changes directly via tool calls -- // no manual Replace/Child import buttons needed in that case. if (!applied) { function doImport(mode) { var htmlContent = extractHtml(text); var parentUuid = (mode === 'child') ? Canvas.getSelectedUuid() : null; Loading Loading @@ -1730,6 +1746,7 @@ childBtn.textContent = '\u2B07 ' + I18n.t('I18N_INSERT_CHILD', 'Child einf\u00fcgen'); childBtn.addEventListener('click', function() { doImport('child'); }); actions.appendChild(childBtn); } div.appendChild(actions); chatHistory.appendChild(div); Loading editor/src/webedit_api.cpp +417 −91 File changed.Preview size limit exceeded, changes collapsed. Show changes editor/src/webedit_api.h +9 −0 Changes for editor/src/webedit_api.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -287,6 +287,15 @@ namespace webedit { void mcpLoadDocument(const std::string &domain, const std::string &docId, DocumentState &doc); void mcpSaveDocument(const std::string &domain, const std::string &docId, DocumentState &doc); // Session-scoped counterpart of the mcpXxx tool entry points, used by the // in-editor AI chat (handleAiPrompt): dispatches on the same tool names/ // semantics as Mcp::callTool, but acts directly on getDocState(sessionid) // instead of a (domain, docId)-loaded DB copy, so calls mutate the live // document the browser has open. Throws McpError on failure; sets // mutated=true if the tool changed the document. json_object *callAiTool(const std::string &sessionid, const std::string &name, json_object *args, bool &mutated); // Core element-mutation logic shared by the handleXxx HTTP handlers and the // mcpXxx entry points (kept in one place because the sibling/child pointer // splicing here has real correctness subtlety). Throw McpError on failure. Loading editor/src/webedit_mcp.cpp +10 −16 Changes for editor/src/webedit_mcp.cpp: 10 added lines, 16 removed lines. Original line number Diff line number Diff line Loading @@ -90,13 +90,17 @@ json_object *getObjectArg(json_object *args, const char *key) { return v; } struct SchemaProp { const char *name; const char *type; const char *description; }; json_object *makeTool(const char *name, const char *description, json_object *schema) { json_object *tool = json_object_new_object(); json_object_object_add(tool, "name", json_object_new_string(name)); json_object_object_add(tool, "description", json_object_new_string(description)); json_object_object_add(tool, "inputSchema", schema); return tool; } json_object *makeSchema(std::initializer_list<SchemaProp> props, } // namespace json_object *webedit::makeSchema(std::initializer_list<SchemaProp> props, std::initializer_list<const char*> required) { json_object *propsObj = json_object_new_object(); for (const auto &p : props) { Loading @@ -120,16 +124,6 @@ json_object *makeSchema(std::initializer_list<SchemaProp> props, return schema; } json_object *makeTool(const char *name, const char *description, json_object *schema) { json_object *tool = json_object_new_object(); json_object_object_add(tool, "name", json_object_new_string(name)); json_object_object_add(tool, "description", json_object_new_string(description)); json_object_object_add(tool, "inputSchema", schema); return tool; } } // namespace webedit::Mcp::Mcp(Database &db, Api &api) : _db(db), _api(api) { } Loading Loading
editor/html/css/editor.css +6 −0 Changes for editor/html/css/editor.css: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -801,6 +801,12 @@ dialog.ai-panel::backdrop { align-self: flex-start; border-bottom-left-radius: 2px; } .ai-msg-status { color: var(--text-secondary); font-size: 11px; font-style: italic; margin-top: 4px; } .ai-msg.assistant .ai-msg-actions { display: flex; gap: 6px; margin-top: 6px; } Loading
editor/html/js/editor.js +19 −2 Changes for editor/html/js/editor.js: 19 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -1649,7 +1649,12 @@ currentAiRequest = null; statusEl.remove(); var result = resp.result || ''; addAssistantMessage(result); if (resp.applied) { Canvas.clearSelection(); DocumentTree.clearSelection(); PropertiesPanel.clear(); refreshDocument(); } addAssistantMessage(result, false, !!resp.applied, resp.tool_calls || 0); resetSendBtn(); }).catch(function(err) { currentAiRequest = null; Loading Loading @@ -1680,7 +1685,7 @@ return div; } function addAssistantMessage(text, noSave) { function addAssistantMessage(text, noSave, applied, toolCalls) { var div = document.createElement('div'); div.className = 'ai-msg assistant'; Loading @@ -1688,6 +1693,14 @@ content.textContent = text; div.appendChild(content); if (applied) { var appliedStatus = document.createElement('div'); appliedStatus.className = 'ai-msg-status'; appliedStatus.textContent = '\u2713 ' + (toolCalls || 0) + ' ' + I18n.t('I18N_AI_APPLIED', '\u00C4nderung(en) direkt am Dokument angewendet'); div.appendChild(appliedStatus); } var actions = document.createElement('div'); actions.className = 'ai-msg-actions'; Loading @@ -1701,6 +1714,9 @@ }); actions.appendChild(copyBtn); // The model already applied its changes directly via tool calls -- // no manual Replace/Child import buttons needed in that case. if (!applied) { function doImport(mode) { var htmlContent = extractHtml(text); var parentUuid = (mode === 'child') ? Canvas.getSelectedUuid() : null; Loading Loading @@ -1730,6 +1746,7 @@ childBtn.textContent = '\u2B07 ' + I18n.t('I18N_INSERT_CHILD', 'Child einf\u00fcgen'); childBtn.addEventListener('click', function() { doImport('child'); }); actions.appendChild(childBtn); } div.appendChild(actions); chatHistory.appendChild(div); Loading
editor/src/webedit_api.cpp +417 −91 File changed.Preview size limit exceeded, changes collapsed. Show changes
editor/src/webedit_api.h +9 −0 Changes for editor/src/webedit_api.h: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -287,6 +287,15 @@ namespace webedit { void mcpLoadDocument(const std::string &domain, const std::string &docId, DocumentState &doc); void mcpSaveDocument(const std::string &domain, const std::string &docId, DocumentState &doc); // Session-scoped counterpart of the mcpXxx tool entry points, used by the // in-editor AI chat (handleAiPrompt): dispatches on the same tool names/ // semantics as Mcp::callTool, but acts directly on getDocState(sessionid) // instead of a (domain, docId)-loaded DB copy, so calls mutate the live // document the browser has open. Throws McpError on failure; sets // mutated=true if the tool changed the document. json_object *callAiTool(const std::string &sessionid, const std::string &name, json_object *args, bool &mutated); // Core element-mutation logic shared by the handleXxx HTTP handlers and the // mcpXxx entry points (kept in one place because the sibling/child pointer // splicing here has real correctness subtlety). Throw McpError on failure. Loading
editor/src/webedit_mcp.cpp +10 −16 Changes for editor/src/webedit_mcp.cpp: 10 added lines, 16 removed lines. Original line number Diff line number Diff line Loading @@ -90,13 +90,17 @@ json_object *getObjectArg(json_object *args, const char *key) { return v; } struct SchemaProp { const char *name; const char *type; const char *description; }; json_object *makeTool(const char *name, const char *description, json_object *schema) { json_object *tool = json_object_new_object(); json_object_object_add(tool, "name", json_object_new_string(name)); json_object_object_add(tool, "description", json_object_new_string(description)); json_object_object_add(tool, "inputSchema", schema); return tool; } json_object *makeSchema(std::initializer_list<SchemaProp> props, } // namespace json_object *webedit::makeSchema(std::initializer_list<SchemaProp> props, std::initializer_list<const char*> required) { json_object *propsObj = json_object_new_object(); for (const auto &p : props) { Loading @@ -120,16 +124,6 @@ json_object *makeSchema(std::initializer_list<SchemaProp> props, return schema; } json_object *makeTool(const char *name, const char *description, json_object *schema) { json_object *tool = json_object_new_object(); json_object_object_add(tool, "name", json_object_new_string(name)); json_object_object_add(tool, "description", json_object_new_string(description)); json_object_object_add(tool, "inputSchema", schema); return tool; } } // namespace webedit::Mcp::Mcp(Database &db, Api &api) : _db(db), _api(api) { } Loading