mulmoclaude 0.5.2 → 0.6.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/Dockerfile.sandbox +100 -0
- package/README.md +17 -4
- package/bin/mulmoclaude.js +46 -15
- package/bin/prepare-dist.js +18 -2
- package/client/assets/chunk-CernVdwh.js +1 -0
- package/client/assets/chunk-D8eiyYIV-C1eAZMzz.js +1 -0
- package/client/assets/html2canvas-CDGcmOD3-BbPeutDg.js +5 -0
- package/client/assets/index-BbgSjFQ8.js +4968 -0
- package/client/assets/index-ECD0lgIv.css +2 -0
- package/client/assets/{index.es-D4YyL_Dg-BgT6a3Nd.js → index.es-DqtpmBm8-DJdTPdnc.js} +5 -5
- package/client/assets/material-symbols-outlined-BLDfUw-_.woff2 +0 -0
- package/client/assets/runtime-protocol-vue-6WYa8hAs.js +1 -0
- package/client/assets/runtime-vue-BVUzgYGA.js +1 -0
- package/client/assets/typeof-DBp4T-Ny-C2xoZtcz.js +1 -0
- package/client/assets/vue-1e_vz2LW.js +1 -0
- package/client/assets/vue.runtime.esm-bundler-DQ8Kjjui.js +4 -0
- package/client/index.html +33 -2
- package/package.json +20 -18
- package/sandbox-entrypoint.sh +106 -0
- package/server/accounting/accountNormalize.ts +32 -0
- package/server/accounting/defaultAccounts.ts +87 -0
- package/server/accounting/eventPublisher.ts +51 -0
- package/server/accounting/journal.ts +252 -0
- package/server/accounting/openingBalances.ts +114 -0
- package/server/accounting/report.ts +237 -0
- package/server/accounting/service.ts +718 -0
- package/server/accounting/snapshotCache.ts +333 -0
- package/server/accounting/timeSeries.ts +265 -0
- package/server/accounting/types.ts +148 -0
- package/server/agent/activeTools.ts +128 -0
- package/server/agent/attachmentConverter.ts +10 -5
- package/server/agent/backend/claude-code.ts +8 -2
- package/server/agent/backend/types.ts +1 -1
- package/server/agent/config.ts +101 -31
- package/server/agent/index.ts +45 -33
- package/server/agent/mcp-server.ts +146 -69
- package/server/agent/mcp-tools/index.ts +1 -5
- package/server/agent/mcp-tools/notify.ts +2 -22
- package/server/agent/mcp-tools/x.ts +0 -4
- package/server/agent/mcpHealth.ts +168 -0
- package/server/agent/plugin-names.ts +20 -77
- package/server/agent/prompt.ts +259 -51
- package/server/agent/resumeFailover.ts +1 -1
- package/server/agent/stream.ts +0 -1
- package/server/api/auth/bearerAuth.ts +5 -5
- package/server/api/csrfGuard.ts +1 -1
- package/server/api/routes/accounting.ts +366 -0
- package/server/api/routes/agent.ts +509 -46
- package/server/api/routes/attachment.ts +104 -0
- package/server/api/routes/chart.ts +2 -1
- package/server/api/routes/config.ts +12 -12
- package/server/api/routes/files.ts +105 -48
- package/server/api/routes/image.ts +70 -25
- package/server/api/routes/journal.ts +35 -0
- package/server/api/routes/mulmo-script.ts +358 -118
- package/server/api/routes/mulmoScriptValidate.ts +1 -1
- package/server/api/routes/news.ts +1 -1
- package/server/api/routes/notifications.ts +92 -22
- package/server/api/routes/notifier.ts +98 -0
- package/server/api/routes/pdf.ts +188 -48
- package/server/api/routes/plugins.ts +34 -14
- package/server/api/routes/presentHtml.ts +58 -3
- package/server/api/routes/roles.ts +1 -8
- package/server/api/routes/runtime-plugin.ts +224 -0
- package/server/api/routes/scheduler.ts +7 -5
- package/server/api/routes/schedulerHandlers.ts +1 -1
- package/server/api/routes/schedulerTasks.ts +8 -7
- package/server/api/routes/sessions.ts +234 -121
- package/server/api/routes/skills.ts +56 -51
- package/server/api/routes/sources.ts +52 -45
- package/server/api/routes/translation.ts +44 -0
- package/server/api/routes/wiki/frontmatter.ts +13 -65
- package/server/api/routes/wiki/history.ts +261 -0
- package/server/api/routes/wiki/pageIndex.ts +1 -1
- package/server/api/routes/wiki.ts +50 -26
- package/server/events/file-change.ts +83 -0
- package/server/events/notifications.ts +247 -91
- package/server/events/pub-sub/index.ts +1 -1
- package/server/events/relay-client.ts +5 -5
- package/server/events/scheduler-adapter.ts +2 -2
- package/server/events/session-store/index.ts +110 -22
- package/server/events/task-manager/index.ts +10 -9
- package/server/index.ts +509 -33
- package/server/notifier/engine.ts +419 -0
- package/server/notifier/legacy-adapters.ts +76 -0
- package/server/notifier/runtime-api.ts +74 -0
- package/server/notifier/store.ts +70 -0
- package/server/notifier/types.ts +121 -0
- package/server/plugins/dev-loader.ts +171 -0
- package/server/plugins/dev-watcher.ts +150 -0
- package/server/plugins/diagnostics.ts +188 -0
- package/server/plugins/preset-list.ts +52 -0
- package/server/plugins/preset-loader.ts +112 -0
- package/server/plugins/runtime-chat-api.ts +38 -0
- package/server/plugins/runtime-loader.ts +430 -0
- package/server/plugins/runtime-registry.ts +112 -0
- package/server/plugins/runtime-tasks-api.ts +50 -0
- package/server/plugins/runtime.ts +378 -0
- package/server/services/translation/cache.ts +72 -0
- package/server/services/translation/index.ts +106 -0
- package/server/services/translation/llm.ts +140 -0
- package/server/services/translation/types.ts +35 -0
- package/server/system/credentials.ts +13 -2
- package/server/system/env.ts +6 -1
- package/server/system/logger/formatters.ts +46 -4
- package/server/system/logger/index.ts +4 -4
- package/server/system/logger/sinks.ts +26 -5
- package/server/system/logger/types.ts +2 -2
- package/server/utils/dev-plugin-args.d.mts +11 -0
- package/server/utils/dev-plugin-args.mjs +43 -0
- package/server/utils/errors.ts +13 -4
- package/server/utils/files/accounting-io.ts +295 -0
- package/server/utils/files/atomic.ts +17 -49
- package/server/utils/files/attachment-store.ts +182 -0
- package/server/utils/files/html-io.ts +1 -7
- package/server/utils/files/html-store.ts +19 -0
- package/server/utils/files/image-store.ts +20 -22
- package/server/utils/files/index.ts +5 -15
- package/server/utils/files/journal-io.ts +7 -35
- package/server/utils/files/json.ts +2 -29
- package/server/utils/files/markdown-image-fill.ts +6 -37
- package/server/utils/files/markdown-store.ts +6 -21
- package/server/utils/files/naming.ts +3 -39
- package/server/utils/files/plugins-io.ts +100 -0
- package/server/utils/files/reference-dirs-io.ts +1 -9
- package/server/utils/files/roles-io.ts +2 -10
- package/server/utils/files/safe.ts +17 -19
- package/server/utils/files/scheduler-io.ts +1 -7
- package/server/utils/files/scheduler-overrides-io.ts +3 -12
- package/server/utils/files/session-io.ts +21 -30
- package/server/utils/files/spreadsheet-store.ts +9 -22
- package/server/utils/files/translation-io.ts +46 -0
- package/server/utils/files/user-tasks-io.ts +1 -7
- package/server/utils/files/workspace-io.ts +3 -79
- package/server/utils/gemini.ts +33 -11
- package/server/utils/html/htmlArtifactSplicer.ts +41 -0
- package/server/utils/markdown/frontmatter.ts +112 -0
- package/server/utils/regex.ts +56 -0
- package/server/utils/router.ts +41 -0
- package/server/utils/slug.ts +5 -3
- package/server/utils/time.ts +12 -0
- package/server/workspace/chat-index/indexer.ts +15 -2
- package/server/workspace/chat-index/summarizer.ts +1 -1
- package/server/workspace/custom-dirs.ts +1 -1
- package/server/workspace/helps/gemini.md +1 -1
- package/server/workspace/helps/guide.md +61 -0
- package/server/workspace/helps/index.md +4 -0
- package/server/workspace/helps/presenthtml.md +80 -0
- package/server/workspace/helps/sandbox.md +7 -0
- package/server/workspace/helps/storyteller.md +101 -0
- package/server/workspace/helps/telegram.md +1 -0
- package/server/workspace/helps/wiki.md +9 -7
- package/server/workspace/journal/archivist-cli.ts +7 -33
- package/server/workspace/journal/archivist-schemas.ts +5 -43
- package/server/workspace/journal/dailyPass.ts +34 -187
- package/server/workspace/journal/diff.ts +3 -28
- package/server/workspace/journal/index.ts +10 -81
- package/server/workspace/journal/indexFile.ts +3 -24
- package/server/workspace/journal/latestDaily.ts +51 -0
- package/server/workspace/journal/memoryExtractor.ts +4 -20
- package/server/workspace/journal/optimizationPass.ts +4 -21
- package/server/workspace/journal/paths.ts +4 -23
- package/server/workspace/journal/state.ts +6 -29
- package/server/workspace/memory/io.ts +213 -0
- package/server/workspace/memory/llm-classifier.ts +158 -0
- package/server/workspace/memory/migrate.ts +263 -0
- package/server/workspace/memory/run.ts +84 -0
- package/server/workspace/memory/topic-cluster.ts +218 -0
- package/server/workspace/memory/topic-detect.ts +67 -0
- package/server/workspace/memory/topic-index-hook.ts +128 -0
- package/server/workspace/memory/topic-io.ts +180 -0
- package/server/workspace/memory/topic-migrate.ts +248 -0
- package/server/workspace/memory/topic-run.ts +172 -0
- package/server/workspace/memory/topic-swap.ts +135 -0
- package/server/workspace/memory/topic-types.ts +142 -0
- package/server/workspace/memory/types.ts +83 -0
- package/server/workspace/news/reader.ts +4 -5
- package/server/workspace/paths.ts +124 -47
- package/server/workspace/roles.ts +2 -11
- package/server/workspace/skills/parser.ts +38 -55
- package/server/workspace/skills/user-tasks.ts +1 -2
- package/server/workspace/skills-preset/mc-library/SKILL.md +188 -0
- package/server/workspace/skills-preset.ts +196 -0
- package/server/workspace/sources/fetchers/githubIssues.ts +13 -11
- package/server/workspace/sources/fetchers/index.ts +1 -1
- package/server/workspace/sources/fetchers/rssParser.ts +1 -1
- package/server/workspace/sources/pipeline/index.ts +2 -2
- package/server/workspace/sources/pipeline/notify.ts +3 -3
- package/server/workspace/sources/pipeline/write.ts +2 -2
- package/server/workspace/sources/registry.ts +39 -61
- package/server/workspace/sources/robots.ts +1 -1
- package/server/workspace/tool-trace/classify.ts +2 -1
- package/server/workspace/tool-trace/index.ts +1 -1
- package/server/workspace/tool-trace/writeSearch.ts +6 -1
- package/server/workspace/wiki-backlinks/index.ts +19 -7
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +1 -0
- package/server/workspace/wiki-history/hook/snapshot.mjs +98 -0
- package/server/workspace/wiki-history/hook/snapshot.ts +135 -0
- package/server/workspace/wiki-history/provision.ts +181 -0
- package/server/workspace/wiki-pages/io.ts +217 -0
- package/server/workspace/wiki-pages/snapshot.ts +380 -0
- package/server/workspace/workspace.ts +75 -13
- package/src/App.vue +115 -40
- package/src/_runtime/protocol-vue.ts +21 -0
- package/src/_runtime/vue.ts +22 -0
- package/src/components/ChatInput.vue +14 -10
- package/src/components/CopyChatButton.vue +76 -0
- package/src/components/FileContentRenderer.vue +67 -14
- package/src/components/FileTree.vue +2 -2
- package/src/components/FilesView.vue +17 -1
- package/src/components/NewsView.vue +16 -2
- package/src/components/NotificationBell.vue +320 -93
- package/src/components/PageChatComposer.vue +5 -4
- package/src/components/PluginLauncher.vue +42 -6
- package/src/components/PluginScopedRoot.vue +87 -0
- package/src/components/RoleSelector.vue +12 -1
- package/src/components/RolesView.vue +562 -0
- package/src/components/SentAttachmentChip.vue +102 -0
- package/src/components/SessionHistoryPanel.vue +109 -20
- package/src/components/SessionRoleIcon.vue +7 -4
- package/src/components/SessionSidebar.vue +20 -7
- package/src/components/SessionTabBar.vue +1 -1
- package/src/components/SettingsMcpTab.vue +4 -4
- package/src/components/SettingsModal.vue +2 -0
- package/src/components/SidebarHeader.vue +16 -5
- package/src/components/SourcesManager.vue +23 -9
- package/src/components/SourcesView.vue +1 -1
- package/src/components/StackView.vue +102 -6
- package/src/components/SuggestionsPanel.vue +105 -16
- package/src/components/SystemFileBanner.vue +1 -1
- package/src/components/TodoExplorer.vue +4 -5
- package/src/components/todo/TodoAddDialog.vue +2 -3
- package/src/components/todo/TodoEditDialog.vue +1 -2
- package/src/components/todo/TodoEditPanel.vue +2 -3
- package/src/components/todo/TodoKanbanView.vue +8 -5
- package/src/components/todo/TodoListView.vue +3 -5
- package/src/components/todo/TodoTableView.vue +7 -5
- package/src/composables/useAccountingChannel.ts +58 -0
- package/src/composables/useActiveSession.ts +4 -25
- package/src/composables/useAppApi.ts +6 -44
- package/src/composables/useClipboardCopy.ts +3 -20
- package/src/composables/useContentDisplay.ts +33 -2
- package/src/composables/useDevPluginReload.ts +23 -0
- package/src/composables/useDynamicFavicon.ts +5 -31
- package/src/composables/useEventListeners.ts +0 -20
- package/src/composables/useExpandedDirs.ts +4 -15
- package/src/composables/useFaviconState.ts +12 -46
- package/src/composables/useFileChange.ts +53 -0
- package/src/composables/useFreshPluginData.ts +6 -43
- package/src/composables/useHealth.ts +14 -43
- package/src/composables/useImageErrorRepair.ts +104 -0
- package/src/composables/useLatestDaily.ts +40 -0
- package/src/composables/useMarkdownDoc.ts +39 -0
- package/src/composables/useMarkdownLinkHandler.ts +1 -1
- package/src/composables/useMcpTools.ts +3 -16
- package/src/composables/useNotifications.ts +138 -112
- package/src/composables/usePdfDownload.ts +17 -3
- package/src/composables/usePendingCalls.ts +8 -26
- package/src/composables/usePluginErrorBoundary.ts +68 -0
- package/src/composables/usePubSub.ts +9 -17
- package/src/composables/useRunElapsed.ts +5 -22
- package/src/composables/useSandboxStatus.ts +4 -20
- package/src/composables/useSessionDerived.ts +7 -15
- package/src/composables/useSessionHistory.ts +70 -29
- package/src/composables/useSessionSync.ts +25 -3
- package/src/composables/useSkillsList.ts +59 -0
- package/src/composables/useTranslatedQueries.ts +109 -0
- package/src/config/apiRoutes.ts +181 -80
- package/src/config/historyFilters.ts +5 -3
- package/src/config/hostEvents.ts +17 -0
- package/src/config/mcpCatalog.ts +277 -5
- package/src/config/pubsubChannels.ts +134 -12
- package/src/config/roles.ts +212 -147
- package/src/config/systemFileDescriptors.ts +5 -5
- package/src/config/toolNames.ts +52 -30
- package/src/config/workspacePaths.ts +26 -2
- package/src/lang/de.ts +483 -27
- package/src/lang/en.ts +448 -27
- package/src/lang/es.ts +474 -27
- package/src/lang/fr.ts +476 -27
- package/src/lang/ja.ts +465 -27
- package/src/lang/ko.ts +466 -27
- package/src/lang/pt-BR.ts +473 -27
- package/src/lang/zh.ts +463 -27
- package/src/lib/vue-i18n.ts +1 -1
- package/src/lib/wiki-page/slug.ts +66 -0
- package/src/main.ts +85 -0
- package/src/plugins/_extras.ts +58 -0
- package/src/plugins/_generated/metas.ts +42 -0
- package/src/plugins/_generated/registrations.ts +44 -0
- package/src/plugins/_generated/server-bindings.ts +47 -0
- package/src/plugins/accounting/Preview.vue +106 -0
- package/src/plugins/accounting/View.vue +632 -0
- package/src/plugins/accounting/actions.ts +34 -0
- package/src/plugins/accounting/api.ts +301 -0
- package/src/plugins/accounting/components/AccountEditor.vue +250 -0
- package/src/plugins/accounting/components/AccountRow.vue +50 -0
- package/src/plugins/accounting/components/AccountsList.vue +102 -0
- package/src/plugins/accounting/components/AccountsModal.vue +300 -0
- package/src/plugins/accounting/components/BalanceSheet.vue +186 -0
- package/src/plugins/accounting/components/BookSettings.vue +284 -0
- package/src/plugins/accounting/components/BookSwitcher.vue +78 -0
- package/src/plugins/accounting/components/DateRangePicker.vue +140 -0
- package/src/plugins/accounting/components/JournalEntryForm.vue +504 -0
- package/src/plugins/accounting/components/JournalList.vue +553 -0
- package/src/plugins/accounting/components/Ledger.vue +206 -0
- package/src/plugins/accounting/components/NewBookForm.vue +211 -0
- package/src/plugins/accounting/components/OpeningBalancesForm.vue +271 -0
- package/src/plugins/accounting/components/ProfitLoss.vue +160 -0
- package/src/plugins/accounting/components/accountDraft.ts +13 -0
- package/src/plugins/accounting/components/accountNumbering.ts +103 -0
- package/src/plugins/accounting/components/accountValidation.ts +75 -0
- package/src/plugins/accounting/components/useLatestRequest.ts +44 -0
- package/src/plugins/accounting/countries.ts +158 -0
- package/src/plugins/accounting/currencies.ts +64 -0
- package/src/plugins/accounting/dates.ts +51 -0
- package/src/plugins/accounting/definition.ts +199 -0
- package/src/plugins/accounting/fiscalYear.ts +136 -0
- package/src/plugins/accounting/index.ts +49 -0
- package/src/plugins/accounting/meta.ts +91 -0
- package/src/plugins/accounting/timeSeriesEnums.ts +16 -0
- package/src/plugins/api.ts +125 -0
- package/src/plugins/canvas/View.vue +38 -28
- package/src/plugins/canvas/definition.ts +10 -8
- package/src/plugins/canvas/index.ts +15 -8
- package/src/plugins/canvas/meta.ts +12 -0
- package/src/plugins/chart/Preview.vue +1 -1
- package/src/plugins/chart/View.vue +2 -2
- package/src/plugins/chart/definition.ts +12 -2
- package/src/plugins/chart/index.ts +15 -7
- package/src/plugins/chart/meta.ts +18 -0
- package/src/plugins/editImages/definition.ts +44 -0
- package/src/plugins/editImages/index.ts +43 -0
- package/src/plugins/editImages/meta.ts +5 -0
- package/src/plugins/generateImage/View.vue +3 -1
- package/src/plugins/generateImage/definition.ts +2 -0
- package/src/plugins/generateImage/index.ts +13 -5
- package/src/plugins/generateImage/meta.ts +5 -0
- package/src/plugins/index.ts +35 -0
- package/src/plugins/manageRoles/Preview.vue +7 -4
- package/src/plugins/manageRoles/View.vue +12 -8
- package/src/plugins/manageRoles/definition.ts +6 -0
- package/src/plugins/manageRoles/index.ts +7 -6
- package/src/plugins/manageSkills/View.vue +11 -7
- package/src/plugins/manageSkills/definition.ts +4 -1
- package/src/plugins/manageSkills/index.ts +14 -7
- package/src/plugins/manageSkills/meta.ts +21 -0
- package/src/plugins/manageSource/definition.ts +4 -1
- package/src/plugins/manageSource/index.ts +15 -7
- package/src/plugins/manageSource/meta.ts +21 -0
- package/src/plugins/markdown/Preview.vue +10 -8
- package/src/plugins/markdown/View.vue +84 -17
- package/src/plugins/markdown/definition.ts +7 -1
- package/src/plugins/markdown/index.ts +15 -8
- package/src/plugins/markdown/meta.ts +16 -0
- package/src/plugins/meta-types.ts +97 -0
- package/src/plugins/metas.ts +224 -0
- package/src/plugins/presentForm/Preview.vue +4 -15
- package/src/plugins/presentForm/View.vue +35 -78
- package/src/plugins/presentForm/definition.ts +7 -6
- package/src/plugins/presentForm/index.ts +12 -5
- package/src/plugins/presentForm/meta.ts +11 -0
- package/src/plugins/presentForm/plugin.ts +8 -9
- package/src/plugins/presentForm/types.ts +0 -24
- package/src/plugins/presentHtml/Preview.vue +1 -8
- package/src/plugins/presentHtml/View.vue +401 -30
- package/src/plugins/presentHtml/definition.ts +8 -5
- package/src/plugins/presentHtml/index.ts +15 -8
- package/src/plugins/presentHtml/meta.ts +14 -0
- package/src/plugins/presentMulmoScript/View.vue +327 -107
- package/src/plugins/presentMulmoScript/definition.ts +34 -7
- package/src/plugins/presentMulmoScript/helpers.ts +4 -5
- package/src/plugins/presentMulmoScript/index.ts +20 -7
- package/src/plugins/presentMulmoScript/meta.ts +52 -0
- package/src/plugins/scheduler/AutomationsPreview.vue +2 -8
- package/src/plugins/scheduler/Preview.vue +5 -2
- package/src/plugins/scheduler/TasksTab.vue +16 -36
- package/src/plugins/scheduler/View.vue +22 -54
- package/src/plugins/scheduler/automationsDefinition.ts +14 -9
- package/src/plugins/scheduler/automationsMeta.ts +5 -0
- package/src/plugins/scheduler/calendarDefinition.ts +4 -7
- package/src/plugins/scheduler/calendarMeta.ts +28 -0
- package/src/plugins/scheduler/formatSchedule.ts +6 -24
- package/src/plugins/scheduler/index.ts +26 -52
- package/src/plugins/scope.ts +57 -0
- package/src/plugins/server-bindings-types.ts +38 -0
- package/src/plugins/server.ts +32 -0
- package/src/plugins/skill/Preview.vue +25 -0
- package/src/plugins/skill/View.vue +125 -0
- package/src/plugins/skill/definition.ts +23 -0
- package/src/plugins/skill/index.ts +36 -0
- package/src/plugins/skill/plugin.ts +31 -0
- package/src/plugins/skill/types.ts +21 -0
- package/src/plugins/spreadsheet/Preview.vue +1 -3
- package/src/plugins/spreadsheet/View.vue +29 -49
- package/src/plugins/spreadsheet/cellHighlights.ts +2 -3
- package/src/plugins/spreadsheet/definition.ts +5 -2
- package/src/plugins/spreadsheet/index.ts +15 -8
- package/src/plugins/spreadsheet/keyboardNav.ts +38 -0
- package/src/plugins/spreadsheet/meta.ts +14 -0
- package/src/plugins/textResponse/Preview.vue +9 -1
- package/src/plugins/textResponse/View.vue +59 -8
- package/src/plugins/textResponse/index.ts +11 -3
- package/src/plugins/textResponse/plugin.ts +8 -10
- package/src/plugins/textResponse/types.ts +28 -0
- package/src/plugins/wiki/Preview.vue +6 -4
- package/src/plugins/wiki/View.vue +463 -254
- package/src/plugins/wiki/components/WikiPageBody.vue +159 -0
- package/src/plugins/wiki/helpers.ts +17 -0
- package/src/plugins/wiki/history/HistoryDetail.vue +325 -0
- package/src/plugins/wiki/history/HistoryTab.vue +167 -0
- package/src/plugins/wiki/history/RestoreConfirm.vue +63 -0
- package/src/plugins/wiki/history/api.ts +52 -0
- package/src/plugins/wiki/history/diff.ts +145 -0
- package/src/plugins/wiki/index.ts +42 -32
- package/src/plugins/wiki/meta.ts +10 -0
- package/src/plugins/wiki/pageEditLoader.ts +53 -0
- package/src/plugins/wiki/route.ts +8 -0
- package/src/router/guards.ts +2 -1
- package/src/router/index.ts +19 -0
- package/src/router/pageRoutes.ts +1 -0
- package/src/tools/index.ts +50 -51
- package/src/tools/runtimeLoader.ts +141 -0
- package/src/tools/types.ts +44 -1
- package/src/types/notification.ts +23 -0
- package/src/types/pastedFile.ts +10 -0
- package/src/types/session.ts +61 -3
- package/src/types/sse.ts +21 -6
- package/src/utils/agent/eventDispatch.ts +12 -9
- package/src/utils/agent/pastedAttachment.ts +35 -0
- package/src/utils/agent/request.ts +32 -3
- package/src/utils/agent/toolCalls.ts +7 -1
- package/src/utils/api.ts +1 -1
- package/src/utils/chat/exportMarkdown.ts +243 -0
- package/src/utils/errors.ts +10 -2
- package/src/utils/files/expandedDirs.ts +1 -1
- package/src/utils/filesPreview/todoPreview.ts +13 -2
- package/src/utils/format/date.ts +1 -3
- package/src/utils/format/jsonSyntax.ts +5 -0
- package/src/utils/html/iframeHeightReporterScript.ts +62 -0
- package/src/utils/html/previewCsp.ts +29 -2
- package/src/utils/image/htmlSrcAttrs.ts +122 -0
- package/src/utils/image/imageRepairInlineScript.ts +115 -0
- package/src/utils/image/resolve.ts +17 -3
- package/src/utils/image/rewriteMarkdownImageRefs.ts +62 -9
- package/src/utils/markdown/frontmatter.ts +125 -0
- package/src/utils/markdown/taskList.ts +7 -2
- package/src/utils/plugin/runtime.ts +132 -0
- package/src/utils/session/mergeSessions.ts +40 -37
- package/src/utils/session/sessionEntries.ts +74 -18
- package/src/utils/session/sessionHelpers.ts +54 -10
- package/src/utils/tools/result.ts +76 -14
- package/src/vite-env.d.ts +6 -0
- package/client/assets/html2canvas-Cx501zZr-Bug0qRNv.js +0 -5
- package/client/assets/index-CY-WpQUm.css +0 -2
- package/client/assets/index-DbTz2Mfs.js +0 -4911
- package/client/assets/material-symbols-outlined-NzYEeyps.woff2 +0 -0
- package/server/api/routes/html.ts +0 -114
- package/server/api/routes/todos.ts +0 -293
- package/server/api/routes/todosColumnsHandlers.ts +0 -333
- package/server/api/routes/todosHandlers.ts +0 -274
- package/server/api/routes/todosItemsHandlers.ts +0 -386
- package/server/utils/files/todos-io.ts +0 -29
- package/src/components/NotificationToast.vue +0 -75
- package/src/plugins/editImage/definition.ts +0 -27
- package/src/plugins/editImage/index.ts +0 -37
- package/src/plugins/presentHtml/helpers.ts +0 -72
- package/src/plugins/scheduler/LegacySchedulerView.vue +0 -32
- package/src/plugins/scheduler/legacyShape.ts +0 -34
- package/src/plugins/todo/Preview.vue +0 -68
- package/src/plugins/todo/View.vue +0 -378
- package/src/plugins/todo/composables/useTodos.ts +0 -179
- package/src/plugins/todo/definition.ts +0 -45
- package/src/plugins/todo/index.ts +0 -62
- package/src/plugins/todo/labels.ts +0 -163
- package/src/plugins/todo/priority.ts +0 -98
- package/src/plugins/todo/viewModes.ts +0 -19
- package/src/plugins/wiki/definition.ts +0 -25
- package/src/tools/legacyPluginNames.ts +0 -13
- package/src/utils/format/frontmatter.ts +0 -80
- package/src/utils/image/rewriteHtmlImageRefs.ts +0 -50
- package/src/utils/notification/dispatch.ts +0 -58
- /package/client/assets/{purify.es-Fx1Nqyry-BwJECkqS.js → purify.es-Fx1Nqyry-BSVNht6S.js} +0 -0
- /package/src/plugins/{editImage → editImages}/Preview.vue +0 -0
- /package/src/plugins/{editImage → editImages}/View.vue +0 -0
- /package/src/{config/schedulerActions.ts → plugins/scheduler/actions.ts} +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Vue-free server-facing barrel for every built-in plugin's MCP
|
|
2
|
+
// binding (ToolDefinition + REST endpoint). Sibling barrels:
|
|
3
|
+
//
|
|
4
|
+
// - `src/plugins/index.ts` — REGISTRATIONS with Vue View / Preview
|
|
5
|
+
// (frontend uses this; server can't —
|
|
6
|
+
// Vue dependency)
|
|
7
|
+
// - `src/plugins/metas.ts` — per-plugin META (toolName, apiRoutes,
|
|
8
|
+
// workspaceDirs, staticChannels). Both
|
|
9
|
+
// server and frontend safe.
|
|
10
|
+
// - `src/plugins/server.ts` — THIS FILE: server-only binding table
|
|
11
|
+
// (definition + MCP dispatch endpoint).
|
|
12
|
+
// Imports each plugin's `definition.ts`
|
|
13
|
+
// directly (no Vue, server-safe).
|
|
14
|
+
//
|
|
15
|
+
// **Auto-generated**. Standard plugins (one dir, one META declaring
|
|
16
|
+
// `apiNamespace` + `mcpDispatch`) flow through
|
|
17
|
+
// `_generated/server-bindings.ts`; cross-namespace bindings (image
|
|
18
|
+
// plugins reaching the host's `/api/image/*` routes) and external
|
|
19
|
+
// npm plugins (mindmap / quiz / present3d) live in `_extras.ts`.
|
|
20
|
+
// Adding a standard plugin requires no edit here.
|
|
21
|
+
|
|
22
|
+
import { ServerPluginBinding } from "./server-bindings-types";
|
|
23
|
+
import { GENERATED_SERVER_BINDINGS } from "./_generated/server-bindings";
|
|
24
|
+
import { EXTRA_SERVER_BINDINGS } from "./_extras";
|
|
25
|
+
|
|
26
|
+
export type { ServerPluginBinding };
|
|
27
|
+
|
|
28
|
+
/** All built-in plugin MCP bindings. The two scheduler defs share
|
|
29
|
+
* one dispatch URL (the server splits calendar vs task actions via
|
|
30
|
+
* the action enum), so the codegen emits both rows pointing at the
|
|
31
|
+
* calendar META — see `_generated/server-bindings.ts`. */
|
|
32
|
+
export const BUILT_IN_SERVER_BINDINGS: readonly ServerPluginBinding[] = [...GENERATED_SERVER_BINDINGS, ...EXTRA_SERVER_BINDINGS];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Sidebar / chat-history thumbnail for a skill envelope.
|
|
3
|
+
Single line: extension icon + skill name. The full body and
|
|
4
|
+
description live behind the canvas's expand toggle. -->
|
|
5
|
+
<div class="flex items-center gap-1.5 text-sm text-gray-700">
|
|
6
|
+
<span class="material-icons text-purple-500 text-sm shrink-0">extension</span>
|
|
7
|
+
<span class="truncate font-medium">{{ skillName }}</span>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
import { computed } from "vue";
|
|
13
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
14
|
+
import type { SkillData } from "./types";
|
|
15
|
+
|
|
16
|
+
// Sidebar previews are rendered with `:result="result"` (see
|
|
17
|
+
// SessionSidebar.vue), not `:selected-result`. Codex iter-2 review
|
|
18
|
+
// on PR #1220 — the previous prop name left `props.result`
|
|
19
|
+
// undefined and broke skill rendering in the chat-history sidebar.
|
|
20
|
+
const props = defineProps<{
|
|
21
|
+
result: ToolResultComplete<SkillData>;
|
|
22
|
+
}>();
|
|
23
|
+
|
|
24
|
+
const skillName = computed(() => props.result.data?.skillName ?? "skill");
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- The canvas reserves the full pane height for the selected
|
|
3
|
+
result regardless of how much vertical space it actually
|
|
4
|
+
needs. Without explicit centering, a short collapsed skill
|
|
5
|
+
card sits top-anchored with a tall empty void below. Outer
|
|
6
|
+
is `h-full flex flex-col` so the inner can use `my-auto` to
|
|
7
|
+
vertically centre when the card is short, while still
|
|
8
|
+
falling back to natural top-alignment + scrolling when the
|
|
9
|
+
user expands the markdown body and content overflows
|
|
10
|
+
(auto-margins collapse to 0 in the overflow case, unlike
|
|
11
|
+
`justify-content: center` which would clip the top of tall
|
|
12
|
+
content out of reach). #1218 follow-up. -->
|
|
13
|
+
<div class="h-full flex flex-col overflow-y-auto p-6">
|
|
14
|
+
<div class="my-auto max-w-3xl mx-auto w-full">
|
|
15
|
+
<div class="rounded-lg border border-purple-200 bg-purple-50 shadow-sm">
|
|
16
|
+
<!-- Collapsed header — clickable. The whole card collapses by
|
|
17
|
+
default; clicking expands the skill body. The use of
|
|
18
|
+
`<details>` keeps keyboard / a11y semantics correct. -->
|
|
19
|
+
<details class="group">
|
|
20
|
+
<summary class="cursor-pointer list-none p-4 flex items-start gap-3 hover:bg-purple-100/40 rounded-lg" :data-testid="'skill-summary-' + skillName">
|
|
21
|
+
<span class="material-icons text-purple-600 text-base mt-0.5 shrink-0">extension</span>
|
|
22
|
+
<div class="flex-1 min-w-0">
|
|
23
|
+
<div class="flex items-baseline gap-2 flex-wrap">
|
|
24
|
+
<span class="font-medium text-purple-900">{{ skillName }}</span>
|
|
25
|
+
<span v-if="skillScope !== 'unknown'" class="text-[10px] uppercase tracking-wide text-purple-500 px-1.5 py-0.5 rounded-full bg-purple-100">
|
|
26
|
+
{{ skillScope }}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
<div v-if="skillDescription" class="text-sm text-gray-700 mt-1">{{ skillDescription }}</div>
|
|
30
|
+
<div v-else class="text-sm text-gray-500 italic mt-1">{{ t("pluginSkill.noDescription") }}</div>
|
|
31
|
+
</div>
|
|
32
|
+
<span class="material-icons text-gray-400 text-base shrink-0 group-open:rotate-180 transition-transform">expand_more</span>
|
|
33
|
+
</summary>
|
|
34
|
+
|
|
35
|
+
<!-- Expanded body — markdown-rendered. -->
|
|
36
|
+
<div class="border-t border-purple-200 p-4 bg-white rounded-b-lg">
|
|
37
|
+
<div v-if="skillPath" class="text-[11px] font-mono text-gray-400 mb-3 break-all">{{ skillPath }}</div>
|
|
38
|
+
<!-- eslint-disable-next-line vue/no-v-html -- DOMPurify-sanitized markdown of the SKILL.md body Claude CLI synthesised. The body comes from the user's local skill file, surfaced verbatim here. -->
|
|
39
|
+
<div class="markdown-content prose prose-slate max-w-none" v-html="renderedHtml"></div>
|
|
40
|
+
</div>
|
|
41
|
+
</details>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { computed } from "vue";
|
|
49
|
+
import { useI18n } from "vue-i18n";
|
|
50
|
+
import { marked } from "marked";
|
|
51
|
+
import DOMPurify from "dompurify";
|
|
52
|
+
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
53
|
+
import type { SkillData } from "./types";
|
|
54
|
+
|
|
55
|
+
const { t } = useI18n();
|
|
56
|
+
|
|
57
|
+
const props = defineProps<{
|
|
58
|
+
selectedResult: ToolResultComplete<SkillData>;
|
|
59
|
+
}>();
|
|
60
|
+
|
|
61
|
+
const skillName = computed(() => props.selectedResult.data?.skillName ?? "");
|
|
62
|
+
const skillScope = computed(() => props.selectedResult.data?.skillScope ?? "unknown");
|
|
63
|
+
const skillPath = computed(() => props.selectedResult.data?.skillPath ?? null);
|
|
64
|
+
const skillDescription = computed(() => props.selectedResult.data?.skillDescription ?? null);
|
|
65
|
+
const body = computed(() => props.selectedResult.data?.body ?? "");
|
|
66
|
+
|
|
67
|
+
const renderedHtml = computed(() => DOMPurify.sanitize(marked(body.value, { breaks: true, gfm: true }) as string));
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.markdown-content :deep(h1) {
|
|
72
|
+
font-size: 1.5rem;
|
|
73
|
+
font-weight: bold;
|
|
74
|
+
margin-top: 1em;
|
|
75
|
+
margin-bottom: 0.5em;
|
|
76
|
+
}
|
|
77
|
+
.markdown-content :deep(h2) {
|
|
78
|
+
font-size: 1.25rem;
|
|
79
|
+
font-weight: bold;
|
|
80
|
+
margin-top: 1em;
|
|
81
|
+
margin-bottom: 0.5em;
|
|
82
|
+
}
|
|
83
|
+
.markdown-content :deep(h3) {
|
|
84
|
+
font-size: 1.125rem;
|
|
85
|
+
font-weight: bold;
|
|
86
|
+
margin-top: 0.75em;
|
|
87
|
+
margin-bottom: 0.5em;
|
|
88
|
+
}
|
|
89
|
+
.markdown-content :deep(p) {
|
|
90
|
+
margin-bottom: 0.75em;
|
|
91
|
+
}
|
|
92
|
+
.markdown-content :deep(ul),
|
|
93
|
+
.markdown-content :deep(ol) {
|
|
94
|
+
margin-left: 1.5em;
|
|
95
|
+
margin-bottom: 0.75em;
|
|
96
|
+
}
|
|
97
|
+
.markdown-content :deep(code) {
|
|
98
|
+
background-color: #f5f5f5;
|
|
99
|
+
padding: 0.2em 0.4em;
|
|
100
|
+
border-radius: 3px;
|
|
101
|
+
font-family: monospace;
|
|
102
|
+
font-size: 0.9em;
|
|
103
|
+
}
|
|
104
|
+
.markdown-content :deep(pre) {
|
|
105
|
+
background-color: #f5f5f5;
|
|
106
|
+
padding: 0.75em;
|
|
107
|
+
border-radius: 4px;
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
margin-bottom: 0.75em;
|
|
110
|
+
}
|
|
111
|
+
.markdown-content :deep(pre code) {
|
|
112
|
+
background-color: transparent;
|
|
113
|
+
padding: 0;
|
|
114
|
+
}
|
|
115
|
+
.markdown-content :deep(blockquote) {
|
|
116
|
+
border-left: 3px solid #d1d5db;
|
|
117
|
+
padding-left: 1em;
|
|
118
|
+
color: #4b5563;
|
|
119
|
+
margin: 0.75em 0;
|
|
120
|
+
}
|
|
121
|
+
.markdown-content :deep(a) {
|
|
122
|
+
color: #2563eb;
|
|
123
|
+
text-decoration: underline;
|
|
124
|
+
}
|
|
125
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Skill plugin — pseudo-tool used only by the client to render
|
|
2
|
+
// `type: "skill"` jsonl entries (#1218). The LLM never invokes this
|
|
3
|
+
// tool directly — it sees the real `Skill` tool defined by Claude
|
|
4
|
+
// CLI; this plugin's job is purely to claim a `toolName` slot in
|
|
5
|
+
// `getPlugin()` so the canvas routes skill envelopes through
|
|
6
|
+
// `View.vue` instead of the default text-response renderer.
|
|
7
|
+
|
|
8
|
+
import type { ToolDefinition } from "gui-chat-protocol";
|
|
9
|
+
|
|
10
|
+
export const TOOL_NAME = "skill";
|
|
11
|
+
|
|
12
|
+
export const TOOL_DEFINITION: ToolDefinition = {
|
|
13
|
+
type: "function",
|
|
14
|
+
name: TOOL_NAME,
|
|
15
|
+
description: "Internal: collapsed view of a SKILL.md body Claude CLI synthesised after a `Skill` tool invocation.",
|
|
16
|
+
parameters: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {},
|
|
19
|
+
required: [],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const SYSTEM_PROMPT = "";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill plugin (#1218) — internal-only, claims `toolName: "skill"` so
|
|
3
|
+
* the canvas routes synthetic envelopes built by `makeSkillResult`
|
|
4
|
+
* through `View.vue` instead of the default text-response renderer.
|
|
5
|
+
*
|
|
6
|
+
* Discovered automatically by `yarn plugins:codegen` (which scans
|
|
7
|
+
* `src/plugins/<name>/index.ts` for an exported `REGISTRATION`).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ToolPlugin } from "gui-chat-protocol/vue";
|
|
11
|
+
import type { PluginRegistration } from "../../tools/types";
|
|
12
|
+
import type { SkillData, SkillArgs } from "./types";
|
|
13
|
+
import { pluginCore, TOOL_NAME } from "./plugin";
|
|
14
|
+
import View from "./View.vue";
|
|
15
|
+
import Preview from "./Preview.vue";
|
|
16
|
+
|
|
17
|
+
export const plugin: ToolPlugin<SkillData, unknown, SkillArgs> = {
|
|
18
|
+
...pluginCore,
|
|
19
|
+
viewComponent: View,
|
|
20
|
+
previewComponent: Preview,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type { SkillData, SkillArgs } from "./types";
|
|
24
|
+
|
|
25
|
+
export { TOOL_DEFINITION, SYSTEM_PROMPT, executeSkill, pluginCore } from "./plugin";
|
|
26
|
+
export { TOOL_NAME };
|
|
27
|
+
|
|
28
|
+
export { View, Preview };
|
|
29
|
+
|
|
30
|
+
const skillPlugin = { plugin };
|
|
31
|
+
export default skillPlugin;
|
|
32
|
+
|
|
33
|
+
export const REGISTRATION: PluginRegistration = {
|
|
34
|
+
toolName: TOOL_NAME,
|
|
35
|
+
entry: plugin,
|
|
36
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Skill plugin core — pseudo-tool, never advertised to the LLM.
|
|
2
|
+
// `isEnabled: () => false` keeps it out of the agent's tool list;
|
|
3
|
+
// the plugin only exists so the canvas's `getPlugin("skill")`
|
|
4
|
+
// returns a registration that points the renderer at this View.
|
|
5
|
+
|
|
6
|
+
import type { ToolPluginCore, ToolContext, ToolResult } from "gui-chat-protocol";
|
|
7
|
+
import type { SkillData, SkillArgs } from "./types";
|
|
8
|
+
import { TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition";
|
|
9
|
+
|
|
10
|
+
export { TOOL_NAME, TOOL_DEFINITION, SYSTEM_PROMPT } from "./definition";
|
|
11
|
+
|
|
12
|
+
export const executeSkill = async (_context: ToolContext, args: SkillArgs): Promise<ToolResult<SkillData, unknown>> => ({
|
|
13
|
+
data: {
|
|
14
|
+
skillName: args.skillName,
|
|
15
|
+
skillScope: args.skillScope,
|
|
16
|
+
skillPath: args.skillPath,
|
|
17
|
+
skillDescription: args.skillDescription,
|
|
18
|
+
body: args.body,
|
|
19
|
+
},
|
|
20
|
+
message: args.skillDescription ?? args.skillName,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const pluginCore: ToolPluginCore<SkillData, unknown, SkillArgs> = {
|
|
24
|
+
toolDefinition: TOOL_DEFINITION,
|
|
25
|
+
execute: executeSkill,
|
|
26
|
+
generatingMessage: "Loading skill...",
|
|
27
|
+
// Pseudo-tool — only the client builds these envelopes when
|
|
28
|
+
// parsing `type: "skill"` jsonl entries / `SseSkill` events.
|
|
29
|
+
isEnabled: () => false,
|
|
30
|
+
systemPrompt: SYSTEM_PROMPT,
|
|
31
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Wire shape of a `toolName: "skill"` ToolResultComplete envelope —
|
|
2
|
+
// produced by `makeSkillResult` in `src/utils/tools/result.ts` from
|
|
3
|
+
// either a `type: "skill"` jsonl entry on session reload, or a
|
|
4
|
+
// `SseSkill` event broadcast at flush time during a live run.
|
|
5
|
+
|
|
6
|
+
import type { SkillScope } from "../../types/session";
|
|
7
|
+
|
|
8
|
+
export interface SkillData {
|
|
9
|
+
skillName: string;
|
|
10
|
+
skillScope: SkillScope;
|
|
11
|
+
skillPath: string | null;
|
|
12
|
+
skillDescription: string | null;
|
|
13
|
+
/** Full SKILL.md body as Claude CLI synthesised it. Frontmatter is
|
|
14
|
+
* already stripped (Claude CLI does that before injection); the
|
|
15
|
+
* body starts with `Base directory for this skill: <path>` and
|
|
16
|
+
* ends with `ARGUMENTS: <user message>`. The View renders this
|
|
17
|
+
* with markdown when the user expands the collapsed card. */
|
|
18
|
+
body: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type SkillArgs = SkillData;
|
|
@@ -21,9 +21,7 @@ const props = defineProps<{
|
|
|
21
21
|
result: ToolResult<SpreadsheetToolData>;
|
|
22
22
|
}>();
|
|
23
23
|
|
|
24
|
-
const displayTitle = computed(() =>
|
|
25
|
-
return props.result.title || t("pluginSpreadsheet.previewUntitled");
|
|
26
|
-
});
|
|
24
|
+
const displayTitle = computed(() => props.result.title || t("pluginSpreadsheet.previewUntitled"));
|
|
27
25
|
|
|
28
26
|
const sheetCount = computed(() => {
|
|
29
27
|
const sheets = props.result.data?.sheets;
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
</div>
|
|
42
42
|
|
|
43
43
|
<!-- Spreadsheet table -->
|
|
44
|
+
<!-- eslint-disable-next-line vue/no-v-html -- XLSX.utils.sheet_to_html output of app-owned sheet data; trusted in-process render -->
|
|
44
45
|
<div ref="tableContainer" class="table-container" @click="handleTableClick" v-html="renderedHtml"></div>
|
|
45
46
|
</div>
|
|
46
47
|
</div>
|
|
@@ -110,7 +111,7 @@ import { computed, ref, watch, onMounted, onUnmounted } from "vue";
|
|
|
110
111
|
import { useI18n } from "vue-i18n";
|
|
111
112
|
import * as XLSX from "xlsx";
|
|
112
113
|
import type { ToolResult } from "gui-chat-protocol";
|
|
113
|
-
import type { SpreadsheetToolData, SpreadsheetSheet } from "./definition";
|
|
114
|
+
import type { SpreadsheetToolData, SpreadsheetSheet, SpreadsheetEndpoints } from "./definition";
|
|
114
115
|
import {
|
|
115
116
|
SpreadsheetEngine,
|
|
116
117
|
indexToColumn,
|
|
@@ -122,15 +123,19 @@ import {
|
|
|
122
123
|
type CellValue,
|
|
123
124
|
} from "./engine";
|
|
124
125
|
import { applyCellHighlights, clearCellHighlights } from "./cellHighlights";
|
|
126
|
+
import { getArrowKeyOffset, isWithinSheetBounds } from "./keyboardNav";
|
|
125
127
|
import { errorMessage as formatErrorMessage } from "../../utils/errors";
|
|
126
128
|
|
|
127
129
|
// Import all spreadsheet functions to populate the function registry
|
|
128
130
|
import "./engine/functions";
|
|
129
131
|
import { apiGet, apiPut } from "../../utils/api";
|
|
130
|
-
import {
|
|
132
|
+
import { pluginEndpoints } from "../api";
|
|
131
133
|
import type { FilesContentResponseLike } from "./engine/responseDecoder";
|
|
132
134
|
import { isObj, isRecord } from "../../utils/types";
|
|
133
135
|
|
|
136
|
+
const endpoints = pluginEndpoints<SpreadsheetEndpoints>("spreadsheet");
|
|
137
|
+
const filesEndpoints = pluginEndpoints<{ content: string }>("files");
|
|
138
|
+
|
|
134
139
|
const { t } = useI18n();
|
|
135
140
|
|
|
136
141
|
/**
|
|
@@ -202,6 +207,11 @@ function isFilePath(value: unknown): value is string {
|
|
|
202
207
|
return typeof value === "string" && value.startsWith("artifacts/spreadsheets/") && value.endsWith(".json");
|
|
203
208
|
}
|
|
204
209
|
|
|
210
|
+
// Editor textarea backing ref. Declared up here (rather than next to
|
|
211
|
+
// the other view-state refs further down) so the `fetchSheets().then`
|
|
212
|
+
// initializer below can assign to it without TDZ.
|
|
213
|
+
const editableData = ref(JSON.stringify(resolvedSheets.value || [], null, 2));
|
|
214
|
+
|
|
205
215
|
async function fetchSheets(): Promise<void> {
|
|
206
216
|
const raw = props.selectedResult.data?.sheets;
|
|
207
217
|
// Clear any stale error from a previous result BEFORE the early
|
|
@@ -218,7 +228,7 @@ async function fetchSheets(): Promise<void> {
|
|
|
218
228
|
return;
|
|
219
229
|
}
|
|
220
230
|
loading.value = true;
|
|
221
|
-
const response = await apiGet<FilesContentResponseLike>(
|
|
231
|
+
const response = await apiGet<FilesContentResponseLike>(filesEndpoints.content, { path: raw });
|
|
222
232
|
if (!response.ok) {
|
|
223
233
|
errorMessage.value = t("pluginSpreadsheet.loadFailed", { error: response.error });
|
|
224
234
|
resolvedSheets.value = [];
|
|
@@ -250,7 +260,7 @@ async function persistSheets(sheets: SpreadsheetSheet[]): Promise<void> {
|
|
|
250
260
|
// Send the full workspace-relative path so the route doesn't have
|
|
251
261
|
// to reconstruct one from a basename — paths under
|
|
252
262
|
// `artifacts/spreadsheets/` are now sharded by YYYY/MM (#764).
|
|
253
|
-
const result = await apiPut<unknown>(
|
|
263
|
+
const result = await apiPut<unknown>(endpoints.update.url, {
|
|
254
264
|
relativePath: raw,
|
|
255
265
|
sheets,
|
|
256
266
|
});
|
|
@@ -273,7 +283,9 @@ async function persistSheets(sheets: SpreadsheetSheet[]): Promise<void> {
|
|
|
273
283
|
}
|
|
274
284
|
|
|
275
285
|
const activeSheetIndex = ref(0);
|
|
276
|
-
|
|
286
|
+
// Editor state declared together with the other refs above. Seeded
|
|
287
|
+
// in the on-mount `fetchSheets().then(...)` block above (the `.value`
|
|
288
|
+
// assignment is in scope by the time that .then callback fires).
|
|
277
289
|
const editorTextarea = ref<HTMLTextAreaElement | null>(null);
|
|
278
290
|
const editorDetails = ref<HTMLDetailsElement | null>(null);
|
|
279
291
|
const tableContainer = ref<HTMLDivElement | null>(null);
|
|
@@ -288,7 +300,7 @@ const miniEditorFormula = ref("");
|
|
|
288
300
|
const miniEditorFormat = ref("");
|
|
289
301
|
|
|
290
302
|
// Referenced cells state (for formula highlighting)
|
|
291
|
-
const referencedCells = ref<
|
|
303
|
+
const referencedCells = ref<{ row: number; col: number }[]>([]);
|
|
292
304
|
|
|
293
305
|
// Check if spreadsheet data has been modified
|
|
294
306
|
const hasChanges = computed(() => {
|
|
@@ -527,7 +539,7 @@ function handleTableClick(event: MouseEvent) {
|
|
|
527
539
|
const row = cell.parentElement as HTMLTableRowElement;
|
|
528
540
|
|
|
529
541
|
const colIndex = cell.cellIndex;
|
|
530
|
-
const rowIndex = row
|
|
542
|
+
const { rowIndex } = row;
|
|
531
543
|
|
|
532
544
|
// Check if the main editor details is open
|
|
533
545
|
const isEditorOpen = editorDetails.value?.open ?? false;
|
|
@@ -620,56 +632,24 @@ watch(
|
|
|
620
632
|
{ flush: "post" },
|
|
621
633
|
);
|
|
622
634
|
|
|
623
|
-
|
|
635
|
+
function isEditableTarget(target: EventTarget | null): boolean {
|
|
636
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
637
|
+
return target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable;
|
|
638
|
+
}
|
|
639
|
+
|
|
624
640
|
function handleKeyboardNavigation(event: KeyboardEvent) {
|
|
625
|
-
// Only handle arrow keys when mini editor is open and not focused on input
|
|
626
641
|
if (!miniEditorOpen.value || !miniEditorCell.value) return;
|
|
627
|
-
|
|
628
|
-
// Don't interfere if user is typing in an input field
|
|
629
|
-
const target = event.target as HTMLElement;
|
|
630
|
-
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable) {
|
|
631
|
-
return;
|
|
632
|
-
}
|
|
642
|
+
if (isEditableTarget(event.target)) return;
|
|
633
643
|
|
|
634
644
|
const { row, col } = miniEditorCell.value;
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
// Determine new position based on arrow key
|
|
639
|
-
switch (event.key) {
|
|
640
|
-
case "ArrowUp":
|
|
641
|
-
newRow = Math.max(0, row - 1);
|
|
642
|
-
break;
|
|
643
|
-
case "ArrowDown":
|
|
644
|
-
newRow = row + 1;
|
|
645
|
-
break;
|
|
646
|
-
case "ArrowLeft":
|
|
647
|
-
newCol = Math.max(0, col - 1);
|
|
648
|
-
break;
|
|
649
|
-
case "ArrowRight":
|
|
650
|
-
newCol = col + 1;
|
|
651
|
-
break;
|
|
652
|
-
default:
|
|
653
|
-
return; // Not an arrow key, ignore
|
|
654
|
-
}
|
|
645
|
+
const next = getArrowKeyOffset(event.key, row, col);
|
|
646
|
+
if (!next) return;
|
|
655
647
|
|
|
656
|
-
// Get current sheet data to validate bounds
|
|
657
648
|
try {
|
|
658
649
|
const sheets = JSON.parse(editableData.value);
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
if (!currentSheet || !currentSheet.data) return;
|
|
662
|
-
|
|
663
|
-
// Validate new position is within bounds
|
|
664
|
-
if (newRow < 0 || newRow >= currentSheet.data.length || newCol < 0 || !currentSheet.data[newRow] || newCol >= currentSheet.data[newRow].length) {
|
|
665
|
-
return; // Out of bounds, ignore
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// Prevent default scrolling behavior
|
|
650
|
+
if (!isWithinSheetBounds(sheets[activeSheetIndex.value], next.row, next.col)) return;
|
|
669
651
|
event.preventDefault();
|
|
670
|
-
|
|
671
|
-
// Move to new cell
|
|
672
|
-
openMiniEditor(newRow, newCol);
|
|
652
|
+
openMiniEditor(next.row, next.col);
|
|
673
653
|
} catch (error) {
|
|
674
654
|
console.error("Failed to navigate cells:", error);
|
|
675
655
|
}
|
|
@@ -28,9 +28,8 @@ export interface HighlightableContainer {
|
|
|
28
28
|
// Overload: the spreadsheet root container is known to return a
|
|
29
29
|
// table when asked for the table id, so callers can keep the
|
|
30
30
|
// result strongly typed without casting.
|
|
31
|
-
querySelector(selector: "#spreadsheet-table")
|
|
32
|
-
|
|
33
|
-
querySelectorAll(selector: string): ArrayLike<HighlightableElement> & Iterable<HighlightableElement>;
|
|
31
|
+
querySelector: ((selector: "#spreadsheet-table") => HighlightableTable | null) & ((selector: string) => HighlightableElement | null);
|
|
32
|
+
querySelectorAll: (selector: string) => ArrayLike<HighlightableElement> & Iterable<HighlightableElement>;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export interface CellCoord {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
import { META } from "./meta";
|
|
3
|
+
import type { ResolvedRoute } from "../meta-types";
|
|
2
4
|
|
|
3
|
-
export const TOOL_NAME =
|
|
5
|
+
export const TOOL_NAME = META.toolName;
|
|
6
|
+
export type SpreadsheetEndpoints = { readonly [K in keyof typeof META.apiRoutes]: ResolvedRoute };
|
|
4
7
|
|
|
5
8
|
export interface SpreadsheetCell {
|
|
6
9
|
v: string | number;
|
|
@@ -9,7 +12,7 @@ export interface SpreadsheetCell {
|
|
|
9
12
|
|
|
10
13
|
export interface SpreadsheetSheet {
|
|
11
14
|
name: string;
|
|
12
|
-
data:
|
|
15
|
+
data: SpreadsheetCell[][];
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export interface SpreadsheetToolData {
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import type { ToolPlugin } from "../../tools/types";
|
|
1
|
+
import type { PluginRegistration, ToolPlugin } from "../../tools/types";
|
|
2
2
|
import type { ToolResult } from "gui-chat-protocol";
|
|
3
|
-
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
4
|
-
import
|
|
3
|
+
import toolDefinition, { TOOL_NAME, type SpreadsheetEndpoints, type SpreadsheetToolData } from "./definition";
|
|
4
|
+
import { pluginEndpoints } from "../api";
|
|
5
|
+
import { wrapWithScope } from "../scope";
|
|
5
6
|
import View from "./View.vue";
|
|
6
7
|
import Preview from "./Preview.vue";
|
|
7
|
-
import {
|
|
8
|
-
import { API_ROUTES } from "../../config/apiRoutes";
|
|
8
|
+
import { apiCall } from "../../utils/api";
|
|
9
9
|
import { makeUuid } from "../../utils/id";
|
|
10
10
|
|
|
11
11
|
const spreadsheetPlugin: ToolPlugin<SpreadsheetToolData> = {
|
|
12
12
|
toolDefinition,
|
|
13
13
|
|
|
14
14
|
async execute(_context, args) {
|
|
15
|
-
const
|
|
15
|
+
const endpoints = pluginEndpoints<SpreadsheetEndpoints>("spreadsheet");
|
|
16
|
+
const { method, url } = endpoints.create;
|
|
17
|
+
const result = await apiCall<ToolResult<SpreadsheetToolData>>(url, { method, body: args });
|
|
16
18
|
if (!result.ok) {
|
|
17
19
|
return {
|
|
18
20
|
toolName: TOOL_NAME,
|
|
@@ -29,9 +31,14 @@ const spreadsheetPlugin: ToolPlugin<SpreadsheetToolData> = {
|
|
|
29
31
|
|
|
30
32
|
isEnabled: () => true,
|
|
31
33
|
generatingMessage: "Creating spreadsheet...",
|
|
32
|
-
viewComponent: View,
|
|
33
|
-
previewComponent: Preview,
|
|
34
|
+
viewComponent: wrapWithScope("spreadsheet", View),
|
|
35
|
+
previewComponent: wrapWithScope("spreadsheet", Preview),
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
export default spreadsheetPlugin;
|
|
37
39
|
export { TOOL_NAME };
|
|
40
|
+
|
|
41
|
+
export const REGISTRATION: PluginRegistration = {
|
|
42
|
+
toolName: TOOL_NAME,
|
|
43
|
+
entry: spreadsheetPlugin,
|
|
44
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Pure helpers behind the spreadsheet mini-editor's arrow-key
|
|
2
|
+
// navigation. Lifted out of View.vue so each rule can be unit-tested
|
|
3
|
+
// without spinning up Vue or a DOM.
|
|
4
|
+
|
|
5
|
+
export interface CellPosition {
|
|
6
|
+
row: number;
|
|
7
|
+
col: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Sheet shape we actually rely on — the mini-editor only reads
|
|
11
|
+
// `data` as a 2D array, so the type is intentionally loose to match
|
|
12
|
+
// what arrives from `JSON.parse(editableData.value)`.
|
|
13
|
+
export interface SheetLike {
|
|
14
|
+
data?: unknown[][];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getArrowKeyOffset(key: string, row: number, col: number): CellPosition | null {
|
|
18
|
+
switch (key) {
|
|
19
|
+
case "ArrowUp":
|
|
20
|
+
return { row: Math.max(0, row - 1), col };
|
|
21
|
+
case "ArrowDown":
|
|
22
|
+
return { row: row + 1, col };
|
|
23
|
+
case "ArrowLeft":
|
|
24
|
+
return { row, col: Math.max(0, col - 1) };
|
|
25
|
+
case "ArrowRight":
|
|
26
|
+
return { row, col: col + 1 };
|
|
27
|
+
default:
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isWithinSheetBounds(sheet: SheetLike | null | undefined, row: number, col: number): boolean {
|
|
33
|
+
if (!sheet?.data) return false;
|
|
34
|
+
if (row < 0 || row >= sheet.data.length) return false;
|
|
35
|
+
const rowData = sheet.data[row];
|
|
36
|
+
if (!rowData) return false;
|
|
37
|
+
return col >= 0 && col < rowData.length;
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { definePluginMeta } from "../meta-types";
|
|
2
|
+
|
|
3
|
+
export const META = definePluginMeta({
|
|
4
|
+
toolName: "presentSpreadsheet",
|
|
5
|
+
apiNamespace: "spreadsheet",
|
|
6
|
+
apiRoutes: {
|
|
7
|
+
/** POST /api/spreadsheet — create a new spreadsheet. */
|
|
8
|
+
create: { method: "POST", path: "" },
|
|
9
|
+
/** PUT /api/spreadsheet/update — overwrite an existing
|
|
10
|
+
* spreadsheet. Body carries the workspace-relative path. */
|
|
11
|
+
update: { method: "PUT", path: "/update" },
|
|
12
|
+
},
|
|
13
|
+
mcpDispatch: "create",
|
|
14
|
+
});
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
2
|
+
<div class="p-2">
|
|
3
|
+
<div class="preview-text text-sm leading-snug" :class="textColorClass">{{ previewText }}</div>
|
|
4
|
+
<div v-if="attachments.length > 0" class="flex flex-wrap gap-1 mt-1.5" data-testid="text-response-preview-attachments">
|
|
5
|
+
<SentAttachmentChip v-for="path in attachments" :key="path" :path="path" variant="thumb" />
|
|
6
|
+
</div>
|
|
7
|
+
</div>
|
|
3
8
|
</template>
|
|
4
9
|
|
|
5
10
|
<script setup lang="ts">
|
|
@@ -7,6 +12,7 @@ import { computed } from "vue";
|
|
|
7
12
|
import { marked } from "marked";
|
|
8
13
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
9
14
|
import type { TextResponseData } from "./types";
|
|
15
|
+
import SentAttachmentChip from "../../components/SentAttachmentChip.vue";
|
|
10
16
|
|
|
11
17
|
const props = defineProps<{
|
|
12
18
|
result: ToolResultComplete<TextResponseData>;
|
|
@@ -27,6 +33,8 @@ const textColorClass = computed(() => {
|
|
|
27
33
|
|
|
28
34
|
const previewText = computed(() => markdownToPlainText(props.result.data?.text ?? ""));
|
|
29
35
|
|
|
36
|
+
const attachments = computed<string[]>(() => props.result.data?.attachments ?? []);
|
|
37
|
+
|
|
30
38
|
function markdownToPlainText(markdown: string): string {
|
|
31
39
|
const html = marked(markdown, { breaks: true, gfm: true }) as string;
|
|
32
40
|
const spaced = html
|