@patterkit/core 0.2.6 → 0.3.1

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.cjs CHANGED
@@ -29,6 +29,8 @@ __export(index_exports, {
29
29
  isValidGameId: () => import_model4.isValidGameId,
30
30
  newId: () => import_toolkit.newId,
31
31
  parseSource: () => import_toolkit3.parseSource,
32
+ planProject: () => planProject,
33
+ planScene: () => planScene,
32
34
  slug: () => slug,
33
35
  validateProject: () => validateProject
34
36
  });
@@ -472,6 +474,76 @@ function defaultMatchesType(value, decl) {
472
474
  return typeof value === "string" && (decl.stages?.includes(value) ?? true);
473
475
  }
474
476
  }
477
+
478
+ // src/scene-scaffold.ts
479
+ var trimSlash = (p) => p.replace(/\/+$/, "");
480
+ function planProject(opts) {
481
+ const locale = opts.locale ?? "en";
482
+ const project = {
483
+ schema: "patter/project@0",
484
+ project: { id: (0, import_toolkit.newId)("proj"), name: opts.name },
485
+ locales: { default: locale, all: [locale] },
486
+ voiced: true
487
+ };
488
+ return { path: `${slug(opts.name) || "project"}.patterproj`, content: canonicalStringify(project) };
489
+ }
490
+ function planScene(target, scaffold) {
491
+ const base = slug(scaffold.gameId) || slug(scaffold.name) || "scene";
492
+ let stem = base;
493
+ for (let n = 2; target.takenStems.has(stem); n++) stem = `${base}-${n}`;
494
+ const strings = {};
495
+ const say = (prefix, text) => {
496
+ const id = (0, import_toolkit.newId)(prefix);
497
+ strings[id] = text;
498
+ return { id, kind: "text" };
499
+ };
500
+ const opening = {
501
+ id: (0, import_toolkit.newId)("sn"),
502
+ type: "snippet",
503
+ beats: [say("T", scaffold.opening?.trim() || `Write the scene for "${scaffold.name}" here.`)]
504
+ };
505
+ const children = [opening];
506
+ if (scaffold.outcomes.length <= 1) {
507
+ opening.jump = { to: "END" };
508
+ } else {
509
+ children.push({
510
+ id: (0, import_toolkit.newId)("g"),
511
+ type: "group",
512
+ selector: "choice",
513
+ children: scaffold.outcomes.map((o) => ({
514
+ id: (0, import_toolkit.newId)("opt"),
515
+ type: "group",
516
+ prompt: say("L", o.title?.trim() || o.gameId),
517
+ gameData: { outcome: o.gameId },
518
+ children: [{
519
+ id: (0, import_toolkit.newId)("sn"),
520
+ type: "snippet",
521
+ beats: [say("T", "What happens next.")],
522
+ jump: { to: "END" }
523
+ }]
524
+ }))
525
+ });
526
+ }
527
+ const sceneId = (0, import_toolkit.newId)("scn");
528
+ const scene = {
529
+ id: sceneId,
530
+ type: "scene",
531
+ name: scaffold.name,
532
+ gameId: scaffold.gameId,
533
+ blocks: [{ id: (0, import_toolkit.newId)("blk"), type: "block", name: "Main", children }]
534
+ };
535
+ const flow = { schema: "patter/flow@0", scene };
536
+ const locale = { schema: "patter/strings@0", scene: sceneId, locale: target.locale, default: true, strings };
537
+ const flowDir = trimSlash(target.layout?.flow ?? "scenes/");
538
+ const stringsDir = trimSlash(target.layout?.strings ?? "loc/");
539
+ return {
540
+ sceneId,
541
+ writes: [
542
+ { path: `${flowDir}/${stem}.patterflow`, content: canonicalStringify(flow) },
543
+ { path: `${stringsDir}/${target.locale}/${stem}.patterloc`, content: canonicalStringify(locale) }
544
+ ]
545
+ };
546
+ }
475
547
  // Annotate the CommonJS export names for ESM import in node:
