@solidrt/cli 0.0.38 → 0.0.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidrt/cli",
3
- "version": "0.0.38",
3
+ "version": "0.0.39",
4
4
  "license": "MIT",
5
5
  "author": "Antoine van Wel",
6
6
  "type": "module",
@@ -22,23 +22,23 @@
22
22
  "@jridgewell/remapping": "^2.3.0",
23
23
  "@jridgewell/trace-mapping": "^0.3.25",
24
24
  "@modelcontextprotocol/sdk": "^1.29.0",
25
- "babel-preset-solid": "2.0.0-beta.20",
25
+ "babel-preset-solid": "2.0.0-beta.26",
26
26
  "bonjour-service": "^1.4.0",
27
27
  "qrcode-generator": "^2.0.4",
28
28
  "zod": "^4.4.3"
29
29
  },
30
30
  "optionalDependencies": {
31
- "@solidrt/darwin-arm64": "0.0.38",
32
- "@solidrt/linux-arm64-gnu": "0.0.38",
33
- "@solidrt/linux-x64-gnu": "0.0.38",
34
- "@solidrt/win32-x64-msvc": "0.0.38"
31
+ "@solidrt/darwin-arm64": "0.0.39",
32
+ "@solidrt/linux-arm64-gnu": "0.0.39",
33
+ "@solidrt/linux-x64-gnu": "0.0.39",
34
+ "@solidrt/win32-x64-msvc": "0.0.39"
35
35
  },
36
36
  "peerDependencies": {
37
- "@solidrt/core": "0.0.38",
37
+ "@solidrt/core": "0.0.39",
38
38
  "typescript": "^7"
39
39
  },
40
40
  "devDependencies": {
41
- "@solidrt/flux-types": "0.0.38",
41
+ "@solidrt/flux-types": "0.0.39",
42
42
  "@types/babel__core": "^7.20.5",
43
43
  "@types/bun": "latest"
44
44
  }
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">
2
+ <defs>
3
+ <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
4
+ <stop offset="0" stop-color="#5b7cfa"/>
5
+ <stop offset="1" stop-color="#8a5cf6"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="96" height="96" rx="22" fill="url(#bg)"/>
9
+ <circle cx="48" cy="48" r="21" fill="none" stroke="#ffffff" stroke-opacity="0.9" stroke-width="6"/>
10
+ <circle cx="48" cy="48" r="7" fill="#ffffff"/>
11
+ </svg>
@@ -9,12 +9,12 @@
9
9
  "android": "srt client --android"
10
10
  },
11
11
  "dependencies": {
12
- "@solidrt/core": "0.0.38",
13
- "@solidrt/components": "0.0.38"
12
+ "@solidrt/core": "0.0.39",
13
+ "@solidrt/components": "0.0.39"
14
14
  },
15
15
  "devDependencies": {
16
- "@solidrt/cli": "0.0.38",
17
- "@solidrt/flux-types": "0.0.38",
16
+ "@solidrt/cli": "0.0.39",
17
+ "@solidrt/flux-types": "0.0.39",
18
18
  "typescript": "^7"
19
19
  }
20
20
  }
@@ -141,9 +141,11 @@ export async function runInitCommand() {
141
141
 
142
142
  // The assets/ convention folder, created up front: everything in it ships
143
143
  // with the app, and the dev watcher only picks up an assets/ folder that
144
- // exists when it starts.
144
+ // exists when it starts. It starts with a placeholder app icon (picked up
145
+ // through the assets/icon.svg convention) for the author to replace.
145
146
  await mkdir(join(dir, "assets"), { recursive: true })
146
- console.log(" Write assets/")
147
+ await writeFile(join(dir, "assets", "icon.svg"), await readFile(join(SCAFFOLD_DIR, "icon.svg")))
148
+ console.log(" Write assets/icon.svg")
147
149
 
148
150
  // The scaffold package.json carries a placeholder name; set it from the
149
151
  // target folder. A core-level app gets no component framework dependency.
@@ -103,7 +103,7 @@ let TOOLS: {
103
103
  name: "get_stats",
104
104
  readOnly: true,
105
105
  description:
106
- "Performance statistics from a running app client: fps, CPU%, memory, smoothed JS/layout/paint/hover frame times (ms), setProperty writes per frame, demand-gate reuse/skip counts per second, and live texture count. Layout-activity counters cover the last full rebuild, raw: nodes (live node count, mounted AND detached), mountedNodes/orphanNodes (live at query time: nodes reachable from the root vs not - orphans growing at a stable tree shape mean an unmount leak; absent when no engine is running), measureCalls (text measures; mostly cache hits, cheap), paraShapes (paragraphs actually shaped; the expensive signal - high layoutMs with near-zero paraShapes means the cost is not text shaping), dirtiedNodes (layout caches cleared by property writes since the previous rebuild; how much of the tree a write burst invalidated), cacheGets/cacheHits (layout-cache lookups during the rebuild; a hit on a container skips its whole subtree, so a healthy incremental rebuild shows a near-100% hit rate - a low rate at scale means the layout cache is being defeated).",
106
+ "Performance statistics from a running app client: fps, CPU%, memory, smoothed JS/layout/paint/hover frame times (ms), setProperty writes per frame, demand-gate reuse/skip counts per second, and live texture count. Layout-activity counters cover the last full rebuild, raw: nodes (live node count, mounted AND detached), mountedNodes/orphanNodes (live at query time: nodes reachable from the root vs not - orphans growing at a stable tree shape mean an unmount leak; absent when no engine is running), measureCalls (text measures; mostly cache hits, cheap), paraShapes (paragraphs actually shaped; the expensive signal - high layoutMs with near-zero paraShapes means the cost is not text shaping), dirtiedNodes (layout caches cleared by property writes since the previous rebuild; how much of the tree a write burst invalidated), cacheGets/cacheHits (layout-cache lookups during the rebuild; a hit on a container skips its whole subtree, so a healthy incremental rebuild shows a near-100% hit rate - a low rate at scale means the layout cache is being defeated). GPU-side health, read live at query time (absent when no engine is running): rasterQueue (raster commands sent but not yet executed; stuck nonzero means the raster thread is backlogged - the state where fps and frameMs go blind because no frames complete), idleTicks (cumulative idle frame signals emitted while the GPU had nothing queued; idleTicks racing while rasterQueue sits nonzero would mean the idle-tick gate is broken), fenceTimeouts (cumulative present-fence waits that expired instead of signaling - each one is a frame where the GPU was over budget for 100ms+ and one-frame-in-flight pacing was lost; zero on a healthy machine, climbing means the GPU is the bottleneck right now).",
107
107
  inputSchema: { client: CLIENT_ARG },
108
108
  },
109
109
  {
@@ -137,9 +137,12 @@ let TOOLS: {
137
137
  name: "get_snapshot",
138
138
  readOnly: true,
139
139
  description:
140
- "Capture a PNG image of any node in a running app client's render tree, by node id (get ids from get_render_tree). Returns the rendered pixels of that node's subtree, so you can see what the app actually drew. The node must be currently mounted and have a non-zero layout box. Works on an idle client (the capture requests its own frame); a timeout means the client's JS thread is busy or wedged, not that the app is idle.",
140
+ "Capture a PNG image of any node in a running app client's render tree, by node id (get ids from get_render_tree). Returns the rendered pixels of that node's subtree, so you can see what the app actually drew. Capture the smallest node that contains what you are checking (e.g. the <texture> leaf itself) - that is exactly the content at its own pixel size; the window root is mostly empty layout around it and orders of magnitude more pixels. Reserve root captures for when layout/positioning itself is the question. The node must be currently mounted and have a non-zero layout box. Works on an idle client (the capture requests its own frame); a timeout means the client's JS thread is busy or wedged, not that the app is idle.",
141
141
  inputSchema: {
142
- nodeId: z.number().int().describe("Id of the node to capture, from get_render_tree"),
142
+ nodeId: z
143
+ .number()
144
+ .int()
145
+ .describe("Id of the node to capture, from get_render_tree; prefer the smallest relevant node over the root"),
143
146
  save_to: SAVE_TO_ARG,
144
147
  client: CLIENT_ARG,
145
148
  },
@@ -37,7 +37,7 @@ export type PackFolder = {
37
37
  export function buildPackFolder(entry: string, bytecode: Buffer): PackFolder {
38
38
  let identity = loadAppIdentity(entry)
39
39
  let projectDir = projectDirFor(resolve(entry))
40
- let { assets } = collectAssets(entry)
40
+ let { assets, icon } = collectAssets(entry)
41
41
  let copies = assets.map((a) => ({ from: join(projectDir, a.path), to: a.path }))
42
42
 
43
43
  // The full resolved font set: custom fonts are already collected assets;
@@ -70,6 +70,7 @@ export function buildPackFolder(entry: string, bytecode: Buffer): PackFolder {
70
70
  appId: identity.appId,
71
71
  org: identity.org,
72
72
  displayName: identity.displayName,
73
+ ...(icon ? { icon } : {}),
73
74
  runtimeVersion: RUNTIME_VERSION,
74
75
  solidrtVersion: SOLIDRT_VERSION,
75
76
  bundle: { path: "bundle.bin", sha256: hashHex(bytecode), size: bytecode.length },
package/src/project.ts CHANGED
@@ -8,7 +8,10 @@ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "nod
8
8
  // "appId": "com.example.app", // stable identity: storage dir, Android package id
9
9
  // "org": "Example", // optional display metadata (publisher)
10
10
  // "displayName": "Example App", // optional display metadata (launcher/window)
11
- // "fonts": { ... } // see fonts.ts
11
+ // "fonts": { ... }, // see fonts.ts
12
+ // "icon": "./assets/icon.svg" // optional app icon (SVG, under assets/);
13
+ // // an undeclared assets/icon.svg is picked
14
+ // // up by convention
12
15
  // }
13
16
  //
14
17
  // Everything defaults from the package name (or the entry filename when there
@@ -71,11 +74,13 @@ export const SOLIDRT_VERSION: string = pkgVersion === "0.0.0" ? "unknown" : pkgV
71
74
  export function buildManifest(code: string, entry: string): string {
72
75
  let identity = loadAppIdentity(entry)
73
76
  let sha256 = new Bun.CryptoHasher("sha256").update(code).digest("hex")
74
- let { assets, fonts } = collectAssets(entry)
77
+ let { assets, fonts, icon } = collectAssets(entry)
75
78
  return JSON.stringify({
76
79
  appId: identity.appId,
80
+ displayName: identity.displayName,
77
81
  runtimeVersion: RUNTIME_VERSION,
78
82
  solidrtVersion: SOLIDRT_VERSION,
83
+ ...(icon ? { icon } : {}),
79
84
  bundle: { path: "bundle.js", sha256, size: Buffer.byteLength(code, "utf8") },
80
85
  ...(assets.length ? { assets } : {}),
81
86
  ...(fonts.length ? { fonts } : {}),
@@ -116,11 +121,19 @@ function walkAssets(assetsDir: string, dir: string, out: ManifestAsset[]) {
116
121
 
117
122
  // The convention-first asset set: everything under the project's assets/
118
123
  // folder (next to package.json), collected wholesale in sorted order so the
119
- // manifest bytes are deterministic. Fonts are annotations pointing into that
120
- // set: `solidrt.fonts` path entries must live under assets/ so they reach dev
121
- // clients and the version store (`false` entries only drop pack defaults and
122
- // have no manifest presence).
123
- export function collectAssets(entry: string): { assets: ManifestAsset[]; fonts: ManifestFont[] } {
124
+ // manifest bytes are deterministic. Fonts and the icon are annotations
125
+ // pointing into that set: `solidrt.fonts` path entries and `solidrt.icon`
126
+ // must live under assets/ so they reach dev clients and the version store
127
+ // (`false` font entries only drop pack defaults and have no manifest
128
+ // presence). The icon is SVG-only for now: the launcher renders SVG natively,
129
+ // and the raster surfaces (window icon, OS embedding) come with later stages
130
+ // (okf/backlog/app-icons.md). An undeclared assets/icon.svg is picked up by
131
+ // convention.
132
+ export function collectAssets(entry: string): {
133
+ assets: ManifestAsset[]
134
+ fonts: ManifestFont[]
135
+ icon: string | null
136
+ } {
124
137
  let project = findProjectPackage(entry)
125
138
  let projectDir = projectDirFor(entry)
126
139
  let assetsDir = resolve(projectDir, "assets")
@@ -146,7 +159,27 @@ export function collectAssets(entry: string): { assets: ManifestAsset[]; fonts:
146
159
  fonts.push({ path, alias })
147
160
  }
148
161
  }
149
- return { assets, fonts }
162
+
163
+ let icon: string | null = null
164
+ let declared = project?.pkg.solidrt?.icon
165
+ if (declared !== undefined) {
166
+ if (typeof declared !== "string") fail('"solidrt": "icon" must be a string path')
167
+ let path = assetPathFor(projectDir, resolve(projectDir, declared))
168
+ if (!path) {
169
+ fail(`"solidrt.icon": ${declared} must live under assets/ (the icon ships as a version asset)`)
170
+ }
171
+ if (!path.toLowerCase().endsWith(".svg")) {
172
+ fail(`"solidrt.icon": ${declared} must be an .svg file`)
173
+ }
174
+ if (!assets.some((a) => a.path === path)) {
175
+ fail(`"solidrt.icon": no such file: ${resolve(projectDir, declared)}`)
176
+ }
177
+ icon = path
178
+ } else if (assets.some((a) => a.path === "assets/icon.svg")) {
179
+ icon = "assets/icon.svg"
180
+ }
181
+
182
+ return { assets, fonts, icon }
150
183
  }
151
184
 
152
185
  // Resolve the app identity for a pack. All three fields are guaranteed