citadel_cli 1.3.0 → 1.4.1

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)
23
36
 
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:
37
+ Commands are the core concept in Citadel. Think `user add 1234` or
38
+ `qa deploy my_feature_branch`.
39
+
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
@@ -165,13 +206,15 @@ const config = {
165
206
  commandTimeoutMs: 10000,
166
207
  includeHelpCommand: true,
167
208
  fontFamily: '"JetBrains Mono", monospace',
168
- fontSize: '0.875rem', // CSS size (e.g. '14px') or Tailwind class (e.g. 'text-sm')
209
+ fontSize: '0.875rem', // CSS font-size value (e.g. '14px', '0.875rem')
169
210
  maxHeight: '80vh',
170
- initialHeight: '40vh',
211
+ initialHeight: '50vh',
171
212
  minHeight: '200',
172
- outputFontSize: 'text-xs', // optional override for output text only
213
+ outputFontSize: '0.75rem', // optional CSS font-size override for output text
173
214
  resetStateOnHide: false,
215
+ closeOnEscape: true,
174
216
  showCitadelKey: '.',
217
+ showOnLoad: false,
175
218
  cursorType: 'blink', // 'blink', 'spin', 'solid', or 'bbs'
176
219
  cursorSpeed: 530,
177
220
  storage: {
@@ -187,6 +230,64 @@ Then to make the component aware of them:
187
230
  <Citadel commandRegistry={cmdRegistry} config={config} />
188
231
  ```
189
232
 
233
+ ## Performance Metrics
234
+
235
+ Citadel includes scripts to capture and compare before/after performance and
236
+ size metrics.
237
+
238
+ ### Metrics collected
239
+
240
+ - Build metrics:
241
+ - Bundle size (raw + gzip) for `dist/citadel.es.js`, `dist/citadel.umd.cjs`,
242
+ and `dist/citadel.css`
243
+ - Total LOC and extension breakdown
244
+ - Dependency presence for `tailwindcss`, `postcss`, and `autoprefixer`
245
+ - `node_modules` size (`du -sk`)
246
+ - Runtime metrics (Chromium):
247
+ - JS heap usage before/after interaction
248
+ - Input latency (keydown to input update)
249
+ - FPS sample over a short window
250
+ - Long task count and duration
251
+ - DOM node count
252
+
253
+ All outputs are written to `test-results/metrics/`.
254
+
255
+ ### Commands
256
+
257
+ ```bash
258
+ npm run metrics:build
259
+ npm run metrics:runtime
260
+ npm run metrics:compare -- --before <before.json> --after <after.json>
261
+ npm run metrics:all -- --label <label>
262
+ npm run metrics:report -- --label <label> --before-build <before-build.json> --before-runtime <before-runtime.json>
263
+ ```
264
+
265
+ ### Before/After workflow
266
+
267
+ 1. Capture a baseline snapshot:
268
+
269
+ ```bash
270
+ npm run metrics:all -- --label before
271
+ ```
272
+
273
+ 2. After your changes, capture the new snapshot and generate comparisons:
274
+
275
+ ```bash
276
+ npm run metrics:all -- --label after \
277
+ --before-build test-results/metrics/build-before-<timestamp>.json \
278
+ --before-runtime test-results/metrics/runtime-before-<timestamp>.json
279
+ ```
280
+
281
+ 3. Open generated reports:
282
+ - `test-results/metrics/run-after.md`
283
+ - `test-results/metrics/compare-build-after.md` (if `--before-build` provided)
284
+ - `test-results/metrics/compare-runtime-after.md` (if `--before-runtime` provided)
285
+
286
+ Notes:
287
+ - `metrics:runtime` starts a local dev server and requires local port binding.
288
+ - If you only want comparison output from existing snapshots, use
289
+ `npm run metrics:report`.
290
+
190
291
  ## Contributing
191
292
 
192
293
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on developing, testing, and releasing Citadel CLI.
@@ -4,9 +4,5 @@
4
4
  "name": "index",
5
5
  "src": "src/index.ts",
6
6
  "isEntry": true
7
- },
8
- "style.css": {
9
- "file": "citadel.css",
10
- "src": "style.css"
11
7
  }
12
8
  }
package/dist/citadel.css CHANGED
@@ -1 +1,472 @@
1
- ._panelContainer_1pav9_3{position:fixed;height:var(--citadel-default-height);min-height:var(--citadel-min-height);max-height:var(--citadel-max-height);background-color:var(--citadel-bg);overflow:hidden;width:100%;box-sizing:border-box;margin:0;padding:0;bottom:0;left:0;right:0}._innerContainer_1pav9_19{height:100%;flex:1;width:100%;display:flex;flex-direction:column;margin:0;padding:0}._inputSection_1pav9_29{border-top:1px solid var(--citadel-border);padding:1rem;margin:0;box-sizing:border-box}._resizeHandle_1pav9_36{width:100%;height:6px;background:transparent;cursor:ns-resize;position:absolute;top:-3px;left:0;right:0;z-index:10;-moz-user-select:none;user-select:none;-webkit-user-select:none;pointer-events:all}._resizeHandle_1pav9_36:hover{background:#ffffff1a}@keyframes _citadel_slideUp_1pav9_65{0%{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes _citadel_slideDown_1pav9_69{0%{transform:translateY(0)}to{transform:translateY(100%)}}._citadel_slideUp_1pav9_65{animation:_citadel_slideUp_1pav9_65 .2s ease-out forwards}._citadel_slideDown_1pav9_69{animation:_citadel_slideDown_1pav9_69 .2s ease-out forwards}._inlineContainer_1pav9_73{position:relative;width:100%;height:100%;display:flex;flex-direction:column;background-color:var(--citadel-bg);overflow:hidden;box-sizing:border-box}
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
+ }