@seamlet/astro 0.6.0 → 0.8.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.
@@ -5,6 +5,9 @@ function defineSchema(fields) {
5
5
  function text(options) {
6
6
  return { kind: "text", ...options };
7
7
  }
8
+ function richText(options) {
9
+ return { kind: "richText", ...options };
10
+ }
8
11
  function number(options) {
9
12
  return { kind: "number", ...options };
10
13
  }
@@ -54,6 +57,7 @@ function defaultValue(field) {
54
57
  return false;
55
58
  case "choices":
56
59
  case "references":
60
+ case "richText":
57
61
  return [];
58
62
  case "number":
59
63
  case "date":
@@ -147,6 +151,102 @@ function withoutPrefix(address, prefix) {
147
151
  return hasPrefix ? address.slice(prefix.length) : void 0;
148
152
  }
149
153
 
154
+ // src/schema/rich-text.ts
155
+ var PARAGRAPH_BREAK = /\n\s*\n/u;
156
+ var PARAGRAPH_SEPARATOR = "\n\n";
157
+ var LINE_SEPARATOR = "\n";
158
+ var SPACE = " ";
159
+ function richTextOf(plain) {
160
+ return plain.split(PARAGRAPH_BREAK).map((paragraph) => paragraph.trim()).filter((paragraph) => paragraph !== "").map((paragraph) => ({ kind: "paragraph", spans: [{ text: paragraph }] }));
161
+ }
162
+ function plainTextOf(text2) {
163
+ return text2.map(plainBlockOf).join(PARAGRAPH_SEPARATOR);
164
+ }
165
+ function linkAddressesOf(text2) {
166
+ return linesOf(text2).flatMap(
167
+ (line) => line.flatMap((span) => span.href === void 0 ? [] : [span.href])
168
+ );
169
+ }
170
+ function asOneParagraph(text2) {
171
+ const lines = linesOf(text2).filter((line) => line.length > 0);
172
+ if (lines.length === 0) return [];
173
+ const spans = lines.flatMap(
174
+ (line, index) => index === 0 ? line : [{ text: SPACE }, ...line]
175
+ );
176
+ return [{ kind: "paragraph", spans: normalizedLine(spans) }];
177
+ }
178
+ function readRichText(input) {
179
+ if (!Array.isArray(input)) return void 0;
180
+ const blocks = input.map(readBlock);
181
+ const isEveryBlockRead = blocks.every((block) => block !== void 0);
182
+ if (!isEveryBlockRead) return void 0;
183
+ return blocks.filter(hasWords);
184
+ }
185
+ function normalizedLine(spans) {
186
+ return spans.filter((span) => span.text !== "").reduce((joined, span) => {
187
+ const previous = joined.at(-1);
188
+ if (previous !== void 0 && haveSameMarks(previous, span)) {
189
+ return [
190
+ ...joined.slice(0, -1),
191
+ { ...previous, text: previous.text + span.text }
192
+ ];
193
+ }
194
+ return [...joined, span];
195
+ }, []);
196
+ }
197
+ function readBlock(input) {
198
+ if (!isObject(input)) return void 0;
199
+ if (input["kind"] === "paragraph") {
200
+ const spans = readLine(input["spans"]);
201
+ return spans === void 0 ? void 0 : { kind: "paragraph", spans };
202
+ }
203
+ if (input["kind"] === "list" && Array.isArray(input["items"])) {
204
+ const items = input["items"].map(readLine);
205
+ const isEveryItemRead = items.every((item) => item !== void 0);
206
+ if (!isEveryItemRead) return void 0;
207
+ return { kind: "list", items: items.filter((item) => item.length > 0) };
208
+ }
209
+ return void 0;
210
+ }
211
+ function readLine(input) {
212
+ if (!Array.isArray(input)) return void 0;
213
+ const spans = input.map(readSpan);
214
+ const isEverySpanRead = spans.every((span) => span !== void 0);
215
+ return isEverySpanRead ? normalizedLine(spans) : void 0;
216
+ }
217
+ function readSpan(input) {
218
+ if (!isObject(input)) return void 0;
219
+ const { text: text2, bold, italic, href } = input;
220
+ const hasValidMarks = typeof text2 === "string" && (bold === void 0 || bold === true) && (italic === void 0 || italic === true) && (href === void 0 || typeof href === "string");
221
+ if (!hasValidMarks) return void 0;
222
+ return {
223
+ text: text2,
224
+ ...bold === true ? { bold } : {},
225
+ ...italic === true ? { italic } : {},
226
+ ...typeof href === "string" ? { href } : {}
227
+ };
228
+ }
229
+ function haveSameMarks(first, second) {
230
+ return first.bold === second.bold && first.italic === second.italic && first.href === second.href;
231
+ }
232
+ function hasWords(block) {
233
+ return block.kind === "paragraph" ? block.spans.length > 0 : block.items.length > 0;
234
+ }
235
+ function linesOf(text2) {
236
+ return text2.flatMap(
237
+ (block) => block.kind === "paragraph" ? [block.spans] : block.items
238
+ );
239
+ }
240
+ function plainBlockOf(block) {
241
+ return block.kind === "paragraph" ? plainLineOf(block.spans) : block.items.map(plainLineOf).join(LINE_SEPARATOR);
242
+ }
243
+ function plainLineOf(line) {
244
+ return line.map((span) => span.text).join("");
245
+ }
246
+ function isObject(value) {
247
+ return typeof value === "object" && value !== null && !Array.isArray(value);
248
+ }
249
+
150
250
  // src/schema/record-ids.ts
151
251
  var RECORD_ID_KEY = "id";
152
252
  var RECORD_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/u;
@@ -240,6 +340,7 @@ function issuesInField(field, value, path, idsOf) {
240
340
  (record, index) => issuesInFields(field.item, record, [...path, index], idsOf)
241
341
  );
242
342
  case "text":
343
+ case "richText":
243
344
  case "number":
244
345
  case "date":
245
346
  case "image":
@@ -312,6 +413,8 @@ function parseField(field, input, path) {
312
413
  switch (field.kind) {
313
414
  case "text":
314
415
  return parseText(field, input, path);
416
+ case "richText":
417
+ return parseRichText(field, input, path);
315
418
  case "number":
316
419
  return parseNumber(field, input, path);
317
420
  case "date":
@@ -353,6 +456,24 @@ function parseText(field, input, path) {
353
456
  }
354
457
  return valid(input);
355
458
  }
459
+ function parseRichText(field, input, path) {
460
+ const read = typeof input === "string" ? richTextOf(input) : readRichText(input);
461
+ if (read === void 0) {
462
+ return invalidType(defaultValue(field), path);
463
+ }
464
+ const text2 = field.inline === true ? asOneParagraph(read) : read;
465
+ const hasUnsupportedLink = linkAddressesOf(text2).some(
466
+ (href) => linkTargetOf(href) === void 0
467
+ );
468
+ const linkIssues = hasUnsupportedLink ? [{ path, code: "unsupported-link-href" }] : [];
469
+ const lengthIssues = tooLongIssues(field.maxLength, plainTextOf(text2), path);
470
+ return { value: text2, issues: [...linkIssues, ...lengthIssues] };
471
+ }
472
+ function tooLongIssues(maxLength, text2, path) {
473
+ if (maxLength === void 0) return [];
474
+ const isTooLong = countCharacters(text2) > maxLength;
475
+ return isTooLong ? [{ path, code: "too-long", maxLength }] : [];
476
+ }
356
477
  function parseNumber(field, input, path) {
357
478
  if (input === null) {
358
479
  return valid(null);
@@ -668,6 +789,7 @@ function openingHoursField(options = {}) {
668
789
  export {
669
790
  defineSchema,
670
791
  text,
792
+ richText,
671
793
  number,
672
794
  date,
673
795
  image,
@@ -685,6 +807,11 @@ export {
685
807
  defaultContent,
686
808
  linkTargetOf,
687
809
  hrefOf,
810
+ richTextOf,
811
+ plainTextOf,
812
+ asOneParagraph,
813
+ readRichText,
814
+ normalizedLine,
688
815
  RECORD_ID_KEY,
689
816
  isRecordId,
690
817
  newRecord,
@@ -6,6 +6,18 @@ interface TextField {
6
6
  /** In characters as people see them (see countCharacters), not UTF-16 units. */
7
7
  readonly maxLength?: number;
8
8
  }
9
+ /**
10
+ * A text with paragraphs, lists, bold, italic and links inside it: a paragraph with a link in a
11
+ * sentence stays one field. `inline` keeps it one paragraph without lists, for a text inside an
12
+ * element that holds one line of words.
13
+ */
14
+ interface RichTextField {
15
+ readonly kind: "richText";
16
+ readonly label: string;
17
+ readonly inline?: boolean;
18
+ /** In characters of its words as people see them, without the marks. */
19
+ readonly maxLength?: number;
20
+ }
9
21
  interface NumberField {
10
22
  readonly kind: "number";
11
23
  readonly label: string;
@@ -96,7 +108,7 @@ interface CollectionField<F extends RecordFields = RecordFields> {
96
108
  /** The field of a record that names it in the editor: a text field, or a link by its text. */
97
109
  readonly itemTitle?: keyof F & string;
98
110
  }
99
- type Field = TextField | NumberField | DateField | ImageField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | ReferenceField | ReferencesField | GroupField | CollectionField;
111
+ type Field = TextField | RichTextField | NumberField | DateField | ImageField | LinkField | ToggleField | ChoiceField | ChoicesField | UrlField | TimeField | ReferenceField | ReferencesField | GroupField | CollectionField;
100
112
  type FieldKind = Field["kind"];
101
113
  /** Field names to fields; the order of the keys is the order of the form. */
102
114
  interface Fields {
@@ -110,6 +122,7 @@ type FieldOptions<F extends Field> = Omit<F, "kind">;
110
122
  /** The root of a site's schema. Only checks the shape and keeps the literal types. */
111
123
  declare function defineSchema<F extends Fields>(fields: F): F;
112
124
  declare function text(options: FieldOptions<TextField>): TextField;
125
+ declare function richText(options: FieldOptions<RichTextField>): RichTextField;
113
126
  declare function number(options: FieldOptions<NumberField>): NumberField;
114
127
  declare function date(options: FieldOptions<DateField>): DateField;
115
128
  declare function image(options: FieldOptions<ImageField>): ImageField;
@@ -125,6 +138,37 @@ declare function references(options: FieldOptions<ReferencesField>): ReferencesF
125
138
  declare function group<F extends Fields>(options: FieldOptions<GroupField<F>>): GroupField<F>;
126
139
  declare function collection<F extends RecordFields>(options: FieldOptions<CollectionField<F>>): CollectionField<F>;
127
140
 
141
+ /** A piece of a text with its marks; a link is a piece with an address. */
142
+ interface RichSpan {
143
+ readonly text: string;
144
+ readonly bold?: true;
145
+ readonly italic?: true;
146
+ /** A path of the site, a web address, `tel:` or `mailto:`; see linkTargetOf. */
147
+ readonly href?: string;
148
+ }
149
+ type RichLine = readonly RichSpan[];
150
+ type RichBlock = {
151
+ readonly kind: "paragraph";
152
+ readonly spans: RichLine;
153
+ } | {
154
+ readonly kind: "list";
155
+ readonly items: readonly RichLine[];
156
+ };
157
+ type RichText = readonly RichBlock[];
158
+ /** A plain text as paragraphs: how a text field becomes formatted text and keeps its words. */
159
+ declare function richTextOf(plain: string): RichText;
160
+ /** The words of a text without its marks: for a length, a search, a summary. */
161
+ declare function plainTextOf(text: RichText): string;
162
+ /** One paragraph of all the text: a text that stands inside a sentence-level element. */
163
+ declare function asOneParagraph(text: RichText): RichText;
164
+ /**
165
+ * Formatted text read from any input, or undefined when it is not one: the same text gives the
166
+ * same value, with empty pieces left out and neighbors with the same marks joined.
167
+ */
168
+ declare function readRichText(input: unknown): RichText | undefined;
169
+ /** The same line with empty pieces left out and neighbors with the same marks joined. */
170
+ declare function normalizedLine(spans: readonly RichSpan[]): RichLine;
171
+
128
172
  /** The point of a photo that must stay in view when a site crops it: 0 to 1 from the left and the top. */
129
173
  interface ImageFocus {
130
174
  readonly x: number;
@@ -145,7 +189,7 @@ interface LinkValue {
145
189
  * The value of each field. A text can be empty (""), the other scalar fields can be unset (null):
146
190
  * content is filled in by people, so a site must be ready for a missing price or date.
147
191
  */
148
- type ValueOf<F extends Field> = F extends TextField ? string : F extends NumberField ? number | null : F extends DateField ? string | null : F extends ImageField ? ImageValue | null : F extends LinkField ? LinkValue | null : F extends ToggleField ? boolean : F extends ChoiceField<infer Options> ? OptionValue<Options> | null : F extends ChoicesField<infer Options> ? readonly OptionValue<Options>[] : F extends UrlField ? string | null : F extends TimeField ? string | null : F extends ReferenceField ? string | null : F extends ReferencesField ? readonly string[] : F extends GroupField<infer Group> ? ContentOf<Group> : F extends CollectionField<infer Item> ? readonly RecordOf<Item>[] : never;
192
+ type ValueOf<F extends Field> = F extends TextField ? string : F extends RichTextField ? RichText : F extends NumberField ? number | null : F extends DateField ? string | null : F extends ImageField ? ImageValue | null : F extends LinkField ? LinkValue | null : F extends ToggleField ? boolean : F extends ChoiceField<infer Options> ? OptionValue<Options> | null : F extends ChoicesField<infer Options> ? readonly OptionValue<Options>[] : F extends UrlField ? string | null : F extends TimeField ? string | null : F extends ReferenceField ? string | null : F extends ReferencesField ? readonly string[] : F extends GroupField<infer Group> ? ContentOf<Group> : F extends CollectionField<infer Item> ? readonly RecordOf<Item>[] : never;
149
193
  /**
150
194
  * A record of a collection: its fields and its id. The id is missing only in content no editor has
151
195
  * saved yet, such as a site's initial content (see withRecordIds).
@@ -163,8 +207,8 @@ type ContentOf<F extends Fields> = {
163
207
  interface Content {
164
208
  readonly [name: string]: FieldValue;
165
209
  }
166
- type FieldValue = string | number | boolean | null | readonly string[] | ImageValue | LinkValue | Content | readonly Content[];
210
+ type FieldValue = string | number | boolean | null | readonly string[] | ImageValue | LinkValue | RichText | Content | readonly Content[];
167
211
  declare function defaultValue(field: Field): FieldValue;
168
212
  declare function defaultContent(fields: Fields): Content;
169
213
 
170
- export { number as A, reference as B, type ContentOf as C, type DateField as D, references as E, type Fields as F, type GroupField as G, text as H, type ImageField as I, time as J, toggle as K, type LinkValue as L, url as M, type NumberField as N, type OptionValue as O, type RecordFields as R, type TextField as T, type UrlField as U, type ValueOf as V, type Content as a, type ImageValue as b, type Field as c, type CollectionField as d, type LinkField as e, type ChoicesField as f, type TimeField as g, type ChoiceField as h, type ChoiceOption as i, type FieldKind as j, type FieldValue as k, type ImageFocus as l, type RecordOf as m, type ReferenceField as n, type ReferencesField as o, type ToggleField as p, choice as q, choices as r, collection as s, date as t, defaultContent as u, defaultValue as v, defineSchema as w, group as x, image as y, link as z };
214
+ export { time as $, defaultContent as A, defaultValue as B, type ContentOf as C, type DateField as D, defineSchema as E, type Fields as F, type GroupField as G, group as H, type ImageField as I, image as J, link as K, type LinkValue as L, normalizedLine as M, type NumberField as N, type OptionValue as O, number as P, plainTextOf as Q, type RichText as R, readRichText as S, type TextField as T, type UrlField as U, type ValueOf as V, reference as W, references as X, richText as Y, richTextOf as Z, text as _, type Content as a, toggle as a0, url as a1, type ImageValue as b, type Field as c, type CollectionField as d, type RecordFields as e, type LinkField as f, type ChoicesField as g, type TimeField as h, type ChoiceField as i, type ChoiceOption as j, type FieldKind as k, type FieldValue as l, type ImageFocus as m, type RecordOf as n, type ReferenceField as o, type ReferencesField as p, type RichBlock as q, type RichLine as r, type RichSpan as s, type RichTextField as t, type ToggleField as u, asOneParagraph as v, choice as w, choices as x, collection as y, date as z };
@@ -1,5 +1,5 @@
1
- import { F as Fields, a as Content } from './content-C_WpiWBm.js';
2
- import { F as FieldPath } from './parse-content-9RIWhb6z.js';
1
+ import { R as RichText, F as Fields, a as Content } from './content-6k-E7NWf.js';
2
+ import { F as FieldPath } from './parse-content-C9ldnmvj.js';
3
3
 
4
4
  interface Box {
5
5
  readonly top: number;
@@ -17,12 +17,53 @@ declare function shortcutOf(event: KeyboardEvent): Shortcut | undefined;
17
17
 
18
18
  type InlineEditingOutcome = "kept" | "reverted";
19
19
 
20
+ interface TextPoint {
21
+ /** The paragraph or the list item, counted from the start of the text. */
22
+ readonly line: number;
23
+ /** Characters from the start of the line; a line break is one character. */
24
+ readonly offset: number;
25
+ }
26
+ interface TextSelection {
27
+ readonly start: TextPoint;
28
+ readonly end: TextPoint;
29
+ }
30
+ type Mark = "bold" | "italic";
31
+ /** What the selected words have, or the words at the cursor when nothing is selected. */
32
+ interface TextFormats {
33
+ readonly isBold: boolean;
34
+ readonly isItalic: boolean;
35
+ readonly isList: boolean;
36
+ /** The address of the link the selection is in, when all of it is in one link. */
37
+ readonly href: string | undefined;
38
+ }
39
+ /** Whether the selection holds no characters: it is a cursor. */
40
+ declare function isCollapsed(selection: TextSelection): boolean;
41
+ /** The marks every selected word has, and the link they are in. */
42
+ declare function formatsAt(text: RichText, selection: TextSelection): TextFormats;
43
+ /** The words of the link the cursor is in, or undefined outside a link. */
44
+ declare function linkSelectionAt(text: RichText, point: TextPoint): TextSelection | undefined;
45
+ /** The mark on every selected word, or off them all when every one has it already. */
46
+ declare function toggleMark(text: RichText, selection: TextSelection, mark: Mark): RichText;
47
+ /** The selected words lead to the address, or lead nowhere when it is undefined. */
48
+ declare function setLink(text: RichText, selection: TextSelection, href: string | undefined): RichText;
49
+ /** The selected lines become a list, or paragraphs again when they all are a list already. */
50
+ declare function toggleList(text: RichText, selection: TextSelection): RichText;
51
+
52
+ /** A change of formatting the editor's toolbar asks for. */
53
+ type TextFormat = "bold" | "italic" | "list";
54
+ /** The selection in the typed text: where it is on the page and what its words have. */
55
+ interface TypedSelection {
56
+ readonly box: Box;
57
+ readonly isCollapsed: boolean;
58
+ readonly formats: TextFormats;
59
+ }
60
+
20
61
  /**
21
62
  * The version of what the two say to each other. A site updates @seamlet/astro when it likes, so the
22
63
  * editor may meet a bridge of another version: it tells by this number, said with `ready`, instead
23
64
  * of misreading the page. Raised whenever a message changes in a way the other side cannot read.
24
65
  */
25
- declare const PROTOCOL_VERSION = 1;
66
+ declare const PROTOCOL_VERSION = 2;
26
67
  interface Point {
27
68
  readonly x: number;
28
69
  readonly y: number;
@@ -99,6 +140,18 @@ type PageMessage =
99
140
  readonly type: "typed";
100
141
  readonly path: string;
101
142
  readonly text: string;
143
+ }
144
+ /** The formatted text typed in place, after every character and every change of its marks. */
145
+ | {
146
+ readonly type: "rich-typed";
147
+ readonly path: string;
148
+ readonly text: RichText;
149
+ }
150
+ /** Where the selection in the typed formatted text is now, for the editor's toolbar. */
151
+ | {
152
+ readonly type: "typed-selection";
153
+ readonly path: string;
154
+ readonly selection: TypedSelection | undefined;
102
155
  } | {
103
156
  readonly type: "typing-ended";
104
157
  readonly path: string;
@@ -127,6 +180,25 @@ type EditorMessage =
127
180
  readonly text: string;
128
181
  readonly isSingleLine: boolean;
129
182
  readonly point: Point;
183
+ }
184
+ /** Type the formatted text of the field in its element, if the element shows it as it is. */
185
+ | {
186
+ readonly type: "start-rich-typing";
187
+ readonly path: string;
188
+ readonly text: RichText;
189
+ /** The words alone, in an element of one line. */
190
+ readonly isInline: boolean;
191
+ readonly point: Point;
192
+ }
193
+ /** Mark the selected words of the formatted text typed in place, or make its lines a list. */
194
+ | {
195
+ readonly type: "format-typed";
196
+ readonly format: TextFormat;
197
+ }
198
+ /** The selected words of the typed text, or the link at the cursor, lead here, or nowhere. */
199
+ | {
200
+ readonly type: "link-typed";
201
+ readonly href: string | undefined;
130
202
  } | {
131
203
  readonly type: "stop-typing";
132
204
  }
@@ -140,4 +212,4 @@ declare function readPageMessage(data: unknown): PageMessage | undefined;
140
212
  /** The message the editor sent, or undefined for anything that is not one. */
141
213
  declare function readEditorMessage(data: unknown): EditorMessage | undefined;
142
214
 
143
- export { type Box as B, type EditorMessage as E, type InlineEditingOutcome as I, type LinkBox as L, type MarkerBox as M, type PageMessage as P, type Shortcut as S, type Viewport as V, type EditorKey as a, PROTOCOL_VERSION as b, type PageLayout as c, type Point as d, readPageMessage as e, readEditorMessage as r, shortcutOf as s };
215
+ export { type Box as B, type EditorMessage as E, type InlineEditingOutcome as I, type LinkBox as L, type Mark as M, type PageMessage as P, type Shortcut as S, type TextSelection as T, type Viewport as V, type EditorKey as a, type MarkerBox as b, PROTOCOL_VERSION as c, type PageLayout as d, type Point as e, type TextFormat as f, type TextFormats as g, type TextPoint as h, type TypedSelection as i, formatsAt as j, isCollapsed as k, linkSelectionAt as l, readPageMessage as m, shortcutOf as n, toggleMark as o, readEditorMessage as r, setLink as s, toggleList as t };
@@ -1,4 +1,4 @@
1
- import { F as Fields, C as ContentOf } from './content-C_WpiWBm.js';
1
+ import { F as Fields, C as ContentOf } from './content-6k-E7NWf.js';
2
2
 
3
3
  /** Where a value lives in the content: field names and, inside a collection, record indexes. */
4
4
  type FieldPath = readonly (string | number)[];
@@ -1,6 +1,7 @@
1
- export { B as Box, a as EditorKey, E as EditorMessage, I as InlineEditingOutcome, L as LinkBox, M as MarkerBox, b as PROTOCOL_VERSION, c as PageLayout, P as PageMessage, d as Point, S as Shortcut, V as Viewport, r as readEditorMessage, e as readPageMessage, s as shortcutOf } from './messages-CBf5diaN.js';
2
- import { F as Fields, I as ImageField } from './content-C_WpiWBm.js';
3
- import { F as FieldPath } from './parse-content-9RIWhb6z.js';
1
+ import { T as TextSelection } from './messages-Dx0jNy3N.js';
2
+ export { B as Box, a as EditorKey, E as EditorMessage, I as InlineEditingOutcome, L as LinkBox, M as Mark, b as MarkerBox, c as PROTOCOL_VERSION, d as PageLayout, P as PageMessage, e as Point, S as Shortcut, f as TextFormat, g as TextFormats, h as TextPoint, i as TypedSelection, V as Viewport, j as formatsAt, k as isCollapsed, l as linkSelectionAt, r as readEditorMessage, m as readPageMessage, s as setLink, n as shortcutOf, t as toggleList, o as toggleMark } from './messages-Dx0jNy3N.js';
3
+ import { F as Fields, I as ImageField, R as RichText } from './content-6k-E7NWf.js';
4
+ import { F as FieldPath } from './parse-content-C9ldnmvj.js';
4
5
 
5
6
  declare function carriesFiles(event: DragEvent): boolean;
6
7
 
@@ -16,6 +17,17 @@ declare function imageFieldAt(schema: Fields, path: string | undefined): {
16
17
  */
17
18
  declare function closestMarkedPath(path: FieldPath, isMarked: (path: string) => boolean): string | undefined;
18
19
 
20
+ /** The elements of a text, put into the editable element in place of what it held. */
21
+ declare function renderRichText(element: HTMLElement, text: RichText): void;
22
+ /** What the editable element holds now, as formatted text. */
23
+ declare function readRichTextFrom(element: HTMLElement): RichText;
24
+ /** Where the selection is in the text of the element, or undefined when it is elsewhere. */
25
+ declare function readSelectionFrom(element: HTMLElement): TextSelection | undefined;
26
+ /** Puts the selection on the text of an element this module rendered. */
27
+ declare function selectIn(element: HTMLElement, selection: TextSelection): void;
28
+ /** Puts plain words in place of the selection, their line breaks as `<br>`, the cursor after them. */
29
+ declare function insertPlainText(element: HTMLElement, words: string): void;
30
+
19
31
  /** Where a site hands out its own photos: `/seamlet-photos/menu/cafe/01.jpg`. */
20
32
  declare const OWN_PHOTOS_PATH = "/seamlet-photos";
21
33
  /** An address of the site itself that is not an upload, and that cannot climb out of the site. */
@@ -23,4 +35,4 @@ declare function isOwnPhotoSrc(src: string): boolean;
23
35
  /** Where the site hands out its own photo; undefined for an upload or another site's address. */
24
36
  declare function ownPhotoPath(src: string): string | undefined;
25
37
 
26
- export { OWN_PHOTOS_PATH, carriesFiles, closestMarkedPath, imageFieldAt, isOwnPhotoSrc, ownPhotoPath };
38
+ export { OWN_PHOTOS_PATH, TextSelection, carriesFiles, closestMarkedPath, imageFieldAt, insertPlainText, isOwnPhotoSrc, ownPhotoPath, readRichTextFrom, readSelectionFrom, renderRichText, selectIn };
package/dist/protocol.js CHANGED
@@ -7,21 +7,43 @@ import {
7
7
  PROTOCOL_VERSION,
8
8
  carriesFiles,
9
9
  closestMarkedPath,
10
+ formatsAt,
10
11
  imageFieldAt,
12
+ insertPlainText,
13
+ isCollapsed,
14
+ linkSelectionAt,
11
15
  readEditorMessage,
12
16
  readPageMessage,
13
- shortcutOf
14
- } from "./chunk-XCBYYKWR.js";
15
- import "./chunk-GLXC5OLC.js";
17
+ readRichTextFrom,
18
+ readSelectionFrom,
19
+ renderRichText,
20
+ selectIn,
21
+ setLink,
22
+ shortcutOf,
23
+ toggleList,
24
+ toggleMark
25
+ } from "./chunk-27XIZEVS.js";
26
+ import "./chunk-ZUJEM3P7.js";
16
27
  export {
17
28
  OWN_PHOTOS_PATH,
18
29
  PROTOCOL_VERSION,
19
30
  carriesFiles,
20
31
  closestMarkedPath,
32
+ formatsAt,
21
33
  imageFieldAt,
34
+ insertPlainText,
35
+ isCollapsed,
22
36
  isOwnPhotoSrc,
37
+ linkSelectionAt,
23
38
  ownPhotoPath,
24
39
  readEditorMessage,
25
40
  readPageMessage,
26
- shortcutOf
41
+ readRichTextFrom,
42
+ readSelectionFrom,
43
+ renderRichText,
44
+ selectIn,
45
+ setLink,
46
+ shortcutOf,
47
+ toggleList,
48
+ toggleMark
27
49
  };
package/dist/schema.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { b as ImageValue, L as LinkValue, c as Field, d as CollectionField, F as Fields, a as Content, R as RecordFields, C as ContentOf, G as GroupField, e as LinkField, T as TextField, U as UrlField, f as ChoicesField, g as TimeField, I as ImageField } from './content-C_WpiWBm.js';
2
- export { h as ChoiceField, i as ChoiceOption, D as DateField, j as FieldKind, k as FieldValue, l as ImageFocus, N as NumberField, O as OptionValue, m as RecordOf, n as ReferenceField, o as ReferencesField, p as ToggleField, V as ValueOf, q as choice, r as choices, s as collection, t as date, u as defaultContent, v as defaultValue, w as defineSchema, x as group, y as image, z as link, A as number, B as reference, E as references, H as text, J as time, K as toggle, M as url } from './content-C_WpiWBm.js';
3
- import { F as FieldPath } from './parse-content-9RIWhb6z.js';
4
- export { C as ContentIssue, P as ParsedContent, c as countCharacters, f as formatFieldPath, p as parseContent } from './parse-content-9RIWhb6z.js';
1
+ import { b as ImageValue, L as LinkValue, c as Field, d as CollectionField, F as Fields, a as Content, e as RecordFields, C as ContentOf, G as GroupField, f as LinkField, T as TextField, U as UrlField, g as ChoicesField, h as TimeField, I as ImageField } from './content-6k-E7NWf.js';
2
+ export { i as ChoiceField, j as ChoiceOption, D as DateField, k as FieldKind, l as FieldValue, m as ImageFocus, N as NumberField, O as OptionValue, n as RecordOf, o as ReferenceField, p as ReferencesField, q as RichBlock, r as RichLine, s as RichSpan, R as RichText, t as RichTextField, u as ToggleField, V as ValueOf, v as asOneParagraph, w as choice, x as choices, y as collection, z as date, A as defaultContent, B as defaultValue, E as defineSchema, H as group, J as image, K as link, M as normalizedLine, P as number, Q as plainTextOf, S as readRichText, W as reference, X as references, Y as richText, Z as richTextOf, _ as text, $ as time, a0 as toggle, a1 as url } from './content-6k-E7NWf.js';
3
+ import { F as FieldPath } from './parse-content-C9ldnmvj.js';
4
+ export { C as ContentIssue, P as ParsedContent, c as countCharacters, f as formatFieldPath, p as parseContent } from './parse-content-C9ldnmvj.js';
5
5
 
6
6
  declare const FIELD_PATH_ATTRIBUTE = "data-cms-path";
7
7
  interface FieldMarker {
package/dist/schema.js CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  REVEAL_ATTRIBUTE,
6
6
  REVEAL_EVENT,
7
7
  WEEKDAYS,
8
+ asOneParagraph,
8
9
  choice,
9
10
  choices,
10
11
  collection,
@@ -26,19 +27,24 @@ import {
26
27
  link,
27
28
  linkTargetOf,
28
29
  newRecord,
30
+ normalizedLine,
29
31
  number,
30
32
  openingHoursField,
31
33
  parseContent,
34
+ plainTextOf,
35
+ readRichText,
32
36
  reference,
33
37
  references,
34
38
  resolveFieldPath,
39
+ richText,
40
+ richTextOf,
35
41
  seoFields,
36
42
  text,
37
43
  time,
38
44
  toggle,
39
45
  url,
40
46
  withRecordIds
41
- } from "./chunk-GLXC5OLC.js";
47
+ } from "./chunk-ZUJEM3P7.js";
42
48
  export {
43
49
  FIELD_PATH_ATTRIBUTE,
44
50
  RECORD_ID_KEY,
@@ -46,6 +52,7 @@ export {
46
52
  REVEAL_ATTRIBUTE,
47
53
  REVEAL_EVENT,
48
54
  WEEKDAYS,
55
+ asOneParagraph,
49
56
  choice,
50
57
  choices,
51
58
  collection,
@@ -67,12 +74,17 @@ export {
67
74
  link,
68
75
  linkTargetOf,
69
76
  newRecord,
77
+ normalizedLine,
70
78
  number,
71
79
  openingHoursField,
72
80
  parseContent,
81
+ plainTextOf,
82
+ readRichText,
73
83
  reference,
74
84
  references,
75
85
  resolveFieldPath,
86
+ richText,
87
+ richTextOf,
76
88
  seoFields,
77
89
  text,
78
90
  time,
package/dist/site.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ImageMetadata } from 'astro';
2
2
  import { AstroComponentFactory } from 'astro/runtime/server/index.js';
3
- import { F as Fields, C as ContentOf } from './content-C_WpiWBm.js';
3
+ import { F as Fields, C as ContentOf } from './content-6k-E7NWf.js';
4
4
 
5
5
  interface SeamletSiteDefinition<F extends Fields> {
6
6
  /** The site's address in the CMS: `/sites/<id>`. */
package/dist/site.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseContent
3
- } from "./chunk-GLXC5OLC.js";
3
+ } from "./chunk-ZUJEM3P7.js";
4
4
 
5
5
  // src/site.ts
6
6
  import { cmsOrigin } from "virtual:seamlet/config";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seamlet/astro",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Connects an Astro site to the Seamlet CMS: the schema of its content, the preview the editor frames, and its published content.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -46,11 +46,13 @@
46
46
  "./routes/own-photos": {
47
47
  "types": "./dist/routes/own-photos.d.ts",
48
48
  "default": "./dist/routes/own-photos.js"
49
- }
49
+ },
50
+ "./components/RichText.astro": "./components/RichText.astro"
50
51
  },
51
52
  "files": [
52
53
  "dist",
53
- "routes"
54
+ "routes",
55
+ "components"
54
56
  ],
55
57
  "scripts": {
56
58
  "build": "tsup"