feat(ai): generate with Enter shortcut (#236)

This commit is contained in:
tianma-if committed 2026-08-21 19:04:42 +08:00
1 parent db58da7c3e
commit 798628f0b5
2 files changed
+44 -4

No files matched your search

@@ -491,7 +491,11 @@ export const AiAssistantDialog = ({
</Button>
) : (
<Button type="button" variant="solid" className="h-10 min-w-0 w-full gap-1.5 whitespace-nowrap px-3 text-sm font-semibold" disabled={generateDisabled} onClick={() => void generate()}>
<Sparkles className="h-4 w-4 shrink-0" />{t("aiAssistant.generate")}
<Sparkles className="h-4 w-4 shrink-0" />
{t("aiAssistant.generate")}
<kbd aria-hidden="true" className="ml-0.5 rounded border border-emerald-300/60 bg-emerald-600/35 px-1 py-0.5 text-[10px] font-medium leading-none text-emerald-50">
Enter
</kbd>
</Button>
)}
</div>
@@ -562,6 +566,19 @@ export const AiAssistantDialog = ({
setCustomInstruction(event.currentTarget.value);
clearResult();
}}
onKeyDown={(event) => {
if (
event.key !== "Enter"
|| event.shiftKey
|| event.nativeEvent.isComposing
|| isGenerating
|| isReadingAttachments
) {
return;
}
event.preventDefault();
void generate();
}}
placeholder={t("aiAssistant.customInstructionPlaceholder")}
maxLength={2_000}
/>
+26 -3
View File
@@ -27,9 +27,11 @@ const mockAiGeneration = async (
page: Page,
replacement: string,
onRequest?: (body: Record<string, unknown>) => void,
delayMs = 0,
) => {
await page.route("**/api/v1/ai/generate", async (route) => {
onRequest?.(route.request().postDataJSON() as Record<string, unknown>);
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
await route.fulfill({
status: 200,
contentType: "text/event-stream; charset=utf-8",
@@ -338,13 +340,15 @@ test.describe("AI custom prompts", () => {
test("keeps a Chinese custom instruction usable while prompts initialize", async ({ page }) => {
const memo = await createMemo(page, `e2e-ai-ime-${Date.now()}`, "中文输入状态测试");
let submittedBody: Record<string, unknown> | null = null;
let generationRequestCount = 0;
await page.route("**/api/v1/ai/prompts*", async (route) => {
await new Promise((resolve) => setTimeout(resolve, 800));
await route.continue();
});
await mockAiGeneration(page, "宋词结果", (body) => {
generationRequestCount += 1;
submittedBody = body;
});
}, 300);
await ensureAuthenticatedPage(page);
await page.getByRole("button", { name: new RegExp(notebookName) }).click();
await page.locator(`[data-memo-id="${memo.id}"]`).locator("button").first().click();
@@ -355,12 +359,31 @@ test.describe("AI custom prompts", () => {
await textarea.fill("写个宋词");
const generateButton = dialog.getByRole("button", { name: "生成", exact: true });
await expect(generateButton).toBeEnabled();
await generateButton.click();
await expect(generateButton.locator("kbd")).toHaveText("Enter");
await textarea.press("Shift+Enter");
await textarea.pressSequentially("不要参考原笔记");
await expect(textarea).toHaveValue("写个宋词\n不要参考原笔记");
await textarea.evaluate((element) => {
element.dispatchEvent(new KeyboardEvent("keydown", {
bubbles: true,
isComposing: true,
key: "Enter",
}));
});
expect(submittedBody).toBeNull();
await textarea.press("Enter");
await expect.poll(() => generationRequestCount).toBe(1);
await textarea.press("Enter");
await page.waitForTimeout(50);
expect(generationRequestCount).toBe(1);
await expect(dialog.getByText("宋词结果", { exact: true })).toBeVisible();
expect(submittedBody).toMatchObject({
action: "custom",
instruction: "写个宋词",
instruction: "写个宋词\n不要参考原笔记",
});
});