@bendyline/squisq-react 1.1.0 → 1.1.2
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/dist/DocPlayer.d.ts.map +1 -1
- package/dist/DocPlayer.js +6 -1
- package/dist/DocPlayer.js.map +1 -1
- package/dist/LinearDocView.d.ts.map +1 -1
- package/dist/LinearDocView.js +10 -2
- package/dist/LinearDocView.js.map +1 -1
- package/dist/MarkdownRenderer.d.ts.map +1 -1
- package/dist/MarkdownRenderer.js +8 -1
- package/dist/MarkdownRenderer.js.map +1 -1
- package/dist/layers/ImageLayer.js +6 -6
- package/dist/layers/ImageLayer.js.map +1 -1
- package/dist/layers/ShapeLayer.js +2 -2
- package/dist/layers/ShapeLayer.js.map +1 -1
- package/dist/layers/TableLayer.js +2 -2
- package/dist/layers/TableLayer.js.map +1 -1
- package/dist/layers/VideoLayer.js +2 -2
- package/dist/layers/VideoLayer.js.map +1 -1
- package/dist/squisq-player.global.js +13 -5
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-entry.d.ts.map +1 -1
- package/dist/standalone-entry.js +14 -1
- package/dist/standalone-entry.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/package.json +11 -11
- package/src/DocPlayer.tsx +7 -1
- package/src/LinearDocView.tsx +10 -2
- package/src/MarkdownRenderer.tsx +10 -7
- package/src/layers/ImageLayer.tsx +6 -6
- package/src/layers/ShapeLayer.tsx +2 -2
- package/src/layers/TableLayer.tsx +2 -2
- package/src/layers/VideoLayer.tsx +2 -2
- package/src/standalone-entry.tsx +13 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "React component library for doc playback, block rendering, and media layers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -52,17 +52,17 @@
|
|
|
52
52
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@bendyline/squisq": "1.2.
|
|
55
|
+
"@bendyline/squisq": "1.2.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@types/react": "
|
|
59
|
-
"preact": "
|
|
60
|
-
"react": "
|
|
61
|
-
"react-dom": "
|
|
62
|
-
"@testing-library/react": "
|
|
63
|
-
"@testing-library/jest-dom": "
|
|
64
|
-
"jsdom": "
|
|
65
|
-
"tsup": "
|
|
66
|
-
"typescript": "
|
|
58
|
+
"@types/react": "18.3.28",
|
|
59
|
+
"preact": "10.29.0",
|
|
60
|
+
"react": "18.3.1",
|
|
61
|
+
"react-dom": "18.3.1",
|
|
62
|
+
"@testing-library/react": "16.3.2",
|
|
63
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
64
|
+
"jsdom": "25.0.1",
|
|
65
|
+
"tsup": "8.5.1",
|
|
66
|
+
"typescript": "5.9.3"
|
|
67
67
|
}
|
|
68
68
|
}
|
package/src/DocPlayer.tsx
CHANGED
|
@@ -323,11 +323,17 @@ export function DocPlayer({
|
|
|
323
323
|
if (isPlaying && coverWasShowing.current && coverBlock && !renderMode) {
|
|
324
324
|
coverWasShowing.current = false;
|
|
325
325
|
setCoverGraceActive(true);
|
|
326
|
+
// Intentionally no cleanup: if coverBlock's memoized reference changes
|
|
327
|
+
// mid-grace (e.g., due to a preview re-render), clearing the timer would
|
|
328
|
+
// leave coverGraceActive stuck at true because the effect body won't
|
|
329
|
+
// re-run (coverWasShowing.current is now false).
|
|
326
330
|
coverGraceTimer.current = setTimeout(() => setCoverGraceActive(false), 3000);
|
|
327
|
-
return () => clearTimeout(coverGraceTimer.current);
|
|
328
331
|
}
|
|
329
332
|
}, [isPlaying, coverBlock, renderMode]);
|
|
330
333
|
|
|
334
|
+
// Always clear the grace timer on unmount
|
|
335
|
+
useEffect(() => () => clearTimeout(coverGraceTimer.current), []);
|
|
336
|
+
|
|
331
337
|
// Determine if we should show the cover block
|
|
332
338
|
// Show cover when: has cover block, not playing, at time 0, not in render mode
|
|
333
339
|
// OR during the grace period after first play, OR when coverForced (render mode)
|
package/src/LinearDocView.tsx
CHANGED
|
@@ -257,8 +257,10 @@ function getTemplateDefaults(
|
|
|
257
257
|
return { fact: headingText, explanation: bodyText || headingText };
|
|
258
258
|
case 'comparisonBar':
|
|
259
259
|
return { leftLabel: 'A', leftValue: 60, rightLabel: 'B', rightValue: 40 };
|
|
260
|
-
case 'listBlock':
|
|
261
|
-
|
|
260
|
+
case 'listBlock': {
|
|
261
|
+
const items = extractListItems(contents);
|
|
262
|
+
return { items: items.length > 0 ? items : ['Item 1', 'Item 2', 'Item 3'] };
|
|
263
|
+
}
|
|
262
264
|
case 'definitionCard':
|
|
263
265
|
return { term: headingText, definition: bodyText || headingText };
|
|
264
266
|
case 'dateEvent':
|
|
@@ -406,6 +408,12 @@ export function LinearDocView({
|
|
|
406
408
|
border-top: 1px solid var(--squisq-linear-muted);
|
|
407
409
|
margin: 1.5em 0;
|
|
408
410
|
}
|
|
411
|
+
.squisq-linear-content img {
|
|
412
|
+
max-width: 100%;
|
|
413
|
+
height: auto;
|
|
414
|
+
border-radius: 6px;
|
|
415
|
+
margin: 0.5em 0;
|
|
416
|
+
}
|
|
409
417
|
.squisq-linear-content strong {
|
|
410
418
|
font-weight: 700;
|
|
411
419
|
}
|
package/src/MarkdownRenderer.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
MarkdownTableRow,
|
|
23
23
|
MarkdownTableCell,
|
|
24
24
|
} from '@bendyline/squisq/markdown';
|
|
25
|
+
import { useMediaUrl } from './hooks/MediaContext';
|
|
25
26
|
|
|
26
27
|
// ── Props ──────────────────────────────────────────────────────────
|
|
27
28
|
|
|
@@ -86,13 +87,7 @@ function renderInline(nodes: MarkdownInlineNode[], keyPrefix = ''): React.ReactN
|
|
|
86
87
|
|
|
87
88
|
case 'image':
|
|
88
89
|
return (
|
|
89
|
-
<
|
|
90
|
-
key={key}
|
|
91
|
-
className="squisq-md-image"
|
|
92
|
-
src={node.url}
|
|
93
|
-
alt={node.alt ?? ''}
|
|
94
|
-
title={node.title ?? undefined}
|
|
95
|
-
/>
|
|
90
|
+
<MdImage key={key} src={node.url} alt={node.alt ?? ''} title={node.title ?? undefined} />
|
|
96
91
|
);
|
|
97
92
|
|
|
98
93
|
case 'break':
|
|
@@ -343,6 +338,14 @@ function renderBlocks(nodes: MarkdownBlockNode[], keyPrefix = ''): React.ReactNo
|
|
|
343
338
|
return nodes.map((node, i) => renderBlock(node, `${keyPrefix}b${i}`));
|
|
344
339
|
}
|
|
345
340
|
|
|
341
|
+
// ── Image with MediaProvider resolution ───────────────────────────
|
|
342
|
+
|
|
343
|
+
/** Renders an <img> that resolves its src through the MediaProvider when available. */
|
|
344
|
+
function MdImage({ src, alt, title }: { src: string; alt: string; title?: string }) {
|
|
345
|
+
const resolved = useMediaUrl(src, '.');
|
|
346
|
+
return <img className="squisq-md-image" src={resolved} alt={alt} title={title} />;
|
|
347
|
+
}
|
|
348
|
+
|
|
346
349
|
// ── Main Component ─────────────────────────────────────────────────
|
|
347
350
|
|
|
348
351
|
/**
|
|
@@ -61,8 +61,8 @@ export function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerP
|
|
|
61
61
|
<foreignObject x={finalX} y={finalY} width={width} height={height}>
|
|
62
62
|
<div
|
|
63
63
|
style={{
|
|
64
|
-
width:
|
|
65
|
-
height:
|
|
64
|
+
width: `${width}px`,
|
|
65
|
+
height: `${height}px`,
|
|
66
66
|
overflow: 'hidden',
|
|
67
67
|
}}
|
|
68
68
|
>
|
|
@@ -71,8 +71,8 @@ export function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerP
|
|
|
71
71
|
alt={content.alt || ''}
|
|
72
72
|
className={kbStyle.className}
|
|
73
73
|
style={{
|
|
74
|
-
width:
|
|
75
|
-
height:
|
|
74
|
+
width: `${width}px`,
|
|
75
|
+
height: `${height}px`,
|
|
76
76
|
objectFit: 'cover',
|
|
77
77
|
objectPosition: 'center',
|
|
78
78
|
display: 'block',
|
|
@@ -101,8 +101,8 @@ export function ImageLayer({ layer, basePath, viewport, blockTime }: ImageLayerP
|
|
|
101
101
|
src={src}
|
|
102
102
|
alt={content.alt || ''}
|
|
103
103
|
style={{
|
|
104
|
-
width:
|
|
105
|
-
height:
|
|
104
|
+
width: `${width}px`,
|
|
105
|
+
height: `${height}px`,
|
|
106
106
|
objectFit: 'cover',
|
|
107
107
|
objectPosition: 'center',
|
|
108
108
|
display: 'block',
|
|
@@ -49,8 +49,8 @@ export function ShapeLayer({ layer, viewport, blockTime }: ShapeLayerProps) {
|
|
|
49
49
|
<foreignObject x={x} y={y} width={width} height={height}>
|
|
50
50
|
<div
|
|
51
51
|
style={{
|
|
52
|
-
width:
|
|
53
|
-
height:
|
|
52
|
+
width: `${width}px`,
|
|
53
|
+
height: `${height}px`,
|
|
54
54
|
background: fill,
|
|
55
55
|
borderRadius: content.borderRadius ? `${content.borderRadius}px` : undefined,
|
|
56
56
|
pointerEvents: 'none',
|
|
@@ -51,8 +51,8 @@ export function TableLayer({ layer, viewport, blockTime }: TableLayerProps) {
|
|
|
51
51
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
52
52
|
{...({ xmlns: 'http://www.w3.org/1999/xhtml' } as any)}
|
|
53
53
|
style={{
|
|
54
|
-
width:
|
|
55
|
-
height:
|
|
54
|
+
width: `${width}px`,
|
|
55
|
+
height: `${height}px`,
|
|
56
56
|
display: 'flex',
|
|
57
57
|
alignItems: 'center',
|
|
58
58
|
justifyContent: 'center',
|
|
@@ -134,8 +134,8 @@ export function VideoLayer({
|
|
|
134
134
|
data-clip-start={content.clipStart}
|
|
135
135
|
data-clip-end={content.clipEnd}
|
|
136
136
|
style={{
|
|
137
|
-
width:
|
|
138
|
-
height:
|
|
137
|
+
width: `${width}px`,
|
|
138
|
+
height: `${height}px`,
|
|
139
139
|
objectFit: content.fit || 'cover',
|
|
140
140
|
objectPosition: 'center',
|
|
141
141
|
display: 'block',
|
package/src/standalone-entry.tsx
CHANGED
|
@@ -86,10 +86,22 @@ function createInlineMediaProvider(
|
|
|
86
86
|
images: Record<string, string>,
|
|
87
87
|
basePath: string,
|
|
88
88
|
): MediaProvider {
|
|
89
|
+
// Doc image references may not exactly match image map keys
|
|
90
|
+
// (e.g., "images/hero.jpg" vs "hero.jpg"). Build a filename-keyed
|
|
91
|
+
// lookup so resolution can fall back to basename matching.
|
|
92
|
+
const byFilename: Record<string, string> = {};
|
|
93
|
+
for (const key of Object.keys(images)) {
|
|
94
|
+
const filename = key.split('/').pop()!;
|
|
95
|
+
byFilename[filename] = images[key];
|
|
96
|
+
}
|
|
97
|
+
|
|
89
98
|
return {
|
|
90
99
|
async resolveUrl(relativePath: string): Promise<string> {
|
|
91
100
|
if (relativePath in images) return images[relativePath];
|
|
92
|
-
|
|
101
|
+
const stripped = relativePath.replace(/^\.\//, '');
|
|
102
|
+
if (stripped !== relativePath && stripped in images) return images[stripped];
|
|
103
|
+
const filename = relativePath.split('/').pop()!;
|
|
104
|
+
if (filename in byFilename) return byFilename[filename];
|
|
93
105
|
if (
|
|
94
106
|
relativePath.startsWith('http') ||
|
|
95
107
|
relativePath.startsWith('data:') ||
|