@spatika/editor 1.5.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.
- package/README.md +43 -0
- package/dist/composites/AiCommandDock.d.ts +13 -0
- package/dist/composites/AiCommandDock.d.ts.map +1 -0
- package/dist/composites/AiCommandDock.js +63 -0
- package/dist/composites/AiCommandMenu.d.ts +16 -0
- package/dist/composites/AiCommandMenu.d.ts.map +1 -0
- package/dist/composites/AiCommandMenu.js +51 -0
- package/dist/composites/AiCommandPanel.d.ts +12 -0
- package/dist/composites/AiCommandPanel.d.ts.map +1 -0
- package/dist/composites/AiCommandPanel.js +12 -0
- package/dist/composites/AiOverlay.d.ts +7 -0
- package/dist/composites/AiOverlay.d.ts.map +1 -0
- package/dist/composites/AiOverlay.js +8 -0
- package/dist/composites/EditorBubbleMenu.d.ts +10 -0
- package/dist/composites/EditorBubbleMenu.d.ts.map +1 -0
- package/dist/composites/EditorBubbleMenu.js +16 -0
- package/dist/composites/EditorToolbar.d.ts +20 -0
- package/dist/composites/EditorToolbar.d.ts.map +1 -0
- package/dist/composites/EditorToolbar.js +45 -0
- package/dist/composites/ImageBubbleMenu.d.ts +7 -0
- package/dist/composites/ImageBubbleMenu.d.ts.map +1 -0
- package/dist/composites/ImageBubbleMenu.js +15 -0
- package/dist/composites/MentionMenu.d.ts +11 -0
- package/dist/composites/MentionMenu.d.ts.map +1 -0
- package/dist/composites/MentionMenu.js +10 -0
- package/dist/composites/SlashCommandMenu.d.ts +13 -0
- package/dist/composites/SlashCommandMenu.d.ts.map +1 -0
- package/dist/composites/SlashCommandMenu.js +10 -0
- package/dist/composites/SpatikaEditor.d.ts +14 -0
- package/dist/composites/SpatikaEditor.d.ts.map +1 -0
- package/dist/composites/SpatikaEditor.js +159 -0
- package/dist/composites/TableGridPicker.d.ts +9 -0
- package/dist/composites/TableGridPicker.d.ts.map +1 -0
- package/dist/composites/TableGridPicker.js +54 -0
- package/dist/composites/ToolbarButton.d.ts +7 -0
- package/dist/composites/ToolbarButton.d.ts.map +1 -0
- package/dist/composites/ToolbarButton.js +7 -0
- package/dist/composites/ToolbarFormatPanels.d.ts +9 -0
- package/dist/composites/ToolbarFormatPanels.d.ts.map +1 -0
- package/dist/composites/ToolbarFormatPanels.js +35 -0
- package/dist/composites/ToolbarInsertPanels.d.ts +14 -0
- package/dist/composites/ToolbarInsertPanels.d.ts.map +1 -0
- package/dist/composites/ToolbarInsertPanels.js +96 -0
- package/dist/composites/ToolbarPopover.d.ts +14 -0
- package/dist/composites/ToolbarPopover.d.ts.map +1 -0
- package/dist/composites/ToolbarPopover.js +48 -0
- package/dist/extensions/ai-action.d.ts +24 -0
- package/dist/extensions/ai-action.d.ts.map +1 -0
- package/dist/extensions/ai-action.js +53 -0
- package/dist/extensions/mention.d.ts +17 -0
- package/dist/extensions/mention.d.ts.map +1 -0
- package/dist/extensions/mention.js +129 -0
- package/dist/extensions/resizable-image-view.d.ts +18 -0
- package/dist/extensions/resizable-image-view.d.ts.map +1 -0
- package/dist/extensions/resizable-image-view.js +70 -0
- package/dist/extensions/resizable-image.d.ts +4 -0
- package/dist/extensions/resizable-image.d.ts.map +1 -0
- package/dist/extensions/resizable-image.js +68 -0
- package/dist/extensions/slash-command.d.ts +18 -0
- package/dist/extensions/slash-command.d.ts.map +1 -0
- package/dist/extensions/slash-command.js +178 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/lib/ai-command-menu.d.ts +9 -0
- package/dist/lib/ai-command-menu.d.ts.map +1 -0
- package/dist/lib/ai-command-menu.js +43 -0
- package/dist/lib/cn.d.ts +4 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +6 -0
- package/dist/lib/editor-format-colors.d.ts +10 -0
- package/dist/lib/editor-format-colors.d.ts.map +1 -0
- package/dist/lib/editor-format-colors.js +24 -0
- package/dist/lib/image-upload.d.ts +14 -0
- package/dist/lib/image-upload.d.ts.map +1 -0
- package/dist/lib/image-upload.js +29 -0
- package/dist/lib/insert-editor-images.d.ts +5 -0
- package/dist/lib/insert-editor-images.d.ts.map +1 -0
- package/dist/lib/insert-editor-images.js +32 -0
- package/dist/lib/types.d.ts +131 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +12 -0
- package/dist/lib/use-spatika-editor.d.ts +13 -0
- package/dist/lib/use-spatika-editor.d.ts.map +1 -0
- package/dist/lib/use-spatika-editor.js +171 -0
- package/dist/styles.css +1056 -0
- package/package.json +101 -0
- package/src/composites/AiCommandDock.test.tsx +48 -0
- package/src/composites/AiCommandDock.tsx +137 -0
- package/src/composites/AiCommandMenu.test.tsx +67 -0
- package/src/composites/AiCommandMenu.tsx +120 -0
- package/src/composites/AiCommandPanel.test.tsx +42 -0
- package/src/composites/AiCommandPanel.tsx +56 -0
- package/src/composites/AiOverlay.test.tsx +18 -0
- package/src/composites/AiOverlay.tsx +18 -0
- package/src/composites/EditorBubbleMenu.test.tsx +49 -0
- package/src/composites/EditorBubbleMenu.tsx +73 -0
- package/src/composites/EditorToolbar.test.tsx +146 -0
- package/src/composites/EditorToolbar.tsx +362 -0
- package/src/composites/ImageBubbleMenu.test.tsx +43 -0
- package/src/composites/ImageBubbleMenu.tsx +51 -0
- package/src/composites/MentionMenu.test.tsx +25 -0
- package/src/composites/MentionMenu.tsx +46 -0
- package/src/composites/SlashCommandMenu.test.tsx +33 -0
- package/src/composites/SlashCommandMenu.tsx +54 -0
- package/src/composites/SpatikaEditor.test.tsx +167 -0
- package/src/composites/SpatikaEditor.tsx +302 -0
- package/src/composites/TableGridPicker.test.tsx +26 -0
- package/src/composites/TableGridPicker.tsx +138 -0
- package/src/composites/ToolbarButton.test.tsx +21 -0
- package/src/composites/ToolbarButton.tsx +24 -0
- package/src/composites/ToolbarFormatPanels.test.tsx +46 -0
- package/src/composites/ToolbarFormatPanels.tsx +78 -0
- package/src/composites/ToolbarInsertPanels.test.tsx +60 -0
- package/src/composites/ToolbarInsertPanels.tsx +200 -0
- package/src/composites/ToolbarPopover.test.tsx +24 -0
- package/src/composites/ToolbarPopover.tsx +95 -0
- package/src/extensions/ai-action.test.ts +26 -0
- package/src/extensions/ai-action.ts +73 -0
- package/src/extensions/mention.test.ts +31 -0
- package/src/extensions/mention.ts +158 -0
- package/src/extensions/resizable-image-view.test.ts +26 -0
- package/src/extensions/resizable-image-view.ts +96 -0
- package/src/extensions/resizable-image.test.ts +28 -0
- package/src/extensions/resizable-image.ts +75 -0
- package/src/extensions/slash-command.test.ts +45 -0
- package/src/extensions/slash-command.ts +197 -0
- package/src/index.ts +49 -0
- package/src/lib/ai-command-menu.test.ts +48 -0
- package/src/lib/ai-command-menu.ts +57 -0
- package/src/lib/cn.ts +7 -0
- package/src/lib/editor-format-colors.test.ts +15 -0
- package/src/lib/editor-format-colors.ts +31 -0
- package/src/lib/image-upload.test.ts +19 -0
- package/src/lib/image-upload.ts +49 -0
- package/src/lib/insert-editor-images.test.ts +76 -0
- package/src/lib/insert-editor-images.ts +44 -0
- package/src/lib/types.ts +149 -0
- package/src/lib/use-spatika-editor.test.tsx +56 -0
- package/src/lib/use-spatika-editor.ts +191 -0
- package/src/styles/editor.css +1056 -0
- package/src/test/setup.ts +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @spatika/editor
|
|
2
|
+
|
|
3
|
+
Tiptap-powered rich text editor with Spatika glass theming, slash commands, @mentions, AI hooks, and an extensible toolbar.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @spatika/editor @spatika/tokens
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `react` and `react-dom` (^18 or ^19).
|
|
12
|
+
|
|
13
|
+
## Bootstrap
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import "@spatika/tokens/styles.css";
|
|
17
|
+
import "@spatika/editor/styles.css";
|
|
18
|
+
import { SpatikaThemeProvider } from "@spatika/react";
|
|
19
|
+
import { SpatikaEditor } from "@spatika/editor";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`SpatikaEditor` is also re-exported from `@spatika/react`.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
<SpatikaEditor
|
|
28
|
+
value={html}
|
|
29
|
+
onChange={setHtml}
|
|
30
|
+
placeholder="Start writing…"
|
|
31
|
+
mentions={[{ id: "1", label: "Asha Verma" }]}
|
|
32
|
+
onAiCommand={async (context) => rewrite(context.actionId, context.selectedText)}
|
|
33
|
+
toolbar={{ layout: "ribbon" }}
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Headless
|
|
38
|
+
|
|
39
|
+
Use `useSpatikaEditor` when you need full control over chrome while keeping Spatika's default extensions.
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT — uses MIT-licensed Tiptap packages only.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { AiActionDefinition, AiCommandMenuConfig } from "../lib/types";
|
|
3
|
+
import type { AiCommandRunRequest } from "./AiCommandMenu";
|
|
4
|
+
type AiCommandDockProps = {
|
|
5
|
+
editor: Editor | null;
|
|
6
|
+
config?: AiCommandMenuConfig;
|
|
7
|
+
actions?: AiActionDefinition[];
|
|
8
|
+
onRun: (request: AiCommandRunRequest) => void;
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function AiCommandDock({ editor, config, actions, onRun, className }: AiCommandDockProps): import("react").JSX.Element | null;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=AiCommandDock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiCommandDock.d.ts","sourceRoot":"","sources":["../../src/composites/AiCommandDock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAgB3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAI5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAoB3D,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,kBAAkB,sCAwF9F"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowLeft, ArrowUp, MessageSquarePlus, PenLine, Plus, Search, Sparkles, Table2, } from "lucide-react";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { resolveAiCommandActions, resolveAiPromptConfig, } from "../lib/ai-command-menu";
|
|
5
|
+
import { AI_PROMPT_ACTION_ID } from "../lib/types";
|
|
6
|
+
import { cn } from "../lib/cn";
|
|
7
|
+
import { AiCommandPanel } from "./AiCommandPanel";
|
|
8
|
+
const DEFAULT_ACTION_ICONS = {
|
|
9
|
+
proofread: _jsx(Search, { className: "size-4" }),
|
|
10
|
+
rewrite: _jsx(PenLine, { className: "size-4" }),
|
|
11
|
+
shorten: _jsx(ArrowLeft, { className: "size-4" }),
|
|
12
|
+
improve: _jsx(Sparkles, { className: "size-4" }),
|
|
13
|
+
expand: _jsx(Plus, { className: "size-4" }),
|
|
14
|
+
continue: _jsx(Plus, { className: "size-4" }),
|
|
15
|
+
table: _jsx(Table2, { className: "size-4" }),
|
|
16
|
+
comment: _jsx(MessageSquarePlus, { className: "size-4" }),
|
|
17
|
+
};
|
|
18
|
+
function withDefaultIcons(actions) {
|
|
19
|
+
return actions.map((action) => ({
|
|
20
|
+
...action,
|
|
21
|
+
icon: action.icon ?? DEFAULT_ACTION_ICONS[action.id],
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
export function AiCommandDock({ editor, config, actions, onRun, className }) {
|
|
25
|
+
const [open, setOpen] = useState(false);
|
|
26
|
+
const [prompt, setPrompt] = useState("");
|
|
27
|
+
const rootRef = useRef(null);
|
|
28
|
+
const panelId = useId();
|
|
29
|
+
const title = config?.title ?? "AI Toolkit examples";
|
|
30
|
+
const commandActions = withDefaultIcons(resolveAiCommandActions(config, actions));
|
|
31
|
+
const promptConfig = resolveAiPromptConfig(config?.prompt);
|
|
32
|
+
const hasSelection = Boolean(editor?.state?.selection && !editor.state.selection.empty);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!open)
|
|
35
|
+
return;
|
|
36
|
+
const onPointerDown = (event) => {
|
|
37
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
38
|
+
setOpen(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
document.addEventListener("mousedown", onPointerDown);
|
|
42
|
+
return () => document.removeEventListener("mousedown", onPointerDown);
|
|
43
|
+
}, [open]);
|
|
44
|
+
const runAction = (actionId, userPrompt) => {
|
|
45
|
+
onRun({ actionId, prompt: userPrompt });
|
|
46
|
+
setOpen(false);
|
|
47
|
+
setPrompt("");
|
|
48
|
+
};
|
|
49
|
+
const submitPrompt = () => {
|
|
50
|
+
const trimmed = prompt.trim();
|
|
51
|
+
if (!trimmed)
|
|
52
|
+
return;
|
|
53
|
+
runAction(AI_PROMPT_ACTION_ID, trimmed);
|
|
54
|
+
};
|
|
55
|
+
if (!editor)
|
|
56
|
+
return null;
|
|
57
|
+
return (_jsxs("div", { ref: rootRef, className: cn("spk-editor-ai-dock", className), children: [open ? (_jsx(AiCommandPanel, { id: panelId, title: title, actions: commandActions, hasSelection: hasSelection, onSelect: (actionId) => runAction(actionId), className: "spk-editor-ai-dock-panel" })) : null, promptConfig.enabled ? (_jsxs("div", { className: "spk-editor-ai-dock-bar", role: "search", children: [_jsx("button", { type: "button", className: cn("spk-editor-ai-dock-trigger", open && "spk-editor-ai-dock-trigger--active"), "aria-label": "AI commands", "aria-expanded": open, "aria-controls": open ? panelId : undefined, onClick: () => setOpen((value) => !value), children: _jsx(Sparkles, { className: "size-4" }) }), _jsxs("label", { className: "spk-editor-ai-dock-field", children: [_jsx("span", { className: "sr-only", children: promptConfig.placeholder }), _jsx("input", { type: "text", className: "spk-editor-ai-dock-input", value: prompt, placeholder: promptConfig.placeholder, onFocus: () => setOpen(true), onChange: (event) => setPrompt(event.target.value), onKeyDown: (event) => {
|
|
58
|
+
if (event.key === "Enter")
|
|
59
|
+
submitPrompt();
|
|
60
|
+
if (event.key === "Escape")
|
|
61
|
+
setOpen(false);
|
|
62
|
+
} })] }), _jsx("button", { type: "button", className: "spk-editor-ai-dock-submit", "aria-label": promptConfig.submitLabel, disabled: !prompt.trim(), onClick: submitPrompt, children: _jsx(ArrowUp, { className: "size-4" }) })] })) : null] }));
|
|
63
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { AiActionDefinition, AiCommandMenuConfig } from "../lib/types";
|
|
3
|
+
export type AiCommandRunRequest = {
|
|
4
|
+
actionId: string;
|
|
5
|
+
prompt?: string;
|
|
6
|
+
};
|
|
7
|
+
type AiCommandMenuProps = {
|
|
8
|
+
editor: Editor | null;
|
|
9
|
+
config?: AiCommandMenuConfig;
|
|
10
|
+
actions?: AiActionDefinition[];
|
|
11
|
+
onRun: (request: AiCommandRunRequest) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function AiCommandMenu({ editor, config, actions, onRun, className }: AiCommandMenuProps): import("react").JSX.Element | null;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=AiCommandMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiCommandMenu.d.ts","sourceRoot":"","sources":["../../src/composites/AiCommandMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAO3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAM5E,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,kBAAkB,sCA6F9F"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowUp, Sparkles } from "lucide-react";
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { resolveAiCommandActions, resolveAiPromptConfig, } from "../lib/ai-command-menu";
|
|
5
|
+
import { AI_PROMPT_ACTION_ID } from "../lib/types";
|
|
6
|
+
import { cn } from "../lib/cn";
|
|
7
|
+
import { AiCommandPanel } from "./AiCommandPanel";
|
|
8
|
+
import { ToolbarButton } from "./ToolbarButton";
|
|
9
|
+
export function AiCommandMenu({ editor, config, actions, onRun, className }) {
|
|
10
|
+
const [open, setOpen] = useState(false);
|
|
11
|
+
const [prompt, setPrompt] = useState("");
|
|
12
|
+
const rootRef = useRef(null);
|
|
13
|
+
const titleId = useId();
|
|
14
|
+
const title = config?.title ?? "AI Toolkit examples";
|
|
15
|
+
const commandActions = resolveAiCommandActions(config, actions);
|
|
16
|
+
const promptConfig = resolveAiPromptConfig(config?.prompt);
|
|
17
|
+
const hasSelection = Boolean(editor?.state?.selection && !editor.state.selection.empty);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!open)
|
|
20
|
+
return;
|
|
21
|
+
const onPointerDown = (event) => {
|
|
22
|
+
if (!rootRef.current?.contains(event.target)) {
|
|
23
|
+
setOpen(false);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
document.addEventListener("mousedown", onPointerDown);
|
|
27
|
+
return () => document.removeEventListener("mousedown", onPointerDown);
|
|
28
|
+
}, [open]);
|
|
29
|
+
const close = () => {
|
|
30
|
+
setOpen(false);
|
|
31
|
+
setPrompt("");
|
|
32
|
+
};
|
|
33
|
+
const runAction = (actionId, userPrompt) => {
|
|
34
|
+
onRun({ actionId, prompt: userPrompt });
|
|
35
|
+
close();
|
|
36
|
+
};
|
|
37
|
+
const submitPrompt = () => {
|
|
38
|
+
const trimmed = prompt.trim();
|
|
39
|
+
if (!trimmed)
|
|
40
|
+
return;
|
|
41
|
+
runAction(AI_PROMPT_ACTION_ID, trimmed);
|
|
42
|
+
};
|
|
43
|
+
if (!editor)
|
|
44
|
+
return null;
|
|
45
|
+
return (_jsxs("div", { ref: rootRef, className: cn("spk-editor-ai-menu", className), children: [_jsx(ToolbarButton, { "aria-label": "AI commands", "aria-expanded": open, "aria-haspopup": "dialog", "aria-controls": open ? titleId : undefined, active: open, onClick: () => setOpen((value) => !value), children: _jsx(Sparkles, { className: "size-4" }) }), open ? (_jsxs("div", { className: "spk-editor-ai-menu-shell", onMouseDown: (event) => event.stopPropagation(), children: [_jsx(AiCommandPanel, { id: titleId, title: title, actions: commandActions, hasSelection: hasSelection, onSelect: (actionId) => runAction(actionId) }), promptConfig.enabled ? (_jsxs("div", { className: "spk-editor-ai-menu-prompt", children: [_jsxs("label", { className: "spk-editor-ai-menu-prompt-field", children: [_jsx("span", { className: "sr-only", children: promptConfig.placeholder }), _jsx("input", { type: "text", className: "spk-editor-ai-menu-prompt-input", value: prompt, placeholder: promptConfig.placeholder, onChange: (event) => setPrompt(event.target.value), onKeyDown: (event) => {
|
|
46
|
+
if (event.key === "Enter")
|
|
47
|
+
submitPrompt();
|
|
48
|
+
if (event.key === "Escape")
|
|
49
|
+
close();
|
|
50
|
+
}, autoFocus: true })] }), _jsx("button", { type: "button", className: "spk-editor-ai-menu-prompt-submit", "aria-label": promptConfig.submitLabel, disabled: !prompt.trim(), onClick: submitPrompt, children: _jsx(ArrowUp, { className: "size-4" }) })] })) : null] })) : null] }));
|
|
51
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AiActionDefinition } from "../lib/types";
|
|
2
|
+
type AiCommandPanelProps = {
|
|
3
|
+
title: string;
|
|
4
|
+
actions: AiActionDefinition[];
|
|
5
|
+
hasSelection: boolean;
|
|
6
|
+
onSelect: (actionId: string) => void;
|
|
7
|
+
className?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function AiCommandPanel({ title, actions, hasSelection, onSelect, className, id, }: AiCommandPanelProps): import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=AiCommandPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiCommandPanel.d.ts","sourceRoot":"","sources":["../../src/composites/AiCommandPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAIvD,KAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAMF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,OAAO,EACP,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,EAAE,GACH,EAAE,mBAAmB,+BA+BrB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Sparkles } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
function actionDisabled(action, hasSelection) {
|
|
5
|
+
return Boolean(action.requiresSelection && !hasSelection);
|
|
6
|
+
}
|
|
7
|
+
export function AiCommandPanel({ title, actions, hasSelection, onSelect, className, id, }) {
|
|
8
|
+
return (_jsxs("div", { id: id, className: cn("spk-editor-ai-panel", className), role: "dialog", "aria-label": title, children: [_jsx("div", { className: "spk-editor-ai-panel-title", children: title }), _jsx("ul", { className: "spk-editor-ai-panel-list", "aria-label": title, children: actions.map((action) => {
|
|
9
|
+
const disabled = actionDisabled(action, hasSelection);
|
|
10
|
+
return (_jsx("li", { children: _jsxs("button", { type: "button", className: "spk-editor-ai-panel-item", disabled: disabled, onClick: () => onSelect(action.id), children: [_jsx("span", { className: "spk-editor-ai-panel-item-icon", "aria-hidden": "true", children: action.icon ?? _jsx(Sparkles, { className: "size-4" }) }), _jsxs("span", { className: "spk-editor-ai-panel-item-copy", children: [_jsx("span", { className: "spk-editor-ai-panel-item-label", children: action.label }), action.description ? (_jsx("span", { className: "spk-editor-ai-panel-item-desc", children: action.description })) : null] })] }) }, action.id));
|
|
11
|
+
}) })] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AiOverlay.d.ts","sourceRoot":"","sources":["../../src/composites/AiOverlay.tsx"],"names":[],"mappings":"AAGA,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,cAAc,sCAS/D"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Sparkles } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
export function AiOverlay({ visible, className }) {
|
|
5
|
+
if (!visible)
|
|
6
|
+
return null;
|
|
7
|
+
return (_jsxs("div", { className: cn("spk-editor-ai-overlay", className), "aria-hidden": "true", children: [_jsx(Sparkles, { className: "spk-editor-ai-overlay-icon size-5" }), _jsx("span", { children: "AI is working\u2026" })] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { AiActionDefinition } from "../lib/types";
|
|
3
|
+
type EditorBubbleMenuProps = {
|
|
4
|
+
editor: Editor | null;
|
|
5
|
+
aiActions?: AiActionDefinition[];
|
|
6
|
+
onAiAction?: (actionId: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function EditorBubbleMenu({ editor, aiActions, onAiAction }: EditorBubbleMenuProps): import("react").JSX.Element | null;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=EditorBubbleMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditorBubbleMenu.d.ts","sourceRoot":"","sources":["../../src/composites/EditorBubbleMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,qBAAqB,sCA4DxF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
3
|
+
import { Bold, Code, Italic, Sparkles, Strikethrough, Underline as UnderlineIcon } from "lucide-react";
|
|
4
|
+
import { ToolbarButton } from "./ToolbarButton";
|
|
5
|
+
export function EditorBubbleMenu({ editor, aiActions, onAiAction }) {
|
|
6
|
+
if (!editor)
|
|
7
|
+
return null;
|
|
8
|
+
return (_jsxs(BubbleMenu, { editor: editor, className: "spk-editor-bubble-menu", shouldShow: ({ editor: ed, state }) => {
|
|
9
|
+
const { selection } = state;
|
|
10
|
+
if (selection.empty)
|
|
11
|
+
return false;
|
|
12
|
+
if (ed.isActive("image"))
|
|
13
|
+
return false;
|
|
14
|
+
return true;
|
|
15
|
+
}, children: [_jsx(ToolbarButton, { "aria-label": "Bold", active: editor.isActive("bold"), onClick: () => editor.chain().focus().toggleBold().run(), children: _jsx(Bold, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Italic", active: editor.isActive("italic"), onClick: () => editor.chain().focus().toggleItalic().run(), children: _jsx(Italic, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Underline", active: editor.isActive("underline"), onClick: () => editor.chain().focus().toggleUnderline().run(), children: _jsx(UnderlineIcon, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Strikethrough", active: editor.isActive("strike"), onClick: () => editor.chain().focus().toggleStrike().run(), children: _jsx(Strikethrough, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Inline code", active: editor.isActive("code"), onClick: () => editor.chain().focus().toggleCode().run(), children: _jsx(Code, { className: "size-4" }) }), aiActions?.map((action) => (_jsx(ToolbarButton, { "aria-label": action.label, onClick: () => onAiAction?.(action.id), children: action.icon ?? _jsx(Sparkles, { className: "size-4" }) }, action.id)))] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { AiActionDefinition, AiCommandMenuConfig, ImageInsertConfig, SpatikaEditorToolbarConfig } from "../lib/types";
|
|
4
|
+
import { type AiCommandRunRequest } from "./AiCommandMenu";
|
|
5
|
+
type EditorToolbarProps = {
|
|
6
|
+
editor: Editor | null;
|
|
7
|
+
config?: SpatikaEditorToolbarConfig;
|
|
8
|
+
toolbarActions?: ReactNode;
|
|
9
|
+
aiActions?: AiActionDefinition[];
|
|
10
|
+
aiCommandMenu?: AiCommandMenuConfig | false;
|
|
11
|
+
useAiCommandMenu?: boolean;
|
|
12
|
+
onAiCommand?: (request: AiCommandRunRequest) => void;
|
|
13
|
+
onAiAction?: (actionId: string) => void;
|
|
14
|
+
onInsertImage?: () => void;
|
|
15
|
+
imageInsert?: ImageInsertConfig;
|
|
16
|
+
className?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function EditorToolbar({ editor, config, toolbarActions, aiActions, aiCommandMenu, useAiCommandMenu, onAiCommand, onAiAction, onInsertImage, imageInsert, className, }: EditorToolbarProps): import("react").JSX.Element | null;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=EditorToolbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditorToolbar.d.ts","sourceRoot":"","sources":["../../src/composites/EditorToolbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAgC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAG3H,OAAO,EAAiB,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM1E,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;IAC5C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAMF,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,cAAc,EACd,SAAS,EACT,aAAa,EACb,gBAAwB,EACxB,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACX,SAAS,GACV,EAAE,kBAAkB,sCAgSpB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, Bold, Code, Heading1, Heading2, Heading3, Highlighter, ImageIcon, Italic, Link2, List, ListOrdered, ListTodo, Minus, Palette, Pilcrow, Quote, Redo2, Sparkles, SquareCode, Strikethrough, Subscript, Superscript, Table2, Underline as UnderlineIcon, Undo2, } from "lucide-react";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { DEFAULT_TOOLBAR } from "../lib/types";
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
import { AiCommandMenu } from "./AiCommandMenu";
|
|
7
|
+
import { ToolbarButton } from "./ToolbarButton";
|
|
8
|
+
import { ImageInsertPanel, LinkInsertPanel, TableInsertPanel } from "./ToolbarInsertPanels";
|
|
9
|
+
import { HighlightColorPanel, TextColorPanel } from "./ToolbarFormatPanels";
|
|
10
|
+
import { ToolbarPopover } from "./ToolbarPopover";
|
|
11
|
+
function ToolbarGroup({ children, className }) {
|
|
12
|
+
return _jsx("div", { className: cn("spk-editor-toolbar-group", className), children: children });
|
|
13
|
+
}
|
|
14
|
+
export function EditorToolbar({ editor, config, toolbarActions, aiActions, aiCommandMenu, useAiCommandMenu = false, onAiCommand, onAiAction, onInsertImage, imageInsert, className, }) {
|
|
15
|
+
const toolbar = { ...DEFAULT_TOOLBAR, ...config };
|
|
16
|
+
const [, setRevision] = useState(0);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!editor)
|
|
19
|
+
return;
|
|
20
|
+
const refresh = () => setRevision((value) => value + 1);
|
|
21
|
+
editor.on("transaction", refresh);
|
|
22
|
+
return () => {
|
|
23
|
+
editor.off("transaction", refresh);
|
|
24
|
+
};
|
|
25
|
+
}, [editor]);
|
|
26
|
+
if (!editor)
|
|
27
|
+
return null;
|
|
28
|
+
const historyGroup = toolbar.history ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { "aria-label": "Undo", disabled: !editor.can().undo(), onClick: () => editor.chain().focus().undo().run(), children: _jsx(Undo2, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Redo", disabled: !editor.can().redo(), onClick: () => editor.chain().focus().redo().run(), children: _jsx(Redo2, { className: "size-4" }) })] })) : null;
|
|
29
|
+
const textStyleGroup = toolbar.textStyle ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { "aria-label": "Paragraph", active: editor.isActive("paragraph"), onClick: () => editor.chain().focus().setParagraph().run(), children: _jsx(Pilcrow, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Heading 1", active: editor.isActive("heading", { level: 1 }), onClick: () => editor.chain().focus().toggleHeading({ level: 1 }).run(), children: _jsx(Heading1, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Heading 2", active: editor.isActive("heading", { level: 2 }), onClick: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), children: _jsx(Heading2, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Heading 3", active: editor.isActive("heading", { level: 3 }), onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), children: _jsx(Heading3, { className: "size-4" }) })] })) : null;
|
|
30
|
+
const inlineGroup = toolbar.inlineFormat ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { "aria-label": "Bold", active: editor.isActive("bold"), onClick: () => editor.chain().focus().toggleBold().run(), children: _jsx(Bold, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Italic", active: editor.isActive("italic"), onClick: () => editor.chain().focus().toggleItalic().run(), children: _jsx(Italic, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Underline", active: editor.isActive("underline"), onClick: () => editor.chain().focus().toggleUnderline().run(), children: _jsx(UnderlineIcon, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Strikethrough", active: editor.isActive("strike"), onClick: () => editor.chain().focus().toggleStrike().run(), children: _jsx(Strikethrough, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Inline code", active: editor.isActive("code"), onClick: () => editor.chain().focus().toggleCode().run(), children: _jsx(Code, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Subscript", active: editor.isActive("subscript"), onClick: () => editor.chain().focus().toggleSubscript().run(), children: _jsx(Subscript, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Superscript", active: editor.isActive("superscript"), onClick: () => editor.chain().focus().toggleSuperscript().run(), children: _jsx(Superscript, { className: "size-4" }) }), _jsx(ToolbarPopover, { label: "Text color", title: "Text color", icon: _jsx(Palette, { className: "size-4" }), active: Boolean(editor.getAttributes("textStyle").color), children: ({ close }) => _jsx(TextColorPanel, { editor: editor, close: close }) }), _jsx(ToolbarPopover, { label: "Highlight", title: "Highlight color", icon: _jsx(Highlighter, { className: "size-4" }), active: editor.isActive("highlight"), children: ({ close }) => _jsx(HighlightColorPanel, { editor: editor, close: close }) }), _jsx(ToolbarButton, { "aria-label": "Blockquote", active: editor.isActive("blockquote"), onClick: () => editor.chain().focus().toggleBlockquote().run(), children: _jsx(Quote, { className: "size-4" }) })] })) : null;
|
|
31
|
+
const listGroup = toolbar.lists ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { "aria-label": "Bullet list", active: editor.isActive("bulletList"), onClick: () => editor.chain().focus().toggleBulletList().run(), children: _jsx(List, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Numbered list", active: editor.isActive("orderedList"), onClick: () => editor.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrdered, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Task list", active: editor.isActive("taskList"), onClick: () => editor.chain().focus().toggleTaskList().run(), children: _jsx(ListTodo, { className: "size-4" }) })] })) : null;
|
|
32
|
+
const alignGroup = toolbar.alignment ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarButton, { "aria-label": "Align left", active: editor.isActive({ textAlign: "left" }), onClick: () => editor.chain().focus().setTextAlign("left").run(), children: _jsx(AlignLeft, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Align center", active: editor.isActive({ textAlign: "center" }), onClick: () => editor.chain().focus().setTextAlign("center").run(), children: _jsx(AlignCenter, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Align right", active: editor.isActive({ textAlign: "right" }), onClick: () => editor.chain().focus().setTextAlign("right").run(), children: _jsx(AlignRight, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Justify", active: editor.isActive({ textAlign: "justify" }), onClick: () => editor.chain().focus().setTextAlign("justify").run(), children: _jsx(AlignJustify, { className: "size-4" }) })] })) : null;
|
|
33
|
+
const insertGroup = toolbar.insert ? (_jsxs(ToolbarGroup, { children: [_jsx(ToolbarPopover, { label: "Insert link", title: "Insert link", icon: _jsx(Link2, { className: "size-4" }), children: ({ close }) => _jsx(LinkInsertPanel, { editor: editor, close: close }) }), _jsx(ToolbarButton, { "aria-label": "Insert divider", onClick: () => editor.chain().focus().setHorizontalRule().run(), children: _jsx(Minus, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Code block", active: editor.isActive("codeBlock"), onClick: () => editor.chain().focus().toggleCodeBlock().run(), children: _jsx(SquareCode, { className: "size-4" }) }), _jsx(ToolbarPopover, { label: "Insert table", title: "Insert table", icon: _jsx(Table2, { className: "size-4" }), children: ({ close }) => _jsx(TableInsertPanel, { editor: editor, close: close }) }), onInsertImage ? (_jsx(ToolbarButton, { "aria-label": "Insert image", onClick: onInsertImage, children: _jsx(ImageIcon, { className: "size-4" }) })) : (_jsx(ToolbarPopover, { label: "Insert image", title: "Insert image", icon: _jsx(ImageIcon, { className: "size-4" }), children: ({ close }) => _jsx(ImageInsertPanel, { editor: editor, close: close, config: imageInsert }) }))] })) : null;
|
|
34
|
+
const aiGroup = toolbar.ai && useAiCommandMenu && onAiCommand ? (_jsx(ToolbarGroup, { children: _jsx(AiCommandMenu, { editor: editor, config: typeof aiCommandMenu === "object" ? aiCommandMenu : undefined, actions: aiActions, onRun: onAiCommand }) })) : toolbar.ai && aiActions?.length ? (_jsx(ToolbarGroup, { children: aiActions.map((action) => (_jsx(ToolbarButton, { "aria-label": action.label, onClick: () => onAiAction?.(action.id), children: action.icon ?? _jsx(Sparkles, { className: "size-4" }) }, action.id))) })) : null;
|
|
35
|
+
const groupNodes = [
|
|
36
|
+
{ id: "history", node: historyGroup },
|
|
37
|
+
{ id: "text-style", node: textStyleGroup },
|
|
38
|
+
{ id: "inline", node: inlineGroup },
|
|
39
|
+
{ id: "lists", node: listGroup },
|
|
40
|
+
{ id: "align", node: alignGroup },
|
|
41
|
+
{ id: "insert", node: insertGroup },
|
|
42
|
+
{ id: "ai", node: aiGroup },
|
|
43
|
+
].filter((entry) => entry.node);
|
|
44
|
+
return (_jsxs("div", { className: cn("spk-editor-toolbar", toolbar.layout === "compact" && "spk-editor-toolbar--compact", className), role: "toolbar", "aria-label": "Editor formatting", children: [_jsx("div", { className: "spk-editor-toolbar-row", children: groupNodes.map((entry) => (_jsx("div", { children: entry.node }, entry.id))) }), toolbarActions ? _jsx("div", { className: "spk-editor-toolbar-actions", children: toolbarActions }) : null] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
type ImageBubbleMenuProps = {
|
|
3
|
+
editor: Editor | null;
|
|
4
|
+
};
|
|
5
|
+
export declare function ImageBubbleMenu({ editor }: ImageBubbleMenuProps): import("react").JSX.Element | null;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=ImageBubbleMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageBubbleMenu.d.ts","sourceRoot":"","sources":["../../src/composites/ImageBubbleMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAM3C,KAAK,oBAAoB,GAAG;IAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,oBAAoB,sCAwC/D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { BubbleMenu } from "@tiptap/react/menus";
|
|
3
|
+
import { AlignCenter, AlignLeft, AlignRight, Trash2 } from "lucide-react";
|
|
4
|
+
import { ToolbarButton } from "./ToolbarButton";
|
|
5
|
+
export function ImageBubbleMenu({ editor }) {
|
|
6
|
+
if (!editor)
|
|
7
|
+
return null;
|
|
8
|
+
const setAlign = (align) => {
|
|
9
|
+
editor.chain().focus().updateAttributes("image", { align }).run();
|
|
10
|
+
};
|
|
11
|
+
const setWidth = (width) => {
|
|
12
|
+
editor.chain().focus().updateAttributes("image", { width }).run();
|
|
13
|
+
};
|
|
14
|
+
return (_jsxs(BubbleMenu, { editor: editor, className: "spk-editor-bubble-menu", shouldShow: ({ editor: ed }) => ed.isActive("image"), children: [_jsx(ToolbarButton, { "aria-label": "Align image left", onClick: () => setAlign("left"), children: _jsx(AlignLeft, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Align image center", onClick: () => setAlign("center"), children: _jsx(AlignCenter, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Align image right", onClick: () => setAlign("right"), children: _jsx(AlignRight, { className: "size-4" }) }), _jsx(ToolbarButton, { "aria-label": "Image width 50%", onClick: () => setWidth("50%"), children: "50%" }), _jsx(ToolbarButton, { "aria-label": "Image width 100%", onClick: () => setWidth("100%"), children: "100%" }), _jsx(ToolbarButton, { "aria-label": "Delete image", onClick: () => editor.chain().focus().deleteSelection().run(), children: _jsx(Trash2, { className: "size-4" }) })] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MentionContact } from "../lib/types";
|
|
2
|
+
type MentionMenuProps = {
|
|
3
|
+
contacts: MentionContact[];
|
|
4
|
+
query: string;
|
|
5
|
+
activeIndex: number;
|
|
6
|
+
open: boolean;
|
|
7
|
+
onSelect: (contact: MentionContact) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function MentionMenu({ contacts, query, activeIndex, open, onSelect }: MentionMenuProps): import("react").JSX.Element | null;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=MentionMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MentionMenu.d.ts","sourceRoot":"","sources":["../../src/composites/MentionMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAGnD,KAAK,gBAAgB,GAAG;IACtB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,gBAAgB,sCAkC7F"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
export function MentionMenu({ contacts, query, activeIndex, open, onSelect }) {
|
|
4
|
+
if (!open || contacts.length === 0)
|
|
5
|
+
return null;
|
|
6
|
+
return (_jsxs("div", { className: "spk-editor-suggestion", role: "listbox", "aria-label": "Mentions", children: [_jsx("div", { className: "spk-editor-suggestion-header", children: query ? `People matching “@${query}”` : "Mention someone" }), _jsx("ul", { className: "spk-editor-suggestion-list", children: contacts.map((contact, index) => (_jsx("li", { children: _jsxs("button", { type: "button", role: "option", "aria-selected": index === activeIndex, className: cn("spk-editor-suggestion-item", index === activeIndex && "spk-editor-suggestion-item--active"), onMouseDown: (event) => {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
onSelect(contact);
|
|
9
|
+
}, children: [_jsx("span", { className: "spk-editor-suggestion-title", children: contact.label }), contact.subtitle ? (_jsx("span", { className: "spk-editor-suggestion-desc", children: contact.subtitle })) : null] }) }, contact.id))) })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
import type { SlashCommandItem } from "../lib/types";
|
|
3
|
+
type SlashCommandMenuProps = {
|
|
4
|
+
editor: Editor | null;
|
|
5
|
+
items: SlashCommandItem[];
|
|
6
|
+
query: string;
|
|
7
|
+
activeIndex: number;
|
|
8
|
+
open: boolean;
|
|
9
|
+
onSelect: (item: SlashCommandItem) => void;
|
|
10
|
+
};
|
|
11
|
+
export declare function SlashCommandMenu({ items, query, activeIndex, open, onSelect, }: SlashCommandMenuProps): import("react").JSX.Element | null;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=SlashCommandMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlashCommandMenu.d.ts","sourceRoot":"","sources":["../../src/composites/SlashCommandMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,KAAK,qBAAqB,GAAG;IAC3B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC5C,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,KAAK,EACL,WAAW,EACX,IAAI,EACJ,QAAQ,GACT,EAAE,qBAAqB,sCAkCvB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
export function SlashCommandMenu({ items, query, activeIndex, open, onSelect, }) {
|
|
4
|
+
if (!open || items.length === 0)
|
|
5
|
+
return null;
|
|
6
|
+
return (_jsxs("div", { className: "spk-editor-suggestion", role: "listbox", "aria-label": "Slash commands", children: [_jsx("div", { className: "spk-editor-suggestion-header", children: query ? `Results for “/${query}”` : "Insert block" }), _jsx("ul", { className: "spk-editor-suggestion-list", children: items.map((item, index) => (_jsx("li", { children: _jsxs("button", { type: "button", role: "option", "aria-selected": index === activeIndex, className: cn("spk-editor-suggestion-item", index === activeIndex && "spk-editor-suggestion-item--active"), onMouseDown: (event) => {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
onSelect(item);
|
|
9
|
+
}, children: [_jsx("span", { className: "spk-editor-suggestion-title", children: item.title }), item.description ? (_jsx("span", { className: "spk-editor-suggestion-desc", children: item.description })) : null] }) }, item.id))) })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SpatikaEditorHandle } from "../lib/types";
|
|
2
|
+
export declare const SpatikaEditor: import("react").ForwardRefExoticComponent<import("..").UseSpatikaEditorOptions & {
|
|
3
|
+
className?: string;
|
|
4
|
+
heightClassName?: string;
|
|
5
|
+
toolbar?: import("..").SpatikaEditorToolbarConfig;
|
|
6
|
+
toolbarActions?: import("react").ReactNode;
|
|
7
|
+
isAiProcessing?: boolean;
|
|
8
|
+
aiActions?: import("..").AiActionDefinition[];
|
|
9
|
+
aiCommandMenu?: import("..").AiCommandMenuConfig | false;
|
|
10
|
+
imageInsert?: import("..").ImageInsertConfig;
|
|
11
|
+
onInsertImage?: () => void;
|
|
12
|
+
mobileFullscreen?: boolean;
|
|
13
|
+
} & import("react").RefAttributes<SpatikaEditorHandle>>;
|
|
14
|
+
//# sourceMappingURL=SpatikaEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpatikaEditor.d.ts","sourceRoot":"","sources":["../../src/composites/SpatikaEditor.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,mBAAmB,EAAsB,MAAM,cAAc,CAAC;AAmB5E,eAAO,MAAM,aAAa;;;;;;;;;;;uDA2QzB,CAAC"}
|