@mp-lb/mdkit 0.0.1-main.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/README.md +132 -0
- package/dist/collaboration/useMdKitCollaboration.d.ts +10 -0
- package/dist/collaboration/useMdKitCollaboration.js +90 -0
- package/dist/core/documentEngine.d.ts +38 -0
- package/dist/core/documentEngine.js +95 -0
- package/dist/core/documentEngine.test.d.ts +1 -0
- package/dist/core/documentEngine.test.js +119 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +1 -0
- package/dist/document/MdKitConflictPanel.d.ts +7 -0
- package/dist/document/MdKitConflictPanel.js +41 -0
- package/dist/document/MdKitDocumentToolbar.d.ts +13 -0
- package/dist/document/MdKitDocumentToolbar.js +48 -0
- package/dist/document/documentTypes.d.ts +57 -0
- package/dist/document/documentTypes.js +1 -0
- package/dist/document/useMdKitDocument.d.ts +33 -0
- package/dist/document/useMdKitDocument.js +396 -0
- package/dist/document/useMdKitDocument.test.d.ts +1 -0
- package/dist/document/useMdKitDocument.test.js +151 -0
- package/dist/fastify.d.ts +3 -0
- package/dist/fastify.js +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +11 -0
- package/dist/markdown/MarkdownBubbleMenu.d.ts +6 -0
- package/dist/markdown/MarkdownBubbleMenu.js +29 -0
- package/dist/markdown/MdKitEditor.d.ts +25 -0
- package/dist/markdown/MdKitEditor.js +7 -0
- package/dist/markdown/MdKitEditor.test.d.ts +1 -0
- package/dist/markdown/MdKitEditor.test.js +126 -0
- package/dist/markdown/TiptapMarkdownSurface.d.ts +23 -0
- package/dist/markdown/TiptapMarkdownSurface.js +430 -0
- package/dist/markdown/editorDebug.d.ts +5 -0
- package/dist/markdown/editorDebug.js +1 -0
- package/dist/markdown/markdownFenceRanges.d.ts +6 -0
- package/dist/markdown/markdownFenceRanges.js +41 -0
- package/dist/markdown/normalizeMarkdownSerialization.d.ts +1 -0
- package/dist/markdown/normalizeMarkdownSerialization.js +34 -0
- package/dist/markdown/normalizeMarkdownSerialization.test.d.ts +1 -0
- package/dist/markdown/normalizeMarkdownSerialization.test.js +16 -0
- package/dist/markdown/prepareMarkdownForEditorHydration.d.ts +1 -0
- package/dist/markdown/prepareMarkdownForEditorHydration.js +12 -0
- package/dist/markdown/prepareMarkdownForEditorHydration.test.d.ts +1 -0
- package/dist/markdown/prepareMarkdownForEditorHydration.test.js +13 -0
- package/dist/markdown/preserveMarkdownWhitespace.d.ts +1 -0
- package/dist/markdown/preserveMarkdownWhitespace.js +86 -0
- package/dist/markdown/preserveMarkdownWhitespace.test.d.ts +1 -0
- package/dist/markdown/preserveMarkdownWhitespace.test.js +25 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +13 -0
- package/dist/theme/MdKitThemeEditor.d.ts +8 -0
- package/dist/theme/MdKitThemeEditor.js +13 -0
- package/dist/theme/editorTheme.d.ts +20 -0
- package/dist/theme/editorTheme.js +47 -0
- package/dist/transport/fastify.d.ts +7 -0
- package/dist/transport/fastify.js +19 -0
- package/dist/transport/http.d.ts +43 -0
- package/dist/transport/http.js +80 -0
- package/dist/transport/index.d.ts +5 -0
- package/dist/transport/index.js +2 -0
- package/dist/transport/rest.d.ts +6 -0
- package/dist/transport/rest.js +34 -0
- package/dist/transport/store.d.ts +21 -0
- package/dist/transport/store.js +1 -0
- package/dist/transport/trpcClient.d.ts +81 -0
- package/dist/transport/trpcClient.js +21 -0
- package/dist/transport/trpcServer.d.ts +72 -0
- package/dist/transport/trpcServer.js +45 -0
- package/dist/trpc/client.d.ts +3 -0
- package/dist/trpc/client.js +1 -0
- package/dist/trpc/server.d.ts +3 -0
- package/dist/trpc/server.js +1 -0
- package/dist/trpc.d.ts +3 -0
- package/dist/trpc.js +1 -0
- package/dist/ui/joinClassNames.d.ts +1 -0
- package/dist/ui/joinClassNames.js +1 -0
- package/dist/versioning/VersionHistoryPanel.d.ts +9 -0
- package/dist/versioning/VersionHistoryPanel.js +29 -0
- package/dist/versioning/useMdKitDocumentVersions.d.ts +16 -0
- package/dist/versioning/useMdKitDocumentVersions.js +88 -0
- package/dist/versioning/useMdKitDocumentVersions.test.d.ts +1 -0
- package/dist/versioning/useMdKitDocumentVersions.test.js +41 -0
- package/docs/.vitepress/config.ts +34 -0
- package/docs/.vitepress/dist/404.html +22 -0
- package/docs/.vitepress/dist/api.html +120 -0
- package/docs/.vitepress/dist/architecture.html +25 -0
- package/docs/.vitepress/dist/assets/api.md.asncK3PQ.js +96 -0
- package/docs/.vitepress/dist/assets/api.md.asncK3PQ.lean.js +1 -0
- package/docs/.vitepress/dist/assets/app.BQvrHyG0.js +1 -0
- package/docs/.vitepress/dist/assets/architecture.md.BHQLarmZ.js +1 -0
- package/docs/.vitepress/dist/assets/architecture.md.BHQLarmZ.lean.js +1 -0
- package/docs/.vitepress/dist/assets/chunks/framework.RRduUuAx.js +19 -0
- package/docs/.vitepress/dist/assets/chunks/theme.CkCo6Nk1.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.CITl-897.js +137 -0
- package/docs/.vitepress/dist/assets/index.md.CITl-897.lean.js +1 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/shadcn.md.C3idOo2N.js +57 -0
- package/docs/.vitepress/dist/assets/shadcn.md.C3idOo2N.lean.js +1 -0
- package/docs/.vitepress/dist/assets/style.BtrGaL3i.css +1 -0
- package/docs/.vitepress/dist/assets/styling.md.B2C6kVFa.js +91 -0
- package/docs/.vitepress/dist/assets/styling.md.B2C6kVFa.lean.js +1 -0
- package/docs/.vitepress/dist/hashmap.json +1 -0
- package/docs/.vitepress/dist/index.html +161 -0
- package/docs/.vitepress/dist/shadcn.html +81 -0
- package/docs/.vitepress/dist/styling.html +115 -0
- package/docs/.vitepress/dist/vp-icons.css +1 -0
- package/docs/api.md +343 -0
- package/docs/architecture.md +67 -0
- package/docs/index.md +244 -0
- package/docs/shadcn.md +118 -0
- package/docs/styling.md +247 -0
- package/package.json +105 -0
- package/src/styles.css +676 -0
package/docs/index.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
Markdown Editor Kit is a React package for teams that need more than a bare
|
|
4
|
+
markdown editor widget. It starts with a controlled markdown editor that behaves
|
|
5
|
+
like a textarea, then adds autosave, version history, conflict handling, and
|
|
6
|
+
collaboration through adapters.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pnpm add @mp-lb/mdkit
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Import the stylesheet once if you want reset-resistant markdown defaults and
|
|
13
|
+
generic fallback styling for the base panels:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import "@mp-lb/mdkit/styles.css";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
See [Styling](./styling.md) for reset handling, dark mode, fonts, sizing, and
|
|
20
|
+
custom panel styles.
|
|
21
|
+
|
|
22
|
+
## Basic Editor
|
|
23
|
+
|
|
24
|
+
`MdKitEditor` is the textarea-like entry point. It has no persistence, no
|
|
25
|
+
version history, and no collaboration. You own the `value` and `onChange` state.
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { useState } from "react";
|
|
29
|
+
import { MdKitEditor } from "@mp-lb/mdkit";
|
|
30
|
+
import "@mp-lb/mdkit/styles.css";
|
|
31
|
+
|
|
32
|
+
export function MarkdownEditorExample() {
|
|
33
|
+
const [markdown, setMarkdown] = useState("# Hello markdown");
|
|
34
|
+
|
|
35
|
+
return <MdKitEditor value={markdown} onChange={setMarkdown} />;
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use this when you want a local editor, a form field, or a debug surface.
|
|
40
|
+
|
|
41
|
+
## Connected Editor
|
|
42
|
+
|
|
43
|
+
The connected workflow combines:
|
|
44
|
+
|
|
45
|
+
- `useMdKitDocument` for loading, autosave, dirty state, and conflict detection
|
|
46
|
+
- `useMdKitDocumentVersions` for version browsing and restore
|
|
47
|
+
- `useMdKitCollaboration` for Hocuspocus/Yjs collaboration
|
|
48
|
+
- `MdKitDocumentToolbar`, `VersionHistoryPanel`, and `MdKitConflictPanel` for
|
|
49
|
+
a complete base-panel UI
|
|
50
|
+
|
|
51
|
+
The TypeScript-first path is tRPC. REST is also supported for high-compatibility
|
|
52
|
+
backends and non-TypeScript stacks.
|
|
53
|
+
|
|
54
|
+
### Frontend With tRPC
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useMemo, useState } from "react";
|
|
58
|
+
import {
|
|
59
|
+
MdKitConflictPanel,
|
|
60
|
+
MdKitDocumentToolbar,
|
|
61
|
+
MdKitEditor,
|
|
62
|
+
VersionHistoryPanel,
|
|
63
|
+
useMdKitCollaboration,
|
|
64
|
+
useMdKitDocument,
|
|
65
|
+
useMdKitDocumentVersions,
|
|
66
|
+
type MdKitDocumentVersionDetail,
|
|
67
|
+
} from "@mp-lb/mdkit";
|
|
68
|
+
import {
|
|
69
|
+
createMdKitTrpcAdapter,
|
|
70
|
+
createMdKitTrpcClient,
|
|
71
|
+
} from "@mp-lb/mdkit/trpc/client";
|
|
72
|
+
|
|
73
|
+
const documentId = "docs/example.md";
|
|
74
|
+
|
|
75
|
+
export function ConnectedMarkdownEditor({
|
|
76
|
+
apiUrl,
|
|
77
|
+
}: {
|
|
78
|
+
apiUrl: string;
|
|
79
|
+
}) {
|
|
80
|
+
const [versionHistoryOpen, setVersionHistoryOpen] = useState(false);
|
|
81
|
+
const [conflictOpen, setConflictOpen] = useState(false);
|
|
82
|
+
|
|
83
|
+
const trpc = useMemo(
|
|
84
|
+
() => createMdKitTrpcClient({ url: `${apiUrl}/trpc` }),
|
|
85
|
+
[apiUrl],
|
|
86
|
+
);
|
|
87
|
+
const adapter = useMemo(() => createMdKitTrpcAdapter({ client: trpc }), [trpc]);
|
|
88
|
+
|
|
89
|
+
const document = useMdKitDocument({ adapter, documentId });
|
|
90
|
+
const versions = useMdKitDocumentVersions({ adapter, documentId });
|
|
91
|
+
const collaboration = useMdKitCollaboration({
|
|
92
|
+
collaborator: { id: "user-1", name: "Ada" },
|
|
93
|
+
documentId,
|
|
94
|
+
endpoint: `${apiUrl.replace(/^http/, "ws")}/collaboration`,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const restoreVersion = async (version: MdKitDocumentVersionDetail) => {
|
|
98
|
+
await trpc.restoreDocumentVersion.mutate({
|
|
99
|
+
documentId,
|
|
100
|
+
versionId: version.id,
|
|
101
|
+
});
|
|
102
|
+
await document.resync();
|
|
103
|
+
await versions.refresh();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<>
|
|
108
|
+
<MdKitDocumentToolbar
|
|
109
|
+
collaboration={collaboration}
|
|
110
|
+
document={document}
|
|
111
|
+
versions={versions}
|
|
112
|
+
onOpenConflict={() => setConflictOpen(true)}
|
|
113
|
+
onOpenVersionHistory={() => setVersionHistoryOpen(true)}
|
|
114
|
+
/>
|
|
115
|
+
|
|
116
|
+
<MdKitEditor
|
|
117
|
+
collaboration={collaboration}
|
|
118
|
+
fillHeight
|
|
119
|
+
readOnly={document.conflict}
|
|
120
|
+
value={document.value}
|
|
121
|
+
onChange={document.setContent}
|
|
122
|
+
onFocusChange={document.setFocused}
|
|
123
|
+
/>
|
|
124
|
+
|
|
125
|
+
{versionHistoryOpen ? (
|
|
126
|
+
<div role="dialog" aria-label="Version history">
|
|
127
|
+
<VersionHistoryPanel
|
|
128
|
+
controller={versions}
|
|
129
|
+
onRestoreVersion={restoreVersion}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
) : null}
|
|
133
|
+
|
|
134
|
+
{document.conflict && conflictOpen ? (
|
|
135
|
+
<div role="dialog" aria-label="Resolve conflict">
|
|
136
|
+
<MdKitConflictPanel document={document} />
|
|
137
|
+
</div>
|
|
138
|
+
) : null}
|
|
139
|
+
</>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The modal shells above are intentionally plain. Put `VersionHistoryPanel` and
|
|
145
|
+
`MdKitConflictPanel` inside your own dialog, drawer, side panel, or editor
|
|
146
|
+
replacement view. If your app uses shadcn/ui, see [Shadcn Plugin](./shadcn.md)
|
|
147
|
+
for the source-installed workflow component.
|
|
148
|
+
|
|
149
|
+
### Backend With Fastify And tRPC
|
|
150
|
+
|
|
151
|
+
The backend only needs a store object. Replace `createYourDocumentStore()` with
|
|
152
|
+
Postgres, MongoDB, Redis, files, or any other durable storage.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import cors from "@fastify/cors";
|
|
156
|
+
import websocket from "@fastify/websocket";
|
|
157
|
+
import { Database } from "@hocuspocus/extension-database";
|
|
158
|
+
import { Server } from "@hocuspocus/server";
|
|
159
|
+
import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify";
|
|
160
|
+
import Fastify from "fastify";
|
|
161
|
+
import { createMdKitTrpcRouter } from "@mp-lb/mdkit/trpc/server";
|
|
162
|
+
|
|
163
|
+
const app = Fastify();
|
|
164
|
+
const store = createYourDocumentStore();
|
|
165
|
+
|
|
166
|
+
const collaboration = Server.configure({
|
|
167
|
+
extensions: [
|
|
168
|
+
new Database({
|
|
169
|
+
fetch: ({ documentName }) => store.readCollaborationState(documentName),
|
|
170
|
+
store: ({ documentName, state }) =>
|
|
171
|
+
store.writeCollaborationState(documentName, state),
|
|
172
|
+
}),
|
|
173
|
+
],
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
await app.register(cors, { origin: true });
|
|
177
|
+
await app.register(websocket);
|
|
178
|
+
|
|
179
|
+
await app.register(fastifyTRPCPlugin, {
|
|
180
|
+
prefix: "/trpc",
|
|
181
|
+
trpcOptions: {
|
|
182
|
+
router: createMdKitTrpcRouter(store),
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
app.get("/collaboration", { websocket: true }, (socket, request) => {
|
|
187
|
+
collaboration.handleConnection(socket, request.raw, {});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await app.listen({ port: Number(process.env.PORT ?? 4312) });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The store object implements `MdKitTransportStore`: current document reads and
|
|
194
|
+
writes, version list/read/restore, and optional collaboration state storage. See
|
|
195
|
+
[API Reference](./api.md#transport-helpers).
|
|
196
|
+
|
|
197
|
+
### REST Compatibility
|
|
198
|
+
|
|
199
|
+
If you want REST instead of tRPC, use the REST frontend adapter:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { createMdKitRestAdapter } from "@mp-lb/mdkit";
|
|
203
|
+
|
|
204
|
+
const adapter = createMdKitRestAdapter({
|
|
205
|
+
baseUrl: "https://api.example.com/mdkit",
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
On Fastify, register the matching REST endpoints:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
import { registerMdKitFastify } from "@mp-lb/mdkit/fastify";
|
|
213
|
+
|
|
214
|
+
await registerMdKitFastify(app, {
|
|
215
|
+
prefix: "/mdkit",
|
|
216
|
+
store,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The mdkit testbench uses this split deliberately: `Connected (panels)` uses the
|
|
221
|
+
REST adapter, while `Connected (shadcn)` uses the tRPC adapter.
|
|
222
|
+
|
|
223
|
+
## Questions
|
|
224
|
+
|
|
225
|
+
### Do the backends need to know about each other?
|
|
226
|
+
|
|
227
|
+
Storage, version history, and collaboration can stay separate. Storage stores
|
|
228
|
+
the current markdown snapshot. Version history stores markdown snapshots.
|
|
229
|
+
Hocuspocus stores live Yjs collaboration state. They only need glue if your
|
|
230
|
+
product wants collaborative edits to automatically become saved markdown
|
|
231
|
+
versions.
|
|
232
|
+
|
|
233
|
+
### Does mdkit require tRPC or these exact REST endpoints?
|
|
234
|
+
|
|
235
|
+
No. The frontend hooks only need an `MdKitDocumentAdapter`. You can use REST,
|
|
236
|
+
tRPC, GraphQL, server actions, IndexedDB, Rails, Go, or anything else as long as
|
|
237
|
+
your adapter returns the documented shapes.
|
|
238
|
+
|
|
239
|
+
### Do I have to use the base panels?
|
|
240
|
+
|
|
241
|
+
No. The base panels are the fastest way to get a complete workflow working in
|
|
242
|
+
any React app. If you want a fully custom UI, use `useMdKitDocument`,
|
|
243
|
+
`useMdKitDocumentVersions`, and `useMdKitCollaboration` directly and render your
|
|
244
|
+
own controls.
|
package/docs/shadcn.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Shadcn Plugin
|
|
2
|
+
|
|
3
|
+
The core npm package is design-system agnostic. It exports the editor, hooks,
|
|
4
|
+
base panels, types, and generic CSS. It does not export shadcn components at
|
|
5
|
+
runtime.
|
|
6
|
+
|
|
7
|
+
The shadcn path should be a registry/plugin item. Installing it copies editable
|
|
8
|
+
source into your app, and that source imports your app's own shadcn primitives:
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
import { Badge } from "@/components/ui/badge";
|
|
13
|
+
import {
|
|
14
|
+
Dialog,
|
|
15
|
+
DialogContent,
|
|
16
|
+
DialogHeader,
|
|
17
|
+
DialogTitle,
|
|
18
|
+
} from "@/components/ui/dialog";
|
|
19
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
20
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
That means the workflow component can own polished app UI: toolbar, version
|
|
24
|
+
history dialog, conflict dialog, tabs, buttons, and layout. The underlying state
|
|
25
|
+
still comes from the mdkit hooks. The recommended transport for this path is the
|
|
26
|
+
tRPC adapter from `@mp-lb/mdkit/trpc/client`.
|
|
27
|
+
|
|
28
|
+
## Intended Shape
|
|
29
|
+
|
|
30
|
+
The installed component should be one app-local workflow component that you
|
|
31
|
+
render next to `MdKitEditor`.
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import {
|
|
35
|
+
MdKitEditor,
|
|
36
|
+
useMdKitCollaboration,
|
|
37
|
+
useMdKitDocument,
|
|
38
|
+
useMdKitDocumentVersions,
|
|
39
|
+
} from "@mp-lb/mdkit";
|
|
40
|
+
import {
|
|
41
|
+
createMdKitTrpcAdapter,
|
|
42
|
+
createMdKitTrpcClient,
|
|
43
|
+
} from "@mp-lb/mdkit/trpc/client";
|
|
44
|
+
import { MdKitConnectedWorkflow } from "@/components/mdkit/mdkit-connected-workflow";
|
|
45
|
+
|
|
46
|
+
export function EditorScreen() {
|
|
47
|
+
const client = createMdKitTrpcClient({ url: "/trpc" });
|
|
48
|
+
const adapter = createMdKitTrpcAdapter({ client });
|
|
49
|
+
const document = useMdKitDocument({ adapter, documentId });
|
|
50
|
+
const versions = useMdKitDocumentVersions({ adapter, documentId });
|
|
51
|
+
const collaboration = useMdKitCollaboration({
|
|
52
|
+
collaborator,
|
|
53
|
+
documentId,
|
|
54
|
+
endpoint: hocuspocusEndpoint,
|
|
55
|
+
});
|
|
56
|
+
const restoreVersion = async (version) => {
|
|
57
|
+
await client.restoreDocumentVersion.mutate({
|
|
58
|
+
documentId,
|
|
59
|
+
versionId: version.id,
|
|
60
|
+
});
|
|
61
|
+
await document.resync();
|
|
62
|
+
await versions.refresh();
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<MdKitConnectedWorkflow
|
|
67
|
+
collaboration={collaboration}
|
|
68
|
+
document={document}
|
|
69
|
+
versions={versions}
|
|
70
|
+
onRestoreVersion={restoreVersion}
|
|
71
|
+
>
|
|
72
|
+
<MdKitEditor
|
|
73
|
+
collaboration={collaboration}
|
|
74
|
+
readOnly={document.conflict}
|
|
75
|
+
value={document.value}
|
|
76
|
+
onChange={document.setContent}
|
|
77
|
+
onFocusChange={document.setFocused}
|
|
78
|
+
/>
|
|
79
|
+
</MdKitConnectedWorkflow>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The shadcn component is deliberately source-installed instead of imported from
|
|
85
|
+
the npm package. That is how shadcn is designed to work: the code lives in your
|
|
86
|
+
app, uses your aliases, and can be edited like any other local component.
|
|
87
|
+
|
|
88
|
+
## Why Not A Runtime Export?
|
|
89
|
+
|
|
90
|
+
mdkit should not:
|
|
91
|
+
|
|
92
|
+
- bundle copies of shadcn primitives
|
|
93
|
+
- depend on shadcn as a package
|
|
94
|
+
- ask you to pass a map of local `Button`, `Dialog`, or `Tabs` components into
|
|
95
|
+
mdkit
|
|
96
|
+
- ship Tailwind-only wrappers from the npm package and hope your build scans
|
|
97
|
+
external package source
|
|
98
|
+
|
|
99
|
+
The npm package stays focused on the durable logic. The registry item owns the
|
|
100
|
+
app-local shadcn composition.
|
|
101
|
+
|
|
102
|
+
## Fallback Path
|
|
103
|
+
|
|
104
|
+
If you do not use shadcn, use the base panels from the npm package:
|
|
105
|
+
|
|
106
|
+
- `MdKitDocumentToolbar`
|
|
107
|
+
- `VersionHistoryPanel`
|
|
108
|
+
- `MdKitConflictPanel`
|
|
109
|
+
|
|
110
|
+
They render plain semantic HTML with stable `mdkit-*` classes. Import
|
|
111
|
+
`@mp-lb/mdkit/styles.css` for generic fallback styling, or
|
|
112
|
+
replace the CSS entirely. See [Styling](./styling.md#component-styling).
|
|
113
|
+
|
|
114
|
+
## Status
|
|
115
|
+
|
|
116
|
+
The mdkit testbench currently contains the reference shadcn workflow component.
|
|
117
|
+
It is the target shape for the future registry/plugin item. Until that registry
|
|
118
|
+
item exists, treat the base panels as the supported published path.
|
package/docs/styling.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Styling
|
|
2
|
+
|
|
3
|
+
`MdKitEditor` can render without package CSS, but most applications should
|
|
4
|
+
import the stylesheet once. It gives the editor a reset-resistant markdown
|
|
5
|
+
baseline and exposes CSS variables for theme changes.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import "@mp-lb/mdkit/styles.css";
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Without this stylesheet, the editor still works, but headings, lists,
|
|
12
|
+
blockquotes, code blocks, spacing, and focus areas are left to your app's CSS.
|
|
13
|
+
|
|
14
|
+
## CSS Resets
|
|
15
|
+
|
|
16
|
+
Many app frameworks include CSS resets. Tailwind Preflight, for example, removes
|
|
17
|
+
default margins and list styling. The package stylesheet restores markdown
|
|
18
|
+
editor defaults under `.mdkit-markdown-editor`, including:
|
|
19
|
+
|
|
20
|
+
- heading sizes and spacing
|
|
21
|
+
- paragraph spacing
|
|
22
|
+
- unordered and ordered list markers
|
|
23
|
+
- compact list item spacing
|
|
24
|
+
- inline code and code block styling
|
|
25
|
+
- blockquote styling
|
|
26
|
+
- link color
|
|
27
|
+
- full-width editor layout
|
|
28
|
+
|
|
29
|
+
Import the stylesheet after global reset styles when possible:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import "./app.css";
|
|
33
|
+
import "@mp-lb/mdkit/styles.css";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
If your bundler or framework controls CSS order differently, make sure the
|
|
37
|
+
mdkit stylesheet is not overridden by a later broad reset such as
|
|
38
|
+
`ul { list-style: none; }`.
|
|
39
|
+
|
|
40
|
+
## Default Styling
|
|
41
|
+
|
|
42
|
+
The simplest setup is:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { MdKitEditor } from "@mp-lb/mdkit";
|
|
46
|
+
import "@mp-lb/mdkit/styles.css";
|
|
47
|
+
|
|
48
|
+
export function Editor({ markdown, setMarkdown }) {
|
|
49
|
+
return <MdKitEditor value={markdown} onChange={setMarkdown} />;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The editor fills the available width by default. Use `fillHeight` only when you
|
|
54
|
+
want the editor to fill its parent's height, own its scroll area, and make the
|
|
55
|
+
empty area below the last line clickable.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<MdKitEditor fillHeight value={markdown} onChange={setMarkdown} />
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Custom Styling
|
|
62
|
+
|
|
63
|
+
The package stylesheet is intentionally controlled through CSS variables. You
|
|
64
|
+
can override them with a class:
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
.my-markdown-editor {
|
|
68
|
+
--hsk-background: #ffffff;
|
|
69
|
+
--hsk-foreground: #172033;
|
|
70
|
+
--hsk-muted: #eef1f4;
|
|
71
|
+
--hsk-muted-foreground: #5b6472;
|
|
72
|
+
--hsk-border: #d8dee8;
|
|
73
|
+
--hsk-link: #4f46e5;
|
|
74
|
+
--hsk-font-family: Inter, system-ui, sans-serif;
|
|
75
|
+
--hsk-font-size: 16px;
|
|
76
|
+
--hsk-line-height: 1.7;
|
|
77
|
+
--hsk-surface-padding: 1rem;
|
|
78
|
+
--hsk-block-gap: 0.75rem;
|
|
79
|
+
--hsk-list-item-gap: 0.125rem;
|
|
80
|
+
--hsk-code-background: #eef1f4;
|
|
81
|
+
--hsk-code-radius: 0.35rem;
|
|
82
|
+
--hsk-code-block-radius: 0.75rem;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<MdKitEditor
|
|
88
|
+
className="my-markdown-editor"
|
|
89
|
+
value={markdown}
|
|
90
|
+
onChange={setMarkdown}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
You can also pass variables through `style` when the values are generated at
|
|
95
|
+
runtime:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<MdKitEditor
|
|
99
|
+
style={{
|
|
100
|
+
"--hsk-font-family": "ui-serif, Georgia, serif",
|
|
101
|
+
"--hsk-font-size": "18px",
|
|
102
|
+
"--hsk-line-height": "1.8",
|
|
103
|
+
}}
|
|
104
|
+
value={markdown}
|
|
105
|
+
onChange={setMarkdown}
|
|
106
|
+
/>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
See [`MdKitEditorProps`](./api.md#mdkiteditorprops) for the full component
|
|
110
|
+
props.
|
|
111
|
+
|
|
112
|
+
## Component Styling
|
|
113
|
+
|
|
114
|
+
Editor styling and workflow component styling are separate.
|
|
115
|
+
|
|
116
|
+
`MdKitEditor` is styled through CSS variables on `.mdkit-markdown-editor`.
|
|
117
|
+
Workflow panels such as `MdKitDocumentToolbar`, `MdKitConflictPanel`, and
|
|
118
|
+
`VersionHistoryPanel` are intentionally design-system agnostic. They render raw
|
|
119
|
+
semantic markup with stable `mdkit-*` class names. Without this stylesheet they
|
|
120
|
+
are plain HTML; with this stylesheet they get generic fallback styling: square
|
|
121
|
+
corners, one-pixel borders, clear spacing, and basic buttons.
|
|
122
|
+
|
|
123
|
+
Style them in your app when you want them to match your product:
|
|
124
|
+
|
|
125
|
+
```css
|
|
126
|
+
.mdkit-document-toolbar {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: 0.5rem;
|
|
130
|
+
border-bottom: 1px solid var(--border);
|
|
131
|
+
padding: 0.5rem 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.mdkit-document-toolbar-status,
|
|
135
|
+
.mdkit-document-toolbar-actions,
|
|
136
|
+
.mdkit-document-toolbar-conflict {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 0.5rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.mdkit-document-toolbar[data-conflict="true"] {
|
|
143
|
+
color: #991b1b;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The toolbar class hooks are:
|
|
148
|
+
|
|
149
|
+
- `.mdkit-document-toolbar`
|
|
150
|
+
- `.mdkit-document-toolbar-status`
|
|
151
|
+
- `.mdkit-document-toolbar-actions`
|
|
152
|
+
- `.mdkit-document-toolbar-error`
|
|
153
|
+
- `.mdkit-document-toolbar-conflict`
|
|
154
|
+
|
|
155
|
+
It also exposes `data-conflict`, `data-dirty`, and `data-save-status`
|
|
156
|
+
attributes for state-based styling.
|
|
157
|
+
|
|
158
|
+
Version history and conflict panels use the same fallback panel CSS and expose:
|
|
159
|
+
|
|
160
|
+
- `.mdkit-version-history-panel`
|
|
161
|
+
- `.mdkit-version-history-header`
|
|
162
|
+
- `.mdkit-version-history-list`
|
|
163
|
+
- `.mdkit-version-history-item`
|
|
164
|
+
- `.mdkit-version-history-preview`
|
|
165
|
+
- `.mdkit-conflict-panel`
|
|
166
|
+
- `.mdkit-conflict-panel-content`
|
|
167
|
+
- `.mdkit-conflict-panel-action-row`
|
|
168
|
+
- `.mdkit-panel-primary-action`
|
|
169
|
+
- `.mdkit-panel-secondary-action`
|
|
170
|
+
|
|
171
|
+
## Theme Helpers
|
|
172
|
+
|
|
173
|
+
For app code, CSS variables are usually the simplest integration. The package
|
|
174
|
+
also exports theme helpers when you want to store or generate a theme object:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import {
|
|
178
|
+
MdKitEditor,
|
|
179
|
+
createMdKitEditorThemeStyle,
|
|
180
|
+
darkMdKitEditorTheme,
|
|
181
|
+
} from "@mp-lb/mdkit";
|
|
182
|
+
|
|
183
|
+
const style = createMdKitEditorThemeStyle({
|
|
184
|
+
...darkMdKitEditorTheme,
|
|
185
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
186
|
+
lineHeight: "1.75",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
<MdKitEditor style={style} value={markdown} onChange={setMarkdown} />;
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Related exports are listed in the [API reference](./api.md#styling).
|
|
193
|
+
|
|
194
|
+
## Dark Mode
|
|
195
|
+
|
|
196
|
+
For class-based dark mode, scope variable overrides to your dark selector:
|
|
197
|
+
|
|
198
|
+
```css
|
|
199
|
+
.my-markdown-editor {
|
|
200
|
+
--hsk-background: #ffffff;
|
|
201
|
+
--hsk-foreground: #172033;
|
|
202
|
+
--hsk-muted: #eef1f4;
|
|
203
|
+
--hsk-muted-foreground: #5b6472;
|
|
204
|
+
--hsk-border: #d8dee8;
|
|
205
|
+
--hsk-link: #4f46e5;
|
|
206
|
+
--hsk-code-background: #eef1f4;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.dark .my-markdown-editor {
|
|
210
|
+
--hsk-background: #0b1220;
|
|
211
|
+
--hsk-foreground: #e5edf7;
|
|
212
|
+
--hsk-muted: #172033;
|
|
213
|
+
--hsk-muted-foreground: #94a3b8;
|
|
214
|
+
--hsk-border: #314158;
|
|
215
|
+
--hsk-link: #38bdf8;
|
|
216
|
+
--hsk-code-background: #111827;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Then apply the class normally:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
<MdKitEditor
|
|
224
|
+
className="my-markdown-editor"
|
|
225
|
+
value={markdown}
|
|
226
|
+
onChange={setMarkdown}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
You can also switch theme objects in React:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
const style = createMdKitEditorThemeStyle(
|
|
234
|
+
isDark ? darkMdKitEditorTheme : defaultMdKitEditorTheme,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
<MdKitEditor style={style} value={markdown} onChange={setMarkdown} />;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## What Not To Customize First
|
|
241
|
+
|
|
242
|
+
Prefer changing CSS variables before overriding internal selectors like
|
|
243
|
+
`.hsk-tiptap p` or `.hsk-editor-surface`. Direct selector overrides are still an
|
|
244
|
+
escape hatch, but they couple your app to mdkit's internal DOM.
|
|
245
|
+
|
|
246
|
+
Use `MdKitThemeEditor` for theme builders, documentation, and debug tooling. It
|
|
247
|
+
is not required for normal editor integration.
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mp-lb/mdkit",
|
|
3
|
+
"version": "0.0.1-main.2.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"docs",
|
|
10
|
+
"src/styles.css"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"source": "./src/index.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./core": {
|
|
19
|
+
"source": "./src/core/index.ts",
|
|
20
|
+
"import": "./dist/core/index.js",
|
|
21
|
+
"types": "./dist/core/index.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./fastify": {
|
|
24
|
+
"source": "./src/fastify.ts",
|
|
25
|
+
"import": "./dist/fastify.js",
|
|
26
|
+
"types": "./dist/fastify.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./trpc": {
|
|
29
|
+
"source": "./src/trpc.ts",
|
|
30
|
+
"import": "./dist/trpc.js",
|
|
31
|
+
"types": "./dist/trpc.d.ts"
|
|
32
|
+
},
|
|
33
|
+
"./styles.css": "./src/styles.css",
|
|
34
|
+
"./trpc/client": {
|
|
35
|
+
"source": "./src/trpc/client.ts",
|
|
36
|
+
"import": "./dist/trpc/client.js",
|
|
37
|
+
"types": "./dist/trpc/client.d.ts"
|
|
38
|
+
},
|
|
39
|
+
"./trpc/server": {
|
|
40
|
+
"source": "./src/trpc/server.ts",
|
|
41
|
+
"import": "./dist/trpc/server.js",
|
|
42
|
+
"types": "./dist/trpc/server.d.ts"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"registry": "https://registry.npmjs.org/"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@trpc/client": "^11.7.1",
|
|
51
|
+
"@trpc/server": "^11.7.1",
|
|
52
|
+
"fastify": "^5.1.0",
|
|
53
|
+
"react": "^19.2.0",
|
|
54
|
+
"react-dom": "^19.2.0"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"@trpc/client": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@trpc/server": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"fastify": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@hocuspocus/provider": "^2.15.0",
|
|
69
|
+
"@tiptap/extension-collaboration": "^3.14.0",
|
|
70
|
+
"@tiptap/extension-collaboration-caret": "^3.14.0",
|
|
71
|
+
"@tiptap/extension-link": "^3.14.0",
|
|
72
|
+
"@tiptap/extension-placeholder": "^3.14.0",
|
|
73
|
+
"@tiptap/markdown": "^3.20.1",
|
|
74
|
+
"@tiptap/react": "^3.14.0",
|
|
75
|
+
"@tiptap/starter-kit": "^3.14.0",
|
|
76
|
+
"lucide-react": "^0.554.0",
|
|
77
|
+
"yjs": "^13.6.24",
|
|
78
|
+
"zod": "^4.1.12"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
82
|
+
"@testing-library/react": "16.3.2",
|
|
83
|
+
"@trpc/client": "^11.7.1",
|
|
84
|
+
"@trpc/server": "^11.7.1",
|
|
85
|
+
"@types/react": "^19.2.5",
|
|
86
|
+
"@types/react-dom": "^19.2.3",
|
|
87
|
+
"dotenv": "^17.4.2",
|
|
88
|
+
"dotenv-expand": "^13.0.0",
|
|
89
|
+
"fastify": "^5.1.0",
|
|
90
|
+
"jsdom": "29.1.1",
|
|
91
|
+
"vite": "7.2.4",
|
|
92
|
+
"vitepress": "^1.6.4",
|
|
93
|
+
"vitest": "4.1.5"
|
|
94
|
+
},
|
|
95
|
+
"scripts": {
|
|
96
|
+
"dev": "tsc --watch --project tsconfig.json",
|
|
97
|
+
"build": "tsc --project tsconfig.json",
|
|
98
|
+
"docs:build": "vitepress build docs",
|
|
99
|
+
"docs:dev": "node scripts/load-docs-env.mjs dev",
|
|
100
|
+
"docs:preview": "node scripts/load-docs-env.mjs preview",
|
|
101
|
+
"release:check": "pnpm typecheck && pnpm test && pnpm build && pnpm docs:build",
|
|
102
|
+
"test": "vitest run",
|
|
103
|
+
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
104
|
+
}
|
|
105
|
+
}
|