@typecad/ui 1.0.0-alpha.12 → 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 (113) 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/engine-index.js +2 -0
  10. package/dist/preview/build-program.d.ts +7 -1
  11. package/dist/preview/build-program.js +94 -15
  12. package/dist/preview/host-gfx.d.ts +3 -0
  13. package/dist/preview/host-gfx.js +30 -3
  14. package/dist/preview/host-ui-runtime.d.ts +115 -0
  15. package/dist/preview/host-ui-runtime.js +928 -46
  16. package/dist/ui-engine/block-layout.js +25 -11
  17. package/dist/ui-engine/color.d.ts +6 -0
  18. package/dist/ui-engine/color.js +80 -2
  19. package/dist/ui-engine/compat-report.d.ts +8 -0
  20. package/dist/ui-engine/compat-report.js +124 -0
  21. package/dist/ui-engine/css-imports.d.ts +4 -0
  22. package/dist/ui-engine/css-imports.js +70 -0
  23. package/dist/ui-engine/css-parser.js +58 -14
  24. package/dist/ui-engine/default-font.d.ts +25 -0
  25. package/dist/ui-engine/default-font.js +80 -0
  26. package/dist/ui-engine/font-assets.d.ts +2 -2
  27. package/dist/ui-engine/font-assets.js +22 -5
  28. package/dist/ui-engine/html-parser.d.ts +4 -0
  29. package/dist/ui-engine/html-parser.js +241 -26
  30. package/dist/ui-engine/image-assets.d.ts +8 -1
  31. package/dist/ui-engine/image-assets.js +35 -2
  32. package/dist/ui-engine/image-decode.d.ts +22 -0
  33. package/dist/ui-engine/image-decode.js +194 -0
  34. package/dist/ui-engine/inline-parser.js +1 -1
  35. package/dist/ui-engine/layout-engine.d.ts +19 -0
  36. package/dist/ui-engine/layout-engine.js +59 -3
  37. package/dist/ui-engine/model.d.ts +18 -0
  38. package/dist/ui-engine/model.js +75 -7
  39. package/dist/ui-engine/runtime-header/blend-bodies.js +29 -9
  40. package/dist/ui-engine/runtime-header/canvas-helpers.js +3 -0
  41. package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -111
  42. package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +21 -0
  43. package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -179
  44. package/dist/ui-engine/runtime-header/forward-decls.js +215 -167
  45. package/dist/ui-engine/runtime-header/init-press-input.js +138 -135
  46. package/dist/ui-engine/runtime-header/keyboard.js +682 -380
  47. package/dist/ui-engine/runtime-header/node-draw-body.js +1656 -1386
  48. package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -214
  49. package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -568
  50. package/dist/ui-engine/runtime-header/scroll-physics.js +25 -1
  51. package/dist/ui-engine/runtime-header/state-bindings-nav.js +9 -0
  52. package/dist/ui-engine/runtime-header/structs.js +228 -216
  53. package/dist/ui-engine/runtime-header/text-rendering.js +888 -770
  54. package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -116
  55. package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +897 -633
  56. package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -25
  57. package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +78 -23
  58. package/dist/ui-engine/runtime-header/types-defines.js +110 -93
  59. package/dist/ui-engine/shadcn-kit.d.ts +1 -0
  60. package/dist/ui-engine/shadcn-kit.js +502 -0
  61. package/dist/ui-engine/style-resolver.d.ts +4 -0
  62. package/dist/ui-engine/style-resolver.js +58 -6
  63. package/dist/ui-engine/transpile-ui.js +12 -5
  64. package/dist/ui-engine/ua-stylesheet.d.ts +22 -1
  65. package/dist/ui-engine/ua-stylesheet.js +119 -32
  66. package/dist/ui-engine/ui-lowering.js +37 -3
  67. package/dist/ui-engine/ui-registry.js +32 -4
  68. package/dist/ui-engine/yoga-layout.js +57 -52
  69. package/package.json +9 -3
  70. package/src/engine-index.ts +53 -51
  71. package/src/preview/build-program.ts +810 -734
  72. package/src/preview/host-gfx.ts +30 -3
  73. package/src/preview/host-ui-runtime.ts +4133 -3289
  74. package/src/ui-engine/block-layout.ts +24 -11
  75. package/src/ui-engine/color.ts +77 -2
  76. package/src/ui-engine/compat-report.ts +139 -0
  77. package/src/ui-engine/css-imports.ts +69 -0
  78. package/src/ui-engine/css-parser.ts +64 -15
  79. package/src/ui-engine/default-font.ts +95 -0
  80. package/src/ui-engine/font-assets.ts +545 -525
  81. package/src/ui-engine/html-parser.ts +615 -397
  82. package/src/ui-engine/image-assets.ts +37 -2
  83. package/src/ui-engine/image-decode.ts +244 -0
  84. package/src/ui-engine/inline-parser.ts +1 -1
  85. package/src/ui-engine/layout-engine.ts +61 -3
  86. package/src/ui-engine/model.ts +1317 -1230
  87. package/src/ui-engine/runtime-header/blend-bodies.ts +29 -9
  88. package/src/ui-engine/runtime-header/canvas-helpers.ts +3 -0
  89. package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -117
  90. package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +21 -0
  91. package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -185
  92. package/src/ui-engine/runtime-header/forward-decls.ts +227 -179
  93. package/src/ui-engine/runtime-header/init-press-input.ts +144 -141
  94. package/src/ui-engine/runtime-header/keyboard.ts +689 -386
  95. package/src/ui-engine/runtime-header/node-draw-body.ts +1681 -1411
  96. package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -220
  97. package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -574
  98. package/src/ui-engine/runtime-header/scroll-physics.ts +25 -1
  99. package/src/ui-engine/runtime-header/state-bindings-nav.ts +9 -0
  100. package/src/ui-engine/runtime-header/structs.ts +234 -222
  101. package/src/ui-engine/runtime-header/text-rendering.ts +894 -776
  102. package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -122
  103. package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -638
  104. package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -31
  105. package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +78 -23
  106. package/src/ui-engine/runtime-header/types-defines.ts +116 -99
  107. package/src/ui-engine/shadcn-kit.ts +503 -0
  108. package/src/ui-engine/style-resolver.ts +471 -416
  109. package/src/ui-engine/transpile-ui.ts +12 -5
  110. package/src/ui-engine/ua-stylesheet.ts +137 -31
  111. package/src/ui-engine/ui-lowering.ts +486 -452
  112. package/src/ui-engine/ui-registry.ts +556 -524
  113. package/src/ui-engine/yoga-layout.ts +307 -309
@@ -0,0 +1,187 @@
1
+ Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
2
+ Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
3
+
4
+
5
+ Bitstream Vera Fonts Copyright
6
+ ------------------------------
7
+
8
+ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
9
+ a trademark of Bitstream, Inc.
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of the fonts accompanying this license ("Fonts") and associated
13
+ documentation files (the "Font Software"), to reproduce and distribute the
14
+ Font Software, including without limitation the rights to use, copy, merge,
15
+ publish, distribute, and/or sell copies of the Font Software, and to permit
16
+ persons to whom the Font Software is furnished to do so, subject to the
17
+ following conditions:
18
+
19
+ The above copyright and trademark notices and this permission notice shall
20
+ be included in all copies of one or more of the Font Software typefaces.
21
+
22
+ The Font Software may be modified, altered, or added to, and in particular
23
+ the designs of glyphs or characters in the Fonts may be modified and
24
+ additional glyphs or characters may be added to the Fonts, only if the fonts
25
+ are renamed to names not containing either the words "Bitstream" or the word
26
+ "Vera".
27
+
28
+ This License becomes null and void to the extent applicable to Fonts or Font
29
+ Software that has been modified and is distributed under the "Bitstream
30
+ Vera" names.
31
+
32
+ The Font Software may be sold as part of a larger software package but no
33
+ copy of one or more of the Font Software typefaces may be sold by itself.
34
+
35
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
36
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
38
+ TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
39
+ FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
40
+ ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
41
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
42
+ THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
43
+ FONT SOFTWARE.
44
+
45
+ Except as contained in this notice, the names of Gnome, the Gnome
46
+ Foundation, and Bitstream Inc., shall not be used in advertising or
47
+ otherwise to promote the sale, use or other dealings in this Font Software
48
+ without prior written authorization from the Gnome Foundation or Bitstream
49
+ Inc., respectively. For further information, contact: fonts at gnome dot
50
+ org.
51
+
52
+ Arev Fonts Copyright
53
+ ------------------------------
54
+
55
+ Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining
58
+ a copy of the fonts accompanying this license ("Fonts") and
59
+ associated documentation files (the "Font Software"), to reproduce
60
+ and distribute the modifications to the Bitstream Vera Font Software,
61
+ including without limitation the rights to use, copy, merge, publish,
62
+ distribute, and/or sell copies of the Font Software, and to permit
63
+ persons to whom the Font Software is furnished to do so, subject to
64
+ the following conditions:
65
+
66
+ The above copyright and trademark notices and this permission notice
67
+ shall be included in all copies of one or more of the Font Software
68
+ typefaces.
69
+
70
+ The Font Software may be modified, altered, or added to, and in
71
+ particular the designs of glyphs or characters in the Fonts may be
72
+ modified and additional glyphs or characters may be added to the
73
+ Fonts, only if the fonts are renamed to names not containing either
74
+ the words "Tavmjong Bah" or the word "Arev".
75
+
76
+ This License becomes null and void to the extent applicable to Fonts
77
+ or Font Software that has been modified and is distributed under the
78
+ "Tavmjong Bah Arev" names.
79
+
80
+ The Font Software may be sold as part of a larger software package but
81
+ no copy of one or more of the Font Software typefaces may be sold by
82
+ itself.
83
+
84
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
88
+ TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92
+ OTHER DEALINGS IN THE FONT SOFTWARE.
93
+
94
+ Except as contained in this notice, the name of Tavmjong Bah shall not
95
+ be used in advertising or otherwise to promote the sale, use or other
96
+ dealings in this Font Software without prior written authorization
97
+ from Tavmjong Bah. For further information, contact: tavmjong @ free
98
+ . fr.
99
+
100
+ TeX Gyre DJV Math
101
+ -----------------
102
+ Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
103
+
104
+ Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
105
+ (on behalf of TeX users groups) are in public domain.
106
+
107
+ Letters imported from Euler Fraktur from AMSfonts are (c) American
108
+ Mathematical Society (see below).
109
+ Bitstream Vera Fonts Copyright
110
+ Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
111
+ is a trademark of Bitstream, Inc.
112
+
113
+ Permission is hereby granted, free of charge, to any person obtaining a copy
114
+ of the fonts accompanying this license (“Fonts”) and associated
115
+ documentation
116
+ files (the “Font Software”), to reproduce and distribute the Font Software,
117
+ including without limitation the rights to use, copy, merge, publish,
118
+ distribute,
119
+ and/or sell copies of the Font Software, and to permit persons to whom
120
+ the Font Software is furnished to do so, subject to the following
121
+ conditions:
122
+
123
+ The above copyright and trademark notices and this permission notice
124
+ shall be
125
+ included in all copies of one or more of the Font Software typefaces.
126
+
127
+ The Font Software may be modified, altered, or added to, and in particular
128
+ the designs of glyphs or characters in the Fonts may be modified and
129
+ additional
130
+ glyphs or characters may be added to the Fonts, only if the fonts are
131
+ renamed
132
+ to names not containing either the words “Bitstream” or the word “Vera”.
133
+
134
+ This License becomes null and void to the extent applicable to Fonts or
135
+ Font Software
136
+ that has been modified and is distributed under the “Bitstream Vera”
137
+ names.
138
+
139
+ The Font Software may be sold as part of a larger software package but
140
+ no copy
141
+ of one or more of the Font Software typefaces may be sold by itself.
142
+
143
+ THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
144
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
145
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
146
+ TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
147
+ FOUNDATION
148
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
149
+ SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
150
+ ACTION
151
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
152
+ INABILITY TO USE
153
+ THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
154
+ Except as contained in this notice, the names of GNOME, the GNOME
155
+ Foundation,
156
+ and Bitstream Inc., shall not be used in advertising or otherwise to promote
157
+ the sale, use or other dealings in this Font Software without prior written
158
+ authorization from the GNOME Foundation or Bitstream Inc., respectively.
159
+ For further information, contact: fonts at gnome dot org.
160
+
161
+ AMSFonts (v. 2.2) copyright
162
+
163
+ The PostScript Type 1 implementation of the AMSFonts produced by and
164
+ previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
165
+ available for general use. This has been accomplished through the
166
+ cooperation
167
+ of a consortium of scientific publishers with Blue Sky Research and Y&Y.
168
+ Members of this consortium include:
169
+
170
+ Elsevier Science IBM Corporation Society for Industrial and Applied
171
+ Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
172
+
173
+ In order to assure the authenticity of these fonts, copyright will be
174
+ held by
175
+ the American Mathematical Society. This is not meant to restrict in any way
176
+ the legitimate use of the fonts, such as (but not limited to) electronic
177
+ distribution of documents containing these fonts, inclusion of these fonts
178
+ into other public domain or commercial font collections or computer
179
+ applications, use of the outline data to create derivative fonts and/or
180
+ faces, etc. However, the AMS does require that the AMS copyright notice be
181
+ removed from any derivative versions of the fonts which have been altered in
182
+ any way. In addition, to ensure the fidelity of TeX documents using Computer
183
+ Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
184
+ has requested that any alterations which yield different font metrics be
185
+ given a different name.
186
+
187
+ $Id$
@@ -0,0 +1,49 @@
1
+ # Bundled DejaVu fonts
2
+
3
+ These are the unmodified TrueType files from the [DejaVu fonts] project,
4
+ release **2.37** (the final upstream release). They are bundled here so every
5
+ cuttlefish UI project gets an antialiased, continuously-sized font out of the
6
+ box (see `@font-face` injection in `packages/ui/src/ui-engine/default-font.ts`).
7
+
8
+ Do **not** rename or modify these files: the license terms (Bitstream Vera
9
+ License, see [LICENSE](./LICENSE)) permit free redistribution of unmodified
10
+ copies under the original name, but modified variants must be renamed to
11
+ something without the word "DejaVu". Build-time subsetting/rasterization into
12
+ glyph bitmaps is fine — only the installable font files must stay pristine.
13
+
14
+ ## Files
15
+
16
+ | File | Face | Used for |
17
+ | ----------------------- | ----------- | ----------------------------------------- |
18
+ | `DejaVuSans.ttf` | Regular | Default injected face (weight 400) |
19
+ | `DejaVuSans-Bold.ttf` | Bold | Default injected face (weight 700) |
20
+ | `DejaVuSansMono.ttf` | Mono Regular | Default injected mono face (`pre`/`code`/`kbd`) |
21
+ | `DejaVuSansMono-Bold.ttf` | Mono Bold | Default injected mono face (weight 700) |
22
+ | `DejaVuSans-Oblique.ttf`| Oblique | Opt-in via `@font-face` (italic) |
23
+ | `DejaVuSans-ExtraLight.ttf` | ExtraLight | Opt-in via `@font-face` (weight 300) |
24
+
25
+ Only `DejaVuSans(.ttf/-Bold.ttf)` and `DejaVuSansMono(.ttf/-Bold.ttf)` are
26
+ injected by default (the "DejaVu Sans" and "DejaVu Sans Mono" families); the
27
+ oblique and extra-light faces ship for projects that declare them explicitly.
28
+ Devices never receive these files — the build rasterizes the exact (family,
29
+ px, weight, style, characters) subsets a UI uses into glyph tables.
30
+
31
+ ## Checksums (SHA-256, release 2.37)
32
+
33
+ ```
34
+ e6476c1b80502924294eed40894c5b18e06c181444ca953e5334262df9c27724 DejaVuSans-Bold.ttf
35
+ 6cb0746a1f68d176bebe83752cee35ebdf726cb1a1e88a01fc038ddf76de9ea8 DejaVuSans-ExtraLight.ttf
36
+ 4af75fa16ee6d3ad43e1ecec41862c24954af26a55c6bb1ebb27bd486a50f5f4 DejaVuSans-Oblique.ttf
37
+ 7da195a74c55bef988d0d48f9508bd5d849425c1770dba5d7bfc6ce9ed848954 DejaVuSans.ttf
38
+ b4a6c3e4faab8773f4ff761d56451646409f29abedd68f05d38c2df667d3c582 DejaVuSansMono.ttf
39
+ bce60f1b4421acd9ea51ba6623d7024ecbe6817a953e3654df62a5e6bdf8f769 DejaVuSansMono-Bold.ttf
40
+ ```
41
+
42
+ ## License
43
+
44
+ Bitstream Vera License (DejaVu variant) — permissive, GPL-compatible, free to
45
+ redistribute and embed in commercial firmware. The full text is in
46
+ [LICENSE](./LICENSE) and must accompany any redistribution of these files.
47
+ Generated font tables carry an attribution comment pointing back here.
48
+
49
+ [DejaVu fonts]: https://dejavu-fonts.github.io/
@@ -14,6 +14,7 @@ import { resolveColor, resolveColorInternal } from "./ui-engine/color.js";
14
14
  import { emitRuntimeHeader } from "./ui-engine/runtime-header.js";
15
15
  import { emitCuttlefishGfx } from "./ui-engine/runtime-header/cuttlefish-gfx.js";
16
16
  import { splitUiFile } from "./ui-engine/ui-file-splitter.js";
17
+ import { warmUpImageDecoding } from "./ui-engine/image-decode.js";
17
18
  /**
18
19
  * Build and return the TranspilerUIHook — the object cuttlefish core uses
19
20
  * to access all UI capabilities (parsing, layout, lowering, emission).
@@ -36,6 +37,7 @@ export function registerTranspilerUI() {
36
37
  emitRuntimeHeader,
37
38
  emitCuttlefishGfx,
38
39
  splitUiFile,
40
+ warmUpImageDecoding,
39
41
  generateProjectUITypeDeclarations,
40
42
  };
41
43
  }
@@ -1,7 +1,13 @@
1
1
  import { ResolvedCuttlefishConfig } from "@typecad/cuttlefish/config-loader";
2
- import type { PreviewSnapshot } from "./types.js";
2
+ import type { PreviewDiagnostic, PreviewSnapshot } from "./types.js";
3
3
  export interface BuildPreviewSnapshotOptions {
4
4
  config: ResolvedCuttlefishConfig;
5
5
  projectRoot: string;
6
6
  }
7
+ /** Load the stock 5x7 GFX font for the preview runtime. A project-local
8
+ * Adafruit_GFX copy wins (byte-identical to what the firmware compiles
9
+ * against); otherwise fall back to the table bundled in
10
+ * @typecad/cuttlefish (api/shared glcdfont.ts) so stock-font text renders
11
+ * without a vendored library. */
12
+ export declare function loadFont(projectRoot: string, diagnostics: PreviewDiagnostic[]): number[];
7
13
  export declare function buildPreviewSnapshot(options: BuildPreviewSnapshotOptions): Promise<PreviewSnapshot>;
