@supersuit/artifacts 0.5.0 → 0.6.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,21 @@
|
|
|
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.6.0 (2026-09-27)
|
|
7
|
+
|
|
8
|
+
**"Updated" names the exact minute, in the reader's own time zone.**
|
|
9
|
+
|
|
10
|
+
- The line under an artifact's summary read "Updated September 27, 2026", so a page republished
|
|
11
|
+
three times in one evening looked the same each time. It now reads "Updated September 27, 2026
|
|
12
|
+
at 8:07 AM CDT": the server renders UTC, and a small client component, `UpdatedTime`, swaps in
|
|
13
|
+
the viewer's zone once the page runs. The value is a `<time dateTime>` carrying the ISO stamp.
|
|
14
|
+
- New `formatUpdated(iso, timeZone?)` in `src/artifacts/updated-at.ts`; a bad value renders
|
|
15
|
+
nothing rather than an invented time.
|
|
16
|
+
- DETECTOR: an artifact page whose "Updated" line has no clock time.
|
|
17
|
+
- REMEDY: take 0.6.0; nothing to configure.
|
|
18
|
+
- Tests: `updated-at.test.ts` (exact minute and zone, UTC default, bad input), verified red with
|
|
19
|
+
the minute removed.
|
|
20
|
+
|
|
6
21
|
## 0.5.0 (2026-09-27)
|
|
7
22
|
|
|
8
23
|
**Inline definitions: a page can define its jargon, and a reader sees it in line.**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatUpdated(iso: string, timeZone?: string): string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// The "Updated" line under an artifact's summary, to the exact minute with its zone named.
|
|
2
|
+
// The server renders UTC; the reader swaps in the viewer's own zone once it runs, so the minute
|
|
3
|
+
// is always one a person can place (Gary, 2026-09-27).
|
|
4
|
+
export function formatUpdated(iso, timeZone = 'UTC') {
|
|
5
|
+
const d = new Date(iso);
|
|
6
|
+
if (Number.isNaN(d.getTime()))
|
|
7
|
+
return '';
|
|
8
|
+
const date = d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', timeZone });
|
|
9
|
+
const time = d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', timeZone, timeZoneName: 'short' });
|
|
10
|
+
return `${date} at ${time}`;
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { formatUpdated } from '../artifacts/updated-at.js';
|
|
5
|
+
/** The exact minute a page was last updated, in the reader's own time zone once the page runs. */
|
|
6
|
+
export function UpdatedTime({ iso }) {
|
|
7
|
+
const [text, setText] = useState(() => formatUpdated(iso));
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
setText(formatUpdated(iso, Intl.DateTimeFormat().resolvedOptions().timeZone));
|
|
10
|
+
}, [iso]);
|
|
11
|
+
return (_jsx("time", { dateTime: iso, suppressHydrationWarning: true, children: text }));
|
|
12
|
+
}
|
package/lib/routes/artifacts.js
CHANGED
|
@@ -16,6 +16,7 @@ import { showToc, tocOf, TocInline, TocRail } from '../artifacts/toc.js';
|
|
|
16
16
|
import { renderShareCard } from '../brand/share-card.js';
|
|
17
17
|
import { ArtifactReader } from '../reader/artifact-reader.js';
|
|
18
18
|
import { ReaderWatch } from '../reader/reader-watch.js';
|
|
19
|
+
import { UpdatedTime } from '../reader/updated-time.js';
|
|
19
20
|
import { ACCESS_LEVELS, GRANT_COOKIE, GRANT_TTL_SECONDS, decide, firstName, mintGrant, safeReturnPath, signInUrl, verifyGrant, verifyPass, } from '../artifacts/reader.js';
|
|
20
21
|
import { FLAG_KINDS, summarize } from '../artifacts/readers-store.js';
|
|
21
22
|
import { AckDoor, ConfidentialBanner, NO_PRINT_CSS, NotAllowedDoor, SignInDoor, Watermark, ackText } from '../artifacts/confidential.js';
|
|
@@ -145,9 +146,8 @@ export function createArtifactRoutes(config) {
|
|
|
145
146
|
words = [];
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
|
-
const when = new Date(a.updatedAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
|
149
149
|
const toc = showToc(a.markdown, a.toc) ? tocOf(a.markdown) : [];
|
|
150
|
-
return (_jsxs(_Fragment, { children: [toc.length ? _jsx(TocRail, { items: toc }) : null, _jsxs("div", { id: "artifact-narration-root", children: [_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-24 sm:pt-28' : 'pt-12'} pb-8 text-center`, children: [_jsx("p", { "data-nospeak": true, className: "mb-4 text-[11px] font-medium uppercase tracking-[0.3em]", style: { color: brand.accent }, children: brand.kicker }), _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 }), _jsxs("p", { "data-nospeak": true, className: "mt-4 text-xs opacity-50", children: ["Updated ",
|
|
150
|
+
return (_jsxs(_Fragment, { children: [toc.length ? _jsx(TocRail, { items: toc }) : null, _jsxs("div", { id: "artifact-narration-root", children: [_jsxs("div", { className: `mx-auto max-w-2xl px-6 ${top ? 'pt-24 sm:pt-28' : 'pt-12'} pb-8 text-center`, children: [_jsx("p", { "data-nospeak": true, className: "mb-4 text-[11px] font-medium uppercase tracking-[0.3em]", style: { color: brand.accent }, children: brand.kicker }), _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 }), _jsxs("p", { "data-nospeak": true, className: "mt-4 text-xs opacity-50", children: ["Updated ", _jsx(UpdatedTime, { iso: a.updatedAt })] })] }), 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, _jsxs("article", { className: "mx-auto max-w-2xl px-6 pb-24", children: [toc.length ? _jsx(TocInline, { items: toc }) : null, _jsx(ArtifactMarkdown, { markdown: a.markdown, definitions: a.definitions, notes: config.state && a.state ? { artifactId: a.id, accent: brand.accent } : undefined })] })] }), a.narration && words.length > 0 ? (_jsx(ArtifactReader, { src: a.narration, words: words, rootId: "artifact-narration-root", label: brand.narratorLabel(a.voice), accent: brand.accent, ground: brand.ground })) : null] }));
|
|
151
151
|
}
|
|
152
152
|
const cookieLine = (value, maxAge) => `${GRANT_COOKIE}=${value}; Path=/; Max-Age=${maxAge}; HttpOnly; Secure; SameSite=Lax`;
|
|
153
153
|
/** GET /api/reader/enter?pass=<pass>&to=/<id>: where the sign-in authority sends a signed-in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supersuit/artifacts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|