@bendyline/squisq-react 1.2.0 → 1.3.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/dist/InlineAudioPlayer.d.ts +15 -0
- package/dist/InlineAudioPlayer.d.ts.map +1 -0
- package/dist/InlineAudioPlayer.js +22 -0
- package/dist/InlineAudioPlayer.js.map +1 -0
- package/dist/InlineVideoPlayer.d.ts +21 -0
- package/dist/InlineVideoPlayer.d.ts.map +1 -0
- package/dist/InlineVideoPlayer.js +29 -0
- package/dist/InlineVideoPlayer.js.map +1 -0
- package/dist/LinearDocView.d.ts.map +1 -1
- package/dist/LinearDocView.js +124 -13
- package/dist/LinearDocView.js.map +1 -1
- package/dist/MarkdownRenderer.d.ts.map +1 -1
- package/dist/MarkdownRenderer.js +111 -2
- package/dist/MarkdownRenderer.js.map +1 -1
- package/dist/SocialCaptionOverlay.d.ts.map +1 -1
- package/dist/SocialCaptionOverlay.js +3 -2
- package/dist/SocialCaptionOverlay.js.map +1 -1
- package/dist/__tests__/JsonView.test.d.ts +2 -0
- package/dist/__tests__/JsonView.test.d.ts.map +1 -0
- package/dist/__tests__/JsonView.test.js +93 -0
- package/dist/__tests__/JsonView.test.js.map +1 -0
- package/dist/__tests__/LinearDocView.test.js +62 -0
- package/dist/__tests__/LinearDocView.test.js.map +1 -1
- package/dist/hooks/MediaContext.d.ts.map +1 -1
- package/dist/hooks/MediaContext.js +13 -7
- package/dist/hooks/MediaContext.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/jsonView/JsonView.d.ts +26 -0
- package/dist/jsonView/JsonView.d.ts.map +1 -0
- package/dist/jsonView/JsonView.js +12 -0
- package/dist/jsonView/JsonView.js.map +1 -0
- package/dist/jsonView/RenderNode.d.ts +22 -0
- package/dist/jsonView/RenderNode.d.ts.map +1 -0
- package/dist/jsonView/RenderNode.js +30 -0
- package/dist/jsonView/RenderNode.js.map +1 -0
- package/dist/jsonView/index.d.ts +3 -0
- package/dist/jsonView/index.d.ts.map +1 -0
- package/dist/jsonView/index.js +2 -0
- package/dist/jsonView/index.js.map +1 -0
- package/dist/jsonView/useJsonViewTokens.d.ts +14 -0
- package/dist/jsonView/useJsonViewTokens.d.ts.map +1 -0
- package/dist/jsonView/useJsonViewTokens.js +34 -0
- package/dist/jsonView/useJsonViewTokens.js.map +1 -0
- package/dist/jsonView/viewers.d.ts +30 -0
- package/dist/jsonView/viewers.d.ts.map +1 -0
- package/dist/jsonView/viewers.js +226 -0
- package/dist/jsonView/viewers.js.map +1 -0
- package/dist/squisq-player.css +1 -1
- package/dist/squisq-player.css.map +1 -1
- package/dist/squisq-player.global.js +24 -10
- package/dist/squisq-player.global.js.map +1 -1
- package/dist/standalone-source.js +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
- package/src/InlineAudioPlayer.tsx +46 -0
- package/src/InlineVideoPlayer.tsx +70 -0
- package/src/LinearDocView.tsx +136 -14
- package/src/MarkdownRenderer.tsx +156 -10
- package/src/SocialCaptionOverlay.tsx +3 -2
- package/src/__tests__/JsonView.test.tsx +111 -0
- package/src/__tests__/LinearDocView.test.tsx +66 -0
- package/src/hooks/MediaContext.tsx +15 -8
- package/src/index.ts +9 -0
- package/src/jsonView/JsonView.tsx +51 -0
- package/src/jsonView/RenderNode.tsx +51 -0
- package/src/jsonView/index.ts +2 -0
- package/src/jsonView/json-view.css +206 -0
- package/src/jsonView/useJsonViewTokens.ts +57 -0
- package/src/jsonView/viewers.tsx +343 -0
- package/src/styles/doc-animations.css +36 -0
- package/src/styles/index.css +7 -0
- package/src/types.ts +5 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only viewer renderers for `<JsonView>`. Each viewer is a small
|
|
3
|
+
* pure function that takes a value + schema slice and renders inline.
|
|
4
|
+
* Group / card-stack viewers recurse into `RenderNode`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Fragment, useMemo } from 'react';
|
|
8
|
+
import {
|
|
9
|
+
arrayItemKind,
|
|
10
|
+
type ControlKind,
|
|
11
|
+
type SquisqAnnotatedSchema,
|
|
12
|
+
} from '@bendyline/squisq/jsonForm';
|
|
13
|
+
import { parseMarkdown } from '@bendyline/squisq/markdown';
|
|
14
|
+
import { MarkdownRenderer } from '../MarkdownRenderer';
|
|
15
|
+
import { RenderNode } from './RenderNode';
|
|
16
|
+
|
|
17
|
+
export interface ViewerProps {
|
|
18
|
+
value: unknown;
|
|
19
|
+
schema: SquisqAnnotatedSchema;
|
|
20
|
+
rootSchema: SquisqAnnotatedSchema;
|
|
21
|
+
rootData: unknown;
|
|
22
|
+
pointer: string;
|
|
23
|
+
density: 'comfortable' | 'compact';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Primitive viewers ──────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export function TextViewer({ value }: ViewerProps) {
|
|
29
|
+
if (value === undefined || value === null || value === '') {
|
|
30
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
31
|
+
}
|
|
32
|
+
return <span className="squisq-jv-value">{String(value)}</span>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function MultilineViewer({ value }: ViewerProps) {
|
|
36
|
+
if (value === undefined || value === null || value === '') {
|
|
37
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
38
|
+
}
|
|
39
|
+
return <span className="squisq-jv-value squisq-jv-value--multiline">{String(value)}</span>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function RichTextViewer({ value }: ViewerProps) {
|
|
43
|
+
const nodes = useMemo(() => {
|
|
44
|
+
if (typeof value !== 'string' || value === '') return null;
|
|
45
|
+
try {
|
|
46
|
+
const doc = parseMarkdown(value);
|
|
47
|
+
return doc.children;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}, [value]);
|
|
52
|
+
if (!nodes) return <span className="squisq-jv-empty">—</span>;
|
|
53
|
+
return (
|
|
54
|
+
<div className="squisq-jv-richtext">
|
|
55
|
+
<MarkdownRenderer nodes={nodes} />
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function NumberViewer({ value }: ViewerProps) {
|
|
61
|
+
if (value === undefined || value === null) {
|
|
62
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
63
|
+
}
|
|
64
|
+
return <span className="squisq-jv-value">{String(value)}</span>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function BooleanViewer({ value }: ViewerProps) {
|
|
68
|
+
const on = Boolean(value);
|
|
69
|
+
return (
|
|
70
|
+
<span className={`squisq-jv-toggle squisq-jv-toggle--${on ? 'on' : 'off'}`}>
|
|
71
|
+
{on ? 'On' : 'Off'}
|
|
72
|
+
</span>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function EnumViewer({ value, schema }: ViewerProps) {
|
|
77
|
+
if (value === undefined || value === null || value === '') {
|
|
78
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
79
|
+
}
|
|
80
|
+
const labels = schema.squisq?.enumLabels;
|
|
81
|
+
const display = labels && typeof value === 'string' ? (labels[value] ?? value) : String(value);
|
|
82
|
+
return <span className="squisq-jv-value">{display}</span>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function ColorViewer({ value }: ViewerProps) {
|
|
86
|
+
if (typeof value !== 'string' || value === '') {
|
|
87
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
88
|
+
}
|
|
89
|
+
return (
|
|
90
|
+
<span className="squisq-jv-color">
|
|
91
|
+
<span className="squisq-jv-color__swatch" style={{ background: value }} aria-hidden />
|
|
92
|
+
<span className="squisq-jv-color__hex">{value}</span>
|
|
93
|
+
</span>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function DateViewer({ value, schema }: ViewerProps) {
|
|
98
|
+
if (typeof value !== 'string' || value === '') {
|
|
99
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
100
|
+
}
|
|
101
|
+
const fmt = schema.format;
|
|
102
|
+
let display = value;
|
|
103
|
+
try {
|
|
104
|
+
const date = new Date(value);
|
|
105
|
+
if (!Number.isNaN(date.getTime())) {
|
|
106
|
+
if (fmt === 'time') {
|
|
107
|
+
display = date.toLocaleTimeString();
|
|
108
|
+
} else if (fmt === 'date') {
|
|
109
|
+
display = date.toLocaleDateString();
|
|
110
|
+
} else {
|
|
111
|
+
display = date.toLocaleString();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
/* fall through to raw value */
|
|
116
|
+
}
|
|
117
|
+
return <span className="squisq-jv-value">{display}</span>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ── Composite viewers ─────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
export function ChipBinViewer({ value, schema }: ViewerProps) {
|
|
123
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
124
|
+
return <span className="squisq-jv-empty">—</span>;
|
|
125
|
+
}
|
|
126
|
+
const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
|
|
127
|
+
const labels = itemSchema?.squisq?.enumLabels;
|
|
128
|
+
return (
|
|
129
|
+
<div className="squisq-jv-chip-bin">
|
|
130
|
+
{value.map((item, i) => {
|
|
131
|
+
const label =
|
|
132
|
+
labels && typeof item === 'string' ? (labels[item] ?? String(item)) : String(item);
|
|
133
|
+
return (
|
|
134
|
+
<span key={i} className="squisq-jv-chip">
|
|
135
|
+
{label}
|
|
136
|
+
</span>
|
|
137
|
+
);
|
|
138
|
+
})}
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function CardStackViewer(props: ViewerProps) {
|
|
144
|
+
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
145
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
146
|
+
return <span className="squisq-jv-empty">No items</span>;
|
|
147
|
+
}
|
|
148
|
+
const itemSchema = (Array.isArray(schema.items) ? schema.items[0] : schema.items) ?? {};
|
|
149
|
+
const itemLabel = itemSchema.squisq?.itemLabel;
|
|
150
|
+
return (
|
|
151
|
+
<div className="squisq-jv-card-stack">
|
|
152
|
+
{value.map((item, i) => {
|
|
153
|
+
const title = resolveItemTitle(itemLabel, item, i);
|
|
154
|
+
return (
|
|
155
|
+
<div key={i} className="squisq-jv-card">
|
|
156
|
+
{title ? <h4 className="squisq-jv-card__title">{title}</h4> : null}
|
|
157
|
+
<RenderNode
|
|
158
|
+
value={item}
|
|
159
|
+
schema={itemSchema}
|
|
160
|
+
rootSchema={rootSchema}
|
|
161
|
+
rootData={rootData}
|
|
162
|
+
pointer={`${pointer}/${i}`}
|
|
163
|
+
density={density}
|
|
164
|
+
suppressTopGroupTitle
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
);
|
|
168
|
+
})}
|
|
169
|
+
</div>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function resolveItemTitle(
|
|
174
|
+
spec: string | { fromField: string } | undefined,
|
|
175
|
+
item: unknown,
|
|
176
|
+
index: number,
|
|
177
|
+
): string {
|
|
178
|
+
if (!spec) return `Item ${index + 1}`;
|
|
179
|
+
if (typeof spec === 'string') return spec;
|
|
180
|
+
if (item && typeof item === 'object') {
|
|
181
|
+
const v = (item as Record<string, unknown>)[spec.fromField];
|
|
182
|
+
if (typeof v === 'string' && v !== '') return v;
|
|
183
|
+
if (typeof v === 'number') return String(v);
|
|
184
|
+
}
|
|
185
|
+
return `Item ${index + 1}`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function GroupViewer(props: ViewerProps & { suppressTitle?: boolean }) {
|
|
189
|
+
const { value, schema, rootSchema, rootData, pointer, density, suppressTitle } = props;
|
|
190
|
+
const title = !suppressTitle ? (schema.squisq?.label ?? schema.title) : undefined;
|
|
191
|
+
const help = schema.squisq?.help ?? schema.description;
|
|
192
|
+
const obj = (value && typeof value === 'object' ? (value as Record<string, unknown>) : {}) ?? {};
|
|
193
|
+
const propEntries = Object.entries(schema.properties ?? {});
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<section className="squisq-jv-group">
|
|
197
|
+
{title ? <h3 className="squisq-jv-group__title">{title}</h3> : null}
|
|
198
|
+
{help ? <p className="squisq-jv-group__help">{help}</p> : null}
|
|
199
|
+
{propEntries.map(([key, propSchema]) => (
|
|
200
|
+
<Fragment key={key}>
|
|
201
|
+
<RowOrSection
|
|
202
|
+
label={propSchema.squisq?.label ?? propSchema.title ?? key}
|
|
203
|
+
help={propSchema.squisq?.help ?? propSchema.description}
|
|
204
|
+
kindHint={propSchema}
|
|
205
|
+
>
|
|
206
|
+
<RenderNode
|
|
207
|
+
value={obj[key]}
|
|
208
|
+
schema={propSchema}
|
|
209
|
+
rootSchema={rootSchema}
|
|
210
|
+
rootData={rootData}
|
|
211
|
+
pointer={`${pointer}/${key}`}
|
|
212
|
+
density={density}
|
|
213
|
+
suppressTopGroupTitle
|
|
214
|
+
/>
|
|
215
|
+
</RowOrSection>
|
|
216
|
+
</Fragment>
|
|
217
|
+
))}
|
|
218
|
+
</section>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Decide whether to use the inline label/value Row layout, or to drop
|
|
224
|
+
* the row chrome entirely (for nested groups, card-stacks, richtext —
|
|
225
|
+
* which present their own headings).
|
|
226
|
+
*/
|
|
227
|
+
function RowOrSection({
|
|
228
|
+
label,
|
|
229
|
+
help,
|
|
230
|
+
kindHint,
|
|
231
|
+
children,
|
|
232
|
+
}: {
|
|
233
|
+
label: string;
|
|
234
|
+
help?: string;
|
|
235
|
+
kindHint: SquisqAnnotatedSchema;
|
|
236
|
+
children: React.ReactNode;
|
|
237
|
+
}) {
|
|
238
|
+
const composite = isCompositeKind(kindHint);
|
|
239
|
+
if (composite) {
|
|
240
|
+
return <>{children}</>;
|
|
241
|
+
}
|
|
242
|
+
return (
|
|
243
|
+
<div className="squisq-jv-row">
|
|
244
|
+
<div className="squisq-jv-label" title={help}>
|
|
245
|
+
{label}
|
|
246
|
+
</div>
|
|
247
|
+
<div>{children}</div>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function isCompositeKind(schema: SquisqAnnotatedSchema): boolean {
|
|
253
|
+
const t = schema.type;
|
|
254
|
+
const arrayLike = t === 'array' || (Array.isArray(t) && t.includes('array'));
|
|
255
|
+
if (arrayLike) {
|
|
256
|
+
return arrayItemKind(schema) === 'object';
|
|
257
|
+
}
|
|
258
|
+
const objectLike = t === 'object' || (Array.isArray(t) && t.includes('object'));
|
|
259
|
+
if (objectLike) return true;
|
|
260
|
+
if (schema.oneOf || schema.anyOf) return true;
|
|
261
|
+
if (schema.format === 'markdown' || schema.squisq?.control === 'richtext') return true;
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function TabsViewer(props: ViewerProps) {
|
|
266
|
+
const { value, schema, rootSchema, rootData, pointer, density } = props;
|
|
267
|
+
const branches = (schema.oneOf ?? schema.anyOf ?? []).slice();
|
|
268
|
+
const matchedIndex = pickMatchingBranch(branches as SquisqAnnotatedSchema[], value);
|
|
269
|
+
const branch = branches[matchedIndex] as SquisqAnnotatedSchema | undefined;
|
|
270
|
+
if (!branch) {
|
|
271
|
+
return <TextViewer {...props} />;
|
|
272
|
+
}
|
|
273
|
+
return (
|
|
274
|
+
<div className="squisq-jv-tabs">
|
|
275
|
+
<span className="squisq-jv-tabs__discriminator">
|
|
276
|
+
{branch.squisq?.label ?? branch.title ?? `Option ${matchedIndex + 1}`}
|
|
277
|
+
</span>
|
|
278
|
+
<RenderNode
|
|
279
|
+
value={value}
|
|
280
|
+
schema={branch}
|
|
281
|
+
rootSchema={rootSchema}
|
|
282
|
+
rootData={rootData}
|
|
283
|
+
pointer={pointer}
|
|
284
|
+
density={density}
|
|
285
|
+
/>
|
|
286
|
+
</div>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function pickMatchingBranch(branches: SquisqAnnotatedSchema[], value: unknown): number {
|
|
291
|
+
for (let i = 0; i < branches.length; i++) {
|
|
292
|
+
if (matchesShape(branches[i], value)) return i;
|
|
293
|
+
}
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function matchesShape(schema: SquisqAnnotatedSchema, value: unknown): boolean {
|
|
298
|
+
const t = schema.type;
|
|
299
|
+
const target = Array.isArray(t) ? t.find((x) => x !== 'null') : t;
|
|
300
|
+
if (!target) return true;
|
|
301
|
+
switch (target) {
|
|
302
|
+
case 'string':
|
|
303
|
+
return typeof value === 'string';
|
|
304
|
+
case 'number':
|
|
305
|
+
case 'integer':
|
|
306
|
+
return typeof value === 'number';
|
|
307
|
+
case 'boolean':
|
|
308
|
+
return typeof value === 'boolean';
|
|
309
|
+
case 'null':
|
|
310
|
+
return value === null;
|
|
311
|
+
case 'array':
|
|
312
|
+
return Array.isArray(value);
|
|
313
|
+
case 'object':
|
|
314
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
315
|
+
default:
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ── Registry ──────────────────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
// eslint-disable-next-line react-refresh/only-export-components
|
|
323
|
+
export const VIEWERS: Record<ControlKind, React.ComponentType<ViewerProps>> = {
|
|
324
|
+
text: TextViewer,
|
|
325
|
+
multiline: MultilineViewer,
|
|
326
|
+
richtext: RichTextViewer,
|
|
327
|
+
color: ColorViewer,
|
|
328
|
+
date: DateViewer,
|
|
329
|
+
time: DateViewer,
|
|
330
|
+
datetime: DateViewer,
|
|
331
|
+
slider: NumberViewer,
|
|
332
|
+
stepper: NumberViewer,
|
|
333
|
+
segmented: EnumViewer,
|
|
334
|
+
radio: EnumViewer,
|
|
335
|
+
combobox: EnumViewer,
|
|
336
|
+
toggle: BooleanViewer,
|
|
337
|
+
checkbox: BooleanViewer,
|
|
338
|
+
card: GroupViewer,
|
|
339
|
+
group: GroupViewer,
|
|
340
|
+
'card-stack': CardStackViewer,
|
|
341
|
+
'chip-bin': ChipBinViewer,
|
|
342
|
+
tabs: TabsViewer,
|
|
343
|
+
};
|
|
@@ -456,3 +456,39 @@
|
|
|
456
456
|
background: rgba(255, 255, 255, 0.9);
|
|
457
457
|
color: #1a1a1a;
|
|
458
458
|
}
|
|
459
|
+
|
|
460
|
+
/* ── Inline media players ────────────────────────────────────────── */
|
|
461
|
+
/* Used by <InlineVideoPlayer> / <InlineAudioPlayer>, which the
|
|
462
|
+
* MarkdownRenderer swaps in for raw <video>/<audio> htmlElements so
|
|
463
|
+
* their src goes through MediaContext. The wrapper is a <span> with
|
|
464
|
+
* `display: inline-block` so the player flows inline with surrounding
|
|
465
|
+
* text but still respects its own intrinsic dimensions. */
|
|
466
|
+
|
|
467
|
+
.squisq-inline-video-player {
|
|
468
|
+
display: inline-block;
|
|
469
|
+
max-width: 480px;
|
|
470
|
+
width: 100%;
|
|
471
|
+
margin: 0.5em 0;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.squisq-inline-video-player video {
|
|
475
|
+
display: block;
|
|
476
|
+
width: 100%;
|
|
477
|
+
max-width: 100%;
|
|
478
|
+
height: auto;
|
|
479
|
+
border-radius: 4px;
|
|
480
|
+
background: #000;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.squisq-inline-audio-player {
|
|
484
|
+
display: inline-block;
|
|
485
|
+
max-width: 360px;
|
|
486
|
+
width: 100%;
|
|
487
|
+
margin: 0.25em 0;
|
|
488
|
+
vertical-align: middle;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.squisq-inline-audio-player audio {
|
|
492
|
+
display: block;
|
|
493
|
+
width: 100%;
|
|
494
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -29,8 +29,12 @@ export type ControlsLayout = 'overlay' | 'sidebar' | 'bottom';
|
|
|
29
29
|
* - `'linear'` — Long-scrolling document view. Renders the full markdown as
|
|
30
30
|
* readable HTML with template-annotated sections shown as inline SVG cards.
|
|
31
31
|
* No audio, no timeline, no controls — just a scrollable page.
|
|
32
|
+
* - `'page'` — Plain semantic HTML preview matching what the
|
|
33
|
+
* `markdownDocToPlainHtml` export produces. No SquisqPlayer, no SVG
|
|
34
|
+
* cards — just `<h1>`/`<p>`/`<ul>` etc. inside a sandboxed iframe.
|
|
35
|
+
* Use when you want a WYSIWYG view of the simple HTML export.
|
|
32
36
|
*/
|
|
33
|
-
export type DisplayMode = 'video' | 'slideshow' | 'linear';
|
|
37
|
+
export type DisplayMode = 'video' | 'slideshow' | 'linear' | 'page';
|
|
34
38
|
|
|
35
39
|
/**
|
|
36
40
|
* Caption display style.
|