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,386 +0,0 @@
|
|
|
1
|
-
// Pure handlers for the id-based REST routes used by the file-explorer
|
|
2
|
-
// todo view (TodoExplorer.vue). The MCP `manageTodoList` action route
|
|
3
|
-
// continues to live in todosHandlers.ts; these handlers are intended
|
|
4
|
-
// for the web UI which knows item ids directly and doesn't need the
|
|
5
|
-
// substring-match contract that the MCP handlers use.
|
|
6
|
-
//
|
|
7
|
-
// Each function takes the current items + columns + an input record
|
|
8
|
-
// and returns a result. The Express route is responsible for loading
|
|
9
|
-
// and saving JSON to disk.
|
|
10
|
-
|
|
11
|
-
import type { TodoItem, TodoPriority } from "./todos.js";
|
|
12
|
-
import { type StatusColumn, defaultStatusId, doneColumnId } from "./todosColumnsHandlers.js";
|
|
13
|
-
import { mergeLabels } from "../../../src/plugins/todo/labels.js";
|
|
14
|
-
import { makeId } from "../../utils/id.js";
|
|
15
|
-
|
|
16
|
-
const ORDER_STEP = 1000;
|
|
17
|
-
const PRIORITIES: readonly TodoPriority[] = ["low", "medium", "high", "urgent"];
|
|
18
|
-
|
|
19
|
-
// ── Result type ───────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
export type ItemsActionResult = { kind: "error"; status: number; error: string } | { kind: "success"; items: TodoItem[]; item?: TodoItem };
|
|
22
|
-
|
|
23
|
-
// ── Migration ─────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
// Backfill `status` and `order` on items that pre-date the kanban
|
|
26
|
-
// extension. Pure / idempotent: items that already have valid values
|
|
27
|
-
// pass through unchanged. Done via a single forward pass that assigns
|
|
28
|
-
// monotonically-increasing order values per status column so the
|
|
29
|
-
// kanban view has a stable initial sort.
|
|
30
|
-
//
|
|
31
|
-
// Reasoning for the order assignment: legacy items only carry a
|
|
32
|
-
// `createdAt`, and we want oldest-first within each column. We can't
|
|
33
|
-
// just use createdAt as the order key because it's a milliseconds
|
|
34
|
-
// number which makes hand-editing painful and conflicts with the
|
|
35
|
-
// 1000-step convention drag-drop uses for new items.
|
|
36
|
-
export function migrateItems(rawItems: TodoItem[], columns: StatusColumn[]): TodoItem[] {
|
|
37
|
-
const doneId = doneColumnId(columns);
|
|
38
|
-
const openId = defaultStatusId(columns);
|
|
39
|
-
const validStatusIds = new Set(columns.map((column) => column.id));
|
|
40
|
-
|
|
41
|
-
// First pass: backfill status. Items pointing at a column that no
|
|
42
|
-
// longer exists are reassigned to the default open or done column
|
|
43
|
-
// depending on `completed`.
|
|
44
|
-
//
|
|
45
|
-
// Note: we deliberately do NOT re-sync `completed` to status on
|
|
46
|
-
// every read. Earlier versions of this function did, but that
|
|
47
|
-
// overrode the legacy MCP `check` / `uncheck` actions — those
|
|
48
|
-
// actions only flip the boolean, never touch status, so a sync
|
|
49
|
-
// pass kept reverting them on the next read. Treating the two
|
|
50
|
-
// fields as independent at the storage layer leaves both the REST
|
|
51
|
-
// PATCH path (which keeps them in sync explicitly) and the legacy
|
|
52
|
-
// MCP actions (which only touch `completed`) working correctly.
|
|
53
|
-
const withStatus = rawItems.map((item): TodoItem => {
|
|
54
|
-
const hasValidStatus = typeof item.status === "string" && validStatusIds.has(item.status);
|
|
55
|
-
if (hasValidStatus) return item;
|
|
56
|
-
const status = item.completed ? doneId : openId;
|
|
57
|
-
return { ...item, status };
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Second pass: backfill order per column. Items that already have
|
|
61
|
-
// an order keep item untouched — only items missing order get one
|
|
62
|
-
// assigned, and they go after the column's current max so they
|
|
63
|
-
// sort to the bottom in createdAt order. This preserves any
|
|
64
|
-
// hand-managed ordering even when a column is a mix of legacy
|
|
65
|
-
// and kanban-aware items.
|
|
66
|
-
const byStatus = new Map<string, TodoItem[]>();
|
|
67
|
-
for (const item of withStatus) {
|
|
68
|
-
const key = item.status ?? openId;
|
|
69
|
-
if (!byStatus.has(key)) byStatus.set(key, []);
|
|
70
|
-
byStatus.get(key)!.push(item);
|
|
71
|
-
}
|
|
72
|
-
const orderById = new Map<string, number>();
|
|
73
|
-
for (const [, group] of byStatus) {
|
|
74
|
-
const missing = group.filter((item) => typeof item.order !== "number");
|
|
75
|
-
if (missing.length === 0) continue;
|
|
76
|
-
const existingMax = group.filter((item) => typeof item.order === "number").reduce((acc, item) => Math.max(acc, item.order!), 0);
|
|
77
|
-
const sorted = [...missing].sort((left, right) => left.createdAt - right.createdAt);
|
|
78
|
-
sorted.forEach((item, i) => {
|
|
79
|
-
orderById.set(item.id, existingMax + (i + 1) * ORDER_STEP);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
return withStatus.map((item): TodoItem => {
|
|
83
|
-
const next = orderById.get(item.id);
|
|
84
|
-
if (next === undefined) return item;
|
|
85
|
-
return { ...item, order: next };
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ── Validators ────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
function isPriority(value: unknown): value is TodoPriority {
|
|
92
|
-
return typeof value === "string" && PRIORITIES.includes(value as TodoPriority);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// YYYY-MM-DD only — keep item boring so the column is sortable as text.
|
|
96
|
-
function isDueDate(value: unknown): value is string {
|
|
97
|
-
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function nextOrder(items: TodoItem[], statusId: string): number {
|
|
101
|
-
const inColumn = items.filter((item) => item.status === statusId).map((item) => item.order ?? 0);
|
|
102
|
-
if (inColumn.length === 0) return ORDER_STEP;
|
|
103
|
-
return Math.max(...inColumn) + ORDER_STEP;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ── Create ────────────────────────────────────────────────────────
|
|
107
|
-
|
|
108
|
-
export interface CreateInput {
|
|
109
|
-
text?: string;
|
|
110
|
-
note?: string;
|
|
111
|
-
status?: string;
|
|
112
|
-
priority?: string;
|
|
113
|
-
dueDate?: string;
|
|
114
|
-
labels?: string[];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Resolve the status field from input, validating against known
|
|
118
|
-
// columns. Returns the resolved column id or an error result.
|
|
119
|
-
type ResolveStatusResult = { kind: "ok"; status: string } | { kind: "error"; status: number; error: string };
|
|
120
|
-
|
|
121
|
-
function resolveStatus(input: CreateInput, columns: StatusColumn[]): ResolveStatusResult {
|
|
122
|
-
if (input.status === undefined || input.status === "") {
|
|
123
|
-
return { kind: "ok", status: defaultStatusId(columns) };
|
|
124
|
-
}
|
|
125
|
-
const validStatusIds = new Set(columns.map((column) => column.id));
|
|
126
|
-
if (validStatusIds.has(input.status)) {
|
|
127
|
-
return { kind: "ok", status: input.status };
|
|
128
|
-
}
|
|
129
|
-
return {
|
|
130
|
-
kind: "error",
|
|
131
|
-
status: 400,
|
|
132
|
-
error: `unknown status: ${input.status}`,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Apply optional priority + dueDate to an item, returning an error
|
|
137
|
-
// result on validation failure.
|
|
138
|
-
function applyOptionalFields(item: TodoItem, input: CreateInput): ItemsActionResult | null {
|
|
139
|
-
if (input.priority !== undefined && input.priority !== "") {
|
|
140
|
-
if (!isPriority(input.priority)) {
|
|
141
|
-
return { kind: "error", status: 400, error: "invalid priority" };
|
|
142
|
-
}
|
|
143
|
-
item.priority = input.priority;
|
|
144
|
-
}
|
|
145
|
-
if (input.dueDate !== undefined && input.dueDate !== "") {
|
|
146
|
-
if (!isDueDate(input.dueDate)) {
|
|
147
|
-
return {
|
|
148
|
-
kind: "error",
|
|
149
|
-
status: 400,
|
|
150
|
-
error: "dueDate must be YYYY-MM-DD",
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
item.dueDate = input.dueDate;
|
|
154
|
-
}
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export function handleCreate(items: TodoItem[], columns: StatusColumn[], input: CreateInput): ItemsActionResult {
|
|
159
|
-
if (!input.text || input.text.trim().length === 0) {
|
|
160
|
-
return { kind: "error", status: 400, error: "text required" };
|
|
161
|
-
}
|
|
162
|
-
const resolved = resolveStatus(input, columns);
|
|
163
|
-
if (resolved.kind === "error") return resolved;
|
|
164
|
-
|
|
165
|
-
const status = resolved.status;
|
|
166
|
-
const item: TodoItem = {
|
|
167
|
-
id: makeId("todo"),
|
|
168
|
-
text: input.text.trim(),
|
|
169
|
-
completed: status === doneColumnId(columns),
|
|
170
|
-
createdAt: Date.now(),
|
|
171
|
-
status,
|
|
172
|
-
order: nextOrder(items, status),
|
|
173
|
-
};
|
|
174
|
-
if (input.note !== undefined && input.note !== "") item.note = input.note;
|
|
175
|
-
const normalizedLabels = mergeLabels([], input.labels ?? []);
|
|
176
|
-
if (normalizedLabels.length > 0) item.labels = normalizedLabels;
|
|
177
|
-
|
|
178
|
-
const fieldError = applyOptionalFields(item, input);
|
|
179
|
-
if (fieldError) return fieldError;
|
|
180
|
-
|
|
181
|
-
return { kind: "success", items: [...items, item], item };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ── Patch ─────────────────────────────────────────────────────────
|
|
185
|
-
|
|
186
|
-
export interface PatchInput {
|
|
187
|
-
text?: string;
|
|
188
|
-
note?: string | null;
|
|
189
|
-
status?: string;
|
|
190
|
-
priority?: string | null;
|
|
191
|
-
dueDate?: string | null;
|
|
192
|
-
labels?: string[];
|
|
193
|
-
completed?: boolean;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Each `applyXxx` helper mutates `updated` in place and returns either
|
|
197
|
-
// `null` (success) or an error result. Splitting them out keeps the
|
|
198
|
-
// top-level `handlePatch` linear so item stays under the cognitive
|
|
199
|
-
// complexity threshold and so each field's edit semantics live in one
|
|
200
|
-
// obvious place.
|
|
201
|
-
|
|
202
|
-
function applyTextPatch(updated: TodoItem, input: PatchInput): ItemsActionResult | null {
|
|
203
|
-
if (typeof input.text !== "string") return null;
|
|
204
|
-
if (input.text.trim().length === 0) {
|
|
205
|
-
return { kind: "error", status: 400, error: "text cannot be empty" };
|
|
206
|
-
}
|
|
207
|
-
updated.text = input.text.trim();
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function applyNotePatch(updated: TodoItem, input: PatchInput): void {
|
|
212
|
-
if (input.note === null || input.note === "") {
|
|
213
|
-
delete updated.note;
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (typeof input.note === "string") updated.note = input.note;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function applyLabelsPatch(updated: TodoItem, input: PatchInput): void {
|
|
220
|
-
if (!Array.isArray(input.labels)) return;
|
|
221
|
-
const merged = mergeLabels([], input.labels);
|
|
222
|
-
if (merged.length > 0) updated.labels = merged;
|
|
223
|
-
else delete updated.labels;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function applyPriorityPatch(updated: TodoItem, input: PatchInput): ItemsActionResult | null {
|
|
227
|
-
if (input.priority === null || input.priority === "") {
|
|
228
|
-
delete updated.priority;
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
if (input.priority === undefined) return null;
|
|
232
|
-
if (!isPriority(input.priority)) {
|
|
233
|
-
return { kind: "error", status: 400, error: "invalid priority" };
|
|
234
|
-
}
|
|
235
|
-
updated.priority = input.priority;
|
|
236
|
-
return null;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function applyDueDatePatch(updated: TodoItem, input: PatchInput): ItemsActionResult | null {
|
|
240
|
-
if (input.dueDate === null || input.dueDate === "") {
|
|
241
|
-
delete updated.dueDate;
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
if (input.dueDate === undefined) return null;
|
|
245
|
-
if (!isDueDate(input.dueDate)) {
|
|
246
|
-
return { kind: "error", status: 400, error: "dueDate must be YYYY-MM-DD" };
|
|
247
|
-
}
|
|
248
|
-
updated.dueDate = input.dueDate;
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function applyStatusPatch(updated: TodoItem, target: TodoItem, items: TodoItem[], columns: StatusColumn[], input: PatchInput): ItemsActionResult | null {
|
|
253
|
-
if (typeof input.status !== "string" || input.status === target.status) {
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
|
-
const validStatusIds = new Set(columns.map((column) => column.id));
|
|
257
|
-
if (!validStatusIds.has(input.status)) {
|
|
258
|
-
return {
|
|
259
|
-
kind: "error",
|
|
260
|
-
status: 400,
|
|
261
|
-
error: `unknown status: ${input.status}`,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
updated.status = input.status;
|
|
265
|
-
updated.order = nextOrder(items, input.status);
|
|
266
|
-
updated.completed = input.status === doneColumnId(columns);
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Explicit `completed` toggle without changing status: lets the user
|
|
271
|
-
// check / uncheck a card and have item move between the done column and
|
|
272
|
-
// a default open column the obvious way.
|
|
273
|
-
function applyCompletedPatch(updated: TodoItem, items: TodoItem[], columns: StatusColumn[], input: PatchInput): void {
|
|
274
|
-
if (typeof input.completed !== "boolean") return;
|
|
275
|
-
if (input.completed === updated.completed) return;
|
|
276
|
-
updated.completed = input.completed;
|
|
277
|
-
const targetStatus = input.completed ? doneColumnId(columns) : defaultStatusId(columns);
|
|
278
|
-
if (targetStatus !== updated.status) {
|
|
279
|
-
updated.status = targetStatus;
|
|
280
|
-
updated.order = nextOrder(items, targetStatus);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export function handlePatch(items: TodoItem[], columns: StatusColumn[], itemId: string, input: PatchInput): ItemsActionResult {
|
|
285
|
-
const target = items.find((item) => item.id === itemId);
|
|
286
|
-
if (!target) {
|
|
287
|
-
return { kind: "error", status: 404, error: `item not found: ${itemId}` };
|
|
288
|
-
}
|
|
289
|
-
const updated: TodoItem = { ...target };
|
|
290
|
-
|
|
291
|
-
// Each step short-circuits on validation failure. Order matters:
|
|
292
|
-
// status changes happen before completed-toggling so an explicit
|
|
293
|
-
// completed: true alongside a non-done status doesn't fight itself.
|
|
294
|
-
const steps: Array<() => ItemsActionResult | null | void> = [
|
|
295
|
-
() => applyTextPatch(updated, input),
|
|
296
|
-
() => applyNotePatch(updated, input),
|
|
297
|
-
() => applyLabelsPatch(updated, input),
|
|
298
|
-
() => applyPriorityPatch(updated, input),
|
|
299
|
-
() => applyDueDatePatch(updated, input),
|
|
300
|
-
() => applyStatusPatch(updated, target, items, columns, input),
|
|
301
|
-
() => applyCompletedPatch(updated, items, columns, input),
|
|
302
|
-
];
|
|
303
|
-
for (const step of steps) {
|
|
304
|
-
const err = step();
|
|
305
|
-
if (err) return err;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const next = items.map((item) => (item.id === itemId ? updated : item));
|
|
309
|
-
return { kind: "success", items: next, item: updated };
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// ── Move (drag & drop) ────────────────────────────────────────────
|
|
313
|
-
|
|
314
|
-
// Reorder + cross-column move in a single call. `position` is the
|
|
315
|
-
// 0-based index the item should occupy in its target column AFTER
|
|
316
|
-
// the move (with the moving item itself excluded from the count).
|
|
317
|
-
//
|
|
318
|
-
// We rebuild the entire target column's order field for simplicity:
|
|
319
|
-
// it's O(n) per column, which for a kanban with hundreds of items is
|
|
320
|
-
// negligible and makes the math obviously correct.
|
|
321
|
-
export interface MoveInput {
|
|
322
|
-
status?: string;
|
|
323
|
-
position?: number;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
export function handleMove(items: TodoItem[], columns: StatusColumn[], itemId: string, input: MoveInput): ItemsActionResult {
|
|
327
|
-
const target = items.find((item) => item.id === itemId);
|
|
328
|
-
if (!target) {
|
|
329
|
-
return { kind: "error", status: 404, error: `item not found: ${itemId}` };
|
|
330
|
-
}
|
|
331
|
-
const validStatusIds = new Set(columns.map((column) => column.id));
|
|
332
|
-
const newStatus = input.status ?? target.status ?? defaultStatusId(columns);
|
|
333
|
-
if (!validStatusIds.has(newStatus)) {
|
|
334
|
-
return {
|
|
335
|
-
kind: "error",
|
|
336
|
-
status: 400,
|
|
337
|
-
error: `unknown status: ${newStatus}`,
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
const isDone = newStatus === doneColumnId(columns);
|
|
341
|
-
const updatedSelf: TodoItem = {
|
|
342
|
-
...target,
|
|
343
|
-
status: newStatus,
|
|
344
|
-
completed: isDone,
|
|
345
|
-
};
|
|
346
|
-
// Re-collect the items in the target column with the moving item
|
|
347
|
-
// pulled out, then splice item back in at `position`.
|
|
348
|
-
const others = items.filter((item) => item.id !== itemId && item.status === newStatus).sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
|
|
349
|
-
const insertAt = clampPosition(input.position, others.length);
|
|
350
|
-
const reordered = [...others];
|
|
351
|
-
reordered.splice(insertAt, 0, updatedSelf);
|
|
352
|
-
// Reassign order values 1000 / 2000 / 3000 ...
|
|
353
|
-
const reorderedById = new Map<string, number>();
|
|
354
|
-
reordered.forEach((item, i) => reorderedById.set(item.id, (i + 1) * ORDER_STEP));
|
|
355
|
-
const nextItems = items.map((item): TodoItem => {
|
|
356
|
-
const newOrder = reorderedById.get(item.id);
|
|
357
|
-
if (item.id === itemId) {
|
|
358
|
-
const out: TodoItem = {
|
|
359
|
-
...updatedSelf,
|
|
360
|
-
order: newOrder ?? updatedSelf.order ?? ORDER_STEP,
|
|
361
|
-
};
|
|
362
|
-
return out;
|
|
363
|
-
}
|
|
364
|
-
if (newOrder !== undefined) return { ...item, order: newOrder };
|
|
365
|
-
return item;
|
|
366
|
-
});
|
|
367
|
-
const finalSelf = nextItems.find((item) => item.id === itemId)!;
|
|
368
|
-
return { kind: "success", items: nextItems, item: finalSelf };
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function clampPosition(raw: number | undefined, max: number): number {
|
|
372
|
-
if (typeof raw !== "number" || !Number.isFinite(raw)) return max;
|
|
373
|
-
if (raw < 0) return 0;
|
|
374
|
-
if (raw > max) return max;
|
|
375
|
-
return Math.floor(raw);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// ── Delete ────────────────────────────────────────────────────────
|
|
379
|
-
|
|
380
|
-
export function handleDeleteItem(items: TodoItem[], itemId: string): ItemsActionResult {
|
|
381
|
-
const target = items.find((item) => item.id === itemId);
|
|
382
|
-
if (!target) {
|
|
383
|
-
return { kind: "error", status: 404, error: `item not found: ${itemId}` };
|
|
384
|
-
}
|
|
385
|
-
return { kind: "success", items: items.filter((item) => item.id !== itemId) };
|
|
386
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// Domain I/O: todo items + status columns
|
|
2
|
-
// data/todos/todos.json — items
|
|
3
|
-
// data/todos/columns.json — status columns
|
|
4
|
-
//
|
|
5
|
-
// Sync API. Optional `root` for test DI.
|
|
6
|
-
|
|
7
|
-
import { WORKSPACE_FILES } from "../../workspace/paths.js";
|
|
8
|
-
import { workspacePath } from "../../workspace/paths.js";
|
|
9
|
-
import { resolvePath } from "./workspace-io.js";
|
|
10
|
-
import { loadJsonFile } from "./json.js";
|
|
11
|
-
import { writeFileAtomicSync } from "./atomic.js";
|
|
12
|
-
|
|
13
|
-
const root = (workspaceRoot?: string) => workspaceRoot ?? workspacePath;
|
|
14
|
-
|
|
15
|
-
export function loadTodos<T>(fallback: T, workspaceRoot?: string): T {
|
|
16
|
-
return loadJsonFile(resolvePath(root(workspaceRoot), WORKSPACE_FILES.todosItems), fallback);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function saveTodos(items: unknown, workspaceRoot?: string): void {
|
|
20
|
-
writeFileAtomicSync(resolvePath(root(workspaceRoot), WORKSPACE_FILES.todosItems), JSON.stringify(items, null, 2));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function loadColumns<T>(fallback: T, workspaceRoot?: string): T {
|
|
24
|
-
return loadJsonFile(resolvePath(root(workspaceRoot), WORKSPACE_FILES.todosColumns), fallback);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function saveColumns(columns: unknown, workspaceRoot?: string): void {
|
|
28
|
-
writeFileAtomicSync(resolvePath(root(workspaceRoot), WORKSPACE_FILES.todosColumns), JSON.stringify(columns, null, 2));
|
|
29
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { ref, watch } from "vue";
|
|
3
|
-
import { useI18n } from "vue-i18n";
|
|
4
|
-
import { useNotifications } from "../composables/useNotifications";
|
|
5
|
-
|
|
6
|
-
const { t } = useI18n();
|
|
7
|
-
import { NOTIFICATION_ICONS } from "../types/notification";
|
|
8
|
-
import type { NotificationPayload } from "../types/notification";
|
|
9
|
-
import { ONE_SECOND_MS } from "../../server/utils/time";
|
|
10
|
-
import { formatSmartTime } from "../utils/format/date";
|
|
11
|
-
|
|
12
|
-
const AUTO_HIDE_MS = 5 * ONE_SECOND_MS;
|
|
13
|
-
|
|
14
|
-
const { latest } = useNotifications();
|
|
15
|
-
const visible = ref<NotificationPayload | null>(null);
|
|
16
|
-
let hideTimer: ReturnType<typeof setTimeout> | null = null;
|
|
17
|
-
|
|
18
|
-
watch(latest, (item) => {
|
|
19
|
-
if (!item) return;
|
|
20
|
-
visible.value = item;
|
|
21
|
-
if (hideTimer !== null) clearTimeout(hideTimer);
|
|
22
|
-
hideTimer = setTimeout(() => {
|
|
23
|
-
visible.value = null;
|
|
24
|
-
hideTimer = null;
|
|
25
|
-
}, AUTO_HIDE_MS);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
function dismiss(): void {
|
|
29
|
-
if (hideTimer !== null) clearTimeout(hideTimer);
|
|
30
|
-
hideTimer = null;
|
|
31
|
-
visible.value = null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function iconName(notif: NotificationPayload): string {
|
|
35
|
-
return notif.icon ?? NOTIFICATION_ICONS[notif.kind] ?? "notifications";
|
|
36
|
-
}
|
|
37
|
-
</script>
|
|
38
|
-
|
|
39
|
-
<template>
|
|
40
|
-
<Transition name="toast">
|
|
41
|
-
<div
|
|
42
|
-
v-if="visible"
|
|
43
|
-
data-testid="notification-toast"
|
|
44
|
-
class="fixed top-4 right-4 z-50 max-w-sm rounded-lg bg-slate-800 text-white shadow-lg p-4 flex items-start gap-3"
|
|
45
|
-
>
|
|
46
|
-
<span class="material-icons text-sky-300" aria-hidden="true">
|
|
47
|
-
{{ iconName(visible) }}
|
|
48
|
-
</span>
|
|
49
|
-
<div class="flex-1 min-w-0">
|
|
50
|
-
<p class="text-sm font-medium break-words">{{ visible.title }}</p>
|
|
51
|
-
<p v-if="visible.body" class="mt-0.5 text-xs text-slate-300 break-words">
|
|
52
|
-
{{ visible.body }}
|
|
53
|
-
</p>
|
|
54
|
-
<p class="mt-1 text-xs text-slate-400">
|
|
55
|
-
{{ formatSmartTime(visible.firedAt) }}
|
|
56
|
-
</p>
|
|
57
|
-
</div>
|
|
58
|
-
<button type="button" class="text-slate-400 hover:text-white" :aria-label="t('common.dismiss')" @click="dismiss">
|
|
59
|
-
<span class="material-icons text-base">close</span>
|
|
60
|
-
</button>
|
|
61
|
-
</div>
|
|
62
|
-
</Transition>
|
|
63
|
-
</template>
|
|
64
|
-
|
|
65
|
-
<style scoped>
|
|
66
|
-
.toast-enter-active,
|
|
67
|
-
.toast-leave-active {
|
|
68
|
-
transition: all 200ms ease;
|
|
69
|
-
}
|
|
70
|
-
.toast-enter-from,
|
|
71
|
-
.toast-leave-to {
|
|
72
|
-
opacity: 0;
|
|
73
|
-
transform: translateY(-8px);
|
|
74
|
-
}
|
|
75
|
-
</style>
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "gui-chat-protocol";
|
|
2
|
-
|
|
3
|
-
export const TOOL_NAME = "editImage";
|
|
4
|
-
|
|
5
|
-
export interface ImageToolData {
|
|
6
|
-
imageData: string;
|
|
7
|
-
prompt: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const toolDefinition: ToolDefinition = {
|
|
11
|
-
type: "function",
|
|
12
|
-
name: TOOL_NAME,
|
|
13
|
-
description: "Edit the previously generated image based on a text prompt.",
|
|
14
|
-
prompt: `When the user asks 'turn this image into ...', call ${TOOL_NAME} API to generate a new image.`,
|
|
15
|
-
parameters: {
|
|
16
|
-
type: "object",
|
|
17
|
-
properties: {
|
|
18
|
-
prompt: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Description of the edits to be made to the image in English",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
required: ["prompt"],
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export default toolDefinition;
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { ToolPlugin } from "../../tools/types";
|
|
2
|
-
import type { ToolResult } from "gui-chat-protocol";
|
|
3
|
-
import toolDefinition, { TOOL_NAME } from "./definition";
|
|
4
|
-
import type { ImageToolData } from "./definition";
|
|
5
|
-
import View from "./View.vue";
|
|
6
|
-
import Preview from "./Preview.vue";
|
|
7
|
-
import { apiPost } from "../../utils/api";
|
|
8
|
-
import { API_ROUTES } from "../../config/apiRoutes";
|
|
9
|
-
import { makeUuid } from "../../utils/id";
|
|
10
|
-
|
|
11
|
-
const editImagePlugin: ToolPlugin<ImageToolData> = {
|
|
12
|
-
toolDefinition,
|
|
13
|
-
|
|
14
|
-
async execute(_context, args) {
|
|
15
|
-
const result = await apiPost<ToolResult<ImageToolData>>(API_ROUTES.image.edit, args);
|
|
16
|
-
if (!result.ok) {
|
|
17
|
-
return {
|
|
18
|
-
toolName: TOOL_NAME,
|
|
19
|
-
uuid: makeUuid(),
|
|
20
|
-
message: result.error,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
return {
|
|
24
|
-
...result.data,
|
|
25
|
-
toolName: TOOL_NAME,
|
|
26
|
-
uuid: makeUuid(),
|
|
27
|
-
};
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
isEnabled: () => true,
|
|
31
|
-
generatingMessage: "Editing image...",
|
|
32
|
-
viewComponent: View,
|
|
33
|
-
previewComponent: Preview,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export default editImagePlugin;
|
|
37
|
-
export { TOOL_NAME };
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
// Pure helpers for presentHtml/Preview.vue. Replaces the former
|
|
2
|
-
// `/<[^>]*>/g` + `/\s+/g` regex pair — both flagged by
|
|
3
|
-
// `sonarjs/slow-regex` for backtracking risk — with a single
|
|
4
|
-
// linear walker that produces the exact same output.
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Produce a short plain-text preview from an HTML fragment:
|
|
8
|
-
*
|
|
9
|
-
* - every `<...>` span (a `<` with a later `>`) is replaced by a
|
|
10
|
-
* single space
|
|
11
|
-
* - a bare `<` with no matching `>` is kept as a literal character
|
|
12
|
-
* (matches the old regex, which required both brackets to match)
|
|
13
|
-
* - runs of whitespace are collapsed to one space
|
|
14
|
-
* - the result is trimmed and truncated to `maxLength` characters
|
|
15
|
-
*
|
|
16
|
-
* The walker is O(n) in the input length and does no regex
|
|
17
|
-
* backtracking.
|
|
18
|
-
*/
|
|
19
|
-
export function stripHtmlToPreview(html: string, maxLength: number): string {
|
|
20
|
-
const state: WalkerState = {
|
|
21
|
-
out: [],
|
|
22
|
-
// Start as if we just emitted a space so leading whitespace
|
|
23
|
-
// and a leading tag both get trimmed without a separate pass.
|
|
24
|
-
lastWasSpace: true,
|
|
25
|
-
};
|
|
26
|
-
let i = 0;
|
|
27
|
-
while (i < html.length) {
|
|
28
|
-
const char = html[i];
|
|
29
|
-
if (char === "<") {
|
|
30
|
-
const close = html.indexOf(">", i + 1);
|
|
31
|
-
if (close !== -1) {
|
|
32
|
-
// Real tag span `<...>` — skip it, emit a separator.
|
|
33
|
-
emitSeparator(state);
|
|
34
|
-
i = close + 1;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
// No closing `>` anywhere after — treat as literal.
|
|
38
|
-
}
|
|
39
|
-
emitChar(state, char);
|
|
40
|
-
i++;
|
|
41
|
-
}
|
|
42
|
-
trimTrailingSpace(state.out);
|
|
43
|
-
return state.out.join("").slice(0, maxLength);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface WalkerState {
|
|
47
|
-
out: string[];
|
|
48
|
-
lastWasSpace: boolean;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function emitChar(state: WalkerState, char: string): void {
|
|
52
|
-
if (isWhitespace(char)) {
|
|
53
|
-
emitSeparator(state);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
state.out.push(char);
|
|
57
|
-
state.lastWasSpace = false;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function emitSeparator(state: WalkerState): void {
|
|
61
|
-
if (state.lastWasSpace) return;
|
|
62
|
-
state.out.push(" ");
|
|
63
|
-
state.lastWasSpace = true;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function trimTrailingSpace(out: string[]): void {
|
|
67
|
-
if (out.length > 0 && out[out.length - 1] === " ") out.pop();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function isWhitespace(char: string): boolean {
|
|
71
|
-
return char === " " || char === "\t" || char === "\n" || char === "\r" || char === "\v" || char === "\f";
|
|
72
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<!-- View-only fallback for chat sessions saved before the
|
|
3
|
-
manageScheduler split (#824). The legacy tool returned two
|
|
4
|
-
distinct payload shapes — calendar items vs task records —
|
|
5
|
-
so we route on shape and mount the matching post-split view.
|
|
6
|
-
Never produces fresh tool calls (the plugin is not exposed
|
|
7
|
-
to the LLM); only renders persisted history. -->
|
|
8
|
-
<AutomationsView v-if="renderAutomations" :selected-result="selectedResult" @update-result="(result) => emit('updateResult', result)" />
|
|
9
|
-
<CalendarView v-else :selected-result="selectedResult" @update-result="(result) => emit('updateResult', result)" />
|
|
10
|
-
</template>
|
|
11
|
-
|
|
12
|
-
<script setup lang="ts">
|
|
13
|
-
import { computed } from "vue";
|
|
14
|
-
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
15
|
-
import AutomationsView from "./AutomationsView.vue";
|
|
16
|
-
import CalendarView from "./CalendarView.vue";
|
|
17
|
-
import { isLegacyAutomationsShape } from "./legacyShape";
|
|
18
|
-
import type { SchedulerData } from "./index";
|
|
19
|
-
|
|
20
|
-
const props = defineProps<{
|
|
21
|
-
selectedResult?: ToolResultComplete<SchedulerData>;
|
|
22
|
-
}>();
|
|
23
|
-
|
|
24
|
-
const emit = defineEmits<{ updateResult: [result: ToolResultComplete] }>();
|
|
25
|
-
|
|
26
|
-
// Shape-based dispatch. Errors on the side of CalendarView when
|
|
27
|
-
// the payload shape is unknown — calendar was the more common
|
|
28
|
-
// pre-split action and the view degrades gracefully on missing
|
|
29
|
-
// fields, while the automations view assumes the task shape and
|
|
30
|
-
// would render an empty Tasks tab.
|
|
31
|
-
const renderAutomations = computed(() => isLegacyAutomationsShape(props.selectedResult?.data));
|
|
32
|
-
</script>
|