Skip to content

Commit ebaca57

Browse files
committed
Document that a source arrives laid out and that a link follows it
The editor section loses the Format button and gains the two behaviours that replace it, the source laid out as it arrives and the address kept in step with it. editor.ts and json-scan.ts are linked now that both are on main.
1 parent a8711ee commit ebaca57

1 file changed

Lines changed: 8 additions & 7 deletions

File tree

‎docs/h5p-build-page.md‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@
22

33
`/h5p/build/` is for an AI chat that wrote a source with the [h5p-author skill](https://github.com/missing-elements/h5p-offline-player/tree/main/skills/h5p-author) but cannot build it. ChatGPT's sandbox reaches npm only through OpenAI's own mirror, which was reported refusing every install from July 2026, so `npx @missing-elements/h5p-author` does not run there, and the skill hands over the source and this page's address instead. The page builds with `@missing-elements/h5p-author/browser`, the same build as the command's, and lists every problem at once by its path, ready to copy back into the chat with [the type files](#the-type-files) that chat lacked. Its libraries are `@missing-elements/h5p-libraries/install`'s pack, one part per library, so a build fetches only the parts its package needs and carries the editor libraries an H5P site's upload check asks for. The package then plays in the player on the page, and downloads from it. Why it exists, and what was measured to build it this way: [design/author.md](https://github.com/missing-elements/h5p-offline-player/blob/main/design/author.md) of h5p-offline-player, under *Where `npx` cannot install, a page builds*.
44

5-
The source is not uploaded: the package is built in the browser. A link can carry the source in its fragment, which no server sees: `/h5p/build/#source=<base64url>`, the bytes being the source's JSON as UTF-8, or that JSON compressed with raw deflate (Node's `zlib.deflateRawSync`, Python's `zlib.compressobj(wbits=-15)`) to keep the link short. A linked source is built at once, unless it names files of its own, which only the person can choose. A source that names a picture by web address gets it only where that host allows CORS.
5+
The source is not uploaded: the package is built in the browser. A link can carry the source in its fragment, which no server sees: `/h5p/build/#source=<base64url>`, the bytes being the source's JSON as UTF-8, or that JSON compressed with raw deflate (Node's `zlib.deflateRawSync`, Python's `zlib.compressobj(wbits=-15)`) to keep the link short. A linked source is built at once, unless it names files of its own, which only the person can choose. A page opened by such a link keeps the fragment in step with the source as the person changes it, so the address can be copied to send the changed source on ([below](#the-source-editor)). A source that names a picture by web address gets it only where that host allows CORS.
66

77
| File | What it holds |
88
| --- | --- |
99
| [src/h5p/build/index.njk](../src/h5p/build/index.njk) | The page's markup, in the site's layout, and its Content-Security-Policy: GitHub Pages cannot send a Content-Security-Policy header for a site, so a `<meta>` tag is the only policy it gets |
10-
| [src/h5p/build/build-page.ts](../src/h5p/build/build-page.ts) | The page's wiring: the build, the player, the download, a link's source |
11-
| [src/h5p/build/build-source.ts](../src/h5p/build/build-source.ts) | What it does with a source with no DOM: the link's fragment, the files a source names, the content types it is written in, the text for the chat |
12-
| `src/h5p/build/editor.ts` | The source's editor, CodeMirror 6, mounted in a shadow root and loaded apart from the page: see [The source editor](#the-source-editor) |
13-
| `src/h5p/build/json-scan.ts` | What it reads of JSON text with no DOM: where a text stops being JSON and what is wrong there, and where a problem's path points |
10+
| [src/h5p/build/build-page.ts](../src/h5p/build/build-page.ts) | The page's wiring: the build, the player, the download, a link's source and the address that follows it |
11+
| [src/h5p/build/build-source.ts](../src/h5p/build/build-source.ts) | What it does with a source with no DOM: the link's fragment, read and written, the files a source names, the content types it is written in, the text for the chat |
12+
| [src/h5p/build/editor.ts](../src/h5p/build/editor.ts) | The source's editor, CodeMirror 6, mounted in a shadow root and loaded apart from the page: see [The source editor](#the-source-editor) |
13+
| [src/h5p/build/json-scan.ts](../src/h5p/build/json-scan.ts) | What it reads of JSON text with no DOM: where a text stops being JSON and what is wrong there, where a problem's path points, and the source laid out |
1414
| [src/assets/site.css](../src/assets/site.css) | Its styles, under *H5P build page* |
1515
| [vite.config.ts](../vite.config.ts) | The page's script, `/h5p/build/build-page.js`, with the player, its runtime, the pack and their licence files in `/h5p/assets/`, and the type files in `/h5p/build/types/` |
16-
| [e2e/](../e2e/), [playwright.config.ts](../playwright.config.ts) | The browser tests: a pasted source, one from a link and one with a file of its own build, play and download; the source's editor marks a mistake and the problems the build finds, and `json-scan.spec.ts` tests the reading of JSON with no browser; a source's problems are listed, and copied for the chat with the type files; `/h5p/bundles/` serves every bundle and part, and its tool and a bundle build a quiz in an empty folder; `/h5p/skill/` serves the skill's files as the package carries them, and writes out every address |
16+
| [e2e/](../e2e/), [playwright.config.ts](../playwright.config.ts) | The browser tests: a pasted source, one from a link and one with a file of its own build, play and download; the source's editor marks a mistake and the problems the build finds, lays a whole paste out, and a link follows the source as it changes; `json-scan.spec.ts` tests the reading of JSON with no browser; a source's problems are listed, and copied for the chat with the type files; `/h5p/bundles/` serves every bundle and part, and its tool and a bundle build a quiz in an empty folder; `/h5p/skill/` serves the skill's files as the package carries them, and writes out every address |
1717

1818
The player's Service Worker is in `/h5p/assets/` too, so its scope is `/h5p/assets/h5p/`: no other page of the site is under it. The address names who runs the page, as H5P Group's trademark policy asks of a name with "H5P" in it, and the page uses the word descriptively.
1919

@@ -26,7 +26,8 @@ A person who gets a source from a chat is a teacher, not a developer, and a miss
2626
- **JSON in colours**, with line numbers, matching brackets and undo. The colours are the site's token colours, custom properties that reach into the editor, so light and dark need no code.
2727
- **A mistake in the JSON is marked as it is typed**, and said under the source with its line and column, in words for someone who does not write JSON ("A comma is missing here"), with a button that goes there. `JSON.parse` still decides whether text is JSON. `json-scan.ts` says where, because `JSON.parse` says it differently in each browser and Safari does not say at all. `parseSource` uses it too, so the copy for the chat carries the line and the column.
2828
- **The problems the build finds are marked where they are**, and the name of a problem in the list is a button that goes there. A problem's path, `params.chapters[0].metadata.title`, is looked up in the text by `locate`: a name and its value, and for what the source is missing, the object it is missing from. The marks are of the text as it was built, so a change to the text clears them.
29-
- **Format** lays the source out, and takes it out of the code fence a chat may have put it in.
29+
- **A source arrives laid out**, so the page has no Format button. One opened from a link or a file, or pasted over all of the text or into an empty editor, is laid out by `formatSource`, two spaces and a field to a line, and taken out of the code fence a chat may have put it in. A paste into the text goes in as it is, and so does text that is not JSON: the line under the source says where it stops being JSON. Where the editor cannot load, the textarea lays a whole paste out too.
30+
- **A link that carried the source follows it.** A page opened with `#source=` replaces its address when the source changes to another source: `history.replaceState`, so the history gets no entry and no `hashchange` fires, and the page does not read the address back. The address carries `compactSource` of the text, the JSON on one line, compressed with raw deflate where there is a `CompressionStream` and that is shorter (`fragmentFromSource`), which is what `sourceFromFragment` reads. So a change of layout writes nothing, and neither does text that is not JSON or not an object: the address keeps the last source it carried, and a link never carries a broken one. An emptied editor leaves `#source=` with nothing after it, so the page keeps following, and a link that could not be read gives way to the first source written. A page opened with no source in its address writes none. The write waits for the typing to pause, since Safari throws on more than a hundred `replaceState` calls in thirty seconds, and one it refuses is made again at the next change.
3031

3132
The editor has to fit the page's policy, which has no `style-src 'unsafe-inline'`: an editor that adds a `<style>` to the page is refused. In a shadow root CodeMirror adopts its styles as a constructed stylesheet instead, which the policy does not govern. Chrome's own editing also gives text typed over a selection of coloured text a `style` attribute, which the policy refuses and reports; `contenteditable="plaintext-only"` does not stop it. So the editor takes that typing itself (`typeOverSelection`), through its input handlers, and a bracket or a quote typed over a selection still wraps it. Tab is not the editor's: it leaves it, so the keyboard is never trapped. The browser tests that watch the page fail on any policy report.
3233

0 commit comments

Comments
 (0)