@plitzi/sdk-server 0.38.7 → 0.38.8
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/CHANGELOG.md +8 -0
- package/README.md +4 -4
- package/dist/modules/actions/runtime/projectData.js +1 -1
- package/dist/modules/project/plugins.d.ts +1 -1
- package/dist/modules/project/plugins.js +2 -2
- package/dist/modules/runtime/bundle.d.ts +1 -1
- package/dist/modules/runtime/bundle.js +1 -1
- package/package.json +3 -3
- package/skills/plitzi-authoring/SKILL.md +34 -149
- package/skills/plitzi-authoring/recipes/plugin.ts +1 -1
- package/skills/plitzi-authoring/reference/accessibility.md +1 -2
- package/skills/plitzi-authoring/reference/authoring-errors.md +1 -0
- package/skills/plitzi-authoring/reference/drawing.md +1 -1
- package/skills/plitzi-authoring/reference/flows.md +4 -2
- package/skills/plitzi-authoring/reference/index.md +33 -0
- package/skills/plitzi-authoring/reference/plugins.md +2 -2
- package/skills/plitzi-authoring/reference/recipes.md +25 -0
- package/skills/plitzi-authoring/reference/rules.md +61 -0
- package/skills/plitzi-authoring/reference/snippets-and-export.md +2 -2
- package/skills/plitzi-authoring/reference/structure.md +1 -1
- package/skills/plitzi-authoring/reference/testing.md +1 -1
- package/skills/plitzi-authoring/reference/validation.md +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -159,7 +159,7 @@ serve it — worth running over anything that arrives as a file.
|
|
|
159
159
|
A space on Plitzi comes out whole, as a project wired to this server: `npx @plitzi/cli create my-site --from <space>`
|
|
160
160
|
writes its pages as authoring code, its actions as `defineAction` code, its functions, the source of its plugins and
|
|
161
161
|
runtime, and its files into `public/` — a draft, or any published snapshot (`--environment`, `--revision`) — and
|
|
162
|
-
`plitzi pull` keeps it in step, and `plitzi push` puts what changed in it back on the space. See
|
|
162
|
+
`plitzi space pull` keeps it in step, and `plitzi space push` puts what changed in it back on the space. See
|
|
163
163
|
`docs/en/projects-from-spaces.md` in the workspace.
|
|
164
164
|
|
|
165
165
|
## Projects
|
|
@@ -221,7 +221,7 @@ What it wires from the project:
|
|
|
221
221
|
- **Its files.** `public/` served as it is, `src/data/` read and never served (`dataDir`), the `kv` in `state/kv.json`
|
|
222
222
|
(`createFileKv`), `PLITZI_SIGNING_SECRET` what `ctx.sign` signs with.
|
|
223
223
|
- **Where it is.** `/health` answers with the space's permanent URL (or the cloud `name`), and `tmp/dev-server.json`
|
|
224
|
-
records the name, the port and the URL for `plitzi check`, `shot` and `visual`. A signal closes it (`closeOnSignals`).
|
|
224
|
+
records the name, the port and the URL for `plitzi page check`, `shot` and `visual`. A signal closes it (`closeOnSignals`).
|
|
225
225
|
|
|
226
226
|
While developing — `NODE_ENV` other than `production` — `devMode` and `devReload` are on, and:
|
|
227
227
|
|
|
@@ -541,7 +541,7 @@ createServer({ dataDir: path.join(PROJECT_ROOT, 'src/data'), adapters: { ... } }
|
|
|
541
541
|
```
|
|
542
542
|
|
|
543
543
|
The data is read through one lookup, `action.lookups.getData(spaceId, at)` — the space's own data as its files' text by
|
|
544
|
-
path, as of the version being rendered or run: what a cloud deployment keeps for a space (`plitzi push`). Given none,
|
|
544
|
+
path, as of the version being rendered or run: what a cloud deployment keeps for a space (`plitzi space push`). Given none,
|
|
545
545
|
`createServer` derives it from `dataDir` (its `.json` files, read again once they change), so the folder and the
|
|
546
546
|
platform are read the same way.
|
|
547
547
|
|
|
@@ -642,7 +642,7 @@ All fields are optional and combined with AND logic. Calling `invalidate()` with
|
|
|
642
642
|
|
|
643
643
|
Plugins are React component bundles that extend the Plitzi schema renderer. They are defined globally at server config level, compiled or copied on first use, and cached on disk for one week. The `getSpaceDeployment` adapter controls which plugins each space gets access to via `pluginNames`.
|
|
644
644
|
|
|
645
|
-
A compiled plugin is one module and its stylesheet, built as `plitzi pack
|
|
645
|
+
A compiled plugin is one module and its stylesheet, built as `plitzi plugin pack` builds it: React and the SDK kept out, and everything else inside — images and fonts as data URIs, and a file imported whole the way Vite imports it (`worker.js?raw` for its text, `engine.wasm?inline` for a data URI).
|
|
646
646
|
|
|
647
647
|
Whatever stylesheet the server writes for a plugin — compiled, copied or downloaded — goes into the `plitzi-sdk-plugin` cascade layer (`inPluginLayer` from `@plitzi/sdk-shared/style`), below the space's own: a space's classes and `customCss` win over what the plugin shipped, whatever the specificity. A `css` given as a web path (`/builder-assets/…`) is linked as it is.
|
|
648
648
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile, readdir, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
//#region src/modules/actions/runtime/projectData.ts
|
|
4
|
-
/** What a project keeps in its data folder: JSON, the only files `plitzi push` sends as the space's data. */
|
|
4
|
+
/** What a project keeps in its data folder: JSON, the only files `plitzi space push` sends as the space's data. */
|
|
5
5
|
var DATA_EXTENSION = ".json";
|
|
6
6
|
/**
|
|
7
7
|
* A data folder as the lookup a deployment would otherwise write — what a self-hosted server derives from `dataDir`, so
|
|
@@ -23,7 +23,7 @@ export declare const projectPlugins: (root: string) => Promise<ProjectPlugins>;
|
|
|
23
23
|
/**
|
|
24
24
|
* A plugin edited while developing is built again by the server, and the open pages swap it where it is drawn — the
|
|
25
25
|
* rest of the page, its state included, stays as it was (`devReload`). Its server half (`functions/`) is loaded again
|
|
26
|
-
* here, in place. A plugin ADDED — a new folder with an `index.ts`, what `plitzi add
|
|
26
|
+
* here, in place. A plugin ADDED — a new folder with an `index.ts`, what `plitzi plugin add` writes — is registered
|
|
27
27
|
* here, and the pages load again to render it; one removed is turned off. A folder the server cannot build, or whose
|
|
28
28
|
* server half has no entry, is said in the terminal as the layout check says it at boot — once, until it changes — and
|
|
29
29
|
* the server goes on. Answers what stops watching.
|
|
@@ -39,7 +39,7 @@ var folderFunctions = async (root, folder) => {
|
|
|
39
39
|
const functions = path.join(root, PLUGINS_DIR, folder, PLUGIN_FUNCTIONS_DIR);
|
|
40
40
|
return existsSync(path.join(functions, "index.ts")) ? (await loadFunctions(functions)).at(0) : void 0;
|
|
41
41
|
};
|
|
42
|
-
/** A plugin of `vendor/plugins`, from the manifest `plitzi pack
|
|
42
|
+
/** A plugin of `vendor/plugins`, from the manifest `plitzi plugin pack` wrote beside its bundle when it was published. */
|
|
43
43
|
var builtPlugin = (root, type) => {
|
|
44
44
|
const read = readVendorPlugin(root, type);
|
|
45
45
|
if ("problem" in read) throw new Error(read.problem.message);
|
|
@@ -88,7 +88,7 @@ var projectPlugins = async (root) => {
|
|
|
88
88
|
/**
|
|
89
89
|
* A plugin edited while developing is built again by the server, and the open pages swap it where it is drawn — the
|
|
90
90
|
* rest of the page, its state included, stays as it was (`devReload`). Its server half (`functions/`) is loaded again
|
|
91
|
-
* here, in place. A plugin ADDED — a new folder with an `index.ts`, what `plitzi add
|
|
91
|
+
* here, in place. A plugin ADDED — a new folder with an `index.ts`, what `plitzi plugin add` writes — is registered
|
|
92
92
|
* here, and the pages load again to render it; one removed is turned off. A folder the server cannot build, or whose
|
|
93
93
|
* server half has no entry, is said in the terminal as the layout check says it at boot — once, until it changes — and
|
|
94
94
|
* the server goes on. Answers what stops watching.
|
|
@@ -19,7 +19,7 @@ export declare const inspectRuntime: (bytes: Uint8Array) => {
|
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
21
|
* A project's runtime module, as a server of its own runs it — `src/runtime/index.ts`, or what `build` compiled it to —
|
|
22
|
-
* or nothing when the project has none: a project starts one by writing it (`plitzi add
|
|
22
|
+
* or nothing when the project has none: a project starts one by writing it (`plitzi runtime add`), with nothing to wire.
|
|
23
23
|
*/
|
|
24
24
|
export declare const loadRuntimeModule: (file: string) => Promise<SpaceRuntime | undefined>;
|
|
25
25
|
/**
|
|
@@ -93,7 +93,7 @@ var runtimeOf = (loaded) => {
|
|
|
93
93
|
};
|
|
94
94
|
/**
|
|
95
95
|
* A project's runtime module, as a server of its own runs it — `src/runtime/index.ts`, or what `build` compiled it to —
|
|
96
|
-
* or nothing when the project has none: a project starts one by writing it (`plitzi add
|
|
96
|
+
* or nothing when the project has none: a project starts one by writing it (`plitzi runtime add`), with nothing to wire.
|
|
97
97
|
*/
|
|
98
98
|
var loadRuntimeModule = async (file) => {
|
|
99
99
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plitzi/sdk-server",
|
|
3
|
-
"version": "0.38.
|
|
3
|
+
"version": "0.38.8",
|
|
4
4
|
"license": "AGPL-3.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -96,8 +96,8 @@
|
|
|
96
96
|
"./dist/env.js"
|
|
97
97
|
],
|
|
98
98
|
"dependencies": {
|
|
99
|
-
"@plitzi/plitzi-sdk": "0.38.
|
|
100
|
-
"@plitzi/sdk-shared": "0.38.
|
|
99
|
+
"@plitzi/plitzi-sdk": "0.38.8",
|
|
100
|
+
"@plitzi/sdk-shared": "0.38.8",
|
|
101
101
|
"ejs": "^6.0.1",
|
|
102
102
|
"esbuild": "^0.28.2",
|
|
103
103
|
"jsonwebtoken": "^9.0.3",
|
|
@@ -9,174 +9,59 @@ description: >-
|
|
|
9
9
|
|
|
10
10
|
# Authoring Plitzi spaces
|
|
11
11
|
|
|
12
|
-
A space is two JSON documents
|
|
13
|
-
|
|
14
|
-
declaration, and refuses a declaration that would not render.
|
|
12
|
+
A space is two JSON documents full of cross-referenced ids — **never write them by hand**. `@plitzi/sdk-authoring`
|
|
13
|
+
derives them from a small declaration, and refuses one that would not render.
|
|
15
14
|
|
|
16
15
|
```ts
|
|
17
|
-
import {
|
|
16
|
+
import { heading, styles, text } from '@plitzi/sdk-authoring';
|
|
18
17
|
|
|
19
|
-
const page = styles('page', { display: 'flex', 'flex-direction': 'column',
|
|
18
|
+
const page = styles('page', { display: 'flex', 'flex-direction': 'column', gap: '16px' });
|
|
20
19
|
|
|
21
20
|
export const space = {
|
|
22
21
|
name: 'My space',
|
|
23
22
|
permanentUrl: 'my-space',
|
|
24
23
|
pages: [{ name: 'Home', slug: '', class: page, body: [heading('Hello', { subType: 'h1' }), text('A paragraph.')] }]
|
|
25
24
|
};
|
|
26
|
-
|
|
27
|
-
const { schema, style, warnings } = authorSpace(space);
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
and open a reference below only when the task names its subject. What an element takes, a step's params, what a
|
|
32
|
-
trigger hands its flow: `npx @plitzi/cli explain <name>` answers in a few lines (over MCP, `plitzi://explain/<name>`).
|
|
33
|
-
The published `.d.ts` documents the rest — search it by name, never read it whole.
|
|
34
|
-
|
|
35
|
-
This skill writes a space that lives in code. One that lives on Plitzi — edited in the builder, published from there —
|
|
36
|
-
is edited through the Plitzi MCP server instead, never both on one space; `plitzi create --from <space>` turns the
|
|
37
|
-
second into the first. If the MCP asks for a sign-in nobody can give, a project in code needs no account.
|
|
27
|
+
A space on Plitzi — edited in the builder — is edited through the Plitzi MCP server instead, never both on one space.
|
|
38
28
|
|
|
39
29
|
## How to work
|
|
40
30
|
|
|
41
|
-
1. **
|
|
42
|
-
|
|
43
|
-
2. **
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
3. **Read every warning, then every suggestion.** A warning names something written that will not do what it says —
|
|
49
|
-
zero warnings is the bar. A suggestion (`[suggest]`) names a shorter way to the same page — a layout, a component,
|
|
50
|
-
a `list`, a button's own `content` — or what nothing uses. Take them, the ones that save the most first
|
|
51
|
-
([efficiency](reference/efficiency.md)). Then `npm run lint:space`: how the source is written — long files, rows of
|
|
52
|
-
data inline, colours that are not tokens — at file:line. Leave both clean.
|
|
53
|
-
4. **Look at it.** `npm run check -- /about --width 1440,390` says in text whether the page is whole — every element on
|
|
54
|
-
screen, nothing overflowing, a clean console; `npm run shot -- /about --width 390 --scheme dark` is the picture
|
|
55
|
-
(`--frames 4` shows what moves); `npm run visual` runs the checks. Look at desktop, tablet and mobile, light and dark, and the page while its data is still loading.
|
|
56
|
-
5. **Go through the [review checklist](reference/review-checklist.md) before saying it is done.** It is the feedback a
|
|
57
|
-
reviewer gives on every change, written down so you do not need to hear it.
|
|
31
|
+
1. **Find, then write.** `npx plitzi element where <id|class|words>` answers the file, the line and the call: edit there, and
|
|
32
|
+
extend what the space already has. People move files — never keep a note of where something is.
|
|
33
|
+
2. **Author.** `npm run author`. A refusal says what to write instead: do exactly that. Every problem comes at once —
|
|
34
|
+
fix them all before running again. Zero warnings; take the suggestions, the biggest first.
|
|
35
|
+
3. **Look.** `npm run check -- /path --width 1440,390` says in text whether the page is whole; `npm run shot` only
|
|
36
|
+
when the text is not enough.
|
|
37
|
+
4. **Before saying done:** `npm run lint:space` clean, and the [review checklist](reference/review-checklist.md).
|
|
58
38
|
|
|
59
|
-
|
|
39
|
+
What a name means — an element, a step, a trigger, a code, any export (`pageFamily`, `SpaceSpec`): `npx plitzi explain
|
|
40
|
+
<name>`, in a few lines. Never read the `.d.ts`.
|
|
60
41
|
|
|
61
|
-
|
|
62
|
-
namespace for the whole space — layouts and every page share it — so a helper that runs more than once builds inside
|
|
63
|
-
`scope('promos', ref => …)`: every id in it is prefixed (`promos-panel`), and `ref('slides')` names one in full. An `id` is the element's name in the
|
|
64
|
-
space (`data-plitzi-el` in the page); the DOM id a URL's `#fragment` lands on is its `anchor`.
|
|
65
|
-
2. **Share with classes, layouts and components, never with copies.** A look used twice is a `styles()` class.
|
|
66
|
-
Chrome shown on several pages — a header, a sidebar, a footer — is a **layout** the pages name, written once. A block
|
|
67
|
-
placed many times with different content — a product card, a testimonial — is a **component** placed with
|
|
68
|
-
`component(id, { props })`. A menu, a card grid, a list of steps is DATA, never a block pasted per item: a short
|
|
69
|
-
menu mapped in code is fine; rows — cards, dishes, tiles — are one `list` (`items: [ … ]`) whose row is written
|
|
70
|
-
once. A link or a button says its words as
|
|
71
|
-
its own `content`, not through a `text` inside it. Pages of one shape are one `pageFamily` over data. See
|
|
72
|
-
[layouts](reference/layouts.md), [components](reference/components.md), [structure](reference/structure.md) and
|
|
73
|
-
[efficiency](reference/efficiency.md).
|
|
74
|
-
3. **One element, one base selector.** An element takes a shared `class` OR its own `css`, never both — authoring
|
|
75
|
-
refuses the pair. "This class plus one thing" is the class with rules on top, last in the list:
|
|
76
|
-
`class: [cover, { opacity: '0.25' }]` (needs the element's `id`; it becomes the class `<id>--own`). A look that
|
|
77
|
-
never changes is its own class.
|
|
78
|
-
4. **Visible by default; decide which way the logic flips.** An element is on screen unless something hides it. When
|
|
79
|
-
the logic REVEALS it (hidden → shown: an empty state, a "get started" card, an admin-only panel), it starts hidden —
|
|
80
|
-
`visible: 'source'`, or `visible: false` plus a binding for a computed condition — so it never flashes while data
|
|
81
|
-
loads (`condition-starts-visible` warns). When a flag HIDES it (shown → hidden: a sidebar label until the sidebar
|
|
82
|
-
is folded), it keeps the default and an absent flag must leave it shown. See
|
|
83
|
-
[data and visibility](reference/data-and-visibility.md). What a PERSON switches on — a feature in beta, a rollout —
|
|
84
|
-
is a feature flag, not a visibility: gated off, it is not rendered at all. See [feature flags](reference/feature-flags.md).
|
|
85
|
-
5. **Know where a template is evaluated.** A binding's template (`bindTemplate`) and a step's params evaluate full
|
|
86
|
-
Twig. An ATTRIBUTE resolves a name with filters (`{{ list_games.item.slug }}`, `{{ post.slug|url_encode }}`) against
|
|
87
|
-
the sources around the element; a condition there is used as written. Inside any template a source is spelled in
|
|
88
|
-
full — `apiContainer_stats`, `list_rows` — and a query param is `navigation.queryParams.x`. A template that feeds a
|
|
89
|
-
list its `items` hands over its value (`returns: 'value'`). `authorSpace` refuses a template it cannot read and a
|
|
90
|
-
name nothing answers to. See [templates](reference/templates.md).
|
|
91
|
-
6. **Tokens, not colours.** Every colour is a variable of the space with a `light` and a `dark` value; both themes are
|
|
92
|
-
checked from the first commit.
|
|
93
|
-
7. **Times in UTC, and say so.** Format with an explicit zone (`|date('j M · H:i', 'UTC')`) and print "UTC" beside it:
|
|
94
|
-
a page rendered on a server and hydrated in a browser in another zone must agree on the hour.
|
|
95
|
-
8. **Breakpoints are ranges.** `tablet` (48–64rem) and `mobile` (up to 48rem) each inherit only from `desktop`; a rule
|
|
96
|
-
meant for both is written under `compact`.
|
|
97
|
-
9. **Never invent data.** Numbers, names and states on screen come from a source. A panel with nothing true to say is
|
|
98
|
-
an empty state, not a placeholder figure. With no backend, the data is JSON read by an `apiContainer`: a server
|
|
99
|
-
project's `src/data/*.json`, read on the server and never served; with no server, `public/data/*.json`, which is
|
|
100
|
-
public — see [data and visibility](reference/data-and-visibility.md).
|
|
101
|
-
10. **Never hand-write** `flat`, derived ids, `styleSelectors`, `beforeNode`/`afterNode`/`flowId`, or a
|
|
102
|
-
`styleVariant` binding's key — use the factories, `variantFrom` and `activeOn`.
|
|
103
|
-
11. **A switch is named for how it leaves its default.** `toggleState` turns a key nobody has set yet ON, so a key
|
|
104
|
-
named for the default (`showPlates`, on by default) takes a first click to do nothing: name it `platesOff`, and read
|
|
105
|
-
every switch through `computed` (`plates: '{{ state.platesOff ? false : true }}'`) so its default shows before
|
|
106
|
-
anybody touches it.
|
|
107
|
-
12. **Hand plugins over as their declarations**: `authorSpace(space, { plugins: [declaration] })` holds a plugin —
|
|
108
|
-
its own type, or a `custom({ renderType })` host — to the events, actions and attributes it declares. See
|
|
109
|
-
[plugins](reference/plugins.md).
|
|
110
|
-
13. **Usable without sight.** Screen readers and browser agents (Claude in Chrome) find a page's controls in its
|
|
111
|
-
accessibility tree: every button and link has words (an icon-only button a `title`), every field a `label`
|
|
112
|
-
(`hideLabel: true` hides it and keeps the name), every image an `alt` or `decorative: true`. Clicks go on a
|
|
113
|
-
`button` — it holds children, so a whole card can be one — or a `link`, never on a container. See
|
|
114
|
-
[accessibility](reference/accessibility.md).
|
|
115
|
-
14. **Motion that stays smooth.** Animate `opacity` and `transform` only; no animated blur, shadow or size. See
|
|
116
|
-
[motion](reference/colours-and-motion.md).
|
|
42
|
+
## Five rules authoring cannot check for you
|
|
117
43
|
|
|
118
|
-
|
|
44
|
+
1. **Name what is referred to.** An `id` for every element a binding, a flow or a test addresses; ids are one namespace,
|
|
45
|
+
so a helper that runs twice builds inside `scope('promos', ref => …)`.
|
|
46
|
+
2. **Share, never copy.** A look used twice is a class; chrome on several pages a layout; a block placed again with
|
|
47
|
+
other content a component; rows of data one `list`.
|
|
48
|
+
3. **What is revealed starts hidden** (`visible: 'source'`), so it never flashes while its data loads.
|
|
49
|
+
4. **Know where a template runs.** A binding and a step's params are full Twig; an attribute resolves a name with
|
|
50
|
+
filters. A source is spelled in full: `apiContainer_stats`.
|
|
51
|
+
5. **Tokens, not colours** — a variable with a `light` and a `dark` value.
|
|
119
52
|
|
|
120
|
-
|
|
53
|
+
The rest, and why: [rules](reference/rules.md).
|
|
121
54
|
|
|
122
|
-
|
|
123
|
-
| --- | --- |
|
|
124
|
-
| Show data with no backend: a JSON file, a card per row, a count, an empty state, a computed value | [recipes/show-data.ts](recipes/show-data.ts) |
|
|
125
|
-
| Data typed by a sample of it: completed and checked paths, typed rows | [recipes/typed-data.ts](recipes/typed-data.ts) |
|
|
126
|
-
| Data read by the page server (`--mode server`) | [recipes/server-data.ts](recipes/server-data.ts) |
|
|
127
|
-
| An accordion, one answer open at a time | [recipes/accordion.ts](recipes/accordion.ts) |
|
|
128
|
-
| Filter and sort a list from a select | [recipes/filter-a-list.ts](recipes/filter-a-list.ts) |
|
|
129
|
-
| A page per record (`/products/:slug`), and "not found" | [recipes/detail-page.ts](recipes/detail-page.ts) |
|
|
130
|
-
| Link to a section of a page (`/#plans`) | [recipes/link-to-a-section.ts](recipes/link-to-a-section.ts) |
|
|
131
|
-
| A carousel: slides with arrows, dots and autoplay; a marquee; a row that swipes | [recipes/carousel.ts](recipes/carousel.ts) |
|
|
132
|
-
| Do something every few seconds — a ticker, a poll, a slideshow by hand | [recipes/every-few-seconds.ts](recipes/every-few-seconds.ts) |
|
|
133
|
-
| A marquee that scrolls for ever and stops under the pointer | [recipes/marquee.ts](recipes/marquee.ts) |
|
|
134
|
-
| A row that scrolls sideways, with arrows that hide at its ends | [recipes/scroll-a-row.ts](recipes/scroll-a-row.ts) |
|
|
135
|
-
| A link built from a row: WhatsApp with the product's name, `mailto:` | [recipes/link-with-data.ts](recipes/link-with-data.ts) |
|
|
136
|
-
| React to a click or a submit; a modal; a switch | [recipes/forms-and-modals.ts](recipes/forms-and-modals.ts) |
|
|
137
|
-
| A feature behind a flag | [recipes/feature-flag.ts](recipes/feature-flag.ts) |
|
|
138
|
-
| An element of your own (a plugin), checked | [recipes/plugin.ts](recipes/plugin.ts) |
|
|
139
|
-
| Controls a screen reader and a browser agent can use | [recipes/usable-without-sight.ts](recipes/usable-without-sight.ts) |
|
|
140
|
-
| Tokens, a shared class, "the class plus one thing", tablet and phone at once | [recipes/style-a-page.ts](recipes/style-a-page.ts) |
|
|
141
|
-
| A design in Tailwind classes, kept as classes the builder edits | [recipes/from-tailwind.ts](recipes/from-tailwind.ts) |
|
|
142
|
-
| A page from elsewhere in a frame (`embed`: a map, a player) and a drawing of your own (`svg`) | [recipes/embed-and-svg.ts](recipes/embed-and-svg.ts) |
|
|
55
|
+
## What to read
|
|
143
56
|
|
|
144
|
-
|
|
57
|
+
Start with [the cheatsheet](CHEATSHEET.md), then only what the task names:
|
|
145
58
|
|
|
146
|
-
|
|
|
59
|
+
| The task | Read |
|
|
147
60
|
| --- | --- |
|
|
148
|
-
| [elements-and-styles
|
|
149
|
-
| [
|
|
150
|
-
|
|
|
151
|
-
| [
|
|
152
|
-
|
|
|
153
|
-
| [
|
|
154
|
-
| [
|
|
155
|
-
| [kept-state.md](reference/kept-state.md) | State that outlives a reload: `keepState`, transient and painted keys |
|
|
156
|
-
| [auth.md](reference/auth.md) | Signing people in: providers, `authLogin`, `auth.*`, visitor roles |
|
|
157
|
-
| [feature-flags.md](reference/feature-flags.md) | Switching a part of the space on or off — a beta, a rollout, the old version kept until the new one ships |
|
|
158
|
-
| [lists.md](reference/lists.md) | Rendering rows, filtering and sorting them, a detail page for one record, carousels |
|
|
159
|
-
| [typed-sources.md](reference/typed-sources.md) | Data typed by a sample of it: `source()`, typed rows, `twig` for templates |
|
|
160
|
-
| [validation.md](reference/validation.md) | How `authorSpace` checks, the loop that wastes no attempts, and what it cannot see |
|
|
161
|
-
| [authoring-errors.md](reference/authoring-errors.md) | What `authorSpace` refuses or warns about, and what to write instead |
|
|
162
|
-
| [templates.md](reference/templates.md) | Any `{{ … }}` or `{% … %}`: where it runs, naming sources, filters, tests, dates |
|
|
163
|
-
| [flows.md](reference/flows.md) | Clicks, submits, page loads, every few seconds, server actions, modals, state |
|
|
164
|
-
| [realtime.md](reference/realtime.md) | Pages that see each other: channels, presence, cursors, a shared board, who may hear a topic |
|
|
165
|
-
| [plugins.md](reference/plugins.md) | A component of your own: props, binding them, writing state, channels, registering, behaving in the builder |
|
|
166
|
-
| [drawing.md](reference/drawing.md) | A plugin that draws or animates: a canvas sized to the device, WebGL, a loop that stops when unseen |
|
|
167
|
-
| [structure.md](reference/structure.md) | A space bigger than one screen: files, helpers, naming, keeping it short |
|
|
168
|
-
| [testing.md](reference/testing.md) | Any test: `inspectPage`, handles, fixtures, catching a flash from the first frame, shortcuts, counting renders |
|
|
169
|
-
| [performance.md](reference/performance.md) | A page with many elements, a busy flow, something that feels slow: what renders, what it costs, how to measure it |
|
|
170
|
-
| [snippets-and-export.md](reference/snippets-and-export.md) | Publishing a snippet; turning an exported JSON — or a server action — into code |
|
|
171
|
-
| [accessibility.md](reference/accessibility.md) | Icon buttons, fields, images, clickable cards, toggles, headings, landmarks, a canvas — anything a screen reader or a browser agent has to use |
|
|
172
|
-
| [review-checklist.md](reference/review-checklist.md) | Before you say it is done |
|
|
173
|
-
|
|
174
|
-
**What to read for a kind of task** — the cheatsheet, then only these:
|
|
175
|
-
|
|
176
|
-
- A site — landing, catalogue, blog: elements-and-styles, layouts, components, lists.
|
|
177
|
-
- An app with state and actions: data-and-visibility, templates, flows — realtime when pages see each other.
|
|
178
|
-
- A component of your own: plugins.
|
|
179
|
-
- A refusal or a warning: search authoring-errors.md for its code — never read it whole.
|
|
180
|
-
- Tests: testing.
|
|
181
|
-
|
|
182
|
-
Nothing else needs reading up front.
|
|
61
|
+
| A site — landing, catalogue, blog | [elements-and-styles](reference/elements-and-styles.md), [layouts](reference/layouts.md), [components](reference/components.md), [lists](reference/lists.md) |
|
|
62
|
+
| An app with state and actions | [data-and-visibility](reference/data-and-visibility.md), [templates](reference/templates.md), [flows](reference/flows.md) |
|
|
63
|
+
| A component of your own | [plugins](reference/plugins.md) |
|
|
64
|
+
| A whole working file to copy | [recipes](reference/recipes.md) |
|
|
65
|
+
| A refusal or a warning | `npx plitzi explain <code>` |
|
|
66
|
+
| Tests | [testing](reference/testing.md) |
|
|
67
|
+
| Anything else | [the index](reference/index.md): every reference, and when |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* An element of your own (a plugin): authored from its declaration — the one `plitzi add
|
|
2
|
+
* An element of your own (a plugin): authored from its declaration — the one `plitzi plugin add` writes — so a flow on
|
|
3
3
|
* its events, a step to its actions and its attributes are all checked. Hand the declaration to `authorSpace` too.
|
|
4
4
|
*/
|
|
5
5
|
import {
|
|
@@ -111,5 +111,4 @@ can't click something that only appears on focus.
|
|
|
111
111
|
- `authorSpace` warnings: zero, as always.
|
|
112
112
|
- In a test, `page.getByRole('button', { name: 'Close' })` fails when the name is missing.
|
|
113
113
|
`expect(locator).toMatchAriaSnapshot(…)` pins the whole outline.
|
|
114
|
-
- Through MCP, `
|
|
115
|
-
no name.
|
|
114
|
+
- Through MCP, `plitzi_look` returns the page's tree (its default view) and lists everything that has no name.
|
|
@@ -112,6 +112,7 @@ The space is not written until these are fixed.
|
|
|
112
112
|
| `part-missing` | a compound element — a carousel, a tab container, a dropdown — without a part it renders through | the part the message names, inside it (`carousel()` writes its own track) |
|
|
113
113
|
| `prop-missing` | an instance without a prop its component requires | `component('card', { props: { name: … } })`, or bind it |
|
|
114
114
|
| `prop-name` | a prop whose name a template cannot read as `props.x` | letters, digits and `_` |
|
|
115
|
+
| `prop-type-unknown` | a prop declared with a type no editor offers and nothing checks a value against | one of text, textarea, select, boolean, number, scalar, json, elementIds — words are `text` |
|
|
115
116
|
| `prop-unknown` | a prop the component does not declare, handed in or read | the prop it suggests, or declare it: `props: { name: { type: 'text' } }` |
|
|
116
117
|
| `prop-value` | a prop handed in with a value its declaration does not take | a value of the declared type, or one of its options |
|
|
117
118
|
| `props-outside-component` | `props.x` read outside a component | read the source it would have come from |
|
|
@@ -17,7 +17,7 @@ It sizes the canvas to the device (at most 2× — `maxPixelRatio`), follows its
|
|
|
17
17
|
can see it move: on a live page (in the builder it draws one still frame), for a visitor who did not ask for less
|
|
18
18
|
motion, with the tab in front and the canvas on screen; `paused` holds it (bind it to a hover). A shader that does not
|
|
19
19
|
compile — GLSL ES 1.0 wants `196.0`, not `196` — throws a `ShaderError` with the driver's log, printed as
|
|
20
|
-
`[plugin <type> "<id>"] fragment shader failed: …` (`plitzi check` lists it under the console) and `error` says it;
|
|
20
|
+
`[plugin <type> "<id>"] fragment shader failed: …` (`plitzi page check` lists it under the console) and `error` says it;
|
|
21
21
|
show the still image meanwhile, as above. `useAnimationFrame(onFrame, { target })` is the same loop for motion that
|
|
22
22
|
is not a canvas.
|
|
23
23
|
|
|
@@ -21,8 +21,8 @@ source — `when({ field: 'flags.newCheckout', operator: '=', value: true }, …
|
|
|
21
21
|
same flags, decided on the server. Use the step builders — they fill in where a step runs and what it takes:
|
|
22
22
|
|
|
23
23
|
- **Where it runs.** A global callback registers under its source MODULE (`state`, `auth`, `actions`), an element
|
|
24
|
-
callback under an element's id, a utility under nothing.
|
|
25
|
-
|
|
24
|
+
callback under an element's id, a utility under nothing. Either half named wrong is a control that silently does
|
|
25
|
+
nothing.
|
|
26
26
|
- **Which `setState`.** `setState({ key })` writes `runtime.state.<key>` and is read as `state.<key>`; never put
|
|
27
27
|
`state.` in the key. `updateElement(…)` changes one element's own attribute or state.
|
|
28
28
|
- **Flip in one step.** `toggleState({ key })` for app state, `toggleElement({ category: 'state', key: 'visibility' },
|
|
@@ -144,6 +144,8 @@ without a word.
|
|
|
144
144
|
The toast carries no class of the space's, so this — not a `.Toastify__*` rule in `customCss` — is where it is
|
|
145
145
|
dressed.
|
|
146
146
|
|
|
147
|
+
Copy link: `copyToClipboard('{{ navigation.href }}')`, then the toast — it fails with no clipboard.
|
|
148
|
+
|
|
147
149
|
## Modals, dropdowns, tabs
|
|
148
150
|
|
|
149
151
|
- A modal or dialog is declared `visible: false` and driven with `openModal('credits')` / `closeModal('credits')`
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Every reference, and when to read it
|
|
2
|
+
|
|
3
|
+
Read one only when the task names its subject.
|
|
4
|
+
|
|
5
|
+
| Read | When |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| [rules.md](rules.md) | Every rule of writing a space, with its why |
|
|
8
|
+
| [recipes.md](recipes.md) | A whole working file for a common task, to copy and rename |
|
|
9
|
+
| [elements-and-styles.md](elements-and-styles.md) | Any element or CSS: factories, fields, classes, states, variants, tokens, fonts, lists, links |
|
|
10
|
+
| [colours-and-motion.md](colours-and-motion.md) | Colours for both themes, tokens, keyframes, and motion that stays smooth (good practices) |
|
|
11
|
+
| [tailwind.md](tailwind.md) | A design written in Tailwind classes: `tw()`, its breakpoints and states, what it refuses |
|
|
12
|
+
| [layouts.md](layouts.md) | Anything shown on more than one page; menus; reducing duplication of elements and styles |
|
|
13
|
+
| [efficiency.md](efficiency.md) | The same page with fewer elements: the suggestions `authorSpace` makes, and the short way for each long one |
|
|
14
|
+
| [components.md](components.md) | One block placed many times — a card, a tier, a testimonial: props, slots, binding a row into one, why it is closed |
|
|
15
|
+
| [data-and-visibility.md](data-and-visibility.md) | Bindings, providers, offline data, loading/empty/error states, live data, caching, showing and hiding, kept state |
|
|
16
|
+
| [kept-state.md](kept-state.md) | State that outlives a reload: `keepState`, transient and painted keys |
|
|
17
|
+
| [auth.md](auth.md) | Signing people in: providers, `authLogin`, `auth.*`, visitor roles |
|
|
18
|
+
| [feature-flags.md](feature-flags.md) | Switching a part of the space on or off — a beta, a rollout, the old version kept until the new one ships |
|
|
19
|
+
| [lists.md](lists.md) | Rendering rows, filtering and sorting them, a detail page for one record, carousels |
|
|
20
|
+
| [typed-sources.md](typed-sources.md) | Data typed by a sample of it: `source()`, typed rows, `twig` for templates |
|
|
21
|
+
| [validation.md](validation.md) | How `authorSpace` checks, the loop that wastes no attempts, and what it cannot see |
|
|
22
|
+
| [authoring-errors.md](authoring-errors.md) | What `authorSpace` refuses or warns about, and what to write instead |
|
|
23
|
+
| [templates.md](templates.md) | Any `{{ … }}` or `{% … %}`: where it runs, naming sources, filters, tests, dates |
|
|
24
|
+
| [flows.md](flows.md) | Clicks, submits, page loads, every few seconds, server actions, modals, state |
|
|
25
|
+
| [realtime.md](realtime.md) | Pages that see each other: channels, presence, cursors, a shared board, who may hear a topic |
|
|
26
|
+
| [plugins.md](plugins.md) | A component of your own: props, binding them, writing state, channels, registering, behaving in the builder |
|
|
27
|
+
| [drawing.md](drawing.md) | A plugin that draws or animates: a canvas sized to the device, WebGL, a loop that stops when unseen |
|
|
28
|
+
| [structure.md](structure.md) | A space bigger than one screen: files, helpers, naming, keeping it short |
|
|
29
|
+
| [testing.md](testing.md) | Any test: `inspectPage`, handles, fixtures, catching a flash from the first frame, shortcuts, counting renders |
|
|
30
|
+
| [performance.md](performance.md) | A page with many elements, a busy flow, something that feels slow: what renders, what it costs, how to measure it |
|
|
31
|
+
| [snippets-and-export.md](snippets-and-export.md) | Publishing a snippet; turning an exported JSON — or a server action — into code |
|
|
32
|
+
| [accessibility.md](accessibility.md) | Icon buttons, fields, images, clickable cards, toggles, headings, landmarks, a canvas — anything a screen reader or a browser agent has to use |
|
|
33
|
+
| [review-checklist.md](review-checklist.md) | Before you say it is done |
|
|
@@ -9,7 +9,7 @@ custom({ id: 'arcade', renderType: 'nebulaRun', shipColor: 'amber', bind: { best
|
|
|
9
9
|
|
|
10
10
|
## Creating one
|
|
11
11
|
|
|
12
|
-
The CLI writes it, in the shape Plitzi's own elements are written in: `npx @plitzi/cli add
|
|
12
|
+
The CLI writes it, in the shape Plitzi's own elements are written in: `npx @plitzi/cli plugin add seat-picker` (a
|
|
13
13
|
package of its own and packing it: the CLI skill).
|
|
14
14
|
|
|
15
15
|
One folder, four files: `SeatPicker.tsx` (the component), `declaration.ts` (its `type`, the `triggers` it fires, the
|
|
@@ -88,7 +88,7 @@ the widths the space's styles use, instead of a width of the plugin's own.
|
|
|
88
88
|
|
|
89
89
|
## Its own server code
|
|
90
90
|
|
|
91
|
-
`plitzi add
|
|
91
|
+
`plitzi plugin add board --server` writes `functions/index.ts` in its folder (the CLI skill). Its routes answer
|
|
92
92
|
under `/fn/plugins/board/`; the component names them, with nothing for the space to wire:
|
|
93
93
|
|
|
94
94
|
```tsx
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Recipes
|
|
2
|
+
|
|
3
|
+
Each is a whole file that authors with no warning — CI holds it to that. Copy the one that fits, then change the names.
|
|
4
|
+
|
|
5
|
+
| To… | Open |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Show data with no backend: a JSON file, a card per row, a count, an empty state, a computed value | [recipes/show-data.ts](../recipes/show-data.ts) |
|
|
8
|
+
| Data typed by a sample of it: completed and checked paths, typed rows | [recipes/typed-data.ts](../recipes/typed-data.ts) |
|
|
9
|
+
| Data read by the page server (`--mode server`) | [recipes/server-data.ts](../recipes/server-data.ts) |
|
|
10
|
+
| An accordion, one answer open at a time | [recipes/accordion.ts](../recipes/accordion.ts) |
|
|
11
|
+
| Filter and sort a list from a select | [recipes/filter-a-list.ts](../recipes/filter-a-list.ts) |
|
|
12
|
+
| A page per record (`/products/:slug`), and "not found" | [recipes/detail-page.ts](../recipes/detail-page.ts) |
|
|
13
|
+
| Link to a section of a page (`/#plans`) | [recipes/link-to-a-section.ts](../recipes/link-to-a-section.ts) |
|
|
14
|
+
| A carousel: slides with arrows, dots and autoplay; a marquee; a row that swipes | [recipes/carousel.ts](../recipes/carousel.ts) |
|
|
15
|
+
| Do something every few seconds — a ticker, a poll, a slideshow by hand | [recipes/every-few-seconds.ts](../recipes/every-few-seconds.ts) |
|
|
16
|
+
| A marquee that scrolls for ever and stops under the pointer | [recipes/marquee.ts](../recipes/marquee.ts) |
|
|
17
|
+
| A row that scrolls sideways, with arrows that hide at its ends | [recipes/scroll-a-row.ts](../recipes/scroll-a-row.ts) |
|
|
18
|
+
| A link built from a row: WhatsApp with the product's name, `mailto:` | [recipes/link-with-data.ts](../recipes/link-with-data.ts) |
|
|
19
|
+
| React to a click or a submit; a modal; a switch | [recipes/forms-and-modals.ts](../recipes/forms-and-modals.ts) |
|
|
20
|
+
| A feature behind a flag | [recipes/feature-flag.ts](../recipes/feature-flag.ts) |
|
|
21
|
+
| An element of your own (a plugin), checked | [recipes/plugin.ts](../recipes/plugin.ts) |
|
|
22
|
+
| Controls a screen reader and a browser agent can use | [recipes/usable-without-sight.ts](../recipes/usable-without-sight.ts) |
|
|
23
|
+
| Tokens, a shared class, "the class plus one thing", tablet and phone at once | [recipes/style-a-page.ts](../recipes/style-a-page.ts) |
|
|
24
|
+
| A design in Tailwind classes, kept as classes the builder edits | [recipes/from-tailwind.ts](../recipes/from-tailwind.ts) |
|
|
25
|
+
| A page from elsewhere in a frame (`embed`: a map, a player) and a drawing of your own (`svg`) | [recipes/embed-and-svg.ts](../recipes/embed-and-svg.ts) |
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# The rules of writing a space
|
|
2
|
+
|
|
3
|
+
Every rule, with its why. The skill's core keeps the five that go wrong most in a line each; the rest authoring
|
|
4
|
+
refuses or warns of, with the fix.
|
|
5
|
+
|
|
6
|
+
1. **Name what is referred to.** Give an `id` to every element a binding, a flow or a test addresses. Ids are ONE
|
|
7
|
+
namespace for the whole space — layouts and every page share it — so a helper that runs more than once builds inside
|
|
8
|
+
`scope('promos', ref => …)`: every id in it is prefixed (`promos-panel`), and `ref('slides')` names one in full. An `id` is the element's name in the
|
|
9
|
+
space (`data-plitzi-el` in the page); the DOM id a URL's `#fragment` lands on is its `anchor`.
|
|
10
|
+
2. **Share with classes, layouts and components, never with copies.** A look used twice is a `styles()` class.
|
|
11
|
+
Chrome shown on several pages — a header, a sidebar, a footer — is a **layout** the pages name, written once. A block
|
|
12
|
+
placed many times with different content — a product card, a testimonial — is a **component** placed with
|
|
13
|
+
`component(id, { props })`. A menu, a card grid, a list of steps is DATA, never a block pasted per item: a short
|
|
14
|
+
menu mapped in code is fine; rows — cards, dishes, tiles — are one `list` (`items: [ … ]`) whose row is written
|
|
15
|
+
once. A link or a button says its words as
|
|
16
|
+
its own `content`, not through a `text` inside it. Pages of one shape are one `pageFamily` over data. See
|
|
17
|
+
[layouts](layouts.md), [components](components.md), [structure](structure.md) and
|
|
18
|
+
[efficiency](efficiency.md).
|
|
19
|
+
3. **One element, one base selector.** An element takes a shared `class` OR its own `css`, never both — authoring
|
|
20
|
+
refuses the pair. "This class plus one thing" is the class with rules on top, last in the list:
|
|
21
|
+
`class: [cover, { opacity: '0.25' }]` (needs the element's `id`; it becomes the class `<id>--own`). A look that
|
|
22
|
+
never changes is its own class.
|
|
23
|
+
4. **Visible by default; decide which way the logic flips.** An element is on screen unless something hides it. When
|
|
24
|
+
the logic REVEALS it (hidden → shown: an empty state, a "get started" card, an admin-only panel), it starts hidden —
|
|
25
|
+
`visible: 'source'`, or `visible: false` plus a binding for a computed condition — so it never flashes while data
|
|
26
|
+
loads (`condition-starts-visible` warns). When a flag HIDES it (shown → hidden: a sidebar label until the sidebar
|
|
27
|
+
is folded), it keeps the default and an absent flag must leave it shown. See
|
|
28
|
+
[data and visibility](data-and-visibility.md). What a PERSON switches on — a feature in beta, a rollout —
|
|
29
|
+
is a feature flag, not a visibility: gated off, it is not rendered at all. See [feature flags](feature-flags.md).
|
|
30
|
+
5. **Know where a template is evaluated.** A binding's template (`bindTemplate`) and a step's params evaluate full
|
|
31
|
+
Twig. An ATTRIBUTE resolves a name with filters (`{{ list_games.item.slug }}`, `{{ post.slug|url_encode }}`) against
|
|
32
|
+
the sources around the element; a condition there is used as written. Inside any template a source is spelled in
|
|
33
|
+
full — `apiContainer_stats`, `list_rows` — and a query param is `navigation.queryParams.x`. A template that feeds a
|
|
34
|
+
list its `items` hands over its value (`returns: 'value'`). `authorSpace` refuses a template it cannot read and a
|
|
35
|
+
name nothing answers to. See [templates](templates.md).
|
|
36
|
+
6. **Tokens, not colours.** Every colour is a variable of the space with a `light` and a `dark` value; both themes are
|
|
37
|
+
checked from the first commit.
|
|
38
|
+
7. **Times in UTC, and say so.** Format with an explicit zone (`|date('j M · H:i', 'UTC')`) and print "UTC" beside it:
|
|
39
|
+
a page rendered on a server and hydrated in a browser in another zone must agree on the hour.
|
|
40
|
+
8. **Breakpoints are ranges.** `tablet` (48–64rem) and `mobile` (up to 48rem) each inherit only from `desktop`; a rule
|
|
41
|
+
meant for both is written under `compact`.
|
|
42
|
+
9. **Never invent data.** Numbers, names and states on screen come from a source. A panel with nothing true to say is
|
|
43
|
+
an empty state, not a placeholder figure. With no backend, the data is JSON read by an `apiContainer`: a server
|
|
44
|
+
project's `src/data/*.json`, read on the server and never served; with no server, `public/data/*.json`, which is
|
|
45
|
+
public — see [data and visibility](data-and-visibility.md).
|
|
46
|
+
10. **Never hand-write** `flat`, derived ids, `styleSelectors`, `beforeNode`/`afterNode`/`flowId`, or a
|
|
47
|
+
`styleVariant` binding's key — use the factories, `variantFrom` and `activeOn`.
|
|
48
|
+
11. **A switch is named for how it leaves its default.** `toggleState` turns a key nobody has set yet ON, so a key
|
|
49
|
+
named for the default (`showPlates`, on by default) takes a first click to do nothing: name it `platesOff`, and read
|
|
50
|
+
every switch through `computed` (`plates: '{{ state.platesOff ? false : true }}'`) so its default shows before
|
|
51
|
+
anybody touches it.
|
|
52
|
+
12. **Hand plugins over as their declarations**: `authorSpace(space, { plugins: [declaration] })` holds a plugin —
|
|
53
|
+
its own type, or a `custom({ renderType })` host — to the events, actions and attributes it declares. See
|
|
54
|
+
[plugins](plugins.md).
|
|
55
|
+
13. **Usable without sight.** Screen readers and browser agents (Claude in Chrome) find a page's controls in its
|
|
56
|
+
accessibility tree: every button and link has words (an icon-only button a `title`), every field a `label`
|
|
57
|
+
(`hideLabel: true` hides it and keeps the name), every image an `alt` or `decorative: true`. Clicks go on a
|
|
58
|
+
`button` — it holds children, so a whole card can be one — or a `link`, never on a container. See
|
|
59
|
+
[accessibility](accessibility.md).
|
|
60
|
+
14. **Motion that stays smooth.** Animate `opacity` and `transform` only; no animated blur, shadow or size. See
|
|
61
|
+
[motion](colours-and-motion.md).
|
|
@@ -31,8 +31,8 @@ Never rewrite an exported space by hand. `specFromSpace({ schema, style })` read
|
|
|
31
31
|
A server action reads back the same way: `actionSpecFromEntry({ id, document })` gives the `defineAction` spec that writes
|
|
32
32
|
it — only when that spec writes the very same document, otherwise `reason` says why and it stays JSON — and
|
|
33
33
|
`actionToSource(spec)` writes the module. For the whole space as a project — pages, actions, functions, the source of
|
|
34
|
-
its plugins and runtime, its files — use `plitzi create --from <space>` (then `plitzi pull` to bring the space's changes in, and
|
|
35
|
-
`plitzi push` to put the project's back) rather than exporting the parts by hand.
|
|
34
|
+
its plugins and runtime, its files — use `plitzi create --from <space>` (then `plitzi space pull` to bring the space's changes in, and
|
|
35
|
+
`plitzi space push` to put the project's back) rather than exporting the parts by hand.
|
|
36
36
|
|
|
37
37
|
For a document you did not author — an export, a JSON edited by hand — run the gate before serving it:
|
|
38
38
|
`validateSpace({ schema, style })`.
|
|
@@ -117,7 +117,7 @@ the rest.
|
|
|
117
117
|
|
|
118
118
|
## Checked for you
|
|
119
119
|
|
|
120
|
-
`npm run lint:space` (`plitzi lint`) reads the source for what this page asks — a file past 400 lines, pages in one
|
|
120
|
+
`npm run lint:space` (`plitzi space lint`) reads the source for what this page asks — a file past 400 lines, pages in one
|
|
121
121
|
file, rows of data inline, a colour that is not a token, CSS copied three times, a row singled out by its id inside a
|
|
122
122
|
`map`, a minted id, a file nothing imports — at file:line, beside authoring's suggestions. Leave it clean; a departure
|
|
123
123
|
on purpose says why where it is: `// plitzi-lint-disable-next-line <code> -- why` for lint's own codes. A suggestion of
|
|
@@ -71,7 +71,7 @@ the space with it (the e2e harness: `renderSpace(page, space, { debugMode: true
|
|
|
71
71
|
|
|
72
72
|
## Asking the page what it holds
|
|
73
73
|
|
|
74
|
-
`plitzi check` lists every flow that failed while the page loaded; `--state` adds the state and every source by its
|
|
74
|
+
`plitzi page check` lists every flow that failed while the page loaded; `--state` adds the state and every source by its
|
|
75
75
|
full name, `--element <id>` one element — what it reads, its own state, whether it is on screen. They come from the
|
|
76
76
|
page's dev tools (debug mode, any development server). In a test, `readDevTools(page, { state: true })` from
|
|
77
77
|
`@plitzi/sdk-authoring` answers the same.
|
|
@@ -46,7 +46,7 @@ bar.
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
2. **Fix, re-run, until it prints zero warnings.** What has one fix (`author` says how many) is written for you: `npx
|
|
49
|
-
plitzi fix` shows it as a diff of your source, `--write` writes it and keeps it only if the space authors with each
|
|
49
|
+
plitzi space fix` shows it as a diff of your source, `--write` writes it and keeps it only if the space authors with each
|
|
50
50
|
fix gone and nothing new — a fix it cannot place, or that would add a problem, is said with the reason. A
|
|
51
51
|
suggestion with one reading is written the same way (`content-attribute`: words and an icon held as children become
|
|
52
52
|
the button's or link's own `content` and `icon`). Then typecheck the project.
|