@bendyline/docblocks-react 2.2.0 → 2.2.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.
@@ -0,0 +1,606 @@
1
+ import {
2
+ Dialog
3
+ } from "./chunk-LG6HAWCK.js";
4
+
5
+ // src/Settings/Settings.tsx
6
+ import {
7
+ useCallback,
8
+ useEffect,
9
+ useId,
10
+ useLayoutEffect,
11
+ useRef,
12
+ useState
13
+ } from "react";
14
+
15
+ // src/preferences/theme.ts
16
+ var ACCENT_COLORS = [
17
+ "brown",
18
+ "green",
19
+ "blue",
20
+ "purple",
21
+ "maroon",
22
+ "orange",
23
+ "gray"
24
+ ];
25
+ var DB_CHROME_COLORS = {
26
+ light: "#f3eede",
27
+ dark: "#262219"
28
+ };
29
+ var THEME_STORAGE_KEY = "docblocks:themePreference";
30
+ var ACCENT_STORAGE_KEY = "docblocks:accentColor";
31
+ function loadThemePreference() {
32
+ try {
33
+ const raw = localStorage.getItem(THEME_STORAGE_KEY);
34
+ if (raw === "light" || raw === "dark" || raw === "auto") return raw;
35
+ } catch {
36
+ }
37
+ return "auto";
38
+ }
39
+ function saveThemePreference(value) {
40
+ try {
41
+ localStorage.setItem(THEME_STORAGE_KEY, value);
42
+ } catch {
43
+ }
44
+ }
45
+ function loadAccentColor() {
46
+ try {
47
+ const raw = localStorage.getItem(ACCENT_STORAGE_KEY);
48
+ if (ACCENT_COLORS.some((color) => color === raw)) return raw;
49
+ } catch {
50
+ }
51
+ return "brown";
52
+ }
53
+ function saveAccentColor(value) {
54
+ try {
55
+ localStorage.setItem(ACCENT_STORAGE_KEY, value);
56
+ } catch {
57
+ }
58
+ }
59
+
60
+ // src/preferences/write-canvas.ts
61
+ var SANS_STACK = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
62
+ var SERIF_STACK = 'Georgia, "Times New Roman", serif';
63
+ var WRITE_CANVAS_FONT_SCHEMES = [
64
+ { id: "theme", label: "Inherit from theme", group: "theme" },
65
+ {
66
+ id: "serif-sans",
67
+ label: "Serif headings \xB7 Sans body",
68
+ group: "system",
69
+ headerFont: SERIF_STACK,
70
+ bodyFont: SANS_STACK
71
+ },
72
+ {
73
+ id: "sans-sans",
74
+ label: "Sans headings \xB7 Sans body",
75
+ group: "system",
76
+ headerFont: SANS_STACK,
77
+ bodyFont: SANS_STACK
78
+ },
79
+ {
80
+ id: "serif-serif",
81
+ label: "Serif headings \xB7 Serif body",
82
+ group: "system",
83
+ headerFont: SERIF_STACK,
84
+ bodyFont: SERIF_STACK
85
+ },
86
+ {
87
+ id: "pt-serif",
88
+ label: "PT Serif",
89
+ group: "curated",
90
+ headerFont: '"PT Serif", Georgia, serif',
91
+ bodyFont: '"PT Serif", Georgia, serif'
92
+ },
93
+ {
94
+ id: "hanken",
95
+ label: "Hanken Grotesk",
96
+ group: "curated",
97
+ headerFont: '"Hanken Grotesk", system-ui, sans-serif',
98
+ bodyFont: '"Hanken Grotesk", system-ui, sans-serif'
99
+ },
100
+ {
101
+ id: "playfair-pt-serif",
102
+ label: "Playfair Display \xB7 PT Serif",
103
+ group: "curated",
104
+ headerFont: '"Playfair Display", Georgia, serif',
105
+ bodyFont: '"PT Serif", Georgia, serif'
106
+ },
107
+ {
108
+ id: "hanken-lora",
109
+ label: "Hanken Grotesk \xB7 Lora",
110
+ group: "curated",
111
+ headerFont: '"Hanken Grotesk", system-ui, sans-serif',
112
+ bodyFont: '"Lora", Georgia, serif'
113
+ },
114
+ {
115
+ id: "dm-serif-dm-sans",
116
+ label: "DM Serif Display \xB7 DM Sans",
117
+ group: "curated",
118
+ headerFont: '"DM Serif Display", Georgia, serif',
119
+ bodyFont: '"DM Sans", system-ui, sans-serif'
120
+ },
121
+ {
122
+ id: "inter",
123
+ label: "Inter",
124
+ group: "curated",
125
+ headerFont: '"Inter", system-ui, sans-serif',
126
+ bodyFont: '"Inter", system-ui, sans-serif'
127
+ }
128
+ ];
129
+ var FONT_SCHEMES_BY_ID = new Map(WRITE_CANVAS_FONT_SCHEMES.map((scheme) => [scheme.id, scheme]));
130
+ var DEFAULT_WRITE_CANVAS_FONT_SCHEME = "theme";
131
+ function resolveWriteCanvasFonts(scheme) {
132
+ const option = FONT_SCHEMES_BY_ID.get(scheme);
133
+ const resolved = {};
134
+ if (option?.headerFont) resolved.headerFont = option.headerFont;
135
+ if (option?.bodyFont) resolved.bodyFont = option.bodyFont;
136
+ return resolved;
137
+ }
138
+ function isWriteCanvasFontScheme(value) {
139
+ return typeof value === "string" && FONT_SCHEMES_BY_ID.has(value);
140
+ }
141
+ var WRITE_CANVAS_TEXT_SIZE_MIN = 12;
142
+ var WRITE_CANVAS_TEXT_SIZE_MAX = 32;
143
+ var WRITE_CANVAS_LINE_SPACING_MIN = 1;
144
+ var WRITE_CANVAS_LINE_SPACING_MAX = 2.4;
145
+ var DEFAULT_WRITE_CANVAS_PREFERENCES = {
146
+ textSize: 16,
147
+ lineSpacing: 1.7,
148
+ fontScheme: DEFAULT_WRITE_CANVAS_FONT_SCHEME
149
+ };
150
+ var WRITE_CANVAS_STORAGE_KEY = "docblocks:writeCanvasSettings";
151
+ function loadWriteCanvasPreferences() {
152
+ try {
153
+ const raw = localStorage.getItem(WRITE_CANVAS_STORAGE_KEY);
154
+ if (!raw) return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };
155
+ const stored = JSON.parse(raw);
156
+ if (!isRecord(stored)) return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };
157
+ return {
158
+ textSize: numberInRange(
159
+ stored.textSize,
160
+ WRITE_CANVAS_TEXT_SIZE_MIN,
161
+ WRITE_CANVAS_TEXT_SIZE_MAX
162
+ ) ? stored.textSize : DEFAULT_WRITE_CANVAS_PREFERENCES.textSize,
163
+ lineSpacing: numberInRange(
164
+ stored.lineSpacing,
165
+ WRITE_CANVAS_LINE_SPACING_MIN,
166
+ WRITE_CANVAS_LINE_SPACING_MAX
167
+ ) ? stored.lineSpacing : DEFAULT_WRITE_CANVAS_PREFERENCES.lineSpacing,
168
+ // Older stored preferences predate fontScheme; fall back to the default.
169
+ fontScheme: isWriteCanvasFontScheme(stored.fontScheme) ? stored.fontScheme : DEFAULT_WRITE_CANVAS_PREFERENCES.fontScheme
170
+ };
171
+ } catch {
172
+ return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };
173
+ }
174
+ }
175
+ function saveWriteCanvasPreferences(value) {
176
+ try {
177
+ localStorage.setItem(WRITE_CANVAS_STORAGE_KEY, JSON.stringify(value));
178
+ } catch {
179
+ }
180
+ }
181
+ function numberInRange(value, min, max) {
182
+ return typeof value === "number" && Number.isFinite(value) && value >= min && value <= max;
183
+ }
184
+ function isRecord(value) {
185
+ return typeof value === "object" && value !== null && !Array.isArray(value);
186
+ }
187
+
188
+ // src/Settings/Settings.tsx
189
+ import { jsx, jsxs } from "react/jsx-runtime";
190
+ var ACCENT_LABELS = {
191
+ brown: "Brown",
192
+ green: "Green",
193
+ blue: "Blue",
194
+ purple: "Purple",
195
+ maroon: "Maroon",
196
+ orange: "Orange",
197
+ gray: "Gray"
198
+ };
199
+ function SettingsDialog({ title = "Settings", onClose, children }) {
200
+ return /* @__PURE__ */ jsx(Dialog, { title, onClose, size: "wide", children });
201
+ }
202
+ function ThemeSettings({ value, onChange, name = "theme" }) {
203
+ return /* @__PURE__ */ jsxs("fieldset", { className: "db-settings-fieldset", children: [
204
+ /* @__PURE__ */ jsx("legend", { className: "db-settings-legend", children: "Theme" }),
205
+ /* @__PURE__ */ jsxs("div", { className: "db-settings-radio-row", children: [
206
+ /* @__PURE__ */ jsxs("label", { className: "db-settings-radio db-settings-radio--inline", children: [
207
+ /* @__PURE__ */ jsx(
208
+ "input",
209
+ {
210
+ type: "radio",
211
+ name,
212
+ value: "auto",
213
+ checked: value === "auto",
214
+ onChange: () => onChange("auto")
215
+ }
216
+ ),
217
+ "System default"
218
+ ] }),
219
+ /* @__PURE__ */ jsxs("label", { className: "db-settings-radio db-settings-radio--inline", children: [
220
+ /* @__PURE__ */ jsx(
221
+ "input",
222
+ {
223
+ type: "radio",
224
+ name,
225
+ value: "light",
226
+ checked: value === "light",
227
+ onChange: () => onChange("light")
228
+ }
229
+ ),
230
+ "Light"
231
+ ] }),
232
+ /* @__PURE__ */ jsxs("label", { className: "db-settings-radio db-settings-radio--inline", children: [
233
+ /* @__PURE__ */ jsx(
234
+ "input",
235
+ {
236
+ type: "radio",
237
+ name,
238
+ value: "dark",
239
+ checked: value === "dark",
240
+ onChange: () => onChange("dark")
241
+ }
242
+ ),
243
+ "Dark"
244
+ ] })
245
+ ] })
246
+ ] });
247
+ }
248
+ function AccentColorSettings({
249
+ value,
250
+ onChange,
251
+ name = "accent-color"
252
+ }) {
253
+ return /* @__PURE__ */ jsxs("fieldset", { className: "db-settings-fieldset", children: [
254
+ /* @__PURE__ */ jsx("legend", { className: "db-settings-legend", children: "Accent color" }),
255
+ /* @__PURE__ */ jsx("p", { className: "db-settings-hint", children: "Used in both light and dark appearances." }),
256
+ /* @__PURE__ */ jsx("div", { className: "db-settings-accent-grid", children: ACCENT_COLORS.map((color) => /* @__PURE__ */ jsxs(
257
+ "label",
258
+ {
259
+ className: `db-settings-accent${value === color ? " db-settings-accent--selected" : ""}`,
260
+ children: [
261
+ /* @__PURE__ */ jsx(
262
+ "input",
263
+ {
264
+ className: "db-settings-accent-input",
265
+ type: "radio",
266
+ name,
267
+ value: color,
268
+ checked: value === color,
269
+ onChange: () => onChange(color)
270
+ }
271
+ ),
272
+ /* @__PURE__ */ jsx(
273
+ "span",
274
+ {
275
+ className: `db-settings-accent-swatch db-settings-accent-swatch--${color}`,
276
+ "aria-hidden": "true"
277
+ }
278
+ ),
279
+ /* @__PURE__ */ jsx("span", { children: ACCENT_LABELS[color] })
280
+ ]
281
+ },
282
+ color
283
+ )) })
284
+ ] });
285
+ }
286
+ function WriteCanvasSettingsControls({ value, onChange }) {
287
+ return /* @__PURE__ */ jsxs("fieldset", { className: "db-settings-fieldset", children: [
288
+ /* @__PURE__ */ jsx("legend", { className: "db-settings-legend", children: "Write canvas" }),
289
+ /* @__PURE__ */ jsx("p", { className: "db-settings-hint", children: "Adjust the writing view without changing the document or its exported text." }),
290
+ /* @__PURE__ */ jsxs("label", { className: "db-settings-slider", children: [
291
+ /* @__PURE__ */ jsxs("span", { className: "db-settings-slider-header", children: [
292
+ /* @__PURE__ */ jsx("span", { children: "Text size" }),
293
+ /* @__PURE__ */ jsxs("output", { className: "db-settings-slider-value", "aria-hidden": "true", children: [
294
+ value.textSize,
295
+ "px"
296
+ ] })
297
+ ] }),
298
+ /* @__PURE__ */ jsx(
299
+ "input",
300
+ {
301
+ type: "range",
302
+ min: WRITE_CANVAS_TEXT_SIZE_MIN,
303
+ max: WRITE_CANVAS_TEXT_SIZE_MAX,
304
+ step: 1,
305
+ value: value.textSize,
306
+ "aria-label": "Text size",
307
+ "aria-valuetext": `${value.textSize} pixels`,
308
+ onChange: (event) => onChange({ ...value, textSize: Number(event.currentTarget.value) })
309
+ }
310
+ )
311
+ ] }),
312
+ /* @__PURE__ */ jsxs("label", { className: "db-settings-slider", children: [
313
+ /* @__PURE__ */ jsxs("span", { className: "db-settings-slider-header", children: [
314
+ /* @__PURE__ */ jsx("span", { children: "Line spacing" }),
315
+ /* @__PURE__ */ jsx("output", { className: "db-settings-slider-value", "aria-hidden": "true", children: formatLineSpacing(value.lineSpacing) })
316
+ ] }),
317
+ /* @__PURE__ */ jsx(
318
+ "input",
319
+ {
320
+ type: "range",
321
+ min: WRITE_CANVAS_LINE_SPACING_MIN,
322
+ max: WRITE_CANVAS_LINE_SPACING_MAX,
323
+ step: 0.1,
324
+ value: value.lineSpacing,
325
+ "aria-label": "Line spacing",
326
+ "aria-valuetext": `${value.lineSpacing} times`,
327
+ onChange: (event) => onChange({ ...value, lineSpacing: Number(event.currentTarget.value) })
328
+ }
329
+ )
330
+ ] }),
331
+ /* @__PURE__ */ jsx(
332
+ FontSchemePicker,
333
+ {
334
+ value: value.fontScheme,
335
+ onChange: (fontScheme) => onChange({ ...value, fontScheme })
336
+ }
337
+ )
338
+ ] });
339
+ }
340
+ function FontSchemePicker({ value, onChange }) {
341
+ const selectedIndex = Math.max(
342
+ 0,
343
+ WRITE_CANVAS_FONT_SCHEMES.findIndex((scheme) => scheme.id === value)
344
+ );
345
+ const selectedScheme = WRITE_CANVAS_FONT_SCHEMES[selectedIndex];
346
+ const [isOpen, setIsOpen] = useState(false);
347
+ const [activeIndex, setActiveIndex] = useState(selectedIndex);
348
+ const [menuPosition, setMenuPosition] = useState(null);
349
+ const rootRef = useRef(null);
350
+ const triggerRef = useRef(null);
351
+ const listboxRef = useRef(null);
352
+ const optionRefs = useRef([]);
353
+ const labelId = useId();
354
+ const listboxId = useId();
355
+ const close = useCallback((returnFocus) => {
356
+ setIsOpen(false);
357
+ setMenuPosition(null);
358
+ if (returnFocus) triggerRef.current?.focus({ preventScroll: true });
359
+ }, []);
360
+ const updateMenuPosition = useCallback(() => {
361
+ const trigger = triggerRef.current;
362
+ if (!trigger) return;
363
+ const rect = trigger.getBoundingClientRect();
364
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
365
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
366
+ const gutter = 8;
367
+ const gap = 4;
368
+ const width = Math.min(rect.width, viewportWidth - gutter * 2);
369
+ const left = Math.min(Math.max(gutter, rect.left), viewportWidth - width - gutter);
370
+ const spaceBelow = viewportHeight - rect.bottom - gap - gutter;
371
+ const spaceAbove = rect.top - gap - gutter;
372
+ const openAbove = spaceBelow < 260 && spaceAbove > spaceBelow;
373
+ const availableHeight = openAbove ? spaceAbove : spaceBelow;
374
+ const maxHeight = Math.max(80, Math.min(420, availableHeight));
375
+ setMenuPosition(
376
+ openAbove ? {
377
+ left,
378
+ bottom: viewportHeight - rect.top + gap,
379
+ width,
380
+ maxHeight
381
+ } : {
382
+ left,
383
+ top: rect.bottom + gap,
384
+ width,
385
+ maxHeight
386
+ }
387
+ );
388
+ }, []);
389
+ const open = useCallback(
390
+ (index) => {
391
+ setActiveIndex(index);
392
+ updateMenuPosition();
393
+ setIsOpen(true);
394
+ },
395
+ [updateMenuPosition]
396
+ );
397
+ useEffect(() => {
398
+ if (!isOpen) setActiveIndex(selectedIndex);
399
+ }, [isOpen, selectedIndex]);
400
+ useLayoutEffect(() => {
401
+ if (!isOpen) return;
402
+ updateMenuPosition();
403
+ listboxRef.current?.focus({ preventScroll: true });
404
+ optionRefs.current[activeIndex]?.scrollIntoView?.({ block: "nearest" });
405
+ }, [activeIndex, isOpen, updateMenuPosition]);
406
+ useEffect(() => {
407
+ if (!isOpen) return;
408
+ const handleOutsidePointer = (event) => {
409
+ if (!rootRef.current?.contains(event.target)) close(false);
410
+ };
411
+ const handleViewportChange = () => updateMenuPosition();
412
+ document.addEventListener("mousedown", handleOutsidePointer);
413
+ window.addEventListener("resize", handleViewportChange);
414
+ window.addEventListener("scroll", handleViewportChange, true);
415
+ return () => {
416
+ document.removeEventListener("mousedown", handleOutsidePointer);
417
+ window.removeEventListener("resize", handleViewportChange);
418
+ window.removeEventListener("scroll", handleViewportChange, true);
419
+ };
420
+ }, [close, isOpen, updateMenuPosition]);
421
+ const select = useCallback(
422
+ (index) => {
423
+ const scheme = WRITE_CANVAS_FONT_SCHEMES[index];
424
+ if (!scheme) return;
425
+ onChange(scheme.id);
426
+ close(true);
427
+ },
428
+ [close, onChange]
429
+ );
430
+ const handleTriggerKeyDown = (event) => {
431
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
432
+ event.preventDefault();
433
+ open(selectedIndex);
434
+ }
435
+ };
436
+ const handleListboxKeyDown = (event) => {
437
+ switch (event.key) {
438
+ case "ArrowDown":
439
+ event.preventDefault();
440
+ setActiveIndex((current) => (current + 1) % WRITE_CANVAS_FONT_SCHEMES.length);
441
+ break;
442
+ case "ArrowUp":
443
+ event.preventDefault();
444
+ setActiveIndex(
445
+ (current) => (current - 1 + WRITE_CANVAS_FONT_SCHEMES.length) % WRITE_CANVAS_FONT_SCHEMES.length
446
+ );
447
+ break;
448
+ case "Home":
449
+ event.preventDefault();
450
+ setActiveIndex(0);
451
+ break;
452
+ case "End":
453
+ event.preventDefault();
454
+ setActiveIndex(WRITE_CANVAS_FONT_SCHEMES.length - 1);
455
+ break;
456
+ case "Enter":
457
+ case " ":
458
+ event.preventDefault();
459
+ select(activeIndex);
460
+ break;
461
+ case "Escape":
462
+ event.preventDefault();
463
+ event.stopPropagation();
464
+ close(true);
465
+ break;
466
+ case "Tab":
467
+ close(false);
468
+ break;
469
+ }
470
+ };
471
+ const menuStyle = menuPosition ? {
472
+ left: `${menuPosition.left}px`,
473
+ top: menuPosition.top === void 0 ? void 0 : `${menuPosition.top}px`,
474
+ bottom: menuPosition.bottom === void 0 ? void 0 : `${menuPosition.bottom}px`,
475
+ width: `${menuPosition.width}px`,
476
+ maxHeight: `${menuPosition.maxHeight}px`
477
+ } : void 0;
478
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: "db-settings-select db-font-picker", children: [
479
+ /* @__PURE__ */ jsx("span", { className: "db-settings-select-header", children: /* @__PURE__ */ jsx("span", { id: labelId, children: "Font" }) }),
480
+ /* @__PURE__ */ jsxs(
481
+ "button",
482
+ {
483
+ ref: triggerRef,
484
+ type: "button",
485
+ className: "db-font-picker-trigger",
486
+ "aria-label": `Font, ${selectedScheme.label}`,
487
+ "aria-haspopup": "listbox",
488
+ "aria-expanded": isOpen,
489
+ "aria-controls": isOpen ? listboxId : void 0,
490
+ onClick: () => isOpen ? close(false) : open(selectedIndex),
491
+ onKeyDown: handleTriggerKeyDown,
492
+ children: [
493
+ /* @__PURE__ */ jsx(FontSchemePreview, { scheme: selectedScheme, compact: true }),
494
+ /* @__PURE__ */ jsx(
495
+ "span",
496
+ {
497
+ className: `db-font-picker-caret${isOpen ? " db-font-picker-caret--open" : ""}`,
498
+ "aria-hidden": "true"
499
+ }
500
+ )
501
+ ]
502
+ }
503
+ ),
504
+ isOpen && /* @__PURE__ */ jsxs(
505
+ "div",
506
+ {
507
+ ref: listboxRef,
508
+ id: listboxId,
509
+ className: "db-font-picker-menu",
510
+ style: menuStyle,
511
+ role: "listbox",
512
+ tabIndex: -1,
513
+ "aria-labelledby": labelId,
514
+ "aria-activedescendant": `${listboxId}-option-${activeIndex}`,
515
+ onKeyDown: handleListboxKeyDown,
516
+ children: [
517
+ WRITE_CANVAS_FONT_SCHEMES.filter((scheme) => scheme.group === "theme").map(
518
+ (scheme) => renderFontSchemeOption(scheme)
519
+ ),
520
+ FONT_SCHEME_GROUPS.map(({ group, label }) => /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": label, children: [
521
+ /* @__PURE__ */ jsx("div", { className: "db-font-picker-group", "aria-hidden": "true", children: label }),
522
+ WRITE_CANVAS_FONT_SCHEMES.filter((scheme) => scheme.group === group).map(
523
+ (scheme) => renderFontSchemeOption(scheme)
524
+ )
525
+ ] }, group))
526
+ ]
527
+ }
528
+ )
529
+ ] });
530
+ function renderFontSchemeOption(scheme) {
531
+ const index = WRITE_CANVAS_FONT_SCHEMES.indexOf(scheme);
532
+ const selected = scheme.id === value;
533
+ const active = index === activeIndex;
534
+ return /* @__PURE__ */ jsxs(
535
+ "div",
536
+ {
537
+ ref: (element) => {
538
+ optionRefs.current[index] = element;
539
+ },
540
+ id: `${listboxId}-option-${index}`,
541
+ className: `db-font-picker-option${selected ? " db-font-picker-option--selected" : ""}${active ? " db-font-picker-option--active" : ""}`,
542
+ role: "option",
543
+ "aria-selected": selected,
544
+ onMouseDown: (event) => event.preventDefault(),
545
+ onMouseMove: () => setActiveIndex(index),
546
+ onClick: () => select(index),
547
+ children: [
548
+ /* @__PURE__ */ jsx(FontSchemePreview, { scheme }),
549
+ /* @__PURE__ */ jsx("span", { className: "db-font-picker-check", "aria-hidden": "true", children: selected ? "\u2713" : "" })
550
+ ]
551
+ },
552
+ scheme.id
553
+ );
554
+ }
555
+ }
556
+ function FontSchemePreview({
557
+ scheme,
558
+ compact = false
559
+ }) {
560
+ const labels = scheme.label.split(/\s*\u00b7\s*/u);
561
+ const headingLabel = labels[0] ?? scheme.label;
562
+ const bodyLabel = labels[1] ?? (scheme.id === "theme" ? "Use the active theme" : "Headings & body");
563
+ return /* @__PURE__ */ jsxs("span", { className: `db-font-picker-preview${compact ? " db-font-picker-preview--compact" : ""}`, children: [
564
+ /* @__PURE__ */ jsx(
565
+ "span",
566
+ {
567
+ className: "db-font-picker-heading",
568
+ style: scheme.headerFont ? { fontFamily: scheme.headerFont } : void 0,
569
+ children: headingLabel
570
+ }
571
+ ),
572
+ /* @__PURE__ */ jsx(
573
+ "span",
574
+ {
575
+ className: "db-font-picker-body",
576
+ style: scheme.bodyFont ? { fontFamily: scheme.bodyFont } : void 0,
577
+ children: bodyLabel
578
+ }
579
+ )
580
+ ] });
581
+ }
582
+ var FONT_SCHEME_GROUPS = [
583
+ { group: "system", label: "System fonts" },
584
+ { group: "curated", label: "Curated pairings" }
585
+ ];
586
+ function formatLineSpacing(value) {
587
+ return `${value.toFixed(1).replace(/\.0$/, "")}\xD7`;
588
+ }
589
+
590
+ export {
591
+ WRITE_CANVAS_FONT_SCHEMES,
592
+ DEFAULT_WRITE_CANVAS_FONT_SCHEME,
593
+ resolveWriteCanvasFonts,
594
+ DEFAULT_WRITE_CANVAS_PREFERENCES,
595
+ loadWriteCanvasPreferences,
596
+ saveWriteCanvasPreferences,
597
+ DB_CHROME_COLORS,
598
+ loadThemePreference,
599
+ saveThemePreference,
600
+ loadAccentColor,
601
+ saveAccentColor,
602
+ SettingsDialog,
603
+ ThemeSettings,
604
+ AccentColorSettings,
605
+ WriteCanvasSettingsControls
606
+ };
package/dist/editor.d.ts CHANGED
@@ -1,3 +1,14 @@
1
+ import { ViewportPreset } from '@bendyline/squisq/schemas';
2
+
3
+ declare const PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
4
+ /** Map the host surface orientation to Squisq's matching preview canvas. */
5
+ declare function previewViewportPresetForOrientation(isPortrait: boolean): ViewportPreset;
6
+ /**
7
+ * Follow the host viewport orientation so slideshow/video previews make useful
8
+ * use of portrait screens. Squisq still gives document and manual selections
9
+ * precedence over this host-provided default.
10
+ */
11
+ declare function useResponsivePreviewViewportPreset(): ViewportPreset;
1
12
  /**
2
13
  * Placeholder prompts shown by DocBlocks when a Markdown document is empty.
3
14
  * Hosts pick one prompt for each mounted document generation and pass it to
@@ -8,4 +19,4 @@ type EmptyDocumentPrompt = (typeof EMPTY_DOCUMENT_PROMPTS)[number];
8
19
  /** Pick a prompt using the supplied random source, or `Math.random`. */
