@typecad/ui 1.0.0-alpha.13 → 1.0.0-alpha.14
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/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/node-draw-body.js +23 -21
- package/dist/ui-engine/shadcn-kit.d.ts +1 -1
- package/dist/ui-engine/shadcn-kit.js +4 -0
- 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 +67 -7
- 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/node-draw-body.ts +23 -21
- package/src/ui-engine/shadcn-kit.ts +4 -0
- package/src/wizard/index.ts +46 -38
- package/src/wizard/integration-wizard.ts +689 -619
- package/src/wizard/package-writer.ts +98 -0
package/src/cli.ts
CHANGED
|
@@ -1,87 +1,90 @@
|
|
|
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
|
-
|
|
10
|
-
import { runIntegrationWizard } from "./wizard/integration-wizard.js";
|
|
11
|
-
|
|
12
|
-
const USAGE = `
|
|
13
|
-
@typecad/ui — HTML/CSS-driven graphics for microcontrollers
|
|
14
|
-
|
|
15
|
-
Usage:
|
|
16
|
-
npx @typecad/ui --config Configure a display (+ touch) for your
|
|
17
|
-
cuttlefish project interactively. Writes the
|
|
18
|
-
\`display\` section of cuttlefish.config.ts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
console.log("unknown");
|
|
58
|
-
return 0;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
console.error("
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
);
|
|
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
|
+
|
|
10
|
+
import { runIntegrationWizard } from "./wizard/integration-wizard.js";
|
|
11
|
+
|
|
12
|
+
const USAGE = `
|
|
13
|
+
@typecad/ui — HTML/CSS-driven graphics for microcontrollers
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
npx @typecad/ui --config Configure a display (+ touch) for your
|
|
17
|
+
cuttlefish project interactively. Writes the
|
|
18
|
+
\`display\` section of cuttlefish.config.ts
|
|
19
|
+
and a \`preview\` npm script.
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--config Run the integration wizard
|
|
23
|
+
--help, -h Show this help
|
|
24
|
+
--version, -v Print the installed @typecad/ui version
|
|
25
|
+
|
|
26
|
+
The wizard asks which display module you are using (ILI9341 / ST7796S SPI TFT,
|
|
27
|
+
SSD1309 I2C OLED, desktop simulator, or custom), then walks through bus pins,
|
|
28
|
+
SPI/I2C speed, rotation, and touch controller wiring with hardware-aware
|
|
29
|
+
defaults. It never touches the rest of your cuttlefish.config.ts, and adds a
|
|
30
|
+
\`preview\` script to package.json so the desktop preview renderer starts with
|
|
31
|
+
\`npm run preview\`.
|
|
32
|
+
|
|
33
|
+
Docs: https://github.com/justind000/typecode/tree/main/packages/ui
|
|
34
|
+
`.trim();
|
|
35
|
+
|
|
36
|
+
function printUsage(): void {
|
|
37
|
+
console.log(USAGE);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function main(argv: string[]): Promise<number> {
|
|
41
|
+
const args = argv.slice(2);
|
|
42
|
+
|
|
43
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
44
|
+
printUsage();
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
49
|
+
// Lazily read the version so importing this module stays side-effect free.
|
|
50
|
+
const { readFile } = await import("node:fs/promises");
|
|
51
|
+
const { fileURLToPath } = await import("node:url");
|
|
52
|
+
const { dirname, join } = await import("node:path");
|
|
53
|
+
try {
|
|
54
|
+
const packageJson = JSON.parse(
|
|
55
|
+
await readFile(join(dirname(dirname(fileURLToPath(import.meta.url))), "package.json"), "utf-8"),
|
|
56
|
+
) as { version?: string };
|
|
57
|
+
console.log(packageJson.version ?? "unknown");
|
|
58
|
+
return 0;
|
|
59
|
+
} catch {
|
|
60
|
+
console.log("unknown");
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (args.length === 0 || (args.length === 1 && (args[0] === "--config" || args[0] === "config"))) {
|
|
66
|
+
if (!process.stdin.isTTY) {
|
|
67
|
+
console.error("The integration wizard is interactive and needs a terminal.");
|
|
68
|
+
console.error("Run `npx @typecad/ui --config` from your project directory, or configure");
|
|
69
|
+
console.error("the `display` section of cuttlefish.config.ts manually:");
|
|
70
|
+
console.error("https://github.com/justind000/typecode/tree/main/packages/ui#display-configuration");
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
const result = await runIntegrationWizard(process.cwd());
|
|
74
|
+
return result.exitCode;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.error(`Unknown option${args.length > 1 ? "s" : ""}: ${args.join(" ")}\n`);
|
|
78
|
+
printUsage();
|
|
79
|
+
return 2;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main(process.argv).then(
|
|
83
|
+
(code) => {
|
|
84
|
+
process.exitCode = code;
|
|
85
|
+
},
|
|
86
|
+
(error) => {
|
|
87
|
+
console.error(error instanceof Error ? error.message : error);
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
},
|
|
90
|
+
);
|
|
@@ -665,7 +665,6 @@ export class PreviewUIRuntime {
|
|
|
665
665
|
}
|
|
666
666
|
|
|
667
667
|
private navigate(screenIdx: number): void {
|
|
668
|
-
this.onDiagnostics?.(`NAV -> screen ${screenIdx}`);
|
|
669
668
|
const next = Math.trunc(Number(screenIdx));
|
|
670
669
|
if (!Number.isFinite(next) || next < 0 || next >= this.screenCount || next === this.activeScreen) return;
|
|
671
670
|
this.activeScreen = next;
|
package/src/ui-engine/model.ts
CHANGED
|
@@ -322,9 +322,14 @@ function borderStyle(style: CSSProperty): 0 | 1 | 2 {
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
function borderWidthOf(style: CSSProperty): number {
|
|
325
|
+
// A none-style border renders nothing and must consume no space either:
|
|
326
|
+
// the draw path subtracts borderWidth*2 from the text max width, so a
|
|
327
|
+
// residual UA width on a borderless kit button wrapped its last glyph
|
|
328
|
+
// ("Primar" / "y"). Style none wins over any declared width.
|
|
329
|
+
if (borderStyle(style) === 0) return 0;
|
|
325
330
|
const px = cssPx(style.borderWidth);
|
|
326
331
|
if (px > 0) return Math.max(1, Math.min(8, px));
|
|
327
|
-
return
|
|
332
|
+
return 1;
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
/** Per-side border width: the per-side field wins when set, else falls back to
|
|
@@ -1237,10 +1237,16 @@ static inline uint8_t ui_render_screen_bands(int16_t rx, int16_t ry, int16_t rw,
|
|
|
1237
1237
|
int16_t origBoxH = __ui_nodes[c].box.h;
|
|
1238
1238
|
__ui_nodes[c].box.x = static_cast<int16_t>(origBoxX - rx);
|
|
1239
1239
|
__ui_nodes[c].box.y = static_cast<int16_t>(origBoxY - ry - bandTop);
|
|
1240
|
-
// Clamp the face to the scroll viewport (band-local coords). The
|
|
1241
|
-
//
|
|
1242
|
-
//
|
|
1243
|
-
//
|
|
1240
|
+
// Clamp the face to the scroll viewport (band-local DRAW coords). The
|
|
1241
|
+
// clamp must operate on the node's DRAW position — box.y is
|
|
1242
|
+
// content-local and the ancestor scroll is subtracted later, inside
|
|
1243
|
+
// ui_draw_y_for_node, so comparing box.y against display-space clip
|
|
1244
|
+
// bounds passed scrolled content whose CONTENT coordinate numerically
|
|
1245
|
+
// fell inside the viewport: a button scrolled up past the viewport top
|
|
1246
|
+
// painted over the header, and the smearing stuck (nothing repaints
|
|
1247
|
+
// the header afterwards). Clamp the draw position and shift the box by
|
|
1248
|
+
// the delta; text wrapping and layout metrics shrink with the box.
|
|
1249
|
+
// Fully clipped faces drop out here.
|
|
1244
1250
|
{
|
|
1245
1251
|
// int32 math: the unclipped sentinels are ±32767 and the band-local
|
|
1246
1252
|
// conversion subtracts region/band offsets — int16 arithmetic wraps
|
|
@@ -1251,29 +1257,25 @@ static inline uint8_t ui_render_screen_bands(int16_t rx, int16_t ry, int16_t rw,
|
|
|
1251
1257
|
int32_t clipBotL = static_cast<int32_t>(__ui_scroll_candidates[ci].clipBottom) - ry - bandTop;
|
|
1252
1258
|
int32_t clipLeftL = static_cast<int32_t>(__ui_scroll_candidates[ci].clipLeft) - rx;
|
|
1253
1259
|
int32_t clipRightL = static_cast<int32_t>(__ui_scroll_candidates[ci].clipRight) - rx;
|
|
1254
|
-
int16_t
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
}
|
|
1262
|
-
int16_t boxX0 = __ui_nodes[c].box.x;
|
|
1263
|
-
if (boxX0 < clipLeftL) {
|
|
1264
|
-
__ui_nodes[c].box.x = static_cast<int16_t>(clipLeftL);
|
|
1265
|
-
__ui_nodes[c].box.w = static_cast<int16_t>(__ui_nodes[c].box.w - (clipLeftL - boxX0));
|
|
1266
|
-
}
|
|
1267
|
-
if (static_cast<int32_t>(__ui_nodes[c].box.x) + __ui_nodes[c].box.w > clipRightL) {
|
|
1268
|
-
__ui_nodes[c].box.w = static_cast<int16_t>(clipRightL - __ui_nodes[c].box.x);
|
|
1269
|
-
}
|
|
1270
|
-
if (__ui_nodes[c].box.w <= 0 || __ui_nodes[c].box.h <= 0) {
|
|
1260
|
+
int16_t dispY0 = ui_draw_y_for_node(c);
|
|
1261
|
+
int16_t dispX0 = ui_draw_x_for_node(c);
|
|
1262
|
+
int32_t y0 = dispY0 < clipTopL ? clipTopL : dispY0;
|
|
1263
|
+
int32_t y1 = static_cast<int32_t>(dispY0) + __ui_nodes[c].box.h > clipBotL ? clipBotL : static_cast<int32_t>(dispY0) + __ui_nodes[c].box.h;
|
|
1264
|
+
int32_t x0 = dispX0 < clipLeftL ? clipLeftL : dispX0;
|
|
1265
|
+
int32_t x1 = static_cast<int32_t>(dispX0) + __ui_nodes[c].box.w > clipRightL ? clipRightL : static_cast<int32_t>(dispX0) + __ui_nodes[c].box.w;
|
|
1266
|
+
if (x1 - x0 <= 0 || y1 - y0 <= 0) {
|
|
1271
1267
|
__ui_nodes[c].box.x = origBoxX;
|
|
1272
1268
|
__ui_nodes[c].box.y = origBoxY;
|
|
1273
1269
|
__ui_nodes[c].box.w = origBoxW;
|
|
1274
1270
|
__ui_nodes[c].box.h = origBoxH;
|
|
1275
1271
|
continue;
|
|
1276
1272
|
}
|
|
1273
|
+
// box.y/box.x already carry the band shift; add the clamp delta on
|
|
1274
|
+
// top (y0 - dispY0 is 0 when unclipped, preserving the shift).
|
|
1275
|
+
__ui_nodes[c].box.y = static_cast<int16_t>(__ui_nodes[c].box.y + (y0 - dispY0));
|
|
1276
|
+
__ui_nodes[c].box.h = static_cast<int16_t>(y1 - y0);
|
|
1277
|
+
__ui_nodes[c].box.x = static_cast<int16_t>(__ui_nodes[c].box.x + (x0 - dispX0));
|
|
1278
|
+
__ui_nodes[c].box.w = static_cast<int16_t>(x1 - x0);
|
|
1277
1279
|
}
|
|
1278
1280
|
int16_t baseDrawX = ui_base_draw_x_for_node(c);
|
|
1279
1281
|
int16_t baseDrawY = ui_base_draw_y_for_node(c);
|
|
@@ -136,6 +136,10 @@ export const SHADCN_KIT_CSS = `/* ----------------------------------------------
|
|
|
136
136
|
padding: 10px 16px;
|
|
137
137
|
font-weight: bold;
|
|
138
138
|
transition: background 80ms;
|
|
139
|
+
/* Reset the UA's browser-default button border (shadcn's button reset):
|
|
140
|
+
ghost must be truly borderless — variants that want one (outline)
|
|
141
|
+
re-declare it themselves. */
|
|
142
|
+
border: none;
|
|
139
143
|
}
|
|
140
144
|
.btn:pressed { transform: translateY(1px); }
|
|
141
145
|
.btn-primary { background: var(--primary); color: var(--primary-foreground); }
|
package/src/wizard/index.ts
CHANGED
|
@@ -1,38 +1,46 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Public surface of the @typecad/ui integration wizard (`npx @typecad/ui
|
|
3
|
-
// --config`). Pure helpers (catalog, rendering, config splicing, starter
|
|
4
|
-
// template) are exported for tests and tooling; the interactive flow lives in
|
|
5
|
-
// integration-wizard.ts.
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
DISPLAY_CATALOG,
|
|
10
|
-
TOUCH_CATALOG,
|
|
11
|
-
findPinConflicts,
|
|
12
|
-
} from "./display-catalog.js";
|
|
13
|
-
export type {
|
|
14
|
-
DisplayCatalogEntry,
|
|
15
|
-
TouchCatalogEntry,
|
|
16
|
-
WizardBus,
|
|
17
|
-
WizardTouchKind,
|
|
18
|
-
} from "./display-catalog.js";
|
|
19
|
-
|
|
20
|
-
export {
|
|
21
|
-
findCuttlefishConfig,
|
|
22
|
-
findSyntaxError,
|
|
23
|
-
readConfigSection,
|
|
24
|
-
readEntryPath,
|
|
25
|
-
renderDisplayBody,
|
|
26
|
-
renderDisplayProperty,
|
|
27
|
-
upsertDisplaySection,
|
|
28
|
-
} from "./config-writer.js";
|
|
29
|
-
export type {
|
|
30
|
-
ConfigRecord,
|
|
31
|
-
RenderDisplayOptions,
|
|
32
|
-
UpsertDisplayResult,
|
|
33
|
-
} from "./config-writer.js";
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Public surface of the @typecad/ui integration wizard (`npx @typecad/ui
|
|
3
|
+
// --config`). Pure helpers (catalog, rendering, config splicing, starter
|
|
4
|
+
// template) are exported for tests and tooling; the interactive flow lives in
|
|
5
|
+
// integration-wizard.ts.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
DISPLAY_CATALOG,
|
|
10
|
+
TOUCH_CATALOG,
|
|
11
|
+
findPinConflicts,
|
|
12
|
+
} from "./display-catalog.js";
|
|
13
|
+
export type {
|
|
14
|
+
DisplayCatalogEntry,
|
|
15
|
+
TouchCatalogEntry,
|
|
16
|
+
WizardBus,
|
|
17
|
+
WizardTouchKind,
|
|
18
|
+
} from "./display-catalog.js";
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
findCuttlefishConfig,
|
|
22
|
+
findSyntaxError,
|
|
23
|
+
readConfigSection,
|
|
24
|
+
readEntryPath,
|
|
25
|
+
renderDisplayBody,
|
|
26
|
+
renderDisplayProperty,
|
|
27
|
+
upsertDisplaySection,
|
|
28
|
+
} from "./config-writer.js";
|
|
29
|
+
export type {
|
|
30
|
+
ConfigRecord,
|
|
31
|
+
RenderDisplayOptions,
|
|
32
|
+
UpsertDisplayResult,
|
|
33
|
+
} from "./config-writer.js";
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
findPackageJson,
|
|
37
|
+
previewScriptCommand,
|
|
38
|
+
readPackageScript,
|
|
39
|
+
upsertPackageScript,
|
|
40
|
+
} from "./package-writer.js";
|
|
41
|
+
export type { UpsertScriptResult } from "./package-writer.js";
|
|
42
|
+
|
|
43
|
+
export { renderStarterUi } from "./starter-ui.js";
|
|
44
|
+
|
|
45
|
+
export { runIntegrationWizard } from "./integration-wizard.js";
|
|
46
|
+
export type { WizardRunResult, WizardStreams } from "./integration-wizard.js";
|