@graview/core 0.1.43 → 0.1.44

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 (79) hide show
  1. package/README.md +4 -2
  2. package/dist/app-places.d.ts +19 -5
  3. package/dist/app-places.d.ts.map +1 -1
  4. package/dist/app-places.js +78 -18
  5. package/dist/app-places.js.map +1 -1
  6. package/dist/archived-seen.d.ts +64 -0
  7. package/dist/archived-seen.d.ts.map +1 -0
  8. package/dist/archived-seen.js +104 -0
  9. package/dist/archived-seen.js.map +1 -0
  10. package/dist/capabilities.js +1 -1
  11. package/dist/capabilities.js.map +1 -1
  12. package/dist/check.d.ts +1 -1
  13. package/dist/check.d.ts.map +1 -1
  14. package/dist/cli/check/context.d.ts +3 -0
  15. package/dist/cli/check/context.d.ts.map +1 -1
  16. package/dist/cli/check/context.js.map +1 -1
  17. package/dist/cli/check/lenses.js +1 -1
  18. package/dist/cli/check/lenses.js.map +1 -1
  19. package/dist/cli/check.d.ts +7 -1
  20. package/dist/cli/check.d.ts.map +1 -1
  21. package/dist/cli/check.js +2 -9
  22. package/dist/cli/check.js.map +1 -1
  23. package/dist/document/compile-checked.js +1 -1
  24. package/dist/document/compile-checked.js.map +1 -1
  25. package/dist/document/compile.d.ts +7 -0
  26. package/dist/document/compile.d.ts.map +1 -1
  27. package/dist/document/compile.js.map +1 -1
  28. package/dist/document/diff.js +7 -5
  29. package/dist/document/diff.js.map +1 -1
  30. package/dist/document/edit.d.ts +8 -0
  31. package/dist/document/edit.d.ts.map +1 -1
  32. package/dist/document/edit.js +125 -26
  33. package/dist/document/edit.js.map +1 -1
  34. package/dist/document/index.d.ts +2 -0
  35. package/dist/document/index.d.ts.map +1 -1
  36. package/dist/document/index.js +1 -0
  37. package/dist/document/index.js.map +1 -1
  38. package/dist/document/logo.d.ts +16 -0
  39. package/dist/document/logo.d.ts.map +1 -1
  40. package/dist/document/logo.js +19 -0
  41. package/dist/document/logo.js.map +1 -1
  42. package/dist/document.schema.json +2 -2
  43. package/dist/index.d.ts +3 -3
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/ops/log.d.ts +23 -0
  48. package/dist/ops/log.d.ts.map +1 -1
  49. package/dist/ops/log.js.map +1 -1
  50. package/dist/ops/writers.d.ts +21 -3
  51. package/dist/ops/writers.d.ts.map +1 -1
  52. package/dist/ops/writers.js +34 -2
  53. package/dist/ops/writers.js.map +1 -1
  54. package/dist/permissions/sight.d.ts +28 -8
  55. package/dist/permissions/sight.d.ts.map +1 -1
  56. package/dist/permissions/sight.js +37 -20
  57. package/dist/permissions/sight.js.map +1 -1
  58. package/dist/places.d.ts +17 -0
  59. package/dist/places.d.ts.map +1 -1
  60. package/dist/places.js +34 -3
  61. package/dist/places.js.map +1 -1
  62. package/dist/seat.d.ts +4 -1
  63. package/dist/seat.d.ts.map +1 -1
  64. package/dist/seat.js +4 -1
  65. package/dist/seat.js.map +1 -1
  66. package/dist/seen.d.ts.map +1 -1
  67. package/dist/seen.js +27 -4
  68. package/dist/seen.js.map +1 -1
  69. package/dist/store.d.ts +5 -2
  70. package/dist/store.d.ts.map +1 -1
  71. package/dist/store.js +20 -7
  72. package/dist/store.js.map +1 -1
  73. package/dist/version.d.ts +1 -1
  74. package/dist/version.js +1 -1
  75. package/dist/views/types.d.ts +34 -0
  76. package/dist/views/types.d.ts.map +1 -1
  77. package/dist/views/types.js +2 -0
  78. package/dist/views/types.js.map +1 -1
  79. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { type Finding } from "./findings.js";
2
2
  import { type GraviewDocument } from "./schema.js";
