@quillmark/svelte 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/THEMING.md +2 -2
- package/dist/core/codec/blocks.js +21 -1
- package/dist/core/codec/breaks.d.ts +10 -2
- package/dist/core/codec/breaks.js +65 -9
- package/dist/core/codec/code.d.ts +6 -4
- package/dist/core/codec/code.js +7 -4
- package/dist/core/codec/decode.js +91 -62
- package/dist/core/codec/encode.d.ts +5 -8
- package/dist/core/codec/encode.js +55 -85
- package/dist/core/codec/field.js +43 -24
- package/dist/core/codec/index.d.ts +2 -1
- package/dist/core/codec/index.js +6 -3
- package/dist/core/codec/islands.d.ts +13 -9
- package/dist/core/codec/islands.js +75 -49
- package/dist/core/codec/keymap.js +2 -1
- package/dist/core/codec/marks.d.ts +17 -13
- package/dist/core/codec/marks.js +39 -42
- package/dist/core/codec/positions.d.ts +4 -1
- package/dist/core/codec/positions.js +5 -1
- package/dist/core/codec/reconcile.d.ts +6 -0
- package/dist/core/codec/reconcile.js +19 -0
- package/dist/core/codec/schema.d.ts +5 -8
- package/dist/core/codec/schema.js +30 -156
- package/dist/core/codec/table-view.js +77 -26
- package/dist/core/codec/table.d.ts +6 -5
- package/dist/core/codec/table.js +14 -31
- package/dist/core/codec/urls.d.ts +20 -0
- package/dist/core/codec/urls.js +45 -0
- package/dist/preset/recipes.css +1 -1
- package/dist/preset/scale.css +7 -4
- package/dist/preview/bridge.d.ts +8 -1
- package/dist/preview/bridge.js +31 -25
- package/dist/preview/controller.js +13 -13
- package/dist/preview/geometry.d.ts +12 -1
- package/dist/preview/geometry.js +14 -1
- package/dist/preview/paint.js +6 -4
- package/dist/preview/strings.d.ts +0 -2
- package/dist/preview/strings.js +5 -6
- package/dist/visual/ArrayField.svelte +11 -13
- package/dist/visual/Card.svelte +5 -8
- package/dist/visual/DateField.svelte +2 -2
- package/dist/visual/Field.svelte +7 -3
- package/dist/visual/FieldHint.svelte +1 -1
- package/dist/visual/ObjectField.svelte +6 -1
- package/dist/visual/VisualEditor.svelte +1 -1
- package/dist/visual/VisualEditorInner.svelte +65 -18
- package/dist/visual/controls.css +8 -9
- package/dist/visual/ext.d.ts +12 -13
- package/dist/visual/ext.js +22 -19
- package/dist/visual/index.js +2 -3
- package/dist/visual/links.d.ts +4 -0
- package/dist/visual/links.js +15 -4
- package/dist/visual/strings.d.ts +7 -4
- package/dist/visual/strings.js +1 -0
- package/dist/visual/structure.d.ts +9 -1
- package/dist/visual/structure.js +26 -4
- package/package.json +2 -2
|
@@ -32,7 +32,14 @@
|
|
|
32
32
|
import { errorMessage, reportError } from '../core/errors.js';
|
|
33
33
|
import { bloomInside } from '../core/bloom.js';
|
|
34
34
|
import { createLifespan } from '../core/teardown.js';
|
|
35
|
-
import type {
|
|
35
|
+
import type {
|
|
36
|
+
Addr,
|
|
37
|
+
CardAddr,
|
|
38
|
+
Diagnostic,
|
|
39
|
+
PayloadItem,
|
|
40
|
+
Resolved,
|
|
41
|
+
ResolvedField
|
|
42
|
+
} from '@quillmark/wasm';
|
|
36
43
|
import type { VisualEditorProps } from './props.js';
|
|
37
44
|
import { mergeStrings, setWording } from './strings.js';
|
|
38
45
|
import type { CardId, ChangeSource } from './signals.js';
|
|
@@ -45,6 +52,8 @@
|
|
|
45
52
|
groupSections,
|
|
46
53
|
groupLabel,
|
|
47
54
|
cardTitle,
|
|
55
|
+
titleFields,
|
|
56
|
+
fieldValues,
|
|
48
57
|
bodyEnabled,
|
|
49
58
|
humanize,
|
|
50
59
|
provenanceMap,
|
|
@@ -144,6 +153,7 @@
|
|
|
144
153
|
*/
|
|
145
154
|
function bump(source: ChangeSource, cardId?: CardId, at?: Addr | number): void {
|
|
146
155
|
revision++;
|
|
156
|
+
liveTitles = {};
|
|
147
157
|
const path =
|
|
148
158
|
at == null ? undefined : typeof at === 'number' ? cardPath(at, liveKinds()) : pathFor(at);
|
|
149
159
|
onChange?.({ source, cardId, path });
|
|
@@ -154,8 +164,11 @@
|
|
|
154
164
|
return cardIds.indexOf(id);
|
|
155
165
|
}
|
|
156
166
|
/** The inverse, for the two signals that arrive as an address and nothing else (a
|
|
157
|
-
* focus, a prose commit), off the same array the mutation boundary reads.
|
|
158
|
-
|
|
167
|
+
* focus, a prose commit), off the same array the mutation boundary reads.
|
|
168
|
+
* `undefined` for an address outside the live card array, as {@link pathFor}: the
|
|
169
|
+
* key is a host's only handle on a card, so a stale one is dropped rather than
|
|
170
|
+
* reported. */
|
|
171
|
+
function cardIdOf(addr: Addr): CardId | undefined {
|
|
159
172
|
return addr.card == null ? 'main' : cardIds[addr.card];
|
|
160
173
|
}
|
|
161
174
|
|
|
@@ -214,7 +227,9 @@
|
|
|
214
227
|
lastCaretField = undefined;
|
|
215
228
|
lastCaretPos = undefined;
|
|
216
229
|
const field = pathFor(plain);
|
|
217
|
-
|
|
230
|
+
// Both halves or neither: a stale addr names no path and no live card.
|
|
231
|
+
if (field != null && activeCardId != null)
|
|
232
|
+
onActiveLeafChange?.({ field, cardId: activeCardId });
|
|
218
233
|
}
|
|
219
234
|
/** No card kinds are read for a main address; the shared empty stands in so the
|
|
220
235
|
* common case allocates nothing. */
|
|
@@ -261,7 +276,35 @@
|
|
|
261
276
|
* bump `revision` (see {@link bump}). */
|
|
262
277
|
function proseChanged(addr: Addr): void {
|
|
263
278
|
const plain = normalize(addr);
|
|
264
|
-
|
|
279
|
+
const cardId = cardIdOf(plain);
|
|
280
|
+
if (cardId == null) return;
|
|
281
|
+
refreshTitle(plain, cardId);
|
|
282
|
+
onChange?.({ source: 'prose', cardId, path: pathFor(plain) });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** A card's header after a prose commit to a field its `{field}` title names: the
|
|
286
|
+
* one piece of the model that lane changes, held beside the model and dropped on
|
|
287
|
+
* the next bump, which rebuilds it from the document. The kind is the derived
|
|
288
|
+
* tree's ({@link liveKinds}), so the document is read only for a title that
|
|
289
|
+
* names the field. */
|
|
290
|
+
let liveTitles = $state<Record<CardId, string>>({});
|
|
291
|
+
function refreshTitle(addr: Addr, cardId: CardId): void {
|
|
292
|
+
const { card: at, field } = addr;
|
|
293
|
+
if (field == null) return;
|
|
294
|
+
const shown = at == null ? model.main : model.cards[at];
|
|
295
|
+
if (!shown) return;
|
|
296
|
+
const cardSchema = at == null ? quill.schema.main : quill.schema.card_kinds?.[shown.kind];
|
|
297
|
+
if (!titleFields(cardSchema?.ui?.title).includes(field)) return;
|
|
298
|
+
const live = at == null ? doc.main : doc.cards[at];
|
|
299
|
+
if (!live) return;
|
|
300
|
+
liveTitles = {
|
|
301
|
+
...liveTitles,
|
|
302
|
+
[cardId]: cardTitle(cardSchema, shown.kind, fieldValues(live.payloadItems), undefined)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
function titled(c: CardModel): CardModel {
|
|
306
|
+
const t = liveTitles[c.id];
|
|
307
|
+
return t == null ? c : { ...c, titlePlaceholder: t };
|
|
265
308
|
}
|
|
266
309
|
|
|
267
310
|
// ── Commit routing ──────────────────────────────────────────────────────────
|
|
@@ -423,10 +466,11 @@
|
|
|
423
466
|
// Ids are never reused, so an entry keyed to the gone card is unreachable rather
|
|
424
467
|
// than mis-attributed to whichever card takes this position: it needs dropping
|
|
425
468
|
// because nothing else will ever clear it.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
469
|
+
const prefix = `${id}:`;
|
|
470
|
+
const stale = [...commitErrors.keys()].filter((k) => k.startsWith(prefix));
|
|
471
|
+
// Guarded: `editCommitErrors` clones the map, and a removal matching nothing has
|
|
472
|
+
// nothing to re-derive.
|
|
473
|
+
if (stale.length) editCommitErrors((m) => stale.forEach((k) => m.delete(k)));
|
|
430
474
|
// No addr: the removed card has none left and every survivor's shifted. The
|
|
431
475
|
// stack changed, not a leaf, and the id is the only handle the removal leaves.
|
|
432
476
|
bump('structure', id);
|
|
@@ -435,7 +479,12 @@
|
|
|
435
479
|
const i = cardIndexOf(id);
|
|
436
480
|
if (i < 0) return;
|
|
437
481
|
try {
|
|
438
|
-
|
|
482
|
+
// Insert the retyped card ahead of the original and drop the original after.
|
|
483
|
+
// `insertCard` validates the whole card before it mutates and `removeCard`
|
|
484
|
+
// cannot throw, so a refusal leaves the document as it was; the reverse order
|
|
485
|
+
// would leave it a card short.
|
|
486
|
+
doc.insertCard({ ...doc.card(i), kind }, i);
|
|
487
|
+
doc.removeCard(i + 1);
|
|
439
488
|
bump('structure', id, i);
|
|
440
489
|
} catch (e) {
|
|
441
490
|
reportError(onError, {
|
|
@@ -536,15 +585,13 @@
|
|
|
536
585
|
isMain: boolean,
|
|
537
586
|
kind: string,
|
|
538
587
|
card: {
|
|
539
|
-
payloadItems:
|
|
588
|
+
payloadItems: readonly PayloadItem[];
|
|
540
589
|
ext?: Record<string, unknown>;
|
|
541
590
|
},
|
|
542
591
|
cardSchema: Parameters<typeof fieldModels>[0] | undefined,
|
|
543
592
|
rows: ResolvedCardRows
|
|
544
593
|
): CardModel {
|
|
545
|
-
const values
|
|
546
|
-
for (const p of card.payloadItems)
|
|
547
|
-
if (p.type === 'field' && p.key != null) values[p.key] = p.value;
|
|
594
|
+
const values = fieldValues(card.payloadItems);
|
|
548
595
|
const fields = cardSchema ? fieldModels(cardSchema) : [];
|
|
549
596
|
const sections = cardSchema
|
|
550
597
|
? groupSections(fields, groupOrder(cardSchema), (g) => groupLabel(cardSchema, g))
|
|
@@ -588,12 +635,12 @@
|
|
|
588
635
|
// chrome, so a resolve failure degrades to no ghosts, never a blank form.
|
|
589
636
|
let resolved: Resolved | undefined;
|
|
590
637
|
try {
|
|
591
|
-
resolved = quill.
|
|
638
|
+
resolved = quill.reader(doc).resolve();
|
|
592
639
|
} catch (e) {
|
|
593
640
|
reportError(onError, {
|
|
594
641
|
code: 'resolve-failed',
|
|
595
642
|
severity: 'error',
|
|
596
|
-
message: `
|
|
643
|
+
message: `reader.resolve threw; ghosted defaults fall back to none: ${errorMessage(e)}`,
|
|
597
644
|
cause: e
|
|
598
645
|
});
|
|
599
646
|
}
|
|
@@ -807,7 +854,7 @@
|
|
|
807
854
|
<div class="qm-primary">
|
|
808
855
|
<Card
|
|
809
856
|
bind:this={mainCard}
|
|
810
|
-
card={model.main}
|
|
857
|
+
card={titled(model.main)}
|
|
811
858
|
{doc}
|
|
812
859
|
{quill}
|
|
813
860
|
isFirst={true}
|
|
@@ -839,7 +886,7 @@
|
|
|
839
886
|
<div class="qm-card-slot" bind:this={slotEls[i]} animate:reorder={isReordering}>
|
|
840
887
|
<Card
|
|
841
888
|
bind:this={cardRefs[i]}
|
|
842
|
-
card={c}
|
|
889
|
+
card={titled(c)}
|
|
843
890
|
{doc}
|
|
844
891
|
{quill}
|
|
845
892
|
isFirst={i === 0}
|
package/dist/visual/controls.css
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
Every selector here is `:where()`-wrapped, so a recipe computes at specificity zero.
|
|
13
13
|
That is the point twice over: a consumer's stylesheet beats the chrome without
|
|
14
14
|
`!important`, and a component's own scoped block overrides its shared base —
|
|
15
|
-
`.qm-card-delete` recolours a `.qm-chip` under the pointer
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
`.qm-card-delete` recolours a `.qm-chip` under the pointer — with no specificity
|
|
16
|
+
fight. Unlayered with it, so a host's own layered reset never reaches these rules
|
|
17
|
+
whatever order the sheets load in (`core/theme.css`). */
|
|
18
18
|
|
|
19
19
|
/* The themed focus ring in place of the raw UA outline, as a
|
|
20
20
|
rule rather than a pair of rungs each control assembles. `--_qm-ring`
|
|
@@ -63,11 +63,10 @@
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/* A typed value's box (every scalar control and every prose leaf): the text and
|
|
66
|
-
numeric inputs, the
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
edited, and nothing raises it (no literal is minted, so `check:style` stays
|
|
66
|
+
numeric inputs, the enum trigger, the date field, and the inline richtext leaves.
|
|
67
|
+
One rule, because a leaf that agrees with the input beside it "by construction" has
|
|
68
|
+
to draw the same declarations, not a copy of them: five hand-kept copies agree
|
|
69
|
+
until one is edited, and nothing raises it (no literal is minted, so `check:style` stays
|
|
71
70
|
green while a control renders subtly the wrong height).
|
|
72
71
|
|
|
73
72
|
Leading sits on the wide list rather than on the box's own, because a box's
|
|
@@ -91,7 +90,7 @@
|
|
|
91
90
|
color: var(--_qm-ink);
|
|
92
91
|
caret-color: currentColor;
|
|
93
92
|
}
|
|
94
|
-
/* The box is
|
|
93
|
+
/* The box is a fill and not an edge (ARCHITECTURE §Styling), and the fill is the
|
|
95
94
|
well rung: a field is a hole cut into the card rather than a plane of its own,
|
|
96
95
|
and with no edge to carry it the step is the whole of what says so. */
|
|
97
96
|
:where(.qm-control-box),
|
package/dist/visual/ext.d.ts
CHANGED
|
@@ -4,23 +4,22 @@ import type { Document, CardAddr } from '@quillmark/wasm';
|
|
|
4
4
|
* main). A key whose patch value is `undefined` is dropped; every other key in the
|
|
5
5
|
* namespace is carried through untouched.
|
|
6
6
|
*
|
|
7
|
-
* **The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* merely documented: key N+1 inherits the merge instead of re-deriving it.
|
|
7
|
+
* **The whole map is the write unit.** `storeExt` replaces `$ext` entire, so a writer
|
|
8
|
+
* that stores only its own key silently destroys every sibling namespace, and a
|
|
9
|
+
* namespace-replacing write destroys the sibling keys inside it. `tips` and `title`
|
|
10
|
+
* are siblings here, so either would wipe every renamed card's title. Routing every
|
|
11
|
+
* writer through this function is what makes that unexpressible rather than merely
|
|
12
|
+
* documented: key N+1 inherits the merge instead of re-deriving it.
|
|
14
13
|
*
|
|
15
14
|
* The drop is an explicit `delete`, not a stored `undefined`: whether a JS
|
|
16
15
|
* `undefined` survives the wasm-bindgen crossing is not a property worth depending
|
|
17
16
|
* on.
|
|
18
17
|
*
|
|
19
|
-
* **A namespace emptied of keys is removed, not stored empty
|
|
20
|
-
* `
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
18
|
+
* **A namespace emptied of keys is removed, not stored empty**, and `$ext` goes with
|
|
19
|
+
* it when `editor` was the last namespace — `removeExt` being the one call that drops
|
|
20
|
+
* the map, where `storeExt({})` records an explicit empty one. So a document nothing
|
|
21
|
+
* has renamed or hinted carries no editor slot rather than an empty one. A patch
|
|
22
|
+
* dropping keys the namespace does not have writes nothing, so dismissing tips on a
|
|
23
|
+
* document that carries none is not a mutation.
|
|
25
24
|
*/
|
|
26
25
|
export declare function patchEditorExt(doc: Document, addr: CardAddr, patch: Record<string, unknown>): void;
|
package/dist/visual/ext.js
CHANGED
|
@@ -3,29 +3,27 @@
|
|
|
3
3
|
* main). A key whose patch value is `undefined` is dropped; every other key in the
|
|
4
4
|
* namespace is carried through untouched.
|
|
5
5
|
*
|
|
6
|
-
* **The
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* merely documented: key N+1 inherits the merge instead of re-deriving it.
|
|
6
|
+
* **The whole map is the write unit.** `storeExt` replaces `$ext` entire, so a writer
|
|
7
|
+
* that stores only its own key silently destroys every sibling namespace, and a
|
|
8
|
+
* namespace-replacing write destroys the sibling keys inside it. `tips` and `title`
|
|
9
|
+
* are siblings here, so either would wipe every renamed card's title. Routing every
|
|
10
|
+
* writer through this function is what makes that unexpressible rather than merely
|
|
11
|
+
* documented: key N+1 inherits the merge instead of re-deriving it.
|
|
13
12
|
*
|
|
14
13
|
* The drop is an explicit `delete`, not a stored `undefined`: whether a JS
|
|
15
14
|
* `undefined` survives the wasm-bindgen crossing is not a property worth depending
|
|
16
15
|
* on.
|
|
17
16
|
*
|
|
18
|
-
* **A namespace emptied of keys is removed, not stored empty
|
|
19
|
-
* `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
17
|
+
* **A namespace emptied of keys is removed, not stored empty**, and `$ext` goes with
|
|
18
|
+
* it when `editor` was the last namespace — `removeExt` being the one call that drops
|
|
19
|
+
* the map, where `storeExt({})` records an explicit empty one. So a document nothing
|
|
20
|
+
* has renamed or hinted carries no editor slot rather than an empty one. A patch
|
|
21
|
+
* dropping keys the namespace does not have writes nothing, so dismissing tips on a
|
|
22
|
+
* document that carries none is not a mutation.
|
|
24
23
|
*/
|
|
25
24
|
export function patchEditorExt(doc, addr, patch) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const current = doc.getExtNamespace(addr, 'editor');
|
|
25
|
+
const ext = doc.getExt(addr) ?? {};
|
|
26
|
+
const current = ext.editor;
|
|
29
27
|
const next = { ...(current ?? {}) };
|
|
30
28
|
for (const [key, value] of Object.entries(patch)) {
|
|
31
29
|
if (value === undefined)
|
|
@@ -34,7 +32,12 @@ export function patchEditorExt(doc, addr, patch) {
|
|
|
34
32
|
next[key] = value;
|
|
35
33
|
}
|
|
36
34
|
if (Object.keys(next).length > 0)
|
|
37
|
-
doc.
|
|
38
|
-
else if (current)
|
|
39
|
-
|
|
35
|
+
doc.storeExt(addr, { ...ext, editor: next });
|
|
36
|
+
else if (current) {
|
|
37
|
+
const { editor: _editor, ...rest } = ext;
|
|
38
|
+
if (Object.keys(rest).length > 0)
|
|
39
|
+
doc.storeExt(addr, rest);
|
|
40
|
+
else
|
|
41
|
+
doc.removeExt(addr);
|
|
42
|
+
}
|
|
40
43
|
}
|
package/dist/visual/index.js
CHANGED
|
@@ -25,7 +25,6 @@ export { createField } from '../core/codec/index.js';
|
|
|
25
25
|
// it. The empty body's per-card hook is the `bodyPlaceholder` key inside it.
|
|
26
26
|
export { DEFAULT_VISUAL_STRINGS } from './strings.js';
|
|
27
27
|
// The `$ext.editor` write unit. A consumer seeding editor-side chrome
|
|
28
|
-
// state (a card title, the tips channel) goes through this rather than
|
|
29
|
-
//
|
|
30
|
-
// it.
|
|
28
|
+
// state (a card title, the tips channel) goes through this rather than `storeExt`,
|
|
29
|
+
// which replaces the whole map and takes every sibling with it.
|
|
31
30
|
export { patchEditorExt } from './ext.js';
|
package/dist/visual/links.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ import type { Command, EditorState } from 'prosemirror-state';
|
|
|
9
9
|
* A rooted value is left alone, being the spelling that asks for the embedding
|
|
10
10
|
* page, and so is anything already carrying a scheme the link mark renders.
|
|
11
11
|
*
|
|
12
|
+
* `localhost:5173` names a host too, and `SCHEME` reads its host as the scheme: a
|
|
13
|
+
* colon followed by a port is a host with a port, so it takes the host prefix rather
|
|
14
|
+
* than the refusal a scheme nothing renders gets.
|
|
15
|
+
*
|
|
12
16
|
* `''` for a blank value and for a scheme the mark renders inert, both being nothing
|
|
13
17
|
* to apply: `setLink` declines an empty href, so the refusal lands at the prompt's
|
|
14
18
|
* own exit rather than storing a link that draws as plain text.
|
package/dist/visual/links.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { rendersHref } from '../core/codec/index.js';
|
|
1
|
+
import { rendersHref, storableUrl } from '../core/codec/index.js';
|
|
2
2
|
/** A scheme: a letter, then letters, digits, `+`, `-` or `.`, then `:` (RFC 3986). */
|
|
3
3
|
const SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
4
4
|
/** Rooted at the page rather than at a host: an absolute path, a query, a fragment,
|
|
@@ -6,6 +6,9 @@ const SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
|
6
6
|
const ROOTED = /^[/#?]/;
|
|
7
7
|
/** A bare mail address: one `@`, no scheme and no path. */
|
|
8
8
|
const ADDRESS = /^[^\s/@]+@[^\s/@]+$/;
|
|
9
|
+
/** A host carrying a port, which spells a scheme to `SCHEME` and is not one: what
|
|
10
|
+
* follows the colon is digits, and no scheme the mark renders takes a port. */
|
|
11
|
+
const HOST_PORT = /^[a-z0-9.-]+:\d+([/?#]|$)/i;
|
|
9
12
|
/**
|
|
10
13
|
* The href a typed value stands for. A value with no scheme names a host, and
|
|
11
14
|
* stored verbatim it is a relative href resolving against whatever page the editor
|
|
@@ -16,15 +19,23 @@ const ADDRESS = /^[^\s/@]+@[^\s/@]+$/;
|
|
|
16
19
|
* A rooted value is left alone, being the spelling that asks for the embedding
|
|
17
20
|
* page, and so is anything already carrying a scheme the link mark renders.
|
|
18
21
|
*
|
|
22
|
+
* `localhost:5173` names a host too, and `SCHEME` reads its host as the scheme: a
|
|
23
|
+
* colon followed by a port is a host with a port, so it takes the host prefix rather
|
|
24
|
+
* than the refusal a scheme nothing renders gets.
|
|
25
|
+
*
|
|
19
26
|
* `''` for a blank value and for a scheme the mark renders inert, both being nothing
|
|
20
27
|
* to apply: `setLink` declines an empty href, so the refusal lands at the prompt's
|
|
21
28
|
* own exit rather than storing a link that draws as plain text.
|
|
22
29
|
*/
|
|
23
30
|
export function normalizeHref(raw) {
|
|
24
|
-
const value = raw.trim();
|
|
25
|
-
if (!value
|
|
31
|
+
const value = storableUrl(raw.trim());
|
|
32
|
+
if (!value)
|
|
33
|
+
return '';
|
|
34
|
+
if (!rendersHref(value) && !HOST_PORT.test(value))
|
|
26
35
|
return '';
|
|
27
|
-
if (SCHEME.test(value)
|
|
36
|
+
if (SCHEME.test(value) && rendersHref(value))
|
|
37
|
+
return value;
|
|
38
|
+
if (ROOTED.test(value))
|
|
28
39
|
return value;
|
|
29
40
|
return ADDRESS.test(value) ? `mailto:${value}` : `https://${value}`;
|
|
30
41
|
}
|
package/dist/visual/strings.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
16
16
|
cardTitle: string;
|
|
17
17
|
/** The trailing strip's visible words, which are also every strip's accessible
|
|
18
18
|
* name; the plus rides the wording. One string over
|
|
19
|
-
* one kind and many: the trigger seeds
|
|
19
|
+
* one kind and many: the trigger seeds a card, and which kind is the menu's word
|
|
20
20
|
* to say or the schema's to have already settled. */
|
|
21
21
|
addCard: string;
|
|
22
22
|
arrayAdd: string;
|
|
@@ -64,6 +64,9 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
64
64
|
retypeLabel: string;
|
|
65
65
|
retypePlaceholder: string;
|
|
66
66
|
noCardKinds: string;
|
|
67
|
+
/** The body leaf's accessible name: no label names it, the body being the one
|
|
68
|
+
* surface in a card that is paper ({@link Card}). */
|
|
69
|
+
bodyLabel: string;
|
|
67
70
|
/**
|
|
68
71
|
* The flat built-in invitation: the package's own words, kept to the one thing
|
|
69
72
|
* true of every body so it claims nothing about the card it sits in.
|
|
@@ -91,9 +94,9 @@ export interface VisualStrings extends TableChromeStrings, SlashStrings {
|
|
|
91
94
|
* - **`edit::field_coercion_failed`**: re-words from the app's own control state, the
|
|
92
95
|
* refused value being in neither document (unchanged on throw) nor schema.
|
|
93
96
|
* - **`parse::yaml_error_with_location`, `invalid_structure`**: does not re-word. No
|
|
94
|
-
* `path
|
|
95
|
-
* engine prose riding under no key: the parser's own
|
|
96
|
-
* snippet exist only inside `message`.
|
|
97
|
+
* `path`; `args` names the block (`blockIndex`) and `location` places the failure in
|
|
98
|
+
* the document's own coordinates, engine prose riding under no key: the parser's own
|
|
99
|
+
* text, its column and its caret snippet exist only inside `message`.
|
|
97
100
|
* - **`LiveSession.warnings`**: does not re-word. Backend text, an external feed.
|
|
98
101
|
*
|
|
99
102
|
* The last two are the boundary's shape, not a gap in it, so the fallback arm is
|
package/dist/visual/strings.js
CHANGED
|
@@ -55,6 +55,7 @@ export const DEFAULT_VISUAL_STRINGS = {
|
|
|
55
55
|
retypeLabel: 'Change to',
|
|
56
56
|
retypePlaceholder: 'Choose a type…',
|
|
57
57
|
noCardKinds: 'This document declares no card types — delete this card to remove it.',
|
|
58
|
+
bodyLabel: 'Body',
|
|
58
59
|
bodyGhost: 'Write…',
|
|
59
60
|
bodyPlaceholder: () => undefined
|
|
60
61
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QuillCardSchema, QuillFieldSchema, ResolvedField, Resolved } from '@quillmark/wasm';
|
|
1
|
+
import type { PayloadItem, QuillCardSchema, QuillFieldSchema, ResolvedField, Resolved } from '@quillmark/wasm';
|
|
2
2
|
/** The control a field type maps to (VISUAL_EDITOR §"Structure mirrors the schema"). */
|
|
3
3
|
export type ControlKind = 'prose' | 'text' | 'enum' | 'variant' | 'number' | 'boolean' | 'date' | 'array' | 'object';
|
|
4
4
|
/** One field, projected: its schema, the control it renders as, and its layout hints. */
|
|
@@ -233,8 +233,16 @@ export interface PlacedField {
|
|
|
233
233
|
* Pure, and stays pure: no width, no measurement, nothing to re-derive on resize.
|
|
234
234
|
*/
|
|
235
235
|
export declare function placeFields(fields: FieldModel[]): PlacedField[];
|
|
236
|
+
/** A card's field values by key, off its payload items. */
|
|
237
|
+
export declare function fieldValues(items: readonly PayloadItem[]): Record<string, unknown>;
|
|
236
238
|
/** Interpolate a `{field}` card-title template against live field values. */
|
|
237
239
|
export declare function interpolateTitle(template: string, values: Record<string, unknown>): string;
|
|
240
|
+
/** A field value as title text. A parsed field rests as the authored string and a
|
|
241
|
+
* committed one as `Content`, whose `text` is the same words; a scalar reads as
|
|
242
|
+
* itself and any other container as nothing. */
|
|
243
|
+
export declare function titleText(v: unknown): string;
|
|
244
|
+
/** The field names a `{field}` title reads. */
|
|
245
|
+
export declare function titleFields(template: string | undefined): string[];
|
|
238
246
|
/**
|
|
239
247
|
* Resolve a card instance's header title: the per-instance `$ext.editor.title`
|
|
240
248
|
* override wins; else the schema `ui.title` (literal or `{field}` template
|
package/dist/visual/structure.js
CHANGED
|
@@ -268,12 +268,34 @@ export function placeFields(fields) {
|
|
|
268
268
|
flush();
|
|
269
269
|
return out;
|
|
270
270
|
}
|
|
271
|
+
/** A card's field values by key, off its payload items. */
|
|
272
|
+
export function fieldValues(items) {
|
|
273
|
+
const values = {};
|
|
274
|
+
for (const p of items)
|
|
275
|
+
if (p.type === 'field')
|
|
276
|
+
values[p.key] = p.value;
|
|
277
|
+
return values;
|
|
278
|
+
}
|
|
271
279
|
/** Interpolate a `{field}` card-title template against live field values. */
|
|
272
280
|
export function interpolateTitle(template, values) {
|
|
273
|
-
return template.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) =>
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
281
|
+
return template.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_m, name) => titleText(values[name]));
|
|
282
|
+
}
|
|
283
|
+
/** A field value as title text. A parsed field rests as the authored string and a
|
|
284
|
+
* committed one as `Content`, whose `text` is the same words; a scalar reads as
|
|
285
|
+
* itself and any other container as nothing. */
|
|
286
|
+
export function titleText(v) {
|
|
287
|
+
if (v == null)
|
|
288
|
+
return '';
|
|
289
|
+
if (typeof v !== 'object')
|
|
290
|
+
return String(v);
|
|
291
|
+
const text = v.text;
|
|
292
|
+
return typeof text === 'string' ? text : '';
|
|
293
|
+
}
|
|
294
|
+
/** The field names a `{field}` title reads. */
|
|
295
|
+
export function titleFields(template) {
|
|
296
|
+
if (!template)
|
|
297
|
+
return [];
|
|
298
|
+
return [...template.matchAll(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g)].map((m) => m[1]);
|
|
277
299
|
}
|
|
278
300
|
/**
|
|
279
301
|
* Resolve a card instance's header title: the per-instance `$ext.editor.title`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillmark/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Editor, live-preview and source surfaces over a Quillmark WASM session.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@quillmark/wasm": ">=0.
|
|
69
|
+
"@quillmark/wasm": ">=0.113.0-0",
|
|
70
70
|
"svelte": "^5.0.0"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|