feat(editor-ext): share wikilink and database-view node schemas
Add WikilinkNode and DatabaseView schema-only nodes to @docmost/editor-ext and register them in the Hocuspocus server tiptapExtensions list. Without the shared schema, jsonToNode on the server hit a RangeError for those node types and stripUnknownNodes dropped them on every collab save, so wikilinks disappeared on page reload and database-view embeds lost their config and rendered as empty placeholders.
This commit is contained in:
parent
dbd79cc17c
commit
b802f1d647
4 changed files with 193 additions and 0 deletions
|
|
@ -38,6 +38,8 @@ import {
|
|||
Columns,
|
||||
Column,
|
||||
Status,
|
||||
WikilinkNode,
|
||||
DatabaseView,
|
||||
addUniqueIdsToDoc,
|
||||
htmlToMarkdown,
|
||||
} from '@docmost/editor-ext';
|
||||
|
|
@ -101,6 +103,8 @@ export const tiptapExtensions = [
|
|||
Columns,
|
||||
Column,
|
||||
Status,
|
||||
WikilinkNode,
|
||||
DatabaseView,
|
||||
] as any;
|
||||
|
||||
export function jsonToHtml(tiptapJson: any) {
|
||||
|
|
|
|||
|
|
@ -30,4 +30,6 @@ export * from "./lib/columns";
|
|||
export * from "./lib/status";
|
||||
export * from "./lib/pdf";
|
||||
export * from "./lib/resizable-nodeview";
|
||||
export * from "./lib/wikilink";
|
||||
export * from "./lib/database-view";
|
||||
|
||||
|
|
|
|||
88
packages/editor-ext/src/lib/database-view.ts
Normal file
88
packages/editor-ext/src/lib/database-view.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { Node, mergeAttributes } from "@tiptap/core";
|
||||
|
||||
export interface DatabaseViewAttrs {
|
||||
tableId: string;
|
||||
viewId: string;
|
||||
viewType: string;
|
||||
bridgeUrl?: string | null;
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
databaseView: {
|
||||
insertDatabaseView: (attrs: DatabaseViewAttrs) => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared DatabaseView node (schema only, no NodeView).
|
||||
*
|
||||
* Registered on the Hocuspocus server so embedded Baserow views survive
|
||||
* collab saves. The client extends this node to attach the React renderer.
|
||||
*/
|
||||
export const DatabaseView = Node.create({
|
||||
name: "database-view",
|
||||
group: "block",
|
||||
atom: true,
|
||||
selectable: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
tableId: {
|
||||
default: "",
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-table-id") ?? "",
|
||||
renderHTML: (attrs) => ({ "data-table-id": attrs.tableId }),
|
||||
},
|
||||
viewId: {
|
||||
default: "",
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-view-id") ?? "",
|
||||
renderHTML: (attrs) => ({ "data-view-id": attrs.viewId }),
|
||||
},
|
||||
viewType: {
|
||||
default: "grid",
|
||||
parseHTML: (el: HTMLElement) =>
|
||||
el.getAttribute("data-view-type") ?? "grid",
|
||||
renderHTML: (attrs) => ({ "data-view-type": attrs.viewType }),
|
||||
},
|
||||
bridgeUrl: {
|
||||
default: null,
|
||||
parseHTML: (el: HTMLElement) =>
|
||||
el.getAttribute("data-bridge-url") ?? null,
|
||||
renderHTML: (attrs) =>
|
||||
attrs.bridgeUrl ? { "data-bridge-url": attrs.bridgeUrl } : {},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-node-type=database-view]" }];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return [
|
||||
"div",
|
||||
mergeAttributes(HTMLAttributes, { "data-node-type": "database-view" }),
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
insertDatabaseView:
|
||||
(attrs: DatabaseViewAttrs) =>
|
||||
({ commands }) =>
|
||||
commands.insertContent({
|
||||
type: this.name,
|
||||
attrs: {
|
||||
tableId: attrs.tableId,
|
||||
viewId: attrs.viewId,
|
||||
viewType: attrs.viewType,
|
||||
bridgeUrl: attrs.bridgeUrl ?? null,
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default DatabaseView;
|
||||
99
packages/editor-ext/src/lib/wikilink.ts
Normal file
99
packages/editor-ext/src/lib/wikilink.ts
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import { Node } from "@tiptap/core";
|
||||
|
||||
export interface WikilinkAttrs {
|
||||
pageId: string | null;
|
||||
slugId?: string | null;
|
||||
spaceSlug?: string | null;
|
||||
title: string;
|
||||
alias: string | null;
|
||||
}
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands<ReturnType> {
|
||||
wikilink: {
|
||||
insertWikilink: (attrs: WikilinkAttrs) => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared Wikilink node (schema only, no NodeView).
|
||||
*
|
||||
* Lives in @docmost/editor-ext so both the Hocuspocus server and the React
|
||||
* client share the exact same schema. The client extends this node to add
|
||||
* the React NodeView, input rule, and suggestion plugin.
|
||||
*
|
||||
* Without registering this node on the server, Hocuspocus' jsonToNode strips
|
||||
* wikilink nodes on save (unknown node type).
|
||||
*/
|
||||
export const WikilinkNode = Node.create({
|
||||
name: "wikilink",
|
||||
group: "inline",
|
||||
inline: true,
|
||||
atom: true,
|
||||
selectable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
pageId: {
|
||||
default: null,
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-page-id"),
|
||||
renderHTML: (attrs) =>
|
||||
attrs.pageId ? { "data-page-id": attrs.pageId } : {},
|
||||
},
|
||||
slugId: {
|
||||
default: null,
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-slug-id"),
|
||||
renderHTML: (attrs) =>
|
||||
attrs.slugId ? { "data-slug-id": attrs.slugId } : {},
|
||||
},
|
||||
spaceSlug: {
|
||||
default: null,
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-space-slug"),
|
||||
renderHTML: (attrs) =>
|
||||
attrs.spaceSlug ? { "data-space-slug": attrs.spaceSlug } : {},
|
||||
},
|
||||
title: {
|
||||
default: "",
|
||||
parseHTML: (el: HTMLElement) =>
|
||||
el.getAttribute("data-title") ?? el.textContent ?? "",
|
||||
renderHTML: (attrs) => ({ "data-title": attrs.title }),
|
||||
},
|
||||
alias: {
|
||||
default: null,
|
||||
parseHTML: (el: HTMLElement) => el.getAttribute("data-alias"),
|
||||
renderHTML: (attrs) =>
|
||||
attrs.alias ? { "data-alias": attrs.alias } : {},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "span[data-wikilink]" }];
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes, node }) {
|
||||
const display = node.attrs.alias ?? node.attrs.title ?? "?";
|
||||
const isBroken = !node.attrs.pageId;
|
||||
return [
|
||||
"span",
|
||||
{
|
||||
"data-wikilink": "true",
|
||||
...HTMLAttributes,
|
||||
class: isBroken ? "wikilink wikilink--broken" : "wikilink",
|
||||
},
|
||||
`[[${display}]]`,
|
||||
];
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
insertWikilink:
|
||||
(attrs: WikilinkAttrs) =>
|
||||
({ commands }) =>
|
||||
commands.insertContent({ type: this.name, attrs }),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default WikilinkNode;
|
||||
Loading…
Add table
Reference in a new issue