feat(web): add markdown round-trip and replace textarea with Tiptap (#501)
All checks were successful
ci/woodpecker/push/orchestrator Pipeline was successful
ci/woodpecker/push/api Pipeline was successful
ci/woodpecker/push/web Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #501.
This commit is contained in:
2026-02-24 01:40:34 +00:00
committed by jason.woltje
parent a81c4a5edd
commit d5ecc0b107
6 changed files with 132 additions and 182 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import React, { useState, useRef } from "react";
import { LinkAutocomplete } from "./LinkAutocomplete";
import React from "react";
import { KnowledgeEditor } from "./KnowledgeEditor";
interface EntryEditorProps {
content: string;
@@ -9,57 +9,21 @@ interface EntryEditorProps {
}
/**
* EntryEditor - Markdown editor with live preview and link autocomplete
* EntryEditor - WYSIWYG editor for knowledge entries.
* Wraps KnowledgeEditor (Tiptap) with markdown round-trip.
* Content is stored as markdown; the editor provides rich text editing.
*/
export function EntryEditor({ content, onChange }: EntryEditorProps): React.JSX.Element {
const [showPreview, setShowPreview] = useState(false);
const textareaRef = useRef<HTMLTextAreaElement>(null);
return (
<div className="entry-editor relative">
<div className="flex justify-between items-center mb-2">
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Content (Markdown)
</label>
<button
type="button"
onClick={() => {
setShowPreview(!showPreview);
}}
className="text-sm text-blue-600 dark:text-blue-400 hover:underline"
>
{showPreview ? "Edit" : "Preview"}
</button>
</div>
{showPreview ? (
<div className="prose prose-sm max-w-none dark:prose-invert p-4 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 min-h-[300px]">
<div className="whitespace-pre-wrap">{content}</div>
</div>
) : (
<div className="relative">
<textarea
ref={textareaRef}
value={content}
onChange={(e) => {
onChange(e.target.value);
}}
className="w-full min-h-[300px] p-4 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Write your content here... (Markdown supported)"
/>
<LinkAutocomplete
textareaRef={textareaRef}
onInsert={(newContent) => {
onChange(newContent);
}}
/>
</div>
)}
<p className="mt-2 text-xs text-gray-500 dark:text-gray-400">
Supports Markdown formatting. Type <code className="text-xs">[[</code> to insert links to
other entries.
</p>
<div className="entry-editor">
<label className="block text-sm font-medium mb-2" style={{ color: "var(--text-2)" }}>
Content
</label>
<KnowledgeEditor
content={content}
onChange={onChange}
placeholder="Write your content here... Supports markdown formatting."
/>
</div>
);
}