citadel_cli 1.4.0 → 1.4.2

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/README.md CHANGED
@@ -1,28 +1,43 @@
1
1
  # Citadel CLI
2
2
 
3
- A keyboard-first command console for the power users of your web apps.
3
+ Embed a terminal-style command console directly inside your React app.
4
4
 
5
- # Use Cases
5
+ Citadel helps you turn repetitive UI workflows into fast keyboard commands for
6
+ developers, support engineers, and power users, without sending them to a
7
+ separate admin tool.
6
8
 
7
- - **API Testing & Debugging**: Execute REST calls, inspect responses, and manipulate cookies/localStorage without leaving your
8
- application context
9
- - **Power User Workflows**: Transform repetitive click-through sequences into fast keyboard commands for advanced users
10
- - **DevOps Integration**: Add command-line control to CI/CD dashboards and deployment tools for rapid operations
11
- - **Internal Tool Enhancement**: Give your team's web applications the speed and efficiency of terminal interfaces
12
- - Perform (multiple) REST API calls & view results, view/modify cookies/localstorage. Do JavaScript things without affecting the application.
9
+ ## Why Developers Add Citadel
10
+
11
+ - **Move faster in existing apps**: expose internal actions as commands instead
12
+ of building more buttons and forms
13
+ - **Debug in context**: call APIs, inspect JSON, clear storage, and run app
14
+ actions without leaving the page
15
+ - **Keep UI clean**: hidden-by-default overlay (toggle key is configurable;
16
+ default is `.`, and can be shown on load if desired)
17
+ - **Scale safely**: typed command DSL with argument help, async handlers, and
18
+ structured result rendering (`text`, `json`, `image`, `error`, `bool`)
19
+
20
+ ## Common Use Cases
21
+
22
+ - **Internal Tools**: replace repetitive click paths with direct commands
23
+ - **Support & Operations**: add safe operational commands to admin dashboards
24
+ - **API Testing & Debugging**: execute REST calls and inspect responses inline
25
+ - **Power User Workflows**: give advanced users terminal speed in web UI
13
26
 
