@peekling/runtime 0.1.0 → 0.1.2

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.
Files changed (52) hide show
  1. package/README.md +59 -8
  2. package/dist/canvas-renderer.d.ts +17 -0
  3. package/dist/canvas-renderer.d.ts.map +1 -0
  4. package/dist/canvas-renderer.js +192 -0
  5. package/dist/canvas.d.ts +5 -0
  6. package/dist/canvas.d.ts.map +1 -0
  7. package/dist/canvas.js +6 -0
  8. package/dist/configuration-validation.d.ts.map +1 -1
  9. package/dist/configuration-validation.js +176 -1
  10. package/dist/content.d.ts +3 -0
  11. package/dist/content.d.ts.map +1 -1
  12. package/dist/content.js +33 -7
  13. package/dist/index.d.ts +2 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/input.d.ts.map +1 -1
  16. package/dist/input.js +4 -1
  17. package/dist/interaction.d.ts +37 -0
  18. package/dist/interaction.d.ts.map +1 -0
  19. package/dist/interaction.js +204 -0
  20. package/dist/overrides.d.ts +8 -0
  21. package/dist/overrides.d.ts.map +1 -1
  22. package/dist/overrides.js +18 -16
  23. package/dist/path-sampler.d.ts +9 -0
  24. package/dist/path-sampler.d.ts.map +1 -0
  25. package/dist/path-sampler.js +47 -0
  26. package/dist/peekling.css +1 -1
  27. package/dist/peekling.css.sri +1 -1
  28. package/dist/peekling.js +1365 -86
  29. package/dist/peekling.js.sri +1 -1
  30. package/dist/peekling.min.js +1 -1
  31. package/dist/peekling.min.js.sri +1 -1
  32. package/dist/plan-compiler.d.ts +2 -0
  33. package/dist/plan-compiler.d.ts.map +1 -1
  34. package/dist/plan-compiler.js +71 -14
  35. package/dist/plan.d.ts +27 -1
  36. package/dist/plan.d.ts.map +1 -1
  37. package/dist/plan.js +281 -22
  38. package/dist/registry.js +2 -2
  39. package/dist/renderer.d.ts +18 -1
  40. package/dist/renderer.d.ts.map +1 -1
  41. package/dist/renderer.js +1 -0
  42. package/dist/runtime-validation.d.ts.map +1 -1
  43. package/dist/runtime-validation.js +35 -0
  44. package/dist/runtime.d.ts +49 -3
  45. package/dist/runtime.d.ts.map +1 -1
  46. package/dist/runtime.js +365 -14
  47. package/dist/targets.d.ts +11 -0
  48. package/dist/targets.d.ts.map +1 -0
  49. package/dist/targets.js +106 -0
  50. package/dist/types.d.ts +61 -1
  51. package/dist/types.d.ts.map +1 -1
  52. package/package.json +5 -1
@@ -1 +1 @@
1
- sha384-iJtnL5uLVg+cNs8qk9f4hwjOyplSaORCqdss7xunkEfv9w1j5rUCkFqBcK1WslGW
1
+ sha384-1p+Dobs8owBmemeSfa0ciVA+xFHqPe9FXpAO4pbheJPfZowG117I/+kFlsDa4jUq
@@ -3,7 +3,9 @@ export interface PlanCompileContext {
3
3
  states?: ReadonlySet<string>;
4
4
  capabilities?: ReadonlySet<PlanCapability>;
5
5
  contentIds?: ReadonlySet<string>;
6
+ targets?: ReadonlySet<string>;
6
7
  validateSelector?: (selector: string) => boolean;
8
+ validatePath?: (path: string) => boolean;
7
9
  }
