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