14
27
  ![Animated screenshot of Citadel CLI](https://github.com/user-attachments/assets/b64da0f7-a4a0-4f76-bc03-c0e40c0e14e5)
15
28
 
16
- # Installation
29
+ ## Installation
17
30
 
18
31
  ```bash
19
32
  npm i citadel_cli
20
33
  ```
21
34
 
22
- ## "Hello world" Example
35
+ ## Quick Start (Hello World)
36
+
37
+ Commands are the core concept in Citadel. Think `user add 1234` or
38
+ `qa deploy my_feature_branch`.
23
39
 
24
- A core concept in Citadel are commands. Commands are things like "user add 1234"
25
- or "qa1 deploy my_feature_branch". To initialize and add commands:
40
+ To get running:
26
41
 
27
42
  1. Define commands with the typed DSL
28
43
  2. Build a `CommandRegistry` from those definitions
@@ -83,6 +98,7 @@ Handlers must return one of the following:
83
98
  - `JsonCommandResult`
84
99
  - `ImageCommandResult`
85
100
  - `ErrorCommandResult`
101
+ - `BooleanCommandResult`
86
102
 
87
103
  ## Typed DSL
88
104
 
@@ -117,6 +133,31 @@ DSL handlers receive:
117
133
  - `namedArgs`: argument-name map (`Record<string, string | undefined>`)
118
134
  - `commandPath`: dot-delimited path string
119
135
 
136
+ Helper constructors exported by the DSL:
137
+
138
+ - `text(value)`
139
+ - `json(value)`
140
+ - `image(url, altText?)`
141
+ - `error(message)`
142
+ - `bool(value, trueText?, falseText?)`
143
+
144
+ ### Boolean Result Example
145
+
146
+ ```typescript
147
+ import { command, createCommandRegistry, bool } from "citadel_cli";
148
+
149
+ const registry = createCommandRegistry([
150
+ command("bool.random")
151
+ .describe("Return a random boolean")
152
+ .handle(async () => bool(Math.random() >= 0.5, "👍", "👎")),
153
+ ]);
154
+ ```
155
+
156
+ Demo registries include boolean commands:
157
+
158
+ - Basic example: `bool.true`, `bool.false`, `bool.random`
159
+ - DevOps example: `bool.deploy.window`, `bool.error.budget.healthy`, `bool.autoscale.recommended`
160
+
120
161
  ## Legacy `addCommand` API
121
162
 
122
163
  `CommandRegistry#addCommand` still works and is fully supported. The DSL is now
@@ -170,8 +211,11 @@ const config = {
170
211
  initialHeight: '50vh',
171
212
  minHeight: '200',
172
213
  outputFontSize: '0.75rem', // optional CSS font-size override for output text
214
+ showOutputPane: true, // set false to hide command output pane
173
215
  resetStateOnHide: false,
216
+ closeOnEscape: true,
174
217
  showCitadelKey: '.',
218
+ showOnLoad: false,
175
219
  cursorType: 'blink', // 'blink', 'spin', 'solid', or 'bbs'
176
220
  cursorSpeed: 530,
177
221
  storage: {
@@ -0,0 +1,472 @@
1
+ :host {
2
+ --citadel-bg: oklch(20.8% 0.042 265.8);
3
+ --citadel-surface: oklch(27.9% 0.041 260);
4
+ --citadel-border: oklch(37.2% 0.044 257.3);
5
+ --citadel-text: oklch(92.8% 0.006 264.5);
6
+ --citadel-muted: oklch(70.7% 0.022 261.3);
7
+ --citadel-subtle: oklch(55.1% 0.023 264.4);
8
+ --citadel-word: oklch(70.7% 0.165 254.6);
9
+ --citadel-arg: oklch(72.3% 0.219 149.6);
10
+ --citadel-error: oklch(70.4% 0.191 22.2);
11
+ --citadel-success: oklch(72.3% 0.219 149.6);
12
+ --citadel-min-height: 200px;
13
+ --citadel-max-height: 80vh;
14
+ --citadel-default-height: 35vh;
15
+ --citadel-panel-enter-duration: 200ms;
16
+ --citadel-panel-exit-duration: 200ms;
17
+ --citadel-panel-enter-easing: cubic-bezier(0.16, 1, 0.3, 1);
18
+ --citadel-panel-exit-easing: cubic-bezier(0.4, 0, 1, 1);
19
+
20
+ display: block;
21
+ pointer-events: auto;
22
+ color: var(--citadel-text);
23
+ font-synthesis: none;
24
+ text-rendering: optimizeLegibility;
25
+ -webkit-font-smoothing: antialiased;
26
+ -moz-osx-font-smoothing: grayscale;
27
+ }
28
+
29
+ :host([data-display-mode='panel']) {
30
+ position: fixed;
31
+ bottom: 0;
32
+ left: 0;
33
+ right: 0;
34
+ width: 100%;
35
+ height: var(--citadel-default-height);
36
+ max-height: var(--citadel-max-height);
37
+ min-height: var(--citadel-min-height);
38
+ z-index: 2147483647;
39
+ overflow: hidden;
40
+ }
41
+
42
+ :host([data-display-mode='inline']) {
43
+ position: relative;
44
+ bottom: auto;
45
+ left: auto;
46
+ right: auto;
47
+ z-index: auto;
48
+ width: 100%;
49
+ height: 100%;
50
+ max-height: none;
51
+ min-height: 0;
52
+ overflow: hidden;
53
+ }
54
+
55
+ #citadel-root {
56
+ width: 100%;
57
+ height: 100%;
58
+ }
59
+
60
+ #citadel-root,
61
+ #citadel-root * {
62
+ box-sizing: border-box;
63
+ }
64
+
65
+ .panelContainer {
66
+ position: fixed;
67
+ bottom: 0;
68
+ left: 0;
69
+ right: 0;
70
+ width: 100%;
71
+ height: var(--citadel-default-height);
72
+ min-height: var(--citadel-min-height);
73
+ max-height: var(--citadel-max-height);
74
+ margin: 0;
75
+ padding: 0;
76
+ background-color: var(--citadel-bg);
77
+ overflow: hidden;
78
+ transform: translateY(0);
79
+ will-change: transform;
80
+ }
81
+
82
+ .innerContainer {
83
+ width: 100%;
84
+ height: 100%;
85
+ margin: 0;
86
+ padding: 0;
87
+ display: flex;
88
+ flex: 1;
89
+ flex-direction: column;
90
+ }
91
+
92
+ .citadel-tty {
93
+ background: var(--citadel-bg);
94
+ }
95
+
96
+ .citadel-tty-output-pane {
97
+ display: flex;
98
+ flex: 1 1 auto;
99
+ min-height: 0;
100
+ padding: 0.75rem 1rem 0;
101
+ }
102
+
103
+ .citadel-tty-input-region {
104
+ flex-shrink: 0;
105
+ }
106
+
107
+ .resizeHandle {
108
+ position: absolute;
109
+ top: -3px;
110
+ left: 0;
111
+ right: 0;
112
+ z-index: 10;
113
+ width: 100%;
114
+ height: 6px;
115
+ background: transparent;
116
+ cursor: ns-resize;
117
+ user-select: none;
118
+ -webkit-user-select: none;
119
+ pointer-events: all;
120
+ }
121
+
122
+ .resizeHandle:hover {
123
+ background: color-mix(in oklch, var(--citadel-text) 10%, transparent);
124
+ }
125
+
126
+ @keyframes citadel_slideUp {
127
+ from {
128
+ transform: translateY(100%);
129
+ }
130
+
131
+ to {
132
+ transform: translateY(0);
133
+ }
134
+ }
135
+
136
+ @keyframes citadel_slideDown {
137
+ from {
138
+ transform: translateY(0);
139
+ }
140
+
141
+ to {
142
+ transform: translateY(100%);
143
+ }
144
+ }
145
+
146
+ .citadel_slideUp {
147
+ animation: citadel_slideUp var(--citadel-panel-enter-duration) var(--citadel-panel-enter-easing) forwards;
148
+ }
149
+
150
+ .citadel_slideDown {
151
+ animation: citadel_slideDown var(--citadel-panel-exit-duration) var(--citadel-panel-exit-easing) forwards;
152
+ }
153
+
154
+ @media (prefers-reduced-motion: reduce) {
155
+ :host {
156
+ --citadel-panel-enter-duration: 1ms;
157
+ --citadel-panel-exit-duration: 1ms;
158
+ }
159
+ }
160
+
161
+ .inlineContainer {
162
+ position: relative;
163
+ width: 100%;
164
+ height: 100%;
165
+ display: flex;
166
+ flex-direction: column;
167
+ background-color: var(--citadel-bg);
168
+ overflow: hidden;
169
+ }
170
+
171
+ .citadel-output {
172
+ width: 100%;
173
+ height: 100%;
174
+ overflow-y: auto;
175
+ padding: 0.75rem;
176
+ border: 1px solid var(--citadel-border);
177
+ border-radius: 0.5rem;
178
+ background: color-mix(in oklch, var(--citadel-bg) 75%, black);
179
+ text-align: left;
180
+ }
181
+
182
+ .citadel-output-item {
183
+ margin-bottom: 1rem;
184
+ }
185
+
186
+ .citadel-output-item:last-child {
187
+ margin-bottom: 0;
188
+ }
189
+
190
+ .citadel-output-content {
191
+ margin-top: 0.35rem;
192
+ color: var(--citadel-text);
193
+ }
194
+
195
+ .citadel-output-content > :first-child {
196
+ margin-top: 0;
197
+ }
198
+
199
+ .citadel-output-content > :last-child {
200
+ margin-bottom: 0;
201
+ }
202
+
203
+ .citadel-output-line {
204
+ display: flex;
205
+ flex-wrap: wrap;
206
+ align-items: center;
207
+ gap: 0.5rem;
208
+ }
209
+
210
+ .citadel-output-command,
211
+ .citadel-output-command-word {
212
+ color: var(--citadel-text);
213
+ }
214
+
215
+ .citadel-output-command-arg {
216
+ color: var(--citadel-arg);
217
+ }
218
+
219
+ .citadel-output-separator {
220
+ color: var(--citadel-muted);
221
+ }
222
+
223
+ .citadel-output-timestamp {
224
+ color: var(--citadel-subtle);
225
+ }
226
+
227
+ .citadel-status-dot {
228
+ width: 1rem;
229
+ height: 1rem;
230
+ border-radius: 999px;
231
+ }
232
+
233
+ .citadel-status-dot-success {
234
+ background: var(--citadel-success);
235
+ }
236
+
237
+ .citadel-status-dot-failure {
238
+ background: var(--citadel-error);
239
+ }
240
+
241
+ .citadel-spinner {
242
+ width: 1rem;
243
+ height: 1rem;
244
+ border: 2px solid color-mix(in oklch, var(--citadel-text) 60%, transparent);
245
+ border-top-color: color-mix(in oklch, var(--citadel-muted) 80%, black);
246
+ border-radius: 999px;
247
+ animation: citadel-spin 0.9s linear infinite;
248
+ }
249
+
250
+ @keyframes citadel-spin {
251
+ to {
252
+ transform: rotate(360deg);
253
+ }
254
+ }
255
+
256
+ .citadel-input-shell {
257
+ width: 100%;
258
+ padding: 1rem;
259
+ border-radius: 0.5rem;
260
+ background-color: var(--citadel-bg);
261
+ }
262
+
263
+ .citadel-input-line {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: 0.5rem;
267
+ }
268
+
269
+ .citadel-input-prompt {
270
+ color: var(--citadel-muted);
271
+ }
272
+
273
+ .citadel-input-row {
274
+ display: flex;
275
+ align-items: center;
276
+ flex: 1 1 auto;
277
+ min-width: 0;
278
+ }
279
+
280
+ .citadel-input-segments {
281
+ display: flex;
282
+ align-items: center;
283
+ gap: 0.25rem;
284
+ }
285
+
286
+ .citadel-input-segment-arg {
287
+ color: var(--citadel-text);
288
+ white-space: pre;
289
+ }
290
+
291
+ .citadel-input-segment-word {
292
+ color: var(--citadel-word);
293
+ white-space: pre;
294
+ }
295
+
296
+ .citadel-input-segment-space {
297
+ color: var(--citadel-text);
298
+ white-space: pre;
299
+ }
300
+
301
+ .citadel-input-segment-space-command {
302
+ color: var(--citadel-word);
303
+ }
304
+
305
+ .citadel-input-control {
306
+ position: relative;
307
+ flex: 1 1 auto;
308
+ min-width: 0;
309
+ }
310
+
311
+ .citadel-input-measure {
312
+ position: absolute;
313
+ top: 0;
314
+ left: 0;
315
+ visibility: hidden;
316
+ pointer-events: none;
317
+ white-space: pre;
318
+ font: inherit;
319
+ letter-spacing: inherit;
320
+ }
321
+
322
+ .citadel-input-field {
323
+ width: 100%;
324
+ margin: 0;
325
+ padding: 0;
326
+ border: 0;
327
+ border-radius: 0;
328
+ outline: none;
329
+ background: transparent;
330
+ font: inherit;
331
+ line-height: inherit;
332
+ letter-spacing: inherit;
333
+ appearance: none;
334
+ -webkit-appearance: none;
335
+ caret-color: transparent;
336
+ }
337
+
338
+ .citadel-input-field::placeholder {
339
+ color: var(--citadel-subtle);
340
+ opacity: 1;
341
+ }
342
+
343
+ .citadel-input-field.is-command-mode {
344
+ color: var(--citadel-word);
345
+ }
346
+
347
+ .citadel-input-field.is-argument-mode {
348
+ color: var(--citadel-text);
349
+ }
350
+
351
+ .citadel-input-cursor {
352
+ position: absolute;
353
+ top: 0;
354
+ pointer-events: none;
355
+ }
356
+
357
+ @keyframes citadel-invalid-glow {
358
+ 0%,
359
+ 100% {
360
+ box-shadow: 0 0 0 color-mix(in oklch, var(--citadel-error) 0%, transparent);
361
+ }
362
+
363
+ 50% {
364
+ box-shadow: 0 0 8px color-mix(in oklch, var(--citadel-error) 70%, transparent);
365
+ }
366
+ }
367
+
368
+ .invalid-input-animation {
369
+ animation: citadel-invalid-glow 0.4s ease-in-out;
370
+ }
371
+
372
+ .citadel-cursor-wrapper {
373
+ position: relative;
374
+ display: inline-block;
375
+ }
376
+
377
+ .command-cursor {
378
+ display: inline-block;
379
+ white-space: pre;
380
+ }
381
+
382
+ @keyframes citadel-shake {
383
+ 0%,
384
+ 100% {
385
+ transform: translateX(0);
386
+ }
387
+
388
+ 25% {
389
+ transform: translateX(-4px);
390
+ }
391
+
392
+ 75% {
393
+ transform: translateX(4px);
394
+ }
395
+ }
396
+
397
+ .animate-shake {
398
+ animation: citadel-shake 0.2s ease-in-out;
399
+ }
400
+
401
+ .citadel-available-commands {
402
+ margin-top: 0.5rem;
403
+ padding: 0.5rem 1rem;
404
+ border-top: 1px solid var(--citadel-border);
405
+ }
406
+
407
+ .citadel-available-commands-content {
408
+ color: var(--citadel-muted);
409
+ }
410
+
411
+ .citadel-available-chip-list {
412
+ display: flex;
413
+ flex-wrap: wrap;
414
+ gap: 0.5rem;
415
+ }
416
+
417
+ .citadel-available-chip {
418
+ padding: 0.25rem 0.5rem;
419
+ border-radius: 0.375rem;
420
+ background: var(--citadel-surface);
421
+ }
422
+
423
+ .citadel-available-chip-text {
424
+ color: white;
425
+ }
426
+
427
+ .citadel-available-chip-prefix {
428
+ text-decoration: underline;
429
+ }
430
+
431
+ .citadel-available-next-arg {
432
+ color: var(--citadel-word);
433
+ }
434
+
435
+ .citadel-available-next-desc {
436
+ margin-left: 0.5rem;
437
+ color: var(--citadel-muted);
438
+ }
439
+
440
+ .citadel-result-json,
441
+ .citadel-result-text {
442
+ color: var(--citadel-text);
443
+ }
444
+
445
+ .citadel-result-json {
446
+ margin: 0;
447
+ }
448
+
449
+ .citadel-result-text {
450
+ white-space: pre;
451
+ }
452
+
453
+ .citadel-result-error {
454
+ margin-top: 0.25rem;
455
+ color: var(--citadel-error);
456
+ }
457
+
458
+ .citadel-result-pending {
459
+ color: var(--citadel-muted);
460
+ }
461
+
462
+ .citadel-result-image-wrap {
463
+ margin-block: 0.5rem;
464
+ }
465
+
466
+ .citadel-result-image {
467
+ max-width: 400px;
468
+ max-height: 300px;
469
+ height: auto;
470
+ object-fit: contain;
471
+ border-radius: 0.5rem;
472
+ }