@bendyline/docblocks-react 2.2.1 → 2.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.
@@ -1,3 +1,6 @@
1
+ import {
2
+ useMenuKeyboard
3
+ } from "./chunk-MRUK56JS.js";
1
4
  import {
2
5
  loadTransformStyleSummaries
3
6
  } from "./chunk-YBEYTVU2.js";
@@ -36,6 +39,8 @@ function loadRunExportModule() {
36
39
  runExportModulePromise ?? (runExportModulePromise = import("./run-export-Z5BVFLIR.js"));
37
40
  return runExportModulePromise;
38
41
  }
42
+ var ExportCancelledError = class extends Error {
43
+ };
39
44
  var SHARED_USE_MODES = Object.freeze({
40
45
  slideshow: "slideshow",
41
46
  video: "video",
@@ -69,10 +74,10 @@ function loadVideoExportModules() {
69
74
  return videoExportModulesPromise;
70
75
  }
71
76
  function exportErrorMessage(caught) {
72
- const detail = caught instanceof Error ? caught.message.trim() : "";
77
+ const detail = caught instanceof Error ? caught.message.replace(/^Error invoking remote method '[^']+': Error:\s*/, "").trim() : "";
73
78
  return detail ? `Export failed: ${detail}` : "Export failed. The document could not be exported.";
74
79
  }
75
- function quickLabel(opts, transformSummaries) {
80
+ function quickLabel(opts, transformSummaries, destinationPath) {
76
81
  const baseExt = FORMAT_EXTENSIONS[opts.format].toUpperCase().replace(".", "");
77
82
  const isRecursiveHtml = opts.format === "html" && opts.includeLinkedDocs;
78
83
  const ext = opts.format === "html" && (opts.htmlBundle === "zip" || isRecursiveHtml) ? "ZIP" : baseExt;
@@ -92,13 +97,16 @@ function quickLabel(opts, transformSummaries) {
92
97
  if (transform) parts.push(transform.name);
93
98
  }
94
99
  if (parts.length > 0) {
95
- return `Export ${ext} with ${parts.join(" + ")}`;
100
+ const label2 = `Export ${ext} with ${parts.join(" + ")}`;
101
+ return destinationPath ? `${label2} to ${destinationPath}` : label2;
96
102
  }
97
- return `Export ${ext}`;
103
+ const label = `Export ${ext}`;
104
+ return destinationPath ? `${label} to ${destinationPath}` : label;
98
105
  }
99
106
  function ExportToolbarControls({
100
107
  selectedFile,
101
108
  mediaContainer,
109
+ mediaProvider,
102
110
  saveBlob,
103
111
  destinationAdapter,
104
112
  trigger = "menu",
@@ -123,9 +131,14 @@ function ExportToolbarControls({
123
131
  const [exporting, setExporting] = useState(false);
124
132
  const [exportError, setExportError] = useState(null);
125
133
  const [destinationTarget, setDestinationTarget] = useState(null);
134
+ const [quickDestination, setQuickDestination] = useState(null);
135
+ const [quickDestinationPending, setQuickDestinationPending] = useState(false);
126
136
  const destinationRequestRef = useRef(0);
127
- const menuRef = useRef(null);
137
+ const menuContainerRef = useRef(null);
138
+ const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } = useMenuKeyboard(menuOpen, setMenuOpen);
128
139
  const lastOptions = loadLastExportOptions();
140
+ const quickDestinationKey = lastOptions ? JSON.stringify([selectedFile, lastOptions]) : null;
141
+ const quickDestinationTarget = quickDestination?.key === quickDestinationKey ? quickDestination.target : null;
129
142
  const docThemeId = useMemo(() => {
130
143
  const fm = markdownDoc?.frontmatter;
131
144
  if (!fm) return null;
@@ -142,12 +155,17 @@ function ExportToolbarControls({
142
155
  useEffect(() => {
143
156
  if (!menuOpen) return;
144
157
  const onPointerDown = (e) => {
145
- if (menuRef.current && !menuRef.current.contains(e.target)) {
146
- setMenuOpen(false);
158
+ if (menuContainerRef.current && !menuContainerRef.current.contains(e.target)) {
159
+ closeMenu(false);
147
160
  }
148
161
  };
149
162
  document.addEventListener("pointerdown", onPointerDown);
150
163
  return () => document.removeEventListener("pointerdown", onPointerDown);
164
+ }, [closeMenu, menuOpen]);
165
+ useEffect(() => {
166
+ if (menuOpen) return;
167
+ setQuickDestination(null);
168
+ setQuickDestinationPending(false);
151
169
  }, [menuOpen]);
152
170
  useEffect(() => {
153
171
  if (!menuOpen || lastOptions?.format !== "pptx" || !lastOptions.transformStyle || transformSummaries.length > 0) {
@@ -163,9 +181,33 @@ function ExportToolbarControls({
163
181
  cancelled = true;
164
182
  };
165
183
  }, [lastOptions?.format, lastOptions?.transformStyle, menuOpen, transformSummaries.length]);
184
+ useEffect(() => {
185
+ if (!menuOpen || !destinationAdapter || !quickDestinationKey) return;
186
+ const quickOptions = loadLastExportOptions();
187
+ if (!quickOptions) return;
188
+ let cancelled = false;
189
+ setQuickDestination(null);
190
+ setQuickDestinationPending(true);
191
+ void loadRunExportModule().then(
192
+ ({ buildExportFilename }) => destinationAdapter.resolveTarget(buildExportFilename(selectedFile, quickOptions))
193
+ ).then((target) => {
194
+ if (!cancelled) setQuickDestination({ key: quickDestinationKey, target });
195
+ }).catch(() => {
196
+ if (!cancelled) setQuickDestination(null);
197
+ }).finally(() => {
198
+ if (!cancelled) setQuickDestinationPending(false);
199
+ });
200
+ return () => {
201
+ cancelled = true;
202
+ };
203
+ }, [destinationAdapter, menuOpen, quickDestinationKey, selectedFile]);
166
204
  const handleToggleMenu = useCallback(() => {
205
+ if (!menuOpen && destinationAdapter) {
206
+ setQuickDestination(null);
207
+ setQuickDestinationPending(true);
208
+ }
167
209
  setMenuOpen((prev) => !prev);
168
- }, []);
210
+ }, [destinationAdapter, menuOpen]);
169
211
  const refreshDestination = useCallback(
170
212
  async (options) => {
171
213
  if (!destinationAdapter) return;
@@ -245,6 +287,7 @@ function ExportToolbarControls({
245
287
  const pickedTarget = await destinationAdapter.pickTarget(filename, destinationTarget);
246
288
  if (pickedTarget === null) return;
247
289
  setDestinationTarget(pickedTarget);
290
+ setQuickDestination(null);
248
291
  } catch {
249
292
  }
250
293
  },
@@ -254,7 +297,8 @@ function ExportToolbarControls({
254
297
  async (blob, filename, target) => {
255
298
  if (!destinationAdapter) return;
256
299
  const savedTarget = await destinationAdapter.saveBlob(blob, filename, target);
257
- if (savedTarget) setDestinationTarget(savedTarget);
300
+ if (!savedTarget) throw new ExportCancelledError();
301
+ setDestinationTarget(savedTarget);
258
302
  },
259
303
  [destinationAdapter]
260
304
  );
@@ -279,7 +323,7 @@ function ExportToolbarControls({
279
323
  );
280
324
  setDialogOpen(false);
281
325
  } catch (caught) {
282
- setExportError(exportErrorMessage(caught));
326
+ if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
283
327
  } finally {
284
328
  setExporting(false);
285
329
  }
@@ -303,10 +347,8 @@ function ExportToolbarControls({
303
347
  const { runExport } = await loadRunExportModule();
304
348
  let quickTarget = null;
305
349
  if (destinationAdapter) {
306
- const { buildExportFilename } = await loadRunExportModule();
307
- quickTarget = await destinationAdapter.resolveTarget(
308
- buildExportFilename(selectedFile, lastOptions)
309
- );
350
+ if (!quickDestinationTarget) return;
351
+ quickTarget = quickDestinationTarget;
310
352
  }
311
353
  await runExport(
312
354
  markdownSource,
@@ -316,7 +358,7 @@ function ExportToolbarControls({
316
358
  destinationAdapter ? async (blob, filename) => saveToDestination(blob, filename, quickTarget) : saveBlob
317
359
  );
318
360
  } catch (caught) {
319
- setExportError(exportErrorMessage(caught));
361
+ if (!(caught instanceof ExportCancelledError)) setExportError(exportErrorMessage(caught));
320
362
  } finally {
321
363
  setExporting(false);
322
364
  }
@@ -327,7 +369,8 @@ function ExportToolbarControls({
327
369
  mediaContainer,
328
370
  saveBlob,
329
371
  saveToDestination,
330
- selectedFile
372
+ selectedFile,
373
+ quickDestinationTarget
331
374
  ]);
332
375
  const handleDismissExportError = useCallback(() => {
333
376
  setExportError(null);
@@ -347,13 +390,15 @@ function ExportToolbarControls({
347
390
  "data-tooltip": exporting ? "Exporting..." : "Export document",
348
391
  children: /* @__PURE__ */ jsx(ExportGlyph, {})
349
392
  }
350
- ) : /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu", ref: menuRef, children: [
393
+ ) : /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu", ref: menuContainerRef, children: [
351
394
  /* @__PURE__ */ jsxs(
352
395
  "button",
353
396
  {
397
+ ref: triggerRef,
354
398
  type: "button",
355
399
  className: "db-toolbar-menu-trigger",
356
400
  onClick: handleToggleMenu,
401
+ onKeyDown: handleTriggerKeyDown,
357
402
  "aria-label": "Export and share",
358
403
  "aria-haspopup": "menu",
359
404
  "aria-expanded": menuOpen,
@@ -364,62 +409,85 @@ function ExportToolbarControls({
364
409
  ]
365
410
  }
366
411
  ),
367
- menuOpen && /* @__PURE__ */ jsxs("div", { className: "db-toolbar-menu-dropdown", role: "menu", children: [
368
- lastOptions && /* @__PURE__ */ jsx(
369
- "button",
370
- {
371
- type: "button",
372
- role: "menuitem",
373
- className: "db-toolbar-menu-item",
374
- onClick: handleQuickExport,
375
- disabled: exporting,
376
- children: quickLabel(lastOptions, transformSummaries)
377
- }
378
- ),
379
- /* @__PURE__ */ jsx(
380
- "button",
381
- {
382
- type: "button",
383
- role: "menuitem",
384
- className: "db-toolbar-menu-item",
385
- onClick: handleOpenDialog,
386
- children: "Export..."
387
- }
388
- ),
389
- /* @__PURE__ */ jsx(
390
- "button",
391
- {
392
- type: "button",
393
- role: "menuitem",
394
- className: "db-toolbar-menu-item",
395
- onClick: handleOpenShareDialog,
396
- children: "Share link with content embedded..."
397
- }
398
- ),
399
- showVideoExport && /* @__PURE__ */ jsxs(Fragment, { children: [
400
- /* @__PURE__ */ jsx("div", { className: "db-toolbar-menu-divider", role: "separator" }),
401
- /* @__PURE__ */ jsx(
402
- "button",
403
- {
404
- type: "button",
405
- role: "menuitem",
406
- className: "db-toolbar-menu-item",
407
- onClick: () => void handleOpenVideoModal("mp4"),
408
- children: "Export video..."
409
- }
410
- ),
411
- showAnimatedGifExport && /* @__PURE__ */ jsx(
412
- "button",
413
- {
414
- type: "button",
415
- role: "menuitem",
416
- className: "db-toolbar-menu-item",
417
- onClick: () => void handleOpenVideoModal("gif"),
418
- children: "Export animated gif..."
419
- }
420
- )
421
- ] })
422
- ] })
412
+ menuOpen && /* @__PURE__ */ jsxs(
413
+ "div",
414
+ {
415
+ ref: menuRef,
416
+ className: "db-toolbar-menu-dropdown db-export-toolbar-menu-dropdown",
417
+ role: "menu",
418
+ onKeyDown: handleMenuKeyDown,
419
+ children: [
420
+ lastOptions && /* @__PURE__ */ jsx(
421
+ "button",
422
+ {
423
+ type: "button",
424
+ role: "menuitem",
425
+ tabIndex: -1,
426
+ className: "db-toolbar-menu-item",
427
+ onClick: handleQuickExport,
428
+ disabled: exporting || Boolean(destinationAdapter) && !quickDestinationTarget,
429
+ title: quickDestinationTarget ? quickLabel(
430
+ lastOptions,
431
+ transformSummaries,
432
+ quickDestinationTarget.displayPath
433
+ ) : void 0,
434
+ children: quickDestinationTarget ? quickLabel(
435
+ lastOptions,
436
+ transformSummaries,
437
+ quickDestinationTarget.displayPath
438
+ ) : quickLabel(lastOptions, transformSummaries) + (destinationAdapter ? quickDestinationPending ? " (finding destination...)" : " (destination unavailable)" : "")
439
+ }
440
+ ),
441
+ /* @__PURE__ */ jsx(
442
+ "button",
443
+ {
444
+ type: "button",
445
+ role: "menuitem",
446
+ tabIndex: -1,
447
+ className: "db-toolbar-menu-item",
448
+ onClick: handleOpenDialog,
449
+ children: "Export..."
450
+ }
451
+ ),
452
+ /* @__PURE__ */ jsx(
453
+ "button",
454
+ {
455
+ type: "button",
456
+ role: "menuitem",
457
+ tabIndex: -1,
458
+ className: "db-toolbar-menu-item",
459
+ onClick: handleOpenShareDialog,
460
+ children: "Share link with content embedded..."
461
+ }
462
+ ),
463
+ showVideoExport && /* @__PURE__ */ jsxs(Fragment, { children: [
464
+ /* @__PURE__ */ jsx("div", { className: "db-toolbar-menu-divider", role: "separator" }),
465
+ /* @__PURE__ */ jsx(
466
+ "button",
467
+ {
468
+ type: "button",
469
+ role: "menuitem",
470
+ tabIndex: -1,
471
+ className: "db-toolbar-menu-item",
472
+ onClick: () => void handleOpenVideoModal("mp4"),
473
+ children: "Export video..."
474
+ }
475
+ ),
476
+ showAnimatedGifExport && /* @__PURE__ */ jsx(
477
+ "button",
478
+ {
479
+ type: "button",
480
+ role: "menuitem",
481
+ tabIndex: -1,
482
+ className: "db-toolbar-menu-item",
483
+ onClick: () => void handleOpenVideoModal("gif"),
484
+ children: "Export animated gif..."
485
+ }
486
+ )
487
+ ] })
488
+ ]
489
+ }
490
+ )
423
491
  ] }),
424
492
  dialogOpen && /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
425
493
  ExportDialog,
@@ -476,6 +544,7 @@ function ExportToolbarControls({
476
544
  {
477
545
  doc: videoDoc,
478
546
  playerScript: videoModules.playerScript,
547
+ ...mediaProvider ? { mediaProvider } : {},
479
548
  colorScheme,
480
549
  uiPalette: videoExportPalette,
481
550
  defaultConfig: {
@@ -1,10 +1,11 @@
1
- import {
2
- useMenuKeyboard
3
- } from "./chunk-TFMO7EVO.js";
1
+ import "./chunk-APC4KI4B.js";
4
2
  import {
5
3
  isFileDirty,
6
4
  useGitContext
7
5
  } from "./chunk-HFYPTTCH.js";
6
+ import {
7
+ useMenuKeyboard
8
+ } from "./chunk-MRUK56JS.js";
8
9
 
9
10
  // src/Git/GitToolbarControl.tsx
10
11
  import { useEffect, useRef, useState } from "react";
@@ -1,6 +1,4 @@
1
- import {
2
- useMenuKeyboard
3
- } from "./chunk-TFMO7EVO.js";
1
+ import "./chunk-APC4KI4B.js";
4
2
  import {
5
3
  BADGE_GLYPHS,
6
4
  BADGE_LABELS,
@@ -9,6 +7,9 @@ import {
9
7
  summarizeDirty,
10
8
  useGitContext
11
9
  } from "./chunk-HFYPTTCH.js";
10
+ import {
11
+ useMenuKeyboard
12
+ } from "./chunk-MRUK56JS.js";
12
13
  import {
13
14
  Dialog
14
15
  } from "./chunk-LG6HAWCK.js";
File without changes
@@ -1,4 +1,24 @@
1
1
  // src/editor.ts
2
+ import { useEffect, useState } from "react";
3
+ var PORTRAIT_FORM_FACTOR_QUERY = "(orientation: portrait)";
4
+ function previewViewportPresetForOrientation(isPortrait) {
5
+ return isPortrait ? "portrait" : "landscape";
6
+ }
7
+ function useResponsivePreviewViewportPreset() {
8
+ const [isPortrait, setIsPortrait] = useState(() => {
9
+ if (typeof globalThis.matchMedia !== "function") return false;
10
+ return globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY).matches;
11
+ });
12
+ useEffect(() => {
13
+ if (typeof globalThis.matchMedia !== "function") return;
14
+ const query = globalThis.matchMedia(PORTRAIT_FORM_FACTOR_QUERY);
15
+ const update = () => setIsPortrait(query.matches);
16
+ update();
17
+ query.addEventListener("change", update);
18
+ return () => query.removeEventListener("change", update);
19
+ }, []);
20
+ return previewViewportPresetForOrientation(isPortrait);
21
+ }
2
22
  var EMPTY_DOCUMENT_PROMPTS = [
3
23
  "Start typing your content, or drop images on top of me...",
4
24
  "Write anything -- paste markdown, drag in images, or just start typing...",
@@ -64,6 +84,9 @@ function pickEmptyDocumentPrompt(random = Math.random) {
64
84
  }
65
85
 
66
86
  export {
87
+ PORTRAIT_FORM_FACTOR_QUERY,
88
+ previewViewportPresetForOrientation,
89
+ useResponsivePreviewViewportPreset,
67
90
  EMPTY_DOCUMENT_PROMPTS,
68
91
  pickEmptyDocumentPrompt
69
92
  };
@@ -1,5 +1,9 @@
1
- // src/Git/useMenuKeyboard.ts
2
- import { useCallback, useEffect, useRef } from "react";
1
+ // src/components/useMenuKeyboard.ts
2
+ import {
3
+ useCallback,
4
+ useEffect,
5
+ useRef
6
+ } from "react";
3
7
  function useMenuKeyboard(open, setOpen) {
4
8
  const menuRef = useRef(null);
5
9
  const triggerRef = useRef(null);
@@ -22,7 +26,7 @@ function useMenuKeyboard(open, setOpen) {
22
26
  (returnFocus) => {
23
27
  pendingFocusRef.current = "none";
24
28
  setOpen(false);
25
- if (returnFocus) triggerRef.current?.focus();
29
+ if (returnFocus) triggerRef.current?.focus({ preventScroll: true });
26
30
  },
27
31
  [setOpen]
28
32
  );
@@ -32,6 +36,17 @@ function useMenuKeyboard(open, setOpen) {
32
36
  pendingFocusRef.current = "none";
33
37
  focusItem(where === "last" ? items().length - 1 : 0);
34
38
  }, [open, focusItem, items]);
39
+ useEffect(() => {
40
+ if (!open) return;
41
+ const handleDocumentKeyDown = (event) => {
42
+ if (event.key !== "Escape" || event.defaultPrevented) return;
43
+ event.preventDefault();
44
+ event.stopPropagation();
45
+ closeMenu(true);
46
+ };
47
+ document.addEventListener("keydown", handleDocumentKeyDown);
48
+ return () => document.removeEventListener("keydown", handleDocumentKeyDown);
49
+ }, [closeMenu, open]);
35
50
  const handleTriggerKeyDown = useCallback(
36
51
  (event) => {
37
52
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
@@ -48,12 +63,16 @@ function useMenuKeyboard(open, setOpen) {
48
63
  );
49
64
  const handleMenuKeyDown = useCallback(
50
65
  (event) => {
51
- if (event.key === "Escape" || event.key === "Tab") {
66
+ if (event.key === "Escape") {
52
67
  event.preventDefault();
53
68
  event.stopPropagation();
54
69
  closeMenu(true);
55
70
  return;
56
71
  }
72
+ if (event.key === "Tab") {
73
+ closeMenu(false);
74
+ return;
75
+ }
57
76
  const all = items();
58
77
  if (all.length === 0) return;
59
78
  const current = all.indexOf(document.activeElement);