9
20
  declare function pickEmptyDocumentPrompt(random?: () => number): EmptyDocumentPrompt;
10
21
 
11
- export { EMPTY_DOCUMENT_PROMPTS, type EmptyDocumentPrompt, pickEmptyDocumentPrompt };
22
+ export { EMPTY_DOCUMENT_PROMPTS, type EmptyDocumentPrompt, PORTRAIT_FORM_FACTOR_QUERY, pickEmptyDocumentPrompt, previewViewportPresetForOrientation, useResponsivePreviewViewportPreset };
package/dist/editor.js CHANGED
@@ -1,8 +1,14 @@
1
1
  import {
2
2
  EMPTY_DOCUMENT_PROMPTS,
3
- pickEmptyDocumentPrompt
4
- } from "./chunk-BPGEBGAN.js";
3
+ PORTRAIT_FORM_FACTOR_QUERY,
4
+ pickEmptyDocumentPrompt,
5
+ previewViewportPresetForOrientation,
6
+ useResponsivePreviewViewportPreset
7
+ } from "./chunk-JDJVRDOP.js";
5
8
  export {
6
9
  EMPTY_DOCUMENT_PROMPTS,
7
- pickEmptyDocumentPrompt
10
+ PORTRAIT_FORM_FACTOR_QUERY,
11
+ pickEmptyDocumentPrompt,
12
+ previewViewportPresetForOrientation,
13
+ useResponsivePreviewViewportPreset
8
14
  };
