create-rezi 0.1.0-alpha.18 → 0.1.0-alpha.19

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
@@ -23,10 +23,13 @@ Currently available templates:
23
23
  - Alias: `dash`
24
24
  - `stress-test` - Visual benchmark matrix starter
25
25
  - Aliases: `stress`, `chaos`, `bench`
26
+ - `cli-tool` - Multi-screen CLI workflow starter with page router
27
+ - Aliases: `cli`, `tool`, `multiscreen`
26
28
 
27
29
  ```bash
28
30
  npm create rezi my-app -- --template dashboard
29
31
  npm create rezi my-app -- --template stress-test
32
+ npm create rezi my-app -- --template cli-tool
30
33
  ```
31
34
 
32
35
  List templates and highlights from the CLI:
@@ -37,7 +40,7 @@ npm create rezi -- --list-templates
37
40
 
38
41
  ## Options
39
42
 
40
- - `--template, -t <name>`: Select a template (`dashboard` or `stress-test`, plus aliases).
43
+ - `--template, -t <name>`: Select a template (`dashboard`, `stress-test`, or `cli-tool`, plus aliases).
41
44
  - `--no-install, --skip-install`: Skip dependency installation.
42
45
  - `--pm, --package-manager <npm|pnpm|yarn|bun>`: Choose a package manager.
43
46
  - `--list-templates, --templates`: Print available templates and highlights.
package/dist/index.js CHANGED
@@ -66,7 +66,7 @@ function printHelp() {
66
66
  stdout.write(" npm create rezi my-app\n");
67
67
  stdout.write(" bun create rezi my-app\n\n");
68
68
  stdout.write("Options:\n");
69
- stdout.write(" --template, -t <name> dashboard | stress-test\n");
69
+ stdout.write(" --template, -t <name> dashboard | stress-test | cli-tool\n");
70
70
  stdout.write(" --no-install Skip dependency install\n");
71
71
  stdout.write(" --pm <npm|pnpm|yarn|bun> Choose a package manager\n");
72
72
  stdout.write(" --list-templates Show templates and highlights\n");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAavB,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,OAAO,GAAe;QAC1B,OAAO,EAAE,IAAI;QACb,aAAa,EAAE,KAAK;QACpB,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACrC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,kBAAkB,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;YACxD,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,cAAc,IAAI,GAAG,KAAK,gBAAgB,EAAE,CAAC;YACvD,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;YACxB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAC5D,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC;YACzB,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YACnD,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,mBAAmB,EAAE,CAAC;YAClD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACtD,OAAO,CAAC,cAAc,GAAG,KAAuB,CAAC;YACjD,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAmB,CAAC;YACrE,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,GAAG,GAAG,CAAC;YACxB,SAAS;QACX,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAChC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACzB,MAAM,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,MAAM,CAAC,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACnF,MAAM,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACxE,MAAM,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;IACzE,MAAM,CAAC,KAAK,CAAC,+DAA+D,CAAC,CAAC;IAC9E,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;AACjE,CAAC;AAED,SAAS,cAAc;IACrB,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC3D,oBAAoB,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,aAAa,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,KAAK,GAAG,aAAa,IAAI,CAAC,CAAC;QAC/F,MAAM,CAAC,KAAK,CAAC,SAAS,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,qBAAqB,UAAU,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,oBAAoB;IAC3B,0FAA0F;IAC1F,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE,CAAC;IACtD,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAc;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,oBAAoB,EAAE,CAAC;IAC1C,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC9C,IACE,UAAU,KAAK,KAAK;QACpB,UAAU,KAAK,MAAM;QACrB,UAAU,KAAK,MAAM;QACrB,UAAU,KAAK,KAAK,EACpB,CAAC;QACD,OAAO,UAA4B,CAAC;IACtC,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,gCAAgC,KAAK,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,EAAsC,EACtC,MAAc,EACd,QAAgB;IAEhB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,MAAM,KAAK,QAAQ,KAAK,CAAC,CAAC;IAC9D,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,EAAsC;IAClE,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACxD,oBAAoB,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QAC/C,MAAM,aAAa,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,KAAK,GAAG,aAAa,IAAI,CAAC,CAAC;QAC/F,MAAM,CAAC,KAAK,CAAC,SAAS,QAAQ,CAAC,WAAW,IAAI,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,qBAAqB,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,eAAe,oBAAoB,CAAC,MAAM,gBAAgB,CAAC,CAAC;IAC7F,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,oBAAoB,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,WAAW,CAAC;IACrD,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,IAAI,QAAQ,IAAI,oBAAoB,CAAC,MAAM,EAAE,CAAC;QACxF,OAAO,oBAAoB,CAAC,QAAQ,GAAG,CAAC,CAAC,EAAE,GAAG,IAAI,oBAAoB,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,WAAW,CAAC;IAChG,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,UAAU,CAAC,EAAkB,EAAE,SAAiB;IACvD,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE;QACrC,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,SAAS;KACjB,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;IAC1C,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CACrB,SAAiB,EACjB,cAA8B,EAC9B,UAAmB;IAEnB,MAAM,cAAc,GAAG,GAAG,cAAc,UAAU,CAAC;IACnD,MAAM,YAAY,GAChB,cAAc,KAAK,KAAK,IAAI,cAAc,KAAK,KAAK;QAClD,CAAC,CAAC,GAAG,cAAc,YAAY;QAC/B,CAAC,CAAC,GAAG,cAAc,QAAQ,CAAC;IAEhC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAChC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,KAAK,cAAc,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,cAAc,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,MAAM,UAAU,CAAC,EAAE,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC;QAC1F,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,MAAM,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,WAAW,GAAG,qBAAqB,CAAC,aAAa,CAAC,CAAC;QACzD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChF,MAAM,CAAC,KAAK,CAAC,uBAAuB,aAAa,IAAI,CAAC,CAAC;YACvD,MAAM,CAAC,KAAK,CAAC,eAAe,OAAO,IAAI,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;YAC/D,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,KAAK,CAAC,yBAAyB,WAAW,IAAI,CAAC,CAAC;QACzD,CAAC;QAED,MAAM,cAAc,GAAG,qBAAqB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAErE,MAAM,CAAC,KAAK,CAAC,0BAA0B,SAAS,OAAO,CAAC,CAAC;QAEzD,MAAM,aAAa,CAAC;YAClB,SAAS;YACT,WAAW;YACX,WAAW;YACX,WAAW;SACZ,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,kCAAkC,cAAc,OAAO,CAAC,CAAC;YACtE,UAAU,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;QACxC,CAAC;QAED,cAAc,CAAC,SAAS,EAAE,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7D,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9F,IAAI,MAAM,EAAE,CAAC;IACX,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACnB,MAAM,CAAC,KAAK,CAAC,wBAAwB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3F,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,4 @@
1
- export type TemplateKey = "dashboard" | "stress-test";
1
+ export type TemplateKey = "dashboard" | "stress-test" | "cli-tool";
2
2
  export type TemplateDefinition = {
3
3
  key: TemplateKey;
4
4
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,aAAa,CAAC;AAEtD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS,kBAAkB,EAsBpD,CAAC;AAcX,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAQvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWvD;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/D;AA+BD,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAyB/F"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;AAEnE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAAS,kBAAkB,EAgCpD,CAAC;AAiBX,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAQvE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWvD;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/D;AA+BD,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAyB/F"}
package/dist/scaffold.js CHANGED
@@ -22,6 +22,16 @@ export const TEMPLATE_DEFINITIONS = [
22
22
  ],
23
23
  dir: "stress-test",
24
24
  },
