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

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 (135) hide show
  1. package/README.md +21 -1
  2. package/assets/fonts/dejavu/DejaVuSans-Bold.ttf +0 -0
  3. package/assets/fonts/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  4. package/assets/fonts/dejavu/DejaVuSans-Oblique.ttf +0 -0
  5. package/assets/fonts/dejavu/DejaVuSans.ttf +0 -0
  6. package/assets/fonts/dejavu/DejaVuSansMono-Bold.ttf +0 -0
  7. package/assets/fonts/dejavu/DejaVuSansMono.ttf +0 -0
  8. package/assets/fonts/dejavu/LICENSE +187 -0
  9. package/assets/fonts/dejavu/README.md +49 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +74 -0
  12. package/dist/engine-index.js +2 -0
  13. package/dist/preview/build-program.d.ts +7 -1
  14. package/dist/preview/build-program.js +94 -15
  15. package/dist/preview/host-gfx.d.ts +3 -0
  16. package/dist/preview/host-gfx.js +30 -3
  17. package/dist/preview/host-ui-runtime.d.ts +115 -0
  18. package/dist/preview/host-ui-runtime.js +928 -46
  19. package/dist/ui-engine/block-layout.js +25 -11
  20. package/dist/ui-engine/color.d.ts +6 -0
  21. package/dist/ui-engine/color.js +80 -2
  22. package/dist/ui-engine/compat-report.d.ts +8 -0
  23. package/dist/ui-engine/compat-report.js +124 -0
  24. package/dist/ui-engine/css-imports.d.ts +4 -0
  25. package/dist/ui-engine/css-imports.js +70 -0
  26. package/dist/ui-engine/css-parser.js +58 -14
  27. package/dist/ui-engine/default-font.d.ts +25 -0
  28. package/dist/ui-engine/default-font.js +80 -0
  29. package/dist/ui-engine/font-assets.d.ts +2 -2
  30. package/dist/ui-engine/font-assets.js +22 -5
  31. package/dist/ui-engine/html-parser.d.ts +4 -0
  32. package/dist/ui-engine/html-parser.js +241 -26
  33. package/dist/ui-engine/image-assets.d.ts +8 -1
  34. package/dist/ui-engine/image-assets.js +35 -2
  35. package/dist/ui-engine/image-decode.d.ts +22 -0
  36. package/dist/ui-engine/image-decode.js +194 -0
  37. package/dist/ui-engine/inline-parser.js +1 -1
  38. package/dist/ui-engine/layout-engine.d.ts +19 -0
  39. package/dist/ui-engine/layout-engine.js +59 -3
  40. package/dist/ui-engine/model.d.ts +18 -0
  41. package/dist/ui-engine/model.js +76 -8
  42. package/dist/ui-engine/runtime-header/blend-bodies.js +29 -9
  43. package/dist/ui-engine/runtime-header/canvas-helpers.js +5 -2
  44. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -111
  45. package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +21 -0
  46. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -179
  47. package/dist/ui-engine/runtime-header/forward-decls.js +215 -167
  48. package/dist/ui-engine/runtime-header/init-press-input.js +138 -135
  49. package/dist/ui-engine/runtime-header/keyboard.js +682 -380
  50. package/dist/ui-engine/runtime-header/node-draw-body.js +1656 -1377
  51. package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -214
  52. package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -568
  53. package/dist/ui-engine/runtime-header/scroll-physics.js +25 -1
  54. package/dist/ui-engine/runtime-header/state-bindings-nav.js +9 -0
  55. package/dist/ui-engine/runtime-header/structs.js +228 -216
  56. package/dist/ui-engine/runtime-header/text-rendering.js +888 -770
  57. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -116
  58. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +897 -633
  59. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -25
  60. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +79 -23
  61. package/dist/ui-engine/runtime-header/types-defines.js +110 -93
  62. package/dist/ui-engine/shadcn-kit.d.ts +1 -0
  63. package/dist/ui-engine/shadcn-kit.js +502 -0
  64. package/dist/ui-engine/style-resolver.d.ts +4 -0
  65. package/dist/ui-engine/style-resolver.js +58 -6
  66. package/dist/ui-engine/transpile-ui.js +12 -5
  67. package/dist/ui-engine/ua-stylesheet.d.ts +22 -1
  68. package/dist/ui-engine/ua-stylesheet.js +119 -32
  69. package/dist/ui-engine/ui-lowering.js +37 -3
  70. package/dist/ui-engine/ui-registry.js +32 -4
  71. package/dist/ui-engine/yoga-layout.js +57 -52
  72. package/dist/wizard/config-writer.d.ts +49 -0
  73. package/dist/wizard/config-writer.js +366 -0
  74. package/dist/wizard/display-catalog.d.ts +75 -0
  75. package/dist/wizard/display-catalog.js +207 -0
  76. package/dist/wizard/index.d.ts +7 -0
  77. package/dist/wizard/index.js +10 -0
  78. package/dist/wizard/integration-wizard.d.ts +15 -0
  79. package/dist/wizard/integration-wizard.js +486 -0
  80. package/dist/wizard/prompts.d.ts +32 -0
  81. package/dist/wizard/prompts.js +95 -0
  82. package/dist/wizard/starter-ui.d.ts +1 -0
  83. package/dist/wizard/starter-ui.js +56 -0
  84. package/package.json +18 -5
  85. package/src/cli.ts +87 -0
  86. package/src/engine-index.ts +53 -51
  87. package/src/preview/build-program.ts +810 -734
  88. package/src/preview/host-gfx.ts +30 -3
  89. package/src/preview/host-ui-runtime.ts +4133 -3289
  90. package/src/ui-engine/block-layout.ts +24 -11
  91. package/src/ui-engine/color.ts +77 -2
  92. package/src/ui-engine/compat-report.ts +139 -0
  93. package/src/ui-engine/css-imports.ts +69 -0
  94. package/src/ui-engine/css-parser.ts +64 -15
  95. package/src/ui-engine/default-font.ts +95 -0
  96. package/src/ui-engine/font-assets.ts +545 -525
  97. package/src/ui-engine/html-parser.ts +615 -397
  98. package/src/ui-engine/image-assets.ts +37 -2
  99. package/src/ui-engine/image-decode.ts +244 -0
  100. package/src/ui-engine/inline-parser.ts +1 -1
  101. package/src/ui-engine/layout-engine.ts +61 -3
  102. package/src/ui-engine/model.ts +1317 -1230
  103. package/src/ui-engine/runtime-header/blend-bodies.ts +29 -9
  104. package/src/ui-engine/runtime-header/canvas-helpers.ts +5 -2
  105. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -117
  106. package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +21 -0
  107. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -185
  108. package/src/ui-engine/runtime-header/forward-decls.ts +227 -179
  109. package/src/ui-engine/runtime-header/init-press-input.ts +144 -141
  110. package/src/ui-engine/runtime-header/keyboard.ts +689 -386
  111. package/src/ui-engine/runtime-header/node-draw-body.ts +1681 -1402
  112. package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -220
  113. package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -574
  114. package/src/ui-engine/runtime-header/scroll-physics.ts +25 -1
  115. package/src/ui-engine/runtime-header/state-bindings-nav.ts +9 -0
  116. package/src/ui-engine/runtime-header/structs.ts +234 -222
  117. package/src/ui-engine/runtime-header/text-rendering.ts +894 -776
  118. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -122
  119. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -638
  120. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -31
  121. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +79 -23
  122. package/src/ui-engine/runtime-header/types-defines.ts +116 -99
  123. package/src/ui-engine/shadcn-kit.ts +503 -0
  124. package/src/ui-engine/style-resolver.ts +471 -416
  125. package/src/ui-engine/transpile-ui.ts +12 -5
  126. package/src/ui-engine/ua-stylesheet.ts +137 -31
  127. package/src/ui-engine/ui-lowering.ts +486 -452
  128. package/src/ui-engine/ui-registry.ts +556 -524
  129. package/src/ui-engine/yoga-layout.ts +307 -309
  130. package/src/wizard/config-writer.ts +404 -0
  131. package/src/wizard/display-catalog.ts +273 -0
  132. package/src/wizard/index.ts +38 -0
  133. package/src/wizard/integration-wizard.ts +619 -0
  134. package/src/wizard/prompts.ts +145 -0
  135. package/src/wizard/starter-ui.ts +58 -0
