@typecad/ui 1.0.0-alpha.12 → 1.0.0-alpha.14

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.
Files changed (124) hide show
  1. package/assets/fonts/dejavu/DejaVuSans-Bold.ttf +0 -0
  2. package/assets/fonts/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  3. package/assets/fonts/dejavu/DejaVuSans-Oblique.ttf +0 -0
  4. package/assets/fonts/dejavu/DejaVuSans.ttf +0 -0
  5. package/assets/fonts/dejavu/DejaVuSansMono-Bold.ttf +0 -0
  6. package/assets/fonts/dejavu/DejaVuSansMono.ttf +0 -0
  7. package/assets/fonts/dejavu/LICENSE +187 -0
  8. package/assets/fonts/dejavu/README.md +49 -0
  9. package/dist/cli.js +22 -19
  10. package/dist/engine-index.js +2 -0
  11. package/dist/preview/build-program.d.ts +7 -1
  12. package/dist/preview/build-program.js +94 -15
  13. package/dist/preview/host-gfx.d.ts +3 -0
  14. package/dist/preview/host-gfx.js +30 -3
  15. package/dist/preview/host-ui-runtime.d.ts +115 -0
  16. package/dist/preview/host-ui-runtime.js +927 -46
  17. package/dist/ui-engine/block-layout.js +25 -11
  18. package/dist/ui-engine/color.d.ts +6 -0
  19. package/dist/ui-engine/color.js +80 -2
  20. package/dist/ui-engine/compat-report.d.ts +8 -0
  21. package/dist/ui-engine/compat-report.js +124 -0
  22. package/dist/ui-engine/css-imports.d.ts +4 -0
  23. package/dist/ui-engine/css-imports.js +70 -0
  24. package/dist/ui-engine/css-parser.js +58 -14
  25. package/dist/ui-engine/default-font.d.ts +25 -0
  26. package/dist/ui-engine/default-font.js +80 -0
  27. package/dist/ui-engine/font-assets.d.ts +2 -2
  28. package/dist/ui-engine/font-assets.js +22 -5
  29. package/dist/ui-engine/html-parser.d.ts +4 -0
  30. package/dist/ui-engine/html-parser.js +241 -26
  31. package/dist/ui-engine/image-assets.d.ts +8 -1
  32. package/dist/ui-engine/image-assets.js +35 -2
  33. package/dist/ui-engine/image-decode.d.ts +22 -0
  34. package/dist/ui-engine/image-decode.js +194 -0
  35. package/dist/ui-engine/inline-parser.js +1 -1
  36. package/dist/ui-engine/layout-engine.d.ts +19 -0
  37. package/dist/ui-engine/layout-engine.js +59 -3
  38. package/dist/ui-engine/model.d.ts +18 -0
  39. package/dist/ui-engine/model.js +82 -8
  40. package/dist/ui-engine/runtime-header/blend-bodies.js +29 -9
  41. package/dist/ui-engine/runtime-header/canvas-helpers.js +3 -0
  42. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -111
  43. package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +21 -0
  44. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -179
  45. package/dist/ui-engine/runtime-header/forward-decls.js +215 -167
  46. package/dist/ui-engine/runtime-header/init-press-input.js +138 -135
  47. package/dist/ui-engine/runtime-header/keyboard.js +682 -380
  48. package/dist/ui-engine/runtime-header/node-draw-body.js +1658 -1386
  49. package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -214
  50. package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -568
  51. package/dist/ui-engine/runtime-header/scroll-physics.js +25 -1
  52. package/dist/ui-engine/runtime-header/state-bindings-nav.js +9 -0
  53. package/dist/ui-engine/runtime-header/structs.js +228 -216
  54. package/dist/ui-engine/runtime-header/text-rendering.js +888 -770
  55. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -116
  56. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +897 -633
  57. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -25
  58. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +78 -23
  59. package/dist/ui-engine/runtime-header/types-defines.js +110 -93
  60. package/dist/ui-engine/shadcn-kit.d.ts +1 -0
  61. package/dist/ui-engine/shadcn-kit.js +506 -0
  62. package/dist/ui-engine/style-resolver.d.ts +4 -0
  63. package/dist/ui-engine/style-resolver.js +58 -6
  64. package/dist/ui-engine/transpile-ui.js +12 -5
  65. package/dist/ui-engine/ua-stylesheet.d.ts +22 -1
  66. package/dist/ui-engine/ua-stylesheet.js +119 -32
  67. package/dist/ui-engine/ui-lowering.js +37 -3
  68. package/dist/ui-engine/ui-registry.js +32 -4
  69. package/dist/ui-engine/yoga-layout.js +57 -52
  70. package/dist/wizard/index.d.ts +2 -0
  71. package/dist/wizard/index.js +1 -0
  72. package/dist/wizard/integration-wizard.d.ts +3 -0
  73. package/dist/wizard/integration-wizard.js +67 -7
  74. package/dist/wizard/package-writer.d.ts +20 -0
  75. package/dist/wizard/package-writer.js +79 -0
  76. package/package.json +11 -5
  77. package/src/cli.ts +90 -87
  78. package/src/engine-index.ts +53 -51
  79. package/src/preview/build-program.ts +810 -734
  80. package/src/preview/host-gfx.ts +30 -3
  81. package/src/preview/host-ui-runtime.ts +4132 -3289
  82. package/src/ui-engine/block-layout.ts +24 -11
  83. package/src/ui-engine/color.ts +77 -2
  84. package/src/ui-engine/compat-report.ts +139 -0
  85. package/src/ui-engine/css-imports.ts +69 -0
  86. package/src/ui-engine/css-parser.ts +64 -15
  87. package/src/ui-engine/default-font.ts +95 -0
  88. package/src/ui-engine/font-assets.ts +545 -525
  89. package/src/ui-engine/html-parser.ts +615 -397
  90. package/src/ui-engine/image-assets.ts +37 -2
  91. package/src/ui-engine/image-decode.ts +244 -0
  92. package/src/ui-engine/inline-parser.ts +1 -1
  93. package/src/ui-engine/layout-engine.ts +61 -3
  94. package/src/ui-engine/model.ts +1322 -1230
  95. package/src/ui-engine/runtime-header/blend-bodies.ts +29 -9
  96. package/src/ui-engine/runtime-header/canvas-helpers.ts +3 -0
  97. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -117
  98. package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +21 -0
  99. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -185
  100. package/src/ui-engine/runtime-header/forward-decls.ts +227 -179
  101. package/src/ui-engine/runtime-header/init-press-input.ts +144 -141
  102. package/src/ui-engine/runtime-header/keyboard.ts +689 -386
  103. package/src/ui-engine/runtime-header/node-draw-body.ts +1683 -1411
  104. package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -220
  105. package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -574
  106. package/src/ui-engine/runtime-header/scroll-physics.ts +25 -1
  107. package/src/ui-engine/runtime-header/state-bindings-nav.ts +9 -0
  108. package/src/ui-engine/runtime-header/structs.ts +234 -222
  109. package/src/ui-engine/runtime-header/text-rendering.ts +894 -776
  110. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -122
  111. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -638
  112. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -31
  113. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +78 -23
  114. package/src/ui-engine/runtime-header/types-defines.ts +116 -99
  115. package/src/ui-engine/shadcn-kit.ts +507 -0
  116. package/src/ui-engine/style-resolver.ts +471 -416
  117. package/src/ui-engine/transpile-ui.ts +12 -5
  118. package/src/ui-engine/ua-stylesheet.ts +137 -31
  119. package/src/ui-engine/ui-lowering.ts +486 -452
  120. package/src/ui-engine/ui-registry.ts +556 -524
  121. package/src/ui-engine/yoga-layout.ts +307 -309
  122. package/src/wizard/index.ts +46 -38
  123. package/src/wizard/integration-wizard.ts +689 -619
  124. package/src/wizard/package-writer.ts +98 -0
