@typecad/ui 0.1.0-alpha.2 → 1.0.0-alpha.11

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 (86) hide show
  1. package/README.md +7 -1
  2. package/dist/engine-index.js +2 -2
  3. package/dist/preview/host-ui-runtime.d.ts +1 -0
  4. package/dist/preview/host-ui-runtime.js +56 -11
  5. package/dist/ui-engine/font-assets.d.ts +8 -0
  6. package/dist/ui-engine/font-assets.js +13 -0
  7. package/dist/ui-engine/layout-engine.js +19 -6
  8. package/dist/ui-engine/model.d.ts +1 -1
  9. package/dist/ui-engine/model.js +10 -4
  10. package/dist/ui-engine/runtime-header/antialiasing.js +52 -32
  11. package/dist/ui-engine/runtime-header/blend-bodies.js +4 -4
  12. package/dist/ui-engine/runtime-header/canvas-helpers.js +42 -69
  13. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +22 -10
  14. package/dist/ui-engine/runtime-header/color-mono-refresh.js +10 -10
  15. package/dist/ui-engine/runtime-header/cuttlefish-gfx.d.ts +1 -0
  16. package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +563 -0
  17. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +26 -10
  18. package/dist/ui-engine/runtime-header/display-shim.js +8 -8
  19. package/dist/ui-engine/runtime-header/forward-decls.js +52 -10
  20. package/dist/ui-engine/runtime-header/image-drawing.js +154 -84
  21. package/dist/ui-engine/runtime-header/init-press-input.js +20 -5
  22. package/dist/ui-engine/runtime-header/keyboard.js +62 -18
  23. package/dist/ui-engine/runtime-header/node-decoration.js +8 -9
  24. package/dist/ui-engine/runtime-header/node-draw-body.d.ts +1 -0
  25. package/dist/ui-engine/runtime-header/node-draw-body.js +1402 -0
  26. package/dist/ui-engine/runtime-header/paint-order-coords.js +48 -18
  27. package/dist/ui-engine/runtime-header/paint-rects-repair.js +38 -17
  28. package/dist/ui-engine/runtime-header/scroll-physics.js +15 -18
  29. package/dist/ui-engine/runtime-header/state-bindings-nav.js +23 -13
  30. package/dist/ui-engine/runtime-header/structs.js +12 -10
  31. package/dist/ui-engine/runtime-header/text-rendering.js +132 -71
  32. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +29 -10
  33. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +336 -749
  34. package/dist/ui-engine/runtime-header/tick/flush-phase.js +7 -3
  35. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +19 -3
  36. package/dist/ui-engine/runtime-header/tick/transitions-phase.js +31 -24
  37. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +46 -41
  38. package/dist/ui-engine/runtime-header/types-defines.js +23 -3
  39. package/dist/ui-engine/runtime-header.d.ts +10 -1
  40. package/dist/ui-engine/runtime-header.js +7 -1
  41. package/dist/ui-engine/transpile-ui.js +2 -2
  42. package/dist/ui-engine/ua-stylesheet.js +46 -3
  43. package/dist/ui-engine/ui-lowering.d.ts +1 -3
  44. package/dist/ui-engine/ui-lowering.js +18 -24
  45. package/dist/ui-engine/ui-registry.js +1 -6
  46. package/package.json +2 -2
  47. package/src/engine-index.ts +2 -2
  48. package/src/preview/host-ui-runtime.ts +56 -12
  49. package/src/ui-engine/font-assets.ts +13 -0
  50. package/src/ui-engine/layout-engine.ts +19 -6
  51. package/src/ui-engine/model.ts +10 -5
  52. package/src/ui-engine/runtime-header/antialiasing.ts +52 -32
  53. package/src/ui-engine/runtime-header/blend-bodies.ts +4 -4
  54. package/src/ui-engine/runtime-header/canvas-helpers.ts +42 -69
  55. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +22 -10
  56. package/src/ui-engine/runtime-header/color-mono-refresh.ts +10 -10
  57. package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +566 -0
  58. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +26 -10
  59. package/src/ui-engine/runtime-header/display-shim.ts +8 -8
  60. package/src/ui-engine/runtime-header/forward-decls.ts +52 -10
  61. package/src/ui-engine/runtime-header/image-drawing.ts +154 -84
  62. package/src/ui-engine/runtime-header/init-press-input.ts +20 -5
  63. package/src/ui-engine/runtime-header/keyboard.ts +62 -18
  64. package/src/ui-engine/runtime-header/node-decoration.ts +8 -9
  65. package/src/ui-engine/runtime-header/node-draw-body.ts +1402 -0
  66. package/src/ui-engine/runtime-header/paint-order-coords.ts +48 -18
  67. package/src/ui-engine/runtime-header/paint-rects-repair.ts +38 -17
  68. package/src/ui-engine/runtime-header/scroll-physics.ts +15 -18
  69. package/src/ui-engine/runtime-header/state-bindings-nav.ts +23 -13
  70. package/src/ui-engine/runtime-header/structs.ts +12 -10
  71. package/src/ui-engine/runtime-header/text-rendering.ts +132 -71
  72. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +29 -10
  73. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +336 -749
  74. package/src/ui-engine/runtime-header/tick/flush-phase.ts +7 -3
  75. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +19 -3
  76. package/src/ui-engine/runtime-header/tick/transitions-phase.ts +31 -24
  77. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +46 -41
  78. package/src/ui-engine/runtime-header/types-defines.ts +23 -3
  79. package/src/ui-engine/runtime-header.ts +17 -1
  80. package/src/ui-engine/transpile-ui.ts +2 -2
  81. package/src/ui-engine/ua-stylesheet.ts +46 -3
  82. package/src/ui-engine/ui-lowering.ts +19 -26
  83. package/src/ui-engine/ui-registry.ts +1 -7
  84. package/dist/ui-engine/scroll-memory-diagnostics.d.ts +0 -16
  85. package/dist/ui-engine/scroll-memory-diagnostics.js +0 -58
  86. package/src/ui-engine/scroll-memory-diagnostics.ts +0 -79
