mulmoclaude 0.1.2 → 0.4.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/bin/mulmoclaude.js +7 -24
- package/client/assets/html2canvas-Cx501zZr-Cv5snK9D.js +5 -0
- package/client/assets/index-CubzmCVK.css +2 -0
- package/client/assets/{index-D8rhwXLq.js → index-DtcyExH9.js} +80 -61
- package/client/assets/{index.es-D4YyL_Dg-BfRHLTZV.js → index.es-D4YyL_Dg-DnizuhIY.js} +5 -5
- package/client/index.html +2 -4
- package/package.json +13 -13
- package/server/agent/attachmentConverter.ts +2 -2
- package/server/agent/config.ts +12 -12
- package/server/agent/index.ts +9 -3
- package/server/agent/mcp-server.ts +19 -19
- package/server/agent/mcp-tools/index.ts +6 -6
- package/server/agent/mcp-tools/x.ts +7 -6
- package/server/agent/prompt.ts +195 -29
- package/server/agent/resumeFailover.ts +5 -5
- package/server/agent/sandboxMounts.ts +10 -10
- package/server/agent/stream.ts +4 -4
- package/server/api/auth/bearerAuth.ts +3 -3
- package/server/api/auth/token.ts +2 -2
- package/server/api/routes/agent.ts +21 -3
- package/server/api/routes/config.ts +1 -1
- package/server/api/routes/files.ts +22 -21
- package/server/api/routes/html.ts +2 -2
- package/server/api/routes/image.ts +7 -7
- package/server/api/routes/mulmo-script.ts +33 -31
- package/server/api/routes/pdf.ts +2 -2
- package/server/api/routes/plugins.ts +16 -6
- package/server/api/routes/roles.ts +2 -2
- package/server/api/routes/scheduler.ts +14 -12
- package/server/api/routes/schedulerHandlers.ts +12 -12
- package/server/api/routes/schedulerTasks.ts +19 -17
- package/server/api/routes/sessions.ts +26 -26
- package/server/api/routes/sessionsCursor.ts +4 -4
- package/server/api/routes/skills.ts +5 -5
- package/server/api/routes/sources.ts +3 -3
- package/server/api/routes/todosColumnsHandlers.ts +30 -30
- package/server/api/routes/todosHandlers.ts +1 -1
- package/server/api/routes/todosItemsHandlers.ts +14 -14
- package/server/api/routes/wiki.ts +36 -22
- package/server/api/sandboxStatus.ts +1 -1
- package/server/events/notifications.ts +6 -6
- package/server/events/pub-sub/index.ts +3 -3
- package/server/events/relay-client.ts +17 -16
- package/server/events/scheduler-adapter.ts +20 -20
- package/server/events/session-store/index.ts +10 -10
- package/server/events/task-manager/index.ts +7 -7
- package/server/index.ts +59 -65
- package/server/system/config.ts +5 -5
- package/server/system/credentials.ts +7 -5
- package/server/system/env.ts +5 -5
- package/server/utils/date.ts +18 -18
- package/server/utils/files/atomic.ts +16 -16
- package/server/utils/files/html-io.ts +5 -5
- package/server/utils/files/image-store.ts +19 -8
- package/server/utils/files/journal-io.ts +4 -4
- package/server/utils/files/json.ts +5 -5
- package/server/utils/files/markdown-store.ts +4 -4
- package/server/utils/files/naming.ts +2 -2
- package/server/utils/files/reference-dirs-io.ts +3 -3
- package/server/utils/files/roles-io.ts +12 -12
- package/server/utils/files/safe.ts +14 -14
- package/server/utils/files/scheduler-io.ts +5 -5
- package/server/utils/files/scheduler-overrides-io.ts +2 -2
- package/server/utils/files/session-io.ts +35 -35
- package/server/utils/files/spreadsheet-store.ts +7 -7
- package/server/utils/files/todos-io.ts +9 -9
- package/server/utils/files/user-tasks-io.ts +5 -5
- package/server/utils/files/workspace-io.ts +12 -12
- package/server/utils/gemini.ts +2 -2
- package/server/utils/gitignore.ts +9 -9
- package/server/utils/json.ts +5 -5
- package/server/utils/logBackgroundError.ts +12 -3
- package/server/utils/markdown.ts +5 -5
- package/server/utils/port.d.mts +6 -0
- package/server/utils/port.mjs +48 -0
- package/server/utils/request.ts +12 -6
- package/server/utils/spawn.ts +1 -1
- package/server/utils/types.ts +2 -2
- package/server/workspace/chat-index/indexer.ts +15 -15
- package/server/workspace/chat-index/summarizer.ts +4 -4
- package/server/workspace/custom-dirs.ts +16 -16
- package/server/workspace/journal/archivist.ts +35 -35
- package/server/workspace/journal/dailyPass.ts +31 -28
- package/server/workspace/journal/diff.ts +2 -2
- package/server/workspace/journal/index.ts +4 -4
- package/server/workspace/journal/indexFile.ts +29 -25
- package/server/workspace/journal/optimizationPass.ts +2 -2
- package/server/workspace/journal/state.ts +6 -6
- package/server/workspace/paths.ts +3 -3
- package/server/workspace/reference-dirs.ts +20 -20
- package/server/workspace/roles.ts +6 -6
- package/server/workspace/skills/discovery.ts +4 -4
- package/server/workspace/skills/parser.ts +6 -6
- package/server/workspace/skills/scheduler.ts +3 -3
- package/server/workspace/skills/user-tasks.ts +34 -34
- package/server/workspace/skills/writer.ts +3 -3
- package/server/workspace/sources/arxivDiscovery.ts +10 -10
- package/server/workspace/sources/classifier.ts +7 -7
- package/server/workspace/sources/fetchers/arxiv.ts +7 -7
- package/server/workspace/sources/fetchers/githubIssues.ts +7 -7
- package/server/workspace/sources/fetchers/githubReleases.ts +7 -7
- package/server/workspace/sources/fetchers/rss.ts +5 -5
- package/server/workspace/sources/fetchers/rssParser.ts +4 -4
- package/server/workspace/sources/interests.ts +12 -12
- package/server/workspace/sources/paths.ts +6 -6
- package/server/workspace/sources/pipeline/fetch.ts +36 -13
- package/server/workspace/sources/pipeline/index.ts +8 -13
- package/server/workspace/sources/pipeline/notify.ts +3 -3
- package/server/workspace/sources/pipeline/plan.ts +15 -13
- package/server/workspace/sources/pipeline/write.ts +5 -5
- package/server/workspace/sources/rateLimiter.ts +1 -1
- package/server/workspace/sources/registry.ts +16 -16
- package/server/workspace/sources/robots.ts +14 -14
- package/server/workspace/sources/sourceState.ts +17 -10
- package/server/workspace/sources/types.ts +9 -0
- package/server/workspace/sources/urls.ts +1 -1
- package/server/workspace/tool-trace/classify.ts +4 -4
- package/server/workspace/tool-trace/index.ts +1 -1
- package/server/workspace/tool-trace/writeSearch.ts +26 -16
- package/server/workspace/wiki-backlinks/index.ts +8 -8
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +15 -15
- package/server/workspace/workspace.ts +7 -7
- package/src/App.vue +315 -141
- package/src/components/CanvasViewToggle.vue +10 -7
- package/src/components/ChatInput.vue +67 -33
- package/src/components/FileContentHeader.vue +7 -4
- package/src/components/FileContentRenderer.vue +20 -6
- package/src/components/FileTree.vue +6 -3
- package/src/components/FileTreePane.vue +11 -8
- package/src/components/FilesView.vue +5 -3
- package/src/components/LockStatusPopup.vue +17 -14
- package/src/components/NotificationBell.vue +14 -5
- package/src/components/NotificationToast.vue +6 -3
- package/src/components/PluginLauncher.vue +19 -56
- package/src/components/RightSidebar.vue +13 -10
- package/src/components/RoleSelector.vue +2 -2
- package/src/components/SessionHistoryPanel.vue +38 -34
- package/src/components/SessionTabBar.vue +8 -10
- package/src/components/SettingsMcpTab.vue +49 -36
- package/src/components/SettingsModal.vue +24 -22
- package/src/components/SettingsReferenceDirsTab.vue +39 -34
- package/src/components/SettingsWorkspaceDirsTab.vue +37 -27
- package/src/components/SidebarHeader.vue +25 -4
- package/src/components/StackView.vue +4 -1
- package/src/components/SuggestionsPanel.vue +7 -4
- package/src/components/TodoExplorer.vue +26 -15
- package/src/components/ToolResultsPanel.vue +27 -13
- package/src/components/todo/TodoAddDialog.vue +19 -14
- package/src/components/todo/TodoEditDialog.vue +7 -2
- package/src/components/todo/TodoEditPanel.vue +17 -12
- package/src/components/todo/TodoKanbanView.vue +10 -5
- package/src/components/todo/TodoListView.vue +10 -7
- package/src/components/todo/TodoTableView.vue +5 -2
- package/src/composables/useAppApi.ts +9 -0
- package/src/composables/useClickOutside.ts +2 -2
- package/src/composables/useDynamicFavicon.ts +172 -37
- package/src/composables/useEventListeners.ts +7 -8
- package/src/composables/useFaviconState.ts +13 -2
- package/src/composables/useFileSelection.ts +24 -6
- package/src/composables/useFreshPluginData.ts +3 -3
- package/src/composables/useKeyNavigation.ts +11 -11
- package/src/composables/useLayoutMode.ts +32 -0
- package/src/composables/useMcpTools.ts +2 -2
- package/src/composables/useNotifications.ts +3 -3
- package/src/composables/usePdfDownload.ts +4 -4
- package/src/composables/usePendingCalls.ts +1 -1
- package/src/composables/usePubSub.ts +10 -10
- package/src/composables/useRoles.ts +1 -1
- package/src/composables/useSandboxStatus.ts +1 -1
- package/src/composables/useSessionDerived.ts +3 -3
- package/src/composables/useSessionHistory.ts +7 -17
- package/src/composables/useSessionSync.ts +8 -8
- package/src/composables/useViewLayout.ts +20 -34
- package/src/config/roles.ts +2 -2
- package/src/lang/de.ts +536 -0
- package/src/lang/en.ts +558 -0
- package/src/lang/es.ts +543 -0
- package/src/lang/fr.ts +536 -0
- package/src/lang/ja.ts +536 -0
- package/src/lang/ko.ts +540 -0
- package/src/lang/pt-BR.ts +534 -0
- package/src/lang/zh.ts +537 -0
- package/src/lib/vue-i18n.ts +97 -0
- package/src/main.ts +2 -0
- package/src/plugins/canvas/View.vue +102 -186
- package/src/plugins/canvas/definition.ts +0 -8
- package/src/plugins/chart/Preview.vue +5 -5
- package/src/plugins/chart/View.vue +9 -4
- package/src/plugins/manageRoles/Preview.vue +4 -1
- package/src/plugins/manageRoles/View.vue +59 -43
- package/src/plugins/manageSkills/Preview.vue +8 -3
- package/src/plugins/manageSkills/View.vue +29 -25
- package/src/plugins/manageSource/Preview.vue +2 -2
- package/src/plugins/manageSource/View.vue +73 -52
- package/src/plugins/markdown/Preview.vue +1 -1
- package/src/plugins/markdown/View.vue +26 -36
- package/src/plugins/presentHtml/Preview.vue +1 -1
- package/src/plugins/presentHtml/View.vue +7 -4
- package/src/plugins/presentHtml/helpers.ts +8 -8
- package/src/plugins/presentMulmoScript/Preview.vue +1 -1
- package/src/plugins/presentMulmoScript/View.vue +40 -30
- package/src/plugins/presentMulmoScript/helpers.ts +1 -1
- package/src/plugins/scheduler/Preview.vue +13 -10
- package/src/plugins/scheduler/TasksTab.vue +57 -28
- package/src/plugins/scheduler/View.vue +28 -19
- package/src/plugins/scheduler/formatSchedule.ts +93 -0
- package/src/plugins/spreadsheet/Preview.vue +8 -3
- package/src/plugins/spreadsheet/View.vue +21 -12
- package/src/plugins/textResponse/Preview.vue +15 -58
- package/src/plugins/textResponse/View.vue +29 -9
- package/src/plugins/todo/Preview.vue +13 -8
- package/src/plugins/todo/View.vue +38 -24
- package/src/plugins/todo/composables/useTodos.ts +5 -5
- package/src/plugins/ui-image/ImagePreview.vue +6 -3
- package/src/plugins/ui-image/ImageView.vue +7 -4
- package/src/plugins/wiki/Preview.vue +10 -7
- package/src/plugins/wiki/View.vue +202 -81
- package/src/plugins/wiki/helpers.ts +4 -4
- package/src/plugins/wiki/route.ts +112 -0
- package/src/router/guards.ts +46 -28
- package/src/router/index.ts +41 -26
- package/src/types/session.ts +4 -3
- package/src/types/vue-i18n.d.ts +20 -0
- package/src/utils/agent/request.ts +22 -3
- package/src/utils/canvas/layoutMode.ts +26 -0
- package/src/utils/dom/scrollable.ts +2 -2
- package/src/utils/files/expandedDirs.ts +1 -1
- package/src/utils/files/sortChildren.ts +6 -6
- package/src/utils/format/frontmatter.ts +6 -6
- package/src/utils/image/cacheBust.ts +16 -0
- package/src/utils/image/resolve.ts +16 -0
- package/src/utils/image/rewriteMarkdownImageRefs.ts +5 -5
- package/src/utils/markdown/extractFirstH1.ts +2 -2
- package/src/utils/path/relativeLink.ts +15 -15
- package/src/utils/path/workspaceLinkRouter.ts +81 -0
- package/src/utils/role/icon.ts +2 -2
- package/src/utils/role/merge.ts +2 -2
- package/src/utils/role/plugins.ts +1 -1
- package/src/utils/session/sessionFactory.ts +2 -2
- package/src/utils/session/sessionHelpers.ts +2 -2
- package/src/utils/tools/dedup.ts +4 -4
- package/src/utils/tools/result.ts +3 -3
- package/src/utils/types.ts +2 -2
- package/src/vite-env.d.ts +9 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +0 -1
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +0 -5
- package/client/assets/index-KNLBjwuh.css +0 -1
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +0 -1
- package/src/composables/useCanvasViewMode.ts +0 -121
- package/src/utils/canvas/viewMode.ts +0 -46
- /package/client/assets/{purify.es-Fx1Nqyry-PeS5RUhs.js → purify.es-Fx1Nqyry-BwJECkqS.js} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full flex flex-col overflow-hidden">
|
|
3
3
|
<div class="px-4 py-2 border-b border-gray-100 shrink-0 flex items-center justify-between gap-2">
|
|
4
|
-
<span class="text-sm font-medium text-gray-700 truncate">
|
|
4
|
+
<span class="text-sm font-medium text-gray-700 truncate"> {{ t("pluginManageSource.heading") }} </span>
|
|
5
5
|
<div class="flex items-center gap-2 shrink-0">
|
|
6
|
-
<span class="text-xs text-gray-500"> {{ sources.length
|
|
6
|
+
<span class="text-xs text-gray-500"> {{ t("pluginManageSource.sourceCount", sources.length, { named: { count: sources.length } }) }} </span>
|
|
7
7
|
<button
|
|
8
8
|
class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-600 hover:bg-gray-50 disabled:opacity-50"
|
|
9
9
|
:disabled="adding || busy === 'rebuild'"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
@click="startAdd"
|
|
12
12
|
>
|
|
13
13
|
<span class="material-icons text-sm align-middle">add</span>
|
|
14
|
-
|
|
14
|
+
{{ t("pluginManageSource.addButton") }}
|
|
15
15
|
</button>
|
|
16
16
|
<button
|
|
17
17
|
class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-600 hover:bg-gray-50 disabled:opacity-50"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
@click="rebuild"
|
|
21
21
|
>
|
|
22
22
|
<span class="material-icons text-sm align-middle">refresh</span>
|
|
23
|
-
{{ busy === "rebuild" ? "
|
|
23
|
+
{{ busy === "rebuild" ? t("pluginManageSource.rebuilding") : t("pluginManageSource.rebuildNow") }}
|
|
24
24
|
</button>
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
<div v-if="adding" class="px-4 py-3 border-b border-blue-200 bg-blue-50/50 shrink-0 space-y-2" data-testid="sources-add-form">
|
|
29
29
|
<div class="flex flex-wrap items-center gap-2">
|
|
30
30
|
<label class="text-xs text-gray-700">
|
|
31
|
-
|
|
31
|
+
{{ t("pluginManageSource.typeField") }}
|
|
32
32
|
<select v-model="draft.kind" class="ml-1 text-xs border border-gray-300 rounded px-1 py-0.5" data-testid="sources-draft-kind" @change="onKindChange">
|
|
33
|
-
<option value="rss">
|
|
34
|
-
<option value="github-releases">
|
|
35
|
-
<option value="github-issues">
|
|
36
|
-
<option value="arxiv">
|
|
33
|
+
<option value="rss">{{ t("pluginManageSource.kindRss") }}</option>
|
|
34
|
+
<option value="github-releases">{{ t("pluginManageSource.kindGithubReleases") }}</option>
|
|
35
|
+
<option value="github-issues">{{ t("pluginManageSource.kindGithubIssues") }}</option>
|
|
36
|
+
<option value="arxiv">{{ t("pluginManageSource.kindArxiv") }}</option>
|
|
37
37
|
</select>
|
|
38
38
|
</label>
|
|
39
39
|
<input
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
<input
|
|
47
47
|
v-model="draft.title"
|
|
48
48
|
class="w-40 text-xs border border-gray-300 rounded px-2 py-1"
|
|
49
|
-
placeholder="
|
|
49
|
+
:placeholder="t('pluginManageSource.titlePlaceholder')"
|
|
50
50
|
data-testid="sources-draft-title"
|
|
51
51
|
@keydown.enter="commitAdd"
|
|
52
52
|
/>
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
</span>
|
|
58
58
|
<div class="flex gap-2">
|
|
59
59
|
<button class="px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-50" data-testid="sources-draft-cancel" @click="cancelAdd">
|
|
60
|
-
|
|
60
|
+
{{ t("common.cancel") }}
|
|
61
61
|
</button>
|
|
62
62
|
<button
|
|
63
63
|
class="px-2 py-1 rounded bg-blue-500 text-white hover:bg-blue-600 disabled:opacity-50"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
data-testid="sources-draft-add"
|
|
66
66
|
@click="commitAdd"
|
|
67
67
|
>
|
|
68
|
-
{{ busy === "add" ? "
|
|
68
|
+
{{ busy === "add" ? t("pluginManageSource.addingLabel") : t("pluginManageSource.addAndRebuild") }}
|
|
69
69
|
</button>
|
|
70
70
|
</div>
|
|
71
71
|
</div>
|
|
@@ -85,10 +85,11 @@
|
|
|
85
85
|
|
|
86
86
|
<div class="flex-1 overflow-y-auto">
|
|
87
87
|
<div v-if="sources.length === 0" class="flex flex-col items-center justify-center h-full p-6 gap-4" data-testid="sources-empty">
|
|
88
|
-
<p class="text-sm text-gray-500 italic text-center max-w-md">
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
<i18n-t keypath="pluginManageSource.emptyPickPack" tag="p" class="text-sm text-gray-500 italic text-center max-w-md">
|
|
89
|
+
<template #addBold>
|
|
90
|
+
<strong>{{ t("pluginManageSource.emptyAddStrong") }}</strong>
|
|
91
|
+
</template>
|
|
92
|
+
</i18n-t>
|
|
92
93
|
<div class="w-full max-w-md space-y-2" data-testid="sources-presets">
|
|
93
94
|
<button
|
|
94
95
|
v-for="preset in PRESETS"
|
|
@@ -102,12 +103,14 @@
|
|
|
102
103
|
<span class="text-sm font-medium text-gray-800">
|
|
103
104
|
{{ preset.label }}
|
|
104
105
|
</span>
|
|
105
|
-
<span class="text-[11px] text-gray-500 shrink-0">
|
|
106
|
+
<span class="text-[11px] text-gray-500 shrink-0">
|
|
107
|
+
{{ t("pluginManageSource.sourceCount", preset.entries.length, { named: { count: preset.entries.length } }) }}
|
|
108
|
+
</span>
|
|
106
109
|
</div>
|
|
107
110
|
<div class="text-xs text-gray-500 mt-1">
|
|
108
111
|
{{ preset.description }}
|
|
109
112
|
</div>
|
|
110
|
-
<div v-if="busy === 'preset-' + preset.id" class="text-xs text-blue-600 mt-1 italic">
|
|
113
|
+
<div v-if="busy === 'preset-' + preset.id" class="text-xs text-blue-600 mt-1 italic">{{ t("pluginManageSource.registering") }}</div>
|
|
111
114
|
</button>
|
|
112
115
|
</div>
|
|
113
116
|
</div>
|
|
@@ -151,7 +154,7 @@
|
|
|
151
154
|
:data-testid="`source-remove-${source.slug}`"
|
|
152
155
|
@click="remove(source.slug)"
|
|
153
156
|
>
|
|
154
|
-
{{ busy === source.slug ? "
|
|
157
|
+
{{ busy === source.slug ? t("pluginManageSource.removingLabel") : t("pluginManageSource.removeLabel") }}
|
|
155
158
|
</button>
|
|
156
159
|
</li>
|
|
157
160
|
</ul>
|
|
@@ -162,14 +165,14 @@
|
|
|
162
165
|
<div v-if="sources.length > 0 && (briefLoading || briefHtml || briefError)" class="p-4" data-testid="sources-brief">
|
|
163
166
|
<div class="flex items-baseline justify-between mb-2">
|
|
164
167
|
<h3 class="text-sm font-semibold text-gray-800">
|
|
165
|
-
|
|
166
|
-
<span v-if="briefDate" class="text-xs text-gray-400 font-normal">
|
|
168
|
+
{{ t("pluginManageSource.todaysBrief") }}
|
|
169
|
+
<span v-if="briefDate" class="text-xs text-gray-400 font-normal"> {{ t("pluginManageSource.briefDateLabel", { date: briefDate }) }} </span>
|
|
167
170
|
</h3>
|
|
168
171
|
<button v-if="briefFilePath" class="text-[11px] text-gray-500 hover:text-gray-700" :title="briefFilePath">
|
|
169
172
|
{{ briefFilePath }}
|
|
170
173
|
</button>
|
|
171
174
|
</div>
|
|
172
|
-
<div v-if="briefLoading" class="text-xs text-gray-500 italic">
|
|
175
|
+
<div v-if="briefLoading" class="text-xs text-gray-500 italic">{{ t("pluginManageSource.todaysBriefLoading") }}</div>
|
|
173
176
|
<div v-else-if="briefError" class="text-xs text-gray-500 italic" data-testid="sources-brief-empty">
|
|
174
177
|
{{ briefError }}
|
|
175
178
|
</div>
|
|
@@ -179,15 +182,24 @@
|
|
|
179
182
|
</div>
|
|
180
183
|
|
|
181
184
|
<div v-if="lastRebuild" class="px-4 py-2 border-t border-gray-100 shrink-0 text-xs text-gray-600" data-testid="sources-rebuild-summary">
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
{{
|
|
186
|
+
t("pluginManageSource.lastRebuildSummary", {
|
|
187
|
+
date: lastRebuild.isoDate,
|
|
188
|
+
itemCount: lastRebuild.itemCount,
|
|
189
|
+
planned: lastRebuild.plannedCount,
|
|
190
|
+
duplicates: lastRebuild.duplicateCount,
|
|
191
|
+
})
|
|
192
|
+
}}
|
|
193
|
+
<span v-if="lastRebuild.archiveErrors.length > 0" class="text-red-600">
|
|
194
|
+
{{ t("pluginManageSource.archiveErrorsSuffix", { count: lastRebuild.archiveErrors.length }) }}
|
|
195
|
+
</span>
|
|
185
196
|
</div>
|
|
186
197
|
</div>
|
|
187
198
|
</template>
|
|
188
199
|
|
|
189
200
|
<script setup lang="ts">
|
|
190
201
|
import { computed, onMounted, ref, watch } from "vue";
|
|
202
|
+
import { useI18n } from "vue-i18n";
|
|
191
203
|
import { marked } from "marked";
|
|
192
204
|
import DOMPurify from "dompurify";
|
|
193
205
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
@@ -195,6 +207,8 @@ import type { ManageSourceData, RebuildSummary, Source } from "./index";
|
|
|
195
207
|
import { apiGet, apiPost, apiDelete } from "../../utils/api";
|
|
196
208
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
197
209
|
|
|
210
|
+
const { t } = useI18n();
|
|
211
|
+
|
|
198
212
|
const props = defineProps<{
|
|
199
213
|
selectedResult: ToolResultComplete<ManageSourceData>;
|
|
200
214
|
}>();
|
|
@@ -245,12 +259,12 @@ function onKindChange(): void {
|
|
|
245
259
|
const primaryPlaceholder = computed(() => {
|
|
246
260
|
switch (draft.value.kind) {
|
|
247
261
|
case "rss":
|
|
248
|
-
return "
|
|
262
|
+
return t("pluginManageSource.primaryRssPlaceholder");
|
|
249
263
|
case "github-releases":
|
|
250
264
|
case "github-issues":
|
|
251
|
-
return "
|
|
265
|
+
return t("pluginManageSource.primaryGithubPlaceholder");
|
|
252
266
|
case "arxiv":
|
|
253
|
-
return "
|
|
267
|
+
return t("pluginManageSource.primaryArxivPlaceholder");
|
|
254
268
|
}
|
|
255
269
|
return "";
|
|
256
270
|
});
|
|
@@ -258,13 +272,13 @@ const primaryPlaceholder = computed(() => {
|
|
|
258
272
|
const primaryHint = computed(() => {
|
|
259
273
|
switch (draft.value.kind) {
|
|
260
274
|
case "rss":
|
|
261
|
-
return "
|
|
275
|
+
return t("pluginManageSource.primaryRssHint");
|
|
262
276
|
case "github-releases":
|
|
263
|
-
return "
|
|
277
|
+
return t("pluginManageSource.primaryGithubRelHint");
|
|
264
278
|
case "github-issues":
|
|
265
|
-
return "
|
|
279
|
+
return t("pluginManageSource.primaryGithubIssHint");
|
|
266
280
|
case "arxiv":
|
|
267
|
-
return "
|
|
281
|
+
return t("pluginManageSource.primaryArxivHint");
|
|
268
282
|
}
|
|
269
283
|
return "";
|
|
270
284
|
});
|
|
@@ -351,11 +365,11 @@ async function commitAdd(): Promise<void> {
|
|
|
351
365
|
busy.value = "add";
|
|
352
366
|
const response = await apiPost<unknown>(API_ROUTES.sources.create, payload);
|
|
353
367
|
if (!response.ok) {
|
|
354
|
-
draftError.value = response.error || "
|
|
368
|
+
draftError.value = response.error || t("pluginManageSource.flashRegisterFailed");
|
|
355
369
|
busy.value = null;
|
|
356
370
|
return;
|
|
357
371
|
}
|
|
358
|
-
flash(
|
|
372
|
+
flash(t("pluginManageSource.flashRegistered"));
|
|
359
373
|
adding.value = false;
|
|
360
374
|
await refreshList();
|
|
361
375
|
// C: auto-rebuild so the user sees items without an extra click.
|
|
@@ -443,7 +457,7 @@ async function installPreset(preset: Preset): Promise<void> {
|
|
|
443
457
|
const alreadyHave = new Set(sources.value.map((source) => source.slug));
|
|
444
458
|
const toRegister = preset.entries.filter((entry) => !alreadyHave.has(entry.slug));
|
|
445
459
|
if (toRegister.length === 0) {
|
|
446
|
-
flash(
|
|
460
|
+
flash(t("pluginManageSource.flashPresetAlreadyRegistered", { label: preset.label }));
|
|
447
461
|
busy.value = null;
|
|
448
462
|
return;
|
|
449
463
|
}
|
|
@@ -465,9 +479,16 @@ async function installPreset(preset: Preset): Promise<void> {
|
|
|
465
479
|
}
|
|
466
480
|
}
|
|
467
481
|
if (failures.length > 0) {
|
|
468
|
-
flash(
|
|
482
|
+
flash(
|
|
483
|
+
t("pluginManageSource.flashPresetPartial", {
|
|
484
|
+
ok: toRegister.length - failures.length,
|
|
485
|
+
total: toRegister.length,
|
|
486
|
+
errors: failures.join("; "),
|
|
487
|
+
}),
|
|
488
|
+
true,
|
|
489
|
+
);
|
|
469
490
|
} else {
|
|
470
|
-
flash(
|
|
491
|
+
flash(t("pluginManageSource.flashPresetRegistered", toRegister.length, { named: { count: toRegister.length, label: preset.label } }));
|
|
471
492
|
}
|
|
472
493
|
await refreshList();
|
|
473
494
|
await rebuildInline();
|
|
@@ -479,12 +500,12 @@ async function installPreset(preset: Preset): Promise<void> {
|
|
|
479
500
|
async function rebuildInline(): Promise<void> {
|
|
480
501
|
const response = await apiPost<RebuildSummary>(API_ROUTES.sources.rebuild);
|
|
481
502
|
if (!response.ok) {
|
|
482
|
-
flash(
|
|
503
|
+
flash(t("pluginManageSource.flashRegisterSucceededRebuildFailed", { error: response.error }), true);
|
|
483
504
|
return;
|
|
484
505
|
}
|
|
485
506
|
const summary = response.data;
|
|
486
507
|
lastRebuild.value = summary;
|
|
487
|
-
flash(
|
|
508
|
+
flash(t("pluginManageSource.flashRebuildReady", summary.plannedCount, { named: { itemCount: summary.itemCount, planned: summary.plannedCount } }));
|
|
488
509
|
await loadBrief(summary.isoDate);
|
|
489
510
|
}
|
|
490
511
|
|
|
@@ -525,13 +546,13 @@ watch(
|
|
|
525
546
|
function kindLabel(kind: Source["fetcherKind"]): string {
|
|
526
547
|
switch (kind) {
|
|
527
548
|
case "rss":
|
|
528
|
-
return "
|
|
549
|
+
return t("pluginManageSource.kindRss");
|
|
529
550
|
case "github-releases":
|
|
530
|
-
return "
|
|
551
|
+
return t("pluginManageSource.kindGithubRel");
|
|
531
552
|
case "github-issues":
|
|
532
|
-
return "
|
|
553
|
+
return t("pluginManageSource.kindGithubIss");
|
|
533
554
|
case "arxiv":
|
|
534
|
-
return "
|
|
555
|
+
return t("pluginManageSource.kindArxiv");
|
|
535
556
|
}
|
|
536
557
|
}
|
|
537
558
|
|
|
@@ -559,22 +580,22 @@ function flash(message: string, isError = false): void {
|
|
|
559
580
|
async function refreshList(): Promise<void> {
|
|
560
581
|
const response = await apiGet<{ sources: Source[] }>(API_ROUTES.sources.list);
|
|
561
582
|
if (!response.ok) {
|
|
562
|
-
flash(
|
|
583
|
+
flash(t("pluginManageSource.flashRefreshListFailed", { error: response.error }), true);
|
|
563
584
|
return;
|
|
564
585
|
}
|
|
565
586
|
localSources.value = response.data.sources;
|
|
566
587
|
}
|
|
567
588
|
|
|
568
589
|
async function remove(slug: string): Promise<void> {
|
|
569
|
-
if (!confirm(
|
|
590
|
+
if (!confirm(t("pluginManageSource.confirmRemove", { slug }))) return;
|
|
570
591
|
busy.value = slug;
|
|
571
592
|
const response = await apiDelete<unknown>(API_ROUTES.sources.remove.replace(":slug", encodeURIComponent(slug)));
|
|
572
593
|
busy.value = null;
|
|
573
594
|
if (!response.ok) {
|
|
574
|
-
flash(
|
|
595
|
+
flash(t("pluginManageSource.flashRemoveFailed", { error: response.error }), true);
|
|
575
596
|
return;
|
|
576
597
|
}
|
|
577
|
-
flash(
|
|
598
|
+
flash(t("pluginManageSource.flashRemoved", { slug }));
|
|
578
599
|
await refreshList();
|
|
579
600
|
}
|
|
580
601
|
|
|
@@ -582,13 +603,13 @@ async function rebuild(): Promise<void> {
|
|
|
582
603
|
busy.value = "rebuild";
|
|
583
604
|
const response = await apiPost<RebuildSummary>(API_ROUTES.sources.rebuild);
|
|
584
605
|
if (!response.ok) {
|
|
585
|
-
flash(
|
|
606
|
+
flash(t("pluginManageSource.flashRebuildFailed", { error: response.error }), true);
|
|
586
607
|
busy.value = null;
|
|
587
608
|
return;
|
|
588
609
|
}
|
|
589
610
|
const summary = response.data;
|
|
590
611
|
lastRebuild.value = summary;
|
|
591
|
-
flash(
|
|
612
|
+
flash(t("pluginManageSource.flashRebuildComplete", { itemCount: summary.itemCount, planned: summary.plannedCount }));
|
|
592
613
|
await Promise.all([refreshList(), loadBrief(summary.isoDate)]);
|
|
593
614
|
busy.value = null;
|
|
594
615
|
}
|
|
@@ -638,16 +659,16 @@ async function loadBrief(isoDate: string): Promise<void> {
|
|
|
638
659
|
if (!response.ok) {
|
|
639
660
|
if (response.status === 404) {
|
|
640
661
|
briefMarkdown.value = "";
|
|
641
|
-
briefError.value = "
|
|
662
|
+
briefError.value = t("pluginManageSource.briefNone");
|
|
642
663
|
} else {
|
|
643
|
-
briefError.value = response.error || "
|
|
664
|
+
briefError.value = response.error || t("pluginManageSource.briefLoadFailed");
|
|
644
665
|
}
|
|
645
666
|
briefLoading.value = false;
|
|
646
667
|
return;
|
|
647
668
|
}
|
|
648
669
|
briefMarkdown.value = response.data.content ?? "";
|
|
649
670
|
if (!briefMarkdown.value.trim()) {
|
|
650
|
-
briefError.value = "
|
|
671
|
+
briefError.value = t("pluginManageSource.briefEmpty");
|
|
651
672
|
}
|
|
652
673
|
briefLoading.value = false;
|
|
653
674
|
}
|
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="markdown-container">
|
|
3
3
|
<div v-if="loading" class="min-h-full p-8 flex items-center justify-center">
|
|
4
|
-
<div class="text-gray-500">
|
|
4
|
+
<div class="text-gray-500">{{ t("pluginMarkdown.loading") }}</div>
|
|
5
5
|
</div>
|
|
6
6
|
<div v-else-if="loadError && !markdownContent" class="min-h-full p-8 flex items-center justify-center">
|
|
7
|
-
<div class="load-error-banner" role="alert"
|
|
7
|
+
<div class="load-error-banner" role="alert">{{ t("pluginMarkdown.loadFailed", { error: loadError }) }}</div>
|
|
8
8
|
</div>
|
|
9
9
|
<div v-else-if="!markdownContent" class="min-h-full p-8 flex items-center justify-center">
|
|
10
|
-
<div class="text-gray-500">
|
|
10
|
+
<div class="text-gray-500">{{ t("pluginMarkdown.noContent") }}</div>
|
|
11
11
|
</div>
|
|
12
12
|
<template v-else>
|
|
13
|
+
<div class="flex justify-end px-4 py-2 border-b border-gray-100 shrink-0">
|
|
14
|
+
<div class="button-group">
|
|
15
|
+
<button class="download-btn download-btn-green" :disabled="pdfDownloading" @click="downloadPdf">
|
|
16
|
+
<span class="material-icons">{{ pdfDownloading ? "hourglass_empty" : "download" }}</span>
|
|
17
|
+
{{ t("pluginMarkdown.pdf") }}
|
|
18
|
+
</button>
|
|
19
|
+
</div>
|
|
20
|
+
<span v-if="pdfError" class="text-xs text-red-500 self-center ml-2" :title="pdfError">{{ t("pluginMarkdown.pdfFailedShort") }}</span>
|
|
21
|
+
</div>
|
|
13
22
|
<div v-if="loadError" class="load-error-banner" role="alert">
|
|
14
|
-
|
|
23
|
+
{{ t("pluginMarkdown.refreshFailed", { error: loadError }) }}
|
|
15
24
|
</div>
|
|
16
25
|
<div class="markdown-content-wrapper">
|
|
17
26
|
<div class="p-4">
|
|
18
|
-
<div class="header-row">
|
|
19
|
-
<h1 class="document-title">
|
|
20
|
-
{{ selectedResult.title || "Document" }}
|
|
21
|
-
</h1>
|
|
22
|
-
<div class="button-group">
|
|
23
|
-
<button class="download-btn download-btn-green" :disabled="pdfDownloading" @click="downloadPdf">
|
|
24
|
-
<span class="material-icons">{{ pdfDownloading ? "hourglass_empty" : "download" }}</span>
|
|
25
|
-
PDF
|
|
26
|
-
</button>
|
|
27
|
-
</div>
|
|
28
|
-
<span v-if="pdfError" class="text-xs text-red-500 self-center ml-2" :title="pdfError">⚠ PDF failed</span>
|
|
29
|
-
</div>
|
|
30
27
|
<div class="markdown-content prose prose-slate max-w-none" v-html="renderedHtml"></div>
|
|
31
28
|
</div>
|
|
32
29
|
</div>
|
|
33
30
|
|
|
34
31
|
<div class="bottom-bar-wrapper">
|
|
35
32
|
<details ref="sourceDetails" class="markdown-source" @toggle="onDetailsToggle">
|
|
36
|
-
<summary>
|
|
33
|
+
<summary>{{ t("pluginMarkdown.editSource") }}</summary>
|
|
37
34
|
<textarea v-model="editableMarkdown" class="markdown-editor" spellcheck="false"></textarea>
|
|
38
35
|
<div class="editor-actions">
|
|
39
36
|
<button class="apply-btn" :disabled="!hasChanges || saving" @click="applyMarkdown">
|
|
40
|
-
{{ saving ? "
|
|
37
|
+
{{ saving ? t("pluginMarkdown.saving") : t("pluginMarkdown.applyChanges") }}
|
|
41
38
|
</button>
|
|
42
|
-
<button class="cancel-btn" @click="cancelEdit">
|
|
39
|
+
<button class="cancel-btn" @click="cancelEdit">{{ t("pluginMarkdown.cancel") }}</button>
|
|
43
40
|
</div>
|
|
44
|
-
<p v-if="saveError" class="save-error" role="alert"
|
|
41
|
+
<p v-if="saveError" class="save-error" role="alert">{{ t("pluginMarkdown.saveError", { error: saveError }) }}</p>
|
|
45
42
|
</details>
|
|
46
|
-
<button v-show="!editing" class="copy-btn" :title="copied ? '
|
|
43
|
+
<button v-show="!editing" class="copy-btn" :title="copied ? t('pluginMarkdown.copiedLabel') : t('pluginMarkdown.copyLabel')" @click="copyText">
|
|
47
44
|
<span class="material-icons">{{ copied ? "check" : "content_copy" }}</span>
|
|
48
45
|
</button>
|
|
49
46
|
</div>
|
|
@@ -53,7 +50,10 @@
|
|
|
53
50
|
|
|
54
51
|
<script setup lang="ts">
|
|
55
52
|
import { computed, ref, watch, nextTick } from "vue";
|
|
53
|
+
import { useI18n } from "vue-i18n";
|
|
56
54
|
import { marked } from "marked";
|
|
55
|
+
|
|
56
|
+
const { t } = useI18n();
|
|
57
57
|
import type { ToolResult } from "gui-chat-protocol";
|
|
58
58
|
import { isFilePath, type MarkdownToolData } from "./definition";
|
|
59
59
|
import { rewriteMarkdownImageRefs } from "../../utils/image/rewriteMarkdownImageRefs";
|
|
@@ -156,8 +156,8 @@ const sourceDetails = ref<HTMLDetailsElement>();
|
|
|
156
156
|
const editing = ref(false);
|
|
157
157
|
const { copied, copy } = useClipboardCopy();
|
|
158
158
|
|
|
159
|
-
function onDetailsToggle(
|
|
160
|
-
const open = (
|
|
159
|
+
function onDetailsToggle(event: Event) {
|
|
160
|
+
const open = (event.target as HTMLDetailsElement).open;
|
|
161
161
|
editing.value = open;
|
|
162
162
|
if (!open) {
|
|
163
163
|
editableMarkdown.value = markdownContent.value;
|
|
@@ -198,7 +198,9 @@ async function applyMarkdown() {
|
|
|
198
198
|
});
|
|
199
199
|
saving.value = false;
|
|
200
200
|
if (!result.ok) {
|
|
201
|
-
|
|
201
|
+
// Store the raw error; the template formats it via t() so locale
|
|
202
|
+
// switches re-render without double-translating.
|
|
203
|
+
saveError.value = result.error;
|
|
202
204
|
return;
|
|
203
205
|
}
|
|
204
206
|
}
|
|
@@ -245,18 +247,6 @@ watch(
|
|
|
245
247
|
min-height: 0;
|
|
246
248
|
}
|
|
247
249
|
|
|
248
|
-
.header-row {
|
|
249
|
-
display: flex;
|
|
250
|
-
align-items: center;
|
|
251
|
-
justify-content: space-between;
|
|
252
|
-
margin-bottom: 1em;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.document-title {
|
|
256
|
-
font-size: 2em;
|
|
257
|
-
margin: 0;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
250
|
.button-group {
|
|
261
251
|
display: flex;
|
|
262
252
|
gap: 0.5em;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full flex flex-col overflow-hidden">
|
|
3
3
|
<div class="px-4 py-2 border-b border-gray-100 shrink-0 flex items-center justify-between">
|
|
4
|
-
<span class="text-sm font-medium text-gray-700 truncate">{{ title ?? "
|
|
4
|
+
<span class="text-sm font-medium text-gray-700 truncate">{{ title ?? t("pluginPresentHtml.untitled") }}</span>
|
|
5
5
|
<div class="flex items-center gap-2">
|
|
6
6
|
<button
|
|
7
7
|
class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-500 hover:bg-gray-50 shrink-0"
|
|
8
|
-
title="
|
|
8
|
+
:title="t('pluginPresentHtml.saveAsPdf')"
|
|
9
9
|
@click="printToPdf"
|
|
10
10
|
>
|
|
11
11
|
<span class="material-icons text-sm align-middle">picture_as_pdf</span>
|
|
12
|
-
|
|
12
|
+
{{ t("pluginPresentHtml.pdf") }}
|
|
13
13
|
</button>
|
|
14
14
|
<button class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-500 hover:bg-gray-50 shrink-0" @click="sourceOpen = !sourceOpen">
|
|
15
|
-
{{ sourceOpen ? "
|
|
15
|
+
{{ sourceOpen ? t("pluginPresentHtml.hideSource") : t("pluginPresentHtml.showSource") }}
|
|
16
16
|
</button>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
@@ -25,9 +25,12 @@
|
|
|
25
25
|
|
|
26
26
|
<script setup lang="ts">
|
|
27
27
|
import { computed, ref } from "vue";
|
|
28
|
+
import { useI18n } from "vue-i18n";
|
|
28
29
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
29
30
|
import type { PresentHtmlData } from "./index";
|
|
30
31
|
|
|
32
|
+
const { t } = useI18n();
|
|
33
|
+
|
|
31
34
|
const props = defineProps<{
|
|
32
35
|
selectedResult: ToolResultComplete<PresentHtmlData>;
|
|
33
36
|
}>();
|
|
@@ -25,8 +25,8 @@ export function stripHtmlToPreview(html: string, maxLength: number): string {
|
|
|
25
25
|
};
|
|
26
26
|
let i = 0;
|
|
27
27
|
while (i < html.length) {
|
|
28
|
-
const
|
|
29
|
-
if (
|
|
28
|
+
const char = html[i];
|
|
29
|
+
if (char === "<") {
|
|
30
30
|
const close = html.indexOf(">", i + 1);
|
|
31
31
|
if (close !== -1) {
|
|
32
32
|
// Real tag span `<...>` — skip it, emit a separator.
|
|
@@ -36,7 +36,7 @@ export function stripHtmlToPreview(html: string, maxLength: number): string {
|
|
|
36
36
|
}
|
|
37
37
|
// No closing `>` anywhere after — treat as literal.
|
|
38
38
|
}
|
|
39
|
-
emitChar(state,
|
|
39
|
+
emitChar(state, char);
|
|
40
40
|
i++;
|
|
41
41
|
}
|
|
42
42
|
trimTrailingSpace(state.out);
|
|
@@ -48,12 +48,12 @@ interface WalkerState {
|
|
|
48
48
|
lastWasSpace: boolean;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function emitChar(state: WalkerState,
|
|
52
|
-
if (isWhitespace(
|
|
51
|
+
function emitChar(state: WalkerState, char: string): void {
|
|
52
|
+
if (isWhitespace(char)) {
|
|
53
53
|
emitSeparator(state);
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
-
state.out.push(
|
|
56
|
+
state.out.push(char);
|
|
57
57
|
state.lastWasSpace = false;
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -67,6 +67,6 @@ function trimTrailingSpace(out: string[]): void {
|
|
|
67
67
|
if (out.length > 0 && out[out.length - 1] === " ") out.pop();
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
function isWhitespace(
|
|
71
|
-
return
|
|
70
|
+
function isWhitespace(char: string): boolean {
|
|
71
|
+
return char === " " || char === "\t" || char === "\n" || char === "\r" || char === "\v" || char === "\f";
|
|
72
72
|
}
|