@@ -18,6 +18,9 @@ import { measureWithFonts, Box } from "./layout-engine.js";
18
18
  import { lowerUIToCpp, LoweredUI } from "./ui-lowering.js";
19
19
  import { buildUIFontAssets } from "./font-assets.js";
20
20
  import { buildKeyframeSets } from "./keyframes.js";
21
+ import { injectDefaultFontFaces } from "./default-font.js";
22
+ import { cssCompatDiagnostics } from "./compat-report.js";
23
+ import { expandCssImports } from "./css-imports.js";
21
24
 
22
25
  export interface TranspileUIOptions {
23
26
  colorFormat: "rgb565" | "mono";
@@ -28,11 +31,13 @@ export interface TranspileUIOptions {
28
31
 
29
32
  export function transpileUI(html: string, css: string, opts: TranspileUIOptions): LoweredUI {
30
33
  const tree = parseHtml(html);
31
- const rules = parseCss(css);
32
- const fontFaces = parseFontFaces(css);
33
- const rawKeyframes = parseKeyframes(css);
34
+ const cssBaseDir = path.resolve(opts.assetBaseDir ?? process.cwd());
35
+ const expandedCss = expandCssImports(css, cssBaseDir);
36
+ const rules = parseCss(expandedCss);
37
+ const fontFaces = injectDefaultFontFaces(parseFontFaces(expandedCss), opts.colorFormat);
38
+ const rawKeyframes = parseKeyframes(expandedCss);
34
39
  const styled = resolveStyles(tree, rules);
35
- const fontAssets = buildUIFontAssets(styled, fontFaces, path.resolve(opts.assetBaseDir ?? process.cwd()));
40
+ const fontAssets = buildUIFontAssets(styled, fontFaces, cssBaseDir);
36
41
 
37
42
  // Select layout engine: Yoga for flexbox, BlockLayout as fallback.
38
43
  const engine = selectEngine(styled);
@@ -41,5 +46,7 @@ export function transpileUI(html: string, css: string, opts: TranspileUIOptions)
41
46
 
42
47
  const keyframeSets = buildKeyframeSets(rawKeyframes, opts.colorFormat);
43
48
 
44
- return lowerUIToCpp(styled, boxes, opts.colorFormat, opts.storage, [], rules, undefined, fontAssets, [], new Map(), keyframeSets);
49
+ const result = lowerUIToCpp(styled, boxes, opts.colorFormat, opts.storage, [], rules, undefined, fontAssets, [], new Map(), keyframeSets);
50
+ result.diagnostics.push(...cssCompatDiagnostics([styled], fontAssets, opts.viewport, opts.colorFormat, "inline"));
51
+ return result;
45
52
  }
@@ -1,14 +1,91 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // UA (User-Agent) default stylesheet.
3
3
  // Applied before user CSS so elements have sensible built-in behavior without
4
- // requiring explicit CSS. User CSS overrides (cascade: last-wins).
5
- // Minimal and embedded-oriented, not a browser clone.
4
+ // requiring explicit CSS. Author CSS overrides it (cascade: the UA layer loses
5
+ // to any author rule; see style-resolver.ts).
6
+ //
7
+ // The sheet is display-anchored, like a browser's per-device-class defaults:
8
+ // the root type size (what a browser fixes at 16px on desktop) scales with the
9
+ // target display — 16px on large color panels, ~14px on 320×240 TFTs, ~12px on
10
+ // tiny panels — and every UA size derives from that root, so heading ratios
11
+ // and touch-target minimums stay web-familiar at every pixel budget.
12
+ // Monochrome displays additionally snap sizes to the stock font's buckets
13
+ // (only ~4 distinct rendered sizes exist there).
6
14
  // ---------------------------------------------------------------------------
7
15
 
8
16
  import { parseCss } from "./css-parser.js";
9
17
  import type { CSSRule } from "./css-parser.js";
18
+ import { getDisplayProfile } from "@typecad/cuttlefish/stores/display-profile-store";
19
+ import { DEFAULT_FONT_FAMILY, DEFAULT_MONO_FAMILY, defaultFontApplies } from "./default-font.js";
10
20
 
11
- const UA_CSS = `
21
+ /** The derived UA scale for one display profile. */
22
+ export interface UAScale {
23
+ /** Root (body text) size in px — the display-anchored "16px". */
24
+ root: number;
25
+ /** Monochrome display: stock bitmap font, quantized text sizes. */
26
+ mono: boolean;
27
+ /** Heading sizes h1..h6 in px. */
28
+ h: [number, number, number, number, number, number];
29
+ /** Vertical margin for headings and paragraphs (browser-like spacing). */
30
+ blockMargin: number;
31
+ /** Minimum touch-target height for buttons/selects/checks/radios. */
32
+ controlMinHeight: number;
33
+ /** Button/select vertical + horizontal padding. */
34
+ buttonPadV: number;
35
+ buttonPadH: number;
36
+ }
37
+
38
+ /** Derive the UA type scale from the display class. Height is the portrait
39
+ * dimension the profile reports; an unbound profile gets the color/large
40
+ * default (root 16 — the classic browser default). */
41
+ export function uaScaleFor(height: number | undefined, mono: boolean): UAScale {
42
+ if (mono) {
43
+ // Stock font buckets: ≤12 → size 1, 13–20 → 2, 21–28 → 3. Snap UA sizes
44
+ // to 12/20/28 so every declared size renders at a DISTINCT bucket.
45
+ const tall = (height ?? 128) >= 96;
46
+ return {
47
+ root: 12,
48
+ mono: true,
49
+ h: tall ? [28, 20, 20, 12, 12, 12] : [20, 20, 12, 12, 12, 12],
50
+ blockMargin: 6,
51
+ controlMinHeight: 32,
52
+ buttonPadV: 6,
53
+ buttonPadH: 12,
54
+ };
55
+ }
56
+ const root = height === undefined ? 16 : height >= 300 ? 16 : height >= 150 ? 14 : 12;
57
+ const r = (m: number): number => Math.round(root * m);
58
+ return {
59
+ root,
60
+ mono: false,
61
+ h: [r(1.5), r(1.25), r(1.125), r(1), r(0.875), r(0.75)],
62
+ blockMargin: r(0.5),
63
+ controlMinHeight: Math.max(32, Math.min(48, r(3))),
64
+ buttonPadV: r(0.625),
65
+ buttonPadH: r(1),
66
+ };
67
+ }
68
+
69
+ /** Read the active display profile (null when unbound — the default scale). */
70
+ function profileFor(): { height: number | undefined; mono: boolean; colorFormat: string | undefined } {
71
+ try {
72
+ const p = getDisplayProfile();
73
+ return {
74
+ height: p.height,
75
+ mono: p.colorFormat === "mono",
76
+ colorFormat: p.colorFormat,
77
+ };
78
+ } catch {
79
+ return { height: undefined, mono: false, colorFormat: undefined };
80
+ }
81
+ }
82
+
83
+ /** Build the UA stylesheet text for a scale. Sizes interpolate as concrete px
84
+ * so the CSS pipeline stays unit-simple. */
85
+ function uaCssFor(s: UAScale, useDefaultFont: boolean): string {
86
+ const gap = Math.round(s.root * 0.625);
87
+ const headerGap = Math.round(s.root * 0.5);
88
+ return `
12
89
  /* Browser-like light defaults: dark text on a near-white background.
13
90
  A dark theme overrides these via user CSS (e.g. screen { color: #f0f0f0;
14
91
  background: #1a1a1a }). Without these, screen fg defaults to white
@@ -17,7 +94,7 @@ screen {
17
94
  display: flex;
18
95
  flex-direction: column;
19
96
  color: #1a1a1a;
20
- background: #ffffff;
97
+ background: #ffffff;${useDefaultFont ? `\n /* Bundled default font: AA + continuous sizes out of the box. */\n font-family: "${DEFAULT_FONT_FAMILY}";` : ""}
21
98
  }
22
99
  body {
23
100
  display: flex;
@@ -30,40 +107,41 @@ view {
30
107
  flex-direction: column;
31
108
  }
32
109
  /* Scrollable content area — the standard pattern for multi-screen apps.
33
- align-items:center + gap give readable card-style layouts out of the box. */
34
- .scrollBody {
110
+ align-items:center + gap give readable card-style layouts out of the box.
111
+ .ui-scroll-body / .ui-screen-header are the framework-prefixed spellings. */
112
+ .scrollBody, .ui-scroll-body {
35
113
  overflow: scroll;
36
114
  flex: 1;
37
115
  align-items: center;
38
- gap: 10px;
116
+ gap: ${gap}px;
39
117
  }
40
118
  /* Header bar (back link + title). flex-direction:row is the expected layout;
41
119
  without it the back link and title stack vertically. */
42
- .screenHeader {
120
+ .screenHeader, .ui-screen-header {
43
121
  flex-direction: row;
44
122
  align-items: center;
45
- gap: 8px;
123
+ gap: ${headerGap}px;
46
124
  }
47
125
  button {
48
- padding: 10px 16px;
126
+ padding: ${s.buttonPadV}px ${s.buttonPadH}px;
49
127
  border: 1px solid;
50
- min-height: 48px;
128
+ min-height: ${s.controlMinHeight}px;
51
129
  text-align: center;
52
130
  }
53
131
  input {
54
- padding: 8px;
132
+ padding: ${Math.round(s.root * 0.5)}px;
55
133
  border: 1px solid;
56
134
  }
57
135
  select {
58
- padding: 10px 12px;
136
+ padding: ${s.buttonPadV}px ${s.buttonPadH}px;
59
137
  border: 1px solid;
60
- min-height: 48px;
138
+ min-height: ${s.controlMinHeight}px;
61
139
  }
62
140
  check, radio {
63
- min-height: 48px;
141
+ min-height: ${s.controlMinHeight}px;
64
142
  }
65
143
  range {
66
- height: 40px;
144
+ height: ${Math.round(s.root * 2.5)}px;
67
145
  }
68
146
  list {
69
147
  overflow: scroll;
@@ -75,17 +153,35 @@ a {
75
153
  label {
76
154
  text-align: left;
77
155
  }
78
- /* h3 is non-bold by default: the runtime bumps textSize for bold text
79
- (roughly doubling per-char advance), which wraps multi-word captions
80
- like "width / height / min / max" to 3 lines. Normal weight keeps
81
- them readable. Users can override with font-weight:bold if needed. */
82
- h1 { font-size: 24px; font-weight: bold; }
83
- h2 { font-size: 20px; font-weight: bold; }
84
- h3 { font-size: 18px; font-weight: normal; }
85
- h4 { font-size: 16px; font-weight: bold; }
86
- h5 { font-size: 14px; }
87
- h6 { font-size: 12px; }
88
- p { font-size: 16px; }
156
+ /* Headings and paragraphs carry browser-like margins so a bare heading stack
157
+ doesn't render glued together. Sizes derive from the display-anchored root
158
+ (see uaScaleFor). */
159
+ h1 { font-size: ${s.h[0]}px; font-weight: bold; margin: ${s.blockMargin}px 0; }
160
+ h2 { font-size: ${s.h[1]}px; font-weight: bold; margin: ${s.blockMargin}px 0; }
161
+ h3 { font-size: ${s.h[2]}px; font-weight: bold; margin: ${s.blockMargin}px 0; }
162
+ h4 { font-size: ${s.h[3]}px; font-weight: bold; margin: ${s.blockMargin}px 0; }
163
+ h5 { font-size: ${s.h[4]}px; margin: ${s.blockMargin}px 0; }
164
+ h6 { font-size: ${s.h[5]}px; margin: ${s.blockMargin}px 0; }
165
+ p { font-size: ${s.root}px; margin: ${s.blockMargin}px 0; }
166
+ small { font-size: ${Math.round(s.root * 0.8)}px; }
167
+ hr { height: 1px; background: #808080; margin: ${s.blockMargin}px 0; }
168
+ /* Definition lists: bold term, indented description — the settings-screen
169
+ idiom web authors reach for. */
170
+ dt { font-weight: bold; }
171
+ dd { margin-left: ${Math.round(s.root * 0.75)}px; }
172
+ /* Tables: the equal-width flex approximation — tr is a row, td/th are
173
+ stretched cells (flex:1 basis 0), th bold + centered like browsers.
174
+ No auto column sizing; set widths/flex-grow on cells for custom columns. */
175
+ table { gap: 1px; }
176
+ tr { flex-direction: row; align-items: stretch; gap: 1px; }
177
+ td { flex: 1; padding: ${Math.round(s.root * 0.3)}px; text-align: left; }
178
+ th { flex: 1; padding: ${Math.round(s.root * 0.3)}px; text-align: center; font-weight: bold; }
179
+ caption { text-align: center; margin: ${Math.round(s.root * 0.25)}px 0; }
180
+ /* Grouped form chrome. */
181
+ fieldset { border: 1px solid; padding: ${s.blockMargin}px; }
182
+ /* Preformatted text keeps its whitespace (engine white-space modes); the
183
+ monospace family rides on the bundled DejaVu Sans Mono when available. */
184
+ pre { white-space: pre; }${useDefaultFont ? `\npre, code, kbd, samp { font-family: "${DEFAULT_MONO_FAMILY}"; }` : ""}
89
185
  /* Keyboard structural defaults (the OSK is runtime-generated, not
90
186
  user-authored HTML). Border keeps keys visually separated. Colors
91
187
  are design choices left to user CSS. */
@@ -93,11 +189,21 @@ p { font-size: 16px; }
93
189
  border: 1px solid;
94
190
  }
95
191
  `;
192
+ }
96
193
 
97
- let UA_RULES: CSSRule[] | null = null;
194
+ const cache = new Map<string, CSSRule[]>();
98
195
 
99
- /** Get the parsed UA default rules. Lazily computed, cached. */
196
+ /** Get the parsed UA default rules for the active display profile. Lazily
197
+ * computed, cached per scale (profile changes pick up a new scale key). */
100
198
  export function getUARules(): CSSRule[] {
101
- if (!UA_RULES) UA_RULES = parseCss(UA_CSS);
102
- return UA_RULES;
199
+ const profile = profileFor();
200
+ const scale = uaScaleFor(profile.height, profile.mono);
201
+ const useDefaultFont = defaultFontApplies(profile.colorFormat);
202
+ const key = `${scale.root}|${scale.mono}|${useDefaultFont}|${scale.h.join(",")}`;
203
+ let rules = cache.get(key);
204
+ if (!rules) {
205
+ rules = parseCss(uaCssFor(scale, useDefaultFont));
206
+ cache.set(key, rules);
207
+ }
208
+ return rules;
103
209
  }