25
+ {
26
+ key: "cli-tool",
27
+ label: "Multi-Screen CLI Tool",
28
+ description: "Task-oriented multi-screen TUI with first-party page routing",
29
+ highlights: [
30
+ "home/logs/settings/detail screens with router history and focus restoration",
31
+ "global route keybindings plus breadcrumb + tabs helpers wired to router state",
32
+ ],
33
+ dir: "cli-tool",
34
+ },
25
35
  ];
26
36
  const TEMPLATE_BY_KEY = new Map(TEMPLATE_DEFINITIONS.map((template) => [template.key, template]));
27
37
  const TEMPLATE_ALIASES = new Map(TEMPLATE_DEFINITIONS.map((template) => [template.key, template.key]));
@@ -29,6 +39,9 @@ TEMPLATE_ALIASES.set("dash", "dashboard");
29
39
  TEMPLATE_ALIASES.set("stress", "stress-test");
30
40
  TEMPLATE_ALIASES.set("chaos", "stress-test");
31
41
  TEMPLATE_ALIASES.set("bench", "stress-test");
42
+ TEMPLATE_ALIASES.set("cli", "cli-tool");
43
+ TEMPLATE_ALIASES.set("tool", "cli-tool");
44
+ TEMPLATE_ALIASES.set("multiscreen", "cli-tool");
32
45
  const PACKAGE_NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
33
46
  export function normalizeTemplateName(value) {
34
47
  const cleaned = value.trim().toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-rezi",
3
- "version": "0.1.0-alpha.18",
3
+ "version": "0.1.0-alpha.19",
4
4
  "description": "Scaffold a Rezi terminal UI app.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://rtlzeromemory.github.io/Rezi/",
@@ -28,6 +28,6 @@
28
28
  "bun": ">=1.3.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@rezi-ui/testkit": "0.1.0-alpha.18"
31
+ "@rezi-ui/testkit": "0.1.0-alpha.19"
32
32
  }
33
33
  }
