@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.
Files changed (39) hide show
  1. package/README.md +99 -85
  2. package/THIRD-PARTY-NOTICES.md +2 -4
  3. package/dist/clay.standalone.js +22030 -0
  4. package/{all.js → entries/all.js} +7 -1
  5. package/entries/clay-data.js +16 -0
  6. package/{clay-dom.js → entries/clay-dom.js} +7 -1
  7. package/{clay-events.js → entries/clay-events.js} +7 -1
  8. package/{clay-internals.js → entries/clay-internals.js} +7 -1
  9. package/{clay-options.js → entries/clay-options.js} +7 -1
  10. package/{clay-ui.js → entries/clay-ui.js} +7 -1
  11. package/{clay-utils.js → entries/clay-utils.js} +7 -1
  12. package/{clay.js → entries/clay.js} +15 -1
  13. package/package.json +46 -15
  14. package/src/core/autosave.js +3 -2
  15. package/src/core/etag.js +114 -0
  16. package/src/core/host-attrs.js +7 -2
  17. package/src/core/host-meta.js +20 -3
  18. package/src/core/persist.js +3 -2
  19. package/src/core/save-conflict-notice.js +196 -0
  20. package/src/core/save-core.js +60 -3
  21. package/src/core/save.js +80 -1
  22. package/src/core/snapshot.js +34 -17
  23. package/src/lib/attr-aliases.js +13 -0
  24. package/src/lib/dirty-gate.js +2 -1
  25. package/src/lib/root-attrs.js +23 -7
  26. package/src/loader-logic.js +39 -0
  27. package/src/loader.js +11 -5
  28. package/src/plugins/demo.js +2 -2
  29. package/src/plugins/indicator.js +13 -2
  30. package/src/plugins/sortable.js +5 -5
  31. package/src/standalone.js +98 -0
  32. package/src/sync/live-sync.js +42 -0
  33. package/src/ui/index.js +7 -0
  34. package/src/vendor/hypercms.vendor.js +13 -13
  35. package/src/vendor/richclay.vendor.js +15 -15
  36. package/_headers +0 -14
  37. package/clay-data.js +0 -16
  38. package/src/lib/load-vendor-script.js +0 -57
  39. /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 below).
58
+ - `?editmode=false` — force view mode (URL param wins over everything else).
27
59
 
28
- ## Host attributes
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
- A host can put these on `<html>` in the response it serves. They are ephemeral:
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
- - `savetoken="…"` — the per-document save credential. clayjs posts to
35
- `/_/save/{token}` and sends no cookies, because the token is the credential.
36
- A token also implies edit mode, which is the only such signal a sandboxed
37
- document can see. `htmlclaytoken` is the older spelling of the same thing.
38
- Edit mode is decided in this order: the `?editmode` param, then
39
- `window.clayEditMode`, then a save token, then the platform's owner cookie.
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.3.0**: no name below changes without a major version.
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, as above.
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
- below. `clay-events`, `clay-dom`, `clay-options` and `all.js` add HTML attributes and DOM helpers rather
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
- **`clay.internals`** — the pieces the library builds itself out of, for code that needs to sit inside the
95
- save lifecycle rather than call it. Lower level than `clay.*` deliberately: it assumes you know the
96
- lifecycle.
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
- ## Deploy
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).
@@ -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 and website/vendor/richclay.min.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 and website/vendor/richclay.min.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