@markdy/cli 0.7.28 → 0.8.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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { SceneAST } from '@markdy/core';
1
+ import { DiagramAST } from '@markdy/core';
2
2
  import { Server } from 'node:http';
3
3
 
4
4
  interface CliIo {
@@ -15,8 +15,7 @@ interface RunResult {
15
15
  type LoadedScene = {
16
16
  filePath: string;
17
17
  source: string;
18
- ast: SceneAST;
19
- imports: Record<string, SceneAST>;
18
+ ast: DiagramAST;
20
19
  };
21
20
  declare function runCli(argv: string[], io?: CliIo, runtime?: CliRuntime): Promise<RunResult>;
22
21
  declare function buildStandaloneHtml(scene: LoadedScene): Promise<string>;
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { PRESETS, PRESET_NAMES, parse, registerActorPack } from "@markdy/core";
5
- import { systemsPack } from "@markdy/stdlib-systems";
4
+ import { parse } from "@markdy/core";
6
5
  import { createRequire } from "module";
7
6
  import { basename, dirname, extname, join, resolve, sep } from "path";
8
7
  import { fileURLToPath, pathToFileURL } from "url";
@@ -11,152 +10,126 @@ import { readdir, readFile, stat, writeFile } from "fs/promises";
11
10
  import { spawn } from "child_process";
12
11
 
13
12
  // src/format.ts
14
- var BARE_TOKEN_RE = /^[\p{L}\p{N}_.$#/+:-]+$/u;
15
- var MODIFIER_KEYS = ["scale", "rotate", "opacity", "size", "z"];
16
13
  function formatScene(ast) {
17
- const sections = [];
18
- sections.push(formatSceneHeader(ast));
19
- const vars = Object.entries(ast.vars);
20
- if (vars.length > 0) {
21
- sections.push(vars.map(([name, value]) => `var ${name} = ${value}`).join("\n"));
22
- }
23
- if (ast.imports.length > 0) {
24
- sections.push(ast.imports.map(formatImportDecl).join("\n"));
25
- }
26
- const assets = Object.entries(ast.assets);
27
- if (assets.length > 0) {
28
- sections.push(
29
- assets.map(([name, def]) => `asset ${name} = ${def.type}(${formatToken(def.value)})`).join("\n")
30
- );
31
- }
32
- const actors = Object.entries(ast.actors);
33
- if (actors.length > 0) {
34
- sections.push(actors.map(([name, actor]) => formatActorDecl(name, actor)).join("\n"));
35
- }
36
- const defs = Object.entries(ast.defs);
37
- if (defs.length > 0) {
38
- sections.push(defs.map(([name, def]) => formatTemplateDef(name, def)).join("\n\n"));
39
- }
40
- const seqs = Object.entries(ast.seqs);
41
- if (seqs.length > 0) {
42
- sections.push(seqs.map(([name, seq]) => formatSequenceDef(name, seq)).join("\n\n"));
43
- }
44
- const timeline = formatTimeline(ast);
45
- if (timeline) {
46
- sections.push(timeline);
47
- }
48
- return `${sections.filter(Boolean).join("\n\n")}
49
- `;
50
- }
51
- function formatSceneHeader(ast) {
52
- const parts = [
53
- `scene width=${formatNumber(ast.meta.width)}`,
54
- `height=${formatNumber(ast.meta.height)}`,
55
- `fps=${formatNumber(ast.meta.fps)}`,
56
- `bg=${ast.meta.bg}`
57
- ];
58
- if (ast.meta.duration !== void 0) {
59
- parts.push(`duration=${formatNumber(ast.meta.duration)}`);
60
- }
61
- return parts.join(" ");
62
- }
63
- function formatImportDecl(decl) {
64
- return `import ${JSON.stringify(decl.path)} as ${decl.namespace}`;
65
- }
66
- function formatActorDecl(name, actor) {
67
- const ctor = `${actor.type}(${actor.args.map(formatToken).join(", ")})`;
68
- const position = actor.anchor ? `at ${actor.anchor}` : `at (${formatNumber(actor.x)}, ${formatNumber(actor.y)})`;
69
- const modifiers = MODIFIER_KEYS.flatMap((key) => {
70
- const value = actor[key];
71
- return value === void 0 ? [] : [`${key}=${formatNumber(value)}`];
72
- }).join(", ");
73
- return modifiers.length > 0 ? `actor ${name} = ${ctor} ${position} with ${modifiers}` : `actor ${name} = ${ctor} ${position}`;
74
- }
75
- function formatTemplateDef(name, def) {
76
- const args = def.bodyArgs.map(formatTemplateArg).join(", ");
77
- return `def ${name}(${def.params.join(", ")}) {
78
- ${def.actorType}(${args})
79
- }`;
80
- }
81
- function formatTemplateArg(arg) {
82
- return arg.startsWith("${") && arg.endsWith("}") ? arg : formatToken(arg);
83
- }
84
- function formatSequenceDef(name, seq) {
85
- const lines = seq.events.map((event) => {
86
- const params = event.paramsRaw.trim();
87
- const suffix = params ? `(${params})` : "()";
88
- return ` @+${formatNumber(event.offset)}: $.${event.action}${suffix}`;
89
- });
90
- return `seq ${name}(${seq.params.join(", ")}) {
91
- ${lines.join("\n")}
92
- }`;
93
- }
94
- function formatTimeline(ast) {
95
- if (ast.events.length === 0 && ast.chapters.length === 0) {
96
- return "";
97
- }
98
- const blocks = [];
99
- const topLevelEvents = ast.events.filter((event) => event.chapter === void 0);
100
- for (const event of topLevelEvents) {
101
- blocks.push({ line: event.line, text: formatEvent(event) });
102
- }
103
- const chaptersByLine = [...ast.chapters].sort((a, b) => a.startLine - b.startLine);
104
- for (let index = 0; index < chaptersByLine.length; index++) {
105
- const chapter = chaptersByLine[index];
106
- const nextStartLine = chaptersByLine[index + 1]?.startLine ?? Number.POSITIVE_INFINITY;
107
- const events = ast.events.filter(
108
- (event) => event.chapter === chapter.name && event.line > chapter.startLine && event.line < nextStartLine
109
- ).sort((a, b) => a.line - b.line);
110
- blocks.push({ line: chapter.startLine, text: formatChapter(chapter, events) });
111
- }
112
- return blocks.sort((a, b) => a.line - b.line).map((block) => block.text).join("\n\n");
113
- }
114
- function formatChapter(chapter, events) {
115
- const body = events.length > 0 ? `
116
- ${events.map((event) => ` ${formatEvent(event)}`).join("\n")}
117
- ` : "\n";
118
- return `scene ${JSON.stringify(chapter.name)} {${body}}`;
119
- }
120
- function formatEvent(event) {
121
- const params = Object.entries(event.params).map(([key, value]) => `${key}=${formatParamValue(value)}`).join(", ");
122
- return `@${formatNumber(event.time)}: ${event.actor}.${event.action}(${params})`;
123
- }
124
- function formatParamValue(value) {
125
- if (Array.isArray(value)) {
126
- return `(${value.map(formatTupleValue).join(", ")})`;
127
- }
128
- if (typeof value === "number") {
129
- return formatNumber(value);
130
- }
131
- if (typeof value === "string") {
132
- return formatToken(value);
14
+ const lines = [];
15
+ const sceneParts = [`scene`];
16
+ if (ast.meta.title) sceneParts.push(JSON.stringify(ast.meta.title));
17
+ sceneParts.push(`theme=${ast.meta.theme}`);
18
+ if (ast.meta.width !== 1280) sceneParts.push(`width=${ast.meta.width}`);
19
+ if (ast.meta.height !== 720) sceneParts.push(`height=${ast.meta.height}`);
20
+ if (ast.meta.fps !== 60) sceneParts.push(`fps=${ast.meta.fps}`);
21
+ if (ast.meta.duration !== void 0) sceneParts.push(`duration=${ast.meta.duration}`);
22
+ lines.push(sceneParts.join(" "));
23
+ if (ast.meta.direction !== "LR") {
24
+ lines.push(`layout ${ast.meta.direction}`);
25
+ }
26
+ for (const style of Object.values(ast.styles)) {
27
+ lines.push(formatStyle(style));
28
+ }
29
+ for (const node of Object.values(ast.nodes)) {
30
+ lines.push(formatNode(node));
31
+ }
32
+ for (const edge of ast.edges) {
33
+ lines.push(formatEdge(edge));
34
+ }
35
+ for (const group of Object.values(ast.groups)) {
36
+ lines.push(formatGroup(group));
37
+ }
38
+ for (const pattern of Object.values(ast.patterns)) {
39
+ lines.push(`pattern ${pattern.name}(${pattern.params.join(", ")}):`);
40
+ for (const cue of pattern.body) {
41
+ lines.push(` ${formatCue(cue)}`);
42
+ }
43
+ lines.push("");
133
44
  }
134
- if (typeof value === "boolean") {
135
- return value ? "true" : "false";
45
+ for (const beat of ast.beats) {
46
+ lines.push(formatBeat(beat));
47
+ lines.push("");
136
48
  }
137
- return JSON.stringify(value);
49
+ return `${lines.join("\n").trim()}
50
+ `;
138
51
  }
139
- function formatTupleValue(value) {
140
- if (typeof value === "number") return formatNumber(value);
141
- if (typeof value === "string") return formatToken(value);
52
+ function formatStyle(style) {
53
+ const props = Object.entries(style.props).map(([k, v]) => `${k}=${formatValue(v)}`).join(" ");
54
+ return `style ${style.name} = ${props}`;
55
+ }
56
+ function formatNode(node) {
57
+ let line = `${node.kind} ${node.id}`;
58
+ if (node.label !== node.id && node.label) line += ` ${JSON.stringify(node.label)}`;
59
+ if (node.style) line += ` style=${node.style}`;
60
+ return line;
61
+ }
62
+ function formatEdge(edge) {
63
+ const op = edge.kind === "request" ? "->" : edge.kind === "response" ? "<-" : edge.kind === "event" ? "~>" : "--";
64
+ const [left, right] = edge.kind === "response" ? [edge.to, edge.from] : [edge.from, edge.to];
65
+ let chain = `${left} ${op} ${right}`;
66
+ if (edge.label) chain += ` ${JSON.stringify(edge.label)}`;
67
+ return `edge ${edge.id}: ${chain}`;
68
+ }
69
+ function formatGroup(group) {
70
+ const label = group.label ? ` ${JSON.stringify(group.label)}` : "";
71
+ return `group ${group.id}${label}: ${group.members.join(" ")}`;
72
+ }
73
+ function formatBeat(beat) {
74
+ const label = beat.label ? ` ${JSON.stringify(beat.label)}` : "";
75
+ const lines = [`beat ${beat.name}${label}:`];
76
+ for (const cue of beat.cues) {
77
+ lines.push(` ${formatCue(cue)}`);
78
+ }
79
+ return lines.join("\n");
80
+ }
81
+ function formatCue(cue) {
82
+ if (cue.kind === "parallel") {
83
+ return cue.cues.map(formatCue).join(" & ");
84
+ }
85
+ if (cue.kind === "flow") {
86
+ let chain = "";
87
+ for (let i = 0; i < cue.segments.length; i++) {
88
+ const seg = cue.segments[i];
89
+ const op = seg.op === "request" ? "->" : seg.op === "response" ? "<-" : seg.op === "event" ? "~>" : "--";
90
+ const [left, right] = seg.op === "response" ? [seg.to, seg.from] : [seg.from, seg.to];
91
+ if (i === 0) chain += left;
92
+ chain += ` ${op} ${right}`;
93
+ if (seg.label) chain += ` ${JSON.stringify(seg.label)}`;
94
+ }
95
+ if (cue.dur !== void 0) chain += ` dur=${cue.dur}s`;
96
+ return chain;
97
+ }
98
+ if (cue.kind === "show" || cue.kind === "hide") {
99
+ let line = `${cue.kind} ${cue.targets.join(" ")}`;
100
+ if (cue.kind === "show" && cue.stagger !== void 0) line += ` stagger=${cue.stagger}s`;
101
+ if (cue.dur !== void 0) line += ` dur=${cue.dur}s`;
102
+ return line;
103
+ }
104
+ if (cue.kind === "glow") {
105
+ let line = `glow ${cue.targets.join(" ")}`;
106
+ if (cue.color) line += ` color=${cue.color}`;
107
+ if (cue.strength !== void 0) line += ` strength=${cue.strength}`;
108
+ if (cue.dur !== void 0) line += ` dur=${cue.dur}s`;
109
+ return line;
110
+ }
111
+ if (cue.kind === "focus") {
112
+ let line = `focus ${cue.targets.join(" ")}`;
113
+ if (cue.zoom !== void 0) line += ` zoom=${cue.zoom}`;
114
+ if (cue.dur !== void 0) line += ` dur=${cue.dur}s`;
115
+ return line;
116
+ }
117
+ if (cue.kind === "use") {
118
+ const args = Object.entries(cue.args).map(([k, v]) => k.startsWith("__pos_") ? v : `${k}=${v}`).join(", ");
119
+ return `use ${cue.pattern}(${args})`;
120
+ }
121
+ return "";
122
+ }
123
+ function formatValue(value) {
124
+ if (typeof value === "number") return String(value);
125
+ if (typeof value === "string") return value;
142
126
  return JSON.stringify(value);
143
127
  }
144
- function formatToken(value) {
145
- return BARE_TOKEN_RE.test(value) ? value : JSON.stringify(value);
146
- }
147
- function formatNumber(value) {
148
- return Number.isInteger(value) ? String(value) : String(round3(value));
149
- }
150
- function round3(value) {
151
- return Math.round(value * 1e3) / 1e3;
152
- }
153
128
 
154
129
  // src/index.ts
155
130
  var DEFAULT_PORT = 4242;
156
- var IMPORT_RE = /^import\s+"([^"]+)"\s+as\s+(\w+)\s*$/;
157
131
  var MARKDY_EXT = ".markdy";
158
132
  var PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
159
- registerActorPack(systemsPack);
160
133
  async function runCli(argv, io = defaultIo(), runtime = defaultRuntime()) {
161
134
  const parsed = parseArgv(argv);
162
135
  if (hasFlag(parsed, "help")) {
@@ -205,7 +178,7 @@ async function lintCommand(parsed, io) {
205
178
  try {
206
179
  const scene = await loadSceneFromFile(file, cache);
207
180
  io.stdout(`OK ${file}`);
208
- warningCount += printWarnings(scene.ast.warnings, file, io);
181
+ warningCount += printWarnings(scene.ast.diagnostics, file, io);
209
182
  } catch (error) {
210
183
  errorCount++;
211
184
  io.stderr(`FAIL ${file}`);
@@ -302,37 +275,23 @@ async function explainCommand(parsed, io) {
302
275
  const summary = [
303
276
  `File: ${scene.filePath}`,
304
277
  `Viewport: ${scene.ast.meta.width}\xD7${scene.ast.meta.height} @ ${scene.ast.meta.fps}fps`,
305
- `Background: ${scene.ast.meta.bg}`,
306
- `Duration: ${scene.ast.meta.duration ?? 0}s`,
307
- `Actors: ${Object.keys(scene.ast.actors).length}`,
308
- `Events: ${scene.ast.events.length}`,
309
- `Chapters: ${scene.ast.chapters.length > 0 ? scene.ast.chapters.map((chapter) => chapter.name).join(", ") : "(none)"}`,
310
- `Imports: ${scene.ast.imports.length > 0 ? scene.ast.imports.map((item) => `${item.namespace} -> ${item.path}`).join(", ") : "(none)"}`,
311
- `Warnings: ${scene.ast.warnings.length}`
278
+ `Theme: ${scene.ast.meta.theme}`,
279
+ `Duration: ${scene.ast.meta.duration ?? "(auto)"}`,
280
+ `Nodes: ${Object.keys(scene.ast.nodes).length}`,
281
+ `Beats: ${scene.ast.beats.map((b) => b.name).join(", ") || "(none)"}`,
282
+ `Diagnostics: ${scene.ast.diagnostics.length}`
312
283
  ];
313
284
  io.stdout(summary.join("\n"));
314
- if (scene.ast.warnings.length > 0) {
315
- printWarnings(scene.ast.warnings, scene.filePath, io);
285
+ if (scene.ast.diagnostics.length > 0) {
286
+ printWarnings(scene.ast.diagnostics, scene.filePath, io);
316
287
  }
317
288
  return { exitCode: 0 };
318
289
  }
319
290
  async function newCommand(parsed, io) {
320
- const presetOrTarget = parsed.positionals[0];
321
- const second = parsed.positionals[1];
291
+ const target = parsed.positionals[0] ?? "scene.markdy";
322
292
  const force = hasFlag(parsed, "force");
323
- let presetName = "basic";
324
- let target = "scene.markdy";
325
- if (presetOrTarget) {
326
- if (PRESET_NAMES.includes(presetOrTarget)) {
327
- presetName = presetOrTarget;
328
- target = second ?? target;
329
- } else {
330
- target = presetOrTarget;
331
- }
332
- }
333
293
  const resolvedTarget = resolve(target);
334
- const content = presetName === "basic" ? defaultSceneTemplate() : `preset ${presetName}
335
- `;
294
+ const content = defaultSceneTemplate();
336
295
  if (!force && await exists(resolvedTarget)) {
337
296
  io.stderr(`markdy new: target already exists: ${resolvedTarget} (pass --force to overwrite)`);
338
297
  return { exitCode: 1 };
@@ -395,10 +354,9 @@ async function checkAllCommand(parsed, io) {
395
354
  async function launchPlayground(parsed, io, runtime, scene) {
396
355
  const portFlag = getStringFlag(parsed, "port");
397
356
  const preferredPort = portFlag ? Number(portFlag) : DEFAULT_PORT;
398
- const code = scene?.source ?? PRESETS.explainer([]);
399
- const imports = scene?.imports ?? {};
357
+ const code = scene?.source ?? defaultSceneTemplate();
400
358
  const sourcePath = scene?.filePath;
401
- const server = await startPreviewServer(code, imports, sourcePath, preferredPort);
359
+ const server = await startPreviewServer(code, sourcePath, preferredPort);
402
360
  const address = server.address();
403
361
  const port = typeof address === "object" && address ? address.port : preferredPort;
404
362
  const url = `http://127.0.0.1:${port}`;
@@ -408,10 +366,10 @@ async function launchPlayground(parsed, io, runtime, scene) {
408
366
  }
409
367
  return { exitCode: 0, server };
410
368
  }
411
- async function startPreviewServer(code, imports, sourcePath, preferredPort) {
369
+ async function startPreviewServer(code, sourcePath, preferredPort) {
412
370
  const coreDist = resolvePackageDist("@markdy/core");
413
371
  const rendererDist = resolvePackageDist("@markdy/renderer-dom");
414
- const html = buildPlaygroundHtml(code, imports, sourcePath);
372
+ const html = buildPlaygroundHtml(code, sourcePath);
415
373
  const server = createServer(async (request, response) => {
416
374
  try {
417
375
  const requestUrl = new URL(request.url ?? "/", "http://127.0.0.1");
@@ -470,32 +428,11 @@ async function loadSceneFromFile(filePath, cache = /* @__PURE__ */ new Map(), st
470
428
  const source = await readFile(resolvedPath, "utf8").catch((error) => {
471
429
  throw new Error(`Unable to read ${resolvedPath}: ${describeError(error)}`);
472
430
  });
473
- const imports = await resolveSceneImports(source, resolvedPath, cache, [...stack, resolvedPath]);
474
- const ast = parse(source, Object.keys(imports).length > 0 ? { imports } : void 0);
475
- const loaded = { filePath: resolvedPath, source, ast, imports };
431
+ const ast = parse(source);
432
+ const loaded = { filePath: resolvedPath, source, ast };
476
433
  cache.set(resolvedPath, loaded);
477
434
  return loaded;
478
435
  }
479
- async function resolveSceneImports(source, filePath, cache, stack) {
480
- const imports = {};
481
- for (const declaration of scanImports(source)) {
482
- const childPath = resolve(dirname(filePath), declaration.path);
483
- const child = await loadSceneFromFile(childPath, cache, stack);
484
- imports[declaration.namespace] = child.ast;
485
- }
486
- return imports;
487
- }
488
- function scanImports(source) {
489
- const imports = [];
490
- for (const line of source.split(/\r?\n/)) {
491
- const trimmed = line.trim();
492
- if (!trimmed.startsWith("import ")) continue;
493
- const match = IMPORT_RE.exec(trimmed);
494
- if (!match) continue;
495
- imports.push({ path: match[1], namespace: match[2] });
496
- }
497
- return imports;
498
- }
499
436
  async function collectSceneFiles(inputs) {
500
437
  const candidates = inputs.length > 0 ? inputs : [process.cwd()];
501
438
  const out = /* @__PURE__ */ new Set();
@@ -533,7 +470,7 @@ async function walkMarkdyFiles(root) {
533
470
  }
534
471
  return files;
535
472
  }
536
- function buildPlaygroundHtml(code, imports, sourcePath) {
473
+ function buildPlaygroundHtml(code, sourcePath) {
537
474
  return `<!doctype html>
538
475
  <html lang="en">
539
476
  <head>
@@ -629,7 +566,7 @@ function buildPlaygroundHtml(code, imports, sourcePath) {
629
566
  <strong>Markdy Playground</strong>
630
567
  <div><code>${escapeHtml(sourcePath ?? "scratch scene")}</code></div>
631
568
  </div>
632
- <div>Resolved imports: ${Object.keys(imports).length}</div>
569
+ <div>MarkdyScript 0.8 diagram playground</div>
633
570
  </header>
634
571
  <main>
635
572
  <section class="editor">
@@ -648,7 +585,6 @@ function buildPlaygroundHtml(code, imports, sourcePath) {
648
585
  <script type="module">
649
586
  import { createPlayer } from "@markdy/renderer-dom";
650
587
 
651
- const imports = ${JSON.stringify(imports)};
652
588
  const textarea = document.getElementById("code");
653
589
  const viewport = document.getElementById("viewport");
654
590
  const warnings = document.getElementById("warnings");
@@ -665,10 +601,9 @@ function buildPlaygroundHtml(code, imports, sourcePath) {
665
601
  player = createPlayer({
666
602
  container: viewport,
667
603
  code: textarea.value,
668
- imports,
669
604
  onWarning(warning) {
670
605
  const item = document.createElement("li");
671
- item.textContent = \`line \${warning.line}: \${warning.message} (\${warning.kind})\`;
606
+ item.textContent = \`line \${warning.line}: \${warning.message}\`;
672
607
  warnings.appendChild(item);
673
608
  }
674
609
  });
@@ -731,9 +666,8 @@ async function buildStandaloneHtml(scene) {
731
666
  createPlayer({
732
667
  container: document.getElementById("app"),
733
668
  code: ${JSON.stringify(scene.source)},
734
- imports: ${JSON.stringify(scene.imports)},
735
669
  onWarning(warning) {
736
- console.warn(\`[markdy] line \${warning.line}: \${warning.message} (\${warning.kind})\`);
670
+ console.warn(\`[markdy] line \${warning.line}: \${warning.message}\`);
737
671
  }
738
672
  });
739
673
  </script>
@@ -750,12 +684,10 @@ function helpText() {
750
684
  " markdy fmt <file-or-dir> [--write | --check]",
751
685
  " markdy render <file.markdy> [--out file.html] [--port 4242] [--no-open]",
752
686
  " markdy explain <file.markdy> [--json]",
753
- " markdy new [preset-name] [target.markdy] [--force]",
687
+ " markdy new [target.markdy] [--force]",
754
688
  " markdy docs [--open]",
755
689
  " markdy ai [--open]",
756
- " markdy check-all [dir] [--strict]",
757
- "",
758
- `Built-in presets: ${PRESET_NAMES.join(", ")}`
690
+ " markdy check-all [dir] [--strict]"
759
691
  ].join("\n");
760
692
  }
761
693
  function parseArgv(argv) {
@@ -824,10 +756,10 @@ function getStringFlag(parsed, name) {
824
756
  return typeof value === "string" ? value : void 0;
825
757
  }
826
758
  function printWarnings(warnings, file, io) {
827
- for (const warning of warnings) {
828
- io.stderr(`WARN ${file}:${warning.line} ${warning.kind} ${warning.message}`);
759
+ for (const warning of warnings.filter((w) => w.severity === "warning")) {
760
+ io.stderr(`WARN ${file}:${warning.line} ${warning.message}`);
829
761
  }
830
- return warnings.length;
762
+ return warnings.filter((w) => w.severity === "warning").length;
831
763
  }
832
764
  function stableStringify(value) {
833
765
  const seen = /* @__PURE__ */ new WeakSet();
@@ -888,14 +820,16 @@ function describeError(error) {
888
820
  }
889
821
  function defaultSceneTemplate() {
890
822
  return [
891
- "scene width=960 height=540 fps=30 bg=#0d1117",
823
+ 'scene "My Architecture" theme=midnight',
824
+ "layout LR",
892
825
  "",
893
- 'actor title = caption("hello, markdy") at top',
894
- "actor hero = figure(#c68642, m, \u{1F60E}) at (480, 360)",
826
+ "browser Client",
827
+ "service API",
828
+ "database DB",
895
829
  "",
896
- "@0.0: title.fade_in(dur=0.4)",
897
- "@0.4: hero.enter(from=bottom, dur=0.5)",
898
- '@1.2: hero.say("ship it", dur=1.4)',
830
+ "beat intro:",
831
+ " show $nodes",
832
+ ' Client -> API "GET /health" -> DB "lookup"',
899
833
  ""
900
834
  ].join("\n");
901
835
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/cli",
3
- "version": "0.7.28",
3
+ "version": "0.8.0",
4
4
  "description": "First-party CLI for MarkdyScript: lint, format, explain, render, and local playground tooling.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -41,9 +41,9 @@
41
41
  "access": "public"
42
42
  },
43
43
  "dependencies": {
44
- "@markdy/core": "0.7.28",
45
- "@markdy/renderer-dom": "0.7.28",
46
- "@markdy/stdlib-systems": "0.7.28"
44
+ "@markdy/core": "0.8.0",
45
+ "@markdy/stdlib-systems": "0.8.0",
46
+ "@markdy/renderer-dom": "0.8.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^25.9.5",