@@ -1,12 +1,16 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import ts from "typescript";
4
- import { effectiveDisplaySize, resolveDisplayProfile } from "@typecad/cuttlefish/api/shared";
4
+ import { effectiveDisplaySize, resolveDisplayProfile, GLCDFONT_BYTES } from "@typecad/cuttlefish/api/shared";
5
5
  import { parseCss, parseFontFaces, parseKeyframes } from "../ui-engine/css-parser.js";
6
+ import { expandCssImports } from "../ui-engine/css-imports.js";
7
+ import { injectDefaultFontFaces } from "../ui-engine/default-font.js";
6
8
  import { extractStyleBlocks, parseHtmlWithKeyboards } from "../ui-engine/html-parser.js";
7
9
  import { splitUiFile } from "../ui-engine/ui-file-splitter.js";
8
10
  import { buildUIFontAssets } from "../ui-engine/font-assets.js";
9
- import { loadImageAssets } from "../ui-engine/image-assets.js";
11
+ import { loadImageAssets, applyDecodedImageSizes } from "../ui-engine/image-assets.js";
12
+ import { warmUpImageDecoding } from "../ui-engine/image-decode.js";
13
+ import { SHADCN_KIT_CSS } from "../ui-engine/shadcn-kit.js";
10
14
  import { buildKeyframeSets } from "../ui-engine/keyframes.js";
