@supersuit/artifacts 0.40.1 → 0.41.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/CHANGELOG.md CHANGED
@@ -3,6 +3,37 @@
3
3
  `@supersuit/artifacts`. One entry per version, newest first. Each entry says what changed, how a
4
4
  host can tell whether it is affected (DETECTOR), what a host does about it (REMEDY), and the tests.
5
5
 
6
+ ## 0.41.0
7
+
8
+ **A reader who may not see a page's history is no longer told its version number.** The line under
9
+ the summary read "Version N · Updated <minute>" to everyone. A page sent to someone reads as a
10
+ message, and "Version 9" on a message tells its reader about eight drafts they were never meant to
11
+ think about. The number now goes with the history: whoever `history` lets see past versions
12
+ (the owner and `admins` by default, every reader under `history: 'everyone'`) sees
13
+ "Version N · Updated <minute>", and everyone else sees "Updated <minute>". There is no new config
14
+ key; the existing `history` setting is the toggle, and its default is off for readers.
15
+
16
+ - `CoverProps` gains `showVersion: boolean`, true exactly when this reader may see the number. The
17
+ `meta` node a Cover places already follows it. `version` is still handed over unchanged, so a
18
+ Cover that draws the number itself keeps drawing it until it reads `showVersion`.
19
+ - A past version's page (`/<id>/v/<n>`) is unchanged: only a reader who may see the history reaches
20
+ it, so it still names its version.
21
+
22
+ DETECTOR: a host whose pack has a `Cover` that renders `version`: `rg -n "version" <your packs>`.
23
+ Such a Cover still shows the number to every reader after this release, because the shell cannot
24
+ take a prop away from a pack. A host that relied on every reader seeing the version line and does
25
+ not set `history: 'everyone'` will find the number gone for signed-out readers.
26
+
27
+ REMEDY: in a Cover that draws the number, draw it only when `showVersion` is true. To show every
28
+ reader the number (and the history with it), set `history: 'everyone'`.
29
+
30
+ Tests: `src/routes/page-menu.test.tsx` (a visitor, a reader and a stranger get "Updated" with no
31
+ version number; an admin and `history: 'everyone'` get "Version 2 · Updated"),
32
+ `src/routes/extension-points.test.tsx` (a Cover is handed `showVersion` false for a reader and true
33
+ under `history: 'everyone'`), `src/routes/versions.test.tsx` and `src/routes/broadcast.test.tsx`
34
+ (the two tests that pinned the old line now pin the new one). All three new tests failed against
35
+ 0.40.1.
36
+
6
37
  ## 0.40.1
7
38
 
8
39
  **0.40.0 was tagged and never published: this is the first release carrying it.** The publish
package/README.md CHANGED
@@ -194,7 +194,8 @@ before 0.31.0 has no `declared` and takes its file on its next publish, as every
194
194
 
195
195
  ## Version history
196
196
 
197
- Every page names its version under the summary, "Version 4 · Updated <minute>", and the page
197
+ Every page says when it was last updated under the summary, and to its admins that line also names
198
+ the version: "Version 4 · Updated <minute>". The page
198
199
  menu's **Version history** opens a panel listing every version newest first: its number, the minute it went live in
199
200
  the reader's own time zone, and its change note. The panel is drawn over the page (a sheet on a
200
201
  phone, a side panel on a desk), so opening it moves no text. Each past version is readable at
@@ -205,9 +206,15 @@ the same password or confidential door as the page itself. A version that does n
205
206
  **Past versions are for admins by default (0.21.0).** An earlier draft can hold what the current
206
207
  one was edited to remove, so only the page's admins see Version history in the page menu or open
207
208
  `/<id>/v/<n>`: the reader signed in as `ownerEmail` or any address in `admins` (case does not
208
- matter). Everyone else still sees "Version N · Updated <minute>", and a past version is a 404 to
209
- them. With neither configured, nobody does. `history: 'everyone'` in the config restores the old
210
- behaviour, where every reader who can open the page reads its history.
209
+ matter). A past version is a 404 to everyone else. With neither configured, nobody does.
210
+ `history: 'everyone'` in the config restores the old behaviour, where every reader who can open
211
+ the page reads its history.
212
+
213
+ **The version number goes with the history (0.41.0).** A page sent to someone reads as a message,
214
+ and "Version 9" on a message tells its reader about eight drafts. So whoever may not see past
215
+ versions sees only "Updated <minute>" under the summary; admins, and every reader under
216
+ `history: 'everyone'`, see "Version N · Updated <minute>". A pack's `Cover` is handed
217
+ `showVersion` alongside `version`, and draws the number itself only when it is true.
211
218
 
