codexmate 0.0.24 → 0.0.26

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.
Files changed (136) hide show
  1. package/README.md +416 -391
  2. package/README.zh.md +349 -324
  3. package/cli/agents-files.js +224 -224
  4. package/cli/archive-helpers.js +446 -446
  5. package/cli/auth-profiles.js +375 -375
  6. package/cli/builtin-proxy.js +1079 -1044
  7. package/cli/claude-proxy.js +1022 -998
  8. package/cli/config-bootstrap.js +384 -384
  9. package/cli/config-health.js +338 -338
  10. package/cli/doctor-core.js +903 -903
  11. package/cli/import-skills-url.js +356 -334
  12. package/cli/openai-bridge.js +997 -950
  13. package/cli/openclaw-config.js +629 -629
  14. package/cli/session-convert-args.js +65 -0
  15. package/cli/session-convert-io.js +82 -0
  16. package/cli/session-convert.js +43 -0
  17. package/cli/session-usage.concurrent.js +28 -28
  18. package/cli/session-usage.js +118 -112
  19. package/cli/session-usage.models.js +176 -176
  20. package/cli/skills.js +1141 -1141
  21. package/cli/zip-commands.js +510 -510
  22. package/cli.js +15218 -13310
  23. package/lib/automation.js +404 -0
  24. package/lib/cli-file-utils.js +151 -151
  25. package/lib/cli-models-utils.js +379 -379
  26. package/lib/cli-network-utils.js +190 -190
  27. package/lib/cli-path-utils.js +85 -69
  28. package/lib/cli-session-utils.js +121 -121
  29. package/lib/cli-sessions.js +417 -386
  30. package/lib/cli-utils.js +155 -155
  31. package/lib/download-artifacts.js +92 -77
  32. package/lib/mcp-stdio.js +453 -440
  33. package/lib/task-orchestrator.js +869 -869
  34. package/lib/text-diff.js +303 -303
  35. package/lib/workflow-engine.js +340 -340
  36. package/package.json +74 -77
  37. package/plugins/README.md +20 -20
  38. package/plugins/README.zh-CN.md +20 -20
  39. package/plugins/prompt-templates/comment-polish/index.mjs +25 -25
  40. package/plugins/prompt-templates/computed.mjs +253 -253
  41. package/plugins/prompt-templates/index.mjs +8 -8
  42. package/plugins/prompt-templates/manifest.mjs +15 -15
  43. package/plugins/prompt-templates/methods.mjs +619 -619
  44. package/plugins/prompt-templates/overview.mjs +90 -90
  45. package/plugins/prompt-templates/ownership.mjs +19 -19
  46. package/plugins/prompt-templates/rule-ack/index.mjs +21 -21
  47. package/plugins/prompt-templates/storage.mjs +64 -64
  48. package/plugins/registry.mjs +16 -16
  49. package/web-ui/app.js +625 -609
  50. package/web-ui/index.html +35 -35
  51. package/web-ui/logic.agents-diff.mjs +386 -386
  52. package/web-ui/logic.claude.mjs +168 -168
  53. package/web-ui/logic.mjs +5 -5
  54. package/web-ui/logic.runtime.mjs +128 -128
  55. package/web-ui/logic.session-convert.mjs +70 -0
  56. package/web-ui/logic.sessions.mjs +709 -614
  57. package/web-ui/modules/api.mjs +90 -90
  58. package/web-ui/modules/app.computed.dashboard.mjs +171 -126
  59. package/web-ui/modules/app.computed.index.mjs +17 -17
  60. package/web-ui/modules/app.computed.main-tabs.mjs +205 -205
  61. package/web-ui/modules/app.computed.session.mjs +946 -653
  62. package/web-ui/modules/app.constants.mjs +15 -15
  63. package/web-ui/modules/app.methods.agents.mjs +632 -632
  64. package/web-ui/modules/app.methods.claude-config.mjs +179 -174
  65. package/web-ui/modules/app.methods.codex-config.mjs +860 -784
  66. package/web-ui/modules/app.methods.index.mjs +92 -92
  67. package/web-ui/modules/app.methods.install.mjs +205 -177
  68. package/web-ui/modules/app.methods.navigation.mjs +743 -695
  69. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -814
  70. package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -372
  71. package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -369
  72. package/web-ui/modules/app.methods.providers.mjs +404 -404
  73. package/web-ui/modules/app.methods.runtime.mjs +345 -345
  74. package/web-ui/modules/app.methods.session-actions.mjs +596 -537
  75. package/web-ui/modules/app.methods.session-browser.mjs +985 -698
  76. package/web-ui/modules/app.methods.session-timeline.mjs +479 -448
  77. package/web-ui/modules/app.methods.session-trash.mjs +424 -422
  78. package/web-ui/modules/app.methods.startup-claude.mjs +522 -417
  79. package/web-ui/modules/app.methods.task-orchestration.mjs +556 -556
  80. package/web-ui/modules/config-mode.computed.mjs +124 -124
  81. package/web-ui/modules/config-template-confirm-pref.mjs +33 -33
  82. package/web-ui/modules/i18n.dict.mjs +2113 -2039
  83. package/web-ui/modules/i18n.mjs +56 -56
  84. package/web-ui/modules/plugins.computed.mjs +3 -3
  85. package/web-ui/modules/plugins.methods.mjs +3 -3
  86. package/web-ui/modules/plugins.storage.mjs +11 -11
  87. package/web-ui/modules/sessions-filters-url.mjs +85 -85
  88. package/web-ui/modules/skills.computed.mjs +107 -107
  89. package/web-ui/modules/skills.methods.mjs +481 -481
  90. package/web-ui/partials/index/layout-footer.html +13 -13
  91. package/web-ui/partials/index/layout-header.html +475 -465
  92. package/web-ui/partials/index/modal-config-template-agents.html +174 -174
  93. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  94. package/web-ui/partials/index/modal-health-check.html +45 -45
  95. package/web-ui/partials/index/modal-openclaw-config.html +280 -280
  96. package/web-ui/partials/index/modal-skills.html +200 -200
  97. package/web-ui/partials/index/modals-basic.html +165 -165
  98. package/web-ui/partials/index/panel-config-claude.html +184 -179
  99. package/web-ui/partials/index/panel-config-codex.html +283 -283
  100. package/web-ui/partials/index/panel-config-openclaw.html +83 -83
  101. package/web-ui/partials/index/panel-dashboard.html +186 -186
  102. package/web-ui/partials/index/panel-docs.html +147 -147
  103. package/web-ui/partials/index/panel-market.html +177 -177
  104. package/web-ui/partials/index/panel-orchestration.html +391 -391
  105. package/web-ui/partials/index/panel-plugins.html +279 -279
  106. package/web-ui/partials/index/panel-sessions.html +326 -301
  107. package/web-ui/partials/index/panel-settings.html +258 -258
  108. package/web-ui/partials/index/panel-usage.html +342 -354
  109. package/{res → web-ui/res}/json5.min.js +1 -1
  110. package/{res → web-ui/res}/vue.global.prod.js +13 -13
  111. package/web-ui/session-helpers.mjs +576 -573
  112. package/web-ui/source-bundle.cjs +233 -233
  113. package/web-ui/styles/base-theme.css +268 -264
  114. package/web-ui/styles/controls-forms.css +423 -376
  115. package/web-ui/styles/dashboard.css +274 -274
  116. package/web-ui/styles/docs-panel.css +247 -247
  117. package/web-ui/styles/feedback.css +108 -108
  118. package/web-ui/styles/health-check-dialog.css +144 -144
  119. package/web-ui/styles/layout-shell.css +603 -602
  120. package/web-ui/styles/modals-core.css +464 -464
  121. package/web-ui/styles/navigation-panels.css +390 -390
  122. package/web-ui/styles/openclaw-structured.css +266 -266
  123. package/web-ui/styles/plugins-panel.css +523 -523
  124. package/web-ui/styles/responsive.css +454 -456
  125. package/web-ui/styles/sessions-list.css +415 -400
  126. package/web-ui/styles/sessions-preview.css +411 -411
  127. package/web-ui/styles/sessions-toolbar-trash.css +330 -268
  128. package/web-ui/styles/sessions-usage.css +945 -888
  129. package/web-ui/styles/settings-panel.css +166 -166
  130. package/web-ui/styles/skills-list.css +303 -303
  131. package/web-ui/styles/skills-market.css +406 -406
  132. package/web-ui/styles/task-orchestration.css +822 -822
  133. package/web-ui/styles/titles-cards.css +408 -408
  134. package/web-ui/styles.css +21 -21
  135. package/web-ui.html +17 -17
  136. /package/{res → web-ui/res}/logo-pack.webp +0 -0
