@bendyline/docblocks-react 2.1.0 → 2.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.
Files changed (49) hide show
  1. package/README.md +12 -3
  2. package/dist/ExportDialog-VYATD5UC.js +9 -0
  3. package/dist/{ExportToolbarControls-GHAOJMH6.js → ExportToolbarControls-C46GMPKC.js} +70 -41
  4. package/dist/{GitToolbarControl-GNMRVKWQ.js → GitToolbarControl-FW3BZFXV.js} +2 -3
  5. package/dist/{GitUI-VHOBQY43.js → GitUI-GMH3PAYL.js} +3 -4
  6. package/dist/LazyEditorShell-LU4QFPDX.js +5 -0
  7. package/dist/{ShareDialog-63ENBEDZ.js → ShareDialog-PAMOQQFB.js} +0 -1
  8. package/dist/{chunk-UNYZQXWR.js → chunk-3CZ27FOA.js} +45 -10
  9. package/dist/{chunk-FEWQUQML.js → chunk-3QSZY74C.js} +1 -2
  10. package/dist/chunk-BI7NVU6T.js +31 -0
  11. package/dist/{chunk-VHDP4IPL.js → chunk-BPGEBGAN.js} +0 -1
  12. package/dist/{chunk-DD4VFJQI.js → chunk-EBWYGTN7.js} +2 -1
  13. package/dist/{chunk-ZI56BNB7.js → chunk-GHQ4X7KM.js} +27 -7
  14. package/dist/{chunk-5A4KEWKT.js → chunk-HFYPTTCH.js} +0 -1
  15. package/dist/{chunk-FMGTHSTX.js → chunk-LG6HAWCK.js} +0 -1
  16. package/dist/{chunk-57ZJJOC5.js → chunk-TFMO7EVO.js} +0 -1
  17. package/dist/{chunk-4XXSWN3T.js → chunk-YBEYTVU2.js} +0 -1
  18. package/dist/editor.js +1 -2
  19. package/dist/export/index.d.ts +18 -8
  20. package/dist/export/index.js +10 -7
  21. package/dist/index.d.ts +16 -7
  22. package/dist/index.js +359 -103
  23. package/dist/run-export-Z5BVFLIR.js +9 -0
  24. package/dist/settings/index.js +2 -3
  25. package/package.json +8 -8
  26. package/src/styles/docblocks.css +485 -9
  27. package/dist/ExportDialog-7LDIXJZ3.js +0 -10
  28. package/dist/ExportDialog-7LDIXJZ3.js.map +0 -1
  29. package/dist/ExportToolbarControls-GHAOJMH6.js.map +0 -1
  30. package/dist/GitToolbarControl-GNMRVKWQ.js.map +0 -1
  31. package/dist/GitUI-VHOBQY43.js.map +0 -1
  32. package/dist/LazyEditorShell-EF2BWRXU.js +0 -6
  33. package/dist/LazyEditorShell-EF2BWRXU.js.map +0 -1
  34. package/dist/ShareDialog-63ENBEDZ.js.map +0 -1
  35. package/dist/chunk-4XXSWN3T.js.map +0 -1
  36. package/dist/chunk-57ZJJOC5.js.map +0 -1
  37. package/dist/chunk-5A4KEWKT.js.map +0 -1
  38. package/dist/chunk-DD4VFJQI.js.map +0 -1
  39. package/dist/chunk-FEWQUQML.js.map +0 -1
  40. package/dist/chunk-FMGTHSTX.js.map +0 -1
  41. package/dist/chunk-UNYZQXWR.js.map +0 -1
  42. package/dist/chunk-VHDP4IPL.js.map +0 -1
  43. package/dist/chunk-ZI56BNB7.js.map +0 -1
  44. package/dist/editor.js.map +0 -1
  45. package/dist/export/index.js.map +0 -1
  46. package/dist/index.js.map +0 -1
  47. package/dist/run-export-ICTZITXC.js +0 -10
  48. package/dist/run-export-ICTZITXC.js.map +0 -1
  49. package/dist/settings/index.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Export/ExportToolbarControls.tsx"],"sourcesContent":["/**\n * ExportToolbarControls — toolbar-right slot content.\n *\n * Renders an export/share menu on the right side of the toolbar containing\n * document export, sharing, and video export actions.\n *\n * Must be rendered inside <EditorProvider> so useEditorContext() works.\n */\n\nimport {\n lazy,\n Suspense,\n useState,\n useCallback,\n useEffect,\n useRef,\n useMemo,\n type ComponentType,\n} from 'react';\nimport { useEditorContext, usePreviewSettings } from '@bendyline/squisq-editor-react';\nimport type { SharedDocumentMode } from '@bendyline/docblocks/share';\nimport { getThemeSummaries } from '@bendyline/squisq/schemas';\nimport { parseMarkdown } from '@bendyline/squisq/markdown';\nimport type { ContentContainer } from '@bendyline/squisq/storage';\nimport type { DisplayMode } from '@bendyline/squisq-react';\nimport type { VideoExportModalProps } from '@bendyline/squisq-video-react';\nimport type { ExportOptions } from './export-options.js';\nimport {\n DEFAULT_OPTIONS,\n FORMAT_EXTENSIONS,\n loadLastExportOptions,\n saveExportOptions,\n} from './export-options.js';\nimport type { ExportBlobSaver } from './run-export.js';\nimport { loadTransformStyleSummaries, type ExportSummaryOption } from './transform-summaries.js';\nimport { Dialog } from '../components/Dialog.js';\n\nconst ExportDialog = lazy(() =>\n import('./ExportDialog.js').then((module) => ({ default: module.ExportDialog })),\n);\nconst ShareDialog = lazy(() =>\n import('./ShareDialog.js').then((module) => ({ default: module.ShareDialog })),\n);\n\nlet runExportModulePromise: Promise<typeof import('./run-export.js')> | null = null;\n\nfunction loadRunExportModule(): Promise<typeof import('./run-export.js')> {\n runExportModulePromise ??= import('./run-export.js');\n return runExportModulePromise;\n}\n\nexport interface ExportToolbarControlsProps {\n /** Currently selected file path — used to derive the download filename. */\n selectedFile: string | null;\n /** Media container for resolving images during export. */\n mediaContainer?: ContentContainer | null;\n /** Override the default browser download behavior for host-provided save flows. */\n saveBlob?: ExportBlobSaver;\n /** Optional host adapter for displaying, picking, and saving to a native target path. */\n destinationAdapter?: ExportDestinationAdapter;\n /** Render the trigger as a direct Export button instead of the overflow menu. */\n trigger?: 'menu' | 'button';\n /** Whether to show video export in the overflow menu. */\n showVideoExport?: boolean;\n /** Resolved host color scheme for the video export dialog. */\n colorScheme?: 'light' | 'dark';\n /** Host palette overrides for the video export dialog and progress UI. */\n videoExportPalette?: VideoExportModalProps['uiPalette'];\n /** HTTP(S) DocBlocks page used as the base of generated shared links. */\n shareBaseUrl?: string;\n /** One-time Use mode supplied by an opened shared document link. */\n initialSharedMode?: SharedDocumentMode | null;\n}\n\n/** Host-specific operations behind the shared export destination control. */\nexport interface ExportDestinationTarget {\n grantId: string | null;\n displayPath: string;\n}\n\nexport interface ExportDestinationAdapter {\n resolveTarget: (filename: string) => Promise<ExportDestinationTarget>;\n pickTarget: (\n filename: string,\n currentTarget?: ExportDestinationTarget | null,\n ) => Promise<ExportDestinationTarget | null>;\n saveBlob: (\n blob: Blob,\n filename: string,\n target?: ExportDestinationTarget | null,\n ) => Promise<ExportDestinationTarget | null>;\n hint?: string;\n}\n\ntype ParsedMarkdown = ReturnType<typeof parseMarkdown>;\n\ninterface VideoExportModules {\n Modal: ComponentType<VideoExportModalProps>;\n markdownToDoc: (doc: ParsedMarkdown) => VideoExportModalProps['doc'];\n playerScript: string;\n}\n\nconst SHARED_USE_MODES: Readonly<Record<SharedDocumentMode, DisplayMode>> = Object.freeze({\n slideshow: 'slideshow',\n video: 'video',\n page: 'linear',\n document: 'page',\n narrate: 'narrate',\n});\n\nfunction SharedModeInitializer({ mode }: { mode: SharedDocumentMode }) {\n const appliedRef = useRef(false);\n const { setActiveView } = useEditorContext();\n const { setSelectedDisplayMode } = usePreviewSettings();\n\n useEffect(() => {\n if (appliedRef.current) return;\n appliedRef.current = true;\n setSelectedDisplayMode(SHARED_USE_MODES[mode]);\n setActiveView('preview');\n }, [mode, setActiveView, setSelectedDisplayMode]);\n\n return null;\n}\n\nlet videoExportModulesPromise: Promise<VideoExportModules> | null = null;\n\nfunction loadVideoExportModules(): Promise<VideoExportModules> {\n videoExportModulesPromise ??= Promise.all([\n import('@bendyline/squisq/doc'),\n import('@bendyline/squisq-video-react'),\n import('@bendyline/squisq-react/standalone-source'),\n ]).then(([docModule, videoModule, playerModule]) => ({\n Modal: videoModule.VideoExportModal,\n markdownToDoc: docModule.markdownToDoc,\n playerScript: playerModule.PLAYER_BUNDLE,\n }));\n return videoExportModulesPromise;\n}\n\n/**\n * Turn whatever `runExport` rejected with into something a user can act on.\n * The pipeline throws real `Error`s (unreadable image, converter failure,\n * destination write refused) whose messages are already user-facing.\n */\nfunction exportErrorMessage(caught: unknown): string {\n const detail = caught instanceof Error ? caught.message.trim() : '';\n return detail ? `Export failed: ${detail}` : 'Export failed. The document could not be exported.';\n}\n\n/** Build the quick-export label from saved options. */\nfunction quickLabel(opts: ExportOptions, transformSummaries: ExportSummaryOption[]): string {\n const baseExt = FORMAT_EXTENSIONS[opts.format].toUpperCase().replace('.', '');\n // Recursive HTML always emits a ZIP (multi-doc tree), regardless of\n // the saved `htmlBundle` value. Applies to both plain and rendered\n // styles since both now ship recursive bundle helpers.\n const isRecursiveHtml = opts.format === 'html' && opts.includeLinkedDocs;\n const ext =\n opts.format === 'html' && (opts.htmlBundle === 'zip' || isRecursiveHtml) ? 'ZIP' : baseExt;\n const parts: string[] = [];\n\n if (opts.format === 'html' && opts.htmlStyle === 'rendered') {\n parts.push('rendered');\n }\n if (isRecursiveHtml) {\n parts.push('linked docs');\n }\n // Plain HTML also applies themes now (squisq's `markdownDocToPlainHtml`\n // emits theme-driven CSS), so include the theme name in the quick-\n // export label for every HTML style — not just rendered.\n if (opts.themeId !== 'standard') {\n const theme = getThemeSummaries().find((t) => t.id === opts.themeId);\n if (theme) parts.push(theme.name);\n }\n if (opts.format === 'pptx' && opts.transformStyle) {\n const transform = transformSummaries.find((t) => t.id === opts.transformStyle);\n if (transform) parts.push(transform.name);\n }\n\n if (parts.length > 0) {\n return `Export ${ext} with ${parts.join(' + ')}`;\n }\n return `Export ${ext}`;\n}\n\nexport function ExportToolbarControls({\n selectedFile,\n mediaContainer,\n saveBlob,\n destinationAdapter,\n trigger = 'menu',\n showVideoExport = true,\n colorScheme = 'light',\n videoExportPalette,\n shareBaseUrl,\n initialSharedMode = null,\n}: ExportToolbarControlsProps) {\n const { markdownSource, markdownDoc } = useEditorContext();\n const [menuOpen, setMenuOpen] = useState(false);\n const [dialogOpen, setDialogOpen] = useState(false);\n const [shareDialogOpen, setShareDialogOpen] = useState(false);\n const [videoModalOpen, setVideoModalOpen] = useState(false);\n const [videoLoading, setVideoLoading] = useState(false);\n const [videoLoadError, setVideoLoadError] = useState<string | null>(null);\n const [videoModules, setVideoModules] = useState<VideoExportModules | null>(null);\n const [videoDoc, setVideoDoc] = useState<VideoExportModalProps['doc'] | null>(null);\n const [transformSummaries, setTransformSummaries] = useState<ExportSummaryOption[]>([]);\n const [exporting, setExporting] = useState(false);\n const [exportError, setExportError] = useState<string | null>(null);\n const [destinationTarget, setDestinationTarget] = useState<ExportDestinationTarget | null>(null);\n const destinationRequestRef = useRef(0);\n const menuRef = useRef<HTMLDivElement>(null);\n\n const lastOptions = loadLastExportOptions();\n\n /** Doc's currently-set squisq theme, pulled from the markdown\n * frontmatter. The Document Settings dialog and Theme Customizer\n * write the theme under `squisq-theme` (canonical) or `theme`\n * (legacy); some older docs persist it as `themeId`. We honor all\n * three so an author who set their theme anywhere upstream sees it\n * pre-selected in the export dialog. */\n const docThemeId = useMemo(() => {\n const fm = markdownDoc?.frontmatter as Record<string, unknown> | undefined;\n if (!fm) return null;\n const candidates = [fm['squisq-theme'], fm['theme'], fm['themeId']];\n for (const v of candidates) {\n if (typeof v === 'string' && v.trim()) return v.trim();\n }\n return null;\n }, [markdownDoc]);\n\n /** Options used to populate the export dialog. Layered: built-in\n * defaults → user's last-chosen export options (if any) → doc's\n * current frontmatter theme (wins). The frontmatter override\n * guarantees that \"set theme in the editor, then export\" pre-selects\n * the right theme instead of resurrecting whatever the user picked\n * for some unrelated previous doc. */\n const dialogInitial = useMemo(() => {\n const base = lastOptions ?? DEFAULT_OPTIONS;\n return docThemeId ? { ...base, themeId: docThemeId } : base;\n }, [lastOptions, docThemeId]);\n\n // Close menu on outside click\n useEffect(() => {\n if (!menuOpen) return;\n const onPointerDown = (e: PointerEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setMenuOpen(false);\n }\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [menuOpen]);\n\n useEffect(() => {\n if (\n !menuOpen ||\n lastOptions?.format !== 'pptx' ||\n !lastOptions.transformStyle ||\n transformSummaries.length > 0\n ) {\n return;\n }\n\n let cancelled = false;\n loadTransformStyleSummaries()\n .then((nextSummaries) => {\n if (!cancelled) setTransformSummaries(nextSummaries);\n })\n .catch(() => {\n if (!cancelled) setTransformSummaries([]);\n });\n\n return () => {\n cancelled = true;\n };\n }, [lastOptions?.format, lastOptions?.transformStyle, menuOpen, transformSummaries.length]);\n\n const handleToggleMenu = useCallback(() => {\n setMenuOpen((prev) => !prev);\n }, []);\n\n const refreshDestination = useCallback(\n async (options: ExportOptions) => {\n if (!destinationAdapter) return;\n\n const requestId = destinationRequestRef.current + 1;\n destinationRequestRef.current = requestId;\n\n try {\n const { buildExportFilename } = await loadRunExportModule();\n const filename = buildExportFilename(selectedFile, options);\n const target = await destinationAdapter.resolveTarget(filename);\n if (destinationRequestRef.current === requestId) setDestinationTarget(target);\n } catch {\n if (destinationRequestRef.current === requestId) setDestinationTarget(null);\n }\n },\n [destinationAdapter, selectedFile],\n );\n\n const handleOpenDialog = useCallback(() => {\n setMenuOpen(false);\n setExportError(null);\n setDialogOpen(true);\n void refreshDestination(dialogInitial);\n }, [dialogInitial, refreshDestination]);\n\n const handleCloseDialog = useCallback(() => {\n setDialogOpen(false);\n setExportError(null);\n }, []);\n\n const handleOpenShareDialog = useCallback(() => {\n setMenuOpen(false);\n setShareDialogOpen(true);\n }, []);\n\n const handleCloseShareDialog = useCallback(() => {\n setShareDialogOpen(false);\n }, []);\n\n const handleOpenVideoModal = useCallback(async () => {\n setMenuOpen(false);\n setVideoModalOpen(true);\n setVideoLoading(true);\n setVideoLoadError(null);\n\n try {\n const modules = videoModules ?? (await loadVideoExportModules());\n setVideoModules(modules);\n setVideoDoc(modules.markdownToDoc(parseMarkdown(markdownSource)));\n } catch {\n setVideoLoadError('Video export could not be loaded.');\n } finally {\n setVideoLoading(false);\n }\n }, [markdownSource, videoModules]);\n\n const handleCloseVideoModal = useCallback(() => {\n setVideoModalOpen(false);\n setVideoDoc(null);\n setVideoLoadError(null);\n }, []);\n\n const handleOptionsChange = useCallback(\n (options: ExportOptions) => {\n void refreshDestination(options);\n },\n [refreshDestination],\n );\n\n const handlePickDestination = useCallback(\n async (options: ExportOptions) => {\n if (!destinationAdapter) return;\n try {\n const { buildExportFilename } = await loadRunExportModule();\n const filename = buildExportFilename(selectedFile, options);\n const pickedTarget = await destinationAdapter.pickTarget(filename, destinationTarget);\n if (pickedTarget === null) return;\n setDestinationTarget(pickedTarget);\n } catch {\n // Native hosts surface picker failures through their own UI channel.\n }\n },\n [destinationAdapter, destinationTarget, selectedFile],\n );\n\n const saveToDestination = useCallback(\n async (blob: Blob, filename: string, target: ExportDestinationTarget | null) => {\n if (!destinationAdapter) return;\n const savedTarget = await destinationAdapter.saveBlob(blob, filename, target);\n if (savedTarget) setDestinationTarget(savedTarget);\n },\n [destinationAdapter],\n );\n\n const handleDestinationSaveBlob = useCallback(\n async (blob: Blob, filename: string) => {\n await saveToDestination(blob, filename, destinationTarget);\n },\n [destinationTarget, saveToDestination],\n );\n\n const handleExport = useCallback(\n async (opts: ExportOptions) => {\n setExporting(true);\n setExportError(null);\n try {\n const { runExport } = await loadRunExportModule();\n await runExport(\n markdownSource,\n selectedFile,\n opts,\n mediaContainer,\n destinationAdapter ? handleDestinationSaveBlob : saveBlob,\n );\n // Only a completed export dismisses the dialog. Closing in a\n // `finally` used to make a failure look exactly like a success.\n setDialogOpen(false);\n } catch (caught: unknown) {\n setExportError(exportErrorMessage(caught));\n } finally {\n setExporting(false);\n }\n },\n [\n markdownSource,\n selectedFile,\n mediaContainer,\n destinationAdapter,\n handleDestinationSaveBlob,\n saveBlob,\n ],\n );\n\n const handleQuickExport = useCallback(async () => {\n if (!lastOptions) return;\n setMenuOpen(false);\n setExporting(true);\n setExportError(null);\n try {\n saveExportOptions(lastOptions);\n const { runExport } = await loadRunExportModule();\n let quickTarget: ExportDestinationTarget | null = null;\n if (destinationAdapter) {\n const { buildExportFilename } = await loadRunExportModule();\n quickTarget = await destinationAdapter.resolveTarget(\n buildExportFilename(selectedFile, lastOptions),\n );\n }\n await runExport(\n markdownSource,\n selectedFile,\n lastOptions,\n mediaContainer,\n destinationAdapter\n ? async (blob, filename) => saveToDestination(blob, filename, quickTarget)\n : saveBlob,\n );\n } catch (caught: unknown) {\n // No dialog is open on this path, so the failure gets its own —\n // same shape as the video-export load failure below.\n setExportError(exportErrorMessage(caught));\n } finally {\n setExporting(false);\n }\n }, [\n destinationAdapter,\n lastOptions,\n markdownSource,\n mediaContainer,\n saveBlob,\n saveToDestination,\n selectedFile,\n ]);\n\n const handleDismissExportError = useCallback(() => {\n setExportError(null);\n }, []);\n\n const LoadedVideoExportModal = videoModules?.Modal;\n\n return (\n <>\n {initialSharedMode && <SharedModeInitializer mode={initialSharedMode} />}\n {trigger === 'button' ? (\n <button\n type=\"button\"\n className=\"squisq-toolbar-button db-toolbar-export-trigger\"\n onClick={handleOpenDialog}\n disabled={exporting}\n aria-label=\"Export document\"\n data-tooltip={exporting ? 'Exporting...' : 'Export document'}\n >\n <ExportGlyph />\n </button>\n ) : (\n <div className=\"db-toolbar-menu\" ref={menuRef}>\n <button\n type=\"button\"\n className=\"db-toolbar-menu-trigger\"\n onClick={handleToggleMenu}\n aria-label=\"Export and share\"\n aria-haspopup=\"menu\"\n aria-expanded={menuOpen}\n title=\"Export and share\"\n >\n <ShareGlyph />\n </button>\n\n {menuOpen && (\n <div className=\"db-toolbar-menu-dropdown\" role=\"menu\">\n {lastOptions && (\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"db-toolbar-menu-item\"\n onClick={handleQuickExport}\n disabled={exporting}\n >\n {quickLabel(lastOptions, transformSummaries)}\n </button>\n )}\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"db-toolbar-menu-item\"\n onClick={handleOpenDialog}\n >\n Export...\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"db-toolbar-menu-item\"\n onClick={handleOpenShareDialog}\n >\n Share link with content embedded...\n </button>\n {showVideoExport && (\n <>\n <div className=\"db-toolbar-menu-divider\" role=\"separator\" />\n <button\n type=\"button\"\n role=\"menuitem\"\n className=\"db-toolbar-menu-item\"\n onClick={handleOpenVideoModal}\n >\n Export Video...\n </button>\n </>\n )}\n </div>\n )}\n </div>\n )}\n\n {dialogOpen && (\n <Suspense fallback={null}>\n <ExportDialog\n initial={dialogInitial}\n exporting={exporting}\n error={exportError}\n destination={\n destinationAdapter\n ? {\n value: destinationTarget?.displayPath ?? '',\n onPick: handlePickDestination,\n hint: destinationAdapter.hint,\n }\n : undefined\n }\n onExport={handleExport}\n onOptionsChange={destinationAdapter ? handleOptionsChange : undefined}\n onClose={handleCloseDialog}\n />\n </Suspense>\n )}\n\n {/* Quick export fails with no dialog open — give the failure its own,\n mirroring the video-export load-failure surface below. */}\n {exportError && !dialogOpen && (\n <Dialog\n title=\"Export Document\"\n onClose={handleDismissExportError}\n footer={\n <button\n type=\"button\"\n className=\"db-export-btn db-export-btn--secondary\"\n onClick={handleDismissExportError}\n >\n Close\n </button>\n }\n >\n <p className=\"db-export-error\" role=\"alert\">\n {exportError}\n </p>\n </Dialog>\n )}\n\n {shareDialogOpen && (\n <Suspense fallback={null}>\n <ShareDialog\n markdown={markdownSource}\n selectedFile={selectedFile}\n baseUrl={\n shareBaseUrl ??\n (typeof window === 'undefined'\n ? 'https://bendyline.github.io/docblocks/'\n : window.location.href)\n }\n onClose={handleCloseShareDialog}\n />\n </Suspense>\n )}\n\n {videoModalOpen && (videoLoading || videoLoadError) && (\n <div className=\"db-dialog-overlay\">\n <div className=\"db-dialog\">\n <div className=\"db-dialog-header\">\n <h2 className=\"db-dialog-title\">Export Video</h2>\n <button\n className=\"db-dialog-close\"\n onClick={handleCloseVideoModal}\n aria-label=\"Close\"\n >\n &times;\n </button>\n </div>\n <div className=\"db-dialog-body\">\n <p className=\"db-export-hint\">{videoLoadError ?? 'Loading...'}</p>\n </div>\n </div>\n </div>\n )}\n\n {videoModalOpen &&\n videoModules &&\n LoadedVideoExportModal &&\n videoDoc &&\n !videoLoading &&\n !videoLoadError && (\n <LoadedVideoExportModal\n doc={videoDoc}\n playerScript={videoModules.playerScript}\n colorScheme={colorScheme}\n uiPalette={videoExportPalette}\n onClose={handleCloseVideoModal}\n />\n )}\n </>\n );\n}\n\nfunction ExportGlyph() {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M4.5 1.75h4.25L12 5v7.25a1.5 1.5 0 0 1-1.5 1.5h-6A1.5 1.5 0 0 1 3 12.25v-9a1.5 1.5 0 0 1 1.5-1.5z\" />\n <path d=\"M8.75 1.75V5h3.25\" />\n <path d=\"M6 8.75h4.5\" />\n <path d=\"M8.75 7 10.5 8.75 8.75 10.5\" />\n </svg>\n );\n}\n\nfunction ShareGlyph() {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M8 10.5V1.75\" />\n <path d=\"M5.25 4.5 8 1.75l2.75 2.75\" />\n <path d=\"M4.25 6.75h-1a1.5 1.5 0 0 0-1.5 1.5v4.5a1.5 1.5 0 0 0 1.5 1.5h9.5a1.5 1.5 0 0 0 1.5-1.5v-4.5a1.5 1.5 0 0 0-1.5-1.5h-1\" />\n </svg>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Git/GitToolbarControl.tsx"],"sourcesContent":["/**\n * GitToolbarControl — per-file git actions in the editor toolbar.\n *\n * A single branch-glyph button (styled like the export trigger) opening a\n * small dropdown: view working-tree changes, per-file history, and \"Open\n * on <host>\" when a web remote is known. A dot overlay marks the selected\n * file as modified. Null-renders without a git context, a repository, or a\n * selected file. Deliberately has no editor dependency.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useGitContext } from './GitContext.js';\nimport { isFileDirty } from './git-status.js';\nimport { useMenuKeyboard } from './useMenuKeyboard.js';\n\nexport interface GitToolbarControlProps {\n /** Currently selected file path (slash-prefixed), or null. */\n selectedFile: string | null;\n}\n\nexport function GitToolbarControl({ selectedFile }: GitToolbarControlProps) {\n const git = useGitContext();\n const [menuOpen, setMenuOpen] = useState(false);\n /** Wraps trigger + menu — the outside-click hit test, not the menu itself. */\n const rootRef = useRef<HTMLDivElement>(null);\n const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } =\n useMenuKeyboard(menuOpen, setMenuOpen);\n\n // Close on outside click (same pointerdown pattern as ExportToolbarControls).\n useEffect(() => {\n if (!menuOpen) return;\n const onPointerDown = (e: PointerEvent) => {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) {\n setMenuOpen(false);\n }\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [menuOpen]);\n\n if (!git || !git.repo || !selectedFile) return null;\n\n const dirty = isFileDirty(git.status, selectedFile);\n const label = dirty ? 'Git actions (file modified)' : 'Git actions';\n\n // Each of these replaces the menu with a dialog, so focus belongs there,\n // not back on the trigger.\n const openDiff = () => {\n closeMenu(false);\n git.openDialog({ kind: 'diff', path: selectedFile });\n };\n const openHistory = () => {\n closeMenu(false);\n git.openDialog({ kind: 'history', path: selectedFile });\n };\n const openOnRemote = () => {\n closeMenu(false);\n git.openOnRemote(selectedFile);\n };\n\n return (\n <div className=\"db-toolbar-menu\" ref={rootRef}>\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"squisq-toolbar-button db-git-toolbar-trigger\"\n onClick={() => setMenuOpen((prev) => !prev)}\n onKeyDown={handleTriggerKeyDown}\n aria-label={label}\n aria-haspopup=\"menu\"\n aria-expanded={menuOpen}\n data-tooltip={label}\n >\n <BranchGlyph />\n {dirty && <span className=\"db-git-toolbar-dot\" aria-hidden=\"true\" />}\n </button>\n\n {menuOpen && (\n <div\n ref={menuRef}\n className=\"db-toolbar-menu-dropdown\"\n role=\"menu\"\n aria-label={label}\n onKeyDown={handleMenuKeyDown}\n >\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-toolbar-menu-item\"\n disabled={!dirty}\n onClick={openDiff}\n >\n View changes\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-toolbar-menu-item\"\n onClick={openHistory}\n >\n File history…\n </button>\n {git.remoteWeb && (\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-toolbar-menu-item\"\n onClick={openOnRemote}\n >\n Open on {git.remoteWeb.host}\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction BranchGlyph() {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"4.5\" cy=\"12\" r=\"2\" />\n <circle cx=\"11.5\" cy=\"4\" r=\"2\" />\n <path d=\"M4.5 10V7.5A2.5 2.5 0 0 1 7 5h2.5\" />\n </svg>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Git/GitStatusBar.tsx","../src/Git/GitCommitDialog.tsx","../src/Git/commit-selection.ts","../src/Git/GitBranchesDialog.tsx","../src/Git/GitHistoryDialog.tsx","../src/Git/GitDiffDialog.tsx","../src/Git/GitDiffView.tsx","../src/Git/GitCloneDialog.tsx","../src/Git/git-clone-utils.ts","../src/Git/GitAuthGuidanceDialog.tsx","../src/Git/GitConflictsDialog.tsx","../src/Git/GitUI.tsx"],"sourcesContent":["/**\n * GitStatusBar — slim sidebar row showing branch, ahead/behind, and dirty\n * count, with the repo actions menu (commit, sync, branches, history,\n * remote conveniences). Renders nothing unless the active workspace is a\n * git repository.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useGitContext } from './GitContext.js';\nimport { formatAheadBehind, summarizeDirty } from './git-status.js';\nimport { useMenuKeyboard } from './useMenuKeyboard.js';\n\nexport function GitStatusBar() {\n const git = useGitContext();\n const [menuOpen, setMenuOpen] = useState(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu } =\n useMenuKeyboard(menuOpen, setMenuOpen);\n\n useEffect(() => {\n if (!menuOpen) return;\n const onPointerDown = (e: PointerEvent) => {\n if (rootRef.current && !rootRef.current.contains(e.target as Node)) {\n setMenuOpen(false);\n }\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => document.removeEventListener('pointerdown', onPointerDown);\n }, [menuOpen]);\n\n const runItem = useCallback(\n (action: () => void) => {\n // The item is going away; don't yank focus back to the trigger.\n closeMenu(false);\n action();\n },\n [closeMenu],\n );\n\n if (!git || !git.repo || !git.status) return null;\n\n const { status } = git;\n const branchLabel = status.detached ? 'detached HEAD' : (status.branch ?? 'no branch');\n const aheadBehind = formatAheadBehind(status);\n const dirty = summarizeDirty(status);\n const merging = status.operation === 'merge';\n const chipLabelParts = [`Git: branch ${branchLabel}`];\n if (status.ahead > 0) chipLabelParts.push(`${status.ahead} ahead`);\n if (status.behind > 0) chipLabelParts.push(`${status.behind} behind`);\n if (dirty) chipLabelParts.push(dirty);\n if (merging) chipLabelParts.push('merge in progress');\n\n const canCreatePr =\n git.capabilities?.ghAvailable === true && git.remoteWeb?.host === 'github.com';\n\n return (\n <div ref={rootRef} className=\"db-git-statusbar\">\n <button\n ref={triggerRef}\n type=\"button\"\n className=\"db-git-statusbar-chip\"\n onClick={() => setMenuOpen((open) => !open)}\n onKeyDown={handleTriggerKeyDown}\n aria-haspopup=\"menu\"\n aria-expanded={menuOpen}\n aria-label={chipLabelParts.join(', ')}\n title={chipLabelParts.join(', ')}\n >\n <span className=\"db-git-branch-glyph\" aria-hidden=\"true\">\n {'⎇'}\n </span>\n <span className=\"db-git-statusbar-branch\">{branchLabel}</span>\n {aheadBehind && <span className=\"db-git-statusbar-counts\">{aheadBehind}</span>}\n {dirty && <span className=\"db-git-statusbar-dirty\">{dirty}</span>}\n {merging && <span className=\"db-git-statusbar-merge\">merging</span>}\n {git.busy && (\n <span className=\"db-git-statusbar-busy\" aria-hidden=\"true\">\n {'…'}\n </span>\n )}\n </button>\n\n {menuOpen && (\n <div\n ref={menuRef}\n className=\"db-git-menu\"\n role=\"menu\"\n aria-label=\"Git actions\"\n onKeyDown={handleMenuKeyDown}\n >\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n disabled={status.changes.length === 0 && !merging}\n onClick={() => runItem(() => git.openDialog({ kind: 'commit' }))}\n >\n {merging ? 'Commit merge…' : 'Commit…'}\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n disabled={git.busy !== null}\n onClick={() => runItem(() => void git.push())}\n >\n {status.upstream === null && !status.unborn ? 'Publish branch' : 'Push'}\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n disabled={git.busy !== null}\n onClick={() => runItem(() => void git.pull())}\n >\n Pull\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n disabled={git.busy !== null}\n onClick={() => runItem(() => void git.fetchRemote())}\n >\n Fetch\n </button>\n <div className=\"db-git-menu-divider\" role=\"separator\" />\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n onClick={() => runItem(() => git.openDialog({ kind: 'branches' }))}\n >\n Branches…\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n onClick={() => runItem(() => git.openDialog({ kind: 'history' }))}\n >\n Commit history…\n </button>\n {(git.remoteWeb || canCreatePr) && (\n <div className=\"db-git-menu-divider\" role=\"separator\" />\n )}\n {git.remoteWeb && (\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n onClick={() => runItem(() => git.openOnRemote())}\n >\n Open on {git.remoteWeb.host}\n </button>\n )}\n {canCreatePr && (\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n className=\"db-git-menu-item\"\n disabled={git.busy !== null}\n onClick={() => runItem(() => void git.createPullRequest())}\n >\n Create pull request\n </button>\n )}\n </div>\n )}\n </div>\n );\n}\n","/**\n * GitCommitDialog — message + per-file selection for committing changes.\n *\n * All changed files are included by default. While git has an operation in\n * progress (merge, rebase, cherry-pick, revert) the host must record the\n * whole index — git forbids a pathspec commit mid-operation — so every row\n * is locked and the dialog says so, rather than offering checkboxes whose\n * state the commit would ignore. Selection logic lives in\n * commit-selection.ts so it stays unit-testable.\n */\n\nimport { useMemo, useRef, useState } from 'react';\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\nimport { BADGE_LABELS, badgeKindFor } from './git-status.js';\nimport {\n allSelected,\n canCommit,\n commitsWholeIndex,\n isIncluded,\n isLocked,\n isMergeCommit,\n NO_OVERRIDES,\n resolveSelection,\n selectedPaths,\n setAllOverrides,\n toggleOverride,\n type CommitOverrides,\n} from './commit-selection.js';\n\n/** Human-readable name for the in-progress operation, for the hint line. */\nconst OPERATION_LABELS: Record<string, string> = {\n merge: 'merge',\n rebase: 'rebase',\n 'cherry-pick': 'cherry-pick',\n revert: 'revert',\n bisect: 'bisect',\n};\n\nexport function GitCommitDialog({ onClose }: { onClose: () => void }) {\n const git = useGitContext();\n const status = git?.status ?? null;\n const merging = isMergeCommit(status);\n /** Mid-operation: the commit records the index whole, so nothing unticks. */\n const wholeIndex = commitsWholeIndex(status);\n const changes = status?.changes;\n\n const [message, setMessage] = useState('');\n /** Only the user's explicit choices persist across status refreshes. */\n const [overrides, setOverrides] = useState<CommitOverrides>(NO_OVERRIDES);\n const messageRef = useRef<HTMLTextAreaElement>(null);\n\n // Resolved every render against the live change list: a file that appears\n // while the dialog is open (autosave triggers a status refresh every\n // 1.5s) is rendered checked *and* committed, rather than rendered checked\n // and silently dropped from a mount-time snapshot.\n const selection = useMemo(\n () => resolveSelection(changes ?? [], wholeIndex, overrides),\n [changes, wholeIndex, overrides],\n );\n\n if (!git || !status) return null;\n\n const allOn = allSelected(selection);\n const commitDisabled = !canCommit(message, selection) || git.busy !== null;\n\n const handleCommit = async () => {\n const ok = await git.commit(message, selectedPaths(selection));\n if (ok) onClose();\n };\n\n return (\n <Dialog\n title={merging ? 'Commit merge' : 'Commit changes'}\n onClose={onClose}\n size=\"wide\"\n closeOnBackdrop={false}\n initialFocusRef={messageRef}\n footer={\n <>\n <button type=\"button\" className=\"db-git-secondary-btn\" onClick={onClose}>\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"db-git-primary-btn\"\n disabled={commitDisabled}\n onClick={() => void handleCommit()}\n >\n Commit\n </button>\n </>\n }\n >\n {wholeIndex && (\n <p className=\"db-settings-hint\">\n {merging\n ? 'You’re completing a merge. Committing will conclude it.'\n : `A ${OPERATION_LABELS[status.operation ?? ''] ?? 'git operation'} is in progress.`}{' '}\n Git records every change below in this commit — files cannot be left out until the{' '}\n {merging ? 'merge' : 'operation'} is finished.\n </p>\n )}\n <textarea\n ref={messageRef}\n className=\"db-git-commit-message\"\n value={message}\n onChange={(e) => setMessage(e.target.value)}\n placeholder=\"Commit message\"\n aria-label=\"Commit message\"\n rows={3}\n />\n {/* Nothing is selectable mid-operation, so the bulk toggle would be a\n no-op control. Hide it rather than render a dead button. */}\n {!wholeIndex && (\n <div className=\"db-git-form-row\">\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n onClick={() => setOverrides(setAllOverrides(selection, !allOn))}\n >\n {allOn ? 'Select none' : 'Select all'}\n </button>\n </div>\n )}\n <ul className=\"db-git-file-list\">\n {status.changes.map((change) => {\n const kind = badgeKindFor(change);\n const locked = isLocked(selection, change.path);\n const checked = isIncluded(selection, change.path);\n return (\n <li key={change.path} className=\"db-git-file-row\">\n <label>\n <input\n type=\"checkbox\"\n checked={checked}\n disabled={locked}\n title={\n locked\n ? `Included automatically while the ${merging ? 'merge' : 'operation'} is in progress`\n : undefined\n }\n onChange={() => setOverrides((o) => toggleOverride(o, selection, change.path))}\n />\n <span>{change.path.replace(/^\\/+/, '')}</span>\n </label>\n <span className=\"db-git-file-kind\">{BADGE_LABELS[kind]}</span>\n </li>\n );\n })}\n </ul>\n {git.lastResult?.tone === 'error' && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {git.lastResult.message}\n </p>\n )}\n </Dialog>\n );\n}\n","/**\n * Commit-dialog selection state — pure helpers, unit-testable.\n *\n * All changed files are included by default, *except* while git has an\n * operation in progress (merge, rebase, cherry-pick, revert, bisect). Git\n * forbids a pathspec commit mid-operation, so the host drops the pathspec\n * and records the whole index — see `commit()` in\n * desktop/main/git/commands.ts. Every changed file is therefore committed\n * whatever the user ticks, and the whole list is locked rather than\n * rendering checkboxes that silently do nothing.\n *\n * The selection is *derived* from the live change list on every render\n * rather than snapshotted when the dialog mounts. Git status refreshes\n * (autosave pushes one every 1.5s) can add or drop files while the dialog\n * is open, and a snapshot would leave those rows rendering a checkbox that\n * no longer corresponds to what gets committed. The user's explicit\n * include/exclude choices are the only thing carried across refreshes —\n * see `CommitOverrides`.\n */\n\nimport type { GitFileChange, GitStatus } from '@bendyline/docblocks/host';\n\n/**\n * A changed file with no explicit user choice is committed. Stated once,\n * here, so the checkbox, `selectedPaths`, and the toggle all agree instead\n * of each re-deriving \"included\" from a key being absent.\n */\nexport const INCLUDED_BY_DEFAULT = true;\n\n/**\n * The user's explicit include/exclude choices, keyed by normalised path.\n * A path absent from this map has never been touched by the user and\n * therefore takes `INCLUDED_BY_DEFAULT`. Overrides may name paths that are\n * not currently changed (a file toggled off, saved back to its committed\n * contents, then changed again keeps the user's choice); `resolveSelection`\n * ignores those until they reappear in the change list.\n */\nexport type CommitOverrides = ReadonlyMap<string, boolean>;\n\nexport interface CommitSelection {\n /**\n * Normalised path → include in the commit. Covers exactly the paths in\n * the change list it was resolved from, so what the dialog renders and\n * what `selectedPaths` returns cannot drift apart.\n */\n included: Map<string, boolean>;\n /** Paths the user cannot exclude (every change while mid-operation). */\n locked: Set<string>;\n}\n\nfunction normalise(path: string): string {\n return path.startsWith('/') ? path : `/${path}`;\n}\n\nexport const NO_OVERRIDES: CommitOverrides = new Map<string, boolean>();\n\n/**\n * Resolve the live change list plus the user's choices into the selection\n * the dialog renders and commits. Pure — call it on every render.\n *\n * `wholeIndex` (see `commitsWholeIndex`) locks *every* changed file, not\n * just the conflicted ones: the host cannot honour a pathspec while an\n * operation is in progress, so an unticked file would be committed anyway.\n * Locking the whole list is what makes the checkboxes honest.\n */\nexport function resolveSelection(\n changes: readonly GitFileChange[],\n wholeIndex: boolean,\n overrides: CommitOverrides = NO_OVERRIDES,\n): CommitSelection {\n const included = new Map<string, boolean>();\n const locked = new Set<string>();\n for (const change of changes) {\n const path = normalise(change.path);\n if (wholeIndex) locked.add(path);\n included.set(path, wholeIndex ? true : (overrides.get(path) ?? INCLUDED_BY_DEFAULT));\n }\n return { included, locked };\n}\n\n/** Whether `path` (raw or normalised) is committed under this selection. */\nexport function isIncluded(selection: CommitSelection, path: string): boolean {\n return selection.included.get(normalise(path)) ?? INCLUDED_BY_DEFAULT;\n}\n\n/** Whether `path` (raw or normalised) is force-included and cannot be toggled. */\nexport function isLocked(selection: CommitSelection, path: string): boolean {\n return selection.locked.has(normalise(path));\n}\n\n/**\n * Flip one path, returning the next overrides map. Reads the *current*\n * resolved state rather than the raw override, so the first click on a\n * never-touched (default-included) file excludes it — one click, matching\n * what its checkbox shows.\n */\nexport function toggleOverride(\n overrides: CommitOverrides,\n selection: CommitSelection,\n path: string,\n): CommitOverrides {\n const key = normalise(path);\n if (isLocked(selection, key)) return overrides;\n const next = new Map(overrides);\n next.set(key, !isIncluded(selection, key));\n return next;\n}\n\n/**\n * Explicitly set every currently-changed file. Locked paths are left to\n * `resolveSelection`, which force-includes them regardless.\n */\nexport function setAllOverrides(selection: CommitSelection, value: boolean): CommitOverrides {\n const next = new Map<string, boolean>();\n for (const key of selection.included.keys()) {\n if (!selection.locked.has(key)) next.set(key, value);\n }\n return next;\n}\n\n/** True when every currently-changed file is included. */\nexport function allSelected(selection: CommitSelection): boolean {\n return [...selection.included.values()].every(Boolean);\n}\n\nexport function selectedPaths(selection: CommitSelection): string[] {\n return [...selection.included.entries()].filter(([, on]) => on).map(([path]) => path);\n}\n\nexport function canCommit(message: string, selection: CommitSelection): boolean {\n return message.trim().length > 0 && selectedPaths(selection).length > 0;\n}\n\n/** True when committing concludes an in-progress merge. */\nexport function isMergeCommit(status: GitStatus | null): boolean {\n return status?.operation === 'merge';\n}\n\n/**\n * True when the commit will record the entire index regardless of the\n * paths passed to it — i.e. git has an operation in progress. Mirrors the\n * host's `if (rels && operation === null)` pathspec guard, so it is *any*\n * operation and not merge alone: a cherry-pick or revert drops the\n * pathspec exactly the same way.\n */\nexport function commitsWholeIndex(status: GitStatus | null): boolean {\n return status !== null && status.operation !== null;\n}\n","/**\n * GitBranchesDialog — switch between local branches or create a new one.\n *\n * Loads the branch list once from the host git API on mount. Remote\n * branches are display-only in v1 (a collapsed disclosure); switching and\n * creating go through the GitValue actions so errors (e.g. uncommitted\n * changes blocking a checkout) surface inline via lastResult.\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type { GitBranchInfo } from '@bendyline/docblocks/host';\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\n\nexport function GitBranchesDialog({\n createFocus,\n onClose,\n}: {\n createFocus?: boolean;\n onClose: () => void;\n}) {\n const git = useGitContext();\n const gitApi = git?.gitApi ?? null;\n const repositoryId = git?.repositoryId ?? null;\n\n const [branches, setBranches] = useState<GitBranchInfo[] | null>(null);\n const [loadError, setLoadError] = useState<string | null>(null);\n const [newName, setNewName] = useState('');\n const createInputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (!gitApi || !repositoryId) return;\n let cancelled = false;\n void gitApi.listBranches(repositoryId).then(\n (result) => {\n if (cancelled) return;\n if (result.ok) setBranches(result.value);\n else setLoadError(result.error.message || 'Could not list branches');\n },\n // A rejected call (the host went away) left the dialog on\n // \"Loading branches…\" forever. Fail visibly instead.\n (error: unknown) => {\n if (cancelled) return;\n setLoadError(error instanceof Error ? error.message : 'Could not list branches');\n },\n );\n return () => {\n cancelled = true;\n };\n }, [gitApi, repositoryId]);\n\n if (!git) return null;\n\n const locals = branches?.filter((b) => b.kind === 'local') ?? [];\n const remotes = branches?.filter((b) => b.kind === 'remote') ?? [];\n const canCreate = newName.trim().length > 0 && git.busy === null;\n\n const handleSwitch = async (name: string) => {\n const ok = await git.switchBranch(name);\n if (ok) onClose();\n };\n\n const handleCreate = async () => {\n if (!canCreate) return;\n const ok = await git.createBranch(newName.trim());\n if (ok) onClose();\n };\n\n return (\n <Dialog\n title=\"Branches\"\n onClose={onClose}\n initialFocusRef={createFocus ? createInputRef : undefined}\n >\n {branches === null && loadError === null && <p className=\"db-git-hint\">Loading branches…</p>}\n {loadError !== null && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {loadError}\n </p>\n )}\n {branches !== null && (\n <div className=\"db-git-branch-list\">\n {locals.map((branch) =>\n branch.current ? (\n <span\n key={branch.name}\n className=\"db-git-branch-row db-git-branch-row--current\"\n aria-current=\"true\"\n >\n {branch.name}\n </span>\n ) : (\n <button\n key={branch.name}\n type=\"button\"\n className=\"db-git-branch-row\"\n disabled={git.busy !== null}\n onClick={() => void handleSwitch(branch.name)}\n >\n {branch.name}\n </button>\n ),\n )}\n </div>\n )}\n {git.lastResult?.tone === 'error' && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {git.lastResult.message}\n </p>\n )}\n <hr />\n <div className=\"db-git-form-row\">\n <label className=\"db-git-form-label\" htmlFor=\"db-git-new-branch-name\">\n New branch\n </label>\n <input\n id=\"db-git-new-branch-name\"\n ref={createInputRef}\n className=\"db-git-form-input\"\n type=\"text\"\n value={newName}\n onChange={(e) => setNewName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') void handleCreate();\n }}\n placeholder=\"feature/my-branch\"\n spellCheck={false}\n />\n <button\n type=\"button\"\n className=\"db-git-primary-btn\"\n disabled={!canCreate}\n onClick={() => void handleCreate()}\n >\n Create\n </button>\n </div>\n {remotes.length > 0 && (\n <details>\n <summary>Remote branches ({remotes.length})</summary>\n <div className=\"db-git-branch-list\">\n {remotes.map((branch) => (\n <span key={branch.name} className=\"db-git-branch-row\">\n {branch.name}\n </span>\n ))}\n </div>\n </details>\n )}\n </Dialog>\n );\n}\n","/**\n * GitHistoryDialog — paged commit log for the repository or a single file.\n *\n * Pages `git log` 50 commits at a time; a row expands (one at a time) to\n * show the full message body and the files changed in that commit — each\n * file opens the commit-vs-parent diff dialog (which replaces this one;\n * acceptable for v1).\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport type { GitFileChange, GitLogEntry } from '@bendyline/docblocks/host';\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\nimport { BADGE_GLYPHS, BADGE_LABELS, badgeKindFor } from './git-status.js';\n\nexport interface GitHistoryDialogProps {\n /** Slash-prefixed workspace-relative path; absent → whole-repo history. */\n path?: string;\n onClose: () => void;\n}\n\nconst PAGE_SIZE = 50;\n\ntype CommitDetail = { body: string; files: GitFileChange[] } | { error: string };\n\n/** Message body beyond the subject line, or '' when it adds nothing. */\nfunction extraBody(body: string, subject: string): string {\n const trimmedBody = body.trim();\n const trimmedSubject = subject.trim();\n if (!trimmedBody || trimmedBody === trimmedSubject) return '';\n if (trimmedBody.startsWith(trimmedSubject)) {\n return trimmedBody.slice(trimmedSubject.length).trim();\n }\n return trimmedBody;\n}\n\nfunction formatDate(iso: string): string {\n const date = new Date(iso);\n return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;\n}\n\nexport function GitHistoryDialog({ path, onClose }: GitHistoryDialogProps) {\n const git = useGitContext();\n const gitApi = git?.gitApi ?? null;\n const repositoryId = git?.repositoryId ?? null;\n\n const [entries, setEntries] = useState<GitLogEntry[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [hasMore, setHasMore] = useState(false);\n const [expandedSha, setExpandedSha] = useState<string | null>(null);\n const [details, setDetails] = useState<ReadonlyMap<string, CommitDetail>>(new Map());\n /**\n * Bumped whenever the repository or file being logged changes. A slow\n * page from the previous repo/file resolving afterwards would otherwise\n * append the wrong repository's commits to the new list.\n */\n const generationRef = useRef(0);\n\n const loadPage = useCallback(\n async (skip: number) => {\n if (!gitApi || !repositoryId) return;\n const generation = generationRef.current;\n const stale = () => generation !== generationRef.current;\n setLoading(true);\n setError(null);\n try {\n const result = await gitApi.log(repositoryId, { path, maxCount: PAGE_SIZE, skip });\n if (stale()) return;\n if (!result.ok) {\n setError(result.error.message);\n return;\n }\n setEntries((prev) => (skip === 0 ? result.value : [...prev, ...result.value]));\n setHasMore(result.value.length === PAGE_SIZE);\n } catch (caught: unknown) {\n // A rejected log left the dialog claiming \"No commits yet\".\n if (stale()) return;\n setError(caught instanceof Error ? caught.message : 'Could not load history');\n } finally {\n if (!stale()) setLoading(false);\n }\n },\n [gitApi, repositoryId, path],\n );\n\n useEffect(() => {\n generationRef.current += 1;\n setEntries([]);\n setExpandedSha(null);\n setDetails(new Map());\n setHasMore(false);\n setLoading(false);\n void loadPage(0);\n }, [loadPage]);\n\n const handleRowClick = (sha: string) => {\n if (expandedSha === sha) {\n setExpandedSha(null);\n return;\n }\n setExpandedSha(sha);\n if (!gitApi || !repositoryId || details.has(sha)) return;\n const generation = generationRef.current;\n const record = (detail: CommitDetail) => {\n // The row renders \"Loading…\" until a detail lands, so a rejection\n // that recorded nothing left it spinning for the dialog's lifetime.\n if (generation !== generationRef.current) return;\n setDetails((prev) => {\n const next = new Map(prev);\n next.set(sha, detail);\n return next;\n });\n };\n void gitApi.commitFiles(repositoryId, sha).then(\n (result) => record(result.ok ? result.value : { error: result.error.message }),\n (error: unknown) =>\n record({\n error: error instanceof Error ? error.message : 'Could not load this commit',\n }),\n );\n };\n\n if (!git) return null;\n\n const baseName = path ? path.replace(/^\\/+/, '').split('/').pop() : null;\n const title = baseName ? `History — ${baseName}` : 'Commit history';\n\n return (\n <Dialog title={title} size=\"wide\" onClose={onClose}>\n {error && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {error}\n </p>\n )}\n {!error && entries.length === 0 && loading && <p className=\"db-git-hint\">Loading history…</p>}\n {!error && entries.length === 0 && !loading && <p className=\"db-git-hint\">No commits yet.</p>}\n {entries.length > 0 && (\n <ul className=\"db-git-history-list\">\n {entries.map((entry) => {\n const expanded = expandedSha === entry.sha;\n const detail = details.get(entry.sha);\n const meta = [\n entry.authorName,\n entry.sha.slice(0, 7),\n formatDate(entry.authorDate),\n entry.refs.join(', '),\n ]\n .filter(Boolean)\n .join(' · ');\n const body =\n detail && !('error' in detail) ? extraBody(detail.body, entry.subject) : '';\n return (\n <li key={entry.sha} className=\"db-git-history-item\">\n <button\n type=\"button\"\n className=\"db-git-history-toggle\"\n aria-expanded={expanded}\n onClick={() => handleRowClick(entry.sha)}\n >\n <span className=\"db-git-history-subject\">{entry.subject}</span>\n <span className=\"db-git-history-meta\">{meta}</span>\n </button>\n {expanded && !detail && <p className=\"db-git-hint\">Loading…</p>}\n {expanded && detail && 'error' in detail && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {detail.error}\n </p>\n )}\n {expanded && detail && !('error' in detail) && (\n <>\n {body && <pre className=\"db-git-history-body\">{body}</pre>}\n <ul className=\"db-git-history-files\">\n {detail.files.map((file) => {\n const kind = badgeKindFor(file);\n return (\n <li key={file.path}>\n <button\n type=\"button\"\n className=\"db-git-history-file\"\n onClick={() =>\n git.openDialog({\n kind: 'diff',\n path: file.path,\n revision: { sha: entry.sha },\n })\n }\n >\n <span\n className=\"db-git-file-kind\"\n aria-label={BADGE_LABELS[kind]}\n title={BADGE_LABELS[kind]}\n >\n {BADGE_GLYPHS[kind]}\n </span>\n {file.path.replace(/^\\/+/, '')}\n </button>\n </li>\n );\n })}\n </ul>\n </>\n )}\n </li>\n );\n })}\n </ul>\n )}\n {hasMore && (\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n disabled={loading}\n onClick={() => void loadPage(entries.length)}\n >\n Show older commits\n </button>\n )}\n </Dialog>\n );\n}\n","/**\n * GitDiffDialog — full-size modal showing a file diff.\n *\n * Without a revision: working tree vs HEAD (HEAD side via the git host API,\n * working side via the workspace FileSystemProvider). With a revision: that\n * commit vs its parent, both sides via readFileAtRevision. Absent sides\n * render as empty text with an \"(absent)\" label; binary files short-circuit\n * to a hint since there is no text diff to show.\n */\n\nimport { useEffect, useState } from 'react';\nimport { getFileSystemProviderV2, parseWorkspacePath } from '@bendyline/docblocks/filesystem';\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\nimport { GitDiffView } from './GitDiffView.js';\n\nexport interface GitDiffDialogProps {\n /** Slash-prefixed workspace-relative path. */\n path: string;\n /** Absent → working tree vs HEAD; present → that commit vs its parent. */\n revision?: { sha: string };\n onClose: () => void;\n}\n\ntype DiffLoadState =\n | { phase: 'loading' }\n | { phase: 'error'; message: string }\n | { phase: 'binary' }\n | {\n phase: 'ready';\n original: string;\n modified: string;\n labels: { original: string; modified: string };\n };\n\nexport function GitDiffDialog({ path, revision, onClose }: GitDiffDialogProps) {\n const git = useGitContext();\n const gitApi = git?.gitApi ?? null;\n const provider = git?.provider ?? null;\n const repositoryId = git?.repositoryId ?? null;\n const sha = revision?.sha ?? null;\n\n const [state, setState] = useState<DiffLoadState>({ phase: 'loading' });\n\n useEffect(() => {\n if (!gitApi || !repositoryId) {\n setState({ phase: 'error', message: 'Git is not available for this workspace.' });\n return;\n }\n let cancelled = false;\n setState({ phase: 'loading' });\n\n const load = async (): Promise<DiffLoadState> => {\n if (sha) {\n const short = sha.slice(0, 7);\n const [parent, at] = await Promise.all([\n gitApi.readFileAtRevision(repositoryId, path, { kind: 'parent-of', sha }),\n gitApi.readFileAtRevision(repositoryId, path, { kind: 'commit', sha }),\n ]);\n if (!parent.ok) return { phase: 'error', message: parent.error.message };\n if (!at.ok) return { phase: 'error', message: at.error.message };\n if (\n (parent.value.content === null && parent.value.binary) ||\n (at.value.content === null && at.value.binary)\n ) {\n return { phase: 'binary' };\n }\n return {\n phase: 'ready',\n original: parent.value.content ?? '',\n modified: at.value.content ?? '',\n labels: {\n original: parent.value.content === null ? `${short}^ (absent)` : `${short}^`,\n modified: at.value.content === null ? `${short} (absent)` : short,\n },\n };\n }\n\n if (!provider) {\n return { phase: 'error', message: 'Workspace files are not available.' };\n }\n const providerV2 = getFileSystemProviderV2(provider);\n const [head, working] = await Promise.all([\n gitApi.readFileAtRevision(repositoryId, path, { kind: 'head' }),\n providerV2\n ? providerV2\n .readFile(parseWorkspacePath(path))\n .then((file) => (file ? new TextDecoder().decode(file.data) : null))\n : provider.readFile(path),\n ]);\n if (!head.ok) return { phase: 'error', message: head.error.message };\n if (head.value.content === null && head.value.binary) return { phase: 'binary' };\n return {\n phase: 'ready',\n original: head.value.content ?? '',\n modified: working ?? '',\n labels: {\n original: head.value.content === null ? 'HEAD (absent)' : 'HEAD',\n modified: working === null ? 'Working tree (absent)' : 'Working tree',\n },\n };\n };\n\n load()\n .catch(\n (err: unknown): DiffLoadState => ({\n phase: 'error',\n message: err instanceof Error ? err.message : 'Failed to load the diff',\n }),\n )\n .then((next) => {\n if (!cancelled) setState(next);\n });\n return () => {\n cancelled = true;\n };\n }, [gitApi, provider, repositoryId, path, sha]);\n\n if (!git) return null;\n\n const fullPath = path.replace(/^\\/+/, '');\n const baseName = fullPath.split('/').pop() || fullPath;\n\n return (\n <Dialog title={baseName} size=\"full\" onClose={onClose}>\n <p className=\"db-settings-hint\">{fullPath}</p>\n {state.phase === 'loading' && <p className=\"db-git-hint\">Loading changes…</p>}\n {state.phase === 'error' && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {state.message}\n </p>\n )}\n {state.phase === 'binary' && (\n <p className=\"db-git-hint\">Binary file — no text diff available.</p>\n )}\n {state.phase === 'ready' && (\n <GitDiffView\n original={state.original}\n modified={state.modified}\n fileName={baseName}\n theme={git.theme}\n labels={state.labels}\n />\n )}\n </Dialog>\n );\n}\n","/**\n * GitDiffView — read-only Monaco side-by-side diff.\n *\n * Monaco is never statically imported: squisq's useMonacoLoader() lazily\n * imports the editor bundle once per page and hands back the namespace.\n * The diff editor and both text models are created imperatively into a\n * host div and disposed on unmount (the classic Monaco leak). Content and\n * theme changes update the existing editor without recreating it.\n */\n\nimport { useEffect, useRef } from 'react';\nimport {\n detectLanguageFromFileName,\n useMonacoLoader,\n type UseMonacoLoaderResult,\n} from '@bendyline/squisq-editor-react';\n\ntype MonacoModule = NonNullable<UseMonacoLoaderResult['monaco']>;\ntype DiffEditorInstance = ReturnType<MonacoModule['editor']['createDiffEditor']>;\ntype TextModel = ReturnType<MonacoModule['editor']['createModel']>;\n\nexport interface GitDiffViewProps {\n original: string;\n modified: string;\n /** Used for language detection (markdown for .md, plain text otherwise). */\n fileName: string;\n theme: 'light' | 'dark';\n /** Optional column headers rendered above the two panes. */\n labels?: { original: string; modified: string };\n}\n\nfunction languageFor(fileName: string): string {\n const detected = detectLanguageFromFileName(fileName);\n if (detected) return detected;\n return fileName.toLowerCase().endsWith('.md') ? 'markdown' : 'plaintext';\n}\n\nexport function GitDiffView({ original, modified, fileName, theme, labels }: GitDiffViewProps) {\n const { monaco, ready } = useMonacoLoader();\n const hostRef = useRef<HTMLDivElement | null>(null);\n const editorRef = useRef<DiffEditorInstance | null>(null);\n const modelsRef = useRef<{ original: TextModel; modified: TextModel } | null>(null);\n\n const language = languageFor(fileName);\n\n // Create the editor once monaco settles; dispose editor AND models on unmount.\n useEffect(() => {\n const host = hostRef.current;\n if (!monaco || !host) return;\n const originalModel = monaco.editor.createModel('', 'plaintext');\n const modifiedModel = monaco.editor.createModel('', 'plaintext');\n const editor = monaco.editor.createDiffEditor(host, {\n readOnly: true,\n renderSideBySide: true,\n automaticLayout: true,\n minimap: { enabled: false },\n wordWrap: 'on',\n scrollBeyondLastLine: false,\n renderOverviewRuler: false,\n });\n editor.setModel({ original: originalModel, modified: modifiedModel });\n editorRef.current = editor;\n modelsRef.current = { original: originalModel, modified: modifiedModel };\n return () => {\n editorRef.current = null;\n modelsRef.current = null;\n editor.dispose();\n originalModel.dispose();\n modifiedModel.dispose();\n };\n }, [monaco]);\n\n // Sync content + language into the existing models — never recreate the editor.\n useEffect(() => {\n if (!monaco) return;\n const models = modelsRef.current;\n if (!models) return;\n if (models.original.getValue() !== original) models.original.setValue(original);\n if (models.modified.getValue() !== modified) models.modified.setValue(modified);\n monaco.editor.setModelLanguage(models.original, language);\n monaco.editor.setModelLanguage(models.modified, language);\n }, [monaco, original, modified, language]);\n\n // React to theme prop changes.\n useEffect(() => {\n if (!monaco) return;\n monaco.editor.setTheme(theme === 'dark' ? 'vs-dark' : 'vs');\n }, [monaco, theme]);\n\n if (!ready) {\n return <div className=\"db-git-diff-loading\">Loading diff…</div>;\n }\n\n return (\n <>\n {labels && (\n <div className=\"db-git-diff-labels\">\n <span>{labels.original}</span>\n <span>{labels.modified}</span>\n </div>\n )}\n <div ref={hostRef} className=\"db-git-diff-host\" />\n </>\n );\n}\n","/**\n * GitCloneDialog — clone a remote repository into a new local workspace.\n *\n * The renderer only collects the URL; the main process shows the native\n * destination picker, streams progress back, and registers the clone as a\n * workspace. An auth failure swaps the body to the shared credential\n * guidance copy (no credentials are ever collected here).\n */\n\nimport { useEffect, useRef, useState } from 'react';\nimport type {\n ElectronWorkspaceInfo,\n GitCloneHandle,\n GitCloneProgress,\n} from '@bendyline/docblocks/host';\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\nimport { deriveCloneFolderName, isLikelyGitUrl } from './git-clone-utils.js';\nimport { GitCredentialGuidance } from './GitAuthGuidanceDialog.js';\n\nexport function GitCloneDialog({\n onClose,\n onCloned,\n}: {\n onClose: () => void;\n onCloned: (info: ElectronWorkspaceInfo) => void;\n}) {\n const git = useGitContext();\n const gitApi = git?.gitApi ?? null;\n\n const [url, setUrl] = useState('');\n const [cloning, setCloning] = useState(false);\n const [progress, setProgress] = useState<GitCloneProgress | null>(null);\n const [error, setError] = useState<string | null>(null);\n /** Non-null → show embedded credential guidance instead of the form. */\n const [authDetail, setAuthDetail] = useState<{ detail?: string } | null>(null);\n const handleRef = useRef<GitCloneHandle | null>(null);\n const urlInputRef = useRef<HTMLInputElement>(null);\n\n // Closing the dialog mid-clone abandons it — kill git and clean up.\n useEffect(\n () => () => {\n handleRef.current?.cancel();\n },\n [],\n );\n\n if (!gitApi || typeof gitApi.clone !== 'function') return null;\n\n const canClone = isLikelyGitUrl(url) && !cloning;\n\n const handleClone = async () => {\n if (!canClone) return;\n setCloning(true);\n setError(null);\n setProgress(null);\n const handle = gitApi.clone(url.trim(), (p) => setProgress(p));\n handleRef.current = handle;\n const result = await handle.result;\n handleRef.current = null;\n setCloning(false);\n setProgress(null);\n if (!result.ok) {\n switch (result.error.code) {\n case 'invalid-url':\n setError('That doesn’t look like a valid repository URL.');\n break;\n case 'destination-exists':\n setError('A folder with that name already exists there. Pick a different location.');\n break;\n case 'auth-failed':\n setAuthDetail({ detail: result.error.message });\n break;\n case 'cancelled':\n break; // user cancelled — just return to the form\n default:\n setError(result.error.message);\n }\n return;\n }\n if (result.value === null) return; // user cancelled the destination picker\n onCloned(result.value);\n };\n\n const progressPercent = progress?.percent ?? null;\n const progressText =\n progress !== null\n ? `${progress.phase}…${progressPercent !== null ? ` ${Math.round(progressPercent)}%` : ''}`\n : 'Starting clone…';\n\n if (authDetail !== null) {\n return (\n <Dialog\n title=\"Clone repository\"\n onClose={onClose}\n footer={\n <>\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n onClick={() => setAuthDetail(null)}\n >\n Back\n </button>\n <button type=\"button\" className=\"db-git-primary-btn\" onClick={onClose}>\n Close\n </button>\n </>\n }\n >\n <p className=\"db-settings-hint\">Git could not authenticate with that remote.</p>\n <GitCredentialGuidance detail={authDetail.detail} />\n </Dialog>\n );\n }\n\n return (\n <Dialog\n title=\"Clone repository\"\n onClose={onClose}\n closeOnBackdrop={!cloning}\n initialFocusRef={urlInputRef}\n footer={\n <>\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n onClick={onClose}\n disabled={cloning}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"db-git-primary-btn\"\n disabled={!canClone}\n onClick={() => void handleClone()}\n >\n Clone\n </button>\n </>\n }\n >\n <div className=\"db-git-form-row\">\n <label className=\"db-git-form-label\" htmlFor=\"db-git-clone-url\">\n Repository URL\n </label>\n <input\n id=\"db-git-clone-url\"\n ref={urlInputRef}\n className=\"db-git-form-input\"\n type=\"text\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') void handleClone();\n }}\n placeholder=\"https://github.com/owner/repo.git\"\n disabled={cloning}\n spellCheck={false}\n />\n </div>\n {isLikelyGitUrl(url) && (\n <p className=\"db-git-hint\">\n Will clone into: <b>{deriveCloneFolderName(url)}</b> — you&rsquo;ll pick where next\n </p>\n )}\n <p className=\"db-git-hint\">\n HTTPS and SSH URLs both work. You&rsquo;ll choose the destination folder in the next step.\n </p>\n {error !== null && (\n <p className=\"db-git-form-error\" role=\"alert\">\n {error}\n </p>\n )}\n {cloning && (\n <div className=\"db-git-progress\">\n {progressPercent !== null && (\n <div\n className=\"db-git-progress-bar\"\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(progressPercent)}\n style={{ width: `${progressPercent}%` }}\n />\n )}\n <p className=\"db-git-progress-text\">{progressText}</p>\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n onClick={() => handleRef.current?.cancel()}\n >\n Cancel\n </button>\n </div>\n )}\n </Dialog>\n );\n}\n","/**\n * Clone-dialog helpers — pure URL utilities, unit-testable.\n */\n\n/** Folder name a clone of this URL will produce (mirrors the host's rule). */\nexport function deriveCloneFolderName(url: string): string {\n const trimmed = url.trim().replace(/\\/+$/, '');\n const lastSegment = trimmed.split(/[/:\\\\]/).pop() ?? '';\n const withoutGit = lastSegment.replace(/\\.git$/i, '');\n const sanitized = withoutGit.replace(/[^A-Za-z0-9._-]/g, '-');\n return sanitized.length > 0 ? sanitized : 'repository';\n}\n\n/** Loose shape check for enabling the Clone button — the host re-validates. */\nexport function isLikelyGitUrl(url: string): boolean {\n const trimmed = url.trim();\n if (trimmed.length === 0 || /\\s/.test(trimmed)) return false;\n if (trimmed.startsWith('-') || trimmed.toLowerCase().startsWith('file:')) return false;\n if (/^(https?|ssh|git):\\/\\/\\S+\\/\\S+/i.test(trimmed)) return true;\n // scp-like: user@host:owner/repo\n if (/^[\\w.-]+@[\\w.-]+:\\S+/.test(trimmed)) return true;\n return false;\n}\n","/**\n * GitAuthGuidanceDialog — shown when a git operation fails to authenticate.\n *\n * DocBlocks deliberately never collects or stores credentials, so this\n * dialog only explains the three supported sign-in routes (credential\n * helper, GitHub CLI, SSH key). The copy is exported as a standalone\n * component so the clone dialog can embed it after an auth failure.\n */\n\nimport { maybeGetDocBlocksHost } from '@bendyline/docblocks/host';\nimport { Dialog } from '../components/Dialog.js';\n\nconst LEARN_MORE_URL =\n 'https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git';\n\nconst OPERATION_LABELS: Record<'push' | 'pull' | 'fetch' | 'clone' | 'pr', string> = {\n push: 'push',\n pull: 'pull',\n fetch: 'fetch',\n clone: 'clone',\n pr: 'create the pull request',\n};\n\n/** The credential guidance copy, reusable outside this dialog (clone flow). */\nexport function GitCredentialGuidance({ detail }: { detail?: string }) {\n return (\n <>\n <p className=\"db-settings-hint\">\n DocBlocks never stores Git credentials. Sign in with one of these, then try again:\n </p>\n <ul className=\"db-git-hint\">\n <li>\n A git credential helper (<code>git config credential.helper</code>)\n </li>\n <li>\n The GitHub CLI (<code>gh auth login</code>)\n </li>\n <li>An SSH key registered with your remote</li>\n </ul>\n {detail && (\n <details>\n <summary>Details</summary>\n <p className=\"db-git-hint\">{detail}</p>\n </details>\n )}\n </>\n );\n}\n\nexport function GitAuthGuidanceDialog({\n operation,\n detail,\n onClose,\n}: {\n operation: 'push' | 'pull' | 'fetch' | 'clone' | 'pr';\n detail?: string;\n onClose: () => void;\n}) {\n return (\n <Dialog\n title=\"Git needs credentials\"\n onClose={onClose}\n footer={\n <>\n <button\n type=\"button\"\n className=\"db-git-secondary-btn\"\n onClick={() => void maybeGetDocBlocksHost()?.shell.openExternal(LEARN_MORE_URL)}\n >\n Learn more\n </button>\n <button type=\"button\" className=\"db-git-primary-btn\" onClick={onClose}>\n Close\n </button>\n </>\n }\n >\n <p className=\"db-settings-hint\">\n Git could not authenticate with the remote while trying to {OPERATION_LABELS[operation]}.\n </p>\n <GitCredentialGuidance detail={detail} />\n </Dialog>\n );\n}\n","/**\n * GitConflictsDialog — lists conflicted files after a pull hits a merge\n * conflict. Each row opens the file in the editor so the user can resolve\n * the markers; the primary action hands off to the commit dialog, which\n * concludes the merge.\n */\n\nimport { Dialog } from '../components/Dialog.js';\nimport { useGitContext } from './GitContext.js';\n\nexport function GitConflictsDialog({\n paths,\n onOpenFile,\n onClose,\n}: {\n paths: string[];\n onOpenFile: (path: string) => void;\n onClose: () => void;\n}) {\n const git = useGitContext();\n\n return (\n <Dialog\n title=\"Merge conflicts\"\n onClose={onClose}\n footer={\n <>\n <button type=\"button\" className=\"db-git-secondary-btn\" onClick={onClose}>\n Close\n </button>\n <button\n type=\"button\"\n className=\"db-git-primary-btn\"\n onClick={() => git?.openDialog({ kind: 'commit' })}\n >\n Open commit dialog\n </button>\n </>\n }\n >\n <p className=\"db-settings-hint\">\n These files contain conflict markers. Edit each one to resolve the conflicts, then commit to\n complete the merge.\n </p>\n <ul className=\"db-git-file-list\">\n {paths.map((path) => (\n <li key={path} className=\"db-git-file-row\">\n <button\n type=\"button\"\n onClick={() => {\n onOpenFile(path);\n onClose();\n }}\n >\n {path.replace(/^\\/+/, '')}\n </button>\n </li>\n ))}\n </ul>\n </Dialog>\n );\n}\n","/**\n * GitUI — single mount point for the git status bar, the aria-live\n * operation result line, and every git dialog. Dialog visibility lives in\n * GitContext so the file tree, toolbar, and native menu can all open them.\n */\n\nimport type { ElectronWorkspaceInfo } from '@bendyline/docblocks/host';\nimport { useGitContext } from './GitContext.js';\nimport { GitStatusBar } from './GitStatusBar.js';\nimport { GitCommitDialog } from './GitCommitDialog.js';\nimport { GitBranchesDialog } from './GitBranchesDialog.js';\nimport { GitHistoryDialog } from './GitHistoryDialog.js';\nimport { GitDiffDialog } from './GitDiffDialog.js';\nimport { GitCloneDialog } from './GitCloneDialog.js';\nimport { GitAuthGuidanceDialog } from './GitAuthGuidanceDialog.js';\nimport { GitConflictsDialog } from './GitConflictsDialog.js';\n\nexport interface GitUIProps {\n /** Open a workspace file in the editor (from conflicts/history dialogs). */\n onOpenFile: (path: string) => void;\n /** A clone finished and its folder is registered — open the workspace. */\n onWorkspaceCloned?: (info: ElectronWorkspaceInfo) => void;\n}\n\nexport function GitUI({ onOpenFile, onWorkspaceCloned }: GitUIProps) {\n const git = useGitContext();\n if (!git || !git.available) return null;\n\n const { dialog, closeDialog } = git;\n\n return (\n <>\n <GitStatusBar />\n {git.repo && (\n <span className=\"db-git-inline-status\" role=\"status\" aria-live=\"polite\">\n {git.lastResult?.message ?? ''}\n </span>\n )}\n {dialog.kind === 'commit' && <GitCommitDialog onClose={closeDialog} />}\n {dialog.kind === 'branches' && (\n <GitBranchesDialog createFocus={dialog.createFocus} onClose={closeDialog} />\n )}\n {dialog.kind === 'history' && <GitHistoryDialog path={dialog.path} onClose={closeDialog} />}\n {dialog.kind === 'diff' && (\n <GitDiffDialog path={dialog.path} revision={dialog.revision} onClose={closeDialog} />\n )}\n {dialog.kind === 'clone' && (\n <GitCloneDialog\n onClose={closeDialog}\n onCloned={(info) => {\n closeDialog();\n onWorkspaceCloned?.(info);\n }}\n />\n )}\n {dialog.kind === 'auth-guidance' && (\n <GitAuthGuidanceDialog\n operation={dialog.operation}\n detail={dialog.detail}\n onClose={closeDialog}\n />\n )}\n {dialog.kind === 'conflicts' && (\n <GitConflictsDialog paths={dialog.paths} onOpenFile={onOpenFile} onClose={closeDialog} />\n )}\n </>\n );\n}\n"],"mappings":"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","names":["useRef","useState","jsx","jsxs","useState","useRef","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useCallback","useEffect","error","useEffect","useState","useEffect","useRef","Fragment","jsx","jsxs","jsx","jsxs","useState","useEffect","useEffect","useRef","useState","Fragment","jsx","jsxs","OPERATION_LABELS","Fragment","jsx","jsxs","useState","useRef","useEffect","Fragment","jsx","jsxs","Fragment","jsx","jsxs"]}
@@ -1,6 +0,0 @@
1
- // src/DocBlocksShell/LazyEditorShell.ts
2
- import { EditorShell } from "@bendyline/squisq-editor-react";
3
- export {
4
- EditorShell as default
5
- };
6
- //# sourceMappingURL=LazyEditorShell-EF2BWRXU.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/DocBlocksShell/LazyEditorShell.ts"],"sourcesContent":["/** Deferred boundary for the large Squisq editor implementation. */\nexport { EditorShell as default } from '@bendyline/squisq-editor-react';\n"],"mappings":";AACA,SAAwB,mBAAe;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Export/ShareDialog.tsx","../src/Export/shared-document.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { SHARED_DOCUMENT_LIMITS, type SharedDocumentMode } from '@bendyline/docblocks/share';\nimport { buildSharedDocumentUrl, createSharedDocumentArchive } from './shared-document.js';\n\nexport interface ShareDialogProps {\n markdown: string;\n selectedFile: string | null;\n baseUrl: string;\n onClose: () => void;\n}\n\nconst MODES: ReadonlyArray<{ value: SharedDocumentMode | ''; label: string }> = [\n { value: '', label: 'Write (no default)' },\n { value: 'slideshow', label: 'Slideshow' },\n { value: 'video', label: 'Video' },\n { value: 'page', label: 'Page' },\n { value: 'document', label: 'Document' },\n { value: 'narrate', label: 'Narrate' },\n];\n\nexport function ShareDialog({ markdown, selectedFile, baseUrl, onClose }: ShareDialogProps) {\n const [mode, setMode] = useState<SharedDocumentMode | ''>('');\n const [archive, setArchive] = useState<Uint8Array | null>(null);\n const [archiveError, setArchiveError] = useState<string | null>(null);\n const [copyStatus, setCopyStatus] = useState<'idle' | 'copied' | 'failed'>('idle');\n const linkRef = useRef<HTMLTextAreaElement>(null);\n\n useEffect(() => {\n let cancelled = false;\n setArchive(null);\n setArchiveError(null);\n void createSharedDocumentArchive(markdown, selectedFile)\n .then((nextArchive) => {\n if (!cancelled) setArchive(nextArchive);\n })\n .catch((error: unknown) => {\n if (!cancelled) {\n setArchiveError(\n error instanceof Error ? error.message : 'DocBlocks could not create the shared link.',\n );\n }\n });\n return () => {\n cancelled = true;\n };\n }, [markdown, selectedFile]);\n\n const linkResult = useMemo(() => {\n if (!archive) return { url: '', error: archiveError };\n try {\n return { url: buildSharedDocumentUrl(baseUrl, archive, mode || null), error: null };\n } catch (error: unknown) {\n return {\n url: '',\n error:\n error instanceof Error ? error.message : 'DocBlocks could not create the shared link.',\n };\n }\n }, [archive, archiveError, baseUrl, mode]);\n\n useEffect(() => setCopyStatus('idle'), [linkResult.url]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') onClose();\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [onClose]);\n\n const copyLink = useCallback(async () => {\n if (!linkResult.url) return;\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(linkResult.url);\n } else {\n linkRef.current?.focus();\n linkRef.current?.select();\n if (typeof document.execCommand !== 'function' || !document.execCommand('copy')) {\n throw new Error('Clipboard access is unavailable.');\n }\n }\n setCopyStatus('copied');\n } catch {\n linkRef.current?.focus();\n linkRef.current?.select();\n setCopyStatus('failed');\n }\n }, [linkResult.url]);\n\n const isLong = linkResult.url.length > SHARED_DOCUMENT_LIMITS.portableUrlCharacters;\n\n return (\n <div\n className=\"db-dialog-overlay\"\n onClick={(event) => {\n if (event.target === event.currentTarget) onClose();\n }}\n >\n <div\n className=\"db-dialog db-export-dialog db-share-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"db-share-dialog-title\"\n >\n <div className=\"db-dialog-header\">\n <h2 className=\"db-dialog-title\" id=\"db-share-dialog-title\">\n Share document via URL\n </h2>\n <button className=\"db-dialog-close\" type=\"button\" onClick={onClose} aria-label=\"Close\">\n &times;\n </button>\n </div>\n\n <div className=\"db-dialog-body\">\n <div className=\"db-share-warning\" role=\"note\">\n The contents of this document are embedded in the URL. Anyone with the link receives a\n copy of the document, so do not use it for secrets.\n </div>\n\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-share-mode\">\n Default mode (optional)\n </label>\n <select\n id=\"db-share-mode\"\n className=\"db-export-select\"\n value={mode}\n onChange={(event) => setMode(event.target.value as SharedDocumentMode | '')}\n >\n {MODES.map((option) => (\n <option key={option.value || 'write'} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n <span className=\"db-export-hint\">\n Without a default, the copied document opens in Write. Other choices open directly in\n Use.\n </span>\n </div>\n\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-share-link\">\n Share link\n </label>\n <textarea\n ref={linkRef}\n id=\"db-share-link\"\n className=\"db-share-link\"\n value={linkResult.url}\n readOnly\n rows={3}\n spellCheck={false}\n aria-describedby=\"db-share-link-status\"\n placeholder={linkResult.error ? '' : 'Creating link...'}\n />\n <span\n id=\"db-share-link-status\"\n className={`db-export-hint${isLong ? ' db-share-length-warning' : ''}`}\n >\n {linkResult.error\n ? linkResult.error\n : linkResult.url\n ? isLong\n ? `${linkResult.url.length.toLocaleString()} characters. Links longer than 4,096 characters may be truncated by messaging or email tools.`\n : `${linkResult.url.length.toLocaleString()} characters.`\n : 'Compressing this document into a Markdown-only DBK...'}\n </span>\n </div>\n\n <p className=\"db-share-temporary-note\">\n Opening the link creates a temporary workspace. Changes there do not alter this document\n or the link.\n </p>\n </div>\n\n <div className=\"db-export-footer\">\n <button\n className=\"db-export-btn db-export-btn--secondary\"\n type=\"button\"\n onClick={onClose}\n >\n Close\n </button>\n <button\n className=\"db-export-btn db-export-btn--primary\"\n type=\"button\"\n onClick={() => void copyLink()}\n disabled={!linkResult.url}\n >\n {copyStatus === 'copied'\n ? 'Copied'\n : copyStatus === 'failed'\n ? 'Select and copy'\n : 'Copy link'}\n </button>\n </div>\n </div>\n </div>\n );\n}\n","import {\n SHARED_DOCUMENT_LIMITS,\n createSharedDocumentUrl,\n type SharedDocumentMode,\n} from '@bendyline/docblocks/share';\n\nexport const PUBLIC_DOCBLOCKS_SHARE_URL = 'https://bendyline.github.io/docblocks/';\n\n/** Use the current web deployment, but never emit an unopenable app:// link. */\nexport function resolveSharedDocumentBaseUrl(currentUrl: string): string {\n try {\n const current = new URL(currentUrl);\n if (current.protocol === 'http:' || current.protocol === 'https:') {\n current.hash = '';\n current.searchParams.delete('action');\n return current.toString();\n }\n } catch {\n // Fall through to the canonical public site.\n }\n return PUBLIC_DOCBLOCKS_SHARE_URL;\n}\n\nexport function sharedDocumentFilename(selectedFile: string | null): string {\n const basename = selectedFile?.replace(/\\\\/gu, '/').split('/').pop() ?? 'shared.md';\n const stem = basename.replace(/\\.md$/iu, '').replace(/[^A-Za-z0-9._-]+/gu, '-');\n const bounded = stem.replace(/^-+|-+$/gu, '').slice(0, 80) || 'shared';\n return `${bounded}.md`;\n}\n\n/** Create a compressed DBK containing exactly one bounded Markdown file. */\nexport async function createSharedDocumentArchive(\n markdown: string,\n selectedFile: string | null,\n): Promise<Uint8Array> {\n const encoded = new TextEncoder().encode(markdown);\n if (encoded.byteLength > SHARED_DOCUMENT_LIMITS.markdownBytes) {\n throw new Error(\n `This document is too large for a shared URL (maximum ${SHARED_DOCUMENT_LIMITS.markdownBytes.toLocaleString()} UTF-8 bytes).`,\n );\n }\n\n const [{ MemoryContentContainer }, { containerToZip }] = await Promise.all([\n import('@bendyline/squisq/storage'),\n import('@bendyline/squisq-formats/container'),\n ]);\n const container = new MemoryContentContainer();\n await container.writeFile(sharedDocumentFilename(selectedFile), encoded, 'text/markdown');\n const blob = await containerToZip(container, { compression: 'DEFLATE', compressionLevel: 9 });\n return new Uint8Array(await blob.arrayBuffer());\n}\n\nexport function buildSharedDocumentUrl(\n baseUrl: string,\n archive: Uint8Array,\n mode: SharedDocumentMode | null,\n): string {\n return createSharedDocumentUrl(resolveSharedDocumentBaseUrl(baseUrl), archive, mode);\n}\n"],"mappings":"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","names":["SHARED_DOCUMENT_LIMITS","SHARED_DOCUMENT_LIMITS"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Export/transform-summaries.ts"],"sourcesContent":["export interface ExportSummaryOption {\n id: string;\n name: string;\n description?: string;\n}\n\nlet transformStyleSummariesPromise: Promise<ExportSummaryOption[]> | null = null;\n\nexport function loadTransformStyleSummaries(): Promise<ExportSummaryOption[]> {\n transformStyleSummariesPromise ??= import('@bendyline/squisq/transform').then(\n ({ getTransformStyleSummaries }) => getTransformStyleSummaries(),\n );\n return transformStyleSummariesPromise;\n}\n"],"mappings":";AAMA,IAAI,iCAAwE;AAErE,SAAS,8BAA8D;AAC5E,sEAAmC,OAAO,6BAA6B,EAAE;AAAA,IACvE,CAAC,EAAE,2BAA2B,MAAM,2BAA2B;AAAA,EACjE;AACA,SAAO;AACT;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Git/useMenuKeyboard.ts"],"sourcesContent":["/**\n * useMenuKeyboard — the WAI-ARIA menu-button key contract for a trigger +\n * `role=\"menu\"` dropdown pair.\n *\n * Trigger: ArrowDown / ArrowUp / Enter / Space open the menu and focus\n * its first (or last) item.\n * Menu: ArrowDown/ArrowUp cycle, Home/End jump, Escape closes and\n * returns focus to the trigger, Tab closes.\n *\n * Items are not tab stops (`tabIndex={-1}`); the trigger is the single tab\n * stop, and focus moves within the menu by arrow key.\n *\n * NOTE: five dropdowns in this package share this contract — the two git\n * ones (which use this), plus AppMenu, ExportToolbarControls and\n * WorkspaceSettingsButton, which are owned elsewhere and still lack it.\n * This hook lives under Git/ only because that is the scope it was written\n * in; it has no git dependency and should be lifted to a shared location\n * and applied to the other three.\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\n\nexport interface MenuKeyboard {\n /** Attach to the `role=\"menu\"` element. */\n menuRef: React.RefObject<HTMLDivElement>;\n /** Attach to the button that opens the menu. */\n triggerRef: React.RefObject<HTMLButtonElement>;\n handleMenuKeyDown: (event: React.KeyboardEvent) => void;\n handleTriggerKeyDown: (event: React.KeyboardEvent) => void;\n /** Close from a click handler without stealing focus back. */\n closeMenu: (returnFocus: boolean) => void;\n}\n\nexport function useMenuKeyboard(open: boolean, setOpen: (open: boolean) => void): MenuKeyboard {\n const menuRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLButtonElement>(null);\n /** Where to land when the menu opens: keyboard users must go inside it. */\n const pendingFocusRef = useRef<'none' | 'first' | 'last'>('none');\n\n const items = useCallback(\n () =>\n [...(menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]') ?? [])].filter(\n (item) => !item.disabled,\n ),\n [],\n );\n\n const focusItem = useCallback(\n (index: number) => {\n const all = items();\n if (all.length === 0) return;\n all[((index % all.length) + all.length) % all.length]?.focus({ preventScroll: true });\n },\n [items],\n );\n\n const closeMenu = useCallback(\n (returnFocus: boolean) => {\n pendingFocusRef.current = 'none';\n setOpen(false);\n if (returnFocus) triggerRef.current?.focus();\n },\n [setOpen],\n );\n\n // Land inside the menu once it has rendered, but only when the keyboard\n // opened it — a pointer user's focus should stay where they clicked.\n useEffect(() => {\n if (!open || pendingFocusRef.current === 'none') return;\n const where = pendingFocusRef.current;\n pendingFocusRef.current = 'none';\n focusItem(where === 'last' ? items().length - 1 : 0);\n }, [open, focusItem, items]);\n\n const handleTriggerKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n pendingFocusRef.current = event.key === 'ArrowUp' ? 'last' : 'first';\n setOpen(true);\n return;\n }\n // Enter/Space already activate the button (which toggles the menu);\n // this only says where focus should land when they do.\n if (event.key === 'Enter' || event.key === ' ') {\n if (!open) pendingFocusRef.current = 'first';\n }\n },\n [open, setOpen],\n );\n\n const handleMenuKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === 'Escape' || event.key === 'Tab') {\n event.preventDefault();\n event.stopPropagation();\n closeMenu(true);\n return;\n }\n const all = items();\n if (all.length === 0) return;\n const current = all.indexOf(document.activeElement as HTMLButtonElement);\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n focusItem(current + 1);\n break;\n case 'ArrowUp':\n event.preventDefault();\n focusItem(current - 1);\n break;\n case 'Home':\n event.preventDefault();\n focusItem(0);\n break;\n case 'End':\n event.preventDefault();\n focusItem(all.length - 1);\n break;\n default:\n break;\n }\n },\n [closeMenu, focusItem, items],\n );\n\n return { menuRef, triggerRef, handleMenuKeyDown, handleTriggerKeyDown, closeMenu };\n}\n"],"mappings":";AAoBA,SAAS,aAAa,WAAW,cAAc;AAaxC,SAAS,gBAAgB,MAAe,SAAgD;AAC7F,QAAM,UAAU,OAAuB,IAAI;AAC3C,QAAM,aAAa,OAA0B,IAAI;AAEjD,QAAM,kBAAkB,OAAkC,MAAM;AAEhE,QAAM,QAAQ;AAAA,IACZ,MACE,CAAC,GAAI,QAAQ,SAAS,iBAAoC,mBAAmB,KAAK,CAAC,CAAE,EAAE;AAAA,MACrF,CAAC,SAAS,CAAC,KAAK;AAAA,IAClB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,UAAkB;AACjB,YAAM,MAAM,MAAM;AAClB,UAAI,IAAI,WAAW,EAAG;AACtB,WAAM,QAAQ,IAAI,SAAU,IAAI,UAAU,IAAI,MAAM,GAAG,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,IACtF;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,gBAAyB;AACxB,sBAAgB,UAAU;AAC1B,cAAQ,KAAK;AACb,UAAI,YAAa,YAAW,SAAS,MAAM;AAAA,IAC7C;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAIA,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,gBAAgB,YAAY,OAAQ;AACjD,UAAM,QAAQ,gBAAgB;AAC9B,oBAAgB,UAAU;AAC1B,cAAU,UAAU,SAAS,MAAM,EAAE,SAAS,IAAI,CAAC;AAAA,EACrD,GAAG,CAAC,MAAM,WAAW,KAAK,CAAC;AAE3B,QAAM,uBAAuB;AAAA,IAC3B,CAAC,UAA+B;AAC9B,UAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,WAAW;AACxD,cAAM,eAAe;AACrB,wBAAgB,UAAU,MAAM,QAAQ,YAAY,SAAS;AAC7D,gBAAQ,IAAI;AACZ;AAAA,MACF;AAGA,UAAI,MAAM,QAAQ,WAAW,MAAM,QAAQ,KAAK;AAC9C,YAAI,CAAC,KAAM,iBAAgB,UAAU;AAAA,MACvC;AAAA,IACF;AAAA,IACA,CAAC,MAAM,OAAO;AAAA,EAChB;AAEA,QAAM,oBAAoB;AAAA,IACxB,CAAC,UAA+B;AAC9B,UAAI,MAAM,QAAQ,YAAY,MAAM,QAAQ,OAAO;AACjD,cAAM,eAAe;AACrB,cAAM,gBAAgB;AACtB,kBAAU,IAAI;AACd;AAAA,MACF;AACA,YAAM,MAAM,MAAM;AAClB,UAAI,IAAI,WAAW,EAAG;AACtB,YAAM,UAAU,IAAI,QAAQ,SAAS,aAAkC;AACvE,cAAQ,MAAM,KAAK;AAAA,QACjB,KAAK;AACH,gBAAM,eAAe;AACrB,oBAAU,UAAU,CAAC;AACrB;AAAA,QACF,KAAK;AACH,gBAAM,eAAe;AACrB,oBAAU,UAAU,CAAC;AACrB;AAAA,QACF,KAAK;AACH,gBAAM,eAAe;AACrB,oBAAU,CAAC;AACX;AAAA,QACF,KAAK;AACH,gBAAM,eAAe;AACrB,oBAAU,IAAI,SAAS,CAAC;AACxB;AAAA,QACF;AACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,WAAW,WAAW,KAAK;AAAA,EAC9B;AAEA,SAAO,EAAE,SAAS,YAAY,mBAAmB,sBAAsB,UAAU;AACnF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Git/GitContext.tsx","../src/Git/git-status.ts"],"sourcesContent":["/**\n * GitContext — the seam between DocBlocksShell and every git UI surface.\n *\n * The shell calls useGit() once and provides the value here; the status\n * bar, file-tree badges, toolbar control, and dialogs all consume it.\n * Consumers must tolerate a null context (FileExplorer renders standalone\n * in tests and on surfaces with no git).\n */\n\nimport { createContext, useContext } from 'react';\nimport type {\n DocBlocksHostGitAPI,\n GitCapabilities,\n GitRepoDetection,\n GitStatus,\n} from '@bendyline/docblocks/host';\nimport type { FileSystemProvider } from '@bendyline/docblocks/filesystem';\nimport type { GitBadgeKind } from './git-status.js';\n\nexport type GitDialogState =\n | { kind: 'none' }\n | { kind: 'commit' }\n | { kind: 'branches'; createFocus?: boolean }\n | { kind: 'history'; path?: string }\n /** `revision` absent → working tree vs HEAD; present → that commit vs its parent. */\n | { kind: 'diff'; path: string; revision?: { sha: string } }\n | { kind: 'clone' }\n | {\n kind: 'auth-guidance';\n operation: 'push' | 'pull' | 'fetch' | 'clone' | 'pr';\n detail?: string;\n }\n | { kind: 'conflicts'; paths: string[] };\n\nexport type GitBusyKind = 'push' | 'pull' | 'fetch' | 'commit' | 'branch' | 'pr' | null;\n\nexport interface GitInlineResult {\n tone: 'info' | 'error';\n message: string;\n}\n\nexport interface GitValue {\n /** Tier 1 — Electron host with a working git. Gates the clone entry. */\n available: boolean;\n capabilities: GitCapabilities | null;\n /** Tier 2 — the active workspace root is a repository. Gates everything else. */\n repo: GitRepoDetection | null;\n /** Main-owned repository capability; never a native path. */\n repositoryId: string | null;\n /** Parsed web location of the first remote, when one exists and parses. */\n remoteWeb: { host: string; owner: string; repo: string; webUrl: string } | null;\n /** Raw host API for read-heavy dialogs (log, branches, file-at-revision). */\n gitApi: DocBlocksHostGitAPI | null;\n provider: FileSystemProvider | null;\n theme: 'light' | 'dark';\n\n status: GitStatus | null;\n badges: ReadonlyMap<string, GitBadgeKind>;\n busy: GitBusyKind;\n /** Latest operation outcome, surfaced in an aria-live region. */\n lastResult: GitInlineResult | null;\n\n refresh: () => void;\n /** Debounced refresh — used after autosave writes. */\n scheduleRefresh: () => void;\n\n /** Actions route their typed errors to dialogs/lastResult internally. */\n commit: (message: string, paths: string[]) => Promise<boolean>;\n push: () => Promise<void>;\n pull: () => Promise<void>;\n fetchRemote: () => Promise<void>;\n createBranch: (name: string) => Promise<boolean>;\n switchBranch: (name: string) => Promise<boolean>;\n /** Open the repo (or a file within it) on the remote's web host. */\n openOnRemote: (filePath?: string) => void;\n createPullRequest: () => Promise<void>;\n\n dialog: GitDialogState;\n openDialog: (dialog: GitDialogState) => void;\n closeDialog: () => void;\n}\n\nexport const GitContext = createContext<GitValue | null>(null);\n\n/** Null when no provider is mounted — callers must degrade gracefully. */\nexport function useGitContext(): GitValue | null {\n return useContext(GitContext);\n}\n","/**\n * Pure helpers mapping GitStatus onto UI concerns — file-tree badges,\n * ahead/behind chips, dirty summaries. No React, no host access.\n */\n\nimport type { GitFileChange, GitStatus } from '@bendyline/docblocks/host';\n\nexport type GitBadgeKind =\n | 'modified'\n | 'added'\n | 'untracked'\n | 'renamed'\n | 'deleted'\n | 'conflicted'\n | 'dir';\n\nexport const BADGE_GLYPHS: Record<GitBadgeKind, string> = {\n modified: 'M',\n added: 'A',\n untracked: 'U',\n renamed: 'R',\n deleted: 'D',\n conflicted: '!',\n dir: '•',\n};\n\nexport const BADGE_LABELS: Record<GitBadgeKind, string> = {\n modified: 'modified',\n added: 'added',\n untracked: 'untracked',\n renamed: 'renamed',\n deleted: 'deleted',\n conflicted: 'has conflicts',\n dir: 'contains changes',\n};\n\nfunction normalise(path: string): string {\n return path.startsWith('/') ? path : `/${path}`;\n}\n\n/** True when any path segment is a dot-entry (.git, .github, .gitignore…). */\nfunction isDotPath(path: string): boolean {\n return normalise(path)\n .split('/')\n .some((segment) => segment.startsWith('.'));\n}\n\nexport function badgeKindFor(change: GitFileChange): GitBadgeKind {\n if (change.conflicted) return 'conflicted';\n if (change.worktree === 'untracked') return 'untracked';\n if (change.index === 'deleted' || change.worktree === 'deleted') return 'deleted';\n if (change.index === 'renamed' || change.worktree === 'renamed') return 'renamed';\n if (change.index === 'added' || change.worktree === 'added') return 'added';\n return 'modified';\n}\n\n/**\n * Slash-prefixed path → badge kind, including directory rollups: any\n * ancestor directory of a changed file gets a 'dir' dot (or 'conflicted'\n * when a conflicted descendant wins). Deleted files aren't in the tree, so\n * they contribute only their ancestor rollup. Dot-paths are excluded so a\n * `.gitignore` edit doesn't inexplicably dot the root.\n */\nexport function buildBadgeMap(changes: readonly GitFileChange[]): Map<string, GitBadgeKind> {\n const badges = new Map<string, GitBadgeKind>();\n const dirKinds = new Map<string, GitBadgeKind>();\n\n for (const change of changes) {\n if (isDotPath(change.path)) continue;\n const filePath = normalise(change.path);\n const kind = badgeKindFor(change);\n if (kind !== 'deleted') {\n badges.set(filePath, kind);\n }\n const segments = filePath.split('/').slice(1, -1);\n let dir = '';\n for (const segment of segments) {\n dir += `/${segment}`;\n const rollup = kind === 'conflicted' ? 'conflicted' : 'dir';\n if (dirKinds.get(dir) !== 'conflicted') dirKinds.set(dir, rollup);\n }\n }\n for (const [dir, kind] of dirKinds) {\n if (!badges.has(dir)) badges.set(dir, kind);\n }\n return badges;\n}\n\n/** \"2↑ 1↓\" — empty string when neither applies. */\nexport function formatAheadBehind(status: Pick<GitStatus, 'ahead' | 'behind'>): string {\n const parts: string[] = [];\n if (status.ahead > 0) parts.push(`${status.ahead}↑`);\n if (status.behind > 0) parts.push(`${status.behind}↓`);\n return parts.join(' ');\n}\n\nexport function isFileDirty(status: GitStatus | null, path: string | null): boolean {\n if (!status || !path) return false;\n const target = normalise(path);\n return status.changes.some((c) => normalise(c.path) === target);\n}\n\nexport function conflictedPaths(status: GitStatus | null): string[] {\n if (!status) return [];\n return status.changes.filter((c) => c.conflicted).map((c) => normalise(c.path));\n}\n\n/** \"3 changes\" / \"1 change\" / \"\" when clean. */\nexport function summarizeDirty(status: GitStatus | null): string {\n const count = status?.changes.length ?? 0;\n if (count === 0) return '';\n return count === 1 ? '1 change' : `${count} changes`;\n}\n"],"mappings":";AASA,SAAS,eAAe,kBAAkB;AAyEnC,IAAM,aAAa,cAA+B,IAAI;AAGtD,SAAS,gBAAiC;AAC/C,SAAO,WAAW,UAAU;AAC9B;;;ACvEO,IAAM,eAA6C;AAAA,EACxD,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,KAAK;AACP;AAEO,IAAM,eAA6C;AAAA,EACxD,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,KAAK;AACP;AAEA,SAAS,UAAU,MAAsB;AACvC,SAAO,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AAC/C;AAGA,SAAS,UAAU,MAAuB;AACxC,SAAO,UAAU,IAAI,EAClB,MAAM,GAAG,EACT,KAAK,CAAC,YAAY,QAAQ,WAAW,GAAG,CAAC;AAC9C;AAEO,SAAS,aAAa,QAAqC;AAChE,MAAI,OAAO,WAAY,QAAO;AAC9B,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,MAAI,OAAO,UAAU,aAAa,OAAO,aAAa,UAAW,QAAO;AACxE,MAAI,OAAO,UAAU,aAAa,OAAO,aAAa,UAAW,QAAO;AACxE,MAAI,OAAO,UAAU,WAAW,OAAO,aAAa,QAAS,QAAO;AACpE,SAAO;AACT;AASO,SAAS,cAAc,SAA8D;AAC1F,QAAM,SAAS,oBAAI,IAA0B;AAC7C,QAAM,WAAW,oBAAI,IAA0B;AAE/C,aAAW,UAAU,SAAS;AAC5B,QAAI,UAAU,OAAO,IAAI,EAAG;AAC5B,UAAM,WAAW,UAAU,OAAO,IAAI;AACtC,UAAM,OAAO,aAAa,MAAM;AAChC,QAAI,SAAS,WAAW;AACtB,aAAO,IAAI,UAAU,IAAI;AAAA,IAC3B;AACA,UAAM,WAAW,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE;AAChD,QAAI,MAAM;AACV,eAAW,WAAW,UAAU;AAC9B,aAAO,IAAI,OAAO;AAClB,YAAM,SAAS,SAAS,eAAe,eAAe;AACtD,UAAI,SAAS,IAAI,GAAG,MAAM,aAAc,UAAS,IAAI,KAAK,MAAM;AAAA,IAClE;AAAA,EACF;AACA,aAAW,CAAC,KAAK,IAAI,KAAK,UAAU;AAClC,QAAI,CAAC,OAAO,IAAI,GAAG,EAAG,QAAO,IAAI,KAAK,IAAI;AAAA,EAC5C;AACA,SAAO;AACT;AAGO,SAAS,kBAAkB,QAAqD;AACrF,QAAM,QAAkB,CAAC;AACzB,MAAI,OAAO,QAAQ,EAAG,OAAM,KAAK,GAAG,OAAO,KAAK,QAAG;AACnD,MAAI,OAAO,SAAS,EAAG,OAAM,KAAK,GAAG,OAAO,MAAM,QAAG;AACrD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEO,SAAS,YAAY,QAA0B,MAA8B;AAClF,MAAI,CAAC,UAAU,CAAC,KAAM,QAAO;AAC7B,QAAM,SAAS,UAAU,IAAI;AAC7B,SAAO,OAAO,QAAQ,KAAK,CAAC,MAAM,UAAU,EAAE,IAAI,MAAM,MAAM;AAChE;AAEO,SAAS,gBAAgB,QAAoC;AAClE,MAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,SAAO,OAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,UAAU,EAAE,IAAI,CAAC;AAChF;AAGO,SAAS,eAAe,QAAkC;AAC/D,QAAM,QAAQ,QAAQ,QAAQ,UAAU;AACxC,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,UAAU,IAAI,aAAa,GAAG,KAAK;AAC5C;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Export/export-options.ts"],"sourcesContent":["/**\n * Export option types and localStorage persistence for quick-export.\n */\n\nexport type ExportFormat = 'docx' | 'pdf' | 'pptx' | 'md' | 'html';\n\n/** Visual style for HTML export. */\nexport type HtmlStyle =\n /** Render via SquisqPlayer (themed, embeds player bundle). */\n | 'rendered'\n /** Plain semantic HTML — small, no JS, no playback. */\n | 'plain';\n\n/** Packaging for HTML export. */\nexport type HtmlBundle =\n /** Single self-contained .html file (images base64-embedded). */\n | 'single'\n /** ZIP containing index.html + asset files. */\n | 'zip';\n\nexport interface ExportOptions {\n format: ExportFormat;\n themeId: string;\n /** Transform style for PPTX — controls how content is segmented and styled. */\n transformStyle: string;\n /** Only applies to PDF */\n pageSize: 'letter' | 'a4';\n /** HTML rendering style. Only applies to format=html. */\n htmlStyle: HtmlStyle;\n /** HTML bundle layout. Only applies to format=html. */\n htmlBundle: HtmlBundle;\n /**\n * Recursive HTML bundle: walk relative `.md` links from the entry\n * document, render every reachable sibling/child page, and rewrite\n * cross-doc links from `.md` → `.html`. The output is a ZIP\n * regardless of `htmlBundle` because multi-doc output needs a\n * directory tree. Supported for both plain (`markdownDocsToPlainHtmlBundle`)\n * and rendered (`markdownDocsToHtmlBundle`) HTML styles. Only\n * meaningful when `format === 'html'`.\n */\n includeLinkedDocs: boolean;\n /**\n * Rename the entry page from `<basename>.html` to `index.html` so the\n * export drops straight into a static-site host that expects an\n * `index.html` landing page. For recursive bundles, cross-doc links\n * targeting the entry also rewrite to `index.html` so siblings don't\n * 404. For a single-doc HTML download, this just renames the file.\n * Only meaningful when `format === 'html'`.\n */\n entryAsIndex: boolean;\n}\n\nexport const FORMAT_LABELS: Record<ExportFormat, string> = {\n docx: 'Word Document (.docx)',\n pdf: 'PDF (.pdf)',\n pptx: 'PowerPoint (.pptx)',\n md: 'Markdown (.md)',\n html: 'HTML (.html)',\n};\n\nexport const FORMAT_EXTENSIONS: Record<ExportFormat, string> = {\n docx: '.docx',\n pdf: '.pdf',\n pptx: '.pptx',\n md: '.md',\n html: '.html',\n};\n\nconst STORAGE_KEY = 'docblocks-export-options';\n\nexport const DEFAULT_OPTIONS: ExportOptions = {\n format: 'pdf',\n themeId: 'standard',\n transformStyle: 'documentary',\n pageSize: 'letter',\n htmlStyle: 'plain',\n htmlBundle: 'single',\n includeLinkedDocs: false,\n entryAsIndex: false,\n};\n\nexport function loadLastExportOptions(): ExportOptions | null {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Partial<ExportOptions>;\n return { ...DEFAULT_OPTIONS, ...parsed };\n } catch {\n return null;\n }\n}\n\nexport function saveExportOptions(options: ExportOptions): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(options));\n } catch {\n // Ignore storage errors\n }\n}\n"],"mappings":";AAoDO,IAAM,gBAA8C;AAAA,EACzD,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAM;AACR;AAEO,IAAM,oBAAkD;AAAA,EAC7D,MAAM;AAAA,EACN,KAAK;AAAA,EACL,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,MAAM;AACR;AAEA,IAAM,cAAc;AAEb,IAAM,kBAAiC;AAAA,EAC5C,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,cAAc;AAChB;AAEO,SAAS,wBAA8C;AAC5D,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,WAAW;AAC5C,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,WAAO,EAAE,GAAG,iBAAiB,GAAG,OAAO;AAAA,EACzC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,SAA8B;AAC9D,MAAI;AACF,iBAAa,QAAQ,aAAa,KAAK,UAAU,OAAO,CAAC;AAAA,EAC3D,QAAQ;AAAA,EAER;AACF;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/preferences/theme.ts","../src/preferences/write-canvas.ts","../src/Settings/Settings.tsx"],"sourcesContent":["/** Appearance preferences shared by the shell and settings UI. */\n\nexport type ThemePreference = 'auto' | 'light' | 'dark';\n\nexport const ACCENT_COLORS = [\n 'brown',\n 'green',\n 'blue',\n 'purple',\n 'maroon',\n 'orange',\n 'gray',\n] as const;\n\nexport type AccentColor = (typeof ACCENT_COLORS)[number];\n\n/**\n * Chrome (titlebar) background per resolved theme. Keep in sync with\n * `--db-bg-subtle` for the default accent in styles/docblocks.css and with\n * the static `<meta name=\"theme-color\">` values in the site's index.html.\n * The shell writes these into the theme-color metas so the installed web\n * app's titlebar (Window Controls Overlay caption area, Android status bar)\n * follows the resolved theme.\n */\nexport const DB_CHROME_COLORS: Record<'light' | 'dark', string> = {\n light: '#f3eede',\n dark: '#262219',\n};\n\nconst THEME_STORAGE_KEY = 'docblocks:themePreference';\nconst ACCENT_STORAGE_KEY = 'docblocks:accentColor';\n\nexport function loadThemePreference(): ThemePreference {\n try {\n const raw = localStorage.getItem(THEME_STORAGE_KEY);\n if (raw === 'light' || raw === 'dark' || raw === 'auto') return raw;\n } catch {\n // ignore unavailable storage\n }\n return 'auto';\n}\n\nexport function saveThemePreference(value: ThemePreference): void {\n try {\n localStorage.setItem(THEME_STORAGE_KEY, value);\n } catch {\n // ignore quota errors\n }\n}\n\nexport function loadAccentColor(): AccentColor {\n try {\n const raw = localStorage.getItem(ACCENT_STORAGE_KEY);\n if (ACCENT_COLORS.some((color) => color === raw)) return raw as AccentColor;\n } catch {\n // ignore unavailable storage\n }\n return 'brown';\n}\n\nexport function saveAccentColor(value: AccentColor): void {\n try {\n localStorage.setItem(ACCENT_STORAGE_KEY, value);\n } catch {\n // ignore quota errors\n }\n}\n","import type { WriteCanvasSettings } from '@bendyline/squisq-editor-react';\n\nexport type WriteCanvasPreferences = Required<WriteCanvasSettings>;\n\nexport const WRITE_CANVAS_TEXT_SIZE_MIN = 12;\nexport const WRITE_CANVAS_TEXT_SIZE_MAX = 32;\nexport const WRITE_CANVAS_LINE_SPACING_MIN = 1.2;\nexport const WRITE_CANVAS_LINE_SPACING_MAX = 2.4;\n\nexport const DEFAULT_WRITE_CANVAS_PREFERENCES: Readonly<WriteCanvasPreferences> = {\n textSize: 16,\n lineSpacing: 1.7,\n};\n\nconst WRITE_CANVAS_STORAGE_KEY = 'docblocks:writeCanvasSettings';\n\nexport function loadWriteCanvasPreferences(): WriteCanvasPreferences {\n try {\n const raw = localStorage.getItem(WRITE_CANVAS_STORAGE_KEY);\n if (!raw) return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };\n\n const stored: unknown = JSON.parse(raw);\n if (!isRecord(stored)) return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };\n\n return {\n textSize: numberInRange(\n stored.textSize,\n WRITE_CANVAS_TEXT_SIZE_MIN,\n WRITE_CANVAS_TEXT_SIZE_MAX,\n )\n ? stored.textSize\n : DEFAULT_WRITE_CANVAS_PREFERENCES.textSize,\n lineSpacing: numberInRange(\n stored.lineSpacing,\n WRITE_CANVAS_LINE_SPACING_MIN,\n WRITE_CANVAS_LINE_SPACING_MAX,\n )\n ? stored.lineSpacing\n : DEFAULT_WRITE_CANVAS_PREFERENCES.lineSpacing,\n };\n } catch {\n return { ...DEFAULT_WRITE_CANVAS_PREFERENCES };\n }\n}\n\nexport function saveWriteCanvasPreferences(value: WriteCanvasPreferences): void {\n try {\n localStorage.setItem(WRITE_CANVAS_STORAGE_KEY, JSON.stringify(value));\n } catch {\n // Ignore unavailable storage and quota errors; the in-memory choice still works.\n }\n}\n\nfunction numberInRange(value: unknown, min: number, max: number): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= min && value <= max;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n","/** Shared controls used by the app and host-specific DocBlocks settings dialogs. */\n\nimport type { ReactNode } from 'react';\nimport { Dialog } from '../components/Dialog.js';\nimport { ACCENT_COLORS, type AccentColor, type ThemePreference } from '../preferences/theme.js';\nimport {\n WRITE_CANVAS_LINE_SPACING_MAX,\n WRITE_CANVAS_LINE_SPACING_MIN,\n WRITE_CANVAS_TEXT_SIZE_MAX,\n WRITE_CANVAS_TEXT_SIZE_MIN,\n type WriteCanvasPreferences,\n} from '../preferences/write-canvas.js';\n\nexport type { AccentColor, ThemePreference } from '../preferences/theme.js';\n\nconst ACCENT_LABELS: Record<AccentColor, string> = {\n brown: 'Brown',\n green: 'Green',\n blue: 'Blue',\n purple: 'Purple',\n maroon: 'Maroon',\n orange: 'Orange',\n gray: 'Gray',\n};\n\nexport interface SettingsDialogProps {\n title?: string;\n onClose: () => void;\n children: ReactNode;\n}\n\nexport function SettingsDialog({ title = 'Settings', onClose, children }: SettingsDialogProps) {\n return (\n <Dialog title={title} onClose={onClose} size=\"wide\">\n {children}\n </Dialog>\n );\n}\n\nexport interface ThemeSettingsProps {\n value: ThemePreference;\n onChange: (theme: ThemePreference) => void;\n name?: string;\n}\n\nexport function ThemeSettings({ value, onChange, name = 'theme' }: ThemeSettingsProps) {\n return (\n <fieldset className=\"db-settings-fieldset\">\n <legend className=\"db-settings-legend\">Theme</legend>\n <label className=\"db-settings-radio\">\n <input\n type=\"radio\"\n name={name}\n value=\"auto\"\n checked={value === 'auto'}\n onChange={() => onChange('auto')}\n />\n System default\n </label>\n <label className=\"db-settings-radio\">\n <input\n type=\"radio\"\n name={name}\n value=\"light\"\n checked={value === 'light'}\n onChange={() => onChange('light')}\n />\n Light\n </label>\n <label className=\"db-settings-radio\">\n <input\n type=\"radio\"\n name={name}\n value=\"dark\"\n checked={value === 'dark'}\n onChange={() => onChange('dark')}\n />\n Dark\n </label>\n </fieldset>\n );\n}\n\nexport interface AccentColorSettingsProps {\n value: AccentColor;\n onChange: (color: AccentColor) => void;\n name?: string;\n}\n\nexport function AccentColorSettings({\n value,\n onChange,\n name = 'accent-color',\n}: AccentColorSettingsProps) {\n return (\n <fieldset className=\"db-settings-fieldset\">\n <legend className=\"db-settings-legend\">Accent color</legend>\n <p className=\"db-settings-hint\">Used in both light and dark appearances.</p>\n <div className=\"db-settings-accent-grid\">\n {ACCENT_COLORS.map((color) => (\n <label\n className={`db-settings-accent${\n value === color ? ' db-settings-accent--selected' : ''\n }`}\n key={color}\n >\n <input\n className=\"db-settings-accent-input\"\n type=\"radio\"\n name={name}\n value={color}\n checked={value === color}\n onChange={() => onChange(color)}\n />\n <span\n className={`db-settings-accent-swatch db-settings-accent-swatch--${color}`}\n aria-hidden=\"true\"\n />\n <span>{ACCENT_LABELS[color]}</span>\n </label>\n ))}\n </div>\n </fieldset>\n );\n}\n\nexport interface WriteCanvasSettingsControlsProps {\n value: WriteCanvasPreferences;\n onChange: (settings: WriteCanvasPreferences) => void;\n}\n\nexport function WriteCanvasSettingsControls({ value, onChange }: WriteCanvasSettingsControlsProps) {\n return (\n <fieldset className=\"db-settings-fieldset\">\n <legend className=\"db-settings-legend\">Write canvas</legend>\n <p className=\"db-settings-hint\">\n Adjust the writing view without changing the document or its exported text.\n </p>\n <label className=\"db-settings-slider\">\n <span className=\"db-settings-slider-header\">\n <span>Text size</span>\n <output className=\"db-settings-slider-value\" aria-hidden=\"true\">\n {value.textSize}px\n </output>\n </span>\n <input\n type=\"range\"\n min={WRITE_CANVAS_TEXT_SIZE_MIN}\n max={WRITE_CANVAS_TEXT_SIZE_MAX}\n step={1}\n value={value.textSize}\n aria-label=\"Text size\"\n aria-valuetext={`${value.textSize} pixels`}\n onChange={(event) => onChange({ ...value, textSize: Number(event.currentTarget.value) })}\n />\n </label>\n <label className=\"db-settings-slider\">\n <span className=\"db-settings-slider-header\">\n <span>Line spacing</span>\n <output className=\"db-settings-slider-value\" aria-hidden=\"true\">\n {formatLineSpacing(value.lineSpacing)}\n </output>\n </span>\n <input\n type=\"range\"\n min={WRITE_CANVAS_LINE_SPACING_MIN}\n max={WRITE_CANVAS_LINE_SPACING_MAX}\n step={0.1}\n value={value.lineSpacing}\n aria-label=\"Line spacing\"\n aria-valuetext={`${value.lineSpacing} times`}\n onChange={(event) =>\n onChange({ ...value, lineSpacing: Number(event.currentTarget.value) })\n }\n />\n </label>\n </fieldset>\n );\n}\n\nfunction formatLineSpacing(value: number): string {\n return `${value.toFixed(1).replace(/\\.0$/, '')}\\u00d7`;\n}\n"],"mappings":";;;;;AAIO,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAYO,IAAM,mBAAqD;AAAA,EAChE,OAAO;AAAA,EACP,MAAM;AACR;AAEA,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAEpB,SAAS,sBAAuC;AACrD,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,iBAAiB;AAClD,QAAI,QAAQ,WAAW,QAAQ,UAAU,QAAQ,OAAQ,QAAO;AAAA,EAClE,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,SAAS,oBAAoB,OAA8B;AAChE,MAAI;AACF,iBAAa,QAAQ,mBAAmB,KAAK;AAAA,EAC/C,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,kBAA+B;AAC7C,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,kBAAkB;AACnD,QAAI,cAAc,KAAK,CAAC,UAAU,UAAU,GAAG,EAAG,QAAO;AAAA,EAC3D,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB,OAA0B;AACxD,MAAI;AACF,iBAAa,QAAQ,oBAAoB,KAAK;AAAA,EAChD,QAAQ;AAAA,EAER;AACF;;;AC9DO,IAAM,6BAA6B;AACnC,IAAM,6BAA6B;AACnC,IAAM,gCAAgC;AACtC,IAAM,gCAAgC;AAEtC,IAAM,mCAAqE;AAAA,EAChF,UAAU;AAAA,EACV,aAAa;AACf;AAEA,IAAM,2BAA2B;AAE1B,SAAS,6BAAqD;AACnE,MAAI;AACF,UAAM,MAAM,aAAa,QAAQ,wBAAwB;AACzD,QAAI,CAAC,IAAK,QAAO,EAAE,GAAG,iCAAiC;AAEvD,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,CAAC,SAAS,MAAM,EAAG,QAAO,EAAE,GAAG,iCAAiC;AAEpE,WAAO;AAAA,MACL,UAAU;AAAA,QACR,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF,IACI,OAAO,WACP,iCAAiC;AAAA,MACrC,aAAa;AAAA,QACX,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF,IACI,OAAO,cACP,iCAAiC;AAAA,IACvC;AAAA,EACF,QAAQ;AACN,WAAO,EAAE,GAAG,iCAAiC;AAAA,EAC/C;AACF;AAEO,SAAS,2BAA2B,OAAqC;AAC9E,MAAI;AACF,iBAAa,QAAQ,0BAA0B,KAAK,UAAU,KAAK,CAAC;AAAA,EACtE,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,cAAc,OAAgB,KAAa,KAA8B;AAChF,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS,OAAO,SAAS;AACzF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;;;AC1BI,cAgBE,YAhBF;AAlBJ,IAAM,gBAA6C;AAAA,EACjD,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AACR;AAQO,SAAS,eAAe,EAAE,QAAQ,YAAY,SAAS,SAAS,GAAwB;AAC7F,SACE,oBAAC,UAAO,OAAc,SAAkB,MAAK,QAC1C,UACH;AAEJ;AAQO,SAAS,cAAc,EAAE,OAAO,UAAU,OAAO,QAAQ,GAAuB;AACrF,SACE,qBAAC,cAAS,WAAU,wBAClB;AAAA,wBAAC,YAAO,WAAU,sBAAqB,mBAAK;AAAA,IAC5C,qBAAC,WAAM,WAAU,qBACf;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA,OAAM;AAAA,UACN,SAAS,UAAU;AAAA,UACnB,UAAU,MAAM,SAAS,MAAM;AAAA;AAAA,MACjC;AAAA,MAAE;AAAA,OAEJ;AAAA,IACA,qBAAC,WAAM,WAAU,qBACf;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA,OAAM;AAAA,UACN,SAAS,UAAU;AAAA,UACnB,UAAU,MAAM,SAAS,OAAO;AAAA;AAAA,MAClC;AAAA,MAAE;AAAA,OAEJ;AAAA,IACA,qBAAC,WAAM,WAAU,qBACf;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL;AAAA,UACA,OAAM;AAAA,UACN,SAAS,UAAU;AAAA,UACnB,UAAU,MAAM,SAAS,MAAM;AAAA;AAAA,MACjC;AAAA,MAAE;AAAA,OAEJ;AAAA,KACF;AAEJ;AAQO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA,OAAO;AACT,GAA6B;AAC3B,SACE,qBAAC,cAAS,WAAU,wBAClB;AAAA,wBAAC,YAAO,WAAU,sBAAqB,0BAAY;AAAA,IACnD,oBAAC,OAAE,WAAU,oBAAmB,sDAAwC;AAAA,IACxE,oBAAC,SAAI,WAAU,2BACZ,wBAAc,IAAI,CAAC,UAClB;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,qBACT,UAAU,QAAQ,kCAAkC,EACtD;AAAA,QAGA;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL;AAAA,cACA,OAAO;AAAA,cACP,SAAS,UAAU;AAAA,cACnB,UAAU,MAAM,SAAS,KAAK;AAAA;AAAA,UAChC;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,wDAAwD,KAAK;AAAA,cACxE,eAAY;AAAA;AAAA,UACd;AAAA,UACA,oBAAC,UAAM,wBAAc,KAAK,GAAE;AAAA;AAAA;AAAA,MAdvB;AAAA,IAeP,CACD,GACH;AAAA,KACF;AAEJ;AAOO,SAAS,4BAA4B,EAAE,OAAO,SAAS,GAAqC;AACjG,SACE,qBAAC,cAAS,WAAU,wBAClB;AAAA,wBAAC,YAAO,WAAU,sBAAqB,0BAAY;AAAA,IACnD,oBAAC,OAAE,WAAU,oBAAmB,yFAEhC;AAAA,IACA,qBAAC,WAAM,WAAU,sBACf;AAAA,2BAAC,UAAK,WAAU,6BACd;AAAA,4BAAC,UAAK,uBAAS;AAAA,QACf,qBAAC,YAAO,WAAU,4BAA2B,eAAY,QACtD;AAAA,gBAAM;AAAA,UAAS;AAAA,WAClB;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,cAAW;AAAA,UACX,kBAAgB,GAAG,MAAM,QAAQ;AAAA,UACjC,UAAU,CAAC,UAAU,SAAS,EAAE,GAAG,OAAO,UAAU,OAAO,MAAM,cAAc,KAAK,EAAE,CAAC;AAAA;AAAA,MACzF;AAAA,OACF;AAAA,IACA,qBAAC,WAAM,WAAU,sBACf;AAAA,2BAAC,UAAK,WAAU,6BACd;AAAA,4BAAC,UAAK,0BAAY;AAAA,QAClB,oBAAC,YAAO,WAAU,4BAA2B,eAAY,QACtD,4BAAkB,MAAM,WAAW,GACtC;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,cAAW;AAAA,UACX,kBAAgB,GAAG,MAAM,WAAW;AAAA,UACpC,UAAU,CAAC,UACT,SAAS,EAAE,GAAG,OAAO,aAAa,OAAO,MAAM,cAAc,KAAK,EAAE,CAAC;AAAA;AAAA,MAEzE;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,SAAS,kBAAkB,OAAuB;AAChD,SAAO,GAAG,MAAM,QAAQ,CAAC,EAAE,QAAQ,QAAQ,EAAE,CAAC;AAChD;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Dialog.tsx"],"sourcesContent":["/**\n * Dialog — shared modal primitive.\n *\n * Promotes the `db-dialog-overlay` / `db-dialog` pattern used by the\n * settings dialogs into a reusable component, adding aria-modal and\n * initial focus. Escape and (by default) backdrop clicks close it.\n */\n\nimport React, { useCallback, useEffect, useId, useRef } from 'react';\n\nexport interface DialogProps {\n title: string;\n onClose: () => void;\n /** default 380px / wide 560px / full min(1100px, 94vw) column. */\n size?: 'default' | 'wide' | 'full';\n children: React.ReactNode;\n /** Right-aligned action row below the body. */\n footer?: React.ReactNode;\n /** Element to focus on open; defaults to the close button. */\n initialFocusRef?: React.RefObject<HTMLElement | null>;\n /** Set false when a stray backdrop click would lose user input. */\n closeOnBackdrop?: boolean;\n /** Additional class on the dialog panel. */\n className?: string;\n /** Additional class on the dialog body. */\n bodyClassName?: string;\n /** Additional class on the footer wrapper. */\n footerClassName?: string;\n}\n\nexport function Dialog({\n title,\n onClose,\n size = 'default',\n children,\n footer,\n initialFocusRef,\n closeOnBackdrop = true,\n className,\n bodyClassName,\n footerClassName,\n}: DialogProps) {\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n const dialogRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n return;\n }\n if (e.key !== 'Tab') return;\n\n const dialog = dialogRef.current;\n if (!dialog) return;\n const focusable = [...dialog.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)].filter(\n (element) =>\n !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true',\n );\n if (focusable.length === 0) {\n e.preventDefault();\n dialog.focus();\n return;\n }\n\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n if (e.shiftKey && document.activeElement === first) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && document.activeElement === last) {\n e.preventDefault();\n first.focus();\n }\n };\n window.addEventListener('keydown', onKey);\n return () => window.removeEventListener('keydown', onKey);\n }, [onClose]);\n\n useEffect(() => {\n const previousFocus =\n document.activeElement instanceof HTMLElement ? document.activeElement : null;\n const target = initialFocusRef?.current ?? closeButtonRef.current;\n target?.focus();\n return () => {\n if (previousFocus?.isConnected) previousFocus.focus();\n };\n // Focus only on mount — refocusing on every re-render would fight the user.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent) => {\n if (closeOnBackdrop && e.target === e.currentTarget) onClose();\n },\n [closeOnBackdrop, onClose],\n );\n\n const sizeClass = size === 'wide' ? 'db-dialog--wide' : size === 'full' ? 'db-dialog--full' : '';\n const panelClassName = ['db-dialog', sizeClass, className].filter(Boolean).join(' ');\n const bodyClasses = ['db-dialog-body', bodyClassName].filter(Boolean).join(' ');\n const footerClasses = ['db-dialog-footer', footerClassName].filter(Boolean).join(' ');\n\n return (\n <div className=\"db-dialog-overlay\" onClick={handleBackdropClick}>\n <div\n ref={dialogRef}\n className={panelClassName}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n tabIndex={-1}\n >\n <div className=\"db-dialog-header\">\n <h2 id={titleId} className=\"db-dialog-title\">\n {title}\n </h2>\n <button\n ref={closeButtonRef}\n type=\"button\"\n className=\"db-dialog-close\"\n onClick={onClose}\n aria-label=\"Close\"\n >\n &times;\n </button>\n </div>\n <div className={bodyClasses}>{children}</div>\n {footer && <div className={footerClasses}>{footer}</div>}\n </div>\n </div>\n );\n}\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button',\n 'input',\n 'select',\n 'textarea',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/Export/ExportDialog.tsx"],"sourcesContent":["/**\n * ExportDialog — modal for choosing export format and options.\n */\n\nimport { useState, useCallback, useEffect, useMemo } from 'react';\nimport { getThemeSummaries } from '@bendyline/squisq/schemas';\nimport type { ExportFormat, ExportOptions, HtmlBundle, HtmlStyle } from './export-options.js';\nimport { FORMAT_LABELS, saveExportOptions } from './export-options.js';\nimport { loadTransformStyleSummaries, type ExportSummaryOption } from './transform-summaries.js';\nimport { Dialog } from '../components/Dialog.js';\n\nexport interface ExportDialogProps {\n /** Initial options (pre-populated from last export). */\n initial: ExportOptions;\n /** Whether the export is currently running. */\n exporting: boolean;\n /**\n * Message from a failed export attempt. Rendered in the dialog, which\n * stays open so the user can adjust options and retry — a failure must\n * never look like a success.\n */\n error?: string | null;\n /** Optional host-provided destination path control. */\n destination?: ExportDestinationControl;\n /** Called when the user confirms the export. */\n onExport: (options: ExportOptions) => void;\n /** Called whenever the currently selected options change. */\n onOptionsChange?: (options: ExportOptions) => void;\n /** Called when the dialog is dismissed. */\n onClose: () => void;\n}\n\nexport interface ExportDestinationControl {\n value: string;\n onPick: (options: ExportOptions) => void | Promise<void>;\n hint?: string;\n}\n\nconst FORMATS: ExportFormat[] = ['pdf', 'docx', 'pptx', 'html', 'md'];\n\n/** Short labels for the Format chip row. The fuller `FORMAT_LABELS` list\n * is still used as a tooltip so users can confirm the file extension. */\nconst FORMAT_CHIP_LABELS: Record<ExportFormat, string> = {\n pdf: 'PDF',\n docx: 'Word',\n pptx: 'PowerPoint',\n html: 'HTML',\n md: 'Markdown',\n};\n\nconst HTML_STYLE_CHIPS: { key: HtmlStyle; label: string; hint: string }[] = [\n {\n key: 'plain',\n label: 'Plain',\n hint: 'A lightweight static HTML document.',\n },\n {\n key: 'rendered',\n label: 'Rendered',\n hint: 'Renders via SquisqPlayer with themes and playback support.',\n },\n];\n\nconst HTML_BUNDLE_CHIPS: { key: HtmlBundle; label: string; hint: string }[] = [\n {\n key: 'single',\n label: 'Single file',\n hint: 'One .html file with images embedded as base64 data URIs.',\n },\n {\n key: 'zip',\n label: 'ZIP archive',\n hint: 'A .zip with index.html plus separate image (and JS) files.',\n },\n];\n\ninterface ChipOption<T extends string> {\n key: T;\n label: string;\n title?: string;\n}\n\nfunction ChipRadioGroup<T extends string>({\n name,\n value,\n options,\n onChange,\n}: {\n name: string;\n value: T;\n options: ChipOption<T>[];\n onChange: (next: T) => void;\n}) {\n return (\n <div className=\"db-export-chips\" role=\"radiogroup\" aria-label={name}>\n {options.map((opt) => {\n const active = opt.key === value;\n return (\n <button\n key={opt.key}\n type=\"button\"\n role=\"radio\"\n aria-checked={active}\n className={`db-export-chip${active ? ' db-export-chip--active' : ''}`}\n onClick={() => onChange(opt.key)}\n title={opt.title}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nexport function ExportDialog({\n initial,\n exporting,\n error,\n destination,\n onExport,\n onOptionsChange,\n onClose,\n}: ExportDialogProps) {\n const [format, setFormat] = useState<ExportFormat>(initial.format);\n const [themeId, setThemeId] = useState(initial.themeId);\n const [transformStyle, setTransformStyle] = useState(initial.transformStyle);\n const [pageSize, setPageSize] = useState(initial.pageSize);\n const [htmlStyle, setHtmlStyle] = useState<HtmlStyle>(initial.htmlStyle);\n const [htmlBundle, setHtmlBundle] = useState<HtmlBundle>(initial.htmlBundle);\n const [includeLinkedDocs, setIncludeLinkedDocs] = useState<boolean>(initial.includeLinkedDocs);\n const [entryAsIndex, setEntryAsIndex] = useState<boolean>(initial.entryAsIndex);\n const [transforms, setTransforms] = useState<ExportSummaryOption[]>([]);\n\n const themes = getThemeSummaries();\n\n // Both HTML styles honor themes now: rendered HTML through the\n // SquisqPlayer's theme system, plain HTML through squisq's\n // `markdownDocToPlainHtml` which emits theme-driven CSS variables\n // and Google Fonts links. Show the dropdown for any export format\n // whose pipeline actually applies the picked theme.\n const showTheme = format === 'docx' || format === 'pdf' || format === 'pptx' || format === 'html';\n const showTransform = format === 'pptx';\n const showPageSize = format === 'pdf';\n const showHtmlOptions = format === 'html';\n // Both HTML styles now have a recursive bundle: plain via\n // `markdownDocsToPlainHtmlBundle`, rendered via\n // `markdownDocsToHtmlBundle` (which rewrites Doc-tree links to\n // `.html` before SquisqPlayer serialization). So the option applies\n // to any HTML export.\n const showIncludeLinked = format === 'html';\n // Multi-doc output is inherently a directory tree, so when recursion\n // is on the Bundle row would only confuse — ZIP is the only valid\n // packaging. Hide the chip row and surface the implication in a hint.\n const showHtmlBundle = showHtmlOptions && !includeLinkedDocs;\n // `entryAsIndex` is only honored in pipelines where we control the\n // output filename ourselves:\n // • single-file HTML downloads → renames `<doc>.html` → `index.html`\n // • recursive bundles → passed through to squisq's bundle helpers\n // In the single-doc ZIP path, squisq's `docToHtmlZip` and our plain-\n // HTML zip writer always emit `index.html` inside the archive, so the\n // checkbox has no observable effect — hide it to avoid surfacing an\n // inert control.\n const showEntryAsIndex = showHtmlOptions && (htmlBundle === 'single' || includeLinkedDocs);\n\n useEffect(() => {\n if (!showTransform) return;\n let cancelled = false;\n\n loadTransformStyleSummaries()\n .then((nextTransforms) => {\n if (!cancelled) setTransforms(nextTransforms);\n })\n .catch(() => {\n if (!cancelled) setTransforms([]);\n });\n\n return () => {\n cancelled = true;\n };\n }, [showTransform]);\n\n const currentOptions = useMemo<ExportOptions>(\n () => ({\n format,\n themeId,\n transformStyle,\n pageSize,\n htmlStyle,\n htmlBundle,\n includeLinkedDocs: showIncludeLinked && includeLinkedDocs,\n entryAsIndex: showEntryAsIndex && entryAsIndex,\n }),\n [\n format,\n themeId,\n transformStyle,\n pageSize,\n htmlStyle,\n htmlBundle,\n includeLinkedDocs,\n entryAsIndex,\n showIncludeLinked,\n showEntryAsIndex,\n ],\n );\n\n useEffect(() => {\n onOptionsChange?.(currentOptions);\n }, [currentOptions, onOptionsChange]);\n\n const handleExport = useCallback(() => {\n saveExportOptions(currentOptions);\n onExport(currentOptions);\n }, [currentOptions, onExport]);\n\n return (\n <Dialog\n title=\"Export Document\"\n onClose={onClose}\n className=\"db-export-dialog\"\n footerClassName=\"db-export-footer\"\n footer={\n <>\n <button\n type=\"button\"\n className=\"db-export-btn db-export-btn--secondary\"\n onClick={onClose}\n >\n Cancel\n </button>\n <button\n type=\"button\"\n className=\"db-export-btn db-export-btn--primary\"\n onClick={handleExport}\n disabled={exporting}\n >\n {exporting ? 'Exporting...' : 'Export'}\n </button>\n </>\n }\n >\n {/* Format — chip radio row for fast switching */}\n <div className=\"db-export-field\">\n <span className=\"db-export-label\">Format</span>\n <ChipRadioGroup\n name=\"Format\"\n value={format}\n options={FORMATS.map((f) => ({\n key: f,\n label: FORMAT_CHIP_LABELS[f],\n title: FORMAT_LABELS[f],\n }))}\n onChange={setFormat}\n />\n </div>\n\n {destination && (\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-export-path\">\n Export to\n </label>\n <div className=\"db-export-path-row\">\n <input\n id=\"db-export-path\"\n className=\"db-export-path-input\"\n value={destination.value}\n readOnly\n disabled={exporting}\n spellCheck={false}\n autoComplete=\"off\"\n />\n <button\n type=\"button\"\n className=\"db-export-path-pick\"\n onClick={() => void destination.onPick(currentOptions)}\n disabled={exporting}\n aria-label=\"Choose export location\"\n title=\"Choose export location\"\n >\n ...\n </button>\n </div>\n {destination.hint && <span className=\"db-export-hint\">{destination.hint}</span>}\n </div>\n )}\n\n {/* HTML style + bundle (HTML only) — also chip rows */}\n {showHtmlOptions && (\n <>\n <div className=\"db-export-field\">\n <span className=\"db-export-label\">Style</span>\n <ChipRadioGroup\n name=\"Style\"\n value={htmlStyle}\n options={HTML_STYLE_CHIPS}\n onChange={setHtmlStyle}\n />\n <span className=\"db-export-hint\">\n {HTML_STYLE_CHIPS.find((c) => c.key === htmlStyle)?.hint}\n </span>\n </div>\n {showHtmlBundle && (\n <div className=\"db-export-field\">\n <span className=\"db-export-label\">Bundle</span>\n <ChipRadioGroup\n name=\"Bundle\"\n value={htmlBundle}\n options={HTML_BUNDLE_CHIPS}\n onChange={setHtmlBundle}\n />\n <span className=\"db-export-hint\">\n {HTML_BUNDLE_CHIPS.find((c) => c.key === htmlBundle)?.hint}\n </span>\n </div>\n )}\n {showIncludeLinked && (\n <div className=\"db-export-field\">\n <label className=\"db-export-checkbox\">\n <input\n type=\"checkbox\"\n checked={includeLinkedDocs}\n onChange={(e) => setIncludeLinkedDocs(e.target.checked)}\n />\n <span>Include linked-to documents</span>\n </label>\n <span className=\"db-export-hint\">\n {includeLinkedDocs\n ? 'Follows relative .md links from this page and bundles every reachable document as its own .html in a ZIP. Cross-doc links rewrite from .md to .html.'\n : 'Only this document is exported.'}\n </span>\n </div>\n )}\n {showEntryAsIndex && (\n <div className=\"db-export-field\">\n <label className=\"db-export-checkbox\">\n <input\n type=\"checkbox\"\n checked={entryAsIndex}\n onChange={(e) => setEntryAsIndex(e.target.checked)}\n />\n <span>Name entry page index.html</span>\n </label>\n <span className=\"db-export-hint\">\n {entryAsIndex\n ? includeLinkedDocs\n ? 'Renames this page to index.html in the ZIP and rewrites any cross-doc links pointing back at it. Drops straight into a static-site host.'\n : 'Downloads as index.html instead of the document name. Drops straight into a static-site host.'\n : 'Exports under the document name.'}\n </span>\n </div>\n )}\n </>\n )}\n\n {/* Theme */}\n {showTheme && (\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-export-theme\">\n Theme\n </label>\n <select\n id=\"db-export-theme\"\n className=\"db-export-select\"\n value={themeId}\n onChange={(e) => setThemeId(e.target.value)}\n >\n {themes.map((t) => (\n <option key={t.id} value={t.id}>\n {t.name}\n </option>\n ))}\n </select>\n <span className=\"db-export-hint\">\n {themes.find((t) => t.id === themeId)?.description}\n </span>\n </div>\n )}\n\n {/* Transform (PPTX only) */}\n {showTransform && (\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-export-transform\">\n Transform\n </label>\n <select\n id=\"db-export-transform\"\n className=\"db-export-select\"\n value={transformStyle}\n onChange={(e) => setTransformStyle(e.target.value)}\n >\n {transforms.length === 0 ? (\n <option value={transformStyle}>Loading...</option>\n ) : (\n transforms.map((t) => (\n <option key={t.id} value={t.id}>\n {t.name}\n </option>\n ))\n )}\n </select>\n <span className=\"db-export-hint\">\n {transforms.find((t) => t.id === transformStyle)?.description}\n </span>\n </div>\n )}\n\n {/* Page size (PDF only) */}\n {showPageSize && (\n <div className=\"db-export-field\">\n <label className=\"db-export-label\" htmlFor=\"db-export-pagesize\">\n Page Size\n </label>\n <select\n id=\"db-export-pagesize\"\n className=\"db-export-select\"\n value={pageSize}\n onChange={(e) => setPageSize(e.target.value as 'letter' | 'a4')}\n >\n <option value=\"letter\">US Letter</option>\n <option value=\"a4\">A4</option>\n </select>\n </div>\n )}\n\n {error && (\n <p className=\"db-export-error\" role=\"alert\">\n {error}\n </p>\n )}\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/editor.ts"],"sourcesContent":["/**\n * Placeholder prompts shown by DocBlocks when a Markdown document is empty.\n * Hosts pick one prompt for each mounted document generation and pass it to\n * Squisq, so the copy remains stable while that document is open.\n */\nexport const EMPTY_DOCUMENT_PROMPTS = [\n 'Start typing your content, or drop images on top of me...',\n 'Write anything -- paste markdown, drag in images, or just start typing...',\n 'Type away. Markdown syntax works too...',\n 'Chapter 1 begins here...',\n 'Once upon a time...',\n \"A blank page. Exciting, isn't it?\",\n 'The first word is always the hardest...',\n 'Plot twist: this is where it all starts...',\n 'Write something the future you will thank you for...',\n 'Begin at the beginning...',\n \"Let's begin.\",\n 'We can be heroes...',\n 'Here comes the sun...',\n 'And so it begins...',\n 'Let it be, let it be...',\n \"Don't stop believing...\",\n 'Started from the bottom...',\n 'This is the start of something new...',\n 'Ground control to Major Tom...',\n 'Come as you are...',\n 'Every new beginning...',\n \"Hello, is it me you're looking for?\",\n 'Imagine all the people...',\n 'Anything you want, you got it...',\n 'Rise above...',\n 'Do you have the time...',\n 'Here we are now, entertain us...',\n 'All the small things...',\n 'Should I stay or should I go?',\n 'I am a patient boy -- I wait, I wait, I wait...',\n 'Destination unknown...',\n 'Easy like Sunday morning...',\n 'Dance, dance, dance to the radio...',\n 'New day rising...',\n 'Visit the world...',\n 'Positive mental attitude...',\n 'Better I sing than I cry...',\n 'Disconnect for a minute...',\n 'We need new noise...',\n 'Shoot the moon...',\n \"I'll surprise you sometime -- I'll come around...\",\n 'Coming out of my cage, and doing just fine...',\n \"Throw me to the wolves -- I'll come back, leading the pack...\",\n 'There comes a time -- no excuses...',\n \"It's up to me now, turn on the bright lights...\",\n 'Do what you want...',\n 'Fingers crossed...',\n 'This is the start of how it all ends...',\n 'It has to start somewhere, it has to start sometime -- what better place than here? what better time than now?',\n \"I know myself, and I'll begin again...\",\n 'Dig me out, dig me in...',\n \"Everybody's full of sugar, honey, ice and tea...\",\n \"Take time with a wounded hand, 'cause it likes to heal...\",\n 'Grab a brush and put on a little makeup...',\n 'Amber is the color of your energy...',\n \"Wait -- they don't love it like I love it...\",\n \"A world's been buried, where love is the law; a you-topia...\",\n] as const;\n\nexport type EmptyDocumentPrompt = (typeof EMPTY_DOCUMENT_PROMPTS)[number];\n\n/** Pick a prompt using the supplied random source, or `Math.random`. */\nexport function pickEmptyDocumentPrompt(random: () => number = Math.random): EmptyDocumentPrompt {\n const index = Math.floor(random() * EMPTY_DOCUMENT_PROMPTS.length);\n return EMPTY_DOCUMENT_PROMPTS[index] ?? EMPTY_DOCUMENT_PROMPTS[0];\n}\n"],"mappings":";AAKO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,SAAS,wBAAwB,SAAuB,KAAK,QAA6B;AAC/F,QAAM,QAAQ,KAAK,MAAM,OAAO,IAAI,uBAAuB,MAAM;AACjE,SAAO,uBAAuB,KAAK,KAAK,uBAAuB,CAAC;AAClE;","names":[]}