@connextar/house 0.1.1 → 0.3.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.
Files changed (148) hide show
  1. package/README.md +286 -28
  2. package/dist/changelog/changelog.d.ts +60 -0
  3. package/dist/changelog/changelog.d.ts.map +1 -0
  4. package/dist/changelog/changelog.js +61 -0
  5. package/dist/changelog/changelog.js.map +1 -0
  6. package/dist/changelog/index.d.ts +2 -0
  7. package/dist/changelog/index.d.ts.map +1 -0
  8. package/dist/changelog/index.js +2 -0
  9. package/dist/changelog/index.js.map +1 -0
  10. package/dist/editor/block-handle.d.ts +15 -0
  11. package/dist/editor/block-handle.d.ts.map +1 -0
  12. package/dist/editor/block-handle.js +106 -0
  13. package/dist/editor/block-handle.js.map +1 -0
  14. package/dist/editor/blocks.d.ts +35 -0
  15. package/dist/editor/blocks.d.ts.map +1 -0
  16. package/dist/editor/blocks.js +114 -0
  17. package/dist/editor/blocks.js.map +1 -0
  18. package/dist/editor/bubble-toolbar.d.ts +11 -0
  19. package/dist/editor/bubble-toolbar.d.ts.map +1 -0
  20. package/dist/editor/bubble-toolbar.js +87 -0
  21. package/dist/editor/bubble-toolbar.js.map +1 -0
  22. package/dist/editor/content-index.d.ts +2 -0
  23. package/dist/editor/content-index.d.ts.map +1 -0
  24. package/dist/editor/content-index.js +2 -0
  25. package/dist/editor/content-index.js.map +1 -0
  26. package/dist/editor/content.d.ts +30 -0
  27. package/dist/editor/content.d.ts.map +1 -0
  28. package/dist/editor/content.js +256 -0
  29. package/dist/editor/content.js.map +1 -0
  30. package/dist/editor/editor.d.ts +52 -0
  31. package/dist/editor/editor.d.ts.map +1 -0
  32. package/dist/editor/editor.js +160 -0
  33. package/dist/editor/editor.js.map +1 -0
  34. package/dist/editor/emoji-index.d.ts +2 -0
  35. package/dist/editor/emoji-index.d.ts.map +1 -0
  36. package/dist/editor/emoji-index.js +2 -0
  37. package/dist/editor/emoji-index.js.map +1 -0
  38. package/dist/editor/emoji-picker.d.ts +16 -0
  39. package/dist/editor/emoji-picker.d.ts.map +1 -0
  40. package/dist/editor/emoji-picker.js +37 -0
  41. package/dist/editor/emoji-picker.js.map +1 -0
  42. package/dist/editor/emoji.d.ts +18 -0
  43. package/dist/editor/emoji.d.ts.map +1 -0
  44. package/dist/editor/emoji.js +418 -0
  45. package/dist/editor/emoji.js.map +1 -0
  46. package/dist/editor/index.d.ts +5 -0
  47. package/dist/editor/index.d.ts.map +1 -0
  48. package/dist/editor/index.js +6 -0
  49. package/dist/editor/index.js.map +1 -0
  50. package/dist/editor/suggestion-popup.d.ts +17 -0
  51. package/dist/editor/suggestion-popup.d.ts.map +1 -0
  52. package/dist/editor/suggestion-popup.js +57 -0
  53. package/dist/editor/suggestion-popup.js.map +1 -0
  54. package/dist/editor/suggestions.d.ts +93 -0
  55. package/dist/editor/suggestions.d.ts.map +1 -0
  56. package/dist/editor/suggestions.js +179 -0
  57. package/dist/editor/suggestions.js.map +1 -0
  58. package/dist/editor/ui.d.ts +54 -0
  59. package/dist/editor/ui.d.ts.map +1 -0
  60. package/dist/editor/ui.js +58 -0
  61. package/dist/editor/ui.js.map +1 -0
  62. package/dist/house.css +333 -0
  63. package/dist/internal/cx.d.ts +3 -0
  64. package/dist/internal/cx.d.ts.map +1 -0
  65. package/dist/internal/cx.js +5 -0
  66. package/dist/internal/cx.js.map +1 -0
  67. package/dist/internal/next-link.d.ts +23 -0
  68. package/dist/internal/next-link.d.ts.map +1 -0
  69. package/dist/internal/next-link.js +3 -0
  70. package/dist/internal/next-link.js.map +1 -0
  71. package/dist/markdown/guide.d.ts +98 -0
  72. package/dist/markdown/guide.d.ts.map +1 -0
  73. package/dist/markdown/guide.js +328 -0
  74. package/dist/markdown/guide.js.map +1 -0
  75. package/dist/markdown/index.d.ts +4 -0
  76. package/dist/markdown/index.d.ts.map +1 -0
  77. package/dist/markdown/index.js +4 -0
  78. package/dist/markdown/index.js.map +1 -0
  79. package/dist/markdown/table-view.d.ts +28 -0
  80. package/dist/markdown/table-view.d.ts.map +1 -0
  81. package/dist/markdown/table-view.js +25 -0
  82. package/dist/markdown/table-view.js.map +1 -0
  83. package/dist/markdown/table.d.ts +35 -0
  84. package/dist/markdown/table.d.ts.map +1 -0
  85. package/dist/markdown/table.js +79 -0
  86. package/dist/markdown/table.js.map +1 -0
  87. package/dist/release/changelog.d.ts +72 -0
  88. package/dist/release/changelog.d.ts.map +1 -0
  89. package/dist/release/changelog.js +92 -0
  90. package/dist/release/changelog.js.map +1 -0
  91. package/dist/release/index.d.ts +3 -0
  92. package/dist/release/index.d.ts.map +1 -0
  93. package/dist/release/index.js +3 -0
  94. package/dist/release/index.js.map +1 -0
  95. package/dist/release/version.d.ts +33 -0
  96. package/dist/release/version.d.ts.map +1 -0
  97. package/dist/release/version.js +57 -0
  98. package/dist/release/version.js.map +1 -0
  99. package/dist/trail/index.d.ts +3 -0
  100. package/dist/trail/index.d.ts.map +1 -0
  101. package/dist/trail/index.js +3 -0
  102. package/dist/trail/index.js.map +1 -0
  103. package/dist/trail/rules.d.ts +44 -0
  104. package/dist/trail/rules.d.ts.map +1 -0
  105. package/dist/trail/rules.js +74 -0
  106. package/dist/trail/rules.js.map +1 -0
  107. package/dist/trail/trail.d.ts +49 -0
  108. package/dist/trail/trail.d.ts.map +1 -0
  109. package/dist/trail/trail.js +188 -0
  110. package/dist/trail/trail.js.map +1 -0
  111. package/dist/ux/errors.d.ts +26 -0
  112. package/dist/ux/errors.d.ts.map +1 -0
  113. package/dist/ux/errors.js +33 -0
  114. package/dist/ux/errors.js.map +1 -0
  115. package/dist/ux/feedback.d.ts +23 -0
  116. package/dist/ux/feedback.d.ts.map +1 -0
  117. package/dist/ux/feedback.js +22 -0
  118. package/dist/ux/feedback.js.map +1 -0
  119. package/dist/ux/index.d.ts +20 -0
  120. package/dist/ux/index.d.ts.map +1 -0
  121. package/dist/ux/index.js +20 -0
  122. package/dist/ux/index.js.map +1 -0
  123. package/dist/ux/navigation-index.d.ts +10 -0
  124. package/dist/ux/navigation-index.d.ts.map +1 -0
  125. package/dist/ux/navigation-index.js +10 -0
  126. package/dist/ux/navigation-index.js.map +1 -0
  127. package/dist/ux/navigation-progress.d.ts +53 -0
  128. package/dist/ux/navigation-progress.d.ts.map +1 -0
  129. package/dist/ux/navigation-progress.js +245 -0
  130. package/dist/ux/navigation-progress.js.map +1 -0
  131. package/dist/ux/optimistic.d.ts +139 -0
  132. package/dist/ux/optimistic.d.ts.map +1 -0
  133. package/dist/ux/optimistic.js +242 -0
  134. package/dist/ux/optimistic.js.map +1 -0
  135. package/dist/ux/use-action.d.ts +60 -0
  136. package/dist/ux/use-action.d.ts.map +1 -0
  137. package/dist/ux/use-action.js +100 -0
  138. package/dist/ux/use-action.js.map +1 -0
  139. package/dist/ux/use-optimistic-list.d.ts +69 -0
  140. package/dist/ux/use-optimistic-list.d.ts.map +1 -0
  141. package/dist/ux/use-optimistic-list.js +115 -0
  142. package/dist/ux/use-optimistic-list.js.map +1 -0
  143. package/dist/ux/use-optimistic-value.d.ts +40 -0
  144. package/dist/ux/use-optimistic-value.d.ts.map +1 -0
  145. package/dist/ux/use-optimistic-value.js +68 -0
  146. package/dist/ux/use-optimistic-value.js.map +1 -0
  147. package/docs/ReleaseProcess.md +315 -0
  148. package/package.json +83 -11
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { DragHandle } from "@tiptap/extension-drag-handle-react";
4
+ import { ArrowDown, ArrowUp, Check, Copy, GripVertical, Plus, Trash2 } from "lucide-react";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import { BLOCK_TYPES } from "./blocks.js";
7
+ import { ghostButtonClass, Menu, MenuContent, MenuItem, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, } from "./ui.js";
8
+ /**
9
+ * Stable on purpose: the DragHandle wrapper re-registers its plugin when this
10
+ * changes identity, and a plugin re-registration recreates every plugin view in
11
+ * the editor — see the note in bubble-toolbar.tsx.
12
+ */
13
+ const HANDLE_POSITION = { placement: "left-start" };
14
+ const handleButtonClass = `${ghostButtonClass} size-6 p-0 text-muted-foreground hover:text-foreground`;
15
+ /**
16
+ * The gutter beside whichever block the pointer is over: a `+` that starts a new
17
+ * block underneath (with the `/` menu already open, as Notion does) and a grip
18
+ * that drags the block to reorder it or, clicked, opens the block menu — turn
19
+ * into, duplicate, move up, move down, delete.
20
+ *
21
+ * Everything works on the block's document position rather than the caret, so a
22
+ * block can be acted on without ever being clicked into. The drag itself is the
23
+ * extension's: it understands nested lists and shows the drop cursor.
24
+ */
25
+ export function BlockHandle({ editor }) {
26
+ const [hovered, setHovered] = useState(null);
27
+ const [menuOpen, setMenuOpen] = useState(false);
28
+ const menuOpenRef = useRef(false);
29
+ useEffect(() => {
30
+ menuOpenRef.current = menuOpen;
31
+ }, [menuOpen]);
32
+ // Leave the last block in place while its menu is open, so the menu still
33
+ // knows what it belongs to after the pointer wanders. Stable identity for the
34
+ // same reason as HANDLE_POSITION.
35
+ const onNodeChange = useCallback(({ node, pos }) => {
36
+ if (menuOpenRef.current)
37
+ return;
38
+ setHovered(node ? { node, pos } : null);
39
+ }, []);
40
+ const range = (block) => ({ from: block.pos, to: block.pos + block.node.nodeSize });
41
+ /** Turn the whole block into another type, without moving the caret by hand. */
42
+ const turnInto = (block, typeId) => {
43
+ const type = BLOCK_TYPES.find((t) => t.id === typeId);
44
+ if (!type)
45
+ return;
46
+ const inside = { from: block.pos + 1, to: Math.max(block.pos + 1, block.pos + block.node.nodeSize - 1) };
47
+ type.apply(editor.chain().focus().setTextSelection(inside)).run();
48
+ };
49
+ const duplicate = (block) => {
50
+ // The node is immutable, so it can be inserted twice.
51
+ editor
52
+ .chain()
53
+ .focus()
54
+ .insertContentAt(block.pos + block.node.nodeSize, block.node)
55
+ .run();
56
+ };
57
+ const remove = (block) => {
58
+ editor.chain().focus().deleteRange(range(block)).run();
59
+ };
60
+ /** Swap the block with its previous (-1) or next (+1) sibling. */
61
+ const move = (block, direction) => {
62
+ const $pos = editor.state.doc.resolve(block.pos);
63
+ const parent = $pos.parent;
64
+ const siblingIndex = $pos.index() + direction;
65
+ if (siblingIndex < 0 || siblingIndex >= parent.childCount)
66
+ return;
67
+ const sibling = parent.child(siblingIndex);
68
+ const { from, to } = range(block);
69
+ editor
70
+ .chain()
71
+ .focus()
72
+ .command(({ tr }) => {
73
+ tr.delete(from, to);
74
+ const insertAt = direction === -1 ? from - sibling.nodeSize : from + sibling.nodeSize;
75
+ tr.insert(insertAt, block.node);
76
+ return true;
77
+ })
78
+ .run();
79
+ };
80
+ /**
81
+ * The `+`: a fresh paragraph under this block with the `/` menu open — or, if
82
+ * this block is already an empty paragraph, just the menu, right here.
83
+ */
84
+ const insertBelow = (block) => {
85
+ const isEmptyParagraph = block.node.type.name === "paragraph" && block.node.content.size === 0;
86
+ if (isEmptyParagraph) {
87
+ editor
88
+ .chain()
89
+ .focus()
90
+ .setTextSelection(block.pos + 1)
91
+ .insertContent("/")
92
+ .run();
93
+ return;
94
+ }
95
+ const after = block.pos + block.node.nodeSize;
96
+ editor
97
+ .chain()
98
+ .focus()
99
+ .insertContentAt(after, { type: "paragraph" })
100
+ .setTextSelection(after + 1)
101
+ .insertContent("/")
102
+ .run();
103
+ };
104
+ return (_jsxs(DragHandle, { editor: editor, nested: true, className: "house-block-handle", computePositionConfig: HANDLE_POSITION, onNodeChange: onNodeChange, children: [_jsx("button", { type: "button", className: handleButtonClass, "aria-label": "Add a block below", title: "Add a block below", onClick: () => hovered && insertBelow(hovered), children: _jsx(Plus, { className: "size-4", "aria-hidden": true }) }), _jsxs(Menu, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsx(MenuTrigger, { asChild: true, children: _jsx("button", { type: "button", className: `${handleButtonClass} cursor-grab active:cursor-grabbing`, "aria-label": "Block options \u2014 drag to move", title: "Drag to move, click for options", children: _jsx(GripVertical, { className: "size-4", "aria-hidden": true }) }) }), _jsxs(MenuContent, { className: "w-48", children: [_jsxs(MenuSub, { children: [_jsx(MenuSubTrigger, { children: "Turn into" }), _jsx(MenuSubContent, { className: "w-48", children: BLOCK_TYPES.map((type) => (_jsxs(MenuItem, { onSelect: () => hovered && turnInto(hovered, type.id), children: [_jsx(type.icon, { className: "size-4", "aria-hidden": true }), _jsx("span", { className: "flex-1", children: type.label }), hovered && type.matchesNode(hovered.node) ? (_jsx(Check, { className: "size-4 text-primary", "aria-hidden": true })) : null] }, type.id))) })] }), _jsxs(MenuItem, { onSelect: () => hovered && duplicate(hovered), children: [_jsx(Copy, { className: "size-4", "aria-hidden": true }), "Duplicate"] }), _jsxs(MenuItem, { onSelect: () => hovered && move(hovered, -1), children: [_jsx(ArrowUp, { className: "size-4", "aria-hidden": true }), "Move up"] }), _jsxs(MenuItem, { onSelect: () => hovered && move(hovered, 1), children: [_jsx(ArrowDown, { className: "size-4", "aria-hidden": true }), "Move down"] }), _jsx(MenuSeparator, {}), _jsxs(MenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => hovered && remove(hovered), children: [_jsx(Trash2, { className: "size-4", "aria-hidden": true }), "Delete"] })] })] })] }));
105
+ }
106
+ //# sourceMappingURL=block-handle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-handle.js","sourceRoot":"","sources":["../../src/editor/block-handle.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ChainedCommands, Editor } from "@tiptap/core";
2
+ import type { Node as PMNode } from "@tiptap/pm/model";
3
+ import { type LucideIcon } from "lucide-react";
4
+ /**
5
+ * The block vocabulary of the editor — one list, read by three surfaces: the
6
+ * `/` menu, the bubble menu's "Turn into", and each block's grip menu.
7
+ *
8
+ * These are exactly the blocks Markdown can say, which is the whole reason there
9
+ * are no callouts, toggles or columns: the stored format is Markdown text, and a
10
+ * block the read-only renderer (and every printed or emailed copy) could not show
11
+ * would silently degrade on the surfaces people hand to someone else.
12
+ */
13
+ export interface BlockType {
14
+ id: "paragraph" | "heading1" | "heading2" | "heading3" | "bulletList" | "orderedList" | "taskList" | "blockquote" | "codeBlock";
15
+ label: string;
16
+ description: string;
17
+ icon: LucideIcon;
18
+ /** Extra words the `/` menu matches on. */
19
+ keywords: string[];
20
+ /** Markdown shortcut shown as a hint, e.g. "# ". */
21
+ shortcut: string;
22
+ /**
23
+ * Convert whatever the selection is in. `clearNodes()` first lifts the
24
+ * selection out of lists and quotes, so "turn a quote into a heading" and
25
+ * "turn a list into paragraphs" both work in one step.
26
+ */
27
+ apply: (chain: ChainedCommands) => ChainedCommands;
28
+ isActive: (editor: Editor) => boolean;
29
+ /** Whether a given block node (from the grip, not the caret) is this type. */
30
+ matchesNode: (node: PMNode) => boolean;
31
+ }
32
+ export declare const BLOCK_TYPES: readonly BlockType[];
33
+ /** The block type the selection currently sits in, for "Turn into" state. */
34
+ export declare function activeBlockType(editor: Editor): BlockType;
35
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/editor/blocks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAUL,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EACE,WAAW,GACX,UAAU,GACV,UAAU,GACV,UAAU,GACV,YAAY,GACZ,aAAa,GACb,UAAU,GACV,YAAY,GACZ,WAAW,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,eAAe,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,8EAA8E;IAC9E,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAsBD,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EA0F3C,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAEzD"}
@@ -0,0 +1,114 @@
1
+ import { Code2, Heading1, Heading2, Heading3, List, ListOrdered, ListTodo, Pilcrow, Quote, } from "lucide-react";
2
+ const named = (name) => (node) => node.type.name === name;
3
+ const headingLevel = (level) => (node) => node.type.name === "heading" && node.attrs.level === level;
4
+ const PARAGRAPH = {
5
+ id: "paragraph",
6
+ label: "Text",
7
+ description: "Plain writing",
8
+ icon: Pilcrow,
9
+ keywords: ["paragraph", "plain", "body"],
10
+ shortcut: "",
11
+ apply: (chain) => chain.clearNodes().setParagraph(),
12
+ isActive: (editor) => editor.isActive("paragraph") &&
13
+ !editor.isActive("bulletList") &&
14
+ !editor.isActive("orderedList") &&
15
+ !editor.isActive("taskList") &&
16
+ !editor.isActive("blockquote"),
17
+ matchesNode: named("paragraph"),
18
+ };
19
+ export const BLOCK_TYPES = [
20
+ PARAGRAPH,
21
+ {
22
+ id: "heading1",
23
+ label: "Heading 1",
24
+ description: "Big section heading",
25
+ icon: Heading1,
26
+ keywords: ["h1", "title", "big"],
27
+ shortcut: "# ",
28
+ apply: (chain) => chain.clearNodes().setHeading({ level: 1 }),
29
+ isActive: (editor) => editor.isActive("heading", { level: 1 }),
30
+ matchesNode: headingLevel(1),
31
+ },
32
+ {
33
+ id: "heading2",
34
+ label: "Heading 2",
35
+ description: "Medium section heading",
36
+ icon: Heading2,
37
+ keywords: ["h2", "subtitle"],
38
+ shortcut: "## ",
39
+ apply: (chain) => chain.clearNodes().setHeading({ level: 2 }),
40
+ isActive: (editor) => editor.isActive("heading", { level: 2 }),
41
+ matchesNode: headingLevel(2),
42
+ },
43
+ {
44
+ id: "heading3",
45
+ label: "Heading 3",
46
+ description: "Small section heading",
47
+ icon: Heading3,
48
+ keywords: ["h3", "subheading"],
49
+ shortcut: "### ",
50
+ apply: (chain) => chain.clearNodes().setHeading({ level: 3 }),
51
+ isActive: (editor) => editor.isActive("heading", { level: 3 }),
52
+ matchesNode: headingLevel(3),
53
+ },
54
+ {
55
+ id: "bulletList",
56
+ label: "Bulleted list",
57
+ description: "A simple list",
58
+ icon: List,
59
+ keywords: ["bullet", "unordered", "points"],
60
+ shortcut: "- ",
61
+ apply: (chain) => chain.clearNodes().toggleBulletList(),
62
+ isActive: (editor) => editor.isActive("bulletList"),
63
+ matchesNode: named("bulletList"),
64
+ },
65
+ {
66
+ id: "orderedList",
67
+ label: "Numbered list",
68
+ description: "A list with numbers",
69
+ icon: ListOrdered,
70
+ keywords: ["numbered", "ordered", "steps"],
71
+ shortcut: "1. ",
72
+ apply: (chain) => chain.clearNodes().toggleOrderedList(),
73
+ isActive: (editor) => editor.isActive("orderedList"),
74
+ matchesNode: named("orderedList"),
75
+ },
76
+ {
77
+ id: "taskList",
78
+ label: "To-do list",
79
+ description: "Tick things off",
80
+ icon: ListTodo,
81
+ keywords: ["todo", "task", "checkbox", "tick", "checklist"],
82
+ shortcut: "[] ",
83
+ apply: (chain) => chain.clearNodes().toggleTaskList(),
84
+ isActive: (editor) => editor.isActive("taskList"),
85
+ matchesNode: named("taskList"),
86
+ },
87
+ {
88
+ id: "blockquote",
89
+ label: "Quote",
90
+ description: "Set a passage apart",
91
+ icon: Quote,
92
+ keywords: ["quote", "blockquote", "citation"],
93
+ shortcut: "> ",
94
+ apply: (chain) => chain.clearNodes().setBlockquote(),
95
+ isActive: (editor) => editor.isActive("blockquote"),
96
+ matchesNode: named("blockquote"),
97
+ },
98
+ {
99
+ id: "codeBlock",
100
+ label: "Code",
101
+ description: "Monospaced block",
102
+ icon: Code2,
103
+ keywords: ["code", "snippet", "monospace", "pre"],
104
+ shortcut: "``` ",
105
+ apply: (chain) => chain.clearNodes().setCodeBlock(),
106
+ isActive: (editor) => editor.isActive("codeBlock"),
107
+ matchesNode: named("codeBlock"),
108
+ },
109
+ ];
110
+ /** The block type the selection currently sits in, for "Turn into" state. */
111
+ export function activeBlockType(editor) {
112
+ return BLOCK_TYPES.find((type) => type.id !== "paragraph" && type.isActive(editor)) ?? PARAGRAPH;
113
+ }
114
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../src/editor/blocks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type Editor } from "@tiptap/react";
2
+ /**
3
+ * The formatting controls, shown only over a text selection — the Notion model.
4
+ * There is no standing toolbar: an empty editor is just a page, and the controls
5
+ * appear when there is something to apply them to. Block changes live under
6
+ * "Turn into", the same list the `/` menu and the block grip use.
7
+ */
8
+ export declare function BubbleToolbar({ editor }: {
9
+ editor: Editor;
10
+ }): import("react").JSX.Element | null;
11
+ //# sourceMappingURL=bubble-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bubble-toolbar.d.ts","sourceRoot":"","sources":["../../src/editor/bubble-toolbar.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAyC5D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,sCAgE3D"}
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { TextSelection } from "@tiptap/pm/state";
4
+ import { useEditorState } from "@tiptap/react";
5
+ import { BubbleMenu } from "@tiptap/react/menus";
6
+ import { Bold, Check, ChevronDown, Code, Italic, Link as LinkIcon, Strikethrough } from "lucide-react";
7
+ import { useState } from "react";
8
+ import { BLOCK_TYPES, activeBlockType } from "./blocks.js";
9
+ import { ghostButtonClass, IconButton, Menu, MenuContent, MenuItem, MenuLabel, MenuTrigger, PopoverContent, PopoverRoot, PopoverTrigger, primaryButtonClass, TextInput, VerticalSeparator, } from "./ui.js";
10
+ /**
11
+ * Everything the BubbleMenu wrapper is handed must be referentially stable. It
12
+ * re-registers its ProseMirror plugin whenever these props change identity, and
13
+ * re-registering reconfigures the editor's plugin set — which destroys and
14
+ * recreates *every* plugin view, including the suggestion popups, mid-session.
15
+ * An inline object here silently killed the `/` menu on its first re-render.
16
+ */
17
+ const BUBBLE_OPTIONS = { placement: "top", offset: 8 };
18
+ /** Only over a real text selection: not an empty caret, not a node selection (a
19
+ * dragged block), and not inside a code block, where inline marks are noise. */
20
+ const shouldShowBubble = ({ editor: current, state: editorState }) => {
21
+ const { selection } = editorState;
22
+ if (!current.isEditable || selection.empty)
23
+ return false;
24
+ if (!(selection instanceof TextSelection))
25
+ return false;
26
+ if (current.isActive("codeBlock"))
27
+ return false;
28
+ return editorState.doc.textBetween(selection.from, selection.to).trim().length > 0;
29
+ };
30
+ /**
31
+ * The formatting controls, shown only over a text selection — the Notion model.
32
+ * There is no standing toolbar: an empty editor is just a page, and the controls
33
+ * appear when there is something to apply them to. Block changes live under
34
+ * "Turn into", the same list the `/` menu and the block grip use.
35
+ */
36
+ export function BubbleToolbar({ editor }) {
37
+ const state = useEditorState({
38
+ editor,
39
+ selector: ({ editor: current }) => ({
40
+ bold: current.isActive("bold"),
41
+ italic: current.isActive("italic"),
42
+ strike: current.isActive("strike"),
43
+ code: current.isActive("code"),
44
+ link: current.isActive("link"),
45
+ linkHref: current.getAttributes("link").href ?? "",
46
+ block: activeBlockType(current),
47
+ }),
48
+ });
49
+ if (!state)
50
+ return null;
51
+ const chain = () => editor.chain().focus();
52
+ const BlockIcon = state.block.icon;
53
+ return (_jsxs(BubbleMenu, { editor: editor, pluginKey: "houseFormattingBubble", updateDelay: 120, options: BUBBLE_OPTIONS, shouldShow: shouldShowBubble, className: "house-bubble-menu flex items-center gap-0.5 rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-md", children: [_jsxs(Menu, { children: [_jsx(MenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: `${ghostButtonClass} h-8 px-2 text-xs`, children: [_jsx(BlockIcon, { className: "size-4", "aria-hidden": true }), state.block.label, _jsx(ChevronDown, { className: "size-3.5 opacity-60", "aria-hidden": true })] }) }), _jsxs(MenuContent, { className: "w-52", children: [_jsx(MenuLabel, { children: "Turn into" }), BLOCK_TYPES.map((type) => (_jsxs(MenuItem, { onSelect: () => void type.apply(chain()).run(), children: [_jsx(type.icon, { className: "size-4", "aria-hidden": true }), _jsx("span", { className: "flex-1", children: type.label }), type.id === state.block.id ? _jsx(Check, { className: "size-4 text-primary", "aria-hidden": true }) : null] }, type.id)))] })] }), _jsx(VerticalSeparator, {}), _jsx(MarkToggle, { label: "Bold", pressed: state.bold, onToggle: () => chain().toggleBold().run(), children: _jsx(Bold, { className: "size-4" }) }), _jsx(MarkToggle, { label: "Italic", pressed: state.italic, onToggle: () => chain().toggleItalic().run(), children: _jsx(Italic, { className: "size-4" }) }), _jsx(MarkToggle, { label: "Strikethrough", pressed: state.strike, onToggle: () => chain().toggleStrike().run(), children: _jsx(Strikethrough, { className: "size-4" }) }), _jsx(MarkToggle, { label: "Inline code", pressed: state.code, onToggle: () => chain().toggleCode().run(), children: _jsx(Code, { className: "size-4" }) }), _jsx(LinkControl, { editor: editor, active: state.link, href: state.linkHref })] }));
54
+ }
55
+ function MarkToggle({ label, pressed, onToggle, children, }) {
56
+ return (_jsx(IconButton, { "aria-label": label, title: label, pressed: pressed, onClick: onToggle, children: children }));
57
+ }
58
+ /** Set, change or clear the link on the current selection. */
59
+ function LinkControl({ editor, active, href }) {
60
+ const [open, setOpen] = useState(false);
61
+ const [draft, setDraft] = useState("");
62
+ const apply = () => {
63
+ const url = draft.trim();
64
+ const valid = /^https?:\/\//i.test(url) || (url.startsWith("/") && !url.startsWith("//"));
65
+ if (url && valid) {
66
+ editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
67
+ }
68
+ else if (!url) {
69
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
70
+ }
71
+ setOpen(false);
72
+ };
73
+ return (_jsxs(PopoverRoot, { open: open, onOpenChange: (next) => {
74
+ setOpen(next);
75
+ if (next)
76
+ setDraft(href);
77
+ }, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(IconButton, { "aria-label": "Link", title: "Link", pressed: active, children: _jsx(LinkIcon, { className: "size-4" }) }) }), _jsxs(PopoverContent, { className: "w-80 space-y-2", children: [_jsx("p", { className: "text-sm font-medium", children: "Link" }), _jsx(TextInput, { value: draft, onChange: (event) => setDraft(event.target.value), placeholder: "https://\u2026", "aria-label": "Link address", onKeyDown: (event) => {
78
+ if (event.key === "Enter") {
79
+ event.preventDefault();
80
+ apply();
81
+ }
82
+ } }), _jsxs("div", { className: "flex justify-end gap-2", children: [active ? (_jsx("button", { type: "button", className: `${ghostButtonClass} h-8 px-3`, onClick: () => {
83
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
84
+ setOpen(false);
85
+ }, children: "Remove" })) : null, _jsx("button", { type: "button", className: primaryButtonClass, onClick: apply, children: "Apply" })] })] })] }));
86
+ }
87
+ //# sourceMappingURL=bubble-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bubble-toolbar.js","sourceRoot":"","sources":["../../src/editor/bubble-toolbar.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { RichTextContent, richTextPreview, safeHref } from "./content.js";
2
+ //# sourceMappingURL=content-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-index.d.ts","sourceRoot":"","sources":["../../src/editor/content-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { RichTextContent, richTextPreview, safeHref } from "./content.js";
2
+ //# sourceMappingURL=content-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-index.js","sourceRoot":"","sources":["../../src/editor/content-index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Read-only display for what `RichTextEditor` writes — the Markdown dialect the
3
+ * editor produces (headings, lists incl. task lists, quotes, code fences,
4
+ * bold/italic/strike/inline code, links, `@`-references, `#hashtags`), with a
5
+ * bare newline shown as a line break so legacy plain-text values in the same
6
+ * columns read exactly as they always did.
7
+ *
8
+ * Separate from a guide renderer deliberately: a help centre wants guide-scale
9
+ * headings and link rewriting; a note wants task lists, fences and line breaks.
10
+ * Folding both dialects and both type scales into one would serve neither well.
11
+ *
12
+ * This is a trust boundary: whatever is stored in a text column is drawn here,
13
+ * often in front of a *different* person. Everything comes out as React
14
+ * elements with checked hrefs — never as markup.
15
+ *
16
+ * Server-safe: no hooks, so views can render bodies without a client boundary.
17
+ * Styled by `.house-prose` in `@connextar/house/house.css`.
18
+ */
19
+ export declare function RichTextContent({ content, className }: {
20
+ content: string;
21
+ className?: string;
22
+ }): import("react").JSX.Element;
23
+ /** Only hrefs safe to hand a browser; anything else renders as plain text. */
24
+ export declare function safeHref(href: string): string | null;
25
+ /**
26
+ * A plain-text preview of a Markdown body — for list cards and search rows.
27
+ * Strips block and inline syntax rather than rendering it.
28
+ */
29
+ export declare function richTextPreview(content: string, max?: number): string;
30
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/editor/content.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,+BAE9F;AAiBD,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiBpD;AAsPD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAM,GAAG,MAAM,CAclE"}