@@ -1,279 +1,279 @@
1
- <div
2
- v-show="mainTab === 'plugins'"
3
- class="mode-content"
4
- id="panel-plugins"
5
- role="tabpanel"
6
- aria-labelledby="tab-plugins">
7
- <div class="plugins-layout">
8
- <aside class="plugins-sidebar" :aria-label="t('plugins.sidebar.ariaList')">
9
- <div class="selector-header plugins-sidebar-header">
10
- <div>
11
- <span class="selector-title">{{ t('plugins.sidebar.title') }}</span>
12
- <div class="plugins-panel-note">{{ t('plugins.sidebar.note') }}</div>
13
- </div>
14
- <div class="settings-tab-actions">
15
- <button type="button" class="btn-tool btn-tool-compact" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">
16
- {{ pluginsLoading ? t('plugins.refreshing') : t('plugins.refresh') }}
17
- </button>
18
- </div>
19
- </div>
20
-
21
- <div class="plugins-list" role="list">
22
- <button
23
- v-for="plugin in pluginsCatalog"
24
- :key="'plugin-' + plugin.id"
25
- type="button"
26
- :class="['plugins-item', { active: pluginsActiveId === plugin.id }]"
27
- :aria-current="pluginsActiveId === plugin.id ? 'page' : null"
28
- :disabled="loading || !!initError || pluginsLoading"
29
- @click="selectPlugin(plugin.id)">
30
- <div class="plugins-item-main">
31
- <div class="plugins-item-title">{{ plugin.title }}</div>
32
- <div class="plugins-item-meta">{{ plugin.description }}</div>
33
- </div>
34
- <span :class="['pill', plugin.tone]">{{ plugin.statusLabel }}</span>
35
- </button>
36
- </div>
37
- </aside>
38
-
39
- <section class="plugins-main" :aria-label="t('plugins.main.ariaWorkspace')">
40
- <div v-if="pluginsLoading" class="skills-empty-state">{{ t('common.loading') }}</div>
41
- <div v-else-if="pluginsError" class="skills-empty-state">
42
- <div class="plugins-panel-note">{{ pluginsError }}</div>
43
- <button type="button" class="btn-mini" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">{{ t('common.refresh') }}</button>
44
- </div>
45
- <div v-else-if="pluginsActiveId === 'prompt-templates'" class="plugins-panel">
46
- <div class="plugins-panel-head">
47
- <div class="plugins-panel-title">{{ t('plugins.promptTemplates.title') }}</div>
48
- <div v-if="pluginsActiveAttribution" class="plugins-panel-note">{{ pluginsActiveAttribution }}</div>
49
- </div>
50
-
51
- <div class="prompt-templates-modebar" role="tablist" :aria-label="t('plugins.promptTemplates.mode.aria')">
52
- <button
53
- type="button"
54
- role="tab"
55
- :aria-selected="promptTemplatesMode === 'compose'"
56
- :class="['mode-pill', { active: promptTemplatesMode === 'compose' }]"
57
- @click="promptTemplatesMode = 'compose'">{{ t('plugins.promptTemplates.mode.compose') }}</button>
58
- <button
59
- type="button"
60
- role="tab"
61
- :aria-selected="promptTemplatesMode !== 'compose'"
62
- :class="['mode-pill', { active: promptTemplatesMode !== 'compose' }]"
63
- @click="promptTemplatesMode = 'manage'">{{ t('plugins.promptTemplates.mode.manage') }}</button>
64
- </div>
65
-
66
- <div v-if="promptTemplatesMode === 'compose'" class="prompt-compose">
67
- <div class="prompt-compose-workspace">
68
- <div class="prompt-compose-selected">
69
- <div class="prompt-compose-selected-title">{{ (promptComposerActiveTemplate && promptComposerActiveTemplate.name) ? promptComposerActiveTemplate.name : t('plugins.promptTemplates.compose.chooseTemplate') }}</div>
70
- <div class="prompt-compose-selected-meta">{{ (promptComposerActiveTemplate && promptComposerActiveTemplate.description) ? promptComposerActiveTemplate.description : t('plugins.promptTemplates.compose.chooseTemplateHint') }}</div>
71
- <div v-if="promptComposerActiveTemplate && promptComposerActiveTemplate.isBuiltin && (promptComposerActiveTemplate.createdBy || (promptComposerActiveTemplate.maintainers && promptComposerActiveTemplate.maintainers.length))" class="plugins-panel-note">
72
- {{ t('plugins.meta.attribution', { createdBy: promptComposerActiveTemplate.createdBy || '', maintainers: (promptComposerActiveTemplate.maintainers || []).join(', ') }) }}
73
- </div>
74
- </div>
75
-
76
- <div class="prompt-compose-form">
77
- <label class="form-label">{{ t('plugins.promptTemplates.compose.selectTemplate') }}</label>
78
- <select
79
- class="form-select prompt-compose-template-select"
80
- :value="promptComposerSelectedTemplateId"
81
- @change="selectPromptComposerTemplate($event.target.value)"
82
- :disabled="pluginsLoading || !promptTemplatesList.length">
83
- <option v-for="tpl in promptTemplatesList" :key="'compose-tpl-' + tpl.id" :value="tpl.id">
84
- {{ tpl.name }}{{ tpl.isBuiltin ? t('plugins.promptTemplates.compose.builtinSuffix') : '' }}
85
- </option>
86
- </select>
87
-
88
- <div v-if="!promptComposerActiveTemplate" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.empty') }}</div>
89
- <div v-else-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
90
- <div v-if="promptComposerActiveTemplate && !promptComposerActiveTemplate.isBuiltin" class="prompt-compose-actions">
91
- <button
92
- type="button"
93
- class="btn-mini"
94
- @click="selectPromptTemplate(promptComposerSelectedTemplateId)"
95
- :disabled="pluginsLoading || !promptComposerSelectedTemplateId">{{ t('plugins.promptTemplates.compose.goManage') }}</button>
96
- </div>
97
- </div>
98
-
99
- <div v-if="promptComposerActiveTemplate && promptComposerActiveTemplate.vars && promptComposerActiveTemplate.vars.length" class="prompt-vars-block">
100
- <div class="prompt-vars-head">
101
- <div>
102
- <div class="prompt-vars-title">{{ t('plugins.promptTemplates.vars.title') }}</div>
103
- <div v-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
104
- <div v-if="promptComposerMissingVars.length" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.missingCount', { count: promptComposerMissingVars.length }) }}</div>
105
- </div>
106
- <div class="prompt-editor-actions">
107
- <button v-if="promptComposerMissingVars.length" type="button" class="btn-mini" @click="focusPromptComposerFirstMissingVar" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.compose.jumpToMissing') }}</button>
108
- <button type="button" class="btn-mini" @click="resetPromptComposerVarValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
109
- </div>
110
- </div>
111
- <div class="prompt-vars-grid">
112
- <label v-for="(name, idx) in promptComposerActiveTemplate.vars" :key="'prompt-compose-var-' + name" class="prompt-var-row">
113
- <span class="prompt-var-label mono">{{ name }}</span>
114
- <template v-if="idx === 0">
115
- <input ref="promptComposerFirstField" :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
116
- </template>
117
- <template v-else>
118
- <input :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
119
- </template>
120
- </label>
121
- </div>
122
- </div>
123
-
124
- <div class="prompt-preview-block prompt-compose-preview">
125
- <div class="prompt-vars-head">
126
- <div>
127
- <div class="prompt-vars-title">{{ t('plugins.promptTemplates.compose.outputTitle') }}</div>
128
- <div class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.outputHint') }}</div>
129
- </div>
130
- <button type="button" class="btn-mini" @click="copyPromptComposerRendered" :disabled="pluginsLoading || !promptComposerRendered">{{ t('plugins.promptTemplates.compose.copy') }}</button>
131
- </div>
132
- <textarea class="form-input prompt-preview-textarea" :value="promptComposerRendered" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.compose.outputAria')"></textarea>
133
- </div>
134
- </div>
135
- </div>
136
-
137
- <div v-else class="prompt-templates-grid">
138
- <div class="prompt-templates-pane prompt-templates-pane-list">
139
- <div class="prompt-templates-toolbar">
140
- <input
141
- class="form-input"
142
- type="text"
143
- v-model.trim="promptTemplatesKeyword"
144
- :aria-label="t('plugins.promptTemplates.manage.searchAria')"
145
- :placeholder="t('plugins.promptTemplates.manage.searchPlaceholder')">
146
- <button type="button" class="btn-mini" @click="createPromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.manage.create') }}</button>
147
- <button type="button" class="btn-mini" @click="exportPromptTemplates" :disabled="pluginsLoading || !promptTemplatesList.length">{{ t('plugins.promptTemplates.manage.export') }}</button>
148
- <button type="button" class="btn-mini" @click="triggerPromptTemplatesImport" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.manage.import') }}</button>
149
- </div>
150
-
151
- <div v-if="pluginsLoading && !promptTemplatesLoadedOnce" class="skills-empty-state">{{ t('plugins.promptTemplates.manage.loading') }}</div>
152
- <div v-else-if="!filteredPromptTemplates.length" class="skills-empty-state">{{ t('plugins.promptTemplates.manage.empty') }}</div>
153
- <div v-else class="prompt-templates-list" role="list">
154
- <button
155
- v-for="tpl in filteredPromptTemplates"
156
- :key="'prompt-template-' + tpl.id"
157
- type="button"
158
- :class="['prompt-template-item', { active: promptTemplateSelectedId === tpl.id }]"
159
- :aria-current="promptTemplateSelectedId === tpl.id ? 'page' : null"
160
- @click="selectPromptTemplate(tpl.id)">
161
- <div class="prompt-template-item-main">
162
- <div class="prompt-template-item-title">{{ tpl.name }}</div>
163
- <div class="prompt-template-item-meta">
164
- <span class="mono">{{ t('plugins.promptTemplates.manage.vars', { count: tpl.varCount }) }}</span>
165
- <span>·</span>
166
- <span class="mono">{{ tpl.updatedLabel }}</span>
167
- </div>
168
- </div>
169
- <span :class="['pill', tpl.isBuiltin ? 'configured' : 'source']">{{ tpl.isBuiltin ? t('plugins.promptTemplates.manage.builtin') : t('plugins.promptTemplates.manage.custom') }}</span>
170
- </button>
171
- </div>
172
- </div>
173
-
174
- <div class="prompt-templates-pane prompt-templates-pane-editor">
175
- <div v-if="!promptTemplateDraft" class="skills-empty-state">{{ t('plugins.promptTemplates.editor.selectHint') }}</div>
176
- <template v-else>
177
- <div class="prompt-editor-head">
178
- <div class="prompt-editor-title-row">
179
- <input class="form-input prompt-editor-name" type="text" v-model.trim="promptTemplateDraft.name" :disabled="promptTemplateDraft.isBuiltin" :placeholder="t('plugins.promptTemplates.editor.namePlaceholder')" :aria-label="t('plugins.promptTemplates.editor.nameAria')">
180
- <div v-if="!promptTemplateDraft.isBuiltin" class="prompt-editor-actions">
181
- <button type="button" class="btn-mini" @click="duplicatePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.duplicate') }}</button>
182
- <button type="button" class="btn-mini delete" @click="deletePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.delete') }}</button>
183
- <button type="button" class="btn-mini" @click="savePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.save') }}</button>
184
- </div>
185
- </div>
186
- <div v-if="promptTemplateDraft.isBuiltin" class="plugins-panel-note">
187
- {{ t('plugins.promptTemplates.editor.builtinReadOnly') }}
188
- </div>
189
- <div v-if="promptTemplateDraft.isBuiltin && (promptTemplateDraft.createdBy || (promptTemplateDraft.maintainers && promptTemplateDraft.maintainers.length))" class="plugins-panel-note">
190
- {{ t('plugins.meta.attribution', { createdBy: promptTemplateDraft.createdBy || '', maintainers: (promptTemplateDraft.maintainers || []).join(', ') }) }}
191
- </div>
192
- <input class="form-input" type="text" v-model.trim="promptTemplateDraft.description" :disabled="promptTemplateDraft.isBuiltin" :placeholder="t('plugins.promptTemplates.editor.descPlaceholder')" :aria-label="t('plugins.promptTemplates.editor.descAria')">
193
- </div>
194
-
195
- <div class="prompt-editor-body">
196
- <label class="form-label">{{ t('plugins.promptTemplates.editor.templateLabel') }}</label>
197
- <textarea
198
- class="form-input prompt-editor-textarea"
199
- v-model="promptTemplateDraft.template"
200
- :disabled="promptTemplateDraft.isBuiltin"
201
- rows="10"
202
- spellcheck="false"
203
- :aria-label="t('plugins.promptTemplates.editor.templateAria')"
204
- :placeholder="t('plugins.promptTemplates.editor.templatePlaceholder')"></textarea>
205
-
206
- <div class="prompt-vars-block">
207
- <div class="prompt-vars-head">
208
- <div>
209
- <div class="prompt-vars-title">{{ t('plugins.promptTemplates.vars.title') }}</div>
210
- <div class="plugins-panel-note">{{ t('plugins.promptTemplates.vars.hint') }}</div>
211
- </div>
212
- <div v-if="!promptTemplateDraft.isBuiltin" class="prompt-editor-actions">
213
- <button type="button" class="btn-mini" @click="addPromptTemplateVariable" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.add') }}</button>
214
- <button type="button" class="btn-mini" @click="resetPromptVariableValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
215
- </div>
216
- </div>
217
-
218
- <div v-if="!promptTemplateVars.length" class="prompt-vars-empty">{{ t('plugins.promptTemplates.vars.empty') }}</div>
219
- <div v-else class="prompt-vars-grid">
220
- <label v-for="name in promptTemplateVars" :key="'prompt-var-' + name" class="prompt-var-row">
221
- <span class="prompt-var-label mono">{{ name }}</span>
222
- <input class="form-input prompt-var-input" type="text" :readonly="promptTemplateDraft.isBuiltin" :value="promptTemplateVarValues[name] || ''" @input="setPromptVariableValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
223
- </label>
224
- </div>
225
- </div>
226
-
227
- <div class="prompt-preview-block">
228
- <div class="prompt-vars-head">
229
- <div>
230
- <div class="prompt-vars-title">{{ t('plugins.promptTemplates.preview.title') }}</div>
231
- <div class="plugins-panel-note">{{ t('plugins.promptTemplates.preview.hint') }}</div>
232
- </div>
233
- <button type="button" class="btn-mini" @click="copyRenderedPrompt" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.preview.copy') }}</button>
234
- </div>
235
- <textarea class="form-input prompt-preview-textarea" :value="renderedPrompt" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.preview.outputAria')"></textarea>
236
- </div>
237
- </div>
238
- </template>
239
- </div>
240
- </div>
241
- </div>
242
-
243
- <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
244
- </section>
245
- </div>
246
- </div>
247
-
248
- <input
249
- ref="promptTemplatesImportInput"
250
- type="file"
251
- accept=".json,application/json"
252
- style="display:none"
253
- @change="handlePromptTemplatesImportChange">
254
-
255
- <!-- 新增变量(Prompt Templates) -->
256
- <div v-if="showPromptTemplateVarModal" class="modal-overlay" @click.self="closePromptTemplateVarModal">
257
- <div class="modal" role="dialog" aria-modal="true" aria-labelledby="add-prompt-var-modal-title">
258
- <div class="modal-title" id="add-prompt-var-modal-title">{{ t('plugins.promptTemplates.varModal.title') }}</div>
259
-
260
- <div class="form-group">
261
- <label class="form-label">{{ t('plugins.promptTemplates.varModal.nameLabel') }}</label>
262
- <input
263
- ref="promptTemplateVarNameInput"
264
- v-model.trim="promptTemplateVarDraftName"
265
- :class="['form-input', { invalid: !!promptTemplateVarDraftError }]"
266
- :placeholder="t('placeholder.varNameExample')"
267
- autocomplete="off"
268
- spellcheck="false"
269
- @keydown.enter.prevent="confirmAddPromptTemplateVariable">
270
- <div v-if="promptTemplateVarDraftError" class="form-hint form-error">{{ promptTemplateVarDraftError }}</div>
271
- <div v-else class="form-hint">{{ t('hint.varNameRules') }}</div>
272
- </div>
273
-
274
- <div class="btn-group">
275
- <button class="btn btn-cancel" @click="closePromptTemplateVarModal">{{ t('plugins.promptTemplates.varModal.cancel') }}</button>
276
- <button class="btn btn-confirm" @click="confirmAddPromptTemplateVariable">{{ t('plugins.promptTemplates.varModal.add') }}</button>
277
- </div>
278
- </div>
279
- </div>
1
+ <div
2
+ v-show="mainTab === 'plugins'"
3
+ class="mode-content"
4
+ id="panel-plugins"
5
+ role="tabpanel"
6
+ aria-labelledby="tab-plugins">
7
+ <div class="plugins-layout">
8
+ <aside class="plugins-sidebar" :aria-label="t('plugins.sidebar.ariaList')">
9
+ <div class="selector-header plugins-sidebar-header">
10
+ <div>
11
+ <span class="selector-title">{{ t('plugins.sidebar.title') }}</span>
12
+ <div class="plugins-panel-note">{{ t('plugins.sidebar.note') }}</div>
13
+ </div>
14
+ <div class="settings-tab-actions">
15
+ <button type="button" class="btn-tool btn-tool-compact" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">
16
+ {{ pluginsLoading ? t('plugins.refreshing') : t('plugins.refresh') }}
17
+ </button>
18
+ </div>
19
+ </div>
20
+
21
+ <div class="plugins-list" role="list">
22
+ <button
23
+ v-for="plugin in pluginsCatalog"
24
+ :key="'plugin-' + plugin.id"
25
+ type="button"
26
+ :class="['plugins-item', { active: pluginsActiveId === plugin.id }]"
27
+ :aria-current="pluginsActiveId === plugin.id ? 'page' : null"
28
+ :disabled="loading || !!initError || pluginsLoading"
29
+ @click="selectPlugin(plugin.id)">
30
+ <div class="plugins-item-main">
31
+ <div class="plugins-item-title">{{ plugin.title }}</div>
32
+ <div class="plugins-item-meta">{{ plugin.description }}</div>
33
+ </div>
34
+ <span :class="['pill', plugin.tone]">{{ plugin.statusLabel }}</span>
35
+ </button>
36
+ </div>
37
+ </aside>
38
+
39
+ <section class="plugins-main" :aria-label="t('plugins.main.ariaWorkspace')">
40
+ <div v-if="pluginsLoading" class="skills-empty-state">{{ t('common.loading') }}</div>
41
+ <div v-else-if="pluginsError" class="skills-empty-state">
42
+ <div class="plugins-panel-note">{{ pluginsError }}</div>
43
+ <button type="button" class="btn-mini" @click="loadPluginsOverview({ forceRefresh: true, silent: false })" :disabled="loading || !!initError || pluginsLoading">{{ t('common.refresh') }}</button>
44
+ </div>
45
+ <div v-else-if="pluginsActiveId === 'prompt-templates'" class="plugins-panel">
46
+ <div class="plugins-panel-head">
47
+ <div class="plugins-panel-title">{{ t('plugins.promptTemplates.title') }}</div>
48
+ <div v-if="pluginsActiveAttribution" class="plugins-panel-note">{{ pluginsActiveAttribution }}</div>
49
+ </div>
50
+
51
+ <div class="prompt-templates-modebar" role="tablist" :aria-label="t('plugins.promptTemplates.mode.aria')">
52
+ <button
53
+ type="button"
54
+ role="tab"
55
+ :aria-selected="promptTemplatesMode === 'compose'"
56
+ :class="['mode-pill', { active: promptTemplatesMode === 'compose' }]"
57
+ @click="promptTemplatesMode = 'compose'">{{ t('plugins.promptTemplates.mode.compose') }}</button>
58
+ <button
59
+ type="button"
60
+ role="tab"
61
+ :aria-selected="promptTemplatesMode !== 'compose'"
62
+ :class="['mode-pill', { active: promptTemplatesMode !== 'compose' }]"
63
+ @click="promptTemplatesMode = 'manage'">{{ t('plugins.promptTemplates.mode.manage') }}</button>
64
+ </div>
65
+
66
+ <div v-if="promptTemplatesMode === 'compose'" class="prompt-compose">
67
+ <div class="prompt-compose-workspace">
68
+ <div class="prompt-compose-selected">
69
+ <div class="prompt-compose-selected-title">{{ (promptComposerActiveTemplate && promptComposerActiveTemplate.name) ? promptComposerActiveTemplate.name : t('plugins.promptTemplates.compose.chooseTemplate') }}</div>
70
+ <div class="prompt-compose-selected-meta">{{ (promptComposerActiveTemplate && promptComposerActiveTemplate.description) ? promptComposerActiveTemplate.description : t('plugins.promptTemplates.compose.chooseTemplateHint') }}</div>
71
+ <div v-if="promptComposerActiveTemplate && promptComposerActiveTemplate.isBuiltin && (promptComposerActiveTemplate.createdBy || (promptComposerActiveTemplate.maintainers && promptComposerActiveTemplate.maintainers.length))" class="plugins-panel-note">
72
+ {{ t('plugins.meta.attribution', { createdBy: promptComposerActiveTemplate.createdBy || '', maintainers: (promptComposerActiveTemplate.maintainers || []).join(', ') }) }}
73
+ </div>
74
+ </div>
75
+
76
+ <div class="prompt-compose-form">
77
+ <label class="form-label">{{ t('plugins.promptTemplates.compose.selectTemplate') }}</label>
78
+ <select
79
+ class="form-select prompt-compose-template-select"
80
+ :value="promptComposerSelectedTemplateId"
81
+ @change="selectPromptComposerTemplate($event.target.value)"
82
+ :disabled="pluginsLoading || !promptTemplatesList.length">
83
+ <option v-for="tpl in promptTemplatesList" :key="'compose-tpl-' + tpl.id" :value="tpl.id">
84
+ {{ tpl.name }}{{ tpl.isBuiltin ? t('plugins.promptTemplates.compose.builtinSuffix') : '' }}
85
+ </option>
86
+ </select>
87
+
88
+ <div v-if="!promptComposerActiveTemplate" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.empty') }}</div>
89
+ <div v-else-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
90
+ <div v-if="promptComposerActiveTemplate && !promptComposerActiveTemplate.isBuiltin" class="prompt-compose-actions">
91
+ <button
92
+ type="button"
93
+ class="btn-mini"
94
+ @click="selectPromptTemplate(promptComposerSelectedTemplateId)"
95
+ :disabled="pluginsLoading || !promptComposerSelectedTemplateId">{{ t('plugins.promptTemplates.compose.goManage') }}</button>
96
+ </div>
97
+ </div>
98
+
99
+ <div v-if="promptComposerActiveTemplate && promptComposerActiveTemplate.vars && promptComposerActiveTemplate.vars.length" class="prompt-vars-block">
100
+ <div class="prompt-vars-head">
101
+ <div>
102
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.vars.title') }}</div>
103
+ <div v-if="!promptComposerActiveTemplate.isBuiltin" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.varsHint') }}</div>
104
+ <div v-if="promptComposerMissingVars.length" class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.missingCount', { count: promptComposerMissingVars.length }) }}</div>
105
+ </div>
106
+ <div class="prompt-editor-actions">
107
+ <button v-if="promptComposerMissingVars.length" type="button" class="btn-mini" @click="focusPromptComposerFirstMissingVar" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.compose.jumpToMissing') }}</button>
108
+ <button type="button" class="btn-mini" @click="resetPromptComposerVarValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
109
+ </div>
110
+ </div>
111
+ <div class="prompt-vars-grid">
112
+ <label v-for="(name, idx) in promptComposerActiveTemplate.vars" :key="'prompt-compose-var-' + name" class="prompt-var-row">
113
+ <span class="prompt-var-label mono">{{ name }}</span>
114
+ <template v-if="idx === 0">
115
+ <input ref="promptComposerFirstField" :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
116
+ </template>
117
+ <template v-else>
118
+ <input :class="['form-input', 'prompt-var-input', { 'is-missing': promptComposerMissingVars.includes(name) }]" type="text" :value="promptComposerVarValues[name] || ''" @input="setPromptComposerVarValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
119
+ </template>
120
+ </label>
121
+ </div>
122
+ </div>
123
+
124
+ <div class="prompt-preview-block prompt-compose-preview">
125
+ <div class="prompt-vars-head">
126
+ <div>
127
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.compose.outputTitle') }}</div>
128
+ <div class="plugins-panel-note">{{ t('plugins.promptTemplates.compose.outputHint') }}</div>
129
+ </div>
130
+ <button type="button" class="btn-mini" @click="copyPromptComposerRendered" :disabled="pluginsLoading || !promptComposerRendered">{{ t('plugins.promptTemplates.compose.copy') }}</button>
131
+ </div>
132
+ <textarea class="form-input prompt-preview-textarea" :value="promptComposerRendered" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.compose.outputAria')"></textarea>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <div v-else class="prompt-templates-grid">
138
+ <div class="prompt-templates-pane prompt-templates-pane-list">
139
+ <div class="prompt-templates-toolbar">
140
+ <input
141
+ class="form-input"
142
+ type="text"
143
+ v-model.trim="promptTemplatesKeyword"
144
+ :aria-label="t('plugins.promptTemplates.manage.searchAria')"
145
+ :placeholder="t('plugins.promptTemplates.manage.searchPlaceholder')">
146
+ <button type="button" class="btn-mini" @click="createPromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.manage.create') }}</button>
147
+ <button type="button" class="btn-mini" @click="exportPromptTemplates" :disabled="pluginsLoading || !promptTemplatesList.length">{{ t('plugins.promptTemplates.manage.export') }}</button>
148
+ <button type="button" class="btn-mini" @click="triggerPromptTemplatesImport" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.manage.import') }}</button>
149
+ </div>
150
+
151
+ <div v-if="pluginsLoading && !promptTemplatesLoadedOnce" class="skills-empty-state">{{ t('plugins.promptTemplates.manage.loading') }}</div>
152
+ <div v-else-if="!filteredPromptTemplates.length" class="skills-empty-state">{{ t('plugins.promptTemplates.manage.empty') }}</div>
153
+ <div v-else class="prompt-templates-list" role="list">
154
+ <button
155
+ v-for="tpl in filteredPromptTemplates"
156
+ :key="'prompt-template-' + tpl.id"
157
+ type="button"
158
+ :class="['prompt-template-item', { active: promptTemplateSelectedId === tpl.id }]"
159
+ :aria-current="promptTemplateSelectedId === tpl.id ? 'page' : null"
160
+ @click="selectPromptTemplate(tpl.id)">
161
+ <div class="prompt-template-item-main">
162
+ <div class="prompt-template-item-title">{{ tpl.name }}</div>
163
+ <div class="prompt-template-item-meta">
164
+ <span class="mono">{{ t('plugins.promptTemplates.manage.vars', { count: tpl.varCount }) }}</span>
165
+ <span>·</span>
166
+ <span class="mono">{{ tpl.updatedLabel }}</span>
167
+ </div>
168
+ </div>
169
+ <span :class="['pill', tpl.isBuiltin ? 'configured' : 'source']">{{ tpl.isBuiltin ? t('plugins.promptTemplates.manage.builtin') : t('plugins.promptTemplates.manage.custom') }}</span>
170
+ </button>
171
+ </div>
172
+ </div>
173
+
174
+ <div class="prompt-templates-pane prompt-templates-pane-editor">
175
+ <div v-if="!promptTemplateDraft" class="skills-empty-state">{{ t('plugins.promptTemplates.editor.selectHint') }}</div>
176
+ <template v-else>
177
+ <div class="prompt-editor-head">
178
+ <div class="prompt-editor-title-row">
179
+ <input class="form-input prompt-editor-name" type="text" v-model.trim="promptTemplateDraft.name" :disabled="promptTemplateDraft.isBuiltin" :placeholder="t('plugins.promptTemplates.editor.namePlaceholder')" :aria-label="t('plugins.promptTemplates.editor.nameAria')">
180
+ <div v-if="!promptTemplateDraft.isBuiltin" class="prompt-editor-actions">
181
+ <button type="button" class="btn-mini" @click="duplicatePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.duplicate') }}</button>
182
+ <button type="button" class="btn-mini delete" @click="deletePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.delete') }}</button>
183
+ <button type="button" class="btn-mini" @click="savePromptTemplate" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.editor.save') }}</button>
184
+ </div>
185
+ </div>
186
+ <div v-if="promptTemplateDraft.isBuiltin" class="plugins-panel-note">
187
+ {{ t('plugins.promptTemplates.editor.builtinReadOnly') }}
188
+ </div>
189
+ <div v-if="promptTemplateDraft.isBuiltin && (promptTemplateDraft.createdBy || (promptTemplateDraft.maintainers && promptTemplateDraft.maintainers.length))" class="plugins-panel-note">
190
+ {{ t('plugins.meta.attribution', { createdBy: promptTemplateDraft.createdBy || '', maintainers: (promptTemplateDraft.maintainers || []).join(', ') }) }}
191
+ </div>
192
+ <input class="form-input" type="text" v-model.trim="promptTemplateDraft.description" :disabled="promptTemplateDraft.isBuiltin" :placeholder="t('plugins.promptTemplates.editor.descPlaceholder')" :aria-label="t('plugins.promptTemplates.editor.descAria')">
193
+ </div>
194
+
195
+ <div class="prompt-editor-body">
196
+ <label class="form-label">{{ t('plugins.promptTemplates.editor.templateLabel') }}</label>
197
+ <textarea
198
+ class="form-input prompt-editor-textarea"
199
+ v-model="promptTemplateDraft.template"
200
+ :disabled="promptTemplateDraft.isBuiltin"
201
+ rows="10"
202
+ spellcheck="false"
203
+ :aria-label="t('plugins.promptTemplates.editor.templateAria')"
204
+ :placeholder="t('plugins.promptTemplates.editor.templatePlaceholder')"></textarea>
205
+
206
+ <div class="prompt-vars-block">
207
+ <div class="prompt-vars-head">
208
+ <div>
209
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.vars.title') }}</div>
210
+ <div class="plugins-panel-note">{{ t('plugins.promptTemplates.vars.hint') }}</div>
211
+ </div>
212
+ <div v-if="!promptTemplateDraft.isBuiltin" class="prompt-editor-actions">
213
+ <button type="button" class="btn-mini" @click="addPromptTemplateVariable" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.add') }}</button>
214
+ <button type="button" class="btn-mini" @click="resetPromptVariableValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
215
+ </div>
216
+ </div>
217
+
218
+ <div v-if="!promptTemplateVars.length" class="prompt-vars-empty">{{ t('plugins.promptTemplates.vars.empty') }}</div>
219
+ <div v-else class="prompt-vars-grid">
220
+ <label v-for="name in promptTemplateVars" :key="'prompt-var-' + name" class="prompt-var-row">
221
+ <span class="prompt-var-label mono">{{ name }}</span>
222
+ <input class="form-input prompt-var-input" type="text" :readonly="promptTemplateDraft.isBuiltin" :value="promptTemplateVarValues[name] || ''" @input="setPromptVariableValue(name, $event.target.value)" :placeholder="t('plugins.promptTemplates.vars.valuePlaceholder', { name })">
223
+ </label>
224
+ </div>
225
+ </div>
226
+
227
+ <div class="prompt-preview-block">
228
+ <div class="prompt-vars-head">
229
+ <div>
230
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.preview.title') }}</div>
231
+ <div class="plugins-panel-note">{{ t('plugins.promptTemplates.preview.hint') }}</div>
232
+ </div>
233
+ <button type="button" class="btn-mini" @click="copyRenderedPrompt" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.preview.copy') }}</button>
234
+ </div>
235
+ <textarea class="form-input prompt-preview-textarea" :value="renderedPrompt" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.preview.outputAria')"></textarea>
236
+ </div>
237
+ </div>
238
+ </template>
239
+ </div>
240
+ </div>
241
+ </div>
242
+
243
+ <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
244
+ </section>
245
+ </div>
246
+ </div>
247
+
248
+ <input
249
+ ref="promptTemplatesImportInput"
250
+ type="file"
251
+ accept=".json,application/json"
252
+ style="display:none"
253
+ @change="handlePromptTemplatesImportChange">
254
+
255
+ <!-- 新增变量(Prompt Templates) -->
256
+ <div v-if="showPromptTemplateVarModal" class="modal-overlay" @click.self="closePromptTemplateVarModal">
257
+ <div class="modal" role="dialog" aria-modal="true" aria-labelledby="add-prompt-var-modal-title">
258
+ <div class="modal-title" id="add-prompt-var-modal-title">{{ t('plugins.promptTemplates.varModal.title') }}</div>
259
+
260
+ <div class="form-group">
261
+ <label class="form-label">{{ t('plugins.promptTemplates.varModal.nameLabel') }}</label>
262
+ <input
263
+ ref="promptTemplateVarNameInput"
264
+ v-model.trim="promptTemplateVarDraftName"
265
+ :class="['form-input', { invalid: !!promptTemplateVarDraftError }]"
266
+ :placeholder="t('placeholder.varNameExample')"
267
+ autocomplete="off"
268
+ spellcheck="false"
269
+ @keydown.enter.prevent="confirmAddPromptTemplateVariable">
270
+ <div v-if="promptTemplateVarDraftError" class="form-hint form-error">{{ promptTemplateVarDraftError }}</div>
271
+ <div v-else class="form-hint">{{ t('hint.varNameRules') }}</div>
272
+ </div>
273
+
274
+ <div class="btn-group">
275
+ <button class="btn btn-cancel" @click="closePromptTemplateVarModal">{{ t('plugins.promptTemplates.varModal.cancel') }}</button>
276
+ <button class="btn btn-confirm" @click="confirmAddPromptTemplateVariable">{{ t('plugins.promptTemplates.varModal.add') }}</button>
277
+ </div>
278
+ </div>
279
+ </div>