@@ -0,0 +1,42 @@
1
+ # __APP_NAME__
2
+
3
+ Scaffolded with `create-rezi` using the **__TEMPLATE_LABEL__** template.
4
+
5
+ ## What This Template Demonstrates
6
+
7
+ - A first-party multi-screen app powered by `createApp({ routes, initialRoute })`.
8
+ - Screen navigation via router API (`navigate`, `replace`, `back`) and global keybindings.
9
+ - Route-aware helpers (`ui.routerTabs`, `ui.routerBreadcrumb`) wired to live router state.
10
+ - Focus restoration when returning to a previous screen with `back()`.
11
+ - A parameterized detail route (`detail` with `id` param) for drill-down flows.
12
+
13
+ ## Screens
14
+
15
+ - `home`: Status summary and quick actions.
16
+ - `logs`: Streaming logs console + entry list.
17
+ - `settings`: Form controls (`input`, `checkbox`, `select`, `slider`).
18
+ - `detail`: Parameterized log detail screen (opened from Logs entries).
19
+
20
+ ## Controls
21
+
22
+ - `ctrl+1`: Home
23
+ - `ctrl+2`: Logs
24
+ - `ctrl+3`: Settings
25
+ - `q` or `ctrl+c`: Quit
26
+
27
+ From the Logs screen:
28
+
29
+ - Activate a log entry to open the `detail` route.
30
+ - Use `back()` from Detail to restore the previous Logs focus state.
31
+
32
+ ## Quickstart
33
+
34
+ ```bash
35
+ # npm
36
+ npm install
37
+ npm run start
38
+
39
+ # bun
40
+ bun install
41
+ bun run start
42
+ ```
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "__PACKAGE_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "start": "tsx src/main.ts",
8
+ "dev": "tsx watch src/main.ts",
9
+ "build": "tsc --pretty false",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@rezi-ui/core": "^0.1.0-alpha.17",
14
+ "@rezi-ui/node": "^0.1.0-alpha.17"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.13.1",
18
+ "tsx": "^4.20.0",
19
+ "typescript": "^5.6.3"
20
+ },
21
+ "engines": {
22
+ "node": ">=18"
23
+ }
24
+ }
@@ -0,0 +1,1037 @@
1
+ import { exit } from "node:process";
2
+ import type {
3
+ BadgeVariant,
4
+ LogEntry,
5
+ RouteDefinition,
6
+ RouteRenderContext,
7
+ RouterApi,
8
+ TextStyle,
9
+ ThemeDefinition,
10
+ VNode,
11
+ } from "@rezi-ui/core";
12
+ import { createApp, darkTheme, lightTheme, nordTheme, ui } from "@rezi-ui/core";
13
+ import { createNodeBackend } from "@rezi-ui/node";
14
+
15
+ type ThemeName = "nord" | "dark" | "light";
16
+ type EnvironmentName = "development" | "staging" | "production";
17
+ type TopLevelRouteId = "home" | "logs" | "settings";
18
+
19
+ type AppState = {
20
+ nowMs: number;
21
+ tick: number;
22
+ logs: readonly LogEntry[];
23
+ logsScrollTop: number;
24
+ expandedLogIds: readonly string[];
25
+ autoRefresh: boolean;
26
+ includeDebug: boolean;
27
+ operatorName: string;
28
+ environment: EnvironmentName;
29
+ themeName: ThemeName;
30
+ commandTimeoutSec: number;
31
+ viewportCols: number;
32
+ viewportRows: number;
33
+ };
34
+
35
+ const PRODUCT_NAME = "__APP_NAME__";
36
+ const PRODUCT_TAGLINE = "Task-oriented multi-screen workflow with first-party routing";
37
+ const UI_FPS_CAP = 30;
38
+ const PANEL_PADDING_X = 1;
39
+ const PANEL_PADDING_Y = 0;
40
+ const LOG_SEED_COUNT = 24;
41
+ const LOG_HISTORY_LIMIT = 200;
42
+ const LOG_TICK_MS = 900;
43
+ const STACKED_LAYOUT_COLS = 118;
44
+ const COMPACT_LAYOUT_COLS = 96;
45
+ const COMPACT_LAYOUT_ROWS = 28;
46
+ const HISTORY_MAX_ITEMS_COMPACT = 5;
47
+ const HISTORY_MAX_ITEMS_DEFAULT = 9;
48
+
49
+ const THEME_BY_NAME: Record<ThemeName, ThemeDefinition> = {
50
+ nord: nordTheme,
51
+ dark: darkTheme,
52
+ light: lightTheme,
53
+ };
54
+
55
+ const LOG_MESSAGES: readonly string[] = Object.freeze([
56
+ "Indexed 14 workspace files",
57
+ "Loaded plugin metadata",
58
+ "Synced runtime configuration",
59
+ "Queued incremental diagnostics",
60
+ "Completed background health check",
61
+ "Applied command palette cache",
62
+ ]);
63
+
64
+ const LOG_SOURCES: readonly string[] = Object.freeze([
65
+ "core",
66
+ "scheduler",
67
+ "file-watcher",
68
+ "runtime",
69
+ ]);
70
+
71
+ const LOG_LEVELS: readonly LogEntry["level"][] = Object.freeze(["info", "warn", "error", "debug"]);
72
+
73
+ const ENV_OPTIONS: readonly Readonly<{ value: EnvironmentName; label: string }>[] = Object.freeze([
74
+ { value: "development", label: "Development" },
75
+ { value: "staging", label: "Staging" },
76
+ { value: "production", label: "Production" },
77
+ ]);
78
+
79
+ const THEME_OPTIONS: readonly Readonly<{ value: ThemeName; label: string }>[] = Object.freeze([
80
+ { value: "nord", label: "Nord" },
81
+ { value: "dark", label: "Dark" },
82
+ { value: "light", label: "Light" },
83
+ ]);
84
+
85
+ type ViewStyles = Readonly<{
86
+ rootStyle: TextStyle;
87
+ panelStyle: TextStyle;
88
+ stripStyle: TextStyle;
89
+ sectionLabelStyle: TextStyle;
90
+ metaStyle: TextStyle;
91
+ quietStyle: TextStyle;
92
+ }>;
93
+
94
+ function themeLabel(themeName: ThemeName): string {
95
+ return THEME_OPTIONS.find((option) => option.value === themeName)?.label ?? themeName;
96
+ }
97
+
98
+ function themeBadgeVariant(themeName: ThemeName): BadgeVariant {
99
+ if (themeName === "light") return "success";
100
+ if (themeName === "dark") return "default";
101
+ return "info";
102
+ }
103
+
104
+ function environmentBadgeVariant(environment: EnvironmentName): BadgeVariant {
105
+ if (environment === "production") return "warning";
106
+ if (environment === "staging") return "info";
107
+ return "success";
108
+ }
109
+
110
+ function levelBadgeVariant(level: LogEntry["level"]): BadgeVariant {
111
+ if (level === "error") return "error";
112
+ if (level === "warn") return "warning";
113
+ if (level === "debug") return "info";
114
+ return "default";
115
+ }
116
+
117
+ function getViewStyles(themeName: ThemeName): ViewStyles {
118
+ const colors = THEME_BY_NAME[themeName].colors;
119
+ return Object.freeze({
120
+ rootStyle: { bg: colors.bg.base, fg: colors.fg.primary },
121
+ panelStyle: { bg: colors.bg.elevated, fg: colors.fg.primary },
122
+ stripStyle: { bg: colors.bg.subtle, fg: colors.fg.primary },
123
+ sectionLabelStyle: { fg: colors.fg.secondary, bold: true },
124
+ metaStyle: { fg: colors.fg.secondary, dim: true },
125
+ quietStyle: { fg: colors.fg.muted, dim: true },
126
+ });
127
+ }
128
+
129
+ function panel(
130
+ title: string,
131
+ children: readonly VNode[],
132
+ style: TextStyle,
133
+ opts: Readonly<{ flex?: number; minWidth?: number }> = Object.freeze({}),
134
+ ): VNode {
135
+ return ui.box(
136
+ {
137
+ title,
138
+ border: "rounded",
139
+ px: PANEL_PADDING_X,
140
+ py: PANEL_PADDING_Y,
141
+ style,
142
+ ...(opts.flex === undefined ? {} : { flex: opts.flex }),
143
+ ...(opts.minWidth === undefined ? {} : { minWidth: opts.minWidth }),
144
+ },
145
+ children,
146
+ );
147
+ }
148
+
149
+ function applyTheme(context: RouteRenderContext<AppState>, themeName: ThemeName): void {
150
+ context.update((prev) => Object.freeze({ ...prev, themeName }));
151
+ app.setTheme(THEME_BY_NAME[themeName]);
152
+ }
153
+
154
+ function formatTime(timestampMs: number): string {
155
+ return new Date(timestampMs).toLocaleTimeString("en-US", { hour12: false });
156
+ }
157
+
158
+ function isThemeName(value: string): value is ThemeName {
159
+ return value === "nord" || value === "dark" || value === "light";
160
+ }
161
+
162
+ function isEnvironment(value: string): value is EnvironmentName {
163
+ return value === "development" || value === "staging" || value === "production";
164
+ }
165
+
166
+ function routeLabelFromId(routeId: string): string {
167
+ if (routeId === "home") return "Home";
168
+ if (routeId === "logs") return "Logs";
169
+ if (routeId === "settings") return "Settings";
170
+ if (routeId === "detail") return "Detail";
171
+ return routeId[0] ? routeId[0].toUpperCase() + routeId.slice(1) : routeId;
172
+ }
173
+
174
+ function trimMiddle(value: string, maxChars: number): string {
175
+ if (value.length <= maxChars) return value;
176
+ if (maxChars <= 3) return value.slice(0, maxChars);
177
+ const sideWidth = Math.max(1, Math.floor((maxChars - 1) / 2));
178
+ return `${value.slice(0, sideWidth)}…${value.slice(value.length - sideWidth)}`;
179
+ }
180
+
181
+ function formatHistoryTrail(
182
+ router: RouterApi,
183
+ viewportCols: number,
184
+ compact: boolean,
185
+ ): Readonly<{ summary: string; count: number }> {
186
+ const entries = router.history();
187
+ const labels: string[] = [];
188
+ for (const entry of entries) {
189
+ const label = routeLabelFromId(entry.id);
190
+ if (labels[labels.length - 1] === label) continue;
191
+ labels.push(label);
192
+ }
193
+ const maxItems = compact ? HISTORY_MAX_ITEMS_COMPACT : HISTORY_MAX_ITEMS_DEFAULT;
194
+ const tail = labels.slice(-maxItems);
195
+ const prefix = labels.length > tail.length ? ["…"] : [];
196
+ const text = [...prefix, ...tail].join(" > ");
197
+ const maxChars = Math.max(24, viewportCols - 20);
198
+ return Object.freeze({
199
+ summary: trimMiddle(text, maxChars),
200
+ count: entries.length,
201
+ });
202
+ }
203
+
204
+ function navigateTopLevel(router: RouterApi, routeId: TopLevelRouteId): void {
205
+ const current = router.currentRoute();
206
+ if (current.id === routeId) return;
207
+ if (current.id === "detail") {
208
+ router.navigate(routeId);
209
+ return;
210
+ }
211
+ router.replace(routeId);
212
+ }
213
+
214
+ function isStackedLayout(state: Readonly<AppState>): boolean {
215
+ return state.viewportCols < STACKED_LAYOUT_COLS;
216
+ }
217
+
218
+ function isCompactLayout(state: Readonly<AppState>): boolean {
219
+ return state.viewportCols < COMPACT_LAYOUT_COLS || state.viewportRows < COMPACT_LAYOUT_ROWS;
220
+ }
221
+
222
+ function buildLogEntry(
223
+ tick: number,
224
+ environment: EnvironmentName,
225
+ includeDebug: boolean,
226
+ timestamp = Date.now(),
227
+ ): LogEntry {
228
+ const source = LOG_SOURCES[tick % LOG_SOURCES.length] ?? "core";
229
+ const message = LOG_MESSAGES[tick % LOG_MESSAGES.length] ?? "Updated background task";
230
+ const baseLevel = LOG_LEVELS[tick % LOG_LEVELS.length] ?? "info";
231
+ const level = includeDebug || baseLevel !== "debug" ? baseLevel : "info";
232
+
233
+ return Object.freeze({
234
+ id: `log-${String(tick)}`,
235
+ timestamp,
236
+ level,
237
+ source,
238
+ message: `${message} (${environment})`,
239
+ details:
240
+ `tick=${String(tick)}\n` +
241
+ `environment=${environment}\n` +
242
+ `source=${source}\n` +
243
+ `level=${level}`,
244
+ });
245
+ }
246
+
247
+ function initialLogs(
248
+ seedCount = LOG_SEED_COUNT,
249
+ environment: EnvironmentName = "staging",
250
+ ): readonly LogEntry[] {
251
+ const nowMs = Date.now();
252
+ const seed: LogEntry[] = [];
253
+ for (let i = 0; i < seedCount; i++) {
254
+ const tick = i + 1;
255
+ const timestamp = nowMs - (seedCount - tick) * 1200;
256
+ seed.push(buildLogEntry(tick, environment, true, timestamp));
257
+ }
258
+ return Object.freeze(seed);
259
+ }
260
+
261
+ function appendTickLog(prev: Readonly<AppState>): AppState {
262
+ const nextTick = prev.tick + 1;
263
+ const nextEntry = buildLogEntry(nextTick, prev.environment, prev.includeDebug);
264
+ const nextLogs = Object.freeze([...prev.logs, nextEntry].slice(-LOG_HISTORY_LIMIT));
265
+ const nextExpanded = Object.freeze(
266
+ prev.expandedLogIds.filter((entryId) => nextLogs.some((entry) => entry.id === entryId)),
267
+ );
268
+
269
+ return Object.freeze({
270
+ ...prev,
271
+ nowMs: Date.now(),
272
+ tick: nextTick,
273
+ logs: nextLogs,
274
+ expandedLogIds: nextExpanded,
275
+ });
276
+ }
277
+
278
+ function updateExpanded(
279
+ previous: readonly string[],
280
+ entryId: string,
281
+ expanded: boolean,
282
+ ): readonly string[] {
283
+ if (expanded) {
284
+ if (previous.includes(entryId)) return previous;
285
+ return Object.freeze([...previous, entryId]);
286
+ }
287
+ return Object.freeze(previous.filter((id) => id !== entryId));
288
+ }
289
+
290
+ const initialState: AppState = Object.freeze({
291
+ nowMs: Date.now(),
292
+ tick: LOG_SEED_COUNT,
293
+ logs: initialLogs(LOG_SEED_COUNT),
294
+ logsScrollTop: 0,
295
+ expandedLogIds: Object.freeze([]),
296
+ autoRefresh: true,
297
+ includeDebug: true,
298
+ operatorName: "operator",
299
+ environment: "staging",
300
+ themeName: "nord",
301
+ commandTimeoutSec: 30,
302
+ viewportCols: 120,
303
+ viewportRows: 36,
304
+ });
305
+
306
+ // biome-ignore lint/style/useConst: app is assigned after route declarations for route-screen closures.
307
+ let app!: ReturnType<typeof createApp<AppState>>;
308
+ let isStopping = false;
309
+
310
+ function renderShell(
311
+ title: string,
312
+ context: RouteRenderContext<AppState>,
313
+ body: VNode,
314
+ bodyTitle = title,
315
+ ): VNode {
316
+ const state = context.state;
317
+ const styles = getViewStyles(state.themeName);
318
+ const compact = isCompactLayout(state);
319
+ const stacked = isStackedLayout(state);
320
+ const history = formatHistoryTrail(context.router, state.viewportCols, compact);
321
+ const currentRouteId = context.router.currentRoute().id;
322
+
323
+ return ui.column({ p: 1, gap: 1, items: "stretch", style: styles.rootStyle }, [
324
+ ui.box(
325
+ { border: "rounded", px: PANEL_PADDING_X, py: PANEL_PADDING_Y, style: styles.stripStyle },
326
+ [
327
+ ui.column({ gap: 1 }, [
328
+ ui.row({ items: "center", gap: 1, wrap: true }, [
329
+ ui.text(`${PRODUCT_NAME} · ${title}`, { variant: "heading" }),
330
+ ui.badge(`Env ${state.environment.toUpperCase()}`, {
331
+ variant: environmentBadgeVariant(state.environment),
332
+ }),
333
+ ui.tag(`Theme ${themeLabel(state.themeName)}`, {
334
+ variant: themeBadgeVariant(state.themeName),
335
+ }),
336
+ ui.status(state.autoRefresh ? "online" : "away", {
337
+ label: state.autoRefresh ? "Streaming" : "Paused",
338
+ }),
339
+ ui.badge(`${String(state.viewportCols)}×${String(state.viewportRows)}`, {
340
+ variant: "default",
341
+ }),
342
+ ui.badge(`tick:${String(state.tick)}`, { variant: "default" }),
343
+ ]),
344
+ ...(compact
345
+ ? []
346
+ : [
347
+ ui.text(PRODUCT_TAGLINE, { style: styles.sectionLabelStyle }),
348
+ ui.text(`Operator ${state.operatorName} · ${formatTime(state.nowMs)}`, {
349
+ style: styles.metaStyle,
350
+ }),
351
+ ]),
352
+ ]),
353
+ ],
354
+ ),
355
+ ui.box(
356
+ { border: "rounded", px: PANEL_PADDING_X, py: PANEL_PADDING_Y, style: styles.stripStyle },
357
+ [
358
+ ui.column({ gap: 1 }, [
359
+ ui.routerTabs(context.router, topLevelRoutes, {
360
+ id: "app-route-tabs",
361
+ variant: "pills",
362
+ }),
363
+ ...(compact
364
+ ? []
365
+ : [
366
+ ui.routerBreadcrumb(context.router, routes, {
367
+ id: "app-route-breadcrumb",
368
+ separator: " > ",
369
+ }),
370
+ ]),
371
+ ui.text(`History (${String(history.count)}): ${history.summary}`, {
372
+ style: styles.metaStyle,
373
+ }),
374
+ ui.row({ gap: 1, wrap: true }, [
375
+ ui.kbd("f1"),
376
+ ui.text("Home", { style: styles.quietStyle }),
377
+ ui.kbd("f2"),
378
+ ui.text("Logs", { style: styles.quietStyle }),
379
+ ui.kbd("f3"),
380
+ ui.text("Settings", { style: styles.quietStyle }),
381
+ ]),
382
+ ui.row({ gap: 1, wrap: true }, [
383
+ ui.kbd("alt+1"),
384
+ ui.text("Home", { style: styles.quietStyle }),
385
+ ui.kbd("alt+2"),
386
+ ui.text("Logs", { style: styles.quietStyle }),
387
+ ui.kbd("alt+3"),
388
+ ui.text("Settings", { style: styles.quietStyle }),
389
+ ]),
390
+ ...(currentRouteId === "detail"
391
+ ? [
392
+ ui.row({ gap: 1, wrap: true }, [
393
+ ui.kbd("esc"),
394
+ ui.text("Back from detail", { style: styles.quietStyle }),
395
+ ]),
396
+ ]
397
+ : []),
398
+ ...(stacked
399
+ ? [
400
+ ui.text("Adaptive layout: stacked panels enabled", {
401
+ style: styles.quietStyle,
402
+ }),
403
+ ]
404
+ : []),
405
+ ]),
406
+ ],
407
+ ),
408
+ panel(bodyTitle, [body], styles.panelStyle, { flex: 1 }),
409
+ ui.text("Shortcuts: F1/F2/F3 or Alt+1/2/3 · Esc from Detail goes back · q quits", {
410
+ style: styles.quietStyle,
411
+ }),
412
+ ]);
413
+ }
414
+
415
+ function renderHome(
416
+ _params: Readonly<Record<string, string>>,
417
+ context: RouteRenderContext<AppState>,
418
+ ): VNode {
419
+ const state = context.state;
420
+ const styles = getViewStyles(state.themeName);
421
+ const stacked = isStackedLayout(state);
422
+ const latest = state.logs[state.logs.length - 1];
423
+ const hasLatest = latest !== undefined;
424
+ const telemetryPanels = [
425
+ panel(
426
+ "Live stream",
427
+ [
428
+ ui.column({ gap: 1 }, [
429
+ ui.row({ gap: 1, wrap: true }, [
430
+ ui.status(state.autoRefresh ? "online" : "away", {
431
+ label: state.autoRefresh ? "Live stream enabled" : "Live stream paused",
432
+ }),
433
+ ui.badge(`logs:${String(state.logs.length)}`, { variant: "info" }),
434
+ ui.badge(`tick:${String(state.tick)}`, { variant: "info" }),
435
+ ui.badge(`history:${String(context.router.history().length)}`, { variant: "success" }),
436
+ ]),
437
+ ui.text(
438
+ latest
439
+ ? `Latest: [${latest.level.toUpperCase()}] ${latest.message} @ ${formatTime(latest.timestamp)}`
440
+ : "Latest: no log entries yet",
441
+ ),
442
+ ]),
443
+ ],
444
+ styles.stripStyle,
445
+ { flex: 2, minWidth: 44 },
446
+ ),
447
+ panel(
448
+ "Quick actions",
449
+ [
450
+ ui.column({ gap: 1 }, [
451
+ ui.row({ gap: 1, wrap: true }, [
452
+ ui.button({
453
+ id: "home-open-logs",
454
+ label: "Open Logs",
455
+ onPress: () => navigateTopLevel(context.router, "logs"),
456
+ }),
457
+ ui.button({
458
+ id: "home-open-settings",
459
+ label: "Open Settings",
460
+ onPress: () => navigateTopLevel(context.router, "settings"),
461
+ }),
462
+ ui.button({
463
+ id: "home-open-latest-detail",
464
+ label: "Open Latest Detail",
465
+ disabled: !hasLatest,
466
+ onPress: () => {
467
+ if (!latest) return;
468
+ context.router.navigate("detail", Object.freeze({ id: latest.id }));
469
+ },
470
+ }),
471
+ ]),
472
+ ui.text(
473
+ "Tip: open a detail entry, then press Esc/Back to verify focus/history behavior.",
474
+ { style: styles.quietStyle },
475
+ ),
476
+ ]),
477
+ ],
478
+ styles.stripStyle,
479
+ { flex: 1, minWidth: 32 },
480
+ ),
481
+ ];
482
+
483
+ return renderShell(
484
+ "Home",
485
+ context,
486
+ ui.column({ gap: 1 }, [
487
+ ui.callout(
488
+ "This app uses first-party page routing with keybindings, history, and focus restoration.",
489
+ {
490
+ title: "Router-ready CLI Template",
491
+ variant: "info",
492
+ },
493
+ ),
494
+ ui.row({ gap: 1, wrap: true }, [
495
+ ui.badge(`timeout:${String(state.commandTimeoutSec)}s`, { variant: "info" }),
496
+ ui.badge(`theme:${themeLabel(state.themeName)}`, {
497
+ variant: themeBadgeVariant(state.themeName),
498
+ }),
499
+ ui.tag(`debug:${state.includeDebug ? "on" : "off"}`, {
500
+ variant: state.includeDebug ? "info" : "default",
501
+ }),
502
+ ]),
503
+ stacked
504
+ ? ui.column({ gap: 1 }, telemetryPanels)
505
+ : ui.row({ gap: 1, wrap: true, items: "stretch" }, telemetryPanels),
506
+ ]),
507
+ "Overview",
508
+ );
509
+ }
510
+
511
+ function renderLogs(
512
+ _params: Readonly<Record<string, string>>,
513
+ context: RouteRenderContext<AppState>,
514
+ ): VNode {
515
+ const state = context.state;
516
+ const styles = getViewStyles(state.themeName);
517
+ const stacked = isStackedLayout(state);
518
+ const compact = isCompactLayout(state);
519
+ const recent = [...state.logs].slice(-8).reverse();
520
+
521
+ const logsConsole = ui.logsConsole({
522
+ id: "logs-console",
523
+ entries: state.logs,
524
+ scrollTop: state.logsScrollTop,
525
+ expandedEntries: state.expandedLogIds,
526
+ ...(state.includeDebug
527
+ ? {}
528
+ : { levelFilter: Object.freeze(["info", "warn", "error"] as const) }),
529
+ onScroll: (scrollTop) => {
530
+ context.update((prev) => Object.freeze({ ...prev, logsScrollTop: scrollTop }));
531
+ },
532
+ onEntryToggle: (entryId, expanded) => {
533
+ context.update((prev) =>
534
+ Object.freeze({
535
+ ...prev,
536
+ expandedLogIds: updateExpanded(prev.expandedLogIds, entryId, expanded),
537
+ }),
538
+ );
539
+ },
540
+ onClear: () => {
541
+ context.update((prev) =>
542
+ Object.freeze({
543
+ ...prev,
544
+ logs: Object.freeze([]),
545
+ logsScrollTop: 0,
546
+ expandedLogIds: Object.freeze([]),
547
+ }),
548
+ );
549
+ },
550
+ });
551
+
552
+ const recentPanel = panel(
553
+ "Open recent entry",
554
+ [
555
+ ui.column({ gap: 1 }, [
556
+ ...recent.map((entry) => {
557
+ const when = formatTime(entry.timestamp);
558
+ const sourceLabel =
559
+ entry.source.length > 10 ? `${entry.source.slice(0, 10)}…` : entry.source;
560
+ return ui.row({ gap: 1, items: "center", wrap: true }, [
561
+ ui.badge(entry.level.toUpperCase(), { variant: levelBadgeVariant(entry.level) }),
562
+ ui.button({
563
+ id: `open-${entry.id}`,
564
+ label: `${sourceLabel} ${when}`,
565
+ onPress: () => context.router.navigate("detail", Object.freeze({ id: entry.id })),
566
+ }),
567
+ ]);
568
+ }),
569
+ ...(recent.length === 0
570
+ ? [ui.text("No entries in history.", { style: styles.metaStyle })]
571
+ : []),
572
+ ui.row({ gap: 1, wrap: true }, [
573
+ ui.button({
574
+ id: "logs-open-latest-detail",
575
+ label: "Latest detail",
576
+ disabled: recent.length === 0,
577
+ onPress: () => {
578
+ const latest = recent[0];
579
+ if (!latest) return;
580
+ context.router.navigate("detail", Object.freeze({ id: latest.id }));
581
+ },
582
+ }),
583
+ ui.button({
584
+ id: "logs-open-settings",
585
+ label: "Settings",
586
+ onPress: () => navigateTopLevel(context.router, "settings"),
587
+ }),
588
+ ]),
589
+ ]),
590
+ ],
591
+ styles.stripStyle,
592
+ stacked ? { minWidth: 34 } : { flex: 2, minWidth: 34 },
593
+ );
594
+
595
+ const streamPanel = panel("Log stream", [logsConsole], styles.stripStyle, {
596
+ ...(stacked ? {} : { flex: 3 }),
597
+ minWidth: 52,
598
+ });
599
+
600
+ return renderShell(
601
+ "Logs",
602
+ context,
603
+ ui.column({ gap: 1 }, [
604
+ ui.row({ gap: 1, wrap: true }, [
605
+ ui.tag(`debug:${state.includeDebug ? "on" : "off"}`, {
606
+ variant: state.includeDebug ? "info" : "default",
607
+ }),
608
+ ui.tag(`refresh:${state.autoRefresh ? "on" : "off"}`, {
609
+ variant: state.autoRefresh ? "success" : "warning",
610
+ }),
611
+ ui.badge(`entries:${String(state.logs.length)}`, { variant: "default" }),
612
+ ui.button({
613
+ id: "logs-toggle-refresh",
614
+ label: state.autoRefresh ? "Pause stream" : "Resume stream",
615
+ onPress: () => {
616
+ context.update((prev) => Object.freeze({ ...prev, autoRefresh: !prev.autoRefresh }));
617
+ },
618
+ }),
619
+ ui.button({
620
+ id: "logs-clear-inline",
621
+ label: "Clear logs",
622
+ onPress: () => {
623
+ context.update((prev) =>
624
+ Object.freeze({
625
+ ...prev,
626
+ logs: Object.freeze([]),
627
+ logsScrollTop: 0,
628
+ expandedLogIds: Object.freeze([]),
629
+ }),
630
+ );
631
+ },
632
+ }),
633
+ ]),
634
+ stacked
635
+ ? ui.column({ gap: 1 }, [streamPanel, recentPanel])
636
+ : ui.row({ gap: compact ? 1 : 2, wrap: true, items: "stretch" }, [
637
+ streamPanel,
638
+ recentPanel,
639
+ ]),
640
+ ]),
641
+ compact ? "Logs (compact)" : "Live Logs",
642
+ );
643
+ }
644
+
645
+ function renderSettings(
646
+ _params: Readonly<Record<string, string>>,
647
+ context: RouteRenderContext<AppState>,
648
+ ): VNode {
649
+ const state = context.state;
650
+ const styles = getViewStyles(state.themeName);
651
+ const stacked = isStackedLayout(state);
652
+ const compact = isCompactLayout(state);
653
+
654
+ const profilePanel = panel(
655
+ "Profile",
656
+ [
657
+ ui.column({ gap: 1 }, [
658
+ ui.text("Operator", { style: styles.sectionLabelStyle }),
659
+ ui.input({
660
+ id: "settings-operator",
661
+ value: state.operatorName,
662
+ onInput: (value) => {
663
+ context.update((prev) => Object.freeze({ ...prev, operatorName: value }));
664
+ },
665
+ }),
666
+ ui.text("Environment", { style: styles.sectionLabelStyle }),
667
+ ui.select({
668
+ id: "settings-environment",
669
+ value: state.environment,
670
+ options: ENV_OPTIONS,
671
+ onChange: (value) => {
672
+ if (!isEnvironment(value)) return;
673
+ context.update((prev) => Object.freeze({ ...prev, environment: value }));
674
+ },
675
+ }),
676
+ ui.text("Theme", { style: styles.sectionLabelStyle }),
677
+ ui.select({
678
+ id: "settings-theme",
679
+ value: state.themeName,
680
+ options: THEME_OPTIONS,
681
+ onChange: (value) => {
682
+ if (!isThemeName(value)) return;
683
+ applyTheme(context, value);
684
+ },
685
+ }),
686
+ ]),
687
+ ],
688
+ styles.stripStyle,
689
+ stacked ? { minWidth: 34 } : { flex: 1, minWidth: 34 },
690
+ );
691
+
692
+ const runtimePanel = panel(
693
+ "Runtime",
694
+ [
695
+ ui.column({ gap: 1 }, [
696
+ ui.checkbox({
697
+ id: "settings-auto-refresh",
698
+ checked: state.autoRefresh,
699
+ label: "Auto-refresh log stream",
700
+ onChange: (checked) => {
701
+ context.update((prev) => Object.freeze({ ...prev, autoRefresh: checked }));
702
+ },
703
+ }),
704
+ ui.checkbox({
705
+ id: "settings-include-debug",
706
+ checked: state.includeDebug,
707
+ label: "Include debug level entries",
708
+ onChange: (checked) => {
709
+ context.update((prev) => Object.freeze({ ...prev, includeDebug: checked }));
710
+ },
711
+ }),
712
+ ui.text(`Command timeout: ${String(state.commandTimeoutSec)}s`, {
713
+ style: styles.sectionLabelStyle,
714
+ }),
715
+ ui.slider({
716
+ id: "settings-timeout",
717
+ value: state.commandTimeoutSec,
718
+ min: 5,
719
+ max: 120,
720
+ step: 5,
721
+ onChange: (value) => {
722
+ context.update((prev) => Object.freeze({ ...prev, commandTimeoutSec: value }));
723
+ },
724
+ }),
725
+ ui.text(
726
+ `Current preset: ${themeLabel(state.themeName)} · ${state.environment.toUpperCase()}`,
727
+ { style: styles.metaStyle },
728
+ ),
729
+ ]),
730
+ ],
731
+ styles.stripStyle,
732
+ stacked ? { minWidth: 34 } : { flex: 1, minWidth: 34 },
733
+ );
734
+
735
+ return renderShell(
736
+ "Settings",
737
+ context,
738
+ ui.column({ gap: 1 }, [
739
+ ui.callout("Adjust runtime behavior, filtering, and presentation preferences.", {
740
+ title: "Preferences",
741
+ variant: "info",
742
+ }),
743
+ panel(
744
+ "Theme presets",
745
+ [
746
+ ui.row(
747
+ { gap: 1, wrap: true },
748
+ THEME_OPTIONS.map((option) =>
749
+ ui.button({
750
+ id: `settings-theme-preset-${option.value}`,
751
+ label: option.label,
752
+ disabled: option.value === state.themeName,
753
+ onPress: () => applyTheme(context, option.value),
754
+ }),
755
+ ),
756
+ ),
757
+ ],
758
+ styles.stripStyle,
759
+ { minWidth: 34 },
760
+ ),
761
+ stacked
762
+ ? ui.column({ gap: 1 }, [profilePanel, runtimePanel])
763
+ : ui.row({ gap: compact ? 1 : 2, wrap: true, items: "stretch" }, [
764
+ profilePanel,
765
+ runtimePanel,
766
+ ]),
767
+ panel(
768
+ "Current session",
769
+ [
770
+ ui.row({ gap: 1, wrap: true }, [
771
+ ui.badge(`env:${state.environment}`, {
772
+ variant: environmentBadgeVariant(state.environment),
773
+ }),
774
+ ui.badge(`theme:${themeLabel(state.themeName)}`, {
775
+ variant: themeBadgeVariant(state.themeName),
776
+ }),
777
+ ui.badge(`timeout:${String(state.commandTimeoutSec)}s`, { variant: "info" }),
778
+ ui.tag(`debug:${state.includeDebug ? "on" : "off"}`, {
779
+ variant: state.includeDebug ? "info" : "default",
780
+ }),
781
+ ]),
782
+ ],
783
+ styles.stripStyle,
784
+ ),
785
+ ui.row({ gap: 1, wrap: true }, [
786
+ ui.button({
787
+ id: "settings-open-logs",
788
+ label: "Logs",
789
+ onPress: () => navigateTopLevel(context.router, "logs"),
790
+ }),
791
+ ui.button({
792
+ id: "settings-open-home",
793
+ label: "Home",
794
+ onPress: () => navigateTopLevel(context.router, "home"),
795
+ }),
796
+ ]),
797
+ ]),
798
+ compact ? "Settings (compact)" : "Configuration",
799
+ );
800
+ }
801
+
802
+ function renderDetail(
803
+ params: Readonly<Record<string, string>>,
804
+ context: RouteRenderContext<AppState>,
805
+ ): VNode {
806
+ const styles = getViewStyles(context.state.themeName);
807
+ const entryId = (params as Readonly<{ id?: string }>).id;
808
+ const logs = context.state.logs;
809
+ const index = entryId ? logs.findIndex((entry) => entry.id === entryId) : -1;
810
+ const entry = index >= 0 ? logs[index] : undefined;
811
+
812
+ if (!entry) {
813
+ return renderShell(
814
+ "Log Detail",
815
+ context,
816
+ ui.box({ border: "single", px: 1, py: 0, style: styles.stripStyle }, [
817
+ ui.column({ gap: 1 }, [
818
+ ui.callout("The selected log entry no longer exists in the bounded history window.", {
819
+ title: "Entry unavailable",
820
+ variant: "warning",
821
+ }),
822
+ ui.button({
823
+ id: "detail-missing-back",
824
+ label: "Back to Logs",
825
+ onPress: () => {
826
+ if (context.router.canGoBack()) context.router.back();
827
+ else navigateTopLevel(context.router, "logs");
828
+ },
829
+ }),
830
+ ]),
831
+ ]),
832
+ "Log Detail",
833
+ );
834
+ }
835
+
836
+ const previous = index > 0 ? logs[index - 1] : undefined;
837
+ const next = index >= 0 && index < logs.length - 1 ? logs[index + 1] : undefined;
838
+
839
+ return renderShell(
840
+ "Log Detail",
841
+ context,
842
+ ui.box({ border: "single", p: 1, style: styles.stripStyle }, [
843
+ ui.column({ gap: 1 }, [
844
+ ui.row({ gap: 1, wrap: true }, [
845
+ ui.badge(entry.level.toUpperCase(), {
846
+ variant: levelBadgeVariant(entry.level),
847
+ }),
848
+ ui.tag(`source:${entry.source}`, { variant: "default" }),
849
+ ui.tag(`time:${formatTime(entry.timestamp)}`, { variant: "default" }),
850
+ ui.badge(`index ${String(index + 1)} / ${String(logs.length)}`, { variant: "default" }),
851
+ ]),
852
+ ui.text(entry.message),
853
+ ui.callout(entry.details ?? "No extra details", {
854
+ title: "Details",
855
+ variant: "info",
856
+ }),
857
+ ui.row({ gap: 1, wrap: true }, [
858
+ ui.button({
859
+ id: "detail-prev",
860
+ label: "Previous",
861
+ disabled: previous === undefined,
862
+ onPress: () => {
863
+ if (!previous) return;
864
+ context.router.replace("detail", Object.freeze({ id: previous.id }));
865
+ },
866
+ }),
867
+ ui.button({
868
+ id: "detail-next",
869
+ label: "Next",
870
+ disabled: next === undefined,
871
+ onPress: () => {
872
+ if (!next) return;
873
+ context.router.replace("detail", Object.freeze({ id: next.id }));
874
+ },
875
+ }),
876
+ ui.button({
877
+ id: "detail-back",
878
+ label: "Back",
879
+ onPress: () => {
880
+ if (context.router.canGoBack()) context.router.back();
881
+ else navigateTopLevel(context.router, "logs");
882
+ },
883
+ }),
884
+ ]),
885
+ ]),
886
+ ]),
887
+ "Log Detail",
888
+ );
889
+ }
890
+
891
+ const routes: readonly RouteDefinition<AppState>[] = Object.freeze([
892
+ {
893
+ id: "home",
894
+ title: "Home",
895
+ screen: renderHome,
896
+ },
897
+ {
898
+ id: "logs",
899
+ title: "Logs",
900
+ screen: renderLogs,
901
+ },
902
+ {
903
+ id: "settings",
904
+ title: "Settings",
905
+ screen: renderSettings,
906
+ },
907
+ {
908
+ id: "detail",
909
+ title: "Detail",
910
+ screen: renderDetail,
911
+ },
912
+ ]);
913
+
914
+ const topLevelRoutes: readonly RouteDefinition<AppState>[] = Object.freeze(
915
+ routes.filter((route) => route.id !== "detail"),
916
+ );
917
+
918
+ app = createApp({
919
+ backend: createNodeBackend({
920
+ fpsCap: UI_FPS_CAP,
921
+ }),
922
+ initialState,
923
+ routes,
924
+ initialRoute: "home",
925
+ config: {
926
+ fpsCap: UI_FPS_CAP,
927
+ },
928
+ theme: THEME_BY_NAME[initialState.themeName],
929
+ });
930
+
931
+ async function shutdown(): Promise<void> {
932
+ if (isStopping) return;
933
+ isStopping = true;
934
+ clearInterval(logTimer);
935
+ try {
936
+ await app.stop();
937
+ } catch {
938
+ // Ignore shutdown races on forced exit paths.
939
+ }
940
+ app.dispose();
941
+ exit(0);
942
+ }
943
+
944
+ app.keys({
945
+ q: () => {
946
+ void shutdown();
947
+ },
948
+ "ctrl+c": () => {
949
+ void shutdown();
950
+ },
951
+ f1: () => {
952
+ const router = app.router;
953
+ if (!router) return;
954
+ navigateTopLevel(router, "home");
955
+ },
956
+ f2: () => {
957
+ const router = app.router;
958
+ if (!router) return;
959
+ navigateTopLevel(router, "logs");
960
+ },
961
+ f3: () => {
962
+ const router = app.router;
963
+ if (!router) return;
964
+ navigateTopLevel(router, "settings");
965
+ },
966
+ "alt+1": () => {
967
+ const router = app.router;
968
+ if (!router) return;
969
+ navigateTopLevel(router, "home");
970
+ },
971
+ "alt+2": () => {
972
+ const router = app.router;
973
+ if (!router) return;
974
+ navigateTopLevel(router, "logs");
975
+ },
976
+ "alt+3": () => {
977
+ const router = app.router;
978
+ if (!router) return;
979
+ navigateTopLevel(router, "settings");
980
+ },
981
+ "ctrl+1": () => {
982
+ const router = app.router;
983
+ if (!router) return;
984
+ navigateTopLevel(router, "home");
985
+ },
986
+ "ctrl+2": () => {
987
+ const router = app.router;
988
+ if (!router) return;
989
+ navigateTopLevel(router, "logs");
990
+ },
991
+ "ctrl+3": () => {
992
+ const router = app.router;
993
+ if (!router) return;
994
+ navigateTopLevel(router, "settings");
995
+ },
996
+ escape: () => {
997
+ const router = app.router;
998
+ if (!router) return;
999
+ const route = router.currentRoute();
1000
+ if (route?.id === "detail") {
1001
+ if (router.canGoBack()) router.back();
1002
+ else navigateTopLevel(router, "logs");
1003
+ }
1004
+ },
1005
+ });
1006
+
1007
+ app.onEvent((event) => {
1008
+ if (event.kind === "engine") {
1009
+ const raw = event.event;
1010
+ if (raw.kind === "resize") {
1011
+ app.update((prev) => {
1012
+ if (prev.viewportCols === raw.cols && prev.viewportRows === raw.rows) return prev;
1013
+ return Object.freeze({
1014
+ ...prev,
1015
+ viewportCols: raw.cols,
1016
+ viewportRows: raw.rows,
1017
+ });
1018
+ });
1019
+ }
1020
+ if (raw.kind === "text" && raw.codepoint >= 0 && raw.codepoint <= 0x10ffff) {
1021
+ const ch = String.fromCodePoint(raw.codepoint).toLowerCase();
1022
+ if (ch === "q") void shutdown();
1023
+ }
1024
+ }
1025
+ if (event.kind === "fatal") {
1026
+ clearInterval(logTimer);
1027
+ }
1028
+ });
1029
+
1030
+ const logTimer = setInterval(() => {
1031
+ app.update((prev) => {
1032
+ if (!prev.autoRefresh) return prev;
1033
+ return appendTickLog(prev);
1034
+ });
1035
+ }, LOG_TICK_MS);
1036
+
1037
+ await app.start();
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "types": ["node"],
10
+ "noUncheckedIndexedAccess": true,
11
+ "exactOptionalPropertyTypes": true
12
+ },
13
+ "include": ["src/**/*.ts"]
14
+ }