@karimsa/mdxserve 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -10
- package/client/CodeBlock.tsx +24 -0
- package/client/DocView.tsx +9 -2
- package/client/ListingView.tsx +14 -10
- package/client/MarkdownTable.tsx +190 -0
- package/client/MdSection.tsx +56 -21
- package/client/MdSectionEditor.tsx +149 -2
- package/client/Mermaid.tsx +33 -7
- package/client/Settings.css +17 -0
- package/client/Settings.tsx +132 -0
- package/client/Table.tsx +2 -14
- package/client/app.css +339 -1
- package/client/block-state/README.md +39 -0
- package/client/block-state/storage.ts +73 -0
- package/client/block-state/useBlockState.ts +120 -0
- package/client/builtins/DataTable.tsx +269 -0
- package/client/builtins/chart-data.ts +22 -10
- package/client/builtins/index.ts +10 -0
- package/client/data-table/ColumnMenu.tsx +168 -0
- package/client/data-table/ColumnOptions.tsx +141 -0
- package/client/data-table/TableCell.tsx +76 -0
- package/client/data-table/TableHeaderCell.tsx +94 -0
- package/client/data-table/model.ts +194 -0
- package/client/data-table/navigation.ts +34 -0
- package/client/data-table/table-state.ts +196 -0
- package/client/data-table/useColumnMenu.ts +84 -0
- package/client/data-table/useTableState.ts +36 -0
- package/client/design/base/prose.css +4 -0
- package/client/diagrams/Dialog.tsx +577 -0
- package/client/diagrams/EditDialog.tsx +67 -0
- package/client/diagrams/Preferences.tsx +213 -0
- package/client/diagrams/edit-block.ts +93 -0
- package/client/diagrams/edit-context.ts +4 -0
- package/client/diagrams/session-id.ts +8 -0
- package/client/diagrams/slash.ts +94 -0
- package/client/diagrams/source.ts +73 -0
- package/client/diagrams/style.css +599 -0
- package/client/entry.tsx +2 -0
- package/client/mdx-components-base.ts +2 -2
- package/client/router.ts +8 -1
- package/client/shell/AppShell.tsx +38 -15
- package/client/shell/Sidebar.tsx +20 -2
- package/client/shell/StandaloneShell.tsx +19 -6
- package/client/shell/TocRail.tsx +4 -3
- package/client/shell/TopBar.tsx +17 -5
- package/client/state.ts +14 -0
- package/client/ui/ConfirmDeleteDialog.tsx +48 -91
- package/client/ui/ExpandModal.tsx +63 -104
- package/client/ui/Modal.tsx +123 -0
- package/client/ui/ResizeHandle.tsx +1 -1
- package/client/ui/SearchDialog.tsx +81 -88
- package/client/ui/modal.css +42 -0
- package/dist/cli.js +2191 -778
- package/dist/diagram-worker.js +17 -0
- package/dist/registry.json +297 -0
- package/package.json +42 -2
- package/skills/mdxserve/SKILL.md +56 -1
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
findDiagram,
|
|
3
|
+
replaceDiagram,
|
|
4
|
+
mermaidEditButtons,
|
|
5
|
+
type DiagramEditTarget,
|
|
6
|
+
} from "./diagrams/edit-block";
|
|
7
|
+
import type { ExistingDiagram } from "./diagrams/edit-context";
|
|
8
|
+
import { mermaidSlash } from "./diagrams/slash";
|
|
9
|
+
import { MermaidDialog } from "./diagrams/Dialog";
|
|
10
|
+
import type { Range } from "@tiptap/core";
|
|
11
|
+
import {
|
|
12
|
+
useEffect,
|
|
13
|
+
useState,
|
|
14
|
+
useRef,
|
|
15
|
+
useCallback,
|
|
16
|
+
type KeyboardEvent,
|
|
17
|
+
type MouseEvent,
|
|
18
|
+
} from "react";
|
|
2
19
|
import type { Editor } from "@tiptap/core";
|
|
3
20
|
import type { Mark } from "@tiptap/pm/model";
|
|
4
21
|
import { EditorContent, useEditor } from "@tiptap/react";
|
|
@@ -7,7 +24,7 @@ import { TaskItem, TaskList } from "@tiptap/extension-list";
|
|
|
7
24
|
import { TableKit } from "@tiptap/extension-table";
|
|
8
25
|
import { Image } from "@tiptap/extension-image";
|
|
9
26
|
import { Markdown } from "@tiptap/markdown";
|
|
10
|
-
import { motion } from "framer-motion";
|
|
27
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
11
28
|
import { isTRPCClientError } from "@trpc/client";
|
|
12
29
|
import { trpcClient } from "./api";
|
|
13
30
|
import { TRANSITIONS } from "./motion";
|
|
@@ -25,6 +42,7 @@ const hint = {
|
|
|
25
42
|
};
|
|
26
43
|
|
|
27
44
|
export interface MdSectionEditorProps {
|
|
45
|
+
initialDiagram?: ExistingDiagram;
|
|
28
46
|
/** Raw markdown for exactly [startLine, endLine], sliced by MdSection.tsx. */
|
|
29
47
|
source: string;
|
|
30
48
|
/** Version token for the whole file at the moment `source` was fetched — the save's stale-write guard. */
|
|
@@ -81,6 +99,7 @@ function unwrapSoftBreaks(editor: Editor): void {
|
|
|
81
99
|
* `@tiptap/extension-link` install is needed to configure `link`.
|
|
82
100
|
*/
|
|
83
101
|
export default function MdSectionEditor({
|
|
102
|
+
initialDiagram,
|
|
84
103
|
source,
|
|
85
104
|
version,
|
|
86
105
|
path,
|
|
@@ -89,6 +108,39 @@ export default function MdSectionEditor({
|
|
|
89
108
|
onDone,
|
|
90
109
|
}: MdSectionEditorProps) {
|
|
91
110
|
const [saving, setSaving] = useState(false);
|
|
111
|
+
const diagramEnabled = useRef(false);
|
|
112
|
+
const [diagramReady, setDiagramReady] = useState(false);
|
|
113
|
+
const [editTarget, setEditTarget] = useState<DiagramEditTarget | null>(null);
|
|
114
|
+
const openedDiagram = useRef(false);
|
|
115
|
+
const [diagramRange, setDiagramRange] = useState<Range | null>(null);
|
|
116
|
+
const openDiagram = useRef<(range: Range) => void>(() => {});
|
|
117
|
+
openDiagram.current = setDiagramRange;
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
let active = true;
|
|
120
|
+
const refresh = () => {
|
|
121
|
+
void trpcClient.getDiagramPreferences
|
|
122
|
+
.query({})
|
|
123
|
+
.then((settings) => {
|
|
124
|
+
if (active) {
|
|
125
|
+
diagramEnabled.current = settings.agent !== "disabled";
|
|
126
|
+
setDiagramReady(diagramEnabled.current);
|
|
127
|
+
if (!diagramEnabled.current) {
|
|
128
|
+
setDiagramRange(null);
|
|
129
|
+
setEditTarget(null);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
.catch(() => {
|
|
134
|
+
diagramEnabled.current = false;
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
refresh();
|
|
138
|
+
window.addEventListener("mdxserve-diagram-preferences", refresh);
|
|
139
|
+
return () => {
|
|
140
|
+
active = false;
|
|
141
|
+
window.removeEventListener("mdxserve-diagram-preferences", refresh);
|
|
142
|
+
};
|
|
143
|
+
}, []);
|
|
92
144
|
// Drives the hints' entrance. Not framer's `initial`/`animate` on mount:
|
|
93
145
|
// this component mounts through Suspense inside a transition, and its
|
|
94
146
|
// first commit can happen while the subtree is still hidden, so a
|
|
@@ -99,6 +151,11 @@ export default function MdSectionEditor({
|
|
|
99
151
|
|
|
100
152
|
const editor = useEditor({
|
|
101
153
|
extensions: [
|
|
154
|
+
mermaidEditButtons(() => diagramEnabled.current, setEditTarget),
|
|
155
|
+
mermaidSlash(
|
|
156
|
+
() => diagramEnabled.current,
|
|
157
|
+
(range) => openDiagram.current(range),
|
|
158
|
+
),
|
|
102
159
|
StarterKit.configure({
|
|
103
160
|
heading: { levels: [1, 2, 3, 4, 5, 6] },
|
|
104
161
|
link: { openOnClick: false },
|
|
@@ -131,6 +188,44 @@ export default function MdSectionEditor({
|
|
|
131
188
|
setEntered(true);
|
|
132
189
|
}, [editor]);
|
|
133
190
|
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (!editor || editor.isDestroyed || !initialDiagram || openedDiagram.current) return;
|
|
193
|
+
openedDiagram.current = true;
|
|
194
|
+
const target = findDiagram(editor, initialDiagram);
|
|
195
|
+
if (target) setEditTarget(target);
|
|
196
|
+
else
|
|
197
|
+
pushToast({
|
|
198
|
+
tone: "warn",
|
|
199
|
+
text: "Diagram changed on disk. Reopen the section before editing.",
|
|
200
|
+
});
|
|
201
|
+
}, [editor, initialDiagram]);
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (!editor || editor.isDestroyed || !diagramReady || openedDiagram.current) return;
|
|
205
|
+
openedDiagram.current = true;
|
|
206
|
+
const document = editor.state.doc;
|
|
207
|
+
const block = document.firstChild;
|
|
208
|
+
// Tiptap adds an empty trailing paragraph after a code block.
|
|
209
|
+
const onlyDiagram =
|
|
210
|
+
document.childCount === 1 ||
|
|
211
|
+
(document.childCount === 2 &&
|
|
212
|
+
document.lastChild?.type.name === "paragraph" &&
|
|
213
|
+
document.lastChild.content.size === 0);
|
|
214
|
+
if (onlyDiagram && block?.type.name === "codeBlock" && block.attrs.language === "mermaid") {
|
|
215
|
+
setEditTarget({ range: { from: 0, to: block.nodeSize }, source: block.textContent });
|
|
216
|
+
}
|
|
217
|
+
}, [editor, diagramReady]);
|
|
218
|
+
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
if (editor && !editor.isDestroyed) editor.view.dispatch(editor.state.tr);
|
|
221
|
+
}, [editor, diagramReady]);
|
|
222
|
+
|
|
223
|
+
const closeDiagram = useCallback(() => {
|
|
224
|
+
setDiagramRange(null);
|
|
225
|
+
setEditTarget(null);
|
|
226
|
+
editor?.commands.focus();
|
|
227
|
+
}, [editor]);
|
|
228
|
+
|
|
134
229
|
async function save() {
|
|
135
230
|
if (!editor || saving) return;
|
|
136
231
|
setSaving(true);
|
|
@@ -187,6 +282,7 @@ export default function MdSectionEditor({
|
|
|
187
282
|
}
|
|
188
283
|
|
|
189
284
|
function onKeyDown(event: KeyboardEvent<HTMLDivElement>) {
|
|
285
|
+
if (diagramRange || editTarget) return;
|
|
190
286
|
if ((event.metaKey || event.ctrlKey) && event.key === "s") {
|
|
191
287
|
event.preventDefault();
|
|
192
288
|
void save();
|
|
@@ -207,6 +303,57 @@ export default function MdSectionEditor({
|
|
|
207
303
|
return (
|
|
208
304
|
<div className="mdx-section" data-editing onClick={onClick} onKeyDown={onKeyDown}>
|
|
209
305
|
<EditorContent editor={editor} />
|
|
306
|
+
<AnimatePresence>
|
|
307
|
+
{(diagramRange || editTarget) && (
|
|
308
|
+
<MermaidDialog
|
|
309
|
+
initialSource={editTarget?.source}
|
|
310
|
+
onClose={closeDiagram}
|
|
311
|
+
onInsert={(source) => {
|
|
312
|
+
if (editTarget) {
|
|
313
|
+
if (!replaceDiagram(editor, editTarget, source)) {
|
|
314
|
+
pushToast({
|
|
315
|
+
tone: "warn",
|
|
316
|
+
text: "Diagram changed. Reopen it before applying changes.",
|
|
317
|
+
});
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
closeDiagram();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (!diagramRange) return;
|
|
324
|
+
if (diagramRange.to > editor.state.doc.content.size) {
|
|
325
|
+
closeDiagram();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const from = editor.state.doc.resolve(diagramRange.from);
|
|
329
|
+
if (
|
|
330
|
+
from.parent.type.name !== "paragraph" ||
|
|
331
|
+
from.parent.textContent !==
|
|
332
|
+
editor.state.doc.textBetween(diagramRange.from, diagramRange.to)
|
|
333
|
+
) {
|
|
334
|
+
pushToast({
|
|
335
|
+
tone: "warn",
|
|
336
|
+
text: "Insertion location changed. Close the dialog and choose a new block.",
|
|
337
|
+
});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
editor
|
|
341
|
+
.chain()
|
|
342
|
+
.focus()
|
|
343
|
+
.insertContentAt(
|
|
344
|
+
{ from: from.before(), to: from.after() },
|
|
345
|
+
{
|
|
346
|
+
type: "codeBlock",
|
|
347
|
+
attrs: { language: "mermaid" },
|
|
348
|
+
content: [{ type: "text", text: source }],
|
|
349
|
+
},
|
|
350
|
+
)
|
|
351
|
+
.run();
|
|
352
|
+
closeDiagram();
|
|
353
|
+
}}
|
|
354
|
+
/>
|
|
355
|
+
)}
|
|
356
|
+
</AnimatePresence>
|
|
210
357
|
{/* Keyboard is the only way out of edit mode (no buttons), so the hints
|
|
211
358
|
float in the right gutter beside the frame, out of the text flow —
|
|
212
359
|
the section keeps its read-mode box exactly. pointer-events-none so
|
package/client/Mermaid.tsx
CHANGED
|
@@ -131,7 +131,17 @@ function useThemeTick(): number {
|
|
|
131
131
|
* mirrored into the expanded modal's header so view controls that live in the
|
|
132
132
|
* code frame (the flow-direction toggle) stay reachable at full size.
|
|
133
133
|
*/
|
|
134
|
-
export function MermaidDiagram({
|
|
134
|
+
export function MermaidDiagram({
|
|
135
|
+
source,
|
|
136
|
+
toolbar,
|
|
137
|
+
onRender,
|
|
138
|
+
fill = false,
|
|
139
|
+
}: {
|
|
140
|
+
source: string;
|
|
141
|
+
toolbar?: ReactNode;
|
|
142
|
+
onRender?: (source: string, valid: boolean) => void;
|
|
143
|
+
fill?: boolean;
|
|
144
|
+
}) {
|
|
135
145
|
// Seeded from the source so a chart-diagram fence never shows the
|
|
136
146
|
// "Rendering diagram…" placeholder — there is nothing to load for it.
|
|
137
147
|
const [state, setState] = useState<State>(() => {
|
|
@@ -177,7 +187,10 @@ export function MermaidDiagram({ source, toolbar }: { source: string; toolbar?:
|
|
|
177
187
|
return mermaid.render(renderId, source);
|
|
178
188
|
})
|
|
179
189
|
.then(({ svg }) => {
|
|
180
|
-
if (!cancelled)
|
|
190
|
+
if (!cancelled) {
|
|
191
|
+
setState({ kind: "ok", svg });
|
|
192
|
+
onRender?.(source, true);
|
|
193
|
+
}
|
|
181
194
|
})
|
|
182
195
|
.catch((error: unknown) => {
|
|
183
196
|
// mermaid.render() draws into a temporary `<div id="d<renderId>">` it
|
|
@@ -191,11 +204,12 @@ export function MermaidDiagram({ source, toolbar }: { source: string; toolbar?:
|
|
|
191
204
|
if (cancelled) return;
|
|
192
205
|
const message = error instanceof Error ? error.message : String(error);
|
|
193
206
|
setState({ kind: "error", message });
|
|
207
|
+
onRender?.(source, false);
|
|
194
208
|
});
|
|
195
209
|
return () => {
|
|
196
210
|
cancelled = true;
|
|
197
211
|
};
|
|
198
|
-
}, [source, id, themeTick]);
|
|
212
|
+
}, [source, id, themeTick, onRender]);
|
|
199
213
|
|
|
200
214
|
return (
|
|
201
215
|
<AnimatePresence mode="wait" initial={false}>
|
|
@@ -230,8 +244,12 @@ export function MermaidDiagram({ source, toolbar }: { source: string; toolbar?:
|
|
|
230
244
|
</DiagramNoticeBody>
|
|
231
245
|
</motion.div>
|
|
232
246
|
) : (
|
|
233
|
-
<motion.div key="ok" {...VARIANTS.fade}>
|
|
234
|
-
|
|
247
|
+
<motion.div key="ok" {...VARIANTS.fade} className={fill ? "h-full w-full" : undefined}>
|
|
248
|
+
{fill ? (
|
|
249
|
+
<PanZoomSvg svg={state.svg} viewportClassName="h-full" />
|
|
250
|
+
) : (
|
|
251
|
+
<ExpandableDiagram svg={state.svg} toolbar={toolbar} />
|
|
252
|
+
)}
|
|
235
253
|
</motion.div>
|
|
236
254
|
)}
|
|
237
255
|
</AnimatePresence>
|
|
@@ -384,6 +402,7 @@ function PanZoomSvg({
|
|
|
384
402
|
*/
|
|
385
403
|
function ExpandableDiagram({ svg, toolbar }: { svg: string; toolbar?: ReactNode }) {
|
|
386
404
|
const [expanded, setExpanded] = useState(false);
|
|
405
|
+
const [closing, setClosing] = useState(false);
|
|
387
406
|
return (
|
|
388
407
|
<>
|
|
389
408
|
{/* Only one copy of the markup is live at a time. Mermaid doesn't namespace
|
|
@@ -392,7 +411,7 @@ function ExpandableDiagram({ svg, toolbar }: { svg: string; toolbar?: ReactNode
|
|
|
392
411
|
SVG — the one svg-pan-zoom has already wrapped and transformed. The
|
|
393
412
|
card sits behind the scrim while expanded, so the placeholder that
|
|
394
413
|
holds its height never shows. */}
|
|
395
|
-
{expanded ? (
|
|
414
|
+
{expanded || closing ? (
|
|
396
415
|
<div className="h-96 w-full" aria-hidden="true" />
|
|
397
416
|
) : (
|
|
398
417
|
<PanZoomSvg svg={svg} viewportClassName="h-96" onExpand={() => setExpanded(true)} />
|
|
@@ -401,7 +420,11 @@ function ExpandableDiagram({ svg, toolbar }: { svg: string; toolbar?: ReactNode
|
|
|
401
420
|
open={expanded}
|
|
402
421
|
svg={svg}
|
|
403
422
|
toolbar={toolbar}
|
|
404
|
-
onClose={() =>
|
|
423
|
+
onClose={() => {
|
|
424
|
+
setClosing(true);
|
|
425
|
+
setExpanded(false);
|
|
426
|
+
}}
|
|
427
|
+
onExitComplete={() => setClosing(false)}
|
|
405
428
|
/>
|
|
406
429
|
</>
|
|
407
430
|
);
|
|
@@ -412,16 +435,19 @@ function DiagramModal({
|
|
|
412
435
|
svg,
|
|
413
436
|
toolbar,
|
|
414
437
|
onClose,
|
|
438
|
+
onExitComplete,
|
|
415
439
|
}: {
|
|
416
440
|
open: boolean;
|
|
417
441
|
svg: string;
|
|
418
442
|
toolbar?: ReactNode;
|
|
419
443
|
onClose: () => void;
|
|
444
|
+
onExitComplete: () => void;
|
|
420
445
|
}) {
|
|
421
446
|
return (
|
|
422
447
|
<ExpandModal
|
|
423
448
|
open={open}
|
|
424
449
|
onClose={onClose}
|
|
450
|
+
onExitComplete={onExitComplete}
|
|
425
451
|
icon="image"
|
|
426
452
|
title="Diagram"
|
|
427
453
|
hint="Drag to pan · scroll to zoom"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
@media (max-width: 639px) {
|
|
2
|
+
.diagram-settings .diagram-settings-body section {
|
|
3
|
+
padding: 20px;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.diagram-settings .diagram-setting-row {
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: stretch;
|
|
9
|
+
gap: 10px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.diagram-settings .diagram-setting-row .diagram-agent-select,
|
|
13
|
+
.diagram-settings .diagram-setting-row select {
|
|
14
|
+
width: 100%;
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { useAtom } from "jotai";
|
|
2
|
+
import { useState, type ReactNode } from "react";
|
|
3
|
+
import ChevronDown from "lucide-react/dist/esm/icons/chevron-down.mjs";
|
|
4
|
+
import Network from "lucide-react/dist/esm/icons/network.mjs";
|
|
5
|
+
import PanelsTopLeft from "lucide-react/dist/esm/icons/panels-top-left.mjs";
|
|
6
|
+
import SettingsIcon from "lucide-react/dist/esm/icons/settings.mjs";
|
|
7
|
+
import CloseIcon from "lucide-react/dist/esm/icons/x.mjs";
|
|
8
|
+
import { contentLayoutAtom, tocVisibleAtom, type ContentLayout } from "./state";
|
|
9
|
+
import { Modal } from "./ui/Modal";
|
|
10
|
+
|
|
11
|
+
export function Settings({
|
|
12
|
+
onClose,
|
|
13
|
+
diagrams,
|
|
14
|
+
status,
|
|
15
|
+
saving = false,
|
|
16
|
+
}: {
|
|
17
|
+
onClose: () => void;
|
|
18
|
+
diagrams?: ReactNode;
|
|
19
|
+
status?: ReactNode;
|
|
20
|
+
saving?: boolean;
|
|
21
|
+
}) {
|
|
22
|
+
const [category, setCategory] = useState<"diagrams" | "layout">(diagrams ? "diagrams" : "layout");
|
|
23
|
+
const [layout, setLayout] = useAtom(contentLayoutAtom);
|
|
24
|
+
const [tocVisible, setTocVisible] = useAtom(tocVisibleAtom);
|
|
25
|
+
return (
|
|
26
|
+
<Modal
|
|
27
|
+
open
|
|
28
|
+
onClose={onClose}
|
|
29
|
+
dismissible={!saving}
|
|
30
|
+
className="diagram-dialog diagram-settings"
|
|
31
|
+
aria-label="Settings"
|
|
32
|
+
>
|
|
33
|
+
<header className="diagram-navbar">
|
|
34
|
+
<div className="diagram-title">
|
|
35
|
+
<SettingsIcon size={17} />
|
|
36
|
+
<h2>Settings</h2>
|
|
37
|
+
</div>
|
|
38
|
+
<button
|
|
39
|
+
type="button"
|
|
40
|
+
className="diagram-icon-button"
|
|
41
|
+
aria-label="Close settings"
|
|
42
|
+
disabled={saving}
|
|
43
|
+
onClick={onClose}
|
|
44
|
+
>
|
|
45
|
+
<CloseIcon size={18} />
|
|
46
|
+
</button>
|
|
47
|
+
</header>
|
|
48
|
+
<div className="diagram-settings-body">
|
|
49
|
+
<aside>
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
className="diagram-settings-category"
|
|
53
|
+
aria-pressed={category === "layout"}
|
|
54
|
+
onClick={() => setCategory("layout")}
|
|
55
|
+
>
|
|
56
|
+
<PanelsTopLeft size={15} />
|
|
57
|
+
Layout
|
|
58
|
+
</button>
|
|
59
|
+
{diagrams && (
|
|
60
|
+
<button
|
|
61
|
+
type="button"
|
|
62
|
+
className="diagram-settings-category"
|
|
63
|
+
aria-pressed={category === "diagrams"}
|
|
64
|
+
onClick={() => setCategory("diagrams")}
|
|
65
|
+
>
|
|
66
|
+
<Network size={15} />
|
|
67
|
+
Diagrams
|
|
68
|
+
</button>
|
|
69
|
+
)}
|
|
70
|
+
</aside>
|
|
71
|
+
{category === "layout" ? (
|
|
72
|
+
<section>
|
|
73
|
+
<h3>Layout</h3>
|
|
74
|
+
<p>Choose how content fills the available space.</p>
|
|
75
|
+
<label className="diagram-setting-row" htmlFor="content-layout">
|
|
76
|
+
<span>
|
|
77
|
+
<strong>Content layout</strong>
|
|
78
|
+
<small>
|
|
79
|
+
{layout === "flexible"
|
|
80
|
+
? "Drag the bars to resize content."
|
|
81
|
+
: "Content automatically fills the available width."}
|
|
82
|
+
</small>
|
|
83
|
+
</span>
|
|
84
|
+
<span className="diagram-agent-select">
|
|
85
|
+
<select
|
|
86
|
+
id="content-layout"
|
|
87
|
+
value={layout}
|
|
88
|
+
onChange={(event) => setLayout(event.target.value as ContentLayout)}
|
|
89
|
+
>
|
|
90
|
+
<option value="flexible">Flexible</option>
|
|
91
|
+
<option value="full-width">Full width</option>
|
|
92
|
+
</select>
|
|
93
|
+
<ChevronDown size={14} aria-hidden="true" />
|
|
94
|
+
</span>
|
|
95
|
+
</label>
|
|
96
|
+
<label className="diagram-setting-row" htmlFor="toc-visibility">
|
|
97
|
+
<span>
|
|
98
|
+
<strong>Table of contents</strong>
|
|
99
|
+
<small>Show the “On this page” navigation beside documents.</small>
|
|
100
|
+
</span>
|
|
101
|
+
<span className="diagram-agent-select">
|
|
102
|
+
<select
|
|
103
|
+
id="toc-visibility"
|
|
104
|
+
value={tocVisible ? "visible" : "hidden"}
|
|
105
|
+
onChange={(event) => setTocVisible(event.target.value === "visible")}
|
|
106
|
+
>
|
|
107
|
+
<option value="visible">Visible</option>
|
|
108
|
+
<option value="hidden">Hidden</option>
|
|
109
|
+
</select>
|
|
110
|
+
<ChevronDown size={14} aria-hidden="true" />
|
|
111
|
+
</span>
|
|
112
|
+
</label>
|
|
113
|
+
</section>
|
|
114
|
+
) : (
|
|
115
|
+
diagrams
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
<footer className="diagram-settings-footer">
|
|
119
|
+
<span>
|
|
120
|
+
{category === "layout" ? (
|
|
121
|
+
"Saved in this browser."
|
|
122
|
+
) : (
|
|
123
|
+
<>
|
|
124
|
+
Also available through <code>mdxserve setup</code>
|
|
125
|
+
</>
|
|
126
|
+
)}
|
|
127
|
+
</span>
|
|
128
|
+
{status ?? <span role="status">Settings save automatically.</span>}
|
|
129
|
+
</footer>
|
|
130
|
+
</Modal>
|
|
131
|
+
);
|
|
132
|
+
}
|
package/client/Table.tsx
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MDXProvider `table` override. Tables with long cells (paths, code) can't
|
|
5
|
-
* shrink below their content, so without this they'd overflow the prose column
|
|
6
|
-
* and run under the TOC rail. Each table scrolls inside its own container.
|
|
7
|
-
*/
|
|
8
|
-
export function Table(props: TableHTMLAttributes<HTMLTableElement>) {
|
|
9
|
-
return (
|
|
10
|
-
<div className="mdx-table-scroll">
|
|
11
|
-
<table {...props} />
|
|
12
|
-
</div>
|
|
13
|
-
);
|
|
14
|
-
}
|
|
1
|
+
// Compatibility export; native Markdown tables use the shared DataTable renderer.
|
|
2
|
+
export { MarkdownTable as Table } from "./MarkdownTable";
|