@@ -5,8 +5,10 @@
5
5
  // into their project's cuttlefish.config.ts: pick a display, answer bus/pin/
6
6
  // speed questions with hardware-aware defaults, then splice the resulting
7
7
  // `display` section into the config without touching any other section.
8
- // Offers to create a starter .ui entry file when none exists and prints the
9
- // next build/flash/library steps at the end.
8
+ // Also adds a `preview` npm script to package.json so the desktop preview
9
+ // renderer starts via `npm run preview`. Offers to create a starter .ui entry
10
+ // file when none exists and prints the next build/flash/library steps at the
11
+ // end.
10
12
  // ---------------------------------------------------------------------------
11
13
  import fs from "node:fs";
12
14
  import path from "node:path";
@@ -14,6 +16,7 @@ import chalk from "chalk";
14
16
  import { createPromptInterface, promptConfirm, promptInt, promptSelect, promptText, } from "./prompts.js";
15
17
  import { DISPLAY_CATALOG, TOUCH_CATALOG, findPinConflicts, } from "./display-catalog.js";
16
18
  import { findCuttlefishConfig, findSyntaxError, readConfigSection, readEntryPath, renderDisplayProperty, upsertDisplaySection, } from "./config-writer.js";
19
+ import { findPackageJson, previewScriptCommand, readPackageScript, upsertPackageScript, } from "./package-writer.js";
17
20
  import { renderStarterUi } from "./starter-ui.js";
