@panphora/clayjs 0.7.4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -85
- package/THIRD-PARTY-NOTICES.md +2 -4
- package/dist/clay.standalone.js +22030 -0
- package/{all.js → entries/all.js} +7 -1
- package/entries/clay-data.js +16 -0
- package/{clay-dom.js → entries/clay-dom.js} +7 -1
- package/{clay-events.js → entries/clay-events.js} +7 -1
- package/{clay-internals.js → entries/clay-internals.js} +7 -1
- package/{clay-options.js → entries/clay-options.js} +7 -1
- package/{clay-ui.js → entries/clay-ui.js} +7 -1
- package/{clay-utils.js → entries/clay-utils.js} +7 -1
- package/{clay.js → entries/clay.js} +15 -1
- package/package.json +46 -15
- package/src/core/autosave.js +3 -2
- package/src/core/etag.js +114 -0
- package/src/core/host-attrs.js +7 -2
- package/src/core/host-meta.js +20 -3
- package/src/core/persist.js +3 -2
- package/src/core/save-conflict-notice.js +196 -0
- package/src/core/save-core.js +60 -3
- package/src/core/save.js +80 -1
- package/src/core/snapshot.js +34 -17
- package/src/lib/attr-aliases.js +13 -0
- package/src/lib/dirty-gate.js +2 -1
- package/src/lib/root-attrs.js +23 -7
- package/src/loader-logic.js +39 -0
- package/src/loader.js +11 -5
- package/src/plugins/demo.js +2 -2
- package/src/plugins/indicator.js +13 -2
- package/src/plugins/sortable.js +5 -5
- package/src/standalone.js +98 -0
- package/src/sync/live-sync.js +42 -0
- package/src/ui/index.js +7 -0
- package/src/vendor/hypercms.vendor.js +13 -13
- package/src/vendor/richclay.vendor.js +15 -15
- package/_headers +0 -14
- package/clay-data.js +0 -16
- package/src/lib/load-vendor-script.js +0 -57
- /package/{sap.js → entries/sap.js} +0 -0
package/README.md
CHANGED
|
@@ -4,39 +4,86 @@
|
|
|
4
4
|
|
|
5
5
|
Self-saving, malleable HTML in one classic `<script>`. Load `clay.js` on a page and it
|
|
6
6
|
becomes editable in place, snapshots its own DOM, and saves that DOM back to the file it
|
|
7
|
-
came from. No build step, no framework.
|
|
7
|
+
came from. No build step, no framework, no account.
|
|
8
|
+
|
|
9
|
+
The document is the app *and* the database. State lives in the DOM, and a save is one POST
|
|
10
|
+
of the entire serialized document. There is no data layer, no JSON on a server, no schema.
|
|
11
|
+
Open the saved file in a text editor and your edit is right there in the HTML.
|
|
12
|
+
|
|
13
|
+
## See it save itself
|
|
14
|
+
|
|
15
|
+
[`examples/notes.html`](https://github.com/panphora/clayjs/blob/main/examples/notes.html) is a complete self-saving document. Two
|
|
16
|
+
attributes do the work: `autosave` on `<html>`, and `editable` on the parts you type into.
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<!DOCTYPE html>
|
|
20
|
+
<html lang="en" autosave>
|
|
21
|
+
<head><meta charset="utf-8"><title>Notes</title></head>
|
|
22
|
+
<body>
|
|
23
|
+
<h1 editable>My notes</h1>
|
|
24
|
+
<div editable><p>Type anything here.</p></div>
|
|
25
|
+
<script src="https://clayjs.com/v1/clay.js"></script>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Type into it, then open the file in a text editor: your words are in the HTML.
|
|
31
|
+
|
|
32
|
+
**Something has to write the bytes.** clayjs runs in the browser; it makes the document
|
|
33
|
+
and posts it, and a host puts it on disk. The shortest path is
|
|
34
|
+
[HTML Clay](https://htmlclay.com), a desktop app: rename the file to `.htmlclay` and
|
|
35
|
+
double-click it, and it serves the file at `http://127.0.0.1` and writes every save back.
|
|
36
|
+
[hyperclay.com](https://hyperclay.com) hosts the same file online. Your own server needs
|
|
37
|
+
one route, about twenty lines: see [the endpoint spec](https://clayjs.com/docs#endpoint).
|
|
38
|
+
Full walkthrough: [the tutorial](https://clayjs.com/get-started) and
|
|
39
|
+
[`examples/`](https://github.com/panphora/clayjs/tree/main/examples).
|
|
8
40
|
|
|
9
41
|
## Use
|
|
10
42
|
|
|
11
43
|
```html
|
|
12
|
-
<script src="https://clayjs.com/clay.js"></script>
|
|
44
|
+
<script src="https://clayjs.com/v1/clay.js"></script>
|
|
13
45
|
```
|
|
14
46
|
|
|
15
47
|
The loader detects edit vs view mode and pulls only the modules it needs. Tune it with
|
|
16
48
|
query params on the script URL:
|
|
17
49
|
|
|
18
50
|
```html
|
|
19
|
-
<script src="/clay.js?plugins=sync,cms&exclude=indicator"></script>
|
|
51
|
+
<script src="https://clayjs.com/v1/clay.js?plugins=sync,cms&exclude=indicator"></script>
|
|
20
52
|
```
|
|
21
53
|
|
|
22
54
|
- `?plugins=` — add optional plugins: `sync`, `cms`, `undo`, `sortable`, `indicator`, `quickcrop`, `upload`, `wire`, `demo`.
|
|
23
55
|
Only `richclay` loads by default, and only in edit mode. `cms` brings `quickcrop` with it, because
|
|
24
56
|
the CMS uses it for `data-hcms-crop` image fields.
|
|
25
57
|
- `?exclude=` — drop a plugin that would otherwise load (a default, or one another plugin pulled in).
|
|
26
|
-
- `?editmode=false` — force view mode (URL param wins over everything
|
|
58
|
+
- `?editmode=false` — force view mode (URL param wins over everything else).
|
|
27
59
|
|
|
28
|
-
|
|
60
|
+
Edit mode is decided in this order: the `?editmode` param, then `window.clayEditMode`,
|
|
61
|
+
then a save token stamped on `<html>` by the host, then the platform's owner cookie. Hosts
|
|
62
|
+
and save tokens are covered in [the reference](https://github.com/panphora/clayjs/blob/main/docs/reference.md) and on
|
|
63
|
+
[clayjs.com/docs](https://clayjs.com/docs#editmode).
|
|
29
64
|
|
|
30
|
-
|
|
31
|
-
the host injects them and strips them back out of whatever gets saved, so they
|
|
32
|
-
never reach the file on disk.
|
|
65
|
+
### One file, no network
|
|
33
66
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
`clay.js` is a small bootstrap that fetches the modules a page asked for. For a page
|
|
68
|
+
that has to load with no connection, download the standalone build instead: every
|
|
69
|
+
module, plugin and satellite in one readable file.
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<script src="clay.standalone.js?plugins=sync"></script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Get it from [clayjs.com/v1/clay.standalone.js](https://clayjs.com/v1/clay.standalone.js)
|
|
76
|
+
(about 1 MB; `/1.1.0/clay.standalone.js` is that exact release, and the file's first
|
|
77
|
+
line says which version it is) or from the npm tarball at `dist/clay.standalone.js`.
|
|
78
|
+
Keep it beside the HTML file and use the same query params: `plugins=` decides what
|
|
79
|
+
runs, not what downloads, and `await clay.loaded.ui` and the other satellites work
|
|
80
|
+
without tags of their own (they are always on, so clay-ui's automatic save toasts run
|
|
81
|
+
on your page unless you handle the `clay:save-*` events yourself). If the page already
|
|
82
|
+
had satellite tags, delete them: a second `sap.js` mounts a second runtime. Saving
|
|
83
|
+
still needs a host that writes the file; [HTML Clay](https://htmlclay.com) does that
|
|
84
|
+
on the machine itself, with no network.
|
|
85
|
+
The two things that still reach out, and why they fail soft, are on
|
|
86
|
+
[clayjs.com/offline](https://clayjs.com/offline).
|
|
40
87
|
|
|
41
88
|
## Readiness
|
|
42
89
|
|
|
@@ -48,12 +95,6 @@ await clay.ready; // or: document.addEventListener("clay:ready", ...)
|
|
|
48
95
|
clay.save();
|
|
49
96
|
```
|
|
50
97
|
|
|
51
|
-
Edit mode exposes `clay.save()` (+ `clay.save.force()`), `clay.getHTML()`, `clay.addDocumentTransform(fn)`,
|
|
52
|
-
`clay.onSnapshot(fn)`, `clay.toggleEditMode()`, `clay.isEditMode`, `clay.isOwner`, `clay.Mutation`,
|
|
53
|
-
`clay.region`, `clay.cacheBust(el)`, plus `clay.undo` / `clay.cms` / `clay.morph` / `clay.RichClay` /
|
|
54
|
-
`clay.quickcrop` when those plugins load. View mode keeps only the always-available members (`toggleEditMode`, `isEditMode`, `isOwner`,
|
|
55
|
-
`Mutation`, `region`, `ready`); edit-only members are simply absent.
|
|
56
|
-
|
|
57
98
|
## API
|
|
58
99
|
|
|
59
100
|
Three tiers. The first two are a promise; the third is not.
|
|
@@ -64,55 +105,23 @@ Three tiers. The first two are a promise; the third is not.
|
|
|
64
105
|
| `clay.*` from a satellite (`clay-ui`, `clay-utils`, `clay-dom`, `clay-events`, `clay-options`, `clay-internals`) | opt-in, one script tag each | stable |
|
|
65
106
|
| anything else under `src/` | reachable by direct import, because `src/` ships | **may change in any release** |
|
|
66
107
|
|
|
67
|
-
The contract starts at **0.
|
|
108
|
+
The contract starts at **1.0.0**: no name below changes without a major version.
|
|
68
109
|
|
|
69
110
|
**`clay.js`** — `ready`, `save()`, `save.force()`, `getHTML()`, `addDocumentTransform(fn)`, `onSnapshot(fn)`,
|
|
70
111
|
`toggleEditMode()`, `isEditMode`, `isOwner`, `Mutation`, `region`, `cacheBust(el)`, plus `undo` / `cms` / `morph` /
|
|
71
|
-
`RichClay` / `quickcrop` when those plugins load. View mode keeps only the always-available members
|
|
72
|
-
|
|
73
|
-
`addDocumentTransform(fn)` runs your callback over a detached clone whenever the page
|
|
74
|
-
prepares to save AND whenever it checks whether anything changed. Keep it pure and
|
|
75
|
-
repeatable: it is a transform, not a "a save is happening" event.
|
|
76
|
-
|
|
77
|
-
`quickcrop(file, options)` opens a crop modal over a File or Blob and resolves
|
|
78
|
-
`{ blob, dataURL, width, height }`, or `null` if the person cancels. `aspect` (a number,
|
|
79
|
-
or null for freeform), `type`, `quality`, `maxWidth`/`maxHeight` and `labels.confirm` are
|
|
80
|
-
the options worth knowing. Its modal and injected stylesheet carry `save-remove`, so they
|
|
81
|
-
never reach the saved file.
|
|
82
|
-
|
|
83
|
-
`region` is the region-policy model: `resolveRegionPolicy`, `isInert`, `skipForPolicy`,
|
|
84
|
-
`strictestPolicy`, the `PERSIST` and `REGION_ATTRS` token constants, and the
|
|
85
|
-
`STRIP_FROM_SAVE`, `FREEZE_SELECTOR` and `STRIP_FROM_COMPARISON` selectors.
|
|
86
|
-
`clay.internals.region` is a different shape, narrower in aim, built for snapshot work.
|
|
112
|
+
`RichClay` / `quickcrop` when those plugins load. View mode keeps only the always-available members
|
|
113
|
+
(`toggleEditMode`, `isEditMode`, `isOwner`, `Mutation`, `region`, `ready`); edit-only members are simply absent.
|
|
87
114
|
|
|
88
115
|
**Satellites** — one script tag each, and each resolves its own `clay.loaded.*` promise. `clay-ui` adds
|
|
89
116
|
`toast`, `toastPersistent`, `ask`, `confirm`, `tell`, `snippet`, `modal`; `clay-utils` adds `clay.utils`
|
|
90
117
|
(`throttle`, `debounce`, `cookie`, `slugify`, `copyToClipboard`); `clay-internals` adds `clay.internals`,
|
|
91
|
-
|
|
118
|
+
the low-level surface for code that needs to sit *inside* the save lifecycle rather than call it.
|
|
119
|
+
`clay-events`, `clay-dom`, `clay-options` and `all.js` add HTML attributes and DOM helpers rather
|
|
92
120
|
than members on `clay`.
|
|
93
121
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```html
|
|
99
|
-
<script src="https://clayjs.com/clay-internals.js"></script>
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
- `captureSnapshot()`, `captureForSave()` — the snapshot pipeline, read side.
|
|
103
|
-
`captureSnapshot` gives you the clone before any stripping; `captureForSave` gives you the
|
|
104
|
-
bytes a save would send.
|
|
105
|
-
- `addDocumentTransform(fn)` — the same registry as `clay.addDocumentTransform`. Core only
|
|
106
|
-
publishes that name in edit mode, so reach for this one from a view-mode page or from the
|
|
107
|
-
satellite with no core loaded.
|
|
108
|
-
- `region.addRegionToken(el, token)`, `region.resolveRegionPolicy(node)`, `region.isInert(node)`,
|
|
109
|
-
`region.isSnapshotRemoved(el)`, `region.PERSIST`, `region.REGION_ATTRS`, and
|
|
110
|
-
`region.selectors.stripFromSave` / `.stripFromComparison` / `.stripFromDirtyCheck` /
|
|
111
|
-
`.noTriggerAutosave` / `.noDirty` / `.snapshotRemove` / `.freeze` — write your own attribute
|
|
112
|
-
without hardcoding our selectors.
|
|
113
|
-
- `save.saveHtml(html, cb, opts)`, `save.replacePageWith(url, cb)`, `save.isSaveInProgress()` — the save
|
|
114
|
-
lane under `clay.save`. **`saveHtml` writes the bytes you hand it straight to the file**, bypassing the
|
|
115
|
-
snapshot pipeline entirely; check `isSaveInProgress()` first.
|
|
122
|
+
Every member, every attribute, and the endpoint spec: **[docs/reference.md](https://github.com/panphora/clayjs/blob/main/docs/reference.md)**,
|
|
123
|
+
also served at [clayjs.com/llms.txt](https://clayjs.com/llms.txt) and rendered as
|
|
124
|
+
[clayjs.com/docs](https://clayjs.com/docs).
|
|
116
125
|
|
|
117
126
|
## Regions
|
|
118
127
|
|
|
@@ -133,41 +142,46 @@ and live sync will not overwrite it, while a `no-dirty` region renders itself fr
|
|
|
133
142
|
so it never warns and an incoming sync frame may replace it. Use `no-dirty` for filter bars,
|
|
134
143
|
projections and drag previews; use `no-trigger-autosave` for a heavy editor you save by hand.
|
|
135
144
|
|
|
145
|
+
## Repo map
|
|
146
|
+
|
|
147
|
+
**Everything in `entries/` is served under a version prefix on clayjs.com.**
|
|
148
|
+
`entries/clay.js` is `https://clayjs.com/v1/clay.js`, which rolls forward within major
|
|
149
|
+
version 1, and `https://clayjs.com/1.0.0/clay.js`, which never changes. Same for each
|
|
150
|
+
satellite. A saved document hardcodes its script URL and has no update channel, which is
|
|
151
|
+
why the version is in the URL and why a served path, once published, is permanent. Repo
|
|
152
|
+
paths are not: `build.js` flattens `entries/` into each prefix on the way out. `clay.js`
|
|
153
|
+
derives its own base URL from its script tag and imports `<base>/src/loader.js`, so `src/`
|
|
154
|
+
is public surface too and ships inside every prefix as its sibling.
|
|
155
|
+
|
|
156
|
+
| | |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `entries/clay.js` | the bootstrap: everything starts here |
|
|
159
|
+
| `entries/clay-ui.js`, `-dom`, `-events`, `-options`, `-utils`, `-internals` | satellites, one script tag each |
|
|
160
|
+
| `entries/all.js` | `All(selector)`, a chainable `querySelectorAll` wrapper |
|
|
161
|
+
| `entries/sap.js` | reactive templating, **generated** from the `sapjs` repo |
|
|
162
|
+
| `entries/clay-data.js` | the HTML data API, **generated** from the `hyper-html-api` repo |
|
|
163
|
+
| `src/` | the implementation, and public surface by direct import |
|
|
164
|
+
| `conformance/` | the byte-exact gate: real browser, pinned Chromium, goldens from the spec |
|
|
165
|
+
| `tests/` | jest unit suite, fixtures, and a stub save server |
|
|
166
|
+
| `website/` | the source of clayjs.com |
|
|
167
|
+
| `docs/reference.md` | the full reference, served as `/llms.txt` |
|
|
168
|
+
| `build.js`, `wrangler.jsonc` | assemble and deploy `public/`, which is a gitignored build output |
|
|
169
|
+
|
|
136
170
|
## Develop
|
|
137
171
|
|
|
138
172
|
```bash
|
|
139
173
|
npm test # jest unit suite
|
|
140
174
|
npm run test:conformance # byte-for-byte fixture gate, in a real browser
|
|
141
175
|
npm run dev # stub save server on :4601 for the tests/fixtures pages
|
|
176
|
+
npm run build # rebuild public/, the deploy output
|
|
142
177
|
```
|
|
143
178
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
`public/` is what wrangler serves to clayjs.com. It is a build output: gitignored,
|
|
147
|
-
disposable, and rebuilt from scratch every time.
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
npm run build # rebuild public/ from source
|
|
151
|
-
npm run deploy # rebuild, then wrangler deploy
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Never edit `public/` by hand, and never copy a file into it. Its contents are
|
|
155
|
-
**derived**:
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
public/ = package.json "files" (minus THIRD-PARTY-NOTICES.md, which nothing requests)
|
|
159
|
-
+ website/* (flattened to the root, so /docs.html works)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
That derivation is the point. `files` is the list npm publishes, so it is already
|
|
163
|
-
the line you edit to ship a new satellite, and deriving from it means a file cannot
|
|
164
|
-
reach npm and miss the site. Before this script existed the mirror was hand-copied,
|
|
165
|
-
and it drifted exactly that way: `src/core/host-attrs.js` never made it across, and
|
|
166
|
-
since `loader.js` imports `is-edit-mode.js`, which imports it, the deployed
|
|
167
|
-
`clay.js` could not boot at all.
|
|
179
|
+
Setup, the pinned-browser requirement, and the things about this repo that will surprise
|
|
180
|
+
you are in [CONTRIBUTING.md](https://github.com/panphora/clayjs/blob/main/CONTRIBUTING.md).
|
|
168
181
|
|
|
169
182
|
## License
|
|
170
183
|
|
|
171
184
|
Our code is MIT-0 (MIT No Attribution): use it, remix it, ship it, no attribution
|
|
172
185
|
needed. Vendored third-party files keep their original permissive licenses; see
|
|
173
|
-
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md).
|
|
186
|
+
[THIRD-PARTY-NOTICES.md](THIRD-PARTY-NOTICES.md). Security reports go through
|
|
187
|
+
[the security policy](https://github.com/panphora/clayjs/blob/main/.github/SECURITY.md).
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -12,15 +12,13 @@ or carry third-party code, and keep their original licenses.
|
|
|
12
12
|
|
|
13
13
|
## Squire 2.4.8 (MIT)
|
|
14
14
|
|
|
15
|
-
- Carried inside src/vendor/richclay.vendor.js
|
|
16
|
-
(richclay bundles Squire)
|
|
15
|
+
- Carried inside src/vendor/richclay.vendor.js (richclay bundles Squire)
|
|
17
16
|
- Copyright (c) 2011-2023 by Neil Jenkins
|
|
18
17
|
- https://github.com/fastmail/Squire
|
|
19
18
|
|
|
20
19
|
## DOMPurify 3.4.11 (Apache-2.0 OR MPL-2.0)
|
|
21
20
|
|
|
22
|
-
- Carried inside src/vendor/richclay.vendor.js
|
|
23
|
-
(richclay bundles DOMPurify)
|
|
21
|
+
- Carried inside src/vendor/richclay.vendor.js (richclay bundles DOMPurify)
|
|
24
22
|
- (c) Cure53 and other contributors
|
|
25
23
|
- https://github.com/cure53/DOMPurify
|
|
26
24
|
|