@markdy/core 0.8.5 → 0.8.6
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 +7 -1
- package/dist/index.js +88 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,12 @@ type Cue = {
|
|
|
77
77
|
zoom?: number;
|
|
78
78
|
dur?: number;
|
|
79
79
|
line: number;
|
|
80
|
+
} | {
|
|
81
|
+
kind: "frame";
|
|
82
|
+
targets: string[];
|
|
83
|
+
zoom?: number;
|
|
84
|
+
dur?: number;
|
|
85
|
+
line: number;
|
|
80
86
|
} | {
|
|
81
87
|
kind: "use";
|
|
82
88
|
pattern: string;
|
|
@@ -163,7 +169,7 @@ type RoutedEdge = {
|
|
|
163
169
|
type TimedCue = {
|
|
164
170
|
start: number;
|
|
165
171
|
duration: number;
|
|
166
|
-
kind: "show" | "hide" | "flow" | "glow" | "focus";
|
|
172
|
+
kind: "show" | "hide" | "flow" | "glow" | "focus" | "frame";
|
|
167
173
|
targets: string[];
|
|
168
174
|
edgeId?: string;
|
|
169
175
|
segments?: FlowSegment[];
|
package/dist/index.js
CHANGED
|
@@ -349,7 +349,8 @@ var EDGE_OPERATORS = {
|
|
|
349
349
|
"~>": "event",
|
|
350
350
|
"--": "dependency"
|
|
351
351
|
};
|
|
352
|
-
var
|
|
352
|
+
var RESERVED_SELECTORS = /* @__PURE__ */ new Set(["$title", "$nodes", "$edges"]);
|
|
353
|
+
var BEAT_CUE_KEYWORDS = /* @__PURE__ */ new Set(["show", "hide", "glow", "focus", "frame", "use"]);
|
|
353
354
|
var SCENE_KEYS = /* @__PURE__ */ new Set(["width", "height", "fps", "theme", "duration", "direction"]);
|
|
354
355
|
function nodeRole(kind) {
|
|
355
356
|
return TECHNICAL_NODE_KINDS[kind] ?? "compute";
|
|
@@ -391,6 +392,7 @@ var DEFAULTS = {
|
|
|
391
392
|
flow: 0.55,
|
|
392
393
|
glow: 0.45,
|
|
393
394
|
focus: 0.6,
|
|
395
|
+
frame: 0.7,
|
|
394
396
|
beatGap: 0.14,
|
|
395
397
|
cueGap: 0.08,
|
|
396
398
|
stagger: 0.06
|
|
@@ -575,7 +577,7 @@ function scheduleBeats(ast, edges) {
|
|
|
575
577
|
}
|
|
576
578
|
return;
|
|
577
579
|
}
|
|
578
|
-
if (cue.kind !== "show" && cue.kind !== "hide" && cue.kind !== "glow" && cue.kind !== "focus") return;
|
|
580
|
+
if (cue.kind !== "show" && cue.kind !== "hide" && cue.kind !== "glow" && cue.kind !== "focus" && cue.kind !== "frame") return;
|
|
579
581
|
const targets = resolveTargets(cue.targets, ast, Object.fromEntries(Object.entries(ast.groups).map(([k, g]) => [k, g.members])));
|
|
580
582
|
scheduled.push({
|
|
581
583
|
start: t,
|
|
@@ -586,7 +588,7 @@ function scheduleBeats(ast, edges) {
|
|
|
586
588
|
stagger: cue.kind === "show" ? cue.stagger ?? DEFAULTS.stagger : void 0,
|
|
587
589
|
color: cue.kind === "glow" ? cue.color : void 0,
|
|
588
590
|
strength: cue.kind === "glow" ? cue.strength : void 0,
|
|
589
|
-
zoom: cue.kind === "focus" ? cue.zoom : void 0
|
|
591
|
+
zoom: cue.kind === "focus" || cue.kind === "frame" ? cue.zoom : void 0
|
|
590
592
|
},
|
|
591
593
|
beat: beat.name
|
|
592
594
|
});
|
|
@@ -834,8 +836,7 @@ function splitTargetLabel(token, lineNo) {
|
|
|
834
836
|
}
|
|
835
837
|
function parseCueLine(line, lineNo) {
|
|
836
838
|
const trimmed = line.trim();
|
|
837
|
-
const
|
|
838
|
-
const props = propsMatch ? parseProps(propsMatch[0]) : {};
|
|
839
|
+
const props = parseProps(trimmed);
|
|
839
840
|
if (trimmed.includes(" & ")) {
|
|
840
841
|
const parts = trimmed.split(/\s+&\s+/);
|
|
841
842
|
return {
|
|
@@ -886,6 +887,18 @@ function parseCueLine(line, lineNo) {
|
|
|
886
887
|
line: lineNo
|
|
887
888
|
};
|
|
888
889
|
}
|
|
890
|
+
if (keyword === "frame") {
|
|
891
|
+
const targetRaw = rest.join(" ").split(/\s+(?:zoom|dur)=/)[0];
|
|
892
|
+
const targets = splitTargets(targetRaw);
|
|
893
|
+
if (targets.length === 0) throw new ParseError(`expected frame target`, lineNo);
|
|
894
|
+
return {
|
|
895
|
+
kind: "frame",
|
|
896
|
+
targets,
|
|
897
|
+
zoom: typeof props.zoom === "number" ? props.zoom : void 0,
|
|
898
|
+
dur: typeof props.dur === "number" ? props.dur : void 0,
|
|
899
|
+
line: lineNo
|
|
900
|
+
};
|
|
901
|
+
}
|
|
889
902
|
if (keyword === "use") {
|
|
890
903
|
const call = rest.join(" ");
|
|
891
904
|
const m = call.match(/^(\w+)\s*\((.*)\)\s*$/);
|
|
@@ -931,10 +944,9 @@ function substitutePatternCue(cue, params, args) {
|
|
|
931
944
|
params.forEach((p, i) => {
|
|
932
945
|
if (resolvedArgs[p] === void 0 && positional[i]) resolvedArgs[p] = positional[i];
|
|
933
946
|
});
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
delete resolvedArgs.__pos_3;
|
|
947
|
+
for (const key of Object.keys(resolvedArgs)) {
|
|
948
|
+
if (key.startsWith("__pos_")) delete resolvedArgs[key];
|
|
949
|
+
}
|
|
938
950
|
const sub = (s) => {
|
|
939
951
|
for (const p of params) {
|
|
940
952
|
if (resolvedArgs[p]) s = s.replaceAll(`$${p}`, resolvedArgs[p]);
|
|
@@ -952,7 +964,7 @@ function substitutePatternCue(cue, params, args) {
|
|
|
952
964
|
}))
|
|
953
965
|
};
|
|
954
966
|
}
|
|
955
|
-
if (cue.kind === "show" || cue.kind === "hide" || cue.kind === "glow" || cue.kind === "focus") {
|
|
967
|
+
if (cue.kind === "show" || cue.kind === "hide" || cue.kind === "glow" || cue.kind === "focus" || cue.kind === "frame") {
|
|
956
968
|
return { ...cue, targets: cue.targets.map(sub) };
|
|
957
969
|
}
|
|
958
970
|
if (cue.kind === "parallel") {
|
|
@@ -980,6 +992,67 @@ function readIndentedBody(blocks, startIdx, parentIndent) {
|
|
|
980
992
|
}
|
|
981
993
|
return { body, nextIdx: i };
|
|
982
994
|
}
|
|
995
|
+
function pushWarning(diagnostics, seen, line, message) {
|
|
996
|
+
const key = `${line}:${message}`;
|
|
997
|
+
if (seen.has(key)) return;
|
|
998
|
+
seen.add(key);
|
|
999
|
+
diagnostics.push({ severity: "warning", message, line });
|
|
1000
|
+
}
|
|
1001
|
+
function visitCues(cues, visit) {
|
|
1002
|
+
for (const cue of cues) {
|
|
1003
|
+
visit(cue);
|
|
1004
|
+
if (cue.kind === "parallel") visitCues(cue.cues, visit);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function validateReferences(ast) {
|
|
1008
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1009
|
+
const hasNode = (id) => Boolean(ast.nodes[id]);
|
|
1010
|
+
const hasGroup = (id) => Boolean(ast.groups[id]);
|
|
1011
|
+
const isKnownTarget = (target) => {
|
|
1012
|
+
if (RESERVED_SELECTORS.has(target)) return true;
|
|
1013
|
+
if (target.startsWith("$")) return hasGroup(target.slice(1));
|
|
1014
|
+
return hasNode(target) || hasGroup(target);
|
|
1015
|
+
};
|
|
1016
|
+
for (const group of Object.values(ast.groups)) {
|
|
1017
|
+
for (const member of group.members) {
|
|
1018
|
+
if (!hasNode(member)) {
|
|
1019
|
+
pushWarning(ast.diagnostics, seen, group.line, `group '${group.id}' references unknown node '${member}'`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
for (const node of Object.values(ast.nodes)) {
|
|
1024
|
+
if (node.style && !ast.styles[node.style]) {
|
|
1025
|
+
pushWarning(ast.diagnostics, seen, node.line, `node '${node.id}' references unknown style '${node.style}'`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
const validateFlowEndpoint = (line, endpoint) => {
|
|
1029
|
+
if (!hasNode(endpoint)) {
|
|
1030
|
+
pushWarning(ast.diagnostics, seen, line, `flow references unknown node '${endpoint}'`);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
for (const edge of ast.edges) {
|
|
1034
|
+
validateFlowEndpoint(edge.line, edge.from);
|
|
1035
|
+
validateFlowEndpoint(edge.line, edge.to);
|
|
1036
|
+
}
|
|
1037
|
+
for (const beat of ast.beats) {
|
|
1038
|
+
visitCues(beat.cues, (cue) => {
|
|
1039
|
+
if (cue.kind === "flow") {
|
|
1040
|
+
for (const segment of cue.segments) {
|
|
1041
|
+
validateFlowEndpoint(cue.line, segment.from);
|
|
1042
|
+
validateFlowEndpoint(cue.line, segment.to);
|
|
1043
|
+
}
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (cue.kind === "show" || cue.kind === "hide" || cue.kind === "glow" || cue.kind === "focus" || cue.kind === "frame") {
|
|
1047
|
+
for (const target of cue.targets) {
|
|
1048
|
+
if (!isKnownTarget(target)) {
|
|
1049
|
+
pushWarning(ast.diagnostics, seen, cue.line, `${cue.kind} references unknown target '${target}'`);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
983
1056
|
function parse(source, opts = {}) {
|
|
984
1057
|
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
985
1058
|
const diagnostics = [];
|
|
@@ -1122,10 +1195,6 @@ function parse(source, opts = {}) {
|
|
|
1122
1195
|
}
|
|
1123
1196
|
throw new ParseError(`unexpected statement`, lineNo);
|
|
1124
1197
|
}
|
|
1125
|
-
const errors = diagnostics.filter((d) => d.severity === "error");
|
|
1126
|
-
if (errors.length) {
|
|
1127
|
-
throw new ParseError(errors[0].message, errors[0].line, errors[0].column);
|
|
1128
|
-
}
|
|
1129
1198
|
const ast = {
|
|
1130
1199
|
meta: { ...meta, title: title || void 0 },
|
|
1131
1200
|
styles,
|
|
@@ -1136,6 +1205,11 @@ function parse(source, opts = {}) {
|
|
|
1136
1205
|
beats,
|
|
1137
1206
|
diagnostics
|
|
1138
1207
|
};
|
|
1208
|
+
validateReferences(ast);
|
|
1209
|
+
const errors = ast.diagnostics.filter((d) => d.severity === "error");
|
|
1210
|
+
if (errors.length) {
|
|
1211
|
+
throw new ParseError(errors[0].message, errors[0].line, errors[0].column);
|
|
1212
|
+
}
|
|
1139
1213
|
if (!opts.parseOnly && Object.keys(nodes).length === 0 && beats.length === 0) {
|
|
1140
1214
|
}
|
|
1141
1215
|
return ast;
|
package/package.json
CHANGED