@graview/core 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -5
- package/dist/app.d.ts +8 -8
- package/dist/app.d.ts.map +1 -1
- package/dist/arrangement.d.ts +2 -2
- package/dist/arrangement.d.ts.map +1 -1
- package/dist/arrangement.js +5 -5
- package/dist/arranging.js +3 -3
- package/dist/capabilities.js +1 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/city.d.ts +5 -5
- package/dist/city.js +3 -3
- package/dist/cli/check/beginnings.js +2 -2
- package/dist/cli/check/brand.d.ts.map +1 -1
- package/dist/cli/check/brand.js +15 -9
- package/dist/cli/check/brand.js.map +1 -1
- package/dist/cli/check/city.js +1 -1
- package/dist/cli/check/context.js +1 -1
- package/dist/cli/check/relations.js +2 -2
- package/dist/cli/check/relations.js.map +1 -1
- package/dist/cli/check/settings.js +8 -8
- package/dist/cli/check/settings.js.map +1 -1
- package/dist/cli/check/views.d.ts +1 -1
- package/dist/cli/check/views.js +1 -1
- package/dist/cli/create.d.ts +1 -1
- package/dist/cli/create.js +5 -5
- package/dist/cli/describe.d.ts +1 -1
- package/dist/cli/describe.js +2 -2
- package/dist/cli/describe.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/columns.d.ts +1 -1
- package/dist/columns.js +1 -1
- package/dist/compiled.d.ts.map +1 -1
- package/dist/compiled.js +6 -2
- package/dist/compiled.js.map +1 -1
- package/dist/conformance/fixtures.d.ts.map +1 -1
- package/dist/conformance/fixtures.js +194 -0
- package/dist/conformance/fixtures.js.map +1 -1
- package/dist/describe.d.ts +1 -1
- package/dist/describe.d.ts.map +1 -1
- package/dist/document/brand-check.d.ts +10 -0
- package/dist/document/brand-check.d.ts.map +1 -0
- package/dist/document/brand-check.js +33 -0
- package/dist/document/brand-check.js.map +1 -0
- package/dist/document/brand.d.ts +10 -0
- package/dist/document/brand.d.ts.map +1 -0
- package/dist/document/brand.js +49 -0
- package/dist/document/brand.js.map +1 -0
- package/dist/document/compile-checked.d.ts.map +1 -1
- package/dist/document/compile-checked.js +6 -2
- package/dist/document/compile-checked.js.map +1 -1
- package/dist/document/compile.d.ts +6 -0
- package/dist/document/compile.d.ts.map +1 -1
- package/dist/document/compile.js +4 -13
- package/dist/document/compile.js.map +1 -1
- package/dist/document/compiled.d.ts.map +1 -1
- package/dist/document/compiled.js +12 -4
- package/dist/document/compiled.js.map +1 -1
- package/dist/document/describe-place.d.ts +16 -0
- package/dist/document/describe-place.d.ts.map +1 -1
- package/dist/document/describe-place.js +14 -1
- package/dist/document/describe-place.js.map +1 -1
- package/dist/document/diff.d.ts +1 -1
- package/dist/document/diff.d.ts.map +1 -1
- package/dist/document/diff.js +52 -7
- package/dist/document/diff.js.map +1 -1
- package/dist/document/edit.d.ts +7 -2
- package/dist/document/edit.d.ts.map +1 -1
- package/dist/document/edit.js +188 -48
- package/dist/document/edit.js.map +1 -1
- package/dist/document/expr/analyze.d.ts +2 -2
- package/dist/document/expr/analyze.js +2 -2
- package/dist/document/expr/print.js +1 -1
- package/dist/document/index.d.ts +3 -1
- package/dist/document/index.d.ts.map +1 -1
- package/dist/document/index.js +1 -0
- package/dist/document/index.js.map +1 -1
- package/dist/document/quote.d.ts +5 -0
- package/dist/document/quote.d.ts.map +1 -0
- package/dist/document/quote.js +17 -0
- package/dist/document/quote.js.map +1 -0
- package/dist/document/respell.d.ts +20 -0
- package/dist/document/respell.d.ts.map +1 -0
- package/dist/document/respell.js +66 -0
- package/dist/document/respell.js.map +1 -0
- package/dist/document/rules.d.ts +2 -2
- package/dist/document/rules.d.ts.map +1 -1
- package/dist/document/rules.js +2 -2
- package/dist/document/rules.js.map +1 -1
- package/dist/document/schema.d.ts +47 -2
- package/dist/document/schema.d.ts.map +1 -1
- package/dist/document/schema.js +27 -3
- package/dist/document/schema.js.map +1 -1
- package/dist/document/thumbnail.d.ts +9 -0
- package/dist/document/thumbnail.d.ts.map +1 -1
- package/dist/document/thumbnail.js +74 -12
- package/dist/document/thumbnail.js.map +1 -1
- package/dist/document/to-document.js +5 -5
- package/dist/document/to-document.js.map +1 -1
- package/dist/document/upgrade.d.ts +3 -1
- package/dist/document/upgrade.d.ts.map +1 -1
- package/dist/document/upgrade.js +6 -3
- package/dist/document/upgrade.js.map +1 -1
- package/dist/graph/graph.js +3 -3
- package/dist/graph/primitives.d.ts +2 -2
- package/dist/graph/primitives.js +2 -2
- package/dist/index.d.ts +12 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/invariants/engine.js +1 -1
- package/dist/invariants/types.d.ts +3 -3
- package/dist/invariants/types.d.ts.map +1 -1
- package/dist/labels.d.ts +1 -1
- package/dist/modules.d.ts +1 -1
- package/dist/modules.js +1 -1
- package/dist/mutations/define-mutation.js +4 -4
- package/dist/mutations/define-mutation.js.map +1 -1
- package/dist/mutations/derive-edits.js +3 -3
- package/dist/mutations/node-ref.js +1 -1
- package/dist/mutations/types.d.ts +1 -1
- package/dist/mutations/words.js +3 -3
- package/dist/ops/types.d.ts +1 -1
- package/dist/ops/withheld.d.ts +3 -3
- package/dist/permissions/policy.d.ts +1 -1
- package/dist/permissions/policy.js +3 -3
- package/dist/permissions/types.d.ts +2 -2
- package/dist/places.d.ts +19 -2
- package/dist/places.d.ts.map +1 -1
- package/dist/places.js +41 -4
- package/dist/places.js.map +1 -1
- package/dist/scaffold/domain.js +3 -3
- package/dist/scaffold/index.d.ts +1 -1
- package/dist/scaffold/index.d.ts.map +1 -1
- package/dist/scaffold/names.d.ts +1 -1
- package/dist/scaffold/names.js +1 -1
- package/dist/scaffold/names.js.map +1 -1
- package/dist/scaffold/project.d.ts.map +1 -1
- package/dist/scaffold/project.js +4 -1
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scene-districts.d.ts +1 -1
- package/dist/schema/define-node.d.ts +4 -4
- package/dist/schema/define-node.d.ts.map +1 -1
- package/dist/schema/define-node.js +8 -8
- package/dist/schema/define-node.js.map +1 -1
- package/dist/schema/figures.d.ts +1 -1
- package/dist/schema/figures.js +7 -7
- package/dist/schema/figures.js.map +1 -1
- package/dist/schema/types.d.ts +1 -1
- package/dist/search.d.ts +13 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +33 -14
- package/dist/search.js.map +1 -1
- package/dist/seen.d.ts +2 -2
- package/dist/seen.js +12 -12
- package/dist/seen.js.map +1 -1
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +3 -3
- package/dist/settings.js.map +1 -1
- package/dist/store.d.ts +2 -2
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +8 -8
- package/dist/store.js.map +1 -1
- package/dist/sync/google-calendar.js +1 -1
- package/dist/sync/types.d.ts +2 -2
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/theme/accent.d.ts +37 -0
- package/dist/theme/accent.d.ts.map +1 -0
- package/dist/theme/accent.js +138 -0
- package/dist/theme/accent.js.map +1 -0
- package/dist/theme/contrast.d.ts +7 -7
- package/dist/theme/contrast.d.ts.map +1 -1
- package/dist/theme/contrast.js +14 -14
- package/dist/theme/contrast.js.map +1 -1
- package/dist/theme/derive.d.ts +4 -4
- package/dist/theme/derive.d.ts.map +1 -1
- package/dist/theme/derive.js +21 -22
- package/dist/theme/derive.js.map +1 -1
- package/dist/theme/fonts.d.ts +19 -0
- package/dist/theme/fonts.d.ts.map +1 -0
- package/dist/theme/fonts.js +74 -0
- package/dist/theme/fonts.js.map +1 -0
- package/dist/theme/identity.d.ts +83 -0
- package/dist/theme/identity.d.ts.map +1 -0
- package/dist/theme/identity.js +128 -0
- package/dist/theme/identity.js.map +1 -0
- package/dist/theme/kit.d.ts +9 -9
- package/dist/theme/kit.d.ts.map +1 -1
- package/dist/theme/kit.js +15 -15
- package/dist/theme/kit.js.map +1 -1
- package/dist/theme/layers.d.ts +2 -2
- package/dist/theme/layers.d.ts.map +1 -1
- package/dist/theme/layers.js +2 -2
- package/dist/theme/layers.js.map +1 -1
- package/dist/theme/look.d.ts +27 -7
- package/dist/theme/look.d.ts.map +1 -1
- package/dist/theme/look.js +30 -6
- package/dist/theme/look.js.map +1 -1
- package/dist/theme/marks.d.ts +28 -0
- package/dist/theme/marks.d.ts.map +1 -0
- package/dist/theme/marks.js +237 -0
- package/dist/theme/marks.js.map +1 -0
- package/dist/theme/palettes.d.ts +15 -7
- package/dist/theme/palettes.d.ts.map +1 -1
- package/dist/theme/palettes.js +61 -53
- package/dist/theme/palettes.js.map +1 -1
- package/dist/theme/types.d.ts +38 -6
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/dist/validate-graph.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/watched.js +8 -8
- package/package.json +1 -1
package/dist/document/edit.js
CHANGED
|
@@ -11,6 +11,11 @@ import { placeSlug } from "../views/types.js";
|
|
|
11
11
|
import { computedOf, validateComputed } from "./computed.js";
|
|
12
12
|
import { parseTemplate, templateBraces, TemplateError } from "./template-parse.js";
|
|
13
13
|
import { validateViews, VIEW_SLOTS } from "./views.js";
|
|
14
|
+
import { brandFindings } from "./brand-check.js";
|
|
15
|
+
import { canonicalize } from "./canonical.js";
|
|
16
|
+
import { quotedAtTheEnd } from "./quote.js";
|
|
17
|
+
import { respellDocument } from "./respell.js";
|
|
18
|
+
import { accentProblem } from "../theme/accent.js";
|
|
14
19
|
/** The ops, for tool schemas and the guide. */
|
|
15
20
|
export const EDIT_OPS = [
|
|
16
21
|
"add-kind",
|
|
@@ -32,6 +37,8 @@ export const EDIT_OPS = [
|
|
|
32
37
|
"add-rule",
|
|
33
38
|
"remove-rule",
|
|
34
39
|
"set-brand",
|
|
40
|
+
"set-name",
|
|
41
|
+
"set-description",
|
|
35
42
|
"set-label",
|
|
36
43
|
"set-describe",
|
|
37
44
|
"set-view",
|
|
@@ -74,11 +81,20 @@ const SHAPES = {
|
|
|
74
81
|
.object({
|
|
75
82
|
op: z.literal("set-brand"),
|
|
76
83
|
accent: z.union([z.string(), z.null()]).optional(),
|
|
77
|
-
name: z.string().min(1).max(60).optional(),
|
|
84
|
+
name: z.union([z.string().min(1).max(60), z.null()]).optional(),
|
|
78
85
|
currency: z.union([z.string(), z.null()]).optional(),
|
|
79
86
|
locale: z.union([z.string(), z.null()]).optional(),
|
|
87
|
+
// The rest of the brand (FR-124, FR-125): each as the document holds it, null to clear it.
|
|
88
|
+
logo: z.union([z.string().min(1), z.object({ src: z.string().min(1), alt: z.string().min(1).max(120).optional() }).strict(), z.null()]).optional(),
|
|
89
|
+
favicon: z.union([z.string().min(1), z.null()]).optional(),
|
|
90
|
+
typography: z.union([z.object({ display: z.union([z.string().min(1).max(200), z.null()]).optional(), body: z.union([z.string().min(1).max(200), z.null()]).optional(), mono: z.union([z.string().min(1).max(200), z.null()]).optional() }).strict(), z.null()]).optional(),
|
|
91
|
+
shape: z.union([z.object({ radius: z.union([z.number().min(0).max(32), z.null()]).optional(), density: z.union([z.number().min(0.75).max(1.5), z.null()]).optional() }).strict(), z.null()]).optional(),
|
|
92
|
+
accents: z.union([z.record(z.string(), z.union([z.number().min(0).max(360), z.null()])), z.null()]).optional(),
|
|
93
|
+
scheme: z.union([z.enum(["light", "dark", "auto"]), z.null()]).optional(),
|
|
80
94
|
})
|
|
81
95
|
.strict(),
|
|
96
|
+
"set-name": z.object({ op: z.literal("set-name"), name: z.string().min(1).max(80) }).strict(),
|
|
97
|
+
"set-description": z.object({ op: z.literal("set-description"), description: z.union([z.string().min(1).max(500), z.null()]) }).strict(),
|
|
82
98
|
"set-label": z.object({ op: z.literal("set-label"), kind: kindName, field: fieldName.optional(), label: z.union([z.string().min(1).max(300), z.null()]) }).strict(),
|
|
83
99
|
"set-describe": z.object({ op: z.literal("set-describe"), kind: kindName, describe: z.union([z.string().min(1).max(300), z.null()]) }).strict(),
|
|
84
100
|
// A kind's slot; the front page (`slot: "home"`, no kind); or a blocks lens's blocks, by its title (FR-84).
|
|
@@ -104,6 +120,10 @@ const SHAPES = {
|
|
|
104
120
|
order: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
105
121
|
hide: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
106
122
|
first: z.union([z.string().min(1).max(80), z.null()]).optional(),
|
|
123
|
+
scene: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
124
|
+
pages: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
125
|
+
// The scene's word as a chat or a tool written before 0.1.17 sends it (FR-132's key, RESPELLED as `scene`).
|
|
126
|
+
overview: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
107
127
|
})
|
|
108
128
|
.strict(),
|
|
109
129
|
"set-computed": z
|
|
@@ -395,6 +415,7 @@ function setTargets(act, doc) {
|
|
|
395
415
|
// ── the edits ────────────────────────────────────────────────────────────────
|
|
396
416
|
class Editor {
|
|
397
417
|
doc;
|
|
418
|
+
options;
|
|
398
419
|
said = [];
|
|
399
420
|
/** The agent tools this change renames or reshapes: a model that listed them before calls what is gone. */
|
|
400
421
|
toolsMoved = new Set();
|
|
@@ -404,8 +425,9 @@ class Editor {
|
|
|
404
425
|
kindOrigin = new Map();
|
|
405
426
|
fieldOrigin = new Map();
|
|
406
427
|
edgeOrigin = new Map();
|
|
407
|
-
constructor(doc, base) {
|
|
428
|
+
constructor(doc, base, options = {}) {
|
|
408
429
|
this.doc = doc;
|
|
430
|
+
this.options = options;
|
|
409
431
|
for (const [kind, spec] of Object.entries(base.kinds)) {
|
|
410
432
|
this.kindOrigin.set(kind, kind);
|
|
411
433
|
for (const f of Object.keys(spec.fields))
|
|
@@ -417,6 +439,128 @@ class Editor {
|
|
|
417
439
|
fail(i, path, message, fix) {
|
|
418
440
|
this.findings.push(error("edit", `edits.${i}${path ? `.${path}` : ""}`, message, fix));
|
|
419
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* THE BRAND, KEY BY KEY (FR-100, FR-124, FR-125): what an edit names is
|
|
444
|
+
* set, null clears it, and what it does not name stays as it was. Each
|
|
445
|
+
* is judged as `graview check` judges it — a mark that could act or load,
|
|
446
|
+
* a face off the list — and an accent that does not read as given is
|
|
447
|
+
* refused with the pair, the ratio and a shade that would (FR-126).
|
|
448
|
+
*/
|
|
449
|
+
setBrand(i, e) {
|
|
450
|
+
const brand = clone(this.doc.brand ?? {});
|
|
451
|
+
const at = `edits.${i}`;
|
|
452
|
+
// An edit that names nothing to change is refused, never applied with nothing said.
|
|
453
|
+
const named = Object.keys(e).filter((key) => key !== "op" && e[key] !== undefined);
|
|
454
|
+
if (named.length === 0)
|
|
455
|
+
return this.fail(i, "", "a set-brand edit names at least one key of the brand to set, or null to clear one", '{"op": "set-brand", "accent": "#0f6e5c"}');
|
|
456
|
+
const emptyPart = ["typography", "shape", "accents"].find((key) => isObject(e[key]) && Object.keys(e[key]).length === 0);
|
|
457
|
+
if (emptyPart)
|
|
458
|
+
return this.fail(i, emptyPart, `${emptyPart} names no part to set; name one, or give null to clear all of it`);
|
|
459
|
+
const judged = brandFindings({ ...(typeof e.logo === "string" || isObject(e.logo) ? { logo: e.logo } : {}), ...(typeof e.favicon === "string" ? { favicon: e.favicon } : {}), ...(isObject(e.typography) ? { typography: Object.fromEntries(Object.entries(e.typography).filter(([, v]) => typeof v === "string")) } : {}) }, { at, ...(this.options.fonts ? { fonts: this.options.fonts } : {}) });
|
|
460
|
+
if (judged.length > 0)
|
|
461
|
+
return void this.findings.push(...judged);
|
|
462
|
+
if (e.accent === null) {
|
|
463
|
+
delete brand["accent"];
|
|
464
|
+
this.said.push("The app goes back to Graview's colors.");
|
|
465
|
+
}
|
|
466
|
+
else if (e.accent !== undefined) {
|
|
467
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(e.accent))
|
|
468
|
+
return this.fail(i, "accent", 'an accent is a color like "#c2577a"');
|
|
469
|
+
const refused = accentProblem(e.accent);
|
|
470
|
+
if (refused)
|
|
471
|
+
return this.fail(i, "accent", refused.sentence, refused.suggestion ? `{"op": "set-brand", "accent": "${refused.suggestion}"}` : "pick a color of another hue");
|
|
472
|
+
brand["accent"] = e.accent;
|
|
473
|
+
this.said.push(`The app's accent color becomes ${e.accent}.`);
|
|
474
|
+
}
|
|
475
|
+
if (e.currency === null) {
|
|
476
|
+
delete brand["currency"];
|
|
477
|
+
this.said.push("Money is said with no currency.");
|
|
478
|
+
}
|
|
479
|
+
else if (e.currency !== undefined) {
|
|
480
|
+
if (!/^[A-Z]{3}$/.test(e.currency))
|
|
481
|
+
return this.fail(i, "currency", 'a currency is its three-letter code, like "USD" or "EUR"');
|
|
482
|
+
brand["currency"] = e.currency;
|
|
483
|
+
this.said.push(`Money is said in ${e.currency}.`);
|
|
484
|
+
}
|
|
485
|
+
if (e.locale === null)
|
|
486
|
+
delete brand["locale"];
|
|
487
|
+
else if (e.locale !== undefined) {
|
|
488
|
+
if (!/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(e.locale))
|
|
489
|
+
return this.fail(i, "locale", 'a locale is a language tag, like "en-US" or "de-DE"');
|
|
490
|
+
brand["locale"] = e.locale;
|
|
491
|
+
this.said.push(`Money is written for ${e.locale}.`);
|
|
492
|
+
}
|
|
493
|
+
if (e.name === null) {
|
|
494
|
+
delete brand["name"];
|
|
495
|
+
this.said.push("The wordmark says the app's name.");
|
|
496
|
+
}
|
|
497
|
+
else if (e.name !== undefined) {
|
|
498
|
+
brand["name"] = e.name;
|
|
499
|
+
this.said.push(`The app's wordmark says ${e.name}.`);
|
|
500
|
+
}
|
|
501
|
+
for (const [key, what] of [["logo", "logo"], ["favicon", "page icon"]]) {
|
|
502
|
+
if (e[key] === undefined)
|
|
503
|
+
continue;
|
|
504
|
+
const was = brand[key];
|
|
505
|
+
if (e[key] === null)
|
|
506
|
+
delete brand[key];
|
|
507
|
+
else
|
|
508
|
+
brand[key] = e[key];
|
|
509
|
+
const src = (mark) => (typeof mark === "string" ? mark : isObject(mark) ? mark["src"] : undefined);
|
|
510
|
+
const alt = (mark) => (isObject(mark) && typeof mark["alt"] === "string" ? mark["alt"] : undefined);
|
|
511
|
+
if (e[key] === null)
|
|
512
|
+
this.said.push(`The app has no ${what}.`);
|
|
513
|
+
else if (was === undefined)
|
|
514
|
+
this.said.push(`The app gets a ${what}.`);
|
|
515
|
+
else if (canonicalize(was) === canonicalize(e[key]))
|
|
516
|
+
this.said.push(`The ${what} stays as it was.`);
|
|
517
|
+
else if (src(was) === src(e[key]))
|
|
518
|
+
this.said.push(`The ${what} is said as ${quotedAtTheEnd(alt(e[key]) ?? brand["name"] ?? this.doc.name)}`);
|
|
519
|
+
else
|
|
520
|
+
this.said.push(`The ${what} changes.`);
|
|
521
|
+
}
|
|
522
|
+
const merge = (key, say, cleared) => {
|
|
523
|
+
if (e[key] === undefined)
|
|
524
|
+
return;
|
|
525
|
+
if (e[key] === null) {
|
|
526
|
+
delete brand[key];
|
|
527
|
+
this.said.push(cleared);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const into = { ...(brand[key] ?? {}) };
|
|
531
|
+
for (const [part, value] of Object.entries(e[key])) {
|
|
532
|
+
if (value === null)
|
|
533
|
+
delete into[part];
|
|
534
|
+
else
|
|
535
|
+
into[part] = value;
|
|
536
|
+
this.said.push(say(part, value));
|
|
537
|
+
}
|
|
538
|
+
if (Object.keys(into).length === 0)
|
|
539
|
+
delete brand[key];
|
|
540
|
+
else
|
|
541
|
+
brand[key] = into;
|
|
542
|
+
};
|
|
543
|
+
const ROLE = { display: "Headings", body: "Body text", mono: "Code" };
|
|
544
|
+
merge("typography", (role, value) => (value === null ? `${ROLE[role]} go${role === "display" ? "" : "es"} back to Graview's face.` : `${ROLE[role]} ${role === "display" ? "are" : "is"} now set in ${value}.`), "Words go back to Graview's faces.");
|
|
545
|
+
merge("shape", (part, value) => (part === "radius" ? (value === null ? "Corners go back to Graview's own." : `Corners are now ${value}px.`) : value === null ? "Spacing goes back to Graview's own." : `Spacing is now ${value} times Graview's own.`), "Corners and spacing go back to Graview's own.");
|
|
546
|
+
if (isObject(e.accents)) {
|
|
547
|
+
const unknown = Object.keys(e.accents).find((kind) => !this.doc.kinds[kind]);
|
|
548
|
+
if (unknown)
|
|
549
|
+
return this.fail(i, "accents", `"${unknown}" is not a kind this app has; it has ${Object.keys(this.doc.kinds).join(", ")}`);
|
|
550
|
+
}
|
|
551
|
+
merge("accents", (kind, hue) => (hue === null ? `${cap(kind)} goes back to its own hue.` : `${cap(kind)} is drawn at hue ${hue}°.`), "Every kind goes back to its own hue.");
|
|
552
|
+
if (e.scheme !== undefined) {
|
|
553
|
+
if (e.scheme === null || e.scheme === "auto")
|
|
554
|
+
delete brand["scheme"];
|
|
555
|
+
else
|
|
556
|
+
brand["scheme"] = e.scheme;
|
|
557
|
+
this.said.push(e.scheme === "light" || e.scheme === "dark" ? `The app opens ${e.scheme} when the reader has not chosen.` : "The app follows the reader's system for light and dark.");
|
|
558
|
+
}
|
|
559
|
+
if (Object.keys(brand).length === 0)
|
|
560
|
+
delete this.doc.brand;
|
|
561
|
+
else
|
|
562
|
+
this.doc.brand = brand;
|
|
563
|
+
}
|
|
420
564
|
kind(i, kind) {
|
|
421
565
|
const spec = this.doc.kinds[kind];
|
|
422
566
|
if (!spec)
|
|
@@ -532,46 +676,23 @@ class Editor {
|
|
|
532
676
|
this.said.push(`The rule "${title}" is removed.`);
|
|
533
677
|
return;
|
|
534
678
|
}
|
|
535
|
-
case "set-brand":
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
this.
|
|
547
|
-
}
|
|
548
|
-
if (e.currency === null) {
|
|
549
|
-
delete brand["currency"];
|
|
550
|
-
this.said.push("Money is said with no currency.");
|
|
551
|
-
}
|
|
552
|
-
else if (e.currency !== undefined) {
|
|
553
|
-
if (!/^[A-Z]{3}$/.test(e.currency))
|
|
554
|
-
return this.fail(i, "currency", 'a currency is its three-letter code, like "USD" or "EUR"');
|
|
555
|
-
brand["currency"] = e.currency;
|
|
556
|
-
this.said.push(`Money is said in ${e.currency}.`);
|
|
557
|
-
}
|
|
558
|
-
if (e.locale === null)
|
|
559
|
-
delete brand["locale"];
|
|
560
|
-
else if (e.locale !== undefined) {
|
|
561
|
-
if (!/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(e.locale))
|
|
562
|
-
return this.fail(i, "locale", 'a locale is a language tag, like "en-US" or "de-DE"');
|
|
563
|
-
brand["locale"] = e.locale;
|
|
564
|
-
this.said.push(`Money is written for ${e.locale}.`);
|
|
565
|
-
}
|
|
566
|
-
if (e.name) {
|
|
567
|
-
brand["name"] = e.name;
|
|
568
|
-
this.said.push(`The app's wordmark says ${e.name}.`);
|
|
569
|
-
}
|
|
570
|
-
// A brand that holds only a name holds nothing to apply.
|
|
571
|
-
if (Object.keys(brand).every((key) => key === "name"))
|
|
572
|
-
delete this.doc.brand;
|
|
679
|
+
case "set-brand":
|
|
680
|
+
return this.setBrand(i, e);
|
|
681
|
+
case "set-name": {
|
|
682
|
+
const already = this.doc.name === e.name;
|
|
683
|
+
this.doc.name = e.name;
|
|
684
|
+
this.said.push(already ? `The app is already called ${quotedAtTheEnd(e.name)}` : `The app is now called ${quotedAtTheEnd(e.name)}`);
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
case "set-description": {
|
|
688
|
+
const already = (this.doc.description ?? null) === e.description;
|
|
689
|
+
if (e.description === null)
|
|
690
|
+
delete this.doc.description;
|
|
573
691
|
else
|
|
574
|
-
this.doc.
|
|
692
|
+
this.doc.description = e.description;
|
|
693
|
+
this.said.push(e.description === null
|
|
694
|
+
? already ? "The app already has no line under its name." : "The app has no line under its name."
|
|
695
|
+
: already ? `The line under the app's name already reads ${quotedAtTheEnd(e.description)}` : `The line under the app's name reads ${quotedAtTheEnd(e.description)}`);
|
|
575
696
|
return;
|
|
576
697
|
}
|
|
577
698
|
case "set-label": {
|
|
@@ -822,9 +943,12 @@ class Editor {
|
|
|
822
943
|
const said = this.removeLensAt(index);
|
|
823
944
|
this.said.push(`${cap(said.replace(/ \(the app opens at its home again\)$/, ""))} is removed${said.endsWith("again)") ? "; the app opens at its home again" : ""}.`);
|
|
824
945
|
}
|
|
825
|
-
arrangePages(i,
|
|
826
|
-
|
|
827
|
-
|
|
946
|
+
arrangePages(i, given) {
|
|
947
|
+
// `overview` is the scene's word as 0.1.15 and 0.1.16 took it (FR-134): read as `scene`, which wins when both are said.
|
|
948
|
+
const { overview, ...rest } = given;
|
|
949
|
+
const e = rest.scene === undefined && overview !== undefined ? { ...rest, scene: overview } : rest;
|
|
950
|
+
if (e.order === undefined && e.hide === undefined && e.first === undefined && e.scene === undefined && e.pages === undefined)
|
|
951
|
+
return this.fail(i, "", 'arrange-pages says at least one of "order", "hide", "first", "scene" or "pages"');
|
|
828
952
|
const kinds = Object.keys(this.doc.kinds);
|
|
829
953
|
for (const part of ["order", "hide"]) {
|
|
830
954
|
for (const [n, kind] of (e[part] ?? []).entries()) {
|
|
@@ -873,6 +997,20 @@ class Editor {
|
|
|
873
997
|
said.push(`the app opens on "${e.first}"`);
|
|
874
998
|
}
|
|
875
999
|
}
|
|
1000
|
+
// What the bar's switch calls the two faces (FR-137); the scene's address stays /places/overview.
|
|
1001
|
+
for (const [face, word] of [["scene", "Scene"], ["pages", "Pages"]]) {
|
|
1002
|
+
const asked = e[face];
|
|
1003
|
+
if (asked === undefined)
|
|
1004
|
+
continue;
|
|
1005
|
+
if (asked === null || asked.trim() === "" || asked.trim() === word) {
|
|
1006
|
+
delete pages[face];
|
|
1007
|
+
said.push(`the switch calls the ${face} ${word}`);
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
pages[face] = asked.trim();
|
|
1011
|
+
said.push(`the switch calls the ${face} "${pages[face]}"`);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
876
1014
|
if (Object.keys(pages).length === 0)
|
|
877
1015
|
delete this.doc.pages;
|
|
878
1016
|
this.said.push(`${cap(listOf(said))}.`);
|
|
@@ -2277,15 +2415,17 @@ function pruneBlock(doc, b, ctx, r, kind) {
|
|
|
2277
2415
|
* `edits.<i>.…`) and no document. The result is NOT compiled here: the caller
|
|
2278
2416
|
* previews it like any other proposed document.
|
|
2279
2417
|
*/
|
|
2280
|
-
export function editDocument(document, edits) {
|
|
2418
|
+
export function editDocument(document, edits, options = {}) {
|
|
2281
2419
|
if (!Array.isArray(edits))
|
|
2282
2420
|
return { ok: false, findings: [error("edit", "edits", "edits are a list, like [{\"op\": \"add-field\", …}]")] };
|
|
2283
|
-
// No edits is no change: the document as it was, and nothing said.
|
|
2421
|
+
// No edits is no change: the document as it was, in the current spelling (FR-134), and nothing said.
|
|
2284
2422
|
if (edits.length === 0)
|
|
2285
|
-
return { ok: true, document: clone(document), said: [], fills: [] };
|
|
2423
|
+
return { ok: true, document: clone(respellDocument(document).document), said: [], fills: [] };
|
|
2286
2424
|
if (edits.length > MAX_EDITS)
|
|
2287
2425
|
return { ok: false, findings: [error("edit", "edits", `at most ${MAX_EDITS} edits at once`)] };
|
|
2288
|
-
|
|
2426
|
+
// Edited as this build spells it, so what the edit writes says each key's current name (FR-134).
|
|
2427
|
+
const current = respellDocument(document).document;
|
|
2428
|
+
const editor = new Editor(clone(current), current, options);
|
|
2289
2429
|
edits.forEach((raw, i) => {
|
|
2290
2430
|
if (editor.findings.length > 0)
|
|
2291
2431
|
return;
|