@karimsa/mdxserve 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +233 -2
- package/client/App.tsx +7 -0
- package/client/CodeBlock.tsx +395 -0
- package/client/CrossFade.tsx +72 -0
- package/client/DocContext.ts +14 -0
- package/client/DocView.tsx +107 -0
- package/client/ErrorBox.tsx +23 -0
- package/client/Heading.tsx +31 -0
- package/client/HomeEmptyState.tsx +101 -0
- package/client/HomeView.tsx +78 -0
- package/client/ListingView.tsx +663 -0
- package/client/MdSection.tsx +234 -0
- package/client/MdSectionEditor.tsx +233 -0
- package/client/Mermaid.tsx +435 -0
- package/client/RenderErrorBoundary.tsx +40 -0
- package/client/Table.tsx +14 -0
- package/client/TaskCheckbox.tsx +38 -0
- package/client/api.ts +138 -0
- package/client/app.css +372 -0
- package/client/builtins/Badge.tsx +109 -0
- package/client/builtins/Button.tsx +111 -0
- package/client/builtins/Callout.tsx +97 -0
- package/client/builtins/Card.tsx +111 -0
- package/client/builtins/Chart.tsx +875 -0
- package/client/builtins/Diff.tsx +722 -0
- package/client/builtins/Dropdown.tsx +417 -0
- package/client/builtins/FileTree.tsx +87 -0
- package/client/builtins/Kbd.tsx +18 -0
- package/client/builtins/Screenshot.tsx +209 -0
- package/client/builtins/Sparkline.tsx +63 -0
- package/client/builtins/Tabs.tsx +169 -0
- package/client/builtins/Tooltip.tsx +52 -0
- package/client/builtins/chart-data.ts +133 -0
- package/client/builtins/index.ts +167 -0
- package/client/design/base/editor.css +151 -0
- package/client/design/base/prose.css +143 -0
- package/client/design/base/reset.css +79 -0
- package/client/design/tokens/colors.css +188 -0
- package/client/design/tokens/elevation.css +42 -0
- package/client/design/tokens/fonts.css +6 -0
- package/client/design/tokens/motion.css +76 -0
- package/client/design/tokens/spacing.css +34 -0
- package/client/design/tokens/typography.css +56 -0
- package/client/doc-module-cache.ts +17 -0
- package/client/editor-link.ts +27 -0
- package/client/entry.tsx +51 -0
- package/client/export-doc.ts +80 -0
- package/client/export-save.ts +96 -0
- package/client/favicon.svg +1 -0
- package/client/file-system-access.d.ts +29 -0
- package/client/format.ts +17 -0
- package/client/hooks.ts +34 -0
- package/client/lucide-icons.d.ts +9 -0
- package/client/mdx-components-base.ts +32 -0
- package/client/mdx-components.ts +18 -0
- package/client/mermaid-chart.ts +109 -0
- package/client/mermaid-direction.ts +73 -0
- package/client/motion.ts +104 -0
- package/client/platform.ts +16 -0
- package/client/route-path.ts +15 -0
- package/client/router.ts +452 -0
- package/client/shell/AppShell.tsx +401 -0
- package/client/shell/Footer.tsx +33 -0
- package/client/shell/NotFoundView.tsx +22 -0
- package/client/shell/Sidebar.tsx +169 -0
- package/client/shell/StandaloneShell.tsx +65 -0
- package/client/shell/TocRail.tsx +53 -0
- package/client/shell/TopBar.tsx +117 -0
- package/client/shell/use-doc-width.ts +61 -0
- package/client/shell/useToc.ts +77 -0
- package/client/ssr-entry.tsx +22 -0
- package/client/standalone-entry.tsx +51 -0
- package/client/state.ts +90 -0
- package/client/theme.ts +65 -0
- package/client/ui/Breadcrumb.tsx +49 -0
- package/client/ui/ConfirmDeleteDialog.tsx +113 -0
- package/client/ui/ExpandModal.tsx +342 -0
- package/client/ui/Icon.tsx +114 -0
- package/client/ui/IconButton.tsx +63 -0
- package/client/ui/Kbd.tsx +17 -0
- package/client/ui/PageNav.tsx +77 -0
- package/client/ui/ResizeHandle.tsx +201 -0
- package/client/ui/SearchDialog.tsx +187 -0
- package/client/ui/Tag.tsx +44 -0
- package/client/ui/Toast.tsx +189 -0
- package/client/ui/TocList.tsx +71 -0
- package/client/ui/icon-set.ts +102 -0
- package/client/ui/toast-count.ts +28 -0
- package/dist/cli.js +5091 -0
- package/dist/registry.json +703 -0
- package/dist/render-worker.js +145 -0
- package/package.json +115 -5
- package/skills/mdxserve/SKILL.md +178 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from "react";
|
|
2
|
+
import { motion } from "framer-motion";
|
|
3
|
+
import { TRANSITIONS } from "../motion";
|
|
4
|
+
|
|
5
|
+
export interface TocItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: ReactNode;
|
|
8
|
+
/** Heading level, 2-4. */
|
|
9
|
+
level?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// `title` shadows the native HTML tooltip attribute (typed `string` on `HTMLAttributes`).
|
|
13
|
+
export interface TocListProps extends Omit<HTMLAttributes<HTMLElement>, "title" | "onSelect"> {
|
|
14
|
+
items?: TocItem[];
|
|
15
|
+
/** id of the heading currently in view. */
|
|
16
|
+
activeId?: string;
|
|
17
|
+
onSelect?: (id: string) => void;
|
|
18
|
+
/** Set to null to hide the label. */
|
|
19
|
+
title?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** "On this page" rail, kept in sync with scroll position by the caller via `activeId`. */
|
|
23
|
+
export function TocList({
|
|
24
|
+
items = [],
|
|
25
|
+
activeId,
|
|
26
|
+
onSelect,
|
|
27
|
+
title = "On this page",
|
|
28
|
+
className,
|
|
29
|
+
...rest
|
|
30
|
+
}: TocListProps) {
|
|
31
|
+
return (
|
|
32
|
+
<nav className={"flex flex-col gap-2" + (className ? " " + className : "")} {...rest}>
|
|
33
|
+
{title ? (
|
|
34
|
+
<div className="font-mono text-[length:var(--size-2xs)] font-semibold uppercase tracking-[var(--tracking-caps)] text-text-subtle">
|
|
35
|
+
{title}
|
|
36
|
+
</div>
|
|
37
|
+
) : null}
|
|
38
|
+
<div className="relative flex flex-col">
|
|
39
|
+
{items.map((item) => {
|
|
40
|
+
const active = item.id === activeId;
|
|
41
|
+
return (
|
|
42
|
+
<a
|
|
43
|
+
key={item.id}
|
|
44
|
+
href={"#" + item.id}
|
|
45
|
+
onClick={(event) => {
|
|
46
|
+
if (onSelect) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
onSelect(item.id);
|
|
49
|
+
}
|
|
50
|
+
}}
|
|
51
|
+
className={
|
|
52
|
+
"relative block break-words border-l-2 border-border-default py-[5px] text-[13px] leading-normal no-underline transition-colors " +
|
|
53
|
+
(active ? "font-semibold text-text-accent" : "font-normal text-text-muted")
|
|
54
|
+
}
|
|
55
|
+
style={{ paddingLeft: 10 + ((item.level ?? 2) - 2) * 12 }}
|
|
56
|
+
>
|
|
57
|
+
{active ? (
|
|
58
|
+
<motion.span
|
|
59
|
+
layoutId="toc-active"
|
|
60
|
+
transition={TRANSITIONS.glide}
|
|
61
|
+
className="absolute -left-0.5 top-0 bottom-0 w-0.5 bg-teal-500"
|
|
62
|
+
/>
|
|
63
|
+
) : null}
|
|
64
|
+
{item.label}
|
|
65
|
+
</a>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</div>
|
|
69
|
+
</nav>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { LucideIcon } from "lucide-react";
|
|
2
|
+
// Deep per-file imports are deliberate, not an oversight: importing named
|
|
3
|
+
// icons from lucide-react's entry point pulls in its whole `icons` map (2,022
|
|
4
|
+
// icons, 1.15 MB pre-bundled in dev) regardless of tree-shaking, because Vite
|
|
5
|
+
// pre-bundles a package's entry module as a unit. Reaching straight into
|
|
6
|
+
// `dist/esm/icons/<name>.mjs` keeps each of these to exactly one icon.
|
|
7
|
+
//
|
|
8
|
+
// This is the static "chrome" tier of client/ui/Icon.tsx: every name an
|
|
9
|
+
// mdxserve UI component passes to an icon prop, resolved synchronously with
|
|
10
|
+
// no dynamic import so the header, toolbars, callouts and toasts never flicker.
|
|
11
|
+
// Any other name (an author's `icon="rocket"` in a doc) falls through to
|
|
12
|
+
// `lucide-react/dynamicIconImports` and loads lazily.
|
|
13
|
+
//
|
|
14
|
+
// Regenerate the candidate list with (then review it — a literal on an
|
|
15
|
+
// icon-passing line is not always an icon name):
|
|
16
|
+
// grep -rhE '(icon|Icon|name)[A-Za-z]*\s*[=:]' client --include='*.tsx' --include='*.ts' --exclude-dir=design-ref \
|
|
17
|
+
// | grep -oE '"[a-z][a-z0-9-]*"' | tr -d '"' | sort -u \
|
|
18
|
+
// | node -e '
|
|
19
|
+
// const lines = require("fs").readFileSync(0, "utf8").split("\n").filter(Boolean);
|
|
20
|
+
// import("./node_modules/lucide-react/dynamicIconImports.mjs").then((mod) => {
|
|
21
|
+
// const names = new Set(Object.keys(mod.default));
|
|
22
|
+
// console.log(lines.filter((line) => names.has(line)).join("\n"));
|
|
23
|
+
// });
|
|
24
|
+
// '
|
|
25
|
+
// Two names live in a value map rather than on an icon-passing line and must
|
|
26
|
+
// be kept by hand: "arrow-down" / "arrow-up" (CodeBlock.tsx's flowchart
|
|
27
|
+
// direction glyphs). "check" and "chevron-down" are Dropdown.tsx's.
|
|
28
|
+
import ArrowDown from "lucide-react/dist/esm/icons/arrow-down.mjs";
|
|
29
|
+
import ArrowLeft from "lucide-react/dist/esm/icons/arrow-left.mjs";
|
|
30
|
+
import ArrowRight from "lucide-react/dist/esm/icons/arrow-right.mjs";
|
|
31
|
+
import ArrowUp from "lucide-react/dist/esm/icons/arrow-up.mjs";
|
|
32
|
+
import ArrowUpDown from "lucide-react/dist/esm/icons/arrow-up-down.mjs";
|
|
33
|
+
import ChartBar from "lucide-react/dist/esm/icons/chart-bar.mjs";
|
|
34
|
+
import ChartColumn from "lucide-react/dist/esm/icons/chart-column.mjs";
|
|
35
|
+
import Check from "lucide-react/dist/esm/icons/check.mjs";
|
|
36
|
+
import ChevronDown from "lucide-react/dist/esm/icons/chevron-down.mjs";
|
|
37
|
+
import ChevronRight from "lucide-react/dist/esm/icons/chevron-right.mjs";
|
|
38
|
+
import CircleCheck from "lucide-react/dist/esm/icons/circle-check.mjs";
|
|
39
|
+
import Code from "lucide-react/dist/esm/icons/code.mjs";
|
|
40
|
+
import Copy from "lucide-react/dist/esm/icons/copy.mjs";
|
|
41
|
+
import Download from "lucide-react/dist/esm/icons/download.mjs";
|
|
42
|
+
import Expand from "lucide-react/dist/esm/icons/expand.mjs";
|
|
43
|
+
import File from "lucide-react/dist/esm/icons/file.mjs";
|
|
44
|
+
import FileText from "lucide-react/dist/esm/icons/file-text.mjs";
|
|
45
|
+
import Folder from "lucide-react/dist/esm/icons/folder.mjs";
|
|
46
|
+
import FolderOpen from "lucide-react/dist/esm/icons/folder-open.mjs";
|
|
47
|
+
import FolderPlus from "lucide-react/dist/esm/icons/folder-plus.mjs";
|
|
48
|
+
import ImageIcon from "lucide-react/dist/esm/icons/image.mjs";
|
|
49
|
+
import Info from "lucide-react/dist/esm/icons/info.mjs";
|
|
50
|
+
import Lightbulb from "lucide-react/dist/esm/icons/lightbulb.mjs";
|
|
51
|
+
import ListChecks from "lucide-react/dist/esm/icons/list-checks.mjs";
|
|
52
|
+
import Maximize from "lucide-react/dist/esm/icons/maximize.mjs";
|
|
53
|
+
import Minus from "lucide-react/dist/esm/icons/minus.mjs";
|
|
54
|
+
import Moon from "lucide-react/dist/esm/icons/moon.mjs";
|
|
55
|
+
import OctagonAlert from "lucide-react/dist/esm/icons/octagon-alert.mjs";
|
|
56
|
+
import PanelLeft from "lucide-react/dist/esm/icons/panel-left.mjs";
|
|
57
|
+
import Pencil from "lucide-react/dist/esm/icons/pencil.mjs";
|
|
58
|
+
import Plus from "lucide-react/dist/esm/icons/plus.mjs";
|
|
59
|
+
import Search from "lucide-react/dist/esm/icons/search.mjs";
|
|
60
|
+
import Sun from "lucide-react/dist/esm/icons/sun.mjs";
|
|
61
|
+
import Trash2 from "lucide-react/dist/esm/icons/trash-2.mjs";
|
|
62
|
+
import TriangleAlert from "lucide-react/dist/esm/icons/triangle-alert.mjs";
|
|
63
|
+
import XIcon from "lucide-react/dist/esm/icons/x.mjs";
|
|
64
|
+
|
|
65
|
+
export const staticIcons: Record<string, LucideIcon> = {
|
|
66
|
+
"arrow-down": ArrowDown,
|
|
67
|
+
"arrow-left": ArrowLeft,
|
|
68
|
+
"arrow-right": ArrowRight,
|
|
69
|
+
"arrow-up": ArrowUp,
|
|
70
|
+
"arrow-up-down": ArrowUpDown,
|
|
71
|
+
"chart-bar": ChartBar,
|
|
72
|
+
"chart-column": ChartColumn,
|
|
73
|
+
check: Check,
|
|
74
|
+
"chevron-down": ChevronDown,
|
|
75
|
+
"chevron-right": ChevronRight,
|
|
76
|
+
"circle-check": CircleCheck,
|
|
77
|
+
code: Code,
|
|
78
|
+
copy: Copy,
|
|
79
|
+
download: Download,
|
|
80
|
+
expand: Expand,
|
|
81
|
+
file: File,
|
|
82
|
+
"file-text": FileText,
|
|
83
|
+
folder: Folder,
|
|
84
|
+
"folder-open": FolderOpen,
|
|
85
|
+
"folder-plus": FolderPlus,
|
|
86
|
+
image: ImageIcon,
|
|
87
|
+
info: Info,
|
|
88
|
+
lightbulb: Lightbulb,
|
|
89
|
+
"list-checks": ListChecks,
|
|
90
|
+
maximize: Maximize,
|
|
91
|
+
minus: Minus,
|
|
92
|
+
moon: Moon,
|
|
93
|
+
"octagon-alert": OctagonAlert,
|
|
94
|
+
"panel-left": PanelLeft,
|
|
95
|
+
pencil: Pencil,
|
|
96
|
+
plus: Plus,
|
|
97
|
+
search: Search,
|
|
98
|
+
sun: Sun,
|
|
99
|
+
"trash-2": Trash2,
|
|
100
|
+
"triangle-alert": TriangleAlert,
|
|
101
|
+
x: XIcon,
|
|
102
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advances the repeat-push counter for one dedupe key.
|
|
3
|
+
*
|
|
4
|
+
* `counts` is keyed by dedupe id and tracks how many pushes a still-live
|
|
5
|
+
* toast has absorbed. Before touching `id`, every key no longer present in
|
|
6
|
+
* `live` is dropped — once react-hot-toast has actually removed a toast
|
|
7
|
+
* (auto-dismissed, closed by the reader, or evicted), a later push reusing
|
|
8
|
+
* that id starts a fresh card at count 1 rather than resuming an old count.
|
|
9
|
+
* Then: if `id` is both live and already counted, increment; otherwise this
|
|
10
|
+
* is the first push for that id (or its first push since it went away), so
|
|
11
|
+
* the count resets to 1.
|
|
12
|
+
*/
|
|
13
|
+
export function nextCount(
|
|
14
|
+
live: ReadonlySet<string>,
|
|
15
|
+
counts: Map<string, number>,
|
|
16
|
+
id: string,
|
|
17
|
+
): number {
|
|
18
|
+
for (const key of counts.keys()) {
|
|
19
|
+
if (!live.has(key)) counts.delete(key);
|
|
20
|
+
}
|
|
21
|
+
if (live.has(id) && counts.has(id)) {
|
|
22
|
+
const incremented = counts.get(id)! + 1;
|
|
23
|
+
counts.set(id, incremented);
|
|
24
|
+
return incremented;
|
|
25
|
+
}
|
|
26
|
+
counts.set(id, 1);
|
|
27
|
+
return 1;
|
|
28
|
+
}
|