Add custom prompt feature and enhance keyboard handling
This commit is contained in:
68
src/App.tsx
68
src/App.tsx
@@ -1259,15 +1259,6 @@ export default function App() {
|
||||
|
||||
const handleOpenAiToolsMenu = useCallback(async () => {
|
||||
if (!selectedTextId || !hasText || isViewingHistory || isConverting) return;
|
||||
if (aiPromptTemplates.length === 0) {
|
||||
setConfirmState({
|
||||
title: "AI Tools",
|
||||
message: "Add at least one prompt template in Settings first.",
|
||||
actionLabel: "OK",
|
||||
onConfirm: () => {}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const menu = await Menu.new({
|
||||
items: [
|
||||
@@ -2151,6 +2142,14 @@ export default function App() {
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.defaultPrevented) return;
|
||||
if (customPromptOpen && event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
setCustomPromptOpen(false);
|
||||
return;
|
||||
}
|
||||
if (customPromptOpen) {
|
||||
return;
|
||||
}
|
||||
const isFind =
|
||||
(event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "f";
|
||||
if (isFind && !settingsOpen && !confirmState) {
|
||||
@@ -2176,7 +2175,7 @@ export default function App() {
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [confirmState, handleSaveVersion, openDocumentSearch, settingsOpen]);
|
||||
}, [confirmState, customPromptOpen, handleSaveVersion, openDocumentSearch, settingsOpen]);
|
||||
|
||||
const renderTextItem = (text: Text) => (
|
||||
<div
|
||||
@@ -2528,7 +2527,7 @@ export default function App() {
|
||||
disabled={
|
||||
isConverting
|
||||
? false
|
||||
: !ollamaModel || isViewingHistory || !hasText || aiPromptTemplates.length === 0
|
||||
: !ollamaModel || isViewingHistory || !hasText
|
||||
}
|
||||
>
|
||||
{isConverting ? "Cancel AI Edit" : "AI Tools"}
|
||||
@@ -2899,6 +2898,53 @@ export default function App() {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{customPromptOpen ? (
|
||||
<div className="modal">
|
||||
<div className="modal__overlay" onClick={() => setCustomPromptOpen(false)} />
|
||||
<div className="modal__card modal__card--wide" role="dialog" aria-modal="true">
|
||||
<div className="modal__title">Custom Prompt</div>
|
||||
<div className="modal__message">
|
||||
Tell the AI what it should do with the current text.
|
||||
</div>
|
||||
<textarea
|
||||
className="modal__textarea"
|
||||
value={customPromptText}
|
||||
onChange={(event) => setCustomPromptText(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "enter") {
|
||||
event.preventDefault();
|
||||
handleRunCustomPrompt().catch((error) => {
|
||||
console.error("Failed to run custom prompt", error);
|
||||
});
|
||||
} else if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
setCustomPromptOpen(false);
|
||||
}
|
||||
}}
|
||||
placeholder="Example: Turn this into a short release note with bullet points."
|
||||
autoFocus
|
||||
/>
|
||||
<div className="modal__hint">Press Cmd/Ctrl+Enter to run.</div>
|
||||
<div className="modal__actions">
|
||||
<button className="button" onClick={() => setCustomPromptOpen(false)}>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
className="button button--primary"
|
||||
onClick={() => {
|
||||
handleRunCustomPrompt().catch((error) => {
|
||||
console.error("Failed to run custom prompt", error);
|
||||
});
|
||||
}}
|
||||
disabled={customPromptText.trim().length === 0}
|
||||
>
|
||||
Run Prompt
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{confirmState ? (
|
||||
<div className="modal">
|
||||
<div className="modal__overlay" onClick={() => setConfirmState(null)} />
|
||||
|
||||
@@ -963,6 +963,10 @@ body:not([data-theme="light"]) .markdown-preview {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.modal__card--wide {
|
||||
width: min(560px, 92vw);
|
||||
}
|
||||
|
||||
.modal__title {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
@@ -971,6 +975,24 @@ body:not([data-theme="light"]) .markdown-preview {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.modal__textarea {
|
||||
width: 100%;
|
||||
min-height: 170px;
|
||||
resize: vertical;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-input);
|
||||
color: var(--ink);
|
||||
caret-color: var(--ink);
|
||||
padding: 12px 14px;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.modal__hint {
|
||||
font-size: 0.8rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.modal__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
Reference in New Issue
Block a user