@bendyline/docblocks-react 2.2.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ExportDialog-VYATD5UC.js → ExportDialog-HPFPXELL.js} +1 -1
- package/dist/{ExportToolbarControls-C46GMPKC.js → ExportToolbarControls-2VB43ITO.js} +1 -6
- package/dist/{chunk-3CZ27FOA.js → chunk-MHTQORMH.js} +1 -17
- package/dist/export/index.d.ts +1 -3
- package/dist/export/index.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +253 -63
- package/package.json +9 -9
- package/src/styles/docblocks.css +29 -12
|
@@ -26,7 +26,7 @@ import { getThemeSummaries } from "@bendyline/squisq/schemas";
|
|
|
26
26
|
import { parseMarkdown } from "@bendyline/squisq/markdown";
|
|
27
27
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
28
28
|
var ExportDialog = lazy(
|
|
29
|
-
() => import("./ExportDialog-
|
|
29
|
+
() => import("./ExportDialog-HPFPXELL.js").then((module) => ({ default: module.ExportDialog }))
|
|
30
30
|
);
|
|
31
31
|
var ShareDialog = lazy(
|
|
32
32
|
() => import("./ShareDialog-PAMOQQFB.js").then((module) => ({ default: module.ShareDialog }))
|
|
@@ -225,10 +225,6 @@ function ExportToolbarControls({
|
|
|
225
225
|
},
|
|
226
226
|
[markdownSource, videoModules]
|
|
227
227
|
);
|
|
228
|
-
const handleOpenAnimatedGifFromDialog = useCallback(() => {
|
|
229
|
-
setDialogOpen(false);
|
|
230
|
-
void handleOpenVideoModal("gif");
|
|
231
|
-
}, [handleOpenVideoModal]);
|
|
232
228
|
const handleCloseVideoModal = useCallback(() => {
|
|
233
229
|
setVideoModalOpen(false);
|
|
234
230
|
setVideoDoc(null);
|
|
@@ -438,7 +434,6 @@ function ExportToolbarControls({
|
|
|
438
434
|
} : void 0,
|
|
439
435
|
onExport: handleExport,
|
|
440
436
|
onOptionsChange: destinationAdapter ? handleOptionsChange : void 0,
|
|
441
|
-
onAnimatedGifExport: showAnimatedGifExport ? handleOpenAnimatedGifFromDialog : void 0,
|
|
442
437
|
onClose: handleCloseDialog
|
|
443
438
|
}
|
|
444
439
|
) }),
|
|
@@ -76,7 +76,6 @@ function ExportDialog({
|
|
|
76
76
|
destination,
|
|
77
77
|
onExport,
|
|
78
78
|
onOptionsChange,
|
|
79
|
-
onAnimatedGifExport,
|
|
80
79
|
onClose
|
|
81
80
|
}) {
|
|
82
81
|
const [format, setFormat] = useState(initial.format);
|
|
@@ -327,23 +326,8 @@ function ExportDialog({
|
|
|
327
326
|
}
|
|
328
327
|
)
|
|
329
328
|
] }),
|
|
330
|
-
onAnimatedGifExport && /* @__PURE__ */ jsxs("div", { className: "db-export-field", children: [
|
|
331
|
-
/* @__PURE__ */ jsx("span", { className: "db-export-label", children: "Animated media" }),
|
|
332
|
-
/* @__PURE__ */ jsx(
|
|
333
|
-
"button",
|
|
334
|
-
{
|
|
335
|
-
type: "button",
|
|
336
|
-
className: "db-export-btn db-export-btn--secondary",
|
|
337
|
-
onClick: onAnimatedGifExport,
|
|
338
|
-
children: "Animated GIF..."
|
|
339
|
-
}
|
|
340
|
-
),
|
|
341
|
-
/* @__PURE__ */ jsx("span", { className: "db-export-hint", children: "Opens timing, orientation, captions, looping, and color controls." })
|
|
342
|
-
] }),
|
|
343
329
|
/* @__PURE__ */ jsxs("p", { className: "db-export-more-formats", children: [
|
|
344
|
-
"Need spreadsheets, ",
|
|
345
|
-
onAnimatedGifExport ? "" : "animated media, ",
|
|
346
|
-
"portable bundles, or automation?",
|
|
330
|
+
"Need spreadsheets, animated media, portable bundles, or automation?",
|
|
347
331
|
" ",
|
|
348
332
|
/* @__PURE__ */ jsx("a", { href: "https://docblocks.com/cli/", target: "_blank", rel: "noopener noreferrer", children: "More formats with the DocBlocks CLI" }),
|
|
349
333
|
"."
|
package/dist/export/index.d.ts
CHANGED
|
@@ -81,8 +81,6 @@ interface ExportDialogProps {
|
|
|
81
81
|
onExport: (options: ExportOptions) => void;
|
|
82
82
|
/** Called whenever the currently selected options change. */
|
|
83
83
|
onOptionsChange?: (options: ExportOptions) => void;
|
|
84
|
-
/** Opens the richer animated-GIF export flow when the host supports ffmpeg.wasm. */
|
|
85
|
-
onAnimatedGifExport?: () => void;
|
|
86
84
|
/** Called when the dialog is dismissed. */
|
|
87
85
|
onClose: () => void;
|
|
88
86
|
}
|
|
@@ -95,7 +93,7 @@ interface ExportDestinationControl {
|
|
|
95
93
|
/** A host-supplied validation error for the current destination value. */
|
|
96
94
|
error?: string | null;
|
|
97
95
|
}
|
|
98
|
-
declare function ExportDialog({ initial, exporting, error, destination, onExport, onOptionsChange,
|
|
96
|
+
declare function ExportDialog({ initial, exporting, error, destination, onExport, onOptionsChange, onClose, }: ExportDialogProps): react_jsx_runtime.JSX.Element;
|
|
99
97
|
|
|
100
98
|
/** Keep a user-edited target name while switching the selected export format. */
|
|
101
99
|
declare function updateExportTargetExtension(targetPath: string, suggestedFilename: string): string;
|
package/dist/export/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -219,7 +219,7 @@ interface DocBlocksShellProps {
|
|
|
219
219
|
/**
|
|
220
220
|
* True when a new deploy is waiting (browser PWA hosts: a fresh service
|
|
221
221
|
* worker finished installing). The shell shows an "Update available"
|
|
222
|
-
* status-bar notice
|
|
222
|
+
* status-bar notice; clicking it opens a nearby Reload/Later prompt.
|
|
223
223
|
*/
|
|
224
224
|
updateAvailable?: boolean;
|
|
225
225
|
/** Activates the waiting update and reloads onto the new version. */
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from "./chunk-BPGEBGAN.js";
|
|
30
30
|
import {
|
|
31
31
|
ExportDialog
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-MHTQORMH.js";
|
|
33
33
|
import {
|
|
34
34
|
buildExportFilename,
|
|
35
35
|
runExport
|
|
@@ -1704,7 +1704,7 @@ import {
|
|
|
1704
1704
|
DocumentCommitConflictError,
|
|
1705
1705
|
DocumentSessionConflictError
|
|
1706
1706
|
} from "@bendyline/docblocks/document";
|
|
1707
|
-
import { isElectronHost as
|
|
1707
|
+
import { isElectronHost as isElectronHost3, getDocBlocksHost } from "@bendyline/docblocks/host";
|
|
1708
1708
|
import {
|
|
1709
1709
|
parseSharedDocumentHash
|
|
1710
1710
|
} from "@bendyline/docblocks/share";
|
|
@@ -1723,6 +1723,7 @@ import {
|
|
|
1723
1723
|
|
|
1724
1724
|
// src/AppMenu/AppMenu.tsx
|
|
1725
1725
|
import { useState as useState5, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
|
|
1726
|
+
import { isElectronHost as isElectronHost2 } from "@bendyline/docblocks/host";
|
|
1726
1727
|
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1727
1728
|
function formatBytes(bytes) {
|
|
1728
1729
|
if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
|
|
@@ -1757,6 +1758,7 @@ function AppMenu({
|
|
|
1757
1758
|
const [showAbout, setShowAbout] = useState5(false);
|
|
1758
1759
|
const [showSettings, setShowSettings] = useState5(false);
|
|
1759
1760
|
const [requestingPersistentStorage, setRequestingPersistentStorage] = useState5(false);
|
|
1761
|
+
const moreInformationUrl = isElectronHost2() ? "https://docblocks.com/desktop/" : "https://docblocks.com/web/";
|
|
1760
1762
|
const menuRef = useRef5(null);
|
|
1761
1763
|
const [storageEstimate, setStorageEstimate] = useState5(null);
|
|
1762
1764
|
useEffect5(() => {
|
|
@@ -1968,6 +1970,22 @@ function AppMenu({
|
|
|
1968
1970
|
/* @__PURE__ */ jsx5("strong", { children: "DocBlocks" }),
|
|
1969
1971
|
" is a local-first Markdown document editor. Your files stay under your control."
|
|
1970
1972
|
] }),
|
|
1973
|
+
/* @__PURE__ */ jsxs5("aside", { className: "db-about-beta", children: [
|
|
1974
|
+
/* @__PURE__ */ jsx5("strong", { children: "Beta Software." }),
|
|
1975
|
+
" We're still working through initial hiccups and issues. Please bear with us, make sure you keep backups, and",
|
|
1976
|
+
" ",
|
|
1977
|
+
/* @__PURE__ */ jsx5(
|
|
1978
|
+
"a",
|
|
1979
|
+
{
|
|
1980
|
+
href: "https://github.com/bendyline/docblocks/issues/new",
|
|
1981
|
+
target: "_blank",
|
|
1982
|
+
rel: "noopener noreferrer",
|
|
1983
|
+
children: "open issues"
|
|
1984
|
+
}
|
|
1985
|
+
),
|
|
1986
|
+
" ",
|
|
1987
|
+
"where you find them. Thanks!"
|
|
1988
|
+
] }),
|
|
1971
1989
|
appVersion && /* @__PURE__ */ jsxs5("p", { className: "db-about-version", children: [
|
|
1972
1990
|
/* @__PURE__ */ jsx5("span", { children: "Version" }),
|
|
1973
1991
|
/* @__PURE__ */ jsx5("code", { "aria-label": `DocBlocks version ${appVersion}`, children: appVersion })
|
|
@@ -1987,6 +2005,8 @@ function AppMenu({
|
|
|
1987
2005
|
"."
|
|
1988
2006
|
] }),
|
|
1989
2007
|
/* @__PURE__ */ jsxs5("p", { className: "db-dialog-links", children: [
|
|
2008
|
+
/* @__PURE__ */ jsx5("a", { href: moreInformationUrl, target: "_blank", rel: "noopener noreferrer", children: "More information..." }),
|
|
2009
|
+
/* @__PURE__ */ jsx5("span", { className: "db-dialog-sep", children: "\xB7" }),
|
|
1990
2010
|
/* @__PURE__ */ jsx5(
|
|
1991
2011
|
"a",
|
|
1992
2012
|
{
|
|
@@ -2260,7 +2280,7 @@ function useDocumentSession(autoSaveDelayMs = 500) {
|
|
|
2260
2280
|
import { lazy, Suspense } from "react";
|
|
2261
2281
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2262
2282
|
var ExportToolbarControlsImplementation = lazy(
|
|
2263
|
-
() => import("./ExportToolbarControls-
|
|
2283
|
+
() => import("./ExportToolbarControls-2VB43ITO.js").then((module) => ({
|
|
2264
2284
|
default: module.ExportToolbarControls
|
|
2265
2285
|
}))
|
|
2266
2286
|
);
|
|
@@ -2719,6 +2739,63 @@ async function decodeDbkWorkspace(bytes, options) {
|
|
|
2719
2739
|
return snapshot;
|
|
2720
2740
|
}
|
|
2721
2741
|
|
|
2742
|
+
// src/DocBlocksShell/editor-link-navigation.ts
|
|
2743
|
+
import { HOST_WIRE_LIMITS, isBoundedString, parseExternalHttpUrl } from "@bendyline/docblocks/host";
|
|
2744
|
+
var URI_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*:/;
|
|
2745
|
+
var MARKDOWN_EXTENSION = /\.(?:md|markdown|mdown)$/iu;
|
|
2746
|
+
function resolveShellEditorLinkTarget(href, documentPath) {
|
|
2747
|
+
if (!isBoundedString(href, HOST_WIRE_LIMITS.urlCharacters, 1)) return null;
|
|
2748
|
+
if (href.startsWith("#")) return { kind: "fragment" };
|
|
2749
|
+
const externalUrl = parseExternalHttpUrl(href);
|
|
2750
|
+
if (externalUrl) return { kind: "external", url: externalUrl };
|
|
2751
|
+
if (documentPath === null || href.startsWith("//") || URI_SCHEME.test(href)) return null;
|
|
2752
|
+
const splitAt = href.search(/[?#]/u);
|
|
2753
|
+
const encodedPath = splitAt === -1 ? href : href.slice(0, splitAt);
|
|
2754
|
+
if (!encodedPath) return null;
|
|
2755
|
+
let decodedPath;
|
|
2756
|
+
try {
|
|
2757
|
+
decodedPath = decodeURIComponent(encodedPath);
|
|
2758
|
+
} catch {
|
|
2759
|
+
return null;
|
|
2760
|
+
}
|
|
2761
|
+
if (decodedPath.includes("\\") || hasControlCharacter(decodedPath)) return null;
|
|
2762
|
+
const documentSegments = normalizeDocumentPath(documentPath);
|
|
2763
|
+
if (!documentSegments) return null;
|
|
2764
|
+
const targetSegments = decodedPath.startsWith("/") ? [] : documentSegments.slice(0, -1);
|
|
2765
|
+
for (const segment of decodedPath.split("/")) {
|
|
2766
|
+
if (segment === "" || segment === ".") continue;
|
|
2767
|
+
if (segment === "..") {
|
|
2768
|
+
if (targetSegments.length === 0) return null;
|
|
2769
|
+
targetSegments.pop();
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
if (segment.length > 255) return null;
|
|
2773
|
+
targetSegments.push(segment);
|
|
2774
|
+
}
|
|
2775
|
+
const path = targetSegments.join("/");
|
|
2776
|
+
if (!path || path.length > HOST_WIRE_LIMITS.pathCharacters || !MARKDOWN_EXTENSION.test(path)) {
|
|
2777
|
+
return null;
|
|
2778
|
+
}
|
|
2779
|
+
return { kind: "workspace", path };
|
|
2780
|
+
}
|
|
2781
|
+
function normalizeDocumentPath(path) {
|
|
2782
|
+
if (!isBoundedString(path, HOST_WIRE_LIMITS.pathCharacters, 1)) return null;
|
|
2783
|
+
const segments = [];
|
|
2784
|
+
for (const segment of path.replace(/\\/g, "/").split("/")) {
|
|
2785
|
+
if (segment === "" || segment === ".") continue;
|
|
2786
|
+
if (segment === ".." || segment.length > 255 || hasControlCharacter(segment)) return null;
|
|
2787
|
+
segments.push(segment);
|
|
2788
|
+
}
|
|
2789
|
+
return segments.length > 0 ? segments : null;
|
|
2790
|
+
}
|
|
2791
|
+
function hasControlCharacter(value) {
|
|
2792
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
2793
|
+
const code = value.charCodeAt(index);
|
|
2794
|
+
if (code <= 31 || code === 127) return true;
|
|
2795
|
+
}
|
|
2796
|
+
return false;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2722
2799
|
// src/DocBlocksShell/import-files.ts
|
|
2723
2800
|
import { FsError as FsError3 } from "@bendyline/docblocks/filesystem";
|
|
2724
2801
|
|
|
@@ -3214,7 +3291,7 @@ function UpdateAvailableNotice({
|
|
|
3214
3291
|
blocked = false,
|
|
3215
3292
|
statusBarVisible
|
|
3216
3293
|
}) {
|
|
3217
|
-
const [promptOpen, setPromptOpen] = useState13(
|
|
3294
|
+
const [promptOpen, setPromptOpen] = useState13(false);
|
|
3218
3295
|
if (!available || !onApplyUpdate) return null;
|
|
3219
3296
|
const promptVisible = promptOpen && !blocked;
|
|
3220
3297
|
return /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
@@ -3232,13 +3309,21 @@ function UpdateAvailableNotice({
|
|
|
3232
3309
|
children: "Update available"
|
|
3233
3310
|
}
|
|
3234
3311
|
),
|
|
3235
|
-
promptVisible && /* @__PURE__ */ jsxs10(
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3312
|
+
promptVisible && /* @__PURE__ */ jsxs10(
|
|
3313
|
+
"div",
|
|
3314
|
+
{
|
|
3315
|
+
id: UPDATE_PROMPT_ID,
|
|
3316
|
+
className: `db-update-banner${statusBarVisible ? "" : " db-update-banner--floating"}`,
|
|
3317
|
+
role: "alert",
|
|
3318
|
+
children: [
|
|
3319
|
+
/* @__PURE__ */ jsx11("span", { children: "A new version of DocBlocks is available. Reload to update the editor and site pages." }),
|
|
3320
|
+
/* @__PURE__ */ jsxs10("div", { className: "db-update-banner-actions", children: [
|
|
3321
|
+
/* @__PURE__ */ jsx11("button", { type: "button", onClick: onApplyUpdate, disabled: blocked, children: "Reload" }),
|
|
3322
|
+
/* @__PURE__ */ jsx11("button", { type: "button", onClick: () => setPromptOpen(false), children: "Later" })
|
|
3323
|
+
] })
|
|
3324
|
+
]
|
|
3325
|
+
}
|
|
3326
|
+
)
|
|
3242
3327
|
] });
|
|
3243
3328
|
}
|
|
3244
3329
|
|
|
@@ -3549,6 +3634,91 @@ async function copyTransientWorkspaceContents(source, destination) {
|
|
|
3549
3634
|
}
|
|
3550
3635
|
}
|
|
3551
3636
|
|
|
3637
|
+
// src/DocBlocksShell/welcome-document.ts
|
|
3638
|
+
var WELCOME_DOCUMENT_PATH = "/aboutDocBlocks.md";
|
|
3639
|
+
var WELCOME_DOCUMENT_CONTENT = [
|
|
3640
|
+
"---",
|
|
3641
|
+
"squisq-theme: warm-earth",
|
|
3642
|
+
"squisq-cover-slide: false",
|
|
3643
|
+
"---",
|
|
3644
|
+
"",
|
|
3645
|
+
'# DocBlocks: one Markdown file, many finished forms {[title subtitle="Local-first writing for pages, documents, slideshows, and video"]}',
|
|
3646
|
+
"",
|
|
3647
|
+
"Write in plain Markdown. Shape it visually. Keep it portable.",
|
|
3648
|
+
"",
|
|
3649
|
+
"## A document editor that never asks you to stop owning the document {[factCard]}",
|
|
3650
|
+
"",
|
|
3651
|
+
"DocBlocks pairs a friendly visual writing surface with the durable source format you already know. There is no account to create, no proprietary document to rescue later, and no requirement to send your work to a hosted editor.",
|
|
3652
|
+
"",
|
|
3653
|
+
"## One source, four ways to publish {[diagram]}",
|
|
3654
|
+
"",
|
|
3655
|
+
"```diagram",
|
|
3656
|
+
"+------------+",
|
|
3657
|
+
"| Markdown |",
|
|
3658
|
+
"+-----+------+",
|
|
3659
|
+
" |",
|
|
3660
|
+
" +---+------+------------+-----------+",
|
|
3661
|
+
" | | | |",
|
|
3662
|
+
" v v v v",
|
|
3663
|
+
"+-------+ +---------+ +------------+ +-------+",
|
|
3664
|
+
"| Pages | |Documents| | Slideshows | | Video |",
|
|
3665
|
+
"+-------+ +---------+ +------------+ +-------+",
|
|
3666
|
+
"```",
|
|
3667
|
+
"",
|
|
3668
|
+
"## Write visually, then look under the hood whenever you like {[content]}",
|
|
3669
|
+
"",
|
|
3670
|
+
"Use the rich editor for headings, lists, tables, links, images, media, and everyday formatting. Switch to raw Markdown for exact control. Add themes, transform styles, and squiggly-square section annotations when a passage deserves its own visual treatment. The readable `.md` source stays at the center of the workflow.",
|
|
3671
|
+
"",
|
|
3672
|
+
"## Preview the finished idea before you export it {[list]}",
|
|
3673
|
+
"",
|
|
3674
|
+
"- **Document** \u2014 Read the work as a polished, continuous document.",
|
|
3675
|
+
"- **Page** \u2014 See how it behaves as a web page.",
|
|
3676
|
+
"- **Slideshow** \u2014 Step through headings as designed presentation blocks.",
|
|
3677
|
+
"- **Video** \u2014 Rehearse timing, motion, narration, and media in a player-ready view.",
|
|
3678
|
+
"",
|
|
3679
|
+
'## Local or installed: choose the workspace that fits {[twoColumn header="Work where your files make sense" left="Web|Browser-local workspaces or folders you explicitly grant" right="Desktop|Real folders, native menus, file opening, and updates"]}',
|
|
3680
|
+
"",
|
|
3681
|
+
'## In the project or in the pipeline {[twoColumn header="The same Markdown keeps moving" left="VS Code|Rich editing beside tabs, source control, and the project tree" right="CLI + MCP|Build, serve, convert, render, inspect, validate, and automate"]}',
|
|
3682
|
+
"",
|
|
3683
|
+
"## The quiet conveniences are there, too {[list]}",
|
|
3684
|
+
"",
|
|
3685
|
+
"- **Workspaces and file navigation** keep related documents organized without hiding their source.",
|
|
3686
|
+
"- **Automatic saving** protects the active revision while still surfacing conflicts or save errors.",
|
|
3687
|
+
"- **Optional version history** keeps plain sibling revisions when you want a trail of changes.",
|
|
3688
|
+
"- **Copy-by-link sharing** creates a bounded Markdown copy that can open directly in a chosen Play view.",
|
|
3689
|
+
"- **Offline-ready, local-first storage** keeps browser work on this device and native work inside folders you choose.",
|
|
3690
|
+
"",
|
|
3691
|
+
"## Export without rebuilding the document {[list]}",
|
|
3692
|
+
"",
|
|
3693
|
+
"- Create **Word documents and PDFs** for review, printing, and fixed-layout sharing.",
|
|
3694
|
+
"- Create **PowerPoint decks** from the same annotated sections you preview as a slideshow.",
|
|
3695
|
+
"- Publish **HTML pages, packaged HTML, and EPUB e-books**.",
|
|
3696
|
+
"- Keep portable **Markdown or DBK bundles** with their media assets.",
|
|
3697
|
+
"- Use the linked CLI registry for **spreadsheets, MP4 video, animated GIF, batch conversion, and more**.",
|
|
3698
|
+
"",
|
|
3699
|
+
"## Local-first is concrete, not decorative {[quote]}",
|
|
3700
|
+
"",
|
|
3701
|
+
"> Your browser workspace stays on this device. Native folders are opened only when you choose them. Your Markdown remains useful in DocBlocks, Git, a terminal, another editor, or an AI-assisted workflow.",
|
|
3702
|
+
"",
|
|
3703
|
+
"## Agents get document tools, not a blank check {[factCard]}",
|
|
3704
|
+
"",
|
|
3705
|
+
"The local MCP server can inspect, validate, preview, compare, and convert documents. Results remain temporary session artifacts until they are deliberately saved, and filesystem access begins with explicit roots rather than assumed authority.",
|
|
3706
|
+
"",
|
|
3707
|
+
"## Start with three small moves {[list]}",
|
|
3708
|
+
"",
|
|
3709
|
+
"1. Create a document with **New file** in the file pane.",
|
|
3710
|
+
"2. Write visually or in Markdown source; switch views whenever it helps.",
|
|
3711
|
+
"3. Open **Play** or **Export** when the idea is ready to become something else.",
|
|
3712
|
+
"",
|
|
3713
|
+
"## Explore DocBlocks {[list]}",
|
|
3714
|
+
"",
|
|
3715
|
+
"- [Desktop app](https://docblocks.com/desktop/) \u2014 macOS, Windows, and Linux.",
|
|
3716
|
+
"- [VS Code extension](https://docblocks.com/vscode/) \u2014 rich Markdown editing inside a workspace.",
|
|
3717
|
+
"- [CLI and MCP server](https://docblocks.com/cli/) \u2014 conversion, rendering, and agent workflows.",
|
|
3718
|
+
"- [Supported formats](https://docblocks.com/formats/) \u2014 current import and export directions.",
|
|
3719
|
+
"- [Documentation](https://docblocks.com/docs/) \u2014 guides for using and extending DocBlocks."
|
|
3720
|
+
].join("\n");
|
|
3721
|
+
|
|
3552
3722
|
// src/DocBlocksShell/DocBlocksShell.tsx
|
|
3553
3723
|
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3554
3724
|
var editorShellModulePromise = null;
|
|
@@ -3870,7 +4040,7 @@ function DocBlocksShell({
|
|
|
3870
4040
|
saveThemePreference(pref);
|
|
3871
4041
|
}, []);
|
|
3872
4042
|
useEffect14(() => {
|
|
3873
|
-
if (
|
|
4043
|
+
if (isElectronHost3() || typeof document === "undefined") return;
|
|
3874
4044
|
const metas = document.querySelectorAll('meta[name="theme-color"]');
|
|
3875
4045
|
metas.forEach((meta) => {
|
|
3876
4046
|
meta.content = DB_CHROME_COLORS[resolvedTheme];
|
|
@@ -3899,8 +4069,8 @@ function DocBlocksShell({
|
|
|
3899
4069
|
const [sidebarWidth, setSidebarWidth] = useState14(loadSidebarWidth);
|
|
3900
4070
|
const [compactLayout, setCompactLayout] = useState14(false);
|
|
3901
4071
|
const effectiveCompact = isMobile || compactLayout;
|
|
3902
|
-
const showBrowserStorageWarning = !
|
|
3903
|
-
const appVersion =
|
|
4072
|
+
const showBrowserStorageWarning = !isElectronHost3();
|
|
4073
|
+
const appVersion = isElectronHost3() ? `${getDocBlocksHost().env.appVersion} desktop` : issueReportVersion ?? "web";
|
|
3904
4074
|
const issueReportUrl = buildIssueReportUrl({
|
|
3905
4075
|
reportedAt: /* @__PURE__ */ new Date(),
|
|
3906
4076
|
version: appVersion,
|
|
@@ -4014,7 +4184,7 @@ function DocBlocksShell({
|
|
|
4014
4184
|
return;
|
|
4015
4185
|
}
|
|
4016
4186
|
let cancelled = false;
|
|
4017
|
-
const electron =
|
|
4187
|
+
const electron = isElectronHost3();
|
|
4018
4188
|
void listWorkspaces2().then((workspaces) => {
|
|
4019
4189
|
if (cancelled) return;
|
|
4020
4190
|
setTransientMoveDestinations(
|
|
@@ -4057,7 +4227,7 @@ function DocBlocksShell({
|
|
|
4057
4227
|
const [selectedFile, setSelectedFile] = useState14(null);
|
|
4058
4228
|
useDocumentTitle(selectedFile, homeDocumentTitle, homeDocumentPath);
|
|
4059
4229
|
const exportDestinationAdapter = useMemo3(() => {
|
|
4060
|
-
if (!
|
|
4230
|
+
if (!isElectronHost3() || !activeWorkspaceId || !selectedFile) return void 0;
|
|
4061
4231
|
const documentId = JSON.stringify([activeWorkspaceId, selectedFile]);
|
|
4062
4232
|
const host = getDocBlocksHost().exports;
|
|
4063
4233
|
return {
|
|
@@ -4098,7 +4268,7 @@ function DocBlocksShell({
|
|
|
4098
4268
|
const baseTarget = createFileSystemDocumentTarget(fsProvider, filePath);
|
|
4099
4269
|
const originKind = transient?.descriptor.origin?.kind;
|
|
4100
4270
|
const needsElectronHost = originKind === "loose-file" || originKind === "dbk";
|
|
4101
|
-
if (!transient?.descriptor.origin || needsElectronHost && !
|
|
4271
|
+
if (!transient?.descriptor.origin || needsElectronHost && !isElectronHost3()) {
|
|
4102
4272
|
return {
|
|
4103
4273
|
key: baseTarget.key,
|
|
4104
4274
|
async commit(request) {
|
|
@@ -4426,39 +4596,8 @@ function DocBlocksShell({
|
|
|
4426
4596
|
return;
|
|
4427
4597
|
}
|
|
4428
4598
|
if (entries.length > 0) return;
|
|
4429
|
-
const welcomePath =
|
|
4430
|
-
const welcomeContent =
|
|
4431
|
-
"# DocBlocks: the local-first Markdown editor",
|
|
4432
|
-
"",
|
|
4433
|
-
"Your markdown can do anything.",
|
|
4434
|
-
"",
|
|
4435
|
-
"DocBlocks is a free, local-first Markdown editor for creating, organizing, and publishing documents without sending your writing to a server. What you write here can become a Word or PDF document, a slide deck, a web page, or a video.",
|
|
4436
|
-
"",
|
|
4437
|
-
"Simple to write. Beautiful wherever it goes.",
|
|
4438
|
-
"",
|
|
4439
|
-
"## Features",
|
|
4440
|
-
"",
|
|
4441
|
-
"- **Rich Markdown editing** \u2014 Write visually or switch to raw Markdown source at any time. Section annotations can change how individual blocks are presented.",
|
|
4442
|
-
"- **Workspaces** \u2014 Organize documents into separate browser workspaces or folders on your device.",
|
|
4443
|
-
"- **Useful everywhere** \u2014 Export Word, PowerPoint, PDF, HTML, and Markdown in the app, with EPUB and additional formats available through the CLI.",
|
|
4444
|
-
"- **Playback and video** \u2014 Preview a document as a rich presentation and export it as an MP4 video.",
|
|
4445
|
-
"- **Private by default** \u2014 No ads, accounts, or tracking. Your browser workspace stays on this device.",
|
|
4446
|
-
"",
|
|
4447
|
-
"## Getting Started",
|
|
4448
|
-
"",
|
|
4449
|
-
"1. Create a document with the **New file** button in the file pane.",
|
|
4450
|
-
"2. Start writing in Markdown \u2014 the editor supports headings, lists, links, images, and more.",
|
|
4451
|
-
"3. Your work is saved automatically.",
|
|
4452
|
-
"",
|
|
4453
|
-
"Built with [squisq](https://github.com/bendyline/squisq) by [Bendyline](https://bendyline.com).",
|
|
4454
|
-
"",
|
|
4455
|
-
"## Explore DocBlocks",
|
|
4456
|
-
"",
|
|
4457
|
-
"- [Desktop app](https://docblocks.com/desktop/) for macOS, Windows, and Linux",
|
|
4458
|
-
"- [VS Code extension](https://docblocks.com/vscode/) for rich editing inside your workspace",
|
|
4459
|
-
"- [CLI and MCP server](https://docblocks.com/cli/) for conversion and agent workflows",
|
|
4460
|
-
"- [Supported formats](https://docblocks.com/formats/) and [documentation](https://docblocks.com/docs/)"
|
|
4461
|
-
].join("\n");
|
|
4599
|
+
const welcomePath = WELCOME_DOCUMENT_PATH;
|
|
4600
|
+
const welcomeContent = WELCOME_DOCUMENT_CONTENT;
|
|
4462
4601
|
let seededContent = welcomeContent;
|
|
4463
4602
|
try {
|
|
4464
4603
|
await writeProviderText(fs, welcomePath, welcomeContent, "create");
|
|
@@ -4510,7 +4649,7 @@ function DocBlocksShell({
|
|
|
4510
4649
|
let cancelled = false;
|
|
4511
4650
|
const isCurrent = () => !cancelled && requestId === navigationRequestRef.current;
|
|
4512
4651
|
void (async () => {
|
|
4513
|
-
const electron =
|
|
4652
|
+
const electron = isElectronHost3();
|
|
4514
4653
|
let workspaceIdRemap = {};
|
|
4515
4654
|
if (electron) {
|
|
4516
4655
|
try {
|
|
@@ -4758,7 +4897,7 @@ function DocBlocksShell({
|
|
|
4758
4897
|
};
|
|
4759
4898
|
}, [showBrowserStorageWarning]);
|
|
4760
4899
|
useEffect14(() => {
|
|
4761
|
-
if (
|
|
4900
|
+
if (isElectronHost3() || typeof window === "undefined") return;
|
|
4762
4901
|
const onBeforeInstallPrompt = (e) => {
|
|
4763
4902
|
e.preventDefault();
|
|
4764
4903
|
setInstallPromptEvent(e);
|
|
@@ -5047,7 +5186,7 @@ function DocBlocksShell({
|
|
|
5047
5186
|
};
|
|
5048
5187
|
}, [documentSession, documentSnapshot.status, documentSnapshot.targetKey]);
|
|
5049
5188
|
useEffect14(() => {
|
|
5050
|
-
if (!
|
|
5189
|
+
if (!isElectronHost3()) return;
|
|
5051
5190
|
const lifecycle = getDocBlocksHost().lifecycle;
|
|
5052
5191
|
const stopPrepare = lifecycle.onPrepareClose(async (request) => {
|
|
5053
5192
|
preparedCloseRequestRef.current = request.requestId;
|
|
@@ -5177,7 +5316,7 @@ function DocBlocksShell({
|
|
|
5177
5316
|
void touchWorkspace2(destination.id).catch(() => void 0);
|
|
5178
5317
|
if (sourceTargetKey) pendingDbkConflictsRef.current.delete(sourceTargetKey);
|
|
5179
5318
|
const origin = transient.descriptor.origin;
|
|
5180
|
-
if (
|
|
5319
|
+
if (isElectronHost3() && origin && (origin.kind === "loose-file" || origin.kind === "dbk")) {
|
|
5181
5320
|
try {
|
|
5182
5321
|
await getDocBlocksHost().external.revoke(origin.resourceId);
|
|
5183
5322
|
} catch {
|
|
@@ -5214,7 +5353,7 @@ function DocBlocksShell({
|
|
|
5214
5353
|
const handleOpenFolder = useCallback14(async () => {
|
|
5215
5354
|
const requestId = ++navigationRequestRef.current;
|
|
5216
5355
|
try {
|
|
5217
|
-
if (
|
|
5356
|
+
if (isElectronHost3()) {
|
|
5218
5357
|
const info = await getDocBlocksHost().workspaces.pickFolder();
|
|
5219
5358
|
if (!info) return;
|
|
5220
5359
|
if (requestId !== navigationRequestRef.current) return;
|
|
@@ -5415,14 +5554,14 @@ function DocBlocksShell({
|
|
|
5415
5554
|
void handleNewFile();
|
|
5416
5555
|
}, [provider, handleNewFile]);
|
|
5417
5556
|
const handleRevealWorkspace = useCallback14(async () => {
|
|
5418
|
-
if (!
|
|
5557
|
+
if (!isElectronHost3() || !activeWorkspaceId) return;
|
|
5419
5558
|
const ws = await getWorkspace(activeWorkspaceId);
|
|
5420
5559
|
if (ws?.type === "electron-native" && ws.rootPath) {
|
|
5421
5560
|
await getDocBlocksHost().shell.revealInFolder(ws.id);
|
|
5422
5561
|
}
|
|
5423
5562
|
}, [activeWorkspaceId]);
|
|
5424
5563
|
useEffect14(() => {
|
|
5425
|
-
if (!
|
|
5564
|
+
if (!isElectronHost3()) return;
|
|
5426
5565
|
const host = getDocBlocksHost();
|
|
5427
5566
|
return host.onMenuCommand((cmd) => {
|
|
5428
5567
|
switch (cmd) {
|
|
@@ -5542,6 +5681,56 @@ function DocBlocksShell({
|
|
|
5542
5681
|
showToast
|
|
5543
5682
|
]
|
|
5544
5683
|
);
|
|
5684
|
+
const handleEditorLinkClick = useCallback14(
|
|
5685
|
+
(href) => {
|
|
5686
|
+
const target = resolveShellEditorLinkTarget(href, selectedFile);
|
|
5687
|
+
if (!target) {
|
|
5688
|
+
showToast(
|
|
5689
|
+
"error",
|
|
5690
|
+
"DocBlocks only opens HTTP(S) links or Markdown files inside this workspace."
|
|
5691
|
+
);
|
|
5692
|
+
return true;
|
|
5693
|
+
}
|
|
5694
|
+
if (target.kind === "fragment") return false;
|
|
5695
|
+
if (target.kind === "external") {
|
|
5696
|
+
if (isElectronHost3()) {
|
|
5697
|
+
void getDocBlocksHost().shell.openExternal(target.url).catch((error) => {
|
|
5698
|
+
showToast(
|
|
5699
|
+
"error",
|
|
5700
|
+
error instanceof Error ? error.message : "Could not open the external link."
|
|
5701
|
+
);
|
|
5702
|
+
});
|
|
5703
|
+
} else if (typeof window !== "undefined") {
|
|
5704
|
+
try {
|
|
5705
|
+
window.open(target.url, "_blank", "noopener,noreferrer");
|
|
5706
|
+
} catch (error) {
|
|
5707
|
+
showToast(
|
|
5708
|
+
"error",
|
|
5709
|
+
error instanceof Error ? error.message : "Could not open the external link."
|
|
5710
|
+
);
|
|
5711
|
+
}
|
|
5712
|
+
}
|
|
5713
|
+
return true;
|
|
5714
|
+
}
|
|
5715
|
+
void (async () => {
|
|
5716
|
+
if (!provider) return;
|
|
5717
|
+
try {
|
|
5718
|
+
if (!await providerEntryExists(provider, target.path)) {
|
|
5719
|
+
showToast("error", "The linked Markdown file was not found in this workspace.");
|
|
5720
|
+
return;
|
|
5721
|
+
}
|
|
5722
|
+
await handleSelect(target.path, "file");
|
|
5723
|
+
} catch (error) {
|
|
5724
|
+
showToast(
|
|
5725
|
+
"error",
|
|
5726
|
+
error instanceof Error ? error.message : "Could not open the linked Markdown file."
|
|
5727
|
+
);
|
|
5728
|
+
}
|
|
5729
|
+
})();
|
|
5730
|
+
return true;
|
|
5731
|
+
},
|
|
5732
|
+
[handleSelect, provider, selectedFile, showToast]
|
|
5733
|
+
);
|
|
5545
5734
|
const handleTreeMutation = useCallback14(
|
|
5546
5735
|
async (change, mutate) => {
|
|
5547
5736
|
if (!provider || !activeWorkspaceId || !selectedFile) {
|
|
@@ -5741,7 +5930,7 @@ function DocBlocksShell({
|
|
|
5741
5930
|
[adoptTransientWorkspace]
|
|
5742
5931
|
);
|
|
5743
5932
|
useEffect14(() => {
|
|
5744
|
-
if (!
|
|
5933
|
+
if (!isElectronHost3()) return;
|
|
5745
5934
|
const host = getDocBlocksHost();
|
|
5746
5935
|
return host.onOpenRequest((req) => {
|
|
5747
5936
|
const requestId = ++navigationRequestRef.current;
|
|
@@ -5807,7 +5996,7 @@ function DocBlocksShell({
|
|
|
5807
5996
|
[adoptTransientWorkspace]
|
|
5808
5997
|
);
|
|
5809
5998
|
useEffect14(() => {
|
|
5810
|
-
if (
|
|
5999
|
+
if (isElectronHost3() || typeof window === "undefined") return;
|
|
5811
6000
|
const launchQueue = window.launchQueue;
|
|
5812
6001
|
if (!launchQueue) return;
|
|
5813
6002
|
launchQueue.setConsumer((params) => {
|
|
@@ -5852,7 +6041,7 @@ function DocBlocksShell({
|
|
|
5852
6041
|
import("@bendyline/squisq/storage"),
|
|
5853
6042
|
import("@bendyline/squisq-formats/container")
|
|
5854
6043
|
]);
|
|
5855
|
-
const electron =
|
|
6044
|
+
const electron = isElectronHost3();
|
|
5856
6045
|
const all = await listWorkspaces2();
|
|
5857
6046
|
const candidates = all.filter(
|
|
5858
6047
|
(w) => electron ? w.type === "electron-native" || w.type === "transient" : w.type !== "electron-native"
|
|
@@ -5994,7 +6183,7 @@ function DocBlocksShell({
|
|
|
5994
6183
|
}
|
|
5995
6184
|
} else if (ws?.type === "transient") {
|
|
5996
6185
|
const origin = ws.origin;
|
|
5997
|
-
if (
|
|
6186
|
+
if (isElectronHost3() && origin && (origin.kind === "loose-file" || origin.kind === "dbk")) {
|
|
5998
6187
|
try {
|
|
5999
6188
|
await getDocBlocksHost().external.revoke(origin.resourceId);
|
|
6000
6189
|
} catch {
|
|
@@ -6007,7 +6196,7 @@ function DocBlocksShell({
|
|
|
6007
6196
|
}
|
|
6008
6197
|
await removeWorkspace(activeWorkspaceId);
|
|
6009
6198
|
if (requestId !== navigationRequestRef.current) return;
|
|
6010
|
-
const electron =
|
|
6199
|
+
const electron = isElectronHost3();
|
|
6011
6200
|
const remaining = (await listWorkspaces2()).filter(
|
|
6012
6201
|
(w) => electron ? w.type === "electron-native" : w.type !== "electron-native"
|
|
6013
6202
|
);
|
|
@@ -6268,6 +6457,7 @@ function DocBlocksShell({
|
|
|
6268
6457
|
articleId: selectedFile,
|
|
6269
6458
|
fileName: selectedFile,
|
|
6270
6459
|
onChange: handleEditorChange,
|
|
6460
|
+
onLinkClick: handleEditorLinkClick,
|
|
6271
6461
|
colorScheme: resolvedTheme,
|
|
6272
6462
|
writeCanvasSettings,
|
|
6273
6463
|
height: "100%",
|
|
@@ -6382,7 +6572,7 @@ function DocBlocksShell({
|
|
|
6382
6572
|
children: "New folder"
|
|
6383
6573
|
}
|
|
6384
6574
|
),
|
|
6385
|
-
(
|
|
6575
|
+
(isElectronHost3() || typeof globalThis.showDirectoryPicker === "function") && /* @__PURE__ */ jsx12(
|
|
6386
6576
|
"button",
|
|
6387
6577
|
{
|
|
6388
6578
|
type: "button",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/docblocks-react",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "React components for DocBlocks — file explorer, workspace picker, and editor shell",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -65,21 +65,21 @@
|
|
|
65
65
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@bendyline/docblocks": "2.2.
|
|
69
|
-
"@bendyline/squisq": "2.3.
|
|
70
|
-
"@bendyline/squisq-editor-react": "2.3.
|
|
71
|
-
"@bendyline/squisq-formats": "2.3.
|
|
72
|
-
"@bendyline/squisq-react": "2.3.
|
|
73
|
-
"@bendyline/squisq-video": "2.2.
|
|
74
|
-
"@bendyline/squisq-video-react": "2.2.
|
|
68
|
+
"@bendyline/docblocks": "2.2.1",
|
|
69
|
+
"@bendyline/squisq": "2.3.3",
|
|
70
|
+
"@bendyline/squisq-editor-react": "2.3.4",
|
|
71
|
+
"@bendyline/squisq-formats": "2.3.3",
|
|
72
|
+
"@bendyline/squisq-react": "2.3.3",
|
|
73
|
+
"@bendyline/squisq-video": "2.2.3",
|
|
74
|
+
"@bendyline/squisq-video-react": "2.2.3"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
77
|
"@happy-dom/global-registrator": "20.9.0",
|
|
78
78
|
"@types/react": "18.3.28",
|
|
79
79
|
"@types/react-dom": "18.3.7",
|
|
80
80
|
"happy-dom": "20.9.0",
|
|
81
|
-
"monaco-editor": "0.50.0",
|
|
82
81
|
"mocha": "11.3.0",
|
|
82
|
+
"monaco-editor": "0.50.0",
|
|
83
83
|
"react": "18.3.1",
|
|
84
84
|
"react-dom": "18.3.1",
|
|
85
85
|
"tsup": "8.5.1",
|
package/src/styles/docblocks.css
CHANGED
|
@@ -1600,6 +1600,21 @@ body.db-resizing-sidebar * {
|
|
|
1600
1600
|
gap: 6px;
|
|
1601
1601
|
}
|
|
1602
1602
|
|
|
1603
|
+
.db-about-beta {
|
|
1604
|
+
margin-bottom: 10px;
|
|
1605
|
+
padding: 10px 12px;
|
|
1606
|
+
border: 1px solid var(--db-accent);
|
|
1607
|
+
border-radius: 6px;
|
|
1608
|
+
background: var(--db-accent-soft-08);
|
|
1609
|
+
color: var(--db-text-secondary);
|
|
1610
|
+
font-size: 12px;
|
|
1611
|
+
line-height: 1.5;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.db-about-beta strong {
|
|
1615
|
+
color: var(--db-accent-deep);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1603
1618
|
.db-about-version {
|
|
1604
1619
|
display: flex;
|
|
1605
1620
|
align-items: center;
|
|
@@ -4335,24 +4350,26 @@ html:has(.db-shell[data-theme='dark'])
|
|
|
4335
4350
|
box-shadow: 0 2px 8px var(--db-shadow);
|
|
4336
4351
|
}
|
|
4337
4352
|
|
|
4338
|
-
/* The expanded Reload/Later prompt
|
|
4339
|
-
the document-conflict card, accent-toned instead of warning. */
|
|
4353
|
+
/* The expanded Reload/Later prompt sits just above its status-bar trigger. */
|
|
4340
4354
|
.db-update-banner {
|
|
4341
|
-
position:
|
|
4342
|
-
|
|
4343
|
-
|
|
4355
|
+
position: absolute;
|
|
4356
|
+
right: 8px;
|
|
4357
|
+
bottom: 30px;
|
|
4344
4358
|
z-index: 210;
|
|
4345
4359
|
display: flex;
|
|
4346
4360
|
gap: 16px;
|
|
4347
4361
|
align-items: center;
|
|
4348
|
-
max-width: min(720px, calc(
|
|
4362
|
+
max-width: min(720px, calc(100% - 16px));
|
|
4349
4363
|
padding: 10px 12px;
|
|
4350
4364
|
color: var(--db-text);
|
|
4351
4365
|
background: var(--db-bg-elevated, var(--db-bg));
|
|
4352
4366
|
border: 1px solid var(--db-accent);
|
|
4353
4367
|
border-radius: 8px;
|
|
4354
4368
|
box-shadow: 0 6px 20px var(--db-shadow);
|
|
4355
|
-
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
.db-update-banner--floating {
|
|
4372
|
+
bottom: 48px;
|
|
4356
4373
|
}
|
|
4357
4374
|
|
|
4358
4375
|
.db-update-banner-actions {
|
|
@@ -5033,15 +5050,15 @@ button.db-git-file-row:hover {
|
|
|
5033
5050
|
padding-bottom: 2px;
|
|
5034
5051
|
}
|
|
5035
5052
|
|
|
5036
|
-
/*
|
|
5037
|
-
|
|
5038
|
-
|
|
5053
|
+
/* Keep the compact labels on the same visual center as the action glyphs.
|
|
5054
|
+
Move only the text; the tab hit area and active underline stay anchored
|
|
5055
|
+
to the titlebar edges. */
|
|
5039
5056
|
.db-shell .squisq-toolbar-view-tab-label {
|
|
5040
|
-
transform: translateY(
|
|
5057
|
+
transform: translateY(1px);
|
|
5041
5058
|
}
|
|
5042
5059
|
|
|
5043
5060
|
.db-shell .squisq-use-mode-trigger {
|
|
5044
|
-
padding-bottom:
|
|
5061
|
+
padding-bottom: 10px;
|
|
5045
5062
|
}
|
|
5046
5063
|
|
|
5047
5064
|
/* The 32px caption row leaves little room around Squisq's 28px action
|