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
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="w-full h-full overflow-y-auto p-8">
|
|
2
|
+
<div class="w-full h-full overflow-y-auto p-8" data-testid="present-form-view">
|
|
3
3
|
<div v-if="formData" class="max-w-3xl w-full mx-auto">
|
|
4
|
-
<!-- Form Title -->
|
|
5
4
|
<h2 v-if="formData.title" class="text-gray-900 text-3xl font-bold mb-4 text-center">
|
|
6
5
|
{{ formData.title }}
|
|
7
6
|
</h2>
|
|
8
7
|
|
|
9
|
-
<!-- Form Description -->
|
|
10
8
|
<p v-if="formData.description" class="text-gray-600 text-center mb-8 text-lg">
|
|
11
9
|
{{ formData.description }}
|
|
12
10
|
</p>
|
|
13
11
|
|
|
14
|
-
<!-- Error Summary -->
|
|
15
12
|
<div v-if="showErrorSummary && fieldErrors.size > 0" class="bg-red-50 border-2 border-red-500 rounded-lg p-4 mb-6" role="alert">
|
|
16
13
|
<h3 class="text-red-800 font-semibold mb-2 flex items-center gap-2">
|
|
17
14
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
|
|
@@ -25,23 +22,21 @@
|
|
|
25
22
|
</h3>
|
|
26
23
|
<ul class="text-red-700 space-y-1">
|
|
27
24
|
<li v-for="[fieldId, error] in fieldErrors" :key="fieldId">
|
|
28
|
-
<a :href="`#${fieldId}`" @click.prevent="focusField(fieldId)"
|
|
25
|
+
<a :href="`#${fieldId}`" class="hover:underline cursor-pointer" @click.prevent="focusField(fieldId)">
|
|
29
26
|
{{ error.message }}
|
|
30
27
|
</a>
|
|
31
28
|
</li>
|
|
32
29
|
</ul>
|
|
33
30
|
</div>
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
<form @submit.prevent="handleSubmit" class="space-y-6">
|
|
32
|
+
<form class="space-y-6" @submit.prevent="handleSubmit">
|
|
37
33
|
<div
|
|
38
34
|
v-for="field in formData.fields"
|
|
39
|
-
:key="field.id"
|
|
40
35
|
:id="field.id"
|
|
36
|
+
:key="field.id"
|
|
41
37
|
class="form-field"
|
|
42
38
|
:class="{ 'has-error': hasError(field.id) && touched.has(field.id) }"
|
|
43
39
|
>
|
|
44
|
-
<!-- Field Label -->
|
|
45
40
|
<label
|
|
46
41
|
:for="`input-${field.id}`"
|
|
47
42
|
class="block text-gray-800 font-semibold mb-2"
|
|
@@ -53,12 +48,10 @@
|
|
|
53
48
|
<span v-if="field.required" class="text-red-500 ml-1" aria-label="required">{{ t("pluginPresentForm.requiredMarker") }}</span>
|
|
54
49
|
</label>
|
|
55
50
|
|
|
56
|
-
<!-- Field Description -->
|
|
57
51
|
<p v-if="field.description" class="text-gray-600 text-sm mb-2">
|
|
58
52
|
{{ field.description }}
|
|
59
53
|
</p>
|
|
60
54
|
|
|
61
|
-
<!-- Text Field -->
|
|
62
55
|
<input
|
|
63
56
|
v-if="field.type === 'text'"
|
|
64
57
|
:id="`input-${field.id}`"
|
|
@@ -67,16 +60,15 @@
|
|
|
67
60
|
:placeholder="field.placeholder"
|
|
68
61
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
69
62
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
70
|
-
@blur="handleBlur(field.id)"
|
|
71
|
-
@input="handleInput(field.id)"
|
|
72
63
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
|
|
73
64
|
:class="{
|
|
74
65
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
75
66
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
76
67
|
}"
|
|
68
|
+
@blur="handleBlur(field.id)"
|
|
69
|
+
@input="handleInput(field.id)"
|
|
77
70
|
/>
|
|
78
71
|
|
|
79
|
-
<!-- Textarea Field -->
|
|
80
72
|
<textarea
|
|
81
73
|
v-else-if="field.type === 'textarea'"
|
|
82
74
|
:id="`input-${field.id}`"
|
|
@@ -85,16 +77,15 @@
|
|
|
85
77
|
:rows="field.rows || 4"
|
|
86
78
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
87
79
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
88
|
-
@blur="handleBlur(field.id)"
|
|
89
|
-
@input="handleInput(field.id)"
|
|
90
80
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors resize-y"
|
|
91
81
|
:class="{
|
|
92
82
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
93
83
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
94
84
|
}"
|
|
85
|
+
@blur="handleBlur(field.id)"
|
|
86
|
+
@input="handleInput(field.id)"
|
|
95
87
|
/>
|
|
96
88
|
|
|
97
|
-
<!-- Number Field -->
|
|
98
89
|
<input
|
|
99
90
|
v-else-if="field.type === 'number'"
|
|
100
91
|
:id="`input-${field.id}`"
|
|
@@ -105,16 +96,15 @@
|
|
|
105
96
|
:step="field.step"
|
|
106
97
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
107
98
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
108
|
-
@blur="handleBlur(field.id)"
|
|
109
|
-
@input="handleInput(field.id)"
|
|
110
99
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
|
|
111
100
|
:class="{
|
|
112
101
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
113
102
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
114
103
|
}"
|
|
104
|
+
@blur="handleBlur(field.id)"
|
|
105
|
+
@input="handleInput(field.id)"
|
|
115
106
|
/>
|
|
116
107
|
|
|
117
|
-
<!-- Date Field -->
|
|
118
108
|
<input
|
|
119
109
|
v-else-if="field.type === 'date'"
|
|
120
110
|
:id="`input-${field.id}`"
|
|
@@ -124,16 +114,15 @@
|
|
|
124
114
|
:max="field.maxDate"
|
|
125
115
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
126
116
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
127
|
-
@blur="handleBlur(field.id)"
|
|
128
|
-
@change="handleInput(field.id)"
|
|
129
117
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
|
|
130
118
|
:class="{
|
|
131
119
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
132
120
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
133
121
|
}"
|
|
122
|
+
@blur="handleBlur(field.id)"
|
|
123
|
+
@change="handleInput(field.id)"
|
|
134
124
|
/>
|
|
135
125
|
|
|
136
|
-
<!-- Time Field -->
|
|
137
126
|
<input
|
|
138
127
|
v-else-if="field.type === 'time'"
|
|
139
128
|
:id="`input-${field.id}`"
|
|
@@ -141,16 +130,15 @@
|
|
|
141
130
|
type="time"
|
|
142
131
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
143
132
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
144
|
-
@blur="handleBlur(field.id)"
|
|
145
|
-
@change="handleInput(field.id)"
|
|
146
133
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
|
|
147
134
|
:class="{
|
|
148
135
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
149
136
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
150
137
|
}"
|
|
138
|
+
@blur="handleBlur(field.id)"
|
|
139
|
+
@change="handleInput(field.id)"
|
|
151
140
|
/>
|
|
152
141
|
|
|
153
|
-
<!-- Radio Field -->
|
|
154
142
|
<div
|
|
155
143
|
v-else-if="field.type === 'radio'"
|
|
156
144
|
class="space-y-2"
|
|
@@ -168,32 +156,31 @@
|
|
|
168
156
|
}"
|
|
169
157
|
>
|
|
170
158
|
<input
|
|
159
|
+
v-model="formValues[field.id]"
|
|
171
160
|
type="radio"
|
|
172
161
|
:name="field.id"
|
|
173
162
|
:value="index"
|
|
174
|
-
|
|
163
|
+
class="mr-3 h-4 w-4 flex-shrink-0"
|
|
175
164
|
@change="handleInput(field.id)"
|
|
176
165
|
@blur="handleBlur(field.id)"
|
|
177
|
-
class="mr-3 h-4 w-4 flex-shrink-0"
|
|
178
166
|
/>
|
|
179
167
|
<span class="text-gray-800">{{ choice }}</span>
|
|
180
168
|
</label>
|
|
181
169
|
</div>
|
|
182
170
|
|
|
183
|
-
<!-- Dropdown Field -->
|
|
184
171
|
<select
|
|
185
172
|
v-else-if="field.type === 'dropdown'"
|
|
186
173
|
:id="`input-${field.id}`"
|
|
187
174
|
v-model="formValues[field.id]"
|
|
188
175
|
:aria-invalid="hasError(field.id) && touched.has(field.id)"
|
|
189
176
|
:aria-describedby="hasError(field.id) && touched.has(field.id) ? `${field.id}-error` : undefined"
|
|
190
|
-
@blur="handleBlur(field.id)"
|
|
191
|
-
@change="handleInput(field.id)"
|
|
192
177
|
class="w-full px-4 py-2 border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors bg-white"
|
|
193
178
|
:class="{
|
|
194
179
|
'border-red-500 focus:ring-red-500': hasError(field.id) && touched.has(field.id),
|
|
195
180
|
'border-gray-300': !hasError(field.id) || !touched.has(field.id),
|
|
196
181
|
}"
|
|
182
|
+
@blur="handleBlur(field.id)"
|
|
183
|
+
@change="handleInput(field.id)"
|
|
197
184
|
>
|
|
198
185
|
<option :value="null" disabled>{{ t("pluginPresentForm.selectOption") }}</option>
|
|
199
186
|
<option v-for="(choice, index) in field.choices" :key="index" :value="index">
|
|
@@ -201,7 +188,6 @@
|
|
|
201
188
|
</option>
|
|
202
189
|
</select>
|
|
203
190
|
|
|
204
|
-
<!-- Checkbox Field -->
|
|
205
191
|
<div
|
|
206
192
|
v-else-if="field.type === 'checkbox'"
|
|
207
193
|
class="space-y-2"
|
|
@@ -219,18 +205,17 @@
|
|
|
219
205
|
}"
|
|
220
206
|
>
|
|
221
207
|
<input
|
|
208
|
+
v-model="formValues[field.id]"
|
|
222
209
|
type="checkbox"
|
|
223
210
|
:value="index"
|
|
224
|
-
|
|
211
|
+
class="mr-3 h-4 w-4 flex-shrink-0"
|
|
225
212
|
@change="handleInput(field.id)"
|
|
226
213
|
@blur="handleBlur(field.id)"
|
|
227
|
-
class="mr-3 h-4 w-4 flex-shrink-0"
|
|
228
214
|
/>
|
|
229
215
|
<span class="text-gray-800">{{ choice }}</span>
|
|
230
216
|
</label>
|
|
231
217
|
</div>
|
|
232
218
|
|
|
233
|
-
<!-- Error Message -->
|
|
234
219
|
<div
|
|
235
220
|
v-if="hasError(field.id) && touched.has(field.id)"
|
|
236
221
|
:id="`${field.id}-error`"
|
|
@@ -247,7 +232,6 @@
|
|
|
247
232
|
{{ fieldErrors.get(field.id)?.message }}
|
|
248
233
|
</div>
|
|
249
234
|
|
|
250
|
-
<!-- Character Count (for text/textarea with maxLength) -->
|
|
251
235
|
<div
|
|
252
236
|
v-if="showCharCount(field)"
|
|
253
237
|
class="text-sm mt-2"
|
|
@@ -265,7 +249,6 @@
|
|
|
265
249
|
</div>
|
|
266
250
|
</div>
|
|
267
251
|
|
|
268
|
-
<!-- Submit Button -->
|
|
269
252
|
<div class="mt-8 flex justify-center">
|
|
270
253
|
<button
|
|
271
254
|
type="submit"
|
|
@@ -277,7 +260,6 @@
|
|
|
277
260
|
</button>
|
|
278
261
|
</div>
|
|
279
262
|
|
|
280
|
-
<!-- Progress Indicator -->
|
|
281
263
|
<div class="mt-4 text-center text-gray-600 text-sm">
|
|
282
264
|
{{ t("pluginPresentForm.progress", { filled: filledRequiredCount, total: requiredFieldsCount }) }}
|
|
283
265
|
</div>
|
|
@@ -349,7 +331,6 @@ function applyNewResult(newResult: ToolResult): void {
|
|
|
349
331
|
isRestoring.value = false;
|
|
350
332
|
}
|
|
351
333
|
|
|
352
|
-
// Initialize form data and restore state
|
|
353
334
|
watch(
|
|
354
335
|
() => props.selectedResult,
|
|
355
336
|
(newResult, oldResult) => {
|
|
@@ -360,7 +341,6 @@ watch(
|
|
|
360
341
|
{ immediate: true },
|
|
361
342
|
);
|
|
362
343
|
|
|
363
|
-
// Save state to viewState - watch all state changes together
|
|
364
344
|
watch(
|
|
365
345
|
[formValues, touched, submitted],
|
|
366
346
|
() => {
|
|
@@ -416,9 +396,7 @@ function isEmpty(value: any): boolean {
|
|
|
416
396
|
return false;
|
|
417
397
|
}
|
|
418
398
|
|
|
419
|
-
// 254
|
|
420
|
-
// the chained `[^\s@]+` regex pattern that sonarjs flags as
|
|
421
|
-
// vulnerable to super-linear backtracking.
|
|
399
|
+
// 254 per RFC 5321; bounding upfront keeps the regex below from backtracking.
|
|
422
400
|
const EMAIL_MAX_LENGTH = 254;
|
|
423
401
|
function isValidEmail(email: string): boolean {
|
|
424
402
|
if (!email || email.length > EMAIL_MAX_LENGTH) return false;
|
|
@@ -433,6 +411,7 @@ function isValidEmail(email: string): boolean {
|
|
|
433
411
|
|
|
434
412
|
function isValidUrl(url: string): boolean {
|
|
435
413
|
try {
|
|
414
|
+
// eslint-disable-next-line no-new -- side-effect probe to validate URL syntax
|
|
436
415
|
new URL(url);
|
|
437
416
|
return true;
|
|
438
417
|
} catch {
|
|
@@ -518,10 +497,9 @@ function validateField(fieldId: string): boolean {
|
|
|
518
497
|
type: "custom",
|
|
519
498
|
});
|
|
520
499
|
return false;
|
|
521
|
-
} else {
|
|
522
|
-
fieldErrors.value.delete(fieldId);
|
|
523
|
-
return true;
|
|
524
500
|
}
|
|
501
|
+
fieldErrors.value.delete(fieldId);
|
|
502
|
+
return true;
|
|
525
503
|
}
|
|
526
504
|
|
|
527
505
|
function handleBlur(fieldId: string): void {
|
|
@@ -530,7 +508,6 @@ function handleBlur(fieldId: string): void {
|
|
|
530
508
|
}
|
|
531
509
|
|
|
532
510
|
function handleInput(fieldId: string): void {
|
|
533
|
-
// Real-time validation for fields that are already touched
|
|
534
511
|
if (touched.value.has(fieldId)) {
|
|
535
512
|
validateField(fieldId);
|
|
536
513
|
}
|
|
@@ -554,15 +531,13 @@ function showCharCount(field: FormField): boolean {
|
|
|
554
531
|
|
|
555
532
|
function isNearLimit(field: FormField): boolean {
|
|
556
533
|
if (field.type !== "text" && field.type !== "textarea") return false;
|
|
557
|
-
const maxLength =
|
|
534
|
+
const { maxLength } = field as TextField | TextareaField;
|
|
558
535
|
if (!maxLength) return false;
|
|
559
536
|
const currentLength = (formValues.value[field.id] || "").length;
|
|
560
537
|
return currentLength / maxLength > 0.9;
|
|
561
538
|
}
|
|
562
539
|
|
|
563
|
-
const requiredFieldsCount = computed(() =>
|
|
564
|
-
return formData.value?.fields.filter((field) => field.required).length || 0;
|
|
565
|
-
});
|
|
540
|
+
const requiredFieldsCount = computed(() => formData.value?.fields.filter((field) => field.required).length || 0);
|
|
566
541
|
|
|
567
542
|
const filledRequiredCount = computed(() => {
|
|
568
543
|
if (!formData.value) return 0;
|
|
@@ -572,27 +547,18 @@ const filledRequiredCount = computed(() => {
|
|
|
572
547
|
function handleSubmit(): void {
|
|
573
548
|
if (submitted.value) return;
|
|
574
549
|
|
|
575
|
-
// Mark all fields as touched
|
|
576
550
|
formData.value?.fields.forEach((field) => {
|
|
577
551
|
touched.value.add(field.id);
|
|
578
552
|
validateField(field.id);
|
|
579
553
|
});
|
|
580
554
|
|
|
581
|
-
// Check for errors
|
|
582
555
|
if (fieldErrors.value.size > 0) {
|
|
583
556
|
showErrorSummary.value = true;
|
|
584
|
-
|
|
585
|
-
const firstErrorFieldId = Array.from(fieldErrors.value.keys())[0];
|
|
557
|
+
const [firstErrorFieldId] = Array.from(fieldErrors.value.keys());
|
|
586
558
|
focusField(firstErrorFieldId);
|
|
587
559
|
return;
|
|
588
560
|
}
|
|
589
561
|
|
|
590
|
-
// Build a markdown bullet list of `- {label}: {value}` lines so the
|
|
591
|
-
// chat history reads naturally for a human while still being trivial
|
|
592
|
-
// for the LLM to parse. Empty values become "(none)". Titles, labels,
|
|
593
|
-
// and choice strings are passed through singleLine() to strip any
|
|
594
|
-
// newlines they might contain — without that, a label like
|
|
595
|
-
// "Address (street\nand number)" would shatter the bullet structure.
|
|
596
562
|
const lines: string[] = [];
|
|
597
563
|
if (formData.value?.title) lines.push(`**${singleLine(formData.value.title)}**`, "");
|
|
598
564
|
formData.value?.fields.forEach((field) => {
|
|
@@ -603,22 +569,15 @@ function handleSubmit(): void {
|
|
|
603
569
|
props.sendTextMessage(lines.join("\n"));
|
|
604
570
|
}
|
|
605
571
|
|
|
606
|
-
// Indent
|
|
607
|
-
//
|
|
608
|
-
// instead of starting a new top-level item. Without this, a textarea
|
|
609
|
-
// value containing newlines would inject phantom bullets into the
|
|
610
|
-
// chat-side payload that the LLM could mis-parse.
|
|
572
|
+
// Indent so multi-line text/textarea values stay attached to their bullet
|
|
573
|
+
// instead of opening a new top-level item under markdown rules.
|
|
611
574
|
function indentContinuation(text: string): string {
|
|
612
575
|
return text.replace(/\n/g, "\n ");
|
|
613
576
|
}
|
|
614
577
|
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
618
|
-
// structure and reshape the payload. Free-form user input
|
|
619
|
-
// (text/textarea values) uses indentContinuation instead — it
|
|
620
|
-
// preserves multi-line values. Implemented via split-trim-join (not a
|
|
621
|
-
// global regex) to avoid sonarjs's slow-regex flag on `\s*\n\s*`.
|
|
578
|
+
// LLM-authored title/label/choice strings — collapse any newline so they
|
|
579
|
+
// can't smuggle phantom bullets into the payload. split-trim-join avoids
|
|
580
|
+
// sonarjs's slow-regex flag on `\s*\n\s*`.
|
|
622
581
|
function singleLine(text: string): string {
|
|
623
582
|
return text
|
|
624
583
|
.split(/\r?\n/)
|
|
@@ -633,14 +592,12 @@ function renderValue(field: FormField, value: any): string {
|
|
|
633
592
|
if (field.type === "radio" || field.type === "dropdown") {
|
|
634
593
|
return value !== null && value !== undefined ? singleLine(field.choices[value]) : empty;
|
|
635
594
|
}
|
|
636
|
-
//
|
|
637
|
-
//
|
|
638
|
-
// itself contains a comma; the sublist is unambiguous and survives
|
|
639
|
-
// round-trip parsing.
|
|
595
|
+
// Sublist (not comma-join): a choice label containing a comma would otherwise
|
|
596
|
+
// be indistinguishable from two separate selections.
|
|
640
597
|
if (field.type === "checkbox") {
|
|
641
598
|
const items: string[] = (value || []).map((idx: number) => singleLine(field.choices[idx]));
|
|
642
599
|
if (items.length === 0) return empty;
|
|
643
|
-
return
|
|
600
|
+
return `\n - ${items.join("\n - ")}`;
|
|
644
601
|
}
|
|
645
602
|
if (typeof value === "string") {
|
|
646
603
|
const trimmed = value.trim();
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Form Tool Definition (Schema)
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
+
import { META } from "./meta";
|
|
3
|
+
import type { ResolvedRoute } from "../meta-types";
|
|
6
4
|
|
|
7
|
-
export const TOOL_NAME =
|
|
5
|
+
export const TOOL_NAME = META.toolName;
|
|
6
|
+
export type PresentFormEndpoints = { readonly [K in keyof typeof META.apiRoutes]: ResolvedRoute };
|
|
8
7
|
|
|
9
8
|
export const TOOL_DEFINITION: ToolDefinition = {
|
|
10
9
|
type: "function",
|
|
11
|
-
name:
|
|
10
|
+
name: META.toolName,
|
|
12
11
|
description:
|
|
13
12
|
"Create a structured form to collect information from the user. Supports various field types including text input, textarea, multiple choice (radio), dropdown menus, checkboxes, date/time pickers, and number inputs. Each field can have validation rules and help text.",
|
|
14
13
|
parameters: {
|
|
@@ -125,3 +124,5 @@ export const TOOL_DEFINITION: ToolDefinition = {
|
|
|
125
124
|
required: ["fields"],
|
|
126
125
|
},
|
|
127
126
|
};
|
|
127
|
+
|
|
128
|
+
export default TOOL_DEFINITION;
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import type { ToolPlugin } from "gui-chat-protocol/vue";
|
|
2
|
+
import type { PluginRegistration } from "../../tools/types";
|
|
2
3
|
import type { FormData, FormArgs } from "./types";
|
|
3
|
-
import { TOOL_DEFINITION } from "./definition";
|
|
4
|
+
import { TOOL_DEFINITION, TOOL_NAME } from "./definition";
|
|
4
5
|
import { executeForm } from "./plugin";
|
|
6
|
+
import { wrapWithScope } from "../scope";
|
|
5
7
|
import View from "./View.vue";
|
|
6
8
|
import Preview from "./Preview.vue";
|
|
7
9
|
|
|
8
|
-
const presentFormPlugin: ToolPlugin<
|
|
10
|
+
const presentFormPlugin: ToolPlugin<FormData, FormData, FormArgs> = {
|
|
9
11
|
toolDefinition: TOOL_DEFINITION,
|
|
10
12
|
execute: executeForm,
|
|
11
13
|
generatingMessage: "Preparing form...",
|
|
12
14
|
isEnabled: () => true,
|
|
13
|
-
viewComponent: View,
|
|
14
|
-
previewComponent: Preview,
|
|
15
|
+
viewComponent: wrapWithScope("form", View),
|
|
16
|
+
previewComponent: wrapWithScope("form", Preview),
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
export default presentFormPlugin;
|
|
18
|
-
export { TOOL_NAME }
|
|
20
|
+
export { TOOL_NAME };
|
|
21
|
+
|
|
22
|
+
export const REGISTRATION: PluginRegistration = {
|
|
23
|
+
toolName: TOOL_NAME,
|
|
24
|
+
entry: presentFormPlugin,
|
|
25
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { definePluginMeta } from "../meta-types";
|
|
2
|
+
|
|
3
|
+
export const META = definePluginMeta({
|
|
4
|
+
toolName: "presentForm",
|
|
5
|
+
apiNamespace: "form",
|
|
6
|
+
apiRoutes: {
|
|
7
|
+
/** POST /api/form — render a form for the user to fill out. */
|
|
8
|
+
dispatch: { method: "POST", path: "" },
|
|
9
|
+
},
|
|
10
|
+
mcpDispatch: "dispatch",
|
|
11
|
+
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// submission payload (see View.vue:handleSubmit) renders as readable
|
|
4
|
-
// markdown bullets in the chat history instead of a JSON-wrapped
|
|
5
|
-
// object.
|
|
1
|
+
// Forked from @mulmochat-plugin/form so handleSubmit can emit a markdown
|
|
2
|
+
// bullet list instead of the upstream JSON-wrapped payload.
|
|
6
3
|
|
|
7
4
|
import type { ToolContext, ToolResult } from "gui-chat-protocol";
|
|
8
5
|
import type { FormData, FormArgs, FormField } from "./types";
|
|
@@ -31,9 +28,7 @@ function validateCheckboxRange(field: FormField & { type: "checkbox" }): void {
|
|
|
31
28
|
if (maxSelections !== undefined && maxSelections > choices.length) {
|
|
32
29
|
throw new Error(`Field '${id}': maxSelections cannot exceed number of choices`);
|
|
33
30
|
}
|
|
34
|
-
// Without this
|
|
35
|
-
// would render but be impossible to submit — the user can never satisfy the
|
|
36
|
-
// minimum because there aren't enough options to pick.
|
|
31
|
+
// Without this, a form would render but be unsubmittable.
|
|
37
32
|
if (minSelections !== undefined && minSelections > choices.length) {
|
|
38
33
|
throw new Error(`Field '${id}': minSelections cannot exceed number of choices`);
|
|
39
34
|
}
|
|
@@ -67,7 +62,7 @@ function validateField(field: FormField, index: number, seenIds: Set<string>): v
|
|
|
67
62
|
validateRangeField(field);
|
|
68
63
|
}
|
|
69
64
|
|
|
70
|
-
export const executeForm = async (_context: ToolContext, args: FormArgs): Promise<ToolResult<
|
|
65
|
+
export const executeForm = async (_context: ToolContext, args: FormArgs): Promise<ToolResult<FormData, FormData>> => {
|
|
71
66
|
try {
|
|
72
67
|
const { title, description, fields } = args;
|
|
73
68
|
if (!fields || !Array.isArray(fields) || fields.length === 0) {
|
|
@@ -81,6 +76,10 @@ export const executeForm = async (_context: ToolContext, args: FormArgs): Promis
|
|
|
81
76
|
const titleSuffix = title ? `: ${title}` : "";
|
|
82
77
|
return {
|
|
83
78
|
message: `Form created with ${fieldCount}${titleSuffix}`,
|
|
79
|
+
// `data` is the view's source (also the host's preview-gate
|
|
80
|
+
// signal); `jsonData` is what the LLM sees in the tool result.
|
|
81
|
+
// Same payload, two audiences — keep both in sync.
|
|
82
|
+
data: formData,
|
|
84
83
|
jsonData: formData,
|
|
85
84
|
instructions:
|
|
86
85
|
"The form has been presented to the user. Wait for the user to fill out and submit it. They will reply with a markdown bullet list of `- {label}: {value}` lines.",
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Form Plugin Types
|
|
3
|
-
*
|
|
4
|
-
* Form-specific type definitions only.
|
|
5
|
-
* Common types should be imported directly from gui-chat-protocol.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// ============================================================================
|
|
9
|
-
// Form-specific Types
|
|
10
|
-
// ============================================================================
|
|
11
|
-
|
|
12
|
-
/** Field type discriminator */
|
|
13
1
|
export type FieldType = "text" | "textarea" | "radio" | "dropdown" | "checkbox" | "date" | "time" | "number";
|
|
14
2
|
|
|
15
|
-
/** Base field interface */
|
|
16
3
|
export interface BaseField {
|
|
17
4
|
id: string;
|
|
18
5
|
type: FieldType;
|
|
@@ -22,7 +9,6 @@ export interface BaseField {
|
|
|
22
9
|
maxLength?: number;
|
|
23
10
|
}
|
|
24
11
|
|
|
25
|
-
/** Text field */
|
|
26
12
|
export interface TextField extends BaseField {
|
|
27
13
|
type: "text";
|
|
28
14
|
placeholder?: string;
|
|
@@ -32,7 +18,6 @@ export interface TextField extends BaseField {
|
|
|
32
18
|
maxLength?: number;
|
|
33
19
|
}
|
|
34
20
|
|
|
35
|
-
/** Textarea field */
|
|
36
21
|
export interface TextareaField extends BaseField {
|
|
37
22
|
type: "textarea";
|
|
38
23
|
placeholder?: string;
|
|
@@ -42,14 +27,12 @@ export interface TextareaField extends BaseField {
|
|
|
42
27
|
defaultValue?: string;
|
|
43
28
|
}
|
|
44
29
|
|
|
45
|
-
/** Radio field */
|
|
46
30
|
export interface RadioField extends BaseField {
|
|
47
31
|
type: "radio";
|
|
48
32
|
choices: string[];
|
|
49
33
|
defaultValue?: string;
|
|
50
34
|
}
|
|
51
35
|
|
|
52
|
-
/** Dropdown field */
|
|
53
36
|
export interface DropdownField extends BaseField {
|
|
54
37
|
type: "dropdown";
|
|
55
38
|
choices: string[];
|
|
@@ -57,7 +40,6 @@ export interface DropdownField extends BaseField {
|
|
|
57
40
|
defaultValue?: string;
|
|
58
41
|
}
|
|
59
42
|
|
|
60
|
-
/** Checkbox field */
|
|
61
43
|
export interface CheckboxField extends BaseField {
|
|
62
44
|
type: "checkbox";
|
|
63
45
|
choices: string[];
|
|
@@ -66,7 +48,6 @@ export interface CheckboxField extends BaseField {
|
|
|
66
48
|
defaultValue?: string[];
|
|
67
49
|
}
|
|
68
50
|
|
|
69
|
-
/** Date field */
|
|
70
51
|
export interface DateField extends BaseField {
|
|
71
52
|
type: "date";
|
|
72
53
|
minDate?: string;
|
|
@@ -75,14 +56,12 @@ export interface DateField extends BaseField {
|
|
|
75
56
|
defaultValue?: string;
|
|
76
57
|
}
|
|
77
58
|
|
|
78
|
-
/** Time field */
|
|
79
59
|
export interface TimeField extends BaseField {
|
|
80
60
|
type: "time";
|
|
81
61
|
format?: "12hr" | "24hr";
|
|
82
62
|
defaultValue?: string;
|
|
83
63
|
}
|
|
84
64
|
|
|
85
|
-
/** Number field */
|
|
86
65
|
export interface NumberField extends BaseField {
|
|
87
66
|
type: "number";
|
|
88
67
|
min?: number;
|
|
@@ -91,17 +70,14 @@ export interface NumberField extends BaseField {
|
|
|
91
70
|
defaultValue?: number;
|
|
92
71
|
}
|
|
93
72
|
|
|
94
|
-
/** Union type for all fields */
|
|
95
73
|
export type FormField = TextField | TextareaField | RadioField | DropdownField | CheckboxField | DateField | TimeField | NumberField;
|
|
96
74
|
|
|
97
|
-
/** Form data stored in result.jsonData */
|
|
98
75
|
export interface FormData {
|
|
99
76
|
title?: string;
|
|
100
77
|
description?: string;
|
|
101
78
|
fields: FormField[];
|
|
102
79
|
}
|
|
103
80
|
|
|
104
|
-
/** Arguments passed to the form tool */
|
|
105
81
|
export interface FormArgs {
|
|
106
82
|
title?: string;
|
|
107
83
|
description?: string;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="p-2 text-sm">
|
|
3
|
-
<div class="font-medium text-gray-700 truncate
|
|
3
|
+
<div class="font-medium text-gray-700 truncate">
|
|
4
4
|
{{ title }}
|
|
5
5
|
</div>
|
|
6
|
-
<div v-if="hint" class="text-xs text-gray-500 leading-relaxed truncate">
|
|
7
|
-
{{ hint }}
|
|
8
|
-
</div>
|
|
9
6
|
</div>
|
|
10
7
|
</template>
|
|
11
8
|
|
|
@@ -13,13 +10,9 @@
|
|
|
13
10
|
import { computed } from "vue";
|
|
14
11
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
15
12
|
import type { PresentHtmlData } from "./index";
|
|
16
|
-
import { stripHtmlToPreview } from "./helpers";
|
|
17
|
-
|
|
18
|
-
const HINT_MAX_LENGTH = 60;
|
|
19
13
|
|
|
20
14
|
const props = defineProps<{ result: ToolResultComplete<PresentHtmlData> }>();
|
|
21
15
|
|
|
22
16
|
const data = computed(() => props.result.data);
|
|
23
17
|
const title = computed(() => data.value?.title ?? "HTML Page");
|
|
24
|
-
const hint = computed(() => stripHtmlToPreview(data.value?.html ?? "", HINT_MAX_LENGTH));
|
|
25
18
|
</script>
|