main
html 362 lines 13.7 KB
Raw
1 <html>
2 <head>
3 <title>Commands</title>
4 <script type="module">
5 import { store } from "/plugins/_commands/webui/commands-store.js";
6 </script>
7 </head>
8 <body>
9 <div x-data>
10 <template x-if="$store.commandsManager">
11 <div class="commands-manager" x-create="$store.commandsManager.onOpen()" x-destroy="$store.commandsManager.cleanup()">
12 <div class="commands-toolbar">
13 <div class="commands-toolbar-copy">
14 <div class="commands-title">Slash Commands</div>
15 <div class="commands-subtitle">
16 YAML-configured commands for the inline chat composer.
17 </div>
18 </div>
19
20 <div class="commands-toolbar-controls">
21 <label class="commands-select">
22 <span>Project</span>
23 <select x-model="$store.commandsManager.projectName" @change="$store.commandsManager.onScopeChanged()">
24 <option value="">Global</option>
25 <template x-for="project in $store.commandsManager.projects" :key="project.key">
26 <option :value="project.key" x-text="project.label"></option>
27 </template>
28 </select>
29 </label>
30
31 <div class="commands-toolbar-actions">
32 <button type="button" class="button secondary" @click="$store.commandsManager.refresh()">
33 <x-icon name="refresh"></x-icon>
34 <span>Refresh</span>
35 </button>
36 <button type="button" class="button secondary" @click="$store.commandsManager.browseScopeFolder()">
37 <x-icon name="folder_open"></x-icon>
38 <span>Browse Scope Folder</span>
39 </button>
40 <button type="button" class="button primary" @click="$store.commandsManager.openCreateCommand()">
41 <x-icon name="add"></x-icon>
42 <span>Create Command</span>
43 </button>
44 </div>
45 </div>
46 </div>
47
48 <div class="commands-loading" x-show="$store.commandsManager.loading">
49 <x-icon class="spinning" name="progress_activity"></x-icon>
50 <span>Loading commands...</span>
51 </div>
52
53 <div class="commands-list" x-show="!$store.commandsManager.loading">
54 <div class="commands-section-heading">
55 <div class="commands-section-title" x-text="`${$store.commandsManager.selectedScopeLabel} commands`"></div>
56 <div class="commands-section-copy">Editable commands saved in the selected scope.</div>
57 </div>
58
59 <template x-if="$store.commandsManager.hasCommands">
60 <div class="commands-grid">
61 <template x-for="command in $store.commandsManager.commands" :key="command.path">
62 <article class="commands-card">
63 <div class="commands-card-header">
64 <div class="commands-card-copy">
65 <div class="commands-card-title">
66 <span class="commands-slash">/</span><span x-text="command.name"></span>
67 </div>
68 <div class="commands-card-description" x-text="command.description"></div>
69 </div>
70
71 <div class="commands-card-actions">
72 <button type="button" class="button icon" title="Edit command" @click="$store.commandsManager.openEditCommand(command)">
73 <x-icon name="edit"></x-icon>
74 </button>
75 <button type="button" class="button icon" title="Duplicate command" @click="$store.commandsManager.duplicateCommand(command)">
76 <x-icon name="content_copy"></x-icon>
77 </button>
78 <button type="button" class="button icon danger" title="Delete command" @click.stop="$confirmClick($event, () => $store.commandsManager.deleteCommand(command))">
79 <x-icon name="delete"></x-icon>
80 </button>
81 </div>
82 </div>
83
84 <div class="commands-card-path" x-text="command.path"></div>
85 </article>
86 </template>
87 </div>
88 </template>
89
90 <template x-if="!$store.commandsManager.hasCommands">
91 <div class="commands-empty-state">
92 <div class="commands-empty-title">No commands in this scope yet.</div>
93 <div class="commands-empty-copy">
94 Create YAML-configured slash commands here and they will appear from <code>/</code> in chat.
95 </div>
96 <button type="button" class="button primary" @click="$store.commandsManager.openCreateCommand()">
97 <x-icon name="add"></x-icon>
98 <span>Create Slash Command</span>
99 </button>
100 </div>
101 </template>
102
103 <template x-if="$store.commandsManager.builtinCommands.length">
104 <section class="commands-section">
105 <div class="commands-section-heading">
106 <div class="commands-section-title">Built-in commands</div>
107 <div class="commands-section-copy">
108 Editing creates an override in <span x-text="$store.commandsManager.selectedScopeLabel"></span>; core files stay unchanged.
109 </div>
110 </div>
111
112 <div class="commands-grid">
113 <template x-for="command in $store.commandsManager.builtinCommands" :key="command.path">
114 <article class="commands-card">
115 <div class="commands-card-header">
116 <div class="commands-card-copy">
117 <div class="commands-card-title">
118 <span class="commands-slash">/</span><span x-text="command.name"></span>
119 </div>
120 <div class="commands-card-description" x-text="command.description"></div>
121 </div>
122
123 <button type="button"
124 class="button icon"
125 :title="$store.commandsManager.scopedOverride(command) ? 'Edit override' : 'Create editable override'"
126 :aria-label="$store.commandsManager.scopedOverride(command) ? `Edit /${command.name} override` : `Create editable /${command.name} override`"
127 @click="$store.commandsManager.editBuiltinCommand(command)">
128 <x-icon name="edit"></x-icon>
129 </button>
130 </div>
131
132 </article>
133 </template>
134 </div>
135 </section>
136 </template>
137 </div>
138 </div>
139 </template>
140 </div>
141
142 <style>
143 .commands-manager {
144 display: flex;
145 flex-direction: column;
146 gap: 1rem;
147 padding: 1rem;
148 }
149
150 .commands-toolbar {
151 display: flex;
152 flex-direction: column;
153 gap: 1rem;
154 padding: 1rem;
155 border: 1px solid var(--color-border);
156 border-radius: 12px;
157 background: color-mix(in srgb, var(--color-panel) 88%, transparent);
158 }
159
160 .commands-toolbar-copy {
161 display: flex;
162 flex-direction: column;
163 gap: 0.25rem;
164 }
165
166 .commands-title {
167 font-size: 1.1rem;
168 font-weight: 600;
169 }
170
171 .commands-subtitle {
172 color: var(--color-text-secondary);
173 font-size: 0.92rem;
174 }
175
176 .commands-toolbar-controls {
177 display: flex;
178 flex-wrap: wrap;
179 gap: 0.75rem;
180 align-items: end;
181 }
182
183 .commands-select {
184 display: flex;
185 flex-direction: column;
186 gap: 0.35rem;
187 min-width: 14rem;
188 flex: 1 1 14rem;
189 }
190
191 .commands-select span {
192 color: var(--color-text-secondary);
193 font-size: 0.82rem;
194 font-weight: 600;
195 }
196
197 .commands-toolbar-actions {
198 display: flex;
199 flex-wrap: wrap;
200 gap: 0.5rem;
201 margin-left: auto;
202 }
203
204 .commands-loading {
205 display: flex;
206 align-items: center;
207 gap: 0.5rem;
208 padding: 0.9rem 1rem;
209 border-radius: 10px;
210 }
211
212 .commands-loading {
213 color: var(--color-text-secondary);
214 border: 1px dashed var(--color-border);
215 }
216
217 .commands-grid {
218 display: grid;
219 grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
220 gap: 0.9rem;
221 }
222
223 .commands-section {
224 margin-top: 1.25rem;
225 }
226
227 .commands-section-heading {
228 margin-bottom: 0.75rem;
229 }
230
231 .commands-section-title {
232 font-size: 1rem;
233 font-weight: 600;
234 }
235
236 .commands-section-copy {
237 margin-top: 0.2rem;
238 color: var(--color-text-secondary);
239 font-size: 0.86rem;
240 }
241
242 .commands-card {
243 display: flex;
244 flex-direction: column;
245 gap: 0.75rem;
246 padding: 1rem;
247 border: 1px solid var(--color-border);
248 border-radius: 12px;
249 background: color-mix(in srgb, var(--color-panel) 90%, transparent);
250 }
251
252 .commands-card-header {
253 display: flex;
254 gap: 0.75rem;
255 justify-content: space-between;
256 align-items: flex-start;
257 }
258
259 .commands-card-copy {
260 min-width: 0;
261 }
262
263 .commands-card-title {
264 font-size: 1rem;
265 font-weight: 600;
266 word-break: break-word;
267 }
268
269 .commands-slash {
270 color: var(--color-highlight);
271 }
272
273 .commands-card-description {
274 margin-top: 0.25rem;
275 color: var(--color-text-secondary);
276 font-size: 0.82rem;
277 line-height: 1.35;
278 }
279
280 .commands-card-actions {
281 display: inline-flex;
282 gap: 0.35rem;
283 flex-shrink: 0;
284 }
285
286 .commands-card-path {
287 color: var(--color-text-secondary);
288 font-family: "Roboto Mono", monospace;
289 font-size: 0.78rem;
290 word-break: break-all;
291 }
292
293 .commands-empty-state {
294 display: flex;
295 flex-direction: column;
296 align-items: flex-start;
297 gap: 0.75rem;
298 padding: 1.4rem;
299 border: 1px dashed var(--color-border);
300 border-radius: 12px;
301 background: color-mix(in srgb, var(--color-background) 84%, var(--color-panel));
302 }
303
304 .commands-empty-title {
305 font-size: 1rem;
306 font-weight: 600;
307 }
308
309 .commands-empty-copy {
310 color: var(--color-text-secondary);
311 max-width: 42rem;
312 }
313
314 .button.primary,
315 .button.secondary,
316 .button.icon {
317 display: inline-flex;
318 align-items: center;
319 gap: 0.35rem;
320 }
321
322 .button.icon {
323 padding: 0.45rem;
324 min-width: 2.25rem;
325 justify-content: center;
326 }
327
328 .button.icon.danger {
329 color: var(--color-accent);
330 }
331
332 .spinning {
333 animation: commands-spin 1s linear infinite;
334 }
335
336 @keyframes commands-spin {
337 from { transform: rotate(0deg); }
338 to { transform: rotate(360deg); }
339 }
340
341 @media (max-width: 760px) {
342 .commands-toolbar-actions {
343 margin-left: 0;
344 }
345
346 .commands-select {
347 min-width: 0;
348 flex-basis: 100%;
349 }
350
351 .commands-toolbar-actions {
352 width: 100%;
353 }
354
355 .commands-toolbar-actions .button {
356 flex: 1 1 100%;
357 justify-content: center;
358 }
359 }
360 </style>
361 </body>
362 </html>