@graview/core 0.1.40 → 0.1.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities.js +1 -1
- package/dist/capabilities.js.map +1 -1
- package/dist/cli/check/brand.d.ts.map +1 -1
- package/dist/cli/check/brand.js +39 -2
- package/dist/cli/check/brand.js.map +1 -1
- package/dist/cli/document-schema.d.ts.map +1 -1
- package/dist/cli/document-schema.js +8 -5
- package/dist/cli/document-schema.js.map +1 -1
- package/dist/document/brand-check.d.ts +2 -0
- package/dist/document/brand-check.d.ts.map +1 -1
- package/dist/document/brand-check.js +8 -1
- package/dist/document/brand-check.js.map +1 -1
- package/dist/document/brand.d.ts.map +1 -1
- package/dist/document/brand.js +3 -2
- package/dist/document/brand.js.map +1 -1
- package/dist/document/connection-spec.d.ts +2 -2
- package/dist/document/connection-spec.d.ts.map +1 -1
- package/dist/document/connection-spec.js +40 -34
- package/dist/document/connection-spec.js.map +1 -1
- package/dist/document/describe-place.d.ts.map +1 -1
- package/dist/document/describe-place.js +3 -2
- package/dist/document/describe-place.js.map +1 -1
- package/dist/document/diff.d.ts.map +1 -1
- package/dist/document/diff.js +7 -2
- package/dist/document/diff.js.map +1 -1
- package/dist/document/edit.d.ts.map +1 -1
- package/dist/document/edit.js +150 -128
- package/dist/document/edit.js.map +1 -1
- package/dist/document/graview-template.d.ts +97 -43
- package/dist/document/graview-template.d.ts.map +1 -1
- package/dist/document/graview-template.js +77 -66
- package/dist/document/graview-template.js.map +1 -1
- package/dist/document/logo.d.ts +12 -0
- package/dist/document/logo.d.ts.map +1 -0
- package/dist/document/logo.js +36 -0
- package/dist/document/logo.js.map +1 -0
- package/dist/document/schema.d.ts +51 -57
- package/dist/document/schema.d.ts.map +1 -1
- package/dist/document/schema.js +372 -338
- package/dist/document/schema.js.map +1 -1
- package/dist/document.schema.json +30 -2
- package/dist/history.d.ts +32 -3
- package/dist/history.d.ts.map +1 -1
- package/dist/history.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/installation.d.ts +1 -1
- package/dist/installation.d.ts.map +1 -1
- package/dist/installation.js +1 -1
- package/dist/installation.js.map +1 -1
- package/dist/mutations/node-ref.d.ts +1 -1
- package/dist/mutations/node-ref.d.ts.map +1 -1
- package/dist/mutations/node-ref.js +1 -1
- package/dist/mutations/node-ref.js.map +1 -1
- package/dist/ops/revisions.d.ts +19 -4
- package/dist/ops/revisions.d.ts.map +1 -1
- package/dist/ops/revisions.js +23 -8
- package/dist/ops/revisions.js.map +1 -1
- package/dist/past.d.ts +15 -0
- package/dist/past.d.ts.map +1 -1
- package/dist/past.js +148 -18
- package/dist/past.js.map +1 -1
- package/dist/schema/app-link-field.d.ts +1 -1
- package/dist/schema/app-link-field.d.ts.map +1 -1
- package/dist/schema/app-link-field.js +1 -1
- package/dist/schema/app-link-field.js.map +1 -1
- package/dist/schema/file-field.d.ts +1 -1
- package/dist/schema/file-field.d.ts.map +1 -1
- package/dist/schema/file-field.js +1 -1
- package/dist/schema/file-field.js.map +1 -1
- package/dist/schema/linked-field.d.ts +1 -1
- package/dist/schema/linked-field.d.ts.map +1 -1
- package/dist/schema/linked-field.js +1 -1
- package/dist/schema/linked-field.js.map +1 -1
- package/dist/schema/markdown-field.d.ts +1 -1
- package/dist/schema/markdown-field.d.ts.map +1 -1
- package/dist/schema/markdown-field.js +1 -1
- package/dist/schema/markdown-field.js.map +1 -1
- package/dist/schema/repeat-field.d.ts +1 -1
- package/dist/schema/repeat-field.d.ts.map +1 -1
- package/dist/schema/repeat-field.js +1 -1
- package/dist/schema/repeat-field.js.map +1 -1
- package/dist/schema/typed-field-builders.d.ts +1 -1
- package/dist/schema/typed-field-builders.d.ts.map +1 -1
- package/dist/schema/typed-field-builders.js +1 -1
- package/dist/schema/typed-field-builders.js.map +1 -1
- package/dist/temporal/effectivity.d.ts +1 -1
- package/dist/temporal/effectivity.d.ts.map +1 -1
- package/dist/temporal/effectivity.js +1 -1
- package/dist/temporal/effectivity.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/theme/marks.d.ts +13 -2
- package/dist/theme/marks.d.ts.map +1 -1
- package/dist/theme/marks.js +16 -0
- package/dist/theme/marks.js.map +1 -1
- package/dist/theme/types.d.ts +17 -2
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/document/edit.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DERIVED_ACTS, derivedActOf, derivedActsOf, givenWhenNeverArchived, givenWhenNeverTidied, RELATION_ACTS, relationActName, relationActsOf } from "../mutations/derived-names.js";
|
|
2
2
|
import { documentRelations, givesRelationAct, renamedRelationAct } from "./relation-acts.js";
|
|
3
|
-
import
|
|
3
|
+
import * as z from "zod";
|
|
4
4
|
import { ExprSyntaxError, parseExpr } from "./expr/parse.js";
|
|
5
5
|
import { printExpr } from "./expr/print.js";
|
|
6
6
|
import { error } from "./findings.js";
|
|
@@ -24,6 +24,7 @@ import { computedOf, validateComputed } from "./computed.js";
|
|
|
24
24
|
import { parseTemplate, templateBraces, TemplateError } from "./template-parse.js";
|
|
25
25
|
import { validateViews, VIEW_SLOTS } from "./views.js";
|
|
26
26
|
import { brandFindings, emojiIconSentence } from "./brand-check.js";
|
|
27
|
+
import { darkLogoSentence, logoParts } from "./logo.js";
|
|
27
28
|
import { emojiOf } from "../theme/marks.js";
|
|
28
29
|
import { canonicalize } from "./canonical.js";
|
|
29
30
|
import { quotedAtTheEnd } from "./quote.js";
|
|
@@ -83,128 +84,145 @@ export const EDIT_OPS = [
|
|
|
83
84
|
const KEYWORDS = new Set(["and", "or", "not", "in", "where", "true", "false", "null"]);
|
|
84
85
|
const MAX_EDITS = 50;
|
|
85
86
|
// ── shapes ─────────────────────────────────────────────────────────────────
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
.object({
|
|
118
|
-
op: z.literal("
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.strict(),
|
|
150
|
-
|
|
151
|
-
.object({
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
87
|
+
/*
|
|
88
|
+
* Each op's shape, built the first time an edit is read rather than when the
|
|
89
|
+
* module loads: a Worker or a page that imports the document format and
|
|
90
|
+
* never edits one does not build sixty zod schemas to get there.
|
|
91
|
+
*/
|
|
92
|
+
function buildShapes() {
|
|
93
|
+
const kindName = z.string().regex(NAME, 'kind names are lower-case words joined by hyphens, like "guest-list"');
|
|
94
|
+
const listKey = z.string().min(1).max(120);
|
|
95
|
+
const listDirection = z.enum(["asc", "desc"]).optional();
|
|
96
|
+
const listSortShape = z.union([listKey, z.object({ by: listKey, direction: listDirection, then: z.union([listKey, z.object({ by: listKey, direction: listDirection }).strict()]).optional() }).strict()]);
|
|
97
|
+
const listGroupShape = z.union([listKey, z.object({ by: listKey, order: z.array(z.string().min(1).max(80)).max(40).optional(), closed: z.array(z.string().min(1).max(80)).max(40).optional() }).strict()]);
|
|
98
|
+
const fieldName = z.string().regex(FIELD_NAME, 'field names are one word or camelCase, like "dueDate"');
|
|
99
|
+
const edgeName = z.string().regex(EDGE_NAME, 'relation names are one camelCase word, like "tendedBy"');
|
|
100
|
+
const actName = z.string().regex(NAME, 'act names are lower-case words joined by hyphens, like "mark-paid"');
|
|
101
|
+
const lensTitle = z.string().min(1).max(80);
|
|
102
|
+
return {
|
|
103
|
+
"add-kind": z.object({ op: z.literal("add-kind"), kind: kindName, act: z.boolean().optional() }).passthrough(),
|
|
104
|
+
"rename-kind": z.object({ op: z.literal("rename-kind"), kind: kindName, to: kindName, noun: z.string().min(1).max(40).optional(), plural: z.string().min(1).max(40).optional() }).strict(),
|
|
105
|
+
"remove-kind": z.object({ op: z.literal("remove-kind"), kind: kindName }).strict(),
|
|
106
|
+
"add-field": z.object({ op: z.literal("add-field"), kind: kindName, field: fieldName, fill: z.unknown().optional(), spec: z.record(z.string(), z.unknown()).optional() }).passthrough(),
|
|
107
|
+
"rename-field": z.object({ op: z.literal("rename-field"), kind: kindName, field: fieldName, to: fieldName }).strict(),
|
|
108
|
+
"retype-field": z.object({ op: z.literal("retype-field"), kind: kindName, field: fieldName, type: z.enum(FIELD_TYPES), options: z.array(z.string().min(1).max(80)).min(1).max(100).optional(), of: z.enum(["string", "number", "date", "image", "video", "file"]).optional(), format: z.enum(["money", "percent", "duration", "markdown"]).optional() }).strict(),
|
|
109
|
+
"set-options": z.object({ op: z.literal("set-options"), kind: kindName, field: fieldName, add: z.array(z.string().min(1).max(80)).optional(), remove: z.array(z.string().min(1).max(80)).optional() }).strict(),
|
|
110
|
+
"set-format": z.object({ op: z.literal("set-format"), kind: kindName, field: fieldName, format: z.union([z.enum(["money", "percent", "duration", "markdown"]), z.null()]).optional(), unit: z.union([z.string().min(1).max(12), z.null()]).optional() }).strict(),
|
|
111
|
+
"set-range": z.object({ op: z.literal("set-range"), kind: kindName, field: fieldName, min: z.union([z.number(), z.null()]).optional(), max: z.union([z.number(), z.null()]).optional(), step: z.union([z.number().positive(), z.null()]).optional() }).strict(),
|
|
112
|
+
"set-required": z.object({ op: z.literal("set-required"), kind: kindName, field: fieldName, required: z.boolean() }).strict(),
|
|
113
|
+
"set-default": z.object({ op: z.literal("set-default"), kind: kindName, field: fieldName, default: z.unknown() }).strict(),
|
|
114
|
+
"set-repeats": z.object({ op: z.literal("set-repeats"), kind: kindName, field: fieldName, repeats: z.boolean() }).strict(),
|
|
115
|
+
"remove-field": z.object({ op: z.literal("remove-field"), kind: kindName, field: fieldName }).strict(),
|
|
116
|
+
"add-relation": z.object({ op: z.literal("add-relation"), kind: kindName, relation: edgeName }).passthrough(),
|
|
117
|
+
"rename-relation": z.object({ op: z.literal("rename-relation"), kind: kindName, relation: edgeName, to: edgeName }).strict(),
|
|
118
|
+
"remove-relation": z.object({ op: z.literal("remove-relation"), kind: kindName, relation: edgeName }).strict(),
|
|
119
|
+
"add-act": z.object({ op: z.literal("add-act"), act: actName, replace: z.boolean().optional() }).passthrough(),
|
|
120
|
+
"remove-act": z.object({ op: z.literal("remove-act"), act: actName }).strict(),
|
|
121
|
+
"add-rule": z.object({ op: z.literal("add-rule"), rule: actName, replace: z.boolean().optional() }).passthrough(),
|
|
122
|
+
"remove-rule": z.object({ op: z.literal("remove-rule"), rule: actName }).strict(),
|
|
123
|
+
"set-brand": z
|
|
124
|
+
.object({
|
|
125
|
+
op: z.literal("set-brand"),
|
|
126
|
+
accent: z.union([z.string(), z.null()]).optional(),
|
|
127
|
+
name: z.union([z.string().min(1).max(60), z.null()]).optional(),
|
|
128
|
+
currency: z.union([z.string(), z.null()]).optional(),
|
|
129
|
+
locale: z.union([z.string(), z.null()]).optional(),
|
|
130
|
+
// An IANA time zone its instants are said in (Roadmap f-app-time-zone).
|
|
131
|
+
timeZone: z.union([z.string().min(1).max(64), z.null()]).optional(),
|
|
132
|
+
// The rest of the brand (FR-124, FR-125): each as the document holds it, null to clear it.
|
|
133
|
+
logo: z
|
|
134
|
+
.union([
|
|
135
|
+
z.string().min(1),
|
|
136
|
+
z.object({ src: z.string().min(1), alt: z.string().min(1).max(120).optional() }).strict(),
|
|
137
|
+
// One for each scheme; with no dark one, `darkFallback: "invert"` asks for the light one inverted in the dark.
|
|
138
|
+
z.object({ light: z.string().min(1), dark: z.string().min(1).optional(), alt: z.string().min(1).max(120).optional(), darkFallback: z.literal("invert").optional() }).strict(),
|
|
139
|
+
z.null(),
|
|
140
|
+
])
|
|
141
|
+
.optional(),
|
|
142
|
+
favicon: z.union([z.string().min(1), z.object({ emoji: z.string() }).strict(), z.null()]).optional(),
|
|
143
|
+
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(),
|
|
144
|
+
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(),
|
|
145
|
+
accents: z.union([z.record(z.string(), z.union([z.number().min(0).max(360), z.null()])), z.null()]).optional(),
|
|
146
|
+
scheme: z.union([z.enum(["light", "dark", "auto"]), z.null()]).optional(),
|
|
147
|
+
})
|
|
148
|
+
.strict(),
|
|
149
|
+
"set-name": z.object({ op: z.literal("set-name"), name: z.string().min(1).max(80) }).strict(),
|
|
150
|
+
"set-description": z.object({ op: z.literal("set-description"), description: z.union([z.string().min(1).max(500), z.null()]) }).strict(),
|
|
151
|
+
"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(),
|
|
152
|
+
"set-describe": z.object({ op: z.literal("set-describe"), kind: kindName, describe: z.union([z.string().min(1).max(300), z.null()]) }).strict(),
|
|
153
|
+
// A kind's slot; the front page (`slot: "home"`, no kind); or a blocks lens's blocks, by its title (FR-84).
|
|
154
|
+
"set-view": z.object({ op: z.literal("set-view"), kind: kindName.optional(), slot: z.enum([...VIEW_SLOTS, "home"]).optional(), lens: lensTitle.optional(), blocks: z.union([z.array(z.unknown()).min(1), z.null()]) }).strict(),
|
|
155
|
+
"set-glance": z.object({ op: z.literal("set-glance"), kind: kindName, fields: z.array(fieldName).max(20) }).strict(),
|
|
156
|
+
// How a record's page orders and groups its facts (FR-148): these first, then each group under its title; none of either takes the choice away.
|
|
157
|
+
"set-page-fields": z
|
|
158
|
+
.object({
|
|
159
|
+
op: z.literal("set-page-fields"),
|
|
160
|
+
kind: kindName,
|
|
161
|
+
fields: z.array(fieldName).max(40),
|
|
162
|
+
groups: z.union([z.array(z.object({ title: z.string().min(1).max(60), fields: z.array(fieldName).min(1).max(40) }).strict()).max(12), z.null()]).optional(),
|
|
163
|
+
})
|
|
164
|
+
.strict(),
|
|
165
|
+
"add-lens": z
|
|
166
|
+
.object({
|
|
167
|
+
op: z.literal("add-lens"),
|
|
168
|
+
title: lensTitle,
|
|
169
|
+
lens: z.string().min(1).max(40).optional(),
|
|
170
|
+
on: kindName.optional(),
|
|
171
|
+
bindings: z.record(z.string(), z.unknown()).optional(),
|
|
172
|
+
options: z.record(z.string(), z.unknown()).optional(),
|
|
173
|
+
// False: the place's page leaves off the forms that begin its kind.
|
|
174
|
+
create: z.boolean().optional(),
|
|
175
|
+
at: z.number().int().min(0).optional(),
|
|
176
|
+
replace: lensTitle.optional(),
|
|
177
|
+
})
|
|
178
|
+
.strict(),
|
|
179
|
+
"remove-lens": z.object({ op: z.literal("remove-lens"), title: lensTitle, on: kindName.optional() }).strict(),
|
|
180
|
+
// A lens kept off the bar's places, the page list and the home's gallery, still reached by its address, a link or navigate.
|
|
181
|
+
"set-lens-hidden": z.object({ op: z.literal("set-lens-hidden"), title: lensTitle, on: kindName.optional(), hidden: z.boolean() }).strict(),
|
|
182
|
+
"set-home": z.object({ op: z.literal("set-home"), blocks: z.union([z.array(z.unknown()).min(1), z.null()]) }).strict(),
|
|
183
|
+
"arrange-pages": z
|
|
184
|
+
.object({
|
|
185
|
+
op: z.literal("arrange-pages"),
|
|
186
|
+
order: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
187
|
+
hide: z.union([z.array(kindName).max(40), z.null()]).optional(),
|
|
188
|
+
first: z.union([z.string().min(1).max(80), z.null()]).optional(),
|
|
189
|
+
primary: z.union([z.array(z.string().min(1).max(80)).max(40), z.null()]).optional(),
|
|
190
|
+
scene: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
191
|
+
pages: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
192
|
+
// What a kind's list offers to filter by: `{ "request": ["status", "became.stage"] }`; null or [] gives a kind back its derived filters.
|
|
193
|
+
filters: z.record(kindName, z.union([z.array(z.string().min(1).max(120)).max(40), z.null()])).optional(),
|
|
194
|
+
// The order a kind's list opens in: `{ "feature": { "by": "score", "direction": "desc", "then": "label" } }` or a key; null gives it back its own.
|
|
195
|
+
sort: z.record(kindName, z.union([listSortShape, z.null()])).optional(),
|
|
196
|
+
// How a kind's list opens grouped: `{ "request": { "by": "status", "order": [...], "closed": ["done"] } }` or a field; null opens it ungrouped.
|
|
197
|
+
group: z.record(kindName, z.union([listGroupShape, z.null()])).optional(),
|
|
198
|
+
// The scene's word as a chat or a tool written before 0.1.17 sends it (FR-132's key, RESPELLED as `scene`).
|
|
199
|
+
overview: z.union([z.string().min(1).max(40), z.null()]).optional(),
|
|
200
|
+
})
|
|
201
|
+
.strict(),
|
|
202
|
+
"set-computed": z
|
|
203
|
+
.object({
|
|
204
|
+
op: z.literal("set-computed"),
|
|
205
|
+
kind: kindName,
|
|
206
|
+
name: fieldName,
|
|
207
|
+
expr: z.union([z.string().min(1).max(2000), z.null()]).optional(),
|
|
208
|
+
label: z.union([z.string().min(1).max(60), z.null()]).optional(),
|
|
209
|
+
description: z.union([z.string().min(1).max(500), z.null()]).optional(),
|
|
210
|
+
})
|
|
211
|
+
.strict(),
|
|
212
|
+
// Who the people are, and who may do and see what (FR-170): a grant and a sight as the document holds one.
|
|
213
|
+
"set-people": z.object({ op: z.literal("set-people"), kind: kindName, people: z.boolean() }).strict(),
|
|
214
|
+
// Whether a kind's records are archived and merged, or only kept and removed (FR-173).
|
|
215
|
+
"set-archives": z.object({ op: z.literal("set-archives"), kind: kindName, archives: z.boolean() }).strict(),
|
|
216
|
+
// What the app's own actor may do on its own to a kind, or to one relation of it (`@graview/core/tidying`).
|
|
217
|
+
"set-tidying": z.object({ op: z.literal("set-tidying"), kind: kindName, relation: z.optional(z.string()), tidying: z.enum(["never", "no-links", "allowed"]) }).strict(),
|
|
218
|
+
"add-grant": z.object({ op: z.literal("add-grant") }).passthrough(),
|
|
219
|
+
"remove-grant": z.object({ op: z.literal("remove-grant") }).passthrough(),
|
|
220
|
+
"add-sight": z.object({ op: z.literal("add-sight") }).passthrough(),
|
|
221
|
+
"remove-sight": z.object({ op: z.literal("remove-sight") }).passthrough(),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
let shapes;
|
|
225
|
+
const opShapes = () => (shapes ??= buildShapes());
|
|
208
226
|
const NONE = new Set();
|
|
209
227
|
const clone = (v) => structuredClone(v);
|
|
210
228
|
const asArray = (v) => [v ?? []].flat();
|
|
@@ -588,8 +606,10 @@ class Editor {
|
|
|
588
606
|
delete brand[key];
|
|
589
607
|
else
|
|
590
608
|
brand[key] = e[key];
|
|
591
|
-
|
|
609
|
+
// A logo's light one is its `src` or its `light`; its dark scheme is said on its own (logo.ts).
|
|
610
|
+
const src = (mark) => (key === "logo" ? logoParts(mark)?.light : typeof mark === "string" ? mark : isObject(mark) ? mark["src"] : undefined);
|
|
592
611
|
const alt = (mark) => (isObject(mark) && typeof mark["alt"] === "string" ? mark["alt"] : undefined);
|
|
612
|
+
const dark = key === "logo" && e[key] !== null ? darkLogoSentence(was, e[key]) : undefined;
|
|
593
613
|
if (e[key] === null)
|
|
594
614
|
this.said.push(`The app has no ${what}.`);
|
|
595
615
|
else if (key === "favicon" && emojiOf(e[key]) !== undefined)
|
|
@@ -598,10 +618,12 @@ class Editor {
|
|
|
598
618
|
this.said.push(`The app gets a ${what}.`);
|
|
599
619
|
else if (canonicalize(was) === canonicalize(e[key]))
|
|
600
620
|
this.said.push(`The ${what} stays as it was.`);
|
|
601
|
-
else if (src(was)
|
|
602
|
-
this.said.push(`The ${what} is said as ${quotedAtTheEnd(alt(e[key]) ?? brand["name"] ?? this.doc.name)}`);
|
|
603
|
-
else
|
|
621
|
+
else if (src(was) !== src(e[key]))
|
|
604
622
|
this.said.push(`The ${what} changes.`);
|
|
623
|
+
else if (alt(was) !== alt(e[key]) || !dark)
|
|
624
|
+
this.said.push(`The ${what} is said as ${quotedAtTheEnd(alt(e[key]) ?? brand["name"] ?? this.doc.name)}`);
|
|
625
|
+
if (dark)
|
|
626
|
+
this.said.push(dark);
|
|
605
627
|
}
|
|
606
628
|
const merge = (key, say, cleared) => {
|
|
607
629
|
if (e[key] === undefined)
|
|
@@ -687,7 +709,7 @@ class Editor {
|
|
|
687
709
|
this.fail(i, "op", `${op === undefined ? "an edit names its op" : `"${String(op)}" is not an edit Graview knows`}`, `one of ${EDIT_OPS.join(", ")}`);
|
|
688
710
|
return;
|
|
689
711
|
}
|
|
690
|
-
const parsed =
|
|
712
|
+
const parsed = opShapes()[op].safeParse(edit);
|
|
691
713
|
if (!parsed.success) {
|
|
692
714
|
this.refuse(i, parsed.error.issues, op);
|
|
693
715
|
return;
|