@craftrpgs/cli 0.2.1 → 0.2.2-alpha.0

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 (3) hide show
  1. package/README.md +4 -2
  2. package/dist/bin.js +1218 -321
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -6301,7 +6301,7 @@ var require_ajv = __commonJS(function(exports, module) {
6301
6301
  } });
6302
6302
  });
6303
6303
 
6304
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/identity.js
6304
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/identity.js
6305
6305
  var require_identity = __commonJS(function(exports) {
6306
6306
  var ALIAS = Symbol.for("yaml.alias");
6307
6307
  var DOC = Symbol.for("yaml.document");
@@ -6355,7 +6355,7 @@ var require_identity = __commonJS(function(exports) {
6355
6355
  exports.isSeq = isSeq;
6356
6356
  });
6357
6357
 
6358
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/visit.js
6358
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/visit.js
6359
6359
  var require_visit = __commonJS(function(exports) {
6360
6360
  var identity = require_identity();
6361
6361
  var BREAK = Symbol("break visit");
@@ -6510,7 +6510,7 @@ var require_visit = __commonJS(function(exports) {
6510
6510
  exports.visitAsync = visitAsync;
6511
6511
  });
6512
6512
 
6513
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/directives.js
6513
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/doc/directives.js
6514
6514
  var require_directives = __commonJS(function(exports) {
6515
6515
  var identity = require_identity();
6516
6516
  var visit = require_visit();
@@ -6662,7 +6662,7 @@ var require_directives = __commonJS(function(exports) {
6662
6662
  exports.Directives = Directives;
6663
6663
  });
6664
6664
 
6665
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/anchors.js
6665
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/doc/anchors.js
6666
6666
  var require_anchors = __commonJS(function(exports) {
6667
6667
  var identity = require_identity();
6668
6668
  var visit = require_visit();
@@ -6724,7 +6724,7 @@ var require_anchors = __commonJS(function(exports) {
6724
6724
  exports.findNewAnchor = findNewAnchor;
6725
6725
  });
6726
6726
 
6727
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/applyReviver.js
6727
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/doc/applyReviver.js
6728
6728
  var require_applyReviver = __commonJS(function(exports) {
6729
6729
  function applyReviver(reviver, obj, key, val) {
6730
6730
  if (val && typeof val === "object") {
@@ -6771,7 +6771,7 @@ var require_applyReviver = __commonJS(function(exports) {
6771
6771
  exports.applyReviver = applyReviver;
6772
6772
  });
6773
6773
 
6774
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/toJS.js
6774
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/toJS.js
6775
6775
  var require_toJS = __commonJS(function(exports) {
6776
6776
  var identity = require_identity();
6777
6777
  function toJS(value, arg, ctx) {
@@ -6798,7 +6798,7 @@ var require_toJS = __commonJS(function(exports) {
6798
6798
  exports.toJS = toJS;
6799
6799
  });
6800
6800
 
6801
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Node.js
6801
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/Node.js
6802
6802
  var require_Node = __commonJS(function(exports) {
6803
6803
  var applyReviver = require_applyReviver();
6804
6804
  var identity = require_identity();
@@ -6835,7 +6835,7 @@ var require_Node = __commonJS(function(exports) {
6835
6835
  exports.NodeBase = NodeBase;
6836
6836
  });
6837
6837
 
6838
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Alias.js
6838
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/Alias.js
6839
6839
  var require_Alias = __commonJS(function(exports) {
6840
6840
  var anchors = require_anchors();
6841
6841
  var visit = require_visit();
@@ -6854,6 +6854,8 @@ var require_Alias = __commonJS(function(exports) {
6854
6854
  });
6855
6855
  }
6856
6856
  resolve(doc, ctx) {
6857
+ if (ctx?.maxAliasCount === 0)
6858
+ throw new ReferenceError("Alias resolution is disabled");
6857
6859
  let nodes;
6858
6860
  if (ctx?.aliasResolveCache) {
6859
6861
  nodes = ctx.aliasResolveCache;
@@ -6943,7 +6945,7 @@ var require_Alias = __commonJS(function(exports) {
6943
6945
  exports.Alias = Alias;
6944
6946
  });
6945
6947
 
6946
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Scalar.js
6948
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/Scalar.js
6947
6949
  var require_Scalar = __commonJS(function(exports) {
6948
6950
  var identity = require_identity();
6949
6951
  var Node = require_Node();
@@ -6971,7 +6973,7 @@ var require_Scalar = __commonJS(function(exports) {
6971
6973
  exports.isScalarValue = isScalarValue;
6972
6974
  });
6973
6975
 
6974
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/createNode.js
6976
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/doc/createNode.js
6975
6977
  var require_createNode = __commonJS(function(exports) {
6976
6978
  var Alias = require_Alias();
6977
6979
  var identity = require_identity();
@@ -7043,7 +7045,7 @@ var require_createNode = __commonJS(function(exports) {
7043
7045
  exports.createNode = createNode;
7044
7046
  });
7045
7047
 
7046
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Collection.js
7048
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/Collection.js
7047
7049
  var require_Collection = __commonJS(function(exports) {
7048
7050
  var createNode = require_createNode();
7049
7051
  var identity = require_identity();
@@ -7158,7 +7160,7 @@ var require_Collection = __commonJS(function(exports) {
7158
7160
  exports.isEmptyPath = isEmptyPath;
7159
7161
  });
7160
7162
 
7161
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyComment.js
7163
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyComment.js
7162
7164
  var require_stringifyComment = __commonJS(function(exports) {
7163
7165
  var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#");
7164
7166
  function indentComment(comment, indent) {
@@ -7175,7 +7177,7 @@ var require_stringifyComment = __commonJS(function(exports) {
7175
7177
  exports.stringifyComment = stringifyComment;
7176
7178
  });
7177
7179
 
7178
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/foldFlowLines.js
7180
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/foldFlowLines.js
7179
7181
  var require_foldFlowLines = __commonJS(function(exports) {
7180
7182
  var FOLD_FLOW = "flow";
7181
7183
  var FOLD_BLOCK = "block";
@@ -7312,7 +7314,7 @@ ${indent}${text.slice(fold + 1, end)}`;
7312
7314
  exports.foldFlowLines = foldFlowLines;
7313
7315
  });
7314
7316
 
7315
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyString.js
7317
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyString.js
7316
7318
  var require_stringifyString = __commonJS(function(exports) {
7317
7319
  var Scalar = require_Scalar();
7318
7320
  var foldFlowLines = require_foldFlowLines();
@@ -7610,7 +7612,7 @@ ${indent}`);
7610
7612
  exports.stringifyString = stringifyString;
7611
7613
  });
7612
7614
 
7613
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringify.js
7615
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringify.js
7614
7616
  var require_stringify = __commonJS(function(exports) {
7615
7617
  var anchors = require_anchors();
7616
7618
  var identity = require_identity();
@@ -7731,7 +7733,7 @@ ${ctx.indent}${str}`;
7731
7733
  exports.stringify = stringify;
7732
7734
  });
7733
7735
 
7734
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyPair.js
7736
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyPair.js
7735
7737
  var require_stringifyPair = __commonJS(function(exports) {
7736
7738
  var identity = require_identity();
7737
7739
  var Scalar = require_Scalar();
@@ -7867,7 +7869,7 @@ ${ctx.indent}`;
7867
7869
  exports.stringifyPair = stringifyPair;
7868
7870
  });
7869
7871
 
7870
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/log.js
7872
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/log.js
7871
7873
  var require_log = __commonJS(function(exports) {
7872
7874
  var node_process = __require("process");
7873
7875
  function debug(logLevel, ...messages) {
@@ -7886,7 +7888,7 @@ var require_log = __commonJS(function(exports) {
7886
7888
  exports.warn = warn;
7887
7889
  });
7888
7890
 
7889
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/merge.js
7891
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/merge.js
7890
7892
  var require_merge = __commonJS(function(exports) {
7891
7893
  var identity = require_identity();
7892
7894
  var Scalar = require_Scalar();
@@ -7903,18 +7905,18 @@ var require_merge = __commonJS(function(exports) {
7903
7905
  };
7904
7906
  var isMergeKey = (ctx, key) => (merge.identify(key) || identity.isScalar(key) && (!key.type || key.type === Scalar.Scalar.PLAIN) && merge.identify(key.value)) && ctx?.doc.schema.tags.some((tag) => tag.tag === merge.tag && tag.default);
7905
7907
  function addMergeToJSMap(ctx, map, value) {
7906
- value = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
7907
- if (identity.isSeq(value))
7908
- for (const it of value.items)
7908
+ const source = resolveAliasValue(ctx, value);
7909
+ if (identity.isSeq(source))
7910
+ for (const it of source.items)
7909
7911
  mergeValue(ctx, map, it);
7910
- else if (Array.isArray(value))
7911
- for (const it of value)
7912
+ else if (Array.isArray(source))
7913
+ for (const it of source)
7912
7914
  mergeValue(ctx, map, it);
7913
7915
  else
7914
- mergeValue(ctx, map, value);
7916
+ mergeValue(ctx, map, source);
7915
7917
  }
7916
7918
  function mergeValue(ctx, map, value) {
7917
- const source = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
7919
+ const source = resolveAliasValue(ctx, value);
7918
7920
  if (!identity.isMap(source))
7919
7921
  throw new Error("Merge sources must be maps or map aliases");
7920
7922
  const srcMap = source.toJSON(null, ctx, Map);
@@ -7935,12 +7937,15 @@ var require_merge = __commonJS(function(exports) {
7935
7937
  }
7936
7938
  return map;
7937
7939
  }
7940
+ function resolveAliasValue(ctx, value) {
7941
+ return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;
7942
+ }
7938
7943
  exports.addMergeToJSMap = addMergeToJSMap;
7939
7944
  exports.isMergeKey = isMergeKey;
7940
7945
  exports.merge = merge;
7941
7946
  });
7942
7947
 
7943
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/addPairToJSMap.js
7948
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/addPairToJSMap.js
7944
7949
  var require_addPairToJSMap = __commonJS(function(exports) {
7945
7950
  var log = require_log();
7946
7951
  var merge = require_merge();
@@ -8001,7 +8006,7 @@ var require_addPairToJSMap = __commonJS(function(exports) {
8001
8006
  exports.addPairToJSMap = addPairToJSMap;
8002
8007
  });
8003
8008
 
8004
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Pair.js
8009
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/Pair.js
8005
8010
  var require_Pair = __commonJS(function(exports) {
8006
8011
  var createNode = require_createNode();
8007
8012
  var stringifyPair = require_stringifyPair();
@@ -8039,7 +8044,7 @@ var require_Pair = __commonJS(function(exports) {
8039
8044
  exports.createPair = createPair;
8040
8045
  });
8041
8046
 
8042
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyCollection.js
8047
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyCollection.js
8043
8048
  var require_stringifyCollection = __commonJS(function(exports) {
8044
8049
  var identity = require_identity();
8045
8050
  var stringify = require_stringify();
@@ -8191,7 +8196,7 @@ ${indent}${end}`;
8191
8196
  exports.stringifyCollection = stringifyCollection;
8192
8197
  });
8193
8198
 
8194
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/YAMLMap.js
8199
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/YAMLMap.js
8195
8200
  var require_YAMLMap = __commonJS(function(exports) {
8196
8201
  var stringifyCollection = require_stringifyCollection();
8197
8202
  var addPairToJSMap = require_addPairToJSMap();
@@ -8318,7 +8323,7 @@ var require_YAMLMap = __commonJS(function(exports) {
8318
8323
  exports.findPair = findPair;
8319
8324
  });
8320
8325
 
8321
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/map.js
8326
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/common/map.js
8322
8327
  var require_map = __commonJS(function(exports) {
8323
8328
  var identity = require_identity();
8324
8329
  var YAMLMap = require_YAMLMap();
@@ -8337,7 +8342,7 @@ var require_map = __commonJS(function(exports) {
8337
8342
  exports.map = map;
8338
8343
  });
8339
8344
 
8340
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/YAMLSeq.js
8345
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/nodes/YAMLSeq.js
8341
8346
  var require_YAMLSeq = __commonJS(function(exports) {
8342
8347
  var createNode = require_createNode();
8343
8348
  var stringifyCollection = require_stringifyCollection();
@@ -8430,7 +8435,7 @@ var require_YAMLSeq = __commonJS(function(exports) {
8430
8435
  exports.YAMLSeq = YAMLSeq;
8431
8436
  });
8432
8437
 
8433
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/seq.js
8438
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/common/seq.js
8434
8439
  var require_seq = __commonJS(function(exports) {
8435
8440
  var identity = require_identity();
8436
8441
  var YAMLSeq = require_YAMLSeq();
@@ -8449,7 +8454,7 @@ var require_seq = __commonJS(function(exports) {
8449
8454
  exports.seq = seq;
8450
8455
  });
8451
8456
 
8452
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/string.js
8457
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/common/string.js
8453
8458
  var require_string = __commonJS(function(exports) {
8454
8459
  var stringifyString = require_stringifyString();
8455
8460
  var string = {
@@ -8465,7 +8470,7 @@ var require_string = __commonJS(function(exports) {
8465
8470
  exports.string = string;
8466
8471
  });
8467
8472
 
8468
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/null.js
8473
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/common/null.js
8469
8474
  var require_null = __commonJS(function(exports) {
8470
8475
  var Scalar = require_Scalar();
8471
8476
  var nullTag = {
@@ -8480,7 +8485,7 @@ var require_null = __commonJS(function(exports) {
8480
8485
  exports.nullTag = nullTag;
8481
8486
  });
8482
8487
 
8483
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/bool.js
8488
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/core/bool.js
8484
8489
  var require_bool = __commonJS(function(exports) {
8485
8490
  var Scalar = require_Scalar();
8486
8491
  var boolTag = {
@@ -8501,7 +8506,7 @@ var require_bool = __commonJS(function(exports) {
8501
8506
  exports.boolTag = boolTag;
8502
8507
  });
8503
8508
 
8504
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyNumber.js
8509
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyNumber.js
8505
8510
  var require_stringifyNumber = __commonJS(function(exports) {
8506
8511
  function stringifyNumber({ format, minFractionDigits, tag, value }) {
8507
8512
  if (typeof value === "bigint")
@@ -8510,7 +8515,7 @@ var require_stringifyNumber = __commonJS(function(exports) {
8510
8515
  if (!isFinite(num))
8511
8516
  return isNaN(num) ? ".nan" : num < 0 ? "-.inf" : ".inf";
8512
8517
  let n = Object.is(value, -0) ? "-0" : JSON.stringify(value);
8513
- if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^\d/.test(n)) {
8518
+ if (!format && minFractionDigits && (!tag || tag === "tag:yaml.org,2002:float") && /^-?\d/.test(n) && !n.includes("e")) {
8514
8519
  let i = n.indexOf(".");
8515
8520
  if (i < 0) {
8516
8521
  i = n.length;
@@ -8525,7 +8530,7 @@ var require_stringifyNumber = __commonJS(function(exports) {
8525
8530
  exports.stringifyNumber = stringifyNumber;
8526
8531
  });
8527
8532
 
8528
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/float.js
8533
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/core/float.js
8529
8534
  var require_float = __commonJS(function(exports) {
8530
8535
  var Scalar = require_Scalar();
8531
8536
  var stringifyNumber = require_stringifyNumber();
@@ -8568,7 +8573,7 @@ var require_float = __commonJS(function(exports) {
8568
8573
  exports.floatNaN = floatNaN;
8569
8574
  });
8570
8575
 
8571
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/int.js
8576
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/core/int.js
8572
8577
  var require_int = __commonJS(function(exports) {
8573
8578
  var stringifyNumber = require_stringifyNumber();
8574
8579
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -8610,7 +8615,7 @@ var require_int = __commonJS(function(exports) {
8610
8615
  exports.intOct = intOct;
8611
8616
  });
8612
8617
 
8613
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/schema.js
8618
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/core/schema.js
8614
8619
  var require_schema = __commonJS(function(exports) {
8615
8620
  var map = require_map();
8616
8621
  var _null = require_null();
@@ -8635,7 +8640,7 @@ var require_schema = __commonJS(function(exports) {
8635
8640
  exports.schema = schema;
8636
8641
  });
8637
8642
 
8638
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/json/schema.js
8643
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/json/schema.js
8639
8644
  var require_schema2 = __commonJS(function(exports) {
8640
8645
  var Scalar = require_Scalar();
8641
8646
  var map = require_map();
@@ -8699,7 +8704,7 @@ var require_schema2 = __commonJS(function(exports) {
8699
8704
  exports.schema = schema;
8700
8705
  });
8701
8706
 
8702
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/binary.js
8707
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/binary.js
8703
8708
  var require_binary = __commonJS(function(exports) {
8704
8709
  var node_buffer = __require("buffer");
8705
8710
  var Scalar = require_Scalar();
@@ -8754,7 +8759,7 @@ var require_binary = __commonJS(function(exports) {
8754
8759
  exports.binary = binary;
8755
8760
  });
8756
8761
 
8757
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/pairs.js
8762
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/pairs.js
8758
8763
  var require_pairs = __commonJS(function(exports) {
8759
8764
  var identity = require_identity();
8760
8765
  var Pair = require_Pair();
@@ -8829,7 +8834,7 @@ ${cn.comment}` : item.comment;
8829
8834
  exports.resolvePairs = resolvePairs;
8830
8835
  });
8831
8836
 
8832
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/omap.js
8837
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/omap.js
8833
8838
  var require_omap = __commonJS(function(exports) {
8834
8839
  var identity = require_identity();
8835
8840
  var toJS = require_toJS();
@@ -8901,7 +8906,7 @@ var require_omap = __commonJS(function(exports) {
8901
8906
  exports.omap = omap;
8902
8907
  });
8903
8908
 
8904
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/bool.js
8909
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/bool.js
8905
8910
  var require_bool2 = __commonJS(function(exports) {
8906
8911
  var Scalar = require_Scalar();
8907
8912
  function boolStringify({ value, source }, ctx) {
@@ -8930,7 +8935,7 @@ var require_bool2 = __commonJS(function(exports) {
8930
8935
  exports.trueTag = trueTag;
8931
8936
  });
8932
8937
 
8933
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/float.js
8938
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/float.js
8934
8939
  var require_float2 = __commonJS(function(exports) {
8935
8940
  var Scalar = require_Scalar();
8936
8941
  var stringifyNumber = require_stringifyNumber();
@@ -8976,7 +8981,7 @@ var require_float2 = __commonJS(function(exports) {
8976
8981
  exports.floatNaN = floatNaN;
8977
8982
  });
8978
8983
 
8979
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/int.js
8984
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/int.js
8980
8985
  var require_int2 = __commonJS(function(exports) {
8981
8986
  var stringifyNumber = require_stringifyNumber();
8982
8987
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -9052,7 +9057,7 @@ var require_int2 = __commonJS(function(exports) {
9052
9057
  exports.intOct = intOct;
9053
9058
  });
9054
9059
 
9055
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/set.js
9060
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/set.js
9056
9061
  var require_set = __commonJS(function(exports) {
9057
9062
  var identity = require_identity();
9058
9063
  var Pair = require_Pair();
@@ -9135,7 +9140,7 @@ var require_set = __commonJS(function(exports) {
9135
9140
  exports.set = set;
9136
9141
  });
9137
9142
 
9138
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
9143
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
9139
9144
  var require_timestamp = __commonJS(function(exports) {
9140
9145
  var stringifyNumber = require_stringifyNumber();
9141
9146
  function parseSexagesimal(str, asBigInt) {
@@ -9217,7 +9222,7 @@ var require_timestamp = __commonJS(function(exports) {
9217
9222
  exports.timestamp = timestamp;
9218
9223
  });
9219
9224
 
9220
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/schema.js
9225
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/yaml-1.1/schema.js
9221
9226
  var require_schema3 = __commonJS(function(exports) {
9222
9227
  var map = require_map();
9223
9228
  var _null = require_null();
@@ -9258,7 +9263,7 @@ var require_schema3 = __commonJS(function(exports) {
9258
9263
  exports.schema = schema;
9259
9264
  });
9260
9265
 
9261
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/tags.js
9266
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/tags.js
9262
9267
  var require_tags = __commonJS(function(exports) {
9263
9268
  var map = require_map();
9264
9269
  var _null = require_null();
@@ -9349,7 +9354,7 @@ var require_tags = __commonJS(function(exports) {
9349
9354
  exports.getTags = getTags;
9350
9355
  });
9351
9356
 
9352
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/Schema.js
9357
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/schema/Schema.js
9353
9358
  var require_Schema = __commonJS(function(exports) {
9354
9359
  var identity = require_identity();
9355
9360
  var map = require_map();
@@ -9379,7 +9384,7 @@ var require_Schema = __commonJS(function(exports) {
9379
9384
  exports.Schema = Schema;
9380
9385
  });
9381
9386
 
9382
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyDocument.js
9387
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/stringify/stringifyDocument.js
9383
9388
  var require_stringifyDocument = __commonJS(function(exports) {
9384
9389
  var identity = require_identity();
9385
9390
  var stringify = require_stringify();
@@ -9459,7 +9464,7 @@ var require_stringifyDocument = __commonJS(function(exports) {
9459
9464
  exports.stringifyDocument = stringifyDocument;
9460
9465
  });
9461
9466
 
9462
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/Document.js
9467
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/doc/Document.js
9463
9468
  var require_Document = __commonJS(function(exports) {
9464
9469
  var Alias = require_Alias();
9465
9470
  var Collection = require_Collection();
@@ -9694,7 +9699,7 @@ var require_Document = __commonJS(function(exports) {
9694
9699
  exports.Document = Document;
9695
9700
  });
9696
9701
 
9697
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/errors.js
9702
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/errors.js
9698
9703
  var require_errors2 = __commonJS(function(exports) {
9699
9704
  class YAMLError extends Error {
9700
9705
  constructor(name, pos, code, message) {
@@ -9759,7 +9764,7 @@ ${pointer}
9759
9764
  exports.prettifyError = prettifyError;
9760
9765
  });
9761
9766
 
9762
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-props.js
9767
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-props.js
9763
9768
  var require_resolve_props = __commonJS(function(exports) {
9764
9769
  function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
9765
9770
  let spaceBefore = false;
@@ -9889,7 +9894,7 @@ var require_resolve_props = __commonJS(function(exports) {
9889
9894
  exports.resolveProps = resolveProps;
9890
9895
  });
9891
9896
 
9892
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-contains-newline.js
9897
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/util-contains-newline.js
9893
9898
  var require_util_contains_newline = __commonJS(function(exports) {
9894
9899
  function containsNewline(key) {
9895
9900
  if (!key)
@@ -9929,7 +9934,7 @@ var require_util_contains_newline = __commonJS(function(exports) {
9929
9934
  exports.containsNewline = containsNewline;
9930
9935
  });
9931
9936
 
9932
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-flow-indent-check.js
9937
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/util-flow-indent-check.js
9933
9938
  var require_util_flow_indent_check = __commonJS(function(exports) {
9934
9939
  var utilContainsNewline = require_util_contains_newline();
9935
9940
  function flowIndentCheck(indent, fc, onError) {
@@ -9944,7 +9949,7 @@ var require_util_flow_indent_check = __commonJS(function(exports) {
9944
9949
  exports.flowIndentCheck = flowIndentCheck;
9945
9950
  });
9946
9951
 
9947
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-map-includes.js
9952
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/util-map-includes.js
9948
9953
  var require_util_map_includes = __commonJS(function(exports) {
9949
9954
  var identity = require_identity();
9950
9955
  function mapIncludes(ctx, items, search) {
@@ -9957,7 +9962,7 @@ var require_util_map_includes = __commonJS(function(exports) {
9957
9962
  exports.mapIncludes = mapIncludes;
9958
9963
  });
9959
9964
 
9960
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-map.js
9965
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-block-map.js
9961
9966
  var require_resolve_block_map = __commonJS(function(exports) {
9962
9967
  var Pair = require_Pair();
9963
9968
  var YAMLMap = require_YAMLMap();
@@ -10064,7 +10069,7 @@ var require_resolve_block_map = __commonJS(function(exports) {
10064
10069
  exports.resolveBlockMap = resolveBlockMap;
10065
10070
  });
10066
10071
 
10067
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-seq.js
10072
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-block-seq.js
10068
10073
  var require_resolve_block_seq = __commonJS(function(exports) {
10069
10074
  var YAMLSeq = require_YAMLSeq();
10070
10075
  var resolveProps = require_resolve_props();
@@ -10112,7 +10117,7 @@ var require_resolve_block_seq = __commonJS(function(exports) {
10112
10117
  exports.resolveBlockSeq = resolveBlockSeq;
10113
10118
  });
10114
10119
 
10115
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-end.js
10120
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-end.js
10116
10121
  var require_resolve_end = __commonJS(function(exports) {
10117
10122
  function resolveEnd(end, offset, reqSpace, onError) {
10118
10123
  let comment = "";
@@ -10152,7 +10157,7 @@ var require_resolve_end = __commonJS(function(exports) {
10152
10157
  exports.resolveEnd = resolveEnd;
10153
10158
  });
10154
10159
 
10155
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-flow-collection.js
10160
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-flow-collection.js
10156
10161
  var require_resolve_flow_collection = __commonJS(function(exports) {
10157
10162
  var identity = require_identity();
10158
10163
  var Pair = require_Pair();
@@ -10343,7 +10348,7 @@ var require_resolve_flow_collection = __commonJS(function(exports) {
10343
10348
  exports.resolveFlowCollection = resolveFlowCollection;
10344
10349
  });
10345
10350
 
10346
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-collection.js
10351
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/compose-collection.js
10347
10352
  var require_compose_collection = __commonJS(function(exports) {
10348
10353
  var identity = require_identity();
10349
10354
  var Scalar = require_Scalar();
@@ -10405,7 +10410,7 @@ var require_compose_collection = __commonJS(function(exports) {
10405
10410
  exports.composeCollection = composeCollection;
10406
10411
  });
10407
10412
 
10408
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-scalar.js
10413
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-block-scalar.js
10409
10414
  var require_resolve_block_scalar = __commonJS(function(exports) {
10410
10415
  var Scalar = require_Scalar();
10411
10416
  function resolveBlockScalar(ctx, scalar, onError) {
@@ -10598,7 +10603,7 @@ var require_resolve_block_scalar = __commonJS(function(exports) {
10598
10603
  exports.resolveBlockScalar = resolveBlockScalar;
10599
10604
  });
10600
10605
 
10601
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-flow-scalar.js
10606
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/resolve-flow-scalar.js
10602
10607
  var require_resolve_flow_scalar = __commonJS(function(exports) {
10603
10608
  var Scalar = require_Scalar();
10604
10609
  var resolveEnd = require_resolve_end();
@@ -10735,7 +10740,7 @@ var require_resolve_flow_scalar = __commonJS(function(exports) {
10735
10740
  while (next === " " || next === "\t")
10736
10741
  next = source[++i + 1];
10737
10742
  } else if (next === "x" || next === "u" || next === "U") {
10738
- const length = { x: 2, u: 4, U: 8 }[next];
10743
+ const length = next === "x" ? 2 : next === "u" ? 4 : 8;
10739
10744
  res += parseCharCode(source, i + 1, length, onError);
10740
10745
  i += length;
10741
10746
  } else {
@@ -10804,17 +10809,18 @@ var require_resolve_flow_scalar = __commonJS(function(exports) {
10804
10809
  const cc = source.substr(offset, length);
10805
10810
  const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
10806
10811
  const code = ok ? parseInt(cc, 16) : NaN;
10807
- if (isNaN(code)) {
10812
+ try {
10813
+ return String.fromCodePoint(code);
10814
+ } catch {
10808
10815
  const raw = source.substr(offset - 2, length + 2);
10809
10816
  onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
10810
10817
  return raw;
10811
10818
  }
10812
- return String.fromCodePoint(code);
10813
10819
  }
10814
10820
  exports.resolveFlowScalar = resolveFlowScalar;
10815
10821
  });
10816
10822
 
10817
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-scalar.js
10823
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/compose-scalar.js
10818
10824
  var require_compose_scalar = __commonJS(function(exports) {
10819
10825
  var identity = require_identity();
10820
10826
  var Scalar = require_Scalar();
@@ -10892,7 +10898,7 @@ var require_compose_scalar = __commonJS(function(exports) {
10892
10898
  exports.composeScalar = composeScalar;
10893
10899
  });
10894
10900
 
10895
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-empty-scalar-position.js
10901
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/util-empty-scalar-position.js
10896
10902
  var require_util_empty_scalar_position = __commonJS(function(exports) {
10897
10903
  function emptyScalarPosition(offset, before, pos) {
10898
10904
  if (before) {
@@ -10919,7 +10925,7 @@ var require_util_empty_scalar_position = __commonJS(function(exports) {
10919
10925
  exports.emptyScalarPosition = emptyScalarPosition;
10920
10926
  });
10921
10927
 
10922
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-node.js
10928
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/compose-node.js
10923
10929
  var require_compose_node = __commonJS(function(exports) {
10924
10930
  var Alias = require_Alias();
10925
10931
  var identity = require_identity();
@@ -11022,7 +11028,7 @@ var require_compose_node = __commonJS(function(exports) {
11022
11028
  exports.composeNode = composeNode;
11023
11029
  });
11024
11030
 
11025
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-doc.js
11031
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/compose-doc.js
11026
11032
  var require_compose_doc = __commonJS(function(exports) {
11027
11033
  var Document = require_Document();
11028
11034
  var composeNode = require_compose_node();
@@ -11062,7 +11068,7 @@ var require_compose_doc = __commonJS(function(exports) {
11062
11068
  exports.composeDoc = composeDoc;
11063
11069
  });
11064
11070
 
11065
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/composer.js
11071
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/compose/composer.js
11066
11072
  var require_composer = __commonJS(function(exports) {
11067
11073
  var node_process = __require("process");
11068
11074
  var directives = require_directives();
@@ -11251,7 +11257,7 @@ ${end.comment}` : end.comment;
11251
11257
  exports.Composer = Composer;
11252
11258
  });
11253
11259
 
11254
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-scalar.js
11260
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/cst-scalar.js
11255
11261
  var require_cst_scalar = __commonJS(function(exports) {
11256
11262
  var resolveBlockScalar = require_resolve_block_scalar();
11257
11263
  var resolveFlowScalar = require_resolve_flow_scalar();
@@ -11441,7 +11447,7 @@ var require_cst_scalar = __commonJS(function(exports) {
11441
11447
  exports.setScalarValue = setScalarValue;
11442
11448
  });
11443
11449
 
11444
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-stringify.js
11450
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/cst-stringify.js
11445
11451
  var require_cst_stringify = __commonJS(function(exports) {
11446
11452
  var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
11447
11453
  function stringifyToken(token) {
@@ -11499,7 +11505,7 @@ var require_cst_stringify = __commonJS(function(exports) {
11499
11505
  exports.stringify = stringify;
11500
11506
  });
11501
11507
 
11502
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-visit.js
11508
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/cst-visit.js
11503
11509
  var require_cst_visit = __commonJS(function(exports) {
11504
11510
  var BREAK = Symbol("break visit");
11505
11511
  var SKIP = Symbol("skip children");
@@ -11558,7 +11564,7 @@ var require_cst_visit = __commonJS(function(exports) {
11558
11564
  exports.visit = visit;
11559
11565
  });
11560
11566
 
11561
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst.js
11567
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/cst.js
11562
11568
  var require_cst = __commonJS(function(exports) {
11563
11569
  var cstScalar = require_cst_scalar();
11564
11570
  var cstStringify = require_cst_stringify();
@@ -11659,7 +11665,7 @@ var require_cst = __commonJS(function(exports) {
11659
11665
  exports.tokenType = tokenType;
11660
11666
  });
11661
11667
 
11662
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/lexer.js
11668
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/lexer.js
11663
11669
  var require_lexer = __commonJS(function(exports) {
11664
11670
  var cst = require_cst();
11665
11671
  function isEmpty(ch) {
@@ -12245,7 +12251,7 @@ var require_lexer = __commonJS(function(exports) {
12245
12251
  exports.Lexer = Lexer;
12246
12252
  });
12247
12253
 
12248
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/line-counter.js
12254
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/line-counter.js
12249
12255
  var require_line_counter = __commonJS(function(exports) {
12250
12256
  class LineCounter {
12251
12257
  constructor() {
@@ -12273,7 +12279,7 @@ var require_line_counter = __commonJS(function(exports) {
12273
12279
  exports.LineCounter = LineCounter;
12274
12280
  });
12275
12281
 
12276
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/parser.js
12282
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/parse/parser.js
12277
12283
  var require_parser = __commonJS(function(exports) {
12278
12284
  var node_process = __require("process");
12279
12285
  var cst = require_cst();
@@ -13122,7 +13128,7 @@ var require_parser = __commonJS(function(exports) {
13122
13128
  exports.Parser = Parser;
13123
13129
  });
13124
13130
 
13125
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/public-api.js
13131
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/public-api.js
13126
13132
  var require_public_api = __commonJS(function(exports) {
13127
13133
  var composer = require_composer();
13128
13134
  var Document = require_Document();
@@ -17867,13 +17873,33 @@ var PLATFORM_INSTRUCTIONS = [
17867
17873
  }
17868
17874
  },
17869
17875
  {
17870
- id: "a-good-turn",
17876
+ id: "only-the-story",
17877
+ revision: 1,
17878
+ instruction: {
17879
+ name: "Only the story",
17880
+ when: "always",
17881
+ section: "Narration",
17882
+ body: "Everything you write outside tool calls is the story the player reads. Never mention your tools, what you look up or record, the current state, trackers, or what you will roll or do next, and never step out of the story to speak as yourself unless the player asks you something out of character. Anything that is not story goes in a hidden note."
17883
+ }
17884
+ },
17885
+ {
17886
+ id: "rolls",
17871
17887
  revision: 2,
17872
17888
  instruction: {
17873
- name: "A good turn",
17889
+ name: "Rolls",
17890
+ when: "always",
17891
+ section: "Rules",
17892
+ body: "When something attempted could fail, roll for it, against a difficulty taken from the files when they set one. The player's dice appear only when you call the tool for their roll: never ask the player to roll in your narration."
17893
+ }
17894
+ },
17895
+ {
17896
+ id: "each-turn",
17897
+ revision: 1,
17898
+ instruction: {
17899
+ name: "Each turn",
17874
17900
  when: "always",
17875
17901
  section: "Narration",
17876
- body: "You are the game master, not a clerk: the tools keep the numbers, and the story is yours. Resolve what the player did, then let the world answer: the people present react from what they want. Build on what is already true before you invent: the scene's purpose, people here who have not spoken, threads left open."
17902
+ body: `Resolve what the player did, then let the people present react from what they want. End each reply on something concrete in front of the player's character that they must answer: someone speaks or acts to them, a threat moves, a choice with a cost. Never end on stillness, a summary or a bare "What do you do?".`
17877
17903
  }
17878
17904
  },
17879
17905
  {
@@ -17898,24 +17924,14 @@ var PLATFORM_INSTRUCTIONS = [
17898
17924
  body: `research() gives exact distances and travel times ("about 2.8 km north, ~35 min on foot"). They keep the world consistent and belong in your tool calls (how much time passes), not recited in the story: "You walk 35 minutes to the butcher's shop" reads like a timetable. Tell the way as the character lives it: "a short walk across the market square", "the sun is well up by the time you reach the butcher's", "half a day's ride along the coast road". Give a figure only when it matters to the moment (a deadline, a chase, a choice between routes) or the player asks, and then say it the way a person would: "a good half hour", "a couple of miles".`
17899
17925
  }
17900
17926
  },
17901
- {
17902
- id: "when-to-roll",
17903
- revision: 1,
17904
- instruction: {
17905
- name: "When to roll",
17906
- when: "always",
17907
- section: "Rules",
17908
- body: "Call for a roll whenever something attempted could fail: what the player's character attempts, and what anyone else attempts that bears on them. Set the difficulty before the dice, and narrate the result honestly against it, failure included."
17909
- }
17910
- },
17911
17927
  {
17912
17928
  id: "combat-tracker",
17913
- revision: 6,
17929
+ revision: 8,
17914
17930
  instruction: {
17915
17931
  name: "Combat tracker",
17916
17932
  when: "always",
17917
17933
  section: "Rules",
17918
- body: `When the fight is over (every foe down, fled or surrendered, or the fighting stops), end it: "<its name>": "End".`,
17934
+ body: `read() the Running a fight procedure (<procedures>) as the fight starts, and run the fight by it. Everyone fighting, companions and allies included, has a place in the turn order. When the fight is over (every foe down, fled or surrendered, or the fighting stops), end it: {"id": "current-state", "op": "remove", "field": "trackers", "value": "Combat"}.`,
17919
17935
  tracker: {
17920
17936
  key: "fight",
17921
17937
  start: {
@@ -17931,18 +17947,18 @@ var PLATFORM_INSTRUCTIONS = [
17931
17947
  },
17932
17948
  starts: "when-started",
17933
17949
  trigger: "a fight begins",
17934
- when: "a fighter's turn ends, or someone joins or leaves the fight"
17950
+ when: "a fighter's turn ends"
17935
17951
  }
17936
17952
  }
17937
17953
  },
17938
17954
  {
17939
17955
  id: "objectives-tracker",
17940
- revision: 4,
17956
+ revision: 5,
17941
17957
  instruction: {
17942
17958
  name: "Objectives",
17943
17959
  when: "always",
17944
17960
  section: "Rules",
17945
- body: `The party's objectives are one list for the whole game: what they are working toward beyond this scene. Add one when the story gives the party one (an order, a job or request taken on, a goal of their own that outlasts the scene), worded as the party knows it then, short, one step to an objective: "Objectives": "Add: Find who opened the lid". A job with several steps they already know (go somewhere, learn something, report back) is one objective per step, in order, never one line joined with "and" or "then", so each is marked done when it happens: "Objectives": "Add: Get into the harbourmaster's office tonight", "Objectives": "Add: Find who signed the berth order", "Objectives": "Add: Tell Captain Vey whose name is on it". Mark one done or failed when the story settles it: "Objectives": "Find who opened the lid: done". Steps they do not know yet are never written ahead of the story: they come as objectives of their own when the story gives them. One that runs over time (keep watch, log every contact) is done only when its end comes.`,
17961
+ body: `The party's objectives are one list for the whole game: what they are working toward beyond this scene. Add one worded as the party knows it then, short, one step to an objective: {"id": "current-state", "op": "add", "field": "trackers[Objectives].items", "value": "Find Item A"}. A job with several steps they already know (go somewhere, learn something, report back) is one objective per step, in order, each an add of its own ("Get into Place A", "Find Item A", "Bring Item A to Character B"), never one item joined with "and" or "then", so each is marked done when it happens. Mark one done or failed when the story settles it: {"id": "current-state", "op": "set", "field": "trackers[Objectives].items[Find Item A]", "value": "done"}. Steps they do not know yet are never written ahead of the story: they come as objectives of their own when the story gives them. One that runs over time (keep watch, log every contact) is done only when its end comes.`,
17946
17962
  tracker: {
17947
17963
  key: "objectives",
17948
17964
  start: {
@@ -17956,19 +17972,19 @@ var PLATFORM_INSTRUCTIONS = [
17956
17972
  question: "In `earlier`, `player_turn` or `narration`, has the party been given an objective, something they are now working toward beyond this scene, that {tracker} does not hold under any wording?",
17957
17973
  yes: 'Yes: an order or an assignment handed to them (they act on it or do not refuse it), a request or a job they take on, or a goal they set themselves that will outlast this scene ("Find what woke the Sleeper", "Get the ledger to the Abbot", "Learn who signed the panel"), and nothing in {tracker} says it.',
17958
17974
  no: "No: {tracker} already holds it, however worded; or it is only floated, refused or still being weighed; or it is only what this scene is for, the next move (go to the archives, ask the clerk, cross the yard), or something done in the same moment.",
17959
- remind: `the story gave the party an objective the current state's Objectives list does not hold (add it as the party knows it now, short, one step to an objective: "Objectives": "Add: <the objective>")`
17975
+ remind: `The story gave the party an objective the current state's Objectives list does not hold: add it as the party knows it now, short, one step to an objective, {"id": "current-state", "op": "add", "field": "trackers[Objectives].items", "value": "<the objective>"}`
17960
17976
  }
17961
17977
  }
17962
17978
  }
17963
17979
  },
17964
17980
  {
17965
17981
  id: "party-tracker",
17966
- revision: 3,
17982
+ revision: 4,
17967
17983
  instruction: {
17968
17984
  name: "Party",
17969
17985
  when: "always",
17970
17986
  section: "Rules",
17971
- body: `The Party is everyone who travels and acts with the player's character as one of them: the player's character, who is always in it, and the companions who have joined. Add someone when they join ("Party": "Add Mira"), and take them off when they leave, part ways for good or die ("Party": "Remove Mira"). Someone who only helps for a while (a guide for the afternoon, a guard who fights beside them once) is not in it unless the story has them join.`,
17987
+ body: `The Party is everyone who travels and acts with the player's character as one of them: the player's character, who is always in it, and the companions who have joined. Add someone when they join, {"id": "current-state", "op": "add", "field": "trackers[Party].records", "value": "Character B"}, and remove them the same way when they leave, part ways for good or die. Someone who only helps for a while (a guide for the afternoon, a guard who fights beside them once) is not in it unless the story has them join.`,
17972
17988
  tracker: {
17973
17989
  key: "party",
17974
17990
  start: {
@@ -17989,61 +18005,19 @@ var PLATFORM_INSTRUCTIONS = [
17989
18005
  question: "In `earlier`, `player_turn` or `narration`, has someone joined the party (now travelling and acting with the player's character as one of them) or left it, that {tracker} does not show?",
17990
18006
  yes: `Yes: someone agrees to come along, is taken on, or sets out with them as one of the group ("I'll come with you", hired for the journey, rescued and staying with them); or someone {tracker} lists leaves, parts ways for good, is left behind or dies.`,
17991
18007
  no: "No: {tracker} already shows who is in the party; or someone only helps for a moment, offers to come but has not joined, or is only met, fought beside or travelled near.",
17992
- remind: `someone joined or left the party and the current state's Party does not show it (add or take them off: "Party": "Add <name>", "Party": "Remove <name>")`
18008
+ remind: `Someone joined or left the party and the current state's Party does not show it: add or remove them, {"id": "current-state", "op": "add", "field": "trackers[Party].records", "value": "<their name>"}`
17993
18009
  }
17994
18010
  }
17995
18011
  }
17996
18012
  },
17997
- {
17998
- id: "death-saves-tracker",
17999
- revision: 3,
18000
- instruction: {
18001
- name: "Death saves",
18002
- when: "always",
18003
- section: "Rules",
18004
- body: `Only for a world whose rules have the dying roll to stay alive. Each save is a roll on their turn: the player's for their character, yours for anyone else. Record each one as it lands ("Death saves for Sara": "Successes +1", "Death saves for Sara": "Failures +1"). When they stabilize, are healed or die, end it ("Death saves for Sara": "End").`,
18005
- tracker: {
18006
- key: "death-saves",
18007
- start: {
18008
- name: "Death saves",
18009
- fields: [
18010
- {
18011
- kind: "count",
18012
- label: "Successes",
18013
- value: 0,
18014
- min: 0,
18015
- max: 3,
18016
- tone: "good",
18017
- display: "pips",
18018
- at: { "3": "Stable" }
18019
- },
18020
- {
18021
- kind: "count",
18022
- label: "Failures",
18023
- value: 0,
18024
- min: 0,
18025
- max: 3,
18026
- tone: "bad",
18027
- display: "pips",
18028
- at: { "3": "Dies" }
18029
- }
18030
- ]
18031
- },
18032
- starts: "when-started",
18033
- trigger: "someone falls dying, and the world's rules have them roll to stay alive",
18034
- perPerson: true,
18035
- when: "they make a death save"
18036
- }
18037
- }
18038
- },
18039
18013
  {
18040
18014
  id: "time-tracker",
18041
- revision: 4,
18015
+ revision: 6,
18042
18016
  instruction: {
18043
18017
  name: "Time",
18044
18018
  when: "always",
18045
18019
  section: "Rules",
18046
- body: `The Time tracker is the in-game time, and only you move it: nothing moves it after the turn. It is always a time someone could tell by a clock or the sky, in the world's own terms: a clock hour ("10:40 pm"), a part of the day ("dawn", "late afternoon") or how long until something ("about 2 hours until sunset"); never only how long passed ("30 minutes later": later than what?). Whenever the story takes time worth counting (a journey, a rest, a wait, a long job or talk), set it to the time it is now in the update() for that stretch of story, beside its other changes: "Time": "It is now dusk". When it shows a clock hour, a span is enough ("Time": "40 minutes pass") and the clock adds it. With nothing else to record, the time is still an update() of its own. A line or two of talk needs no record. Tell it once, as the time it is now: "It is now 12:10 am", "It is now dawn", "It is now about an hour until sunset"; never "a while later" or "30 minutes later". A rest recorded with its length ("scene": "The party rests for 1 hour") already moves the clock, so no "Time": "1 hour passes" after it. Narrate the hour, the light and what is open from the current state's time, never from a guess.`,
18020
+ body: `The in-game time is the current state's "time", and only you move it: nothing moves it after the turn. Whenever the story takes time worth counting, move it in the update() for that stretch of story, beside its other changes. With nothing else to record, the time is still an update() of its own. A line or two of talk needs no record. Tell it in the story once, as the time it is now ("It is now dusk"), never "a while later". Narrate the hour, the light and what is open from the current state's time, never from a guess.`,
18047
18021
  tracker: {
18048
18022
  key: "time",
18049
18023
  start: {
@@ -18058,120 +18032,38 @@ var PLATFORM_INSTRUCTIONS = [
18058
18032
  },
18059
18033
  {
18060
18034
  id: "player-moves",
18061
- revision: 2,
18035
+ revision: 5,
18062
18036
  instruction: {
18063
18037
  name: "Player moves",
18064
18038
  when: "situational",
18065
18039
  trigger: "When the player's character moves: walks somewhere, closes in, backs off, takes cover or follows someone",
18066
18040
  section: "Rules",
18067
- body: `When the move is one they can make, update() the current state with where the player's character ends up first, before you write anything or make any other call ("Kael": "Backs 30 ft away from Lookout 1"). Then tell the turn from there: a move then a shot is update(), then the words, then askPlayerToRoll.`
18068
- }
18069
- },
18070
- {
18071
- id: "filling-gaps",
18072
- revision: 1,
18073
- instruction: {
18074
- name: "Filling gaps",
18075
- when: "always",
18076
- section: "World",
18077
- body: "When research() finds nothing concrete, decide it yourself: how people speak and react from who their files say they are; a small detail a game master decides (a price, a name, what a shop sells) you decide and say, with an update() when it is a value a file keeps (a shop's prices, an innkeeper's name). Only lore, rules and history stay vague. When a place or person the player names has no file, take the nearest real one and play there. When research() says no file is called a name, nobody by that name is in this world: no character speaks of them as someone they know. Take the near match it offers when the player plainly means it, else let the story show nobody knows the name."
18078
- }
18079
- },
18080
- {
18081
- id: "travel-procedure",
18082
- revision: 1,
18083
- instruction: {
18084
- name: "Travel",
18085
- when: "procedure",
18086
- trigger: "When the party goes to another place, sets out on a journey, or boards or leaves a ride (a ship, a caravan)",
18087
- pruning: "after-use",
18088
- section: "World",
18089
- body: `Going to another place is a "scene" change naming everyone who goes and where, in words that find the place without your narration. Anyone not named stays behind.
18090
- - The place exactly as the current state or research() names it: "Move Character A and Character B to Place A", never "to the inn" or "back home".
18091
- - A part of a place by the part and the place ("… to the north gate of Place A", "… to the cellar of Place A"), and the way in when the story gives one ("… into Place A through the kitchen door").
18092
- - When the story stops at one of the places inside a region or a city, name that place. Somewhere the world has no place for (a camp, a clearing, a stretch of road) is said by what is there and where it lies from a place the current state or research() names ("… to a sheltered hollow a mile north of Place A", "… to the alley behind Place A"), never only the region or the city, and the current state makes it.
18093
-
18094
- A place's own map is ground for a scene whatever its size, a district's or a city's too: the party stands and moves on it, and stepping out of a place inside it (out of a tavern into its district) is a move onto that map beside the place they left, never a new place. Only a place with no map of its own, shown on a city's or a region's map, is not ground for a scene: when the story stops somewhere in one (a camp, a clearing, a stretch of road, a gate, a shop), that spot is a place of its own, and the current state makes it and draws its map when you move the party there.
18095
-
18096
- A journey the story plays out before they get there starts as "Character A and Character B set out for Place A by the river road; it takes about an hour" and ends as "Move Character A and Character B to Place A", and nothing in between. Setting out, the update() result tells the same.
18097
-
18098
- A place the current state says the party rides (its "ride": a ship, a caravan, an airship) carries them with the current state aboard it: "Ship A sails for Place A with Character A and Character B aboard; it takes about two days" sets out, "Ship A reaches Place A" gets there, and leaving it is a move off it: "Move Character A and Character B off Ship A onto the docks of Place A". Going below deck or to the bow is a move within it, never a voyage.
18099
-
18100
- Before you narrate the way somewhere or the arrival, or what lies further on, ask research() what the maps say ("What is Place A, and how far is it from here?", "What lies 15 minutes east of here?", "What is further along the path?"): it gives the way and how long it takes, the places inside and around, and where the party comes in. A travel time the files give wins over the map's. What the maps say is what the party knows of the land, never something they read: no one in the story looks at a map unless they hold one. Tell the way as the character lives it ("a short walk across the market square", "half a day's ride along the coast road"), not as research()'s figures.
18101
-
18102
- Someone who goes off without the party is their own change, "Leaves the scene"; where they went belongs in whatHappened, never in the instruction ("Leaves for the next village").`
18041
+ body: `When the move is one they can make, record where the player's character ends up with update(): {"id": "current-state", "op": "set", "field": "people[Character A].position", "value": "30 ft away from Lookout 1"}.`
18103
18042
  }
18104
18043
  },
18105
18044
  {
18106
18045
  id: "fight-procedure",
18107
- revision: 1,
18046
+ revision: 5,
18108
18047
  instruction: {
18109
18048
  name: "Running a fight",
18110
18049
  when: "procedure",
18111
18050
  trigger: "When a fight starts, and on its turns while it runs",
18112
18051
  pruning: "on-compaction",
18113
18052
  section: "Rules",
18114
- body: `A fight runs on its turn order, a tracker in the current state. Start it as the fight starts, as the turn order's line under "Trackers this game keeps" says ("scene": "Start a turn order, Combat at Place A: Character A 18, Lookout 1 15, Character B 12"). An initiative roll (one whose reason says initiative) puts whoever rolled it in the turn order at its total, so the order needs no change for it. As each turn ends, record whose it is now ("Combat": "Lookout 1's turn") and each new round ("Combat": "New round") in the update() with what the turn did.
18053
+ body: `A fight runs on its turn order (the Trackers procedure says how a turn order works). Start it as the fight starts, with everyone taking part, as its line under "Trackers this game keeps" shows, and give each NPC in it a side if theirs is not set (People on the map). Pass the turn as each one ends, and play each turn as the fighter the turn order gives it to.
18115
18054
 
18116
- Sides. When the fight starts, give each NPC in it a side if theirs is not set: the current state marks everyone as the player's character or an NPC with a side. A side is always the one they are now, ally, neutral or enemy ("Character C": "Is now an enemy", "Character D": "Is now an ally"), never only what they stopped being ("Is no longer an enemy"). Hostile arrivals say so ("Add three hostile extras, Bandit 1 to Bandit 3 …").
18055
+ Turns. Every NPC's turn is yours to play, an ally's too (a companion travelling with the party): their move, their roll, what it did, then on to the next. Stop for the player only on the player's character's turn.
18117
18056
 
18118
- Turns. Every NPC's turn is yours to play, an ally's too (a companion travelling with the party): their move, their roll, what it did, then on to the next. Stop for the player only on the player's character's turn. Tell each turn a moment at a time: its words, its roll, then the update() of what it did, before the next turn's words.
18057
+ Reach. A melee attack needs its target within reach, so close the distance first, as a move.
18119
18058
 
18120
- Reach. On a battle map with a grid, when the turn order moves to someone, or someone in it moves, the update() result gives how far everyone is from them and who is within reach ("From Lookout 1 (J9, speed 30 ft): Character A 15 ft south … Within reach (5 ft): nobody"). A melee attack needs its target within reach, so close the distance first, as a move.
18059
+ Stats. As the fight starts, show the stat it turns on under the tokens (hit points as a bar, hidden for enemies), and give each extra in the fight that stat as a detail (People on the map says how). Work out who goes down from the numbers.
18121
18060
 
18122
- Stats on the map. Tokens move, hits and heals float over them and the downed are marked, in the order your update() changes give them. You choose which stats show under every token, for this scene: a bar (hit points, stress), a number (armour class), pips (spell slots, uses) or tags (conditions). Turn one on with a "scene" change when it matters, usually as the fight starts ("Show hit points on the map as a bar, hidden for enemies"); change it ("Show HP from hp and maxHp", "Show enemies' HP too"); turn it off when it no longer matters ("Stop showing HP on the map"). The current state's "shown_on_tokens" says what is shown. An extra shows the stat from its detail by that label ("HP": "6/10"), so give extras in the fight that detail. Anyone without the stat shows nothing.
18123
-
18124
- Going down, in one word, once it has happened: "Falls unconscious", "Is dead", "Surrenders", "Gets back up"; never "falls unconscious if …": work it out from the numbers first. The downed stay in the current state; only someone who is gone (fled, carried off) leaves it.
18125
-
18126
- Table talk. Talk at the table as a game master running a fight would, as much as the moment needs: hand the turn over ("Character B's up.", "Character A, it's your turn. What do you do?"), say how a roll went and what it did ("Seventeen: that hits, for seven damage."), give the logistics a choice turns on (who is in reach, how far, who is flanked, what a move would provoke), and say how hurt someone looks ("he's barely standing") rather than their hit points left. That is table talk; your own bookkeeping stays unsaid. Never write grid squares in the story ("the goblin at I14"): say where someone is by what is there and how far ("behind the overturned cart").`
18127
- }
18128
- },
18129
- {
18130
- id: "trackers-procedure",
18131
- revision: 1,
18132
- instruction: {
18133
- name: "Trackers",
18134
- when: "procedure",
18135
- trigger: "When you start, set up or end a tracker (a turn order, a clock, a plan's steps), or need one's value as it stands now",
18136
- pruning: "after-use",
18137
- section: "Rules",
18138
- body: `A tracker keeps what the scene is counting that is no value in a file: a fight's turn order, a clock that fills toward trouble, the steps of a plan, a record for each suspect or companion, and the like. What belongs to someone for good (hit points, gold, an item, a lasting stat) stays in their file, never in a tracker. Where and how a tracker shows on screen is decided for you.
18139
-
18140
- Starting one. Start one when the story begins counting something the player should follow (a fight starts: its turn order; a threat that grows with each noise or minute: a clock), with a "scene" change that names it and says what it counts in plain words, and the current state builds it: "Start a turn order, Fight at the mill: Character A 18, Lookout 1 15, Character B 12", "Start a hidden 6-step clock, The watch arrives. At 6: crossbowmen at the gate", "Start a tracker, The heist plan: case the vault, bribe the night guard, crack the safe" (a plan the party made, every step known), "Start a tracker for the whole game, Suspects: a record for each of Character B and Lookout 1, suspicion 0 of 5 and their alibi". A tracker the game keeps needs no start: a change with its name as the subject starts it the first time, as its line under "Trackers this game keeps" says.
18141
-
18142
- How long it lasts. A turn order, a clock or a count ends when the party leaves the place unless you say "for the whole game"; a list of steps (a plan, a ritual) lasts the game unless you say it ends here.
18143
-
18144
- Stats. A tracker can show any stat from the sheets of the people it names (HP, Mana, Stamina) and where each stands, read live and never copied in: "Start a turn order, Fight at the mill: Character A 18, Lookout 1 15, showing HP, Mana and where each stands", "Fight at the mill": "Show HP, Mana and Stamina", "Suspects": "Show each suspect's HP too". That is the tracker's own; the stats under the tokens are the map's.
18145
-
18146
- Hidden. One marked hidden is yours alone, for pressure the party cannot see yet; reveal it when they would know.
18147
-
18148
- Changing it. Keep it in step with the story in the same update() as the rest of what happened, with its name as the subject: "Fight at the mill": "Lookout 1's turn" (whose turn it is now), "Fight at the mill": "New round", "The watch arrives": "+1", "The watch arrives": "Fill it" (its trouble came before it filled), "The heist plan": "Bribe the night guard: done", "Suspects": "Character B: Suspicion +1", "The watch arrives": "At 4: the gate bell rings" (what reaching a value brings). A step the story completes is done; the hours or days that pass move a countdown of them, once: recording that time again later in the reply moves nothing; a clock whose trouble the story brings is filled, never left short of it.
18149
-
18150
- As it stands now. The <current-state> block that opens your context is the current state as it stood then, and a tracker's changes since are in the update() results: when you need one as it stands now (before you narrate from its value or change it by an amount that depends on it, or once the story has moved it several times), ask research() for it by its name, "Tracker: The watch arrives", in the same research() as the turn's other questions.
18151
-
18152
- Ending it. End it when it stops mattering (the fight is over, the clock filled and its trouble came): "Fight at the mill": "End".`
18153
- }
18154
- },
18155
- {
18156
- id: "hidden-things-procedure",
18157
- revision: 1,
18158
- instruction: {
18159
- name: "Hidden things",
18160
- when: "procedure",
18161
- trigger: "When someone or something is hidden from the party (an ambush, a stalker, a disguise), or is revealed",
18162
- pruning: "after-use",
18163
- section: "Rules",
18164
- body: `What the party has not seen yet is still in the current state. When it matters (the party hears, smells or spots a sign of it, or it lies in wait), add it as what it is, marked hidden, where it is (on a grid, its square), with what the party perceives of it: "Add a hostile Black Bear at J9, hidden; the party hears only slow, heavy breathing", "Add Character B at the gate, disguised as a hooded pilgrim". A sound, a smell or tracks are never a token.
18165
-
18166
- When the party sees it for what it is, say so ("Black Bear 1": "Is revealed"); an extra that turns out to be one of a kind becomes one ("scene": "The slow, heavy breathing is revealed as a Black Bear"). A note only you keep: ("Black Bear 1": "GM note: asleep; wakes at any loud noise").
18167
-
18168
- The current state shows you who everyone really is and the player's screen only what the party knows: narrate only what the party perceives ("a rustle beneath the thicket"), never the name or kind of what is hidden, until it is revealed.`
18061
+ Table talk. Talk at the table as a game master running a fight would, as much as the moment needs: hand the turn over ("Character B's up.", "Character A, it's your turn. What do you do?"), say how a roll went and what it did ("Seventeen: that hits, for seven damage."), give the logistics a choice turns on (who is in reach, how far, who is flanked, what a move would provoke), and say how hurt someone looks ("he's barely standing") rather than their hit points left. Table talk is not narrating your process; your own bookkeeping stays unsaid.`
18169
18062
  }
18170
18063
  }
18171
18064
  ];
18172
18065
  var RESPONSE_LENGTH = "Response length";
18173
18066
  var LONG_TRAVEL = "Long-distance travel";
18174
- var STORY_PACING_KEY = "platform-story-pacing";
18175
18067
  var PLATFORM_OPTIONS = [
18176
18068
  {
18177
18069
  revision: 3,
@@ -18225,58 +18117,7 @@ var PLATFORM_OPTIONS = [
18225
18117
  }
18226
18118
  },
18227
18119
  {
18228
- revision: 1,
18229
- option: {
18230
- key: STORY_PACING_KEY,
18231
- label: "Story pacing",
18232
- description: "How hard the Game Master pushes the story forward.",
18233
- kind: "one",
18234
- default: ["driven"],
18235
- choices: [
18236
- {
18237
- key: "driven",
18238
- label: "Driven",
18239
- description: "Something moves every turn.",
18240
- instructions: [
18241
- {
18242
- when: "always",
18243
- body: `Each turn, something moves on besides what the player did: a clock, a threat, a rumour, someone who has been waiting.
18244
-
18245
- End each reply on something concrete in front of the player's character that they must answer: someone speaks or acts to them, an offer, a threat that moves, a choice with a cost, time running short. Never end on stillness ("the room is quiet"), a summary, or a bare "What do you do?".`
18246
- }
18247
- ]
18248
- },
18249
- {
18250
- key: "player-lead",
18251
- label: "Player Lead",
18252
- description: "Pushes when you push, lingers when you linger.",
18253
- instructions: [
18254
- {
18255
- when: "always",
18256
- body: `Match the pace to the player's. When they act, press on: consequences land and the people present push back. When they talk, look around or take their time, stay in the moment with them and bring in nothing new unless a thread already in motion arrives.
18257
-
18258
- End each reply on something in front of the player's character to answer: someone's reply or reaction, an invitation, a choice. Never end on stillness ("the room is quiet"), a summary, or a bare "What do you do?".`
18259
- }
18260
- ]
18261
- },
18262
- {
18263
- key: "slice-of-life",
18264
- label: "Slice of life",
18265
- description: "Time to talk and explore; no trouble unless you look for it.",
18266
- instructions: [
18267
- {
18268
- when: "always",
18269
- body: `Let scenes breathe. Spend the reply on the people and the place: conversation, small gestures, how somewhere looks, sounds and smells. Bring in no new threat, deadline or twist: trouble comes only when the player goes looking for it, or from something the story already set in motion.
18270
-
18271
- End each reply on something open in front of the player's character, however quiet the moment: someone speaks to them or catches their eye, an invitation, a small detail worth a closer look. Never end on stillness ("there is room to eat in peace"), a summary, or a bare "What do you do?".`
18272
- }
18273
- ]
18274
- }
18275
- ]
18276
- }
18277
- },
18278
- {
18279
- revision: 1,
18120
+ revision: 2,
18280
18121
  option: {
18281
18122
  key: "platform-long-distance-travel",
18282
18123
  label: LONG_TRAVEL,
@@ -18297,7 +18138,7 @@ End each reply on something open in front of the player's character, however qui
18297
18138
  instructions: [
18298
18139
  {
18299
18140
  when: "always",
18300
- body: `When the party travels somewhere far (a trip of hours or more), take them straight there in the same reply: record it as arriving ("Move Kael and Mira to Harrowmere"), never "set out for", sum up the trip in a sentence or two (how long it took, how the way went), and go on from the arrival. When the player set out from the map, tell the arrival the same way.`
18141
+ body: "When the party travels somewhere far (a trip of hours or more), take them straight there in the same reply: set place to where they are going, never travel, and inc time by how long the trip took; sum up the trip in a sentence or two (how long it took, how the way went), and go on from the arrival. When the player set out from the map, tell the arrival the same way."
18301
18142
  }
18302
18143
  ]
18303
18144
  },
@@ -18308,7 +18149,7 @@ End each reply on something open in front of the player's character, however qui
18308
18149
  instructions: [
18309
18150
  {
18310
18151
  when: "always",
18311
- body: `When the party travels somewhere far (a trip of hours or more), play it as a journey to where the player said they are going. Set out first ("Kael and Mira set out for Harrowmere by the coast road; it takes about three days"), then tell the way in stages over several replies, for as long as the distance really takes: the road and the weather, rests and camps, people and trouble met on the way, with time passing as it would ("It is now dusk"). The party stays on the way the whole time: a camp or a rest is told with the time it takes, never a move to a camp or a stretch of road. Someone met stops the journey where they meet; when the party goes on from there, record it as "Kael and Mira set out again for Harrowmere", never as a move farther along the road. Keep heading for the place the player named unless they change course, and record the arrival ("Move Kael and Mira to Harrowmere") only once the way has been travelled. When the player set out from the map, they are already on the way: go on with the journey.`
18152
+ body: `When the party travels somewhere far (a trip of hours or more), play it as a journey to where the player said they are going. Set out first, setting travel to that place with the way and how long it takes ({"to": "place-b#2f4b9e10", "via": "the coast road", "takes": "about three days"}), then tell the way in stages over several replies, for as long as the distance really takes: the road and the weather, rests and camps, people and trouble met on the way, with the time moving as it would. The party stays on the road the whole time: a camp, a rest or someone met is told there, with the time it takes, never as a place of its own. Keep heading for the place the player named unless they change course, and set place to it only once the way has been travelled. When the player set out from the map, they are already on the way: go on with the journey.`
18312
18153
  }
18313
18154
  ]
18314
18155
  }
@@ -18360,6 +18201,7 @@ var TRACKER_LIMITS = {
18360
18201
  };
18361
18202
  var labelSchema = string2().trim().max(TRACKER_LIMITS.label).nullish();
18362
18203
  var hiddenSchema = boolean2().nullish();
18204
+ var turnStampSchema = number2().int().nonnegative().nullish().catch(null);
18363
18205
  var refSchema = object({
18364
18206
  name: string2().trim().min(1).max(TRACKER_LIMITS.name),
18365
18207
  file: string2().nullish()
@@ -18488,7 +18330,9 @@ var trackerShapeSchema = object({
18488
18330
  lasts: _enum(TRACKER_LASTS).nullish(),
18489
18331
  from: string2().nullish(),
18490
18332
  since: string2().nullish(),
18491
- changed: string2().nullish()
18333
+ changed: string2().nullish(),
18334
+ sinceTurn: turnStampSchema,
18335
+ changedTurn: turnStampSchema
18492
18336
  });
18493
18337
  function isObject2(value) {
18494
18338
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -18533,7 +18377,13 @@ var TRACKER_DEFINITION_LIMITS = {
18533
18377
  };
18534
18378
  var trackerDefinitionSchema = object({
18535
18379
  key: string2().trim().min(1).max(TRACKER_DEFINITION_LIMITS.key).regex(/^[a-z0-9][a-z0-9-]*$/, "Keys are lowercase letters, digits and -"),
18536
- start: trackerShapeSchema.omit({ since: true, changed: true, from: true }),
18380
+ start: trackerShapeSchema.omit({
18381
+ since: true,
18382
+ changed: true,
18383
+ sinceTurn: true,
18384
+ changedTurn: true,
18385
+ from: true
18386
+ }),
18537
18387
  starts: _enum(TRACKER_STARTS),
18538
18388
  when: string2().trim().max(TRACKER_DEFINITION_LIMITS.when).nullish(),
18539
18389
  updates: _enum(TRACKER_UPDATES).nullish(),
@@ -18551,6 +18401,11 @@ var NUMBER_KINDS = new Set(["count", "clock", "scale"]);
18551
18401
  function isNumberField(field) {
18552
18402
  return NUMBER_KINDS.has(field.kind);
18553
18403
  }
18404
+ var QUOTED_PAIR = String.raw`"[^"\n]*"\s*:\s*"[^"\n]*"`;
18405
+ var QUOTED_ASIDE_RE = new RegExp(String.raw`\s*\([^()]*${QUOTED_PAIR}[^()]*\)`, "g");
18406
+ var QUOTED_PAIRS_RE = new RegExp(String.raw`:?\s*${QUOTED_PAIR}(?:\s*,\s*${QUOTED_PAIR})*`, "g");
18407
+ var EXAMPLE_PERSON = "Character A";
18408
+ var EXAMPLE_ORDER = { [EXAMPLE_PERSON]: 18, "Lookout 1": 12 };
18554
18409
  var ARTICLE_RE = /^(?:the|a|an)\s+/;
18555
18410
  var POSSESSIVE_RE = /['’]s\b/g;
18556
18411
  var NON_WORD_RE = /[^\p{L}\p{N}]+/gu;
@@ -19269,12 +19124,14 @@ var gmToolSettingsInputSchema = object({
19269
19124
  askPlayerToRoll: boolean2().optional(),
19270
19125
  askUserQuestion: boolean2().optional(),
19271
19126
  generateNames: boolean2().optional(),
19127
+ gmExpressions: boolean2().optional(),
19272
19128
  research: boolean2().optional()
19273
19129
  }).strict();
19274
19130
  var gmToolSettingsSchema = object({
19275
19131
  askPlayerToRoll: boolean2(),
19276
19132
  askUserQuestion: boolean2(),
19277
19133
  generateNames: boolean2(),
19134
+ gmExpressions: boolean2(),
19278
19135
  research: boolean2()
19279
19136
  }).strict();
19280
19137
 
@@ -28355,6 +28212,9 @@ function sanitizeVfsPathForDisk(path) {
28355
28212
  return path.split("/").map((segment) => segment === "" ? "" : sanitizeVfsSegmentForDisk(segment)).join("/");
28356
28213
  }
28357
28214
 
28215
+ // ../shared/src/files/file-handle.ts
28216
+ var QUOTES = new Set(['"', "'", "“", "”", "‘", "’", "«", "»"]);
28217
+
28358
28218
  // ../shared/src/files/file-reference-content.ts
28359
28219
  var STATE_PATH_LEADING_SLASH_RE = /^\/+/;
28360
28220
  var STATE_PATH_ARRAY_INDEX_RE = /\[(?:\d+|\*)\]/g;
@@ -28578,13 +28438,13 @@ function referenceDisplayValue(value, qualify) {
28578
28438
  }
28579
28439
  return value;
28580
28440
  }
28581
- function projectFileContentReferencesForEditing(content, schema, layout) {
28582
- return projectValue(content, asSchemaNode(schema), referenceHintsFromLayout(layout), "");
28441
+ function projectFileContentReferencesForEditing(content, schema, layout, display) {
28442
+ return projectValue(content, asSchemaNode(schema), referenceHintsFromLayout(layout), "", display);
28583
28443
  }
28584
- function projectValue(value, schema, hints, path) {
28444
+ function projectValue(value, schema, hints, path, display) {
28585
28445
  const directTargets = directReferenceTargets(schema, hints, path);
28586
28446
  if (directTargets.length > 0) {
28587
- return referenceDisplayValue(value, directTargets.length > 1);
28447
+ return display?.(value) ?? referenceDisplayValue(value, directTargets.length > 1);
28588
28448
  }
28589
28449
  const itemSchema = schemaItems(schema);
28590
28450
  if (Array.isArray(value)) {
@@ -28592,9 +28452,9 @@ function projectValue(value, schema, hints, path) {
28592
28452
  const itemTargets = arrayItemReferenceTargets(schema, itemSchema, hints, path);
28593
28453
  if (itemTargets.length > 0) {
28594
28454
  const referenceSchema = referenceSchemaForTargets(itemSchema, itemTargets);
28595
- return value.map((item) => projectValue(item, referenceSchema, hints, itemPath));
28455
+ return value.map((item) => projectValue(item, referenceSchema, hints, itemPath, display));
28596
28456
  }
28597
- return itemSchema || hintHasPathPrefix(hints, itemPath) ? value.map((item) => projectValue(item, itemSchema, hints, itemPath)) : value;
28457
+ return itemSchema || hintHasPathPrefix(hints, itemPath) ? value.map((item) => projectValue(item, itemSchema, hints, itemPath, display)) : value;
28598
28458
  }
28599
28459
  const properties = schemaProperties2(schema);
28600
28460
  if (isRecord5(value) && (properties || hintHasPathPrefix(hints, path))) {
@@ -28606,7 +28466,7 @@ function projectValue(value, schema, hints, path) {
28606
28466
  if (!(childSchema || hintHasPathPrefix(hints, childPath))) {
28607
28467
  continue;
28608
28468
  }
28609
- const projected = projectValue(childValue, asSchemaNode(childSchema), hints, childPath);
28469
+ const projected = projectValue(childValue, asSchemaNode(childSchema), hints, childPath, display);
28610
28470
  if (projected !== childValue) {
28611
28471
  changed = true;
28612
28472
  next[key] = projected;
@@ -28826,6 +28686,14 @@ var fileContentBaseSchema = object({
28826
28686
  description: string2().optional(),
28827
28687
  image: preprocess((value) => value === null ? undefined : value, imageObjectSchema.optional())
28828
28688
  }).passthrough();
28689
+ var IMAGE_URL_KEYS = new Set([
28690
+ "url",
28691
+ "backgroundRemovedUrl",
28692
+ "overlayUrl",
28693
+ "posterUrl",
28694
+ "mp4Url",
28695
+ "webmUrl"
28696
+ ]);
28829
28697
  var IMAGE_OBJECT_JSON_SCHEMA = {
28830
28698
  type: "object",
28831
28699
  format: "image",
@@ -30190,7 +30058,7 @@ function defineCatalog(schema, catalog) {
30190
30058
  return schema.createCatalog(catalog);
30191
30059
  }
30192
30060
 
30193
- // ../../node_modules/.bun/@json-render+react@0.19.0+d86b59289c1a13ae/node_modules/@json-render/react/dist/chunk-WYDS23XB.mjs
30061
+ // ../../node_modules/.bun/@json-render+react@0.19.0+a3a949af4499b2f6/node_modules/@json-render/react/dist/chunk-WYDS23XB.mjs
30194
30062
  var schema = defineSchema((s) => ({
30195
30063
  spec: s.object({
30196
30064
  root: s.string(),
@@ -30245,7 +30113,7 @@ var schema = defineSchema((s) => ({
30245
30113
  ]
30246
30114
  });
30247
30115
 
30248
- // ../../node_modules/.bun/@json-render+shadcn@0.19.0+53a8df2c6ce486a8/node_modules/@json-render/shadcn/dist/chunk-L37EOULD.mjs
30116
+ // ../../node_modules/.bun/@json-render+shadcn@0.19.0+c4b94aca9866a541/node_modules/@json-render/shadcn/dist/chunk-L37EOULD.mjs
30249
30117
  var validationCheckSchema = array(object({
30250
30118
  type: string2(),
30251
30119
  message: string2(),
@@ -30620,6 +30488,372 @@ var shadcnComponentDefinitions = {
30620
30488
  }
30621
30489
  };
30622
30490
 
30491
+ // ../shared/src/json-render/card-catalog.ts
30492
+ var SCRIPT_CARD_TONES = [
30493
+ "neutral",
30494
+ "success",
30495
+ "failure",
30496
+ "critical",
30497
+ "fumble",
30498
+ "partial",
30499
+ "info",
30500
+ "magic"
30501
+ ];
30502
+ var SCRIPT_CARD_ICONS = [
30503
+ "sword",
30504
+ "swords",
30505
+ "shield",
30506
+ "heart",
30507
+ "skull",
30508
+ "coins",
30509
+ "scroll",
30510
+ "sparkles",
30511
+ "flame",
30512
+ "snowflake",
30513
+ "droplet",
30514
+ "dice",
30515
+ "key",
30516
+ "lock",
30517
+ "eye",
30518
+ "moon",
30519
+ "sun",
30520
+ "tent",
30521
+ "crown",
30522
+ "gem",
30523
+ "flask",
30524
+ "book",
30525
+ "target",
30526
+ "zap",
30527
+ "trophy",
30528
+ "users",
30529
+ "user",
30530
+ "map",
30531
+ "compass",
30532
+ "wand",
30533
+ "star",
30534
+ "check",
30535
+ "x",
30536
+ "hourglass",
30537
+ "footprints",
30538
+ "hammer",
30539
+ "cpu",
30540
+ "brain",
30541
+ "vote",
30542
+ "bag",
30543
+ "feather",
30544
+ "ghost",
30545
+ "leaf",
30546
+ "anchor",
30547
+ "castle",
30548
+ "music",
30549
+ "message",
30550
+ "alert",
30551
+ "info",
30552
+ "plus",
30553
+ "minus",
30554
+ "arrow"
30555
+ ];
30556
+ var DEFAULT_SLOT = ["default"];
30557
+ var tone = optional(nullable(_enum(SCRIPT_CARD_TONES)));
30558
+ var ICON_NAME_RE = /^[a-z][a-z-]*$/;
30559
+ var KNOWN_ICONS = new Set(SCRIPT_CARD_ICONS);
30560
+ var icon = optional(nullable(string2().max(16).refine((name) => !ICON_NAME_RE.test(name) || KNOWN_ICONS.has(name), {
30561
+ message: `not a card icon; use one of ${SCRIPT_CARD_ICONS.join(", ")}, or an emoji`
30562
+ })));
30563
+ var SCRIPT_CARD_COLORS = [
30564
+ "red",
30565
+ "orange",
30566
+ "gold",
30567
+ "green",
30568
+ "teal",
30569
+ "blue",
30570
+ "violet",
30571
+ "pink"
30572
+ ];
30573
+ var text = optional(nullable(string2()));
30574
+ var num = optional(nullable(number2()));
30575
+ var className = optional(nullable(string2()));
30576
+ var value = optional(nullable(union([string2(), number2()])));
30577
+ var TONE_DOC = `tone is one of ${SCRIPT_CARD_TONES.map((t) => `"${t}"`).join(", ")}: success emerald, failure rose, critical gold, fumble red, partial amber, info sky, magic violet.`;
30578
+ var ICON_DOC = `icon names a game icon (${SCRIPT_CARD_ICONS.join(", ")}) or is a short text such as an emoji.`;
30579
+ var DIE_DOC = `Each die is { sides, value, kept, tone, color, label, exploded }: sides 4, 6, 8, 10, 12, 20 or 100 draws that die's shape (any other is a square); value is its face, a number or a short text ("+", "−" and " " for Fate dice, "?" for a die players don't see); kept: false strikes it out (the low die of advantage, a pool's dropped dice); a kept d20's natural 20 glows gold and its natural 1 breaks; tone gives it a meaning (a Blades pool's highest die "partial"), while color only colors it (${SCRIPT_CARD_COLORS.join(", ")}: "orange" for fire); label is a word under it ("fire", "hope"); exploded: true marks a die that exploded.`;
30580
+ var scriptCardComponents = {
30581
+ Frame: {
30582
+ props: object({
30583
+ eyebrow: text,
30584
+ title: text,
30585
+ subtitle: text,
30586
+ icon,
30587
+ image: text,
30588
+ tag: text,
30589
+ tone,
30590
+ className
30591
+ }),
30592
+ slots: DEFAULT_SLOT,
30593
+ description: `Only in a script's card: the card's shell. A header (an icon or a portrait image, a small eyebrow line above the title such as "Attack" or "Shop", the title, a subtitle) and its children stacked below. The frame stays neutral and tone tints its icon; only "critical" (a gold rule and a foil sweep) and "fumble" (a red rule) mark the frame itself, so save them for a natural 20 or 1. image is a portrait URL in place of the icon (a merchant, a speaker). tag is a small label at the header's end ("Live", "Secret"). Keep eyebrow to two short parts ("Longsword · advantage"); put mechanics such as a roll's position in Tags. Put an Actions element last among its children for a footer of Buttons. ${TONE_DOC} ${ICON_DOC}`,
30594
+ example: {
30595
+ eyebrow: "Attack",
30596
+ title: "Brom swings at the goblin",
30597
+ subtitle: "Longsword · advantage",
30598
+ icon: "sword",
30599
+ tone: "success"
30600
+ }
30601
+ },
30602
+ Actions: {
30603
+ props: object({
30604
+ align: optional(nullable(_enum(["start", "end", "stretch"]))),
30605
+ className
30606
+ }),
30607
+ slots: DEFAULT_SLOT,
30608
+ description: `Only in a script's card: a row of Buttons. Last inside a Frame it is the frame's footer, set apart by a rule; elsewhere it is a plain row. align "stretch" gives each Button an equal share.`,
30609
+ example: { align: "start" }
30610
+ },
30611
+ Dice: {
30612
+ props: object({
30613
+ dice: array(unknown()),
30614
+ size: optional(nullable(_enum(["sm", "md", "lg"]))),
30615
+ className
30616
+ }),
30617
+ description: `Only in a script's card: dice as they landed, each drawn as its own shape with its face. ${DIE_DOC}`,
30618
+ example: {
30619
+ dice: [
30620
+ { sides: 20, value: 17 },
30621
+ { sides: 20, value: 6, kept: false }
30622
+ ],
30623
+ size: "md"
30624
+ }
30625
+ },
30626
+ Roll: {
30627
+ props: object({
30628
+ label: text,
30629
+ dice: array(unknown()),
30630
+ modifiers: optional(nullable(array(unknown()))),
30631
+ total: value,
30632
+ target: optional(nullable(object({ label: text, value }).passthrough())),
30633
+ outcome: optional(nullable(object({ text, tone }).passthrough())),
30634
+ className
30635
+ }),
30636
+ description: `Only in a script's card: one roll read as a whole: its dice, what was added, the total large, what it was against and the verdict as a stamp. dice as in Dice (${DIE_DOC}). modifiers is [{ value, label }] ({ value: 5, label: "STR" }). target is { label, value } ("AC", 15; "DC", 13). outcome is { text, tone } ("Hit" success, "Critical" critical, "Miss" failure, "Mixed" partial). The total is written by the script; the card doesn't add it up. A lone die with nothing added is its own total: it shows large with the verdict beside it. With a numeric target the card shows how far the roll beat or missed it, except on a natural 20 or 1.`,
30637
+ example: {
30638
+ label: "Attack roll",
30639
+ dice: [{ sides: 20, value: 17 }],
30640
+ modifiers: [{ value: 5, label: "STR" }],
30641
+ total: 22,
30642
+ target: { label: "AC", value: 15 },
30643
+ outcome: { text: "Hit", tone: "success" }
30644
+ }
30645
+ },
30646
+ Hero: {
30647
+ props: object({
30648
+ value: union([string2(), number2()]),
30649
+ label: text,
30650
+ unit: text,
30651
+ caption: text,
30652
+ icon,
30653
+ tone,
30654
+ className
30655
+ }),
30656
+ description: `Only in a script's card: the moment's one big number without dice: a level reached (label "Level", value 4), experience earned ("+450", unit "XP"), gold found, damage dealt, days left. tone "critical" celebrates it in gold with a burst (a level up, a milestone); other tones tint it. caption is a small line under the number. ${ICON_DOC}`,
30657
+ example: {
30658
+ label: "Level",
30659
+ value: 4,
30660
+ icon: "star",
30661
+ tone: "critical",
30662
+ caption: "Fighter · 2,700 XP"
30663
+ }
30664
+ },
30665
+ Outcome: {
30666
+ props: object({
30667
+ title: string2(),
30668
+ text,
30669
+ icon,
30670
+ tone,
30671
+ className
30672
+ }),
30673
+ description: `Only in a script's card: what happened, in words, as a band across the card: "The goblin falls", "Poisoned for 1 hour". title is the headline (keep it under about 60 characters: it shows two lines at most), text the detail under it. ${TONE_DOC}`,
30674
+ example: {
30675
+ title: "Poisoned for 1 hour",
30676
+ text: "Disadvantage on attack rolls and ability checks.",
30677
+ icon: "flask",
30678
+ tone: "failure"
30679
+ }
30680
+ },
30681
+ Meter: {
30682
+ props: object({
30683
+ label: text,
30684
+ value: number2(),
30685
+ max: number2(),
30686
+ min: num,
30687
+ from: num,
30688
+ icon,
30689
+ tone: optional(nullable(_enum([...SCRIPT_CARD_TONES, "health"]))),
30690
+ display: optional(nullable(_enum(["bar", "boxes", "pips"]))),
30691
+ better: optional(nullable(_enum(["higher", "lower"]))),
30692
+ caption: text,
30693
+ valueLabel: text,
30694
+ minLabel: text,
30695
+ maxLabel: text,
30696
+ hideValue: optional(nullable(boolean2())),
30697
+ className
30698
+ }),
30699
+ description: `Only in a script's card: a value against its max: hit points, mana, experience, a trace, votes in. from is where it was before this run: a loss drains and leaves an outlined ghost of what was lost (health shows a skull at zero), a gain glows, and the change shows signed beside the value. tone "health" (the default) goes green, amber, red as it empties; any other tone fixes the color. display "boxes" or "pips" draws one cell per point (supplies, death saves; up to 20); pips with an icon draw that icon in each (hearts, skulls). min below zero makes it two-sided around a centre tick (faction standing −5 to +5, sanity); valueLabel names where it stands ("Friendly"); minLabel and maxLabel label the ends (put them on the last of a group). better: "lower" when going up is bad (a trace, an alarm). caption is a small line under it. hideValue hides the numbers (an enemy's health). ${TONE_DOC}`,
30700
+ example: {
30701
+ label: "Hit points",
30702
+ value: 9,
30703
+ from: 21,
30704
+ max: 32,
30705
+ icon: "heart"
30706
+ }
30707
+ },
30708
+ Changes: {
30709
+ props: object({
30710
+ items: array(unknown()),
30711
+ className
30712
+ }),
30713
+ description: `Only in a script's card: what this run changed, a row each: { label, from, to, delta, unit, icon, detail, tone, better }. from → to shows before and after; delta is the signed difference (worked out from numbers when left out); a row with only delta reads "Gold −25 gp"; a row with only to reads as something gained ("New feature: Second Wind"). A row that didn't change is left out. Going up is green unless better is "lower"; tone "success" or "failure" overrides it. ${ICON_DOC}`,
30714
+ example: {
30715
+ items: [
30716
+ { label: "Hit point maximum", from: 32, to: 41, icon: "heart" },
30717
+ { label: "Gold", delta: -25, unit: "gp", icon: "coins" }
30718
+ ]
30719
+ }
30720
+ },
30721
+ Facts: {
30722
+ props: object({
30723
+ items: array(unknown()),
30724
+ layout: optional(nullable(_enum(["inline", "grid"]))),
30725
+ columns: num,
30726
+ className
30727
+ }),
30728
+ description: `Only in a script's card: short label/value pairs, [{ label, value, tone }]: DC 15 · Range 60 ft · Save WIS. layout "inline" (the default) runs them on one wrapping line; "grid" sets them in cells with the value large (columns, 2 to 4).`,
30729
+ example: {
30730
+ items: [
30731
+ { label: "DC", value: 15 },
30732
+ { label: "Range", value: "60 ft" }
30733
+ ],
30734
+ layout: "inline"
30735
+ }
30736
+ },
30737
+ Tags: {
30738
+ props: object({
30739
+ items: array(unknown()),
30740
+ className
30741
+ }),
30742
+ description: `Only in a script's card: small pills for conditions, damage types, traits: [{ text, tone, icon }] or plain strings. ${TONE_DOC}`,
30743
+ example: {
30744
+ items: [{ text: "Poisoned", tone: "fumble", icon: "flask" }, "Prone"]
30745
+ }
30746
+ },
30747
+ Roster: {
30748
+ props: object({
30749
+ items: array(unknown()),
30750
+ numbered: optional(nullable(boolean2())),
30751
+ tone,
30752
+ className
30753
+ }),
30754
+ description: `Only in a script's card: people in order: initiative, a party at rest, who voted. Each is { name, subtitle, image, value, valueLabel, dice, modifier, meter: { value, max }, tags, tone, state, you }: image is a portrait URL; without one the game shows the person's own portrait when it knows them by name (the player, a character, an extra), else initials; value is a number at the row's end with valueLabel under it ("19", "init"); dice ([{ sides, value, kept }], up to 3, as Dice takes them) show small before the value with modifier ("+2") between, for a roster of rolls such as initiative; meter is a thin health bar; state "current" lights the row (whose turn it is), "done" and "down" dim it ("down" strikes the name and shows a skull); you: true marks the player's own row. numbered shows 1, 2, 3. tone is the current row's accent (violet by default); an entry's own tone tints its initials ("failure" for enemies).`,
30755
+ example: {
30756
+ numbered: true,
30757
+ items: [
30758
+ {
30759
+ name: "Sera",
30760
+ subtitle: "Ranger",
30761
+ dice: [{ sides: 20, value: 16 }],
30762
+ modifier: "+3",
30763
+ value: 19,
30764
+ valueLabel: "init"
30765
+ },
30766
+ { name: "Goblin", value: 8, tone: "failure", state: "down" }
30767
+ ]
30768
+ }
30769
+ },
30770
+ RollTable: {
30771
+ props: object({
30772
+ rows: array(unknown()),
30773
+ landed: num,
30774
+ rolled: value,
30775
+ sides: num,
30776
+ className
30777
+ }),
30778
+ description: `Only in a script's card: a table of ranges with the row the roll landed on lit: a random encounter table, a move's bands (10+ / 7–9 / 6−), a percentile ladder. rows is [{ range, label, text, tone }]; landed is the index of the row that came up (0 first); rolled is the number shown on it; with sides (20) it shows as that die. text shows only on the landed row, so keep each label short and put what it means in text. ${TONE_DOC}`,
30779
+ example: {
30780
+ rows: [
30781
+ { range: "10+", label: "You do it", tone: "success" },
30782
+ { range: "7–9", label: "You stumble", tone: "partial" },
30783
+ { range: "6−", label: "The GM makes a move", tone: "failure" }
30784
+ ],
30785
+ landed: 1,
30786
+ rolled: 8
30787
+ }
30788
+ },
30789
+ Steps: {
30790
+ props: object({
30791
+ items: array(unknown()),
30792
+ direction: optional(nullable(_enum(["vertical", "horizontal"]))),
30793
+ tone,
30794
+ className
30795
+ }),
30796
+ description: `Only in a script's card: stages in order: a level up's choices, a ritual, clues found, a journey's legs. items is [{ label, detail, status }] with status "done", "current", "failed" or "todo" (the default). direction "horizontal" fits up to five short labels on one line. tone is the current stage's accent (violet by default).`,
30797
+ example: {
30798
+ items: [
30799
+ { label: "Roll hit points", status: "done" },
30800
+ { label: "Choose a feat", status: "current" },
30801
+ { label: "Review your sheet" }
30802
+ ]
30803
+ }
30804
+ },
30805
+ Log: {
30806
+ props: object({
30807
+ items: array(unknown()),
30808
+ className
30809
+ }),
30810
+ description: `Only in a script's card: what happened, line by line: a contest's rounds, a fight's blows, tries at a lock. items is [{ label, text, detail, tone }] or plain strings; label is a short marker ("R1", "Try 2"); detail is a small breakdown after the line ("16+5 · 9+4"); tone success or critical marks the line with a check, failure or fumble with a cross. ${TONE_DOC}`,
30811
+ example: {
30812
+ items: [
30813
+ { label: "R1", text: "21 vs 13", tone: "success" },
30814
+ { label: "R2", text: "9 vs 18", tone: "failure" }
30815
+ ]
30816
+ }
30817
+ },
30818
+ Currency: {
30819
+ props: object({
30820
+ amounts: array(unknown()),
30821
+ label: text,
30822
+ signed: optional(nullable(boolean2())),
30823
+ className
30824
+ }),
30825
+ description: `Only in a script's card: money as coins: [{ amount, unit, color }]. Units pp, gp, ep, sp and cp draw as platinum, gold, electrum, silver and copper coins; any other unit ("credits", "crowns") draws a plain coin, or one of color. signed: true shows amounts as changes, +14 green and −3 red. label is a small word before them ("Your purse").`,
30826
+ example: {
30827
+ label: "Your purse",
30828
+ amounts: [
30829
+ { amount: 23, unit: "gp" },
30830
+ { amount: 7, unit: "sp" }
30831
+ ]
30832
+ }
30833
+ },
30834
+ Versus: {
30835
+ props: object({
30836
+ left: object({}).passthrough(),
30837
+ right: object({}).passthrough(),
30838
+ winner: optional(nullable(_enum(["left", "right"]))),
30839
+ label: text,
30840
+ className
30841
+ }),
30842
+ description: `Only in a script's card: two sides head to head: a contest, an opposed check, a duel. left and right are { name, image, detail, value }: value is the score or roll shown large, detail a line under the name ("+5"). winner "left" or "right" crowns that side and dims the other; leave it out while it's undecided. label is a small line under "VS" ("best of 3").`,
30843
+ example: {
30844
+ left: { name: "Brom", detail: "+5", value: 2 },
30845
+ right: { name: "Old Hask", detail: "+4", value: 1 },
30846
+ winner: "left",
30847
+ label: "best of 3"
30848
+ }
30849
+ }
30850
+ };
30851
+ var SCRIPT_CARD_ONLY_COMPONENTS = new Set([
30852
+ "FileTiles",
30853
+ "Button",
30854
+ ...Object.keys(scriptCardComponents)
30855
+ ]);
30856
+
30623
30857
  // ../shared/src/json-render/catalog.ts
30624
30858
  var {
30625
30859
  Separator,
@@ -30679,7 +30913,7 @@ var extendedShadcnComponents = {
30679
30913
  Stack: {
30680
30914
  props: object({
30681
30915
  direction: nullable(_enum(["horizontal", "vertical"])),
30682
- gap: nullable(_enum(["sm", "md", "lg", "none"])),
30916
+ gap: nullable(_enum(["xs", "sm", "md", "lg", "none"])),
30683
30917
  align: nullable(_enum(["start", "center", "end", "stretch"])),
30684
30918
  justify: nullable(_enum(["start", "center", "end", "between", "around"])),
30685
30919
  wrap: nullable(boolean2()),
@@ -30696,7 +30930,7 @@ var extendedShadcnComponents = {
30696
30930
  Grid: {
30697
30931
  props: object({
30698
30932
  columns: nullable(number2()),
30699
- gap: nullable(_enum(["sm", "md", "lg"])),
30933
+ gap: nullable(_enum(["xs", "sm", "md", "lg"])),
30700
30934
  bgColor: nullable(string2()),
30701
30935
  bgImage: bgImageProp,
30702
30936
  bgImageFit: bgImageFitEnum,
@@ -30909,6 +31143,48 @@ var craftComponents = {
30909
31143
  bgColor: null
30910
31144
  }
30911
31145
  },
31146
+ FileTiles: {
31147
+ props: object({
31148
+ files: array(unknown()),
31149
+ size: nullable(_enum(["sm", "md", "lg"])),
31150
+ layout: nullable(_enum(["grid", "list", "row"])),
31151
+ emptyText: nullable(string2()),
31152
+ shown: nullable(number2().int().min(1)),
31153
+ moreLabel: nullable(string2()),
31154
+ moreLayout: nullable(_enum(["grid", "list", "row"])),
31155
+ className: classNameProp
31156
+ }),
31157
+ description: `Only in a script's card: a grid of the world's files, each its picture and name, opening the file on a tap in play. files lists them by id (a handle's $id, or the handle itself), or as { file, detail, badge, button } for a tile with a line under its name, a badge on its picture and a button that runs one of the script's actions ({ label, press: { action, params, confirm }, disabled, reason }). Craft fills in each name and picture after the run and leaves out any that aren't in the game. layout is how the tiles sit: "grid" (the default), "list" (a row per file, its button at the end: for comparing many, like prices) or "row" (one line that scrolls sideways, keeping the story in view). size is the tile size, "md" by default; a grid's columns follow the size and the screen. shown keeps the set to that many tiles, with a button (moreLabel, "See all" by default) that opens every tile in a window players can scroll and search, laid out as moreLayout (the card's layout by default, a grid for a row).`,
31158
+ example: {
31159
+ files: [],
31160
+ size: "md",
31161
+ layout: "grid",
31162
+ emptyText: null,
31163
+ shown: null,
31164
+ moreLabel: null,
31165
+ moreLayout: null
31166
+ }
31167
+ },
31168
+ Button: {
31169
+ props: object({
31170
+ label: string2(),
31171
+ variant: nullable(_enum(["default", "secondary", "outline", "danger", "choice"])),
31172
+ disabled: nullable(boolean2()),
31173
+ icon: optional(nullable(string2().max(16))),
31174
+ description: optional(nullable(string2())),
31175
+ meta: optional(nullable(union([string2(), number2()]))),
31176
+ selected: optional(nullable(boolean2())),
31177
+ reason: optional(nullable(string2())),
31178
+ turn: optional(nullable(string2().max(200))),
31179
+ className: classNameProp
31180
+ }),
31181
+ description: `Only in a script's card: a button that runs what its element's on.press names. One of the script's actions ({ action: "buy", params: { item: "silver-ring" } }) changes the game's files and redraws the card; setState, pushState and removeState change the card's own state. disabled (often a $cond) greys it out, and reason says why ("Need 27 gp"). variant "default" is the card's one primary action, "secondary" the rest, "danger" for something that can't be undone. variant "choice" draws it as an option row, full width: icon, label, description under it, meta at its end (a price, a tally: "5 gp", "2 votes"). Set selected (often a $cond on the card's state) when the choices are a pick among them: a check shows on the picked one. Once the pick is made and the card redraws, keep selected and set disabled on every choice: the chosen row stays bright with a check and the rest fade. icon names a card icon (sword, shield, heart, coins, flask…) or is an emoji. meta also shows after a plain Button's label. When its action starts the next turn (startsTurn on the action), the press is sent as the player's turn in the Button's label, or in turn when set: words that read as what the player does, for a Confirm whose label says nothing of the pick (often a $template on the card's state).`,
31182
+ example: {
31183
+ label: "Buy · 15 gp",
31184
+ variant: "secondary",
31185
+ disabled: null
31186
+ }
31187
+ },
30912
31188
  ReferenceValue: {
30913
31189
  props: object({
30914
31190
  source: unknown(),
@@ -31461,7 +31737,8 @@ var craftCatalog = defineCatalog(schema, {
31461
31737
  Avatar: withClassNameProp(Avatar),
31462
31738
  Alert: withClassNameProp(Alert),
31463
31739
  ...extendedShadcnComponents,
31464
- ...craftComponents
31740
+ ...craftComponents,
31741
+ ...scriptCardComponents
31465
31742
  },
31466
31743
  actions: {}
31467
31744
  });
@@ -31887,6 +32164,14 @@ function getCatalogPropSchemas(componentType) {
31887
32164
  }
31888
32165
  return props.shape;
31889
32166
  }
32167
+ function catalogPropValueProblem(componentType, propKey, value) {
32168
+ const fieldSchema = getCatalogPropSchemas(componentType)?.[propKey];
32169
+ if (!isPropFieldSchema(fieldSchema)) {
32170
+ return null;
32171
+ }
32172
+ const parsed = fieldSchema.safeParse(value);
32173
+ return parsed.success ? null : parsed.error?.issues?.[0]?.message ?? "does not match the schema";
32174
+ }
31890
32175
  function isCatalogComponentType(componentType) {
31891
32176
  return Object.hasOwn(craftCatalog.data.components, componentType);
31892
32177
  }
@@ -32027,6 +32312,9 @@ function collectJsonRenderLayoutCatalogIssues(input) {
32027
32312
  for (const [key, element] of Object.entries(parsed.elements)) {
32028
32313
  if (isRecord10(element)) {
32029
32314
  collectElementCatalogIssues(key, element, issues);
32315
+ if (typeof element.type === "string" && SCRIPT_CARD_ONLY_COMPONENTS.has(element.type)) {
32316
+ issues.push(`Element "${key}" is a ${element.type}, which only a script's card can use.`);
32317
+ }
32030
32318
  }
32031
32319
  }
32032
32320
  for (const [sectionId, section] of Object.entries(layoutSectionsFromSpec(parsed))) {
@@ -32185,10 +32473,14 @@ function findScriptFileType(types, key) {
32185
32473
  var SCRIPT_SOURCE_MAX_LENGTH = 64 * 1024;
32186
32474
  var SCRIPT_DESCRIPTION_MAX_LENGTH = 1000;
32187
32475
  var SCRIPT_WHEN_MAX_LENGTH = 500;
32476
+ var SCRIPT_AFTER_MAX_LENGTH = 500;
32188
32477
  var SCRIPT_MEMORY_LIMIT_BYTES = 16 * 1024 * 1024;
32189
32478
  var SCRIPT_RESULT_MAX_BYTES = 64 * 1024;
32479
+ var SCRIPT_CARD_MAX_BYTES = 16 * 1024;
32480
+ var SCRIPT_MAX_ACTIONS = 20;
32481
+ var SCRIPT_CARD_STATE_MAX_BYTES = 4 * 1024;
32190
32482
  var SCRIPT_LOG_MAX_BYTES = 16 * 1024;
32191
- var SCRIPT_MAX_PER_CONTAINER = 500;
32483
+ var SCRIPT_MAX_PER_CONTAINER = 30;
32192
32484
 
32193
32485
  // ../shared/src/scripts/name.ts
32194
32486
  var SCRIPT_FILE_SUFFIX_RE = /\.script(\.js)?$/i;
@@ -32208,6 +32500,44 @@ function scriptFunctionName(fileName) {
32208
32500
  return LEADING_DIGIT_RE.test(name) ? `_${name}` : name;
32209
32501
  }
32210
32502
 
32503
+ // ../shared/src/scripts/card.ts
32504
+ var SCRIPT_CARD_EXCLUDED_COMPONENTS = new Set([
32505
+ "Include",
32506
+ "SectionSwitch",
32507
+ "FileReferenceGrid",
32508
+ "PageReferenceGrid",
32509
+ "ReferenceValue",
32510
+ "TurnTracker",
32511
+ "Select",
32512
+ "VoiceSelect"
32513
+ ]);
32514
+ var SCRIPT_CARD_STATE_ACTIONS = new Set([
32515
+ "setState",
32516
+ "pushState",
32517
+ "removeState"
32518
+ ]);
32519
+ var SCRIPT_CARD_RESERVED_ACTIONS = new Set([
32520
+ ...SCRIPT_CARD_STATE_ACTIONS,
32521
+ "validateForm"
32522
+ ]);
32523
+ var BINDING_KEYS = new Set([
32524
+ "action",
32525
+ "params",
32526
+ "confirm",
32527
+ "onSuccess",
32528
+ "onError",
32529
+ "preventDefault"
32530
+ ]);
32531
+ var TILE_KEYS = new Set(["file", "detail", "badge", "button"]);
32532
+ var TILE_BUTTON_KEYS = new Set([
32533
+ "label",
32534
+ "press",
32535
+ "disabled",
32536
+ "reason",
32537
+ "variant"
32538
+ ]);
32539
+ var TILE_BUTTON_VARIANTS = new Set(["default", "secondary", "outline"]);
32540
+
32211
32541
  // ../shared/src/scripts/types.ts
32212
32542
  var SCRIPT_FIELD_TYPES = [
32213
32543
  "string",
@@ -32218,6 +32548,7 @@ var SCRIPT_FIELD_TYPES = [
32218
32548
  "object"
32219
32549
  ];
32220
32550
  var SCRIPT_VISIBILITIES = ["gm", "player"];
32551
+ var SCRIPT_CARD_ORDERS = ["together", "card-first"];
32221
32552
 
32222
32553
  // ../shared/src/scripts/fields.ts
32223
32554
  var FIELD_NAME_RE = /^[A-Za-z_$][\w$]*$/;
@@ -32448,10 +32779,16 @@ var BANNED_MEMBER_NAMES2 = new Set(["__proto__", "constructor", "prototype"]);
32448
32779
  var DEFINITION_KEYS = new Set([
32449
32780
  "description",
32450
32781
  "when",
32782
+ "after",
32451
32783
  "input",
32452
32784
  "run",
32453
- "visibility"
32785
+ "visibility",
32786
+ "cardOrder",
32787
+ "waitsForPlayer",
32788
+ "card",
32789
+ "actions"
32454
32790
  ]);
32791
+ var ACTION_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
32455
32792
  var ACORN_POSITION_SUFFIX_RE = /\s*\(\d+:\d+\)$/;
32456
32793
 
32457
32794
  class LiteralError extends Error {
@@ -32540,6 +32877,23 @@ function stringOption(property, key, maxLength, diagnostics) {
32540
32877
  throw error;
32541
32878
  }
32542
32879
  }
32880
+ function cardOrderOption(property, diagnostics) {
32881
+ const value = property.value.type === "Literal" ? property.value.value : undefined;
32882
+ const order = SCRIPT_CARD_ORDERS.find((option) => option === value);
32883
+ if (!order) {
32884
+ diagnostics.push(at(property.value, 'cardOrder is "together" (the card shows with what the run did on the map, the default) or "card-first" (the card shows first, then the map plays)'));
32885
+ return "together";
32886
+ }
32887
+ return order;
32888
+ }
32889
+ function booleanOption(property, key, diagnostics) {
32890
+ const value = property.value.type === "Literal" ? property.value.value : undefined;
32891
+ if (typeof value !== "boolean") {
32892
+ diagnostics.push(at(property.value, `${key} is true or false`));
32893
+ return false;
32894
+ }
32895
+ return value;
32896
+ }
32543
32897
  function visibilityOption(property, diagnostics) {
32544
32898
  const value = property.value.type === "Literal" ? property.value.value : undefined;
32545
32899
  const visibility = SCRIPT_VISIBILITIES.find((option) => option === value);
@@ -32563,14 +32917,193 @@ function checkRun(property, diagnostics) {
32563
32917
  }
32564
32918
  return { ok: true, usesGame: value.params.length >= 2 };
32565
32919
  }
32920
+ function checkCard(property, diagnostics) {
32921
+ const value = property.value;
32922
+ if (value.type !== "FunctionExpression" && value.type !== "ArrowFunctionExpression") {
32923
+ diagnostics.push(at(value, "card must be a function: card(result, input) { return { root, elements } }"));
32924
+ return false;
32925
+ }
32926
+ if (value.generator) {
32927
+ diagnostics.push(at(property, "card can't be a generator"));
32928
+ }
32929
+ if (value.params.length > 2) {
32930
+ diagnostics.push(at(value, "card takes the result and the input: card(result, input)"));
32931
+ }
32932
+ checkCardLiterals(value, diagnostics);
32933
+ return true;
32934
+ }
32935
+ function actionFields(node, name, diagnostics) {
32936
+ let value = {};
32937
+ try {
32938
+ value = literalValue(node);
32939
+ } catch (error) {
32940
+ if (!(error instanceof LiteralError)) {
32941
+ throw error;
32942
+ }
32943
+ diagnostics.push(at(error.node, `actions.${name}.input: ${error.message}`));
32944
+ }
32945
+ const { fields, errors } = readScriptFields(value);
32946
+ for (const error of errors) {
32947
+ diagnostics.push(at(node, `actions.${name}.input: ${error}`));
32948
+ }
32949
+ return { fields, inputSchema: fieldsToJsonSchema(fields) };
32950
+ }
32951
+ function checkActions(property, diagnostics) {
32952
+ const actions = {};
32953
+ if (property.value.type !== "ObjectExpression") {
32954
+ diagnostics.push(at(property.value, "actions must be an object: actions: { buy: { input: { ... }, run(input, { game, result }) { ... } } }"));
32955
+ return actions;
32956
+ }
32957
+ for (const entry of property.value.properties) {
32958
+ if (entry.type !== "Property" || entry.computed) {
32959
+ diagnostics.push(at(entry, "Write each action out by name; no ...spread"));
32960
+ continue;
32961
+ }
32962
+ const name = propertyKey(entry);
32963
+ if (name === null || !ACTION_NAME_RE.test(name)) {
32964
+ diagnostics.push(at(entry.key, "An action's name is a plain name, like buy or takeFeat"));
32965
+ continue;
32966
+ }
32967
+ if (SCRIPT_CARD_RESERVED_ACTIONS.has(name)) {
32968
+ diagnostics.push(at(entry.key, `${name} is one of the card's own actions; give this action another name`));
32969
+ continue;
32970
+ }
32971
+ if (name in actions) {
32972
+ diagnostics.push(at(entry.key, `There are two actions named ${name}`));
32973
+ continue;
32974
+ }
32975
+ if (entry.value.type !== "ObjectExpression") {
32976
+ diagnostics.push(at(entry.value, `actions.${name} must be an object: { input: { ... }, run(input, { game, result }) { ... } }`));
32977
+ continue;
32978
+ }
32979
+ let definition = {
32980
+ fields: {},
32981
+ inputSchema: fieldsToJsonSchema({})
32982
+ };
32983
+ let hasRun = false;
32984
+ let startsTurn = false;
32985
+ for (const part of entry.value.properties) {
32986
+ const key = part.type === "Property" ? propertyKey(part) : null;
32987
+ if (part.type !== "Property" || key === "input") {
32988
+ if (part.type === "Property") {
32989
+ definition = actionFields(part.value, name, diagnostics);
32990
+ } else {
32991
+ diagnostics.push(at(part, "Write the action out; no ...spread"));
32992
+ }
32993
+ continue;
32994
+ }
32995
+ if (key === "startsTurn") {
32996
+ startsTurn = booleanOption(part, `actions.${name}.startsTurn`, diagnostics);
32997
+ continue;
32998
+ }
32999
+ if (key !== "run") {
33000
+ diagnostics.push(at(part.key, `actions.${name} takes input, run and startsTurn${key ? `, not "${key}"` : ""}`));
33001
+ continue;
33002
+ }
33003
+ const value = part.value;
33004
+ if (value.type !== "FunctionExpression" && value.type !== "ArrowFunctionExpression") {
33005
+ diagnostics.push(at(value, `actions.${name}.run must be a function: run(input, { game, result }) { ... }`));
33006
+ continue;
33007
+ }
33008
+ if (value.generator) {
33009
+ diagnostics.push(at(part, `actions.${name}.run can't be a generator`));
33010
+ }
33011
+ if (value.params.length > 2) {
33012
+ diagnostics.push(at(value, `actions.${name}.run takes the input and the context: run(input, { game, result })`));
33013
+ }
33014
+ hasRun = true;
33015
+ }
33016
+ if (!hasRun) {
33017
+ diagnostics.push(at(entry, `actions.${name} needs run(input, { game, result }) { ... }`));
33018
+ }
33019
+ actions[name] = startsTurn ? { ...definition, startsTurn } : definition;
33020
+ }
33021
+ if (Object.keys(actions).length > SCRIPT_MAX_ACTIONS) {
33022
+ diagnostics.push(at(property.value, `A script can have at most ${SCRIPT_MAX_ACTIONS} actions; this one has ${Object.keys(actions).length}`));
33023
+ }
33024
+ return actions;
33025
+ }
33026
+ function propertyNamed(node, name) {
33027
+ for (const property of node.properties) {
33028
+ if (property.type === "Property" && propertyKey(property) === name) {
33029
+ return property;
33030
+ }
33031
+ }
33032
+ return null;
33033
+ }
33034
+ function writtenValues(node) {
33035
+ if (node.type === "Literal") {
33036
+ const value = node.value;
33037
+ return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" ? [[value, node]] : [];
33038
+ }
33039
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
33040
+ return [[node.quasis[0]?.value.cooked ?? "", node]];
33041
+ }
33042
+ if (node.type === "ConditionalExpression") {
33043
+ return [
33044
+ ...writtenValues(node.consequent),
33045
+ ...writtenValues(node.alternate)
33046
+ ];
33047
+ }
33048
+ if (node.type === "LogicalExpression" && (node.operator === "||" || node.operator === "??")) {
33049
+ return writtenValues(node.right);
33050
+ }
33051
+ return [];
33052
+ }
33053
+ function checkCardLiterals(card, diagnostics) {
33054
+ walk2(card, (node) => {
33055
+ if (node.type !== "ObjectExpression") {
33056
+ return;
33057
+ }
33058
+ const typeProperty = propertyNamed(node, "type");
33059
+ const props = propertyNamed(node, "props");
33060
+ if (!(typeProperty && (props || propertyNamed(node, "children")))) {
33061
+ return;
33062
+ }
33063
+ const [written] = writtenValues(typeProperty.value);
33064
+ const componentType = written?.[0];
33065
+ if (typeof componentType !== "string") {
33066
+ return;
33067
+ }
33068
+ if (SCRIPT_CARD_EXCLUDED_COMPONENTS.has(componentType)) {
33069
+ diagnostics.push(at(typeProperty.value, `A card can't use ${componentType}`));
33070
+ return;
33071
+ }
33072
+ if (!isCatalogComponentType(componentType)) {
33073
+ diagnostics.push(at(typeProperty.value, `${componentType} isn't a component; see the layout component table`));
33074
+ return;
33075
+ }
33076
+ if (props?.value.type !== "ObjectExpression") {
33077
+ return;
33078
+ }
33079
+ for (const prop of props.value.properties) {
33080
+ const propKey = prop.type === "Property" ? propertyKey(prop) : null;
33081
+ if (!(prop.type === "Property" && propKey)) {
33082
+ continue;
33083
+ }
33084
+ for (const [value, valueNode] of writtenValues(prop.value)) {
33085
+ const problem = catalogPropValueProblem(componentType, propKey, value);
33086
+ if (problem) {
33087
+ diagnostics.push(at(valueNode, `${componentType} ${propKey} can't be ${JSON.stringify(value)}: ${problem}`));
33088
+ }
33089
+ }
33090
+ }
33091
+ });
33092
+ }
32566
33093
  function readDefinition(options, diagnostics) {
32567
33094
  let description = null;
32568
33095
  let when = null;
33096
+ let after = null;
32569
33097
  let visibility = "gm";
33098
+ let cardOrder = "together";
33099
+ let waitsForPlayer = false;
32570
33100
  let fieldsValue = {};
32571
33101
  let fieldsNode = options;
32572
33102
  let hasRun = false;
32573
33103
  let usesGame = false;
33104
+ let hasCard = false;
33105
+ let actions = {};
33106
+ let actionsNode = null;
32574
33107
  for (const property of options.properties) {
32575
33108
  if (property.type !== "Property") {
32576
33109
  diagnostics.push(at(property, "Write defineScript()'s options out; no ...spread"));
@@ -32582,15 +33115,26 @@ function readDefinition(options, diagnostics) {
32582
33115
  continue;
32583
33116
  }
32584
33117
  if (key === null || !DEFINITION_KEYS.has(key)) {
32585
- diagnostics.push(at(property.key, `defineScript() takes description, when, input, run and visibility${key ? `, not "${key}"` : ""}`));
33118
+ diagnostics.push(at(property.key, `defineScript() takes description, when, after, input, run, visibility, waitsForPlayer, cardOrder, card and actions${key ? `, not "${key}"` : ""}`));
32586
33119
  continue;
32587
33120
  }
32588
33121
  if (key === "description") {
32589
33122
  description = stringOption(property, key, SCRIPT_DESCRIPTION_MAX_LENGTH, diagnostics);
32590
33123
  } else if (key === "when") {
32591
33124
  when = stringOption(property, key, SCRIPT_WHEN_MAX_LENGTH, diagnostics) || null;
33125
+ } else if (key === "after") {
33126
+ after = stringOption(property, key, SCRIPT_AFTER_MAX_LENGTH, diagnostics) || null;
32592
33127
  } else if (key === "visibility") {
32593
33128
  visibility = visibilityOption(property, diagnostics);
33129
+ } else if (key === "cardOrder") {
33130
+ cardOrder = cardOrderOption(property, diagnostics);
33131
+ } else if (key === "waitsForPlayer") {
33132
+ waitsForPlayer = booleanOption(property, key, diagnostics);
33133
+ } else if (key === "card") {
33134
+ hasCard = checkCard(property, diagnostics);
33135
+ } else if (key === "actions") {
33136
+ actions = checkActions(property, diagnostics);
33137
+ actionsNode = property.key;
32594
33138
  } else if (key === "input") {
32595
33139
  fieldsNode = property.value;
32596
33140
  try {
@@ -32614,6 +33158,9 @@ function readDefinition(options, diagnostics) {
32614
33158
  if (!hasRun) {
32615
33159
  diagnostics.push(at(options, "defineScript() needs run(input) { ... } (or run(input, { game }) to read and change files)"));
32616
33160
  }
33161
+ if (actionsNode && !(hasCard && visibility === "player")) {
33162
+ diagnostics.push(at(actionsNode, 'actions run from Buttons on the card players see: give the script card(result, input) and visibility: "player"'));
33163
+ }
32617
33164
  const { fields, errors } = readScriptFields(fieldsValue);
32618
33165
  for (const error of errors) {
32619
33166
  diagnostics.push(at(fieldsNode, error));
@@ -32624,10 +33171,15 @@ function readDefinition(options, diagnostics) {
32624
33171
  return {
32625
33172
  description,
32626
33173
  when,
33174
+ after,
32627
33175
  visibility,
33176
+ cardOrder,
33177
+ waitsForPlayer,
32628
33178
  fields,
32629
33179
  inputSchema: fieldsToJsonSchema(fields),
32630
- usesGame
33180
+ usesGame,
33181
+ hasCard,
33182
+ actions
32631
33183
  };
32632
33184
  }
32633
33185
  function isAstNode2(value) {
@@ -32800,7 +33352,7 @@ function definitionCall(declaration, diagnostics) {
32800
33352
  }
32801
33353
  const [options, ...extra] = call.arguments;
32802
33354
  if (!options || options.type !== "ObjectExpression" || extra.length > 0) {
32803
- diagnostics.push(at(call, "defineScript() takes one object: { description, when, input, run, visibility }"));
33355
+ diagnostics.push(at(call, "defineScript() takes one object: { description, when, input, run, visibility, cardOrder, card, actions }"));
32804
33356
  return null;
32805
33357
  }
32806
33358
  return options;
@@ -33824,10 +34376,89 @@ var ARRIVAL_KEYS = new Set([
33824
34376
  "details",
33825
34377
  "hidden"
33826
34378
  ]);
33827
- var STAMPS = new Set(["since", "changed"]);
34379
+ var STAMPS = new Set(["since", "changed", "sinceTurn", "changedTurn"]);
33828
34380
  // ../shared/src/scripts/input.ts
33829
34381
  var import_ajv2 = __toESM(require_ajv());
33830
34382
  var compiled = new WeakMap;
34383
+ // ../shared/src/scene-looks.ts
34384
+ var ROLL_KINDS = [
34385
+ "attack",
34386
+ "damage",
34387
+ "heal",
34388
+ "save",
34389
+ "check",
34390
+ "contest",
34391
+ "chance"
34392
+ ];
34393
+ var DELIVERIES = [
34394
+ "strike",
34395
+ "claw",
34396
+ "shot",
34397
+ "thrown",
34398
+ "bolt",
34399
+ "beam",
34400
+ "arc",
34401
+ "wave",
34402
+ "spray",
34403
+ "burst",
34404
+ "touch"
34405
+ ];
34406
+ var PROJECTILES = [
34407
+ "shaft",
34408
+ "blade",
34409
+ "ball",
34410
+ "slug",
34411
+ "missile",
34412
+ "bead",
34413
+ "line",
34414
+ "swarm"
34415
+ ];
34416
+ var ELEMENTS = [
34417
+ "physical",
34418
+ "fire",
34419
+ "cold",
34420
+ "lightning",
34421
+ "acid",
34422
+ "poison",
34423
+ "shadow",
34424
+ "light",
34425
+ "mind",
34426
+ "arcane",
34427
+ "sonic",
34428
+ "nature",
34429
+ "life"
34430
+ ];
34431
+ var APPROACHES = [
34432
+ "sneak",
34433
+ "notice",
34434
+ "search",
34435
+ "read",
34436
+ "persuade",
34437
+ "deceive",
34438
+ "intimidate",
34439
+ "perform",
34440
+ "force",
34441
+ "agility",
34442
+ "endure",
34443
+ "know",
34444
+ "tinker",
34445
+ "tend",
34446
+ "wild",
34447
+ "magic"
34448
+ ];
34449
+ var REACTIONS = [
34450
+ "alert",
34451
+ "question",
34452
+ "love",
34453
+ "anger",
34454
+ "fear",
34455
+ "joy",
34456
+ "sleep",
34457
+ "idea",
34458
+ "sad",
34459
+ "silence"
34460
+ ];
34461
+ var BLOW_KINDS = new Set(["attack", "damage", "heal"]);
33831
34462
  // ../shared/src/scripts/script-types.ts
33832
34463
  var SCRIPT_TYPES_COMPILER_OPTIONS = {
33833
34464
  target: "ES2022",
@@ -33861,6 +34492,7 @@ var HANDLE_MEMBER_NAMES = new Set([
33861
34492
  "$path",
33862
34493
  "$fileTypeSlug"
33863
34494
  ]);
34495
+ var union2 = (list) => list.map((entry) => JSON.stringify(entry)).join(" | ");
33864
34496
  var PRELUDE = `/** A reference to another file, as a reference field stores it. */
33865
34497
  interface StoredReference {
33866
34498
  readonly slug: string;
@@ -33966,6 +34598,10 @@ interface CurrentStatePerson {
33966
34598
  readonly hidden: CurrentStateHidden | null;
33967
34599
  /** An extra's own state (hp, conditions); someone with a file keeps theirs in it. */
33968
34600
  readonly details: { readonly [detail: string]: string | number | boolean };
34601
+ /** The map square(s) they stand on ("M13", "J9–K10"); null off a grid or with no token. */
34602
+ readonly square: string | null;
34603
+ /** Feet to everyone else here with a token, by name, as the map measures it; null off a map. */
34604
+ readonly distances: { readonly [name: string]: number } | null;
33969
34605
  }
33970
34606
 
33971
34607
  /** A file or person a tracker points at, by name; its file is filled in by the game. */
@@ -34118,8 +34754,75 @@ interface CurrentStateChange {
34118
34754
  /** The current state: the scene the GM keeps. */
34119
34755
  interface GameCurrentState {
34120
34756
  get(): Promise<CurrentState>;
34121
- /** Checks the change (an error says what's wrong, with error.code) and returns the current state with it. */
34122
- update(change: CurrentStateChange): Promise<CurrentState>;
34757
+ /**
34758
+ * Checks the change (an error says what's wrong, with error.code) and
34759
+ * returns the current state with it. options.show is where what it makes
34760
+ * shows (an HP change's row, someone going down): see Show.
34761
+ */
34762
+ update(change: CurrentStateChange, options?: { show?: Show }): Promise<CurrentState>;
34763
+ }
34764
+
34765
+ /**
34766
+ * Where a roll, an effect, a reaction or a current-state change shows:
34767
+ * "all" (the default) on the map and as its row in the story; "map" on the
34768
+ * map only, when the script's own card tells it in the story; "none"
34769
+ * nowhere (it still happens: the dice decide, the state changes).
34770
+ */
34771
+ type Show = "all" | "map" | "none";
34772
+
34773
+ /** How a moment looks on the map (knowledge/map-language.md). */
34774
+ interface MomentLook {
34775
+ /** How it travels from who it comes from to who it lands on. */
34776
+ delivery?: ${union2(DELIVERIES)};
34777
+ /** What the eye sees flying, for a shot, a throw or a bolt: a javelin is "shaft", a pistol "slug". */
34778
+ projectile?: ${union2(PROJECTILES)};
34779
+ element?: ${union2(ELEMENTS)};
34780
+ }
34781
+
34782
+ /** What game.roll() takes besides the dice. */
34783
+ interface RollOptions extends MomentLook {
34784
+ /** Who rolls, by their current-state name; leave out for a roll no one in the scene makes. */
34785
+ by?: string;
34786
+ /** Who it is at or for: the target, or everyone an area catches. */
34787
+ at?: string | readonly string[];
34788
+ /** What the roll is for, a few words ("Scimitar attack"). */
34789
+ reason?: string;
34790
+ /** The number it is against: 14, or { value: 14, label: "AC 14" }. */
34791
+ against?: number | { value: number; label?: string; succeedsOn?: "or_higher" | "or_lower" };
34792
+ /** What kind of roll the map shows. */
34793
+ kind?: ${union2(ROLL_KINDS)};
34794
+ approach?: ${union2(APPROACHES)};
34795
+ /** Where the roll shows: "all" (the default), "map" (no row in the story) or "none". */
34796
+ show?: Show;
34797
+ }
34798
+
34799
+ /** What game.roll() returns: the total, every face, and the kept d20 face when there is one. */
34800
+ interface RollResult {
34801
+ total: number;
34802
+ faces: number[];
34803
+ /** The kept face of the first d20, for natural 1s and 20s. */
34804
+ natural?: number;
34805
+ dice: string;
34806
+ }
34807
+
34808
+ /** What game.effect() takes: something landing on people with no roll of its own. */
34809
+ interface EffectOptions extends MomentLook {
34810
+ /** Who it comes from; leave out when it comes from the place itself. */
34811
+ from?: string;
34812
+ /** Who it lands on. */
34813
+ at: string | readonly string[];
34814
+ /** What happened, a few words for the caption ("Scimitar hits"). */
34815
+ what: string;
34816
+ /** Where it shows: "all" (the default), "map" (no row in the story) or "none". */
34817
+ show?: Show;
34818
+ }
34819
+
34820
+ /** What game.react() takes: someone visibly reacting, a bead over their head. */
34821
+ interface ReactOptions {
34822
+ who: string;
34823
+ as: ${union2(REACTIONS)};
34824
+ /** Where it shows: "all" (the default), "map" or "none". */
34825
+ show?: Show;
34123
34826
  }
34124
34827
 
34125
34828
  /** run()'s second argument. */
@@ -34132,6 +34835,34 @@ interface ScriptContext {
34132
34835
  readonly game: Game;
34133
34836
  }
34134
34837
 
34838
+ /** An action's second argument: the game, and the card's last result. */
34839
+ interface ScriptActionContext {
34840
+ /** The world's files, as run() gets them; every change saves together or none does. */
34841
+ readonly game: Game;
34842
+ /** What the card was last drawn from: run()'s result, or the last action's. Read-only. */
34843
+ readonly result: any;
34844
+ }
34845
+
34846
+ /** Something a card's Button can run: \`on: { press: { action: "buy", params: { ... } } }\`. */
34847
+ interface ScriptAction {
34848
+ /** The fields a tap passes as params, by name. */
34849
+ input?: { [field: string]: ScriptInputField };
34850
+ /**
34851
+ * Runs on a tap. Check what the tap asks against the result and the files
34852
+ * (never trust params for a price), throw to refuse with a reason the card
34853
+ * shows, and return the card's next result (or nothing to keep it).
34854
+ */
34855
+ run(input: any, context: ScriptActionContext): unknown;
34856
+ /**
34857
+ * Start the Game Master's next turn once a press works (default false),
34858
+ * with the pressed Button's label as the player's turn: for a pick-one
34859
+ * choice that moves the story on. Only while the card is the newest thing
34860
+ * in the story, and only the first such press on it. Leave it off for an
34861
+ * action players press many times, like a shop's Buy.
34862
+ */
34863
+ startsTurn?: boolean;
34864
+ }
34865
+
34135
34866
  /** One input field: a small JSON Schema. Required unless optional or given a default. */
34136
34867
  interface ScriptInputField {
34137
34868
  type: "string" | "number" | "integer" | "boolean" | "array" | "object";
@@ -34155,12 +34886,71 @@ interface ScriptDefinition {
34155
34886
  description: string;
34156
34887
  /** When the Game Master should run it. */
34157
34888
  when?: string;
34889
+ /**
34890
+ * What in the story has it run after the Game Master tells it (opt in):
34891
+ * a check after each turn asks whether the turn met it, and runs the
34892
+ * script if so. For scripts the story doesn't wait on, like advancing a
34893
+ * clock or awarding XP, never ones that decide what happens.
34894
+ */
34895
+ after?: string;
34158
34896
  /** Who sees it run in play: the GM alone (the default) or the players too. */
34159
34897
  visibility?: "gm" | "player";
34898
+ /**
34899
+ * Where the card players see goes beside what the run did on the map:
34900
+ * "together" (the default) shows it with that moment, its result as the
34901
+ * blow lands; "card-first" shows the card first, then the map plays (a
34902
+ * table roll, then the bandits it called up stepping out).
34903
+ */
34904
+ cardOrder?: "together" | "card-first";
34905
+ /**
34906
+ * End the Game Master's turn as soon as this script has run (default
34907
+ * false): the GM writes nothing after it, and the player answers next.
34908
+ * For scripts that wait on the player, like a shop whose card they buy from.
34909
+ */
34910
+ waitsForPlayer?: boolean;
34160
34911
  /** The input fields, by name. */
34161
34912
  input?: { [field: string]: ScriptInputField };
34162
34913
  /** Runs the script. May be async; return plain JSON. */
34163
34914
  run(input: any, context: ScriptContext): unknown;
34915
+ /**
34916
+ * The script's own card: what players see where it ran (with visibility
34917
+ * "player") and the editor's test run shows. Gets run()'s result and the
34918
+ * input; returns a json-render spec, { root, elements }, whose props can
34919
+ * bind { "$state": "/result/..." }. Return null for the default card.
34920
+ */
34921
+ card?(result: any, input: any): ScriptCardSpec | null | undefined;
34922
+ /**
34923
+ * What the card's Buttons can run, by name. Each changes the game's files
34924
+ * when a player taps it and returns the card's next result; the card is
34925
+ * drawn again from it, for every player. Needs card and visibility "player".
34926
+ */
34927
+ actions?: { [name: string]: ScriptAction };
34928
+ }
34929
+
34930
+ /** What a press runs: one of the script's actions, or a change to the card's own state. */
34931
+ interface ScriptCardBinding {
34932
+ action: string;
34933
+ params?: { [param: string]: unknown };
34934
+ confirm?: { title: string; message: string; confirmLabel?: string; cancelLabel?: string; variant?: "default" | "danger" };
34935
+ onSuccess?: { set: { [statePath: string]: unknown } };
34936
+ onError?: { set: { [statePath: string]: unknown } };
34937
+ }
34938
+
34939
+ /** A json-render spec: the root element's key and every element by key. */
34940
+ interface ScriptCardSpec {
34941
+ root: string;
34942
+ elements: {
34943
+ [key: string]: {
34944
+ type: string;
34945
+ props?: { [prop: string]: unknown };
34946
+ children?: string[];
34947
+ /** What a tap runs: { press: { action: "buy", params: { item: "rope" } } }. */
34948
+ on?: { press: ScriptCardBinding | ScriptCardBinding[] };
34949
+ [key: string]: unknown;
34950
+ };
34951
+ };
34952
+ /** The card's own state as it starts, for picks a Button then confirms (not /result or /viewer). */
34953
+ state?: { [key: string]: unknown };
34164
34954
  }
34165
34955
 
34166
34956
  declare function defineScript(definition: ScriptDefinition): ScriptDefinition;
@@ -34564,6 +35354,34 @@ function renderGame(declared) {
34564
35354
  " * the way somewhere, at a place with no file, or outside a game.",
34565
35355
  " */",
34566
35356
  " currentLocation(options?: { include?: true | readonly string[] }): Promise<AnyFile | null>;",
35357
+ " /**",
35358
+ " * The character of whoever caused this run: who tapped the card's Button,",
35359
+ " * or whose message started the GM's turn. Null with no one (a test run",
35360
+ " * with no character picked), so check it.",
35361
+ " */",
35362
+ " player(options?: { include?: true | readonly string[] }): Promise<AnyFile | null>;",
35363
+ " /**",
35364
+ " * Rolls dice and shows the roll on the map and in the story, with its",
35365
+ ' * dice card, where the script made it: game.roll("1d20+5", { by: "Goblin 1",',
35366
+ ' * at: "Riven", reason: "Scimitar attack", against: { value: 14, label: "AC 14" } }).',
35367
+ " */",
35368
+ " roll(dice: string, options?: RollOptions): Promise<RollResult>;",
35369
+ " /**",
35370
+ " * Asks the player to roll for their own character, and answers with their",
35371
+ ' * dice as roll() does: game.askPlayerToRoll("1d20+5", { by: "Riven",',
35372
+ ' * at: "Goblin 1", reason: "Longsword attack", against: { value: 13, label: "AC 13" } }).',
35373
+ " * In a game the run stops here and the GM's reply ends with the dice in",
35374
+ " * front of the player; once they roll, the run starts over with the same",
35375
+ " * input, its earlier roll() calls give back the same dice, and this call",
35376
+ " * answers with the player's roll. Nothing the run did before it is kept",
35377
+ " * until then. Let a WAITING_FOR_PLAYER error through a catch. Where no",
35378
+ " * player is asked (a test run, the assistant) the dice are rolled for them.",
35379
+ " */",
35380
+ " askPlayerToRoll(dice: string, options?: RollOptions): Promise<RollResult>;",
35381
+ " /** Shows something landing on people with no roll: an auto-hit, a hazard, a spell's effect. */",
35382
+ " effect(options: EffectOptions): Promise<void>;",
35383
+ " /** Shows someone reacting: a bead over their head. */",
35384
+ " react(options: ReactOptions): Promise<void>;",
34567
35385
  " /** The world's file types. */",
34568
35386
  " fileTypes(): FileTypes;",
34569
35387
  " /**",
@@ -34920,7 +35738,7 @@ function coerceOptionalString(value) {
34920
35738
  return typeof value === "string" ? value : String(value);
34921
35739
  }
34922
35740
 
34923
- // ../../node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/index.js
35741
+ // ../../node_modules/.bun/yaml@2.8.4/node_modules/yaml/dist/index.js
34924
35742
  var composer = require_composer();
34925
35743
  var Document = require_Document();
34926
35744
  var Schema = require_Schema();
@@ -37616,7 +38434,7 @@ async function refreshWorkspaceScriptTypes({
37616
38434
  // package.json
37617
38435
  var package_default = {
37618
38436
  name: "@craftrpgs/cli",
37619
- version: "0.2.1",
38437
+ version: "0.2.2-alpha.0",
37620
38438
  description: "Sync Craft projects with a local folder — clone, edit with any coding agent, push back.",
37621
38439
  license: "UNLICENSED",
37622
38440
  homepage: "https://craftrpgs.com",
@@ -38695,7 +39513,7 @@ async function promptForBase({
38695
39513
  rl.close();
38696
39514
  }
38697
39515
  }
38698
- // ../../node_modules/.bun/uuid@11.1.0/node_modules/uuid/dist/esm/rng.js
39516
+ // ../../node_modules/.bun/uuid@11.1.1/node_modules/uuid/dist/esm/rng.js
38699
39517
  import { randomFillSync } from "crypto";
38700
39518
  var rnds8Pool = new Uint8Array(256);
38701
39519
  var poolPtr = rnds8Pool.length;
@@ -38707,7 +39525,7 @@ function rng() {
38707
39525
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
38708
39526
  }
38709
39527
 
38710
- // ../../node_modules/.bun/uuid@11.1.0/node_modules/uuid/dist/esm/stringify.js
39528
+ // ../../node_modules/.bun/uuid@11.1.1/node_modules/uuid/dist/esm/stringify.js
38711
39529
  var byteToHex = [];
38712
39530
  for (let i = 0;i < 256; ++i) {
38713
39531
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -38716,7 +39534,7 @@ function unsafeStringify(arr, offset = 0) {
38716
39534
  return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
38717
39535
  }
38718
39536
 
38719
- // ../../node_modules/.bun/uuid@11.1.0/node_modules/uuid/dist/esm/v7.js
39537
+ // ../../node_modules/.bun/uuid@11.1.1/node_modules/uuid/dist/esm/v7.js
38720
39538
  var _state = {};
38721
39539
  function v7(options, buf, offset) {
38722
39540
  let bytes;
@@ -40888,7 +41706,7 @@ var MAP_USAGE = `Usage: craft map get <location-path> [--json]
40888
41706
  ` + ` craft map set <location-path> --file <map.json>
40889
41707
  ` + `
40890
41708
  ` + "The map JSON is the stored MapData shape; in `set` input, areas/points may\n" + 'use `"link": "<workspace path | slug.type | referenceId>"` instead of\n' + `fileReferenceId, and element ids are minted when omitted. An element's
40891
- ` + '`exit` (a door, stairs, a road to another location) takes `"to"` as a\n' + "workspace path too; no `to` is the way out to the parent map. `get` prints\n" + `links and exit targets as workspace paths. Read
41709
+ ` + '`exit` (a door, stairs, a road to another location) takes `"to"` as a\n' + "workspace path too; no `to` is the way out to the parent map, and\n" + "`arriveAt` is the id of the pin or area it comes out at on that map. `get` prints\n" + `links and exit targets as workspace paths. Read
40892
41710
  ` + `.craft/instructions/authoring-maps.md for the format and
40893
41711
  ` + ".craft/instructions/linking-locations.md for exits.";
40894
41712
  async function runMap(argv) {
@@ -41954,6 +42772,12 @@ function collectAgentFileMentionAliases(markdown) {
41954
42772
  function stripDiacritics(value) {
41955
42773
  return value.normalize("NFKD").replace(/[\u0300-\u036f]/g, "");
41956
42774
  }
42775
+ function matchesOutsideMarkdownCode(markdown, regex) {
42776
+ return collectRegexMatchesOutsideProtectedRanges(markdown, regex);
42777
+ }
42778
+ function replaceOutsideMarkdownCode(markdown, regex, replace) {
42779
+ return replaceRegexMatchesOutsideProtectedRanges(markdown, regex, replace);
42780
+ }
41957
42781
  function collectRegexMatchesOutsideProtectedRanges(markdown, regex) {
41958
42782
  const protectedRanges = collectMarkdownProtectedRanges(markdown);
41959
42783
  const matches = [];
@@ -41969,6 +42793,26 @@ function collectRegexMatchesOutsideProtectedRanges(markdown, regex) {
41969
42793
  }
41970
42794
  return matches;
41971
42795
  }
42796
+ function replaceRegexMatchesOutsideProtectedRanges(markdown, regex, replace) {
42797
+ const protectedRanges = collectMarkdownProtectedRanges(markdown);
42798
+ let output = "";
42799
+ let lastIndex = 0;
42800
+ regex.lastIndex = 0;
42801
+ let match = regex.exec(markdown);
42802
+ while (match !== null) {
42803
+ const start = match.index;
42804
+ const end = start + match[0].length;
42805
+ if (rangeOverlapsAny({ start, end }, protectedRanges)) {
42806
+ match = regex.exec(markdown);
42807
+ continue;
42808
+ }
42809
+ output += markdown.slice(lastIndex, start);
42810
+ output += replace(match);
42811
+ lastIndex = end;
42812
+ match = regex.exec(markdown);
42813
+ }
42814
+ return output + markdown.slice(lastIndex);
42815
+ }
41972
42816
  function collectMarkdownProtectedRanges(markdown) {
41973
42817
  const ranges = [];
41974
42818
  collectFencedCodeRanges(markdown, ranges);
@@ -46375,10 +47219,63 @@ function stripGmPostprocessingMarkers(text) {
46375
47219
  stripped += text.slice(keepFrom);
46376
47220
  return stripped.replace(TRANSLATED_POSTPROCESSING_MARKER_RE, "");
46377
47221
  }
47222
+ var HIDDEN_TAG_NAMES = "hidden-note|note-to-self|[a-z]+-hidden|hidden-[a-z]+|askPlayerToRoll|gmRollDice|askUserQuestion";
47223
+ var HIDDEN_NOTE_TAG_RE = new RegExp(`<(\\/?)(${HIDDEN_TAG_NAMES})>`, "gi");
47224
+ var HIDDEN_NOTE_OPEN_RE = new RegExp(`<(?:${HIDDEN_TAG_NAMES})>`, "i");
47225
+ var HIDDEN_NOTE_EDGE_TAGS_RE = new RegExp(`^<(?:${HIDDEN_TAG_NAMES})>|<\\/(?:${HIDDEN_TAG_NAMES})>$`, "gi");
47226
+
47227
+ // ../shared/src/gm-narration/narration-links.ts
47228
+ var NARRATION_LINK_RE = /!?\[([^[\]\n]{0,200}(?:\n[^[\]\n]{0,200})?)\]\([ \t]{0,16}(?:\n[ \t]{0,16})?(?:<([^<>\n]{0,256})>|([^\s()<>]{0,256}))(?:[ \t\n]{1,16}(?:"[^"\n]{0,200}"|'[^'\n]{0,200}'|\([^()\n]{0,200}\)))?[ \t]{0,16}(?:\n[ \t]{0,16})?\)/gu;
47229
+ var UNFINISHED_LINK_RE = /!?\[(?=[^[]*$)([^[\]\n]{0,200}(?:\n[^[\]\n]{0,200})?)(\](?:\([^)]{0,300})?)?$/gu;
47230
+ var LEFTOVER_TARGET_RE = /\]\([ \t]{0,16}<?[\p{L}\p{N}_-]{0,80}#[0-9a-fA-F]{6,8}>?(?:[ \t]{1,16}[^)\n]{0,200})?\)/gu;
47231
+ var WEB_TARGET_RE = /^https?:\/\//i;
47232
+ function narrationLinks(text, { streaming = false } = {}) {
47233
+ if (!text.includes("[")) {
47234
+ return [];
47235
+ }
47236
+ const links = matchesOutsideMarkdownCode(text, NARRATION_LINK_RE).map((match) => ({
47237
+ words: match[1] ?? "",
47238
+ target: (match[2] ?? match[3] ?? "").trim(),
47239
+ image: match[0].startsWith("!"),
47240
+ start: match.index,
47241
+ end: match.index + match[0].length
47242
+ }));
47243
+ const [tail] = matchesOutsideMarkdownCode(text, UNFINISHED_LINK_RE);
47244
+ if (tail && tail.index >= (links.at(-1)?.end ?? 0) && (tail[2] !== "]" || streaming)) {
47245
+ links.push({
47246
+ words: tail[1] ?? "",
47247
+ target: null,
47248
+ image: tail[0].startsWith("!"),
47249
+ start: tail.index,
47250
+ end: text.length
47251
+ });
47252
+ }
47253
+ return links;
47254
+ }
47255
+ function drawnAsWords(link) {
47256
+ return link.image || link.target === null || !WEB_TARGET_RE.test(link.target);
47257
+ }
47258
+ function narrationLinkWords(text) {
47259
+ let words = text;
47260
+ const links = narrationLinks(text).filter(drawnAsWords);
47261
+ if (links.length > 0) {
47262
+ words = "";
47263
+ let at = 0;
47264
+ for (const link of links) {
47265
+ words += text.slice(at, link.start) + link.words;
47266
+ at = link.end;
47267
+ }
47268
+ words += text.slice(at);
47269
+ }
47270
+ return words.includes("](") ? replaceOutsideMarkdownCode(words, LEFTOVER_TARGET_RE, () => "]") : words;
47271
+ }
47272
+ var NO_LINKS = new Map;
47273
+ var linksByMessage = new WeakMap;
46378
47274
 
46379
47275
  // ../shared/src/tts/text.ts
46380
47276
  function cleanTextForTTS(text) {
46381
- return stripGmPostprocessingMarkers(text).replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, "$1").replace(/!\[.*?\]\(.*?\)/g, "").replace(/\[([^\]]+)\]\(.*?\)/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/(\*{3}|_{3})(.+?)\1/g, "$2").replace(/(\*{2}|_{2})(.+?)\1/g, "$2").replace(/(?<!\w)(\*|_)(.+?)\1(?!\w)/g, "$2").replace(/~~(.+?)~~/g, "$1").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}\s*$/gm, "").replace(/^[\t ]*[-*+]\s+/gm, "").replace(/^[\t ]*\d+\.\s+/gm, "").replace(/<[^>]+>/g, "").replace(/\n{2,}/g, " ").replace(/\n/g, " ").replace(/ {2,}/g, " ").trim();
47277
+ const withoutPictures = stripGmPostprocessingMarkers(text).replace(/```[\s\S]*?```/g, "").replace(/`([^`]+)`/g, "$1").replace(/!\[.*?\]\(.*?\)/g, "");
47278
+ return narrationLinkWords(withoutPictures).replace(/\[([^\]]+)\]\(.*?\)/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/(\*{3}|_{3})(.+?)\1/g, "$2").replace(/(\*{2}|_{2})(.+?)\1/g, "$2").replace(/(?<!\w)(\*|_)(.+?)\1(?!\w)/g, "$2").replace(/~~(.+?)~~/g, "$1").replace(/^>\s+/gm, "").replace(/^[-*_]{3,}\s*$/gm, "").replace(/^[\t ]*[-*+]\s+/gm, "").replace(/^[\t ]*\d+\.\s+/gm, "").replace(/<[^>]+>/g, "").replace(/\n{2,}/g, " ").replace(/\n/g, " ").replace(/ {2,}/g, " ").trim();
46382
47279
  }
46383
47280
  function hashTtsSegmentText(text) {
46384
47281
  let hash = 2166136261;