smoodly 0.0.62 → 0.0.63
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/admin/client-ops.js +1 -1
- package/dist/admin/drift.js +3 -1
- package/dist/admin/editor/EditorView.js +65 -9
- package/dist/admin/editor/InsertPicker.d.ts +8 -27
- package/dist/admin/editor/InsertPicker.js +56 -47
- package/dist/admin/editor/SamplePreview.d.ts +7 -0
- package/dist/admin/editor/SamplePreview.js +86 -0
- package/dist/admin/editor/SectionIcon.d.ts +4 -0
- package/dist/admin/editor/SectionIcon.js +8 -0
- package/dist/admin/editor/StructureView.d.ts +2 -2
- package/dist/admin/editor/StructureView.js +2 -1
- package/dist/admin/editor/canvas-address.d.ts +2 -0
- package/dist/admin/editor/canvas-address.js +3 -1
- package/dist/admin/editor/picker-rows.d.ts +33 -0
- package/dist/admin/editor/picker-rows.js +38 -0
- package/dist/admin/editor/sample-frame.d.ts +36 -0
- package/dist/admin/editor/sample-frame.js +58 -0
- package/dist/admin/editor/sample-picks.d.ts +34 -0
- package/dist/admin/editor/sample-picks.js +49 -0
- package/dist/admin/editor/view-memory.d.ts +5 -0
- package/dist/admin/editor/view-memory.js +19 -0
- package/dist/admin/files.generated.js +1 -1
- package/dist/admin/forms/MediaWidget.d.ts +1 -1
- package/dist/admin/forms/MediaWidget.js +10 -2
- package/dist/admin/ops.d.ts +5 -0
- package/dist/admin/shell/GlobalForm.js +1 -1
- package/dist/admin/shell/GlobalList.js +1 -1
- package/dist/admin/shell/NavBar.js +1 -1
- package/dist/admin/shell/PagesList.js +4 -4
- package/dist/admin/shell/console-links.js +1 -1
- package/dist/admin/shell/health-list.js +1 -1
- package/dist/admin/tree-ops.d.ts +11 -4
- package/dist/admin/tree-ops.js +28 -17
- package/dist/admin/ui/theme.js +32 -13
- package/dist/admin/validate.js +3 -2
- package/dist/api-client.js +12 -2
- package/dist/astro/middleware.js +2 -0
- package/dist/astro/shell.d.ts +3 -0
- package/dist/astro/shell.js +8 -1
- package/dist/canvas-marker.d.ts +5 -0
- package/dist/canvas-marker.js +18 -6
- package/dist/conditions.js +5 -2
- package/dist/media.d.ts +15 -2
- package/dist/media.js +68 -10
- package/dist/next/page-renderer.js +12 -5
- package/dist/resolve.d.ts +6 -1
- package/dist/resolve.js +7 -7
- package/dist/sample-fill.d.ts +40 -0
- package/dist/sample-fill.js +145 -0
- package/dist/sample-tree.d.ts +30 -0
- package/dist/sample-tree.js +76 -0
- package/dist/site.d.ts +13 -0
- package/dist/site.js +10 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/content-modeling/sections.md +19 -3
- package/docs/content-modeling/zones.md +2 -2
- package/docs/getting-started/working-together.md +1 -1
- package/docs/globals/defining.md +1 -1
- package/docs/globals/header-and-footer.md +1 -1
- package/package.json +1 -1
package/dist/site.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { StyleValues, StyleValuesInput } from "./styles.ts";
|
|
|
12
12
|
import type { CustomConfigInput, ResolvedCustom } from "./custom/index.ts";
|
|
13
13
|
import type { PageRecord, PageStore } from "./store.ts";
|
|
14
14
|
import type { PathHit } from "./path-index.ts";
|
|
15
|
+
import type { SamplePicks } from "./sample-fill.ts";
|
|
15
16
|
import type { EntryItem, PageItem, Seo } from "./read-shapes.ts";
|
|
16
17
|
import type { SchemaOr } from "./registration.ts";
|
|
17
18
|
import type { ResolvedConfig } from "./config.ts";
|
|
@@ -144,6 +145,16 @@ export interface ContentReads {
|
|
|
144
145
|
targetPaths(target: PathTarget): Promise<Record<string, string>>;
|
|
145
146
|
/** Everything the renderer needs for one target: null when it is a folder, unpublished (outside draft), or gone. */
|
|
146
147
|
load(target: PathTarget, locale: string, path: string, options?: LoadOptions): Promise<SiteHit | null>;
|
|
148
|
+
/** The picker's rows for a zone of a page type, as raw nodes built from the registry's samples (sample-tree.ts):
|
|
149
|
+
* what `loadSamples` is handed. A static: the service holds no samples. */
|
|
150
|
+
sampleTree(template: string, zone: string, picks?: SamplePicks): PageTree;
|
|
151
|
+
/** The collections the zone's offered sections reference (spec 2026-11-14 filled samples): the picks are their
|
|
152
|
+
* first published entries, read before the tree is built. A static. */
|
|
153
|
+
sampleCollections(template: string, zone: string): string[];
|
|
154
|
+
/** The picker's samples (spec 2026-11-14 add section previews): the page's draft with `tree`'s zones resolved in
|
|
155
|
+
* place of the stored ones, images, references and placed globals as a draft has them. Draft only; nothing is
|
|
156
|
+
* stored. A placement names its item alone. Null for an entry, a page that is gone or not in `locale`. */
|
|
157
|
+
loadSamples(target: PathTarget, locale: string, path: string, tree: PageTree): Promise<SiteHit | null>;
|
|
147
158
|
/** The published read of an address: one locate that tells a record to assemble from a redirect row, and the
|
|
148
159
|
* code-owned page where the index has no row. */
|
|
149
160
|
lookup(locale: string, path: string): Promise<SiteHit | {
|
|
@@ -220,6 +231,8 @@ export declare function siteStatics(options: Omit<SiteOptions, "stores">): {
|
|
|
220
231
|
custom: ResolvedCustom;
|
|
221
232
|
cacheKey: string;
|
|
222
233
|
codePage: (locale: string, path: string) => SitePage | null;
|
|
234
|
+
sampleTree: (template: string, zone: string, picks?: SamplePicks) => PageTree;
|
|
235
|
+
sampleCollections: (template: string, zone: string) => string[];
|
|
223
236
|
};
|
|
224
237
|
/** What createSmoodlySite takes, read off the config. */
|
|
225
238
|
export declare function siteOptions(config: ResolvedConfig, stores?: SiteOptions["stores"]): SiteOptions;
|
package/dist/site.js
CHANGED
|
@@ -2,6 +2,7 @@ import { lastPart } from "./addresses.js";
|
|
|
2
2
|
import { withDefaults } from "./field-defaults.js";
|
|
3
3
|
import { modelKey } from "./model-key.js";
|
|
4
4
|
import { SMOODLY_VERSION } from "./version.js";
|
|
5
|
+
import { sampleTree as buildSampleTree, zoneSampleCollections } from "./sample-tree.js";
|
|
5
6
|
import { pageSeo } from "./seo.js";
|
|
6
7
|
import { pageFieldsOf, siteShape } from "./assemble.js";
|
|
7
8
|
/** The `page` prop for a loaded page. `href` is the app's seam from a
|
|
@@ -60,7 +61,15 @@ export function siteStatics(options) {
|
|
|
60
61
|
...(withSeo ? { seo: pageSeo(undefined, spec.title) } : {}),
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
const sampleSource = {
|
|
65
|
+
sections: options.sections.map((x) => ("schema" in x ? x.schema : x)),
|
|
66
|
+
globals: (options.globals ?? []).map((g) => ({ name: g.name, ...(g.section !== undefined ? { section: g.section } : {}) })),
|
|
67
|
+
};
|
|
68
|
+
// A page type's schema carries its zones (page.ts); the site's own page type names only what the reads use.
|
|
69
|
+
const policyOf = (template, zone) => pageSchemas.find((x) => x.kind === "page" && x.name === template)?.zones?.[zone];
|
|
70
|
+
const sampleTree = (template, zone, picks) => buildSampleTree(sampleSource, policyOf(template, zone), zone, picks);
|
|
71
|
+
const sampleCollections = (template, zone) => zoneSampleCollections(sampleSource, policyOf(template, zone));
|
|
72
|
+
return { shape, locales, homePageSlug, styles, custom, cacheKey, codePage, sampleTree, sampleCollections };
|
|
64
73
|
}
|
|
65
74
|
/** What createSmoodlySite takes, read off the config. */
|
|
66
75
|
export function siteOptions(config, stores) {
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SMOODLY_VERSION = "0.0.
|
|
1
|
+
export declare const SMOODLY_VERSION = "0.0.63";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/write-version.mjs from package.json — do not edit.
|
|
2
|
-
export const SMOODLY_VERSION = "0.0.
|
|
2
|
+
export const SMOODLY_VERSION = "0.0.63";
|
|
@@ -32,7 +32,7 @@ export const heroSchema = smoodly.schema.section({
|
|
|
32
32
|
| --- | --- |
|
|
33
33
|
| `name` | What the content stores for every placed Hero, so keep it stable once pages exist. |
|
|
34
34
|
| `title` | The label in Add section and in the inspector. |
|
|
35
|
-
| `description` |
|
|
35
|
+
| `description` | A line about the section, kept for later use: the admin does not show it today. Optional, like icon. |
|
|
36
36
|
| `fields` | What editors fill. Field order is form order: editors see Kicker, Heading, Text, Buttons, Image, top to bottom. |
|
|
37
37
|
| `sample` | What a new Hero starts with. See Sample content below. |
|
|
38
38
|
|
|
@@ -195,12 +195,28 @@ A registered section that no zone lists never appears in *Add section*. Lorem Co
|
|
|
195
195
|
|
|
196
196
|
## Sample content
|
|
197
197
|
|
|
198
|
-
`sample` is what a section starts with, so a page never shows an empty one. Smoodly uses it
|
|
198
|
+
`sample` is what a section starts with, so a page never shows an empty one. Smoodly uses it three times:
|
|
199
199
|
|
|
200
|
+
- *Add section* shows each section as your site draws it with its sample, so an editor sees what they are about to add.
|
|
200
201
|
- When an editor picks the section in *Add section*, the new section carries the sample.
|
|
201
202
|
- When a page opens with an empty locked zone, Smoodly fills the zone from the locked section's sample.
|
|
202
203
|
|
|
203
|
-
|
|
204
|
+
What Smoodly shows is what it inserts: the picture and the new section are built by the same rules.
|
|
205
|
+
|
|
206
|
+
The sample always comes first. A field it sets is taken as it is, and nothing is added to it or inside it. A field it leaves out starts from its `.default()`. A field with neither is filled by Smoodly, so a section with a thin sample still looks like itself:
|
|
207
|
+
|
|
208
|
+
| Name | Description |
|
|
209
|
+
| --- | --- |
|
|
210
|
+
| `f.text()` | Required: a short Latin phrase where the key reads as a title (title, heading, label, name, button and the like), else one sentence, cut to .max(n). Optional: empty. |
|
|
211
|
+
| `f.richtext()` | Required: a heading, two paragraphs and a list where the toolset has them, else one paragraph. Optional: empty. |
|
|
212
|
+
| `f.image()` | A placeholder picture, required or optional. It shows in the editor and in previews only, never on the live page; publishing treats it as no image, so a required image still asks for a photo. |
|
|
213
|
+
| `f.ref(), f.refList()` | The collection's first published entries: one for a required f.ref, up to three for an f.refList, fewer if .max(n) is lower. |
|
|
214
|
+
| `f.list(item)` | Required: .min(n) items, or three, never more than .max(n). Each item is filled the same way. |
|
|
215
|
+
| `f.object({ … })` | Required: its fields, filled the same way. |
|
|
216
|
+
|
|
217
|
+
Numbers, dates, links, videos and files are not filled (a number, a video and a file stay unset; a date and a link start empty), a list nothing fills starts as an empty list, and settings are never filled: they keep their defaults. An empty list in a sample (`people: []`) counts as left out. To choose the content yourself, put it in the sample.
|
|
218
|
+
|
|
219
|
+
*Add section* has an *Insert filled* switch under its list. Turned off, a section arrives empty: without the sample and without anything generated. The picture still shows the filled section.
|
|
204
220
|
|
|
205
221
|
The sample may set settings too, under their own keys.
|
|
206
222
|
|
|
@@ -58,7 +58,7 @@ The hero zone offers a choice of two openers and takes one of them. The body tak
|
|
|
58
58
|
|
|
59
59
|
## What editors see
|
|
60
60
|
|
|
61
|
-
In the canvas, *Add section* appears between the sections of a zone and at its end. It opens a picker with the sections the policy allows, then *Custom section* where `custom: true`, then a *Global* group with the globals whose section is allowed there. A zone capped with `.max(n)` loses its *Add section* once it is full.
|
|
61
|
+
In the canvas, *Add section* appears between the sections of a zone and at its end. It opens a picker with the sections the policy allows, then *Custom section* where `custom: true`, then a *Global* group with the globals whose section is allowed there. A zone capped with `.max(n)` loses its *Add section* once it is full. The picker shows each section as your site draws it, filled where its `sample` is silent; its *Insert filled* switch, when turned off, inserts the section empty instead ([Sample content](../content-modeling/sections.md#sample-content)).
|
|
62
62
|
|
|
63
63
|
A section's toolbar moves it up or down, duplicates it or removes it. In a locked zone the section is tagged *locked* and the toolbar says *fields only*. Smoodly fills the zone from the section's `sample` the first time the page opens.
|
|
64
64
|
|
|
@@ -66,7 +66,7 @@ An empty zone shows as a box in the canvas so it can take a section. On the site
|
|
|
66
66
|
|
|
67
67
|
## Locking a zone to a global
|
|
68
68
|
|
|
69
|
-
`z.locked()` also takes a global with a section, as `closing` does above. The zone then shows that one global on every page of the type. Editors change it once, under *
|
|
69
|
+
`z.locked()` also takes a global with a section, as `closing` does above. The zone then shows that one global on every page of the type. Editors change it once, under *Globals* or right in any canvas that shows it, and every page follows. The starter's header and footer take a different route: the layout renders them directly, so they need no zone.
|
|
70
70
|
|
|
71
71
|
A global can also be placed by hand. The starter lists Quote in its body zones, so the picker's *Global* group offers the site quote there, and an editor may still add a one-off Quote from the sections list.
|
|
72
72
|
|
|
@@ -20,7 +20,7 @@ Editors sign in at smoodly.io, never on your site: `/admin` sends them there and
|
|
|
20
20
|
|
|
21
21
|
## Who is editing what
|
|
22
22
|
|
|
23
|
-
A lock after a title in Pages, in a collection or in
|
|
23
|
+
A lock after a title in Pages, in a collection or in Globals means someone else has that item open in the language you are looking at. Hover it for their name. Opening the item anyway shows a line under the top bar: *aleksi is also editing this page.* Nothing is locked; it is a heads-up.
|
|
24
24
|
|
|
25
25
|
Presence runs on Smoodly's service, over one connection per open admin tab. Without it the admin works exactly the same, minus the locks and the line; a tab that loses its connection reconnects on its own.
|
|
26
26
|
|
package/docs/globals/defining.md
CHANGED
|
@@ -45,7 +45,7 @@ Register the list in the config's `globals`. A visual global's section must be i
|
|
|
45
45
|
|
|
46
46
|
## In the admin
|
|
47
47
|
|
|
48
|
-
*
|
|
48
|
+
*Globals*, under ⋮ in the admin's top bar, lists every global with its kind, *Visual* or *Fields*, and its status in the selected language. A *Fields* global opens a form like an entry's, the fields in schema order. A *Visual* global opens on a canvas: a page of the site that shows it, the home page for a `layout` global and otherwise the first page that places it, with the global selected and the rest of the page dimmed. Its fields sit beside the canvas under *Content* and *Settings*, text marked for inline editing can be typed in place, and when several pages place it, *Shown on* picks the page. A visual global no page shows yet opens as a form. Either way the page autosaves, and carries the same *Publish* control, *History* and *Discard changes* as a page.
|
|
49
49
|
|
|
50
50
|
A global exists in every language from the start, so there is nothing to add or delete. Publishing is per language, as for pages. A field's value is shared across languages unless it chains `.localized()`: the starter's contact email is one value, its address is translated.
|
|
51
51
|
|
|
@@ -112,6 +112,6 @@ On a single-language site there are no links and the header shows the brand and
|
|
|
112
112
|
|
|
113
113
|
## Editing them
|
|
114
114
|
|
|
115
|
-
Editors change the header and footer under *
|
|
115
|
+
Editors change the header and footer under *Globals*, or in any page's canvas: both are selectable there, tagged *global*, with their own form and their own *Publish* in the inspector. The header's navigation edits in place like any list with [marks](../content-modeling/marks.md). Publishing a page never publishes the footer, because a footer going live on every page is a deliberate click.
|
|
116
116
|
|
|
117
117
|
Next, [The media library](../media/library.md).
|
package/package.json
CHANGED