@pixerate/editor-react 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +103 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +106 -28
- package/dist/index.js.map +1 -1
- package/package.json +13 -8
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,9 @@ function usePromptEditor({
|
|
|
86
86
|
onMilestoneRef.current = onMilestone;
|
|
87
87
|
const containerIdRef = (0, import_react.useRef)(containerId);
|
|
88
88
|
containerIdRef.current = containerId;
|
|
89
|
+
const templateKey = (0, import_react.useMemo)(() => {
|
|
90
|
+
return (templates || []).map((t) => `${t.id || t.name}:${t.latestVersion || ""}`).sort().join(",");
|
|
91
|
+
}, [templates]);
|
|
89
92
|
const resolvedColorMap = (0, import_react.useMemo)(() => {
|
|
90
93
|
if (userColorMap) return userColorMap;
|
|
91
94
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -93,7 +96,9 @@ function usePromptEditor({
|
|
|
93
96
|
map.set(t.name, { from: "#f43f5e", to: "#8b5cf6" });
|
|
94
97
|
});
|
|
95
98
|
return map.size > 0 ? map : DEFAULT_COLOR_MAP;
|
|
96
|
-
}, [userColorMap,
|
|
99
|
+
}, [userColorMap, templateKey]);
|
|
100
|
+
const StarterKitExt = import_starter_kit.default?.configure ? import_starter_kit.default : import_starter_kit.default?.default?.configure ? import_starter_kit.default?.default : import_starter_kit.default;
|
|
101
|
+
const PlaceholderExt = import_extension_placeholder.default?.configure ? import_extension_placeholder.default : import_extension_placeholder.default?.default?.configure ? import_extension_placeholder.default?.default : import_extension_placeholder.default;
|
|
97
102
|
const editor = (0, import_react2.useEditor)(
|
|
98
103
|
{
|
|
99
104
|
immediatelyRender: false,
|
|
@@ -101,11 +106,11 @@ function usePromptEditor({
|
|
|
101
106
|
enableInputRules: false,
|
|
102
107
|
editable: isEditing,
|
|
103
108
|
extensions: [
|
|
104
|
-
|
|
109
|
+
StarterKitExt.configure({
|
|
105
110
|
heading: false,
|
|
106
111
|
codeBlock: false
|
|
107
112
|
}),
|
|
108
|
-
|
|
113
|
+
PlaceholderExt.configure({ placeholder }),
|
|
109
114
|
import_editor.GradientText.configure({
|
|
110
115
|
templateColorMap: resolvedColorMap,
|
|
111
116
|
speed,
|
|
@@ -163,18 +168,56 @@ function usePromptEditor({
|
|
|
163
168
|
},
|
|
164
169
|
editorProps: {
|
|
165
170
|
attributes: {
|
|
166
|
-
class: className || "w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words"
|
|
171
|
+
class: className || "w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words",
|
|
172
|
+
role: "textbox",
|
|
173
|
+
"aria-label": "Prompt editor"
|
|
167
174
|
},
|
|
168
|
-
handleKeyDown
|
|
175
|
+
handleKeyDown(view, event) {
|
|
176
|
+
if (slashCommandActiveRef.current) {
|
|
177
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter") {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
if (event.key === "Escape") {
|
|
182
|
+
clearSlashCommandRef.current?.();
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
event.stopPropagation();
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (onKeyDownRef.current && onKeyDownRef.current(event)) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
169
191
|
if (event.key === "Escape" && onEscapeRef.current) {
|
|
170
192
|
onEscapeRef.current();
|
|
171
193
|
return true;
|
|
172
194
|
}
|
|
173
|
-
if (onKeyDownRef.current) {
|
|
174
|
-
return onKeyDownRef.current(event);
|
|
175
|
-
}
|
|
176
195
|
return false;
|
|
177
196
|
},
|
|
197
|
+
handlePaste: (view, event) => {
|
|
198
|
+
const text = event.clipboardData?.getData("text/plain");
|
|
199
|
+
if (!text) return false;
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
const schema = view.state.schema;
|
|
202
|
+
if (!schema) return false;
|
|
203
|
+
const blocks = text.replace(/\r\n?/g, "\n").split("\n");
|
|
204
|
+
const nodes = [];
|
|
205
|
+
blocks.forEach((line) => {
|
|
206
|
+
const nodeJson = { type: "paragraph" };
|
|
207
|
+
if (line.length > 0) {
|
|
208
|
+
nodeJson.content = [{ type: "text", text: line }];
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const node = import_model.Node.fromJSON(schema, nodeJson);
|
|
212
|
+
nodes.push(node);
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
const parsedSlice = import_model.Slice.maxOpen(import_model.Fragment.fromArray(nodes));
|
|
217
|
+
const tr = view.state.tr.replaceSelection(parsedSlice);
|
|
218
|
+
view.dispatch(tr);
|
|
219
|
+
return true;
|
|
220
|
+
},
|
|
178
221
|
clipboardTextParser: (text, context, _plain, view) => {
|
|
179
222
|
const schema = context?.doc?.type?.schema || context?.schema || view?.state?.schema;
|
|
180
223
|
if (!schema) return import_model.Slice.empty;
|
|
@@ -195,8 +238,13 @@ function usePromptEditor({
|
|
|
195
238
|
}
|
|
196
239
|
}
|
|
197
240
|
},
|
|
198
|
-
[
|
|
241
|
+
[resolvedColorMap, templateKey, isEditing, placeholder, speed]
|
|
199
242
|
);
|
|
243
|
+
(0, import_react.useEffect)(() => {
|
|
244
|
+
if (editor && !editor.isDestroyed) {
|
|
245
|
+
editor.setEditable(isEditing);
|
|
246
|
+
}
|
|
247
|
+
}, [editor, isEditing]);
|
|
200
248
|
return editor;
|
|
201
249
|
}
|
|
202
250
|
|
|
@@ -225,29 +273,57 @@ var EditorContent = ({
|
|
|
225
273
|
};
|
|
226
274
|
|
|
227
275
|
// src/components/BubbleMenu.tsx
|
|
228
|
-
var import_react5 = require("
|
|
229
|
-
var
|
|
276
|
+
var import_react5 = require("react");
|
|
277
|
+
var import_react_dom = require("react-dom");
|
|
278
|
+
var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
|
|
230
279
|
var BubbleMenu = ({
|
|
231
280
|
editor,
|
|
232
281
|
className = "flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1",
|
|
233
|
-
children
|
|
282
|
+
children,
|
|
283
|
+
tippyOptions = { duration: 100 },
|
|
284
|
+
shouldShow = null,
|
|
285
|
+
pluginKey = "bubbleMenu",
|
|
286
|
+
updateDelay
|
|
234
287
|
}) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
288
|
+
const menuEl = (0, import_react5.useRef)(null);
|
|
289
|
+
const [mounted, setMounted] = (0, import_react5.useState)(false);
|
|
290
|
+
if (!menuEl.current && typeof document !== "undefined") {
|
|
291
|
+
menuEl.current = document.createElement("div");
|
|
292
|
+
}
|
|
293
|
+
(0, import_react5.useEffect)(() => {
|
|
294
|
+
if (!editor || !menuEl.current || editor.isDestroyed) {
|
|
295
|
+
return;
|
|
243
296
|
}
|
|
244
|
-
|
|
297
|
+
const element = menuEl.current;
|
|
298
|
+
element.className = className;
|
|
299
|
+
const plugin = (0, import_extension_bubble_menu.BubbleMenuPlugin)({
|
|
300
|
+
editor,
|
|
301
|
+
element,
|
|
302
|
+
pluginKey,
|
|
303
|
+
tippyOptions,
|
|
304
|
+
shouldShow,
|
|
305
|
+
updateDelay
|
|
306
|
+
});
|
|
307
|
+
editor.registerPlugin(plugin);
|
|
308
|
+
setMounted(true);
|
|
309
|
+
return () => {
|
|
310
|
+
setMounted(false);
|
|
311
|
+
editor.unregisterPlugin(pluginKey);
|
|
312
|
+
if (element.parentNode) {
|
|
313
|
+
element.parentNode.removeChild(element);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}, [editor, className, pluginKey]);
|
|
317
|
+
if (!editor || !menuEl.current || !mounted) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
return (0, import_react_dom.createPortal)(children, menuEl.current);
|
|
245
321
|
};
|
|
246
322
|
|
|
247
323
|
// src/components/TemplateRenderer.tsx
|
|
248
324
|
var import_react6 = require("react");
|
|
249
325
|
var import_editor3 = require("@pixerate/editor");
|
|
250
|
-
var
|
|
326
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
251
327
|
var TemplateRenderer = ({
|
|
252
328
|
content,
|
|
253
329
|
templates = [],
|
|
@@ -264,7 +340,7 @@ var TemplateRenderer = ({
|
|
|
264
340
|
const tokens = (0, import_react6.useMemo)(() => {
|
|
265
341
|
return (0, import_editor3.tokenizePrompt)(displayText);
|
|
266
342
|
}, [displayText]);
|
|
267
|
-
return /* @__PURE__ */ (0,
|
|
343
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, children: tokens.map((token, index) => {
|
|
268
344
|
if (token.type === "template") {
|
|
269
345
|
const colors = templateColorMap?.get(token.value) || {
|
|
270
346
|
from: "#f43f5e",
|
|
@@ -274,7 +350,7 @@ var TemplateRenderer = ({
|
|
|
274
350
|
"--color-from": colors.from,
|
|
275
351
|
"--color-to": colors.to
|
|
276
352
|
};
|
|
277
|
-
return /* @__PURE__ */ (0,
|
|
353
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
278
354
|
"span",
|
|
279
355
|
{
|
|
280
356
|
style,
|
|
@@ -287,7 +363,7 @@ var TemplateRenderer = ({
|
|
|
287
363
|
);
|
|
288
364
|
}
|
|
289
365
|
if (token.type === "variable") {
|
|
290
|
-
return /* @__PURE__ */ (0,
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
291
367
|
"span",
|
|
292
368
|
{
|
|
293
369
|
onClick: () => onVariableClick?.(token.value),
|
|
@@ -299,7 +375,7 @@ var TemplateRenderer = ({
|
|
|
299
375
|
);
|
|
300
376
|
}
|
|
301
377
|
if (token.type === "instruction") {
|
|
302
|
-
return /* @__PURE__ */ (0,
|
|
378
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
303
379
|
"span",
|
|
304
380
|
{
|
|
305
381
|
className: "inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300",
|
|
@@ -309,7 +385,7 @@ var TemplateRenderer = ({
|
|
|
309
385
|
index
|
|
310
386
|
);
|
|
311
387
|
}
|
|
312
|
-
return /* @__PURE__ */ (0,
|
|
388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: token.value }, index);
|
|
313
389
|
}) });
|
|
314
390
|
};
|
|
315
391
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/hooks/usePromptEditor.ts","../src/hooks/useEditor.ts","../src/components/EditorContent.tsx","../src/components/BubbleMenu.tsx","../src/components/TemplateRenderer.tsx"],"sourcesContent":["export * from \"./hooks/usePromptEditor\";\nexport * from \"./hooks/useEditor\";\nexport * from \"./components/EditorContent\";\nexport * from \"./components/BubbleMenu\";\nexport * from \"./components/TemplateRenderer\";\n\n// Re-export core types for easy consumer access\nexport type {\n Template,\n TemplateVersion,\n Token,\n TokenType,\n ColorGradient,\n RichTextPresetOptions,\n} from \"@pixerate/editor\";\n","import { useMemo, useEffect, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Slice, Fragment, Node as ProsemirrorNode } from \"@tiptap/pm/model\";\nimport {\n Template,\n ColorGradient,\n GradientText,\n TemplateSuggestions,\n LoadingNode,\n plainTextToTipTapHtml,\n getEditorText,\n} from \"@pixerate/editor\";\n\nexport interface UsePromptEditorProps {\n /** The initial or controlled content of the editor as plain text */\n content: string;\n /** Callback fired when the editor content changes (returns plain text) */\n onContentChange: (text: string) => void;\n /** List of available templates to be suggested and highlighted */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether the editor is editable */\n isEditing?: boolean;\n /** Placeholder text for the editor */\n placeholder?: string;\n /** Additional CSS classes for the editor container */\n className?: string;\n /** Callback fired when the Escape key is pressed */\n onEscape?: () => void;\n /** Custom keydown handler. Return true to prevent default behavior */\n onKeyDown?: (event: KeyboardEvent) => boolean;\n /** Callback fired when text selection changes */\n onSelectionChange?: (selection: { from: number; to: number } | null) => void;\n /** Callback fired when a slash command (e.g., /template) matches */\n onSlashCommandMatch?: (\n match: RegExpMatchArray,\n from: number,\n to: number,\n coords: { top: number; left: number },\n ) => void;\n /** Callback to dismiss/clear the active slash command */\n clearSlashCommand?: () => void;\n /** Whether a slash command menu is currently active */\n slashCommandActive?: boolean;\n /** Callback fired when certain milestones are reached (e.g., used_variable, used_magic) */\n onMilestone?: (milestone: string) => void;\n /** DOM element ID of the container, used for positioning popovers */\n containerId?: string;\n /** Variable names originating from structured JSON output steps */\n jsonVariables?: string[];\n /** Animation speed for gradient decorations */\n speed?: number;\n}\n\nconst DEFAULT_TEMPLATES: Template[] = [];\nconst DEFAULT_COLOR_MAP = new Map<string, ColorGradient>([\n [\"default\", { from: \"#f43f5e\", to: \"#8b5cf6\" }],\n]);\n\nexport function usePromptEditor({\n content,\n onContentChange,\n templates = DEFAULT_TEMPLATES,\n templateColorMap: userColorMap,\n isEditing = true,\n placeholder = \"Write a prompt, use {{template}}, {variable}, or /command...\",\n className,\n onEscape,\n onKeyDown,\n onSelectionChange,\n onSlashCommandMatch,\n clearSlashCommand,\n slashCommandActive = false,\n onMilestone,\n containerId = \"editor-container\",\n jsonVariables,\n speed = 2,\n}: UsePromptEditorProps) {\n const slashCommandActiveRef = useRef(slashCommandActive);\n slashCommandActiveRef.current = slashCommandActive;\n\n const clearSlashCommandRef = useRef(clearSlashCommand);\n clearSlashCommandRef.current = clearSlashCommand;\n\n const onEscapeRef = useRef(onEscape);\n onEscapeRef.current = onEscape;\n\n const onKeyDownRef = useRef(onKeyDown);\n onKeyDownRef.current = onKeyDown;\n\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n const onSlashCommandMatchRef = useRef(onSlashCommandMatch);\n onSlashCommandMatchRef.current = onSlashCommandMatch;\n\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n const onMilestoneRef = useRef(onMilestone);\n onMilestoneRef.current = onMilestone;\n\n const containerIdRef = useRef(containerId);\n containerIdRef.current = containerId;\n\n const resolvedColorMap = useMemo(() => {\n if (userColorMap) return userColorMap;\n const map = new Map<string, ColorGradient>();\n templates.forEach((t) => {\n map.set(t.name, { from: \"#f43f5e\", to: \"#8b5cf6\" });\n });\n return map.size > 0 ? map : DEFAULT_COLOR_MAP;\n }, [userColorMap, templates]);\n\n const editor = useEditor(\n {\n immediatelyRender: false,\n enablePasteRules: false,\n enableInputRules: false,\n editable: isEditing,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n }),\n Placeholder.configure({ placeholder }),\n GradientText.configure({\n templateColorMap: resolvedColorMap,\n speed,\n jsonVariables,\n }),\n TemplateSuggestions.configure({\n templates,\n }),\n LoadingNode,\n ],\n content: plainTextToTipTapHtml(content),\n onUpdate: ({ editor: ed }) => {\n const plainText = getEditorText(ed);\n onContentChangeRef.current?.(plainText);\n\n if (onMilestoneRef.current) {\n if (plainText.match(/(?<!\\{)\\{([^{}]+)\\}(?!\\})/g)) {\n onMilestoneRef.current(\"used_variable\");\n }\n if (plainText.match(/__([^_]+)__/g)) {\n onMilestoneRef.current(\"used_magic\");\n }\n }\n\n const { state, view } = ed;\n const { selection } = state;\n const { $from, from, to } = selection;\n\n onSelectionChangeRef.current?.({ from, to });\n\n if (onSlashCommandMatchRef.current && clearSlashCommandRef.current) {\n const textBeforeCursor = $from?.parent?.textBetween\n ? $from.parent.textBetween(0, $from.parentOffset)\n : \"\";\n const slashMatch = textBeforeCursor.match(\n /(?:^|\\s)\\/([a-zA-Z0-9_-]*)$/,\n );\n\n if (slashMatch) {\n const coords = view.coordsAtPos($from.pos);\n const containerEl =\n document.getElementById(containerIdRef.current) ||\n document.getElementById(\"editor-container\") ||\n (view.dom.closest?.(\".relative\") as HTMLElement | null);\n const containerCoords = containerEl?.getBoundingClientRect();\n\n if (containerCoords) {\n onSlashCommandMatchRef.current(\n slashMatch,\n $from.pos -\n slashMatch[0].length +\n (slashMatch[0].startsWith(\" \") ? 1 : 0),\n $from.pos,\n {\n top: coords.bottom - containerCoords.top + 5,\n left: coords.left - containerCoords.left,\n },\n );\n }\n } else {\n clearSlashCommandRef.current();\n }\n }\n },\n onSelectionUpdate: ({ editor: ed }) => {\n const { from, to } = ed.state.selection;\n onSelectionChangeRef.current?.({ from, to });\n },\n editorProps: {\n attributes: {\n class:\n className ||\n \"w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words\",\n },\n handleKeyDown: (view, event) => {\n if (event.key === \"Escape\" && onEscapeRef.current) {\n onEscapeRef.current();\n return true;\n }\n if (onKeyDownRef.current) {\n return onKeyDownRef.current(event);\n }\n return false;\n },\n clipboardTextParser: (text, context, _plain, view) => {\n const schema =\n (context as any)?.doc?.type?.schema ||\n (context as any)?.schema ||\n view?.state?.schema;\n\n if (!schema) return Slice.empty;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n return Slice.maxOpen(Fragment.fromArray(nodes));\n },\n },\n },\n [templates, resolvedColorMap, isEditing, placeholder, speed],\n );\n\n return editor;\n}\n","import { useEditor as useTipTapEditor, UseEditorOptions } from \"@tiptap/react\";\nimport { createRichTextPreset, RichTextPresetOptions } from \"@pixerate/editor\";\n\nexport interface UseRichEditorProps extends UseEditorOptions {\n richTextOptions?: RichTextPresetOptions;\n}\n\n/**\n * Hook to initialize a TipTap rich-text editor instance with pre-configured extensions.\n */\nexport function useEditor(options: UseRichEditorProps = {}) {\n const { richTextOptions, extensions = [], ...editorOptions } = options;\n\n const preset = createRichTextPreset(richTextOptions);\n const combinedExtensions = [...preset, ...extensions];\n\n return useTipTapEditor({\n extensions: combinedExtensions,\n ...editorOptions,\n });\n}\n","import React from \"react\";\nimport {\n EditorContent as TipTapEditorContent,\n EditorContentProps as TipTapEditorContentProps,\n} from \"@tiptap/react\";\n\nexport interface EditorContentProps extends TipTapEditorContentProps {\n className?: string;\n}\n\nexport const EditorContent: React.FC<EditorContentProps> = ({\n className,\n editor,\n ...props\n}) => {\n return (\n <div className={className}>\n <TipTapEditorContent editor={editor} {...props} />\n </div>\n );\n};\n","import React from \"react\";\nimport { BubbleMenu as TipTapBubbleMenu } from \"@tiptap/react\";\nimport type { Editor } from \"@tiptap/core\";\n\nexport interface BubbleMenuProps {\n editor: Editor | null;\n className?: string;\n children: React.ReactNode;\n}\n\nexport const BubbleMenu: React.FC<BubbleMenuProps> = ({\n editor,\n className = \"flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1\",\n children,\n}) => {\n if (!editor) return null;\n\n return (\n <TipTapBubbleMenu\n editor={editor}\n tippyOptions={{ duration: 100 }}\n className={className}\n >\n {children}\n </TipTapBubbleMenu>\n );\n};\n","import React, { useMemo } from \"react\";\nimport {\n tokenizePrompt,\n resolveTemplates,\n Template,\n ColorGradient,\n} from \"@pixerate/editor\";\n\nexport interface TemplateRendererProps {\n /** Raw prompt or text string to render */\n content: string;\n /** Available templates for recursive resolution and custom coloring */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether to resolve nested templates recursively before displaying */\n resolve?: boolean;\n /** Custom class name for the wrapper */\n className?: string;\n /** Click callback for template badges */\n onTemplateClick?: (templateName: string) => void;\n /** Click callback for variable badges */\n onVariableClick?: (variableName: string) => void;\n}\n\nexport const TemplateRenderer: React.FC<TemplateRendererProps> = ({\n content,\n templates = [],\n templateColorMap,\n resolve = false,\n className = \"font-mono text-sm leading-relaxed whitespace-pre-wrap break-words\",\n onTemplateClick,\n onVariableClick,\n}) => {\n const displayText = useMemo(() => {\n if (!resolve || templates.length === 0) return content;\n return resolveTemplates(content, templates);\n }, [content, templates, resolve]);\n\n const tokens = useMemo(() => {\n return tokenizePrompt(displayText);\n }, [displayText]);\n\n return (\n <div className={className}>\n {tokens.map((token, index) => {\n if (token.type === \"template\") {\n const colors = templateColorMap?.get(token.value) || {\n from: \"#f43f5e\",\n to: \"#8b5cf6\",\n };\n const style = {\n \"--color-from\": colors.from,\n \"--color-to\": colors.to,\n } as React.CSSProperties;\n\n return (\n <span\n key={index}\n style={style}\n onClick={() => onTemplateClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-semibold rounded-md border border-[var(--color-to)]/40 bg-gradient-to-r from-[var(--color-from)]/15 to-[var(--color-to)]/15 text-[var(--color-to)] hover:brightness-110 cursor-pointer transition-all\"\n title={`Template: ${token.value}`}\n >\n {`{{${token.value}}}`}\n </span>\n );\n }\n\n if (token.type === \"variable\") {\n return (\n <span\n key={index}\n onClick={() => onVariableClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-violet-400/40 bg-violet-400/15 text-violet-600 dark:text-violet-300 hover:brightness-110 cursor-pointer transition-all\"\n title={`Variable: ${token.value}`}\n >\n {`{${token.value}}`}\n </span>\n );\n }\n\n if (token.type === \"instruction\") {\n return (\n <span\n key={index}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300\"\n title={`Instruction: ${token.value}`}\n >\n {`__${token.value}__`}\n </span>\n );\n }\n\n return <span key={index}>{token.value}</span>;\n })}\n </div>\n );\n};\n"],"mappings":"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","names":["useEditor","import_react","StarterKit","Placeholder","ProsemirrorNode","import_react","import_editor","useEditor","useTipTapEditor","import_react","TipTapEditorContent","import_react","import_jsx_runtime","TipTapBubbleMenu","import_react","import_editor","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hooks/usePromptEditor.ts","../src/hooks/useEditor.ts","../src/components/EditorContent.tsx","../src/components/BubbleMenu.tsx","../src/components/TemplateRenderer.tsx"],"sourcesContent":["export * from \"./hooks/usePromptEditor\";\nexport * from \"./hooks/useEditor\";\nexport * from \"./components/EditorContent\";\nexport * from \"./components/BubbleMenu\";\nexport * from \"./components/TemplateRenderer\";\n\n// Re-export core types for easy consumer access\nexport type {\n Template,\n TemplateVersion,\n Token,\n TokenType,\n ColorGradient,\n RichTextPresetOptions,\n} from \"@pixerate/editor\";\n","import { useMemo, useEffect, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Slice, Fragment, Node as ProsemirrorNode } from \"@tiptap/pm/model\";\nimport {\n Template,\n ColorGradient,\n GradientText,\n TemplateSuggestions,\n LoadingNode,\n plainTextToTipTapHtml,\n getEditorText,\n} from \"@pixerate/editor\";\n\nexport interface UsePromptEditorProps {\n /** The initial or controlled content of the editor as plain text */\n content: string;\n /** Callback fired when the editor content changes (returns plain text) */\n onContentChange: (text: string) => void;\n /** List of available templates to be suggested and highlighted */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether the editor is editable */\n isEditing?: boolean;\n /** Placeholder text for the editor */\n placeholder?: string;\n /** Additional CSS classes for the editor container */\n className?: string;\n /** Callback fired when the Escape key is pressed */\n onEscape?: () => void;\n /** Custom keydown handler. Return true to prevent default behavior */\n onKeyDown?: (event: KeyboardEvent) => boolean;\n /** Callback fired when text selection changes */\n onSelectionChange?: (selection: { from: number; to: number } | null) => void;\n /** Callback fired when a slash command (e.g., /template) matches */\n onSlashCommandMatch?: (\n match: RegExpMatchArray,\n from: number,\n to: number,\n coords: { top: number; left: number },\n ) => void;\n /** Callback to dismiss/clear the active slash command */\n clearSlashCommand?: () => void;\n /** Whether a slash command menu is currently active */\n slashCommandActive?: boolean;\n /** Callback fired when certain milestones are reached (e.g., used_variable, used_magic) */\n onMilestone?: (milestone: string) => void;\n /** DOM element ID of the container, used for positioning popovers */\n containerId?: string;\n /** Variable names originating from structured JSON output steps */\n jsonVariables?: string[];\n /** Animation speed for gradient decorations */\n speed?: number;\n}\n\nconst DEFAULT_TEMPLATES: Template[] = [];\nconst DEFAULT_COLOR_MAP = new Map<string, ColorGradient>([\n [\"default\", { from: \"#f43f5e\", to: \"#8b5cf6\" }],\n]);\n\nexport function usePromptEditor({\n content,\n onContentChange,\n templates = DEFAULT_TEMPLATES,\n templateColorMap: userColorMap,\n isEditing = true,\n placeholder = \"Write a prompt, use {{template}}, {variable}, or /command...\",\n className,\n onEscape,\n onKeyDown,\n onSelectionChange,\n onSlashCommandMatch,\n clearSlashCommand,\n slashCommandActive = false,\n onMilestone,\n containerId = \"editor-container\",\n jsonVariables,\n speed = 2,\n}: UsePromptEditorProps) {\n const slashCommandActiveRef = useRef(slashCommandActive);\n slashCommandActiveRef.current = slashCommandActive;\n\n const clearSlashCommandRef = useRef(clearSlashCommand);\n clearSlashCommandRef.current = clearSlashCommand;\n\n const onEscapeRef = useRef(onEscape);\n onEscapeRef.current = onEscape;\n\n const onKeyDownRef = useRef(onKeyDown);\n onKeyDownRef.current = onKeyDown;\n\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n const onSlashCommandMatchRef = useRef(onSlashCommandMatch);\n onSlashCommandMatchRef.current = onSlashCommandMatch;\n\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n const onMilestoneRef = useRef(onMilestone);\n onMilestoneRef.current = onMilestone;\n\n const containerIdRef = useRef(containerId);\n containerIdRef.current = containerId;\n\n const templateKey = useMemo(() => {\n return (templates || [])\n .map((t) => `${t.id || t.name}:${(t as any).latestVersion || \"\"}`)\n .sort()\n .join(\",\");\n }, [templates]);\n\n const resolvedColorMap = useMemo(() => {\n if (userColorMap) return userColorMap;\n const map = new Map<string, ColorGradient>();\n templates.forEach((t) => {\n map.set(t.name, { from: \"#f43f5e\", to: \"#8b5cf6\" });\n });\n return map.size > 0 ? map : DEFAULT_COLOR_MAP;\n }, [userColorMap, templateKey]);\n\n const StarterKitExt =\n (StarterKit as any)?.configure\n ? StarterKit\n : (StarterKit as any)?.default?.configure\n ? (StarterKit as any)?.default\n : StarterKit;\n\n const PlaceholderExt =\n (Placeholder as any)?.configure\n ? Placeholder\n : (Placeholder as any)?.default?.configure\n ? (Placeholder as any)?.default\n : Placeholder;\n\n const editor = useEditor(\n {\n immediatelyRender: false,\n enablePasteRules: false,\n enableInputRules: false,\n editable: isEditing,\n extensions: [\n StarterKitExt.configure({\n heading: false,\n codeBlock: false,\n }),\n PlaceholderExt.configure({ placeholder }),\n GradientText.configure({\n templateColorMap: resolvedColorMap,\n speed,\n jsonVariables,\n }),\n TemplateSuggestions.configure({\n templates,\n }),\n LoadingNode,\n ],\n content: plainTextToTipTapHtml(content),\n onUpdate: ({ editor: ed }) => {\n const plainText = getEditorText(ed);\n onContentChangeRef.current?.(plainText);\n\n if (onMilestoneRef.current) {\n if (plainText.match(/(?<!\\{)\\{([^{}]+)\\}(?!\\})/g)) {\n onMilestoneRef.current(\"used_variable\");\n }\n if (plainText.match(/__([^_]+)__/g)) {\n onMilestoneRef.current(\"used_magic\");\n }\n }\n\n const { state, view } = ed;\n const { selection } = state;\n const { $from, from, to } = selection;\n\n onSelectionChangeRef.current?.({ from, to });\n\n if (onSlashCommandMatchRef.current && clearSlashCommandRef.current) {\n const textBeforeCursor = $from?.parent?.textBetween\n ? $from.parent.textBetween(0, $from.parentOffset)\n : \"\";\n const slashMatch = textBeforeCursor.match(\n /(?:^|\\s)\\/([a-zA-Z0-9_-]*)$/,\n );\n\n if (slashMatch) {\n const coords = view.coordsAtPos($from.pos);\n const containerEl =\n document.getElementById(containerIdRef.current) ||\n document.getElementById(\"editor-container\") ||\n (view.dom.closest?.(\".relative\") as HTMLElement | null);\n const containerCoords = containerEl?.getBoundingClientRect();\n\n if (containerCoords) {\n onSlashCommandMatchRef.current(\n slashMatch,\n $from.pos -\n slashMatch[0].length +\n (slashMatch[0].startsWith(\" \") ? 1 : 0),\n $from.pos,\n {\n top: coords.bottom - containerCoords.top + 5,\n left: coords.left - containerCoords.left,\n },\n );\n }\n } else {\n clearSlashCommandRef.current();\n }\n }\n },\n onSelectionUpdate: ({ editor: ed }) => {\n const { from, to } = ed.state.selection;\n onSelectionChangeRef.current?.({ from, to });\n },\n editorProps: {\n attributes: {\n class:\n className ||\n \"w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words\",\n role: \"textbox\",\n \"aria-label\": \"Prompt editor\",\n },\n handleKeyDown(view, event) {\n if (slashCommandActiveRef.current) {\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"Enter\"\n ) {\n event.preventDefault();\n return true;\n }\n if (event.key === \"Escape\") {\n clearSlashCommandRef.current?.();\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n }\n if (onKeyDownRef.current && onKeyDownRef.current(event as KeyboardEvent)) {\n return true;\n }\n if (event.key === \"Escape\" && onEscapeRef.current) {\n onEscapeRef.current();\n return true;\n }\n return false;\n },\n handlePaste: (view, event) => {\n const text = event.clipboardData?.getData(\"text/plain\");\n if (!text) return false;\n event.preventDefault();\n const schema = view.state.schema;\n if (!schema) return false;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n const parsedSlice = Slice.maxOpen(Fragment.fromArray(nodes));\n const tr = view.state.tr.replaceSelection(parsedSlice);\n view.dispatch(tr);\n return true;\n },\n clipboardTextParser: (text, context, _plain, view) => {\n const schema =\n (context as any)?.doc?.type?.schema ||\n (context as any)?.schema ||\n view?.state?.schema;\n\n if (!schema) return Slice.empty;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n return Slice.maxOpen(Fragment.fromArray(nodes));\n },\n },\n },\n [resolvedColorMap, templateKey, isEditing, placeholder, speed],\n );\n\n useEffect(() => {\n if (editor && !editor.isDestroyed) {\n editor.setEditable(isEditing);\n }\n }, [editor, isEditing]);\n\n return editor;\n}\n","import { useEditor as useTipTapEditor, UseEditorOptions } from \"@tiptap/react\";\nimport { createRichTextPreset, RichTextPresetOptions } from \"@pixerate/editor\";\n\nexport interface UseRichEditorProps extends UseEditorOptions {\n richTextOptions?: RichTextPresetOptions;\n}\n\n/**\n * Hook to initialize a TipTap rich-text editor instance with pre-configured extensions.\n */\nexport function useEditor(options: UseRichEditorProps = {}) {\n const { richTextOptions, extensions = [], ...editorOptions } = options;\n\n const preset = createRichTextPreset(richTextOptions);\n const combinedExtensions = [...preset, ...extensions];\n\n return useTipTapEditor({\n extensions: combinedExtensions,\n ...editorOptions,\n });\n}\n","import React from \"react\";\nimport {\n EditorContent as TipTapEditorContent,\n EditorContentProps as TipTapEditorContentProps,\n} from \"@tiptap/react\";\n\nexport interface EditorContentProps extends TipTapEditorContentProps {\n className?: string;\n}\n\nexport const EditorContent: React.FC<EditorContentProps> = ({\n className,\n editor,\n ...props\n}) => {\n return (\n <div className={className}>\n <TipTapEditorContent editor={editor} {...props} />\n </div>\n );\n};\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n} from \"@tiptap/extension-bubble-menu\";\nimport type { Editor } from \"@tiptap/core\";\n\nexport interface BubbleMenuProps {\n editor: Editor | null;\n className?: string;\n children: React.ReactNode;\n tippyOptions?: BubbleMenuPluginProps[\"tippyOptions\"];\n shouldShow?: BubbleMenuPluginProps[\"shouldShow\"];\n pluginKey?: BubbleMenuPluginProps[\"pluginKey\"];\n updateDelay?: BubbleMenuPluginProps[\"updateDelay\"];\n}\n\nexport const BubbleMenu: React.FC<BubbleMenuProps> = ({\n editor,\n className = \"flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1\",\n children,\n tippyOptions = { duration: 100 },\n shouldShow = null,\n pluginKey = \"bubbleMenu\",\n updateDelay,\n}) => {\n const menuEl = useRef<HTMLDivElement | null>(null);\n const [mounted, setMounted] = useState(false);\n\n if (!menuEl.current && typeof document !== \"undefined\") {\n menuEl.current = document.createElement(\"div\");\n }\n\n useEffect(() => {\n if (!editor || !menuEl.current || editor.isDestroyed) {\n return;\n }\n\n const element = menuEl.current;\n element.className = className;\n\n const plugin = BubbleMenuPlugin({\n editor,\n element,\n pluginKey,\n tippyOptions,\n shouldShow,\n updateDelay,\n });\n\n editor.registerPlugin(plugin);\n setMounted(true);\n\n return () => {\n setMounted(false);\n editor.unregisterPlugin(pluginKey);\n if (element.parentNode) {\n element.parentNode.removeChild(element);\n }\n };\n }, [editor, className, pluginKey]);\n\n if (!editor || !menuEl.current || !mounted) {\n return null;\n }\n\n return createPortal(children, menuEl.current);\n};\n","import React, { useMemo } from \"react\";\nimport {\n tokenizePrompt,\n resolveTemplates,\n Template,\n ColorGradient,\n} from \"@pixerate/editor\";\n\nexport interface TemplateRendererProps {\n /** Raw prompt or text string to render */\n content: string;\n /** Available templates for recursive resolution and custom coloring */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether to resolve nested templates recursively before displaying */\n resolve?: boolean;\n /** Custom class name for the wrapper */\n className?: string;\n /** Click callback for template badges */\n onTemplateClick?: (templateName: string) => void;\n /** Click callback for variable badges */\n onVariableClick?: (variableName: string) => void;\n}\n\nexport const TemplateRenderer: React.FC<TemplateRendererProps> = ({\n content,\n templates = [],\n templateColorMap,\n resolve = false,\n className = \"font-mono text-sm leading-relaxed whitespace-pre-wrap break-words\",\n onTemplateClick,\n onVariableClick,\n}) => {\n const displayText = useMemo(() => {\n if (!resolve || templates.length === 0) return content;\n return resolveTemplates(content, templates);\n }, [content, templates, resolve]);\n\n const tokens = useMemo(() => {\n return tokenizePrompt(displayText);\n }, [displayText]);\n\n return (\n <div className={className}>\n {tokens.map((token, index) => {\n if (token.type === \"template\") {\n const colors = templateColorMap?.get(token.value) || {\n from: \"#f43f5e\",\n to: \"#8b5cf6\",\n };\n const style = {\n \"--color-from\": colors.from,\n \"--color-to\": colors.to,\n } as React.CSSProperties;\n\n return (\n <span\n key={index}\n style={style}\n onClick={() => onTemplateClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-semibold rounded-md border border-[var(--color-to)]/40 bg-gradient-to-r from-[var(--color-from)]/15 to-[var(--color-to)]/15 text-[var(--color-to)] hover:brightness-110 cursor-pointer transition-all\"\n title={`Template: ${token.value}`}\n >\n {`{{${token.value}}}`}\n </span>\n );\n }\n\n if (token.type === \"variable\") {\n return (\n <span\n key={index}\n onClick={() => onVariableClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-violet-400/40 bg-violet-400/15 text-violet-600 dark:text-violet-300 hover:brightness-110 cursor-pointer transition-all\"\n title={`Variable: ${token.value}`}\n >\n {`{${token.value}}`}\n </span>\n );\n }\n\n if (token.type === \"instruction\") {\n return (\n <span\n key={index}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300\"\n title={`Instruction: ${token.value}`}\n >\n {`__${token.value}__`}\n </span>\n );\n }\n\n return <span key={index}>{token.value}</span>;\n })}\n </div>\n );\n};\n"],"mappings":"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","names":["useEditor","import_react","StarterKit","Placeholder","ProsemirrorNode","import_react","import_editor","useEditor","useTipTapEditor","import_react","TipTapEditorContent","import_react","import_react","import_editor","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -4,6 +4,7 @@ import { Template, ColorGradient, RichTextPresetOptions } from '@pixerate/editor
|
|
|
4
4
|
export { ColorGradient, RichTextPresetOptions, Template, TemplateVersion, Token, TokenType } from '@pixerate/editor';
|
|
5
5
|
import { UseEditorOptions, EditorContentProps as EditorContentProps$1 } from '@tiptap/react';
|
|
6
6
|
import React from 'react';
|
|
7
|
+
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
7
8
|
|
|
8
9
|
interface UsePromptEditorProps {
|
|
9
10
|
/** The initial or controlled content of the editor as plain text */
|
|
@@ -66,6 +67,10 @@ interface BubbleMenuProps {
|
|
|
66
67
|
editor: Editor | null;
|
|
67
68
|
className?: string;
|
|
68
69
|
children: React.ReactNode;
|
|
70
|
+
tippyOptions?: BubbleMenuPluginProps["tippyOptions"];
|
|
71
|
+
shouldShow?: BubbleMenuPluginProps["shouldShow"];
|
|
72
|
+
pluginKey?: BubbleMenuPluginProps["pluginKey"];
|
|
73
|
+
updateDelay?: BubbleMenuPluginProps["updateDelay"];
|
|
69
74
|
}
|
|
70
75
|
declare const BubbleMenu: React.FC<BubbleMenuProps>;
|
|
71
76
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Template, ColorGradient, RichTextPresetOptions } from '@pixerate/editor
|
|
|
4
4
|
export { ColorGradient, RichTextPresetOptions, Template, TemplateVersion, Token, TokenType } from '@pixerate/editor';
|
|
5
5
|
import { UseEditorOptions, EditorContentProps as EditorContentProps$1 } from '@tiptap/react';
|
|
6
6
|
import React from 'react';
|
|
7
|
+
import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
|
|
7
8
|
|
|
8
9
|
interface UsePromptEditorProps {
|
|
9
10
|
/** The initial or controlled content of the editor as plain text */
|
|
@@ -66,6 +67,10 @@ interface BubbleMenuProps {
|
|
|
66
67
|
editor: Editor | null;
|
|
67
68
|
className?: string;
|
|
68
69
|
children: React.ReactNode;
|
|
70
|
+
tippyOptions?: BubbleMenuPluginProps["tippyOptions"];
|
|
71
|
+
shouldShow?: BubbleMenuPluginProps["shouldShow"];
|
|
72
|
+
pluginKey?: BubbleMenuPluginProps["pluginKey"];
|
|
73
|
+
updateDelay?: BubbleMenuPluginProps["updateDelay"];
|
|
69
74
|
}
|
|
70
75
|
declare const BubbleMenu: React.FC<BubbleMenuProps>;
|
|
71
76
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/hooks/usePromptEditor.ts
|
|
2
|
-
import { useMemo, useRef } from "react";
|
|
2
|
+
import { useMemo, useEffect, useRef } from "react";
|
|
3
3
|
import { useEditor } from "@tiptap/react";
|
|
4
4
|
import StarterKit from "@tiptap/starter-kit";
|
|
5
5
|
import Placeholder from "@tiptap/extension-placeholder";
|
|
@@ -52,6 +52,9 @@ function usePromptEditor({
|
|
|
52
52
|
onMilestoneRef.current = onMilestone;
|
|
53
53
|
const containerIdRef = useRef(containerId);
|
|
54
54
|
containerIdRef.current = containerId;
|
|
55
|
+
const templateKey = useMemo(() => {
|
|
56
|
+
return (templates || []).map((t) => `${t.id || t.name}:${t.latestVersion || ""}`).sort().join(",");
|
|
57
|
+
}, [templates]);
|
|
55
58
|
const resolvedColorMap = useMemo(() => {
|
|
56
59
|
if (userColorMap) return userColorMap;
|
|
57
60
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -59,7 +62,9 @@ function usePromptEditor({
|
|
|
59
62
|
map.set(t.name, { from: "#f43f5e", to: "#8b5cf6" });
|
|
60
63
|
});
|
|
61
64
|
return map.size > 0 ? map : DEFAULT_COLOR_MAP;
|
|
62
|
-
}, [userColorMap,
|
|
65
|
+
}, [userColorMap, templateKey]);
|
|
66
|
+
const StarterKitExt = StarterKit?.configure ? StarterKit : StarterKit?.default?.configure ? StarterKit?.default : StarterKit;
|
|
67
|
+
const PlaceholderExt = Placeholder?.configure ? Placeholder : Placeholder?.default?.configure ? Placeholder?.default : Placeholder;
|
|
63
68
|
const editor = useEditor(
|
|
64
69
|
{
|
|
65
70
|
immediatelyRender: false,
|
|
@@ -67,11 +72,11 @@ function usePromptEditor({
|
|
|
67
72
|
enableInputRules: false,
|
|
68
73
|
editable: isEditing,
|
|
69
74
|
extensions: [
|
|
70
|
-
|
|
75
|
+
StarterKitExt.configure({
|
|
71
76
|
heading: false,
|
|
72
77
|
codeBlock: false
|
|
73
78
|
}),
|
|
74
|
-
|
|
79
|
+
PlaceholderExt.configure({ placeholder }),
|
|
75
80
|
GradientText.configure({
|
|
76
81
|
templateColorMap: resolvedColorMap,
|
|
77
82
|
speed,
|
|
@@ -129,18 +134,56 @@ function usePromptEditor({
|
|
|
129
134
|
},
|
|
130
135
|
editorProps: {
|
|
131
136
|
attributes: {
|
|
132
|
-
class: className || "w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words"
|
|
137
|
+
class: className || "w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words",
|
|
138
|
+
role: "textbox",
|
|
139
|
+
"aria-label": "Prompt editor"
|
|
133
140
|
},
|
|
134
|
-
handleKeyDown
|
|
141
|
+
handleKeyDown(view, event) {
|
|
142
|
+
if (slashCommandActiveRef.current) {
|
|
143
|
+
if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter") {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
if (event.key === "Escape") {
|
|
148
|
+
clearSlashCommandRef.current?.();
|
|
149
|
+
event.preventDefault();
|
|
150
|
+
event.stopPropagation();
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (onKeyDownRef.current && onKeyDownRef.current(event)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
135
157
|
if (event.key === "Escape" && onEscapeRef.current) {
|
|
136
158
|
onEscapeRef.current();
|
|
137
159
|
return true;
|
|
138
160
|
}
|
|
139
|
-
if (onKeyDownRef.current) {
|
|
140
|
-
return onKeyDownRef.current(event);
|
|
141
|
-
}
|
|
142
161
|
return false;
|
|
143
162
|
},
|
|
163
|
+
handlePaste: (view, event) => {
|
|
164
|
+
const text = event.clipboardData?.getData("text/plain");
|
|
165
|
+
if (!text) return false;
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
const schema = view.state.schema;
|
|
168
|
+
if (!schema) return false;
|
|
169
|
+
const blocks = text.replace(/\r\n?/g, "\n").split("\n");
|
|
170
|
+
const nodes = [];
|
|
171
|
+
blocks.forEach((line) => {
|
|
172
|
+
const nodeJson = { type: "paragraph" };
|
|
173
|
+
if (line.length > 0) {
|
|
174
|
+
nodeJson.content = [{ type: "text", text: line }];
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const node = ProsemirrorNode.fromJSON(schema, nodeJson);
|
|
178
|
+
nodes.push(node);
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
const parsedSlice = Slice.maxOpen(Fragment.fromArray(nodes));
|
|
183
|
+
const tr = view.state.tr.replaceSelection(parsedSlice);
|
|
184
|
+
view.dispatch(tr);
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
144
187
|
clipboardTextParser: (text, context, _plain, view) => {
|
|
145
188
|
const schema = context?.doc?.type?.schema || context?.schema || view?.state?.schema;
|
|
146
189
|
if (!schema) return Slice.empty;
|
|
@@ -161,8 +204,13 @@ function usePromptEditor({
|
|
|
161
204
|
}
|
|
162
205
|
}
|
|
163
206
|
},
|
|
164
|
-
[
|
|
207
|
+
[resolvedColorMap, templateKey, isEditing, placeholder, speed]
|
|
165
208
|
);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
if (editor && !editor.isDestroyed) {
|
|
211
|
+
editor.setEditable(isEditing);
|
|
212
|
+
}
|
|
213
|
+
}, [editor, isEditing]);
|
|
166
214
|
return editor;
|
|
167
215
|
}
|
|
168
216
|
|
|
@@ -193,23 +241,53 @@ var EditorContent = ({
|
|
|
193
241
|
};
|
|
194
242
|
|
|
195
243
|
// src/components/BubbleMenu.tsx
|
|
196
|
-
import {
|
|
197
|
-
import {
|
|
244
|
+
import { useEffect as useEffect2, useRef as useRef2, useState } from "react";
|
|
245
|
+
import { createPortal } from "react-dom";
|
|
246
|
+
import {
|
|
247
|
+
BubbleMenuPlugin
|
|
248
|
+
} from "@tiptap/extension-bubble-menu";
|
|
198
249
|
var BubbleMenu = ({
|
|
199
250
|
editor,
|
|
200
251
|
className = "flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1",
|
|
201
|
-
children
|
|
252
|
+
children,
|
|
253
|
+
tippyOptions = { duration: 100 },
|
|
254
|
+
shouldShow = null,
|
|
255
|
+
pluginKey = "bubbleMenu",
|
|
256
|
+
updateDelay
|
|
202
257
|
}) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
258
|
+
const menuEl = useRef2(null);
|
|
259
|
+
const [mounted, setMounted] = useState(false);
|
|
260
|
+
if (!menuEl.current && typeof document !== "undefined") {
|
|
261
|
+
menuEl.current = document.createElement("div");
|
|
262
|
+
}
|
|
263
|
+
useEffect2(() => {
|
|
264
|
+
if (!editor || !menuEl.current || editor.isDestroyed) {
|
|
265
|
+
return;
|
|
211
266
|
}
|
|
212
|
-
|
|
267
|
+
const element = menuEl.current;
|
|
268
|
+
element.className = className;
|
|
269
|
+
const plugin = BubbleMenuPlugin({
|
|
270
|
+
editor,
|
|
271
|
+
element,
|
|
272
|
+
pluginKey,
|
|
273
|
+
tippyOptions,
|
|
274
|
+
shouldShow,
|
|
275
|
+
updateDelay
|
|
276
|
+
});
|
|
277
|
+
editor.registerPlugin(plugin);
|
|
278
|
+
setMounted(true);
|
|
279
|
+
return () => {
|
|
280
|
+
setMounted(false);
|
|
281
|
+
editor.unregisterPlugin(pluginKey);
|
|
282
|
+
if (element.parentNode) {
|
|
283
|
+
element.parentNode.removeChild(element);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}, [editor, className, pluginKey]);
|
|
287
|
+
if (!editor || !menuEl.current || !mounted) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
return createPortal(children, menuEl.current);
|
|
213
291
|
};
|
|
214
292
|
|
|
215
293
|
// src/components/TemplateRenderer.tsx
|
|
@@ -218,7 +296,7 @@ import {
|
|
|
218
296
|
tokenizePrompt,
|
|
219
297
|
resolveTemplates
|
|
220
298
|
} from "@pixerate/editor";
|
|
221
|
-
import { jsx as
|
|
299
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
222
300
|
var TemplateRenderer = ({
|
|
223
301
|
content,
|
|
224
302
|
templates = [],
|
|
@@ -235,7 +313,7 @@ var TemplateRenderer = ({
|
|
|
235
313
|
const tokens = useMemo2(() => {
|
|
236
314
|
return tokenizePrompt(displayText);
|
|
237
315
|
}, [displayText]);
|
|
238
|
-
return /* @__PURE__ */
|
|
316
|
+
return /* @__PURE__ */ jsx2("div", { className, children: tokens.map((token, index) => {
|
|
239
317
|
if (token.type === "template") {
|
|
240
318
|
const colors = templateColorMap?.get(token.value) || {
|
|
241
319
|
from: "#f43f5e",
|
|
@@ -245,7 +323,7 @@ var TemplateRenderer = ({
|
|
|
245
323
|
"--color-from": colors.from,
|
|
246
324
|
"--color-to": colors.to
|
|
247
325
|
};
|
|
248
|
-
return /* @__PURE__ */
|
|
326
|
+
return /* @__PURE__ */ jsx2(
|
|
249
327
|
"span",
|
|
250
328
|
{
|
|
251
329
|
style,
|
|
@@ -258,7 +336,7 @@ var TemplateRenderer = ({
|
|
|
258
336
|
);
|
|
259
337
|
}
|
|
260
338
|
if (token.type === "variable") {
|
|
261
|
-
return /* @__PURE__ */
|
|
339
|
+
return /* @__PURE__ */ jsx2(
|
|
262
340
|
"span",
|
|
263
341
|
{
|
|
264
342
|
onClick: () => onVariableClick?.(token.value),
|
|
@@ -270,7 +348,7 @@ var TemplateRenderer = ({
|
|
|
270
348
|
);
|
|
271
349
|
}
|
|
272
350
|
if (token.type === "instruction") {
|
|
273
|
-
return /* @__PURE__ */
|
|
351
|
+
return /* @__PURE__ */ jsx2(
|
|
274
352
|
"span",
|
|
275
353
|
{
|
|
276
354
|
className: "inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300",
|
|
@@ -280,7 +358,7 @@ var TemplateRenderer = ({
|
|
|
280
358
|
index
|
|
281
359
|
);
|
|
282
360
|
}
|
|
283
|
-
return /* @__PURE__ */
|
|
361
|
+
return /* @__PURE__ */ jsx2("span", { children: token.value }, index);
|
|
284
362
|
}) });
|
|
285
363
|
};
|
|
286
364
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/usePromptEditor.ts","../src/hooks/useEditor.ts","../src/components/EditorContent.tsx","../src/components/BubbleMenu.tsx","../src/components/TemplateRenderer.tsx"],"sourcesContent":["import { useMemo, useEffect, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Slice, Fragment, Node as ProsemirrorNode } from \"@tiptap/pm/model\";\nimport {\n Template,\n ColorGradient,\n GradientText,\n TemplateSuggestions,\n LoadingNode,\n plainTextToTipTapHtml,\n getEditorText,\n} from \"@pixerate/editor\";\n\nexport interface UsePromptEditorProps {\n /** The initial or controlled content of the editor as plain text */\n content: string;\n /** Callback fired when the editor content changes (returns plain text) */\n onContentChange: (text: string) => void;\n /** List of available templates to be suggested and highlighted */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether the editor is editable */\n isEditing?: boolean;\n /** Placeholder text for the editor */\n placeholder?: string;\n /** Additional CSS classes for the editor container */\n className?: string;\n /** Callback fired when the Escape key is pressed */\n onEscape?: () => void;\n /** Custom keydown handler. Return true to prevent default behavior */\n onKeyDown?: (event: KeyboardEvent) => boolean;\n /** Callback fired when text selection changes */\n onSelectionChange?: (selection: { from: number; to: number } | null) => void;\n /** Callback fired when a slash command (e.g., /template) matches */\n onSlashCommandMatch?: (\n match: RegExpMatchArray,\n from: number,\n to: number,\n coords: { top: number; left: number },\n ) => void;\n /** Callback to dismiss/clear the active slash command */\n clearSlashCommand?: () => void;\n /** Whether a slash command menu is currently active */\n slashCommandActive?: boolean;\n /** Callback fired when certain milestones are reached (e.g., used_variable, used_magic) */\n onMilestone?: (milestone: string) => void;\n /** DOM element ID of the container, used for positioning popovers */\n containerId?: string;\n /** Variable names originating from structured JSON output steps */\n jsonVariables?: string[];\n /** Animation speed for gradient decorations */\n speed?: number;\n}\n\nconst DEFAULT_TEMPLATES: Template[] = [];\nconst DEFAULT_COLOR_MAP = new Map<string, ColorGradient>([\n [\"default\", { from: \"#f43f5e\", to: \"#8b5cf6\" }],\n]);\n\nexport function usePromptEditor({\n content,\n onContentChange,\n templates = DEFAULT_TEMPLATES,\n templateColorMap: userColorMap,\n isEditing = true,\n placeholder = \"Write a prompt, use {{template}}, {variable}, or /command...\",\n className,\n onEscape,\n onKeyDown,\n onSelectionChange,\n onSlashCommandMatch,\n clearSlashCommand,\n slashCommandActive = false,\n onMilestone,\n containerId = \"editor-container\",\n jsonVariables,\n speed = 2,\n}: UsePromptEditorProps) {\n const slashCommandActiveRef = useRef(slashCommandActive);\n slashCommandActiveRef.current = slashCommandActive;\n\n const clearSlashCommandRef = useRef(clearSlashCommand);\n clearSlashCommandRef.current = clearSlashCommand;\n\n const onEscapeRef = useRef(onEscape);\n onEscapeRef.current = onEscape;\n\n const onKeyDownRef = useRef(onKeyDown);\n onKeyDownRef.current = onKeyDown;\n\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n const onSlashCommandMatchRef = useRef(onSlashCommandMatch);\n onSlashCommandMatchRef.current = onSlashCommandMatch;\n\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n const onMilestoneRef = useRef(onMilestone);\n onMilestoneRef.current = onMilestone;\n\n const containerIdRef = useRef(containerId);\n containerIdRef.current = containerId;\n\n const resolvedColorMap = useMemo(() => {\n if (userColorMap) return userColorMap;\n const map = new Map<string, ColorGradient>();\n templates.forEach((t) => {\n map.set(t.name, { from: \"#f43f5e\", to: \"#8b5cf6\" });\n });\n return map.size > 0 ? map : DEFAULT_COLOR_MAP;\n }, [userColorMap, templates]);\n\n const editor = useEditor(\n {\n immediatelyRender: false,\n enablePasteRules: false,\n enableInputRules: false,\n editable: isEditing,\n extensions: [\n StarterKit.configure({\n heading: false,\n codeBlock: false,\n }),\n Placeholder.configure({ placeholder }),\n GradientText.configure({\n templateColorMap: resolvedColorMap,\n speed,\n jsonVariables,\n }),\n TemplateSuggestions.configure({\n templates,\n }),\n LoadingNode,\n ],\n content: plainTextToTipTapHtml(content),\n onUpdate: ({ editor: ed }) => {\n const plainText = getEditorText(ed);\n onContentChangeRef.current?.(plainText);\n\n if (onMilestoneRef.current) {\n if (plainText.match(/(?<!\\{)\\{([^{}]+)\\}(?!\\})/g)) {\n onMilestoneRef.current(\"used_variable\");\n }\n if (plainText.match(/__([^_]+)__/g)) {\n onMilestoneRef.current(\"used_magic\");\n }\n }\n\n const { state, view } = ed;\n const { selection } = state;\n const { $from, from, to } = selection;\n\n onSelectionChangeRef.current?.({ from, to });\n\n if (onSlashCommandMatchRef.current && clearSlashCommandRef.current) {\n const textBeforeCursor = $from?.parent?.textBetween\n ? $from.parent.textBetween(0, $from.parentOffset)\n : \"\";\n const slashMatch = textBeforeCursor.match(\n /(?:^|\\s)\\/([a-zA-Z0-9_-]*)$/,\n );\n\n if (slashMatch) {\n const coords = view.coordsAtPos($from.pos);\n const containerEl =\n document.getElementById(containerIdRef.current) ||\n document.getElementById(\"editor-container\") ||\n (view.dom.closest?.(\".relative\") as HTMLElement | null);\n const containerCoords = containerEl?.getBoundingClientRect();\n\n if (containerCoords) {\n onSlashCommandMatchRef.current(\n slashMatch,\n $from.pos -\n slashMatch[0].length +\n (slashMatch[0].startsWith(\" \") ? 1 : 0),\n $from.pos,\n {\n top: coords.bottom - containerCoords.top + 5,\n left: coords.left - containerCoords.left,\n },\n );\n }\n } else {\n clearSlashCommandRef.current();\n }\n }\n },\n onSelectionUpdate: ({ editor: ed }) => {\n const { from, to } = ed.state.selection;\n onSelectionChangeRef.current?.({ from, to });\n },\n editorProps: {\n attributes: {\n class:\n className ||\n \"w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words\",\n },\n handleKeyDown: (view, event) => {\n if (event.key === \"Escape\" && onEscapeRef.current) {\n onEscapeRef.current();\n return true;\n }\n if (onKeyDownRef.current) {\n return onKeyDownRef.current(event);\n }\n return false;\n },\n clipboardTextParser: (text, context, _plain, view) => {\n const schema =\n (context as any)?.doc?.type?.schema ||\n (context as any)?.schema ||\n view?.state?.schema;\n\n if (!schema) return Slice.empty;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n return Slice.maxOpen(Fragment.fromArray(nodes));\n },\n },\n },\n [templates, resolvedColorMap, isEditing, placeholder, speed],\n );\n\n return editor;\n}\n","import { useEditor as useTipTapEditor, UseEditorOptions } from \"@tiptap/react\";\nimport { createRichTextPreset, RichTextPresetOptions } from \"@pixerate/editor\";\n\nexport interface UseRichEditorProps extends UseEditorOptions {\n richTextOptions?: RichTextPresetOptions;\n}\n\n/**\n * Hook to initialize a TipTap rich-text editor instance with pre-configured extensions.\n */\nexport function useEditor(options: UseRichEditorProps = {}) {\n const { richTextOptions, extensions = [], ...editorOptions } = options;\n\n const preset = createRichTextPreset(richTextOptions);\n const combinedExtensions = [...preset, ...extensions];\n\n return useTipTapEditor({\n extensions: combinedExtensions,\n ...editorOptions,\n });\n}\n","import React from \"react\";\nimport {\n EditorContent as TipTapEditorContent,\n EditorContentProps as TipTapEditorContentProps,\n} from \"@tiptap/react\";\n\nexport interface EditorContentProps extends TipTapEditorContentProps {\n className?: string;\n}\n\nexport const EditorContent: React.FC<EditorContentProps> = ({\n className,\n editor,\n ...props\n}) => {\n return (\n <div className={className}>\n <TipTapEditorContent editor={editor} {...props} />\n </div>\n );\n};\n","import React from \"react\";\nimport { BubbleMenu as TipTapBubbleMenu } from \"@tiptap/react\";\nimport type { Editor } from \"@tiptap/core\";\n\nexport interface BubbleMenuProps {\n editor: Editor | null;\n className?: string;\n children: React.ReactNode;\n}\n\nexport const BubbleMenu: React.FC<BubbleMenuProps> = ({\n editor,\n className = \"flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1\",\n children,\n}) => {\n if (!editor) return null;\n\n return (\n <TipTapBubbleMenu\n editor={editor}\n tippyOptions={{ duration: 100 }}\n className={className}\n >\n {children}\n </TipTapBubbleMenu>\n );\n};\n","import React, { useMemo } from \"react\";\nimport {\n tokenizePrompt,\n resolveTemplates,\n Template,\n ColorGradient,\n} from \"@pixerate/editor\";\n\nexport interface TemplateRendererProps {\n /** Raw prompt or text string to render */\n content: string;\n /** Available templates for recursive resolution and custom coloring */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether to resolve nested templates recursively before displaying */\n resolve?: boolean;\n /** Custom class name for the wrapper */\n className?: string;\n /** Click callback for template badges */\n onTemplateClick?: (templateName: string) => void;\n /** Click callback for variable badges */\n onVariableClick?: (variableName: string) => void;\n}\n\nexport const TemplateRenderer: React.FC<TemplateRendererProps> = ({\n content,\n templates = [],\n templateColorMap,\n resolve = false,\n className = \"font-mono text-sm leading-relaxed whitespace-pre-wrap break-words\",\n onTemplateClick,\n onVariableClick,\n}) => {\n const displayText = useMemo(() => {\n if (!resolve || templates.length === 0) return content;\n return resolveTemplates(content, templates);\n }, [content, templates, resolve]);\n\n const tokens = useMemo(() => {\n return tokenizePrompt(displayText);\n }, [displayText]);\n\n return (\n <div className={className}>\n {tokens.map((token, index) => {\n if (token.type === \"template\") {\n const colors = templateColorMap?.get(token.value) || {\n from: \"#f43f5e\",\n to: \"#8b5cf6\",\n };\n const style = {\n \"--color-from\": colors.from,\n \"--color-to\": colors.to,\n } as React.CSSProperties;\n\n return (\n <span\n key={index}\n style={style}\n onClick={() => onTemplateClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-semibold rounded-md border border-[var(--color-to)]/40 bg-gradient-to-r from-[var(--color-from)]/15 to-[var(--color-to)]/15 text-[var(--color-to)] hover:brightness-110 cursor-pointer transition-all\"\n title={`Template: ${token.value}`}\n >\n {`{{${token.value}}}`}\n </span>\n );\n }\n\n if (token.type === \"variable\") {\n return (\n <span\n key={index}\n onClick={() => onVariableClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-violet-400/40 bg-violet-400/15 text-violet-600 dark:text-violet-300 hover:brightness-110 cursor-pointer transition-all\"\n title={`Variable: ${token.value}`}\n >\n {`{${token.value}}`}\n </span>\n );\n }\n\n if (token.type === \"instruction\") {\n return (\n <span\n key={index}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300\"\n title={`Instruction: ${token.value}`}\n >\n {`__${token.value}__`}\n </span>\n );\n }\n\n return <span key={index}>{token.value}</span>;\n })}\n </div>\n );\n};\n"],"mappings":"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","names":["useEditor","jsx","useMemo","jsx"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/usePromptEditor.ts","../src/hooks/useEditor.ts","../src/components/EditorContent.tsx","../src/components/BubbleMenu.tsx","../src/components/TemplateRenderer.tsx"],"sourcesContent":["import { useMemo, useEffect, useRef } from \"react\";\nimport { useEditor } from \"@tiptap/react\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport { Slice, Fragment, Node as ProsemirrorNode } from \"@tiptap/pm/model\";\nimport {\n Template,\n ColorGradient,\n GradientText,\n TemplateSuggestions,\n LoadingNode,\n plainTextToTipTapHtml,\n getEditorText,\n} from \"@pixerate/editor\";\n\nexport interface UsePromptEditorProps {\n /** The initial or controlled content of the editor as plain text */\n content: string;\n /** Callback fired when the editor content changes (returns plain text) */\n onContentChange: (text: string) => void;\n /** List of available templates to be suggested and highlighted */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether the editor is editable */\n isEditing?: boolean;\n /** Placeholder text for the editor */\n placeholder?: string;\n /** Additional CSS classes for the editor container */\n className?: string;\n /** Callback fired when the Escape key is pressed */\n onEscape?: () => void;\n /** Custom keydown handler. Return true to prevent default behavior */\n onKeyDown?: (event: KeyboardEvent) => boolean;\n /** Callback fired when text selection changes */\n onSelectionChange?: (selection: { from: number; to: number } | null) => void;\n /** Callback fired when a slash command (e.g., /template) matches */\n onSlashCommandMatch?: (\n match: RegExpMatchArray,\n from: number,\n to: number,\n coords: { top: number; left: number },\n ) => void;\n /** Callback to dismiss/clear the active slash command */\n clearSlashCommand?: () => void;\n /** Whether a slash command menu is currently active */\n slashCommandActive?: boolean;\n /** Callback fired when certain milestones are reached (e.g., used_variable, used_magic) */\n onMilestone?: (milestone: string) => void;\n /** DOM element ID of the container, used for positioning popovers */\n containerId?: string;\n /** Variable names originating from structured JSON output steps */\n jsonVariables?: string[];\n /** Animation speed for gradient decorations */\n speed?: number;\n}\n\nconst DEFAULT_TEMPLATES: Template[] = [];\nconst DEFAULT_COLOR_MAP = new Map<string, ColorGradient>([\n [\"default\", { from: \"#f43f5e\", to: \"#8b5cf6\" }],\n]);\n\nexport function usePromptEditor({\n content,\n onContentChange,\n templates = DEFAULT_TEMPLATES,\n templateColorMap: userColorMap,\n isEditing = true,\n placeholder = \"Write a prompt, use {{template}}, {variable}, or /command...\",\n className,\n onEscape,\n onKeyDown,\n onSelectionChange,\n onSlashCommandMatch,\n clearSlashCommand,\n slashCommandActive = false,\n onMilestone,\n containerId = \"editor-container\",\n jsonVariables,\n speed = 2,\n}: UsePromptEditorProps) {\n const slashCommandActiveRef = useRef(slashCommandActive);\n slashCommandActiveRef.current = slashCommandActive;\n\n const clearSlashCommandRef = useRef(clearSlashCommand);\n clearSlashCommandRef.current = clearSlashCommand;\n\n const onEscapeRef = useRef(onEscape);\n onEscapeRef.current = onEscape;\n\n const onKeyDownRef = useRef(onKeyDown);\n onKeyDownRef.current = onKeyDown;\n\n const onSelectionChangeRef = useRef(onSelectionChange);\n onSelectionChangeRef.current = onSelectionChange;\n\n const onSlashCommandMatchRef = useRef(onSlashCommandMatch);\n onSlashCommandMatchRef.current = onSlashCommandMatch;\n\n const onContentChangeRef = useRef(onContentChange);\n onContentChangeRef.current = onContentChange;\n\n const onMilestoneRef = useRef(onMilestone);\n onMilestoneRef.current = onMilestone;\n\n const containerIdRef = useRef(containerId);\n containerIdRef.current = containerId;\n\n const templateKey = useMemo(() => {\n return (templates || [])\n .map((t) => `${t.id || t.name}:${(t as any).latestVersion || \"\"}`)\n .sort()\n .join(\",\");\n }, [templates]);\n\n const resolvedColorMap = useMemo(() => {\n if (userColorMap) return userColorMap;\n const map = new Map<string, ColorGradient>();\n templates.forEach((t) => {\n map.set(t.name, { from: \"#f43f5e\", to: \"#8b5cf6\" });\n });\n return map.size > 0 ? map : DEFAULT_COLOR_MAP;\n }, [userColorMap, templateKey]);\n\n const StarterKitExt =\n (StarterKit as any)?.configure\n ? StarterKit\n : (StarterKit as any)?.default?.configure\n ? (StarterKit as any)?.default\n : StarterKit;\n\n const PlaceholderExt =\n (Placeholder as any)?.configure\n ? Placeholder\n : (Placeholder as any)?.default?.configure\n ? (Placeholder as any)?.default\n : Placeholder;\n\n const editor = useEditor(\n {\n immediatelyRender: false,\n enablePasteRules: false,\n enableInputRules: false,\n editable: isEditing,\n extensions: [\n StarterKitExt.configure({\n heading: false,\n codeBlock: false,\n }),\n PlaceholderExt.configure({ placeholder }),\n GradientText.configure({\n templateColorMap: resolvedColorMap,\n speed,\n jsonVariables,\n }),\n TemplateSuggestions.configure({\n templates,\n }),\n LoadingNode,\n ],\n content: plainTextToTipTapHtml(content),\n onUpdate: ({ editor: ed }) => {\n const plainText = getEditorText(ed);\n onContentChangeRef.current?.(plainText);\n\n if (onMilestoneRef.current) {\n if (plainText.match(/(?<!\\{)\\{([^{}]+)\\}(?!\\})/g)) {\n onMilestoneRef.current(\"used_variable\");\n }\n if (plainText.match(/__([^_]+)__/g)) {\n onMilestoneRef.current(\"used_magic\");\n }\n }\n\n const { state, view } = ed;\n const { selection } = state;\n const { $from, from, to } = selection;\n\n onSelectionChangeRef.current?.({ from, to });\n\n if (onSlashCommandMatchRef.current && clearSlashCommandRef.current) {\n const textBeforeCursor = $from?.parent?.textBetween\n ? $from.parent.textBetween(0, $from.parentOffset)\n : \"\";\n const slashMatch = textBeforeCursor.match(\n /(?:^|\\s)\\/([a-zA-Z0-9_-]*)$/,\n );\n\n if (slashMatch) {\n const coords = view.coordsAtPos($from.pos);\n const containerEl =\n document.getElementById(containerIdRef.current) ||\n document.getElementById(\"editor-container\") ||\n (view.dom.closest?.(\".relative\") as HTMLElement | null);\n const containerCoords = containerEl?.getBoundingClientRect();\n\n if (containerCoords) {\n onSlashCommandMatchRef.current(\n slashMatch,\n $from.pos -\n slashMatch[0].length +\n (slashMatch[0].startsWith(\" \") ? 1 : 0),\n $from.pos,\n {\n top: coords.bottom - containerCoords.top + 5,\n left: coords.left - containerCoords.left,\n },\n );\n }\n } else {\n clearSlashCommandRef.current();\n }\n }\n },\n onSelectionUpdate: ({ editor: ed }) => {\n const { from, to } = ed.state.selection;\n onSelectionChangeRef.current?.({ from, to });\n },\n editorProps: {\n attributes: {\n class:\n className ||\n \"w-full border border-input rounded-md p-3 font-mono text-sm leading-relaxed focus:outline-none focus:ring-2 focus:ring-primary whitespace-pre-wrap break-words\",\n role: \"textbox\",\n \"aria-label\": \"Prompt editor\",\n },\n handleKeyDown(view, event) {\n if (slashCommandActiveRef.current) {\n if (\n event.key === \"ArrowUp\" ||\n event.key === \"ArrowDown\" ||\n event.key === \"Enter\"\n ) {\n event.preventDefault();\n return true;\n }\n if (event.key === \"Escape\") {\n clearSlashCommandRef.current?.();\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n }\n if (onKeyDownRef.current && onKeyDownRef.current(event as KeyboardEvent)) {\n return true;\n }\n if (event.key === \"Escape\" && onEscapeRef.current) {\n onEscapeRef.current();\n return true;\n }\n return false;\n },\n handlePaste: (view, event) => {\n const text = event.clipboardData?.getData(\"text/plain\");\n if (!text) return false;\n event.preventDefault();\n const schema = view.state.schema;\n if (!schema) return false;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n const parsedSlice = Slice.maxOpen(Fragment.fromArray(nodes));\n const tr = view.state.tr.replaceSelection(parsedSlice);\n view.dispatch(tr);\n return true;\n },\n clipboardTextParser: (text, context, _plain, view) => {\n const schema =\n (context as any)?.doc?.type?.schema ||\n (context as any)?.schema ||\n view?.state?.schema;\n\n if (!schema) return Slice.empty;\n\n const blocks = text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\");\n const nodes: ProsemirrorNode[] = [];\n\n blocks.forEach((line) => {\n const nodeJson: any = { type: \"paragraph\" };\n if (line.length > 0) {\n nodeJson.content = [{ type: \"text\", text: line }];\n }\n try {\n const node = ProsemirrorNode.fromJSON(schema, nodeJson);\n nodes.push(node);\n } catch {\n // Ignore\n }\n });\n\n return Slice.maxOpen(Fragment.fromArray(nodes));\n },\n },\n },\n [resolvedColorMap, templateKey, isEditing, placeholder, speed],\n );\n\n useEffect(() => {\n if (editor && !editor.isDestroyed) {\n editor.setEditable(isEditing);\n }\n }, [editor, isEditing]);\n\n return editor;\n}\n","import { useEditor as useTipTapEditor, UseEditorOptions } from \"@tiptap/react\";\nimport { createRichTextPreset, RichTextPresetOptions } from \"@pixerate/editor\";\n\nexport interface UseRichEditorProps extends UseEditorOptions {\n richTextOptions?: RichTextPresetOptions;\n}\n\n/**\n * Hook to initialize a TipTap rich-text editor instance with pre-configured extensions.\n */\nexport function useEditor(options: UseRichEditorProps = {}) {\n const { richTextOptions, extensions = [], ...editorOptions } = options;\n\n const preset = createRichTextPreset(richTextOptions);\n const combinedExtensions = [...preset, ...extensions];\n\n return useTipTapEditor({\n extensions: combinedExtensions,\n ...editorOptions,\n });\n}\n","import React from \"react\";\nimport {\n EditorContent as TipTapEditorContent,\n EditorContentProps as TipTapEditorContentProps,\n} from \"@tiptap/react\";\n\nexport interface EditorContentProps extends TipTapEditorContentProps {\n className?: string;\n}\n\nexport const EditorContent: React.FC<EditorContentProps> = ({\n className,\n editor,\n ...props\n}) => {\n return (\n <div className={className}>\n <TipTapEditorContent editor={editor} {...props} />\n </div>\n );\n};\n","import React, { useEffect, useRef, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport {\n BubbleMenuPlugin,\n type BubbleMenuPluginProps,\n} from \"@tiptap/extension-bubble-menu\";\nimport type { Editor } from \"@tiptap/core\";\n\nexport interface BubbleMenuProps {\n editor: Editor | null;\n className?: string;\n children: React.ReactNode;\n tippyOptions?: BubbleMenuPluginProps[\"tippyOptions\"];\n shouldShow?: BubbleMenuPluginProps[\"shouldShow\"];\n pluginKey?: BubbleMenuPluginProps[\"pluginKey\"];\n updateDelay?: BubbleMenuPluginProps[\"updateDelay\"];\n}\n\nexport const BubbleMenu: React.FC<BubbleMenuProps> = ({\n editor,\n className = \"flex items-center gap-1 bg-popover text-popover-foreground border shadow-md rounded-md p-1\",\n children,\n tippyOptions = { duration: 100 },\n shouldShow = null,\n pluginKey = \"bubbleMenu\",\n updateDelay,\n}) => {\n const menuEl = useRef<HTMLDivElement | null>(null);\n const [mounted, setMounted] = useState(false);\n\n if (!menuEl.current && typeof document !== \"undefined\") {\n menuEl.current = document.createElement(\"div\");\n }\n\n useEffect(() => {\n if (!editor || !menuEl.current || editor.isDestroyed) {\n return;\n }\n\n const element = menuEl.current;\n element.className = className;\n\n const plugin = BubbleMenuPlugin({\n editor,\n element,\n pluginKey,\n tippyOptions,\n shouldShow,\n updateDelay,\n });\n\n editor.registerPlugin(plugin);\n setMounted(true);\n\n return () => {\n setMounted(false);\n editor.unregisterPlugin(pluginKey);\n if (element.parentNode) {\n element.parentNode.removeChild(element);\n }\n };\n }, [editor, className, pluginKey]);\n\n if (!editor || !menuEl.current || !mounted) {\n return null;\n }\n\n return createPortal(children, menuEl.current);\n};\n","import React, { useMemo } from \"react\";\nimport {\n tokenizePrompt,\n resolveTemplates,\n Template,\n ColorGradient,\n} from \"@pixerate/editor\";\n\nexport interface TemplateRendererProps {\n /** Raw prompt or text string to render */\n content: string;\n /** Available templates for recursive resolution and custom coloring */\n templates?: Template[];\n /** Custom gradient color map for templates */\n templateColorMap?: Map<string, ColorGradient>;\n /** Whether to resolve nested templates recursively before displaying */\n resolve?: boolean;\n /** Custom class name for the wrapper */\n className?: string;\n /** Click callback for template badges */\n onTemplateClick?: (templateName: string) => void;\n /** Click callback for variable badges */\n onVariableClick?: (variableName: string) => void;\n}\n\nexport const TemplateRenderer: React.FC<TemplateRendererProps> = ({\n content,\n templates = [],\n templateColorMap,\n resolve = false,\n className = \"font-mono text-sm leading-relaxed whitespace-pre-wrap break-words\",\n onTemplateClick,\n onVariableClick,\n}) => {\n const displayText = useMemo(() => {\n if (!resolve || templates.length === 0) return content;\n return resolveTemplates(content, templates);\n }, [content, templates, resolve]);\n\n const tokens = useMemo(() => {\n return tokenizePrompt(displayText);\n }, [displayText]);\n\n return (\n <div className={className}>\n {tokens.map((token, index) => {\n if (token.type === \"template\") {\n const colors = templateColorMap?.get(token.value) || {\n from: \"#f43f5e\",\n to: \"#8b5cf6\",\n };\n const style = {\n \"--color-from\": colors.from,\n \"--color-to\": colors.to,\n } as React.CSSProperties;\n\n return (\n <span\n key={index}\n style={style}\n onClick={() => onTemplateClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-semibold rounded-md border border-[var(--color-to)]/40 bg-gradient-to-r from-[var(--color-from)]/15 to-[var(--color-to)]/15 text-[var(--color-to)] hover:brightness-110 cursor-pointer transition-all\"\n title={`Template: ${token.value}`}\n >\n {`{{${token.value}}}`}\n </span>\n );\n }\n\n if (token.type === \"variable\") {\n return (\n <span\n key={index}\n onClick={() => onVariableClick?.(token.value)}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs font-medium rounded-md border border-violet-400/40 bg-violet-400/15 text-violet-600 dark:text-violet-300 hover:brightness-110 cursor-pointer transition-all\"\n title={`Variable: ${token.value}`}\n >\n {`{${token.value}}`}\n </span>\n );\n }\n\n if (token.type === \"instruction\") {\n return (\n <span\n key={index}\n className=\"inline-flex items-center px-1.5 py-0.5 mx-0.5 text-xs italic font-medium rounded-md border border-rose-400/40 bg-rose-400/15 text-rose-600 dark:text-rose-300\"\n title={`Instruction: ${token.value}`}\n >\n {`__${token.value}__`}\n </span>\n );\n }\n\n return <span key={index}>{token.value}</span>;\n })}\n </div>\n );\n};\n"],"mappings":"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","names":["useEditor","useEffect","useRef","useMemo","jsx"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-react",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "React bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Pixerate/editor.git",
|
|
10
|
+
"directory": "packages/react"
|
|
11
|
+
},
|
|
7
12
|
"type": "module",
|
|
8
13
|
"main": "./dist/index.cjs",
|
|
9
14
|
"module": "./dist/index.js",
|
|
@@ -19,13 +24,13 @@
|
|
|
19
24
|
"dist"
|
|
20
25
|
],
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"@tiptap/core": "^2.11.5",
|
|
23
|
-
"@tiptap/extension-bubble-menu": "^2.11.5",
|
|
24
|
-
"@tiptap/extension-placeholder": "^2.11.5",
|
|
25
|
-
"@tiptap/pm": "^2.11.5",
|
|
26
|
-
"@tiptap/react": "^2.11.5",
|
|
27
|
-
"@tiptap/starter-kit": "^2.11.5",
|
|
28
|
-
"@pixerate/editor": "0.1
|
|
27
|
+
"@tiptap/core": "^2.11.5 || ^3.0.0",
|
|
28
|
+
"@tiptap/extension-bubble-menu": "^2.11.5 || ^3.0.0",
|
|
29
|
+
"@tiptap/extension-placeholder": "^2.11.5 || ^3.0.0",
|
|
30
|
+
"@tiptap/pm": "^2.11.5 || ^3.0.0",
|
|
31
|
+
"@tiptap/react": "^2.11.5 || ^3.0.0",
|
|
32
|
+
"@tiptap/starter-kit": "^2.11.5 || ^3.0.0",
|
|
33
|
+
"@pixerate/editor": "0.2.1"
|
|
29
34
|
},
|
|
30
35
|
"peerDependencies": {
|
|
31
36
|
"react": ">=18.0.0",
|