diff --git a/desktop/src-tauri/tauri.conf.json b/desktop/src-tauri/tauri.conf.json index 0224416a..9c9104a7 100644 --- a/desktop/src-tauri/tauri.conf.json +++ b/desktop/src-tauri/tauri.conf.json @@ -46,7 +46,7 @@ "externalBin": [ "binaries/claude-sidecar" ], - "resources": {}, + "resources": [], "icon": [ "icons/32x32.png", "icons/128x128.png", diff --git a/desktop/src/components/tasks/NewTaskModal.tsx b/desktop/src/components/tasks/NewTaskModal.tsx index de3b0b32..284dfefb 100644 --- a/desktop/src/components/tasks/NewTaskModal.tsx +++ b/desktop/src/components/tasks/NewTaskModal.tsx @@ -1,6 +1,7 @@ -import { useState } from 'react' +import { useState, useEffect } from 'react' import { useTaskStore } from '../../stores/taskStore' import { useSessionStore } from '../../stores/sessionStore' +import { useAdapterStore } from '../../stores/adapterStore' import { Modal } from '../shared/Modal' import { Input } from '../shared/Input' import { Button } from '../shared/Button' @@ -59,6 +60,17 @@ export function NewTaskModal({ open, onClose, editTask }: Props) { const activeSessionId = useSessionStore((s) => s.activeSessionId) const activeSession = sessions.find((s) => s.id === activeSessionId) const defaultWorkDir = activeSession?.workDir || '' + const adapterConfig = useAdapterStore((s) => s.config) + const fetchAdapterConfig = useAdapterStore((s) => s.fetchConfig) + + useEffect(() => { + if (open) fetchAdapterConfig() + }, [open]) + + const isFeishuConfigured = !!(adapterConfig.feishu?.appId && adapterConfig.feishu?.appSecret + && ((adapterConfig.feishu?.pairedUsers?.length ?? 0) > 0 || (adapterConfig.feishu?.allowedUsers?.length ?? 0) > 0)) + const isTelegramConfigured = !!(adapterConfig.telegram?.botToken + && ((adapterConfig.telegram?.pairedUsers?.length ?? 0) > 0 || (adapterConfig.telegram?.allowedUsers?.length ?? 0) > 0)) const isEdit = !!editTask const parsed = editTask ? parseCron(editTask.cron) : null @@ -82,6 +94,8 @@ export function NewTaskModal({ open, onClose, editTask }: Props) { const [permissionMode, setPermissionMode] = useState((editTask?.permissionMode as PermissionMode) || 'default') const [folderPath, setFolderPath] = useState(editTask?.folderPath || defaultWorkDir) const [useWorktree, setUseWorktree] = useState(editTask?.useWorktree || false) + const [notifyEnabled, setNotifyEnabled] = useState(editTask?.notification?.enabled || false) + const [notifyChannels, setNotifyChannels] = useState<('telegram' | 'feishu')[]>(editTask?.notification?.channels || []) const [isSubmitting, setIsSubmitting] = useState(false) // Enhanced scheduling state @@ -118,6 +132,9 @@ export function NewTaskModal({ open, onClose, editTask }: Props) { permissionMode: permissionMode !== 'default' ? permissionMode : undefined, folderPath: folderPath.trim() || undefined, useWorktree: useWorktree || undefined, + notification: notifyEnabled && notifyChannels.length > 0 + ? { enabled: true, channels: notifyChannels } + : undefined, } if (isEdit) { await updateTask(editTask!.id, payload) @@ -308,6 +325,68 @@ export function NewTaskModal({ open, onClose, editTask }: Props) { )} + {/* Notification */} +
+ + {notifyEnabled && ( +
+
+ + +
+ {!isFeishuConfigured && !isTelegramConfigured && ( +

+ warning + {t('newTask.noChannelConfigured')} +

+ )} +
+ )} +
+ {/* Cron preview */}
schedule diff --git a/desktop/src/components/tasks/PromptEditor.tsx b/desktop/src/components/tasks/PromptEditor.tsx index 865e7d80..172470a2 100644 --- a/desktop/src/components/tasks/PromptEditor.tsx +++ b/desktop/src/components/tasks/PromptEditor.tsx @@ -52,7 +52,7 @@ export function PromptEditor({
{/* Row 1: Permission + Model selectors */}
- +
diff --git a/desktop/src/components/tasks/TaskRunsPanel.tsx b/desktop/src/components/tasks/TaskRunsPanel.tsx index 026871b0..93685cf5 100644 --- a/desktop/src/components/tasks/TaskRunsPanel.tsx +++ b/desktop/src/components/tasks/TaskRunsPanel.tsx @@ -1,8 +1,7 @@ import { useEffect, useState } from 'react' import { useTaskStore } from '../../stores/taskStore' -import { useSessionStore } from '../../stores/sessionStore' import { useChatStore } from '../../stores/chatStore' -import { useUIStore } from '../../stores/uiStore' +import { useTabStore } from '../../stores/tabStore' import { useTranslation } from '../../i18n' import { parseRunOutput } from '../../lib/parseRunOutput' import type { TaskRun } from '../../types/task' @@ -53,16 +52,14 @@ const STATUS_CONFIG: Record = { export function TaskRunsPanel({ taskId, onClose, refreshKey }: Props) { const t = useTranslation() const { fetchTaskRuns } = useTaskStore() - const setActiveSession = useSessionStore((s) => s.setActiveSession) const connectToSession = useChatStore((s) => s.connectToSession) - const setActiveView = useUIStore((s) => s.setActiveView) + const openTab = useTabStore((s) => s.openTab) const [runs, setRuns] = useState([]) const [loading, setLoading] = useState(true) const [expandedId, setExpandedId] = useState(null) - const openSession = (sessionId: string) => { - setActiveView('code') - setActiveSession(sessionId) + const openSession = (sessionId: string, taskName?: string) => { + openTab(sessionId, taskName || 'Task Run') connectToSession(sessionId) } @@ -163,7 +160,7 @@ export function TaskRunsPanel({ taskId, onClose, refreshKey }: Props) { {/* Open session — only after run completes (session is empty while running) */} {run.sessionId && run.status !== 'running' && (