Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> | |
| <title>Agent Zero</title> | |
| <link rel="icon" type="image/svg+xml" href="public/favicon.svg"> | |
| <link rel="stylesheet" href="index.css"> | |
| <link rel="stylesheet" href="css/messages.css"> | |
| <link rel="stylesheet" href="components/messages/action-buttons/simple-action-buttons.css"> | |
| <link rel="stylesheet" href="css/toast.css"> | |
| <link rel="stylesheet" href="css/settings.css"> | |
| <link rel="stylesheet" href="css/modals.css"> | |
| <link rel="stylesheet" href="css/speech.css"> | |
| <link rel="stylesheet" href="css/scheduler-datepicker.css"> | |
| <link rel="stylesheet" href="css/notification.css"> | |
| <link rel="stylesheet" href="css/buttons.css"> | |
| <!-- Flatpickr for datetime picker --> | |
| <link rel="stylesheet" href="vendor/flatpickr/flatpickr.min.css"> | |
| <script src="vendor/flatpickr/flatpickr.min.js"></script> | |
| <script> | |
| globalThis.safeCall = function (name, ...args) { | |
| if (window[name]) window[name](...args) | |
| } | |
| </script> | |
| <!-- Pre-initialize schedulerSettings to ensure Alpine doesn't miss it --> | |
| <script> | |
| // Pre-define schedulerSettings skeleton to ensure it's available to Alpine | |
| globalThis.schedulerSettings = function () { | |
| return { | |
| tasks: [], | |
| isLoading: true, | |
| selectedTask: null, | |
| expandedTaskId: null, | |
| sortField: 'name', | |
| sortDirection: 'asc', | |
| filterType: 'all', | |
| filterState: 'all', | |
| pollingInterval: null, | |
| pollingActive: false, | |
| editingTask: { | |
| name: '', | |
| type: 'scheduled', | |
| state: 'idle', | |
| schedule: { | |
| minute: '*', | |
| hour: '*', | |
| day: '*', | |
| month: '*', | |
| weekday: '*', | |
| timezone: '' | |
| }, | |
| token: '', | |
| plan: { | |
| todo: [], | |
| in_progress: null, | |
| done: [] | |
| }, | |
| system_prompt: '', | |
| prompt: '', | |
| attachments: [] | |
| }, | |
| isCreating: false, | |
| isEditing: false, | |
| showLoadingState: false, | |
| viewMode: 'list', | |
| selectedTaskForDetail: null, | |
| attachmentsText: '', | |
| filteredTasks: [], | |
| // Minimal init to avoid errors | |
| init() { | |
| console.log('Basic schedulerSettings initialized'); | |
| // Watch for task type changes | |
| this.$watch('editingTask.type', (newType) => { | |
| if (newType === 'planned') { | |
| // When switching to planned task type, initialize the datetime picker | |
| this.$nextTick(() => { | |
| if (this.initFlatpickr) { | |
| if (this.isCreating) { | |
| this.initFlatpickr('create'); | |
| } else if (this.isEditing) { | |
| this.initFlatpickr('edit'); | |
| } | |
| } | |
| }); | |
| } | |
| }); | |
| } | |
| }; | |
| }; | |
| </script> | |
| <!-- Load module scripts first --> | |
| <script type="module" src="js/scheduler.js"></script> | |
| <script type="module" src="index.js"></script> | |
| <!-- Bootstrap JS (only for logic, importing bundled CSS => UI conflicts) --> | |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" | |
| integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" | |
| crossorigin="anonymous"></script> | |
| <!-- Then load Alpine.js --> | |
| <!-- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js"></script> --> | |
| <script type="module" src="js/initFw.js"></script> | |
| <script src="vendor/ace-min/ace.js"></script> | |
| <link href="vendor/ace-min/ace.min.css" rel="stylesheet"> | |
| <!-- KaTeX CSS --> | |
| <link rel="stylesheet" href="vendor/katex/katex.min.css" crossorigin="anonymous"> | |
| <!-- KaTeX javascript --> | |
| <script defer src="vendor/katex/katex.min.js" crossorigin="anonymous"></script> | |
| <script defer src="vendor/katex/katex.auto-render.min.js" crossorigin="anonymous"></script> | |
| <!-- QR Code Library --> | |
| <script src="vendor/qrcode.min.js"></script> | |
| <!-- Google Icons --> | |
| <link rel="stylesheet" href="vendor/google/google-icons.css" /> | |
| <!-- Link the PWA manifest file --> | |
| <link rel="manifest" href="js/manifest.json"> | |
| <!-- Non-module scripts after Alpine.js --> | |
| <script type="text/javascript" src="js/settings.js"></script> | |
| <script> | |
| </script> | |
| </head> | |
| <body class="dark-mode device-pointer" x-data> | |
| <div class="container"> | |
| <!-- Sidebar Overlay --> | |
| <div id="sidebar-overlay" x-data> | |
| <template x-if="$store.sidebar"> | |
| <div class="sidebar-overlay" :class="{'visible': $store.sidebar.isOpen && $store.sidebar.isMobile()}" @click="$store.sidebar.close()"></div> | |
| </template> | |
| </div> | |
| </template> | |
| <!-- Left Sidebar (Header Icons, Quick Actions, Tabs, Chats, Tasks) --> | |
| <x-component path="sidebar/left-sidebar.html"></x-component> | |
| <!-- Right Panel (Message History and Input Section) --> | |
| <div id="right-panel" class="panel" > | |
| <!-- top section with time, status etc. --> | |
| <x-component path="chat/top-section/chat-top.html"></x-component> | |
| <!-- Welcome Screen Component --> | |
| <div x-data x-show="$store.welcomeStore && $store.welcomeStore.isVisible"> | |
| <x-component path="welcome/welcome-screen.html"></x-component> | |
| </div> | |
| <!-- Message History (actual messages) --> | |
| <div id="chat-history" x-data x-show="!$store.welcomeStore || !$store.welcomeStore.isVisible"></div> | |
| <!-- NEW: Toast Stack Component --> | |
| <div style="position: relative; height: 0;"> | |
| <x-component path="notifications/notification-toast-stack.html"></x-component> | |
| </div> | |
| <div id="toast" class="toast"> | |
| <div class="toast__content"> | |
| <div class="toast__title"></div> | |
| <div class="toast__separator"></div> | |
| <div class="toast__message"></div> | |
| </div> | |
| <button class="toast__copy" style="display: none;">Copy</button> | |
| <button class="toast__close">Close</button> | |
| </div> | |
| <!-- Chat Input Area --> | |
| <div x-show="!$store.welcomeStore || !$store.welcomeStore.isVisible"> | |
| <!-- Progress Bar --> | |
| <x-component path="chat/input/progress.html"></x-component> | |
| <!-- Input Section --> | |
| <x-component path="chat/input/chat-bar.html"></x-component> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="settingsModal" x-data="settingsModalProxy"> | |
| <template x-teleport="body"> | |
| <div x-show="isOpen" class="modal-overlay" @click.self="handleCancel()" | |
| @keydown.escape.window="isOpen && handleCancel()" x-transition> | |
| <div class="modal-container"> | |
| <div class="modal-header"> | |
| <h2 x-text="settings.title"></h2> | |
| <button class="modal-close" @click="handleCancel()">×</button> | |
| </div> | |
| <div class="modal-content"> | |
| <!-- Tab Navigation --> | |
| <div class="settings-tabs-container"> | |
| <div class="settings-tabs"> | |
| <div class="settings-tab" :class="{'active': activeTab === 'agent'}" | |
| @click="switchTab('agent')" title="Agent Settings">Agent Settings</div> | |
| <div class="settings-tab" :class="{'active': activeTab === 'external'}" | |
| @click="switchTab('external')" title="External Services">External Services</div> | |
| <div class="settings-tab" :class="{'active': activeTab === 'mcp'}" | |
| @click="switchTab('mcp')" title="MCP">MCP/A2A</div> | |
| <div class="settings-tab" :class="{'active': activeTab === 'developer'}" | |
| @click="switchTab('developer')" title="Developer">Developer</div> | |
| <div class="settings-tab" :class="{'active': activeTab === 'scheduler'}" | |
| @click="switchTab('scheduler')" title="Task Scheduler">Task Scheduler</div> | |
| <div class="settings-tab" :class="{'active': activeTab === 'backup'}" | |
| @click="switchTab('backup')" title="Backup & Restore">Backup & Restore</div> | |
| </div> | |
| </div> | |
| <!-- Display settings sections for agent, external, developer, mcp, backup tabs --> | |
| <div id="settings-sections" x-show="activeTab !== 'scheduler'"> | |
| <nav> | |
| <ul> | |
| <template x-for="(section, index) in filteredSections" :key="section.title"> | |
| <li> | |
| <a :href="'#section' + (index + 1)"> | |
| <img :src="'/public/' + section.id +'.svg'" :alt="section.title"> | |
| <span x-text="section.title"></span> | |
| </a> | |
| </li> | |
| </template> | |
| <!-- Tunnel navigation entry - only visible in the External Services tab --> | |
| <li x-show="activeTab === 'external'"> | |
| <a href="#section-tunnel"> | |
| <img src="/public/tunnel.svg" alt="Tunnel"> | |
| <span>Flare Tunnel</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </nav> | |
| <template x-for="(section, sectionIndex) in filteredSections" :key="sectionIndex"> | |
| <div :id="'section' + (sectionIndex + 1)" class="section"> | |
| <div class="section-title" x-text="section.title"></div> | |
| <div class="section-description" x-html="section.description"></div> | |
| <template x-for="(field, fieldIndex) in section.fields.filter(f => !f.hidden)" | |
| :key="fieldIndex"> | |
| <div :class="{'field': true, 'field-full': field.type === 'textarea'}"> | |
| <div class="field-label" x-show="field.title || field.description"> | |
| <div class="field-title" x-text="field.title" x-show="field.title"> | |
| </div> | |
| <div class="field-description" x-html="field.description || ''" | |
| x-show="field.description"></div> | |
| </div> | |
| <div class="field-control"> | |
| <!-- Input field --> | |
| <template x-if="field.type === 'text'"> | |
| <input type="text" :class="field.classes" :value="field.value" | |
| :readonly="field.readonly === true" | |
| @input="field.value = $event.target.value"> | |
| </template> | |
| <!-- Number field --> | |
| <template x-if="field.type === 'number'"> | |
| <input type="number" :class="field.classes" :value="field.value" | |
| :readonly="field.readonly === true" | |
| @input="field.value = $event.target.value" :min="field.min" | |
| :max="field.max" :step="field.step"> | |
| </template> | |
| <!-- Password field --> | |
| <template x-if="field.type === 'password'"> | |
| <input type="password" :class="field.classes" :value="field.value" | |
| :readonly="field.readonly === true" :id="field.id" | |
| autocomplete="off" @input="field.value = $event.target.value"> | |
| </template> | |
| <!-- Textarea field --> | |
| <template x-if="field.type === 'textarea'"> | |
| <textarea :class="field.classes" :value="field.value" | |
| :readonly="field.readonly === true" | |
| @input="field.value = $event.target.value" | |
| :style="field.style"></textarea> | |
| </template> | |
| <!-- Switch field --> | |
| <template x-if="field.type === 'switch'"> | |
| <label class="toggle"> | |
| <input type="checkbox" :checked="field.value" | |
| :disabled="field.readonly === true" | |
| @change="field.value = $event.target.checked"> | |
| <span class="toggler"></span> | |
| </label> | |
| </template> | |
| <!-- Range field --> | |
| <template x-if="field.type === 'range'"> | |
| <div class="field-control"> | |
| <input type="range" :min="field.min" :max="field.max" | |
| :step="field.step" :value="field.value" | |
| :disabled="field.readonly === true" | |
| @input="field.value = $event.target.value" | |
| :class="field.classes"> | |
| <span class="range-value" x-text="field.value"></span> | |
| </div> | |
| </template> | |
| <!-- Button field --> | |
| <template x-if="field.type === 'button'"> | |
| <button class="btn btn-field" :class="field.classes" | |
| :disabled="field.readonly === true" | |
| @click="handleFieldButton(field)" x-text="field.value"></button> | |
| </template> | |
| <!-- Select field --> | |
| <template x-if="field.type === 'select'"> | |
| <select :class="field.classes" x-model="field.value" | |
| :disabled="field.readonly === true"> | |
| <template x-for="option in field.options" :key="option.value"> | |
| <option :value="option.value" x-text="option.label" | |
| :selected="option.value === field.value"></option> | |
| </template> | |
| </select> | |
| </template> | |
| <!-- HTML field --> | |
| <template x-if="field.type === 'html'"> | |
| <div :class="field.classes" x-html="field.value"></div> | |
| </template> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| </template> | |
| <!-- Tunnel section content - only visible in External Services tab --> | |
| <div id="section-tunnel" class="section" x-show="activeTab === 'external'"> | |
| <x-component path="settings/tunnel/tunnel-section.html" /> | |
| </div> | |
| </div> | |
| <!-- Task Scheduler Tab Content --> | |
| <div id="scheduler-tab-content" x-show="activeTab === 'scheduler'" x-cloak> | |
| <!-- Settings section structure for task scheduler --> | |
| <nav> | |
| <ul> | |
| <li> | |
| <a href="#section-task-scheduler"> | |
| <img src="/public/task_scheduler.svg" alt="Task Scheduler"> | |
| <span>Task Scheduler</span> | |
| </a> | |
| </li> | |
| </ul> | |
| </nav> | |
| <div id="section-task-scheduler" class="section" x-data="schedulerSettings"> | |
| <div class="section-title">Task Scheduler</div> | |
| <div class="section-description">Manage scheduled tasks and automated processes for | |
| Agent Zero.</div> | |
| <!-- Create Task Form --> | |
| <div class="scheduler-form" x-show="isCreating"> | |
| <div class="scheduler-form-header"> | |
| <div class="scheduler-form-title">Create New Task</div> | |
| <div class="scheduler-form-actions"> | |
| <button class="btn btn-ok btn-field" @click="saveTask()"> | |
| Save | |
| </button> | |
| <button class="btn btn-cancel" @click="cancelEdit()"> | |
| Cancel | |
| </button> | |
| </div> | |
| </div> | |
| <div class="scheduler-form-grid"> | |
| <!-- Task Name --> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Task Name</label> | |
| <div class="scheduler-form-help">A unique name to identify this task | |
| </div> | |
| </div> | |
| <input type="text" x-model="editingTask.name" placeholder="Enter task name"> | |
| </div> | |
| <!-- Task Type Selection --> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Type</label> | |
| <div class="scheduler-form-help">Task execution method</div> | |
| </div> | |
| <select x-model="editingTask.type"> | |
| <option value="scheduled">Scheduled (Cron)</option> | |
| <option value="adhoc">Ad-hoc (Manual)</option> | |
| <option value="planned">Planned (Specific Times)</option> | |
| </select> | |
| </div> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Project</label> | |
| <div class="scheduler-form-help" x-text="editingTask.dedicated_context ? 'Inherited from the active chat project.' : 'Mirrors the shared context project.'"></div> | |
| </div> | |
| <template x-if="isCreating"> | |
| <div class="project-selector"> | |
| <!-- <span class="project-color-ball" | |
| :style="editingTask.project?.color ? { backgroundColor: editingTask.project.color } : { border: '1px solid var(--color-border)' }"></span> --> | |
| <select class="scheduler-project-select" | |
| x-model="selectedProjectSlug" | |
| @change="onProjectSelect($event.target.value)"> | |
| <option value="">No project</option> | |
| <template x-for="proj in projectOptions" :key="proj.name"> | |
| <option :value="proj.name" x-text="proj.title"></option> | |
| </template> | |
| </select> | |
| </div> | |
| </template> | |
| <template x-if="!isCreating"> | |
| <div class="project-display"> | |
| <span class="project-color-ball" | |
| :style="editingTask.project?.color ? { backgroundColor: editingTask.project.color } : { border: '1px solid var(--color-border)' }"></span> | |
| <span x-text="formatProjectLabel(editingTask.project)"></span> | |
| </div> | |
| </template> | |
| </div> | |
| <!-- Task State in Create Form - Add after Task Type --> | |
| <div class="scheduler-form-field" x-show="isCreating"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">State</label> | |
| <div class="scheduler-form-help">Select the initial state of the task | |
| </div> | |
| </div> | |
| <div> | |
| <div class="scheduler-state-selector"> | |
| <span class="scheduler-status-badge scheduler-status-idle" | |
| :class="{'scheduler-status-selected': editingTask.state === 'idle'}" | |
| @click="editingTask.state = 'idle'">idle</span> | |
| <span class="scheduler-status-badge scheduler-status-running" | |
| :class="{'scheduler-status-selected': editingTask.state === 'running'}" | |
| @click="editingTask.state = 'running'">running</span> | |
| <span class="scheduler-status-badge scheduler-status-disabled" | |
| :class="{'scheduler-status-selected': editingTask.state === 'disabled'}" | |
| @click="editingTask.state = 'disabled'">disabled</span> | |
| <span class="scheduler-status-badge scheduler-status-error" | |
| :class="{'scheduler-status-selected': editingTask.state === 'error'}" | |
| @click="editingTask.state = 'error'">error</span> | |
| </div> | |
| <div class="scheduler-state-explanation"> | |
| <span x-show="editingTask.state === 'idle'"><strong>idle</strong>: | |
| ready to run</span> | |
| <span | |
| x-show="editingTask.state === 'running'"><strong>running</strong>: | |
| currently executing</span> | |
| <span | |
| x-show="editingTask.state === 'disabled'"><strong>disabled</strong>: | |
| won't execute automatically</span> | |
| <span x-show="editingTask.state === 'error'"><strong>error</strong>: | |
| task encountered an error</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Schedule (only for scheduled tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask.type === 'scheduled'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Schedule</label> | |
| <div class="scheduler-form-help">Cron schedule for automated execution | |
| (minute hour day month weekday)</div> | |
| </div> | |
| <div class="scheduler-schedule-builder"> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Minute</span> | |
| <input type="text" x-model="editingTask.schedule.minute" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Hour</span> | |
| <input type="text" x-model="editingTask.schedule.hour" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Day</span> | |
| <input type="text" x-model="editingTask.schedule.day" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Month</span> | |
| <input type="text" x-model="editingTask.schedule.month" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Weekday</span> | |
| <input type="text" x-model="editingTask.schedule.weekday" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Plan (for planned tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask.type === 'planned'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Plan</label> | |
| <div class="scheduler-form-help">Specific execution times for this task | |
| </div> | |
| </div> | |
| <div class="scheduler-plan-builder"> | |
| <div class="scheduler-plan-todo"> | |
| <span class="scheduler-plan-label">Upcoming Executions</span> | |
| <div class="scheduler-todo-list"> | |
| <template | |
| x-if="editingTask.plan && Array.isArray(editingTask.plan.todo) && editingTask.plan.todo.length > 0"> | |
| <template x-for="(time, index) in editingTask.plan.todo" | |
| :key="index"> | |
| <div class="scheduler-todo-item"> | |
| <span x-text="formatDate(time)"></span> | |
| <button | |
| @click.prevent="editingTask.plan.todo.splice(index, 1)" | |
| class="scheduler-todo-remove"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| width="16" height="16" viewBox="0 0 24 24" | |
| fill="none" stroke="currentColor" | |
| stroke-width="2" stroke-linecap="round" | |
| stroke-linejoin="round"> | |
| <line x1="18" y1="6" x2="6" y2="18"></line> | |
| <line x1="6" y1="6" x2="18" y2="18"></line> | |
| </svg> | |
| </button> | |
| </div> | |
| </template> | |
| </template> | |
| <template | |
| x-if="!editingTask.plan || !Array.isArray(editingTask.plan.todo) || editingTask.plan.todo.length === 0"> | |
| <div class="scheduler-empty-plan"> | |
| No scheduled execution times yet. Add one below. | |
| </div> | |
| </template> | |
| <div class="scheduler-add-todo"> | |
| <!-- Create form planned task input --> | |
| <input type="text" id="newPlannedTime-create" | |
| x-ref="plannedTimeCreate" | |
| class="scheduler-flatpickr-input" | |
| placeholder="Select date and time"> | |
| <!-- Create Task Form Add Time Button --> | |
| <button @click.prevent=" | |
| const input = $refs.plannedTimeCreate; | |
| if (!input) { | |
| console.error('Input reference not found for plannedTimeCreate'); | |
| return; | |
| } | |
| // Ensure plan structure exists | |
| if (!editingTask.plan) { | |
| editingTask.plan = { todo: [], in_progress: null, done: [] }; | |
| } | |
| if (!Array.isArray(editingTask.plan.todo)) { | |
| editingTask.plan.todo = []; | |
| } | |
| // Get date from Flatpickr if available | |
| let selectedDate; | |
| if (input._flatpickr && input._flatpickr.selectedDates.length > 0) { | |
| selectedDate = input._flatpickr.selectedDates[0]; | |
| } else if (input.value) { | |
| selectedDate = new Date(input.value); | |
| } | |
| if (!selectedDate || isNaN(selectedDate.getTime())) { | |
| alert('Please select a valid date and time'); | |
| return; | |
| } | |
| // Convert to ISO string and add to plan | |
| editingTask.plan.todo.push(selectedDate.toISOString()); | |
| // Sort by date (earliest first) | |
| editingTask.plan.todo.sort(); | |
| // Clear the input | |
| if (input._flatpickr) { | |
| input._flatpickr.clear(); | |
| } else { | |
| input.value = ''; | |
| } | |
| " class="scheduler-add-todo-button"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" | |
| height="16" viewBox="0 0 24 24" fill="none" | |
| stroke="currentColor" stroke-width="2" | |
| stroke-linecap="round" stroke-linejoin="round" | |
| style="margin-right: 4px;"> | |
| <line x1="12" y1="5" x2="12" y2="19"></line> | |
| <line x1="5" y1="12" x2="19" y2="12"></line> | |
| </svg> | |
| Add Time | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Token (for ad-hoc tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask.type === 'adhoc'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Token</label> | |
| <div class="scheduler-form-help">Token used to trigger this task | |
| externally</div> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" x-model="editingTask.token" | |
| placeholder="Token for ad-hoc task"> | |
| <button class="scheduler-task-action" | |
| @click="editingTask.token = generateRandomToken()"> | |
| Generate | |
| </button> | |
| </div> | |
| </div> | |
| <!-- System Prompt --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">System Prompt</label> | |
| <div class="scheduler-form-help">System-level instructions for the | |
| assistant</div> | |
| </div> | |
| <textarea x-model="editingTask.system_prompt" | |
| placeholder="System instructions for the AI"></textarea> | |
| </div> | |
| <!-- User Prompt --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">User Prompt</label> | |
| <div class="scheduler-form-help">The main task prompt that will be | |
| executed</div> | |
| </div> | |
| <textarea x-model="editingTask.prompt" | |
| placeholder="User message for the AI"></textarea> | |
| </div> | |
| <!-- Attachments Field --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Attachments</label> | |
| <div class="scheduler-form-help">Container file paths or URLs, one per | |
| line</div> | |
| </div> | |
| <textarea x-model="attachmentsText" | |
| placeholder="Enter file paths or URLs, one per line"></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Edit Task Form --> | |
| <div class="scheduler-form" x-show="isEditing"> | |
| <div class="scheduler-form-header"> | |
| <div class="scheduler-form-title">Edit Task</div> | |
| <div class="scheduler-form-actions"> | |
| <button class="btn btn-ok btn-field" @click="saveTask()"> | |
| Save | |
| </button> | |
| <button class="btn btn-cancel" @click="cancelEdit()"> | |
| Cancel | |
| </button> | |
| </div> | |
| </div> | |
| <div class="scheduler-form-grid"> | |
| <!-- Task Name --> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Task Name</label> | |
| <div class="scheduler-form-help">A unique name to identify this task | |
| </div> | |
| </div> | |
| <input type="text" x-model="editingTask.name" placeholder="Enter task name"> | |
| </div> | |
| <!-- Task Type (disabled when editing) --> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Task Type</label> | |
| <div class="scheduler-form-help">Task type cannot be changed after | |
| creation</div> | |
| </div> | |
| <select x-model="editingTask.type" disabled> | |
| <option value="scheduled">Scheduled Task</option> | |
| <option value="adhoc">Ad-hoc Task</option> | |
| <option value="planned">Planned Task</option> | |
| </select> | |
| </div> | |
| <div class="scheduler-form-field"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Project</label> | |
| <div class="scheduler-form-help" x-text="editingTask.dedicated_context ? 'Dedicated tasks inherit the active chat project.' : 'Shared tasks mirror the project assigned to their context.'"></div> | |
| </div> | |
| <div class="project-display"> | |
| <span class="project-color-ball" :style="editingTask.project?.color ? { backgroundColor: editingTask.project.color } : { border: '1px solid var(--color-border)' }"></span> | |
| <span x-text="formatProjectLabel(editingTask.project)"></span> | |
| </div> | |
| </div> | |
| <!-- Task State in Edit Form - Add after Task Type --> | |
| <div class="scheduler-form-field" x-show="isEditing"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">State</label> | |
| <div class="scheduler-form-help">Change the task's state</div> | |
| </div> | |
| <div> | |
| <div class="scheduler-state-selector"> | |
| <span class="scheduler-status-badge scheduler-status-idle" | |
| :class="{'scheduler-status-selected': editingTask.state === 'idle'}" | |
| @click="editingTask.state = 'idle'">idle</span> | |
| <span class="scheduler-status-badge scheduler-status-running" | |
| :class="{'scheduler-status-selected': editingTask.state === 'running'}" | |
| @click="editingTask.state = 'running'">running</span> | |
| <span class="scheduler-status-badge scheduler-status-disabled" | |
| :class="{'scheduler-status-selected': editingTask.state === 'disabled'}" | |
| @click="editingTask.state = 'disabled'">disabled</span> | |
| <span class="scheduler-status-badge scheduler-status-error" | |
| :class="{'scheduler-status-selected': editingTask.state === 'error'}" | |
| @click="editingTask.state = 'error'">error</span> | |
| </div> | |
| <div class="scheduler-state-explanation"> | |
| <span x-show="editingTask.state === 'idle'"><strong>idle</strong>: | |
| ready to run</span> | |
| <span | |
| x-show="editingTask.state === 'running'"><strong>running</strong>: | |
| currently executing</span> | |
| <span | |
| x-show="editingTask.state === 'disabled'"><strong>disabled</strong>: | |
| won't execute automatically</span> | |
| <span x-show="editingTask.state === 'error'"><strong>error</strong>: | |
| task encountered an error</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Schedule (for scheduled tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask && editingTask.type === 'scheduled'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Schedule (Cron Expression)</label> | |
| <div class="scheduler-form-help">Format: minute hour day month weekday | |
| (e.g., "* * * * *" for every minute)</div> | |
| </div> | |
| <div class="scheduler-schedule-builder"> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Minute</span> | |
| <input type="text" x-model="editingTask.schedule.minute" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Hour</span> | |
| <input type="text" x-model="editingTask.schedule.hour" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Day</span> | |
| <input type="text" x-model="editingTask.schedule.day" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Month</span> | |
| <input type="text" x-model="editingTask.schedule.month" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| <div class="scheduler-schedule-field"> | |
| <span class="scheduler-schedule-label">Weekday</span> | |
| <input type="text" x-model="editingTask.schedule.weekday" | |
| placeholder="*" maxlength="9"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Plan (for planned tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask.type === 'planned'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Plan</label> | |
| <div class="scheduler-form-help">Specific execution times for this task | |
| </div> | |
| </div> | |
| <div class="scheduler-plan-builder"> | |
| <div class="scheduler-plan-todo"> | |
| <span class="scheduler-plan-label">Upcoming Executions</span> | |
| <div class="scheduler-todo-list"> | |
| <template | |
| x-if="editingTask.plan && Array.isArray(editingTask.plan.todo) && editingTask.plan.todo.length > 0"> | |
| <template x-for="(time, index) in editingTask.plan.todo" | |
| :key="index"> | |
| <div class="scheduler-todo-item"> | |
| <span x-text="formatDate(time)"></span> | |
| <button | |
| @click.prevent="editingTask.plan.todo.splice(index, 1)" | |
| class="scheduler-todo-remove"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| width="16" height="16" viewBox="0 0 24 24" | |
| fill="none" stroke="currentColor" | |
| stroke-width="2" stroke-linecap="round" | |
| stroke-linejoin="round"> | |
| <line x1="18" y1="6" x2="6" y2="18"></line> | |
| <line x1="6" y1="6" x2="18" y2="18"></line> | |
| </svg> | |
| </button> | |
| </div> | |
| </template> | |
| </template> | |
| <template | |
| x-if="!editingTask.plan || !Array.isArray(editingTask.plan.todo) || editingTask.plan.todo.length === 0"> | |
| <div class="scheduler-empty-plan"> | |
| No scheduled execution times yet. Add one below. | |
| </div> | |
| </template> | |
| <div class="scheduler-add-todo"> | |
| <!-- Edit form planned task input --> | |
| <input type="text" id="newPlannedTime-edit" | |
| x-ref="plannedTimeEdit" | |
| class="scheduler-flatpickr-input" | |
| placeholder="Select date and time"> | |
| <!-- Edit Task Form Add Time Button --> | |
| <button @click.prevent=" | |
| const input = $refs.plannedTimeEdit; | |
| if (!input) { | |
| console.error('Input reference not found for plannedTimeEdit'); | |
| return; | |
| } | |
| // Ensure plan structure exists | |
| if (!editingTask.plan) { | |
| editingTask.plan = { todo: [], in_progress: null, done: [] }; | |
| } | |
| if (!Array.isArray(editingTask.plan.todo)) { | |
| editingTask.plan.todo = []; | |
| } | |
| // Get date from Flatpickr if available | |
| let selectedDate; | |
| if (input._flatpickr && input._flatpickr.selectedDates.length > 0) { | |
| selectedDate = input._flatpickr.selectedDates[0]; | |
| } else if (input.value) { | |
| selectedDate = new Date(input.value); | |
| } | |
| if (!selectedDate || isNaN(selectedDate.getTime())) { | |
| alert('Please select a valid date and time'); | |
| return; | |
| } | |
| // Convert to ISO string and add to plan | |
| editingTask.plan.todo.push(selectedDate.toISOString()); | |
| // Sort by date (earliest first) | |
| editingTask.plan.todo.sort(); | |
| // Clear the input | |
| if (input._flatpickr) { | |
| input._flatpickr.clear(); | |
| } else { | |
| input.value = ''; | |
| } | |
| " class="scheduler-add-todo-button"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" | |
| height="16" viewBox="0 0 24 24" fill="none" | |
| stroke="currentColor" stroke-width="2" | |
| stroke-linecap="round" stroke-linejoin="round" | |
| style="margin-right: 4px;"> | |
| <line x1="12" y1="5" x2="12" y2="19"></line> | |
| <line x1="5" y1="12" x2="19" y2="12"></line> | |
| </svg> | |
| Add Time | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Token (for ad-hoc tasks) --> | |
| <div class="scheduler-form-field full-width" | |
| x-show="editingTask.type === 'adhoc'"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Token</label> | |
| <div class="scheduler-form-help">Token used to trigger this task | |
| externally</div> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" x-model="editingTask.token" | |
| placeholder="Token for ad-hoc task"> | |
| <button class="scheduler-task-action" | |
| @click="editingTask.token = generateRandomToken()"> | |
| Generate | |
| </button> | |
| </div> | |
| </div> | |
| <!-- System Prompt --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">System Prompt</label> | |
| <div class="scheduler-form-help">System-level instructions for the | |
| assistant</div> | |
| </div> | |
| <textarea x-model="editingTask.system_prompt" | |
| placeholder="System instructions for the AI"></textarea> | |
| </div> | |
| <!-- User Prompt --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">User Prompt</label> | |
| <div class="scheduler-form-help">The main task prompt that will be | |
| executed</div> | |
| </div> | |
| <textarea x-model="editingTask.prompt" | |
| placeholder="User message for the AI"></textarea> | |
| </div> | |
| <!-- Attachments Field --> | |
| <div class="scheduler-form-field full-width"> | |
| <div class="label-help-wrapper"> | |
| <label class="scheduler-form-label">Attachments</label> | |
| <div class="scheduler-form-help">Container file paths or URLs, one per | |
| line</div> | |
| </div> | |
| <textarea x-model="attachmentsText" | |
| placeholder="Enter file paths or URLs, one per line"></textarea> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Task List View --> | |
| <div class="scheduler-container" | |
| x-show="!isCreating && !isEditing && viewMode === 'list'"> | |
| <!-- Header with Actions --> | |
| <div class="scheduler-header"> | |
| <h2>Task Management</h2> | |
| <div class="scheduler-actions"> | |
| <button class="btn btn-ok" @click="startCreateTask()"> | |
| New Task | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Filters --> | |
| <div class="scheduler-filters"> | |
| <div class="scheduler-filter-group"> | |
| <span class="scheduler-filter-label">Type:</span> | |
| <select class="scheduler-filter-select" x-model="filterType"> | |
| <option value="all">All Types</option> | |
| <option value="scheduled">Scheduled</option> | |
| <option value="adhoc">Ad-hoc</option> | |
| <option value="planned">Planned</option> | |
| </select> | |
| </div> | |
| <div class="scheduler-filter-group"> | |
| <span class="scheduler-filter-label">State:</span> | |
| <select class="scheduler-filter-select" x-model="filterState"> | |
| <option value="all">All States</option> | |
| <option value="idle">Idle</option> | |
| <option value="running">Running</option> | |
| <option value="disabled">Disabled</option> | |
| <option value="error">Error</option> | |
| </select> | |
| </div> | |
| </div> | |
| <!-- Loading State --> | |
| <!-- <div class="scheduler-loading" x-show="isLoading"> | |
| Loading tasks... | |
| </div> --> | |
| <!-- Empty State --> | |
| <div class="scheduler-empty" x-show="!isLoading && filteredTasks.length === 0" | |
| x-effect="$el.style.display = (!isLoading && filteredTasks.length === 0) ? '' : 'none'"> | |
| <!-- <div class="scheduler-empty-icon">📋</div> --> | |
| <div class="scheduler-empty-text">No tasks found</div> | |
| <button class="btn btn-ok" @click="startCreateTask()">Create your first | |
| task</button> | |
| </div> | |
| <!-- Task List Table --> | |
| <table class="scheduler-task-list" x-show="!isLoading && filteredTasks.length > 0" | |
| x-effect="$el.style.display = (!isLoading && filteredTasks.length > 0) ? '' : 'none'"> | |
| <thead> | |
| <tr> | |
| <th @click="changeSort('name')"> | |
| Name | |
| <span class="scheduler-sort-indicator" x-show="sortField === 'name'" | |
| :class="{'scheduler-sort-desc': sortDirection === 'desc'}">↑</span> | |
| </th> | |
| <th @click="changeSort('state')"> | |
| State | |
| <span class="scheduler-sort-indicator" | |
| x-show="sortField === 'state'" | |
| :class="{'scheduler-sort-desc': sortDirection === 'desc'}">↑</span> | |
| </th> | |
| <th>Type</th> | |
| <th>Project</th> | |
| <th>Schedule</th> | |
| <th @click="changeSort('last_run')"> | |
| Last Run | |
| <span class="scheduler-sort-indicator" | |
| x-show="sortField === 'last_run'" | |
| :class="{'scheduler-sort-desc': sortDirection === 'desc'}">↑</span> | |
| </th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <template x-for="task in filteredTasks" :key="task.uuid"> | |
| <tr @click="showTaskDetail(task.uuid)"> | |
| <td> | |
| <span x-text="task.name"></span> | |
| </td> | |
| <td> | |
| <span class="scheduler-status-badge" | |
| :class="getStateBadgeClass(task.state)" | |
| x-text="task.state"></span> | |
| </td> | |
| <td x-text="task.type"></td> | |
| <td> | |
| <span class="project-color-ball" | |
| :style="extractTaskProject(task)?.color ? { backgroundColor: extractTaskProject(task).color } : { border: '1px solid var(--color-border)' }"></span> | |
| <span x-text="formatTaskProject(task)"></span> | |
| </td> | |
| <td> | |
| <span x-show="task.type === 'scheduled'" | |
| x-text="formatSchedule(task)"></span> | |
| <span x-show="task.type === 'adhoc'" | |
| class="scheduler-no-schedule">—</span> | |
| <span x-show="task.type === 'planned'" | |
| x-html="formatPlan(task).replace(/\n/g, '<br>')"></span> | |
| </td> | |
| <td x-text="formatDate(task.last_run)"></td> | |
| <td @click.stop> | |
| <div class="scheduler-task-actions"> | |
| <button class="scheduler-task-action" | |
| @click="runTask(task.uuid)" title="Run Task"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| viewBox="0 0 24 24" fill="currentColor" width="16" | |
| height="16"> | |
| <path d="M8 5v14l11-7z" /> | |
| </svg> | |
| </button> | |
| <button class="scheduler-task-action" | |
| @click="resetTaskState(task.uuid)" title="Reset State"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| viewBox="0 0 24 24" fill="currentColor" width="16" | |
| height="16"> | |
| <path | |
| d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" /> | |
| </svg> | |
| </button> | |
| <button class="scheduler-task-action" | |
| @click="startEditTask(task.uuid)" title="Edit Task"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| viewBox="0 0 24 24" fill="currentColor" width="16" | |
| height="16"> | |
| <path | |
| d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" /> | |
| </svg> | |
| </button> | |
| <button class="scheduler-task-action" | |
| @click="deleteTask(task.uuid)" title="Delete Task"> | |
| <svg xmlns="http://www.w3.org/2000/svg" | |
| viewBox="0 0 24 24" fill="currentColor" width="16" | |
| height="16"> | |
| <path | |
| d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </td> | |
| </tr> | |
| </template> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Task Detail View --> | |
| <div class="scheduler-detail-view" | |
| x-show="!isCreating && !isEditing && viewMode === 'detail' && selectedTaskForDetail"> | |
| <div class="scheduler-detail-header"> | |
| <h2 class="scheduler-detail-title" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.name : ''"></h2> | |
| <div class="scheduler-status-badge" | |
| :class="selectedTaskForDetail ? getStateBadgeClass(selectedTaskForDetail.state) : ''" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.state : ''"> | |
| </div> | |
| <button class="btn btn-cancel" @click="closeTaskDetail()">Close</button> | |
| </div> | |
| <div class="scheduler-detail-content"> | |
| <div class="scheduler-details-grid"> | |
| <div class="scheduler-details-label">Type:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.type : ''"></div> | |
| <div class="scheduler-details-label">Project:</div> | |
| <div class="scheduler-details-value"> | |
| <span class="project-color-ball" | |
| :style="extractTaskProject(selectedTaskForDetail)?.color ? { backgroundColor: extractTaskProject(selectedTaskForDetail).color } : { border: '1px solid var(--color-border)' }"></span> | |
| <span x-text="selectedTaskForDetail ? formatTaskProject(selectedTaskForDetail) : 'No Project'"></span> | |
| </div> | |
| <div class="scheduler-details-label">Created:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? formatDate(selectedTaskForDetail.created_at) : ''"> | |
| </div> | |
| <div class="scheduler-details-label">Last Updated:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? formatDate(selectedTaskForDetail.updated_at) : ''"> | |
| </div> | |
| <div class="scheduler-details-label">Last Run:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? formatDate(selectedTaskForDetail.last_run) : ''"> | |
| </div> | |
| <div class="scheduler-details-label" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'scheduled'"> | |
| Schedule:</div> | |
| <div class="scheduler-details-value" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'scheduled'" | |
| x-text="selectedTaskForDetail ? formatSchedule(selectedTaskForDetail) : ''"> | |
| </div> | |
| <div class="scheduler-details-label" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'adhoc'"> | |
| Token:</div> | |
| <div class="scheduler-details-value" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'adhoc'" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.token : ''"></div> | |
| <div class="scheduler-details-label" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'planned'"> | |
| Plan:</div> | |
| <div class="scheduler-details-value" | |
| x-show="selectedTaskForDetail && selectedTaskForDetail.type === 'planned'"> | |
| <div x-show="selectedTaskForDetail && selectedTaskForDetail.plan"> | |
| <div><strong>Upcoming:</strong></div> | |
| <template | |
| x-if="selectedTaskForDetail && selectedTaskForDetail.plan && selectedTaskForDetail.plan.todo && selectedTaskForDetail.plan.todo.length > 0"> | |
| <div> | |
| <template | |
| x-for="(time, index) in selectedTaskForDetail.plan.todo" | |
| :key="index"> | |
| <div x-text="formatDate(time)"></div> | |
| </template> | |
| </div> | |
| </template> | |
| <template | |
| x-if="!selectedTaskForDetail || !selectedTaskForDetail.plan || !selectedTaskForDetail.plan.todo || selectedTaskForDetail.plan.todo.length === 0"> | |
| <div>No upcoming executions</div> | |
| </template> | |
| <div><strong>In Progress:</strong></div> | |
| <div | |
| x-text="selectedTaskForDetail && selectedTaskForDetail.plan && selectedTaskForDetail.plan.in_progress ? formatDate(selectedTaskForDetail.plan.in_progress) : 'None'"> | |
| </div> | |
| <div><strong>Completed:</strong></div> | |
| <template | |
| x-if="selectedTaskForDetail && selectedTaskForDetail.plan && selectedTaskForDetail.plan.done && selectedTaskForDetail.plan.done.length > 0"> | |
| <div> | |
| <template | |
| x-for="(time, index) in selectedTaskForDetail.plan.done" | |
| :key="index"> | |
| <div x-text="formatDate(time)"></div> | |
| </template> | |
| </div> | |
| </template> | |
| <template | |
| x-if="!selectedTaskForDetail || !selectedTaskForDetail.plan || !selectedTaskForDetail.plan.done || selectedTaskForDetail.plan.done.length === 0"> | |
| <div>No completed executions</div> | |
| </template> | |
| </div> | |
| </div> | |
| <div class="scheduler-details-label">Last Result:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail && selectedTaskForDetail.last_result ? selectedTaskForDetail.last_result : 'No results yet'"> | |
| </div> | |
| <div class="scheduler-details-label">System Prompt:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.system_prompt : ''"> | |
| </div> | |
| <div class="scheduler-details-label">User Prompt:</div> | |
| <div class="scheduler-details-value" | |
| x-text="selectedTaskForDetail ? selectedTaskForDetail.prompt : ''"> | |
| </div> | |
| <div class="scheduler-details-label">Attachments:</div> | |
| <div class="scheduler-details-value"> | |
| <template | |
| x-if="selectedTaskForDetail && selectedTaskForDetail.attachments && selectedTaskForDetail.attachments.length > 0"> | |
| <div> | |
| <template | |
| x-for="(attachment, index) in selectedTaskForDetail.attachments" | |
| :key="index"> | |
| <div x-text="attachment"></div> | |
| </template> | |
| </div> | |
| </template> | |
| <template | |
| x-if="!selectedTaskForDetail || !selectedTaskForDetail.attachments || selectedTaskForDetail.attachments.length === 0"> | |
| <div>No attachments</div> | |
| </template> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="modal-footer"> | |
| <div id="buttons-container"> | |
| <template x-for="button in settings.buttons" :key="button.id"> | |
| <button :class="button.classes" @click="handleButton(button.id)" | |
| x-text="button.title"></button> | |
| </template> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| <!-- Drag and Drop Overlay Component --> | |
| <x-component path="chat/attachments/dragDropOverlay.html"></x-component> | |
| <!-- Register Service Worker for offline support and caching --> | |
| <script> | |
| if ('serviceWorker' in navigator) { | |
| window.addEventListener('load', () => { | |
| navigator.serviceWorker.register('js/sw.js').then(registration => { | |
| console.log('SW registered: ', registration); | |
| }).catch(registrationError => { | |
| console.log('SW registration failed: ', registrationError); | |
| }); | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |