fix(ai): keep assistant actions visible (#236)

This commit is contained in:
tianma-if committed 2026-08-21 18:59:53 +08:00
1 parent 03a9444c92
commit db58da7c3e
2 files changed
+39 -6

No files matched your search

@@ -401,7 +401,7 @@ export const AiAssistantDialog = ({
<section
ref={assignPanelRef}
aria-label={t("aiAssistant.title")}
className="fixed z-[70] max-h-[70dvh] w-[min(36rem,calc(100vw-1.5rem))] overflow-y-auto rounded-xl border border-slate-200 bg-white p-4 shadow-2xl ring-1 ring-slate-950/5"
className="fixed z-[70] flex max-h-[70dvh] w-[min(36rem,calc(100vw-1.5rem))] flex-col overflow-hidden rounded-xl border border-slate-200 bg-white p-4 shadow-2xl ring-1 ring-slate-950/5"
role="dialog"
style={panelStyle}
onKeyDown={(event) => {
@@ -412,7 +412,7 @@ export const AiAssistantDialog = ({
}
}}
>
<div className="mb-3 flex items-center justify-between gap-3">
<div className="mb-3 flex shrink-0 items-center justify-between gap-3">
<div className="flex min-w-0 items-center gap-2">
<Sparkles className="h-5 w-5 shrink-0 text-emerald-600" />
<span className="truncate text-sm font-semibold text-slate-950">{t("aiAssistant.title")}</span>
@@ -424,7 +424,8 @@ export const AiAssistantDialog = ({
<X className="h-4 w-4" />
</Button>
</div>
<div className="grid gap-4">
<div className="min-h-0 flex-1 overflow-y-auto pr-1">
<div className="grid gap-4">
{hasSelection ? (
<p className="max-h-12 overflow-hidden whitespace-pre-wrap border-l-2 border-emerald-200 pl-3 text-xs leading-5 text-slate-500">
{selectionMarkdown}
@@ -474,7 +475,7 @@ export const AiAssistantDialog = ({
</SelectGroup>
</SelectContent>
</Select>
<div className="grid min-w-0 grid-cols-2 gap-2 sm:grid-cols-[auto_auto]">
<div className="grid min-w-0 grid-cols-2 gap-2 sm:grid-cols-[8rem_7rem]">
<Button
type="button"
variant={!selectedPromptId && action === "custom" ? "solid" : "outline"}
@@ -671,9 +672,10 @@ export const AiAssistantDialog = ({
</div>
</div>
) : null}
</div>
</div>
{output ? (
<div className="mt-4 flex flex-wrap justify-between gap-2">
<div className="mt-3 flex shrink-0 flex-wrap justify-between gap-2 border-t border-slate-200 pt-3" data-ai-assistant-actions>
<div className="flex flex-wrap gap-2">
<Button type="button" variant="outline" disabled={isGenerating} onClick={() => void copy()}>{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}{t(copied ? "aiAssistant.copied" : "aiAssistant.copy")}</Button>
<Button type="button" variant="outline" disabled={isGenerating} onClick={() => { setOutput(""); setError(null); }}><Trash2 className="h-4 w-4" />{t("aiAssistant.discard")}</Button>
+32 -1
View File
@@ -182,7 +182,21 @@ test.describe("AI custom prompts", () => {
const assistant = page.getByRole("dialog", { name: "AI 笔记助手" });
await expect(assistant).toBeVisible();
const customInstructionButton = assistant.getByRole("button", { name: "自定义指令", exact: true });
const actionSelect = assistant.getByRole("combobox", { name: "处理方式" });
const generateButton = assistant.getByRole("button", { name: "生成", exact: true });
await expect(customInstructionButton).toBeVisible();
const [actionSelectBox, customInstructionBox, generateBox] = await Promise.all([
actionSelect.boundingBox(),
customInstructionButton.boundingBox(),
generateButton.boundingBox(),
]);
expect(actionSelectBox).not.toBeNull();
expect(customInstructionBox).not.toBeNull();
expect(generateBox).not.toBeNull();
expect(customInstructionBox!.width).toBeGreaterThanOrEqual(120);
expect(generateBox!.width).toBeGreaterThanOrEqual(104);
expect(actionSelectBox!.width / customInstructionBox!.width).toBeLessThan(2.5);
expect(customInstructionBox!.width / generateBox!.width).toBeLessThan(1.25);
const customInstructionLineCount = await customInstructionButton.evaluate((element) => {
const textNode = Array.from(element.childNodes)
.find((node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim());
@@ -409,6 +423,7 @@ test.describe("AI custom prompts", () => {
});
test("inserts generated content at the caret captured when AI opens", async ({ page }) => {
await page.setViewportSize({ width: 1180, height: 720 });
const memo = await createMemo(
page,
`e2e-ai-caret-insert-${Date.now()}`,
@@ -428,7 +443,23 @@ test.describe("AI custom prompts", () => {
const dialog = page.getByRole("dialog", { name: "AI 笔记助手" });
await dialog.getByRole("button", { name: "生成", exact: true }).click();
await expect(dialog.getByText("AI 插入段落", { exact: true })).toBeVisible();
await dialog.getByRole("button", { name: "追加到笔记", exact: true }).click();
const copyButton = dialog.getByRole("button", { name: "复制结果", exact: true });
const appendButton = dialog.getByRole("button", { name: "追加到笔记", exact: true });
await expect(copyButton).toBeVisible();
await expect(appendButton).toBeVisible();
const [dialogBox, copyButtonBox, appendButtonBox] = await Promise.all([
dialog.boundingBox(),
copyButton.boundingBox(),
appendButton.boundingBox(),
]);
expect(dialogBox).not.toBeNull();
expect(copyButtonBox).not.toBeNull();
expect(appendButtonBox).not.toBeNull();
expect(copyButtonBox!.y).toBeGreaterThanOrEqual(dialogBox!.y);
expect(copyButtonBox!.y + copyButtonBox!.height).toBeLessThanOrEqual(dialogBox!.y + dialogBox!.height);
expect(appendButtonBox!.y).toBeGreaterThanOrEqual(dialogBox!.y);
expect(appendButtonBox!.y + appendButtonBox!.height).toBeLessThanOrEqual(dialogBox!.y + dialogBox!.height);
await appendButton.click();
await expect(dialog).toBeHidden();
await expect.poll(() => editor.locator(":scope > p").allTextContents()).toEqual([
"第一段",