mulmoclaude 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/bin/mulmoclaude.js +202 -0
- package/bin/prepare-dist.js +93 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
- package/client/assets/index-D8rhwXLq.js +4906 -0
- package/client/assets/index-KNLBjwuh.css +1 -0
- package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
- package/client/assets/material-icons-Dr0goTwe.woff +0 -0
- package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
- package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
- package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
- package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
- package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
- package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
- package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
- package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
- package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
- package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
- package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
- package/client/index.html +28 -0
- package/package.json +66 -0
- package/server/agent/attachmentConverter.ts +270 -0
- package/server/agent/config.ts +414 -0
- package/server/agent/index.ts +260 -0
- package/server/agent/mcp-server.ts +412 -0
- package/server/agent/mcp-tools/index.ts +63 -0
- package/server/agent/mcp-tools/x.ts +188 -0
- package/server/agent/plugin-names.ts +75 -0
- package/server/agent/prompt.ts +349 -0
- package/server/agent/resumeFailover.ts +129 -0
- package/server/agent/sandboxMounts.ts +329 -0
- package/server/agent/stream.ts +194 -0
- package/server/api/auth/bearerAuth.ts +61 -0
- package/server/api/auth/token.ts +98 -0
- package/server/api/csrfGuard.ts +85 -0
- package/server/api/routes/agent.ts +478 -0
- package/server/api/routes/chart.ts +98 -0
- package/server/api/routes/chat-index.ts +46 -0
- package/server/api/routes/config.ts +258 -0
- package/server/api/routes/dispatchResponse.ts +79 -0
- package/server/api/routes/files.ts +812 -0
- package/server/api/routes/html.ts +101 -0
- package/server/api/routes/image.ts +169 -0
- package/server/api/routes/mulmo-script.ts +712 -0
- package/server/api/routes/mulmoScriptValidate.ts +101 -0
- package/server/api/routes/notifications.ts +69 -0
- package/server/api/routes/pdf.ts +163 -0
- package/server/api/routes/plugins.ts +276 -0
- package/server/api/routes/presentHtml.ts +48 -0
- package/server/api/routes/roles.ts +125 -0
- package/server/api/routes/scheduler.ts +153 -0
- package/server/api/routes/schedulerHandlers.ts +151 -0
- package/server/api/routes/schedulerTasks.ts +163 -0
- package/server/api/routes/sessions.ts +294 -0
- package/server/api/routes/sessionsCursor.ts +59 -0
- package/server/api/routes/skills.ts +195 -0
- package/server/api/routes/sources.ts +540 -0
- package/server/api/routes/todos.ts +263 -0
- package/server/api/routes/todosColumnsHandlers.ts +347 -0
- package/server/api/routes/todosHandlers.ts +274 -0
- package/server/api/routes/todosItemsHandlers.ts +386 -0
- package/server/api/routes/wiki/pageIndex.ts +53 -0
- package/server/api/routes/wiki.ts +363 -0
- package/server/api/sandboxStatus.ts +64 -0
- package/server/events/notifications.ts +160 -0
- package/server/events/pub-sub/index.ts +45 -0
- package/server/events/relay-client.ts +288 -0
- package/server/events/scheduler-adapter.ts +302 -0
- package/server/events/session-store/index.ts +492 -0
- package/server/events/task-manager/index.ts +181 -0
- package/server/index.ts +572 -0
- package/server/system/config.ts +243 -0
- package/server/system/credentials.ts +220 -0
- package/server/system/docker.ts +97 -0
- package/server/system/env.ts +109 -0
- package/server/system/logger/config.ts +112 -0
- package/server/system/logger/formatters.ts +40 -0
- package/server/system/logger/index.ts +53 -0
- package/server/system/logger/rotation.ts +37 -0
- package/server/system/logger/sinks.ts +101 -0
- package/server/system/logger/types.ts +29 -0
- package/server/utils/date.ts +57 -0
- package/server/utils/errors.ts +7 -0
- package/server/utils/fetch.ts +27 -0
- package/server/utils/files/atomic.ts +125 -0
- package/server/utils/files/html-io.ts +20 -0
- package/server/utils/files/image-store.ts +66 -0
- package/server/utils/files/index.ts +45 -0
- package/server/utils/files/journal-io.ts +213 -0
- package/server/utils/files/json.ts +69 -0
- package/server/utils/files/markdown-store.ts +33 -0
- package/server/utils/files/naming.ts +50 -0
- package/server/utils/files/reference-dirs-io.ts +45 -0
- package/server/utils/files/roles-io.ts +45 -0
- package/server/utils/files/safe.ts +106 -0
- package/server/utils/files/scheduler-io.ts +20 -0
- package/server/utils/files/scheduler-overrides-io.ts +64 -0
- package/server/utils/files/session-io.ts +136 -0
- package/server/utils/files/spreadsheet-store.ts +63 -0
- package/server/utils/files/todos-io.ts +29 -0
- package/server/utils/files/user-tasks-io.ts +25 -0
- package/server/utils/files/workspace-io.ts +221 -0
- package/server/utils/gemini.ts +59 -0
- package/server/utils/gitignore.ts +69 -0
- package/server/utils/http.ts +15 -0
- package/server/utils/httpError.ts +61 -0
- package/server/utils/id.ts +16 -0
- package/server/utils/json.ts +83 -0
- package/server/utils/logBackgroundError.ts +22 -0
- package/server/utils/markdown.ts +82 -0
- package/server/utils/request.ts +29 -0
- package/server/utils/slug.ts +50 -0
- package/server/utils/spawn.ts +62 -0
- package/server/utils/time.ts +34 -0
- package/server/utils/types.ts +47 -0
- package/server/workspace/chat-index/index.ts +153 -0
- package/server/workspace/chat-index/indexer.ts +209 -0
- package/server/workspace/chat-index/paths.ts +34 -0
- package/server/workspace/chat-index/summarizer.ts +247 -0
- package/server/workspace/chat-index/types.ts +38 -0
- package/server/workspace/custom-dirs.ts +220 -0
- package/server/workspace/helps/business.md +104 -0
- package/server/workspace/helps/github.md +23 -0
- package/server/workspace/helps/index.md +60 -0
- package/server/workspace/helps/mulmoscript.md +249 -0
- package/server/workspace/helps/sandbox.md +90 -0
- package/server/workspace/helps/spreadsheet.md +43 -0
- package/server/workspace/helps/telegram.md +135 -0
- package/server/workspace/helps/wiki.md +131 -0
- package/server/workspace/journal/archivist.ts +386 -0
- package/server/workspace/journal/dailyPass.ts +743 -0
- package/server/workspace/journal/diff.ts +71 -0
- package/server/workspace/journal/index.ts +185 -0
- package/server/workspace/journal/indexFile.ts +136 -0
- package/server/workspace/journal/linkRewrite.ts +4 -0
- package/server/workspace/journal/memoryExtractor.ts +130 -0
- package/server/workspace/journal/optimizationPass.ts +160 -0
- package/server/workspace/journal/paths.ts +76 -0
- package/server/workspace/journal/state.ts +125 -0
- package/server/workspace/paths.ts +158 -0
- package/server/workspace/reference-dirs.ts +252 -0
- package/server/workspace/roles.ts +37 -0
- package/server/workspace/skills/discovery.ts +125 -0
- package/server/workspace/skills/index.ts +10 -0
- package/server/workspace/skills/parser.ts +144 -0
- package/server/workspace/skills/paths.ts +41 -0
- package/server/workspace/skills/scheduler.ts +149 -0
- package/server/workspace/skills/types.ts +30 -0
- package/server/workspace/skills/user-tasks.ts +257 -0
- package/server/workspace/skills/writer.ts +189 -0
- package/server/workspace/sources/arxivDiscovery.ts +182 -0
- package/server/workspace/sources/classifier.ts +268 -0
- package/server/workspace/sources/fetchers/arxiv.ts +170 -0
- package/server/workspace/sources/fetchers/github.ts +106 -0
- package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
- package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
- package/server/workspace/sources/fetchers/index.ts +71 -0
- package/server/workspace/sources/fetchers/registerAll.ts +15 -0
- package/server/workspace/sources/fetchers/rss.ts +141 -0
- package/server/workspace/sources/fetchers/rssParser.ts +295 -0
- package/server/workspace/sources/httpFetcher.ts +230 -0
- package/server/workspace/sources/interests.ts +120 -0
- package/server/workspace/sources/paths.ts +110 -0
- package/server/workspace/sources/pipeline/dedup.ts +60 -0
- package/server/workspace/sources/pipeline/fetch.ts +136 -0
- package/server/workspace/sources/pipeline/index.ts +249 -0
- package/server/workspace/sources/pipeline/notify.ts +72 -0
- package/server/workspace/sources/pipeline/plan.ts +66 -0
- package/server/workspace/sources/pipeline/summarize.ts +189 -0
- package/server/workspace/sources/pipeline/write.ts +185 -0
- package/server/workspace/sources/rateLimiter.ts +148 -0
- package/server/workspace/sources/registry.ts +326 -0
- package/server/workspace/sources/robots.ts +271 -0
- package/server/workspace/sources/sourceState.ts +135 -0
- package/server/workspace/sources/taxonomy.ts +74 -0
- package/server/workspace/sources/types.ts +144 -0
- package/server/workspace/sources/urls.ts +112 -0
- package/server/workspace/tool-trace/classify.ts +114 -0
- package/server/workspace/tool-trace/index.ts +250 -0
- package/server/workspace/tool-trace/writeSearch.ts +98 -0
- package/server/workspace/wiki-backlinks/index.ts +107 -0
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
- package/server/workspace/workspace.ts +66 -0
- package/src/App.vue +720 -0
- package/src/assets/mulmo_bw.png +0 -0
- package/src/components/CanvasViewToggle.vue +27 -0
- package/src/components/ChatAttachmentPreview.vue +45 -0
- package/src/components/ChatImagePreview.vue +17 -0
- package/src/components/ChatInput.vue +208 -0
- package/src/components/FileContentHeader.vue +49 -0
- package/src/components/FileContentRenderer.vue +162 -0
- package/src/components/FileTree.vue +115 -0
- package/src/components/FileTreePane.vue +85 -0
- package/src/components/FilesView.vue +206 -0
- package/src/components/LockStatusPopup.vue +111 -0
- package/src/components/NotificationBell.vue +131 -0
- package/src/components/NotificationToast.vue +72 -0
- package/src/components/PluginLauncher.vue +138 -0
- package/src/components/RightSidebar.vue +113 -0
- package/src/components/RoleSelector.vue +64 -0
- package/src/components/SessionHistoryPanel.vue +176 -0
- package/src/components/SessionTabBar.vue +81 -0
- package/src/components/SettingsMcpTab.vue +350 -0
- package/src/components/SettingsModal.vue +275 -0
- package/src/components/SettingsReferenceDirsTab.vue +173 -0
- package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
- package/src/components/SidebarHeader.vue +69 -0
- package/src/components/StackView.vue +360 -0
- package/src/components/SuggestionsPanel.vue +65 -0
- package/src/components/TodoExplorer.vue +358 -0
- package/src/components/ToolResultsPanel.vue +77 -0
- package/src/components/todo/TodoAddDialog.vue +131 -0
- package/src/components/todo/TodoEditDialog.vue +47 -0
- package/src/components/todo/TodoEditPanel.vue +113 -0
- package/src/components/todo/TodoKanbanView.vue +249 -0
- package/src/components/todo/TodoListView.vue +79 -0
- package/src/components/todo/TodoTableView.vue +177 -0
- package/src/composables/useActiveSession.ts +40 -0
- package/src/composables/useAppApi.ts +45 -0
- package/src/composables/useCanvasViewMode.ts +121 -0
- package/src/composables/useChatScroll.ts +47 -0
- package/src/composables/useClickOutside.ts +26 -0
- package/src/composables/useClipboardCopy.ts +44 -0
- package/src/composables/useContentDisplay.ts +52 -0
- package/src/composables/useDebugBeat.ts +23 -0
- package/src/composables/useDynamicFavicon.ts +115 -0
- package/src/composables/useEventListeners.ts +42 -0
- package/src/composables/useExpandedDirs.ts +64 -0
- package/src/composables/useFaviconState.ts +30 -0
- package/src/composables/useFileSelection.ts +115 -0
- package/src/composables/useFileSortMode.ts +24 -0
- package/src/composables/useFileTree.ts +85 -0
- package/src/composables/useFreshPluginData.ts +89 -0
- package/src/composables/useHealth.ts +38 -0
- package/src/composables/useImeAwareEnter.ts +57 -0
- package/src/composables/useKeyNavigation.ts +60 -0
- package/src/composables/useMarkdownLinkHandler.ts +46 -0
- package/src/composables/useMarkdownMode.ts +17 -0
- package/src/composables/useMcpTools.ts +71 -0
- package/src/composables/useMergedSessions.ts +27 -0
- package/src/composables/useNotifications.ts +90 -0
- package/src/composables/usePdfDownload.ts +60 -0
- package/src/composables/usePendingCalls.ts +77 -0
- package/src/composables/usePubSub.ts +85 -0
- package/src/composables/useRightSidebar.ts +23 -0
- package/src/composables/useRoles.ts +34 -0
- package/src/composables/useSandboxStatus.ts +67 -0
- package/src/composables/useSelectedResult.ts +49 -0
- package/src/composables/useSessionDerived.ts +51 -0
- package/src/composables/useSessionHistory.ts +81 -0
- package/src/composables/useSessionSync.ts +57 -0
- package/src/composables/useViewLayout.ts +55 -0
- package/src/config/apiRoutes.ts +173 -0
- package/src/config/pubsubChannels.ts +45 -0
- package/src/config/roles.ts +335 -0
- package/src/config/schedulerActions.ts +25 -0
- package/src/config/toolNames.ts +71 -0
- package/src/config/workspacePaths.ts +24 -0
- package/src/index.css +107 -0
- package/src/main.ts +25 -0
- package/src/plugins/canvas/Preview.vue +13 -0
- package/src/plugins/canvas/View.vue +333 -0
- package/src/plugins/canvas/definition.ts +38 -0
- package/src/plugins/canvas/index.ts +36 -0
- package/src/plugins/chart/Preview.vue +49 -0
- package/src/plugins/chart/View.vue +143 -0
- package/src/plugins/chart/definition.ts +58 -0
- package/src/plugins/chart/index.ts +52 -0
- package/src/plugins/editImage/Preview.vue +13 -0
- package/src/plugins/editImage/View.vue +13 -0
- package/src/plugins/editImage/definition.ts +27 -0
- package/src/plugins/editImage/index.ts +36 -0
- package/src/plugins/generateImage/Preview.vue +13 -0
- package/src/plugins/generateImage/View.vue +33 -0
- package/src/plugins/generateImage/definition.ts +32 -0
- package/src/plugins/generateImage/index.ts +56 -0
- package/src/plugins/manageRoles/Preview.vue +49 -0
- package/src/plugins/manageRoles/View.vue +525 -0
- package/src/plugins/manageRoles/definition.ts +43 -0
- package/src/plugins/manageRoles/index.ts +47 -0
- package/src/plugins/manageSkills/Preview.vue +21 -0
- package/src/plugins/manageSkills/View.vue +321 -0
- package/src/plugins/manageSkills/definition.ts +49 -0
- package/src/plugins/manageSkills/index.ts +49 -0
- package/src/plugins/manageSource/Preview.vue +33 -0
- package/src/plugins/manageSource/View.vue +697 -0
- package/src/plugins/manageSource/definition.ts +63 -0
- package/src/plugins/manageSource/index.ts +66 -0
- package/src/plugins/markdown/Preview.vue +77 -0
- package/src/plugins/markdown/View.vue +476 -0
- package/src/plugins/markdown/definition.ts +50 -0
- package/src/plugins/markdown/index.ts +36 -0
- package/src/plugins/presentHtml/Preview.vue +25 -0
- package/src/plugins/presentHtml/View.vue +52 -0
- package/src/plugins/presentHtml/definition.ts +27 -0
- package/src/plugins/presentHtml/helpers.ts +72 -0
- package/src/plugins/presentHtml/index.ts +41 -0
- package/src/plugins/presentMulmoScript/Preview.vue +23 -0
- package/src/plugins/presentMulmoScript/View.vue +1166 -0
- package/src/plugins/presentMulmoScript/definition.ts +95 -0
- package/src/plugins/presentMulmoScript/helpers.ts +162 -0
- package/src/plugins/presentMulmoScript/index.ts +40 -0
- package/src/plugins/scheduler/Preview.vue +67 -0
- package/src/plugins/scheduler/TasksTab.vue +205 -0
- package/src/plugins/scheduler/View.vue +565 -0
- package/src/plugins/scheduler/definition.ts +57 -0
- package/src/plugins/scheduler/index.ts +45 -0
- package/src/plugins/scheduler/viewModes.ts +26 -0
- package/src/plugins/spreadsheet/Preview.vue +29 -0
- package/src/plugins/spreadsheet/View.vue +997 -0
- package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
- package/src/plugins/spreadsheet/definition.ts +121 -0
- package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
- package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
- package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
- package/src/plugins/spreadsheet/engine/engine.ts +176 -0
- package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
- package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
- package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
- package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
- package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
- package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
- package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
- package/src/plugins/spreadsheet/engine/index.ts +27 -0
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
- package/src/plugins/spreadsheet/engine/parser.ts +143 -0
- package/src/plugins/spreadsheet/engine/registry.ts +150 -0
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
- package/src/plugins/spreadsheet/engine/types.ts +64 -0
- package/src/plugins/spreadsheet/index.ts +36 -0
- package/src/plugins/textResponse/Preview.vue +94 -0
- package/src/plugins/textResponse/View.vue +503 -0
- package/src/plugins/textResponse/definition.ts +34 -0
- package/src/plugins/textResponse/index.ts +27 -0
- package/src/plugins/textResponse/plugin.ts +29 -0
- package/src/plugins/textResponse/samples.ts +97 -0
- package/src/plugins/textResponse/types.ts +11 -0
- package/src/plugins/todo/Preview.vue +63 -0
- package/src/plugins/todo/View.vue +364 -0
- package/src/plugins/todo/composables/useTodos.ts +177 -0
- package/src/plugins/todo/definition.ts +45 -0
- package/src/plugins/todo/index.ts +61 -0
- package/src/plugins/todo/labels.ts +163 -0
- package/src/plugins/todo/priority.ts +98 -0
- package/src/plugins/todo/viewModes.ts +19 -0
- package/src/plugins/ui-image/ImagePreview.vue +23 -0
- package/src/plugins/ui-image/ImageView.vue +34 -0
- package/src/plugins/ui-image/index.ts +3 -0
- package/src/plugins/ui-image/types.ts +4 -0
- package/src/plugins/wiki/Preview.vue +65 -0
- package/src/plugins/wiki/View.vue +342 -0
- package/src/plugins/wiki/definition.ts +25 -0
- package/src/plugins/wiki/helpers.ts +59 -0
- package/src/plugins/wiki/index.ts +52 -0
- package/src/router/guards.ts +61 -0
- package/src/router/index.ts +50 -0
- package/src/tools/index.ts +52 -0
- package/src/tools/types.ts +27 -0
- package/src/types/events.ts +16 -0
- package/src/types/fileTree.ts +13 -0
- package/src/types/notification.ts +67 -0
- package/src/types/session.ts +116 -0
- package/src/types/sse.ts +90 -0
- package/src/types/toolCallHistory.ts +13 -0
- package/src/utils/agent/eventDispatch.ts +74 -0
- package/src/utils/agent/request.ts +55 -0
- package/src/utils/agent/toolCalls.ts +62 -0
- package/src/utils/api.ts +218 -0
- package/src/utils/canvas/viewMode.ts +46 -0
- package/src/utils/dom/authTokenMeta.ts +20 -0
- package/src/utils/dom/clickOutside.ts +11 -0
- package/src/utils/dom/externalLink.ts +57 -0
- package/src/utils/dom/scrollable.ts +24 -0
- package/src/utils/errors.ts +11 -0
- package/src/utils/files/expandedDirs.ts +25 -0
- package/src/utils/files/filename.ts +12 -0
- package/src/utils/files/sortChildren.ts +20 -0
- package/src/utils/filesPreview/schedulerPreview.ts +38 -0
- package/src/utils/filesPreview/todoPreview.ts +40 -0
- package/src/utils/format/date.ts +85 -0
- package/src/utils/format/frontmatter.ts +80 -0
- package/src/utils/format/jsonSyntax.ts +109 -0
- package/src/utils/html/previewCsp.ts +65 -0
- package/src/utils/image/resolve.ts +8 -0
- package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
- package/src/utils/markdown/extractFirstH1.ts +39 -0
- package/src/utils/notification/dispatch.ts +22 -0
- package/src/utils/path/relativeLink.ts +130 -0
- package/src/utils/role/icon.ts +20 -0
- package/src/utils/role/merge.ts +10 -0
- package/src/utils/role/plugins.ts +12 -0
- package/src/utils/session/mergeSessions.ts +103 -0
- package/src/utils/session/seedRoleDefault.ts +35 -0
- package/src/utils/session/sessionEntries.ts +121 -0
- package/src/utils/session/sessionFactory.ts +22 -0
- package/src/utils/session/sessionHelpers.ts +99 -0
- package/src/utils/tools/dedup.ts +17 -0
- package/src/utils/tools/mcp.ts +33 -0
- package/src/utils/tools/pendingCalls.ts +16 -0
- package/src/utils/tools/result.ts +40 -0
- package/src/utils/types.ts +44 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="text-sm">
|
|
3
|
+
<div class="font-medium text-gray-700 truncate mb-1">
|
|
4
|
+
{{ title }}
|
|
5
|
+
</div>
|
|
6
|
+
<div v-if="hint" class="text-xs text-gray-500 leading-relaxed truncate">
|
|
7
|
+
{{ hint }}
|
|
8
|
+
</div>
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup lang="ts">
|
|
13
|
+
import { computed } from "vue";
|
|
14
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
15
|
+
import type { PresentChartData } from "./index";
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{ result: ToolResultComplete<PresentChartData> }>();
|
|
18
|
+
|
|
19
|
+
const data = computed(() => props.result.data);
|
|
20
|
+
const title = computed(() => data.value?.title ?? data.value?.document?.title ?? "Chart");
|
|
21
|
+
|
|
22
|
+
// Condensed "n charts: line, bar, …" hint derived from the document
|
|
23
|
+
// so the preview card tells the user what's inside without opening.
|
|
24
|
+
const hint = computed(() => {
|
|
25
|
+
const charts = data.value?.document?.charts ?? [];
|
|
26
|
+
if (charts.length === 0) return "";
|
|
27
|
+
const types = charts
|
|
28
|
+
.map((c) => c.type ?? inferTypeFromOption(c.option))
|
|
29
|
+
.filter((t): t is string => Boolean(t))
|
|
30
|
+
.slice(0, 3);
|
|
31
|
+
const suffix = charts.length > types.length ? ", …" : "";
|
|
32
|
+
const typeList = types.join(", ");
|
|
33
|
+
const plural = charts.length === 1 ? "" : "s";
|
|
34
|
+
const details = typeList ? `: ${typeList}${suffix}` : "";
|
|
35
|
+
return `${charts.length} chart${plural}${details}`;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function inferTypeFromOption(option: Record<string, unknown>): string | null {
|
|
39
|
+
const series = option.series;
|
|
40
|
+
if (Array.isArray(series) && series.length > 0) {
|
|
41
|
+
const first = series[0] as { type?: unknown };
|
|
42
|
+
if (typeof first.type === "string") return first.type;
|
|
43
|
+
} else if (series && typeof series === "object") {
|
|
44
|
+
const t = (series as { type?: unknown }).type;
|
|
45
|
+
if (typeof t === "string") return t;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-full flex flex-col overflow-hidden">
|
|
3
|
+
<div class="px-4 py-2 border-b border-gray-100 shrink-0 flex items-center justify-between">
|
|
4
|
+
<span class="text-sm font-medium text-gray-700 truncate">
|
|
5
|
+
{{ title ?? "Chart" }}
|
|
6
|
+
</span>
|
|
7
|
+
<span class="text-xs text-gray-500 shrink-0"> {{ charts.length }} chart{{ charts.length === 1 ? "" : "s" }} </span>
|
|
8
|
+
</div>
|
|
9
|
+
<div class="flex-1 overflow-y-auto p-4 space-y-4">
|
|
10
|
+
<div v-for="(chart, idx) in charts" :key="idx" class="border border-gray-200 rounded-lg bg-white" :data-testid="`chart-card-${idx}`">
|
|
11
|
+
<div class="px-3 py-2 border-b border-gray-100 flex items-center justify-between gap-2">
|
|
12
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
13
|
+
<span class="text-sm font-medium text-gray-800 truncate">
|
|
14
|
+
{{ chart.title ?? `Chart ${idx + 1}` }}
|
|
15
|
+
</span>
|
|
16
|
+
<span v-if="chart.type" class="text-[10px] uppercase tracking-wide rounded px-1.5 py-0.5 bg-blue-50 text-blue-700 shrink-0">
|
|
17
|
+
{{ chart.type }}
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
<button
|
|
21
|
+
class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-500 hover:bg-gray-50 shrink-0"
|
|
22
|
+
:data-testid="`chart-export-png-${idx}`"
|
|
23
|
+
@click="exportPng(idx, chart.title)"
|
|
24
|
+
>
|
|
25
|
+
<span class="material-icons text-sm align-middle">download</span>
|
|
26
|
+
PNG
|
|
27
|
+
</button>
|
|
28
|
+
</div>
|
|
29
|
+
<div :ref="(el) => setChartRef(idx, el as HTMLDivElement | null)" class="w-full h-[400px]" :data-testid="`chart-canvas-${idx}`" />
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
|
37
|
+
import * as echarts from "echarts";
|
|
38
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
39
|
+
import type { ChartEntry, PresentChartData } from "./index";
|
|
40
|
+
import { isRecord } from "../../utils/types";
|
|
41
|
+
|
|
42
|
+
const props = defineProps<{
|
|
43
|
+
selectedResult: ToolResultComplete<PresentChartData>;
|
|
44
|
+
}>();
|
|
45
|
+
|
|
46
|
+
const data = computed(() => props.selectedResult.data);
|
|
47
|
+
const charts = computed<ChartEntry[]>(() => data.value?.document?.charts ?? []);
|
|
48
|
+
const title = computed(() => data.value?.title ?? data.value?.document?.title);
|
|
49
|
+
|
|
50
|
+
const containers = ref<Array<HTMLDivElement | null>>([]);
|
|
51
|
+
// Kept as a plain array (not `ref`): ECharts instances are managed
|
|
52
|
+
// imperatively and should not trigger Vue re-renders on mutation.
|
|
53
|
+
const instances: echarts.ECharts[] = [];
|
|
54
|
+
|
|
55
|
+
function setChartRef(idx: number, element: HTMLDivElement | null): void {
|
|
56
|
+
containers.value[idx] = element;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function disposeAll(): void {
|
|
60
|
+
for (const instance of instances) {
|
|
61
|
+
instance.dispose();
|
|
62
|
+
}
|
|
63
|
+
instances.length = 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Force-disable mouse-wheel zoom on any dataZoom entry in the user's
|
|
67
|
+
// option. Rationale: in stack view the page needs to scroll past the
|
|
68
|
+
// chart, and `inside`-type dataZoom captures the wheel by default
|
|
69
|
+
// (zoomOnMouseWheel=true), which traps the scroll over the canvas.
|
|
70
|
+
// Toolbox/slider/drag zoom still work — only the wheel is disabled.
|
|
71
|
+
function disableWheelZoom(option: Record<string, unknown>): Record<string, unknown> {
|
|
72
|
+
const dataZoom = option.dataZoom;
|
|
73
|
+
if (dataZoom === undefined || dataZoom === null) return option;
|
|
74
|
+
const normalise = (entry: unknown): unknown => {
|
|
75
|
+
if (!isRecord(entry)) return entry;
|
|
76
|
+
return {
|
|
77
|
+
...entry,
|
|
78
|
+
zoomOnMouseWheel: false,
|
|
79
|
+
moveOnMouseWheel: false,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
const next = Array.isArray(dataZoom) ? dataZoom.map(normalise) : normalise(dataZoom);
|
|
83
|
+
return { ...option, dataZoom: next };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function renderAll(): void {
|
|
87
|
+
disposeAll();
|
|
88
|
+
for (let i = 0; i < charts.value.length; i += 1) {
|
|
89
|
+
const element = containers.value[i];
|
|
90
|
+
const chart = charts.value[i];
|
|
91
|
+
if (!element || !chart) continue;
|
|
92
|
+
const instance = echarts.init(element);
|
|
93
|
+
try {
|
|
94
|
+
instance.setOption(disableWheelZoom(chart.option));
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.warn(`[chart] setOption failed for chart ${i}`, err);
|
|
97
|
+
}
|
|
98
|
+
instances[i] = instance;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleResize(): void {
|
|
103
|
+
for (const instance of instances) instance.resize();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onMounted(() => {
|
|
107
|
+
// Wait a tick so the refs are populated before we render.
|
|
108
|
+
queueMicrotask(renderAll);
|
|
109
|
+
window.addEventListener("resize", handleResize);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
onBeforeUnmount(() => {
|
|
113
|
+
window.removeEventListener("resize", handleResize);
|
|
114
|
+
disposeAll();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Re-render when the selected result switches to a different document
|
|
118
|
+
// (e.g. user clicks another chart result in the sidebar).
|
|
119
|
+
watch(
|
|
120
|
+
() => data.value?.filePath,
|
|
121
|
+
() => queueMicrotask(renderAll),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
function exportPng(idx: number, chartTitle?: string): void {
|
|
125
|
+
const instance = instances[idx];
|
|
126
|
+
if (!instance) return;
|
|
127
|
+
const dataUrl = instance.getDataURL({
|
|
128
|
+
type: "png",
|
|
129
|
+
pixelRatio: 2,
|
|
130
|
+
backgroundColor: "#ffffff",
|
|
131
|
+
});
|
|
132
|
+
let filenameSlug = (chartTitle ?? title.value ?? "chart").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
|
|
133
|
+
while (filenameSlug.startsWith("-")) filenameSlug = filenameSlug.slice(1);
|
|
134
|
+
while (filenameSlug.endsWith("-")) filenameSlug = filenameSlug.slice(0, -1);
|
|
135
|
+
if (!filenameSlug) filenameSlug = "chart";
|
|
136
|
+
const anchor = document.createElement("a");
|
|
137
|
+
anchor.href = dataUrl;
|
|
138
|
+
anchor.download = `${filenameSlug}-${idx + 1}.png`;
|
|
139
|
+
document.body.appendChild(anchor);
|
|
140
|
+
anchor.click();
|
|
141
|
+
document.body.removeChild(anchor);
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
|
|
3
|
+
export const TOOL_NAME = "presentChart";
|
|
4
|
+
|
|
5
|
+
const toolDefinition: ToolDefinition = {
|
|
6
|
+
type: "function",
|
|
7
|
+
name: TOOL_NAME,
|
|
8
|
+
description:
|
|
9
|
+
"Save and present one or more Apache ECharts visualizations as a single document. Use this for line, bar, area, scatter, pie, candlestick, heatmap, sankey, or graph/network charts — anything ECharts supports. Pass ECharts option object(s) directly; the plugin calls setOption on each one. Use `charts: []` array form even for a single chart so multi-chart dashboards share the same slug.",
|
|
10
|
+
parameters: {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
document: {
|
|
14
|
+
type: "object",
|
|
15
|
+
description:
|
|
16
|
+
"Chart document. Contains an optional title and an array of chart entries. Each entry has its own ECharts option object that the UI renders independently.",
|
|
17
|
+
properties: {
|
|
18
|
+
title: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Optional human-friendly title for the whole document. Used to derive the file slug and as the preview label.",
|
|
21
|
+
},
|
|
22
|
+
charts: {
|
|
23
|
+
type: "array",
|
|
24
|
+
description: "List of charts to render, in order. Each charts[i].option is passed as-is to ECharts' setOption().",
|
|
25
|
+
items: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
title: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Optional short label for this specific chart (shown above it in the UI).",
|
|
31
|
+
},
|
|
32
|
+
type: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description:
|
|
35
|
+
"Informational tag shown in the UI (e.g. 'line', 'bar', 'candlestick', 'sankey'). The actual chart type is determined by option.series[].type.",
|
|
36
|
+
},
|
|
37
|
+
option: {
|
|
38
|
+
type: "object",
|
|
39
|
+
description:
|
|
40
|
+
"Full ECharts option object. Include all series, axes, tooltip, legend, dataset — anything ECharts accepts. Keep data inline; large datasets are fine.",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["option"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
required: ["charts"],
|
|
48
|
+
},
|
|
49
|
+
title: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "Short label shown in the canvas preview sidebar. Defaults to document.title, or 'Chart' when both are blank.",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ["document"],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default toolDefinition;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ToolPlugin } from "../../tools/types";
|
|
2
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
3
|
+
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
4
|
+
import View from "./View.vue";
|
|
5
|
+
import Preview from "./Preview.vue";
|
|
6
|
+
import { apiPost } from "../../utils/api";
|
|
7
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
8
|
+
|
|
9
|
+
export interface ChartEntry {
|
|
10
|
+
title?: string;
|
|
11
|
+
type?: string;
|
|
12
|
+
option: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ChartDocument {
|
|
16
|
+
title?: string;
|
|
17
|
+
charts: ChartEntry[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PresentChartData {
|
|
21
|
+
document: ChartDocument;
|
|
22
|
+
title?: string;
|
|
23
|
+
filePath: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const presentChartPlugin: ToolPlugin<PresentChartData> = {
|
|
27
|
+
toolDefinition,
|
|
28
|
+
|
|
29
|
+
async execute(_context, args) {
|
|
30
|
+
const result = await apiPost<ToolResult<PresentChartData>>(API_ROUTES.chart.present, args);
|
|
31
|
+
if (!result.ok) {
|
|
32
|
+
return {
|
|
33
|
+
toolName: TOOL_NAME,
|
|
34
|
+
uuid: crypto.randomUUID(),
|
|
35
|
+
message: result.error,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
...result.data,
|
|
40
|
+
toolName: TOOL_NAME,
|
|
41
|
+
uuid: crypto.randomUUID(),
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
isEnabled: () => true,
|
|
46
|
+
generatingMessage: "Rendering chart…",
|
|
47
|
+
viewComponent: View,
|
|
48
|
+
previewComponent: Preview,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default presentChartPlugin;
|
|
52
|
+
export { TOOL_NAME };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ImagePreview :result="result" alt="Edited image" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { ImagePreview } from "../ui-image";
|
|
7
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
8
|
+
import type { ImageToolData } from "./definition";
|
|
9
|
+
|
|
10
|
+
defineProps<{
|
|
11
|
+
result: ToolResult<ImageToolData>;
|
|
12
|
+
}>();
|
|
13
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ImageView v-if="selectedResult" :selected-result="selectedResult" alt="Edited image" prompt-label="Edit prompt" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { ImageView } from "../ui-image";
|
|
7
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
8
|
+
import type { ImageToolData } from "./definition";
|
|
9
|
+
|
|
10
|
+
defineProps<{
|
|
11
|
+
selectedResult: ToolResult<ImageToolData>;
|
|
12
|
+
}>();
|
|
13
|
+
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
|
|
3
|
+
export const TOOL_NAME = "editImage";
|
|
4
|
+
|
|
5
|
+
export interface ImageToolData {
|
|
6
|
+
imageData: string;
|
|
7
|
+
prompt: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const toolDefinition: ToolDefinition = {
|
|
11
|
+
type: "function",
|
|
12
|
+
name: TOOL_NAME,
|
|
13
|
+
description: "Edit the previously generated image based on a text prompt.",
|
|
14
|
+
prompt: `When the user asks 'turn this image into ...', call ${TOOL_NAME} API to generate a new image.`,
|
|
15
|
+
parameters: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
prompt: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Description of the edits to be made to the image in English",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["prompt"],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default toolDefinition;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ToolPlugin } from "../../tools/types";
|
|
2
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
3
|
+
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
4
|
+
import type { ImageToolData } from "./definition";
|
|
5
|
+
import View from "./View.vue";
|
|
6
|
+
import Preview from "./Preview.vue";
|
|
7
|
+
import { apiPost } from "../../utils/api";
|
|
8
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
9
|
+
|
|
10
|
+
const editImagePlugin: ToolPlugin<ImageToolData> = {
|
|
11
|
+
toolDefinition,
|
|
12
|
+
|
|
13
|
+
async execute(_context, args) {
|
|
14
|
+
const result = await apiPost<ToolResult<ImageToolData>>(API_ROUTES.image.edit, args);
|
|
15
|
+
if (!result.ok) {
|
|
16
|
+
return {
|
|
17
|
+
toolName: TOOL_NAME,
|
|
18
|
+
uuid: crypto.randomUUID(),
|
|
19
|
+
message: result.error,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...result.data,
|
|
24
|
+
toolName: TOOL_NAME,
|
|
25
|
+
uuid: crypto.randomUUID(),
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
isEnabled: () => true,
|
|
30
|
+
generatingMessage: "Editing image...",
|
|
31
|
+
viewComponent: View,
|
|
32
|
+
previewComponent: Preview,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default editImagePlugin;
|
|
36
|
+
export { TOOL_NAME };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ImagePreview :result="result" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { ImagePreview } from "../ui-image";
|
|
7
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
8
|
+
import type { ImageToolData } from "./definition";
|
|
9
|
+
|
|
10
|
+
defineProps<{
|
|
11
|
+
result: ToolResult<ImageToolData>;
|
|
12
|
+
}>();
|
|
13
|
+
</script>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ImageView v-if="imageResult" :selected-result="imageResult" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { ref, watch } from "vue";
|
|
7
|
+
import { ImageView } from "../ui-image";
|
|
8
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
9
|
+
import type { ImageToolData } from "./definition";
|
|
10
|
+
import { TOOL_NAME } from "./definition";
|
|
11
|
+
|
|
12
|
+
const props = defineProps<{
|
|
13
|
+
selectedResult: ToolResult<ImageToolData>;
|
|
14
|
+
sendTextMessage: (text?: string) => void;
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
defineEmits<{
|
|
18
|
+
updateResult: [result: ToolResult];
|
|
19
|
+
}>();
|
|
20
|
+
|
|
21
|
+
// Use ref + watch pattern for proper reactivity from external packages
|
|
22
|
+
const imageResult = ref<ToolResult<ImageToolData> | null>(null);
|
|
23
|
+
|
|
24
|
+
watch(
|
|
25
|
+
() => props.selectedResult,
|
|
26
|
+
(newResult) => {
|
|
27
|
+
if (newResult?.toolName === TOOL_NAME && newResult.data) {
|
|
28
|
+
imageResult.value = newResult;
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{ immediate: true, deep: true },
|
|
32
|
+
);
|
|
33
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
|
|
3
|
+
export const TOOL_NAME = "generateImage";
|
|
4
|
+
|
|
5
|
+
export interface ImageToolData {
|
|
6
|
+
imageData: string;
|
|
7
|
+
prompt?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface GenerateImageArgs {
|
|
11
|
+
prompt: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const toolDefinition: ToolDefinition = {
|
|
15
|
+
type: "function",
|
|
16
|
+
name: TOOL_NAME,
|
|
17
|
+
description:
|
|
18
|
+
"Generate an image based on the prompt and display it on the screen. Be descriptive and specify the concrete details of the images in the prompt. Each call generates one image.",
|
|
19
|
+
prompt: `When the user asks you to generate, draw, or create an image, use the ${TOOL_NAME} API. You may also offer to generate an image when a visual would clearly enhance the conversation, but do not generate images unsolicited during casual discussion.`,
|
|
20
|
+
parameters: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
prompt: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "A detailed prompt describing the image to generate",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["prompt"],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default toolDefinition;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ToolResult } from "gui-chat-protocol";
|
|
2
|
+
import type { ToolPlugin } from "../../tools/types";
|
|
3
|
+
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
4
|
+
import type { ImageToolData } from "./definition";
|
|
5
|
+
import View from "./View.vue";
|
|
6
|
+
import Preview from "./Preview.vue";
|
|
7
|
+
import { apiPost } from "../../utils/api";
|
|
8
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
9
|
+
|
|
10
|
+
function createUploadedImageResult(imageData: string, fileName: string, prompt: string): ToolResult<ImageToolData, never> {
|
|
11
|
+
return {
|
|
12
|
+
toolName: TOOL_NAME,
|
|
13
|
+
data: { imageData, prompt },
|
|
14
|
+
message: "",
|
|
15
|
+
title: fileName,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const generateImagePlugin: ToolPlugin<ImageToolData> = {
|
|
20
|
+
toolDefinition,
|
|
21
|
+
|
|
22
|
+
async execute(_context, args) {
|
|
23
|
+
const result = await apiPost<ToolResult<ImageToolData>>(API_ROUTES.image.generate, args);
|
|
24
|
+
if (!result.ok) {
|
|
25
|
+
return {
|
|
26
|
+
toolName: TOOL_NAME,
|
|
27
|
+
uuid: crypto.randomUUID(),
|
|
28
|
+
message: result.error,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
...result.data,
|
|
33
|
+
toolName: TOOL_NAME,
|
|
34
|
+
uuid: crypto.randomUUID(),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
isEnabled: () => true,
|
|
39
|
+
generatingMessage: "Generating image...",
|
|
40
|
+
inputHandlers: [
|
|
41
|
+
{
|
|
42
|
+
type: "file",
|
|
43
|
+
acceptedTypes: ["image/png", "image/jpeg"],
|
|
44
|
+
handleInput: (fileData: string, fileName: string) => createUploadedImageResult(fileData, fileName, ""),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: "clipboard-image",
|
|
48
|
+
handleInput: (imageData: string) => createUploadedImageResult(imageData, "clipboard-image.png", ""),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
viewComponent: View,
|
|
52
|
+
previewComponent: Preview,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default generateImagePlugin;
|
|
56
|
+
export { TOOL_NAME };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="text-sm">
|
|
3
|
+
<div class="flex items-center gap-1 font-medium text-gray-700 mb-1">
|
|
4
|
+
<span class="material-icons" style="font-size: 14px">manage_accounts</span>
|
|
5
|
+
<span>{{ customRoles.length }} custom role{{ customRoles.length !== 1 ? "s" : "" }}</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div v-for="role in customRoles" :key="role.id" class="text-xs text-gray-600 flex items-center gap-1">
|
|
8
|
+
<span class="material-icons" style="font-size: 12px">{{ role.icon }}</span>
|
|
9
|
+
{{ role.name }}
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup lang="ts">
|
|
15
|
+
import { ref, watch } from "vue";
|
|
16
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
17
|
+
import type { ManageRolesData, CustomRole } from "./index";
|
|
18
|
+
import { useFreshPluginData } from "../../composables/useFreshPluginData";
|
|
19
|
+
import { API_ROUTES } from "../../config/apiRoutes";
|
|
20
|
+
|
|
21
|
+
const props = defineProps<{ result: ToolResultComplete<ManageRolesData> }>();
|
|
22
|
+
const customRoles = ref<CustomRole[]>(props.result.data?.customRoles ?? []);
|
|
23
|
+
|
|
24
|
+
const { refresh } = useFreshPluginData<CustomRole[]>({
|
|
25
|
+
endpoint: () => API_ROUTES.roles.list,
|
|
26
|
+
extract: (json) => (Array.isArray(json) ? (json as CustomRole[]) : null),
|
|
27
|
+
apply: (data) => {
|
|
28
|
+
customRoles.value = data;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Watch the data itself — not just uuid — because the View emits
|
|
33
|
+
// updateResult with the same uuid after an in-place edit. A uuid-only
|
|
34
|
+
// watch would miss those updates and the preview would go stale.
|
|
35
|
+
watch(
|
|
36
|
+
() => props.result.data?.customRoles,
|
|
37
|
+
(next) => {
|
|
38
|
+
customRoles.value = next ?? [];
|
|
39
|
+
},
|
|
40
|
+
{ deep: true },
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
watch(
|
|
44
|
+
() => props.result.uuid,
|
|
45
|
+
() => {
|
|
46
|
+
void refresh();
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
</script>
|