@marver-design/marver 0.3.1 → 0.5.0

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 (50) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/README.md +2 -0
  3. package/dist/auth-B36fMCM3.mjs +245 -0
  4. package/dist/{build-Ckmyci3O.mjs → build-BrCl9hJS.mjs} +56 -14
  5. package/dist/cli.mjs +15 -6
  6. package/dist/collab-CXy8gqoz.mjs +297 -0
  7. package/dist/comments-Ba8mU600.mjs +90 -0
  8. package/dist/comments-odHzYdO3.mjs +179 -0
  9. package/dist/dev-DdeU-Jst.mjs +162 -0
  10. package/dist/{init-DolP_Ld4.mjs → init-DsCUmlCW.mjs} +2 -1
  11. package/dist/{manifest-DW-T52MM.mjs → manifest-C8FODq2S.mjs} +26 -1
  12. package/dist/{plugin-Crp4CAma.mjs → plugin-BtSGAm2h.mjs} +169 -19
  13. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  14. package/dist/serve-D_KBK7Oy.mjs +431 -0
  15. package/dist/sync-CkBk-tUk.mjs +248 -0
  16. package/package.json +3 -1
  17. package/src/client/content/diagram.tsx +46 -2
  18. package/src/client/content/index.tsx +6 -2
  19. package/src/client/content/md.ts +29 -0
  20. package/src/client/content/palette.ts +6 -0
  21. package/src/client/frame-host/bridge.js +269 -3
  22. package/src/client/frame-host/serialize.ts +195 -0
  23. package/src/client/shell/App.tsx +131 -17
  24. package/src/client/shell/Comments.tsx +464 -0
  25. package/src/client/shell/Play.tsx +24 -0
  26. package/src/client/shell/canvas/Canvas.tsx +66 -29
  27. package/src/client/shell/canvas/FrameNode.tsx +130 -10
  28. package/src/client/shell/canvas/frame-registry.ts +18 -0
  29. package/src/client/shell/canvas/snapshots.ts +233 -0
  30. package/src/client/shell/comments-store.ts +180 -0
  31. package/src/client/shell/cursor-arrow-dark.svg +1 -0
  32. package/src/client/shell/cursor-arrow.svg +1 -0
  33. package/src/client/shell/hash.ts +14 -3
  34. package/src/client/shell/icons.tsx +6 -0
  35. package/src/client/shell/labels.ts +10 -0
  36. package/src/client/shell/perf.ts +92 -0
  37. package/src/client/shell/store.ts +101 -6
  38. package/src/client/shell/styles.css +196 -7
  39. package/src/client/stage/main.tsx +2 -0
  40. package/src/shared/events.ts +103 -0
  41. package/templates/AGENTS-embedded.md +13 -1
  42. package/templates/AGENTS-studio.md +13 -1
  43. package/templates/instructions/boards.md +6 -3
  44. package/templates/instructions/configure.md +28 -0
  45. package/templates/instructions/iterate.md +38 -0
  46. package/templates/instructions/publish.md +129 -0
  47. package/templates/instructions/reference/color.md +22 -1
  48. package/templates/instructions/shape.md +23 -16
  49. package/dist/dev-C2oTKuXe.mjs +0 -97
  50. package/dist/serve-OtA9Nlow.mjs +0 -207
@@ -0,0 +1,162 @@
1
+ import { n as NAME, r as PKG } from "./cli.mjs";
2
+ import { o as loadConfig, s as detectHost } from "./manifest-C8FODq2S.mjs";
3
+ import { n as tailwind3Css, r as tailwind4Plugin, t as marverPlugin } from "./plugin-BtSGAm2h.mjs";
4
+ import { basename, dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { createLogger, createServer, searchForWorkspaceRoot } from "vite";
7
+ import react from "@vitejs/plugin-react";
8
+ import { createServer as createServer$1 } from "node:net";
9
+ //#region src/server/dev.ts
10
+ /** packageDir = the installed marver package root (dist/cli.js lives one level down). */
11
+ function packageDir() {
12
+ return join(dirname(fileURLToPath(import.meta.url)), "..");
13
+ }
14
+ const portFree = (p) => new Promise((res) => {
15
+ const s = createServer$1();
16
+ s.once("error", () => res(false));
17
+ s.once("listening", () => s.close(() => res(true)));
18
+ s.listen(p, "127.0.0.1");
19
+ });
20
+ /** C1: a deterministic per-project port in [5200,5399] derived from the root path. Two projects
21
+ * never collide, and the same project always lands on the same port across restarts. */
22
+ const projectPort = (root) => {
23
+ let h = 0;
24
+ for (let i = 0; i < root.length; i++) h = Math.imul(h, 31) + root.charCodeAt(i) | 0;
25
+ return 5200 + Math.abs(h) % 200;
26
+ };
27
+ /** Pick the port to serve on: the desired one if free, else a DETERMINISTIC per-project fallback -
28
+ * never a silent next-free drift, which made a bookmarked tab silently serve a DIFFERENT project. */
29
+ async function pickPort(root, desired) {
30
+ if (await portFree(desired)) return {
31
+ port: desired,
32
+ fellBack: false
33
+ };
34
+ let p = projectPort(root);
35
+ for (let i = 0; i < 200; i++) {
36
+ if (p !== desired && await portFree(p)) return {
37
+ port: p,
38
+ fellBack: true
39
+ };
40
+ p = 5200 + (p - 5200 + 1) % 200;
41
+ }
42
+ return {
43
+ port: desired,
44
+ fellBack: true
45
+ };
46
+ }
47
+ async function dev(root, portFlag) {
48
+ const config = await loadConfig(root);
49
+ const host = detectHost(root);
50
+ const pkgDir = packageDir();
51
+ const clientDir = join(pkgDir, "src", "client");
52
+ const projectName = basename(root);
53
+ const desiredPort = portFlag ?? (config.port !== 5199 ? config.port : void 0) ?? projectPort(root);
54
+ const picked = await pickPort(root, desiredPort);
55
+ const plugins = [react()];
56
+ if (host.tailwind === 4) {
57
+ const tw = await tailwind4Plugin(root);
58
+ if (tw) plugins.push(...tw);
59
+ else console.warn(`[marver] tailwindcss v4 detected but @tailwindcss/vite could not be loaded - utility classes WILL be missing from frames. Fix: npm i -D @tailwindcss/vite (used only by the canvas, never by your app's build).`);
60
+ }
61
+ let css;
62
+ if (host.tailwind === 3) css = await tailwind3Css(root) ?? void 0;
63
+ plugins.push(marverPlugin({
64
+ root,
65
+ clientDir,
66
+ config,
67
+ detectedThemeCss: host.themeCss
68
+ }));
69
+ const logger = createLogger("info");
70
+ const noise = (msg) => /Sourcemap for .*node_modules.*points to/.test(msg);
71
+ const warnBase = logger.warn.bind(logger);
72
+ const warnOnceBase = logger.warnOnce.bind(logger);
73
+ logger.warn = (msg, opts) => {
74
+ if (!noise(msg)) warnBase(msg, opts);
75
+ };
76
+ logger.warnOnce = (msg, opts) => {
77
+ if (!noise(msg)) warnOnceBase(msg, opts);
78
+ };
79
+ const server = await createServer({
80
+ configFile: false,
81
+ root,
82
+ css,
83
+ customLogger: logger,
84
+ plugins,
85
+ server: {
86
+ port: picked.port,
87
+ strictPort: false,
88
+ fs: { allow: [.../* @__PURE__ */ new Set([
89
+ root,
90
+ pkgDir,
91
+ searchForWorkspaceRoot(root)
92
+ ])] },
93
+ watch: { ignored: [
94
+ "**/design/manifest.json",
95
+ "**/design/boards/**",
96
+ "**/design/.local/**",
97
+ "**/design/.dist/**",
98
+ "**/design/comments/**",
99
+ "**/.next/**",
100
+ "**/.turbo/**",
101
+ "**/.vercel/**",
102
+ "**/.output/**",
103
+ "**/dist/**",
104
+ "**/build/**",
105
+ "**/out/**",
106
+ "**/coverage/**",
107
+ "**/.gstack/**",
108
+ "**/.git/**",
109
+ "**/.playwright-mcp/**"
110
+ ] },
111
+ warmup: { clientFiles: [
112
+ join(clientDir, "frame-host", "main.tsx"),
113
+ join(clientDir, "frame-host", "bridge.js"),
114
+ join(clientDir, "content", "index.tsx"),
115
+ join(clientDir, "content", "diagram.tsx"),
116
+ join(clientDir, "content", "md.ts")
117
+ ] }
118
+ },
119
+ resolve: {
120
+ dedupe: ["react", "react-dom"],
121
+ tsconfigPaths: true
122
+ },
123
+ optimizeDeps: {
124
+ exclude: [PKG],
125
+ include: [
126
+ "react",
127
+ "react-dom",
128
+ "react-dom/client",
129
+ "react/jsx-runtime",
130
+ "react/jsx-dev-runtime",
131
+ `${PKG} > marked`,
132
+ `${PKG} > mermaid`,
133
+ `${PKG} > html-to-image`
134
+ ],
135
+ entries: [join(clientDir, "frame-host", "index.html"), "design/**/*.{tsx,jsx}"]
136
+ },
137
+ logLevel: "info"
138
+ });
139
+ await server.listen();
140
+ const addr = server.httpServer?.address();
141
+ const port = typeof addr === "object" && addr ? addr.port : picked.port;
142
+ if (picked.fellBack) console.log(`\n port ${desiredPort} is in use - serving "${projectName}" on ${port} instead`);
143
+ console.log(`\n ${NAME} · ${projectName} → http://localhost:${port}/\n`);
144
+ const { loadCollab, syncOnce } = await import("./sync-CkBk-tUk.mjs").then((n) => n.a);
145
+ if (loadCollab(root)) console.log(` comments: syncing with the published canvas (design/.local/collab.json)\n`);
146
+ let syncing = false;
147
+ const tick = async () => {
148
+ if (syncing) return;
149
+ syncing = true;
150
+ try {
151
+ const collab = loadCollab(root);
152
+ if (collab) await syncOnce(root, collab).catch(() => {});
153
+ } finally {
154
+ syncing = false;
155
+ }
156
+ };
157
+ tick();
158
+ setInterval(tick, 3e4).unref();
159
+ return server;
160
+ }
161
+ //#endregion
162
+ export { dev };
@@ -1,5 +1,5 @@
1
1
  import { n as NAME } from "./cli.mjs";
2
- import { i as DEFAULTS, n as scanFrames, o as detectHost, r as writeManifest, s as readJson } from "./manifest-DW-T52MM.mjs";
2
+ import { a as DEFAULTS, c as readJson, i as writeManifest, r as scanFrames, s as detectHost } from "./manifest-C8FODq2S.mjs";
3
3
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join, relative } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -133,6 +133,7 @@ function init(root, opts) {
133
133
  refresh("tsconfig.json", [STANDALONE_TSCONFIG], tsconfigNow());
134
134
  }
135
135
  write(".gitignore", ".local/\n.dist/\n");
136
+ write(".gitattributes", "comments/*.jsonl merge=union\n");
136
137
  write("scenes/_layout.tsx", readFileSync(join(templates, "root-layout.tsx"), "utf8"));
137
138
  if (!existsSync(join(design, "boards"))) {
138
139
  mkdirSync(join(design, "boards"), { recursive: true });
@@ -247,6 +247,31 @@ function toFrameId(designRelPath) {
247
247
  const noExt = designRelPath.split(sep).join("/").replace(FRAME_EXT, "");
248
248
  return noExt.startsWith("scenes/") ? noExt.slice(7) : noExt;
249
249
  }
250
+ /**
251
+ * A7: which frames does a changed `design/**` file affect? Manifest + directory conventions,
252
+ * NOT a module-graph walk (deterministic, cheap, may over-reload but never misses a
253
+ * conventionally-affected frame). Returns:
254
+ * null -> uncontrolled: leave to default Vite HMR (src/** deps, theme.css, config, assets)
255
+ * [] -> controlled but affects no current frame (e.g. a layout in an empty dir)
256
+ * [ids] -> controlled: the shell drives a rev-stamped reload of these frames
257
+ */
258
+ function affectedFrameIds(absFile, root, manifest) {
259
+ const design = join(root, "design");
260
+ if (absFile !== design && !absFile.startsWith(design + sep)) return null;
261
+ const rel = relative(design, absFile).split(sep).join("/");
262
+ const name = rel.split("/").pop() ?? rel;
263
+ const designRel = `design/${rel}`;
264
+ const tsxFrames = manifest.frames.filter((f) => f.kind === "tsx");
265
+ if (name === "theme.css") return null;
266
+ if (rel === "providers.tsx" || rel === "providers.jsx") return tsxFrames.map((f) => f.id);
267
+ if (/(^|\/)(_layout\.(tsx|jsx)|_fixtures\.(ts|tsx|js|jsx|json))$/.test(rel)) {
268
+ const prefix = `design/${rel.slice(0, rel.length - name.length)}`;
269
+ return tsxFrames.filter((f) => f.file.startsWith(prefix)).map((f) => f.id);
270
+ }
271
+ const direct = manifest.frames.find((f) => f.file === designRel);
272
+ if (direct) return [direct.id];
273
+ return null;
274
+ }
250
275
  function walk(dir, out = []) {
251
276
  if (!existsSync(dir)) return out;
252
277
  for (const e of readdirSync(dir, { withFileTypes: true })) {
@@ -399,4 +424,4 @@ function writeManifest(root, manifest) {
399
424
  }
400
425
  const hash = (s) => createHash("sha256").update(s).digest("hex");
401
426
  //#endregion
402
- export { loadConfig as a, DEFAULTS as i, scanFrames as n, detectHost as o, writeManifest as r, readJson as s, hash as t };
427
+ export { DEFAULTS as a, readJson as c, writeManifest as i, hash as n, loadConfig as o, scanFrames as r, detectHost as s, affectedFrameIds as t };
@@ -1,7 +1,7 @@
1
1
  import { i as ROUTE, n as NAME, r as PKG } from "./cli.mjs";
2
- import { n as scanFrames, r as writeManifest, t as hash } from "./manifest-DW-T52MM.mjs";
2
+ import { i as writeManifest, n as hash, r as scanFrames, t as affectedFrameIds } from "./manifest-C8FODq2S.mjs";
3
3
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, watch, writeFileSync } from "node:fs";
4
- import { dirname, join, resolve, sep } from "node:path";
4
+ import { basename, dirname, join, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { randomBytes } from "node:crypto";
7
7
  //#region src/server/api.ts
@@ -110,6 +110,10 @@ function apiMiddleware(root) {
110
110
  } catch {
111
111
  return json(res, 400, { error: "malformed JSON" });
112
112
  }
113
+ if (body.mustExist && !existsSync(p)) return json(res, 409, {
114
+ error: "board no longer exists on disk",
115
+ gone: true
116
+ });
113
117
  const current = existsSync(p) ? readFileSync(p, "utf8") : "";
114
118
  if (current && body.baseHash !== hash(current)) {
115
119
  let disk = null;
@@ -128,12 +132,126 @@ function apiMiddleware(root) {
128
132
  return json(res, 200, { sha256: hash(next2) });
129
133
  }
130
134
  }
135
+ if (path === "me" && req.method === "GET") return json(res, 200, {
136
+ user: localProfile(root),
137
+ role: "owner",
138
+ local: true
139
+ });
140
+ if (path === "profile" && req.method === "POST") {
141
+ const raw = await readBody(req);
142
+ if (raw == null) return json(res, 400, { error: "body too large" });
143
+ let b;
144
+ try {
145
+ b = JSON.parse(raw);
146
+ } catch {
147
+ return json(res, 400, { error: "malformed JSON" });
148
+ }
149
+ const dir = join(root, "design", ".local");
150
+ mkdirSync(dir, { recursive: true });
151
+ const prof = {
152
+ ...localProfile(root),
153
+ ...typeof b.name === "string" && b.name.trim() ? { name: b.name.trim() } : {},
154
+ ...typeof b.email === "string" ? { email: b.email.trim() } : {},
155
+ ...typeof b.avatar === "string" ? { avatar: b.avatar || void 0 } : {}
156
+ };
157
+ atomicWrite(join(dir, "profile.json"), JSON.stringify(prof, null, 2) + "\n");
158
+ return json(res, 200, { user: prof });
159
+ }
160
+ if (path === "boards.rights" && req.method === "GET") {
161
+ const { listBoards } = await import("./comments-Ba8mU600.mjs").then((n) => n.n);
162
+ return json(res, 200, {
163
+ local: true,
164
+ stored: listBoards(join(root, "design", "comments"))
165
+ });
166
+ }
167
+ const cm = /^comments\/([a-z0-9][a-z0-9-]*)$/.exec(path);
168
+ if (cm) {
169
+ const { appendEvents, readLog } = await import("./comments-Ba8mU600.mjs").then((n) => n.n);
170
+ const dir = join(root, "design", "comments");
171
+ if (req.method === "GET") return json(res, 200, { events: readLog(dir, cm[1]) });
172
+ if (req.method === "POST") {
173
+ const raw = await readBody(req);
174
+ if (raw == null) return json(res, 400, { error: "body too large" });
175
+ let b;
176
+ try {
177
+ b = JSON.parse(raw);
178
+ } catch {
179
+ return json(res, 400, { error: "malformed JSON" });
180
+ }
181
+ const incoming = Array.isArray(b.events) ? b.events : [];
182
+ const me = localProfile(root);
183
+ const stamped = incoming.map((ev) => ({
184
+ ...ev,
185
+ board: ev.board ?? cm[1],
186
+ author: ev.author ?? ([
187
+ "create",
188
+ "reply",
189
+ "react",
190
+ "edit"
191
+ ].includes(ev.type) ? me : void 0)
192
+ }));
193
+ const fresh = appendEvents(dir, cm[1], stamped);
194
+ backgroundPush(root);
195
+ return json(res, 200, { accepted: fresh.length });
196
+ }
197
+ }
198
+ if (path === "sync" && req.method === "POST") {
199
+ const { loadCollab, syncOnce } = await import("./sync-CkBk-tUk.mjs").then((n) => n.a);
200
+ const collab = loadCollab(root);
201
+ if (!collab) return json(res, 200, { connected: false });
202
+ try {
203
+ return json(res, 200, {
204
+ connected: true,
205
+ boards: await syncOnce(root, collab)
206
+ });
207
+ } catch (err) {
208
+ return json(res, 502, {
209
+ connected: true,
210
+ error: err.message
211
+ });
212
+ }
213
+ }
131
214
  json(res, 404, { error: "unknown endpoint" });
132
215
  } catch (err) {
133
216
  json(res, 500, { error: err.message });
134
217
  }
135
218
  };
136
219
  }
220
+ function localProfile(root) {
221
+ try {
222
+ const c = JSON.parse(readFileSync(join(root, "design", ".local", "collab.json"), "utf8"));
223
+ if (typeof c?.email === "string" && c.email) return {
224
+ email: c.email,
225
+ name: c.name ?? "Designer"
226
+ };
227
+ } catch {}
228
+ try {
229
+ const p = JSON.parse(readFileSync(join(root, "design", ".local", "profile.json"), "utf8"));
230
+ if (typeof p?.name === "string") return {
231
+ email: p.email ?? "",
232
+ name: p.name,
233
+ avatar: p.avatar
234
+ };
235
+ } catch {}
236
+ return {
237
+ email: "",
238
+ name: "Designer"
239
+ };
240
+ }
241
+ let pushTimer = null;
242
+ /** Debounced fire-and-forget push after a local write; failures are silent - the
243
+ * 30s sync loop is the reliability layer, this is just latency. */
244
+ function backgroundPush(root) {
245
+ if (pushTimer) return;
246
+ pushTimer = setTimeout(async () => {
247
+ pushTimer = null;
248
+ try {
249
+ const { loadCollab, syncOnce } = await import("./sync-CkBk-tUk.mjs").then((n) => n.a);
250
+ const collab = loadCollab(root);
251
+ if (collab) await syncOnce(root, collab);
252
+ } catch {}
253
+ }, 1500);
254
+ }
137
255
  //#endregion
138
256
  //#region src/server/routes.ts
139
257
  /**
@@ -259,6 +377,20 @@ const VIRTUAL_CONFIG = "virtual:sh-config";
259
377
  const VIRTUAL_DATA = "virtual:sh-data";
260
378
  function marverPlugin(ctx) {
261
379
  const { root, clientDir, config } = ctx;
380
+ let manifest = scanFrames(root);
381
+ let devServer = null;
382
+ const bootId = String(process.hrtime.bigint());
383
+ let invRev = 0;
384
+ const pendingInv = /* @__PURE__ */ new Set();
385
+ const flushInv = debounce(() => {
386
+ if (!devServer || !pendingInv.size) return;
387
+ const frameIds = [...pendingInv];
388
+ pendingInv.clear();
389
+ devServer.ws.send("sh:frame-invalidated", {
390
+ frameIds,
391
+ revision: `${bootId}:${++invRev}`
392
+ });
393
+ }, 120);
262
394
  /** Theme resolution: design/theme.css wrapper > configured > detected > empty (spec §5.4). */
263
395
  const themeFile = () => {
264
396
  const wrapper = join(root, "design", "theme.css");
@@ -269,6 +401,7 @@ function marverPlugin(ctx) {
269
401
  };
270
402
  return {
271
403
  name: "marver",
404
+ enforce: "pre",
272
405
  resolveId(id) {
273
406
  if (id === VIRTUAL_THEME) return themeFile() ?? "\0virtual:sh-theme.css";
274
407
  if (id === VIRTUAL_CONFIG) return "\0virtual:sh-config";
@@ -293,7 +426,8 @@ function marverPlugin(ctx) {
293
426
  themes: config.themes,
294
427
  zoomSpeed: config.zoomSpeed,
295
428
  noTheme: themeFile() == null,
296
- setup: setupPending
429
+ setup: setupPending,
430
+ projectName: basename(root)
297
431
  })}`;
298
432
  }
299
433
  if (id === "\0virtual:sh-data") return "export default null";
@@ -308,19 +442,27 @@ function marverPlugin(ctx) {
308
442
  const bridge = "/@fs/" + join(clientDir, "frame-host", "bridge.js").split("\\").join("/") + "?html=1";
309
443
  return {
310
444
  html,
311
- tags: [{
312
- tag: "script",
313
- attrs: { type: "module" },
314
- children: `import '${VIRTUAL_THEME}'`,
315
- injectTo: "head-prepend"
316
- }, {
317
- tag: "script",
318
- attrs: {
319
- type: "module",
320
- src: bridge
445
+ tags: [
446
+ {
447
+ tag: "script",
448
+ children: `(function(){var a=Element.prototype.attachShadow;if(a)Element.prototype.attachShadow=function(i){if(i&&i.mode==='closed')window.__mvClosedShadow=1;return a.call(this,i)};})();`,
449
+ injectTo: "head-prepend"
321
450
  },
322
- injectTo: "head-prepend"
323
- }]
451
+ {
452
+ tag: "script",
453
+ attrs: { type: "module" },
454
+ children: `import '${VIRTUAL_THEME}'`,
455
+ injectTo: "head-prepend"
456
+ },
457
+ {
458
+ tag: "script",
459
+ attrs: {
460
+ type: "module",
461
+ src: bridge
462
+ },
463
+ injectTo: "head-prepend"
464
+ }
465
+ ]
324
466
  };
325
467
  }
326
468
  },
@@ -330,9 +472,16 @@ function marverPlugin(ctx) {
330
472
  * Edits to loaded frames keep normal HMR (modules present + file exists → pass through).
331
473
  */
332
474
  handleHotUpdate(hctx) {
333
- if (![join(root, "design", "scenes"), join(root, "design", "components")].some((w) => hctx.file.startsWith(w))) return;
334
- if (!existsSync(hctx.file)) return [];
335
- if (hctx.modules.length === 0) return [];
475
+ const affected = affectedFrameIds(hctx.file, root, manifest);
476
+ if (affected === null) {
477
+ if ([join(root, "design", "scenes"), join(root, "design", "components")].some((w) => hctx.file.startsWith(w)) && !existsSync(hctx.file)) return [];
478
+ return;
479
+ }
480
+ if (affected.length) {
481
+ for (const id of affected) pendingInv.add(id);
482
+ flushInv();
483
+ }
484
+ return [];
336
485
  },
337
486
  configureServer(server) {
338
487
  server.middlewares.use((req, res, next) => {
@@ -359,8 +508,9 @@ function marverPlugin(ctx) {
359
508
  });
360
509
  server.middlewares.use(apiMiddleware(root));
361
510
  server.middlewares.use(routesMiddleware(server, clientDir));
511
+ devServer = server;
362
512
  const regen = debounce(() => {
363
- const manifest = scanFrames(root);
513
+ manifest = scanFrames(root);
364
514
  if (writeManifest(root, manifest)) server.ws.send("sh:manifest", manifest);
365
515
  }, 150);
366
516
  const watched = [join(root, "design", "scenes"), join(root, "design", "components")];
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };