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
|
@@ -3,19 +3,21 @@
|
|
|
3
3
|
<!-- Header -->
|
|
4
4
|
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-100 shrink-0 gap-3">
|
|
5
5
|
<div class="flex items-center gap-3 min-w-0">
|
|
6
|
-
<h2 class="text-base font-semibold text-gray-800 shrink-0">
|
|
7
|
-
<span class="text-xs text-gray-500 shrink-0">{{
|
|
6
|
+
<h2 class="text-base font-semibold text-gray-800 shrink-0">{{ t("todoExplorer.heading") }}</h2>
|
|
7
|
+
<span class="text-xs text-gray-500 shrink-0">{{ t("todoExplorer.doneRatio", { done: completedCount, total: items.length }) }}</span>
|
|
8
8
|
<input
|
|
9
9
|
v-model="search"
|
|
10
10
|
data-testid="todo-search"
|
|
11
11
|
type="text"
|
|
12
|
-
placeholder="
|
|
12
|
+
:placeholder="t('todoExplorer.searchPlaceholder')"
|
|
13
13
|
class="px-2 py-1 text-xs border border-gray-200 rounded w-44 focus:outline-none focus:border-blue-400"
|
|
14
14
|
/>
|
|
15
15
|
</div>
|
|
16
16
|
<div class="flex items-center gap-2">
|
|
17
17
|
<!-- Add button -->
|
|
18
|
-
<button data-testid="todo-add-btn" class="px-2 py-1 text-xs rounded bg-blue-500 text-white hover:bg-blue-600" @click="addOpen = true"
|
|
18
|
+
<button data-testid="todo-add-btn" class="px-2 py-1 text-xs rounded bg-blue-500 text-white hover:bg-blue-600" @click="addOpen = true">
|
|
19
|
+
{{ t("todoExplorer.addButton") }}
|
|
20
|
+
</button>
|
|
19
21
|
<!-- Add column button (kanban only) -->
|
|
20
22
|
<button
|
|
21
23
|
v-if="viewMode === TODO_VIEW.kanban"
|
|
@@ -23,7 +25,7 @@
|
|
|
23
25
|
class="px-2 py-1 text-xs rounded border border-gray-300 text-gray-600 hover:bg-gray-50"
|
|
24
26
|
@click="addColumnOpen = true"
|
|
25
27
|
>
|
|
26
|
-
|
|
28
|
+
{{ t("todoExplorer.addColumnButton") }}
|
|
27
29
|
</button>
|
|
28
30
|
<!-- View mode toggle -->
|
|
29
31
|
<div class="flex border border-gray-300 rounded overflow-hidden text-xs">
|
|
@@ -44,7 +46,7 @@
|
|
|
44
46
|
|
|
45
47
|
<!-- Label filter chips -->
|
|
46
48
|
<div v-if="labelInventory.length > 0" class="flex flex-wrap items-center gap-1.5 px-4 py-1.5 border-b border-gray-100 bg-gray-50 shrink-0">
|
|
47
|
-
<span class="text-[11px] text-gray-500 mr-1">
|
|
49
|
+
<span class="text-[11px] text-gray-500 mr-1">{{ t("todoExplorer.labels") }}</span>
|
|
48
50
|
<button
|
|
49
51
|
v-for="entry in labelInventory"
|
|
50
52
|
:key="entry.label"
|
|
@@ -59,8 +61,13 @@
|
|
|
59
61
|
{{ entry.label }}
|
|
60
62
|
<span class="opacity-60">{{ entry.count }}</span>
|
|
61
63
|
</button>
|
|
62
|
-
<button
|
|
63
|
-
|
|
64
|
+
<button
|
|
65
|
+
v-if="activeFilters.size > 0"
|
|
66
|
+
class="ml-auto text-[11px] text-gray-500 hover:text-gray-700"
|
|
67
|
+
:title="t('todoExplorer.clearFiltersTitle')"
|
|
68
|
+
@click="clearFilters"
|
|
69
|
+
>
|
|
70
|
+
{{ t("todoExplorer.clearButton") }}
|
|
64
71
|
</button>
|
|
65
72
|
</div>
|
|
66
73
|
|
|
@@ -71,7 +78,7 @@
|
|
|
71
78
|
|
|
72
79
|
<!-- Body -->
|
|
73
80
|
<div class="flex-1 min-h-0">
|
|
74
|
-
<div v-if="items.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">
|
|
81
|
+
<div v-if="items.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">{{ t("todoExplorer.emptyHint") }}</div>
|
|
75
82
|
<template v-else>
|
|
76
83
|
<TodoKanbanView
|
|
77
84
|
v-if="viewMode === TODO_VIEW.kanban"
|
|
@@ -122,20 +129,22 @@
|
|
|
122
129
|
<!-- Add column dialog -->
|
|
123
130
|
<div v-if="addColumnOpen" class="fixed inset-0 z-50 bg-black/30 flex items-center justify-center" @click="addColumnOpen = false">
|
|
124
131
|
<div class="bg-white rounded-lg shadow-xl w-80 p-5 space-y-3" role="dialog" aria-modal="true" aria-labelledby="todo-add-column-title" @click.stop>
|
|
125
|
-
<h3 id="todo-add-column-title" class="text-base font-semibold text-gray-800">
|
|
132
|
+
<h3 id="todo-add-column-title" class="text-base font-semibold text-gray-800">{{ t("todoExplorer.addColumn") }}</h3>
|
|
126
133
|
<label class="block text-xs text-gray-600">
|
|
127
|
-
|
|
134
|
+
{{ t("todoExplorer.newColumnLabelField") }}
|
|
128
135
|
<input
|
|
129
136
|
v-model="newColumnLabel"
|
|
130
137
|
type="text"
|
|
131
|
-
placeholder="
|
|
138
|
+
:placeholder="t('todoExplorer.newColumnPlaceholder')"
|
|
132
139
|
class="mt-1 w-full px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:border-blue-400"
|
|
133
140
|
@keydown.enter="commitNewColumn"
|
|
134
141
|
/>
|
|
135
142
|
</label>
|
|
136
143
|
<div class="flex justify-end gap-2 pt-1">
|
|
137
|
-
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="addColumnOpen = false">
|
|
138
|
-
|
|
144
|
+
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="addColumnOpen = false">
|
|
145
|
+
{{ t("common.cancel") }}
|
|
146
|
+
</button>
|
|
147
|
+
<button class="px-3 py-1.5 text-sm rounded bg-blue-500 text-white hover:bg-blue-600" @click="commitNewColumn">{{ t("common.add") }}</button>
|
|
139
148
|
</div>
|
|
140
149
|
</div>
|
|
141
150
|
</div>
|
|
@@ -144,6 +153,7 @@
|
|
|
144
153
|
|
|
145
154
|
<script setup lang="ts">
|
|
146
155
|
import { computed, onMounted, onUnmounted, ref, watch } from "vue";
|
|
156
|
+
import { useI18n } from "vue-i18n";
|
|
147
157
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
148
158
|
import type { TodoData, TodoItem } from "../plugins/todo/index";
|
|
149
159
|
import { colorForLabel, filterByLabels, listLabelsWithCount } from "../plugins/todo/labels";
|
|
@@ -153,9 +163,10 @@ import TodoTableView from "./todo/TodoTableView.vue";
|
|
|
153
163
|
import TodoListView from "./todo/TodoListView.vue";
|
|
154
164
|
import TodoAddDialog from "./todo/TodoAddDialog.vue";
|
|
155
165
|
import TodoEditDialog from "./todo/TodoEditDialog.vue";
|
|
156
|
-
|
|
157
166
|
import { TODO_VIEW, TODO_VIEW_MODES as VIEW_MODES, type TodoViewMode as ViewMode } from "../plugins/todo/viewModes";
|
|
158
167
|
|
|
168
|
+
const { t } = useI18n();
|
|
169
|
+
|
|
159
170
|
const VIEW_MODE_KEY = "todo_explorer_view_mode";
|
|
160
171
|
|
|
161
172
|
const props = defineProps<{
|
|
@@ -9,22 +9,21 @@
|
|
|
9
9
|
<div
|
|
10
10
|
v-for="result in results"
|
|
11
11
|
:key="result.uuid"
|
|
12
|
-
class="cursor-pointer rounded border border-gray-300
|
|
12
|
+
class="relative cursor-pointer rounded border border-gray-300 text-sm text-gray-900 hover:opacity-75 transition-opacity"
|
|
13
13
|
:class="result.uuid === selectedUuid ? 'ring-2 ring-blue-500' : ''"
|
|
14
14
|
@click="emit('select', result.uuid)"
|
|
15
15
|
>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</div>
|
|
16
|
+
<span class="absolute top-0 left-2 -translate-y-1/2 bg-gray-100 px-1 text-[10px] text-gray-400 leading-none pointer-events-none">
|
|
17
|
+
{{ sourceLabel(result) }}
|
|
18
|
+
</span>
|
|
19
|
+
<span
|
|
20
|
+
v-if="resultTimestamps.get(result.uuid)"
|
|
21
|
+
class="absolute top-0 right-2 -translate-y-1/2 bg-gray-100 px-1 text-[10px] text-gray-400 leading-none pointer-events-none"
|
|
22
|
+
>
|
|
23
|
+
{{ formatSmartTime(resultTimestamps.get(result.uuid)!) }}
|
|
24
|
+
</span>
|
|
25
|
+
<component :is="getPlugin(result.toolName)?.previewComponent" v-if="getPlugin(result.toolName)?.previewComponent" :result="result" />
|
|
26
|
+
<span v-else class="block truncate p-2">{{ result.title || result.toolName }}</span>
|
|
28
27
|
</div>
|
|
29
28
|
|
|
30
29
|
<!-- Thinking indicator -->
|
|
@@ -53,6 +52,11 @@ import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
|
53
52
|
import { getPlugin } from "../tools";
|
|
54
53
|
import { formatSmartTime } from "../utils/format/date";
|
|
55
54
|
|
|
55
|
+
function sourceLabel(result: ToolResultComplete): string {
|
|
56
|
+
if (result.toolName === "text-response") return result.title ?? "Assistant";
|
|
57
|
+
return result.toolName;
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
interface PendingCall {
|
|
57
61
|
toolUseId: string;
|
|
58
62
|
toolName: string;
|
|
@@ -75,3 +79,13 @@ const emit = defineEmits<{
|
|
|
75
79
|
const root = ref<HTMLDivElement | null>(null);
|
|
76
80
|
defineExpose({ root });
|
|
77
81
|
</script>
|
|
82
|
+
|
|
83
|
+
<style scoped>
|
|
84
|
+
/* Prevent rendered markdown links inside preview cards from navigating.
|
|
85
|
+
Clicking a sidebar card should select the result, not follow links. */
|
|
86
|
+
:deep(a) {
|
|
87
|
+
pointer-events: none;
|
|
88
|
+
color: inherit;
|
|
89
|
+
text-decoration: none;
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
@@ -7,20 +7,20 @@
|
|
|
7
7
|
aria-labelledby="todo-add-dialog-title"
|
|
8
8
|
@click.stop
|
|
9
9
|
>
|
|
10
|
-
<h3 id="todo-add-dialog-title" class="text-base font-semibold text-gray-800">
|
|
10
|
+
<h3 id="todo-add-dialog-title" class="text-base font-semibold text-gray-800">{{ t("todoDialogs.addTitle") }}</h3>
|
|
11
11
|
<label class="block text-xs text-gray-600">
|
|
12
|
-
|
|
12
|
+
{{ t("todoDialogs.fieldText") }}
|
|
13
13
|
<input
|
|
14
14
|
ref="textInput"
|
|
15
15
|
v-model="text"
|
|
16
16
|
type="text"
|
|
17
|
-
placeholder="
|
|
17
|
+
:placeholder="t('todoDialogs.textPlaceholder')"
|
|
18
18
|
class="mt-1 w-full px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:border-blue-400"
|
|
19
19
|
@keydown.enter="submit"
|
|
20
20
|
/>
|
|
21
21
|
</label>
|
|
22
22
|
<label class="block text-xs text-gray-600">
|
|
23
|
-
|
|
23
|
+
{{ t("todoDialogs.fieldNote") }}
|
|
24
24
|
<textarea
|
|
25
25
|
v-model="note"
|
|
26
26
|
rows="2"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
</label>
|
|
30
30
|
<div class="grid grid-cols-2 gap-3">
|
|
31
31
|
<label class="block text-xs text-gray-600">
|
|
32
|
-
|
|
32
|
+
{{ t("todoDialogs.fieldStatus") }}
|
|
33
33
|
<select v-model="status" class="mt-1 w-full px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:border-blue-400">
|
|
34
34
|
<option v-for="col in columns" :key="col.id" :value="col.id">
|
|
35
35
|
{{ col.label }}
|
|
@@ -37,16 +37,16 @@
|
|
|
37
37
|
</select>
|
|
38
38
|
</label>
|
|
39
39
|
<label class="block text-xs text-gray-600">
|
|
40
|
-
|
|
40
|
+
{{ t("todoDialogs.fieldPriority") }}
|
|
41
41
|
<select v-model="priority" class="mt-1 w-full px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:border-blue-400">
|
|
42
|
-
<option value=""
|
|
42
|
+
<option value="">{{ t("todoDialogs.noneOption") }}</option>
|
|
43
43
|
<option v-for="p in PRIORITIES" :key="p" :value="p">
|
|
44
44
|
{{ PRIORITY_LABELS[p] }}
|
|
45
45
|
</option>
|
|
46
46
|
</select>
|
|
47
47
|
</label>
|
|
48
48
|
<label class="block text-xs text-gray-600">
|
|
49
|
-
|
|
49
|
+
{{ t("todoDialogs.fieldDueDate") }}
|
|
50
50
|
<input
|
|
51
51
|
v-model="dueDate"
|
|
52
52
|
type="date"
|
|
@@ -54,18 +54,20 @@
|
|
|
54
54
|
/>
|
|
55
55
|
</label>
|
|
56
56
|
<label class="block text-xs text-gray-600">
|
|
57
|
-
|
|
57
|
+
{{ t("todoDialogs.fieldLabels") }}
|
|
58
58
|
<input
|
|
59
59
|
v-model="labelsText"
|
|
60
60
|
type="text"
|
|
61
|
-
placeholder="
|
|
61
|
+
:placeholder="t('todoDialogs.labelsPlaceholder')"
|
|
62
62
|
class="mt-1 w-full px-2 py-1.5 text-sm border border-gray-300 rounded focus:outline-none focus:border-blue-400"
|
|
63
63
|
/>
|
|
64
64
|
</label>
|
|
65
65
|
</div>
|
|
66
66
|
<div class="flex justify-end gap-2 pt-1">
|
|
67
|
-
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="emit('cancel')">
|
|
68
|
-
|
|
67
|
+
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="emit('cancel')">
|
|
68
|
+
{{ t("common.cancel") }}
|
|
69
|
+
</button>
|
|
70
|
+
<button class="px-3 py-1.5 text-sm rounded bg-blue-500 text-white hover:bg-blue-600" @click="submit">{{ t("common.add") }}</button>
|
|
69
71
|
</div>
|
|
70
72
|
</div>
|
|
71
73
|
</div>
|
|
@@ -73,10 +75,13 @@
|
|
|
73
75
|
|
|
74
76
|
<script setup lang="ts">
|
|
75
77
|
import { onMounted, onUnmounted, ref } from "vue";
|
|
78
|
+
import { useI18n } from "vue-i18n";
|
|
76
79
|
import type { StatusColumn } from "../../plugins/todo/index";
|
|
77
80
|
import { PRIORITIES, PRIORITY_LABELS } from "../../plugins/todo/priority";
|
|
78
81
|
import type { CreateItemInput } from "../../plugins/todo/composables/useTodos";
|
|
79
82
|
|
|
83
|
+
const { t } = useI18n();
|
|
84
|
+
|
|
80
85
|
const props = defineProps<{
|
|
81
86
|
columns: StatusColumn[];
|
|
82
87
|
defaultStatus?: string;
|
|
@@ -123,8 +128,8 @@ function submit(): void {
|
|
|
123
128
|
if (dueDate.value !== "") input.dueDate = dueDate.value;
|
|
124
129
|
const labels = labelsText.value
|
|
125
130
|
.split(",")
|
|
126
|
-
.map((
|
|
127
|
-
.filter((
|
|
131
|
+
.map((item) => item.trim())
|
|
132
|
+
.filter((item) => item.length > 0);
|
|
128
133
|
if (labels.length > 0) input.labels = labels;
|
|
129
134
|
emit("create", input);
|
|
130
135
|
}
|
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
@click.stop
|
|
9
9
|
>
|
|
10
10
|
<div class="flex items-center justify-between px-4 py-2 border-b border-gray-100">
|
|
11
|
-
<h3 id="todo-edit-dialog-title" class="text-base font-semibold text-gray-800">
|
|
12
|
-
<button class="text-gray-400 hover:text-red-500 text-xs px-2 py-0.5" title="
|
|
11
|
+
<h3 id="todo-edit-dialog-title" class="text-base font-semibold text-gray-800">{{ t("todoDialogs.editTitle") }}</h3>
|
|
12
|
+
<button class="text-gray-400 hover:text-red-500 text-xs px-2 py-0.5" :title="t('todoDialogs.deleteTitle')" @click="emit('delete', item.id)">
|
|
13
|
+
{{ t("todoDialogs.deleteButton") }}
|
|
14
|
+
</button>
|
|
13
15
|
</div>
|
|
14
16
|
<TodoEditPanel :item="item" :columns="columns" @save="(input) => emit('save', input)" @cancel="emit('cancel')" />
|
|
15
17
|
</div>
|
|
@@ -18,10 +20,13 @@
|
|
|
18
20
|
|
|
19
21
|
<script setup lang="ts">
|
|
20
22
|
import { onMounted, onUnmounted } from "vue";
|
|
23
|
+
import { useI18n } from "vue-i18n";
|
|
21
24
|
import type { StatusColumn, TodoItem } from "../../plugins/todo/index";
|
|
22
25
|
import type { PatchItemInput } from "../../plugins/todo/composables/useTodos";
|
|
23
26
|
import TodoEditPanel from "./TodoEditPanel.vue";
|
|
24
27
|
|
|
28
|
+
const { t } = useI18n();
|
|
29
|
+
|
|
25
30
|
defineProps<{
|
|
26
31
|
item: TodoItem;
|
|
27
32
|
columns: StatusColumn[];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="border-t border-blue-100 bg-blue-50 p-4 space-y-3 rounded-b-lg">
|
|
3
3
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
4
4
|
<label class="block text-xs text-gray-600 sm:col-span-2">
|
|
5
|
-
|
|
5
|
+
{{ t("todoDialogs.fieldText") }}
|
|
6
6
|
<input
|
|
7
7
|
v-model="text"
|
|
8
8
|
type="text"
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
/>
|
|
11
11
|
</label>
|
|
12
12
|
<label class="block text-xs text-gray-600 sm:col-span-2">
|
|
13
|
-
|
|
13
|
+
{{ t("todoDialogs.fieldNote") }}
|
|
14
14
|
<textarea
|
|
15
15
|
v-model="note"
|
|
16
16
|
rows="2"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
/>
|
|
19
19
|
</label>
|
|
20
20
|
<label class="block text-xs text-gray-600">
|
|
21
|
-
|
|
21
|
+
{{ t("todoDialogs.fieldStatus") }}
|
|
22
22
|
<select v-model="status" class="mt-1 w-full px-2 py-1.5 text-sm bg-white border border-blue-300 rounded focus:outline-none focus:border-blue-500">
|
|
23
23
|
<option v-for="col in columns" :key="col.id" :value="col.id">
|
|
24
24
|
{{ col.label }}
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
</select>
|
|
27
27
|
</label>
|
|
28
28
|
<label class="block text-xs text-gray-600">
|
|
29
|
-
|
|
29
|
+
{{ t("todoDialogs.fieldPriority") }}
|
|
30
30
|
<select v-model="priority" class="mt-1 w-full px-2 py-1.5 text-sm bg-white border border-blue-300 rounded focus:outline-none focus:border-blue-500">
|
|
31
|
-
<option value=""
|
|
31
|
+
<option value="">{{ t("todoDialogs.noneOption") }}</option>
|
|
32
32
|
<option v-for="p in PRIORITIES" :key="p" :value="p">
|
|
33
33
|
{{ PRIORITY_LABELS[p] }}
|
|
34
34
|
</option>
|
|
35
35
|
</select>
|
|
36
36
|
</label>
|
|
37
37
|
<label class="block text-xs text-gray-600">
|
|
38
|
-
|
|
38
|
+
{{ t("todoDialogs.fieldDueDate") }}
|
|
39
39
|
<input
|
|
40
40
|
v-model="dueDate"
|
|
41
41
|
type="date"
|
|
@@ -43,28 +43,33 @@
|
|
|
43
43
|
/>
|
|
44
44
|
</label>
|
|
45
45
|
<label class="block text-xs text-gray-600">
|
|
46
|
-
|
|
46
|
+
{{ t("todoDialogs.fieldLabelsCommaSeparated") }}
|
|
47
47
|
<input
|
|
48
48
|
v-model="labelsText"
|
|
49
49
|
type="text"
|
|
50
|
-
placeholder="
|
|
50
|
+
:placeholder="t('todoDialogs.labelsPlaceholder')"
|
|
51
51
|
class="mt-1 w-full px-2 py-1.5 text-sm bg-white border border-blue-300 rounded focus:outline-none focus:border-blue-500"
|
|
52
52
|
/>
|
|
53
53
|
</label>
|
|
54
54
|
</div>
|
|
55
55
|
<div class="flex items-center gap-2 pt-1">
|
|
56
|
-
<button class="px-3 py-1.5 text-sm rounded bg-blue-500 text-white hover:bg-blue-600" @click="save">
|
|
57
|
-
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="emit('cancel')">
|
|
56
|
+
<button class="px-3 py-1.5 text-sm rounded bg-blue-500 text-white hover:bg-blue-600" @click="save">{{ t("common.save") }}</button>
|
|
57
|
+
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="emit('cancel')">
|
|
58
|
+
{{ t("common.cancel") }}
|
|
59
|
+
</button>
|
|
58
60
|
</div>
|
|
59
61
|
</div>
|
|
60
62
|
</template>
|
|
61
63
|
|
|
62
64
|
<script setup lang="ts">
|
|
63
65
|
import { ref } from "vue";
|
|
66
|
+
import { useI18n } from "vue-i18n";
|
|
64
67
|
import type { StatusColumn, TodoItem, TodoPriority } from "../../plugins/todo/index";
|
|
65
68
|
import { PRIORITIES, PRIORITY_LABELS } from "../../plugins/todo/priority";
|
|
66
69
|
import type { PatchItemInput } from "../../plugins/todo/composables/useTodos";
|
|
67
70
|
|
|
71
|
+
const { t } = useI18n();
|
|
72
|
+
|
|
68
73
|
const props = defineProps<{
|
|
69
74
|
item: TodoItem;
|
|
70
75
|
columns: StatusColumn[];
|
|
@@ -85,8 +90,8 @@ const labelsText = ref((props.item.labels ?? []).join(", "));
|
|
|
85
90
|
function parseLabels(raw: string): string[] {
|
|
86
91
|
return raw
|
|
87
92
|
.split(",")
|
|
88
|
-
.map((
|
|
89
|
-
.filter((
|
|
93
|
+
.map((item) => item.trim())
|
|
94
|
+
.filter((item) => item.length > 0);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
function save(): void {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
<span class="text-xs text-gray-500 shrink-0">{{ itemsByColumn(col.id).length }}</span>
|
|
33
33
|
</div>
|
|
34
34
|
<div class="relative">
|
|
35
|
-
<button class="text-gray-400 hover:text-gray-600 px-1" title="
|
|
35
|
+
<button class="text-gray-400 hover:text-gray-600 px-1" :title="t('todoKanban.columnActions')" @click="toggleMenu(col.id)">
|
|
36
36
|
<span class="material-icons text-base">more_horiz</span>
|
|
37
37
|
</button>
|
|
38
38
|
<div
|
|
@@ -40,11 +40,13 @@
|
|
|
40
40
|
class="absolute right-0 top-6 z-20 bg-white border border-gray-200 rounded shadow-md text-xs w-40 py-1"
|
|
41
41
|
@click.stop
|
|
42
42
|
>
|
|
43
|
-
<button class="w-full text-left px-3 py-1.5 hover:bg-gray-50" @click="startRename(col)">
|
|
43
|
+
<button class="w-full text-left px-3 py-1.5 hover:bg-gray-50" @click="startRename(col)">{{ t("todoKanban.rename") }}</button>
|
|
44
44
|
<button class="w-full text-left px-3 py-1.5 hover:bg-gray-50" @click="markAsDone(col.id)">
|
|
45
|
-
{{ col.isDone ? "
|
|
45
|
+
{{ col.isDone ? t("todoKanban.alreadyDoneColumn") : t("todoKanban.markAsDoneColumn") }}
|
|
46
|
+
</button>
|
|
47
|
+
<button class="w-full text-left px-3 py-1.5 text-red-600 hover:bg-red-50" @click="deleteColumn(col.id)">
|
|
48
|
+
{{ t("todoKanban.deleteColumn") }}
|
|
46
49
|
</button>
|
|
47
|
-
<button class="w-full text-left px-3 py-1.5 text-red-600 hover:bg-red-50" @click="deleteColumn(col.id)">Delete column</button>
|
|
48
50
|
</div>
|
|
49
51
|
</div>
|
|
50
52
|
</div>
|
|
@@ -103,7 +105,7 @@
|
|
|
103
105
|
|
|
104
106
|
<!-- Add card stub -->
|
|
105
107
|
<button class="m-2 text-xs text-gray-500 hover:text-gray-800 hover:bg-gray-200 rounded py-1.5 transition-colors" @click="emit('quickAdd', col.id)">
|
|
106
|
-
|
|
108
|
+
{{ t("todoKanban.addCard") }}
|
|
107
109
|
</button>
|
|
108
110
|
</div>
|
|
109
111
|
</template>
|
|
@@ -113,11 +115,14 @@
|
|
|
113
115
|
|
|
114
116
|
<script setup lang="ts">
|
|
115
117
|
import { computed, nextTick, ref, watch } from "vue";
|
|
118
|
+
import { useI18n } from "vue-i18n";
|
|
116
119
|
import draggable from "vuedraggable";
|
|
117
120
|
import type { StatusColumn, TodoItem } from "../../plugins/todo/index";
|
|
118
121
|
import { colorForLabel } from "../../plugins/todo/labels";
|
|
119
122
|
import { PRIORITY_BORDER, PRIORITY_CLASSES, PRIORITY_LABELS, dueDateClasses, formatDueLabel } from "../../plugins/todo/priority";
|
|
120
123
|
|
|
124
|
+
const { t } = useI18n();
|
|
125
|
+
|
|
121
126
|
// vuedraggable @change event shape. The library emits one of these
|
|
122
127
|
// three keys depending on whether the move was within the same list,
|
|
123
128
|
// added from another list, or removed from the current list. We only
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full overflow-y-auto p-4">
|
|
3
|
-
<div v-if="filteredItems.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">
|
|
3
|
+
<div v-if="filteredItems.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">{{ t("todoTableList.noMatchingFilter") }}</div>
|
|
4
4
|
<ul v-else class="space-y-2 max-w-3xl mx-auto">
|
|
5
5
|
<li v-for="item in filteredItems" :key="item.id" class="rounded-lg border border-gray-200 hover:border-gray-300 transition-colors">
|
|
6
6
|
<div class="flex items-center gap-3 p-3 cursor-pointer group" @click="toggleExpand(item.id)">
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</div>
|
|
26
26
|
<button
|
|
27
27
|
class="opacity-0 group-hover:opacity-100 text-gray-400 hover:text-red-500 text-xs px-1 shrink-0"
|
|
28
|
-
title="
|
|
28
|
+
:title="t('pluginTodo.deleteItem')"
|
|
29
29
|
@click.stop="emit('delete', item.id)"
|
|
30
30
|
>
|
|
31
31
|
✕
|
|
@@ -39,12 +39,15 @@
|
|
|
39
39
|
|
|
40
40
|
<script setup lang="ts">
|
|
41
41
|
import { ref } from "vue";
|
|
42
|
+
import { useI18n } from "vue-i18n";
|
|
42
43
|
import type { StatusColumn, TodoItem } from "../../plugins/todo/index";
|
|
43
44
|
import { colorForLabel } from "../../plugins/todo/labels";
|
|
44
45
|
import { PRIORITY_CLASSES, PRIORITY_LABELS, dueDateClasses, formatDueLabel } from "../../plugins/todo/priority";
|
|
45
46
|
import type { PatchItemInput } from "../../plugins/todo/composables/useTodos";
|
|
46
47
|
import TodoEditPanel from "./TodoEditPanel.vue";
|
|
47
48
|
|
|
49
|
+
const { t } = useI18n();
|
|
50
|
+
|
|
48
51
|
const props = defineProps<{
|
|
49
52
|
filteredItems: TodoItem[];
|
|
50
53
|
columns: StatusColumn[];
|
|
@@ -58,22 +61,22 @@ const emit = defineEmits<{
|
|
|
58
61
|
|
|
59
62
|
const expandedId = ref<string | null>(null);
|
|
60
63
|
|
|
61
|
-
function toggleExpand(
|
|
62
|
-
expandedId.value = expandedId.value ===
|
|
64
|
+
function toggleExpand(itemId: string): void {
|
|
65
|
+
expandedId.value = expandedId.value === itemId ? null : itemId;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
function toggleComplete(item: TodoItem): void {
|
|
66
69
|
emit("toggleComplete", item);
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
function onSave(
|
|
70
|
-
emit("patch",
|
|
72
|
+
function onSave(itemId: string, input: PatchItemInput): void {
|
|
73
|
+
emit("patch", itemId, input);
|
|
71
74
|
expandedId.value = null;
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
function statusLabel(item: TodoItem): string {
|
|
75
78
|
if (!item.status) return "";
|
|
76
|
-
const col = props.columns.find((
|
|
79
|
+
const col = props.columns.find((column) => column.id === item.status);
|
|
77
80
|
return col?.label ?? "";
|
|
78
81
|
}
|
|
79
82
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full overflow-auto">
|
|
3
|
-
<div v-if="filteredItems.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">
|
|
3
|
+
<div v-if="filteredItems.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">{{ t("todoTableList.noMatchingFilter") }}</div>
|
|
4
4
|
<table v-else class="min-w-full text-sm">
|
|
5
5
|
<thead class="bg-gray-50 sticky top-0 z-10">
|
|
6
6
|
<tr class="text-left text-xs font-medium text-gray-500 uppercase">
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
{{ formatShortDate(item.createdAt) }}
|
|
54
54
|
</td>
|
|
55
55
|
<td class="px-3 py-2 text-right">
|
|
56
|
-
<button class="text-gray-300 hover:text-red-500 text-xs" title="
|
|
56
|
+
<button class="text-gray-300 hover:text-red-500 text-xs" :title="t('pluginTodo.deleteItem')" @click="emit('delete', item.id)">✕</button>
|
|
57
57
|
</td>
|
|
58
58
|
</tr>
|
|
59
59
|
<tr v-if="expandedId === item.id">
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
<script setup lang="ts">
|
|
71
71
|
import { computed, ref } from "vue";
|
|
72
|
+
import { useI18n } from "vue-i18n";
|
|
72
73
|
import type { StatusColumn, TodoItem } from "../../plugins/todo/index";
|
|
73
74
|
import { colorForLabel } from "../../plugins/todo/labels";
|
|
74
75
|
import { PRIORITY_CLASSES, PRIORITY_LABELS, PRIORITY_ORDER, dueDateClasses, formatDueLabel } from "../../plugins/todo/priority";
|
|
@@ -76,6 +77,8 @@ import type { PatchItemInput } from "../../plugins/todo/composables/useTodos";
|
|
|
76
77
|
import TodoEditPanel from "./TodoEditPanel.vue";
|
|
77
78
|
import { formatShortDate } from "../../utils/format/date";
|
|
78
79
|
|
|
80
|
+
const { t } = useI18n();
|
|
81
|
+
|
|
79
82
|
type SortKey = "completed" | "text" | "status" | "priority" | "labels" | "dueDate" | "createdAt";
|
|
80
83
|
type SortDir = "asc" | "desc";
|
|
81
84
|
|
|
@@ -20,6 +20,15 @@ export interface AppApi {
|
|
|
20
20
|
refreshRoles: () => void | Promise<void>;
|
|
21
21
|
/** Send a chat message through App.vue's normal sendMessage pipeline. */
|
|
22
22
|
sendMessage: (message: string) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Open a fresh chat session (using the currently selected role) and
|
|
25
|
+
* send `message` as its first turn. Used by plugin views that want
|
|
26
|
+
* to kick off a new conversation instead of threading into whatever
|
|
27
|
+
* session happens to be active.
|
|
28
|
+
*/
|
|
29
|
+
startNewChat: (message: string) => void;
|
|
30
|
+
/** Navigate to a workspace-internal link (wiki page, file, session). */
|
|
31
|
+
navigateToWorkspacePath: (href: string) => void;
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
const APP_API_KEY = Symbol("appApi");
|
|
@@ -16,9 +16,9 @@ interface UseClickOutsideOptions {
|
|
|
16
16
|
export function useClickOutside(opts: UseClickOutsideOptions): {
|
|
17
17
|
handler: (e: MouseEvent) => void;
|
|
18
18
|
} {
|
|
19
|
-
function handler(
|
|
19
|
+
function handler(event: MouseEvent): void {
|
|
20
20
|
if (!opts.isOpen.value) return;
|
|
21
|
-
if (isClickOutside(
|
|
21
|
+
if (isClickOutside(event.target as Node | null, opts.buttonRef.value, opts.popupRef.value)) {
|
|
22
22
|
opts.isOpen.value = false;
|
|
23
23
|
}
|
|
24
24
|
}
|