@markdy/core 0.7.10 → 0.7.12

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
@@ -7,7 +7,7 @@ type AssetDef = {
7
7
  value: string;
8
8
  };
9
9
  type ActorDef = {
10
- type: "sprite" | "text" | "box" | "figure" | "caption";
10
+ type: ActorType;
11
11
  /** Constructor arguments: asset name for sprite, display text for text/caption actors. */
12
12
  args: string[];
13
13
  x: number;
@@ -26,6 +26,8 @@ type ActorDef = {
26
26
  */
27
27
  anchor?: "top" | "bottom" | "center";
28
28
  };
29
+ type BuiltinActorType = "sprite" | "text" | "box" | "figure" | "caption";
30
+ type ActorType = BuiltinActorType | (string & {});
29
31
  type TimelineEvent = {
30
32
  time: number;
31
33
  actor: string;
@@ -89,7 +91,7 @@ type Chapter = {
89
91
  * renderer otherwise no-ops the offending statement.
90
92
  */
91
93
  type ParseWarning = {
92
- kind: "unknown-action" | "unknown-modifier" | "unknown-scene-key" | "unknown-camera-action" | "unknown-preset" | "import-unresolved" | "preset-mixed";
94
+ kind: "unknown-action" | "unknown-modifier" | "unknown-scene-key" | "unknown-camera-action" | "unknown-preset" | "import-unresolved" | "preset-mixed" | "actor-count-threshold" | "label-overflow";
93
95
  message: string;
94
96
  line: number;
95
97
  };
@@ -137,6 +139,17 @@ interface ParseOptions {
137
139
  * `<ns>.<name>` prefix. Missing namespaces produce a soft warning.
138
140
  */
139
141
  imports?: Record<string, SceneAST>;
142
+ /**
143
+ * Emit a non-fatal warning when actor count exceeds this threshold.
144
+ * Set to <= 0 to disable.
145
+ */
146
+ actorCountWarningThreshold?: number;
147
+ /**
148
+ * Emit a non-fatal warning when actor labels exceed this length.
149
+ * Applies to the first constructor arg when it is a string.
150
+ * Set to <= 0 to disable.
151
+ */
152
+ labelLengthWarningThreshold?: number;
140
153
  /**
141
154
  * Internal flag — distinguishes a top-level call from a recursive
142
155
  * call used to expand a `preset`. Preset expansion bypasses the
@@ -167,4 +180,11 @@ type PresetFn = (args: string[]) => string;
167
180
  declare const PRESETS: Record<string, PresetFn>;
168
181
  declare const PRESET_NAMES: readonly string[];
169
182
 
170
- export { type ActorDef, type AssetDef, 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 };
183
+ type ActorPack = {
184
+ name: string;
185
+ actors: readonly string[];
186
+ actions?: Record<string, readonly string[]>;
187
+ };
188
+ declare function registerActorPack(pack: ActorPack): void;
189
+
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 };
package/dist/index.js CHANGED
@@ -224,6 +224,69 @@ actor title = caption("tier list") at top
224
224
  };
225
225
  var PRESET_NAMES = Object.keys(PRESETS);
226
226
 
227
+ // src/registry.ts
228
+ var BUILTIN_ACTOR_TYPES = [
229
+ "sprite",
230
+ "text",
231
+ "box",
232
+ "figure",
233
+ "caption"
234
+ ];
235
+ var UNIVERSAL_ACTIONS = /* @__PURE__ */ new Set([
236
+ "enter",
237
+ "exit",
238
+ "move",
239
+ "fade_in",
240
+ "fade_out",
241
+ "scale",
242
+ "rotate",
243
+ "shake",
244
+ "say",
245
+ "throw",
246
+ "play"
247
+ ]);
248
+ var FIGURE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
249
+ "punch",
250
+ "kick",
251
+ "wave",
252
+ "nod",
253
+ "jump",
254
+ "bounce",
255
+ "face",
256
+ "rotate_part",
257
+ "pose"
258
+ ]);
259
+ var CAMERA_ACTIONS = /* @__PURE__ */ new Set(["pan", "zoom", "shake"]);
260
+ var actorTypes = new Set(BUILTIN_ACTOR_TYPES);
261
+ var actorActions = /* @__PURE__ */ new Map();
262
+ function registerActorPack(pack) {
263
+ for (const actor of pack.actors) {
264
+ actorTypes.add(actor);
265
+ if (!actorActions.has(actor)) actorActions.set(actor, /* @__PURE__ */ new Set());
266
+ }
267
+ if (!pack.actions) return;
268
+ for (const [actorType, actions] of Object.entries(pack.actions)) {
269
+ actorTypes.add(actorType);
270
+ const known = actorActions.get(actorType) ?? /* @__PURE__ */ new Set();
271
+ for (const action of actions) known.add(action);
272
+ actorActions.set(actorType, known);
273
+ }
274
+ }
275
+ function isKnownActorType(type) {
276
+ return actorTypes.has(type);
277
+ }
278
+ function isFigureOnlyAction(action) {
279
+ return FIGURE_ONLY_ACTIONS.has(action);
280
+ }
281
+ function isCameraAction(action) {
282
+ return CAMERA_ACTIONS.has(action);
283
+ }
284
+ function isKnownAction(actorType, action) {
285
+ if (UNIVERSAL_ACTIONS.has(action)) return true;
286
+ if (actorType === "figure" && FIGURE_ONLY_ACTIONS.has(action)) return true;
287
+ return actorActions.get(actorType)?.has(action) ?? false;
288
+ }
289
+
227
290
  // src/parser.ts
228
291
  var ParseError = class extends Error {
229
292
  constructor(message, line) {
@@ -369,36 +432,6 @@ function parseActionParams(action, raw) {
369
432
  }
370
433
  return params;
371
434
  }
372
- var UNIVERSAL_ACTIONS = /* @__PURE__ */ new Set([
373
- "enter",
374
- "exit",
375
- "move",
376
- "fade_in",
377
- "fade_out",
378
- "scale",
379
- "rotate",
380
- "shake",
381
- "say",
382
- "throw",
383
- "play"
384
- ]);
385
- var FIGURE_ONLY_ACTIONS = /* @__PURE__ */ new Set([
386
- "punch",
387
- "kick",
388
- "wave",
389
- "nod",
390
- "jump",
391
- "bounce",
392
- "face",
393
- "rotate_part",
394
- "pose"
395
- ]);
396
- var CAMERA_ACTIONS = /* @__PURE__ */ new Set(["pan", "zoom", "shake"]);
397
- function isKnownAction(actorType, action) {
398
- if (actorType === "camera") return CAMERA_ACTIONS.has(action);
399
- if (UNIVERSAL_ACTIONS.has(action)) return true;
400
- return FIGURE_ONLY_ACTIONS.has(action);
401
- }
402
435
  var MODIFIER_KEYS = /* @__PURE__ */ new Set(["scale", "rotate", "opacity", "size", "z"]);
403
436
  function parseSpaceModifiers(raw, lineNum, warnings) {
404
437
  const result = {};
@@ -469,14 +502,13 @@ function parseActorTrailer(trailerRaw, lineNum, warnings) {
469
502
  return { ...fromSpace, ...fromWith };
470
503
  }
471
504
  var ASSET_RE = /^asset\s+(\w+)\s*=\s*(image|icon)\("([^"]+)"\)$/;
472
- var BUILTIN_ACTOR_TYPES = /* @__PURE__ */ new Set(["sprite", "text", "box", "figure", "caption"]);
473
505
  var ACTOR_NUM_POS_RE = /^actor\s+(\w+)\s*=\s*([\w.]+)\(([^)]*)\)\s+at\s+\(\s*(-?[\d.]+)\s*,\s*(-?[\d.]+)\s*\)(.*)$/;
474
506
  var ACTOR_ANCHOR_POS_RE = /^actor\s+(\w+)\s*=\s*([\w.]+)\(([^)]*)\)\s+at\s+(top|bottom|center)\b(.*)$/;
475
507
  var EVENT_RE = /^@([\d.]+):\s+(\w+)\.(!?\w+)\((.*)\)$/;
476
508
  var REL_EVENT_RE = /^@\+([\d.]+):\s+(\w+)\.(!?\w+)\((.*)\)$/;
477
509
  var VAR_RE = /^var\s+(\w+)\s*=\s*(.+)$/;
478
510
  var DEF_HEADER_RE = /^def\s+(\w+)\(([^)]*)\)\s*\{$/;
479
- var DEF_BODY_RE = /^\s*(sprite|text|box|figure|caption)\(([^)]*)\)\s*$/;
511
+ var DEF_BODY_RE = /^\s*([\w]+)\(([^)]*)\)\s*$/;
480
512
  var SEQ_HEADER_RE = /^seq\s+(\w+)(?:\(([^)]*)\))?\s*\{$/;
481
513
  var SEQ_EVENT_RE = /^@\+([\d.]+):\s+\$\.(!?\w+)\((.*)\)$/;
482
514
  var CHAPTER_HEADER_RE = /^scene\s+"([^"]+)"\s*\{$/;
