@typecad/ui 1.0.0-alpha.13 → 1.0.0-alpha.15

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 (60) hide show
  1. package/README.md +1457 -1469
  2. package/dist/cli.js +22 -19
  3. package/dist/preview/host-ui-runtime.js +0 -1
  4. package/dist/ui-engine/model.js +7 -1
  5. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -115
  6. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -182
  7. package/dist/ui-engine/runtime-header/forward-decls.js +227 -216
  8. package/dist/ui-engine/runtime-header/init-press-input.js +138 -138
  9. package/dist/ui-engine/runtime-header/keyboard.js +676 -676
  10. package/dist/ui-engine/runtime-header/node-draw-body.js +1658 -1656
  11. package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -259
  12. package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -736
  13. package/dist/ui-engine/runtime-header/scroll-physics.js +4 -4
  14. package/dist/ui-engine/runtime-header/structs.js +228 -228
  15. package/dist/ui-engine/runtime-header/text-rendering.js +888 -888
  16. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -118
  17. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +896 -896
  18. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -24
  19. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +11 -11
  20. package/dist/ui-engine/runtime-header/types-defines.js +110 -110
  21. package/dist/ui-engine/shadcn-kit.d.ts +1 -1
  22. package/dist/ui-engine/shadcn-kit.js +4 -0
  23. package/dist/ui-engine/ui-lowering.d.ts +1 -1
  24. package/dist/ui-engine/ui-lowering.js +14 -5
  25. package/dist/ui-engine/ui-registry.js +9 -1
  26. package/dist/wizard/display-catalog.d.ts +0 -3
  27. package/dist/wizard/display-catalog.js +3 -12
  28. package/dist/wizard/index.d.ts +2 -0
  29. package/dist/wizard/index.js +1 -0
  30. package/dist/wizard/integration-wizard.d.ts +3 -0
  31. package/dist/wizard/integration-wizard.js +69 -20
  32. package/dist/wizard/package-writer.d.ts +20 -0
  33. package/dist/wizard/package-writer.js +79 -0
  34. package/package.json +4 -4
  35. package/src/cli.ts +90 -87
  36. package/src/preview/host-ui-runtime.ts +0 -1
  37. package/src/ui-engine/model.ts +6 -1
  38. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -121
  39. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -188
  40. package/src/ui-engine/runtime-header/forward-decls.ts +238 -227
  41. package/src/ui-engine/runtime-header/init-press-input.ts +144 -144
  42. package/src/ui-engine/runtime-header/keyboard.ts +689 -689
  43. package/src/ui-engine/runtime-header/node-draw-body.ts +1683 -1681
  44. package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -265
  45. package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -742
  46. package/src/ui-engine/runtime-header/scroll-physics.ts +4 -4
  47. package/src/ui-engine/runtime-header/structs.ts +234 -234
  48. package/src/ui-engine/runtime-header/text-rendering.ts +894 -894
  49. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -124
  50. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -902
  51. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -30
  52. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +11 -11
  53. package/src/ui-engine/runtime-header/types-defines.ts +116 -116
  54. package/src/ui-engine/shadcn-kit.ts +4 -0
  55. package/src/ui-engine/ui-lowering.ts +12 -4
  56. package/src/ui-engine/ui-registry.ts +9 -1
  57. package/src/wizard/display-catalog.ts +261 -273
  58. package/src/wizard/index.ts +46 -38
  59. package/src/wizard/integration-wizard.ts +679 -619
  60. package/src/wizard/package-writer.ts +98 -0
package/dist/cli.js CHANGED
@@ -7,25 +7,28 @@
7
7
  // npx @typecad/ui --version Show the installed version
8
8
  // ---------------------------------------------------------------------------
9
9
  import { runIntegrationWizard } from "./wizard/integration-wizard.js";
