smoodly 0.0.60 → 0.0.61
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 +3 -1
- package/dist/admin/files.generated.js +1 -1
- package/dist/api-client.js +5 -3
- package/dist/astro/index.js +3 -3
- package/dist/cli/push.js +2 -3
- package/dist/env.js +3 -2
- package/dist/push.d.ts +7 -0
- package/dist/push.js +8 -0
- package/dist/space-kind.d.ts +2 -0
- package/dist/space-kind.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/collections/defining.md +57 -0
- package/docs/collections/entry-pages.md +168 -0
- package/docs/collections/ordering.md +47 -0
- package/docs/collections/queries.md +173 -0
- package/docs/content-modeling/changing-schemas.md +79 -0
- package/docs/content-modeling/custom-section.md +94 -0
- package/docs/content-modeling/marks.md +97 -0
- package/docs/content-modeling/overview.md +55 -0
- package/docs/content-modeling/page-types.md +237 -0
- package/docs/content-modeling/sections.md +264 -0
- package/docs/content-modeling/styles-and-settings.md +184 -0
- package/docs/content-modeling/zones.md +80 -0
- package/docs/fields/choices.md +42 -0
- package/docs/fields/conditional.md +72 -0
- package/docs/fields/link.md +31 -0
- package/docs/fields/list-and-object.md +58 -0
- package/docs/fields/media.md +103 -0
- package/docs/fields/overview.md +56 -0
- package/docs/fields/reference-and-tags.md +62 -0
- package/docs/fields/richtext.md +168 -0
- package/docs/fields/text.md +152 -0
- package/docs/frameworks/astro.md +110 -0
- package/docs/frameworks/nextjs.md +147 -0
- package/docs/getting-started/content-from-code.md +263 -0
- package/docs/getting-started/deploying.md +131 -0
- package/docs/getting-started/export-and-import.md +73 -0
- package/docs/getting-started/first-page.md +98 -0
- package/docs/getting-started/install.md +108 -0
- package/docs/getting-started/keys.md +40 -0
- package/docs/getting-started/project-structure.md +142 -0
- package/docs/getting-started/working-together.md +42 -0
- package/docs/globals/defining.md +99 -0
- package/docs/globals/header-and-footer.md +117 -0
- package/docs/index.md +83 -0
- package/docs/languages/language-switcher.md +103 -0
- package/docs/languages/locales.md +119 -0
- package/docs/languages/localized-fields.md +61 -0
- package/docs/media/images-and-crops.md +131 -0
- package/docs/media/library.md +40 -0
- package/docs/pages/drafts-and-publishing.md +44 -0
- package/docs/pages/page-tree.md +51 -0
- package/docs/pages/paths-and-slugs.md +74 -0
- package/docs/pages/seo.md +126 -0
- package/docs/pages/version-history.md +23 -0
- package/docs/reference/upgrading.md +312 -0
- package/package.json +4 -3
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Deploying
|
|
2
|
+
|
|
3
|
+
Put your Smoodly site on the internet: give it the production space's values and let its build push the schema; then, once it is live, add a preview space to develop against.
|
|
4
|
+
|
|
5
|
+
## What a deploy needs
|
|
6
|
+
|
|
7
|
+
**Astro**
|
|
8
|
+
|
|
9
|
+
A Smoodly site on Astro is a server-rendered Astro site. The starter's `@astrojs/node` adapter deploys to any host that runs `astro build` and starts `dist/server/entry.mjs`. Pages are cached in that process's memory and expire when an editor publishes.
|
|
10
|
+
|
|
11
|
+
On Vercel, swap the adapter for Vercel's, and the cache for its CDN:
|
|
12
|
+
|
|
13
|
+
File: `astro.config.ts`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { defineConfig } from "astro/config";
|
|
17
|
+
import vercel from "@astrojs/vercel";
|
|
18
|
+
import { cacheVercel } from "@astrojs/vercel/cache";
|
|
19
|
+
import smoodly from "smoodly/astro";
|
|
20
|
+
|
|
21
|
+
export default defineConfig({
|
|
22
|
+
output: "server",
|
|
23
|
+
adapter: vercel(),
|
|
24
|
+
// Pages are cached on Vercel's CDN and expired by tag when an editor publishes.
|
|
25
|
+
cache: { provider: cacheVercel() },
|
|
26
|
+
site: process.env.SITE_URL,
|
|
27
|
+
integrations: [smoodly({ config: "src/smoodly/config.ts" })],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Install it with `npm install @astrojs/vercel`. The steps below name Vercel's settings; other hosts have the same ones under other names.
|
|
32
|
+
|
|
33
|
+
Run the function near your visitors. A page that is not in the CDN yet makes one call to Smoodly's API, answered from the edge when cached and otherwise by your space, which lives near where you made the project: Stockholm for a project made from Europe. On Vercel, `vercel.json` sets the function's region with `{ "regions": ["arn1"] }` (Stockholm; the [region list](https://vercel.com/docs/regions) names the rest). Vercel's Speed Insights (`@vercel/speed-insights`, mounted as its Astro or Next component in your layout) records real visitors' Web Vitals; it is your site's script, not Smoodly's, and optional.
|
|
34
|
+
|
|
35
|
+
### Static
|
|
36
|
+
|
|
37
|
+
Set Astro's output to static and every published page is built as a file: visitors need no function, and the site stays up if Smoodly's API is down.
|
|
38
|
+
|
|
39
|
+
File: `astro.config.ts (static)`
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { defineConfig } from "astro/config";
|
|
43
|
+
import vercel from "@astrojs/vercel";
|
|
44
|
+
import smoodly from "smoodly/astro";
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
output: "static",
|
|
48
|
+
adapter: vercel(),
|
|
49
|
+
site: process.env.SITE_URL,
|
|
50
|
+
integrations: [smoodly({ config: "src/smoodly/config.ts" })],
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Publishing does not change the live site. Editors press **Deploy** in the admin's rail when they are done, and the host builds. Give the site the host's deploy hook as `SMOODLY_DEPLOY_HOOK` where `astro build` runs (on Vercel: Settings › Git › Deploy Hooks): the build hands it to Smoodly's API with the site's address; without it the tile shows what is waiting and Deploy stays disabled. `SITE_URL` must be the site's own address, and the build refuses static output without it: the tile reads the build's marker, `/_smoodly/build.json`, from there. It says *Building…* until the new build answers, *N changes* while publishes wait (an unpublish or a delete counts as one too), and *The build has not landed* after ten minutes with no new build, in which case look at the host's log. A host that strips query strings from a cached file can hold the old marker for its max-age.
|
|
55
|
+
|
|
56
|
+
The admin, its revalidate and preview routes and the preview address still run on demand, so the deployment is files plus one function; a host with no function at all cannot run the admin. A static page has no lists by query string and no draft at its real address: drafts are edited through the preview address, as in live mode. A moved page's old address becomes one of the host's redirects at build, so it is answered without a function too. The cache provider in `astro.config` is unused in static output and may stay.
|
|
57
|
+
|
|
58
|
+
**Next.js**
|
|
59
|
+
|
|
60
|
+
A Smoodly site on Next.js deploys like any other Next.js app: to Vercel, or to any host that runs `next build` and `next start`. Pages are served from the host's cache (on Vercel, the CDN: `x-vercel-cache: HIT`) and expire when an editor publishes; the function runs for the first visit of an address and for the editor's preview. The steps below name Vercel's settings; other hosts have the same ones under other names.
|
|
61
|
+
|
|
62
|
+
## Environment variables
|
|
63
|
+
|
|
64
|
+
Set these in your host's project settings. Production gets the production space's values. Your host's Preview environment gets the preview space's once the project has one ([A preview space](#a-preview-space)), and the production space's until then. A missing or wrong key answers every page with a 500 and names the variable in the server log, your host's function logs:
|
|
65
|
+
|
|
66
|
+
| Variable | What it is |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `SMOODLY_SPACE_ID` | The space your content lives in, `sp_…`. A key from another space is refused by name. |
|
|
69
|
+
| `SMOODLY_SECRET_KEY` | The space's secret key (`sk_…`). Server-only: never expose it to the browser. A wrong key fails every page with one of these: the publishable key `holds the publishable key`; a string that is no Smoodly key `is not a Smoodly secret key`; a key the space does not hold `is not one of the space's live keys`. Every read sends the key again, so a corrected key works on the next request. |
|
|
70
|
+
| `SMOODLY_PUBLISHABLE_KEY` | The space's publishable key (`pk_…`): it reads published content only, so a browser may hold it. |
|
|
71
|
+
| `SITE_URL` | The site's own address with its scheme, like `https://www.example.com`. The build pushes it to the space: the admin's operations are accepted from it, and a publish calls it back. Your root layout builds `metadataBase` from it, so the canonical link, hreflang links (on a multilingual site) and social image URLs are absolute. Unset, it falls back to `http://localhost:3000` and the server warns once per process (a build prints it from each of its workers); a value without `http://` or `https://` stops every page with an error that names it. It is read when the server starts, so restart or redeploy after changing it. |
|
|
72
|
+
| `SMOODLY_PREVIEW_SECRET` | Next.js only; an Astro site has no such route and ignores the variable, since its drafts live at the preview address inside the admin. Optional. Lets a link turn draft mode on outside the admin: `/api/preview?secret=…&path=/` sets the draft cookie and redirects to `path`; a wrong or missing secret gets a 401. `/api/preview?disable` turns it off and needs no secret. Leave the variable unset and only the admin can turn draft mode on. |
|
|
73
|
+
|
|
74
|
+
On Astro the same variables are read at build and at run time. `SITE_URL` becomes `site`, fixed into the build, so set it where `astro build` runs (in the host's settings, or in `.env` for a build on your machine); without it, the absolute links in the page head are built from each request's own address, and the build warns.
|
|
75
|
+
|
|
76
|
+
[Keys](../getting-started/keys.md) says what each key can reach, and how to rotate one.
|
|
77
|
+
|
|
78
|
+
### Seeing what a page costs
|
|
79
|
+
|
|
80
|
+
Set `SMOODLY_DEBUG_READS=1` on the server and every page answers with two headers: `x-smoodly-reads`, the number of calls to Smoodly's API the response made (a page is one: the lookup brings the layout globals along, so the layout's reads cost nothing; a list on the page is one more, and so is a global without `layout: true` that a view reads), and `Server-Timing`, each call by its key with when it started and how long it took, then the render. `curl -sD - -o /dev/null https://your-site/?v=1` shows them on a cache miss. Leave the variable unset for visitors.
|
|
81
|
+
|
|
82
|
+
## The push at build
|
|
83
|
+
|
|
84
|
+
Your content model lives in your code, and each build pushes it to the space with the site's address. A Next site's `prebuild` script runs `smoodly push`; an Astro build does it itself. The build log shows `Pushed schema version 1 to the production space sp_…`, or `Schema unchanged (version 1) in the production space sp_…` when nothing changed (`the preview space sp_preview_…` for a preview space), so the log says which space the build reached. The dev server says the same line once when it starts. A push that fails is logged and never fails the build. There is no migration to run and no document to rebuild: a package upgrade or a content-model change reaches the space with the next deploy.
|
|
85
|
+
|
|
86
|
+
In live mode every page renders on request, so the framework's build reads no content: a build without the keys still succeeds and only skips the push. A static Astro build reads every published page, so it needs the keys.
|
|
87
|
+
|
|
88
|
+
If your site takes the package from a packed tarball in the repository rather than from npm, replacing the file is not enough: a host that restores `node_modules` from its build cache (Vercel does) sees the same path and version and keeps the old package. Deploy once without the cache (`vercel --prod --force`), or give the tarball a new name.
|
|
89
|
+
|
|
90
|
+
## The starter's content
|
|
91
|
+
|
|
92
|
+
The installer already filled the space with the starter's content. On a new site that is the production space, so the content you build on is where the live site reads it and a first deploy has nothing to copy. If the installer's seed failed, or you chose to run it later, run it yourself; it fills the space your env file names:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm run seed
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A space without a schema refuses the seed with `no_schema`: start the dev server or build first, which pushes the schema.
|
|
99
|
+
|
|
100
|
+
## A preview space
|
|
101
|
+
|
|
102
|
+
A preview space is a full copy of production to develop against: its pages, entries, history, redirects and media. A project has none until you add one, and many sites never need one before they go live.
|
|
103
|
+
|
|
104
|
+
Add it once the site is live. A dev server and every build push the site's schema to the space they read. Before launch that is what you want. After it, a schema change you try on your machine, or in a preview deployment, would change what the live site serves if they still read production.
|
|
105
|
+
|
|
106
|
+
1. Open the project at smoodly.io, go to *Spaces & keys* and choose **Add a preview space**. Its two keys are shown once; its id stays on the page.
|
|
107
|
+
2. Put the three values in your env file, and in your host's Preview environment.
|
|
108
|
+
3. Wait for the copy; the page shows its progress. While it runs the preview space refuses edits, uploads and schema pushes, its admin says a copy is in progress, and a site reading it may show missing pages.
|
|
109
|
+
4. When the copy is ready, restart the dev server and redeploy the preview environment. Their push was refused if they started during the copy, and a site signs in to its admin only from an address its push gave the space.
|
|
110
|
+
|
|
111
|
+
If a copy fails, the page says why, and the preview space stays locked: its admin and a push say the copy failed. Choose **Try again** there to continue where it stopped, or **Refresh from production** to start over.
|
|
112
|
+
|
|
113
|
+
A preview space's values say what they are: its id is `sp_preview_…` and both keys carry it. The build log names the space a push reached, so you can see which one a build used.
|
|
114
|
+
|
|
115
|
+
**Refresh from production**, on the same page, replaces everything in the preview space with a new copy: preview's own content changes are lost, and its keys and its schema stay the same. After a refresh, restart or redeploy the site that reads preview: it may hold pages it cached before. Nothing travels the other way. Content is edited in production; preview is where code and schema changes are tried against a copy of it.
|
|
116
|
+
|
|
117
|
+
## Sign-in on the real address
|
|
118
|
+
|
|
119
|
+
Editors sign in at `/admin` on the deployed site as they do locally: the admin sends them to smoodly.io and back. The deploy's push gives the space the site's address, and smoodly.io sends an editor back only to an admin address the space recorded that way (a preview deploy's and `localhost` included), so there is nothing to set.
|
|
120
|
+
|
|
121
|
+
## Preview and drafts
|
|
122
|
+
|
|
123
|
+
**Astro**
|
|
124
|
+
|
|
125
|
+
Editors see drafts at the preview address, `/_smoodly/preview/<path>`, inside the admin only: a signed cookie opens it, and nothing there is cached. A live address never shows a draft, and there is no pill.
|
|
126
|
+
|
|
127
|
+
**Next.js**
|
|
128
|
+
|
|
129
|
+
The editor turns on draft mode (Next's way of rendering unpublished content for one browser). While it is on, a small pill at the bottom of each page says *Preview · unpublished changes*. *Exit preview* turns draft mode off through `/api/preview`, which every starter includes. Visitors never get the pill or any of its code.
|
|
130
|
+
|
|
131
|
+
Next, [Astro](../frameworks/astro.md) and [Next.js](../frameworks/nextjs.md) cover what each framework adds.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Export and import
|
|
2
|
+
|
|
3
|
+
Copy a space's content into a folder, and from the folder into a new space: every page, entry, version and media file. It is how you move a site or keep a copy.
|
|
4
|
+
|
|
5
|
+
## What travels
|
|
6
|
+
|
|
7
|
+
`npx smoodly export` writes your site's content into a folder: every page and entry with its whole history, the redirects, and the media library with its files. `npx smoodly import` writes that folder into another space. Every id stays the same, so the site renders exactly as before.
|
|
8
|
+
|
|
9
|
+
To copy production into a project's preview space, use **Add a preview space** or **Refresh from production** at smoodly.io ([Deploying](../getting-started/deploying.md#a-preview-space)): the service copies it for you. These two commands are for moving a site to another project and for keeping a copy.
|
|
10
|
+
|
|
11
|
+
Members do not travel: a space's members are set in the project at smoodly.io. The import lists the source's members, so you can add them to the new space there.
|
|
12
|
+
|
|
13
|
+
Both commands run in your app's folder and read `SMOODLY_SPACE_ID` and `SMOODLY_SECRET_KEY` from `.env.local` (Astro: `.env`). The secret key decides whose content they reach, so there is nothing else to pass. The publishable key is refused, because it reads no drafts and writes nothing:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
smoodly: SMOODLY_SECRET_KEY holds the publishable key (pk_…) — copy the secret key (sk_…) from the space page.
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Export
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx smoodly export
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Exported space sp_…: 14 pages (41 versions), 32 entries (118 versions), 51 assets (38.2 MB), 9 redirects
|
|
27
|
+
→ smoodly-export-2026-10-16/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The folder lands in the folder the command runs in. It holds `manifest.json`, one JSON file per table under `rows/` and the media files under `media/`. Zip it to hand it over. `--out <dir>` names another folder. An existing folder is never overwritten. The starters' `.gitignore` lists `smoodly-export-*`, so the folder stays out of your repository.
|
|
31
|
+
|
|
32
|
+
An editor who saves while the export runs can stop it with `Content changed during export — run it again.` Run it again.
|
|
33
|
+
|
|
34
|
+
## Import into a new space
|
|
35
|
+
|
|
36
|
+
A space that already has content refuses an import, so import into a new one:
|
|
37
|
+
|
|
38
|
+
1. Export, with the env file still naming the space you are leaving.
|
|
39
|
+
2. Create a new project at smoodly.io and copy its production space's three values.
|
|
40
|
+
3. Point the env file at the new space: its `SMOODLY_SPACE_ID`, `SMOODLY_SECRET_KEY` and `SMOODLY_PUBLISHABLE_KEY`.
|
|
41
|
+
4. Push the schema, so the space can serve the site (a build or the dev server would push it too):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx smoodly push
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
5. Import:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npx smoodly import smoodly-export-2026-10-16
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The path is the export folder's, relative or absolute, so it can stay in the old site's folder. A live page reads what landed straight away: there is nothing to rebuild.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Imported 14 pages, 32 entries, 51 assets and 9 redirects into space sp_….
|
|
57
|
+
Members in the source — add them to the space's members in the project at smoodly.io:
|
|
58
|
+
anna@example.com (admin), mikko@example.com (editor)
|
|
59
|
+
Restart or redeploy the app: it may have cached pages from while the space was empty.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
6. Add the members to the new space at smoodly.io: nobody can sign in to its admin until its member list names them. Then restart or redeploy the app.
|
|
63
|
+
|
|
64
|
+
## When import refuses
|
|
65
|
+
|
|
66
|
+
Import checks everything before it writes anything.
|
|
67
|
+
|
|
68
|
+
- **The space already has content.** Import writes only into an empty space, and it never deletes. Use a new project's space.
|
|
69
|
+
- **The space is being copied into.** A preview space refuses an import while a copy from production runs, and after one failed. Wait for the copy, or retry it at smoodly.io.
|
|
70
|
+
- **The service versions differ.** The export and the target space must come from the same version of Smoodly's service. The message says which side is newer.
|
|
71
|
+
- **It stopped midway**, on a dropped connection for example. Run the same command again: rows and files that already landed are skipped. A run that finds everything already there says `Nothing new` and writes no content.
|
|
72
|
+
|
|
73
|
+
Next, [Upgrading](../reference/upgrading.md).
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Your first page
|
|
2
|
+
|
|
3
|
+
Create a page in the admin, fill it with sections and publish it, as an editor would. Then look at the code that decided what the editor was allowed to do.
|
|
4
|
+
|
|
5
|
+
## Create the page
|
|
6
|
+
|
|
7
|
+
This page assumes the Lorem Co. starter from [Install](../getting-started/install.md), running on your machine. The steps in the admin are the same for every framework.
|
|
8
|
+
|
|
9
|
+
1. Open `/admin` on your running site and sign in.
|
|
10
|
+
2. On *Pages*, click *+ New page*.
|
|
11
|
+
3. Enter `services` as the slug, the last part of the page's URL.
|
|
12
|
+
|
|
13
|
+
Lorem Co. has one page type that editors can create, so the admin skips that question and opens the new page. On a site with several page types, it asks which one to use.
|
|
14
|
+
|
|
15
|
+
## Edit it in the canvas
|
|
16
|
+
|
|
17
|
+
The editor shows the page as the site renders it (the canvas) on the left, and the fields of whatever you select (the inspector) on the right.
|
|
18
|
+
|
|
19
|
+
1. Click the hero at the top. It is marked *locked*: editors fill its fields but cannot move or remove it.
|
|
20
|
+
2. In the inspector, change *Heading* to `Services`. The canvas updates as you type, and *Saved* appears at the top. Every change saves as a draft on its own; nothing is live yet.
|
|
21
|
+
3. Click *Add section* under the hero and pick *Points*. It arrives filled with sample content, so the page never shows an empty section.
|
|
22
|
+
4. Change the section's *Heading* to `What we do`. The toolbar on the selected section moves it up or down, duplicates it or removes it.
|
|
23
|
+
|
|
24
|
+
The switch at the top right (next to the eye button) turns the canvas into the structure view: every section is a card with its title and a line of its content. Drag a card by its grip to reorder the page, click a card to edit its fields in the inspector, and switch back to see the page as the site renders it. As with every edit, the live site changes nothing until you publish.
|
|
25
|
+
|
|
26
|
+
Every change can be taken back: Cmd+Z (Ctrl+Z on Windows and Linux), or the left arrow at the top right, undoes the last edit step by step, and Shift+Cmd+Z or the right arrow brings it back. A burst of typing is one step.
|
|
27
|
+
|
|
28
|
+
To reuse a section on another page, pick *Copy* in the section's ⋯ menu (on its card in the structure view, or on its row in the Page Outline), and on the other page pick *Paste before* or *Paste after* in a card's or row's ⋯ menu, or *Paste* on a zone's heading in the structure view. The entry is greyed out when that page's zone does not allow the section.
|
|
29
|
+
|
|
30
|
+
## Publish
|
|
31
|
+
|
|
32
|
+
Click *Publish* at the top right. The status next to it changes from *Draft* to *Published*, and the eye button (*View live*) opens the page at `/services` on your site.
|
|
33
|
+
|
|
34
|
+
A new page's title starts as its slug, so the browser tab says "services". To change it, go back to *Pages*, open the ⋯ menu at the end of the page's row and pick *Rename…*. The admin asks for the title and then the slug; enter `Services` and keep the slug. The title changes on the live page at once.
|
|
35
|
+
|
|
36
|
+
## Why the editor could do that
|
|
37
|
+
|
|
38
|
+
Everything the editor was allowed to do comes from the page type's schema. It is the same file in every framework; in an Astro project it lives under `src/components/pages/`.
|
|
39
|
+
|
|
40
|
+
File: `components/pages/Page.schema.ts`
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export const pageSchema = smoodly.schema.page({
|
|
44
|
+
name: "page",
|
|
45
|
+
title: "Page",
|
|
46
|
+
zones: {
|
|
47
|
+
hero: z.locked(pageHeroSchema),
|
|
48
|
+
body: z.freeform({ sections: [imageTextSchema, pointsSchema, favouriteArticlesSchema, peopleSchema, quoteSchema], custom: true }),
|
|
49
|
+
},
|
|
50
|
+
// …
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The page type's view decides where each zone renders:
|
|
55
|
+
|
|
56
|
+
**Astro**
|
|
57
|
+
|
|
58
|
+
File: `src/page-types/page.astro`
|
|
59
|
+
|
|
60
|
+
```astro
|
|
61
|
+
---
|
|
62
|
+
import { Zone } from "smoodly/astro/components";
|
|
63
|
+
// …
|
|
64
|
+
---
|
|
65
|
+
<Layout head={head} locale={page.locale} locales={page.locales}>
|
|
66
|
+
// …
|
|
67
|
+
<Zone of={zones.hero} />
|
|
68
|
+
<Zone of={zones.body} />
|
|
69
|
+
</Layout>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The file's name pairs it with the schema named `page`.
|
|
73
|
+
|
|
74
|
+
**Next.js**
|
|
75
|
+
|
|
76
|
+
File: `components/pages/Page.tsx`
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { smoodly, Zone, type Props } from "smoodly/react";
|
|
80
|
+
// …
|
|
81
|
+
<Zone of={zones.hero} />
|
|
82
|
+
<Zone of={zones.body} />
|
|
83
|
+
// …
|
|
84
|
+
export const StandardPage = smoodly.page(pageSchema, PageView);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`smoodly.page()` pairs it with the schema.
|
|
88
|
+
|
|
89
|
+
Each zone is a place on the page where editors work, and its policy sets how much they may change:
|
|
90
|
+
|
|
91
|
+
| Name | Description |
|
|
92
|
+
| --- | --- |
|
|
93
|
+
| `z.locked(PageHero)` | Exactly one page hero. Editors fill its fields but cannot add, move or remove it. |
|
|
94
|
+
| `z.freeform({ … })` | Any number of the listed sections, in any order. custom: true also offers the custom section, which editors lay out themselves. |
|
|
95
|
+
|
|
96
|
+
The form in the inspector came from the Points section's own definition: one field for the heading and a list of one to six points. Change a field there and the form changes with it.
|
|
97
|
+
|
|
98
|
+
Next, [Sections](../content-modeling/sections.md) shows how to write a section of your own, and [Zones and freedom](../content-modeling/zones.md) covers the three zone policies.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Install with create-smoodly-app
|
|
2
|
+
|
|
3
|
+
One command creates a website with the Smoodly admin mounted, connected to a space that holds its content. At the end you have the Lorem Co. starter running on your machine.
|
|
4
|
+
|
|
5
|
+
## Before you start
|
|
6
|
+
|
|
7
|
+
You need:
|
|
8
|
+
|
|
9
|
+
- Node.js 22.12 or newer.
|
|
10
|
+
- An account at [smoodly.io](https://smoodly.io/sign-in), with an email address.
|
|
11
|
+
- A project for the site. A new project has one space, **production**: the content the live site reads. You build the site in it; nothing is public until you deploy.
|
|
12
|
+
|
|
13
|
+
Open the project's production space. Its page shows the three values the installer asks for: the space id (`sp_…`), the secret key (`sk_…`) and the publishable key (`pk_…`). Keep the page open; the next step pastes them.
|
|
14
|
+
|
|
15
|
+
## Create the site
|
|
16
|
+
|
|
17
|
+
Run the installer with the folder to create:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx create-smoodly-app my-site
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It asks four things:
|
|
24
|
+
|
|
25
|
+
1. **Framework?** *Astro* renders each section from an `.astro` file and caches whole pages until an editor publishes. *Next.js* renders sections as React server components. Both get the same admin and the same content. [Frameworks](../frameworks/astro.md) has the details.
|
|
26
|
+
2. **Multilingual site?** Answer no for one language. Yes asks for the locales, the first being the default (`en, fi`). You can read about this choice in [Locales and routing](../languages/locales.md).
|
|
27
|
+
3. **Starter?** *Lorem Co.* is a small company site that uses every feature, which you then edit into your own. *Blank* has one of each part and nothing else: a Text section, a Button element, a `posts` collection with its entry page, a Home and a Page type. This guide uses Lorem Co.
|
|
28
|
+
4. **The space's values.** Paste the space id, the secret key and the publishable key from the space page. The secret key is asked hidden. A key pasted into the wrong question is refused with a message saying which one it is.
|
|
29
|
+
|
|
30
|
+
The folder can be a name (`my-site`), a path (`../sites/my-site`) or `.` for the current empty folder. Its last part becomes the package name, so it takes lowercase letters, digits, dots, dashes and underscores.
|
|
31
|
+
|
|
32
|
+
Then it works on its own for a minute or two. It installs the packages and writes the space's values to an env file. It then pushes the site's content model to the space, fills the space with the starter's content and makes a first git commit.
|
|
33
|
+
|
|
34
|
+
> **Note:** If a step fails, the folder stays and the installer prints the rest under *Finish by hand*: the command that picks up where it stopped. A failed push, for example, finishes with `npx smoodly push && npm run seed` in the new folder.
|
|
35
|
+
|
|
36
|
+
## Without questions
|
|
37
|
+
|
|
38
|
+
Every question has a flag, so a script or a coding agent can run the installer with no terminal to answer in:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx create-smoodly-app my-site --yes --framework astro --starter lorem --locales en,fi --space sp_… --secret-key sk_… --publishable-key pk_…
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Flag | What it answers |
|
|
45
|
+
| --- | --- |
|
|
46
|
+
| `--yes` | Takes the default for anything not given: Astro, one language, the Lorem Co. starter. The space's values have no default, so they are still asked. |
|
|
47
|
+
| `--framework astro\|next` | The framework. |
|
|
48
|
+
| `--locales en,fi` | A multilingual site. The first locale is the default. |
|
|
49
|
+
| `--starter lorem\|blank` | The starter. |
|
|
50
|
+
| `--space` | The space id, `sp_…`. |
|
|
51
|
+
| `--secret-key` | The space's secret key, `sk_…`. |
|
|
52
|
+
| `--publishable-key` | The space's publishable key, `pk_…`. |
|
|
53
|
+
|
|
54
|
+
`npx create-smoodly-app --help` prints the same list. Without a terminal and without the three values, the installer stops and names the flags to pass.
|
|
55
|
+
|
|
56
|
+
## Run it
|
|
57
|
+
|
|
58
|
+
When the installer prints *Ready*, start the site:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd my-site
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run dev
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Astro**
|
|
69
|
+
|
|
70
|
+
Open [localhost:4321](http://localhost:4321) for the site and [localhost:4321/admin](http://localhost:4321/admin) for the admin.
|
|
71
|
+
|
|
72
|
+
**Next.js**
|
|
73
|
+
|
|
74
|
+
Open [localhost:3000](http://localhost:3000) for the site and [localhost:3000/admin](http://localhost:3000/admin) for the admin.
|
|
75
|
+
|
|
76
|
+
The admin sends you to smoodly.io to sign in with your account, and back. The *Pages* list shows the Lorem Co. pages: Home, Articles (at `/posts`), and under the *Company* path About us with its Team page, and News (at `/company/news`).
|
|
77
|
+
|
|
78
|
+
## What you got
|
|
79
|
+
|
|
80
|
+
Your content model lives in your code. The dev server and every build push it to your space, so a field you add is editable at once; nothing migrates.
|
|
81
|
+
|
|
82
|
+
**Astro**
|
|
83
|
+
|
|
84
|
+
The site keeps its settings in `.env`, Astro's own env file; the integration reads it at `astro dev` and `astro build`:
|
|
85
|
+
|
|
86
|
+
| Name | Description |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `SMOODLY_SPACE_ID` | The space your content lives in, `sp_…`. |
|
|
89
|
+
| `SMOODLY_SECRET_KEY` | The server-only key the site reads content with, `sk_…`. Never expose it to the browser. |
|
|
90
|
+
| `SMOODLY_PUBLISHABLE_KEY` | The space's public key, `pk_…`. It reads published content and nothing else. |
|
|
91
|
+
| `SITE_URL` | The site's own address, `http://localhost:4321` until you deploy. The integration makes it `site`, absolute links in the page head are built from it, and the push gives it to the space. |
|
|
92
|
+
|
|
93
|
+
The rest is an ordinary Astro project. Your views live in `src/`, and the Smoodly setup lives in `src/smoodly/`.
|
|
94
|
+
|
|
95
|
+
**Next.js**
|
|
96
|
+
|
|
97
|
+
The site keeps its settings in `.env.local`:
|
|
98
|
+
|
|
99
|
+
| Name | Description |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `SMOODLY_SPACE_ID` | The space your content lives in, `sp_…`. |
|
|
102
|
+
| `SMOODLY_SECRET_KEY` | The server-only key the site reads content with, `sk_…`. Never expose it to the browser. |
|
|
103
|
+
| `SMOODLY_PUBLISHABLE_KEY` | The space's public key, `pk_…`. It reads published content and nothing else. |
|
|
104
|
+
| `SITE_URL` | The site's own address, `http://localhost:3000` until you deploy. Absolute links in the page head are built from it, and the push gives it to the space. |
|
|
105
|
+
|
|
106
|
+
The rest is an ordinary Next.js app. Your components live in `components/`, and the Smoodly setup lives in `smoodly/`.
|
|
107
|
+
|
|
108
|
+
[Keys](../getting-started/keys.md) says what each value can reach. [Project structure](../getting-started/project-structure.md) walks through every file. Next, [make your first page](../getting-started/first-page.md) in the admin.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Keys
|
|
2
|
+
|
|
3
|
+
The space id and the two keys a Smoodly site uses, where each one may live, what the public one can reach, and how to rotate or revoke a key.
|
|
4
|
+
|
|
5
|
+
## Three values
|
|
6
|
+
|
|
7
|
+
Your space's page at smoodly.io shows the three values your env file names. The env file is `.env` in an Astro project, Astro's own env file, and `.env.local` in a Next.js app:
|
|
8
|
+
|
|
9
|
+
| Variable | Lives | Does |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `SMOODLY_SPACE_ID` | server | The space your content lives in, `sp_…`. Every read names it, and a key is refused by any other space. |
|
|
12
|
+
| `SMOODLY_SECRET_KEY` | server only | The space's secret key, `sk_…`. The site reads with it, drafts included, and scripts write with it: the push, the seed, `export` and `import`. It reaches this space only. Never ship it to a browser, a client component or a public repository. |
|
|
13
|
+
| `SMOODLY_PUBLISHABLE_KEY` | server and browser | The space's publishable key, `pk_…`. It reads published content and nothing else. |
|
|
14
|
+
|
|
15
|
+
The live site and its admin need the space id and the secret key. The admin's browser holds no key: it posts to Smoodly's API with the editor's session, and the API checks the session against the space's members. The starter's `/api/preview` route reads `SMOODLY_PREVIEW_SECRET` for draft previews outside the admin (Next.js only); leave it unset and the route refuses to turn draft mode on.
|
|
16
|
+
|
|
17
|
+
A preview space's values carry its name: the id is `sp_preview_…` (`sp_eu_preview_…` under the EU jurisdiction), and both keys carry the same `preview`. A production space's have no mark. An env file therefore shows which space it holds. A site needs `smoodly` 0.0.61 or newer to read a preview space; an older version refuses the key as not a Smoodly key. [Deploying](../getting-started/deploying.md#a-preview-space) says when to add one.
|
|
18
|
+
|
|
19
|
+
`SMOODLY_API_URL` is the address of Smoodly's API, `https://api.smoodly.io` unless you set it. Set it only to reach another service address.
|
|
20
|
+
|
|
21
|
+
## What the publishable key can reach
|
|
22
|
+
|
|
23
|
+
Treat it as public. Someone who has it, and nothing else, can read your published content over the API with plain `fetch`, from anywhere, and the media files by their URL.
|
|
24
|
+
|
|
25
|
+
They cannot:
|
|
26
|
+
|
|
27
|
+
- read a draft: drafts take the secret key;
|
|
28
|
+
- write, publish, delete or reorder anything: every write takes the secret key or an editor's session;
|
|
29
|
+
- see who your members are, or who is editing what: presence takes a member's session, never a key;
|
|
30
|
+
- open the admin: it needs a member's session.
|
|
31
|
+
|
|
32
|
+
## Rotating a key
|
|
33
|
+
|
|
34
|
+
Rotate a key from the space page when someone who should not have it may have seen it, or on a schedule of your own. The space mints a new key and the old one keeps working for the time you choose, a day unless you say otherwise, so the site serves without a gap while you put the new key in your env file and your host's settings and redeploy. You can also end the old key at once: then every site still holding it stops reading until it has the new one.
|
|
35
|
+
|
|
36
|
+
## Revoking a leaked key
|
|
37
|
+
|
|
38
|
+
Revoke a key that has leaked. It is refused from its next request, so revoke it once the new key is live: a site still holding it answers its pages with an error naming the key. Every read sends the key again, so a corrected key works on the next request without a restart.
|
|
39
|
+
|
|
40
|
+
Next, [Deploying](../getting-started/deploying.md).
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Project structure
|
|
2
|
+
|
|
3
|
+
What create-smoodly-app puts in your project, and which files you change as you build the site. Everything is ordinary code in your repository; Smoodly adds a folder of setup and mounts the admin.
|
|
4
|
+
|
|
5
|
+
## The files
|
|
6
|
+
|
|
7
|
+
**Astro**
|
|
8
|
+
|
|
9
|
+
A single-language Lorem Co. site:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
my-site/
|
|
13
|
+
├── astro.config.ts the integration: smoodly() mounts the admin, the preview and the pages
|
|
14
|
+
├── src/
|
|
15
|
+
│ ├── layouts/Layout.astro the site's document: head, header, footer
|
|
16
|
+
│ ├── page-types/ one .astro file per page type, by its name
|
|
17
|
+
│ ├── sections/ one .astro file per section
|
|
18
|
+
│ ├── elements/ one .astro file per element richtext may embed
|
|
19
|
+
│ ├── entry-pages/ one .astro file per collection with a path
|
|
20
|
+
│ ├── components/ every schema (Name.schema.ts) and the starter's helpers
|
|
21
|
+
│ ├── smoodly/ the Smoodly setup
|
|
22
|
+
│ ├── live.config.ts Smoodly's content as live collections
|
|
23
|
+
│ ├── strings.ts, messages/ the starter's fixed UI text
|
|
24
|
+
│ └── styles/global.css
|
|
25
|
+
├── scripts/seed.ts the starter content, written from code
|
|
26
|
+
├── public/images/ the starter's photos
|
|
27
|
+
└── .env the space's values
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A multilingual site adds `i18n` to `astro.config.ts`, with the same locales as `src/smoodly/config.ts`; the site checks that they agree when it starts. The root holds the usual `package.json` and `tsconfig.json`, plus a `README.md`, and an `AGENTS.md` for coding agents that `CLAUDE.md` points to.
|
|
31
|
+
|
|
32
|
+
**Next.js**
|
|
33
|
+
|
|
34
|
+
A single-language Lorem Co. site:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
my-site/
|
|
38
|
+
├── app/
|
|
39
|
+
│ ├── (site)/ the website
|
|
40
|
+
│ │ ├── layout.tsx
|
|
41
|
+
│ │ └── [[...path]]/page.tsx
|
|
42
|
+
│ ├── (smoodly)/admin/ the admin
|
|
43
|
+
│ ├── api/preview/route.ts draft previews on and off
|
|
44
|
+
│ └── globals.css
|
|
45
|
+
├── components/
|
|
46
|
+
│ ├── pages/ page types
|
|
47
|
+
│ ├── sections/ sections
|
|
48
|
+
│ ├── elements/ elements for richtext
|
|
49
|
+
│ └── … the starter's helpers: PageFrame, ArticleCard, strings,
|
|
50
|
+
│ and articles.ts and news.ts for their URLs
|
|
51
|
+
├── messages/en.json the starter's fixed UI text
|
|
52
|
+
├── smoodly/ the Smoodly setup
|
|
53
|
+
├── scripts/seed.ts the starter content, written from code
|
|
54
|
+
├── public/images/ the starter's photos
|
|
55
|
+
└── .env.local the space's values
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A multilingual site puts the website under `app/[locale]/` instead of `app/(site)/`, adds `i18n/` and `proxy.ts` for [next-intl](https://next-intl.dev), which handles the translated URLs, and a file in `messages/` per language. The root holds the usual `package.json`, `next.config.ts` and `tsconfig.json`, plus a `README.md`, and an `AGENTS.md` for coding agents that `CLAUDE.md` points to.
|
|
59
|
+
|
|
60
|
+
## The website
|
|
61
|
+
|
|
62
|
+
**Astro**
|
|
63
|
+
|
|
64
|
+
There are no route files. `smoodly()` in `astro.config.ts` serves the pages at every address a page file of your own under `src/pages/` does not take. It also mounts the admin at `/admin`, and the preview address editors see the canvas through. `src/pages/404.astro` is yours: the site's "not found" page.
|
|
65
|
+
|
|
66
|
+
To serve the admin somewhere other than `/admin`, see [The admin](https://smoodly.io/docs/getting-started/admin).
|
|
67
|
+
|
|
68
|
+
**Next.js**
|
|
69
|
+
|
|
70
|
+
The site's route is one file, and it needs no editing:
|
|
71
|
+
|
|
72
|
+
| Name | Description |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| `app/(site)/[[...path]]/page.tsx` | Every CMS URL: the home, every page at any depth and every collection entry. Smoodly finds the page by its URL, renders it with its page type, and Next keeps it cached until a publish expires it. |
|
|
75
|
+
| `app/(site)/layout.tsx` | The site's root layout: fonts, globals.css and the footer. Yours to change. |
|
|
76
|
+
|
|
77
|
+
The admin sits in its own route group as one route file and takes no layout, so the site's layout never wraps it. To serve it somewhere other than `/admin`, see [The admin](https://smoodly.io/docs/getting-started/admin).
|
|
78
|
+
|
|
79
|
+
## Your components
|
|
80
|
+
|
|
81
|
+
This is where you spend your time, in every framework.
|
|
82
|
+
|
|
83
|
+
**Astro**
|
|
84
|
+
|
|
85
|
+
Each section, element and page type is two files. `src/components/…/Name.schema.ts` holds the schema (the fields editors fill). A view named after the schema holds the markup: `src/sections/hero.astro` renders the section named `hero`.
|
|
86
|
+
|
|
87
|
+
| Name | Description |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `src/page-types/` | One view per page type: which zones it renders and where. Lorem Co. has home, page, articlesIndex and newsIndex. |
|
|
90
|
+
| `src/sections/` | One view per section editors place in zones: hero, points, quote and the rest. The header and footer are sections too. |
|
|
91
|
+
| `src/elements/` | One view per element editors insert inside richtext, like callout and cta. |
|
|
92
|
+
| `src/entry-pages/` | One view per collection with a path: articles and news. |
|
|
93
|
+
|
|
94
|
+
A new section is its schema under `src/components/sections/`, the schema in `src/smoodly/sections.ts` and in the zone of each page type that may hold it, and its view in `src/sections/`. The site names a schema with no view, and a view with no schema, at the first request. [Sections](../content-modeling/sections.md) shows how.
|
|
95
|
+
|
|
96
|
+
**Next.js**
|
|
97
|
+
|
|
98
|
+
Each section, element and page type is two files: `Name.schema.ts` holds the schema (the fields editors fill) and `Name.tsx` the React component that renders them.
|
|
99
|
+
|
|
100
|
+
| Name | Description |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `components/pages/` | Page types: which zones a page has and what may go in them. Lorem Co. has Home, Page, Articles, Article, News and News post. |
|
|
103
|
+
| `components/sections/` | Sections editors place in zones: Hero, Points, Quote and the rest. The header and footer are sections too. |
|
|
104
|
+
| `components/elements/` | Small components editors insert inside richtext, like Callout and Cta. |
|
|
105
|
+
|
|
106
|
+
A new section is two files here, its schema in `smoodly/sections.ts`, its component in `smoodly/views.ts`, and its schema in the zone of each page type that may hold it. [Sections](../content-modeling/sections.md) shows how.
|
|
107
|
+
|
|
108
|
+
## The Smoodly setup
|
|
109
|
+
|
|
110
|
+
**Astro**
|
|
111
|
+
|
|
112
|
+
| Name | Description |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `src/smoodly/config.ts` | The registry and settings: the schemas of page types, sections, collections and globals, plus locales, time zone and styles, in one defineConfig(). It imports no view. |
|
|
115
|
+
| `src/smoodly/sections.ts` | The list of section schemas the config registers. |
|
|
116
|
+
| `src/smoodly/collections.ts` | Collections such as articles and people. |
|
|
117
|
+
| `src/smoodly/globals.ts` | Global content such as the header and footer. |
|
|
118
|
+
| `src/smoodly/elements.ts` | The list of element schemas richtext may embed. |
|
|
119
|
+
| `src/smoodly/toolsets.ts` | What each richtext field offers. |
|
|
120
|
+
| `src/live.config.ts` | One loader per collection, global item, the pages and the page tree. Your views read through getLiveCollection and getLiveEntry. |
|
|
121
|
+
|
|
122
|
+
You edit all of them as the site grows. There is no file of wiring: the integration in `astro.config.ts` reads the config and finds the views by their file names.
|
|
123
|
+
|
|
124
|
+
**Next.js**
|
|
125
|
+
|
|
126
|
+
| Name | Description |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `smoodly/config.ts` | The registry and settings: the schemas of page types, sections, collections and globals, plus locales, time zone and styles, in one defineConfig(). It imports no component. |
|
|
129
|
+
| `smoodly/sections.ts` | The list of section schemas the config registers. |
|
|
130
|
+
| `smoodly/views.ts` | The components that render the registered schemas, and the entry pages. |
|
|
131
|
+
| `smoodly/collections.ts` | Collections such as articles and people. |
|
|
132
|
+
| `smoodly/globals.ts` | Global content such as the header and footer. |
|
|
133
|
+
| `smoodly/elements.ts` | The list of element schemas richtext may embed. |
|
|
134
|
+
| `smoodly/toolsets.ts` | What each richtext field offers. |
|
|
135
|
+
| `smoodly/richtext.tsx` | The site's richtext renderer, bound to its elements. |
|
|
136
|
+
| `smoodly/server.ts` | Reads content for the site, and exports content() for writing it from code. Server-only. |
|
|
137
|
+
|
|
138
|
+
You edit the first seven as the site grows. The last two are wiring you rarely touch. The admin itself is one route file, `app/(smoodly)/admin/[[...segments]]/route.ts`.
|
|
139
|
+
|
|
140
|
+
`scripts/seed.ts` is how the starter's content got there: a script that writes pages and entries under the same rules as the admin. [Content from code](../getting-started/content-from-code.md) shows how to write your own.
|
|
141
|
+
|
|
142
|
+
Next, [make your first page](../getting-started/first-page.md).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Working together
|
|
2
|
+
|
|
3
|
+
Who can open the admin, and what happens when two people open the same page: the admin shows who is where, and never lets one person's save silently overwrite another's.
|
|
4
|
+
|
|
5
|
+
## Who can open the admin
|
|
6
|
+
|
|
7
|
+
Everyone who can open the admin is a member of the space, with a role:
|
|
8
|
+
|
|
9
|
+
| | Admin | Editor |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| `Edit, publish, unpublish and delete content; upload and delete media` | yes | yes |
|
|
12
|
+
| `Open Settings` | yes | no |
|
|
13
|
+
| `Manage the space's members` | yes | no |
|
|
14
|
+
|
|
15
|
+
Members are managed in the project at smoodly.io; the admin's Members page lists them and links there ("Manage members at smoodly.io"). A space always keeps at least one admin, so it cannot lock itself out: a member list without one is refused.
|
|
16
|
+
|
|
17
|
+
## Signing in
|
|
18
|
+
|
|
19
|
+
Editors sign in at smoodly.io, never on your site: `/admin` sends them there and back, and someone already signed in at smoodly.io comes straight back without a form. What your site's admin holds is a session for its own space only, renewed while they keep working and good for 30 days from their last visit. *Sign out* in the admin's account menu signs them out of this site and leaves their smoodly.io account signed in; the account menu's *My Account* opens their account at smoodly.io. Someone removed from the project is signed out of its sites at once: their next action sends them to sign in, and the others stop seeing them in presence.
|
|
20
|
+
|
|
21
|
+
## Who is editing what
|
|
22
|
+
|
|
23
|
+
A lock after a title in Pages, in a collection or in Site 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
|
+
|
|
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
|
+
|
|
27
|
+
## When two people save the same thing
|
|
28
|
+
|
|
29
|
+
Every save, publish, discard and restore carries the version you last loaded. If the other person saved in between, yours is refused rather than applied over theirs, and a red notice says who saved and when:
|
|
30
|
+
|
|
31
|
+
> aleksi saved a newer version at 14:32.
|
|
32
|
+
> Your unsaved changes here will be discarded.
|
|
33
|
+
|
|
34
|
+
The form stops taking input until you press **Load their version**, which shows what they saved. Your unsaved edits are gone; the notice says so before you press it.
|
|
35
|
+
|
|
36
|
+
Most of the time you never see that notice. When the other person saves, an open view that has no unsaved edits quietly refreshes to their version, and one with unsaved edits shows the notice at once.
|
|
37
|
+
|
|
38
|
+
Only your own edits count. Opening a new page fills its fixed sections for you, and that is saved like an edit. If the other person opened the page first and their view filled it, yours takes their version without the notice.
|
|
39
|
+
|
|
40
|
+
## Languages are separate
|
|
41
|
+
|
|
42
|
+
Drafts are per language. Someone editing the Finnish page does not lock the English one, and saves in one language never conflict with the other.
|