Skip to content

Commit

Permalink
add note to note linking
Browse files Browse the repository at this point in the history
- Adds the ability to link from one note to another, using the @ symbol.
- Pilots using a "feature" folder to colocate all feature code (plugin, element, mdast)
- Inlines several dependencies to support light customization or ease debugging
- ariakit combobox is intended to be temporary, refactored in or out in upcoming dropdown / tag input refactor
  • Loading branch information
cloverich committed Sep 1, 2024
1 parent cbb3641 commit 5e6ffc9
Show file tree
Hide file tree
Showing 20 changed files with 1,132 additions and 59 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"test": "mocha 'src/**/*.test.bundle.js'"
},
"dependencies": {
"@ariakit/react": "^0.4.8",
"ajv": "^8.6.2",
"ajv-formats": "^2.1.0",
"better-sqlite3": "^9.2.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import {
ELEMENT_UL,
ELEMENT_CODE_BLOCK,
ELEMENT_CODE_LINE,
ELEMENT_LINK,
} from "@udecode/plate"; // todo: sub-package which has only elements?

import { toSlateLink } from "../../../views/edit/editor/features/note-linking/toMdast";

export type Decoration = {
[key in (
| mdast.Emphasis
Expand Down Expand Up @@ -43,7 +46,6 @@ function convertNodes(nodes: mdast.Content[], deco: Decoration): slate.Node[] {
}, []);
}

// NOTE: Added
const DECORATION_MAPPING = {
emphasis: "italic",
strong: "bold",
Expand Down Expand Up @@ -365,9 +367,14 @@ function createBreak(node: mdast.Break) {
export type Link = ReturnType<typeof createLink>;

function createLink(node: mdast.Link, deco: Decoration) {
const { type, children, url, title } = node;
const { children, url, title } = node;

const res = toSlateLink({ url, children, deco, convertNodes });

if (res) return res;

return {
type: "a", // NOTE: Default plate link component uses "a"
type: ELEMENT_LINK,
children: convertNodes(children, deco),
url,
title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import { Node as SNode } from "slate";
// NOTE: https://github.com/inokawa/remark-slate-transformer/issues/31
import { unPrefixUrl } from "../../../hooks/images";

// NOTE: added, and a good example of what changes I would want to make to this library!
import {
ELEMENT_LI,
ELEMENT_LIC,
ELEMENT_OL,
ELEMENT_TODO_LI,
ELEMENT_UL,
ELEMENT_CODE_BLOCK,
} from "@udecode/plate"; // todo: sub-package which has only elements?
} from "@udecode/plate";

import {
createLinkFromNoteLinkFactory,
ELEMENT_NOTE_LINK,
} from "../../../views/edit/editor/features/note-linking";

// NOTE: Changed these, they were just mirroring mdasts' before
// which doesn't make sense
Expand Down Expand Up @@ -56,7 +60,7 @@ function createMdastRoot(node: slate.Node): unistLib.Node {
return root as any as unistLib.Node;
}

function convertNodes(nodes: slate.Node[]): unistLib.Node[] {
export function convertNodes(nodes: slate.Node[]): unistLib.Node[] {
const mdastNodes: unistLib.Node[] = [];
let textQueue: SlateNodes.Text[] = [];
for (let i = 0; i <= nodes.length; i++) {
Expand Down Expand Up @@ -238,6 +242,8 @@ function createMdastNode(
return createImage(node);
case "linkReference":
return createLinkReference(node);
case ELEMENT_NOTE_LINK:
return createLinkFromNoteLink(node);
case "imageReference":
return createImageReference(node);
case "footnote":
Expand Down Expand Up @@ -461,13 +467,15 @@ function createBreak(node: SlateNodes.Break): mdast.Break {
function createLink(node: SlateNodes.Link): mdast.Link {
const { type, url, title, children } = node;
return {
type: "link", // note: changes from type to type: "link" so it can accept "a", see the switch statement
url, // note: converted, "as any" added because mdast.Link thinks its url and not link?
type: "link",
url,
title,
children: convertNodes(children) as any as mdast.Link["children"],
} as any;
children: convertNodes(children) as any, //
};
}

const createLinkFromNoteLink = createLinkFromNoteLinkFactory(convertNodes);

function createImage(node: SlateNodes.Image | SlateNodes.Video): mdast.Image {
const { type, url, title, alt } = node;
return {
Expand Down
60 changes: 36 additions & 24 deletions src/views/edit/PlateContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import React, { useContext } from "react";
import { withProps } from "@udecode/cn";
import { observer } from "mobx-react-lite";
import { Node as SNode } from "slate";
import { Editor, Node as SNode } from "slate";
import {
Plate,
PlateContent,
Expand All @@ -11,6 +11,7 @@ import {
createHistoryPlugin,
isBlockAboveEmpty,
isSelectionAtBlockStart,
isSelectionAtBlockEnd,
PlateLeaf,
PlateElement,
} from "@udecode/plate-common";
Expand Down Expand Up @@ -73,7 +74,6 @@ import {
createLinkPlugin,
createSoftBreakPlugin,
createExitBreakPlugin,
createResetNodePlugin,
createListPlugin,
} from "@udecode/plate";

Expand All @@ -96,17 +96,23 @@ import {

import { autoformatRules } from "./editor/plugins/autoformat/autoformatRules";
import { createCodeBlockNormalizationPlugin } from "./editor/plugins/createCodeBlockNormalizationPlugin";
import { createResetNodePlugin } from "./editor/plugins/createResetNodePlugin";
import { createInlineEscapePlugin } from "./editor/plugins/createInlineEscapePlugin";
import {
NOTE_LINK,
ELEMENT_NOTE_LINK,
createNoteLinkDropdownPlugin,
createNoteLinkElementPlugin,
NoteLinkDropdownElement,
NoteLinkElement,
} from "./editor/features/note-linking";

import {
ELEMENT_VIDEO,
createVideoPlugin,
} from "./editor/plugins/createVideoPlugin";
import { createFilesPlugin } from "./editor/plugins/createFilesPlugin";

// Ideally this is injected; also createVideoPlugin and createFilesPlugin do this
import { IClient } from "../../preload/client/types";
const client: IClient = (window as any).chronicles.createClient();

import { EditorMode } from "./EditorMode";
import { EditableDocument } from "./EditableDocument";
import { JournalResponse } from "../../hooks/useClient";
Expand All @@ -121,8 +127,16 @@ export interface Props {
setSelectedEditorMode: (s: EditorMode) => any;
}

import { JournalsStoreContext } from "../../hooks/useJournalsLoader";
import useClient from "../../hooks/useClient";
import { SearchStore } from "../documents/SearchStore";

export default observer(
({ children, saving, value, setValue }: React.PropsWithChildren<Props>) => {
const jstore = useContext(JournalsStoreContext);
const client = useClient();
const store = new SearchStore(client, jstore!, () => {}, []);

const plugins = createPlugins(
[
createCodeBlockNormalizationPlugin(),
Expand Down Expand Up @@ -166,6 +180,8 @@ export default observer(
// dropped video files and this won't be called.
createVideoPlugin(),
createFilesPlugin(),
createNoteLinkDropdownPlugin({ options: { store } } as any),
createNoteLinkElementPlugin(),

// Backspacing into an element selects the block before deleting it.
createSelectOnBackspacePlugin({
Expand Down Expand Up @@ -256,6 +272,11 @@ export default observer(
},
}),

// When editing "inline" elements, allow space at the end to "escape" from the element.
// ex: link or note link editing.
// See plugin comments for links and details; this is
createInlineEscapePlugin(),

// Set text block indentation for differentiating structural
// elements or emphasizing certain content sections.
// https://platejs.org/docs/indent
Expand Down Expand Up @@ -286,7 +307,8 @@ export default observer(
// being confused about how to exit an e.g. code block to add more content.
createTrailingBlockPlugin({ type: ELEMENT_PARAGRAPH }),

// e.g. # -> h1, ``` -> code block, etc
// convert markdown to wysiwyg sa you type:
// # -> h1, ``` -> code block, etc
createAutoformatPlugin({
options: {
rules: autoformatRules,
Expand All @@ -302,7 +324,6 @@ export default observer(
[ELEMENT_CODE_LINE]: CodeLineElement,
[ELEMENT_CODE_SYNTAX]: CodeSyntaxLeaf,
[MARK_CODE]: CodeLeaf,
// [ELEMENT_HR]: HrElement,
[ELEMENT_H1]: withProps(HeadingElement, { variant: "h1" }),
[ELEMENT_H2]: withProps(HeadingElement, { variant: "h2" }),
[ELEMENT_H3]: withProps(HeadingElement, { variant: "h3" }),
Expand All @@ -311,31 +332,22 @@ export default observer(
[ELEMENT_H6]: withProps(HeadingElement, { variant: "h6" }),
[ELEMENT_IMAGE]: ImageElement,
[ELEMENT_LINK]: LinkElement,
// todo: need more plugins to make these truly usable.
// [ELEMENT_MEDIA_EMBED]: MediaEmbedElement,
// [ELEMENT_MENTION]: MentionElement,
// [ELEMENT_MENTION_INPUT]: MentionInputElement,

// NoteLinkDropdown provides the dropdown when typing `@`; NoteLinkElement
// is the actual element that gets inserted when you select a note.
[NOTE_LINK]: NoteLinkDropdownElement,
[ELEMENT_NOTE_LINK]: NoteLinkElement,

[ELEMENT_UL]: withProps(ListElement, { variant: "ul" }),
[ELEMENT_LI]: withProps(PlateElement, { as: "li" }),
[ELEMENT_OL]: withProps(ListElement, { variant: "ol" }),
[ELEMENT_PARAGRAPH]: ParagraphElement,
// [ELEMENT_TABLE]: TableElement,
// [ELEMENT_TD]: TableCellElement,
// [ELEMENT_TH]: TableCellHeaderElement,
// [ELEMENT_TODO_LI]: TodoListElement,
// [ELEMENT_TR]: TableRowElement,
// [ELEMENT_EXCALIDRAW]: ExcalidrawElement,
[MARK_BOLD]: withProps(PlateLeaf, { as: "strong" }),

// Unsure about these:
// [MARK_HIGHLIGHT]: HighlightLeaf,
[MARK_ITALIC]: withProps(PlateLeaf, { as: "em" }),
// [MARK_KBD]: KbdLeaf,
[MARK_STRIKETHROUGH]: withProps(PlateLeaf, { as: "s" }),
[MARK_SUBSCRIPT]: withProps(PlateLeaf, { as: "sub" }),
[MARK_SUPERSCRIPT]: withProps(PlateLeaf, { as: "sup" }),
[MARK_UNDERLINE]: withProps(PlateLeaf, { as: "u" }),
// [MARK_COMMENT]: CommentLeaf,
},
},
);
Expand Down
Loading

0 comments on commit 5e6ffc9

Please sign in to comment.