11
15
  import { measureWithFonts } from "../ui-engine/layout-engine.js";
12
16
  import { lowerUIToModel } from "../ui-engine/model.js";
@@ -66,6 +70,35 @@ function interpolationToExpression(raw) {
66
70
  return undefined;
67
71
  return "`" + parts.join("") + "`";
68
72
  }
73
+ /** Warn about interactive nodes the preview cannot wire because they lack an
74
+ * id: on:* handlers, bind:* bindings, and {expr} interpolations all resolve
75
+ * their node by id in the preview (the device build auto-wires them without
76
+ * one). Without this the element renders but never reacts. */
77
+ function warnUnnamedInteractiveNodes(trees, diagnostics) {
78
+ const walk = (node) => {
79
+ if (!node.id) {
80
+ const features = [];
81
+ if (node.events && Object.keys(node.events).length > 0)
82
+ features.push(`on:${Object.keys(node.events).join("/on:")}`);
83
+ if (node.bind && Object.keys(node.bind).length > 0)
84
+ features.push(`bind:${Object.keys(node.bind).join("/bind:")}`);
85
+ if (node.hasInterpolation)
86
+ features.push("{expr} interpolation");
87
+ // Images key their loaded asset by node id too (image-assets.ts skips
88
+ // id-less <img src> silently) — warn so empty frames are explainable.
89
+ if (node.tag === "img" && node.src)
90
+ features.push("img src");
91
+ if (features.length > 0) {
92
+ diagnostics.push({
93
+ severity: "warning",
94
+ message: `<${node.tag}> uses ${features.join(", ")} but has no id — the preview loads/wires these by id, so the element renders empty or inert. Add an id, e.g. <${node.tag} id="${node.tag}1"> (the device build behaves the same).`,
95
+ });
96
+ }
97
+ }
98
+ node.children?.forEach(walk);
99
+ };
100
+ trees.forEach(walk);
101
+ }
69
102
  /** Walk styled trees for {expr} interpolation nodes and synthesize preview text
70
103
  * bindings (mirroring the runtime's auto-wire synthesis). Each produces a
71
104
  * PreviewBindingSpec whose expression is a template-literal the preview evals. */