@@ -81,8 +81,6 @@ interface ExportDialogProps {
81
81
  onExport: (options: ExportOptions) => void;
82
82
  /** Called whenever the currently selected options change. */
83
83
  onOptionsChange?: (options: ExportOptions) => void;
84
- /** Opens the richer animated-GIF export flow when the host supports ffmpeg.wasm. */
85
- onAnimatedGifExport?: () => void;
86
84
  /** Called when the dialog is dismissed. */
87
85
  onClose: () => void;
88
86
  }
@@ -95,7 +93,7 @@ interface ExportDestinationControl {
95
93
  /** A host-supplied validation error for the current destination value. */
96
94
  error?: string | null;
97
95
  }
98
- declare function ExportDialog({ initial, exporting, error, destination, onExport, onOptionsChange, onAnimatedGifExport, onClose, }: ExportDialogProps): react_jsx_runtime.JSX.Element;
96
+ declare function ExportDialog({ initial, exporting, error, destination, onExport, onOptionsChange, onClose, }: ExportDialogProps): react_jsx_runtime.JSX.Element;
99
97
 
100
98
  /** Keep a user-edited target name while switching the selected export format. */
101
99
  declare function updateExportTargetExtension(targetPath: string, suggestedFilename: string): string;
@@ -3,7 +3,7 @@ import {
3
3
  } from "../chunk-BI7NVU6T.js";
4
4
  import {
5
5
  ExportDialog
6
- } from "../chunk-3CZ27FOA.js";
6
+ } from "../chunk-MHTQORMH.js";
7
7
  import {
8
8
  buildExportFilename,
9
9
  runExport