@markdy/core 0.7.24 → 0.7.26

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
@@ -116,6 +116,12 @@ type SceneAST = {
116
116
  seqs: Record<string, SequenceDef>;
117
117
  /** User-defined variables — kept in AST for tooling/inspection. */
118
118
  vars: Record<string, string>;
119
+ /**
120
+ * Named actor groups, in declaration order, mapping group name to member
121
+ * actor names. Groups fan out into per-actor events at parse time, so the
122
+ * renderer never sees them — this is kept for tooling and inspection.
123
+ */
124
+ groups: Record<string, string[]>;
119
125
  /** Named chapter blocks in author order. Empty when no chapters were used. */
120
126
  chapters: Chapter[];
121
127
  /** Soft parse issues. Always present; empty in the happy path. */
@@ -180,6 +186,25 @@ type PresetFn = (args: string[]) => string;
180
186
  declare const PRESETS: Record<string, PresetFn>;
181
187
  declare const PRESET_NAMES: readonly string[];
182
188
 
189
+ /**
190
+ * The canonical vocabulary of the language.
191
+ *
192
+ * These arrays are the single source of truth for every tool that needs to
193
+ * know what Markdy understands — the parser, the language server, the
194
+ * playground's syntax highlighter, and the renderer's handler-coverage test.
195
+ * Anything that hard-codes its own copy will silently drift out of date, so
196
+ * import from here instead.
197
+ */
198
+ declare const BUILTIN_ACTOR_TYPES: readonly BuiltinActorType[];
199
+ /** Actions valid on every actor type. */
200
+ declare const UNIVERSAL_ACTION_NAMES: readonly ["enter", "exit", "move", "fade_in", "fade_out", "scale", "rotate", "shake", "say", "throw", "play"];
201
+ /**
202
+ * Actions that require a `figure` actor. Applying one to any other actor
203
+ * type is a hard `ParseError`, not a soft warning — it's always a mistake.
204
+ */
205
+ declare const FIGURE_ONLY_ACTION_NAMES: readonly ["punch", "kick", "wave", "nod", "jump", "bounce", "face", "rotate_part", "pose"];
206
+ /** Actions valid on the reserved `camera` actor. */
207
+ declare const CAMERA_ACTION_NAMES: readonly ["pan", "zoom", "shake"];
183
208
  type ActorPack = {
184
209
  name: string;
185
210
  actors: readonly string[];
@@ -187,4 +212,4 @@ type ActorPack = {
187
212
  };
188
213
  declare function registerActorPack(pack: ActorPack): void;
189
214
 
190
- export { type ActorDef, type ActorPack, type ActorType, type AssetDef, type BuiltinActorType, type Chapter, type ImportDecl, PRESETS, PRESET_NAMES, ParseError, type ParseOptions, type ParseWarning, type PresetFn, type SceneAST, type SceneMeta, type SequenceDef, type TemplateDef, type TimelineEvent, parse, registerActorPack };
215
+ export { type ActorDef, type ActorPack, type ActorType, type AssetDef, BUILTIN_ACTOR_TYPES, type BuiltinActorType, CAMERA_ACTION_NAMES, type Chapter, FIGURE_ONLY_ACTION_NAMES, type ImportDecl, PRESETS, PRESET_NAMES, ParseError, type ParseOptions, type ParseWarning, type PresetFn, type SceneAST, type SceneMeta, type SequenceDef, type TemplateDef, type TimelineEvent, UNIVERSAL_ACTION_NAMES, parse, registerActorPack };
package/dist/index.js CHANGED
@@ -232,7 +232,7 @@ var BUILTIN_ACTOR_TYPES = [
232
232
  "figure",
233
233
  "caption"
234
234
  ];
235
- var UNIVERSAL_ACTIONS = /* @__PURE__ */ new Set([
235
+ var UNIVERSAL_ACTION_NAMES = [
236
236
  "enter",
237
237
  "exit",
238
238
  "move",
@@ -244,8 +244,8 @@ var UNIVERSAL_ACTIONS = /* @__PURE__ */ new Set([
244
244
  "say",
245
245
  "throw",
246
246
  "play"
247
- ]);
248
- var FIGURE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
247
+ ];
248
+ var FIGURE_ONLY_ACTION_NAMES = [
249
249
  "punch",
250
250
  "kick",
251
251
  "wave",
@@ -255,8 +255,11 @@ var FIGURE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
255
255
  "face",
256
256
  "rotate_part",
257
257
  "pose"
258
- ]);
259
- var CAMERA_ACTIONS = /* @__PURE__ */ new Set(["pan", "zoom", "shake"]);
258
+ ];
259
+ var CAMERA_ACTION_NAMES = ["pan", "zoom", "shake"];
260
+ var UNIVERSAL_ACTIONS = new Set(UNIVERSAL_ACTION_NAMES);
261
+ var FIGURE_ONLY_ACTIONS = new Set(FIGURE_ONLY_ACTION_NAMES);
262
+ var CAMERA_ACTIONS = new Set(CAMERA_ACTION_NAMES);
260
263
  var actorTypes = new Set(BUILTIN_ACTOR_TYPES);
261
264
  var actorActions = /* @__PURE__ */ new Map();
262
265
  function registerActorPack(pack) {
@@ -555,6 +558,7 @@ function parse(source, opts = {}) {
555
558
  defs: {},
556
559
  seqs: {},
557
560
  vars: {},
561
+ groups: {},
558
562
  chapters: [],
559
563
  warnings: [],
560
564
  imports: []
@@ -800,6 +804,10 @@ function parse(source, opts = {}) {
800
804
  }
801
805
  continue;
802
806
  }
807
+ if (raw.startsWith("group ")) {
808
+ parseGroupLine(raw, lineNum, ast);
809
+ continue;
810
+ }
803
811
  if (raw.startsWith("@")) {
804
812
  parseEventLine(raw, lineNum, ast, inChapter, topScope);
805
813
  continue;
@@ -927,6 +935,59 @@ function parseActorLine(raw, lineNum, ast) {
927
935
  };
928
936
  return { name, args: resolvedArgs };
929
937
  }
938
+ var GROUP_RE = /^group\s+([A-Za-z_][\w.]*)\s*=\s*(.+)$/;
939
+ function parseGroupLine(raw, lineNum, ast) {
940
+ const m = GROUP_RE.exec(raw);
941
+ if (!m) {
942
+ throw new ParseError(`Invalid group declaration: ${raw}`, lineNum);
943
+ }
944
+ const [, name, membersRaw] = m;
945
+ if (ast.actors[name]) {
946
+ throw new ParseError(`Group "${name}" collides with an actor of the same name`, lineNum);
947
+ }
948
+ if (ast.groups[name]) {
949
+ throw new ParseError(`Group "${name}" is already defined`, lineNum);
950
+ }
951
+ const members = splitByComma(membersRaw).map((part) => part.trim()).filter((part) => part.length > 0);
952
+ if (members.length === 0) {
953
+ throw new ParseError(`Group "${name}" has no members`, lineNum);
954
+ }
955
+ for (const member of members) {
956
+ if (!ast.actors[member]) {
957
+ throw new ParseError(`Unknown actor "${member}" in group "${name}"`, lineNum);
958
+ }
959
+ }
960
+ const seen = /* @__PURE__ */ new Set();
961
+ for (const member of members) {
962
+ if (seen.has(member)) {
963
+ throw new ParseError(`Actor "${member}" listed twice in group "${name}"`, lineNum);
964
+ }
965
+ seen.add(member);
966
+ }
967
+ ast.groups[name] = members;
968
+ }
969
+ function takeStagger(paramsRaw, lineNum) {
970
+ if (!paramsRaw.includes("stagger")) return { step: 0, rest: paramsRaw };
971
+ const kept = [];
972
+ let step = 0;
973
+ for (const part of splitByComma(paramsRaw)) {
974
+ const eq = part.indexOf("=");
975
+ if (eq !== -1 && part.slice(0, eq).trim() === "stagger") {
976
+ const rawValue = part.slice(eq + 1).trim();
977
+ const value = Number(rawValue);
978
+ if (Number.isNaN(value)) {
979
+ throw new ParseError(`Invalid stagger value: ${rawValue}`, lineNum);
980
+ }
981
+ if (value < 0) {
982
+ throw new ParseError(`stagger must not be negative (got ${rawValue})`, lineNum);
983
+ }
984
+ step = value;
985
+ continue;
986
+ }
987
+ kept.push(part);
988
+ }
989
+ return { step, rest: kept.join(",") };
990
+ }
930
991
  function parseEventLine(raw, lineNum, ast, inChapter, topScope) {
931
992
  const scope = inChapter?.scope ?? topScope;
932
993
  const recordEventTime = (t) => {
@@ -977,18 +1038,54 @@ function parseEventLine(raw, lineNum, ast, inChapter, topScope) {
977
1038
  line: lineNum
978
1039
  });
979
1040
  }
980
- const params2 = parseActionParams(action, paramsRaw);
1041
+ const params = parseActionParams(action, paramsRaw);
981
1042
  recordEventTime(time);
982
1043
  pushEvent(ast, scope, {
983
1044
  time,
984
1045
  actor: "camera",
985
1046
  action,
986
- params: params2,
1047
+ params,
987
1048
  line: lineNum,
988
1049
  ...inChapter ? { chapter: inChapter.name } : {}
989
1050
  });
990
1051
  return;
991
1052
  }
1053
+ const groupMembers = ast.groups[actor];
1054
+ if (!groupMembers && !ast.actors[actor]) {
1055
+ throw new ParseError(`Unknown actor: "${actor}"`, lineNum);
1056
+ }
1057
+ if (groupMembers) {
1058
+ const { step, rest } = takeStagger(paramsRaw, lineNum);
1059
+ groupMembers.forEach((member, index) => {
1060
+ emitActorEvent(
1061
+ member,
1062
+ round3(time + index * step),
1063
+ action,
1064
+ rest,
1065
+ mustUnderstand,
1066
+ lineNum,
1067
+ ast,
1068
+ scope,
1069
+ inChapter,
1070
+ recordEventTime
1071
+ );
1072
+ });
1073
+ return;
1074
+ }
1075
+ emitActorEvent(
1076
+ actor,
1077
+ time,
1078
+ action,
1079
+ paramsRaw,
1080
+ mustUnderstand,
1081
+ lineNum,
1082
+ ast,
1083
+ scope,
1084
+ inChapter,
1085
+ recordEventTime
1086
+ );
1087
+ }
1088
+ function emitActorEvent(actor, time, action, paramsRaw, mustUnderstand, lineNum, ast, scope, inChapter, recordEventTime) {
992
1089
  const actorDef = ast.actors[actor];
993
1090
  if (!actorDef) {
994
1091
  throw new ParseError(`Unknown actor: "${actor}"`, lineNum);
@@ -1105,9 +1202,13 @@ function round3(n) {
1105
1202
  return Math.round(n * 1e3) / 1e3;
1106
1203
  }
1107
1204
  export {
1205
+ BUILTIN_ACTOR_TYPES,
1206
+ CAMERA_ACTION_NAMES,
1207
+ FIGURE_ONLY_ACTION_NAMES,
1108
1208
  PRESETS,
1109
1209
  PRESET_NAMES,
1110
1210
  ParseError,
1211
+ UNIVERSAL_ACTION_NAMES,
1111
1212
  parse,
1112
1213
  registerActorPack
1113
1214
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/core",
3
- "version": "0.7.24",
3
+ "version": "0.7.26",
4
4
  "description": "MarkdyScript parser and AST types — zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",