@supersuit/artifacts 0.30.0 → 0.30.1

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,19 @@
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.30.1
7
+
8
+ **Play from here is one compact pill that stays on screen.** A word tapped near the right edge put
9
+ the bar half off the screen, so its label wrapped to three lines inside a pill inside a pill, with
10
+ an emoji play glyph that iOS draws as a picture.
11
+
12
+ - One pill in the page's accent color, the UI font, 13px, never wrapping, a drawn play triangle, and
13
+ the close button inside the same pill.
14
+ - Placed under the tapped word and clamped inside the screen.
15
+ - DETECTOR: on a phone, pause and tap a word near the right edge. A three-line bubble means 0.30.0.
16
+ - REMEDY: update the dependency.
17
+ - Tests: `artifact-reader-controls.test.tsx` (no emoji, no wrapping, kept inside a 390px screen).
18
+
6
19
  ## 0.30.0
7
20
 
8
21
  **Every page has a page menu: one small "⋯" button, top right, whose items depend on who you are
@@ -144,6 +144,8 @@ export function isUserScrollKey(key, target) {
144
144
  return false;
145
145
  return true;
146
146
  }
147
+ /** The Play from here bar's width, so it can be kept inside the screen. */
148
+ const OFFER_WIDTH = 168;
147
149
  export function ArtifactReader({ src, words, rootId, label, accent, ground, }) {
148
150
  const narrator = label;
149
151
  const audioRef = useRef(null);
@@ -184,7 +186,9 @@ export function ArtifactReader({ src, words, rootId, label, accent, ground, }) {
184
186
  return;
185
187
  }
186
188
  const r = el.getBoundingClientRect();
187
- setOffer({ idx, top: r.bottom + window.scrollY + 6, left: Math.max(8, r.left + window.scrollX) });
189
+ // Kept on screen: a word near the right edge would push the bar off it and wrap its label.
190
+ const vw = window.innerWidth || document.documentElement.clientWidth || 1024;
191
+ setOffer({ idx, top: r.bottom + window.scrollY + 8, left: Math.max(8, Math.min(r.left, vw - OFFER_WIDTH - 8)) + window.scrollX });
188
192
  };
189
193
  root.addEventListener('click', onClick);
190
194
  return () => root.removeEventListener('click', onClick);
@@ -264,7 +268,7 @@ export function ArtifactReader({ src, words, rootId, label, accent, ground, }) {
264
268
  follow(true);
265
269
  }, onSeeking: () => {
266
270
  startedRef.current = true;
267
- }, onPause: () => { playingRef.current = false; setPlaying(false); }, onLoadedMetadata: (e) => setDur(e.target.duration), onRateChange: (e) => setRate(e.target.playbackRate) }), offer ? (_jsxs("div", { "data-nospeak": true, className: "absolute z-50 flex items-center gap-1 rounded-full border px-1 py-1 text-sm shadow-lg", style: { top: offer.top, left: offer.left, background: ground, borderColor: accent }, children: [_jsx("button", { type: "button", "data-play-from-here": true, onClick: () => playFrom(offer.idx), className: "rounded-full px-3 py-1 font-medium", style: { background: accent, color: ground }, children: "\u25B6 Play from here" }), _jsx("button", { type: "button", "aria-label": "Close", onClick: () => setOffer(null), className: "px-2 opacity-70", children: "\u00D7" })] })) : null, playing && !following ? (_jsx("button", { type: "button", "data-follow-voice": true, "data-nospeak": true, onClick: backToVoice, className: "fixed left-1/2 z-50 -translate-x-1/2 rounded-full px-4 py-2 text-sm font-medium shadow-lg", style: { bottom: 'calc(env(safe-area-inset-bottom, 0px) + 5.5rem)', background: accent, color: ground }, children: "Back to the voice" })) : null, _jsx(PlayerBar, { playing: playing, onToggle: toggle, label: narrator, accent: accent, ground: ground, value: t, max: dur, step: 0.1, onSeek: (v) => {
271
+ }, onPause: () => { playingRef.current = false; setPlaying(false); }, onLoadedMetadata: (e) => setDur(e.target.duration), onRateChange: (e) => setRate(e.target.playbackRate) }), offer ? (_jsxs("div", { "data-nospeak": true, "data-play-offer": true, className: "absolute z-50 flex items-center rounded-full font-sans text-[13px] leading-none shadow-lg", style: { top: offer.top, left: offer.left, width: OFFER_WIDTH, whiteSpace: 'nowrap', background: accent, color: ground }, children: [_jsxs("button", { type: "button", "data-play-from-here": true, onClick: () => playFrom(offer.idx), className: "flex flex-1 items-center gap-1.5 rounded-l-full py-2 pl-3 pr-1 font-medium", children: [_jsx("svg", { "aria-hidden": "true", width: "10", height: "11", viewBox: "0 0 10 11", children: _jsx("path", { d: "M1 1l8 4.5L1 10z", fill: "currentColor" }) }), "Play from here"] }), _jsx("button", { type: "button", "aria-label": "Close", onClick: () => setOffer(null), className: "rounded-r-full py-2 pl-1 pr-3 text-base leading-none opacity-70", children: "\u00D7" })] })) : null, playing && !following ? (_jsx("button", { type: "button", "data-follow-voice": true, "data-nospeak": true, onClick: backToVoice, className: "fixed left-1/2 z-50 -translate-x-1/2 rounded-full px-4 py-2 text-sm font-medium shadow-lg", style: { bottom: 'calc(env(safe-area-inset-bottom, 0px) + 5.5rem)', background: accent, color: ground }, children: "Back to the voice" })) : null, _jsx(PlayerBar, { playing: playing, onToggle: toggle, label: narrator, accent: accent, ground: ground, value: t, max: dur, step: 0.1, onSeek: (v) => {
268
272
  if (audioRef.current)
269
273
  audioRef.current.currentTime = v;
270
274
  }, progress: `${fmt(t)} / ${fmt(dur)}`, rate: rate, onRate: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supersuit/artifacts",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
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",