mulmoclaude 0.3.0 → 0.5.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-DiKaqnKs.js +5 -0
- package/client/assets/{index-eHWB79u5.js → index-C94GcmNa.js} +203 -198
- package/client/assets/index-CY-WpQUm.css +2 -0
- package/client/assets/{index.es-D4YyL_Dg-BfRHLTZV.js → index.es-D4YyL_Dg-5ipqh8Pe.js} +5 -5
- package/client/assets/material-symbols-outlined-NzYEeyps.woff2 +0 -0
- package/client/index.html +2 -4
- package/package.json +17 -15
- package/server/agent/attachmentConverter.ts +2 -2
- package/server/agent/backend/claude-code.ts +170 -0
- package/server/agent/backend/index.ts +14 -0
- package/server/agent/backend/types.ts +65 -0
- package/server/agent/index.ts +31 -159
- package/server/agent/mcp-server.ts +88 -10
- package/server/agent/mcp-tools/index.ts +8 -7
- package/server/agent/mcp-tools/notify.ts +76 -0
- package/server/agent/mcp-tools/x.ts +12 -2
- package/server/agent/plugin-names.ts +10 -4
- package/server/agent/prompt.ts +187 -26
- package/server/agent/resumeFailover.ts +5 -5
- package/server/agent/sandboxMounts.ts +3 -3
- package/server/api/auth/bearerAuth.ts +3 -3
- package/server/api/auth/token.ts +2 -2
- package/server/api/routes/agent.ts +99 -4
- package/server/api/routes/chart.ts +13 -0
- package/server/api/routes/chat-index.ts +2 -1
- package/server/api/routes/config.ts +35 -8
- package/server/api/routes/files.ts +75 -24
- package/server/api/routes/html.ts +15 -2
- package/server/api/routes/image.ts +75 -20
- package/server/api/routes/mulmo-script.ts +33 -31
- package/server/api/routes/news.ts +146 -0
- package/server/api/routes/notifications.ts +58 -2
- package/server/api/routes/pdf.ts +2 -2
- package/server/api/routes/plugins.ts +73 -91
- package/server/api/routes/presentHtml.ts +9 -0
- package/server/api/routes/roles.ts +12 -2
- package/server/api/routes/scheduler.ts +20 -11
- package/server/api/routes/schedulerTasks.ts +58 -21
- package/server/api/routes/sessions.ts +15 -4
- package/server/api/routes/sessionsCursor.ts +4 -4
- package/server/api/routes/skills.ts +26 -5
- package/server/api/routes/sources.ts +8 -7
- package/server/api/routes/todos.ts +30 -0
- package/server/api/routes/todosColumnsHandlers.ts +13 -27
- package/server/api/routes/todosHandlers.ts +1 -1
- package/server/api/routes/todosItemsHandlers.ts +14 -14
- package/server/api/routes/wiki/frontmatter.ts +86 -0
- package/server/api/routes/wiki.ts +335 -75
- package/server/api/sandboxStatus.ts +1 -1
- package/server/events/notifications.ts +32 -8
- package/server/events/pub-sub/index.ts +3 -3
- package/server/events/relay-client.ts +26 -16
- package/server/events/resolveRelayBridgeOptions.ts +125 -0
- package/server/index.ts +72 -49
- package/server/system/config.ts +5 -5
- package/server/system/credentials.ts +7 -5
- package/server/system/env.ts +15 -5
- package/server/system/macosNotify.ts +152 -0
- package/server/utils/errors.ts +11 -2
- package/server/utils/fetch.ts +54 -0
- package/server/utils/files/atomic.ts +18 -17
- package/server/utils/files/image-store.ts +19 -13
- package/server/utils/files/journal-io.ts +2 -2
- package/server/utils/files/json.ts +5 -5
- package/server/utils/files/markdown-image-fill.ts +131 -0
- package/server/utils/files/markdown-store.ts +22 -6
- package/server/utils/files/naming.ts +20 -10
- package/server/utils/files/reference-dirs-io.ts +3 -3
- package/server/utils/files/roles-io.ts +4 -4
- package/server/utils/files/safe.ts +14 -14
- package/server/utils/files/scheduler-overrides-io.ts +2 -2
- package/server/utils/files/spreadsheet-store.ts +15 -10
- package/server/utils/files/workspace-io.ts +12 -12
- package/server/utils/gemini.ts +30 -4
- package/server/utils/gitignore.ts +9 -9
- package/server/utils/id.ts +40 -8
- package/server/utils/json.ts +5 -5
- package/server/utils/logBackgroundError.ts +12 -3
- package/server/utils/logPreview.ts +24 -0
- 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/promptMeta.ts +32 -0
- package/server/utils/request.ts +12 -6
- package/server/utils/slug.ts +65 -4
- package/server/utils/spawn.ts +1 -1
- package/server/utils/types.ts +2 -2
- package/server/workspace/chat-index/index.ts +1 -1
- package/server/workspace/chat-index/summarizer.ts +5 -5
- package/server/workspace/custom-dirs.ts +5 -5
- package/server/workspace/helps/gemini.md +57 -0
- package/server/workspace/helps/index.md +2 -1
- package/server/workspace/helps/sources.md +42 -0
- package/server/workspace/helps/wiki.md +40 -5
- package/server/workspace/journal/archivist-cli.ts +121 -0
- package/server/workspace/journal/{archivist.ts → archivist-schemas.ts} +12 -120
- package/server/workspace/journal/dailyPass.ts +78 -38
- package/server/workspace/journal/diff.ts +2 -2
- package/server/workspace/journal/index.ts +56 -5
- package/server/workspace/journal/memoryExtractor.ts +1 -1
- package/server/workspace/journal/optimizationPass.ts +4 -5
- package/server/workspace/journal/paths.ts +8 -24
- package/server/workspace/journal/state.ts +18 -8
- package/server/workspace/news/reader.ts +248 -0
- package/server/workspace/paths.ts +4 -3
- package/server/workspace/reference-dirs.ts +3 -3
- package/server/workspace/skills/parser.ts +6 -6
- package/server/workspace/skills/scheduler.ts +5 -4
- package/server/workspace/skills/user-tasks.ts +3 -2
- package/server/workspace/skills/writer.ts +3 -3
- package/server/workspace/sources/arxivDiscovery.ts +2 -2
- package/server/workspace/sources/classifier.ts +1 -1
- package/server/workspace/sources/fetchers/rss.ts +5 -5
- package/server/workspace/sources/fetchers/rssParser.ts +4 -4
- package/server/workspace/sources/interests.ts +3 -3
- package/server/workspace/sources/paths.ts +6 -6
- package/server/workspace/sources/pipeline/fetch.ts +59 -13
- package/server/workspace/sources/pipeline/index.ts +59 -7
- package/server/workspace/sources/pipeline/notify.ts +13 -5
- package/server/workspace/sources/pipeline/plan.ts +11 -9
- package/server/workspace/sources/pipeline/summarize.ts +1 -1
- package/server/workspace/sources/pipeline/write.ts +5 -5
- package/server/workspace/sources/rateLimiter.ts +1 -1
- package/server/workspace/sources/sourceState.ts +9 -4
- 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/workspace.ts +7 -7
- package/src/App.vue +477 -251
- package/src/components/CanvasViewToggle.vue +12 -10
- package/src/components/ChatInput.vue +112 -105
- package/src/components/FileContentHeader.vue +10 -7
- package/src/components/FileContentRenderer.vue +37 -10
- package/src/components/FileTree.vue +34 -4
- package/src/components/FileTreePane.vue +32 -27
- package/src/components/FilesView.vue +5 -3
- package/src/components/FilterChip.vue +22 -0
- package/src/components/LockStatusPopup.vue +19 -13
- package/src/components/NewsView.vue +252 -0
- package/src/components/NotificationBell.vue +35 -9
- package/src/components/NotificationToast.vue +4 -1
- package/src/components/PageChatComposer.vue +101 -0
- package/src/components/PluginLauncher.vue +36 -62
- package/src/components/RightSidebar.vue +13 -10
- package/src/components/RoleSelector.vue +3 -2
- package/src/components/SessionHeaderControls.vue +63 -0
- package/src/components/SessionHistoryExpandButton.vue +30 -0
- package/src/components/SessionHistoryPanel.vue +64 -93
- package/src/components/SessionHistoryToggleButton.vue +40 -0
- package/src/components/SessionRoleIcon.vue +72 -0
- package/src/components/SessionSidebar.vue +96 -0
- package/src/components/SessionTabBar.vue +44 -51
- package/src/components/SettingsMcpTab.vue +361 -52
- package/src/components/SettingsModal.vue +203 -72
- package/src/components/SettingsReferenceDirsTab.vue +72 -51
- package/src/components/SettingsWorkspaceDirsTab.vue +74 -51
- package/src/components/SidebarHeader.vue +50 -16
- package/src/components/SourcesManager.vue +900 -0
- package/src/components/SourcesView.vue +45 -0
- package/src/components/StackView.vue +84 -48
- package/src/components/SuggestionsPanel.vue +25 -36
- package/src/components/SystemFileBanner.vue +106 -0
- package/src/components/ThinkingIndicator.vue +41 -0
- package/src/components/TodoExplorer.vue +72 -22
- package/src/components/todo/TodoAddDialog.vue +17 -12
- package/src/components/todo/TodoEditDialog.vue +7 -2
- package/src/components/todo/TodoEditPanel.vue +15 -10
- package/src/components/todo/TodoKanbanView.vue +16 -6
- package/src/components/todo/TodoListView.vue +14 -3
- package/src/components/todo/TodoTableView.vue +36 -5
- package/src/composables/favicon/conditions.ts +76 -0
- package/src/composables/favicon/resolveColor.ts +93 -0
- package/src/composables/favicon/types.ts +61 -0
- package/src/composables/useAppApi.ts +23 -0
- package/src/composables/useChatScroll.ts +5 -5
- package/src/composables/useCurrentRole.ts +32 -0
- package/src/composables/useDynamicFavicon.ts +174 -58
- package/src/composables/useEventListeners.ts +7 -12
- package/src/composables/useFaviconState.ts +93 -12
- package/src/composables/useFileSelection.ts +25 -6
- package/src/composables/useHealth.ts +76 -7
- package/src/composables/useLayoutMode.ts +27 -0
- package/src/composables/useNewsItems.ts +38 -0
- package/src/composables/useNewsReadState.ts +75 -0
- package/src/composables/useNotifications.ts +76 -13
- package/src/composables/usePendingCalls.ts +11 -1
- package/src/composables/useRoles.ts +6 -10
- package/src/composables/useRunElapsed.ts +80 -0
- package/src/composables/useSessionDerived.ts +21 -5
- package/src/composables/useSessionHistory.ts +7 -17
- package/src/composables/useSidePanelVisible.ts +25 -0
- package/src/composables/useViewLayout.ts +16 -37
- package/src/config/apiRoutes.ts +19 -6
- package/src/config/historyFilters.ts +30 -0
- package/src/config/mcpCatalog.ts +285 -0
- package/src/config/mcpTypes.ts +26 -0
- package/src/config/roles.ts +19 -51
- package/src/config/systemFileDescriptors.ts +170 -0
- package/src/config/toolNames.ts +6 -1
- package/src/config/workspacePaths.ts +1 -0
- package/src/index.css +14 -0
- package/src/lang/de.ts +706 -0
- package/src/lang/en.ts +726 -0
- package/src/lang/es.ts +712 -0
- package/src/lang/fr.ts +704 -0
- package/src/lang/ja.ts +707 -0
- package/src/lang/ko.ts +709 -0
- package/src/lang/pt-BR.ts +702 -0
- package/src/lang/zh.ts +705 -0
- package/src/lib/vue-i18n.ts +97 -0
- package/src/main.ts +3 -0
- package/src/plugins/canvas/View.vue +104 -186
- package/src/plugins/canvas/definition.ts +0 -8
- package/src/plugins/canvas/index.ts +3 -2
- package/src/plugins/chart/Preview.vue +1 -1
- package/src/plugins/chart/View.vue +9 -4
- package/src/plugins/chart/index.ts +3 -2
- package/src/plugins/editImage/index.ts +3 -2
- package/src/plugins/generateImage/index.ts +3 -2
- package/src/plugins/manageRoles/Preview.vue +4 -1
- package/src/plugins/manageRoles/View.vue +67 -46
- package/src/plugins/manageRoles/index.ts +3 -2
- package/src/plugins/manageSkills/Preview.vue +8 -3
- package/src/plugins/manageSkills/View.vue +39 -34
- package/src/plugins/manageSkills/index.ts +3 -2
- package/src/plugins/manageSource/Preview.vue +1 -1
- package/src/plugins/manageSource/View.vue +3 -687
- package/src/plugins/manageSource/index.ts +3 -2
- package/src/plugins/markdown/Preview.vue +1 -1
- package/src/plugins/markdown/View.vue +164 -73
- package/src/plugins/markdown/definition.ts +6 -4
- package/src/plugins/markdown/index.ts +3 -2
- package/src/plugins/presentForm/Preview.vue +99 -0
- package/src/plugins/presentForm/View.vue +675 -0
- package/src/plugins/presentForm/definition.ts +127 -0
- package/src/plugins/presentForm/index.ts +18 -0
- package/src/plugins/presentForm/plugin.ts +94 -0
- package/src/plugins/presentForm/types.ts +109 -0
- package/src/plugins/presentHtml/Preview.vue +1 -1
- package/src/plugins/presentHtml/View.vue +7 -4
- package/src/plugins/presentHtml/index.ts +3 -2
- package/src/plugins/presentMulmoScript/Preview.vue +1 -1
- package/src/plugins/presentMulmoScript/View.vue +36 -26
- package/src/plugins/presentMulmoScript/index.ts +3 -2
- package/src/plugins/scheduler/AutomationsPreview.vue +37 -0
- package/src/plugins/scheduler/AutomationsView.vue +23 -0
- package/src/plugins/scheduler/CalendarView.vue +23 -0
- package/src/plugins/scheduler/LegacySchedulerView.vue +32 -0
- package/src/plugins/scheduler/Preview.vue +7 -4
- package/src/plugins/scheduler/TasksTab.vue +119 -28
- package/src/plugins/scheduler/View.vue +75 -32
- package/src/plugins/scheduler/automationsDefinition.ts +58 -0
- package/src/plugins/scheduler/calendarDefinition.ts +46 -0
- package/src/plugins/scheduler/formatSchedule.ts +93 -0
- package/src/plugins/scheduler/index.ts +68 -14
- package/src/plugins/scheduler/legacyShape.ts +34 -0
- package/src/plugins/spreadsheet/Preview.vue +9 -5
- package/src/plugins/spreadsheet/View.vue +43 -57
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +2 -1
- package/src/plugins/spreadsheet/index.ts +3 -2
- package/src/plugins/textResponse/Preview.vue +15 -58
- package/src/plugins/textResponse/View.vue +42 -45
- package/src/plugins/textResponse/utils.ts +25 -0
- package/src/plugins/todo/Preview.vue +11 -6
- package/src/plugins/todo/View.vue +27 -13
- package/src/plugins/todo/composables/useTodos.ts +3 -1
- package/src/plugins/todo/index.ts +3 -2
- package/src/plugins/ui-image/ImagePreview.vue +6 -3
- package/src/plugins/ui-image/ImageView.vue +7 -4
- package/src/plugins/wiki/Preview.vue +5 -2
- package/src/plugins/wiki/View.vue +539 -92
- package/src/plugins/wiki/index.ts +5 -2
- package/src/plugins/wiki/route.ts +121 -0
- package/src/router/guards.ts +43 -24
- package/src/router/index.ts +53 -26
- package/src/router/pageRoutes.ts +23 -0
- package/src/tools/index.ts +12 -5
- package/src/tools/legacyPluginNames.ts +13 -0
- package/src/types/notification.ts +31 -6
- package/src/types/vue-i18n.d.ts +20 -0
- package/src/utils/agent/eventDispatch.ts +3 -6
- package/src/utils/agent/formatElapsed.ts +37 -0
- package/src/utils/agent/request.ts +22 -1
- package/src/utils/canvas/layoutMode.ts +26 -0
- package/src/utils/canvas/sidePanelVisible.ts +19 -0
- package/src/utils/dom/scrollIntoViewByTestId.ts +38 -0
- package/src/utils/errors.ts +9 -2
- package/src/utils/files/filename.ts +24 -0
- package/src/utils/filesPreview/schedulerPreview.ts +9 -3
- package/src/utils/id.ts +18 -0
- package/src/utils/image/cacheBust.ts +16 -0
- package/src/utils/image/resolve.ts +16 -0
- package/src/utils/markdown/taskList.ts +175 -0
- package/src/utils/mcp/interpolateSpec.ts +97 -0
- package/src/utils/notification/dispatch.ts +51 -15
- package/src/utils/path/workspaceLinkRouter.ts +99 -0
- package/src/utils/session/mergeSessions.ts +5 -0
- package/src/utils/sources/filter.ts +69 -0
- 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-Bm70FDU2.css +0 -1
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +0 -1
- package/server/workspace/journal/linkRewrite.ts +0 -4
- package/src/components/ToolResultsPanel.vue +0 -77
- package/src/composables/useCanvasViewMode.ts +0 -121
- package/src/plugins/scheduler/definition.ts +0 -57
- package/src/utils/canvas/viewMode.ts +0 -46
- package/src/utils/role/plugins.ts +0 -12
- package/src/utils/session/seedRoleDefault.ts +0 -35
- /package/client/assets/{purify.es-Fx1Nqyry-PeS5RUhs.js → purify.es-Fx1Nqyry-BwJECkqS.js} +0 -0
|
@@ -6,6 +6,7 @@ import View from "./View.vue";
|
|
|
6
6
|
import Preview from "./Preview.vue";
|
|
7
7
|
import { apiPost } from "../../utils/api";
|
|
8
8
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
9
|
+
import { makeUuid } from "../../utils/id";
|
|
9
10
|
|
|
10
11
|
function createUploadedImageResult(imageData: string, fileName: string, prompt: string): ToolResult<ImageToolData, never> {
|
|
11
12
|
return {
|
|
@@ -24,14 +25,14 @@ const generateImagePlugin: ToolPlugin<ImageToolData> = {
|
|
|
24
25
|
if (!result.ok) {
|
|
25
26
|
return {
|
|
26
27
|
toolName: TOOL_NAME,
|
|
27
|
-
uuid:
|
|
28
|
+
uuid: makeUuid(),
|
|
28
29
|
message: result.error,
|
|
29
30
|
};
|
|
30
31
|
}
|
|
31
32
|
return {
|
|
32
33
|
...result.data,
|
|
33
34
|
toolName: TOOL_NAME,
|
|
34
|
-
uuid:
|
|
35
|
+
uuid: makeUuid(),
|
|
35
36
|
};
|
|
36
37
|
},
|
|
37
38
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="text-sm">
|
|
3
3
|
<div class="flex items-center gap-1 font-medium text-gray-700 mb-1">
|
|
4
4
|
<span class="material-icons" style="font-size: 14px">manage_accounts</span>
|
|
5
|
-
<span>{{ customRoles.length
|
|
5
|
+
<span>{{ t("pluginManageRoles.previewCount", customRoles.length, { named: { count: customRoles.length } }) }}</span>
|
|
6
6
|
</div>
|
|
7
7
|
<div v-for="role in customRoles" :key="role.id" class="text-xs text-gray-600 flex items-center gap-1">
|
|
8
8
|
<span class="material-icons" style="font-size: 12px">{{ role.icon }}</span>
|
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
|
|
14
14
|
<script setup lang="ts">
|
|
15
15
|
import { ref, watch } from "vue";
|
|
16
|
+
import { useI18n } from "vue-i18n";
|
|
16
17
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
18
|
+
|
|
19
|
+
const { t } = useI18n();
|
|
17
20
|
import type { ManageRolesData, CustomRole } from "./index";
|
|
18
21
|
import { useFreshPluginData } from "../../composables/useFreshPluginData";
|
|
19
22
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full bg-white flex flex-col">
|
|
3
|
-
<div class="flex items-center justify-between px-
|
|
4
|
-
<h2 class="text-lg font-semibold text-gray-800">
|
|
5
|
-
<div class="flex items-center gap-
|
|
6
|
-
<span class="text-sm text-gray-500">
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
<div class="flex items-center justify-between gap-2 px-3 py-2 border-b border-gray-100">
|
|
4
|
+
<h2 class="text-lg font-semibold text-gray-800">{{ t("pluginManageRoles.heading") }}</h2>
|
|
5
|
+
<div class="flex items-center gap-2">
|
|
6
|
+
<span class="text-sm text-gray-500">{{ t("pluginManageRoles.roleCount", customRoles.length, { named: { count: customRoles.length } }) }}</span>
|
|
7
|
+
<button
|
|
8
|
+
v-if="!creating"
|
|
9
|
+
data-testid="role-add-btn"
|
|
10
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded bg-blue-500 text-white hover:bg-blue-600"
|
|
11
|
+
@click="startCreate"
|
|
12
|
+
>
|
|
13
|
+
{{ t("pluginManageRoles.addButton") }}
|
|
12
14
|
</button>
|
|
13
15
|
</div>
|
|
14
16
|
</div>
|
|
@@ -16,12 +18,12 @@
|
|
|
16
18
|
<div class="flex-1 overflow-y-auto">
|
|
17
19
|
<!-- New role creation panel -->
|
|
18
20
|
<div v-if="creating" class="m-4 border border-blue-300 bg-blue-50 rounded-lg p-4 space-y-3">
|
|
19
|
-
<div class="text-sm font-semibold text-gray-700">
|
|
21
|
+
<div class="text-sm font-semibold text-gray-700">{{ t("pluginManageRoles.createPanel") }}</div>
|
|
20
22
|
|
|
21
23
|
<!-- ID + Name + Icon row -->
|
|
22
24
|
<div class="flex gap-3">
|
|
23
25
|
<div class="w-40">
|
|
24
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
26
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldId") }}</label>
|
|
25
27
|
<input
|
|
26
28
|
v-model="newForm.id"
|
|
27
29
|
type="text"
|
|
@@ -30,7 +32,7 @@
|
|
|
30
32
|
/>
|
|
31
33
|
</div>
|
|
32
34
|
<div class="flex-1">
|
|
33
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
35
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldName") }}</label>
|
|
34
36
|
<input
|
|
35
37
|
v-model="newForm.name"
|
|
36
38
|
type="text"
|
|
@@ -39,8 +41,10 @@
|
|
|
39
41
|
</div>
|
|
40
42
|
<div class="w-32">
|
|
41
43
|
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
42
|
-
|
|
43
|
-
<a class="text-blue-400 font-normal ml-1" href="https://fonts.google.com/icons" target="_blank" rel="noopener"
|
|
44
|
+
{{ t("pluginManageRoles.fieldIcon") }}
|
|
45
|
+
<a class="text-blue-400 font-normal ml-1" href="https://fonts.google.com/icons" target="_blank" rel="noopener">{{
|
|
46
|
+
t("pluginManageRoles.helpLink")
|
|
47
|
+
}}</a>
|
|
44
48
|
</label>
|
|
45
49
|
<input
|
|
46
50
|
v-model="newForm.icon"
|
|
@@ -52,7 +56,7 @@
|
|
|
52
56
|
|
|
53
57
|
<!-- Prompt -->
|
|
54
58
|
<div>
|
|
55
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
59
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldPrompt") }}</label>
|
|
56
60
|
<textarea
|
|
57
61
|
v-model="newForm.prompt"
|
|
58
62
|
rows="6"
|
|
@@ -63,14 +67,14 @@
|
|
|
63
67
|
|
|
64
68
|
<!-- Plugins -->
|
|
65
69
|
<div>
|
|
66
|
-
<label class="block text-xs font-medium text-gray-600 mb-2">
|
|
70
|
+
<label class="block text-xs font-medium text-gray-600 mb-2">{{ t("pluginManageRoles.fieldPlugins") }}</label>
|
|
67
71
|
<div class="grid gap-x-4 gap-y-1 [grid-template-columns:repeat(auto-fit,minmax(180px,1fr))]">
|
|
68
72
|
<label
|
|
69
73
|
v-for="plugin in availablePlugins"
|
|
70
74
|
:key="plugin.name"
|
|
71
75
|
class="flex items-center gap-2 text-sm cursor-pointer"
|
|
72
76
|
:class="plugin.enabled ? 'text-gray-700' : 'text-gray-400 cursor-not-allowed'"
|
|
73
|
-
:title="plugin.enabled ? '' :
|
|
77
|
+
:title="plugin.enabled ? '' : t('pluginManageRoles.requiresEnv', { env: plugin.requiredEnv.join(', ') })"
|
|
74
78
|
>
|
|
75
79
|
<input
|
|
76
80
|
v-model="newForm.selectedPlugins"
|
|
@@ -80,7 +84,7 @@
|
|
|
80
84
|
class="cursor-pointer disabled:cursor-not-allowed"
|
|
81
85
|
/>
|
|
82
86
|
{{ plugin.name }}
|
|
83
|
-
<span v-if="!plugin.enabled" class="text-xs text-gray-400">(
|
|
87
|
+
<span v-if="!plugin.enabled" class="text-xs text-gray-400">{{ t("pluginManageRoles.missingEnv", { env: plugin.requiredEnv.join(", ") }) }}</span>
|
|
84
88
|
</label>
|
|
85
89
|
</div>
|
|
86
90
|
</div>
|
|
@@ -88,8 +92,8 @@
|
|
|
88
92
|
<!-- Starter queries -->
|
|
89
93
|
<div>
|
|
90
94
|
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
91
|
-
|
|
92
|
-
<span class="text-gray-400 font-normal">(
|
|
95
|
+
{{ t("pluginManageRoles.fieldStarterQueries") }}
|
|
96
|
+
<span class="text-gray-400 font-normal">{{ t("pluginManageRoles.onePerLine") }}</span>
|
|
93
97
|
</label>
|
|
94
98
|
<textarea
|
|
95
99
|
v-model="newForm.queriesText"
|
|
@@ -106,9 +110,11 @@
|
|
|
106
110
|
:title="newFormError ?? ''"
|
|
107
111
|
@click="saveNew"
|
|
108
112
|
>
|
|
109
|
-
{{ saving ? "
|
|
113
|
+
{{ saving ? t("pluginManageRoles.creating") : t("pluginManageRoles.create") }}
|
|
114
|
+
</button>
|
|
115
|
+
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="cancelCreate">
|
|
116
|
+
{{ t("common.cancel") }}
|
|
110
117
|
</button>
|
|
111
|
-
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="cancelCreate">Cancel</button>
|
|
112
118
|
</div>
|
|
113
119
|
<div v-if="newFormError" class="text-xs text-gray-500" data-testid="role-form-hint">
|
|
114
120
|
{{ newFormError }}
|
|
@@ -119,7 +125,7 @@
|
|
|
119
125
|
</div>
|
|
120
126
|
|
|
121
127
|
<div v-if="!creating && customRoles.length === 0" class="h-full flex items-center justify-center text-gray-400 text-sm">
|
|
122
|
-
|
|
128
|
+
{{ t("pluginManageRoles.emptyHint") }}
|
|
123
129
|
</div>
|
|
124
130
|
|
|
125
131
|
<ul v-if="customRoles.length > 0" class="p-4 space-y-2">
|
|
@@ -134,13 +140,16 @@
|
|
|
134
140
|
<div class="flex-1 min-w-0">
|
|
135
141
|
<div class="font-medium text-sm text-gray-800">
|
|
136
142
|
{{ role.name }}
|
|
137
|
-
<span class="ml-1 text-xs font-mono text-gray-400">
|
|
143
|
+
<span class="ml-1 text-xs font-mono text-gray-400">{{ t("pluginManageRoles.idFormatted", { id: role.id }) }}</span>
|
|
138
144
|
</div>
|
|
139
145
|
<div class="text-xs text-gray-400 truncate">
|
|
140
146
|
{{ role.availablePlugins.join(", ") }}
|
|
141
147
|
</div>
|
|
142
148
|
</div>
|
|
143
|
-
<span
|
|
149
|
+
<span
|
|
150
|
+
class="material-icons text-gray-400 text-sm"
|
|
151
|
+
:title="selectedId === role.id ? t('pluginManageRoles.collapse') : t('pluginManageRoles.expand')"
|
|
152
|
+
>
|
|
144
153
|
{{ selectedId === role.id ? "expand_less" : "expand_more" }}
|
|
145
154
|
</span>
|
|
146
155
|
</div>
|
|
@@ -150,7 +159,7 @@
|
|
|
150
159
|
<!-- ID + Name + Icon row -->
|
|
151
160
|
<div class="flex gap-3">
|
|
152
161
|
<div class="w-40">
|
|
153
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
162
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldId") }}</label>
|
|
154
163
|
<input
|
|
155
164
|
v-model="editForm.id"
|
|
156
165
|
type="text"
|
|
@@ -158,7 +167,7 @@
|
|
|
158
167
|
/>
|
|
159
168
|
</div>
|
|
160
169
|
<div class="flex-1">
|
|
161
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
170
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldName") }}</label>
|
|
162
171
|
<input
|
|
163
172
|
v-model="editForm.name"
|
|
164
173
|
type="text"
|
|
@@ -168,8 +177,10 @@
|
|
|
168
177
|
</div>
|
|
169
178
|
<div class="w-32">
|
|
170
179
|
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
171
|
-
|
|
172
|
-
<a class="text-blue-400 font-normal ml-1" href="https://fonts.google.com/icons" target="_blank" rel="noopener"
|
|
180
|
+
{{ t("pluginManageRoles.fieldIcon") }}
|
|
181
|
+
<a class="text-blue-400 font-normal ml-1" href="https://fonts.google.com/icons" target="_blank" rel="noopener">{{
|
|
182
|
+
t("pluginManageRoles.helpLink")
|
|
183
|
+
}}</a>
|
|
173
184
|
</label>
|
|
174
185
|
<input
|
|
175
186
|
v-model="editForm.icon"
|
|
@@ -181,7 +192,7 @@
|
|
|
181
192
|
|
|
182
193
|
<!-- Prompt -->
|
|
183
194
|
<div>
|
|
184
|
-
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
195
|
+
<label class="block text-xs font-medium text-gray-600 mb-1">{{ t("pluginManageRoles.fieldPrompt") }}</label>
|
|
185
196
|
<textarea
|
|
186
197
|
v-model="editForm.prompt"
|
|
187
198
|
rows="6"
|
|
@@ -192,14 +203,14 @@
|
|
|
192
203
|
|
|
193
204
|
<!-- Plugins -->
|
|
194
205
|
<div>
|
|
195
|
-
<label class="block text-xs font-medium text-gray-600 mb-2">
|
|
206
|
+
<label class="block text-xs font-medium text-gray-600 mb-2">{{ t("pluginManageRoles.fieldPlugins") }}</label>
|
|
196
207
|
<div class="grid gap-x-4 gap-y-1 [grid-template-columns:repeat(auto-fit,minmax(180px,1fr))]">
|
|
197
208
|
<label
|
|
198
209
|
v-for="plugin in availablePlugins"
|
|
199
210
|
:key="plugin.name"
|
|
200
211
|
class="flex items-center gap-2 text-sm cursor-pointer"
|
|
201
212
|
:class="plugin.enabled ? 'text-gray-700' : 'text-gray-400 cursor-not-allowed'"
|
|
202
|
-
:title="plugin.enabled ? '' :
|
|
213
|
+
:title="plugin.enabled ? '' : t('pluginManageRoles.requiresEnv', { env: plugin.requiredEnv.join(', ') })"
|
|
203
214
|
>
|
|
204
215
|
<input
|
|
205
216
|
v-model="editForm.selectedPlugins"
|
|
@@ -209,7 +220,9 @@
|
|
|
209
220
|
class="cursor-pointer disabled:cursor-not-allowed"
|
|
210
221
|
/>
|
|
211
222
|
{{ plugin.name }}
|
|
212
|
-
<span v-if="!plugin.enabled" class="text-xs text-gray-400">
|
|
223
|
+
<span v-if="!plugin.enabled" class="text-xs text-gray-400">{{
|
|
224
|
+
t("pluginManageRoles.missingEnv", { env: plugin.requiredEnv.join(", ") })
|
|
225
|
+
}}</span>
|
|
213
226
|
</label>
|
|
214
227
|
</div>
|
|
215
228
|
</div>
|
|
@@ -217,8 +230,8 @@
|
|
|
217
230
|
<!-- Starter queries -->
|
|
218
231
|
<div>
|
|
219
232
|
<label class="block text-xs font-medium text-gray-600 mb-1">
|
|
220
|
-
|
|
221
|
-
<span class="text-gray-400 font-normal">(
|
|
233
|
+
{{ t("pluginManageRoles.fieldStarterQueries") }}
|
|
234
|
+
<span class="text-gray-400 font-normal">{{ t("pluginManageRoles.onePerLine") }}</span>
|
|
222
235
|
</label>
|
|
223
236
|
<textarea
|
|
224
237
|
v-model="editForm.queriesText"
|
|
@@ -236,16 +249,18 @@
|
|
|
236
249
|
:title="editFormError ?? ''"
|
|
237
250
|
@click="saveEdit(role.id)"
|
|
238
251
|
>
|
|
239
|
-
{{ saving ? "
|
|
252
|
+
{{ saving ? t("pluginManageRoles.updating") : t("pluginManageRoles.update") }}
|
|
253
|
+
</button>
|
|
254
|
+
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="selectedId = null">
|
|
255
|
+
{{ t("common.cancel") }}
|
|
240
256
|
</button>
|
|
241
|
-
<button class="px-3 py-1.5 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50" @click="selectedId = null">Cancel</button>
|
|
242
257
|
</div>
|
|
243
258
|
<button
|
|
244
259
|
class="px-3 py-1.5 text-sm rounded border border-red-200 text-red-500 hover:bg-red-50 disabled:opacity-50"
|
|
245
260
|
:disabled="saving"
|
|
246
261
|
@click="deleteRole(role.id)"
|
|
247
262
|
>
|
|
248
|
-
|
|
263
|
+
{{ t("pluginManageRoles.delete") }}
|
|
249
264
|
</button>
|
|
250
265
|
</div>
|
|
251
266
|
<div v-if="editFormError" class="text-xs text-gray-500">
|
|
@@ -263,6 +278,7 @@
|
|
|
263
278
|
|
|
264
279
|
<script setup lang="ts">
|
|
265
280
|
import { ref, computed, watch, onMounted } from "vue";
|
|
281
|
+
import { useI18n } from "vue-i18n";
|
|
266
282
|
import { useFreshPluginData } from "../../composables/useFreshPluginData";
|
|
267
283
|
import { useAppApi } from "../../composables/useAppApi";
|
|
268
284
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
@@ -271,6 +287,8 @@ import { getAllPluginNames } from "../../tools/index";
|
|
|
271
287
|
import { apiGet, apiPost } from "../../utils/api";
|
|
272
288
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
273
289
|
|
|
290
|
+
const { t } = useI18n();
|
|
291
|
+
|
|
274
292
|
interface PluginEntry {
|
|
275
293
|
name: string;
|
|
276
294
|
enabled: boolean;
|
|
@@ -406,7 +424,10 @@ async function callManage(body: Record<string, unknown>): Promise<ManageResult>
|
|
|
406
424
|
// give us anything useful.
|
|
407
425
|
return {
|
|
408
426
|
success: false,
|
|
409
|
-
error:
|
|
427
|
+
error:
|
|
428
|
+
result.status === 0
|
|
429
|
+
? result.error || t("pluginManageRoles.errNetworkError")
|
|
430
|
+
: result.error || t("pluginManageRoles.errServerError", { status: result.status }),
|
|
410
431
|
};
|
|
411
432
|
}
|
|
412
433
|
return result.data;
|
|
@@ -432,13 +453,13 @@ async function refreshList() {
|
|
|
432
453
|
function validateRoleForm(form: EditForm, excludeId: string | null): string | null {
|
|
433
454
|
const trimmedId = form.id.trim();
|
|
434
455
|
const trimmedName = form.name.trim();
|
|
435
|
-
if (!trimmedId) return "
|
|
456
|
+
if (!trimmedId) return t("pluginManageRoles.errIdRequired");
|
|
436
457
|
if (!/^[a-zA-Z0-9_-]+$/.test(trimmedId)) {
|
|
437
|
-
return "
|
|
458
|
+
return t("pluginManageRoles.errIdInvalid");
|
|
438
459
|
}
|
|
439
|
-
if (!trimmedName) return "
|
|
460
|
+
if (!trimmedName) return t("pluginManageRoles.errNameRequired");
|
|
440
461
|
if (customRoles.value.some((existing) => existing.id === trimmedId && existing.id !== excludeId)) {
|
|
441
|
-
return
|
|
462
|
+
return t("pluginManageRoles.errIdDuplicate", { id: trimmedId });
|
|
442
463
|
}
|
|
443
464
|
return null;
|
|
444
465
|
}
|
|
@@ -473,7 +494,7 @@ async function saveNew() {
|
|
|
473
494
|
creating.value = false;
|
|
474
495
|
await refreshList();
|
|
475
496
|
} else {
|
|
476
|
-
createError.value = result.error ?? "
|
|
497
|
+
createError.value = result.error ?? t("pluginManageRoles.errCreateFailed");
|
|
477
498
|
}
|
|
478
499
|
saving.value = false;
|
|
479
500
|
}
|
|
@@ -505,7 +526,7 @@ async function saveEdit(originalId: string) {
|
|
|
505
526
|
selectedId.value = null;
|
|
506
527
|
await refreshList();
|
|
507
528
|
} else {
|
|
508
|
-
saveError.value = result.error ?? "
|
|
529
|
+
saveError.value = result.error ?? t("pluginManageRoles.errSaveFailed");
|
|
509
530
|
}
|
|
510
531
|
saving.value = false;
|
|
511
532
|
}
|
|
@@ -518,7 +539,7 @@ async function deleteRole(roleId: string) {
|
|
|
518
539
|
selectedId.value = null;
|
|
519
540
|
await refreshList();
|
|
520
541
|
} else {
|
|
521
|
-
saveError.value = result.error ?? "
|
|
542
|
+
saveError.value = result.error ?? t("pluginManageRoles.errDeleteFailed");
|
|
522
543
|
}
|
|
523
544
|
saving.value = false;
|
|
524
545
|
}
|
|
@@ -6,6 +6,7 @@ import View from "./View.vue";
|
|
|
6
6
|
import Preview from "./Preview.vue";
|
|
7
7
|
import { apiPost } from "../../utils/api";
|
|
8
8
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
9
|
+
import { makeUuid } from "../../utils/id";
|
|
9
10
|
|
|
10
11
|
export interface CustomRole {
|
|
11
12
|
id: string;
|
|
@@ -27,14 +28,14 @@ const manageRolesPlugin: ToolPlugin = {
|
|
|
27
28
|
if (!result.ok) {
|
|
28
29
|
return {
|
|
29
30
|
toolName: TOOL_NAME,
|
|
30
|
-
uuid:
|
|
31
|
+
uuid: makeUuid(),
|
|
31
32
|
message: result.error,
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
35
36
|
...result.data,
|
|
36
37
|
toolName: TOOL_NAME,
|
|
37
|
-
uuid:
|
|
38
|
+
uuid: makeUuid(),
|
|
38
39
|
};
|
|
39
40
|
},
|
|
40
41
|
isEnabled: () => true,
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="text-sm">
|
|
2
|
+
<div class="p-2 text-sm">
|
|
3
3
|
<div class="flex items-center gap-1 font-medium text-gray-700 mb-1">
|
|
4
4
|
<span class="material-icons" style="font-size: 14px">auto_awesome</span>
|
|
5
|
-
<span>{{ skills.length
|
|
5
|
+
<span>{{ t("pluginManageSkills.previewCount", skills.length, { named: { count: skills.length } }) }}</span>
|
|
6
6
|
</div>
|
|
7
7
|
<div v-for="skill in skills.slice(0, 6)" :key="skill.name" class="text-xs text-gray-600 truncate">
|
|
8
8
|
{{ skill.name }}
|
|
9
9
|
</div>
|
|
10
|
-
<div v-if="skills.length > 6" class="text-xs text-gray-400 italic"
|
|
10
|
+
<div v-if="skills.length > 6" class="text-xs text-gray-400 italic">
|
|
11
|
+
{{ t("pluginManageSkills.previewMore", { count: skills.length - 6 }) }}
|
|
12
|
+
</div>
|
|
11
13
|
</div>
|
|
12
14
|
</template>
|
|
13
15
|
|
|
14
16
|
<script setup lang="ts">
|
|
15
17
|
import { computed } from "vue";
|
|
18
|
+
import { useI18n } from "vue-i18n";
|
|
16
19
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
17
20
|
import type { ManageSkillsData } from "./index";
|
|
18
21
|
|
|
22
|
+
const { t } = useI18n();
|
|
23
|
+
|
|
19
24
|
const props = defineProps<{ result: ToolResultComplete<ManageSkillsData> }>();
|
|
20
25
|
const skills = computed(() => props.result.data?.skills ?? []);
|
|
21
26
|
</script>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="h-full bg-white flex flex-col overflow-hidden">
|
|
3
3
|
<!-- Header -->
|
|
4
|
-
<div class="flex items-center justify-between px-
|
|
4
|
+
<div class="flex items-center justify-between gap-2 px-3 py-2 border-b border-gray-100 shrink-0">
|
|
5
5
|
<div>
|
|
6
|
-
<h2 class="text-lg font-semibold text-gray-800">
|
|
7
|
-
<p class="text-xs text-gray-400 mt-0.5">{{ skills.length }}
|
|
6
|
+
<h2 class="text-lg font-semibold text-gray-800">{{ t("pluginManageSkills.heading") }}</h2>
|
|
7
|
+
<p class="text-xs text-gray-400 mt-0.5">{{ t("pluginManageSkills.subheading", { count: skills.length }) }}</p>
|
|
8
8
|
</div>
|
|
9
9
|
</div>
|
|
10
10
|
|
|
@@ -32,15 +32,16 @@
|
|
|
32
32
|
{{ skill.source }}
|
|
33
33
|
</div>
|
|
34
34
|
</div>
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
<i18n-t v-if="skills.length === 0" keypath="pluginManageSkills.emptyWithPath" tag="p" class="p-4 text-sm text-gray-400 italic">
|
|
36
|
+
<template #path>
|
|
37
|
+
<code class="text-[11px]">{{ t("pluginManageSkills.emptySkillPath") }}</code>
|
|
38
|
+
</template>
|
|
39
|
+
</i18n-t>
|
|
39
40
|
</div>
|
|
40
41
|
|
|
41
42
|
<!-- Right: detail pane -->
|
|
42
43
|
<div class="flex-1 min-w-0 overflow-y-auto">
|
|
43
|
-
<div v-if="!selected" class="p-6 text-sm text-gray-400 italic">
|
|
44
|
+
<div v-if="!selected" class="p-6 text-sm text-gray-400 italic">{{ t("pluginManageSkills.selectHint") }}</div>
|
|
44
45
|
<div v-else class="p-6">
|
|
45
46
|
<div class="flex items-start justify-between gap-4 mb-4">
|
|
46
47
|
<div class="min-w-0">
|
|
@@ -54,64 +55,64 @@
|
|
|
54
55
|
<div class="flex items-center gap-2 shrink-0">
|
|
55
56
|
<template v-if="editing">
|
|
56
57
|
<button
|
|
57
|
-
class="
|
|
58
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50"
|
|
58
59
|
data-testid="skill-cancel-btn"
|
|
59
60
|
@click="cancelEdit"
|
|
60
61
|
>
|
|
61
|
-
|
|
62
|
+
{{ t("common.cancel") }}
|
|
62
63
|
</button>
|
|
63
64
|
<button
|
|
64
|
-
class="
|
|
65
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded bg-green-600 hover:bg-green-700 text-white disabled:opacity-40"
|
|
65
66
|
:disabled="saving"
|
|
66
67
|
data-testid="skill-save-btn"
|
|
67
68
|
@click="saveEdit"
|
|
68
69
|
>
|
|
69
|
-
<span class="material-icons text-
|
|
70
|
-
|
|
70
|
+
<span class="material-icons text-sm">save</span>
|
|
71
|
+
{{ t("common.save") }}
|
|
71
72
|
</button>
|
|
72
73
|
</template>
|
|
73
74
|
<template v-else>
|
|
74
75
|
<button
|
|
75
76
|
v-if="detail && detail.source === 'project'"
|
|
76
|
-
class="
|
|
77
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-gray-300 text-gray-600 hover:bg-gray-50 disabled:opacity-40"
|
|
77
78
|
:disabled="detailLoading"
|
|
78
79
|
data-testid="skill-edit-btn"
|
|
79
80
|
@click="startEdit"
|
|
80
81
|
>
|
|
81
|
-
<span class="material-icons text-
|
|
82
|
-
|
|
82
|
+
<span class="material-icons text-sm">edit</span>
|
|
83
|
+
{{ t("pluginManageSkills.btnEdit") }}
|
|
83
84
|
</button>
|
|
84
85
|
<button
|
|
85
86
|
v-if="detail && detail.source === 'project'"
|
|
86
|
-
class="
|
|
87
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded border border-red-300 text-red-600 hover:bg-red-50 disabled:opacity-40"
|
|
87
88
|
:disabled="detailLoading || deleting"
|
|
88
89
|
data-testid="skill-delete-btn"
|
|
89
|
-
title="
|
|
90
|
+
:title="t('pluginManageSkills.deleteProjectSkill')"
|
|
90
91
|
@click="deleteSkill"
|
|
91
92
|
>
|
|
92
|
-
<span class="material-icons text-
|
|
93
|
-
|
|
93
|
+
<span class="material-icons text-sm">delete</span>
|
|
94
|
+
{{ t("pluginManageSkills.btnDelete") }}
|
|
94
95
|
</button>
|
|
95
96
|
<button
|
|
96
|
-
class="
|
|
97
|
+
class="h-8 px-2.5 flex items-center gap-1 text-sm rounded bg-blue-600 hover:bg-blue-700 text-white disabled:opacity-40"
|
|
97
98
|
:disabled="detailLoading || !detail"
|
|
98
99
|
data-testid="skill-run-btn"
|
|
99
100
|
@click="runSkill"
|
|
100
101
|
>
|
|
101
|
-
<span class="material-icons text-
|
|
102
|
-
|
|
102
|
+
<span class="material-icons text-sm">play_arrow</span>
|
|
103
|
+
{{ t("pluginManageSkills.btnRun") }}
|
|
103
104
|
</button>
|
|
104
105
|
</template>
|
|
105
106
|
</div>
|
|
106
107
|
</div>
|
|
107
|
-
<div v-if="detailLoading" class="text-sm text-gray-400 italic">
|
|
108
|
+
<div v-if="detailLoading" class="text-sm text-gray-400 italic">{{ t("pluginManageSkills.loading") }}</div>
|
|
108
109
|
<div v-else-if="detailError" class="text-sm text-red-600">
|
|
109
110
|
{{ detailError }}
|
|
110
111
|
</div>
|
|
111
112
|
<!-- Edit mode -->
|
|
112
113
|
<div v-else-if="editing && detail" class="space-y-4">
|
|
113
114
|
<div>
|
|
114
|
-
<label class="block text-xs font-medium text-gray-500 mb-1">
|
|
115
|
+
<label class="block text-xs font-medium text-gray-500 mb-1"> {{ t("pluginManageSkills.fieldDescription") }} </label>
|
|
115
116
|
<input
|
|
116
117
|
v-model="editDescription"
|
|
117
118
|
data-testid="skill-edit-description"
|
|
@@ -119,7 +120,7 @@
|
|
|
119
120
|
/>
|
|
120
121
|
</div>
|
|
121
122
|
<div class="flex-1">
|
|
122
|
-
<label class="block text-xs font-medium text-gray-500 mb-1">
|
|
123
|
+
<label class="block text-xs font-medium text-gray-500 mb-1"> {{ t("pluginManageSkills.fieldBody") }} </label>
|
|
123
124
|
<textarea
|
|
124
125
|
v-model="editBody"
|
|
125
126
|
data-testid="skill-edit-body"
|
|
@@ -130,7 +131,7 @@
|
|
|
130
131
|
<!-- View mode -->
|
|
131
132
|
<!-- eslint-disable-next-line vue/no-v-html -- sanitized via DOMPurify -->
|
|
132
133
|
<div v-else-if="detail && renderedBody" class="markdown-content text-gray-700" data-testid="skill-body-rendered" v-html="renderedBody"></div>
|
|
133
|
-
<p v-else-if="detail" class="text-sm text-gray-400 italic">(
|
|
134
|
+
<p v-else-if="detail" class="text-sm text-gray-400 italic">{{ t("pluginManageSkills.emptyBody") }}</p>
|
|
134
135
|
</div>
|
|
135
136
|
</div>
|
|
136
137
|
</div>
|
|
@@ -139,6 +140,7 @@
|
|
|
139
140
|
|
|
140
141
|
<script setup lang="ts">
|
|
141
142
|
import { computed, onMounted, ref, watch } from "vue";
|
|
143
|
+
import { useI18n } from "vue-i18n";
|
|
142
144
|
import { marked } from "marked";
|
|
143
145
|
import DOMPurify from "dompurify";
|
|
144
146
|
import type { ToolResultComplete } from "gui-chat-protocol/vue";
|
|
@@ -147,6 +149,8 @@ import { useAppApi } from "../../composables/useAppApi";
|
|
|
147
149
|
import { apiGet, apiPut, apiDelete } from "../../utils/api";
|
|
148
150
|
import { API_ROUTES } from "../../config/apiRoutes";
|
|
149
151
|
|
|
152
|
+
const { t } = useI18n();
|
|
153
|
+
|
|
150
154
|
interface SkillDetail {
|
|
151
155
|
name: string;
|
|
152
156
|
description: string;
|
|
@@ -199,7 +203,7 @@ onMounted(async () => {
|
|
|
199
203
|
if (props.selectedResult || skills.value.length > 0) return;
|
|
200
204
|
const response = await apiGet<{ skills: SkillSummary[] }>(API_ROUTES.skills.list);
|
|
201
205
|
if (!response.ok) {
|
|
202
|
-
listError.value =
|
|
206
|
+
listError.value = t("pluginManageSkills.errListFailed", { error: response.error });
|
|
203
207
|
return;
|
|
204
208
|
}
|
|
205
209
|
if (Array.isArray(response.data.skills)) {
|
|
@@ -231,7 +235,7 @@ watch(
|
|
|
231
235
|
return;
|
|
232
236
|
}
|
|
233
237
|
if (!response.ok) {
|
|
234
|
-
detailError.value =
|
|
238
|
+
detailError.value = t("pluginManageSkills.errDetailFailed", { error: response.error });
|
|
235
239
|
detail.value = null;
|
|
236
240
|
} else {
|
|
237
241
|
detail.value = response.data.skill;
|
|
@@ -263,7 +267,7 @@ async function saveEdit(): Promise<void> {
|
|
|
263
267
|
});
|
|
264
268
|
saving.value = false;
|
|
265
269
|
if (!result.ok) {
|
|
266
|
-
detailError.value =
|
|
270
|
+
detailError.value = t("pluginManageSkills.errSaveFailed", { error: result.error });
|
|
267
271
|
return;
|
|
268
272
|
}
|
|
269
273
|
detail.value = {
|
|
@@ -285,12 +289,13 @@ async function saveEdit(): Promise<void> {
|
|
|
285
289
|
// Run = send the skill invocation as a Claude Code slash command.
|
|
286
290
|
// Claude CLI already knows about every ~/.claude/skills/<name>/SKILL.md
|
|
287
291
|
// at spawn, so sending `/<name>` is enough — no need to ship the body.
|
|
288
|
-
//
|
|
292
|
+
// Uses startNewChat (not sendMessage) so the user is routed to /chat
|
|
293
|
+
// to see the response — Skills view is only rendered on /skills.
|
|
289
294
|
const appApi = useAppApi();
|
|
290
295
|
|
|
291
296
|
function runSkill(): void {
|
|
292
297
|
if (!selectedName.value) return;
|
|
293
|
-
appApi.
|
|
298
|
+
appApi.startNewChat(`/${selectedName.value}`);
|
|
294
299
|
}
|
|
295
300
|
|
|
296
301
|
// Delete is project-scope only — see saveProjectSkill / deleteProjectSkill
|
|
@@ -300,14 +305,14 @@ function runSkill(): void {
|
|
|
300
305
|
async function deleteSkill(): Promise<void> {
|
|
301
306
|
if (!detail.value || detail.value.source !== "project") return;
|
|
302
307
|
const name = detail.value.name;
|
|
303
|
-
if (!window.confirm(
|
|
308
|
+
if (!window.confirm(t("pluginManageSkills.confirmDelete", { name }))) {
|
|
304
309
|
return;
|
|
305
310
|
}
|
|
306
311
|
deleting.value = true;
|
|
307
312
|
const result = await apiDelete<unknown>(API_ROUTES.skills.remove.replace(":name", encodeURIComponent(name)));
|
|
308
313
|
deleting.value = false;
|
|
309
314
|
if (!result.ok) {
|
|
310
|
-
detailError.value = result.error || "
|
|
315
|
+
detailError.value = result.error || t("pluginManageSkills.errDeleteFailed");
|
|
311
316
|
return;
|
|
312
317
|
}
|
|
313
318
|
// Remove from the local list, advance selection, clear detail.
|