@@ -527,6 +559,9 @@ function parse(source, opts = {}) {
527
559
  warnings: [],
528
560
  imports: []
529
561
  };
562
+ const actorCountWarningThreshold = opts.actorCountWarningThreshold ?? 10;
563
+ const labelLengthWarningThreshold = opts.labelLengthWarningThreshold ?? 36;
564
+ let actorCountWarned = false;
530
565
  let sceneFound = false;
531
566
  const lines = source.split(/\r?\n/);
532
567
  let inDef = null;
@@ -584,6 +619,9 @@ function parse(source, opts = {}) {
584
619
  throw new ParseError(`Invalid def body (expected "type(args)"): ${raw}`, lineNum);
585
620
  }
586
621
  const [, actorType, bodyArgsRaw] = bm;
622
+ if (!isKnownActorType(actorType)) {
623
+ throw new ParseError(`Unknown actor type in def body: "${actorType}"`, lineNum);
624
+ }
587
625
  const bodyArgs = bodyArgsRaw.trim() ? splitByComma(bodyArgsRaw).map((a) => a.trim()) : [];
588
626
  ast.defs[inDef.name] = {
589
627
  params: inDef.params,
@@ -738,7 +776,28 @@ function parse(source, opts = {}) {
738
776
  continue;
739
777
  }
740
778
  if (raw.startsWith("actor ")) {
741
- parseActorLine(raw, lineNum, ast);
779
+ const actorInfo = parseActorLine(raw, lineNum, ast);
780
+ if (!actorCountWarned && actorCountWarningThreshold > 0) {
781
+ const actorCount = Object.keys(ast.actors).length;
782
+ if (actorCount > actorCountWarningThreshold) {
783
+ actorCountWarned = true;
784
+ ast.warnings.push({
785
+ kind: "actor-count-threshold",
786
+ message: `scene has ${actorCount} actors (threshold ${actorCountWarningThreshold}); consider splitting into multiple scenes`,
787
+ line: lineNum
788
+ });
789
+ }
790
+ }
791
+ if (labelLengthWarningThreshold > 0 && typeof actorInfo.args[0] === "string") {
792
+ const label = actorInfo.args[0];
793
+ if (label.length > labelLengthWarningThreshold) {
794
+ ast.warnings.push({
795
+ kind: "label-overflow",
796
+ message: `actor "${actorInfo.name}" label length ${label.length} exceeds ${labelLengthWarningThreshold}; consider shortening to reduce overlap`,
797
+ line: lineNum
798
+ });
799
+ }
800
+ }
742
801
  continue;
743
802
  }
744
803
  if (raw.startsWith("@")) {
@@ -780,9 +839,7 @@ function parseActorCallArgs(argsRaw) {
780
839
  function parseActorLine(raw, lineNum, ast) {
781
840
  const amAnchor = ACTOR_ANCHOR_POS_RE.exec(raw);
782
841
  const amNum = amAnchor ? null : ACTOR_NUM_POS_RE.exec(raw);
783
- if (!amAnchor && !amNum) {
784
- throw new ParseError(`Invalid actor declaration: ${raw}`, lineNum);
785
- }
842
+ if (!amAnchor && !amNum) throw new ParseError(`Invalid actor declaration: ${raw}`, lineNum);
786
843
  let name;
787
844
  let typeName;
788
845
  let argsRaw;
@@ -827,7 +884,7 @@ function parseActorLine(raw, lineNum, ast) {
827
884
  const rawArgs = parseActorCallArgs(argsRaw);
828
885
  let resolvedType;
829
886
  let resolvedArgs;
830
- if (BUILTIN_ACTOR_TYPES.has(typeName)) {
887
+ if (isKnownActorType(typeName)) {
831
888
  resolvedType = typeName;
832
889
  resolvedArgs = rawArgs;
833
890
  } else if (ast.defs[typeName]) {
@@ -868,6 +925,7 @@ function parseActorLine(raw, lineNum, ast) {
868
925
  ...modifiers,
869
926
  ...anchor ? { anchor } : {}
870
927
  };
928
+ return { name, args: resolvedArgs };
871
929
  }
872
930
  function parseEventLine(raw, lineNum, ast, inChapter, topScope) {
873
931
  const scope = inChapter?.scope ?? topScope;
@@ -909,7 +967,7 @@ function parseEventLine(raw, lineNum, ast, inChapter, topScope) {
909
967
  const mustUnderstand = actionToken.startsWith("!");
910
968
  const action = mustUnderstand ? actionToken.slice(1) : actionToken;
911
969
  if (actor === "camera") {
912
- if (!CAMERA_ACTIONS.has(action)) {
970
+ if (!isCameraAction(action)) {
913
971
  if (mustUnderstand) {
914
972
  throw new ParseError(`Unknown camera action "${action}"`, lineNum);
915
973
  }
@@ -998,7 +1056,7 @@ function pushEvent(ast, scope, ev) {
998
1056
  if (endTime > scope.prevEnd) scope.prevEnd = endTime;
999
1057
  }
1000
1058
  function validateActionForActor(action, actorDef, mustUnderstand, lineNum, warnings) {
1001
- if (FIGURE_ONLY_ACTIONS.has(action)) {
1059
+ if (isFigureOnlyAction(action)) {
1002
1060
  if (actorDef.type !== "figure") {
1003
1061
  throw new ParseError(
1004
1062
  `action "${action}" is figure-only; actor type is "${actorDef.type}"`,
@@ -1050,5 +1108,6 @@ export {
1050
1108
  PRESETS,
1051
1109
  PRESET_NAMES,
1052
1110
  ParseError,
1053
- parse
1111
+ parse,
1112
+ registerActorPack
1054
1113
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markdy/core",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "MarkdyScript parser and AST types — zero runtime dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",