codexmate 0.0.29 → 0.0.31

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 (145) hide show
  1. package/README.md +363 -421
  2. package/README.zh.md +371 -354
  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 +1725 -1725
  7. package/cli/claude-proxy.js +1022 -1022
  8. package/cli/config-bootstrap.js +402 -384
  9. package/cli/config-health.js +454 -338
  10. package/cli/doctor-core.js +903 -903
  11. package/cli/import-skills-url.js +356 -356
  12. package/cli/local-bridge.js +324 -0
  13. package/cli/openai-bridge.js +1653 -1576
  14. package/cli/openclaw-config.js +629 -629
  15. package/cli/session-convert-args.js +69 -65
  16. package/cli/session-convert-io.js +82 -82
  17. package/cli/session-convert.js +150 -43
  18. package/cli/session-usage.concurrent.js +28 -28
  19. package/cli/session-usage.js +118 -118
  20. package/cli/session-usage.models.js +176 -176
  21. package/cli/skills.js +1141 -1141
  22. package/cli/zip-commands.js +510 -510
  23. package/cli.js +15778 -15340
  24. package/lib/automation.js +404 -404
  25. package/lib/cli-file-utils.js +151 -151
  26. package/lib/cli-models-utils.js +440 -440
  27. package/lib/cli-network-utils.js +190 -190
  28. package/lib/cli-path-utils.js +85 -85
  29. package/lib/cli-session-utils.js +121 -121
  30. package/lib/cli-sessions.js +417 -417
  31. package/lib/cli-utils.js +155 -155
  32. package/lib/cli-webhook.js +126 -126
  33. package/lib/download-artifacts.js +92 -92
  34. package/lib/mcp-stdio.js +453 -453
  35. package/lib/task-orchestrator.js +869 -869
  36. package/lib/text-diff.js +303 -303
  37. package/lib/workflow-engine.js +340 -340
  38. package/package.json +76 -76
  39. package/plugins/README.md +20 -20
  40. package/plugins/README.zh-CN.md +20 -20
  41. package/plugins/prompt-templates/comment-polish/index.mjs +25 -25
  42. package/plugins/prompt-templates/computed.mjs +253 -253
  43. package/plugins/prompt-templates/index.mjs +8 -8
  44. package/plugins/prompt-templates/manifest.mjs +15 -15
  45. package/plugins/prompt-templates/methods.mjs +553 -553
  46. package/plugins/prompt-templates/overview.mjs +91 -91
  47. package/plugins/prompt-templates/ownership.mjs +19 -19
  48. package/plugins/prompt-templates/rule-ack/index.mjs +21 -21
  49. package/plugins/prompt-templates/storage.mjs +64 -64
  50. package/plugins/registry.mjs +16 -16
  51. package/web-ui/app.js +646 -639
  52. package/web-ui/index.html +36 -36
  53. package/web-ui/logic.agents-diff.mjs +386 -386
  54. package/web-ui/logic.claude.mjs +168 -168
  55. package/web-ui/logic.codex.mjs +69 -69
  56. package/web-ui/logic.mjs +5 -5
  57. package/web-ui/logic.runtime.mjs +128 -128
  58. package/web-ui/logic.session-convert.mjs +70 -70
  59. package/web-ui/logic.sessions.mjs +765 -765
  60. package/web-ui/modules/api.mjs +90 -90
  61. package/web-ui/modules/app.computed.dashboard.mjs +248 -225
  62. package/web-ui/modules/app.computed.index.mjs +17 -17
  63. package/web-ui/modules/app.computed.main-tabs.mjs +205 -205
  64. package/web-ui/modules/app.computed.session.mjs +999 -999
  65. package/web-ui/modules/app.constants.mjs +15 -15
  66. package/web-ui/modules/app.methods.agents.mjs +632 -632
  67. package/web-ui/modules/app.methods.claude-config.mjs +200 -200
  68. package/web-ui/modules/app.methods.codex-config.mjs +841 -892
  69. package/web-ui/modules/app.methods.index.mjs +94 -94
  70. package/web-ui/modules/app.methods.install.mjs +205 -205
  71. package/web-ui/modules/app.methods.navigation.mjs +774 -761
  72. package/web-ui/modules/app.methods.openclaw-core.mjs +814 -814
  73. package/web-ui/modules/app.methods.openclaw-editing.mjs +372 -372
  74. package/web-ui/modules/app.methods.openclaw-persist.mjs +369 -369
  75. package/web-ui/modules/app.methods.providers.mjs +529 -493
  76. package/web-ui/modules/app.methods.runtime.mjs +345 -345
  77. package/web-ui/modules/app.methods.session-actions.mjs +591 -593
  78. package/web-ui/modules/app.methods.session-browser.mjs +984 -984
  79. package/web-ui/modules/app.methods.session-timeline.mjs +479 -479
  80. package/web-ui/modules/app.methods.session-trash.mjs +438 -438
  81. package/web-ui/modules/app.methods.startup-claude.mjs +534 -533
  82. package/web-ui/modules/app.methods.task-orchestration.mjs +556 -556
  83. package/web-ui/modules/app.methods.webhook.mjs +79 -79
  84. package/web-ui/modules/config-mode.computed.mjs +124 -124
  85. package/web-ui/modules/config-template-confirm-pref.mjs +33 -33
  86. package/web-ui/modules/i18n.dict.mjs +3174 -3131
  87. package/web-ui/modules/i18n.mjs +62 -62
  88. package/web-ui/modules/plugins.computed.mjs +3 -3
  89. package/web-ui/modules/plugins.methods.mjs +3 -3
  90. package/web-ui/modules/plugins.storage.mjs +11 -11
  91. package/web-ui/modules/provider-url-display.mjs +17 -17
  92. package/web-ui/modules/sessions-filters-url.mjs +85 -85
  93. package/web-ui/modules/skills.computed.mjs +107 -107
  94. package/web-ui/modules/skills.methods.mjs +482 -481
  95. package/web-ui/partials/index/layout-footer.html +13 -13
  96. package/web-ui/partials/index/layout-header.html +500 -500
  97. package/web-ui/partials/index/modal-config-template-agents.html +174 -174
  98. package/web-ui/partials/index/modal-confirm-toast.html +32 -32
  99. package/web-ui/partials/index/modal-health-check.html +45 -45
  100. package/web-ui/partials/index/modal-openclaw-config.html +280 -280
  101. package/web-ui/partials/index/modal-skills.html +200 -200
  102. package/web-ui/partials/index/modals-basic.html +162 -162
  103. package/web-ui/partials/index/panel-config-claude.html +194 -194
  104. package/web-ui/partials/index/panel-config-codex.html +357 -323
  105. package/web-ui/partials/index/panel-config-codex.html.bak +337 -0
  106. package/web-ui/partials/index/panel-config-openclaw.html +83 -83
  107. package/web-ui/partials/index/panel-dashboard.html +219 -186
  108. package/web-ui/partials/index/panel-docs.html +147 -147
  109. package/web-ui/partials/index/panel-market.html +177 -177
  110. package/web-ui/partials/index/panel-orchestration.html +391 -391
  111. package/web-ui/partials/index/panel-plugins.html +253 -253
  112. package/web-ui/partials/index/panel-sessions.html +302 -316
  113. package/web-ui/partials/index/panel-settings.html +190 -190
  114. package/web-ui/partials/index/panel-trash.html +88 -88
  115. package/web-ui/partials/index/panel-usage.html +371 -371
  116. package/web-ui/res/json5.min.js +1 -1
  117. package/web-ui/res/vue.global.prod.js +13 -13
  118. package/web-ui/session-helpers.mjs +591 -576
  119. package/web-ui/source-bundle.cjs +233 -233
  120. package/web-ui/styles/base-theme.css +281 -281
  121. package/web-ui/styles/bridge-pool.css +197 -0
  122. package/web-ui/styles/controls-forms.css +422 -422
  123. package/web-ui/styles/dashboard.css +406 -274
  124. package/web-ui/styles/docs-panel.css +271 -271
  125. package/web-ui/styles/feedback.css +108 -108
  126. package/web-ui/styles/health-check-dialog.css +144 -144
  127. package/web-ui/styles/layout-shell.css +626 -626
  128. package/web-ui/styles/modals-core.css +466 -466
  129. package/web-ui/styles/navigation-panels.css +391 -391
  130. package/web-ui/styles/openclaw-structured.css +266 -266
  131. package/web-ui/styles/plugins-panel.css +564 -523
  132. package/web-ui/styles/responsive.css +454 -454
  133. package/web-ui/styles/sessions-list.css +417 -419
  134. package/web-ui/styles/sessions-preview.css +407 -411
  135. package/web-ui/styles/sessions-toolbar-trash.css +348 -330
  136. package/web-ui/styles/sessions-usage.css +1040 -1040
  137. package/web-ui/styles/settings-panel.css +349 -349
  138. package/web-ui/styles/skills-list.css +305 -303
  139. package/web-ui/styles/skills-market.css +429 -406
  140. package/web-ui/styles/task-orchestration.css +822 -822
  141. package/web-ui/styles/titles-cards.css +472 -472
  142. package/web-ui/styles/trash-panel.css +90 -90
  143. package/web-ui/styles/webhook.css +81 -81
  144. package/web-ui/styles.css +24 -23
  145. package/web-ui.html +17 -17
