@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
package/README.md CHANGED
@@ -13,6 +13,26 @@ npm install --save-dev @typecad/cuttlefish
13
13
 
14
14
  `@typecad/ui` is compile-time only — none of its code is shipped to the device. The transpiler intercepts `ui.mount` / `ui.signal` / `ui.bind` / ... calls and lowers them to device variables and binding-table entries, so the package can be safely kept in `dependencies`.
15
15
 
16
+ ### Integration wizard
17
+
18
+ Integrating a display is the one step with real hardware decisions — which panel, which bus, which pins, whether there's touch and how it connects. Run the wizard from your project directory:
19
+
20
+ ```bash
21
+ npx @typecad/ui --config
22
+ ```
23
+
24
+ It asks:
25
+
26
+ 1. **Display** — a built-in profile (ILI9341 320×240 SPI TFT, ST7796S 320×480 SPI TFT, SSD1309 128×64 I2C OLED), the desktop SDL simulator, or a fully custom driver.
27
+ 2. **Bus wiring** — SPI pins (CS/DC/RST/backlight, frequency in MHz, optional SCK/MOSI/MISO override) or the I2C address. Defaults match the framework profiles and the repo's demo wiring (ESP32 VSPI), and any existing `display` section in your config pre-fills the answers.
28
+ 3. **Orientation & rendering** — rotation and antialiasing, with advanced color-order/inversion options behind a confirm.
29
+ 4. **Touch** — none, resistive (XPT2046, STMPE610, 4-wire analog), capacitive (FT6336U, GT911, CST816S), or a custom adapter file — each with its pins, address, and a sensible default calibration.
30
+ 5. **Theme** — optional `themeCss` / `themeClass`.
31
+
32
+ After a summary preview and confirmation, the wizard splices only the `display` section into `cuttlefish.config.ts` — every other section (and its comments) is preserved byte-for-byte, unmanaged display keys like `scroll` are carried over, and the result is syntax-checked before anything is written. If your config's `entry` points at a `.ui` file that doesn't exist yet, it offers to generate a starter screen, and it prints the exact `arduino-cli lib install ...`, preview, compile, and flash commands as next steps.
33
+
34
+ If there is no `cuttlefish.config.ts` yet, create the project first with `npx @typecad/cuttlefish init`, then re-run the wizard.
35
+
16
36
  ## Project layout
17
37
 
18
38
  A TypeCAD UI project has one **entry** — the file you point `cuttlefish.config.ts` at. The entry can be either a `.ui` single-file component or a plain `.ts` module. Both intermix freely with regular cuttlefish TypeScript (HAL pin reads, `setInterval`, `console.log`, your own `.ts` modules) — the `<script>` block of a `.ui` file and a standalone `.ts` file are lowered by the same pipeline.
@@ -217,7 +237,7 @@ The fastest path is a single `.ui` file with markup, styling, and behavior toget
217
237
  </screen>
218
238
  ```
219
239
 
220
- Display hardware and wiring live in `cuttlefish.config.ts` under `display`, so the UI source stays focused on UI behavior.
240
+ Display hardware and wiring live in `cuttlefish.config.ts` under `display`, so the UI source stays focused on UI behavior. Run `npx @typecad/ui --config` to generate that section interactively (see [Integration wizard](#integration-wizard)) — the result looks like the `display` line below.
221
241
 
222
242
  ### 2. Point the entry at the `.ui` file
223
243
 
@@ -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/
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ // ---------------------------------------------------------------------------
3
+ // @typecad/ui CLI — display integration wizard.
4
+ //
5
+ // npx @typecad/ui --config Run the interactive integration wizard
6
+ // npx @typecad/ui --help Show usage
7
+ // npx @typecad/ui --version Show the installed version
8
+ // ---------------------------------------------------------------------------
9
+ 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
29
+ `.trim();
30
+ function printUsage() {
31
+ console.log(USAGE);
32
+ }
33
+ async function main(argv) {
34
+ const args = argv.slice(2);
35
+ if (args.includes("--help") || args.includes("-h")) {
36
+ printUsage();
37
+ return 0;
38
+ }
39
+ if (args.includes("--version") || args.includes("-v")) {
40
+ // Lazily read the version so importing this module stays side-effect free.
41
+ const { readFile } = await import("node:fs/promises");
42
+ const { fileURLToPath } = await import("node:url");
43
+ const { dirname, join } = await import("node:path");
44
+ try {
45
+ const packageJson = JSON.parse(await readFile(join(dirname(dirname(fileURLToPath(import.meta.url))), "package.json"), "utf-8"));
46
+ console.log(packageJson.version ?? "unknown");
47
+ return 0;
48
+ }
49
+ catch {
50
+ console.log("unknown");
51
+ return 0;
52
+ }
53
+ }
54
+ if (args.length === 0 || (args.length === 1 && (args[0] === "--config" || args[0] === "config"))) {
55
+ if (!process.stdin.isTTY) {
56
+ console.error("The integration wizard is interactive and needs a terminal.");
57
+ console.error("Run `npx @typecad/ui --config` from your project directory, or configure");
58
+ console.error("the `display` section of cuttlefish.config.ts manually:");
59
+ console.error("https://github.com/justind000/typecode/tree/main/packages/ui#display-configuration");
60
+ return 1;
61
+ }
62
+ const result = await runIntegrationWizard(process.cwd());
63
+ return result.exitCode;
64
+ }
65
+ console.error(`Unknown option${args.length > 1 ? "s" : ""}: ${args.join(" ")}\n`);
66
+ printUsage();
67
+ return 2;
68
+ }
69
+ main(process.argv).then((code) => {
70
+ process.exitCode = code;
71
+ }, (error) => {
72
+ console.error(error instanceof Error ? error.message : error);
73
+ process.exitCode = 1;
74
+ });
@@ -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: {