@seamlet/astro 0.11.3 → 0.12.1

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 CHANGED
@@ -11,7 +11,7 @@ npx @seamlet/astro init --title "Кофейня «Зерно»" # seamlet.json
11
11
  npx astro add @seamlet/astro node # the integration, and an adapter for the preview
12
12
  ```
13
13
 
14
- `init` takes the id from the name in `package.json` unless `--id` gives it, and the CMS `https://cms.seamlet.ru` unless `--cms` names another; it never overwrites a file the site has, and says what to render in a page. The steps below are what it starts, one by one.
14
+ `init` takes the id from the name in `package.json` unless `--id` gives it, and the CMS `https://cms.seamlet.ru` unless `--cms` names another; it never overwrites a file the site has: a `seamlet.json` the site has already names the site for the modules it writes, and the three modules are written together or, when the site has any of them, not at all. It says what to render in a page. The steps below are what it starts, one by one.
15
15
 
16
16
  1. Install the package. The site needs an adapter, because the preview is rendered on request; its own pages may stay static.
17
17
 
@@ -85,7 +85,7 @@ npx astro add @seamlet/astro node # the integration, and an ad
85
85
  });
86
86
  ```
87
87
 
88
- The CMS takes all of it from the site itself, so connecting a site, or changing its schema, needs no change of the CMS.
88
+ The CMS takes all of it from the site itself, so connecting a site, or changing its schema, needs no change of the CMS. `defineSeamletSite` checks the description exactly as the CMS reads it (`checkSiteDescription`), so a site the CMS would refuse stops its own build and says what to fix: a page address that is not `/` or lower-case segments without a trailing slash (`/menu`, not `/Menu` or `/menu/`), a first page other than `/`, initial content the CMS could not save, `searchTexts` that name no group of a `title` and a `description`, or a schema that cannot work. A schema cannot work with a field name that is empty, holds a dot or is a number (a path of a field reads them as its separator and as a record), an option of the wrong kind (`maxLength: "10"`), a `reference` to a collection it does not have, or an `itemTitle` that names no `text` or `link` field of the record.
89
89
 
90
90
  5. Add the integration; it reads `seamlet.json` and `src/seamlet.ts` itself:
91
91
 
@@ -140,6 +140,18 @@ item: { name: text({ label: "Название" }), isHidden: toggle({ label: "С
140
140
  // content.practices.filter((practice) => !practice.isHidden)
141
141
  ```
142
142
 
143
+ The preview renders the editor's draft before it is saved, and a draft may hold an address a link may not have (`javascript:…`, or one half typed). Put the address of a `link` or a `url` into `href` through `safeHref`, which gives it back when a link may lead there and nothing otherwise:
144
+
145
+ ```astro
146
+ ---
147
+ import { safeHref } from "@seamlet/astro/schema";
148
+ const href =
149
+ content.signup === null ? undefined : safeHref(content.signup.href);
150
+ ---
151
+
152
+ {href !== undefined && <a href={href}>{content.signup?.text}</a>}
153
+ ```
154
+
143
155
  A photo may carry the point that must stay in view when the site crops it, chosen in the editor: put `imagePosition(photo)` into its `object-position` (the middle when no point is chosen):
144
156
 
145
157
  ```astro
@@ -166,7 +178,7 @@ export default defineSeamletSite({
166
178
  });
167
179
  ```
168
180
 
169
- A text with a link in a sentence, bold words or a list is one `richText` field, not a text cut around the link. Its value is data, never HTML: paragraphs (`{ kind: "paragraph", spans }`) and lists (`{ kind: "list", items }`) of pieces `{ text, bold?, italic?, href? }`, where `href` leads where a `link` may. `inline` keeps it one paragraph without lists, for a text inside an element that holds one line. A saved plain string is read as paragraphs, so a `text` field turned into `richText` keeps its words. Render it with the component of the package, which puts out paragraphs and lists (or, with `inline`, the words alone) and never raw HTML; style its elements from around it:
181
+ A text with a link in a sentence, bold words or a list is one `richText` field, not a text cut around the link. Its value is data, never HTML: paragraphs (`{ kind: "paragraph", spans }`) and lists (`{ kind: "list", items }`) of pieces `{ text, bold?, italic?, href? }`, where `href` leads where a `link` may. `inline` keeps it one paragraph without lists, for a text inside an element that holds one line. A saved plain string is read as paragraphs, so a `text` field turned into `richText` keeps its words, and a `richText` turned into `text` keeps them too, without their marks. Which texts to make formatted: prose (a lead, a description, a paragraph about the place) is `richText`; headings, names of records, the words of buttons and links, contacts and short labels stay `text`, so the owner meets one rule: a paragraph can be formatted, the rest is typed as it is. Give every mark a style the site's fonts can show: a font without an italic needs another way to show `<em>` (another family's italic, a colour), or the owner presses «К» and sees nothing. Render it with the component of the package, which puts out paragraphs and lists (or, with `inline`, the words alone) and never raw HTML; style its elements from around it:
170
182
 
