@supersuit/artifacts 0.44.3 → 0.45.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 +23 -10
- package/lib/reader/comment-layer.js +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
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.45.0
|
|
7
|
+
|
|
8
|
+
**The Comments button shows the comments.** Pressing it now opens a list of every comment on the
|
|
9
|
+
page, in page order with open threads first and resolved ones after: the quoted words, the first
|
|
10
|
+
lines of the comment, and who left it. Tapping one closes the list, scrolls to the comment and
|
|
11
|
+
opens its thread, the same jump a `#comment-<id>` link makes. A comment on an earlier version
|
|
12
|
+
jumps to its entry in "Comments on earlier versions". The help line and the Hide comments toggle
|
|
13
|
+
stay below the list.
|
|
14
|
+
|
|
15
|
+
- DETECTOR: on a page with comments, press "Comments · N". Before, only the help text showed.
|
|
16
|
+
- REMEDY: update the dependency.
|
|
17
|
+
- Tests: comment-layer.test.tsx, "the Comments button lists every comment, and a tap opens that thread".
|
|
18
|
+
|
|
19
|
+
## 0.44.3
|
|
20
|
+
|
|
21
|
+
**Escape closes a comment card.** Pressing Escape cancels the comment being written (nothing is
|
|
22
|
+
saved), or closes the open thread, or the comments panel, topmost first. It ignores an Escape
|
|
23
|
+
another layer already handled, so the definition popover and page menu keep theirs.
|
|
24
|
+
|
|
25
|
+
- DETECTOR: select text, press Comment, press Escape. Before, the card stayed open.
|
|
26
|
+
- REMEDY: update the dependency.
|
|
27
|
+
- Tests: comment-layer.test.tsx, "Escape cancels the comment being written, saving nothing".
|
|
28
|
+
|
|
6
29
|
## 0.44.2
|
|
7
30
|
|
|
8
31
|
**An embed's caption no longer throws the read-along out of step.** The caption under a
|
|
@@ -17,16 +40,6 @@ early. A page carrying four embeds ran about sixty words ahead by its end. The e
|
|
|
17
40
|
- Tests: "an embed block is not read aloud" in render.test.tsx, both kinds, with and without a
|
|
18
41
|
player, the wrapped words equal the narrated words.
|
|
19
42
|
|
|
20
|
-
## 0.44.3
|
|
21
|
-
|
|
22
|
-
**Escape closes a comment card.** Pressing Escape cancels the comment being written (nothing is
|
|
23
|
-
saved), or closes the open thread, or the comments panel, topmost first. It ignores an Escape
|
|
24
|
-
another layer already handled, so the definition popover and page menu keep theirs.
|
|
25
|
-
|
|
26
|
-
- DETECTOR: select text, press Comment, press Escape. Before, the card stayed open.
|
|
27
|
-
- REMEDY: update the dependency.
|
|
28
|
-
- Tests: comment-layer.test.tsx, "Escape cancels the comment being written, saving nothing".
|
|
29
|
-
|
|
30
43
|
## 0.44.1
|
|
31
44
|
|
|
32
45
|
**Highlighting text works like any page again.** A press in the gap between paragraphs, in a line's
|
|
@@ -587,11 +587,13 @@ export function CommentLayer({ artifactId, rootId, ownerName, comments, canShare
|
|
|
587
587
|
const open = layout.placed.find((p) => p.item.id === thread);
|
|
588
588
|
const openItem = open?.item ?? layout.earlier.find((i) => i.id === thread);
|
|
589
589
|
const count = tops.filter((i) => !resolvedOf(i)).length;
|
|
590
|
+
const inPageOrder = [...layout.placed.map((p) => p.item).filter((i) => tops.includes(i)), ...layout.earlier.filter((i) => tops.includes(i))];
|
|
591
|
+
const panelList = [...inPageOrder.filter((i) => !resolvedOf(i)), ...inPageOrder.filter((i) => resolvedOf(i))];
|
|
590
592
|
const overlay = mounted ? (_jsxs("div", { "data-comment-ui": true, "data-nospeak": true, className: "font-sans", style: theme, children: [missing ? (_jsxs("div", { "data-comment-missing": true, role: "status", className: "fixed inset-x-3 top-3 z-50 mx-auto max-w-sm rounded-xl border border-[color:var(--a-line-strong)] bg-[color:var(--a-surface)] p-4 font-sans text-sm leading-snug text-[color:var(--a-strong)] shadow-xl", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("p", { className: "m-0 flex-1", children: "That comment can't be shown to you. Comments on this page are visible to its owner and admins." }), _jsx("button", { type: "button", "aria-label": "Close", onClick: () => setMissing(false), className: "-mr-1 -mt-1 px-1 text-lg leading-none opacity-60", children: "\u00D7" })] }), !signedIn && signIn ? (_jsx("a", { "data-comment-missing-sign-in": true, href: signInBack(signIn), className: "mt-3 inline-block rounded-full px-4 py-1.5 font-medium no-underline", style: { background: accent, color: 'var(--a-on-accent, #111)' }, children: "Sign in" })) : null] })) : null, showPins ? layout.placed.map(({ item, left, top, box }) => (_jsxs("div", { children: [box && !resolvedOf(item) ? (_jsx("div", { "data-comment-box": item.id, "aria-hidden": true, className: "pointer-events-none absolute z-30 rounded-md border-2", style: { left: box.left, top: box.top, width: box.width, height: box.height, borderColor: accent, borderStyle: item.kind === 'personal' ? 'dashed' : 'solid', opacity: thread === item.id ? 1 : 0.55 } })) : null, _jsx(Pin, { item: item, left: left, top: top, accent: accent, ground: ground, onOpen: () => setThread(thread === item.id ? null : item.id) })] }, item.id))) : null, chip && !draft ? (
|
|
591
593
|
// On a phone the button sits at the TOP of the screen, never beside the selection: that is
|
|
592
594
|
// where iOS draws its own Copy / Look Up menu, and the two fought for the same tap (Gary,
|
|
593
595
|
// 2026-09-29: "I keep trying to fight with the Apple pop-up"). A mouse keeps it by the text.
|
|
594
|
-
_jsx("button", { type: "button", "data-comment-chip": true, "data-comment-chip-at": onTouch() ? 'top' : 'selection', className: onTouch() ? 'fixed left-1/2 z-50 -translate-x-1/2 rounded-full px-5 py-2 text-sm font-medium shadow-lg' : 'absolute z-50 -translate-x-full rounded-full px-3 py-1 text-xs font-medium shadow-lg', style: onTouch() ? { top: 'calc(env(safe-area-inset-top, 0px) + 12px)', background: accent, color: 'var(--a-on-accent, #111)' } : { left: chip.x, top: chip.y, background: accent, color: 'var(--a-on-accent, #111)' }, onMouseDown: (e) => e.preventDefault(), onClick: () => { setDraft(chip); setChip(null); }, children: "Comment" })) : null, draft ? (_jsx(Floating, { x: draft.x, y: draft.y, children: _jsx(CommentCard, { ownerName: ownerName, mode: comments, canShare: share, closed: closed, requireName: requireName, knownName: knownName, signedIn: signedIn && onServer, quote: draft.quote, accent: accent, signIn: signIn, voice: host, onSave: (body, share, memo, by) => saveNew(body, share, draft, memo, by), onCancel: () => setDraft(null) }) })) : null, openItem && open ? (_jsx(Floating, { x: open.left + 28, y: open.top, children: _jsx(Thread, { item: openItem, replies: items.filter((i) => i.value.parent === openItem.id), canReply: openItem.kind === 'shared' && share, awaiting: closesAt != null, askName: requireName && !knownName, onJump: jump, canResolve: openItem.kind === 'shared' && isOwner, startExpanded: expanded === openItem.id, accent: accent, audioDb: audioDb, onClose: () => { setThread(null); setExpanded(null); }, onRemove: remove, onEdit: edit, onReply: reply, onResolve: resolveThread }, openItem.id) })) : null, drag ? (_jsx("div", { "aria-hidden": true, className: "pointer-events-none fixed z-40 rounded-md border-2 border-dashed", style: { left: Math.min(drag.x1, drag.x2), top: Math.min(drag.y1, drag.y2), width: Math.abs(drag.x2 - drag.x1), height: Math.abs(drag.y2 - drag.y1), borderColor: accent, background: `color-mix(in srgb, ${accent} 12%, transparent)` } })) : null, _jsxs("div", { className: "fixed right-4 z-40 flex flex-col items-end gap-2", style: { bottom: 'calc(env(safe-area-inset-bottom, 0px) + 5.5rem)' }, children: [panel ? (_jsxs("div", { "data-comment-panel": true, className: "w-
|
|
596
|
+
_jsx("button", { type: "button", "data-comment-chip": true, "data-comment-chip-at": onTouch() ? 'top' : 'selection', className: onTouch() ? 'fixed left-1/2 z-50 -translate-x-1/2 rounded-full px-5 py-2 text-sm font-medium shadow-lg' : 'absolute z-50 -translate-x-full rounded-full px-3 py-1 text-xs font-medium shadow-lg', style: onTouch() ? { top: 'calc(env(safe-area-inset-top, 0px) + 12px)', background: accent, color: 'var(--a-on-accent, #111)' } : { left: chip.x, top: chip.y, background: accent, color: 'var(--a-on-accent, #111)' }, onMouseDown: (e) => e.preventDefault(), onClick: () => { setDraft(chip); setChip(null); }, children: "Comment" })) : null, draft ? (_jsx(Floating, { x: draft.x, y: draft.y, children: _jsx(CommentCard, { ownerName: ownerName, mode: comments, canShare: share, closed: closed, requireName: requireName, knownName: knownName, signedIn: signedIn && onServer, quote: draft.quote, accent: accent, signIn: signIn, voice: host, onSave: (body, share, memo, by) => saveNew(body, share, draft, memo, by), onCancel: () => setDraft(null) }) })) : null, openItem && open ? (_jsx(Floating, { x: open.left + 28, y: open.top, children: _jsx(Thread, { item: openItem, replies: items.filter((i) => i.value.parent === openItem.id), canReply: openItem.kind === 'shared' && share, awaiting: closesAt != null, askName: requireName && !knownName, onJump: jump, canResolve: openItem.kind === 'shared' && isOwner, startExpanded: expanded === openItem.id, accent: accent, audioDb: audioDb, onClose: () => { setThread(null); setExpanded(null); }, onRemove: remove, onEdit: edit, onReply: reply, onResolve: resolveThread }, openItem.id) })) : null, drag ? (_jsx("div", { "aria-hidden": true, className: "pointer-events-none fixed z-40 rounded-md border-2 border-dashed", style: { left: Math.min(drag.x1, drag.x2), top: Math.min(drag.y1, drag.y2), width: Math.abs(drag.x2 - drag.x1), height: Math.abs(drag.y2 - drag.y1), borderColor: accent, background: `color-mix(in srgb, ${accent} 12%, transparent)` } })) : null, _jsxs("div", { className: "fixed right-4 z-40 flex flex-col items-end gap-2", style: { bottom: 'calc(env(safe-area-inset-bottom, 0px) + 5.5rem)' }, children: [panel ? (_jsxs("div", { "data-comment-panel": true, className: "flex max-h-[min(70vh,32rem)] w-80 max-w-[calc(100vw-2rem)] flex-col rounded-xl border border-[color:var(--a-line-strong)] bg-[color:var(--a-surface)] p-3 text-xs text-[color:var(--a-strong)] shadow-xl", children: [panelList.length ? (_jsx("ul", { "data-comment-list": true, className: "-mx-1 mb-2 min-h-0 flex-1 list-none overflow-y-auto p-0", children: panelList.map((item) => (_jsx("li", { className: "m-0", children: _jsxs("button", { type: "button", "data-comment-list-item": item.id, onClick: () => { setPanel(false); setTarget(item.id); }, className: "w-full rounded-lg px-2 py-2 text-left hover:bg-[color:var(--a-line)]", style: resolvedOf(item) ? { opacity: 0.6 } : undefined, children: [_jsxs("span", { className: "block truncate text-[12px] italic opacity-70", children: [resolvedOf(item) ? '✓ ' : '', item.value.anchor.kind === 'text' ? `“${item.value.anchor.quote}”` : 'A box on the page'] }), _jsx("span", { className: "line-clamp-2 block text-[14px] leading-snug", children: item.value.body }), _jsx("span", { className: "block text-[11px] opacity-60", children: item.kind === 'personal' ? 'only you' : item.name })] }) }, item.id))) })) : null, _jsx("p", { className: "opacity-80", children: "Select any text, or hold and drag a box over an image, to leave a comment." }), isOwner ? _jsx("p", { "data-comment-owner": true, className: "mt-1 opacity-80", children: "This is your page: you see every comment, with the name of who left it." }) : null, _jsx("button", { type: "button", "data-comment-pins-toggle": true, className: "mt-2 underline", onClick: () => setShowPins((v) => !v), children: showPins ? 'Hide comments' : 'Show comments' })] })) : null, _jsx("button", { type: "button", "data-comment-button": true, "aria-expanded": panel, onClick: () => setPanel((v) => !v), className: "rounded-full border px-3 py-1.5 text-xs font-medium shadow-lg backdrop-blur", style: { borderColor: accent, color: accent, background: `color-mix(in srgb, ${ground} 85%, transparent)` }, children: count ? `Comments · ${count}` : 'Comment' })] })] })) : null;
|
|
595
597
|
return (_jsxs(_Fragment, { children: [_jsx("span", { hidden: true, "data-comment-layer": comments, "data-comment-can-share": share ? 'yes' : 'no' }), mounted && layout.earlier.length ? (_jsxs("section", { "data-comments-earlier": true, "data-comment-ui": true, "data-nospeak": true, className: "mx-auto max-w-2xl px-6 pb-16 text-sm", children: [_jsx("p", { className: "text-[11px] uppercase tracking-[0.2em]", style: { color: accent }, children: "Comments on earlier versions" }), _jsx("ul", { className: "m-0 mt-2 grid list-none gap-3 p-0", children: layout.earlier.map((item) => (_jsxs("li", { "data-comment-earlier-id": item.id, ...(highlight === item.id ? { 'data-comment-highlight': '' } : {}), className: "m-0 border-l-2 py-1 pl-3", style: { borderColor: accent, borderStyle: item.kind === 'personal' ? 'dashed' : 'solid', ...(highlight === item.id ? { background: `color-mix(in srgb, ${accent} 14%, transparent)` } : {}) }, children: [resolvedOf(item) ? _jsxs("span", { "data-comment-resolved-line": true, className: "block text-[13px] font-medium", style: { color: accent }, children: ["\u2713 ", resolvedLine(resolvedOf(item))] }) : null, item.value.anchor.kind === 'text' ? _jsxs("span", { className: "block text-[13px] italic opacity-70", children: ["\u201C", item.value.anchor.quote, "\u201D"] }) : _jsx("span", { className: "block text-[13px] italic opacity-70", children: "A box on part of the page that has changed" }), _jsx("span", { className: "block whitespace-pre-wrap text-[15px] text-[color:var(--a-strong)]", children: item.value.body }), _jsxs("span", { className: "block text-xs opacity-60", children: [item.kind === 'personal' ? 'only you' : item.name, ` · version ${item.value.version}`, item.mine ? _jsx("button", { type: "button", onClick: () => void remove(item), className: "ml-2 underline", children: "delete" }) : null] })] }, item.id))) })] })) : null, overlay ? createPortal(overlay, document.body) : null] }));
|
|
596
598
|
}
|
|
597
599
|
function Pin({ item, left, top, accent, ground, onOpen }) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supersuit/artifacts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.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",
|