From d5c11d12a180f9e976e9bfbebcbda2b77352dbfe Mon Sep 17 00:00:00 2001 From: Aleksandr Shevchuk Date: Fri, 13 Nov 2026 13:05:02 +0200 Subject: [PATCH] The build page says a damaged link is damaged, and how to bring its source A chat writes the link out in its reply, and a compressed source survives no wrong character: a link ChatGPT gave held 43 characters of its own in place of 37. The page then said the link had carried the source, over an empty box, with the reason under Build, out of sight. For a link whose source cannot be read, the hero now says the link arrived damaged, and the ways to bring the source are above its box: ask the chat for the link again, open the source file it gave, or paste the source. They go once a source is there. The reason goes to the console. Closes #38. --- src/assets/site.css | 3 +++ src/h5p/build/build-page.ts | 36 ++++++++++++++++++++++++++++++++++-- src/h5p/build/index.njk | 29 +++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/assets/site.css b/src/assets/site.css index efc9084..9e695fb 100644 --- a/src/assets/site.css +++ b/src/assets/site.css @@ -647,6 +647,9 @@ tbody th a:hover code { text-decoration: underline; } at once for one a chat sent with the source in the link. What is said to one alone is not shown to the other; the class is on before the page paints (index.njk). */ .linked .when-unlinked, :root:not(.linked) .when-linked { display: none; } +/* A link whose source could not be read: what is said of the source it carried gives way to what + is said of the link, from when build-page.ts has tried it. */ +.unread .when-read, :root:not(.unread) .when-unread { display: none; } /* A bar at the top, then bands the width of the window, each holding a column of the page's width. */ :root { diff --git a/src/h5p/build/build-page.ts b/src/h5p/build/build-page.ts index 385f939..70dff9f 100644 --- a/src/h5p/build/build-page.ts +++ b/src/h5p/build/build-page.ts @@ -26,7 +26,8 @@ import { formatSource, jsonProblem, place, type Target } from './json-scan' * a sample that plays (`SAMPLE`), and finds the sample's source in the box, which the first paste * replaces whole (`prefillSample`). The markup says which words are for whom, and the class * `linked` on `` shows them, set before the page paints by the script in `index.njk` and - * kept in step with the address here. + * kept in step with the address here. A link whose source cannot be read is said to be damaged, + * with the ways to bring the source (`linkUnread`). */ type Opened = Awaited> @@ -41,6 +42,8 @@ const sourceField = $('source') const sourceLabel = $('source-label') const sourceState = $('source-state') const sourceFile = $('source-file') +const linkState = $('link-state') +const openSource = $('open-source') const filesStep = $('files-step') const fileList = $('file-list') const filesInput = $('files') @@ -117,6 +120,28 @@ function setStatus(text: string, state: 'idle' | 'building' | 'ready' | 'error') status.dataset.state = state } +/** + * A link whose source cannot be read. A chat writes the link out in its reply, and a compressed + * source survives no wrong character: a link ChatGPT gave held 43 characters of its own in place + * of 37, near its start, and was the command's output everywhere else. The page then said the link + * had carried the source, over an empty box, with the reason under Build, out of sight in a window + * 800px high. So it says the link is damaged where a person who opened it looks. The class `unread` + * on `` shows what the hero says of such a link in place of what it says of a source the link + * carried, for the rest of the visit, since the link carried none whatever comes after. The words + * above the box are the ways to bring the source, and stay until one is here (`checkSource`). + */ +function linkUnread(unread: boolean): void { + document.documentElement.classList.toggle('unread', unread) + showLinkState(unread) +} + +/** The words above the box, and with them the step's marker, which site.css draws from their state. */ +function showLinkState(show: boolean): void { + linkState.hidden = !show + if (show) linkState.dataset.state = 'error' + else delete linkState.dataset.state +} + /** Where a problem is in the source, for the button that goes there: its place in the text, or none. */ function target({ path, at }: Problem): Target { return at === undefined ? { path } : { at } @@ -316,6 +341,8 @@ player.addEventListener('error', (event) => { */ function checkSource(): void { const text = sourceField.value + // A source came after a link that could not be read, from a file or a paste: the ways to bring one go. + if (!linkState.hidden && compactSource(text)) showLinkState(false) const problem = jsonProblem(text) sourceState.dataset.state = problem ? 'error' : text.trim() ? 'ok' : '' if (!problem) { @@ -406,6 +433,8 @@ sourceFile.addEventListener('change', async () => { if (!file) return setSource(await file.text()) }) +// The same file field, from the words above the box on a link that could not be read. +openSource.addEventListener('click', () => sourceFile.click()) // The label is for the textarea, which the editor hides: a click on it goes to the editor. sourceLabel.addEventListener('click', () => editor?.focus()) @@ -537,9 +566,12 @@ async function fromLink(): Promise { try { text = await sourceFromFragment(location.hash) } catch (error) { - setStatus(error instanceof Error ? error.message : String(error), 'error') + // Why it cannot be read is for whoever looks into it; the person is told what to do. + console.warn(error instanceof Error ? error.message : String(error)) + linkUnread(true) return } + linkUnread(false) if (text === null) return carried = compactSource(text) ?? '' setSource(text) diff --git a/src/h5p/build/index.njk b/src/h5p/build/index.njk index bbc01e0..3a55f75 100644 --- a/src/h5p/build/index.njk +++ b/src/h5p/build/index.njk @@ -30,7 +30,10 @@ csp: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' da (#source=), gets the build at once. One who came with none gets a landing page: what it makes, a sample that plays, how to make their own. What is said to one alone is marked when-linked or when-unlinked, and site.css shows it by the class this line puts on before the page - paints; build-page.ts keeps the class in step with the address from then on. -#} + paints; build-page.ts keeps the class in step with the address from then on. A link whose + source cannot be read is a third case, which build-page.ts finds once it has tried: it puts + `unread` on , and what is said of a source the link carried is marked when-read, what is + said in its place when-unread. -#} {% include "topbar.njk" %} @@ -51,11 +54,20 @@ csp: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' da Make your own Paste your chat's JSON -

The link you opened carries an activity an AI chat wrote.

-

+

The link you opened carries an activity an AI chat wrote.

+

This page builds it in your browser, where you play it and download a .h5p file that Moodle or any other H5P site takes. Nothing is uploaded.

+ {#- A link whose source cannot be read: a chat writes the link out itself, and one wrong + character leaves nothing of a compressed source. The page says so here, first, in place + of saying the link carried an activity, and what to do above the source's box. These + two stay for the visit, so they say what stays true once the source has come another way. -#} +

The link you opened arrived damaged, so the activity in it cannot be read.

+

+ This page builds an activity in your browser from its source, which + step 1 says how to bring here. Nothing is uploaded. +

{#- The sample is /h5p/build/sample.h5p, which Vite builds from sample.json beside this file. build-page.ts gives the player its address only for a visitor who came with no source. -#} @@ -157,7 +169,16 @@ csp: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' da

1 Source

The source is your activity as the chat wrote it. The sample's is here to start with: paste yours over it, or open it from a file.

-

The source is your activity as the chat wrote it. The link carried it here.

+

The source is your activity as the chat wrote it. The link carried it here.

+ {#- Shown by build-page.ts for a link whose source cannot be read, until a source is here: above + the box, where a person who opened the link looks, with each way to bring the source. -#} +