duckfn-docs-kit 0.4.0 → 0.4.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/AGENTS.md +43 -11
- package/dist/download.d.ts +51 -0
- package/dist/index.js +1 -1
- package/dist/mermaid/DfkMermaid.d.ts +18 -0
- package/dist/mermaid/render.d.ts +0 -2
- package/dist/panzoom-view.d.ts +17 -0
- package/dist/{register-Dev_kc3Z.js → register-wdwf0LC4.js} +1141 -847
- package/dist/source-dialog.d.ts +35 -0
- package/dist/sql/PreviewTabs.d.ts +39 -23
- package/dist/sql/SvgViewer.d.ts +53 -0
- package/dist/sql/client.js +1 -1
- package/dist/sql/harness.js +1 -1
- package/dist/sql/renderers.d.ts +7 -0
- package/package.json +3 -2
- package/src/codemirror.ts +14 -5
- package/src/{mermaid/title.ts → download.ts} +66 -24
- package/src/mermaid/DfkMermaid.css +52 -15
- package/src/mermaid/DfkMermaid.ts +144 -290
- package/src/mermaid/render.ts +0 -6
- package/src/panzoom-view.ts +235 -0
- package/src/source-dialog.ts +126 -0
- package/src/sql/DfkSql.ts +49 -5
- package/src/sql/PreviewTabs.ts +98 -52
- package/src/sql/SvgViewer.ts +155 -0
- package/src/sql/renderers.ts +423 -170
- package/src/sql/sql.css +176 -8
- package/dist/mermaid/title.d.ts +0 -23
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "edit the source" dialog shared by `<dfk-mermaid>` and the SQL result `svg`
|
|
3
|
+
* viewer: a modal holding a plain-text CodeMirror editor and an Apply / Cancel
|
|
4
|
+
* pair.
|
|
5
|
+
*
|
|
6
|
+
* The editor writes back through the callback {@link open} is given rather than
|
|
7
|
+
* through an event, so the owner keeps its own source as the single truth — the
|
|
8
|
+
* dialog never renders anything and never decides what a change means.
|
|
9
|
+
*
|
|
10
|
+
* The `<dialog>` is built once and reused; the editor mounts on first open and is
|
|
11
|
+
* kept, because a reader who edits one diagram is likely to edit the next. The
|
|
12
|
+
* dialog is shown *before* the editor mounts: CodeMirror measures its container as
|
|
13
|
+
* it is constructed, and a `display: none` dialog measures to zero.
|
|
14
|
+
*
|
|
15
|
+
* This is browser-only code.
|
|
16
|
+
*/
|
|
17
|
+
export interface SourceDialogLabels {
|
|
18
|
+
title: string;
|
|
19
|
+
apply: string;
|
|
20
|
+
cancel: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class SourceDialog {
|
|
23
|
+
#private;
|
|
24
|
+
readonly root: HTMLDialogElement;
|
|
25
|
+
constructor(labels: SourceDialogLabels);
|
|
26
|
+
setLabels(labels: SourceDialogLabels): void;
|
|
27
|
+
/**
|
|
28
|
+
* Shows the dialog with `source` in the editor. `onApply` receives the edited
|
|
29
|
+
* text — only when it differs from what was opened, so an unedited Apply is a
|
|
30
|
+
* plain close.
|
|
31
|
+
*/
|
|
32
|
+
open(source: string, onApply: (value: string) => void): void;
|
|
33
|
+
/** Closes the dialog and releases the editor. The dialog node stays in place. */
|
|
34
|
+
destroy(): void;
|
|
35
|
+
}
|
|
@@ -1,37 +1,53 @@
|
|
|
1
|
+
import { type DownloadPayload } from '../download';
|
|
1
2
|
/**
|
|
2
3
|
* Every renderer's result shell: a tab strip plus the panels behind it.
|
|
3
4
|
*
|
|
4
|
-
* The strip
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The strip holds one tab per item — a preview row, a `Text` view, or the
|
|
6
|
+
* trailing `Table` view — and, at its right end, the chrome that acts on the
|
|
7
|
+
* result as a whole: the active tab's own {@link PreviewTabItem.actions}, a
|
|
8
|
+
* **download** button, and whatever the caller parks last (the fullscreen toggle
|
|
9
|
+
* `<dfk-sql>` owns). Every result therefore has the same chrome, and the panel
|
|
10
|
+
* behind a tab is built the first time that tab is shown.
|
|
9
11
|
*
|
|
10
|
-
* Retained mode: every button and panel is built in the
|
|
11
|
-
* a field. Activating a tab mutates the nodes it owns
|
|
12
|
-
* `aria-selected`, `tabIndex`) — there is no rebuild, and
|
|
13
|
-
* first time it is shown rather than up front.
|
|
14
|
-
*
|
|
15
|
-
* The table panel is mounted on first activation on purpose: VTable measures
|
|
16
|
-
* its container when it is constructed, and a `hidden` panel measures to zero.
|
|
12
|
+
* Retained mode: every button, action container and panel is built in the
|
|
13
|
+
* constructor and held in a field. Activating a tab mutates the nodes it owns
|
|
14
|
+
* (`hidden`, `classList`, `aria-selected`, `tabIndex`) — there is no rebuild, and
|
|
15
|
+
* a panel is filled the first time it is shown rather than up front.
|
|
17
16
|
*/
|
|
18
|
-
/** One
|
|
17
|
+
/** One tab: its label, how to fill its panel, and what it offers the strip. */
|
|
19
18
|
export interface PreviewTabItem {
|
|
20
19
|
label: string;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Fills the panel. Called once, the first time the tab is shown; an async mount
|
|
22
|
+
* may resolve with a disposer, which the strip runs when it is disposed (that is
|
|
23
|
+
* how the table's handle gets released, including when the countdown lands after
|
|
24
|
+
* {@link PreviewTabs.dispose}).
|
|
25
|
+
*/
|
|
26
|
+
mount(panel: HTMLElement): void | (() => void) | Promise<void | (() => void)>;
|
|
27
|
+
/**
|
|
28
|
+
* Controls for the strip's right end, shown only while this tab is active — the
|
|
29
|
+
* place for actions that belong to *this* result (a table's search and view
|
|
30
|
+
* switches, a figure's zoom reset) rather than to every result.
|
|
31
|
+
*/
|
|
32
|
+
actions?: HTMLElement;
|
|
33
|
+
/** This tab's file, or `null` while there is nothing to save yet. */
|
|
34
|
+
download?: () => DownloadPayload | null;
|
|
35
|
+
/** Told the result area's fullscreen state, for figures that zoom inside it. */
|
|
36
|
+
setFullscreen?: (value: boolean) => void;
|
|
26
37
|
}
|
|
27
38
|
export declare class PreviewTabs {
|
|
28
39
|
#private;
|
|
29
40
|
/**
|
|
30
|
-
* @param corner Node parked at the
|
|
31
|
-
*
|
|
32
|
-
*
|
|
41
|
+
* @param corner Node parked at the very end of the strip, after the download
|
|
42
|
+
* button. `<dfk-sql>` passes its fullscreen toggle: it owns that button's state,
|
|
43
|
+
* so it owns the node and only lends it here.
|
|
33
44
|
*/
|
|
34
|
-
constructor(host: HTMLElement, items: readonly PreviewTabItem[],
|
|
35
|
-
/** Releases
|
|
45
|
+
constructor(host: HTMLElement, items: readonly PreviewTabItem[], downloadLabel: string, corner?: HTMLElement);
|
|
46
|
+
/** Releases every mounted item and empties the panels. */
|
|
36
47
|
dispose(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Records the result area's fullscreen state and passes it on to the tab on
|
|
50
|
+
* screen: a figure zooms only where the result is expanded (see `PanZoomView`).
|
|
51
|
+
*/
|
|
52
|
+
setFullscreen(value: boolean): void;
|
|
37
53
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type DownloadPayload } from '../download';
|
|
2
|
+
/**
|
|
3
|
+
* The `svg` result view: an inline SVG a reader can zoom (in the result area's
|
|
4
|
+
* fullscreen), edit, and download — the same interactions a diagram gets from
|
|
5
|
+
* `<dfk-mermaid>`, which is why both are built on `PanZoomView` and
|
|
6
|
+
* `SourceDialog`.
|
|
7
|
+
*
|
|
8
|
+
* A plain class rather than a custom element: nothing hands it content through
|
|
9
|
+
* markup, and the SQL renderer already has the parsed node, so there is no
|
|
10
|
+
* upgrade to wait for.
|
|
11
|
+
*
|
|
12
|
+
* Like an embedded `<dfk-mermaid>`, the element contributes no frame — the result
|
|
13
|
+
* area's panel is the frame — and no floating cluster: its two controls travel out
|
|
14
|
+
* through {@link actions} for the result's tab strip, and its file travels out
|
|
15
|
+
* through {@link downloadPayload} for the strip's download button. Zoom is off
|
|
16
|
+
* until {@link setFullscreen} says the result area is expanded.
|
|
17
|
+
*
|
|
18
|
+
* This is browser-only code.
|
|
19
|
+
*/
|
|
20
|
+
/** The strings a figure's controls need. */
|
|
21
|
+
export interface FigureLabels {
|
|
22
|
+
reset: string;
|
|
23
|
+
edit: string;
|
|
24
|
+
editTitle: string;
|
|
25
|
+
apply: string;
|
|
26
|
+
cancel: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class SvgViewer {
|
|
29
|
+
#private;
|
|
30
|
+
/** The box in the tab panel: the panzoom viewport, and the frame's clipping box. */
|
|
31
|
+
readonly root: HTMLDivElement;
|
|
32
|
+
/** The controls the result area's tab strip shows while this tab is active. */
|
|
33
|
+
readonly actions: HTMLDivElement;
|
|
34
|
+
constructor(svg: SVGElement | null, markup: string, labels: FigureLabels, fallback: string);
|
|
35
|
+
/** Turns zoom and pan on or off; the result area calls this with its fullscreen. */
|
|
36
|
+
setFullscreen(value: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* The file this view would be saved as: the SVG itself, or — when the markup
|
|
39
|
+
* does not parse and the view is showing it as text — that text.
|
|
40
|
+
*/
|
|
41
|
+
downloadPayload(): DownloadPayload;
|
|
42
|
+
dispose(): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parses SVG markup from a result cell into a node this document can host.
|
|
46
|
+
*
|
|
47
|
+
* `image/svg+xml` is strict XML: malformed markup (unclosed tags, a bare `&`,
|
|
48
|
+
* an HTML `<br>`) comes back as a `<parsererror>` element rather than throwing,
|
|
49
|
+
* so the caller can degrade to text. Script-bearing and event-handler content
|
|
50
|
+
* is stripped — inline SVG is *not* isolated (use the `iframe` renderer for
|
|
51
|
+
* untrusted markup).
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseSvgMarkup(document: Document, markup: string): SVGElement | null;
|
package/dist/sql/client.js
CHANGED
package/dist/sql/harness.js
CHANGED
|
@@ -8000,7 +8000,7 @@ var Eu, Du, Ou, ku, Au, ju, Mu, Nu, Pu, Fu, Iu, Lu, Ru, zu, Bu, Vu, Hu, Uu, Wu,
|
|
|
8000
8000
|
11
|
|
8001
8001
|
])), od = {
|
|
8002
8002
|
name: "@duckdb/duckdb-wasm",
|
|
8003
|
-
version: "1.33.1-
|
|
8003
|
+
version: "1.33.1-dev65.0",
|
|
8004
8004
|
description: "DuckDB powered by WebAssembly",
|
|
8005
8005
|
license: "MIT",
|
|
8006
8006
|
repository: {
|
package/dist/sql/renderers.d.ts
CHANGED
|
@@ -25,6 +25,13 @@ export interface RenderContext {
|
|
|
25
25
|
* renderer only borrows it so every result has the same chrome.
|
|
26
26
|
*/
|
|
27
27
|
fullscreenButton: HTMLElement;
|
|
28
|
+
/**
|
|
29
|
+
* Subscribes to the result area's fullscreen state, calling back with the
|
|
30
|
+
* current value straight away and returning the unsubscribe. A renderer whose
|
|
31
|
+
* figure zooms only in fullscreen (`svg`, `mermaid`) forwards the value to its
|
|
32
|
+
* viewer; the subscription must be released by the renderer's disposer.
|
|
33
|
+
*/
|
|
34
|
+
onFullscreenChange(listener: (value: boolean) => void): () => void;
|
|
28
35
|
}
|
|
29
36
|
/**
|
|
30
37
|
* Renders `result` into `context.host`. Resolves with a disposer releasing any
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "duckfn-docs-kit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Shared Docusaurus building blocks (TOC toggle, home-page web components, brand tokens, remark version placeholder) for duckfn-family extension docs sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -58,9 +58,10 @@
|
|
|
58
58
|
"@codemirror/lang-sql": "^6.10.0",
|
|
59
59
|
"@codemirror/state": "^6.7.6",
|
|
60
60
|
"@codemirror/view": "^6.43.13",
|
|
61
|
-
"@duckdb/duckdb-wasm": "1.33.1-
|
|
61
|
+
"@duckdb/duckdb-wasm": "1.33.1-dev65.0",
|
|
62
62
|
"@panzoom/panzoom": "^4.6.2",
|
|
63
63
|
"@visactor/vtable": "^1.26.8",
|
|
64
|
+
"@visactor/vtable-search": "^1.26.8",
|
|
64
65
|
"codemirror": "^6.0.2",
|
|
65
66
|
"filenamify": "^7.0.3",
|
|
66
67
|
"iconify-icon": "^3.0.3",
|
package/src/codemirror.ts
CHANGED
|
@@ -66,11 +66,20 @@ export async function mountCodeEditor(
|
|
|
66
66
|
}),
|
|
67
67
|
],
|
|
68
68
|
parent: container,
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
69
|
+
// The tree the container really belongs to — `getRootNode()`, not CodeMirror's
|
|
70
|
+
// own `getRoot()`. `getRoot()` follows `assignedSlot` before `parentNode`, so a
|
|
71
|
+
// container in a *slotted* subtree is reported as living in the shadow root it
|
|
72
|
+
// is slotted **into**. The SQL result area is exactly that (light DOM, slotted
|
|
73
|
+
// into `<dfk-sql>` through `slot="dfk-result"`), so style-mod mounted the base
|
|
74
|
+
// theme in a shadow root, where it cannot reach slotted nodes at all: the
|
|
75
|
+
// editor laid out as plain block boxes — `.cm-scroller` stacked instead of
|
|
76
|
+
// flex, gutter above the code.
|
|
77
|
+
//
|
|
78
|
+
// `getRootNode()` stops at the tree that owns the container: `document` for
|
|
79
|
+
// that slotted light DOM, the shadow root for a genuine shadow child (the
|
|
80
|
+
// code view, a diagram's dialog) — which is where the `.cm-*` rules are
|
|
81
|
+
// needed. Pinning it to `document` would break the shadow cases instead.
|
|
82
|
+
root: container.getRootNode() as Document | ShadowRoot,
|
|
74
83
|
});
|
|
75
84
|
|
|
76
85
|
return {
|
|
@@ -1,28 +1,46 @@
|
|
|
1
1
|
import filenamify from 'filenamify';
|
|
2
|
+
import {el} from './dom';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* Saving something the kit rendered to a file: the payload shape, the click that
|
|
6
|
+
* performs the save, and how the file is named.
|
|
5
7
|
*
|
|
6
|
-
*
|
|
7
|
-
* of
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Three sources, most specific first, each falling back to the next:
|
|
11
|
-
*
|
|
12
|
-
* 1. **The diagram's own title** — mermaid's frontmatter
|
|
13
|
-
* (`---\ntitle: …\n---`, which mermaid itself draws above the diagram).
|
|
14
|
-
* 2. **The nearest heading above it** — the section the diagram belongs to, which
|
|
15
|
-
* on a docs page is what a reader would call it.
|
|
16
|
-
* 3. **The document title** — the browser tab's title, for a diagram that sits
|
|
17
|
-
* above every heading on its page.
|
|
18
|
-
*
|
|
19
|
-
* Nothing found → {@link DEFAULT_DIAGRAM_FILE}.
|
|
8
|
+
* Shared by `<dfk-mermaid>` (a downloaded diagram) and the SQL result chrome (a
|
|
9
|
+
* downloaded result of any kind), so a docs page has one definition of "what a
|
|
10
|
+
* download is" rather than one per component.
|
|
20
11
|
*
|
|
21
12
|
* This is browser-only code.
|
|
22
13
|
*/
|
|
23
14
|
|
|
24
|
-
/**
|
|
25
|
-
export
|
|
15
|
+
/** One file's worth of text, ready to save. */
|
|
16
|
+
export interface DownloadPayload {
|
|
17
|
+
/** The file name, extension included. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** The blob's MIME type. */
|
|
20
|
+
mime: string;
|
|
21
|
+
/** The file's contents — text for every format the kit produces. */
|
|
22
|
+
text: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Saves a payload through a temporary object URL.
|
|
27
|
+
*
|
|
28
|
+
* The anchor is in the document for the click — some browsers ignore a detached
|
|
29
|
+
* one — and the URL is revoked on the next tick, once the download has been
|
|
30
|
+
* handed to the browser.
|
|
31
|
+
*/
|
|
32
|
+
export function saveDownload(payload: DownloadPayload): void {
|
|
33
|
+
const blob = new Blob([payload.text], {type: payload.mime});
|
|
34
|
+
const url = URL.createObjectURL(blob);
|
|
35
|
+
const link = el('a', {href: url, download: payload.name});
|
|
36
|
+
document.body.appendChild(link);
|
|
37
|
+
link.click();
|
|
38
|
+
link.remove();
|
|
39
|
+
window.setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Used when nothing on the page says anything about what this is. */
|
|
43
|
+
const DEFAULT_BASE = 'diagram';
|
|
26
44
|
|
|
27
45
|
/**
|
|
28
46
|
* The length cap. `filenamify` truncates by grapheme, so a CJK heading is cut at
|
|
@@ -53,15 +71,39 @@ function cleanTitle(value: string | undefined): string {
|
|
|
53
71
|
return value === undefined ? '' : value.replace(EDGE_NOISE, '');
|
|
54
72
|
}
|
|
55
73
|
|
|
56
|
-
|
|
74
|
+
/**
|
|
75
|
+
* What a downloaded figure is called.
|
|
76
|
+
*
|
|
77
|
+
* The name is derived from the page, not from the figure: a diagram or an SVG
|
|
78
|
+
* result has no name of its own, and the reader downloading one is after "the
|
|
79
|
+
* figure from that section", not `diagram.svg`.
|
|
80
|
+
*
|
|
81
|
+
* Three sources, most specific first, each falling back to the next:
|
|
82
|
+
*
|
|
83
|
+
* 1. **The figure's own title** — mermaid's frontmatter (`---\ntitle: …\n---`,
|
|
84
|
+
* which mermaid itself draws above the diagram); only read when `options.source`
|
|
85
|
+
* is given.
|
|
86
|
+
* 2. **The nearest heading above it** — the section the figure belongs to, which
|
|
87
|
+
* on a docs page is what a reader would call it.
|
|
88
|
+
* 3. **The document title** — the browser tab's title, for a figure that sits
|
|
89
|
+
* above every heading on its page.
|
|
90
|
+
*
|
|
91
|
+
* Nothing found → `options.fallback` (or {@link DEFAULT_BASE}), with `extension`
|
|
92
|
+
* appended either way.
|
|
93
|
+
*/
|
|
94
|
+
export function sectionFileName(
|
|
95
|
+
element: Element,
|
|
96
|
+
extension: string,
|
|
97
|
+
options: {source?: string; fallback?: string} = {},
|
|
98
|
+
): string {
|
|
57
99
|
const candidates = [
|
|
58
|
-
frontmatterTitle(source),
|
|
100
|
+
options.source === undefined ? undefined : frontmatterTitle(options.source),
|
|
59
101
|
precedingHeading(element),
|
|
60
102
|
element.ownerDocument.title,
|
|
61
103
|
].map(cleanTitle);
|
|
62
104
|
const title = candidates.find((candidate) => candidate !== '');
|
|
63
105
|
if (title === undefined) {
|
|
64
|
-
return
|
|
106
|
+
return `${options.fallback ?? DEFAULT_BASE}.${extension}`;
|
|
65
107
|
}
|
|
66
108
|
// Filenames are a filesystem concern, so they go through a library rather than a
|
|
67
109
|
// hand-rolled character class. Three things it does that matter here and that a
|
|
@@ -74,7 +116,7 @@ export function diagramFileName(source: string, element: Element): string {
|
|
|
74
116
|
// It also normalises Unicode whitespace and drops format characters, which
|
|
75
117
|
// quietly cleans up Docusaurus' heading anchors: the `<a>` it appends to every
|
|
76
118
|
// heading contributes a zero-width space to `textContent`.
|
|
77
|
-
return `${filenamify(title, {replacement: REPLACEMENT, maxLength: MAX_LENGTH})}
|
|
119
|
+
return `${filenamify(title, {replacement: REPLACEMENT, maxLength: MAX_LENGTH})}.${extension}`;
|
|
78
120
|
}
|
|
79
121
|
|
|
80
122
|
/**
|
|
@@ -102,11 +144,11 @@ function unquote(value: string): string {
|
|
|
102
144
|
}
|
|
103
145
|
|
|
104
146
|
/**
|
|
105
|
-
* The text of the last heading that precedes the
|
|
147
|
+
* The text of the last heading that precedes the figure, or `undefined`.
|
|
106
148
|
*
|
|
107
149
|
* The search is scoped to the enclosing `<article>` where there is one: a docs
|
|
108
150
|
* page's navbar, sidebar and footer are full of headings that have nothing to do
|
|
109
|
-
* with this
|
|
151
|
+
* with this figure, and the article is the one container that holds the page's
|
|
110
152
|
* own content.
|
|
111
153
|
*/
|
|
112
154
|
function precedingHeading(element: Element): string | undefined {
|
|
@@ -124,4 +166,4 @@ function precedingHeading(element: Element): string | undefined {
|
|
|
124
166
|
}
|
|
125
167
|
}
|
|
126
168
|
return found;
|
|
127
|
-
}
|
|
169
|
+
}
|
|
@@ -16,6 +16,14 @@
|
|
|
16
16
|
margin: 1rem 0;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/* Embedded in a SQL result: the result area's tab strip and panel already draw
|
|
20
|
+
the frame, so the element contributes neither margin nor border nor padding —
|
|
21
|
+
two nested frames are one too many. Its zoom controls live in the tab strip
|
|
22
|
+
instead of floating here, which `DfkMermaid.ts` arranges. */
|
|
23
|
+
:host([embedded]) {
|
|
24
|
+
margin: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
.dfk-mermaid-canvas {
|
|
20
28
|
position: relative;
|
|
21
29
|
}
|
|
@@ -31,6 +39,34 @@
|
|
|
31
39
|
padding: 0.75rem;
|
|
32
40
|
}
|
|
33
41
|
|
|
42
|
+
/* The SQL result panel is the frame; this one is only a clipping box. */
|
|
43
|
+
:host([embedded]) .dfk-mermaid-viewport {
|
|
44
|
+
border: none;
|
|
45
|
+
border-radius: 0;
|
|
46
|
+
padding: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Fullscreen (embedded): the result area hands the panel its height, so the
|
|
50
|
+
viewport fills it instead of sizing to the diagram. `:host(...)` rather than a
|
|
51
|
+
descendant selector because the class sits on the element itself, outside this
|
|
52
|
+
shadow tree. */
|
|
53
|
+
:host(.dfk-mermaid-fullscreen) {
|
|
54
|
+
height: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* The canvas is what the viewport's `height: 100%` resolves against, so the
|
|
58
|
+
fullscreen height has to pass *through* it. Left `auto`, the percentage above
|
|
59
|
+
has nothing to resolve against and collapses back to the diagram's own height —
|
|
60
|
+
the figure then looks the same in fullscreen, and the wheel/drag surface (and
|
|
61
|
+
the clip box) stays a strip as tall as the diagram. */
|
|
62
|
+
:host(.dfk-mermaid-fullscreen) .dfk-mermaid-canvas {
|
|
63
|
+
height: 100%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:host(.dfk-mermaid-fullscreen) .dfk-mermaid-viewport {
|
|
67
|
+
height: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
34
70
|
/* Mermaid sizes its own `<svg>` (`width="100%"` plus an inline `max-width`), so
|
|
35
71
|
nothing here fights that: the rules only centre it once the inline cap kicks
|
|
36
72
|
in, and give it a line box so the viewport has a height. */
|
|
@@ -47,12 +83,13 @@
|
|
|
47
83
|
zoom state rather than set on the element once and for all (panzoom's `cursor`
|
|
48
84
|
option does the latter, and it was promising a pan at fit — over text that is
|
|
49
85
|
perfectly selectable). `cursor` is inherited, so these two rules reach the
|
|
50
|
-
diagram and every label in it; the buttons override it with `pointer`.
|
|
51
|
-
|
|
86
|
+
diagram and every label in it; the buttons override it with `pointer`. The state
|
|
87
|
+
classes are written by the shared `PanZoomView`. */
|
|
88
|
+
.dfk-mermaid-viewport.dfk-panzoom-zoomed {
|
|
52
89
|
cursor: grab;
|
|
53
90
|
}
|
|
54
91
|
|
|
55
|
-
.dfk-mermaid-
|
|
92
|
+
.dfk-mermaid-viewport.dfk-panzoom-zoomed.dfk-panzoom-grabbing {
|
|
56
93
|
cursor: grabbing;
|
|
57
94
|
}
|
|
58
95
|
|
|
@@ -205,7 +242,7 @@
|
|
|
205
242
|
|
|
206
243
|
/* --- Source dialog ------------------------------------------------------- */
|
|
207
244
|
|
|
208
|
-
.dfk-
|
|
245
|
+
.dfk-source-dialog {
|
|
209
246
|
width: min(56rem, 92vw);
|
|
210
247
|
max-width: none;
|
|
211
248
|
padding: 0;
|
|
@@ -215,18 +252,18 @@
|
|
|
215
252
|
color: var(--ifm-font-color-base, #181818);
|
|
216
253
|
}
|
|
217
254
|
|
|
218
|
-
.dfk-
|
|
255
|
+
.dfk-source-dialog::backdrop {
|
|
219
256
|
background: rgb(0 0 0 / 45%);
|
|
220
257
|
}
|
|
221
258
|
|
|
222
|
-
.dfk-
|
|
259
|
+
.dfk-source-dialog-body {
|
|
223
260
|
display: flex;
|
|
224
261
|
flex-direction: column;
|
|
225
262
|
gap: 0.5rem;
|
|
226
263
|
padding: 0.75rem;
|
|
227
264
|
}
|
|
228
265
|
|
|
229
|
-
.dfk-
|
|
266
|
+
.dfk-source-dialog-title {
|
|
230
267
|
margin: 0;
|
|
231
268
|
font-size: 0.95rem;
|
|
232
269
|
font-weight: 600;
|
|
@@ -236,7 +273,7 @@
|
|
|
236
273
|
theme in the same root the `.cm-*` rules are needed in (`src/codemirror.ts`).
|
|
237
274
|
The content inset is CodeMirror's own (`padding: 4px 0`) and is left alone —
|
|
238
275
|
see the note in `DfkSql.css` about `ViewState.measure()`. */
|
|
239
|
-
.dfk-
|
|
276
|
+
.dfk-source-dialog-editor {
|
|
240
277
|
border: 1px solid var(--ifm-global-border-color, #e0e0e0);
|
|
241
278
|
border-radius: var(--ifm-global-radius, 0.4rem);
|
|
242
279
|
overflow: hidden;
|
|
@@ -244,24 +281,24 @@
|
|
|
244
281
|
font-size: var(--ifm-code-font-size, 95%);
|
|
245
282
|
}
|
|
246
283
|
|
|
247
|
-
.dfk-
|
|
284
|
+
.dfk-source-dialog-editor .cm-editor {
|
|
248
285
|
outline: none;
|
|
249
286
|
}
|
|
250
287
|
|
|
251
|
-
.dfk-
|
|
288
|
+
.dfk-source-dialog-editor .cm-scroller {
|
|
252
289
|
max-height: 55vh;
|
|
253
290
|
font-family: var(--ifm-font-family-monospace, ui-monospace, SFMono-Regular,
|
|
254
291
|
Menlo, monospace);
|
|
255
292
|
line-height: 1.6;
|
|
256
293
|
}
|
|
257
294
|
|
|
258
|
-
.dfk-
|
|
295
|
+
.dfk-source-dialog-footer {
|
|
259
296
|
display: flex;
|
|
260
297
|
justify-content: flex-end;
|
|
261
298
|
gap: 0.5rem;
|
|
262
299
|
}
|
|
263
300
|
|
|
264
|
-
.dfk-
|
|
301
|
+
.dfk-source-dialog-button {
|
|
265
302
|
padding: 0.3rem 0.9rem;
|
|
266
303
|
border: 1px solid var(--ifm-global-border-color, #e0e0e0);
|
|
267
304
|
border-radius: var(--ifm-global-radius, 0.25rem);
|
|
@@ -272,18 +309,18 @@
|
|
|
272
309
|
cursor: pointer;
|
|
273
310
|
}
|
|
274
311
|
|
|
275
|
-
.dfk-
|
|
312
|
+
.dfk-source-dialog-button:hover {
|
|
276
313
|
border-color: var(--ifm-color-primary, #14459b);
|
|
277
314
|
color: var(--ifm-color-primary, #14459b);
|
|
278
315
|
}
|
|
279
316
|
|
|
280
|
-
.dfk-
|
|
317
|
+
.dfk-source-dialog-apply {
|
|
281
318
|
border-color: var(--ifm-color-primary, #14459b);
|
|
282
319
|
background: var(--ifm-color-primary, #14459b);
|
|
283
320
|
color: var(--ifm-button-color, #fff);
|
|
284
321
|
}
|
|
285
322
|
|
|
286
|
-
.dfk-
|
|
323
|
+
.dfk-source-dialog-apply:hover {
|
|
287
324
|
color: var(--ifm-button-color, #fff);
|
|
288
325
|
filter: brightness(1.1);
|
|
289
326
|
}
|