feat: enhance SkillEditDialog with ContentEditor and improve MarkdownEditor toolbar logic

This commit is contained in:
mlogclub
2026-06-20 20:22:04 +08:00
parent 30ebfcface
commit 86652cc41c
3 changed files with 74 additions and 51 deletions
+1 -1
Submodule docs updated: 6e7ec786d0...f48e9d76da
@@ -2,9 +2,10 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { type Resolver, useForm } from "react-hook-form"; import { Controller, type Resolver, useForm } from "react-hook-form";
import { z } from "zod/v4"; import { z } from "zod/v4";
import { ContentEditor } from "@/components/content-editor";
import { OptionCombobox } from "@/components/option-combobox"; import { OptionCombobox } from "@/components/option-combobox";
import { ProjectDialog } from "@/components/project-dialog"; import { ProjectDialog } from "@/components/project-dialog";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -155,6 +156,7 @@ function SkillEditDialogBody({
handleSubmit, handleSubmit,
reset, reset,
register, register,
control,
formState: { errors }, formState: { errors },
} = form; } = form;
@@ -316,14 +318,21 @@ function SkillEditDialogBody({
</Field> </Field>
<Field data-invalid={!!errors.instruction}> <Field data-invalid={!!errors.instruction}>
<FieldLabel htmlFor="skill-instruction">{t("skillDefinition.instruction")}</FieldLabel> <FieldLabel>{t("skillDefinition.instruction")}</FieldLabel>
<FieldContent> <FieldContent>
<Textarea <Controller
id="skill-instruction" control={control}
rows={12} name="instruction"
placeholder={t("skillDefinition.instructionPlaceholder")} render={({ field }) => (
aria-invalid={!!errors.instruction} <ContentEditor
{...register("instruction")} value={{ mode: "markdown", raw: field.value ?? "" }}
onChange={(next) => field.onChange(next.raw)}
placeholder={t("skillDefinition.instructionPlaceholder")}
disabled={saving || loading}
allowedModes={["markdown"]}
height={360}
/>
)}
/> />
<FieldError errors={[errors.instruction]} /> <FieldError errors={[errors.instruction]} />
</FieldContent> </FieldContent>
@@ -56,29 +56,67 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
const editorId = useId() const editorId = useId()
const editorRef = useRef<ExposeParam>(null) const editorRef = useRef<ExposeParam>(null)
const { resolvedTheme } = useTheme() const { resolvedTheme } = useTheme()
const showModeSwitch = allowedModes.length > 1
const defToolbars = useMemo( const defToolbars = useMemo(
() => [ () => {
<EditorModeSwitch const fullscreenToolbar = (
key="mode-switch" <NormalToolbar
value={mode}
allowedModes={allowedModes}
disabled={disabled}
onChange={onModeChange}
/>,
<NormalToolbar
key="toggle-fullscreen" key="toggle-fullscreen"
title={fullscreen ? t("editor.exitFullscreen") : t("editor.fullscreen")} title={fullscreen ? t("editor.exitFullscreen") : t("editor.fullscreen")}
disabled={disabled} disabled={disabled}
onClick={onToggleFullscreen} onClick={onToggleFullscreen}
> >
{fullscreen ? ( {fullscreen ? (
<Minimize2Icon className="h-[16px] w-[16px]" /> <Minimize2Icon className="h-[16px] w-[16px]" />
) : ( ) : (
<Maximize2Icon className="h-[16px] w-[16px]" /> <Maximize2Icon className="h-[16px] w-[16px]" />
)} )}
</NormalToolbar>, </NormalToolbar>
)
if (!showModeSwitch) {
return [fullscreenToolbar]
}
return [
<EditorModeSwitch
key="mode-switch"
value={mode}
allowedModes={allowedModes}
disabled={disabled}
onChange={onModeChange}
/>,
fullscreenToolbar,
]
},
[allowedModes, disabled, fullscreen, mode, onModeChange, onToggleFullscreen, showModeSwitch, t]
)
const toolbars = useMemo(
() => [
...(showModeSwitch ? [0, "-"] : []),
"bold",
"underline",
"italic",
"strikeThrough",
"-",
"title",
"quote",
"unorderedList",
"orderedList",
"-",
"codeRow",
"code",
"link",
"image",
"-",
"revoke",
"next",
showModeSwitch ? 1 : 0,
"=",
"preview",
"previewOnly",
], ],
[allowedModes, disabled, fullscreen, mode, onModeChange, onToggleFullscreen, t] [showModeSwitch]
) )
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
@@ -99,31 +137,7 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
onChange={onChange} onChange={onChange}
theme={resolvedTheme === "dark" ? "dark" : "light"} theme={resolvedTheme === "dark" ? "dark" : "light"}
preview={false} preview={false}
toolbars={[ toolbars={toolbars}
0,
"-",
"bold",
"underline",
"italic",
"strikeThrough",
"-",
"title",
"quote",
"unorderedList",
"orderedList",
"-",
"codeRow",
"code",
"link",
"image",
"-",
"revoke",
"next",
1,
"=",
"preview",
"previewOnly",
]}
defToolbars={defToolbars} defToolbars={defToolbars}
footers={[]} footers={[]}
noMermaid noMermaid