212
219
  ```ts
213
220
  createArtifactRoutes({ ..., ownerEmail: 'owner@example.com', admins: ['editor@example.com'] })
@@ -84,6 +84,10 @@ export type CoverProps = {
84
84
  /** The version line and, for a signed-in reader, who they are signed in as. Unspoken. */
85
85
  meta: ReactNode;
86
86
  version: number;
87
+ /** Whether this reader may be shown the version number: true for whoever the host's `history`
88
+ * lets see past versions (admins by default), false for everyone else. A Cover that draws
89
+ * `version` itself draws it only when this is true; `meta` already follows it. */
90
+ showVersion: boolean;
87
91
  updatedAt: string;
88
92
  createdAt: string;
89
93
  };
@@ -80,8 +80,9 @@ export type ArtifactRoutesConfig = {
80
80
  /** Sign-in emails, besides `ownerEmail`, of the people who administer this host's pages. They
81
81
  * see a page's version history; with `history: 'admins'` (the default) nobody else does. */
82
82
  admins?: readonly string[];
83
- /** Who sees a page's past versions: the History control under the summary and every
84
- * `/<id>/v/<n>`. `'admins'` (default): only `ownerEmail` and `admins`, signed in; to everyone
83
+ /** Who sees a page's past versions and its version number: Version history in the page menu,
84
+ * every `/<id>/v/<n>`, and "Version N" on the line under the summary (0.41.0; to everyone else
85
+ * that line is only "Updated <minute>"). `'admins'` (default): only `ownerEmail` and `admins`, signed in; to everyone
85
86
  * else the History control is absent and a past version is not found, because an earlier draft
86
87
  * can hold what the current one was edited to remove. `'everyone'`: every reader who can open
87
88
  * the page, which was the only behaviour before 0.21.0. */
@@ -408,9 +408,12 @@ export function createArtifactRoutes(config) {
408
408
  return store.history(id).catch(() => null);
409
409
  }
410
410
  /** "Version N · Updated <minute>": the line under the summary. The way into the history is the
411
- * page menu's Version history (0.30.0), for whoever `history` lets see it. */
412
- function VersionLine({ n, at }) {
413
- return (_jsxs("p", { "data-nospeak": true, className: "mt-4 text-xs opacity-60", children: [`Version ${n} · Updated `, _jsx(UpdatedTime, { iso: at })] }));
411
+ * page menu's Version history (0.30.0), for whoever `history` lets see it. The number is part of
412
+ * that history, so the same readers see it (0.41.0): to anyone else the line is only "Updated
413
+ * <minute>", because a page sent to someone reads as a message, and "Version 9" on a message
414
+ * tells its reader about eight drafts they were never meant to think about. */
415
+ function VersionLine({ n, at, numbered }) {
416
+ return (_jsxs("p", { "data-nospeak": true, className: "mt-4 text-xs opacity-60", children: [numbered ? `Version ${n} · Updated ` : 'Updated ', _jsx(UpdatedTime, { iso: at })] }));
414
417
  }
415
418
  /** The source of one version as published: its front matter's identifying lines, then the body,
416
419
  * so it pastes into a session and republishes onto the same page. Quoted as JSON strings, which
@@ -443,7 +446,7 @@ export function createArtifactRoutes(config) {
443
446
  const title = v.title ?? a.title;
444
447
  const summary = v.summary ?? a.summary;
445
448
  const toc = showToc(v.markdown, a.toc) ? tocOf(v.markdown) : [];
446
- return (_jsxs(_Fragment, { children: [_jsx(Menu, { a: a, reader: reader, history: history, viewing: n, settings: await settingsFor(a, reader), source: isAdmin(reader) ? sourceOf(a.id, { title, subtitle: v.subtitle, summary, to: v.to }, v.markdown) : null }), toc.length ? _jsx(TocRail, { items: toc }) : null, _jsx("div", { "data-nospeak": true, className: `mx-auto max-w-2xl px-6 ${top ? 'pt-20 sm:pt-24' : 'pt-8'}`, children: _jsxs("p", { "data-version-banner": true, role: "status", className: "rounded-lg border px-4 py-3 text-sm", style: { borderColor: brand.accent }, children: [`You are reading version ${n} of ${of}. `, _jsx("a", { href: pagePath(a.id), className: "underline underline-offset-4", style: { color: brand.accent }, children: "Read the current version" })] }) }), _jsx(Framed, { pack: pack, toc: toc.length ? _jsx(TocInline, { items: toc }) : null, cover: pack.Cover ? (_jsx(pack.Cover, { title: title, subtitle: v.subtitle, summary: summary, to: v.to, markdown: v.markdown, kicker: _jsx(Kicker, { to: v.to, brand: brand }), meta: _jsx(VersionLine, { n: n, at: v.at }), version: n, updatedAt: v.at, createdAt: a.createdAt })) : (_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-10 sm:pt-12' : 'pt-10'} pb-8 text-center`, children: [_jsx(Kicker, { to: v.to, brand: brand }), _jsx("h1", { className: "text-4xl sm:text-5xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: title }), v.subtitle ? (_jsx("p", { className: "mx-auto mt-4 max-w-xl text-xl sm:text-2xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: v.subtitle })) : null, _jsx("p", { className: "mx-auto mt-6 max-w-xl text-lg italic opacity-80", children: summary }), _jsx(VersionLine, { n: n, at: v.at })] })), children: _jsx(ArtifactMarkdown, { markdown: v.markdown, simulator: config.simulatorEmbed ? { embed: config.simulatorEmbed } : undefined, messages: config.messagesEmbed ? { embed: config.messagesEmbed } : undefined, definitions: a.definitions, checklist: { artifactId: a.id, accent: brand.accent }, Section: pack.Section, figure: pack.figure }) })] }));
449
+ return (_jsxs(_Fragment, { children: [_jsx(Menu, { a: a, reader: reader, history: history, viewing: n, settings: await settingsFor(a, reader), source: isAdmin(reader) ? sourceOf(a.id, { title, subtitle: v.subtitle, summary, to: v.to }, v.markdown) : null }), toc.length ? _jsx(TocRail, { items: toc }) : null, _jsx("div", { "data-nospeak": true, className: `mx-auto max-w-2xl px-6 ${top ? 'pt-20 sm:pt-24' : 'pt-8'}`, children: _jsxs("p", { "data-version-banner": true, role: "status", className: "rounded-lg border px-4 py-3 text-sm", style: { borderColor: brand.accent }, children: [`You are reading version ${n} of ${of}. `, _jsx("a", { href: pagePath(a.id), className: "underline underline-offset-4", style: { color: brand.accent }, children: "Read the current version" })] }) }), _jsx(Framed, { pack: pack, toc: toc.length ? _jsx(TocInline, { items: toc }) : null, cover: pack.Cover ? (_jsx(pack.Cover, { title: title, subtitle: v.subtitle, summary: summary, to: v.to, markdown: v.markdown, kicker: _jsx(Kicker, { to: v.to, brand: brand }), meta: _jsx(VersionLine, { n: n, at: v.at, numbered: true }), version: n, showVersion: true, updatedAt: v.at, createdAt: a.createdAt })) : (_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-10 sm:pt-12' : 'pt-10'} pb-8 text-center`, children: [_jsx(Kicker, { to: v.to, brand: brand }), _jsx("h1", { className: "text-4xl sm:text-5xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: title }), v.subtitle ? (_jsx("p", { className: "mx-auto mt-4 max-w-xl text-xl sm:text-2xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: v.subtitle })) : null, _jsx("p", { className: "mx-auto mt-6 max-w-xl text-lg italic opacity-80", children: summary }), _jsx(VersionLine, { n: n, at: v.at, numbered: true })] })), children: _jsx(ArtifactMarkdown, { markdown: v.markdown, simulator: config.simulatorEmbed ? { embed: config.simulatorEmbed } : undefined, messages: config.messagesEmbed ? { embed: config.messagesEmbed } : undefined, definitions: a.definitions, checklist: { artifactId: a.id, accent: brand.accent }, Section: pack.Section, figure: pack.figure }) })] }));
447
450
  }
448
451
  /** A broadcast: one message card. The sender's words at the top, then the media (a video with
449
452
  * sound and controls, photos, a voice note's player, link cards) in the order the words put
@@ -481,8 +484,8 @@ export function createArtifactRoutes(config) {
481
484
  const current = a.version ?? (a.versions?.length ?? 0) + 1;
482
485
  return (_jsxs(_Fragment, { children: [_jsx(Menu, { a: a, reader: reader, comments: mode !== 'off', history: history, viewing: current, settings: await settingsFor(a, reader), source: isAdmin(reader) ? sourceOf(a.id, a, a.markdown) : null }), toc.length ? _jsx(TocRail, { items: toc }) : null, _jsx("div", { id: "artifact-narration-root", children: await (async () => {
483
486
  const n = a.version ?? (a.versions?.length ?? 0) + 1;
484
- const meta = (_jsxs(_Fragment, { children: [_jsx(VersionLine, { n: n, at: a.updatedAt }), top && reader ? (_jsxs("p", { "data-nospeak": true, "data-reader-line": true, className: "mt-2 text-xs opacity-60", children: [`Signed in as ${reader.email} · `, _jsx("a", { href: switchUrl(a.id), className: "underline", children: "Use a different account" }), ' · ', _jsx("a", { href: signOutUrl(a.id), className: "underline", children: "Sign out" })] })) : null] }));
485
- const header = pack.Cover ? (_jsx(pack.Cover, { title: a.title, subtitle: a.subtitle, summary: a.summary, to: a.to, markdown: a.markdown, kicker: _jsx(Kicker, { to: a.to, brand: brand }), meta: meta, version: n, updatedAt: a.updatedAt, createdAt: a.createdAt })) : (_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-24 sm:pt-28' : 'pt-12'} pb-8 text-center`, children: [_jsx(Kicker, { to: a.to, brand: brand }), _jsx("h1", { className: "text-4xl sm:text-5xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: a.title }), a.subtitle ? (_jsx("p", { className: "mx-auto mt-4 max-w-xl text-xl sm:text-2xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: a.subtitle })) : null, _jsx("p", { className: "mx-auto mt-6 max-w-xl text-lg italic opacity-80", children: a.summary }), meta] }));
487
+ const meta = (_jsxs(_Fragment, { children: [_jsx(VersionLine, { n: n, at: a.updatedAt, numbered: seesHistory(reader) }), top && reader ? (_jsxs("p", { "data-nospeak": true, "data-reader-line": true, className: "mt-2 text-xs opacity-60", children: [`Signed in as ${reader.email} · `, _jsx("a", { href: switchUrl(a.id), className: "underline", children: "Use a different account" }), ' · ', _jsx("a", { href: signOutUrl(a.id), className: "underline", children: "Sign out" })] })) : null] }));
488
+ const header = pack.Cover ? (_jsx(pack.Cover, { title: a.title, subtitle: a.subtitle, summary: a.summary, to: a.to, markdown: a.markdown, kicker: _jsx(Kicker, { to: a.to, brand: brand }), meta: meta, version: n, showVersion: seesHistory(reader), updatedAt: a.updatedAt, createdAt: a.createdAt })) : (_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-24 sm:pt-28' : 'pt-12'} pb-8 text-center`, children: [_jsx(Kicker, { to: a.to, brand: brand }), _jsx("h1", { className: "text-4xl sm:text-5xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: a.title }), a.subtitle ? (_jsx("p", { className: "mx-auto mt-4 max-w-xl text-xl sm:text-2xl", style: { fontFamily: brand.type.display, color: brand.ink }, children: a.subtitle })) : null, _jsx("p", { className: "mx-auto mt-6 max-w-xl text-lg italic opacity-80", children: a.summary }), meta] }));
486
489
  return (_jsx(Framed, { pack: pack, toc: toc.length ? _jsx(TocInline, { items: toc }) : null, cover: _jsxs(_Fragment, { children: [perReader?.closesAt && !perReader.owner ? (_jsx(FeedbackWindow, { owner: owner, label: perReader.label, note: perReader.note, group: perReader.group, closesAt: perReader.closesAt })) : perReader?.note ? (_jsx("p", { "data-for-reader": true, className: "mx-auto mt-6 mb-0 max-w-2xl px-6 text-lg", children: `For ${perReader.label}: ${perReader.note}` })) : null, header, a.cover ? (_jsx("div", { className: "mx-auto max-w-2xl px-6 pb-8", children: _jsx("img", { src: a.cover, alt: "", className: "w-full rounded-xl border border-[color:var(--a-line)]" }) })) : null] }), children: _jsx(ArtifactMarkdown, { markdown: a.markdown, definitions: a.definitions, notes: config.state && a.state ? { artifactId: a.id, accent: brand.accent } : undefined,
487
490
  // Ticks are kept in the reader's browser on any host; Send needs one that keeps answers.
488
491
  checklist: { artifactId: a.id, send: !!config.state && !!a.state && Object.hasOwn(a.state.slots, CHECKLIST_SLOT), owner, accent: brand.accent }, Section: pack.Section, figure: pack.figure, simulator: config.simulatorEmbed ? { embed: config.simulatorEmbed } : undefined, messages: config.messagesEmbed ? { embed: config.messagesEmbed } : undefined }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersuit/artifacts",
3
- "version": "0.40.1",
3
+ "version": "0.41.0",
4
4
  "description": "Publish markdown as branded, read-aloud pages from a Next.js app: the artifacts store, the route factory, the read-along reader, brand packs, password and confidential pages, and share cards.",
5
5
  "license": "MIT",
6
6
  "type": "module",