mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 02:40:39 +09:00
Add MarkdownEditor component, asset image upload, and rich description editing
Introduce MarkdownEditor built on @mdxeditor/editor with headings, lists, links, quotes, image upload with drag-and-drop, and themed CSS integration. Add asset image upload API (routes, service, storage) and wire image upload into InlineEditor multiline mode, NewIssueDialog, NewProjectDialog, GoalDetail, IssueDetail, and ProjectDetail description fields. Tighten prompt template editor styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0f4ab72888
commit
a4ba4a72cd
16 changed files with 2221 additions and 30 deletions
|
|
@ -1,6 +1,8 @@
|
|||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import Markdown from "react-markdown";
|
||||
import { cn } from "../lib/utils";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { MarkdownEditor } from "./MarkdownEditor";
|
||||
|
||||
interface InlineEditorProps {
|
||||
value: string;
|
||||
|
|
@ -9,6 +11,7 @@ interface InlineEditorProps {
|
|||
className?: string;
|
||||
placeholder?: string;
|
||||
multiline?: boolean;
|
||||
imageUploadHandler?: (file: File) => Promise<string>;
|
||||
}
|
||||
|
||||
/** Shared padding so display and edit modes occupy the exact same box. */
|
||||
|
|
@ -21,6 +24,7 @@ export function InlineEditor({
|
|||
className,
|
||||
placeholder = "Click to edit...",
|
||||
multiline = false,
|
||||
imageUploadHandler,
|
||||
}: InlineEditorProps) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [draft, setDraft] = useState(value);
|
||||
|
|
@ -68,6 +72,35 @@ export function InlineEditor({
|
|||
}
|
||||
|
||||
if (editing) {
|
||||
if (multiline) {
|
||||
return (
|
||||
<div className={cn("space-y-2", pad)}>
|
||||
<MarkdownEditor
|
||||
value={draft}
|
||||
onChange={setDraft}
|
||||
placeholder={placeholder}
|
||||
contentClassName={className}
|
||||
imageUploadHandler={imageUploadHandler}
|
||||
/>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setDraft(value);
|
||||
setEditing(false);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button size="sm" onClick={commit}>
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sharedProps = {
|
||||
ref: inputRef as any,
|
||||
value: draft,
|
||||
|
|
@ -81,21 +114,6 @@ export function InlineEditor({
|
|||
onKeyDown: handleKeyDown,
|
||||
};
|
||||
|
||||
if (multiline) {
|
||||
return (
|
||||
<textarea
|
||||
{...sharedProps}
|
||||
rows={1}
|
||||
className={cn(
|
||||
"w-full resize-none bg-accent/30 rounded outline-none",
|
||||
pad,
|
||||
"py-0.5",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue