@markdy/core 0.8.7 → 0.8.9
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 +3 -1
- package/dist/index.js +138 -17
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -222,6 +222,8 @@ declare function resolveTheme(name: string): ThemeTokens;
|
|
|
222
222
|
|
|
223
223
|
declare const NODE_KINDS: Set<string>;
|
|
224
224
|
declare const EDGE_OPERATORS: Record<string, "request" | "response" | "event" | "dependency">;
|
|
225
|
+
/** Natural-language cue synonyms that AIs reach for, mapped to real cues. */
|
|
226
|
+
declare const CUE_ALIASES: Record<string, string>;
|
|
225
227
|
declare const BEAT_CUE_KEYWORDS: Set<string>;
|
|
226
228
|
declare const SCENE_KEYS: Set<string>;
|
|
227
229
|
declare function nodeRole(kind: string): string;
|
|
@@ -234,4 +236,4 @@ declare const TECHNICAL_NODE_TYPES: readonly ["service", "api", "microservice",
|
|
|
234
236
|
declare const VISUAL_PRIMITIVE_TYPES: readonly ["panel", "surface", "terminal", "metric", "stat", "grid", "matrix", "lane", "track", "marker", "dot", "token_strip", "chips", "glyph_card", "glyph"];
|
|
235
237
|
declare const TECHNICAL_NODE_KINDS: Record<(typeof TECHNICAL_NODE_TYPES)[number], string>;
|
|
236
238
|
|
|
237
|
-
export { BEAT_CUE_KEYWORDS, type BeatDecl, type BeatRange, type Cue, type Diagnostic, type DiagramAST, EDGE_OPERATORS, type EdgeDecl, type EdgeKind, type FlowSegment, type GroupDecl, type LayoutDirection, NODE_ALIASES, NODE_KINDS, type NodeDecl, ParseError, type ParseOptions, type ParseResult, type PatternDecl, type PositionedNode, type RenderPlan, type RoutedEdge, SCENE_KEYS, type SceneMeta, type StyleDecl, TECHNICAL_NODE_KINDS, TECHNICAL_NODE_TYPES, THEMES, type ThemeTokens, type TimedCue, VISUAL_PRIMITIVE_TYPES, canonicalNodeKind, compile, compilePlan, humanizeId, nodeRole, parse, parseAndCompile, resolveTheme };
|
|
239
|
+
export { BEAT_CUE_KEYWORDS, type BeatDecl, type BeatRange, CUE_ALIASES, type Cue, type Diagnostic, type DiagramAST, EDGE_OPERATORS, type EdgeDecl, type EdgeKind, type FlowSegment, type GroupDecl, type LayoutDirection, NODE_ALIASES, NODE_KINDS, type NodeDecl, ParseError, type ParseOptions, type ParseResult, type PatternDecl, type PositionedNode, type RenderPlan, type RoutedEdge, SCENE_KEYS, type SceneMeta, type StyleDecl, TECHNICAL_NODE_KINDS, TECHNICAL_NODE_TYPES, THEMES, type ThemeTokens, type TimedCue, VISUAL_PRIMITIVE_TYPES, canonicalNodeKind, compile, compilePlan, humanizeId, nodeRole, parse, parseAndCompile, resolveTheme };
|
package/dist/index.js
CHANGED
|
@@ -350,7 +350,20 @@ var EDGE_OPERATORS = {
|
|
|
350
350
|
"--": "dependency"
|
|
351
351
|
};
|
|
352
352
|
var RESERVED_SELECTORS = /* @__PURE__ */ new Set(["$title", "$nodes", "$edges"]);
|
|
353
|
-
var
|
|
353
|
+
var CUE_ALIASES = {
|
|
354
|
+
pulse: "focus",
|
|
355
|
+
highlight: "glow",
|
|
356
|
+
emphasize: "glow"
|
|
357
|
+
};
|
|
358
|
+
var BEAT_CUE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
359
|
+
"show",
|
|
360
|
+
"hide",
|
|
361
|
+
"glow",
|
|
362
|
+
"focus",
|
|
363
|
+
"frame",
|
|
364
|
+
"use",
|
|
365
|
+
...Object.keys(CUE_ALIASES)
|
|
366
|
+
]);
|
|
354
367
|
var SCENE_KEYS = /* @__PURE__ */ new Set(["width", "height", "fps", "theme", "duration", "direction", "layout"]);
|
|
355
368
|
function nodeRole(kind) {
|
|
356
369
|
return TECHNICAL_NODE_KINDS[kind] ?? "compute";
|
|
@@ -777,7 +790,9 @@ function stripComment(line) {
|
|
|
777
790
|
}
|
|
778
791
|
if (inString) continue;
|
|
779
792
|
if (ch === "/" && line[i + 1] === "/") return line.slice(0, i);
|
|
780
|
-
if (ch === "#" && (i === 0 || /\s/.test(line[i - 1]))
|
|
793
|
+
if (ch === "#" && (i === 0 || /\s/.test(line[i - 1])) && (i + 1 >= line.length || /\s/.test(line[i + 1]))) {
|
|
794
|
+
return line.slice(0, i);
|
|
795
|
+
}
|
|
781
796
|
}
|
|
782
797
|
return line;
|
|
783
798
|
}
|
|
@@ -905,9 +920,41 @@ function stripCueProps(raw, keys) {
|
|
|
905
920
|
}
|
|
906
921
|
return raw.trim();
|
|
907
922
|
}
|
|
923
|
+
function tokenizeFlowChain(line) {
|
|
924
|
+
const parts = [];
|
|
925
|
+
let current = "";
|
|
926
|
+
let inString = false;
|
|
927
|
+
let escaped = false;
|
|
928
|
+
for (let i = 0; i < line.length; i++) {
|
|
929
|
+
const ch = line[i];
|
|
930
|
+
if (inString) {
|
|
931
|
+
current += ch;
|
|
932
|
+
if (escaped) escaped = false;
|
|
933
|
+
else if (ch === "\\") escaped = true;
|
|
934
|
+
else if (ch === '"') inString = false;
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (ch === '"') {
|
|
938
|
+
inString = true;
|
|
939
|
+
current += ch;
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const op = line.slice(i, i + 2);
|
|
943
|
+
if (op === "->" || op === "<-" || op === "~>" || op === "--") {
|
|
944
|
+
if (current.trim()) parts.push(current.trim());
|
|
945
|
+
parts.push(op);
|
|
946
|
+
current = "";
|
|
947
|
+
i += 1;
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
current += ch;
|
|
951
|
+
}
|
|
952
|
+
if (current.trim()) parts.push(current.trim());
|
|
953
|
+
return parts;
|
|
954
|
+
}
|
|
908
955
|
function parseFlowChain(line, lineNo) {
|
|
909
956
|
const segments = [];
|
|
910
|
-
const parts = line
|
|
957
|
+
const parts = tokenizeFlowChain(line);
|
|
911
958
|
if (parts.length < 3) {
|
|
912
959
|
throw new ParseError(`expected flow chain like A -> B "label"`, lineNo);
|
|
913
960
|
}
|
|
@@ -942,6 +989,12 @@ function splitTargetLabel(token, lineNo) {
|
|
|
942
989
|
function parseCueLine(line, lineNo) {
|
|
943
990
|
const trimmed = line.trim();
|
|
944
991
|
const props = parseProps(trimmed);
|
|
992
|
+
if (/^@\+?\d/.test(trimmed) || /^\w[\w.-]*\.\w+\(/.test(trimmed) || /^camera\./.test(trimmed)) {
|
|
993
|
+
throw new ParseError(
|
|
994
|
+
"unsupported timeline command; use beat cues like show, frame, focus, glow, and flow lines",
|
|
995
|
+
lineNo
|
|
996
|
+
);
|
|
997
|
+
}
|
|
945
998
|
const parallelParts = splitOutsideQuotes(trimmed, "&");
|
|
946
999
|
if (parallelParts.length > 1) {
|
|
947
1000
|
return {
|
|
@@ -960,7 +1013,8 @@ function parseCueLine(line, lineNo) {
|
|
|
960
1013
|
};
|
|
961
1014
|
}
|
|
962
1015
|
const [head, ...rest] = trimmed.split(/\s+/);
|
|
963
|
-
const
|
|
1016
|
+
const rawKeyword = head.toLowerCase();
|
|
1017
|
+
const keyword = CUE_ALIASES[rawKeyword] ?? rawKeyword;
|
|
964
1018
|
if (keyword === "show" || keyword === "hide") {
|
|
965
1019
|
const targetRaw = stripCueProps(rest.join(" "), ["dur", "stagger"]);
|
|
966
1020
|
return {
|
|
@@ -1023,7 +1077,7 @@ function parseCueLine(line, lineNo) {
|
|
|
1023
1077
|
}
|
|
1024
1078
|
return { kind: "use", pattern: m[1], args, line: lineNo };
|
|
1025
1079
|
}
|
|
1026
|
-
throw new ParseError(`unknown cue '${head}'`, lineNo);
|
|
1080
|
+
throw new ParseError(`unknown cue '${head}'; use show, hide, glow, focus, frame, or use`, lineNo);
|
|
1027
1081
|
}
|
|
1028
1082
|
function expandPatternCues(cues, patterns, line) {
|
|
1029
1083
|
const out = [];
|
|
@@ -1121,6 +1175,50 @@ function normalizeCueBlocks(blocks) {
|
|
|
1121
1175
|
}
|
|
1122
1176
|
return normalized;
|
|
1123
1177
|
}
|
|
1178
|
+
function unsupportedSyntaxMessage(line) {
|
|
1179
|
+
if (/^actor\s+/.test(line) || /\bfigure\s*\(/.test(line) || /\bbox\s*\(/.test(line) || /\bat\s*\(/.test(line)) {
|
|
1180
|
+
return "unsupported manual drawing syntax; declare architecture nodes like service API, cache Redis, and database DB";
|
|
1181
|
+
}
|
|
1182
|
+
if (/^@\+?\d/.test(line)) {
|
|
1183
|
+
return "unsupported timeline command; put flow and cue lines inside beat blocks";
|
|
1184
|
+
}
|
|
1185
|
+
if (/^camera\b/.test(line)) {
|
|
1186
|
+
return "unsupported camera command; use frame NodeOrGroup zoom=... inside a beat";
|
|
1187
|
+
}
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
var RESERVED_VAR_NAMES = /* @__PURE__ */ new Set(["nodes", "title", "edges"]);
|
|
1191
|
+
function extractVars(blocks, diagnostics) {
|
|
1192
|
+
const vars = /* @__PURE__ */ new Map();
|
|
1193
|
+
const rest = [];
|
|
1194
|
+
for (const block of blocks) {
|
|
1195
|
+
if (!/^var\b/.test(block.text)) {
|
|
1196
|
+
rest.push(block);
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
const m = block.text.match(/^var\s+([A-Za-z_]\w*)\s*=\s*(.+)$/);
|
|
1200
|
+
if (!m) throw new ParseError("expected var name = value", block.line);
|
|
1201
|
+
const name = m[1];
|
|
1202
|
+
if (RESERVED_VAR_NAMES.has(name)) {
|
|
1203
|
+
diagnostics.push({ severity: "warning", message: `var '${name}' shadows a reserved selector and was ignored`, line: block.line });
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
const raw = m[2].trim();
|
|
1207
|
+
const str = parseStringToken(raw);
|
|
1208
|
+
vars.set(name, str && str.rest === "" ? str.value : raw);
|
|
1209
|
+
}
|
|
1210
|
+
return { vars, rest };
|
|
1211
|
+
}
|
|
1212
|
+
function applyVars(blocks, vars) {
|
|
1213
|
+
if (vars.size === 0) return blocks;
|
|
1214
|
+
return blocks.map((block) => {
|
|
1215
|
+
let text = block.text;
|
|
1216
|
+
for (const [name, value] of vars) {
|
|
1217
|
+
text = text.replace(new RegExp(`\\$${name}(?![\\w])`, "g"), value);
|
|
1218
|
+
}
|
|
1219
|
+
return { ...block, text };
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1124
1222
|
function pushWarning(diagnostics, seen, line, message) {
|
|
1125
1223
|
const key = `${line}:${message}`;
|
|
1126
1224
|
if (seen.has(key)) return;
|
|
@@ -1200,16 +1298,23 @@ function parse(source, opts = {}) {
|
|
|
1200
1298
|
const beats = [];
|
|
1201
1299
|
let title = "";
|
|
1202
1300
|
let edgeCounter = 0;
|
|
1203
|
-
const
|
|
1301
|
+
const rawBlocks = readBlocks(lines.map(stripComment));
|
|
1302
|
+
const { vars, rest } = extractVars(rawBlocks, diagnostics);
|
|
1303
|
+
const blocks = applyVars(rest, vars);
|
|
1204
1304
|
let i = 0;
|
|
1205
1305
|
while (i < blocks.length) {
|
|
1206
1306
|
const block = blocks[i];
|
|
1207
1307
|
const line = block.text;
|
|
1208
1308
|
const lineNo = block.line;
|
|
1309
|
+
const unsupportedMessage = unsupportedSyntaxMessage(line);
|
|
1310
|
+
if (unsupportedMessage) throw new ParseError(unsupportedMessage, lineNo);
|
|
1209
1311
|
if (line.startsWith("scene")) {
|
|
1210
|
-
const
|
|
1211
|
-
|
|
1212
|
-
|
|
1312
|
+
const rest2 = line.slice(5).trim();
|
|
1313
|
+
if (line.endsWith("{")) {
|
|
1314
|
+
throw new ParseError(`nested scene blocks are not supported; use one scene with multiple beat blocks`, lineNo);
|
|
1315
|
+
}
|
|
1316
|
+
let remainder = rest2;
|
|
1317
|
+
const str = parseStringToken(rest2);
|
|
1213
1318
|
if (str) {
|
|
1214
1319
|
title = str.value;
|
|
1215
1320
|
remainder = str.rest;
|
|
@@ -1256,16 +1361,31 @@ function parse(source, opts = {}) {
|
|
|
1256
1361
|
continue;
|
|
1257
1362
|
}
|
|
1258
1363
|
if (line.startsWith("group ")) {
|
|
1259
|
-
const
|
|
1260
|
-
if (
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1364
|
+
const inline = line.match(/^group\s+(\w+)(?:\s+"([^"]*)")?\s*:\s*(.+)$/);
|
|
1365
|
+
if (inline) {
|
|
1366
|
+
groups[inline[1]] = {
|
|
1367
|
+
id: inline[1],
|
|
1368
|
+
label: inline[2],
|
|
1369
|
+
members: splitTargets(inline[3]),
|
|
1370
|
+
props: {},
|
|
1371
|
+
line: lineNo
|
|
1372
|
+
};
|
|
1373
|
+
i++;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const header = line.match(/^group\s+(\w+)(?:\s+"([^"]*)")?\s*:\s*$/);
|
|
1377
|
+
if (!header) throw new ParseError(`expected group name: A B C`, lineNo);
|
|
1378
|
+
const { body, nextIdx } = readIndentedBody(blocks, i + 1, block.indent);
|
|
1379
|
+
const members = body.flatMap((b) => splitTargets(b.text));
|
|
1380
|
+
if (members.length === 0) throw new ParseError(`group '${header[1]}' has no members`, lineNo);
|
|
1381
|
+
groups[header[1]] = {
|
|
1382
|
+
id: header[1],
|
|
1383
|
+
label: header[2],
|
|
1384
|
+
members,
|
|
1265
1385
|
props: {},
|
|
1266
1386
|
line: lineNo
|
|
1267
1387
|
};
|
|
1268
|
-
i
|
|
1388
|
+
i = nextIdx;
|
|
1269
1389
|
continue;
|
|
1270
1390
|
}
|
|
1271
1391
|
if (line.startsWith("edge ")) {
|
|
@@ -1287,7 +1407,7 @@ function parse(source, opts = {}) {
|
|
|
1287
1407
|
continue;
|
|
1288
1408
|
}
|
|
1289
1409
|
if (line.startsWith("beat ")) {
|
|
1290
|
-
const m = line.match(/^beat\s+(\w+)(?:\s+"([^"]*)")?\s*(?::|\{)\s*$/);
|
|
1410
|
+
const m = line.match(/^beat\s+([\w.-]+)(?:\s+"([^"]*)")?\s*(?::|\{)\s*$/);
|
|
1291
1411
|
if (!m) throw new ParseError(`expected beat name:`, lineNo);
|
|
1292
1412
|
const { body, nextIdx } = readBody(blocks, i + 1, block.indent, line.endsWith("{"));
|
|
1293
1413
|
let cues = normalizeCueBlocks(body).map((b) => parseCueLine(b.text, b.line));
|
|
@@ -1358,6 +1478,7 @@ function parseAndCompile(source) {
|
|
|
1358
1478
|
}
|
|
1359
1479
|
export {
|
|
1360
1480
|
BEAT_CUE_KEYWORDS,
|
|
1481
|
+
CUE_ALIASES,
|
|
1361
1482
|
EDGE_OPERATORS,
|
|
1362
1483
|
NODE_ALIASES,
|
|
1363
1484
|
NODE_KINDS,
|
package/package.json
CHANGED