171
183
  ```astro
172
184
  ---
@@ -188,7 +200,7 @@ import RichText from "@seamlet/astro/components/RichText.astro";
188
200
 
189
201
  When the marked element holds the component alone, the editor types the text right on the page: a person clicks a paragraph and types, marks the selected words bold or italic (⌘B, ⌘I or the toolbar beside the selection) and puts a link on them. The element is then rendered with the same elements the component puts out, so style them from around it, never by classes of your own on them.
190
202
 
191
- `plainTextOf` gives its words without marks, for a page title or a description.
203
+ `plainTextOf` gives its words without marks, for a page title or a description; `linesOf` gives its lines (the paragraphs and the items of its lists) in their order.
192
204
 
193
205
  ## Presets
194
206
 
@@ -237,15 +249,15 @@ A field is edited where it is seen, so the editor opens what hides it when a per
237
249
  }
238
250
  ```
239
251
 
240
- What was opened stays open while the page is rendered anew after every edit.
252
+ What was opened stays open while the page is rendered anew after every edit: a part is known again by its `id`, or else by the field marked on it, in it or around it.
241
253
 
242
254
  ## What the integration adds
243
255
 
244
- - `/preview` and `/preview/<page>`: the preview the CMS's editor frames. `GET` shows the published content; `POST` with the content as JSON shows that content without saving it, which is how the editor shows every edit at once. Only the CMS may frame it (`Content-Security-Policy: frame-ancestors`), and it is never indexed or cached.
256
+ - `/preview` and `/preview/<page>`: the preview the CMS's editor frames. `GET` shows the published content; `POST` with the content as JSON shows that content without saving it, which is how the editor shows every edit at once. Only the CMS may frame it (`Content-Security-Policy: frame-ancestors`), and it is never indexed or cached; its refusals (a page the site does not have, a draft that is not JSON or is over 1 MB) say the same.
245
257
  - The bridge on the preview: it tells the editor where the marked elements are and what a person does on the page, and types texts in place, formatted ones too.
246
258
  - `/seamlet/site.json`: what the site tells the CMS about itself (`SITE_DESCRIPTION_PATH`): its id and name from `seamlet.json`, its schema, the content it starts with and its pages. The CMS reads it with `readSiteDescription` whenever the site is opened in its editor.
247
259
  - `/seamlet-photos/<address>`: the site's own photo at an address its content gives it, found by `photos` of `defineSeamletSite`, for the CMS's editor.
248
- - `/media/<site>/<file>`: the photos uploaded in the CMS, taken from it on request; a static build copies every uploaded photo its built pages refer to next to them, so a static host serves them too.
260
+ - `/media/<site>/<file>`: the photos uploaded for the site in the CMS, taken from it on request (`502` when the CMS does not answer in 10 seconds or fails); a static build copies every photo uploaded for the site that its built pages refer to next to them, a few at a time, so a static host serves them too. Loading the published content waits 30 seconds for the CMS and stops the build when it does not answer.
249
261
 
250
262
  A site never renders content as raw HTML (`set:html`): the editor acts on what its preview says.
251
263
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  // The pieces of one line of formatted text with their marks.
3
- import { linkTargetOf, type RichLine } from "@seamlet/astro/schema";
3
+ import { safeHref, type RichLine } from "@seamlet/astro/schema";
4
4
 
5
5
  interface Props {
6
6
  readonly spans: RichLine;
@@ -13,10 +13,7 @@ const pieces = spans.map((span) => ({
13
13
  ...span,
14
14
  lines: span.text.split(LINE_BREAK),
15
15
  // Content is checked when it is saved; a site never renders an address a link may not have.
16
- safeHref:
17
- span.href !== undefined && linkTargetOf(span.href) !== undefined
18
- ? span.href
19
- : undefined,
16
+ safeHref: span.href === undefined ? undefined : safeHref(span.href),
20
17
  }));
21
18
  ---
22
19
 
@@ -3,7 +3,7 @@
3
3
  // the words alone for an element that holds one line (`<p>`, `<h2>`). Nothing is rendered as raw
4
4
  // HTML, and a link leads only where linkTargetOf allows. The site styles the elements from
5
5
  // around it: `.about :global(a)`.
6
- import type { RichText } from "@seamlet/astro/schema";
6
+ import { linesOf, type RichText } from "@seamlet/astro/schema";
7
7
  import RichLine from "./RichLine.astro";
8
8
 
9
9
  interface Props {
@@ -13,9 +13,7 @@ interface Props {
13
13
  }
14
14
 
15
15
  const { value, inline = false } = Astro.props;
16
- const lines = value.flatMap((block) =>
17
- block.kind === "paragraph" ? [block.spans] : block.items,
18
- );
16
+ const lines = linesOf(value);
19
17
  ---
20
18
 
21
19
  {inline
package/dist/bridge.d.ts CHANGED
@@ -1,6 +1,5 @@
1
- import { F as Fields } from './content-6k-E7NWf.js';
2
- import { P as PageMessage, E as EditorMessage } from './messages-Dx0jNy3N.js';
3
- import './parse-content-C9ldnmvj.js';
1
+ import { F as Fields } from './parse-content-Eg8yRq_t.js';
2
+ import { P as PageMessage, E as EditorMessage } from './messages-CvZhLRSE.js';
4
3
 
5
4
  interface BridgeOptions {
6
5
  /** The origin of the CMS whose editor may frame the page, such as `https://cms.example.com`. */