3
+ import { type HostView } from "../views/types.js";
3
4
  export interface Fill {
4
5
  readonly kind: string;
5
6
  readonly field: string;
@@ -21,6 +22,13 @@ export type EditOutcome = {
21
22
  export interface EditOptions {
22
23
  /** Web fonts the host serves itself, beyond `DOCUMENT_FONTS`: what `compileDocument(…, { fonts })` is told, so an edit takes what a compile would. */
23
24
  readonly fonts?: readonly string[];
25
+ /**
26
+ * The views the host keeps (`HostView`; a script view's manifest is one):
27
+ * what `arrange-pages` may name in `order`, `first` and `primary` — by
28
+ * its manifest name or its title, written as `view:<name>`. Unsaid, only
29
+ * `view:<name>` names one, taken on the host's word.
30
+ */
31
+ readonly views?: readonly HostView[];
24
32
  }
25
33
  /** The ops, for tool schemas and the guide. */
26
34
  export declare const EDIT_OPS: readonly ["add-kind", "rename-kind", "remove-kind", "add-field", "rename-field", "retype-field", "set-options", "set-range", "set-format", "set-required", "set-default", "set-repeats", "remove-field", "add-relation", "rename-relation", "remove-relation", "add-act", "remove-act", "add-rule", "remove-rule", "set-brand", "set-name", "set-description", "set-label", "set-describe", "set-view", "set-glance", "set-page-fields", "add-lens", "remove-lens", "set-lens-hidden", "set-home", "arrange-pages", "set-computed", "set-people", "set-archives", "set-tidying", "add-grant", "remove-grant", "add-sight", "remove-sight"];
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/document/edit.ts"],"names":[],"mappings":"AAMA,OAAO,EAAS,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAcpD,OAAO,EAaL,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AA0CrB,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAA;CAAE,GAC5H;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;CAAE,CAAC;AAElE,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,sJAAsJ;IACtJ,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,+CAA+C;AAC/C,eAAO,MAAM,QAAQ,4mBA0CX,CAAC;AACX,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,uFAAuF;AACvF,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAisFzF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,OAAO,GAAE,WAAgB,GAAG,WAAW,CAqCzH;AAED,0HAA0H;AAC1H,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;KAAE,CAAC;CACrI;AAED,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC7F;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,IAAI,EAAE;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,CAiBjL"}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/document/edit.ts"],"names":[],"mappings":"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"}
@@ -17,8 +17,8 @@ import { farEnd } from "./far-end.js";
17
17
  import { linkedRelationFindings } from "./linked-relation-findings.js";
18
18
  import { linkedRelationOfSpec, reachesAnotherApp } from "../schema/linked.js";
19
19
  import { ActSpec, EDGE_NAME, EdgeSpec, FIELD_NAME, FIELD_TYPES, FieldSpec, GrantSpec, KindSpec, NAME, RuleSpec, SightSpec, } from "./schema.js";
20
- import { SHIPPED_LENSES, isShippedLens, SHIPPED_LENS_NAMES, unknownLensOption } from "../places.js";
21
- import { placeSlug } from "../views/types.js";
20
+ import { SHIPPED_LENSES, isShippedLens, SHIPPED_LENS_NAMES, unknownLensOption, viewReference } from "../places.js";
21
+ import { placeSlug, VIEW_REFERENCE } from "../views/types.js";
22
22
  import { documentListFields, kindListFindings, kindListWords, listGroup, listSort, sortTypeOfDocumentField } from "../kind-list.js";
23
23
  import { computedOf, validateComputed } from "./computed.js";
24
24
  import { parseTemplate, templateBraces, TemplateError } from "./template-parse.js";