476
548
  0 && (module.exports = {
477
549
  canonicalStringify,
@@ -483,6 +555,8 @@ function defaultMatchesType(value, decl) {
483
555
  isValidGameId,
484
556
  newId,
485
557
  parseSource,
558
+ planProject,
559
+ planScene,
486
560
  slug,
487
561
  validateProject
488
562
  });
package/dist/index.d.cts CHANGED
@@ -40,4 +40,52 @@ interface ProjectInput {
40
40
  /** Validate a project's structural invariants. Returns an empty array when valid. */
41
41
  declare function validateProject(input: ProjectInput): ValidationIssue[];
42
42
 
43
- export { type ProjectInput, type StringifyOptions, type ValidationIssue, canonicalStringify, hash32, hash4, humanizeNodeRefs, slug, validateProject };
43
+ interface SceneScaffold {
44
+ /** The scene's name: the card's title. */
45
+ name: string;
46
+ /** The scene's address, pinned: the card's gameId, so the pairing never depends on the name. */
47
+ gameId: string;
48
+ /** The opening narration: the card's purpose, say. A prompt to write the scene when absent. */
49
+ opening?: string;
50
+ /** The card's outcomes: `gameId` labels an option, `title` is its words. */
51
+ outcomes: ReadonlyArray<{
52
+ gameId: string;
53
+ title?: string;
54
+ }>;
55
+ }
56
+ interface ScenePlanTarget {
57
+ /** The project's layout (`ProjectFile.layout`); Patter's defaults when absent. */
58
+ layout?: {
59
+ flow?: string;
60
+ strings?: string;
61
+ };
62
+ /** The project's default locale: the strings go there. */
63
+ locale: string;
64
+ /** File stems already taken in the flow folder, so the new one doesn't collide. */
65
+ takenStems: ReadonlySet<string>;
66
+ }
67
+ interface ScenePlan {
68
+ sceneId: string;
69
+ /** Project-relative paths and canonical content: the flow shard, then its strings. */
70
+ writes: {
71
+ path: string;
72
+ content: string;
73
+ }[];
74
+ }
75
+ /**
76
+ * Plan a new, empty Patter project for another tool to create beside its own (Storyletter's
77
+ * "Starter project with Patter"): the project file as Patter's own init writes it, named after
78
+ * the project, in one locale. Scenes are added with `planScene`. Returns the project file's path
79
+ * relative to the new project folder, and its canonical content.
80
+ */
81
+ declare function planProject(opts: {
82
+ name: string;
83
+ locale?: string;
84
+ }): {
85
+ path: string;
86
+ content: string;
87
+ };
88
+ /** Plan a stub scene for a card. Pure: nothing is read or written. */
89
+ declare function planScene(target: ScenePlanTarget, scaffold: SceneScaffold): ScenePlan;
90
+
91
+ export { type ProjectInput, type ScenePlan, type ScenePlanTarget, type SceneScaffold, type StringifyOptions, type ValidationIssue, canonicalStringify, hash32, hash4, humanizeNodeRefs, planProject, planScene, slug, validateProject };
package/dist/index.d.ts CHANGED
@@ -40,4 +40,52 @@ interface ProjectInput {
40
40
  /** Validate a project's structural invariants. Returns an empty array when valid. */
41
41
  declare function validateProject(input: ProjectInput): ValidationIssue[];
42
42
 
43
- export { type ProjectInput, type StringifyOptions, type ValidationIssue, canonicalStringify, hash32, hash4, humanizeNodeRefs, slug, validateProject };
43
+ interface SceneScaffold {
44
+ /** The scene's name: the card's title. */
45
+ name: string;
46
+ /** The scene's address, pinned: the card's gameId, so the pairing never depends on the name. */
47
+ gameId: string;
48
+ /** The opening narration: the card's purpose, say. A prompt to write the scene when absent. */
49
+ opening?: string;
50
+ /** The card's outcomes: `gameId` labels an option, `title` is its words. */
51
+ outcomes: ReadonlyArray<{
52
+ gameId: string;
53
+ title?: string;
54
+ }>;
55
+ }
56
+ interface ScenePlanTarget {
57
+ /** The project's layout (`ProjectFile.layout`); Patter's defaults when absent. */
58
+ layout?: {
59
+ flow?: string;
60
+ strings?: string;
61
+ };
62
+ /** The project's default locale: the strings go there. */
63
+ locale: string;
64
+ /** File stems already taken in the flow folder, so the new one doesn't collide. */
65
+ takenStems: ReadonlySet<string>;
66
+ }
67
+ interface ScenePlan {
68
+ sceneId: string;
69
+ /** Project-relative paths and canonical content: the flow shard, then its strings. */
70
+ writes: {
71
+ path: string;
72
+ content: string;
73
+ }[];
74
+ }
75
+ /**
76
+ * Plan a new, empty Patter project for another tool to create beside its own (Storyletter's
77
+ * "Starter project with Patter"): the project file as Patter's own init writes it, named after
78
+ * the project, in one locale. Scenes are added with `planScene`. Returns the project file's path
79
+ * relative to the new project folder, and its canonical content.
80
+ */
81
+ declare function planProject(opts: {
82
+ name: string;
83
+ locale?: string;
84
+ }): {
85
+ path: string;
86
+ content: string;
87
+ };
88
+ /** Plan a stub scene for a card. Pure: nothing is read or written. */
89
+ declare function planScene(target: ScenePlanTarget, scaffold: SceneScaffold): ScenePlan;
90
+
91
+ export { type ProjectInput, type ScenePlan, type ScenePlanTarget, type SceneScaffold, type StringifyOptions, type ValidationIssue, canonicalStringify, hash32, hash4, humanizeNodeRefs, planProject, planScene, slug, validateProject };
package/dist/index.js CHANGED
@@ -436,6 +436,76 @@ function defaultMatchesType(value, decl) {
436
436
  return typeof value === "string" && (decl.stages?.includes(value) ?? true);
437
437
  }
438
438
  }
439
+
440
+ // src/scene-scaffold.ts
441
+ var trimSlash = (p) => p.replace(/\/+$/, "");
442
+ function planProject(opts) {
443
+ const locale = opts.locale ?? "en";
444
+ const project = {
445
+ schema: "patter/project@0",
446
+ project: { id: newId("proj"), name: opts.name },
447
+ locales: { default: locale, all: [locale] },
448
+ voiced: true
449
+ };
450
+ return { path: `${slug(opts.name) || "project"}.patterproj`, content: canonicalStringify(project) };
451
+ }
452
+ function planScene(target, scaffold) {
453
+ const base = slug(scaffold.gameId) || slug(scaffold.name) || "scene";
454
+ let stem = base;
455
+ for (let n = 2; target.takenStems.has(stem); n++) stem = `${base}-${n}`;
456
+ const strings = {};
457
+ const say = (prefix, text) => {
458
+ const id = newId(prefix);
459
+ strings[id] = text;
460
+ return { id, kind: "text" };
461
+ };
462
+ const opening = {
463
+ id: newId("sn"),
464
+ type: "snippet",
465
+ beats: [say("T", scaffold.opening?.trim() || `Write the scene for "${scaffold.name}" here.`)]
466
+ };
467
+ const children = [opening];
468
+ if (scaffold.outcomes.length <= 1) {
469
+ opening.jump = { to: "END" };
470
+ } else {
471
+ children.push({
472
+ id: newId("g"),
473
+ type: "group",
474
+ selector: "choice",
475
+ children: scaffold.outcomes.map((o) => ({
476
+ id: newId("opt"),
477
+ type: "group",
478
+ prompt: say("L", o.title?.trim() || o.gameId),
479
+ gameData: { outcome: o.gameId },
480
+ children: [{
481
+ id: newId("sn"),
482
+ type: "snippet",
483
+ beats: [say("T", "What happens next.")],
484
+ jump: { to: "END" }
485
+ }]
486
+ }))
487
+ });
488
+ }
489
+ const sceneId = newId("scn");
490
+ const scene = {
491
+ id: sceneId,
492
+ type: "scene",
493
+ name: scaffold.name,
494
+ gameId: scaffold.gameId,
495
+ blocks: [{ id: newId("blk"), type: "block", name: "Main", children }]
496
+ };
497
+ const flow = { schema: "patter/flow@0", scene };
498
+ const locale = { schema: "patter/strings@0", scene: sceneId, locale: target.locale, default: true, strings };
499
+ const flowDir = trimSlash(target.layout?.flow ?? "scenes/");
500
+ const stringsDir = trimSlash(target.layout?.strings ?? "loc/");
501
+ return {
502
+ sceneId,
503
+ writes: [
504
+ { path: `${flowDir}/${stem}.patterflow`, content: canonicalStringify(flow) },
505
+ { path: `${stringsDir}/${target.locale}/${stem}.patterloc`, content: canonicalStringify(locale) }
506
+ ]
507
+ };
508
+ }
439
509
  export {
440
510
  canonicalStringify,
441
511
  effectiveGameId2 as effectiveGameId,
@@ -446,6 +516,8 @@ export {
446
516
  isValidGameId2 as isValidGameId,
447
517
  newId,
448
518
  parseSource,
519
+ planProject,
520
+ planScene,
449
521
  slug,
450
522
  validateProject
451
523
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patterkit/core",
3
- "version": "0.2.6",
3
+ "version": "0.3.1",
4
4
  "description": "Patter core: stable id generation, readable-handle computation, canonical (de)serialisation, and the structural validator.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "build": "tsup src/index.ts --format esm,cjs --dts --clean"
35
35
  },
36
36
  "dependencies": {
37
- "@patterkit/model": "0.7.0",
37
+ "@patterkit/model": "0.8.0",
38
38
  "@wildwinter/toolkit": "^0.1.0",
39
39
  "json5": "^2.2.3"
40
40
  }