18
21
  /** Keys the wizard manages — anything else found in an existing display
19
22
  * section is carried over untouched instead of being silently dropped. */
@@ -405,7 +408,35 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
405
408
  return { exitCode: 1 };
406
409
  }
407
410
  }
408
- // --- 8. Summary + write -------------------------------------------------------
411
+ // --- 8. package.json preview script ------------------------------------------
412
+ // `npm run preview` should start the desktop preview renderer, pointing at
413
+ // the config the wizard just verified. An existing identical script is
414
+ // left untouched; a differing one is only replaced with consent.
415
+ let packageJsonPath;
416
+ let packageJsonText;
417
+ let previewCommand;
418
+ let existingPreview;
419
+ let replacePreview = false;
420
+ const foundPackageJson = findPackageJson(path.dirname(configPath));
421
+ if (foundPackageJson) {
422
+ try {
423
+ packageJsonText = fs.readFileSync(foundPackageJson, "utf-8");
424
+ existingPreview = readPackageScript(packageJsonText, "preview");
425
+ packageJsonPath = foundPackageJson;
426
+ previewCommand = previewScriptCommand(foundPackageJson, configPath);
427
+ }
428
+ catch (err) {
429
+ console.log(` ${chalk.yellow("!")} ${err instanceof Error ? err.message : String(err)} — skipping the npm preview script.`);
430
+ }
431
+ if (previewCommand && existingPreview !== undefined && existingPreview !== previewCommand) {
432
+ console.log(` ${chalk.yellow("!")} package.json already has a preview script: ${chalk.white(existingPreview)}`);
433
+ replacePreview = await promptConfirm(rl, "Replace it with the wizard's preview command?", true);
434
+ }
435
+ }
436
+ else {
437
+ console.log(`${chalk.yellow("!")} No package.json found next to ${path.relative(cwd, configPath)} — skipping the npm preview script.`);
438
+ }
439
+ // --- 9. Summary + write -------------------------------------------------------
409
440
  console.log();
410
441
  console.log(chalk.dim(" The following display section will be written to"));
411
442
  console.log(chalk.dim(` ${path.relative(cwd, configPath)}:`));
@@ -421,6 +452,17 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
421
452
  if (carried.length > 0) {
422
453
  console.log(` ${chalk.dim(`Kept existing display settings the wizard does not manage: ${carried.join(", ")}`)}`);
423
454
  }
455
+ if (previewCommand && packageJsonPath) {
456
+ if (existingPreview === previewCommand) {
457
+ console.log(` ${chalk.dim(`npm script already configured: preview — "${previewCommand}"`)}`);
458
+ }
459
+ else if (existingPreview === undefined || replacePreview) {
460
+ console.log(` ${chalk.dim("Also writes npm script:")} ${chalk.white("preview")}${chalk.dim(` — "${previewCommand}"`)}`);
461
+ }
462
+ else {
463
+ console.log(` ${chalk.dim(`Kept existing preview script: "${existingPreview}"`)}`);
464
+ }
465
+ }
424
466
  console.log();
425
467
  if (!(await promptConfirm(rl, "Write this configuration?", true))) {
426
468
  console.log(chalk.dim(" Aborted — nothing was written."));
@@ -434,7 +476,21 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
434
476
  }
435
477
  fs.writeFileSync(configPath, updated.text, "utf-8");
436
478
  console.log(`${chalk.green("✓")} ${updated.mode === "replaced" ? "Updated" : "Added"} the display section in ${chalk.white(path.relative(cwd, configPath))}`);
