@typecad/ui 1.0.0-alpha.13 → 1.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1457 -1469
- package/dist/cli.js +22 -19
- package/dist/preview/host-ui-runtime.js +0 -1
- package/dist/ui-engine/model.js +7 -1
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -115
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -182
- package/dist/ui-engine/runtime-header/forward-decls.js +227 -216
- package/dist/ui-engine/runtime-header/init-press-input.js +138 -138
- package/dist/ui-engine/runtime-header/keyboard.js +676 -676
- package/dist/ui-engine/runtime-header/node-draw-body.js +1658 -1656
- package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -259
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -736
- package/dist/ui-engine/runtime-header/scroll-physics.js +4 -4
- package/dist/ui-engine/runtime-header/structs.js +228 -228
- package/dist/ui-engine/runtime-header/text-rendering.js +888 -888
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -118
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +896 -896
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -24
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +11 -11
- package/dist/ui-engine/runtime-header/types-defines.js +110 -110
- package/dist/ui-engine/shadcn-kit.d.ts +1 -1
- package/dist/ui-engine/shadcn-kit.js +4 -0
- package/dist/ui-engine/ui-lowering.d.ts +1 -1
- package/dist/ui-engine/ui-lowering.js +14 -5
- package/dist/ui-engine/ui-registry.js +9 -1
- package/dist/wizard/display-catalog.d.ts +0 -3
- package/dist/wizard/display-catalog.js +3 -12
- package/dist/wizard/index.d.ts +2 -0
- package/dist/wizard/index.js +1 -0
- package/dist/wizard/integration-wizard.d.ts +3 -0
- package/dist/wizard/integration-wizard.js +69 -20
- package/dist/wizard/package-writer.d.ts +20 -0
- package/dist/wizard/package-writer.js +79 -0
- package/package.json +4 -4
- package/src/cli.ts +90 -87
- package/src/preview/host-ui-runtime.ts +0 -1
- package/src/ui-engine/model.ts +6 -1
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -121
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -188
- package/src/ui-engine/runtime-header/forward-decls.ts +238 -227
- package/src/ui-engine/runtime-header/init-press-input.ts +144 -144
- package/src/ui-engine/runtime-header/keyboard.ts +689 -689
- package/src/ui-engine/runtime-header/node-draw-body.ts +1683 -1681
- package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -265
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -742
- package/src/ui-engine/runtime-header/scroll-physics.ts +4 -4
- package/src/ui-engine/runtime-header/structs.ts +234 -234
- package/src/ui-engine/runtime-header/text-rendering.ts +894 -894
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -124
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -902
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -30
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +11 -11
- package/src/ui-engine/runtime-header/types-defines.ts +116 -116
- package/src/ui-engine/shadcn-kit.ts +4 -0
- package/src/ui-engine/ui-lowering.ts +12 -4
- package/src/ui-engine/ui-registry.ts +9 -1
- package/src/wizard/display-catalog.ts +261 -273
- package/src/wizard/index.ts +46 -38
- package/src/wizard/integration-wizard.ts +679 -619
- package/src/wizard/package-writer.ts +98 -0
|
@@ -2,123 +2,123 @@
|
|
|
2
2
|
// ui_tick signature + opening, caret blink re-mark, bindings/list-bindings/input-bindings. Contains ui_tick opening.
|
|
3
3
|
// See docs/superpowers/specs/2026-07-12-split-runtime-header-design.md.
|
|
4
4
|
export function emitTickBindingsPhase() {
|
|
5
|
-
return `
|
|
6
|
-
static inline void ui_tick(uint16_t deltaMs) {
|
|
7
|
-
// Touch poll — runs if touch is configured (defined by the emit layer)
|
|
8
|
-
ui_poll_touch();
|
|
9
|
-
// <drawer> slide animation (device parity with the preview's applyDrawers).
|
|
10
|
-
ui_drawer_tick(deltaMs);
|
|
11
|
-
// Allocate the retained framebuffer before transitions run. Geometry repair
|
|
12
|
-
// must clear old animation footprints into RAM, not directly onto the panel.
|
|
13
|
-
(void)ui_get_framebuffer();
|
|
14
|
-
// ⓪' Poll GPIO inputs
|
|
15
|
-
ui_poll_inputs();
|
|
16
|
-
#if defined(UI_HIDE_OSK)
|
|
17
|
-
// Advance the caret blink phase for the active input (desktop target).
|
|
18
|
-
__ui_kb_blink++;
|
|
19
|
-
// Re-mark the edited input dirty every ~16 ticks so the blink repainting
|
|
20
|
-
// keeps cycling even when no other state changes (otherwise the caret
|
|
21
|
-
// freezes once the typed-text dirty clears).
|
|
22
|
-
if (__ui_kb_visible && __ui_kb_target >= 0 && (__ui_kb_blink & 0x10)) {
|
|
23
|
-
ui_mark_dirty(static_cast<uint16_t>(__ui_kb_target));
|
|
24
|
-
}
|
|
25
|
-
#endif
|
|
26
|
-
// ⓪ Evaluate bindings: call each binding's fn, compare to the node's
|
|
27
|
-
// current property value, mark dirty if changed.
|
|
28
|
-
for (uint16_t i = 0; i < __ui_binding_count; i++) {
|
|
29
|
-
if (__ui_bindings[i].prop == PROP_TEXT && __ui_bindings[i].textFn) {
|
|
30
|
-
// Text callbacks are necessarily evaluated to detect changes (the callback
|
|
31
|
-
// may format a signal or read external state), but avoid a stack-sized old
|
|
32
|
-
// copy: compare the generated value against the node buffer after writing.
|
|
33
|
-
// The callback contract is fill-style and must be deterministic for the same
|
|
34
|
-
// inputs; the temporary buffer keeps a change from destroying the old text.
|
|
35
|
-
uint16_t n = __ui_bindings[i].node;
|
|
36
|
-
if (n >= __ui_node_count) continue;
|
|
37
|
-
char nextBuf[UI_TEXT_BUF + 1];
|
|
38
|
-
nextBuf[0] = '\\0';
|
|
39
|
-
__ui_bindings[i].textFn(nextBuf, UI_TEXT_BUF + 1);
|
|
40
|
-
nextBuf[UI_TEXT_BUF] = '\\0';
|
|
41
|
-
if (strcmp(nextBuf, __ui_nodes[n].textBuffer) != 0) {
|
|
42
|
-
strncpy(__ui_nodes[n].textBuffer, nextBuf, UI_TEXT_BUF);
|
|
43
|
-
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
44
|
-
ui_invalidate_text_layout_cache(n);
|
|
45
|
-
ui_mark_dirty(n);
|
|
46
|
-
}
|
|
47
|
-
} else if (__ui_bindings[i].fn) {
|
|
48
|
-
// Color/numeric binding
|
|
49
|
-
uint32_t newVal = __ui_bindings[i].fn();
|
|
50
|
-
if (__ui_bindings[i].prop == PROP_VISIBLE) {
|
|
51
|
-
uint8_t nextVisible = newVal ? 1 : 0;
|
|
52
|
-
if (nextVisible != __ui_nodes[__ui_bindings[i].node].visible) {
|
|
53
|
-
ui_set_visible(__ui_bindings[i].node, nextVisible);
|
|
54
|
-
}
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
if (__ui_bindings[i].prop == PROP_VALUE) {
|
|
58
|
-
// Numeric value binding: drive a progress/range node's value live.
|
|
59
|
-
uint16_t n = __ui_bindings[i].node;
|
|
60
|
-
int16_t v = static_cast<int16_t>(__ui_bindings[i].fn());
|
|
61
|
-
if (v != __ui_nodes[n].value) {
|
|
62
|
-
__ui_nodes[n].value = v;
|
|
63
|
-
ui_mark_dirty(n);
|
|
64
|
-
}
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
// Cache numeric binding results. The first sample establishes the baseline;
|
|
68
|
-
// subsequent identical values return immediately without touching node state.
|
|
69
|
-
if (__ui_bindings[i].initialized && newVal == __ui_bindings[i].lastValue) continue;
|
|
70
|
-
__ui_bindings[i].lastValue = newVal;
|
|
71
|
-
__ui_bindings[i].initialized = 1;
|
|
72
|
-
uint32_t* target = (__ui_bindings[i].prop == PROP_BG) ? &__ui_nodes[__ui_bindings[i].node].bg
|
|
73
|
-
: (__ui_bindings[i].prop == PROP_FG) ? &__ui_nodes[__ui_bindings[i].node].fg
|
|
74
|
-
: (__ui_bindings[i].prop == PROP_BORDER_COLOR) ? &__ui_nodes[__ui_bindings[i].node].borderColor
|
|
75
|
-
: &__ui_nodes[__ui_bindings[i].node].bg;
|
|
76
|
-
if (newVal != *target) {
|
|
77
|
-
*target = newVal;
|
|
78
|
-
// When a background binding writes a new color, ensure hasBg is set
|
|
79
|
-
// so the draw dispatch actually fills (the node may have started
|
|
80
|
-
// transparent but now has a runtime-assigned background).
|
|
81
|
-
if (__ui_bindings[i].prop == PROP_BG) __ui_nodes[__ui_bindings[i].node].hasBg = 1;
|
|
82
|
-
ui_mark_dirty(__ui_bindings[i].node);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
// ⓪b Evaluate list bindings (on-node): refresh item count, recompute content
|
|
87
|
-
// height, and advance any in-flight settle animation (bounce-back / edge-snap).
|
|
88
|
-
// Also note whether any active-screen node is settling (feeds scroll-motion
|
|
89
|
-
// gating below without a second full-node scan).
|
|
90
|
-
uint8_t settlingOnActiveScreen = 0;
|
|
91
|
-
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
92
|
-
if (__ui_nodes[i].virtualized && __ui_nodes[i].listCountFn) {
|
|
93
|
-
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
94
|
-
// Keep the callback result as the list's invalidation token. The callback
|
|
95
|
-
// is still evaluated because it is the public list-count source, but an
|
|
96
|
-
// unchanged token avoids layout/dirty work and preserves the retained
|
|
97
|
-
// viewport canvas.
|
|
98
|
-
uint16_t newCount = __ui_nodes[i].listCountFn();
|
|
99
|
-
if (newCount != __ui_nodes[i].listCount) {
|
|
100
|
-
__ui_nodes[i].listCount = newCount;
|
|
101
|
-
__ui_nodes[i].contentHeight = static_cast<int16_t>(static_cast<uint32_t>(newCount) * ih);
|
|
102
|
-
__ui_nodes[i].lastPaintedScrollY = __ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1);
|
|
103
|
-
ui_mark_dirty(i);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (__ui_nodes[i].settling) {
|
|
107
|
-
if (__ui_nodes[i].screenId == __ui_active_screen) settlingOnActiveScreen = 1;
|
|
108
|
-
ui_scroll_advance_settle(i, deltaMs);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
// ⓪c Evaluate input bindings (two-way): if a bound <input>'s textBuffer
|
|
112
|
-
// changed since last tick (e.g. the user typed via the on-screen keyboard),
|
|
113
|
-
// fire the author's callback with the new text.
|
|
114
|
-
for (uint16_t i = 0; i < __ui_input_binding_count; i++) {
|
|
115
|
-
if (!__ui_input_bindings[i].cb) continue;
|
|
116
|
-
uint16_t n = __ui_input_bindings[i].node;
|
|
117
|
-
if (n >= __ui_node_count) continue;
|
|
118
|
-
const char* cur = __ui_nodes[n].textBuffer;
|
|
119
|
-
if (strcmp(cur, __ui_input_bindings[i].lastSeen) != 0) {
|
|
120
|
-
strncpy(__ui_input_bindings[i].lastSeen, cur, UI_TEXT_BUF);
|
|
121
|
-
__ui_input_bindings[i].lastSeen[UI_TEXT_BUF] = '\\0';
|
|
122
|
-
__ui_input_bindings[i].cb(cur);
|
|
5
|
+
return `
|
|
6
|
+
static inline void ui_tick(uint16_t deltaMs) {
|
|
7
|
+
// Touch poll — runs if touch is configured (defined by the emit layer)
|
|
8
|
+
ui_poll_touch();
|
|
9
|
+
// <drawer> slide animation (device parity with the preview's applyDrawers).
|
|
10
|
+
ui_drawer_tick(deltaMs);
|
|
11
|
+
// Allocate the retained framebuffer before transitions run. Geometry repair
|
|
12
|
+
// must clear old animation footprints into RAM, not directly onto the panel.
|
|
13
|
+
(void)ui_get_framebuffer();
|
|
14
|
+
// ⓪' Poll GPIO inputs
|
|
15
|
+
ui_poll_inputs();
|
|
16
|
+
#if defined(UI_HIDE_OSK)
|
|
17
|
+
// Advance the caret blink phase for the active input (desktop target).
|
|
18
|
+
__ui_kb_blink++;
|
|
19
|
+
// Re-mark the edited input dirty every ~16 ticks so the blink repainting
|
|
20
|
+
// keeps cycling even when no other state changes (otherwise the caret
|
|
21
|
+
// freezes once the typed-text dirty clears).
|
|
22
|
+
if (__ui_kb_visible && __ui_kb_target >= 0 && (__ui_kb_blink & 0x10)) {
|
|
23
|
+
ui_mark_dirty(static_cast<uint16_t>(__ui_kb_target));
|
|
24
|
+
}
|
|
25
|
+
#endif
|
|
26
|
+
// ⓪ Evaluate bindings: call each binding's fn, compare to the node's
|
|
27
|
+
// current property value, mark dirty if changed.
|
|
28
|
+
for (uint16_t i = 0; i < __ui_binding_count; i++) {
|
|
29
|
+
if (__ui_bindings[i].prop == PROP_TEXT && __ui_bindings[i].textFn) {
|
|
30
|
+
// Text callbacks are necessarily evaluated to detect changes (the callback
|
|
31
|
+
// may format a signal or read external state), but avoid a stack-sized old
|
|
32
|
+
// copy: compare the generated value against the node buffer after writing.
|
|
33
|
+
// The callback contract is fill-style and must be deterministic for the same
|
|
34
|
+
// inputs; the temporary buffer keeps a change from destroying the old text.
|
|
35
|
+
uint16_t n = __ui_bindings[i].node;
|
|
36
|
+
if (n >= __ui_node_count) continue;
|
|
37
|
+
char nextBuf[UI_TEXT_BUF + 1];
|
|
38
|
+
nextBuf[0] = '\\0';
|
|
39
|
+
__ui_bindings[i].textFn(nextBuf, UI_TEXT_BUF + 1);
|
|
40
|
+
nextBuf[UI_TEXT_BUF] = '\\0';
|
|
41
|
+
if (strcmp(nextBuf, __ui_nodes[n].textBuffer) != 0) {
|
|
42
|
+
strncpy(__ui_nodes[n].textBuffer, nextBuf, UI_TEXT_BUF);
|
|
43
|
+
__ui_nodes[n].textBuffer[UI_TEXT_BUF] = '\\0';
|
|
44
|
+
ui_invalidate_text_layout_cache(n);
|
|
45
|
+
ui_mark_dirty(n);
|
|
46
|
+
}
|
|
47
|
+
} else if (__ui_bindings[i].fn) {
|
|
48
|
+
// Color/numeric binding
|
|
49
|
+
uint32_t newVal = __ui_bindings[i].fn();
|
|
50
|
+
if (__ui_bindings[i].prop == PROP_VISIBLE) {
|
|
51
|
+
uint8_t nextVisible = newVal ? 1 : 0;
|
|
52
|
+
if (nextVisible != __ui_nodes[__ui_bindings[i].node].visible) {
|
|
53
|
+
ui_set_visible(__ui_bindings[i].node, nextVisible);
|
|
54
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (__ui_bindings[i].prop == PROP_VALUE) {
|
|
58
|
+
// Numeric value binding: drive a progress/range node's value live.
|
|
59
|
+
uint16_t n = __ui_bindings[i].node;
|
|
60
|
+
int16_t v = static_cast<int16_t>(__ui_bindings[i].fn());
|
|
61
|
+
if (v != __ui_nodes[n].value) {
|
|
62
|
+
__ui_nodes[n].value = v;
|
|
63
|
+
ui_mark_dirty(n);
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
// Cache numeric binding results. The first sample establishes the baseline;
|
|
68
|
+
// subsequent identical values return immediately without touching node state.
|
|
69
|
+
if (__ui_bindings[i].initialized && newVal == __ui_bindings[i].lastValue) continue;
|
|
70
|
+
__ui_bindings[i].lastValue = newVal;
|
|
71
|
+
__ui_bindings[i].initialized = 1;
|
|
72
|
+
uint32_t* target = (__ui_bindings[i].prop == PROP_BG) ? &__ui_nodes[__ui_bindings[i].node].bg
|
|
73
|
+
: (__ui_bindings[i].prop == PROP_FG) ? &__ui_nodes[__ui_bindings[i].node].fg
|
|
74
|
+
: (__ui_bindings[i].prop == PROP_BORDER_COLOR) ? &__ui_nodes[__ui_bindings[i].node].borderColor
|
|
75
|
+
: &__ui_nodes[__ui_bindings[i].node].bg;
|
|
76
|
+
if (newVal != *target) {
|
|
77
|
+
*target = newVal;
|
|
78
|
+
// When a background binding writes a new color, ensure hasBg is set
|
|
79
|
+
// so the draw dispatch actually fills (the node may have started
|
|
80
|
+
// transparent but now has a runtime-assigned background).
|
|
81
|
+
if (__ui_bindings[i].prop == PROP_BG) __ui_nodes[__ui_bindings[i].node].hasBg = 1;
|
|
82
|
+
ui_mark_dirty(__ui_bindings[i].node);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ⓪b Evaluate list bindings (on-node): refresh item count, recompute content
|
|
87
|
+
// height, and advance any in-flight settle animation (bounce-back / edge-snap).
|
|
88
|
+
// Also note whether any active-screen node is settling (feeds scroll-motion
|
|
89
|
+
// gating below without a second full-node scan).
|
|
90
|
+
uint8_t settlingOnActiveScreen = 0;
|
|
91
|
+
for (uint16_t i = 0; i < __ui_node_count; i++) {
|
|
92
|
+
if (__ui_nodes[i].virtualized && __ui_nodes[i].listCountFn) {
|
|
93
|
+
uint16_t ih = __ui_nodes[i].listItemHeight > 0 ? __ui_nodes[i].listItemHeight : 24;
|
|
94
|
+
// Keep the callback result as the list's invalidation token. The callback
|
|
95
|
+
// is still evaluated because it is the public list-count source, but an
|
|
96
|
+
// unchanged token avoids layout/dirty work and preserves the retained
|
|
97
|
+
// viewport canvas.
|
|
98
|
+
uint16_t newCount = __ui_nodes[i].listCountFn();
|
|
99
|
+
if (newCount != __ui_nodes[i].listCount) {
|
|
100
|
+
__ui_nodes[i].listCount = newCount;
|
|
101
|
+
__ui_nodes[i].contentHeight = static_cast<int16_t>(static_cast<uint32_t>(newCount) * ih);
|
|
102
|
+
__ui_nodes[i].lastPaintedScrollY = __ui_nodes[i].scrollY - (__ui_nodes[i].box.h > 0 ? __ui_nodes[i].box.h : 1);
|
|
103
|
+
ui_mark_dirty(i);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (__ui_nodes[i].settling) {
|
|
107
|
+
if (__ui_nodes[i].screenId == __ui_active_screen) settlingOnActiveScreen = 1;
|
|
108
|
+
ui_scroll_advance_settle(i, deltaMs);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ⓪c Evaluate input bindings (two-way): if a bound <input>'s textBuffer
|
|
112
|
+
// changed since last tick (e.g. the user typed via the on-screen keyboard),
|
|
113
|
+
// fire the author's callback with the new text.
|
|
114
|
+
for (uint16_t i = 0; i < __ui_input_binding_count; i++) {
|
|
115
|
+
if (!__ui_input_bindings[i].cb) continue;
|
|
116
|
+
uint16_t n = __ui_input_bindings[i].node;
|
|
117
|
+
if (n >= __ui_node_count) continue;
|
|
118
|
+
const char* cur = __ui_nodes[n].textBuffer;
|
|
119
|
+
if (strcmp(cur, __ui_input_bindings[i].lastSeen) != 0) {
|
|
120
|
+
strncpy(__ui_input_bindings[i].lastSeen, cur, UI_TEXT_BUF);
|
|
121
|
+
__ui_input_bindings[i].lastSeen[UI_TEXT_BUF] = '\\0';
|
|
122
|
+
__ui_input_bindings[i].cb(cur);
|
|
123
123
|
}`;
|
|
124
124
|
}
|