10
- const USAGE = `
11
- @typecad/ui — HTML/CSS-driven graphics for microcontrollers
12
-
13
- Usage:
14
- npx @typecad/ui --config Configure a display (+ touch) for your
15
- cuttlefish project interactively. Writes the
16
- \`display\` section of cuttlefish.config.ts.
17
-
18
- Options:
19
- --config Run the integration wizard
20
- --help, -h Show this help
21
- --version, -v Print the installed @typecad/ui version
22
-
23
- The wizard asks which display module you are using (ILI9341 / ST7796S SPI TFT,
24
- SSD1309 I2C OLED, desktop simulator, or custom), then walks through bus pins,
25
- SPI/I2C speed, rotation, and touch controller wiring with hardware-aware
26
- defaults. It never touches the rest of your cuttlefish.config.ts.
27
-
28
- Docs: https://github.com/justind000/typecode/tree/main/packages/ui
10
+ const USAGE = `
11
+ @typecad/ui — HTML/CSS-driven graphics for microcontrollers
12
+
13
+ Usage:
14
+ npx @typecad/ui --config Configure a display (+ touch) for your
15
+ cuttlefish project interactively. Writes the
16
+ \`display\` section of cuttlefish.config.ts
17
+ and a \`preview\` npm script.
18
+
19
+ Options:
20
+ --config Run the integration wizard
21
+ --help, -h Show this help
22
+ --version, -v Print the installed @typecad/ui version
23
+
24
+ The wizard asks which display module you are using (ILI9341 / ST7796S SPI TFT,
25
+ SSD1309 I2C OLED, desktop simulator, or custom), then walks through bus pins,
26
+ SPI/I2C speed, rotation, and touch controller wiring with hardware-aware
27
+ defaults. It never touches the rest of your cuttlefish.config.ts, and adds a
28
+ \`preview\` script to package.json so the desktop preview renderer starts with
29
+ \`npm run preview\`.
30
+
31
+ Docs: https://github.com/justind000/typecode/tree/main/packages/ui
29
32
  `.trim();