@@ -183,7 +183,8 @@ function buildShapes() {
183
183
  "arrange-pages": z
184
184
  .object({
185
185
  op: z.literal("arrange-pages"),
186
- order: z.union([z.array(kindName).max(40), z.null()]).optional(),
186
+ // A kind, or a view the host keeps: `view:<its name>`, or its title where the host says its views.
187
+ order: z.union([z.array(z.string().min(1).max(80)).max(40), z.null()]).optional(),
187
188
  hide: z.union([z.array(kindName).max(40), z.null()]).optional(),
188
189
  first: z.union([z.string().min(1).max(80), z.null()]).optional(),
189
190
  primary: z.union([z.array(z.string().min(1).max(80)).max(40), z.null()]).optional(),
@@ -1152,6 +1153,10 @@ class Editor {
1152
1153
  * the page list or the home's gallery. A lens a custom home links to.
1153
1154
  */
1154
1155
  setLensHidden(i, e) {
1156
+ // A KIND'S LIST, where no lens has the title: kept off the bar's places and the front page (`pages.hide`), still at its address.
1157
+ const kind = e.on === undefined ? this.listNamed(e.title) : undefined;
1158
+ if (kind !== undefined)
1159
+ return this.setListHidden(i, kind, e.hidden);
1155
1160
  const index = this.lensIndex(i, e.title, e.on);
1156
1161
  if (index < 0)
1157
1162
  return;
@@ -1171,6 +1176,37 @@ class Editor {
1171
1176
  else
1172
1177
  this.said.push(e.hidden ? `The lens "${title}" is kept off the bar and the page list; a link or navigate still reaches it.` : `The lens "${title}" is listed on the bar and the page list again.`);
1173
1178
  }
1179
+ /** The kind whose list a title names — the kind's name or its plural — where no lens is called that. */
1180
+ listNamed(title) {
1181
+ const lenses = this.doc.lenses ?? [];
1182
+ if (lenses.some((lens) => isObject(lens) && typeof lens["title"] === "string" && placeSlug(lens["title"]) === placeSlug(title)))
1183
+ return undefined;
1184
+ return Object.keys(this.doc.kinds).find((kind) => kind === title || placeSlug(pluralWords(this.doc, kind)) === placeSlug(title));
1185
+ }
1186
+ /** A kind's list kept off the bar's places and the front page, or put back on them. */
1187
+ setListHidden(i, kind, hidden) {
1188
+ const plural = pluralWords(this.doc, kind);
1189
+ const primary = Array.isArray(this.doc.pages?.primary) ? this.doc.pages.primary : [];
1190
+ if (hidden && primary.some((word) => word === kind || placeSlug(word) === placeSlug(plural))) {
1191
+ return this.fail(i, "hidden", `pages.primary stands the list of ${plural} on the bar, so it cannot be kept off the lists`, 'take it out of "primary" with arrange-pages first');
1192
+ }
1193
+ const pages = (this.doc.pages ??= {});
1194
+ const was = Array.isArray(pages.hide) ? pages.hide : [];
1195
+ if (was.includes(kind) === hidden) {
1196
+ if (Object.keys(pages).length === 0)
1197
+ delete this.doc.pages;
1198
+ this.said.push(hidden ? `The list of ${plural} was already kept off the lists.` : `The list of ${plural} was already listed.`);
1199
+ return;
1200
+ }
1201
+ const hide = hidden ? [...was, kind] : was.filter((one) => one !== kind);
1202
+ if (hide.length > 0)
1203
+ pages.hide = hide;
1204
+ else
1205
+ delete pages.hide;
1206
+ if (Object.keys(pages).length === 0)
1207
+ delete this.doc.pages;
1208
+ this.said.push(hidden ? `The list of ${plural} is kept off the bar's places and the front page; its address and the links to it still reach it.` : `The list of ${plural} is listed on the bar's places and the front page again.`);
1209
+ }
1174
1210
  removeLens(i, e) {
1175
1211
  const index = this.lensIndex(i, e.title, e.on);
1176
1212
  if (index < 0)
@@ -1205,24 +1241,48 @@ class Editor {
1205
1241
  return this.fail(i, `filters.${kind}.${n}`, `${kind} cannot be filtered by "${key}"`, `name one of ${offered.join(", ")}`);
1206
1242
  }
1207
1243
  }
1208
- for (const [n, word] of (e.primary ?? []).entries()) {
1209
- if (typeof word !== "string" || !this.opens(word) || word.trim().toLowerCase() === "home") {
1210
- const titles = (this.doc.lenses ?? []).flatMap((lens) => (isObject(lens) && typeof lens["title"] === "string" ? [`"${lens["title"]}"`] : []));
1211
- return this.fail(i, `primary.${n}`, `"${word}" is not a kind or a place to stand on the bar (Home always does)`, `name a kind (${Object.keys(this.doc.kinds).join(", ")}) or a place (${titles.join(", ") || "none is declared"})`);
1212
- }
1213
- }
1244
+ const titles = (this.doc.lenses ?? []).flatMap((lens) => (isObject(lens) && typeof lens["title"] === "string" ? [`"${lens["title"]}"`] : []));
1214
1245
  const kinds = Object.keys(this.doc.kinds);
1215
- for (const part of ["order", "hide"]) {
1216
- for (const [n, kind] of (e[part] ?? []).entries()) {
1217
- if (!kinds.includes(kind))
1218
- return this.fail(i, `${part}.${n}`, `"${kind}" is not a kind this app has; it has ${kinds.join(", ")}`);
1219
- if (e[part].indexOf(kind) !== n)
1220
- return this.fail(i, `${part}.${n}`, `"${kind}" is named twice`);
1246
+ // A host's view is named as `view:<its name>`; by its title or name too where the host says its views (`EditOptions.views`).
1247
+ const viewsSaid = this.viewsSaid();
1248
+ const primary = [];
1249
+ for (const [n, word] of (e.primary ?? []).entries()) {
1250
+ if (typeof word === "string" && this.opens(word) && word.trim().toLowerCase() !== "home") {
1251
+ primary.push(word);
1252
+ continue;
1221
1253
  }
1222
- }
1254
+ const view = typeof word === "string" ? this.viewNamed(i, `primary.${n}`, word) : undefined;
1255
+ if (view === null)
1256
+ return;
1257
+ if (view === undefined)
1258
+ return this.fail(i, `primary.${n}`, `"${word}" is not a kind or a place to stand on the bar (Home always does)`, `name a kind (${kinds.join(", ")}), a place (${titles.join(", ") || "none is declared"}) or ${viewsSaid}`);
1259
+ primary.push(view);
1260
+ }
1261
+ const order = [];
1262
+ for (const [n, word] of (e.order ?? []).entries()) {
1263
+ const view = kinds.includes(word) ? word : this.viewNamed(i, `order.${n}`, word);
1264
+ if (view === null)
1265
+ return;
1266
+ if (view === undefined)
1267
+ return this.fail(i, `order.${n}`, `"${word}" is not a kind this app has, nor a view its host keeps; it has ${kinds.join(", ")}`, `name a kind or ${viewsSaid}`);
1268
+ if (order.includes(view))
1269
+ return this.fail(i, `order.${n}`, `"${word}" is named twice`);
1270
+ order.push(view);
1271
+ }
1272
+ for (const [n, kind] of (e.hide ?? []).entries()) {
1273
+ if (!kinds.includes(kind))
1274
+ return this.fail(i, `hide.${n}`, `"${kind}" is not a kind this app has; it has ${kinds.join(", ")}`);
1275
+ if (e.hide.indexOf(kind) !== n)
1276
+ return this.fail(i, `hide.${n}`, `"${kind}" is named twice`);
1277
+ }
1278
+ let first = e.first;
1223
1279
  if (typeof e.first === "string" && !this.opens(e.first)) {
1224
- const titles = (this.doc.lenses ?? []).flatMap((lens) => (isObject(lens) && typeof lens["title"] === "string" ? [`"${lens["title"]}"`] : []));
1225
- return this.fail(i, "first", `"${e.first}" is not a place, a kind or "home"`, `name a place (${titles.join(", ") || "none is declared"}), a kind (${kinds.join(", ")}) or "home"`);
1280
+ const view = this.viewNamed(i, "first", e.first);
1281
+ if (view === null)
1282
+ return;
1283
+ if (view === undefined)
1284
+ return this.fail(i, "first", `"${e.first}" is not a place, a kind, a view its host keeps or "home"`, `name a place (${titles.join(", ") || "none is declared"}), a kind (${kinds.join(", ")}), ${viewsSaid} or "home"`);
1285
+ first = view;
1226
1286
  }
1227
1287
  const pages = (this.doc.pages ??= {});
1228
1288
  const said = [];
@@ -1232,18 +1292,19 @@ class Editor {
1232
1292
  said.push("the kinds go back to the order they were declared in");
1233
1293
  }
1234
1294
  else {
1235
- pages.order = [...e.order];
1236
- said.push(`${listOf(e.order.map((k) => pluralWords(this.doc, k)))} come first, in that order`);
1295
+ pages.order = order;
1296
+ const one = order.length === 1 && viewReference(order[0]) !== undefined;
1297
+ said.push(`${listOf(order.map((word) => (viewReference(word) !== undefined ? this.viewWords(word) : pluralWords(this.doc, word))))} ${one ? "comes first" : "come first, in that order"}`);
1237
1298
  }
1238
1299
  }
1239
1300
  if (e.hide !== undefined) {
1240
1301
  if (e.hide === null || e.hide.length === 0) {
1241
1302
  delete pages.hide;
1242
- said.push("the front page shows every kind");
1303
+ said.push("the places and the front page show every kind");
1243
1304
  }
1244
1305
  else {
1245
1306
  pages.hide = [...e.hide];
1246
- said.push(`the front page leaves off ${listOf(e.hide.map((k) => pluralWords(this.doc, k)))}`);
1307
+ said.push(`the places and the front page leave off ${listOf(e.hide.map((k) => pluralWords(this.doc, k)))}`);
1247
1308
  }
1248
1309
  }
1249
1310
  if (e.first !== undefined) {
@@ -1255,8 +1316,8 @@ class Editor {
1255
1316
  said.push("the app opens at its home");
1256
1317
  }
1257
1318
  else {
1258
- pages.first = e.first;
1259
- said.push(`the app opens on "${e.first}"`);
1319
+ pages.first = first;
1320
+ said.push(`the app opens on ${viewReference(first) !== undefined ? this.viewWords(first) : `"${first}"`}`);
1260
1321
  }
1261
1322
  }
1262
1323
  if (e.primary !== undefined) {
@@ -1265,8 +1326,8 @@ class Editor {
1265
1326
  said.push("the bar stands up the main kinds and their pictures");
1266
1327
  }
1267
1328
  else {
1268
- pages.primary = [...e.primary];
1269
- said.push(`the bar stands up ${listOf(e.primary.map((word) => `"${word}"`))} beside Home`);
1329
+ pages.primary = primary;
1330
+ said.push(`the bar stands up ${listOf(primary.map((word) => (viewReference(word) !== undefined ? this.viewWords(word) : `"${word}"`)))} beside Home`);
1270
1331
  }
1271
1332
  }
1272
1333
  // What the bar's switch calls the two faces (FR-137); the scene's address stays /places/overview.
@@ -1434,6 +1495,44 @@ class Editor {
1434
1495
  return [...keys];
1435
1496
  }
1436
1497
  /** Whether `first` names something the app can open on: home, a kind or its plural, a titled lens. */
1498
+ /**
1499
+ * A HOST'S VIEW, NAMED IN THE ARRANGEMENT: `view:<its manifest name>`,
1500
+ * or — where the host says its views (`EditOptions.views`) — its title
1501
+ * or name, written as `view:<name>`. Undefined for a word that is no
1502
+ * view; null once it is refused.
1503
+ */
1504
+ viewNamed(i, path, word) {
1505
+ const name = viewReference(word.trim());
1506
+ const views = this.options.views?.filter((view) => view.attach !== "home" && view.cardinality !== "one" && Boolean(view.title));
1507
+ if (!views) {
1508
+ if (name === undefined)
1509
+ return undefined;
1510
+ // On the host's word: drawn where it is named once the host registers it (`graview check` says so).
1511
+ this.pending.add(name);
1512
+ return VIEW_REFERENCE + name;
1513
+ }
1514
+ const view = name !== undefined ? views.find((one) => one.name === name) : (views.find((one) => one.title === word.trim() || one.name === word.trim()) ?? views.find((one) => placeSlug(one.title) === placeSlug(word)));
1515
+ if (view)
1516
+ return VIEW_REFERENCE + view.name;
1517
+ if (name === undefined)
1518
+ return undefined;
1519
+ return (this.fail(i, path, `the host keeps no view named "${name}"`, `name one of ${views.map((one) => `"${VIEW_REFERENCE}${one.name}" (${one.title})`).join(", ") || "none: the host keeps no picture of a kind"}`), null);
1520
+ }
1521
+ /** The views this edit names on the host's word, said once they are written. */
1522
+ pending = new Set();
1523
+ /** What a fix says a view is named by. */
1524
+ viewsSaid() {
1525
+ const views = this.options.views?.filter((view) => view.attach !== "home" && view.cardinality !== "one" && Boolean(view.title));
1526
+ if (!views)
1527
+ return `a view its host keeps, as "${VIEW_REFERENCE}<its name>"`;
1528
+ return views.length > 0 ? `a view its host keeps (${views.map((one) => `"${one.title}"`).join(", ")})` : "a view (its host keeps none)";
1529
+ }
1530
+ /** A host's view in a sentence: its title, where the host said it. */
1531
+ viewWords(word) {
1532
+ const name = viewReference(word);
1533
+ const title = this.options.views?.find((view) => view.name === name)?.title;
1534
+ return `the view "${title ?? name}"${this.pending.has(name) ? " (once the host registers it)" : ""}`;
1535
+ }
1437
1536
  opens(first) {
1438
1537
  const word = first.trim();
1439
1538
  if (word.toLowerCase() === "home" || word === "/")