@@ -22,7 +22,6 @@ import type { CSSRule, CSSProperty } from "./css-parser.js";
22
22
  import type { DisplayProfile } from "@typecad/cuttlefish/api/shared";
23
23
  import type { UIFontAssetModel } from "./font-assets.js";
24
24
  import type { Diagnostic } from "@typecad/cuttlefish/api/shared";
25
- import { analyzeScrollMemory } from "./scroll-memory-diagnostics.js";
26
25
  import { getListBindings } from "@typecad/cuttlefish/ir/transformers/ui-reactive";
27
26
 
28
27
  export interface LoweredUI {
@@ -40,8 +39,6 @@ export interface LoweredUI {
40
39
  imageTables: string;
41
40
  /** C++ keyframe data arrays + animation table. */
42
41
  keyframeTables: string;
43
- /** Compile-time warnings for scroll viewports that exceed the canvas budget. */
44
- scrollMemoryDiagnostics: Diagnostic[];
45
42
  /** Layout-time diagnostics (viewport overflow, text overflow). Populated by
46
43
  * lowerOnMount after the boxes are computed; empty until then. */
47
44
  diagnostics: Diagnostic[];
@@ -62,16 +59,14 @@ export function lowerUIToCpp(
62
59
  allScreens: StyledNode[] = [],
63
60
  imageAssetIds: Map<string, number> = new Map(),
64
61
  keyframeSets: KeyframeSetModel[] = [],
65
- scrollCanvasBudgetBytes?: number,
66
62
  ): LoweredUI {
67
63
  void storage;
68
64
  const model = lowerUIToModel(root, boxes, colorFormat, display, fontAssets, allScreens, imageAssetIds, keyframeSets);
69
- const scrollMemoryDiagnostics = analyzeScrollMemory(model.nodes, scrollCanvasBudgetBytes);
70
65
 
71
66
  // Tables are mutable RAM (ui_tick updates bg/dirty/elapsed/active each
72
67
  // frame), so no PROGMEM/flash storage keyword — those imply read-only.
73
68
  const fontTables = emitFontTables(model);
74
- const nodeTable = emitNodeTable(model) + "\n" + emitScrollNodeIdLookup(model);
69
+ const nodeTable = emitNodeTable(model);
75
70
  const transitionTable = emitTransitionTable(model);
76
71
  const typeDecl = emitTypeDecl(root);
77
72
 
@@ -111,7 +106,7 @@ export function lowerUIToCpp(
111
106
  const screenCount = model.nodes.length > 0 ? Math.max(...model.nodes.map(n => n.screenId)) + 1 : 1;
112
107
  const imageTables = "const UIImage __ui_images[] = {};\nconst uint16_t __ui_image_count = 0;";
113
108
  const keyframeTables = emitKeyframeTables(model);
114
- return { fontTables, nodeTable, transitionTable, typeDecl, keyboardLoaders, keyboardDispatch, screenCount, imageTables, keyframeTables, scrollMemoryDiagnostics, diagnostics: [] };
109
+ return { fontTables, nodeTable, transitionTable, typeDecl, keyboardLoaders, keyboardDispatch, screenCount, imageTables, keyframeTables, diagnostics: [] };
115
110
  }
116
111
 
117
112
  function sanitizedId(id: string): string {
@@ -199,7 +194,22 @@ function emitKeyboardLoader(name: string, kb: KeyboardTemplate, rules: CSSRule[]
199
194
 
200
195
  /** Emit one key's keys[] + styles[] lines, resolving CSS classes to colors. */
201
196
  function emitKeyLine(key: UIKeyTemplate, kbClasses: string[] | undefined, rules: CSSRule[], colorFormat: ColorFormat): string {
202
- const chEsc = key.ch === "\\" ? "\\\\" : key.ch === "'" ? "\\'" : key.ch;
197
+ // The C++ UIKey.ch field is a single `char`. Special keys (shift=1, bs=2,
198
+ // ok=3, page=4) use the `special` field for their behavior and label — the
199
+ // `ch` value is never read. Their label strings ("OK", "123", "⇧", "⌫") are
200
+ // multi-char or multi-byte and would overflow `char` if emitted as literals.
201
+ // Emit a space placeholder for any key whose ch isn't a single ASCII byte.
202
+ const isSingleAscii = key.special === 0 && key.ch.length === 1 && key.ch.charCodeAt(0) < 128;
203
+ if (key.special === 0 && !isSingleAscii) {
204
+ console.warn(
205
+ `[cuttlefish] keyboard key ch="${key.ch}" is non-ASCII and will be emitted as ` +
206
+ `a space placeholder. The native C++ UIKey.ch field is a single char and ` +
207
+ `cannot hold multi-byte codepoints. Use a custom keyboard layout or the ` +
208
+ `Adafruit path for full Unicode key labels.`,
209
+ );
210
+ }
211
+ const chRaw = isSingleAscii ? key.ch : ' ';
212
+ const chEsc = chRaw === "\\" ? "\\\\" : chRaw === "'" ? "\\'" : chRaw;
203
213
  const style = resolveKeyStyle(key.classes, kbClasses, rules);
204
214
  const bg = style.background ? resolveColor(style.background, colorFormat) : DEFAULT_KEY_BG;
205
215
  const fg = style.color ? resolveColor(style.color, colorFormat) : DEFAULT_KEY_FG;
@@ -219,6 +229,7 @@ function cppKind(kind: UINodeModel["kind"]): string {
219
229
  case "img": return "NODE_IMG";
220
230
  case "list": return "NODE_LIST";
221
231
  case "canvas": return "NODE_CANVAS";
232
+ case "select": return "NODE_SELECT";
222
233
  case "text": return "NODE_TEXT";
223
234
  }
224
235
  }
@@ -278,24 +289,6 @@ function emitFontTables(model: UIProgram): string {
278
289
  return lines.join("\n");
279
290
  }
280
291
 
281
- function emitScrollNodeIdLookup(model: UIProgram): string {
282
- const cases: string[] = [];
283
- for (const n of model.nodes) {
284
- if (!n.scrollable || n.contentHeight <= n.box.h || !n.id) continue;
285
- const id = n.id.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
286
- cases.push(` if (idx == ${n.index}) return "${id}";`);
287
- }
288
- if (cases.length === 0) {
289
- return "static inline const char* __ui_scroll_node_id(uint16_t idx) { (void)idx; return nullptr; }";
290
- }
291
- return [
292
- "static inline const char* __ui_scroll_node_id(uint16_t idx) {",
293
- ...cases,
294
- " return nullptr;",
295
- "}",
296
- ].join("\n");
297
- }
298
-
299
292
  function emitNodeTable(model: UIProgram): string {
300
293
  // Map node index → list binding so virtualized nodes carry their count/item/
301
294
  // tap function pointers on-node (no UIListBinding side table at runtime).
@@ -28,7 +28,6 @@ import { selectEngine } from "./select-engine.js";
28
28
  import { measure, measureWithFonts, Box } from "./layout-engine.js";
29
29
  import { lowerUIToCpp, LoweredUI } from "./ui-lowering.js";
30
30
  import { getDisplayProfile } from "@typecad/cuttlefish/stores/display-profile-store";
31
- import { resolveScrollConfig } from "@typecad/cuttlefish/api/shared";
32
31
  import { buildUIFontAssets } from "./font-assets.js";
33
32
  import type { UIFontAssetModel } from "./font-assets.js";
34
33
  import { emitImageTables, loadImageAssets } from "./image-assets.js";
@@ -220,12 +219,7 @@ export function lowerOnMount(htmlPath: string, opts: LowerOptions): LoweredUI {
220
219
 
221
220
  const imageAssets = loadImageAssets(allStyled.length > 0 ? allStyled : [mod.styled], path.dirname(abs));
222
221
 
223
- const scrollBudget = resolveScrollConfig(getDisplayProfile(), { buildTarget: getDisplayProfile()._buildTarget }).scrollCanvasBudgetBytes;
224
- const result = lowerUIToCpp(mod.styled, allBoxes, opts.colorFormat, opts.storage, mod.keyboards, mod.rules, getDisplayProfile(), mod.fontAssets, allStyled, imageAssets.nodeIdToAssetIndex, keyframeSets, scrollBudget);
225
-
226
- for (const d of result.scrollMemoryDiagnostics) {
227
- mod.mountDiagnostics.push({ ...d, source: d.source ?? path.basename(mod.htmlPath) });
228
- }
222
+ const result = lowerUIToCpp(mod.styled, allBoxes, opts.colorFormat, opts.storage, mod.keyboards, mod.rules, getDisplayProfile(), mod.fontAssets, allStyled, imageAssets.nodeIdToAssetIndex, keyframeSets);
229
223
 
230
224
  // Layout diagnostics: viewport-overflow (node bottom past the viewport) and
231
225
  // text-overflow (node right edge past its parent). Both reference the node by
@@ -1,16 +0,0 @@
1
- import type { Diagnostic } from "@typecad/cuttlefish/api/shared";
2
- import type { UINodeModel } from "./model.js";
3
- import { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES } from "@typecad/cuttlefish/api/shared";
4
- export { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES };
5
- export interface ScrollMemoryIssue {
6
- nodeIndex: number;
7
- id?: string;
8
- width: number;
9
- height: number;
10
- canvasBytes: number;
11
- budgetBytes: number;
12
- }
13
- /** Collect overflow scroll nodes whose viewport canvas exceeds the budget. */
14
- export declare function collectScrollMemoryIssues(nodes: UINodeModel[], budgetBytes?: number): ScrollMemoryIssue[];
15
- /** Emit transpile warnings for scroll viewports that cannot fit a Mode B canvas. */
16
- export declare function analyzeScrollMemory(nodes: UINodeModel[], budgetBytes?: number): Diagnostic[];
@@ -1,58 +0,0 @@
1
- // Compile-time scroll viewport memory analysis. Overflow scroll containers
2
- // need a viewport-sized RGB565 canvas (~w×h×2 bytes) for smooth Mode B
3
- // scrolling; when that exceeds the device budget, runtime falls back to
4
- // Mode C strip scroll or freezes until memory is available.
5
- import { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES } from "@typecad/cuttlefish/api/shared";
6
- export { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES };
7
- /** Collect overflow scroll nodes whose viewport canvas exceeds the budget. */
8
- export function collectScrollMemoryIssues(nodes, budgetBytes = DEFAULT_SCROLL_CANVAS_BUDGET_BYTES) {
9
- const issues = [];
10
- for (const node of nodes) {
11
- if (!node.scrollable)
12
- continue;
13
- if (node.contentHeight <= node.box.h)
14
- continue;
15
- const canvasBytes = node.box.w * node.box.h * 2;
16
- if (canvasBytes <= budgetBytes)
17
- continue;
18
- issues.push({
19
- nodeIndex: node.index,
20
- id: node.id,
21
- width: node.box.w,
22
- height: node.box.h,
23
- canvasBytes,
24
- budgetBytes,
25
- });
26
- }
27
- return issues;
28
- }
29
- function formatKb(bytes) {
30
- return `${Math.round(bytes / 1024)} KB`;
31
- }
32
- function minimizationHint(issue) {
33
- const maxArea = Math.floor(issue.budgetBytes / 2);
34
- const maxHAtWidth = issue.width > 0 ? Math.floor(maxArea / issue.width) : 0;
35
- const maxWAtHeight = issue.height > 0 ? Math.floor(maxArea / issue.height) : 0;
36
- return [
37
- `Reduce the scroll viewport in CSS (e.g. height ≤ ${maxHAtWidth}px at ${issue.width}px width, or width ≤ ${maxWAtHeight}px at ${issue.height}px height).`,
38
- "Trim fonts, images, or inactive screens to free heap for canvas allocation.",
39
- "Use PSRAM or a board with more SRAM if you need a larger scroll viewport.",
40
- "Adjust display.scroll.scrollCanvasBudgetBytes in cuttlefish.config.ts only if your target has more headroom (runtime allocation may still fail).",
41
- ].join("\n");
42
- }
43
- /** Emit transpile warnings for scroll viewports that cannot fit a Mode B canvas. */
44
- export function analyzeScrollMemory(nodes, budgetBytes = DEFAULT_SCROLL_CANVAS_BUDGET_BYTES) {
45
- return collectScrollMemoryIssues(nodes, budgetBytes).map((issue) => {
46
- const label = issue.id ? `#${issue.id}` : `node ${issue.nodeIndex}`;
47
- return {
48
- severity: "warning",
49
- code: "scroll-canvas-memory",
50
- message: `Scroll viewport ${label} (${issue.width}×${issue.height}px) needs ${issue.canvasBytes} bytes ` +
51
- `(${formatKb(issue.canvasBytes)}) for accurate smooth scrolling, exceeding the ` +
52
- `${issue.budgetBytes}-byte budget (~${formatKb(issue.budgetBytes)}). ` +
53
- "Rendering will fall back to Mode C strip scroll or freeze updates until memory is available — " +
54
- "scrolling may tear, stutter, or skip frames.",
55
- hint: minimizationHint(issue),
56
- };
57
- });
58
- }
@@ -1,79 +0,0 @@
1
- // Compile-time scroll viewport memory analysis. Overflow scroll containers
2
- // need a viewport-sized RGB565 canvas (~w×h×2 bytes) for smooth Mode B
3
- // scrolling; when that exceeds the device budget, runtime falls back to
4
- // Mode C strip scroll or freezes until memory is available.
5
-
6
- import type { Diagnostic } from "@typecad/cuttlefish/api/shared";
7
- import type { UINodeModel } from "./model.js";
8
- import { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES } from "@typecad/cuttlefish/api/shared";
9
-
10
- export { DEFAULT_SCROLL_CANVAS_BUDGET_BYTES };
11
-
12
- export interface ScrollMemoryIssue {
13
- nodeIndex: number;
14
- id?: string;
15
- width: number;
16
- height: number;
17
- canvasBytes: number;
18
- budgetBytes: number;
19
- }
20
-
21
- /** Collect overflow scroll nodes whose viewport canvas exceeds the budget. */
22
- export function collectScrollMemoryIssues(
23
- nodes: UINodeModel[],
24
- budgetBytes: number = DEFAULT_SCROLL_CANVAS_BUDGET_BYTES,
25
- ): ScrollMemoryIssue[] {
26
- const issues: ScrollMemoryIssue[] = [];
27
- for (const node of nodes) {
28
- if (!node.scrollable) continue;
29
- if (node.contentHeight <= node.box.h) continue;
30
- const canvasBytes = node.box.w * node.box.h * 2;
31
- if (canvasBytes <= budgetBytes) continue;
32
- issues.push({
33
- nodeIndex: node.index,
34
- id: node.id,
35
- width: node.box.w,
36
- height: node.box.h,
37
- canvasBytes,
38
- budgetBytes,
39
- });
40
- }
41
- return issues;
42
- }
43
-
44
- function formatKb(bytes: number): string {
45
- return `${Math.round(bytes / 1024)} KB`;
46
- }
47
-
48
- function minimizationHint(issue: ScrollMemoryIssue): string {
49
- const maxArea = Math.floor(issue.budgetBytes / 2);
50
- const maxHAtWidth = issue.width > 0 ? Math.floor(maxArea / issue.width) : 0;
51
- const maxWAtHeight = issue.height > 0 ? Math.floor(maxArea / issue.height) : 0;
52
- return [
53
- `Reduce the scroll viewport in CSS (e.g. height ≤ ${maxHAtWidth}px at ${issue.width}px width, or width ≤ ${maxWAtHeight}px at ${issue.height}px height).`,
54
- "Trim fonts, images, or inactive screens to free heap for canvas allocation.",
55
- "Use PSRAM or a board with more SRAM if you need a larger scroll viewport.",
56
- "Adjust display.scroll.scrollCanvasBudgetBytes in cuttlefish.config.ts only if your target has more headroom (runtime allocation may still fail).",
57
- ].join("\n");
58
- }
59
-
60
- /** Emit transpile warnings for scroll viewports that cannot fit a Mode B canvas. */
61
- export function analyzeScrollMemory(
62
- nodes: UINodeModel[],
63
- budgetBytes: number = DEFAULT_SCROLL_CANVAS_BUDGET_BYTES,
64
- ): Diagnostic[] {
65
- return collectScrollMemoryIssues(nodes, budgetBytes).map((issue) => {
66
- const label = issue.id ? `#${issue.id}` : `node ${issue.nodeIndex}`;
67
- return {
68
- severity: "warning" as const,
69
- code: "scroll-canvas-memory",
70
- message:
71
- `Scroll viewport ${label} (${issue.width}×${issue.height}px) needs ${issue.canvasBytes} bytes ` +
72
- `(${formatKb(issue.canvasBytes)}) for accurate smooth scrolling, exceeding the ` +
73
- `${issue.budgetBytes}-byte budget (~${formatKb(issue.budgetBytes)}). ` +
74
- "Rendering will fall back to Mode C strip scroll or freeze updates until memory is available — " +
75
- "scrolling may tear, stutter, or skip frames.",
76
- hint: minimizationHint(issue),
77
- };
78
- });
79
- }