437
- // --- 9. Starter entry file ------------------------------------------------------
479
+ // The preview script rides along with the confirmed write: added when
480
+ // missing, confirmed verbatim when already the wizard's command, and only
481
+ // replaced when the user consented above.
482
+ if (packageJsonPath && packageJsonText !== undefined && previewCommand
483
+ && (existingPreview === undefined || existingPreview === previewCommand || replacePreview)) {
484
+ const scriptUpsert = upsertPackageScript(packageJsonText, "preview", previewCommand);
485
+ if (scriptUpsert.changed) {
486
+ fs.writeFileSync(packageJsonPath, scriptUpsert.text, "utf-8");
487
+ console.log(`${chalk.green("✓")} ${scriptUpsert.previous !== undefined ? "Updated" : "Added"} npm script ${chalk.white("preview")} in ${chalk.white(path.relative(cwd, packageJsonPath))}`);
488
+ }
489
+ else {
490
+ console.log(`${chalk.green("✓")} npm script ${chalk.white("preview")} already configured in ${chalk.white(path.relative(cwd, packageJsonPath))}`);
491
+ }
492
+ }
493
+ // --- 10. Starter entry file ------------------------------------------------------
438
494
  if (entryPath) {
439
495
  const entryAbs = path.resolve(path.dirname(configPath), entryPath);
440
496
  if (!fs.existsSync(entryAbs) && entryAbs.endsWith(".ui")) {
@@ -445,7 +501,7 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
445
501
  }
446
502
  }
447
503
  }
448
- // --- 10. Next steps ---------------------------------------------------------------
504
+ // --- 11. Next steps ---------------------------------------------------------------
449
505
  console.log();
450
506
  console.log(chalk.cyan("Next steps"));
451
507
  console.log();
@@ -463,7 +519,7 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
463
519
  const firstStep = libraries.length > 0 ? 2 : 1;
464
520
  console.log(` ${firstStep}. Preview your UI on the desktop:`);
465
521
  console.log();
466
- console.log(` ${chalk.cyan("npx @typecad/cuttlefish preview")}`);
522
+ console.log(` ${chalk.cyan(previewCommand ? "npm run preview" : "npx @typecad/cuttlefish preview")}`);
467
523
  console.log();
468
524
  console.log(` ${firstStep + 1}. Compile for your board:`);
469
525
  console.log();
@@ -478,7 +534,11 @@ export async function runIntegrationWizard(cwd = process.cwd(), streams = {}) {
478
534
  console.log(chalk.dim(" off-target, calibrate your panel and update display.touch.calibration."));
479
535
  console.log();
480
536
  }
481
- return { exitCode: 0, configPath };
537
+ return {
538
+ exitCode: 0,
539
+ configPath,
540
+ ...(packageJsonPath && previewCommand ? { packageJsonPath } : {}),
541
+ };
482
542
  }
