@graview/core 0.1.14 → 0.1.16
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 +27 -8
- 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/hidden-reads.js +1 -1
- package/dist/cli/check/hidden-reads.js.map +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 +19 -0
- package/dist/compiled.d.ts.map +1 -0
- package/dist/compiled.js +60 -0
- package/dist/compiled.js.map +1 -0
- package/dist/conformance/fixtures.d.ts.map +1 -1
- package/dist/conformance/fixtures.js +22 -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/blocks.d.ts.map +1 -1
- package/dist/document/blocks.js +2 -1
- package/dist/document/blocks.js.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 +10 -29
- package/dist/document/compile.d.ts.map +1 -1
- package/dist/document/compile.js +74 -393
- package/dist/document/compile.js.map +1 -1
- package/dist/document/compiled.d.ts +130 -0
- package/dist/document/compiled.d.ts.map +1 -0
- package/dist/document/compiled.js +467 -0
- package/dist/document/compiled.js.map +1 -0
- 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.map +1 -1
- package/dist/document/diff.js +45 -6
- 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 +174 -46
- 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 +5 -2
- package/dist/document/index.d.ts.map +1 -1
- package/dist/document/index.js +3 -1
- 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/remembered.d.ts +6 -0
- package/dist/document/remembered.d.ts.map +1 -0
- package/dist/document/remembered.js +16 -0
- package/dist/document/remembered.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 +4 -3
- package/dist/document/rules.js.map +1 -1
- package/dist/document/schema.d.ts +45 -2
- package/dist/document/schema.d.ts.map +1 -1
- package/dist/document/schema.js +32 -5
- package/dist/document/schema.js.map +1 -1
- package/dist/document/template-parse.d.ts +18 -0
- package/dist/document/template-parse.d.ts.map +1 -0
- package/dist/document/template-parse.js +124 -0
- package/dist/document/template-parse.js.map +1 -0
- package/dist/document/template.d.ts +1 -17
- package/dist/document/template.d.ts.map +1 -1
- package/dist/document/template.js +6 -115
- package/dist/document/template.js.map +1 -1
- package/dist/document/thumbnail.d.ts +13 -1
- package/dist/document/thumbnail.d.ts.map +1 -1
- package/dist/document/thumbnail.js +106 -16
- package/dist/document/thumbnail.js.map +1 -1
- package/dist/document/to-document.d.ts +1 -4
- package/dist/document/to-document.d.ts.map +1 -1
- package/dist/document/to-document.js +7 -14
- package/dist/document/to-document.js.map +1 -1
- package/dist/document/views.d.ts.map +1 -1
- package/dist/document/views.js +1 -1
- package/dist/document/views.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 +11 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- 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.d.ts +12 -0
- package/dist/mutations/define-mutation.d.ts.map +1 -1
- package/dist/mutations/define-mutation.js +37 -9
- package/dist/mutations/define-mutation.js.map +1 -1
- package/dist/mutations/derive-edits.js +4 -4
- package/dist/mutations/derive-edits.js.map +1 -1
- 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 +14 -1
- package/dist/places.d.ts.map +1 -1
- package/dist/places.js +27 -4
- package/dist/places.js.map +1 -1
- package/dist/refusal.d.ts +11 -6
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +11 -6
- package/dist/refusal.js.map +1 -1
- package/dist/refused.d.ts +13 -6
- package/dist/refused.d.ts.map +1 -1
- package/dist/refused.js +14 -7
- package/dist/refused.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 +2 -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/json-schema.d.ts.map +1 -1
- package/dist/schema/json-schema.js +4 -1
- package/dist/schema/json-schema.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 +19 -19
- 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 +73 -0
- package/dist/theme/fonts.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 +3 -3
- package/dist/theme/look.d.ts.map +1 -1
- package/dist/theme/look.js +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 +2 -2
- package/dist/theme/palettes.js +2 -2
- package/dist/theme/types.d.ts +28 -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 +5 -1
package/dist/document/edit.js
CHANGED
|
@@ -9,8 +9,12 @@ import { ActSpec, EDGE_NAME, EdgeSpec, FIELD_NAME, FIELD_TYPES, FieldSpec, KindS
|
|
|
9
9
|
import { SHIPPED_LENSES, isShippedLens, SHIPPED_LENS_NAMES } from "../places.js";
|
|
10
10
|
import { placeSlug } from "../views/types.js";
|
|
11
11
|
import { computedOf, validateComputed } from "./computed.js";
|
|
12
|
-
import { parseTemplate, templateBraces, TemplateError } from "./template.js";
|
|
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 { accentProblem } from "../theme/accent.js";
|
|
14
18
|
/** The ops, for tool schemas and the guide. */
|
|
15
19
|
export const EDIT_OPS = [
|
|
16
20
|
"add-kind",
|
|
@@ -32,6 +36,8 @@ export const EDIT_OPS = [
|
|
|
32
36
|
"add-rule",
|
|
33
37
|
"remove-rule",
|
|
34
38
|
"set-brand",
|
|
39
|
+
"set-name",
|
|
40
|
+
"set-description",
|
|
35
41
|
"set-label",
|
|
36
42
|
"set-describe",
|
|
37
43
|
"set-view",
|
|
@@ -74,11 +80,20 @@ const SHAPES = {
|
|
|
74
80
|
.object({
|
|
75
81
|
op: z.literal("set-brand"),
|
|
76
82
|
accent: z.union([z.string(), z.null()]).optional(),
|
|
77
|
-
name: z.string().min(1).max(60).optional(),
|
|
83
|
+
name: z.union([z.string().min(1).max(60), z.null()]).optional(),
|
|
78
84
|
currency: z.union([z.string(), z.null()]).optional(),
|
|
79
85
|
locale: z.union([z.string(), z.null()]).optional(),
|
|
86
|
+
// The rest of the brand (FR-124, FR-125): each as the document holds it, null to clear it.
|
|
87
|
+
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(),
|
|
88
|
+
favicon: z.union([z.string().min(1), z.null()]).optional(),
|
|
89
|
+
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(),
|
|
90
|
+
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(),
|
|
91
|
+
accents: z.union([z.record(z.string(), z.union([z.number().min(0).max(360), z.null()])), z.null()]).optional(),
|
|
92
|
+
scheme: z.union([z.enum(["light", "dark", "auto"]), z.null()]).optional(),
|
|
80
93
|
})
|
|
81
94
|
.strict(),
|
|
95
|
+
"set-name": z.object({ op: z.literal("set-name"), name: z.string().min(1).max(80) }).strict(),
|
|
96
|
+
"set-description": z.object({ op: z.literal("set-description"), description: z.union([z.string().min(1).max(500), z.null()]) }).strict(),
|
|
82
97
|
"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
98
|
"set-describe": z.object({ op: z.literal("set-describe"), kind: kindName, describe: z.union([z.string().min(1).max(300), z.null()]) }).strict(),
|
|
84
99
|
// A kind's slot; the front page (`slot: "home"`, no kind); or a blocks lens's blocks, by its title (FR-84).
|
|
@@ -104,6 +119,7 @@ const SHAPES = {
|
|
|
104
119
|
order: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
105
120
|
hide: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
106
121
|
first: z.union([z.string().min(1).max(80), z.null()]).optional(),
|
|
122
|
+
overview: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
107
123
|
})
|
|
108
124
|
.strict(),
|
|
109
125
|
"set-computed": z
|
|
@@ -395,6 +411,7 @@ function setTargets(act, doc) {
|
|
|
395
411
|
// ── the edits ────────────────────────────────────────────────────────────────
|
|
396
412
|
class Editor {
|
|
397
413
|
doc;
|
|
414
|
+
options;
|
|
398
415
|
said = [];
|
|
399
416
|
/** The agent tools this change renames or reshapes: a model that listed them before calls what is gone. */
|
|
400
417
|
toolsMoved = new Set();
|
|
@@ -404,8 +421,9 @@ class Editor {
|
|
|
404
421
|
kindOrigin = new Map();
|
|
405
422
|
fieldOrigin = new Map();
|
|
406
423
|
edgeOrigin = new Map();
|
|
407
|
-
constructor(doc, base) {
|
|
424
|
+
constructor(doc, base, options = {}) {
|
|
408
425
|
this.doc = doc;
|
|
426
|
+
this.options = options;
|
|
409
427
|
for (const [kind, spec] of Object.entries(base.kinds)) {
|
|
410
428
|
this.kindOrigin.set(kind, kind);
|
|
411
429
|
for (const f of Object.keys(spec.fields))
|
|
@@ -417,6 +435,128 @@ class Editor {
|
|
|
417
435
|
fail(i, path, message, fix) {
|
|
418
436
|
this.findings.push(error("edit", `edits.${i}${path ? `.${path}` : ""}`, message, fix));
|
|
419
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* THE BRAND, KEY BY KEY (FR-100, FR-124, FR-125): what an edit names is
|
|
440
|
+
* set, null clears it, and what it does not name stays as it was. Each
|
|
441
|
+
* is judged as `graview check` judges it — a mark that could act or load,
|
|
442
|
+
* a face off the list — and an accent that does not read as given is
|
|
443
|
+
* refused with the pair, the ratio and a shade that would (FR-126).
|
|
444
|
+
*/
|
|
445
|
+
setBrand(i, e) {
|
|
446
|
+
const brand = clone(this.doc.brand ?? {});
|
|
447
|
+
const at = `edits.${i}`;
|
|
448
|
+
// An edit that names nothing to change is refused, never applied with nothing said.
|
|
449
|
+
const named = Object.keys(e).filter((key) => key !== "op" && e[key] !== undefined);
|
|
450
|
+
if (named.length === 0)
|
|
451
|
+
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"}');
|
|
452
|
+
const emptyPart = ["typography", "shape", "accents"].find((key) => isObject(e[key]) && Object.keys(e[key]).length === 0);
|
|
453
|
+
if (emptyPart)
|
|
454
|
+
return this.fail(i, emptyPart, `${emptyPart} names no part to set; name one, or give null to clear all of it`);
|
|
455
|
+
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 } : {}) });
|
|
456
|
+
if (judged.length > 0)
|
|
457
|
+
return void this.findings.push(...judged);
|
|
458
|
+
if (e.accent === null) {
|
|
459
|
+
delete brand["accent"];
|
|
460
|
+
this.said.push("The app goes back to Graview's colors.");
|
|
461
|
+
}
|
|
462
|
+
else if (e.accent !== undefined) {
|
|
463
|
+
if (!/^#[0-9a-fA-F]{6}$/.test(e.accent))
|
|
464
|
+
return this.fail(i, "accent", 'an accent is a color like "#c2577a"');
|
|
465
|
+
const refused = accentProblem(e.accent);
|
|
466
|
+
if (refused)
|
|
467
|
+
return this.fail(i, "accent", refused.sentence, refused.suggestion ? `{"op": "set-brand", "accent": "${refused.suggestion}"}` : "pick a color of another hue");
|
|
468
|
+
brand["accent"] = e.accent;
|
|
469
|
+
this.said.push(`The app's accent color becomes ${e.accent}.`);
|
|
470
|
+
}
|
|
471
|
+
if (e.currency === null) {
|
|
472
|
+
delete brand["currency"];
|
|
473
|
+
this.said.push("Money is said with no currency.");
|
|
474
|
+
}
|
|
475
|
+
else if (e.currency !== undefined) {
|
|
476
|
+
if (!/^[A-Z]{3}$/.test(e.currency))
|
|
477
|
+
return this.fail(i, "currency", 'a currency is its three-letter code, like "USD" or "EUR"');
|
|
478
|
+
brand["currency"] = e.currency;
|
|
479
|
+
this.said.push(`Money is said in ${e.currency}.`);
|
|
480
|
+
}
|
|
481
|
+
if (e.locale === null)
|
|
482
|
+
delete brand["locale"];
|
|
483
|
+
else if (e.locale !== undefined) {
|
|
484
|
+
if (!/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(e.locale))
|
|
485
|
+
return this.fail(i, "locale", 'a locale is a language tag, like "en-US" or "de-DE"');
|
|
486
|
+
brand["locale"] = e.locale;
|
|
487
|
+
this.said.push(`Money is written for ${e.locale}.`);
|
|
488
|
+
}
|
|
489
|
+
if (e.name === null) {
|
|
490
|
+
delete brand["name"];
|
|
491
|
+
this.said.push("The wordmark says the app's name.");
|
|
492
|
+
}
|
|
493
|
+
else if (e.name !== undefined) {
|
|
494
|
+
brand["name"] = e.name;
|
|
495
|
+
this.said.push(`The app's wordmark says ${e.name}.`);
|
|
496
|
+
}
|
|
497
|
+
for (const [key, what] of [["logo", "logo"], ["favicon", "page icon"]]) {
|
|
498
|
+
if (e[key] === undefined)
|
|
499
|
+
continue;
|
|
500
|
+
const was = brand[key];
|
|
501
|
+
if (e[key] === null)
|
|
502
|
+
delete brand[key];
|
|
503
|
+
else
|
|
504
|
+
brand[key] = e[key];
|
|
505
|
+
const src = (mark) => (typeof mark === "string" ? mark : isObject(mark) ? mark["src"] : undefined);
|
|
506
|
+
const alt = (mark) => (isObject(mark) && typeof mark["alt"] === "string" ? mark["alt"] : undefined);
|
|
507
|
+
if (e[key] === null)
|
|
508
|
+
this.said.push(`The app has no ${what}.`);
|
|
509
|
+
else if (was === undefined)
|
|
510
|
+
this.said.push(`The app gets a ${what}.`);
|
|
511
|
+
else if (canonicalize(was) === canonicalize(e[key]))
|
|
512
|
+
this.said.push(`The ${what} stays as it was.`);
|
|
513
|
+
else if (src(was) === src(e[key]))
|
|
514
|
+
this.said.push(`The ${what} is said as ${quotedAtTheEnd(alt(e[key]) ?? brand["name"] ?? this.doc.name)}`);
|
|
515
|
+
else
|
|
516
|
+
this.said.push(`The ${what} changes.`);
|
|
517
|
+
}
|
|
518
|
+
const merge = (key, say, cleared) => {
|
|
519
|
+
if (e[key] === undefined)
|
|
520
|
+
return;
|
|
521
|
+
if (e[key] === null) {
|
|
522
|
+
delete brand[key];
|
|
523
|
+
this.said.push(cleared);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const into = { ...(brand[key] ?? {}) };
|
|
527
|
+
for (const [part, value] of Object.entries(e[key])) {
|
|
528
|
+
if (value === null)
|
|
529
|
+
delete into[part];
|
|
530
|
+
else
|
|
531
|
+
into[part] = value;
|
|
532
|
+
this.said.push(say(part, value));
|
|
533
|
+
}
|
|
534
|
+
if (Object.keys(into).length === 0)
|
|
535
|
+
delete brand[key];
|
|
536
|
+
else
|
|
537
|
+
brand[key] = into;
|
|
538
|
+
};
|
|
539
|
+
const ROLE = { display: "Headings", body: "Body text", mono: "Code" };
|
|
540
|
+
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.");
|
|
541
|
+
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.");
|
|
542
|
+
if (isObject(e.accents)) {
|
|
543
|
+
const unknown = Object.keys(e.accents).find((kind) => !this.doc.kinds[kind]);
|
|
544
|
+
if (unknown)
|
|
545
|
+
return this.fail(i, "accents", `"${unknown}" is not a kind this app has; it has ${Object.keys(this.doc.kinds).join(", ")}`);
|
|
546
|
+
}
|
|
547
|
+
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.");
|
|
548
|
+
if (e.scheme !== undefined) {
|
|
549
|
+
if (e.scheme === null || e.scheme === "auto")
|
|
550
|
+
delete brand["scheme"];
|
|
551
|
+
else
|
|
552
|
+
brand["scheme"] = e.scheme;
|
|
553
|
+
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.");
|
|
554
|
+
}
|
|
555
|
+
if (Object.keys(brand).length === 0)
|
|
556
|
+
delete this.doc.brand;
|
|
557
|
+
else
|
|
558
|
+
this.doc.brand = brand;
|
|
559
|
+
}
|
|
420
560
|
kind(i, kind) {
|
|
421
561
|
const spec = this.doc.kinds[kind];
|
|
422
562
|
if (!spec)
|
|
@@ -532,46 +672,23 @@ class Editor {
|
|
|
532
672
|
this.said.push(`The rule "${title}" is removed.`);
|
|
533
673
|
return;
|
|
534
674
|
}
|
|
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;
|
|
675
|
+
case "set-brand":
|
|
676
|
+
return this.setBrand(i, e);
|
|
677
|
+
case "set-name": {
|
|
678
|
+
const already = this.doc.name === e.name;
|
|
679
|
+
this.doc.name = e.name;
|
|
680
|
+
this.said.push(already ? `The app is already called ${quotedAtTheEnd(e.name)}` : `The app is now called ${quotedAtTheEnd(e.name)}`);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
case "set-description": {
|
|
684
|
+
const already = (this.doc.description ?? null) === e.description;
|
|
685
|
+
if (e.description === null)
|
|
686
|
+
delete this.doc.description;
|
|
573
687
|
else
|
|
574
|
-
this.doc.
|
|
688
|
+
this.doc.description = e.description;
|
|
689
|
+
this.said.push(e.description === null
|
|
690
|
+
? already ? "The app already has no line under its name." : "The app has no line under its name."
|
|
691
|
+
: 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
692
|
return;
|
|
576
693
|
}
|
|
577
694
|
case "set-label": {
|
|
@@ -823,8 +940,8 @@ class Editor {
|
|
|
823
940
|
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
941
|
}
|
|
825
942
|
arrangePages(i, e) {
|
|
826
|
-
if (e.order === undefined && e.hide === undefined && e.first === undefined)
|
|
827
|
-
return this.fail(i, "", 'arrange-pages says at least one of "order", "hide" or "
|
|
943
|
+
if (e.order === undefined && e.hide === undefined && e.first === undefined && e.overview === undefined)
|
|
944
|
+
return this.fail(i, "", 'arrange-pages says at least one of "order", "hide", "first" or "overview"');
|
|
828
945
|
const kinds = Object.keys(this.doc.kinds);
|
|
829
946
|
for (const part of ["order", "hide"]) {
|
|
830
947
|
for (const [n, kind] of (e[part] ?? []).entries()) {
|
|
@@ -873,6 +990,17 @@ class Editor {
|
|
|
873
990
|
said.push(`the app opens on "${e.first}"`);
|
|
874
991
|
}
|
|
875
992
|
}
|
|
993
|
+
if (e.overview !== undefined) {
|
|
994
|
+
// What the scene is called on the bar (FR-132); its address stays /places/overview.
|
|
995
|
+
if (e.overview === null || e.overview.trim() === "" || e.overview.trim() === "Overview") {
|
|
996
|
+
delete pages.overview;
|
|
997
|
+
said.push("the overview is called Overview");
|
|
998
|
+
}
|
|
999
|
+
else {
|
|
1000
|
+
pages.overview = e.overview.trim();
|
|
1001
|
+
said.push(`the overview is called "${pages.overview}"`);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
876
1004
|
if (Object.keys(pages).length === 0)
|
|
877
1005
|
delete this.doc.pages;
|
|
878
1006
|
this.said.push(`${cap(listOf(said))}.`);
|
|
@@ -2277,7 +2405,7 @@ function pruneBlock(doc, b, ctx, r, kind) {
|
|
|
2277
2405
|
* `edits.<i>.…`) and no document. The result is NOT compiled here: the caller
|
|
2278
2406
|
* previews it like any other proposed document.
|
|
2279
2407
|
*/
|
|
2280
|
-
export function editDocument(document, edits) {
|
|
2408
|
+
export function editDocument(document, edits, options = {}) {
|
|
2281
2409
|
if (!Array.isArray(edits))
|
|
2282
2410
|
return { ok: false, findings: [error("edit", "edits", "edits are a list, like [{\"op\": \"add-field\", …}]")] };
|
|
2283
2411
|
// No edits is no change: the document as it was, and nothing said.
|
|
@@ -2285,7 +2413,7 @@ export function editDocument(document, edits) {
|
|
|
2285
2413
|
return { ok: true, document: clone(document), said: [], fills: [] };
|
|
2286
2414
|
if (edits.length > MAX_EDITS)
|
|
2287
2415
|
return { ok: false, findings: [error("edit", "edits", `at most ${MAX_EDITS} edits at once`)] };
|
|
2288
|
-
const editor = new Editor(clone(document), document);
|
|
2416
|
+
const editor = new Editor(clone(document), document, options);
|
|
2289
2417
|
edits.forEach((raw, i) => {
|
|
2290
2418
|
if (editor.findings.length > 0)
|
|
2291
2419
|
return;
|