From 3c2115ce2f6fb647ee03d9b7997661ea97302e9b Mon Sep 17 00:00:00 2001 From: Jiahao Zhu Date: Sat, 24 Jan 2026 03:56:28 +0800 Subject: [PATCH] feat(templates): add auto-run toggle for templates (#9) Add ability to control whether templates automatically run when selected or just populate the prompt input for manual review. Changes: - Add auto-run toggle (On/Off) in template creation modal with hint text - Persist autoRun setting in Rust backend (workspace/templates.rs) - Update template YAML frontmatter serialization to include autoRun field - Set default workspace templates to auto-run enabled - Fix bug where YAML frontmatter parser returned string values ("true"/"false") instead of booleans, causing autoRun: false to be ignored When autoRun is disabled, the template prompt is populated in the input field but not automatically sent, allowing users to review/edit before submitting. --- packages/app/src/app/app.tsx | 15 +++- .../app/src/app/components/template-modal.tsx | 21 ++++++ packages/app/src/app/pages/dashboard.tsx | 8 +- packages/app/src/app/pages/session.tsx | 12 +-- packages/app/src/app/pages/templates.tsx | 10 +-- packages/app/src/app/template-state.ts | 73 +++++++++++++++---- packages/app/src/app/types.ts | 1 + packages/app/src/app/utils/templates.ts | 6 ++ packages/app/src/i18n/locales/en.ts | 4 +- packages/app/src/i18n/locales/zh.ts | 4 +- packages/desktop/src-tauri/Cargo.lock | 2 +- packages/desktop/src-tauri/src/types.rs | 6 ++ .../desktop/src-tauri/src/workspace/files.rs | 3 + .../src-tauri/src/workspace/templates.rs | 2 + 14 files changed, 132 insertions(+), 35 deletions(-) diff --git a/packages/app/src/app/app.tsx b/packages/app/src/app/app.tsx index 55ff078a..fe4cc761 100644 --- a/packages/app/src/app/app.tsx +++ b/packages/app/src/app/app.tsx @@ -642,12 +642,14 @@ export default function App() { setTemplateDraftPrompt, templateDraftScope, setTemplateDraftScope, + templateDraftAutoRun, + setTemplateDraftAutoRun, workspaceTemplates, globalTemplates, openTemplateModal, saveTemplate, deleteTemplate, - runTemplate, + applyTemplate, loadWorkspaceTemplates, } = templateState; @@ -1352,8 +1354,15 @@ export default function App() { applyThemeMode(isDark ? "dark" : "light"); }); + // Listen for setPrompt events from template system (when autoRun is disabled) + const handleSetPrompt = (e: CustomEvent) => { + setPrompt(e.detail); + }; + window.addEventListener("openwork:setPrompt", handleSetPrompt as EventListener); + onCleanup(() => { unsubscribeTheme(); + window.removeEventListener("openwork:setPrompt", handleSetPrompt as EventListener); }); createEffect(() => { @@ -1844,7 +1853,7 @@ export default function App() { setTemplateDraftScope(scope); }, openTemplateModal, - runTemplate, + applyTemplate, deleteTemplate, refreshSkills: (options?: { force?: boolean }) => refreshSkills(options).catch(() => undefined), refreshPlugins: (scopeOverride?: PluginScope) => @@ -2083,12 +2092,14 @@ export default function App() { description={templateDraftDescription()} prompt={templateDraftPrompt()} scope={templateDraftScope()} + autoRun={templateDraftAutoRun()} onClose={() => setTemplateModalOpen(false)} onSave={saveTemplate} onTitleChange={setTemplateDraftTitle} onDescriptionChange={setTemplateDraftDescription} onPromptChange={setTemplateDraftPrompt} onScopeChange={setTemplateDraftScope} + onAutoRunChange={setTemplateDraftAutoRun} /> void; onSave: () => void; onTitleChange: (value: string) => void; onDescriptionChange: (value: string) => void; onPromptChange: (value: string) => void; onScopeChange: (value: "workspace" | "global") => void; + onAutoRunChange: (value: boolean) => void; }; export default function TemplateModal(props: TemplateModalProps) { @@ -88,6 +90,25 @@ export default function TemplateModal(props: TemplateModalProps) { />
{translate("templates.prompt_hint")}
+ + {/* Auto-run Toggle */} +
+
+
{translate("templates.auto_run_label")}
+
{translate("templates.auto_run_hint")}
+
+ +
diff --git a/packages/app/src/app/pages/dashboard.tsx b/packages/app/src/app/pages/dashboard.tsx index 557cf4a1..8060612c 100644 --- a/packages/app/src/app/pages/dashboard.tsx +++ b/packages/app/src/app/pages/dashboard.tsx @@ -78,7 +78,7 @@ export type DashboardViewProps = { setTemplateDraftScope: (value: "workspace" | "global") => void; openTemplateModal: () => void; resetTemplateDraft?: (scope?: "workspace" | "global") => void; - runTemplate: (template: WorkspaceTemplate) => void; + applyTemplate: (template: WorkspaceTemplate) => void; deleteTemplate: (templateId: string) => void; refreshSkills: (options?: { force?: boolean }) => void; refreshPlugins: (scopeOverride?: PluginScope) => void; @@ -536,7 +536,7 @@ export default function DashboardView(props: DashboardViewProps) { {(t) => ( )} @@ -697,7 +697,7 @@ export default function DashboardView(props: DashboardViewProps) { setTemplateDraftScope={props.setTemplateDraftScope} openTemplateModal={props.openTemplateModal} resetTemplateDraft={props.resetTemplateDraft} - runTemplate={props.runTemplate} + applyTemplate={props.applyTemplate} deleteTemplate={props.deleteTemplate} /> diff --git a/packages/app/src/app/pages/session.tsx b/packages/app/src/app/pages/session.tsx index 01c055be..4b870906 100644 --- a/packages/app/src/app/pages/session.tsx +++ b/packages/app/src/app/pages/session.tsx @@ -117,7 +117,7 @@ export default function SessionView(props: SessionViewProps) { const [prevArtifactCount, setPrevArtifactCount] = createSignal(0); const [prevFileCount, setPrevFileCount] = createSignal(0); const [isInitialLoad, setIsInitialLoad] = createSignal(true); - + const pendingArtifactRafIds = new Set(); onMount(() => { @@ -195,7 +195,7 @@ export default function SessionView(props: SessionViewProps) { } setPrevArtifactCount(count); }); - + createEffect(() => { const files = props.workingFiles; const count = files.length; @@ -589,7 +589,7 @@ export default function SessionView(props: SessionViewProps) { } `} - +
@@ -602,7 +602,7 @@ export default function SessionView(props: SessionViewProps) {
- (messagesEndEl = el)} />
- + chatContainerEl} messages={props.messages} /> @@ -624,7 +624,7 @@ export default function SessionView(props: SessionViewProps) {
- void; openTemplateModal: () => void; resetTemplateDraft?: (scope?: "workspace" | "global") => void; - runTemplate: (template: WorkspaceTemplate) => void; + applyTemplate: (template: WorkspaceTemplate) => void; deleteTemplate: (templateId: string) => void; }; @@ -68,9 +68,9 @@ export default function TemplatesView(props: TemplatesViewProps) {
{formatRelativeTime(t.createdAt)}
-
-