@@ -1,253 +1,253 @@
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="promptTemplateDraftRaw.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="promptTemplateDraftRaw.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="promptTemplateDraftRaw.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="resetPromptVariableValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
214
- </div>
215
- </div>
216
-
217
- <div v-if="!promptTemplateVars.length" class="prompt-vars-empty">{{ t('plugins.promptTemplates.vars.empty') }}</div>
218
- <div v-else class="prompt-vars-grid">
219
- <label v-for="name in promptTemplateVars" :key="'prompt-var-' + name" class="prompt-var-row">
220
- <span class="prompt-var-label mono">{{ name }}</span>
221
- <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 })">
222
- </label>
223
- </div>
224
- </div>
225
-
226
- <div class="prompt-preview-block">
227
- <div class="prompt-vars-head">
228
- <div>
229
- <div class="prompt-vars-title">{{ t('plugins.promptTemplates.preview.title') }}</div>
230
- <div class="plugins-panel-note">{{ t('plugins.promptTemplates.preview.hint') }}</div>
231
- </div>
232
- <button type="button" class="btn-mini" @click="copyRenderedPrompt" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.preview.copy') }}</button>
233
- </div>
234
- <textarea class="form-input prompt-preview-textarea" :value="renderedPrompt" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.preview.outputAria')"></textarea>
235
- </div>
236
- </div>
237
- </template>
238
- </div>
239
- </div>
240
- </div>
241
-
242
- <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
243
- </section>
244
- </div>
245
- </div>
246
-
247
- <input
248
- ref="promptTemplatesImportInput"
249
- type="file"
250
- accept=".json,application/json"
251
- style="display:none"
252
- @change="handlePromptTemplatesImportChange">
253
-
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'; if(typeof saveNavState==='function')saveNavState()"><svg style="width:12px;height:12px;vertical-align:-1px;margin-right:4px" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 4h12v12H4z"/><path d="M8 8h4M8 11h2"/></svg>{{ 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'; if(typeof saveNavState==='function')saveNavState()"><svg style="width:12px;height:12px;vertical-align:-1px;margin-right:4px" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M3 5h14M3 10h10M3 15h12"/></svg>{{ 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="promptTemplateDraftRaw.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="promptTemplateDraftRaw.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="promptTemplateDraftRaw.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="resetPromptVariableValues" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.vars.reset') }}</button>
214
+ </div>
215
+ </div>
216
+
217
+ <div v-if="!promptTemplateVars.length" class="prompt-vars-empty">{{ t('plugins.promptTemplates.vars.empty') }}</div>
218
+ <div v-else class="prompt-vars-grid">
219
+ <label v-for="name in promptTemplateVars" :key="'prompt-var-' + name" class="prompt-var-row">
220
+ <span class="prompt-var-label mono">{{ name }}</span>
221
+ <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 })">
222
+ </label>
223
+ </div>
224
+ </div>
225
+
226
+ <div class="prompt-preview-block">
227
+ <div class="prompt-vars-head">
228
+ <div>
229
+ <div class="prompt-vars-title">{{ t('plugins.promptTemplates.preview.title') }}</div>
230
+ <div class="plugins-panel-note">{{ t('plugins.promptTemplates.preview.hint') }}</div>
231
+ </div>
232
+ <button type="button" class="btn-mini" @click="copyRenderedPrompt" :disabled="pluginsLoading">{{ t('plugins.promptTemplates.preview.copy') }}</button>
233
+ </div>
234
+ <textarea class="form-input prompt-preview-textarea" :value="renderedPrompt" rows="10" readonly spellcheck="false" :aria-label="t('plugins.promptTemplates.preview.outputAria')"></textarea>
235
+ </div>
236
+ </div>
237
+ </template>
238
+ </div>
239
+ </div>
240
+ </div>
241
+
242
+ <div v-else class="skills-empty-state">{{ t('plugins.promptTemplates.noPluginSelected') }}</div>
243
+ </section>
244
+ </div>
245
+ </div>
246
+
247
+ <input
248
+ ref="promptTemplatesImportInput"
249
+ type="file"
250
+ accept=".json,application/json"
251
+ style="display:none"
252
+ @change="handlePromptTemplatesImportChange">
253
+