483
543
  finally {
484
544
  rl.close();
@@ -0,0 +1,20 @@
1
+ /** Walk up from `startDir` looking for package.json. */
2
+ export declare function findPackageJson(startDir: string): string | undefined;
3
+ /** Read a single npm script; undefined when missing. Throws on invalid JSON. */
4
+ export declare function readPackageScript(sourceText: string, name: string): string | undefined;
5
+ export interface UpsertScriptResult {
6
+ text: string;
7
+ /** false — the script already had this exact command; text is unchanged. */
8
+ changed: boolean;
9
+ /** The command that was replaced, when a different script existed. */
10
+ previous?: string;
11
+ }
12
+ /** Insert or update one npm script, preserving every other key and their order. */
13
+ export declare function upsertPackageScript(sourceText: string, name: string, command: string): UpsertScriptResult;
14
+ /**
15
+ * The `cuttlefish preview` command the wizard writes as the `preview` npm
16
+ * script. The --config path is made relative to the package.json directory
17
+ * (npm scripts run there), posix-separated, and explicitly ./-prefixed so
18
+ * sibling configs render as `./cuttlefish.config.ts`.
19
+ */
20
+ export declare function previewScriptCommand(packageJsonPath: string, configPath: string): string;
@@ -0,0 +1,79 @@
1
+ // ---------------------------------------------------------------------------
2
+ // package.json script writer for the @typecad/ui integration wizard.
3
+ //
4
+ // package.json is plain JSON (no comments, no expressions), so unlike
5
+ // cuttlefish.config.ts (see config-writer.ts) this needs no AST surgery —
6
+ // parse, compare, set, and re-serialize with the 2-space formatting npm and
7
+ // `cuttlefish create` both write. An unchanged script is a no-op: the
8
+ // original text is returned verbatim so a re-run never reformats the file.
9
+ // ---------------------------------------------------------------------------
10
+ import fs from "node:fs";
11
+ import path from "node:path";
12
+ const PACKAGE_FILENAME = "package.json";
13
+ /** Walk up from `startDir` looking for package.json. */
14
+ export function findPackageJson(startDir) {
15
+ let dir = path.resolve(startDir);
16
+ for (;;) {
17
+ const candidate = path.join(dir, PACKAGE_FILENAME);
18
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
19
+ return candidate;
20
+ }
21
+ const parent = path.dirname(dir);
22
+ if (parent === dir)
23
+ return undefined;
24
+ dir = parent;
25
+ }
26
+ }
27
+ function parsePackageJson(sourceText) {
28
+ try {
29
+ return JSON.parse(sourceText);
30
+ }
31
+ catch (err) {
32
+ throw new Error(`package.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
33
+ }
34
+ }
35
+ function scriptsOf(pkg) {
36
+ const scripts = pkg.scripts;
37
+ return typeof scripts === "object" && scripts !== null && !Array.isArray(scripts)
38
+ ? scripts
39
+ : undefined;
40
+ }
41
+ /** Read a single npm script; undefined when missing. Throws on invalid JSON. */
42
+ export function readPackageScript(sourceText, name) {
43
+ const scripts = scriptsOf(parsePackageJson(sourceText));
44
+ const value = scripts?.[name];
45
+ return typeof value === "string" ? value : undefined;
46
+ }
47
+ /** Insert or update one npm script, preserving every other key and their order. */
48
+ export function upsertPackageScript(sourceText, name, command) {
49
+ const pkg = parsePackageJson(sourceText);
50
+ const scripts = scriptsOf(pkg);
51
+ const previous = scripts?.[name];
52
+ if (previous === command) {
53
+ return { text: sourceText, changed: false };
54
+ }
55
+ if (!scripts) {
56
+ pkg.scripts = {};
57
+ }
58
+ pkg.scripts[name] = command;
59
+ // Keep the file's trailing-newline convention (npm writes LF; a missing
60
+ // final newline stays missing so the diff is one line, not two).
61
+ const eol = sourceText.endsWith("\r\n") ? "\r\n" : sourceText.endsWith("\n") ? "\n" : "";
62
+ return {
63
+ text: `${JSON.stringify(pkg, null, 2)}${eol}`,
64
+ changed: true,
65
+ ...(previous !== undefined ? { previous } : {}),
66
+ };
67
+ }
68
+ /**
69
+ * The `cuttlefish preview` command the wizard writes as the `preview` npm
70
+ * script. The --config path is made relative to the package.json directory
71
+ * (npm scripts run there), posix-separated, and explicitly ./-prefixed so
72
+ * sibling configs render as `./cuttlefish.config.ts`.
73
+ */
74
+ export function previewScriptCommand(packageJsonPath, configPath) {
75
+ let rel = path.relative(path.dirname(packageJsonPath), configPath).split(path.sep).join("/");
76
+ if (!rel.startsWith("."))
77
+ rel = `./${rel}`;
78
+ return `cuttlefish preview --config ${rel}`;
79
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typecad/ui",
3
- "version": "1.0.0-alpha.12",
3
+ "version": "1.0.0-alpha.14",
4
4
  "description": "TypeCAD UI authoring library — HTML/CSS-driven graphics for microcontrollers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,6 +25,9 @@
25
25
  "types": "./dist/ui-engine/*.d.ts",
26
26
  "default": "./dist/ui-engine/*.js"
27
27
  },
28
+ "./themes/*": {
29
+ "default": "./themes/*"
30
+ },
28
31
  "./preview/*": {
29
32
  "types": "./dist/preview/*.d.ts",
30
33
  "default": "./dist/preview/*.js"
@@ -37,14 +40,17 @@
37
40
  "sideEffects": false,
38
41
  "files": [
39
42
  "dist",
40
- "src"
43
+ "src",
44
+ "assets"
41
45
  ],
42
46
  "dependencies": {
43
- "@typecad/cuttlefish": "1.0.0-alpha.12",
44
- "chalk": "^4.1.2",
47
+ "@typecad/cuttlefish": "1.0.0-alpha.14",
48
+ "chalk": "^6.0.0",
45
49
  "css-tree": "^3.2.1",
50
+ "decode-ico": "^0.4.1",
46
51
  "linkedom": "^0.18.12",
47
52
  "opentype.js": "^2.0.0",
53
+ "sharp": "^0.35.3",
48
54
  "typescript": "^5.7.3",
49
55
  "yoga-layout": "^3.2.1"
50
56
  },
@@ -80,7 +86,7 @@
80
86
  "firmware"
81
87
  ],
82
88
  "engines": {
83
- "node": ">=18"
89
+ "node": ">=22.11.0"
84
90
  },
85
91
  "devDependencies": {
86
92
  "@types/node": "^22.10.7",
package/src/cli.ts CHANGED
@@ -1,87 +1,90 @@
1
- #!/usr/bin/env node
2
- // ---------------------------------------------------------------------------
3
- // @typecad/ui CLI — display integration wizard.
4
- //
5
- // npx @typecad/ui --config Run the interactive integration wizard
6
- // npx @typecad/ui --help Show usage
7
- // npx @typecad/ui --version Show the installed version
8
- // ---------------------------------------------------------------------------
9
-
10
- import { runIntegrationWizard } from "./wizard/integration-wizard.js";
11
-
12
- const USAGE = `
13
- @typecad/ui — HTML/CSS-driven graphics for microcontrollers
14
-
15
- Usage:
16
- npx @typecad/ui --config Configure a display (+ touch) for your
17
- cuttlefish project interactively. Writes the
18
- \`display\` section of cuttlefish.config.ts.
19
-
20
- Options:
21
- --config Run the integration wizard
22
- --help, -h Show this help
23
- --version, -v Print the installed @typecad/ui version
24
-
25
- The wizard asks which display module you are using (ILI9341 / ST7796S SPI TFT,
26
- SSD1309 I2C OLED, desktop simulator, or custom), then walks through bus pins,
27
- SPI/I2C speed, rotation, and touch controller wiring with hardware-aware
28
- defaults. It never touches the rest of your cuttlefish.config.ts.
29
-
30
- Docs: https://github.com/justind000/typecode/tree/main/packages/ui
31
- `.trim();
32
-
33
- function printUsage(): void {
34
- console.log(USAGE);
35
- }
36
-
37
- async function main(argv: string[]): Promise<number> {
38
- const args = argv.slice(2);
39
-
40
- if (args.includes("--help") || args.includes("-h")) {
41
- printUsage();
42
- return 0;
43
- }
44
-
45
- if (args.includes("--version") || args.includes("-v")) {
46
- // Lazily read the version so importing this module stays side-effect free.
47
- const { readFile } = await import("node:fs/promises");
48
- const { fileURLToPath } = await import("node:url");
49
- const { dirname, join } = await import("node:path");
50
- try {
51
- const packageJson = JSON.parse(
52
- await readFile(join(dirname(dirname(fileURLToPath(import.meta.url))), "package.json"), "utf-8"),
53
- ) as { version?: string };
54
- console.log(packageJson.version ?? "unknown");
55
- return 0;
56
- } catch {
57
- console.log("unknown");
58
- return 0;
59
- }
60
- }
61
-
62
- if (args.length === 0 || (args.length === 1 && (args[0] === "--config" || args[0] === "config"))) {
63
- if (!process.stdin.isTTY) {
64
- console.error("The integration wizard is interactive and needs a terminal.");
65
- console.error("Run `npx @typecad/ui --config` from your project directory, or configure");
66
- console.error("the `display` section of cuttlefish.config.ts manually:");
67
- console.error("https://github.com/justind000/typecode/tree/main/packages/ui#display-configuration");
68
- return 1;
69
- }
70
- const result = await runIntegrationWizard(process.cwd());
71
- return result.exitCode;
72
- }
73
-
74
- console.error(`Unknown option${args.length > 1 ? "s" : ""}: ${args.join(" ")}\n`);
75
- printUsage();
76
- return 2;
77
- }
78
-
79
- main(process.argv).then(
80
- (code) => {
81
- process.exitCode = code;
82
- },
83
- (error) => {
84
- console.error(error instanceof Error ? error.message : error);
85
- process.exitCode = 1;
86
- },
87
- );
1
+ #!/usr/bin/env node
2
+ // ---------------------------------------------------------------------------
3
+ // @typecad/ui CLI — display integration wizard.
4
+ //
5
+ // npx @typecad/ui --config Run the interactive integration wizard
6
+ // npx @typecad/ui --help Show usage
7
+ // npx @typecad/ui --version Show the installed version
8
+ // ---------------------------------------------------------------------------
9
+
10
+ import { runIntegrationWizard } from "./wizard/integration-wizard.js";
11
+
12
+ const USAGE = `
13
+ @typecad/ui — HTML/CSS-driven graphics for microcontrollers
14
+
15
+ Usage:
16
+ npx @typecad/ui --config Configure a display (+ touch) for your
17
+ cuttlefish project interactively. Writes the
18
+ \`display\` section of cuttlefish.config.ts
19
+ and a \`preview\` npm script.
20
+
21
+ Options:
22
+ --config Run the integration wizard
23
+ --help, -h Show this help
24
+ --version, -v Print the installed @typecad/ui version
25
+
26
+ The wizard asks which display module you are using (ILI9341 / ST7796S SPI TFT,
27
+ SSD1309 I2C OLED, desktop simulator, or custom), then walks through bus pins,
28
+ SPI/I2C speed, rotation, and touch controller wiring with hardware-aware
29
+ defaults. It never touches the rest of your cuttlefish.config.ts, and adds a
30
+ \`preview\` script to package.json so the desktop preview renderer starts with
31
+ \`npm run preview\`.
32
+
33
+ Docs: https://github.com/justind000/typecode/tree/main/packages/ui
34
+ `.trim();
35
+
36
+ function printUsage(): void {
37
+ console.log(USAGE);
38
+ }
39
+
40
+ async function main(argv: string[]): Promise<number> {
41
+ const args = argv.slice(2);
42
+
43
+ if (args.includes("--help") || args.includes("-h")) {
44
+ printUsage();
45
+ return 0;
46
+ }
47
+
48
+ if (args.includes("--version") || args.includes("-v")) {
49
+ // Lazily read the version so importing this module stays side-effect free.
50
+ const { readFile } = await import("node:fs/promises");
51
+ const { fileURLToPath } = await import("node:url");
52
+ const { dirname, join } = await import("node:path");
53
+ try {
54
+ const packageJson = JSON.parse(
55
+ await readFile(join(dirname(dirname(fileURLToPath(import.meta.url))), "package.json"), "utf-8"),
56
+ ) as { version?: string };
57
+ console.log(packageJson.version ?? "unknown");
58
+ return 0;
59
+ } catch {
60
+ console.log("unknown");
61
+ return 0;
62
+ }
63
+ }
64
+
65
+ if (args.length === 0 || (args.length === 1 && (args[0] === "--config" || args[0] === "config"))) {
66
+ if (!process.stdin.isTTY) {
67
+ console.error("The integration wizard is interactive and needs a terminal.");
68
+ console.error("Run `npx @typecad/ui --config` from your project directory, or configure");
69
+ console.error("the `display` section of cuttlefish.config.ts manually:");
70
+ console.error("https://github.com/justind000/typecode/tree/main/packages/ui#display-configuration");
71
+ return 1;
72
+ }
73
+ const result = await runIntegrationWizard(process.cwd());
74
+ return result.exitCode;
75
+ }
76
+
77
+ console.error(`Unknown option${args.length > 1 ? "s" : ""}: ${args.join(" ")}\n`);
78
+ printUsage();
79
+ return 2;
80
+ }
81
+
82
+ main(process.argv).then(
83
+ (code) => {
84
+ process.exitCode = code;
85
+ },
86
+ (error) => {
87
+ console.error(error instanceof Error ? error.message : error);
88
+ process.exitCode = 1;
89
+ },
90
+ );
@@ -1,51 +1,53 @@
1
- // ---------------------------------------------------------------------------
2
- // Engine entry point for @typecad/ui.
3
- //
4
- // Cuttlefish dynamically imports this module when UI usage is detected:
5
- // const engine = await import("@typecad/ui/engine");
6
- // const hook = await engine.registerTranspilerUI();
7
- //
8
- // registerTranspilerUI() returns an object implementing the TranspilerUIHook
9
- // interface defined in @typecad/cuttlefish. This is the single runtime
10
- // contract between the two packages.
11
- // ---------------------------------------------------------------------------
12
-
13
- import type { TranspilerUIHook } from "@typecad/cuttlefish/ui-hook";
14
- import {
15
- resetUIRegistry, loadUIModule, loadUIModuleFromText, getUIModule,
16
- hasUIModule, allUIModules, allLoweredUIModules,
17
- markEntryHasUI, entryHasUI, clearEntryHasUI, lowerOnMount,
18
- generateProjectUITypeDeclarations,
19
- } from "./ui-engine/ui-registry.js";
20
- import { resolveColor, resolveColorInternal } from "./ui-engine/color.js";
21
- import { emitRuntimeHeader } from "./ui-engine/runtime-header.js";
22
- import { emitCuttlefishGfx } from "./ui-engine/runtime-header/cuttlefish-gfx.js";
23
- import { splitUiFile } from "./ui-engine/ui-file-splitter.js";
24
-
25
- export type { TranspilerUIHook };
26
-
27
- /**
28
- * Build and return the TranspilerUIHook — the object cuttlefish core uses
29
- * to access all UI capabilities (parsing, layout, lowering, emission).
30
- */
31
- export function registerTranspilerUI(): TranspilerUIHook {
32
- return {
33
- resetUIRegistry,
34
- loadUIModule,
35
- loadUIModuleFromText,
36
- getUIModule,
37
- hasUIModule,
38
- allUIModules,
39
- allLoweredUIModules,
40
- markEntryHasUI,
41
- entryHasUI,
42
- clearEntryHasUI,
43
- lowerOnMount,
44
- resolveColor,
45
- resolveColorInternal,
46
- emitRuntimeHeader,
47
- emitCuttlefishGfx,
48
- splitUiFile,
49
- generateProjectUITypeDeclarations,
50
- };
51
- }
1
+ // ---------------------------------------------------------------------------
2
+ // Engine entry point for @typecad/ui.
3
+ //
4
+ // Cuttlefish dynamically imports this module when UI usage is detected:
5
+ // const engine = await import("@typecad/ui/engine");
6
+ // const hook = await engine.registerTranspilerUI();
7
+ //
8
+ // registerTranspilerUI() returns an object implementing the TranspilerUIHook
9
+ // interface defined in @typecad/cuttlefish. This is the single runtime
10
+ // contract between the two packages.
11
+ // ---------------------------------------------------------------------------
12
+
13
+ import type { TranspilerUIHook } from "@typecad/cuttlefish/ui-hook";
14
+ import {
15
+ resetUIRegistry, loadUIModule, loadUIModuleFromText, getUIModule,
16
+ hasUIModule, allUIModules, allLoweredUIModules,
17
+ markEntryHasUI, entryHasUI, clearEntryHasUI, lowerOnMount,
18
+ generateProjectUITypeDeclarations,
19
+ } from "./ui-engine/ui-registry.js";
20
+ import { resolveColor, resolveColorInternal } from "./ui-engine/color.js";
21
+ import { emitRuntimeHeader } from "./ui-engine/runtime-header.js";
22
+ import { emitCuttlefishGfx } from "./ui-engine/runtime-header/cuttlefish-gfx.js";
23
+ import { splitUiFile } from "./ui-engine/ui-file-splitter.js";
24
+ import { warmUpImageDecoding } from "./ui-engine/image-decode.js";
25
+
26
+ export type { TranspilerUIHook };
27
+
28
+ /**
29
+ * Build and return the TranspilerUIHook the object cuttlefish core uses
30
+ * to access all UI capabilities (parsing, layout, lowering, emission).
31
+ */
32
+ export function registerTranspilerUI(): TranspilerUIHook {
33
+ return {
34
+ resetUIRegistry,
35
+ loadUIModule,
36
+ loadUIModuleFromText,
37
+ getUIModule,
38
+ hasUIModule,
39
+ allUIModules,
40
+ allLoweredUIModules,
41
+ markEntryHasUI,
42
+ entryHasUI,
43
+ clearEntryHasUI,
44
+ lowerOnMount,
45
+ resolveColor,
46
+ resolveColorInternal,
47
+ emitRuntimeHeader,
48
+ emitCuttlefishGfx,
49
+ splitUiFile,
50
+ warmUpImageDecoding,
51
+ generateProjectUITypeDeclarations,
52
+ };
53
+ }