@hivecommons/promptargs 0.6.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/LICENSE +190 -0
- package/README.md +423 -0
- package/dist/autodetect.d.ts +6 -0
- package/dist/autodetect.js +62 -0
- package/dist/autodetect.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +11 -0
- package/dist/loader.js +36 -0
- package/dist/loader.js.map +1 -0
- package/dist/parser.d.ts +11 -0
- package/dist/parser.js +41 -0
- package/dist/parser.js.map +1 -0
- package/dist/parser.test.d.ts +1 -0
- package/dist/parser.test.js +56 -0
- package/dist/parser.test.js.map +1 -0
- package/dist/resolver.d.ts +10 -0
- package/dist/resolver.js +133 -0
- package/dist/resolver.js.map +1 -0
- package/dist/status.d.ts +9 -0
- package/dist/status.js +22 -0
- package/dist/status.js.map +1 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.html +1065 -0
- package/dist/ui.js +85 -0
- package/dist/ui.js.map +1 -0
- package/package.json +51 -0
package/dist/ui.html
ADDED
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
<title>promptargs builder</title>
|
|
2
|
+
<meta charset="utf-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
4
|
+
<style>
|
|
5
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
background: #1a1a2e;
|
|
9
|
+
color: #e8e4dc;
|
|
10
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
11
|
+
min-height: 100vh;
|
|
12
|
+
padding: 32px 24px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.shell {
|
|
16
|
+
max-width: 680px;
|
|
17
|
+
margin: 0 auto;
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
gap: 24px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.brand {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: baseline;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.brand-name {
|
|
30
|
+
font-family: "Courier New", Consolas, monospace;
|
|
31
|
+
font-size: 20px;
|
|
32
|
+
font-weight: 700;
|
|
33
|
+
color: #e8e4dc;
|
|
34
|
+
letter-spacing: -0.5px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.brand-name b { color: #d4a447; }
|
|
38
|
+
|
|
39
|
+
.brand-tag {
|
|
40
|
+
font-size: 11px;
|
|
41
|
+
color: #6b7a8d;
|
|
42
|
+
letter-spacing: 0.3px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* --- Sections --- */
|
|
46
|
+
.section {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
gap: 8px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.section-label {
|
|
53
|
+
font-size: 11px;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
letter-spacing: 1px;
|
|
57
|
+
color: #6b7a8d;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* --- Template textarea --- */
|
|
61
|
+
.template-input {
|
|
62
|
+
width: 100%;
|
|
63
|
+
min-height: 80px;
|
|
64
|
+
background: #2a2a42;
|
|
65
|
+
border: 1px solid #3a3a56;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
color: #e8e4dc;
|
|
68
|
+
font-family: "Courier New", Consolas, monospace;
|
|
69
|
+
font-size: 14px;
|
|
70
|
+
line-height: 1.6;
|
|
71
|
+
padding: 12px 14px;
|
|
72
|
+
resize: vertical;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.template-input:focus {
|
|
76
|
+
outline: none;
|
|
77
|
+
border-color: #d4a447;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.template-input::placeholder { color: #4a4a66; }
|
|
81
|
+
|
|
82
|
+
/* --- Variable table --- */
|
|
83
|
+
.var-table {
|
|
84
|
+
width: 100%;
|
|
85
|
+
border-collapse: separate;
|
|
86
|
+
border-spacing: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.var-table th {
|
|
90
|
+
font-size: 10px;
|
|
91
|
+
font-weight: 600;
|
|
92
|
+
text-transform: uppercase;
|
|
93
|
+
letter-spacing: 0.8px;
|
|
94
|
+
color: #6b7a8d;
|
|
95
|
+
text-align: left;
|
|
96
|
+
padding: 0 4px 6px;
|
|
97
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.var-table td {
|
|
101
|
+
padding: 2px 4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.var-table .col-name { width: 25%; }
|
|
105
|
+
.var-table .col-values { width: 45%; }
|
|
106
|
+
.var-table .col-source { width: 22%; }
|
|
107
|
+
.var-table .col-rm { width: 8%; text-align: center; }
|
|
108
|
+
|
|
109
|
+
.var-input {
|
|
110
|
+
width: 100%;
|
|
111
|
+
background: #2a2a42;
|
|
112
|
+
border: 1px solid #3a3a56;
|
|
113
|
+
border-radius: 3px;
|
|
114
|
+
color: #e8e4dc;
|
|
115
|
+
font-family: "Courier New", Consolas, monospace;
|
|
116
|
+
font-size: 13px;
|
|
117
|
+
padding: 7px 8px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.var-input:focus {
|
|
121
|
+
outline: none;
|
|
122
|
+
border-color: #d4a447;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.var-input.auto-detected {
|
|
126
|
+
color: #6b7a8d;
|
|
127
|
+
font-style: italic;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.var-input::placeholder { color: #4a4a66; }
|
|
131
|
+
|
|
132
|
+
.source-select {
|
|
133
|
+
width: 100%;
|
|
134
|
+
background: #2a2a42;
|
|
135
|
+
border: 1px solid #3a3a56;
|
|
136
|
+
border-radius: 3px;
|
|
137
|
+
color: #e8e4dc;
|
|
138
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
139
|
+
font-size: 12px;
|
|
140
|
+
padding: 7px 6px;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
appearance: none;
|
|
143
|
+
-webkit-appearance: none;
|
|
144
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236b7a8d'/%3E%3C/svg%3E");
|
|
145
|
+
background-repeat: no-repeat;
|
|
146
|
+
background-position: right 8px center;
|
|
147
|
+
padding-right: 24px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.source-select:focus {
|
|
151
|
+
outline: none;
|
|
152
|
+
border-color: #d4a447;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.name-cell {
|
|
156
|
+
display: flex;
|
|
157
|
+
gap: 4px;
|
|
158
|
+
align-items: center;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.name-cell .var-input { flex: 1; }
|
|
162
|
+
|
|
163
|
+
.insert-btn {
|
|
164
|
+
background: none;
|
|
165
|
+
border: 1px solid #3a3a56;
|
|
166
|
+
border-radius: 3px;
|
|
167
|
+
color: #6b7a8d;
|
|
168
|
+
font-size: 11px;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
padding: 4px 5px;
|
|
171
|
+
line-height: 1;
|
|
172
|
+
flex-shrink: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.insert-btn:hover { color: #d4a447; border-color: #d4a447; }
|
|
176
|
+
.insert-btn:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
177
|
+
|
|
178
|
+
.rm-btn {
|
|
179
|
+
background: none;
|
|
180
|
+
border: none;
|
|
181
|
+
color: #6b7a8d;
|
|
182
|
+
font-size: 16px;
|
|
183
|
+
cursor: pointer;
|
|
184
|
+
padding: 4px 8px;
|
|
185
|
+
border-radius: 3px;
|
|
186
|
+
line-height: 1;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.rm-btn:hover { color: #e05252; background: rgba(224, 82, 82, 0.1); }
|
|
190
|
+
.rm-btn:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
191
|
+
|
|
192
|
+
.add-row {
|
|
193
|
+
background: none;
|
|
194
|
+
border: 1px dashed #3a3a56;
|
|
195
|
+
border-radius: 3px;
|
|
196
|
+
color: #6b7a8d;
|
|
197
|
+
font-size: 12px;
|
|
198
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
199
|
+
padding: 6px 12px;
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
align-self: flex-start;
|
|
202
|
+
margin-top: 4px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.add-row:hover { border-color: #d4a447; color: #d4a447; }
|
|
206
|
+
.add-row:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
207
|
+
|
|
208
|
+
/* --- Mode toggle --- */
|
|
209
|
+
.mode-row {
|
|
210
|
+
display: flex;
|
|
211
|
+
align-items: center;
|
|
212
|
+
gap: 16px;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.mode-toggle {
|
|
216
|
+
display: flex;
|
|
217
|
+
border: 1px solid #3a3a56;
|
|
218
|
+
border-radius: 4px;
|
|
219
|
+
overflow: hidden;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.mode-btn {
|
|
223
|
+
background: #2a2a42;
|
|
224
|
+
border: none;
|
|
225
|
+
color: #6b7a8d;
|
|
226
|
+
font-family: "Courier New", Consolas, monospace;
|
|
227
|
+
font-size: 12px;
|
|
228
|
+
padding: 6px 16px;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
transition: background 0.15s, color 0.15s;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mode-btn + .mode-btn { border-left: 1px solid #3a3a56; }
|
|
234
|
+
.mode-btn:hover { color: #e8e4dc; }
|
|
235
|
+
.mode-btn:focus-visible { outline: 2px solid #d4a447; outline-offset: -2px; }
|
|
236
|
+
|
|
237
|
+
.mode-btn.active {
|
|
238
|
+
background: #d4a447;
|
|
239
|
+
color: #1a1a2e;
|
|
240
|
+
font-weight: 700;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.mode-desc {
|
|
244
|
+
font-size: 12px;
|
|
245
|
+
color: #6b7a8d;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* --- Preview --- */
|
|
249
|
+
.preview-box {
|
|
250
|
+
background: #0e0e1c;
|
|
251
|
+
border: 1px solid #2a2a42;
|
|
252
|
+
border-radius: 4px;
|
|
253
|
+
overflow: hidden;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.preview-header {
|
|
257
|
+
background: #1e1e34;
|
|
258
|
+
padding: 8px 14px;
|
|
259
|
+
display: flex;
|
|
260
|
+
align-items: center;
|
|
261
|
+
justify-content: space-between;
|
|
262
|
+
border-bottom: 1px solid #2a2a42;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.preview-title {
|
|
266
|
+
font-family: "Courier New", Consolas, monospace;
|
|
267
|
+
font-size: 11px;
|
|
268
|
+
color: #6b7a8d;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.preview-count {
|
|
272
|
+
font-family: "Courier New", Consolas, monospace;
|
|
273
|
+
font-size: 11px;
|
|
274
|
+
font-variant-numeric: tabular-nums;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.preview-count.has-results { color: #4caf6a; }
|
|
278
|
+
.preview-count.empty { color: #6b7a8d; }
|
|
279
|
+
|
|
280
|
+
.preview-body {
|
|
281
|
+
padding: 14px;
|
|
282
|
+
font-family: "Courier New", Consolas, monospace;
|
|
283
|
+
font-size: 13px;
|
|
284
|
+
line-height: 1.6;
|
|
285
|
+
max-height: 320px;
|
|
286
|
+
overflow-y: auto;
|
|
287
|
+
white-space: pre-wrap;
|
|
288
|
+
word-break: break-word;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.preview-body .iteration {
|
|
292
|
+
padding: 6px 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.preview-body .iteration + .iteration {
|
|
296
|
+
border-top: 1px dashed #2a2a42;
|
|
297
|
+
margin-top: 6px;
|
|
298
|
+
padding-top: 10px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.preview-body .iter-label {
|
|
302
|
+
font-size: 11px;
|
|
303
|
+
color: #4caf6a;
|
|
304
|
+
margin-bottom: 4px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.preview-body .expanded-text { color: #e8e4dc; }
|
|
308
|
+
|
|
309
|
+
.preview-body .unfilled {
|
|
310
|
+
color: #e05252;
|
|
311
|
+
font-weight: 700;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.preview-empty {
|
|
315
|
+
color: #4a4a66;
|
|
316
|
+
font-style: italic;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* --- CLI command --- */
|
|
320
|
+
.cli-box {
|
|
321
|
+
background: #0e0e1c;
|
|
322
|
+
border: 1px solid #2a2a42;
|
|
323
|
+
border-radius: 4px;
|
|
324
|
+
padding: 10px 14px;
|
|
325
|
+
display: flex;
|
|
326
|
+
align-items: center;
|
|
327
|
+
justify-content: space-between;
|
|
328
|
+
gap: 12px;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.cli-text {
|
|
332
|
+
font-family: "Courier New", Consolas, monospace;
|
|
333
|
+
font-size: 12px;
|
|
334
|
+
color: #e8e4dc;
|
|
335
|
+
flex: 1;
|
|
336
|
+
overflow-x: auto;
|
|
337
|
+
white-space: nowrap;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.cli-text .dollar { color: #4caf6a; margin-right: 6px; }
|
|
341
|
+
|
|
342
|
+
.copy-btn {
|
|
343
|
+
background: #2a2a42;
|
|
344
|
+
border: 1px solid #3a3a56;
|
|
345
|
+
border-radius: 3px;
|
|
346
|
+
color: #d4a447;
|
|
347
|
+
font-size: 11px;
|
|
348
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
349
|
+
font-weight: 600;
|
|
350
|
+
padding: 5px 12px;
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
white-space: nowrap;
|
|
353
|
+
flex-shrink: 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.copy-btn:hover { background: #3a3a56; }
|
|
357
|
+
.copy-btn:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
358
|
+
.copy-btn.copied { color: #4caf6a; border-color: #4caf6a; }
|
|
359
|
+
|
|
360
|
+
/* --- Presets --- */
|
|
361
|
+
.preset-grid {
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-wrap: wrap;
|
|
364
|
+
gap: 6px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.preset-chip {
|
|
368
|
+
background: #2a2a42;
|
|
369
|
+
border: 1px solid #3a3a56;
|
|
370
|
+
border-radius: 4px;
|
|
371
|
+
padding: 6px 12px;
|
|
372
|
+
cursor: pointer;
|
|
373
|
+
transition: border-color 0.15s, background 0.15s;
|
|
374
|
+
text-align: left;
|
|
375
|
+
display: flex;
|
|
376
|
+
flex-direction: column;
|
|
377
|
+
gap: 2px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.preset-chip:hover { border-color: #d4a447; background: #32324e; }
|
|
381
|
+
.preset-chip:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
382
|
+
.preset-chip.active { border-color: #d4a447; background: rgba(212, 164, 71, 0.08); }
|
|
383
|
+
|
|
384
|
+
.preset-name {
|
|
385
|
+
font-family: "Courier New", Consolas, monospace;
|
|
386
|
+
font-size: 12px;
|
|
387
|
+
font-weight: 700;
|
|
388
|
+
color: #d4a447;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.preset-desc {
|
|
392
|
+
font-size: 10px;
|
|
393
|
+
color: #6b7a8d;
|
|
394
|
+
line-height: 1.3;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/* --- Env strip --- */
|
|
398
|
+
.env-strip {
|
|
399
|
+
display: flex;
|
|
400
|
+
flex-wrap: wrap;
|
|
401
|
+
gap: 6px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.env-entry {
|
|
405
|
+
background: #2a2a42;
|
|
406
|
+
border: 1px solid #3a3a56;
|
|
407
|
+
border-radius: 3px;
|
|
408
|
+
padding: 4px 10px;
|
|
409
|
+
font-family: "Courier New", Consolas, monospace;
|
|
410
|
+
font-size: 12px;
|
|
411
|
+
cursor: pointer;
|
|
412
|
+
transition: border-color 0.15s, background 0.15s;
|
|
413
|
+
display: inline-flex;
|
|
414
|
+
align-items: center;
|
|
415
|
+
gap: 4px;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.env-entry:hover { border-color: #d4a447; background: #32324e; }
|
|
419
|
+
|
|
420
|
+
.env-key { color: #d4a447; font-weight: 700; }
|
|
421
|
+
.env-eq { color: #4a4a66; }
|
|
422
|
+
.env-val { color: #6b7a8d; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
423
|
+
|
|
424
|
+
.env-group-label {
|
|
425
|
+
font-size: 10px;
|
|
426
|
+
font-weight: 600;
|
|
427
|
+
text-transform: uppercase;
|
|
428
|
+
letter-spacing: 0.8px;
|
|
429
|
+
color: #4a4a66;
|
|
430
|
+
margin-top: 6px;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.env-group-label:first-child { margin-top: 0; }
|
|
434
|
+
|
|
435
|
+
/* --- Collapsible toggle --- */
|
|
436
|
+
.toggle-btn {
|
|
437
|
+
background: none;
|
|
438
|
+
border: 1px solid #3a3a56;
|
|
439
|
+
border-radius: 3px;
|
|
440
|
+
color: #6b7a8d;
|
|
441
|
+
font-size: 11px;
|
|
442
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
443
|
+
padding: 3px 10px;
|
|
444
|
+
cursor: pointer;
|
|
445
|
+
align-self: flex-start;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.toggle-btn:hover { border-color: #d4a447; color: #d4a447; }
|
|
449
|
+
.toggle-btn:focus-visible { outline: 2px solid #d4a447; outline-offset: 1px; }
|
|
450
|
+
|
|
451
|
+
/* --- Cheatsheet --- */
|
|
452
|
+
.cheatsheet {
|
|
453
|
+
background: #0e0e1c;
|
|
454
|
+
border: 1px solid #2a2a42;
|
|
455
|
+
border-radius: 4px;
|
|
456
|
+
overflow: hidden;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.cheatsheet-toggle {
|
|
460
|
+
background: #1e1e34;
|
|
461
|
+
border: none;
|
|
462
|
+
width: 100%;
|
|
463
|
+
padding: 8px 14px;
|
|
464
|
+
display: flex;
|
|
465
|
+
align-items: center;
|
|
466
|
+
justify-content: space-between;
|
|
467
|
+
cursor: pointer;
|
|
468
|
+
color: #6b7a8d;
|
|
469
|
+
font-family: "Courier New", Consolas, monospace;
|
|
470
|
+
font-size: 11px;
|
|
471
|
+
text-align: left;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.cheatsheet-toggle:hover { color: #d4a447; }
|
|
475
|
+
.cheatsheet-toggle:focus-visible { outline: 2px solid #d4a447; outline-offset: -2px; }
|
|
476
|
+
|
|
477
|
+
.cheatsheet-arrow {
|
|
478
|
+
transition: transform 0.2s;
|
|
479
|
+
font-size: 10px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.cheatsheet.open .cheatsheet-arrow { transform: rotate(90deg); }
|
|
483
|
+
|
|
484
|
+
.cheatsheet-body {
|
|
485
|
+
display: none;
|
|
486
|
+
padding: 14px;
|
|
487
|
+
border-top: 1px solid #2a2a42;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.cheatsheet.open .cheatsheet-body { display: block; }
|
|
491
|
+
|
|
492
|
+
.cheat-table {
|
|
493
|
+
width: 100%;
|
|
494
|
+
border-collapse: separate;
|
|
495
|
+
border-spacing: 0;
|
|
496
|
+
font-size: 12px;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.cheat-table th {
|
|
500
|
+
font-size: 10px;
|
|
501
|
+
font-weight: 600;
|
|
502
|
+
text-transform: uppercase;
|
|
503
|
+
letter-spacing: 0.8px;
|
|
504
|
+
color: #4a4a66;
|
|
505
|
+
text-align: left;
|
|
506
|
+
padding: 0 8px 6px;
|
|
507
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.cheat-table td {
|
|
511
|
+
padding: 4px 8px;
|
|
512
|
+
vertical-align: top;
|
|
513
|
+
border-top: 1px solid #1e1e34;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.cheat-table td:first-child {
|
|
517
|
+
font-family: "Courier New", Consolas, monospace;
|
|
518
|
+
color: #d4a447;
|
|
519
|
+
white-space: nowrap;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.cheat-table td:nth-child(2) {
|
|
523
|
+
color: #6b7a8d;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.cheat-table td:nth-child(3) {
|
|
527
|
+
font-family: "Courier New", Consolas, monospace;
|
|
528
|
+
color: #e8e4dc;
|
|
529
|
+
font-size: 11px;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/* --- Responsive --- */
|
|
533
|
+
@media (max-width: 520px) {
|
|
534
|
+
body { padding: 20px 14px; }
|
|
535
|
+
.var-table .col-source { display: none; }
|
|
536
|
+
.var-table th:nth-child(3) { display: none; }
|
|
537
|
+
.var-table td:nth-child(3) { display: none; }
|
|
538
|
+
.var-table .col-name { width: 30%; }
|
|
539
|
+
.var-table .col-values { width: 58%; }
|
|
540
|
+
}
|
|
541
|
+
</style>
|
|
542
|
+
|
|
543
|
+
<div class="shell">
|
|
544
|
+
<div class="brand">
|
|
545
|
+
<div class="brand-name">prompt<b>args</b></div>
|
|
546
|
+
<div class="brand-tag">builder</div>
|
|
547
|
+
</div>
|
|
548
|
+
|
|
549
|
+
<div class="section">
|
|
550
|
+
<div class="section-label">Examples</div>
|
|
551
|
+
<div class="preset-grid" id="preset-grid"></div>
|
|
552
|
+
</div>
|
|
553
|
+
|
|
554
|
+
<div class="section">
|
|
555
|
+
<label class="section-label" for="tmpl">Template</label>
|
|
556
|
+
<textarea class="template-input" id="tmpl" placeholder="Review {{file}} for {{focus=correctness}} issues. Be {{tone=concise}} in your feedback." spellcheck="false"></textarea>
|
|
557
|
+
</div>
|
|
558
|
+
|
|
559
|
+
<div class="section">
|
|
560
|
+
<div class="section-label">Variables</div>
|
|
561
|
+
<div style="overflow-x: auto;">
|
|
562
|
+
<table class="var-table">
|
|
563
|
+
<thead>
|
|
564
|
+
<tr>
|
|
565
|
+
<th class="col-name">Name</th>
|
|
566
|
+
<th class="col-values">Value(s)</th>
|
|
567
|
+
<th class="col-source">Source</th>
|
|
568
|
+
<th class="col-rm"></th>
|
|
569
|
+
</tr>
|
|
570
|
+
</thead>
|
|
571
|
+
<tbody id="var-body"></tbody>
|
|
572
|
+
</table>
|
|
573
|
+
</div>
|
|
574
|
+
<button class="add-row" id="add-var" type="button">+ add variable</button>
|
|
575
|
+
</div>
|
|
576
|
+
|
|
577
|
+
<div class="section" id="env-section" style="display:none">
|
|
578
|
+
<div class="section-label">Environment (click to insert at cursor)</div>
|
|
579
|
+
<div id="env-git-group">
|
|
580
|
+
<div class="env-group-label">Git context</div>
|
|
581
|
+
<div class="env-strip" id="env-git"></div>
|
|
582
|
+
</div>
|
|
583
|
+
<div id="env-terminal-group" style="display:none">
|
|
584
|
+
<div class="env-group-label">Terminal env</div>
|
|
585
|
+
<div class="env-strip" id="env-terminal"></div>
|
|
586
|
+
</div>
|
|
587
|
+
<button class="toggle-btn" id="env-toggle" type="button" style="display:none">Show terminal env</button>
|
|
588
|
+
</div>
|
|
589
|
+
|
|
590
|
+
<div class="section">
|
|
591
|
+
<div class="section-label">Mustache cheatsheet</div>
|
|
592
|
+
<div class="cheatsheet" id="cheatsheet">
|
|
593
|
+
<button class="cheatsheet-toggle" type="button" id="cheatsheet-toggle">
|
|
594
|
+
<span>Template syntax reference</span>
|
|
595
|
+
<span class="cheatsheet-arrow">▶</span>
|
|
596
|
+
</button>
|
|
597
|
+
<div class="cheatsheet-body">
|
|
598
|
+
<table class="cheat-table">
|
|
599
|
+
<thead><tr><th>Syntax</th><th>What it does</th><th>Example</th></tr></thead>
|
|
600
|
+
<tbody>
|
|
601
|
+
<tr><td>{{name}}</td><td>Variable (required)</td><td>{{file}} → main.go</td></tr>
|
|
602
|
+
<tr><td>{{name=val}}</td><td>Variable with default</td><td>{{tone=concise}} → concise</td></tr>
|
|
603
|
+
<tr><td>{{! text }}</td><td>Comment (ignored)</td><td>{{! TODO: add more }}</td></tr>
|
|
604
|
+
<tr><td>{{#list}}...{{/list}}</td><td>Section (repeat block)</td><td>{{#files}}Review {{.}}{{/files}}</td></tr>
|
|
605
|
+
<tr><td>{{^list}}...{{/list}}</td><td>Inverted section (if empty)</td><td>{{^files}}No files{{/files}}</td></tr>
|
|
606
|
+
<tr><td>{{> partial}}</td><td>Include another template</td><td>{{> header}}</td></tr>
|
|
607
|
+
<tr><td>{{{name}}}</td><td>Unescaped output</td><td>{{{html}}} → raw HTML</td></tr>
|
|
608
|
+
<tr><td>{{&name}}</td><td>Unescaped (alt syntax)</td><td>Same as {{{name}}}</td></tr>
|
|
609
|
+
</tbody>
|
|
610
|
+
</table>
|
|
611
|
+
<div style="margin-top: 10px; font-size: 11px; color: #4a4a66;">
|
|
612
|
+
promptargs extends Mustache with <span style="color:#d4a447">{{name=default}}</span> syntax and array iteration via <span style="color:#d4a447">--cross</span> mode.
|
|
613
|
+
Full spec: <a href="https://mustache.github.io/mustache.5.html" target="_blank" rel="noopener" style="color: #6b7a8d;">mustache.github.io</a>
|
|
614
|
+
</div>
|
|
615
|
+
</div>
|
|
616
|
+
</div>
|
|
617
|
+
</div>
|
|
618
|
+
|
|
619
|
+
<div class="section">
|
|
620
|
+
<div class="section-label">Array mode</div>
|
|
621
|
+
<div class="mode-row">
|
|
622
|
+
<div class="mode-toggle">
|
|
623
|
+
<button class="mode-btn active" data-mode="zip" type="button">zip</button>
|
|
624
|
+
<button class="mode-btn" data-mode="cross" type="button">cross</button>
|
|
625
|
+
</div>
|
|
626
|
+
<span class="mode-desc" id="mode-desc">Arrays pair up 1:1</span>
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
629
|
+
|
|
630
|
+
<div class="section">
|
|
631
|
+
<div class="section-label">Preview</div>
|
|
632
|
+
<div class="preview-box">
|
|
633
|
+
<div class="preview-header">
|
|
634
|
+
<span class="preview-title">expanded output</span>
|
|
635
|
+
<span class="preview-count empty" id="preview-count">0 results</span>
|
|
636
|
+
</div>
|
|
637
|
+
<div class="preview-body" id="preview-body">
|
|
638
|
+
<span class="preview-empty">Type a template above...</span>
|
|
639
|
+
</div>
|
|
640
|
+
</div>
|
|
641
|
+
</div>
|
|
642
|
+
|
|
643
|
+
<div class="section">
|
|
644
|
+
<div class="section-label">CLI command</div>
|
|
645
|
+
<div class="cli-box">
|
|
646
|
+
<div class="cli-text" id="cli-text"><span class="dollar">$</span>promptargs</div>
|
|
647
|
+
<button class="copy-btn" id="copy-btn" type="button">Copy</button>
|
|
648
|
+
</div>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
|
|
652
|
+
<script>
|
|
653
|
+
(function () {
|
|
654
|
+
const VAR_RE = /\{\{(\w+)(?:=([^}]*))?\}\}/g;
|
|
655
|
+
const DEBOUNCE_MS = 120;
|
|
656
|
+
|
|
657
|
+
const PRESETS = [
|
|
658
|
+
{
|
|
659
|
+
name: 'review',
|
|
660
|
+
desc: 'Code review across files and focus areas',
|
|
661
|
+
template: 'Review {{file}} for {{focus=correctness}} issues.\nBe {{tone=concise}} in your feedback.',
|
|
662
|
+
vars: { file: 'api.go,auth.go,db.go', focus: 'security,perf,correctness', tone: 'concise' },
|
|
663
|
+
mode: 'cross',
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
name: 'explain',
|
|
667
|
+
desc: 'Explain code for different audiences',
|
|
668
|
+
template: 'Explain what {{file}} does in {{style=simple}} terms.\nAssume the reader is a {{audience=junior developer}}.',
|
|
669
|
+
vars: { file: 'src/auth.ts', style: 'simple', audience: 'junior developer' },
|
|
670
|
+
mode: 'zip',
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
name: 'fix',
|
|
674
|
+
desc: 'Bug fix with expected vs actual behavior',
|
|
675
|
+
template: 'Fix the {{issue}} in {{file}}.\nExpected: {{expected}}\nActual: {{actual}}',
|
|
676
|
+
vars: { issue: 'null pointer', file: 'main.go', expected: 'returns user object', actual: 'crashes on line 42' },
|
|
677
|
+
mode: 'zip',
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: 'test',
|
|
681
|
+
desc: 'Generate tests for multiple files',
|
|
682
|
+
template: 'Write {{coverage=unit}} tests for {{file}}.\nFocus on edge cases and error paths.\nUse {{framework=vitest}}.',
|
|
683
|
+
vars: { file: 'auth.ts,api.ts,db.ts', coverage: 'unit', framework: 'vitest' },
|
|
684
|
+
mode: 'zip',
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
name: 'migrate',
|
|
688
|
+
desc: 'Batch migrate files between patterns',
|
|
689
|
+
template: 'Migrate {{file}} from {{from}} to {{to}}.\nPreserve all existing behavior.',
|
|
690
|
+
vars: { file: 'UserService.java,OrderService.java,PaymentService.java', from: 'class components', to: 'functional hooks' },
|
|
691
|
+
mode: 'zip',
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
name: 'security',
|
|
695
|
+
desc: 'Security audit across OWASP categories',
|
|
696
|
+
template: 'Audit {{file}} for {{category}} vulnerabilities.\nSeverity threshold: {{severity=medium}}.\nReport format: file, line, finding, fix.',
|
|
697
|
+
vars: { file: 'api.go,auth.go,handlers.go', category: 'injection,XSS,auth-bypass,SSRF', severity: 'medium' },
|
|
698
|
+
mode: 'cross',
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
name: 'docs',
|
|
702
|
+
desc: 'Generate docs for public APIs',
|
|
703
|
+
template: 'Write {{format=JSDoc}} documentation for the public API in {{file}}.\nInclude parameter types, return values, and one usage example.',
|
|
704
|
+
vars: { file: 'src/client.ts,src/server.ts,src/utils.ts', format: 'JSDoc' },
|
|
705
|
+
mode: 'zip',
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
name: 'refactor',
|
|
709
|
+
desc: 'Refactor with specific goals',
|
|
710
|
+
template: 'Refactor {{file}} to improve {{goal}}.\nConstraints: no behavior changes, keep existing tests passing.',
|
|
711
|
+
vars: { file: 'legacy/handler.ts', goal: 'readability,testability,type safety' },
|
|
712
|
+
mode: 'cross',
|
|
713
|
+
},
|
|
714
|
+
];
|
|
715
|
+
|
|
716
|
+
let mode = 'zip';
|
|
717
|
+
let debounceTimer = null;
|
|
718
|
+
|
|
719
|
+
const tmpl = document.getElementById('tmpl');
|
|
720
|
+
const varBody = document.getElementById('var-body');
|
|
721
|
+
const addBtn = document.getElementById('add-var');
|
|
722
|
+
const presetGrid = document.getElementById('preset-grid');
|
|
723
|
+
const previewBody = document.getElementById('preview-body');
|
|
724
|
+
const previewCount = document.getElementById('preview-count');
|
|
725
|
+
const cliText = document.getElementById('cli-text');
|
|
726
|
+
const copyBtn = document.getElementById('copy-btn');
|
|
727
|
+
const modeDesc = document.getElementById('mode-desc');
|
|
728
|
+
|
|
729
|
+
function parseVarsFromTemplate(text) {
|
|
730
|
+
const seen = new Set();
|
|
731
|
+
const vars = [];
|
|
732
|
+
let m;
|
|
733
|
+
VAR_RE.lastIndex = 0;
|
|
734
|
+
while ((m = VAR_RE.exec(text)) !== null) {
|
|
735
|
+
if (!seen.has(m[1])) {
|
|
736
|
+
seen.add(m[1]);
|
|
737
|
+
vars.push({ name: m[1], defaultValue: m[2] });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return vars;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function getRowData() {
|
|
744
|
+
const rows = [];
|
|
745
|
+
for (const tr of varBody.querySelectorAll('tr')) {
|
|
746
|
+
const name = tr.querySelector('.var-name').value.trim();
|
|
747
|
+
const values = tr.querySelector('.var-values').value.trim();
|
|
748
|
+
const source = tr.querySelector('.var-source').value;
|
|
749
|
+
if (name) rows.push({ name, values, source });
|
|
750
|
+
}
|
|
751
|
+
return rows;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function syncVarsFromTemplate() {
|
|
755
|
+
const text = tmpl.value;
|
|
756
|
+
const parsed = parseVarsFromTemplate(text);
|
|
757
|
+
const existing = getRowData();
|
|
758
|
+
const existingNames = new Set(existing.map(r => r.name));
|
|
759
|
+
|
|
760
|
+
for (const v of parsed) {
|
|
761
|
+
if (!existingNames.has(v.name)) {
|
|
762
|
+
addVarRow(v.name, v.defaultValue || '', 'manual');
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
function addVarRow(name, values, source) {
|
|
768
|
+
const tr = document.createElement('tr');
|
|
769
|
+
tr.innerHTML = `
|
|
770
|
+
<td class="col-name"><div class="name-cell"><input class="var-input var-name" value="${esc(name)}" placeholder="name" spellcheck="false"><button class="insert-btn" type="button" title="Insert {{${esc(name)}}} at cursor">↓</button></div></td>
|
|
771
|
+
<td class="col-values"><input class="var-input var-values" value="${esc(values)}" placeholder="value1,value2,..." spellcheck="false"></td>
|
|
772
|
+
<td class="col-source"><select class="source-select var-source">
|
|
773
|
+
<option value="manual"${source === 'manual' ? ' selected' : ''}>manual</option>
|
|
774
|
+
<option value="glob"${source === 'glob' ? ' selected' : ''}>glob</option>
|
|
775
|
+
<option value="file"${source === 'file' ? ' selected' : ''}>@file</option>
|
|
776
|
+
</select></td>
|
|
777
|
+
<td class="col-rm"><button class="rm-btn" type="button" title="Remove">×</button></td>
|
|
778
|
+
`;
|
|
779
|
+
tr.querySelector('.rm-btn').addEventListener('click', () => { tr.remove(); scheduleUpdate(); });
|
|
780
|
+
tr.querySelector('.insert-btn').addEventListener('click', () => {
|
|
781
|
+
const varName = tr.querySelector('.var-name').value.trim();
|
|
782
|
+
if (varName) insertAtCursor(`{{${varName}}}`);
|
|
783
|
+
});
|
|
784
|
+
for (const input of tr.querySelectorAll('input, select')) {
|
|
785
|
+
input.addEventListener('input', scheduleUpdate);
|
|
786
|
+
}
|
|
787
|
+
varBody.appendChild(tr);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
function esc(s) {
|
|
791
|
+
return s.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<');
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function escHtml(s) {
|
|
795
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function insertAtCursor(text) {
|
|
799
|
+
const start = tmpl.selectionStart;
|
|
800
|
+
const end = tmpl.selectionEnd;
|
|
801
|
+
const before = tmpl.value.slice(0, start);
|
|
802
|
+
const after = tmpl.value.slice(end);
|
|
803
|
+
tmpl.value = before + text + after;
|
|
804
|
+
const newPos = start + text.length;
|
|
805
|
+
tmpl.setSelectionRange(newPos, newPos);
|
|
806
|
+
tmpl.focus();
|
|
807
|
+
for (const chip of presetGrid.querySelectorAll('.preset-chip')) chip.classList.remove('active');
|
|
808
|
+
scheduleUpdate();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function splitValues(raw, source) {
|
|
812
|
+
if (!raw) return [];
|
|
813
|
+
if (source === 'file') return [raw];
|
|
814
|
+
return raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function cartesian(arrays) {
|
|
818
|
+
if (arrays.length === 0) return [[]];
|
|
819
|
+
const first = arrays[0];
|
|
820
|
+
const rest = cartesian(arrays.slice(1));
|
|
821
|
+
const out = [];
|
|
822
|
+
for (const v of first) {
|
|
823
|
+
for (const r of rest) {
|
|
824
|
+
out.push([v, ...r]);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return out;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function zip(arrays) {
|
|
831
|
+
const maxLen = Math.max(...arrays.map(a => a.length), 1);
|
|
832
|
+
const out = [];
|
|
833
|
+
for (let i = 0; i < maxLen; i++) {
|
|
834
|
+
out.push(arrays.map(a => a[Math.min(i, a.length - 1)]));
|
|
835
|
+
}
|
|
836
|
+
return out;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function expand(template, values) {
|
|
840
|
+
let result = template;
|
|
841
|
+
VAR_RE.lastIndex = 0;
|
|
842
|
+
result = result.replace(VAR_RE, (match, name, def) => {
|
|
843
|
+
if (name in values) return values[name];
|
|
844
|
+
if (def !== undefined) return def;
|
|
845
|
+
return match;
|
|
846
|
+
});
|
|
847
|
+
return result;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function highlightUnfilled(html) {
|
|
851
|
+
return html.replace(/\{\{(\w+)(?:=[^}]*)?\}\}/g, '<span class="unfilled">$&</span>');
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function update() {
|
|
855
|
+
const text = tmpl.value;
|
|
856
|
+
const rows = getRowData();
|
|
857
|
+
|
|
858
|
+
if (!text.trim()) {
|
|
859
|
+
previewBody.innerHTML = '<span class="preview-empty">Type a template above...</span>';
|
|
860
|
+
previewCount.textContent = '0 results';
|
|
861
|
+
previewCount.className = 'preview-count empty';
|
|
862
|
+
cliText.innerHTML = '<span class="dollar">$</span>promptargs';
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const arrayVars = [];
|
|
867
|
+
const scalarValues = {};
|
|
868
|
+
|
|
869
|
+
for (const row of rows) {
|
|
870
|
+
const vals = splitValues(row.values, row.source);
|
|
871
|
+
if (vals.length > 1) {
|
|
872
|
+
arrayVars.push({ name: row.name, values: vals, source: row.source });
|
|
873
|
+
} else if (vals.length === 1) {
|
|
874
|
+
scalarValues[row.name] = vals[0];
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
let combos;
|
|
879
|
+
if (arrayVars.length === 0) {
|
|
880
|
+
combos = [[]];
|
|
881
|
+
} else if (mode === 'cross') {
|
|
882
|
+
combos = cartesian(arrayVars.map(v => v.values));
|
|
883
|
+
} else {
|
|
884
|
+
combos = zip(arrayVars.map(v => v.values));
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
const results = [];
|
|
888
|
+
for (const combo of combos) {
|
|
889
|
+
const vals = { ...scalarValues };
|
|
890
|
+
arrayVars.forEach((v, i) => { vals[v.name] = combo[i]; });
|
|
891
|
+
results.push({ values: { ...vals }, expanded: expand(text, vals) });
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const count = results.length;
|
|
895
|
+
previewCount.textContent = count === 1 ? '1 result' : `${count} results`;
|
|
896
|
+
previewCount.className = count > 0 ? 'preview-count has-results' : 'preview-count empty';
|
|
897
|
+
|
|
898
|
+
let html = '';
|
|
899
|
+
for (let i = 0; i < results.length; i++) {
|
|
900
|
+
const r = results[i];
|
|
901
|
+
const labelParts = Object.entries(r.values).map(([k, v]) => `${k}=${v}`).join(' ');
|
|
902
|
+
html += `<div class="iteration">`;
|
|
903
|
+
if (results.length > 1) {
|
|
904
|
+
html += `<div class="iter-label">[${i + 1}/${count}] ${escHtml(labelParts)}</div>`;
|
|
905
|
+
}
|
|
906
|
+
html += `<div class="expanded-text">${highlightUnfilled(escHtml(r.expanded))}</div>`;
|
|
907
|
+
html += `</div>`;
|
|
908
|
+
}
|
|
909
|
+
previewBody.innerHTML = html || '<span class="preview-empty">No output</span>';
|
|
910
|
+
|
|
911
|
+
// Build CLI command
|
|
912
|
+
let cli = 'promptargs';
|
|
913
|
+
const hasInline = !text.includes('\n') && text.length < 200;
|
|
914
|
+
if (hasInline) {
|
|
915
|
+
cli += ` "${text.replace(/"/g, '\\"')}"`;
|
|
916
|
+
}
|
|
917
|
+
for (const row of rows) {
|
|
918
|
+
if (!row.values) continue;
|
|
919
|
+
const prefix = row.source === 'file' ? '@' : '';
|
|
920
|
+
const val = row.source === 'glob' ? `"${row.values}"` : `${prefix}${row.values}`;
|
|
921
|
+
cli += ` --${row.name}=${val}`;
|
|
922
|
+
}
|
|
923
|
+
if (mode === 'cross' && arrayVars.length > 1) {
|
|
924
|
+
cli += ' --cross';
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
cliText.innerHTML = `<span class="dollar">$</span>${escHtml(cli)}`;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function scheduleUpdate() {
|
|
931
|
+
clearTimeout(debounceTimer);
|
|
932
|
+
debounceTimer = setTimeout(() => {
|
|
933
|
+
syncVarsFromTemplate();
|
|
934
|
+
update();
|
|
935
|
+
}, DEBOUNCE_MS);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function loadPreset(preset) {
|
|
939
|
+
// Clear existing vars
|
|
940
|
+
varBody.innerHTML = '';
|
|
941
|
+
// Set template
|
|
942
|
+
tmpl.value = preset.template;
|
|
943
|
+
// Set mode
|
|
944
|
+
mode = preset.mode;
|
|
945
|
+
for (const b of document.querySelectorAll('.mode-btn')) {
|
|
946
|
+
b.classList.toggle('active', b.dataset.mode === mode);
|
|
947
|
+
}
|
|
948
|
+
modeDesc.textContent = mode === 'zip' ? 'Arrays pair up 1:1' : 'Every combination of every value';
|
|
949
|
+
// Add vars with values
|
|
950
|
+
syncVarsFromTemplate();
|
|
951
|
+
for (const tr of varBody.querySelectorAll('tr')) {
|
|
952
|
+
const nameInput = tr.querySelector('.var-name');
|
|
953
|
+
const valInput = tr.querySelector('.var-values');
|
|
954
|
+
if (nameInput && preset.vars[nameInput.value] !== undefined) {
|
|
955
|
+
valInput.value = preset.vars[nameInput.value];
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
// Highlight active preset
|
|
959
|
+
for (const chip of presetGrid.querySelectorAll('.preset-chip')) {
|
|
960
|
+
chip.classList.toggle('active', chip.dataset.preset === preset.name);
|
|
961
|
+
}
|
|
962
|
+
update();
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function renderPresets() {
|
|
966
|
+
for (const p of PRESETS) {
|
|
967
|
+
const chip = document.createElement('button');
|
|
968
|
+
chip.className = 'preset-chip';
|
|
969
|
+
chip.type = 'button';
|
|
970
|
+
chip.dataset.preset = p.name;
|
|
971
|
+
chip.innerHTML = `<span class="preset-name">${esc(p.name)}</span><span class="preset-desc">${esc(p.desc)}</span>`;
|
|
972
|
+
chip.addEventListener('click', () => loadPreset(p));
|
|
973
|
+
presetGrid.appendChild(chip);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Event listeners
|
|
978
|
+
tmpl.addEventListener('input', () => {
|
|
979
|
+
// Deactivate preset chip when user edits manually
|
|
980
|
+
for (const chip of presetGrid.querySelectorAll('.preset-chip')) chip.classList.remove('active');
|
|
981
|
+
scheduleUpdate();
|
|
982
|
+
});
|
|
983
|
+
addBtn.addEventListener('click', () => { addVarRow('', '', 'manual'); });
|
|
984
|
+
|
|
985
|
+
for (const btn of document.querySelectorAll('.mode-btn')) {
|
|
986
|
+
btn.addEventListener('click', () => {
|
|
987
|
+
for (const b of document.querySelectorAll('.mode-btn')) b.classList.remove('active');
|
|
988
|
+
btn.classList.add('active');
|
|
989
|
+
mode = btn.dataset.mode;
|
|
990
|
+
modeDesc.textContent = mode === 'zip' ? 'Arrays pair up 1:1' : 'Every combination of every value';
|
|
991
|
+
update();
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
copyBtn.addEventListener('click', () => {
|
|
996
|
+
const text = cliText.textContent.replace(/^\$\s*/, '');
|
|
997
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
998
|
+
copyBtn.textContent = 'Copied';
|
|
999
|
+
copyBtn.classList.add('copied');
|
|
1000
|
+
setTimeout(() => { copyBtn.textContent = 'Copy'; copyBtn.classList.remove('copied'); }, 1500);
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
function renderEnvChips(container, entries) {
|
|
1005
|
+
container.innerHTML = '';
|
|
1006
|
+
for (const [k, v] of entries) {
|
|
1007
|
+
const span = document.createElement('span');
|
|
1008
|
+
span.className = 'env-entry';
|
|
1009
|
+
span.title = `Click to insert {{${k}}} at cursor`;
|
|
1010
|
+
span.innerHTML = `<span class="env-key">{{${escHtml(k)}}}</span> <span class="env-eq">=</span> <span class="env-val">${escHtml(v)}</span>`;
|
|
1011
|
+
span.addEventListener('click', () => insertAtCursor(`{{${k}}}`));
|
|
1012
|
+
container.appendChild(span);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// Render env vars if provided by the server
|
|
1017
|
+
if (window.__PROMPTARGS_ENV__) {
|
|
1018
|
+
const envSection = document.getElementById('env-section');
|
|
1019
|
+
const envGit = document.getElementById('env-git');
|
|
1020
|
+
const envTerminal = document.getElementById('env-terminal');
|
|
1021
|
+
const envGitGroup = document.getElementById('env-git-group');
|
|
1022
|
+
const envTerminalGroup = document.getElementById('env-terminal-group');
|
|
1023
|
+
const envToggle = document.getElementById('env-toggle');
|
|
1024
|
+
const data = window.__PROMPTARGS_ENV__;
|
|
1025
|
+
|
|
1026
|
+
const gitEntries = Object.entries(data.git || {}).filter(([, v]) => v);
|
|
1027
|
+
const termEntries = Object.entries(data.terminal || {}).filter(([, v]) => v);
|
|
1028
|
+
|
|
1029
|
+
if (gitEntries.length > 0 || termEntries.length > 0) {
|
|
1030
|
+
envSection.style.display = '';
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
if (gitEntries.length > 0) {
|
|
1034
|
+
renderEnvChips(envGit, gitEntries);
|
|
1035
|
+
} else {
|
|
1036
|
+
envGitGroup.style.display = 'none';
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
if (termEntries.length > 0) {
|
|
1040
|
+
envToggle.style.display = '';
|
|
1041
|
+
let termVisible = false;
|
|
1042
|
+
envToggle.addEventListener('click', () => {
|
|
1043
|
+
termVisible = !termVisible;
|
|
1044
|
+
envTerminalGroup.style.display = termVisible ? '' : 'none';
|
|
1045
|
+
envToggle.textContent = termVisible ? 'Hide terminal env' : `Show terminal env (${termEntries.length} vars)`;
|
|
1046
|
+
if (termVisible && envTerminal.children.length === 0) {
|
|
1047
|
+
renderEnvChips(envTerminal, termEntries);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
envToggle.textContent = `Show terminal env (${termEntries.length} vars)`;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Cheatsheet toggle
|
|
1055
|
+
const cheatsheet = document.getElementById('cheatsheet');
|
|
1056
|
+
const cheatsheetToggle = document.getElementById('cheatsheet-toggle');
|
|
1057
|
+
cheatsheetToggle.addEventListener('click', () => {
|
|
1058
|
+
cheatsheet.classList.toggle('open');
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
// Init
|
|
1062
|
+
renderPresets();
|
|
1063
|
+
loadPreset(PRESETS[0]);
|
|
1064
|
+
})();
|
|
1065
|
+
</script>
|