docstar-editor 0.1.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 ADDED
@@ -0,0 +1,108 @@
1
+ # docstar-editor
2
+
3
+ A minimal, block-based Markdown editor for React, built on [BlockNote](https://www.blocknotejs.org). Works standalone with zero setup, and opts into real-time collaboration + server-side sync when you connect it to a Hocuspocus-compatible server.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i docstar-editor
9
+ ```
10
+
11
+ Import the base styles once in your app:
12
+
13
+ ```ts
14
+ import "docstar-editor/styles.css";
15
+ ```
16
+
17
+ **Note for bundled apps**: `@blocknote/core` resolves a `prosemirror-view` version that's missing an export it needs. Add this to your own `package.json` to pin a working version for this package's dependency subtree:
18
+
19
+ ```json
20
+ "overrides": {
21
+ "docstar-editor": {
22
+ "prosemirror-view": "1.33.9"
23
+ }
24
+ }
25
+ ```
26
+
27
+ (npm workspaces/pnpm/yarn have their own equivalent — `overrides`/`resolutions` — under the same idea: scope the pin to `docstar-editor`'s own dependency tree, not your whole app.)
28
+
29
+ ## Standalone usage
30
+
31
+ No login, no server required:
32
+
33
+ ```tsx
34
+ import { DocstarEditor } from "docstar-editor";
35
+
36
+ export function Page() {
37
+ return (
38
+ <DocstarEditor
39
+ defaultMarkdown="# Hello\n\nStart writing..."
40
+ onChange={(markdown) => console.log(markdown)}
41
+ />
42
+ );
43
+ }
44
+ ```
45
+
46
+ ## Real-time collaboration
47
+
48
+ Connect the editor to your own Hocuspocus-compatible server (see [`doc-rtc`](../doc-rtc) for a reference implementation) for live multi-user sync with persistence:
49
+
50
+ ```tsx
51
+ import { DocstarEditor } from "docstar-editor";
52
+
53
+ export function Page() {
54
+ return (
55
+ <DocstarEditor
56
+ collab={{
57
+ wsUrl: "wss://your-server.example.com",
58
+ token: authToken,
59
+ workspaceId: "workspace-abc",
60
+ documentId: "doc-123",
61
+ user: { name: "Swayam", color: "#5b8def" },
62
+ }}
63
+ />
64
+ );
65
+ }
66
+ ```
67
+
68
+ When `collab` is provided, the editor connects over WebSocket, syncs edits live via Yjs, and shows collaborator cursors/presence automatically. Omit `workspaceId`/`token` to connect to a server that identifies documents directly by `documentId` instead (see `wsParams` in `CollabConfig`).
69
+
70
+ ## Local development / demo site
71
+
72
+ See [`website/`](./website) — a small standalone demo app (landing page + Playground/Import) that imports the editor directly from source, no build/pack step needed. It's local-only/single-user by design; it doesn't demonstrate the `collab` prop.
73
+
74
+ ```bash
75
+ cd website
76
+ npm install
77
+ npm run dev
78
+ ```
79
+
80
+ The site auto-deploys to GitHub Pages on every push to `main` that touches `website/` or `src/` (see `.github/workflows/deploy-website.yml`). One-time setup: in the repo's **Settings → Pages**, set **Source** to **GitHub Actions**.
81
+
82
+ ## Publishing to npm
83
+
84
+ ```bash
85
+ # 1. Build and sanity-check what will be published
86
+ npm run build
87
+ npm pack --dry-run
88
+
89
+ # 2. Log in (one-time per machine; opens a browser to authenticate)
90
+ npm login
91
+
92
+ # 3. Bump the version (pick one)
93
+ npm version patch # 0.1.0 -> 0.1.1, bug fixes
94
+ npm version minor # 0.1.0 -> 0.2.0, new features
95
+ npm version major # 0.1.0 -> 1.0.0, breaking changes
96
+
97
+ # 4. Publish
98
+ npm publish
99
+
100
+ # 5. Push the version bump commit + tag that `npm version` created
101
+ git push && git push --tags
102
+ ```
103
+
104
+ `npm version` refuses to run with uncommitted changes — commit your work first. `npm publish` is public and irreversible (a given version can never be re-published, only deprecated), so double-check `npm pack --dry-run`'s file list before running it.
105
+
106
+ ## License
107
+
108
+ MIT
@@ -0,0 +1,77 @@
1
+ import * as react from 'react';
2
+ import { Block } from '@blocknote/core';
3
+ import * as Y from 'yjs';
4
+ import { HocuspocusProvider } from '@hocuspocus/provider';
5
+
6
+ interface CollabUser {
7
+ name: string;
8
+ color: string;
9
+ }
10
+ interface CollabConfig {
11
+ /** WebSocket URL of the Hocuspocus-compatible collaboration server, e.g. "wss://editor.docstar.io" */
12
+ wsUrl: string;
13
+ /**
14
+ * Identifies the document. With `workspaceId` set, this is scoped under it
15
+ * (`${workspaceId}:${documentId}`) and hits `${wsUrl}/workspace/${workspaceId}`.
16
+ * Without `workspaceId`, this is used verbatim as the Hocuspocus documentName
17
+ * against `wsUrl` as-is — for connecting to an existing/legacy server that
18
+ * doesn't use the workspace-scoped routing (e.g. `documentName` already
19
+ * equals a raw page id).
20
+ */
21
+ documentId: string;
22
+ /** Auth token issued for the workspace/session, sent to the server's onAuthenticate hook. Only meaningful with `workspaceId`. */
23
+ token?: string;
24
+ /** The workspace this document belongs to. Omit to connect directly to `wsUrl` with `documentId` as the raw documentName. */
25
+ workspaceId?: string;
26
+ /** Extra query params appended to `wsUrl` as-is (e.g. `{ orgId, userId }` for a legacy server). Ignored when `workspaceId` is set. */
27
+ wsParams?: Record<string, string>;
28
+ /** Local user's presence info shown to collaborators. */
29
+ user: CollabUser;
30
+ }
31
+ interface DocstarEditorProps {
32
+ /** Initial content as markdown. Ignored once `collab` is set and a document already exists on the server. */
33
+ defaultMarkdown?: string;
34
+ /** Fires on every content change with the current markdown and raw blocks. */
35
+ onChange?: (markdown: string, blocks: Block[]) => void;
36
+ /** Enables real-time collaboration + server-side sync when provided. Omit for local, single-user mode. */
37
+ collab?: CollabConfig;
38
+ /** Optional extra class name for the editor container. */
39
+ className?: string;
40
+ /** Disables editing. */
41
+ editable?: boolean;
42
+ /** Visual theme. Defaults to "light". */
43
+ theme?: "light" | "dark";
44
+ /** Makes the editor's own background transparent, so it blends into whatever container it's placed in instead of showing its own distinct panel color. */
45
+ transparent?: boolean;
46
+ }
47
+ interface DocstarEditorHandle {
48
+ /** Serializes the current document to Markdown. */
49
+ getMarkdown: () => Promise<string>;
50
+ /** Replaces the current document with the parsed content of a Markdown string. */
51
+ setMarkdown: (markdown: string) => Promise<void>;
52
+ /** Moves focus into the editor. */
53
+ focus: () => void;
54
+ }
55
+
56
+ declare const DocstarEditor: react.ForwardRefExoticComponent<DocstarEditorProps & react.RefAttributes<DocstarEditorHandle>>;
57
+
58
+ interface CollabConnection {
59
+ doc: Y.Doc;
60
+ provider: HocuspocusProvider;
61
+ }
62
+ /**
63
+ * Opens a Yjs doc synced over a Hocuspocus-compatible WebSocket connection.
64
+ *
65
+ * With `workspaceId` set, `workspaceId`/`documentId` are composed into the
66
+ * Hocuspocus documentName so persistence stays scoped per workspace, hitting
67
+ * `${wsUrl}/workspace/${workspaceId}`, and `token` is forwarded for the
68
+ * server's onAuthenticate hook to verify.
69
+ *
70
+ * Without `workspaceId`, `documentId` is used verbatim as the documentName
71
+ * against `wsUrl` as-is (optionally with `wsParams` appended as query
72
+ * params) — for connecting to a server that doesn't use workspace-scoped
73
+ * routing.
74
+ */
75
+ declare function connectProvider(config: CollabConfig): CollabConnection;
76
+
77
+ export { type CollabConfig, type CollabConnection, type CollabUser, DocstarEditor, type DocstarEditorHandle, type DocstarEditorProps, connectProvider };
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ // src/DocstarEditor.tsx
2
+ import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
3
+ import { useCreateBlockNote } from "@blocknote/react";
4
+ import { BlockNoteView, lightDefaultTheme, darkDefaultTheme } from "@blocknote/mantine";
5
+ import "@blocknote/core/fonts/inter.css";
6
+ import "@blocknote/mantine/style.css";
7
+
8
+ // src/collab/connectProvider.ts
9
+ import * as Y from "yjs";
10
+ import { HocuspocusProvider } from "@hocuspocus/provider";
11
+ function connectProvider(config) {
12
+ const doc = new Y.Doc();
13
+ const wsUrl = toWebSocketUrl(config.wsUrl);
14
+ const url = config.workspaceId ? `${wsUrl.replace(/\/$/, "")}/workspace/${config.workspaceId}` : appendParams(wsUrl, config.wsParams);
15
+ const provider = new HocuspocusProvider({
16
+ url,
17
+ name: config.workspaceId ? `${config.workspaceId}:${config.documentId}` : config.documentId,
18
+ // HocuspocusProvider only sends its Auth message when `token` is truthy
19
+ // (see its `isAuthenticationRequired` getter), and the Hocuspocus server
20
+ // core unconditionally queues every other incoming message until that
21
+ // Auth message arrives — regardless of whether an onAuthenticate hook is
22
+ // even configured server-side. Without a token at all (the no-workspace
23
+ // path, which has no auth server-side either), the client never sends
24
+ // that message and the connection hangs forever with no event ever
25
+ // firing. A placeholder value keeps the handshake moving; the server
26
+ // only actually validates it when `workspaceId` requires auth.
27
+ token: config.token || "no-auth-required",
28
+ document: doc
29
+ });
30
+ provider.setAwarenessField("user", config.user);
31
+ return { doc, provider };
32
+ }
33
+ function appendParams(url, params) {
34
+ if (!params || Object.keys(params).length === 0) return url;
35
+ const separator = url.includes("?") ? "&" : "?";
36
+ return `${url}${separator}${new URLSearchParams(params).toString()}`;
37
+ }
38
+ function toWebSocketUrl(url) {
39
+ if (/^https:\/\//i.test(url)) return url.replace(/^https:\/\//i, "wss://");
40
+ if (/^http:\/\//i.test(url)) return url.replace(/^http:\/\//i, "ws://");
41
+ return url;
42
+ }
43
+
44
+ // src/DocstarEditor.tsx
45
+ import { jsx, jsxs } from "react/jsx-runtime";
46
+ var transparentLightTheme = {
47
+ ...lightDefaultTheme,
48
+ colors: { ...lightDefaultTheme.colors, editor: { ...lightDefaultTheme.colors.editor, background: "transparent" } }
49
+ };
50
+ var transparentDarkTheme = {
51
+ ...darkDefaultTheme,
52
+ colors: { ...darkDefaultTheme.colors, editor: { ...darkDefaultTheme.colors.editor, background: "transparent" } }
53
+ };
54
+ var DocstarEditor = forwardRef(
55
+ function DocstarEditor2({
56
+ defaultMarkdown,
57
+ onChange,
58
+ collab,
59
+ className,
60
+ editable = true,
61
+ theme = "light",
62
+ transparent = false
63
+ }, ref) {
64
+ const [connection, setConnection] = useState(null);
65
+ const [status, setStatus] = useState("connecting");
66
+ const [error, setError] = useState(null);
67
+ useEffect(() => {
68
+ if (!collab) return;
69
+ setStatus("connecting");
70
+ setError(null);
71
+ const conn = connectProvider(collab);
72
+ setConnection(conn);
73
+ const onSynced = () => setStatus("synced");
74
+ const onAuthenticationFailed = ({ reason }) => {
75
+ setStatus("error");
76
+ setError(`Authentication failed: ${reason}`);
77
+ };
78
+ const onClose = ({ event }) => {
79
+ setStatus((current) => {
80
+ if (current === "synced") return current;
81
+ setError(`Connection closed (${event.code}) ${event.reason}`.trim());
82
+ return "error";
83
+ });
84
+ };
85
+ conn.provider.on("synced", onSynced);
86
+ conn.provider.on("authenticationFailed", onAuthenticationFailed);
87
+ conn.provider.on("close", onClose);
88
+ const timeout = setTimeout(() => {
89
+ setStatus((current) => {
90
+ if (current !== "connecting") return current;
91
+ setError("Timed out waiting for the server to respond.");
92
+ return "error";
93
+ });
94
+ }, 15e3);
95
+ return () => {
96
+ clearTimeout(timeout);
97
+ conn.provider.off("synced", onSynced);
98
+ conn.provider.off("authenticationFailed", onAuthenticationFailed);
99
+ conn.provider.off("close", onClose);
100
+ conn.provider.destroy();
101
+ conn.doc.destroy();
102
+ };
103
+ }, [collab?.wsUrl, collab?.documentId, collab?.token]);
104
+ const editor = useCreateBlockNote(
105
+ collab ? {
106
+ collaboration: connection ? {
107
+ provider: connection.provider,
108
+ fragment: connection.doc.getXmlFragment("default"),
109
+ user: collab.user
110
+ } : void 0
111
+ } : { initialContent: void 0 },
112
+ [connection]
113
+ );
114
+ const initialMarkdownLoaded = useMemo(() => ({ current: false }), [editor]);
115
+ useEffect(() => {
116
+ if (collab) return;
117
+ if (initialMarkdownLoaded.current) return;
118
+ if (!defaultMarkdown) return;
119
+ initialMarkdownLoaded.current = true;
120
+ editor.tryParseMarkdownToBlocks(defaultMarkdown).then((blocks) => {
121
+ editor.replaceBlocks(editor.document, blocks);
122
+ });
123
+ }, [collab, defaultMarkdown, editor, initialMarkdownLoaded]);
124
+ useImperativeHandle(
125
+ ref,
126
+ () => ({
127
+ getMarkdown: () => editor.blocksToMarkdownLossy(editor.document),
128
+ setMarkdown: async (markdown) => {
129
+ const blocks = await editor.tryParseMarkdownToBlocks(markdown);
130
+ editor.replaceBlocks(editor.document, blocks);
131
+ },
132
+ focus: () => editor.focus()
133
+ }),
134
+ [editor]
135
+ );
136
+ if (collab && status === "connecting") {
137
+ return /* @__PURE__ */ jsx("div", { className, "data-docstar-status": "connecting", children: "Connecting\u2026" });
138
+ }
139
+ if (collab && status === "error") {
140
+ return /* @__PURE__ */ jsxs("div", { className, "data-docstar-status": "error", children: [
141
+ "Couldn't connect to the collaboration server",
142
+ error ? `: ${error}` : ".",
143
+ " ",
144
+ "Changes won't be saved until this reconnects."
145
+ ] });
146
+ }
147
+ const resolvedTheme = transparent ? theme === "dark" ? transparentDarkTheme : transparentLightTheme : theme;
148
+ return /* @__PURE__ */ jsx(
149
+ BlockNoteView,
150
+ {
151
+ editor,
152
+ editable,
153
+ className,
154
+ theme: resolvedTheme,
155
+ onChange: onChange ? () => {
156
+ editor.blocksToMarkdownLossy(editor.document).then((markdown) => {
157
+ onChange(markdown, editor.document);
158
+ });
159
+ } : void 0
160
+ }
161
+ );
162
+ }
163
+ );
164
+ export {
165
+ DocstarEditor,
166
+ connectProvider
167
+ };
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/DocstarEditor.tsx","../src/collab/connectProvider.ts"],"sourcesContent":["import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from \"react\";\nimport { useCreateBlockNote } from \"@blocknote/react\";\nimport { BlockNoteView, lightDefaultTheme, darkDefaultTheme } from \"@blocknote/mantine\";\nimport \"@blocknote/core/fonts/inter.css\";\nimport \"@blocknote/mantine/style.css\";\nimport { connectProvider, type CollabConnection } from \"./collab/connectProvider\";\nimport type { DocstarEditorHandle, DocstarEditorProps } from \"./types\";\n\nconst transparentLightTheme = {\n ...lightDefaultTheme,\n colors: { ...lightDefaultTheme.colors, editor: { ...lightDefaultTheme.colors.editor, background: \"transparent\" } },\n};\nconst transparentDarkTheme = {\n ...darkDefaultTheme,\n colors: { ...darkDefaultTheme.colors, editor: { ...darkDefaultTheme.colors.editor, background: \"transparent\" } },\n};\n\ntype CollabStatus = \"connecting\" | \"synced\" | \"error\";\n\nexport const DocstarEditor = forwardRef<DocstarEditorHandle, DocstarEditorProps>(\n function DocstarEditor(\n {\n defaultMarkdown,\n onChange,\n collab,\n className,\n editable = true,\n theme = \"light\",\n transparent = false,\n },\n ref\n ) {\n const [connection, setConnection] = useState<CollabConnection | null>(null);\n const [status, setStatus] = useState<CollabStatus>(\"connecting\");\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (!collab) return;\n setStatus(\"connecting\");\n setError(null);\n const conn = connectProvider(collab);\n setConnection(conn);\n\n const onSynced = () => setStatus(\"synced\");\n const onAuthenticationFailed = ({ reason }: { reason: string }) => {\n setStatus(\"error\");\n setError(`Authentication failed: ${reason}`);\n };\n const onClose = ({ event }: { event: { code: number; reason: string } }) => {\n setStatus((current) => {\n if (current === \"synced\") return current;\n setError(`Connection closed (${event.code}) ${event.reason}`.trim());\n return \"error\";\n });\n };\n\n conn.provider.on(\"synced\", onSynced);\n conn.provider.on(\"authenticationFailed\", onAuthenticationFailed);\n conn.provider.on(\"close\", onClose);\n\n // Safety net: if the underlying transport fails in a way that never\n // fires synced/authenticationFailed/close (e.g. an invalid WS URL\n // throwing inside the provider's connect logic), don't hang on\n // \"Connecting…\" forever.\n const timeout = setTimeout(() => {\n setStatus((current) => {\n if (current !== \"connecting\") return current;\n setError(\"Timed out waiting for the server to respond.\");\n return \"error\";\n });\n }, 15000);\n\n return () => {\n clearTimeout(timeout);\n conn.provider.off(\"synced\", onSynced);\n conn.provider.off(\"authenticationFailed\", onAuthenticationFailed);\n conn.provider.off(\"close\", onClose);\n conn.provider.destroy();\n conn.doc.destroy();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [collab?.wsUrl, collab?.documentId, collab?.token]);\n\n const editor = useCreateBlockNote(\n collab\n ? {\n collaboration: connection\n ? {\n provider: connection.provider,\n fragment: connection.doc.getXmlFragment(\"default\"),\n user: collab.user,\n }\n : undefined,\n }\n : { initialContent: undefined },\n [connection]\n );\n\n const initialMarkdownLoaded = useMemo(() => ({ current: false }), [editor]);\n\n useEffect(() => {\n if (collab) return; // collab documents load their content from the server\n if (initialMarkdownLoaded.current) return;\n if (!defaultMarkdown) return;\n initialMarkdownLoaded.current = true;\n editor.tryParseMarkdownToBlocks(defaultMarkdown).then((blocks) => {\n editor.replaceBlocks(editor.document, blocks);\n });\n }, [collab, defaultMarkdown, editor, initialMarkdownLoaded]);\n\n useImperativeHandle(\n ref,\n () => ({\n getMarkdown: () => editor.blocksToMarkdownLossy(editor.document),\n setMarkdown: async (markdown: string) => {\n const blocks = await editor.tryParseMarkdownToBlocks(markdown);\n editor.replaceBlocks(editor.document, blocks);\n },\n focus: () => editor.focus(),\n }),\n [editor]\n );\n\n if (collab && status === \"connecting\") {\n return (\n <div className={className} data-docstar-status=\"connecting\">\n Connecting…\n </div>\n );\n }\n\n if (collab && status === \"error\") {\n return (\n <div className={className} data-docstar-status=\"error\">\n Couldn't connect to the collaboration server{error ? `: ${error}` : \".\"}\n {\" \"}Changes won't be saved until this reconnects.\n </div>\n );\n }\n\n const resolvedTheme = transparent\n ? theme === \"dark\"\n ? transparentDarkTheme\n : transparentLightTheme\n : theme;\n\n return (\n <BlockNoteView\n editor={editor}\n editable={editable}\n className={className}\n theme={resolvedTheme}\n onChange={\n onChange\n ? () => {\n editor.blocksToMarkdownLossy(editor.document).then((markdown) => {\n onChange(markdown, editor.document);\n });\n }\n : undefined\n }\n />\n );\n }\n);\n","import * as Y from \"yjs\";\nimport { HocuspocusProvider } from \"@hocuspocus/provider\";\nimport type { CollabConfig } from \"../types\";\n\nexport interface CollabConnection {\n doc: Y.Doc;\n provider: HocuspocusProvider;\n}\n\n/**\n * Opens a Yjs doc synced over a Hocuspocus-compatible WebSocket connection.\n *\n * With `workspaceId` set, `workspaceId`/`documentId` are composed into the\n * Hocuspocus documentName so persistence stays scoped per workspace, hitting\n * `${wsUrl}/workspace/${workspaceId}`, and `token` is forwarded for the\n * server's onAuthenticate hook to verify.\n *\n * Without `workspaceId`, `documentId` is used verbatim as the documentName\n * against `wsUrl` as-is (optionally with `wsParams` appended as query\n * params) — for connecting to a server that doesn't use workspace-scoped\n * routing.\n */\nexport function connectProvider(config: CollabConfig): CollabConnection {\n const doc = new Y.Doc();\n\n const wsUrl = toWebSocketUrl(config.wsUrl);\n const url = config.workspaceId\n ? `${wsUrl.replace(/\\/$/, \"\")}/workspace/${config.workspaceId}`\n : appendParams(wsUrl, config.wsParams);\n\n const provider = new HocuspocusProvider({\n url,\n name: config.workspaceId ? `${config.workspaceId}:${config.documentId}` : config.documentId,\n // HocuspocusProvider only sends its Auth message when `token` is truthy\n // (see its `isAuthenticationRequired` getter), and the Hocuspocus server\n // core unconditionally queues every other incoming message until that\n // Auth message arrives — regardless of whether an onAuthenticate hook is\n // even configured server-side. Without a token at all (the no-workspace\n // path, which has no auth server-side either), the client never sends\n // that message and the connection hangs forever with no event ever\n // firing. A placeholder value keeps the handshake moving; the server\n // only actually validates it when `workspaceId` requires auth.\n token: config.token || \"no-auth-required\",\n document: doc,\n });\n\n provider.setAwarenessField(\"user\", config.user);\n\n return { doc, provider };\n}\n\nfunction appendParams(url: string, params?: Record<string, string>): string {\n if (!params || Object.keys(params).length === 0) return url;\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}${new URLSearchParams(params).toString()}`;\n}\n\n/**\n * `new WebSocket()` requires a `ws:`/`wss:` scheme and throws a SyntaxError\n * for anything else (e.g. `http:`/`https:`), which HocuspocusProvider doesn't\n * guard against — that throw happens silently inside its connect logic, so\n * the UI is left stuck with no `close`/`synced`/`authenticationFailed` event\n * ever firing. Normalize here so a plain http(s) base URL (as apps commonly\n * configure for their API host) just works.\n */\nfunction toWebSocketUrl(url: string): string {\n if (/^https:\\/\\//i.test(url)) return url.replace(/^https:\\/\\//i, \"wss://\");\n if (/^http:\\/\\//i.test(url)) return url.replace(/^http:\\/\\//i, \"ws://\");\n return url;\n}\n"],"mappings":"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","names":["DocstarEditor"]}
@@ -0,0 +1,5 @@
1
+ .docstar-editor {
2
+ border: 1px solid #e2e2e2;
3
+ border-radius: 8px;
4
+ padding: 8px;
5
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "docstar-editor",
3
+ "version": "0.1.1",
4
+ "description": "A minimal block/markdown editor for React with optional real-time collaboration.",
5
+ "license": "MIT",
6
+ "author": "",
7
+ "keywords": [
8
+ "editor",
9
+ "markdown",
10
+ "blocknote",
11
+ "react",
12
+ "collaboration",
13
+ "yjs"
14
+ ],
15
+ "homepage": "https://swayammaheshwari.github.io/docstar-editor/",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/swayammaheshwari/docstar-editor.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/swayammaheshwari/docstar-editor/issues"
22
+ },
23
+ "type": "module",
24
+ "main": "dist/index.js",
25
+ "module": "dist/index.js",
26
+ "types": "dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./styles.css": "./dist/styles.css"
34
+ },
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch"
41
+ },
42
+ "peerDependencies": {
43
+ "react": ">=18",
44
+ "react-dom": ">=18"
45
+ },
46
+ "dependencies": {
47
+ "@blocknote/core": "^0.22.0",
48
+ "@blocknote/mantine": "^0.22.0",
49
+ "@blocknote/react": "^0.22.0",
50
+ "@hocuspocus/provider": "^2.15.0",
51
+ "yjs": "^13.6.18"
52
+ },
53
+ "devDependencies": {
54
+ "@types/react": "^18.3.0",
55
+ "@types/react-dom": "^18.3.0",
56
+ "react": "^18.3.0",
57
+ "react-dom": "^18.3.0",
58
+ "tsup": "^8.3.0",
59
+ "typescript": "^5.6.0"
60
+ },
61
+ "overrides": {
62
+ "prosemirror-view": "1.33.9"
63
+ }
64
+ }