@@ -151,22 +184,33 @@ async function loadProfileRegistry(frameworkPackage) {
151
184
  const registry = new Map();
152
185
  if (!frameworkPackage)
153
186
  return registry;
154
- const profileMod = await import(frameworkPackage + "/displays/ili9341-spi").catch(() => null);
155
- if (profileMod?.BUILT_IN_PROFILES) {
156
- for (const [key, value] of Object.entries(profileMod.BUILT_IN_PROFILES)) {
157
- registry.set(key, value);
187
+ // Every framework package exports its named profiles as BUILT_IN_PROFILES
188
+ // in the shared DisplayProfile shape (Arduino: the displays modules; Zephyr:
189
+ // the display barrel, mapped from its DT-binding descriptors). Try each
190
+ // known entry point — the miss is expected per framework — so this loader
191
+ // stays layout-agnostic and profile names resolve identically here and via
192
+ // the strategy's getProfileRegistry() (which transpile.ts uses).
193
+ for (const modPath of [frameworkPackage + "/displays/ili9341-spi", frameworkPackage + "/display"]) {
194
+ const mod = await import(modPath).catch(() => null);
195
+ const profiles = mod?.BUILT_IN_PROFILES;
196
+ if (profiles) {
197
+ for (const [key, value] of Object.entries(profiles)) {
198
+ registry.set(key, value);
199
+ }
200
+ break;
158
201
  }
159
202
  }
160
203
  return registry;
161
204
  }
162
- function loadFont(projectRoot, diagnostics) {
205
+ /** Load the stock 5x7 GFX font for the preview runtime. A project-local
206
+ * Adafruit_GFX copy wins (byte-identical to what the firmware compiles
207
+ * against); otherwise fall back to the table bundled in
208
+ * @typecad/cuttlefish (api/shared glcdfont.ts) so stock-font text renders
209
+ * without a vendored library. */
210
+ export function loadFont(projectRoot, diagnostics) {
163
211
  const fontPath = path.join(projectRoot, "lib", "Adafruit_GFX_Library", "glcdfont.c");
164
212
  if (!fs.existsSync(fontPath)) {
165
- diagnostics.push({
166
- severity: "warning",
167
- message: `Default GFX font not found at ${fontPath}; text pixels will be blank.`,
168
- });
169
- return Array.from(new Uint8Array(1280));
213
+ return GLCDFONT_BYTES.slice(0, 1280);
170
214
  }
171
215
  const source = fs.readFileSync(fontPath, "utf-8");
172
216
  const match = /font\[\]\s+PROGMEM\s*=\s*\{([\s\S]*?)\};/.exec(source);
@@ -568,6 +612,7 @@ export async function buildPreviewSnapshot(options) {
568
612
  let sourceFile;
569
613
  let htmlText;
570
614
  let cssText;
615
+ let cssFileDir; // directory @imports inside cssText resolve from
571
616
  let htmlFilePath; // the effective .ui.html path (real or synthetic)
572
617
  let uiImports;
573
618
  if (entryExt === ".ui") {
@@ -581,6 +626,7 @@ export async function buildPreviewSnapshot(options) {
581
626
  sourceFile = ts.createSourceFile(entryFile, scriptWithImport, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
582
627
  htmlText = parts.html;
583
628
  cssText = parts.style;
629
+ cssFileDir = entryDir;
584
630
  uiImports = [{ treeName: "screen", htmlPath: htmlFilePath }];
585
631
  }
586
632
  else {
@@ -600,13 +646,24 @@ export async function buildPreviewSnapshot(options) {
600
646
  htmlText = fs.readFileSync(firstImport.htmlPath, "utf-8");
601
647
  const cssPath = themeCssPath(config.display?.themeCss, firstImport.htmlPath, configDir);
602
648
  cssText = fs.existsSync(cssPath) ? fs.readFileSync(cssPath, "utf-8") : "";
649
+ cssFileDir = path.dirname(cssPath);
603
650
  }
604
651
  const registry = await loadProfileRegistry(config.framework);
605
652
  const resolved = resolveDisplayProfile(config.display ?? { profile: "ili9341-spi" }, registry);
606
653
  const profile = resolved.profile;
607
654
  const displaySize = effectiveDisplaySize(profile);
608
655
  const parsedHtml = parseHtmlWithKeyboards(htmlText);
609
- const fullCss = cssText + "\n" + extractStyleBlocks(htmlText);
656
+ // @import parity with the CLI build (loadUIModuleFromText in ui-registry.ts
657
+ // expands imports before parsing). Without this, `@import "./styles/shadcn.css"`
658
+ // in a .ui <style> stays literal in the preview, the kit's tokens never load,
659
+ // and model lowering rejects the raw var() color strings. The sidecar css and
660
+ // the html style blocks can live in different directories, so each part
661
+ // expands against its own base.
662
+ // Built-in shadcn kit first (device parity with ui-registry): user token
663
+ // blocks and recipe overrides win by cascade order.
664
+ const fullCss = SHADCN_KIT_CSS + "\n" +
665
+ expandCssImports(cssText, cssFileDir) + "\n" +
666
+ expandCssImports(extractStyleBlocks(htmlText), path.dirname(htmlFilePath));
610
667
  const cssRules = (() => {
611
668
  const previousThemeClass = getThemeClass();
612
669
  setThemeClass(config.display?.themeClass ?? null);
@@ -617,12 +674,29 @@ export async function buildPreviewSnapshot(options) {
617
674
  setThemeClass(previousThemeClass);
618
675
  }
619
676
  })();
620
- const fontFaces = parseFontFaces(fullCss);
677
+ // Default-font parity with the CLI build (ui-registry.ts / transpile-ui.ts
678
+ // both inject the bundled DejaVu faces before planning font assets). Without
679
+ // this the UA root's font-family: "DejaVu Sans" resolves to no face and the
680
+ // preview falls back to the smoothed 5x7 — the device renders real AA glyphs.
681
+ const fontFaces = injectDefaultFontFaces(parseFontFaces(fullCss), profile.colorFormat);
621
682
  const rawKeyframes = parseKeyframes(fullCss);
622
683
  const styled = resolveStyles(parsedHtml.tree, cssRules);
623
684
  const allStyledScreens = parsedHtml.screens.map((screen) => resolveStyles(screen, cssRules));
624
685
  const fontRoot = { tag: "screen", classes: [], style: {}, children: allStyledScreens };
625
- const fontAssets = buildUIFontAssets(fontRoot, fontFaces, path.dirname(htmlFilePath));
686
+ // Script-side ui.bind(..., 'text', ...) targets render runtime strings the
687
+ // static template can't predict — collect their ids so font planning widens
688
+ // those faces to the fallback charset (markup {expr} and bind:text are
689
+ // detected from the tree inside the planner).
690
+ const dynamicTextIds = new Set();
691
+ const bindTextRe = /ui\.bind\(\s*screen\.([A-Za-z_$][\w$]*)\s*,\s*["']text["']/g;
692
+ for (const m of sourceFile.text.matchAll(bindTextRe))
693
+ dynamicTextIds.add(m[1]);
694
+ const fontAssets = buildUIFontAssets(fontRoot, fontFaces, path.dirname(htmlFilePath), dynamicTextIds);
695
+ // Prime the image-conversion cache (<img src="*.png|jpg|ico|…"> decodes
696
+ // here) and give id-less-size img nodes their natural geometry before the
697
+ // synchronous layout + asset pass below.
698
+ await warmUpImageDecoding(htmlText, path.dirname(htmlFilePath), { maxW: displaySize.width, maxH: displaySize.height });
699
+ applyDecodedImageSizes(allStyledScreens.length > 0 ? allStyledScreens : [styled], path.dirname(htmlFilePath));
626
700
  const viewport = { x: 0, y: 0, w: displaySize.width, h: displaySize.height };
627
701
  const boxes = allStyledScreens.flatMap((screen) => {
628
702
  const engine = selectEngine(screen);
@@ -633,6 +707,11 @@ export async function buildPreviewSnapshot(options) {
633
707
  const program = lowerUIToModel(styled, boxes, profile.colorFormat, profile, fontAssets, allStyledScreens, imageAssets.nodeIdToAssetIndex, keyframeSets, imageAssets.assets);
634
708
  const specs = extractAuthorSpecs(sourceFile, uiImports, program.nodes);
635
709
  const hrefCallbacks = collectHrefCallbacks(allStyledScreens, program.nodes);
710
+ // on:*/bind:*/{expr} features resolve their node by id — the device build
711
+ // auto-wires id-less nodes, but the preview's binding/callback tables key on
712
+ // id, so an unnamed interactive node is silently inert here. Make that
713
+ // visible instead of a dead button.
714
+ warnUnnamedInteractiveNodes(allStyledScreens, diagnostics);
636
715
  // {expr} interpolation bindings synthesized from the HTML (mirrors the runtime's
637
716
  // auto-wire synthesis). Authors write `taps: {count}` in markup; this collects
638
717
  // them so the preview evaluates the template-literal expression each frame.
@@ -28,6 +28,7 @@ export declare class HostAdafruitGFX {
28
28
  private textColor;
29
29
  private textBgColor;
30
30
  private monoSnap;
31
+ private storage;
31
32
  private textSizeX;
32
33
  private textSizeY;
33
34
  private wrap;
@@ -38,6 +39,8 @@ export declare class HostAdafruitGFX {
38
39
  * every color to black/white by luminance, matching the device's
39
40
  * UI_MAYBE_SNAP_MONO565. Uses the same threshold as toMono (0.27). */
40
41
  setMonoSnap(on: boolean): void;
42
+ /** Set the buffer's storage depth (see the `storage` field). */
43
+ setStorageMode(mode: "rgb565" | "rgb666" | "rgb888"): void;
41
44
  private snap;
42
45
  setRotation(_rotation: number): void;
43
46
  setClipRect(rect: {
@@ -62,6 +62,12 @@ export class HostAdafruitGFX {
62
62
  // When true (mono/e-ink target), draw primitives snap color values to black/
63
63
  // white by luminance — mirrors the device's UI_MAYBE_SNAP_MONO565 choke-point.
64
64
  monoSnap = false;
65
+ // Storage depth of values written into the buffer. rgb565 targets store
66
+ // packed 565 (expanded at the canvas push); rgb888/rgb666 targets store
67
+ // packed RGB888, with rgb666 quantizing channels to 6 bits at the push
68
+ // boundary — mirroring the device's panel. Set by PreviewUIRuntime from the
69
+ // snapshot's colorFormat before the first frame.
70
+ storage = "rgb565";
65
71
  textSizeX = 1;
66
72
  textSizeY = 1;
67
73
  wrap = true;
@@ -81,6 +87,10 @@ export class HostAdafruitGFX {
81
87
  setMonoSnap(on) {
82
88
  this.monoSnap = on;
83
89
  }
90
+ /** Set the buffer's storage depth (see the `storage` field). */
91
+ setStorageMode(mode) {
92
+ this.storage = mode;
93
+ }
84
94
  snap(c) {
85
95
  if (!this.monoSnap)
86
96
  return c;
@@ -228,7 +238,11 @@ export class HostAdafruitGFX {
228
238
  }
229
239
  r = Math.min(r, Math.trunc(Math.min(w, h) / 2));
230
240
  this.fillRect(x + r, y, w - 2 * r, h, color);
231
- const delta = Math.max(0, h - 2 * r - 1);
241
+ // No max(0, ·) clamp: for a perfect pill (h == 2r) the raw value is -1 and
242
+ // fillCircleHelper's delta++ turns it into 0, landing the arc lines exactly
243
+ // on the box. Clamping to 0 first made every arc line one row too tall —
244
+ // a 1px orphan stub under each corner of pill-shaped badges.
245
+ const delta = h - 2 * r - 1;
232
246
  this.fillCircleHelper(x + w - r - 1, y + r, r, 1, delta, color);
233
247
  this.fillCircleHelper(x + r, y + r, r, 2, delta, color);
234
248
  }
@@ -538,13 +552,26 @@ export class HostAdafruitGFX {
538
552
  out[p + 1] = (c >> 8) & 0xff;
539
553
  out[p + 2] = c & 0xff;
540
554
  }
541
- else {
542
- // 565 target: unpack to 888.
555
+ else if (this.storage === "rgb565") {
556
+ // 565 target: buffer holds packed 565 — unpack to 888.
543
557
  const { r, g, b } = rgb565ToRgb888(c);
544
558
  out[p] = r;
545
559
  out[p + 1] = g;
546
560
  out[p + 2] = b;
547
561
  }
562
+ else {
563
+ // rgb888/rgb666 target: buffer holds packed RGB888. rgb666
564
+ // quantizes to 6 bits/channel at the push (device panel parity).
565
+ let r = (c >> 16) & 0xff, g = (c >> 8) & 0xff, b = c & 0xff;
566
+ if (this.storage === "rgb666") {
567
+ r = (r & 0xfc) | (r >> 6);
568
+ g = (g & 0xfc) | (g >> 6);
569
+ b = (b & 0xfc) | (b >> 6);
570
+ }
571
+ out[p] = r;
572
+ out[p + 1] = g;
573
+ out[p + 2] = b;
574
+ }
548
575
  out[p + 3] = 255;
549
576
  }
550
577
  return out;