30
33
  function printUsage() {
31
34
  console.log(USAGE);
@@ -609,7 +609,6 @@ export class PreviewUIRuntime {
609
609
  return a.index - b.index;
610
610
  }
611
611
  navigate(screenIdx) {
612
- this.onDiagnostics?.(`NAV -> screen ${screenIdx}`);
613
612
  const next = Math.trunc(Number(screenIdx));
614
613
  if (!Number.isFinite(next) || next < 0 || next >= this.screenCount || next === this.activeScreen)
615
614
  return;
@@ -78,10 +78,16 @@ function borderStyle(style) {
78
78
  return 0;
79
79
  }
80
80
  function borderWidthOf(style) {
81
+ // A none-style border renders nothing and must consume no space either:
82
+ // the draw path subtracts borderWidth*2 from the text max width, so a
83
+ // residual UA width on a borderless kit button wrapped its last glyph
84
+ // ("Primar" / "y"). Style none wins over any declared width.
85
+ if (borderStyle(style) === 0)
86
+ return 0;
81
87
  const px = cssPx(style.borderWidth);
82
88
  if (px > 0)
83
89
  return Math.max(1, Math.min(8, px));
84
- return borderStyle(style) === 0 ? 0 : 1;
90
+ return 1;
85
91
  }
86
92
  /** Per-side border width: the per-side field wins when set, else falls back to
87
93
  * the uniform borderWidth. Used for border-left/top/right/bottom support. */
@@ -2,120 +2,120 @@
2
2
  // Canvas push/draw helpers, buffered scroll canvas push, scrollbar direct.
3
3
  // See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
4
4
  export function emitCanvasScrollbar() {
5
- return `
6
- static inline void ui_push_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
7
- if (!canvas || !display_canvasBuffer(canvas)) return;
8
- UI_COLOR_T* pixels = display_canvasBuffer(canvas);
9
- int16_t stride = display_canvasWidth(canvas);
10
- // The canvas is viewport-sized: buffer row 0 = the first row of the viewport.
11
- // The display destination is (x, y) but the source buffer starts at (0, 0).
12
- // When a full-screen framebuffer is the active draw target, composite into it
13
- // via __ui_gfx (so the single bulk push at frame end captures everything).
14
- // Otherwise write straight to the display in one SPI transaction.
15
- if (__ui_fb) {
16
- // The framebuffer is the composition target. Track this publish so a
17
- // geometry repair or scroll-canvas repair that does not leave a node dirty
18
- // still reaches the panel during the frame-end flush.
19
- ui_fb_add_rect(x, y, w, h);
20
- if (w == stride) {
21
- ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
22
- return;
23
- }
24
- for (int16_t row = 0; row < h; row++) {
25
- ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
26
- }
27
- return;
28
- }
29
- display_startWrite();
30
- display_setAddrWindow(x, y, w, h);
31
- if (w == stride) {
32
- // Full-width: contiguous in buffer, single write.
33
- display_writePixels(pixels, static_cast<uint32_t>(w) * h);
34
- display_endWrite();
35
- return;
36
- }
37
- for (int16_t row = 0; row < h; row++) {
38
- display_writePixels(pixels + static_cast<int32_t>(row) * stride, w);
39
- }
40
- display_endWrite();
41
- }
42
-
43
- static inline void ui_draw_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
44
- if (!canvas || !display_canvasBuffer(canvas)) return;
45
- UI_COLOR_T* pixels = display_canvasBuffer(canvas);
46
- int16_t stride = display_canvasWidth(canvas);
47
- if (w == stride && h == display_canvasHeight(canvas)) {
48
- ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
49
- return;
50
- }
51
- for (int16_t row = 0; row < h; row++) {
52
- ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
53
- }
54
- }
55
-
56
- static inline void ui_push_buffered_scroll_canvas(CuttlefishCanvas16* bufferedScrollCanvas,
57
- CuttlefishCanvas16* bufferedScrollRepaintCanvas,
58
- int16_t bufferedScrollNode,
59
- int16_t bufferedScrollVX,
60
- int16_t bufferedScrollVY,
61
- int16_t bufferedScrollRepaintY,
62
- int16_t bufferedScrollRepaintH,
63
- CuttlefishDisplayTarget* drawTarget) {
64
- if (bufferedScrollNode < 0 || !bufferedScrollCanvas || !display_canvasBuffer(bufferedScrollCanvas)) return;
65
- // Draw scrollbar into the canvas (canvas-local coords: 0,0 = viewport top-left).
66
- ui_display_set_target(bufferedScrollCanvas);
67
- int16_t si = bufferedScrollNode;
68
- int16_t vw = __ui_nodes[si].box.w;
69
- int16_t vh = __ui_nodes[si].box.h;
70
- if (bufferedScrollRepaintCanvas && bufferedScrollRepaintH > 0) {
71
- ui_draw_canvas_rect(bufferedScrollRepaintCanvas, 0, bufferedScrollRepaintY, vw, bufferedScrollRepaintH);
72
- }
73
- int16_t tx = vw - 4;
74
- uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
75
- if (thumbH < 8) thumbH = 8;
76
- // Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
77
- // viewport (which would cast a negative value to a huge uint32_t and place the
78
- // thumb off-screen). Matches the guard in ui_render_scroll_bands.
79
- if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
80
- int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
81
- uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
82
- UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
83
- // Track covers the FULL reserved gutter (4px) so child decorations that
84
- // poke past the content area (e.g. outset shadows, offset up to +6px)
85
- // can't leave 1px ticks in the uncovered edge column. Thumb stays 3px.
86
- ui_display_fill_rect(tx, 0, 4, vh, dimFg);
87
- ui_display_fill_rect(tx, thumbY, 3, thumbH, __ui_nodes[si].fg);
88
- // Record the scrollY this canvas now reflects, so the next scroll frame can
89
- // compute its shift delta (Mode B) from scrollY - lastPaintedScrollY.
90
- __ui_nodes[si].lastPaintedScrollY = __ui_nodes[si].scrollY;
91
- // Push the canvas to the draw target at the viewport position (the
92
- // framebuffer when active, else the display directly).
93
- ui_display_set_target(drawTarget);
94
- ui_push_canvas_rect(bufferedScrollCanvas,
95
- bufferedScrollVX, bufferedScrollVY,
96
- vw, vh);
97
- }
98
-
99
- // Draw a scroll container's scrollbar directly on the display (Mode C).
100
- static inline void ui_draw_scrollbar_direct(int16_t si, int16_t vox, int16_t voy) {
101
- if (si < 0 || si >= static_cast<int16_t>(__ui_node_count)) return;
102
- int16_t vw = __ui_nodes[si].box.w;
103
- int16_t vh = __ui_nodes[si].box.h;
104
- if (__ui_nodes[si].contentHeight <= vh) return;
105
- int16_t tx = vox + vw - 4;
106
- uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
107
- if (thumbH < 8) thumbH = 8;
108
- // Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
109
- // viewport (which would cast a negative value to a huge uint32_t and place the
110
- // thumb off-screen). Matches the guard in ui_render_scroll_bands.
111
- if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
112
- int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
113
- uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
114
- UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
115
- // Full 4px gutter track (see the Mode B variant above).
116
- ui_display_fill_rect(tx, voy, 4, vh, dimFg);
117
- ui_display_fill_rect(tx, static_cast<int16_t>(voy + thumbY), 3, thumbH, __ui_nodes[si].fg);
118
- }
119
-
5
+ return `
6
+ static inline void ui_push_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
7
+ if (!canvas || !display_canvasBuffer(canvas)) return;
8
+ UI_COLOR_T* pixels = display_canvasBuffer(canvas);
9
+ int16_t stride = display_canvasWidth(canvas);
10
+ // The canvas is viewport-sized: buffer row 0 = the first row of the viewport.
11
+ // The display destination is (x, y) but the source buffer starts at (0, 0).
12
+ // When a full-screen framebuffer is the active draw target, composite into it
13
+ // via __ui_gfx (so the single bulk push at frame end captures everything).
14
+ // Otherwise write straight to the display in one SPI transaction.
15
+ if (__ui_fb) {
16
+ // The framebuffer is the composition target. Track this publish so a
17
+ // geometry repair or scroll-canvas repair that does not leave a node dirty
18
+ // still reaches the panel during the frame-end flush.
19
+ ui_fb_add_rect(x, y, w, h);
20
+ if (w == stride) {
21
+ ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
22
+ return;
23
+ }
24
+ for (int16_t row = 0; row < h; row++) {
25
+ ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
26
+ }
27
+ return;
28
+ }
29
+ display_startWrite();
30
+ display_setAddrWindow(x, y, w, h);
31
+ if (w == stride) {
32
+ // Full-width: contiguous in buffer, single write.
33
+ display_writePixels(pixels, static_cast<uint32_t>(w) * h);
34
+ display_endWrite();
35
+ return;
36
+ }
37
+ for (int16_t row = 0; row < h; row++) {
38
+ display_writePixels(pixels + static_cast<int32_t>(row) * stride, w);
39
+ }
40
+ display_endWrite();
41
+ }
42
+
43
+ static inline void ui_draw_canvas_rect(CuttlefishCanvas16* canvas, int16_t x, int16_t y, int16_t w, int16_t h) {
44
+ if (!canvas || !display_canvasBuffer(canvas)) return;
45
+ UI_COLOR_T* pixels = display_canvasBuffer(canvas);
46
+ int16_t stride = display_canvasWidth(canvas);
47
+ if (w == stride && h == display_canvasHeight(canvas)) {
48
+ ui_display_draw_rgb_bitmap(x, y, pixels, w, h);
49
+ return;
50
+ }
51
+ for (int16_t row = 0; row < h; row++) {
52
+ ui_display_draw_rgb_bitmap(x, y + row, pixels + static_cast<int32_t>(row) * stride, w, 1);
53
+ }
54
+ }
55
+
56
+ static inline void ui_push_buffered_scroll_canvas(CuttlefishCanvas16* bufferedScrollCanvas,
57
+ CuttlefishCanvas16* bufferedScrollRepaintCanvas,
58
+ int16_t bufferedScrollNode,
59
+ int16_t bufferedScrollVX,
60
+ int16_t bufferedScrollVY,
61
+ int16_t bufferedScrollRepaintY,
62
+ int16_t bufferedScrollRepaintH,
63
+ CuttlefishDisplayTarget* drawTarget) {
64
+ if (bufferedScrollNode < 0 || !bufferedScrollCanvas || !display_canvasBuffer(bufferedScrollCanvas)) return;
65
+ // Draw scrollbar into the canvas (canvas-local coords: 0,0 = viewport top-left).
66
+ ui_display_set_target(bufferedScrollCanvas);
67
+ int16_t si = bufferedScrollNode;
68
+ int16_t vw = __ui_nodes[si].box.w;
69
+ int16_t vh = __ui_nodes[si].box.h;
70
+ if (bufferedScrollRepaintCanvas && bufferedScrollRepaintH > 0) {
71
+ ui_draw_canvas_rect(bufferedScrollRepaintCanvas, 0, bufferedScrollRepaintY, vw, bufferedScrollRepaintH);
72
+ }
73
+ int16_t tx = vw - 4;
74
+ uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
75
+ if (thumbH < 8) thumbH = 8;
76
+ // Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
77
+ // viewport (which would cast a negative value to a huge uint32_t and place the
78
+ // thumb off-screen). Matches the guard in ui_render_scroll_bands.
79
+ if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
80
+ int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
81
+ uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
82
+ UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
83
+ // Track covers the FULL reserved gutter (4px) so child decorations that
84
+ // poke past the content area (e.g. outset shadows, offset up to +6px)
85
+ // can't leave 1px ticks in the uncovered edge column. Thumb stays 3px.
86
+ ui_display_fill_rect(tx, 0, 4, vh, dimFg);
87
+ ui_display_fill_rect(tx, thumbY, 3, thumbH, __ui_nodes[si].fg);
88
+ // Record the scrollY this canvas now reflects, so the next scroll frame can
89
+ // compute its shift delta (Mode B) from scrollY - lastPaintedScrollY.
90
+ __ui_nodes[si].lastPaintedScrollY = __ui_nodes[si].scrollY;
91
+ // Push the canvas to the draw target at the viewport position (the
92
+ // framebuffer when active, else the display directly).
93
+ ui_display_set_target(drawTarget);
94
+ ui_push_canvas_rect(bufferedScrollCanvas,
95
+ bufferedScrollVX, bufferedScrollVY,
96
+ vw, vh);
97
+ }
98
+
99
+ // Draw a scroll container's scrollbar directly on the display (Mode C).
100
+ static inline void ui_draw_scrollbar_direct(int16_t si, int16_t vox, int16_t voy) {
101
+ if (si < 0 || si >= static_cast<int16_t>(__ui_node_count)) return;
102
+ int16_t vw = __ui_nodes[si].box.w;
103
+ int16_t vh = __ui_nodes[si].box.h;
104
+ if (__ui_nodes[si].contentHeight <= vh) return;
105
+ int16_t tx = vox + vw - 4;
106
+ uint16_t thumbH = static_cast<uint32_t>(vh) * vh / __ui_nodes[si].contentHeight;
107
+ if (thumbH < 8) thumbH = 8;
108
+ // Clamp to vh so (vh - thumbH) below can't underflow on a degenerate sub-8px
109
+ // viewport (which would cast a negative value to a huge uint32_t and place the
110
+ // thumb off-screen). Matches the guard in ui_render_scroll_bands.
111
+ if (thumbH > static_cast<uint16_t>(vh)) thumbH = static_cast<uint16_t>(vh);
112
+ int16_t maxScroll = __ui_nodes[si].contentHeight - vh;
113
+ uint16_t thumbY = static_cast<uint32_t>(vh - thumbH) * __ui_nodes[si].scrollY / (maxScroll > 0 ? maxScroll : 1);
114
+ UI_COLOR_T dimFg = (UI_COLOR_T)((__ui_nodes[si].fg >> 1) & UI_DIM_MASK);
115
+ // Full 4px gutter track (see the Mode B variant above).
116
+ ui_display_fill_rect(tx, voy, 4, vh, dimFg);
117
+ ui_display_fill_rect(tx, static_cast<int16_t>(voy + thumbY), 3, thumbH, __ui_nodes[si].fg);
118
+ }
119
+
120
120
  // Per-node dirty marker (called by press handlers and binding evaluation).`;
121
121
  }