8
10
  export interface CompiledPlanStateSelection {
9
11
  readonly kind: "state" | "capability";
@@ -1 +1 @@
1
- {"version":3,"file":"plan-compiler.d.ts","sourceRoot":"","sources":["../src/plan-compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan-compiler.d.ts","sourceRoot":"","sources":["../src/plan-compiler.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,IAAI,EACT,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAIrB,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AAepB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7B,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC9B,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,eAAe,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,0BAA0B,CAAC,CAAC;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,qBAAqB,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAC1C;AAMD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1E,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;IACtD,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;IAC1D,QAAQ,CAAC,gBAAgB,EAAE,SAAS,QAAQ,CAAC,yBAAyB,CAAC,EAAE,CAAC;CAC3E;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,mBAAmB,GAC3B;IACE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAAC;CAC7D,CAAC;AAEN,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,YAAY,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAKtD;CACF;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,IAAI,EACX,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAkGd;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,kBAAuB,GAC/B,kBAAkB,CA2BpB;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,IAAI,EACX,OAAO,GAAE,kBAAuB,GAC/B,mBAAmB,CAgCrB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAKrD;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,cAAc,GACpB,mBAAmB,CA2FrB;AA0wBD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC/B,kBAAkB,GAAG,SAAS,CAiBhC"}
@@ -316,7 +316,7 @@ function compileEffect(input, path, context, allowPayload) {
316
316
  : compileState(input.state, `${path}.state`, context);
317
317
  const motion = input.motion === undefined
318
318
  ? undefined
319
- : compileMotion(input.motion, `${path}.motion`);
319
+ : compileMotion(input.motion, `${path}.motion`, context);
320
320
  const surfaces = input.surfaces === undefined ? [] : input.surfaces;
321
321
  if (!Array.isArray(surfaces) || surfaces.length > SURFACE_LIMIT) {
322
322
  fail("surface-limit", `${path}.surfaces`, `must contain at most ${SURFACE_LIMIT} surfaces`);
@@ -421,21 +421,78 @@ function compileState(input, path, context) {
421
421
  }
422
422
  return freeze({ kind: "capability", name: input.capability });
423
423
  }
424
- function compileMotion(input, path) {
424
+ function compileMotion(input, path, context) {
425
425
  object(input, path);
426
- closed(input, ["type", "speed", "arrivalRadius"], path);
427
- if (input.type !== "follow-pointer") {
428
- fail("invalid-motion", `${path}.type`, "must be follow-pointer");
426
+ switch (input.type) {
427
+ case "follow-pointer":
428
+ closed(input, ["type", "speed", "arrivalRadius"], path);
429
+ bounded(input.speed, `${path}.speed`, 1, 1_000);
430
+ bounded(input.arrivalRadius, `${path}.arrivalRadius`, 0, 1_000);
431
+ break;
432
+ case "horizontal-patrol":
433
+ closed(input, ["type", "speed", "edgeInset"], path);
434
+ bounded(input.speed, `${path}.speed`, 1, 1_000);
435
+ bounded(input.edgeInset, `${path}.edgeInset`, 0, 1_000);
436
+ break;
437
+ case "viewport-traverse":
438
+ closed(input, ["type", "speed", "edgeInset", "clockwise"], path);
439
+ bounded(input.speed, `${path}.speed`, 1, 1_000);
440
+ bounded(input.edgeInset, `${path}.edgeInset`, 0, 1_000);
441
+ if (input.clockwise !== undefined &&
442
+ typeof input.clockwise !== "boolean") {
443
+ fail("invalid-motion", `${path}.clockwise`, "must be boolean");
444
+ }
445
+ break;
446
+ case "move-to":
447
+ closed(input, ["type", "x", "y", "speed", "arrivalRadius"], path);
448
+ bounded(input.x, `${path}.x`, -100_000, 100_000);
449
+ bounded(input.y, `${path}.y`, -100_000, 100_000);
450
+ bounded(input.speed, `${path}.speed`, 1, 1_000);
451
+ bounded(input.arrivalRadius, `${path}.arrivalRadius`, 0, 1_000);
452
+ break;
453
+ case "move-to-target":
454
+ closed(input, ["type", "target", "anchor", "speed", "arrivalRadius"], path);
455
+ id(input.target, `${path}.target`, "Target");
456
+ if (context.targets && !context.targets.has(input.target)) {
457
+ fail("unknown-target", `${path}.target`, `references unknown target ${input.target}`);
458
+ }
459
+ if (input.anchor !== undefined &&
460
+ !["center", "top", "right", "bottom", "left"].includes(input.anchor)) {
461
+ fail("invalid-motion", `${path}.anchor`, "is not supported");
462
+ }
463
+ bounded(input.speed, `${path}.speed`, 1, 1_000);
464
+ bounded(input.arrivalRadius, `${path}.arrivalRadius`, 0, 1_000);
465
+ break;
466
+ case "jump-to":
467
+ closed(input, ["type", "x", "y", "duration", "height"], path);
468
+ bounded(input.x, `${path}.x`, -100_000, 100_000);
469
+ bounded(input.y, `${path}.y`, -100_000, 100_000);
470
+ bounded(input.duration, `${path}.duration`, 100, 10_000);
471
+ bounded(input.height, `${path}.height`, 0, 2_000);
472
+ break;
473
+ case "svg-path": {
474
+ closed(input, ["type", "path", "duration", "loop", "relative"], path);
475
+ if (typeof input.path !== "string" ||
476
+ input.path.length < 1 ||
477
+ input.path.length > 4_096 ||
478
+ /[\0\r\n]/.test(input.path)) {
479
+ fail("invalid-path", `${path}.path`, "must be bounded SVG path data");
480
+ }
481
+ if (context.validatePath && !context.validatePath(input.path)) {
482
+ fail("invalid-path", `${path}.path`, "must be valid SVG path data");
483
+ }
484
+ bounded(input.duration, `${path}.duration`, 100, 60_000);
485
+ for (const field of ["loop", "relative"]) {
486
+ if (input[field] !== undefined && typeof input[field] !== "boolean") {
487
+ fail("invalid-motion", `${path}.${field}`, "must be boolean");
488
+ }
489
+ }
490
+ break;
491
+ }
492
+ default:
493
+ fail("invalid-motion", `${path}.type`, "is not a supported motion type");
429
494
  }
430
- bounded(input.speed, `${path}.speed`, 1, 1_000);
431
- bounded(input.arrivalRadius, `${path}.arrivalRadius`, 0, 1_000);
432
- return freeze({
433
- type: "follow-pointer",
434
- ...(input.speed === undefined ? {} : { speed: input.speed }),
435
- ...(input.arrivalRadius === undefined
436
- ? {}
437
- : { arrivalRadius: input.arrivalRadius }),
438
- });
495
+ return freeze({ ...input });
439
496
  }
440
497
  function compileSurface(input, path, context, allowPayload) {
441
498
  object(input, path);
package/dist/plan.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import { type CompiledPlan, type PlanEventEvaluation } from "./plan-compiler.js";
2
- import { type BehaviorRequest, type Plan, type World } from "./types.js";
2
+ import { type BehaviorRequest, type HorizontalPatrolMotionEffect, type MotionRequest, type Plan, type PeeklingPreset, type ViewportTraverseMotionEffect, type World } from "./types.js";
3
3
  export declare function compiledPlanSectionRequirements(plan: CompiledPlan): {
4
4
  selectors: readonly string[];
5
5
  thresholds: readonly number[];
6
6
  };
7
7
  /** The engine-owned default Plan policy. It uses the public Plan shape. */
8
8
  export declare function createDefaultPlan(locomotion: boolean): Plan;
9
+ /** Small named starting points that compile into the canonical Plan. */
10
+ export declare function createPresetPlan(preset: PeeklingPreset, locomotion: boolean): Plan;
9
11
  /** Instance-owned evaluator for one immutable compiled Plan. */
10
12
  export declare class PlanRuntime {
11
13
  #private;
@@ -18,4 +20,28 @@ export declare class PlanRuntime {
18
20
  nextWakeAt(): number | undefined;
19
21
  rebase(delta: number): void;
20
22
  }
23
+ export type PatrolDirection = boolean;
24
+ interface PatrolWorld {
25
+ readonly position: Readonly<{
26
+ x: number;
27
+ y: number;
28
+ }>;
29
+ readonly viewport: Readonly<{
30
+ width: number;
31
+ height: number;
32
+ }>;
33
+ readonly characterSize: Readonly<{
34
+ width: number;
35
+ height: number;
36
+ }>;
37
+ }
38
+ export declare function evaluateHorizontalPatrol(effect: Readonly<HorizontalPatrolMotionEffect>, world: Readonly<PatrolWorld>, direction: PatrolDirection): readonly [PatrolDirection, MotionRequest | undefined];
39
+ export declare function evaluateViewportTraverse(effect: Readonly<ViewportTraverseMotionEffect>, world: Readonly<PatrolWorld>, phase?: number): readonly [number, MotionRequest | undefined];
40
+ export declare function motionToward(effect: Readonly<{
41
+ speed?: number;
42
+ }>, position: Readonly<{
43
+ x: number;
44
+ y: number;
45
+ }>, x: number, y: number, arrivalRadius?: number, bounds?: Readonly<Partial<Pick<World, "viewport" | "characterSize">>>): MotionRequest | undefined;
46
+ export {};
21
47
  //# sourceMappingURL=plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,YAAY,EAGjB,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,IAAI,EAGT,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,GAAG;IACnE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,CAgBA;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAsB3D;AAaD,gEAAgE;AAChE,qBAAa,WAAW;;IAatB,YAAY,IAAI,EAAE,YAAY,EAE7B;IAED,IAAI,IAAI,IAAI,YAAY,CAEvB;IAED,IAAI,eAAe,IAAI,mBAAmB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAE/D;IAED,IAAI,mBAAmB,IAAI,mBAAmB,CAAC,kBAAkB,CAAC,CAEjE;IAED,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,eAAe,CAwIhD;IAED,KAAK,IAAI,IAAI,CAQZ;IAED,UAAU,IAAI,MAAM,GAAG,SAAS,CAU/B;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM1B;CAmFF"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,YAAY,EAGjB,KAAK,mBAAmB,EACzB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,4BAA4B,EAEjC,KAAK,aAAa,EAClB,KAAK,IAAI,EAGT,KAAK,cAAc,EAInB,KAAK,4BAA4B,EACjC,KAAK,KAAK,EACX,MAAM,YAAY,CAAC;AAEpB,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,GAAG;IACnE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B,CAgBA;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,OAAO,GAAG,IAAI,CAE3D;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,OAAO,GAClB,IAAI,CA6CN;AAaD,gEAAgE;AAChE,qBAAa,WAAW;;IAmBtB,YAAY,IAAI,EAAE,YAAY,EAE7B;IAED,IAAI,IAAI,IAAI,YAAY,CAEvB;IAED,IAAI,eAAe,IAAI,mBAAmB,CAAC,QAAQ,CAAC,GAAG,SAAS,CAE/D;IAED,IAAI,mBAAmB,IAAI,mBAAmB,CAAC,kBAAkB,CAAC,CAEjE;IAED,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,eAAe,CAwMhD;IAED,KAAK,IAAI,IAAI,CAcZ;IAED,UAAU,IAAI,MAAM,GAAG,SAAS,CAU/B;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM1B;CAgKF;AAgBD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC;AAEtC,UAAU,WAAW;IACnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/D,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,QAAQ,CAAC,4BAA4B,CAAC,EAC9C,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC5B,SAAS,EAAE,eAAe,GACzB,SAAS,CAAC,eAAe,EAAE,aAAa,GAAG,SAAS,CAAC,CAkBvD;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,QAAQ,CAAC,4BAA4B,CAAC,EAC9C,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,EAC5B,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,CAoC9C;AA6DD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,QAAQ,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACpC,QAAQ,EAAE,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,aAAa,SAAI,EACjB,MAAM,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe,CAAC,CAAC,CAAC,GACpE,aAAa,GAAG,SAAS,CAwB3B"}
package/dist/plan.js CHANGED
@@ -19,6 +19,32 @@ export function compiledPlanSectionRequirements(plan) {
19
19
  }
20
20
  /** The engine-owned default Plan policy. It uses the public Plan shape. */
21
21
  export function createDefaultPlan(locomotion) {
22
+ return createPresetPlan("companion", locomotion);
23
+ }
24
+ /** Small named starting points that compile into the canonical Plan. */
25
+ export function createPresetPlan(preset, locomotion) {
26
+ const state = locomotion
27
+ ? { capability: "locomotion" }
28
+ : { state: "idle" };
29
+ if (preset === "still") {
30
+ return {
31
+ baseline: {
32
+ channels: ["state"],
33
+ state: { state: "idle" },
34
+ },
35
+ };
36
+ }
37
+ if (preset === "bottom-patrol" || preset === "viewport-roam") {
38
+ return {
39
+ baseline: {
40
+ channels: ["motion", "state"],
41
+ state,
42
+ motion: preset === "bottom-patrol"
43
+ ? { type: "horizontal-patrol", speed: DEFAULT_SPEED }
44
+ : { type: "viewport-traverse", speed: DEFAULT_SPEED },
45
+ },
46
+ };
47
+ }
22
48
  return {
23
49
  baseline: {
24
50
  channels: ["state"],
@@ -47,6 +73,12 @@ export class PlanRuntime {
47
73
  #eventState = createPlanEventState();
48
74
  #activeChannels = new Map();
49
75
  #activeSurfaces = new Map();
76
+ #patrolDirections = new WeakMap();
77
+ #traversePhases = new WeakMap();
78
+ #jumpOwner;
79
+ #jumpState;
80
+ #pathOwner;
81
+ #pathState;
50
82
  #activeInterrupts = [];
51
83
  #surfaceVersion = 0;
52
84
  #lastEventStatus;
@@ -144,26 +176,78 @@ export class PlanRuntime {
144
176
  if (state?.kind === "capability") {
145
177
  output.capability = state.name;
146
178
  }
147
- const motion = channels.get("motion")?.motion;
148
- if (motion && world.pointer) {
149
- const dx = world.pointer.x - world.position.x;
150
- const dy = world.pointer.y - world.position.y;
151
- const distance = Math.hypot(dx, dy);
152
- const arrivalRadius = motion.arrivalRadius ?? 0;
153
- if (distance > arrivalRadius) {
154
- output.motion = {
155
- x: dx / distance,
156
- y: dy / distance,
157
- speed: motion.speed ?? DEFAULT_SPEED,
158
- maxDistance: distance - arrivalRadius,
159
- };
160
- }
161
- else if (channels.get("motion") === channels.get("state")) {
162
- delete output.capability;
163
- const baseline = this.#baselineState();
164
- if (!output.state && baseline)
165
- output.state = baseline;
166
- }
179
+ const motionOwner = channels.get("motion");
180
+ const motion = motionOwner?.motion;
181
+ const settleCapability = () => {
182
+ if (output.capability !== "locomotion")
183
+ return;
184
+ delete output.capability;
185
+ const baseline = this.#baselineState();
186
+ if (!output.state && baseline)
187
+ output.state = baseline;
188
+ };
189
+ if (motion?.type !== "jump-to") {
190
+ this.#jumpOwner = undefined;
191
+ this.#jumpState = undefined;
192
+ }
193
+ if (motion?.type !== "svg-path") {
194
+ this.#pathOwner = undefined;
195
+ this.#pathState = undefined;
196
+ }
197
+ if (motion?.type === "horizontal-patrol" && world.characterSize) {
198
+ const [direction, request] = evaluateHorizontalPatrol(motion, world, this.#patrolDirections.get(motionOwner) ?? true);
199
+ this.#patrolDirections.set(motionOwner, direction);
200
+ if (request)
201
+ output.motion = request;
202
+ }
203
+ else if (motion?.type === "viewport-traverse" && world.characterSize) {
204
+ const [phase, request] = evaluateViewportTraverse(motion, world, this.#traversePhases.get(motionOwner));
205
+ this.#traversePhases.set(motionOwner, phase);
206
+ if (request)
207
+ output.motion = request;
208
+ }
209
+ else if (motion?.type === "follow-pointer") {
210
+ const request = world.pointer
211
+ ? motionToward(motion, world.position, world.pointer.x, world.pointer.y, motion.arrivalRadius, world)
212
+ : undefined;
213
+ if (request)
214
+ output.motion = request;
215
+ else
216
+ settleCapability();
217
+ }
218
+ else if (motion?.type === "move-to") {
219
+ const request = motionToward(motion, world.position, motion.x, motion.y, motion.arrivalRadius, world);
220
+ if (request)
221
+ output.motion = request;
222
+ else
223
+ settleCapability();
224
+ }
225
+ else if (motion?.type === "move-to-target") {
226
+ const target = world.targets?.[motion.target];
227
+ const point = target && world.characterSize
228
+ ? targetPoint(target, motion.anchor, world.characterSize)
229
+ : undefined;
230
+ const request = point
231
+ ? motionToward(motion, world.position, point.x, point.y, motion.arrivalRadius, world)
232
+ : undefined;
233
+ if (request)
234
+ output.motion = { ...request, trackTarget: true };
235
+ else
236
+ settleCapability();
237
+ }
238
+ else if (motion?.type === "jump-to") {
239
+ const request = this.#evaluateJump(motionOwner, motion, world);
240
+ if (request)
241
+ output.motion = request;
242
+ else
243
+ settleCapability();
244
+ }
245
+ else if (motion?.type === "svg-path") {
246
+ const request = this.#evaluatePath(motionOwner, motion, world);
247
+ if (request)
248
+ output.motion = request;
249
+ else
250
+ settleCapability();
167
251
  }
168
252
  const selected = new Map();
169
253
  for (const surface of this.#plan.baseline.surfaces) {
@@ -198,6 +282,12 @@ export class PlanRuntime {
198
282
  this.#activeChannels.clear();
199
283
  this.#activeSurfaces.clear();
200
284
  this.#activeInterrupts = [];
285
+ this.#patrolDirections = new WeakMap();
286
+ this.#traversePhases = new WeakMap();
287
+ this.#jumpOwner = undefined;
288
+ this.#jumpState = undefined;
289
+ this.#pathOwner = undefined;
290
+ this.#pathState = undefined;
201
291
  this.#eventState.ordering.clear();
202
292
  this.#eventState.sessions.clear();
203
293
  this.#lastEventStatus = undefined;
@@ -224,6 +314,67 @@ export class PlanRuntime {
224
314
  const state = this.#plan.baseline.state;
225
315
  return state?.kind === "state" ? state.name : undefined;
226
316
  }
317
+ #evaluateJump(owner, effect, world) {
318
+ if (this.#jumpOwner !== owner || !this.#jumpState) {
319
+ this.#jumpOwner = owner;
320
+ this.#jumpState = {
321
+ startedAt: world.now,
322
+ from: { ...world.position },
323
+ };
324
+ }
325
+ const duration = effect.duration ?? 800;
326
+ const progress = Math.max(0, Math.min(1, (world.now - this.#jumpState.startedAt) / duration));
327
+ const target = boundedPoint(effect, world);
328
+ const desired = {
329
+ x: this.#jumpState.from.x + (target.x - this.#jumpState.from.x) * progress,
330
+ y: this.#jumpState.from.y + (target.y - this.#jumpState.from.y) * progress,
331
+ };
332
+ const lift = progress === 0 || progress === 1
333
+ ? 0
334
+ : Math.sin(Math.PI * progress) *
335
+ (effect.height ?? world.characterSize?.height ?? 32);
336
+ const request = directMotion(world.position, desired, lift);
337
+ if (progress < 1 || request.maxDistance)
338
+ return request;
339
+ return;
340
+ }
341
+ #evaluatePath(owner, effect, world) {
342
+ const sample = world.samplePath;
343
+ if (!sample)
344
+ return;
345
+ const first = sample(effect.path, 0);
346
+ if (!first)
347
+ return;
348
+ if (this.#pathOwner !== owner || !this.#pathState) {
349
+ this.#pathOwner = owner;
350
+ this.#pathState = {
351
+ startedAt: world.now,
352
+ offset: effect.relative === false
353
+ ? { x: 0, y: 0 }
354
+ : {
355
+ x: world.position.x - first.x,
356
+ y: world.position.y - first.y,
357
+ },
358
+ };
359
+ }
360
+ const duration = effect.duration ?? 4_000;
361
+ const elapsed = Math.max(0, world.now - this.#pathState.startedAt);
362
+ const complete = effect.loop !== true && elapsed >= duration;
363
+ const progress = effect.loop
364
+ ? (elapsed % duration) / duration
365
+ : Math.min(1, elapsed / duration);
366
+ const point = sample(effect.path, progress);
367
+ if (!point)
368
+ return;
369
+ const desired = {
370
+ x: point.x + this.#pathState.offset.x,
371
+ y: point.y + this.#pathState.offset.y,
372
+ };
373
+ const request = directMotion(world.position, boundedPoint(desired, world));
374
+ if (!complete || request.maxDistance)
375
+ return request;
376
+ return;
377
+ }
227
378
  #isContinuous(rule, world) {
228
379
  const condition = rule.when;
229
380
  if (condition.source !== "browser")
@@ -232,9 +383,9 @@ export class PlanRuntime {
232
383
  if (!world.pointer)
233
384
  return false;
234
385
  const motion = rule.effect.motion;
235
- if (!motion)
386
+ if (!motion || motion.type !== "follow-pointer")
236
387
  return true;
237
- return (Math.hypot(world.pointer.x - world.position.x, world.pointer.y - world.position.y) > (motion.arrivalRadius ?? 0));
388
+ return Boolean(motionToward(motion, world.position, world.pointer.x, world.pointer.y, motion.arrivalRadius, world));
238
389
  }
239
390
  if (condition.event !== "section.visibility")
240
391
  return false;
@@ -285,3 +436,111 @@ export class PlanRuntime {
285
436
  function completionSource(name) {
286
437
  return browserCompletionEvent(name) ? "browser" : "application";
287
438
  }
439
+ export function evaluateHorizontalPatrol(effect, world, direction) {
440
+ const halfWidth = world.characterSize.width / 2;
441
+ const inset = effect.edgeInset ?? 0;
442
+ const left = Math.min(world.viewport.width / 2, halfWidth + inset);
443
+ const right = world.viewport.width - left;
444
+ const halfHeight = world.characterSize.height / 2;
445
+ const y = Math.max(halfHeight, world.viewport.height - halfHeight);
446
+ let request = motionToward(effect, world.position, direction ? right : left, y);
447
+ if (!request) {
448
+ direction = !direction;
449
+ request = motionToward(effect, world.position, direction ? right : left, y);
450
+ }
451
+ return [direction, request];
452
+ }
453
+ export function evaluateViewportTraverse(effect, world, phase) {
454
+ const inset = effect.edgeInset ?? 0;
455
+ const halfWidth = world.characterSize.width / 2;
456
+ const halfHeight = world.characterSize.height / 2;
457
+ const left = Math.min(world.viewport.width / 2, halfWidth + inset);
458
+ const right = Math.max(left, world.viewport.width - left);
459
+ const top = Math.min(world.viewport.height / 2, halfHeight + inset);
460
+ const bottom = Math.max(top, world.viewport.height - halfHeight - inset);
461
+ const points = [
462
+ { x: left, y: bottom },
463
+ { x: right, y: bottom },
464
+ { x: right, y: top },
465
+ { x: left, y: top },
466
+ ];
467
+ if (phase === undefined) {
468
+ phase = points.reduce((nearest, point, index) => Math.hypot(point.x - world.position.x, point.y - world.position.y) <
469
+ Math.hypot(points[nearest].x - world.position.x, points[nearest].y - world.position.y)
470
+ ? index
471
+ : nearest, 0);
472
+ }
473
+ let target = points[phase];
474
+ let request = motionToward(effect, world.position, target.x, target.y);
475
+ if (!request) {
476
+ const direction = effect.clockwise === false ? -1 : 1;
477
+ phase = (phase + direction + points.length) % points.length;
478
+ target = points[phase];
479
+ request = motionToward(effect, world.position, target.x, target.y);
480
+ }
481
+ return [phase, request];
482
+ }
483
+ function targetPoint(target, anchor = "center", character) {
484
+ const center = {
485
+ x: target.left + target.width / 2,
486
+ y: target.top + target.height / 2,
487
+ };
488
+ switch (anchor) {
489
+ case "top":
490
+ return { x: center.x, y: target.top - character.height / 2 };
491
+ case "right":
492
+ return { x: target.right + character.width / 2, y: center.y };
493
+ case "bottom":
494
+ return { x: center.x, y: target.bottom + character.height / 2 };
495
+ case "left":
496
+ return { x: target.left - character.width / 2, y: center.y };
497
+ default:
498
+ return center;
499
+ }
500
+ }
501
+ function boundedPoint(point, world) {
502
+ const size = world.characterSize;
503
+ if (!size)
504
+ return { x: point.x, y: point.y };
505
+ return {
506
+ x: Math.max(size.width / 2, Math.min(world.viewport.width - size.width / 2, point.x)),
507
+ y: Math.max(size.height / 2, Math.min(world.viewport.height - size.height / 2, point.y)),
508
+ };
509
+ }
510
+ function directMotion(position, target, lift = 0) {
511
+ const dx = target.x - position.x;
512
+ const dy = target.y - position.y;
513
+ const distance = Math.hypot(dx, dy);
514
+ return {
515
+ x: distance ? dx / distance : 0,
516
+ y: distance ? dy / distance : 0,
517
+ speed: 0,
518
+ maxDistance: distance,
519
+ lift,
520
+ direct: true,
521
+ };
522
+ }
523
+ export function motionToward(effect, position, x, y, arrivalRadius = 0, bounds) {
524
+ const size = bounds?.characterSize;
525
+ const viewport = bounds?.viewport;
526
+ if (size && viewport) {
527
+ const pointerX = x;
528
+ const pointerY = y;
529
+ x = Math.max(size.width / 2, Math.min(viewport.width - size.width / 2, x));
530
+ y = Math.max(size.height / 2, Math.min(viewport.height - size.height / 2, y));
531
+ if (x !== pointerX || y !== pointerY)
532
+ arrivalRadius = 0;
533
+ }
534
+ const dx = x - position.x;
535
+ const dy = y - position.y;
536
+ const distance = Math.hypot(dx, dy);
537
+ const maxDistance = distance - arrivalRadius;
538
+ if (maxDistance <= 4)
539
+ return;
540
+ return {
541
+ x: dx / distance,
542
+ y: dy / distance,
543
+ speed: effect.speed ?? DEFAULT_SPEED,
544
+ maxDistance,
545
+ };
546
+ }
package/dist/registry.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { runtimeMessage } from "./runtime-diagnostics.js";
2
2
  const PEEK_REFERENCE_URL = typeof __PEEK_PACK_URL__ === "string"
3
3
  ? __PEEK_PACK_URL__
4
- : "https://cdn.jsdelivr.net/npm/@peekling/pack-peek@0.1.0/character.json";
4
+ : "https://cdn.jsdelivr.net/npm/@peekling/pack-peek@0.1.1/character.json";
5
5
  const PEEK_REFERENCE_SHA256 = typeof __PEEK_PACK_SHA256__ === "string"
6
6
  ? __PEEK_PACK_SHA256__
7
- : "bc01840e84f72edca4b0421dca4d07623b38d971d851bfc647e1cada03e1cbcc";
7
+ : "9a2e2a41e85f7c4b8d3487a3655db40872675bd9c0780e139a946eabdf71d75f";
8
8
  const REGISTRY = {
9
9
  peek: Object.freeze({
10
10
  url: PEEK_REFERENCE_URL,
@@ -1,8 +1,24 @@
1
1
  import type { NormalizedPack, Point, StateDefinition } from "./types.js";
2
2
  import { type RuntimeStyleAsset } from "./styles.js";
3
+ export interface CharacterRenderer {
4
+ readonly ready: Promise<void>;
5
+ swapAtlas(pack: NormalizedPack, atlasUrl: string): void | Promise<void>;
6
+ setHidden(hidden: boolean): void;
7
+ nextFrameIn(now: number): number | undefined;
8
+ render(stateName: string, now: number, position: Point, devicePixelRatio?: number, lift?: number): void;
9
+ destroy(): void;
10
+ }
11
+ export interface CharacterRendererOptions {
12
+ document: Document;
13
+ pack: NormalizedPack;
14
+ atlasUrl: string;
15
+ scale: number;
16
+ styles: RuntimeStyleAsset;
17
+ }
18
+ export type CharacterRendererFactory = (options: Readonly<CharacterRendererOptions>) => CharacterRenderer;
3
19
  export declare function frameAt(state: StateDefinition, elapsed: number): number;
4
20
  export declare function snapToDevicePixel(value: number, devicePixelRatio?: number): number;
5
- export declare class AtlasRenderer {
21
+ export declare class AtlasRenderer implements CharacterRenderer {
6
22
  #private;
7
23
  readonly host: HTMLElement;
8
24
  readonly ready: Promise<void>;
@@ -13,4 +29,5 @@ export declare class AtlasRenderer {
13
29
  render(stateName: string, now: number, position: Point, devicePixelRatio?: number, lift?: number): void;
14
30
  destroy(): void;
15
31
  }
32
+ export declare const createAtlasRenderer: CharacterRendererFactory;
16
33
  //# sourceMappingURL=renderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGzE,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,wBAAgB,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBvE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAAI,GAAG,MAAM,CAM7E;AAED,qBAAa,aAAa;;IACxB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAe9B,YACE,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,iBAAiB,EAoB1B;IAED,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAGtD;IAoBD,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAG/B;IAED,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuB3C;IAED,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,KAAK,EACf,gBAAgB,SAAI,EACpB,IAAI,SAAI,GACP,IAAI,CAkDN;IAED,OAAO,IAAI,IAAI,CAKd;CAkBF"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGzE,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7C,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,KAAK,EACf,gBAAgB,CAAC,EAAE,MAAM,EACzB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CAAC;IACR,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,OAAO,EAAE,QAAQ,CAAC,wBAAwB,CAAC,KACxC,iBAAiB,CAAC;AAEvB,wBAAgB,OAAO,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBvE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,SAAI,GAAG,MAAM,CAM7E;AAED,qBAAa,aAAc,YAAW,iBAAiB;;IACrD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAe9B,YACE,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,iBAAiB,EAoB1B;IAED,SAAS,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAGtD;IAoBD,SAAS,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAG/B;IAED,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuB3C;IAED,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,KAAK,EACf,gBAAgB,SAAI,EACpB,IAAI,SAAI,GACP,IAAI,CAkDN;IAED,OAAO,IAAI,IAAI,CAKd;CAkBF;AAED,eAAO,MAAM,mBAAmB,EAAE,wBAO/B,CAAC"}
package/dist/renderer.js CHANGED
@@ -174,3 +174,4 @@ export class AtlasRenderer {
174
174
  this.host.setAttribute("aria-hidden", "true");
175
175
  }
176
176
  }
177
+ export const createAtlasRenderer = (options) => new AtlasRenderer(options.document, options.pack, options.atlasUrl, options.scale, options.styles);
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-validation.d.ts","sourceRoot":"","sources":["../src/runtime-validation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,sBAAsB,EACtB,KAAK,yBAAyB,EAC9B,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,MAAM,GACd,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC,CA0B3C"}
1
+ {"version":3,"file":"runtime-validation.d.ts","sourceRoot":"","sources":["../src/runtime-validation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAqB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,sBAAsB,EACtB,KAAK,yBAAyB,EAC9B,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AAEvC,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,MAAM,GACd,SAAS,CAAC,WAAW,EAAE,iBAAiB,CAAC,CA2B3C"}
@@ -16,6 +16,7 @@ export function validateRuntimeConfiguration(options, baseUrl) {
16
16
  try {
17
17
  compilePlan(options.plan, {
18
18
  contentIds: new Set(Object.keys(content)),
19
+ targets: new Set(Object.keys(options.targets ?? {})),
19
20
  });
20
21
  }
21
22
  catch (cause) {
@@ -98,6 +99,40 @@ function configurationMessage(code, path, detail) {
98
99
  return `${detail ?? field} must be a function`;
99
100
  case "invalid-plan":
100
101
  return detail ?? "Plan is invalid";
102
+ case "incompatible-behavior":
103
+ return "Choose preset or plan, not both";
104
+ case "invalid-preset":
105
+ return "preset must be companion, still, bottom-patrol, or viewport-roam";
106
+ case "invalid-targets":
107
+ return "targets must be an object of target IDs and CSS selectors";
108
+ case "target-limit":
109
+ return "targets supports at most 16 entries";
110
+ case "invalid-target":
111
+ return "target IDs must be bounded lowercase IDs";
112
+ case "invalid-target-selector":
113
+ return "target selectors must be bounded CSS selector text";
114
+ case "invalid-interaction":
115
+ return "interaction contains an unsupported value";
116
+ case "invalid-interaction-event":
117
+ return "interaction events must be bounded application Event names";
118
+ case "missing-interaction-event":
119
+ return "pressEvent is required when press is emit";
120
+ case "incompatible-interaction-event":
121
+ return "pressEvent can be used only with the emit press action";
122
+ case "invalid-interaction-state":
123
+ return "interaction states must be valid Pack State names";
124
+ case "invalid-interaction-range":
125
+ return "interaction physics value is outside its supported range";
126
+ case "invalid-interaction-label":
127
+ return "interaction label must be short plain text";
128
+ case "invalid-catch-target":
129
+ return "catchTarget must reference a configured target";
130
+ case "invalid-catch-anchor":
131
+ return "catchAnchor is not supported";
132
+ case "invalid-indicator":
133
+ return "indicator must include a label and valid dot or count settings";
134
+ case "incompatible-indicator":
135
+ return "indicator requires character interaction";
101
136
  default:
102
137
  return detail ?? code;
103
138
  }