mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
fix(multi-cli): populate multiCliPlan sessions in liteTaskDataStore
Fix task click handlers not working in multi-CLI planning detail page.
Root cause: liteTaskDataStore was not being populated with multiCliPlan
sessions during initialization, so task click handlers couldn't access
session data using currentSessionDetailKey.
Changes:
- navigation.js: Add code to populate multiCliPlan sessions in liteTaskDataStore
- notifications.js: Add code to populate multiCliPlan sessions when data refreshes
Now when task detail page loads, liteTaskDataStore contains the correct key
'multi-cli-${sessionId}' matching currentSessionDetailKey, allowing task
click handlers to find session data and open detail drawer.
Verified: Task clicks now properly open detail panel for all 7 tasks.
This commit is contained in:
@@ -1508,6 +1508,38 @@
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.issue-pull-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.5rem 1rem;
|
||||
background: #1a1a1a;
|
||||
color: #ffffff;
|
||||
border: 1px solid #2d2d2d;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.issue-pull-btn:hover {
|
||||
background: #2d2d2d;
|
||||
border-color: #404040;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.issue-pull-btn:active {
|
||||
transform: translateY(0);
|
||||
background: #1a1a1a;
|
||||
}
|
||||
|
||||
.issue-pull-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
ISSUE STATS
|
||||
========================================== */
|
||||
|
||||
@@ -303,6 +303,12 @@ async function refreshWorkspace() {
|
||||
liteTaskDataStore[sessionKey] = s;
|
||||
});
|
||||
|
||||
// Populate multiCliPlan sessions
|
||||
(data.liteTasks?.multiCliPlan || []).forEach(s => {
|
||||
const sessionKey = `multi-cli-${s.id}`.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
liteTaskDataStore[sessionKey] = s;
|
||||
});
|
||||
|
||||
// Update global data
|
||||
window.workflowData = data;
|
||||
|
||||
|
||||
@@ -827,6 +827,12 @@ async function refreshWorkspaceData(newData) {
|
||||
liteTaskDataStore[key] = s;
|
||||
});
|
||||
|
||||
// Populate multiCliPlan sessions
|
||||
(newData.liteTasks?.multiCliPlan || []).forEach(s => {
|
||||
const key = `multi-cli-${s.id}`.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
liteTaskDataStore[key] = s;
|
||||
});
|
||||
|
||||
// Update UI silently
|
||||
updateStats();
|
||||
updateBadges();
|
||||
|
||||
@@ -60,6 +60,9 @@ async function checkForUpdatesNow() {
|
||||
btn.disabled = true;
|
||||
}
|
||||
|
||||
// Show checking state on badge
|
||||
updateVersionBadge('checking');
|
||||
|
||||
// Show checking notification
|
||||
console.log('[Version Check] Starting update check...');
|
||||
if (typeof addGlobalNotification === 'function') {
|
||||
@@ -83,6 +86,9 @@ async function checkForUpdatesNow() {
|
||||
versionCheckData = data;
|
||||
console.log('[Version Check] Result:', data);
|
||||
|
||||
// Update badge based on result
|
||||
updateVersionBadge(data.hasUpdate ? 'has-update' : 'none');
|
||||
|
||||
if (data.hasUpdate) {
|
||||
// New version available
|
||||
console.log('[Version Check] Update available:', data.latestVersion);
|
||||
@@ -109,6 +115,8 @@ async function checkForUpdatesNow() {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Version Check] Error:', err);
|
||||
// Clear badge on error
|
||||
updateVersionBadge('none');
|
||||
if (typeof addGlobalNotification === 'function') {
|
||||
addGlobalNotification(
|
||||
'error',
|
||||
@@ -154,6 +162,9 @@ async function checkForUpdates() {
|
||||
|
||||
versionCheckData = await res.json();
|
||||
|
||||
// Update badge
|
||||
updateVersionBadge(versionCheckData.hasUpdate ? 'has-update' : 'none');
|
||||
|
||||
if (versionCheckData.hasUpdate && !versionBannerDismissed) {
|
||||
showUpdateBanner(versionCheckData);
|
||||
addGlobalNotification(
|
||||
@@ -299,3 +310,30 @@ function getVersionInfo() {
|
||||
function isAutoUpdateEnabled() {
|
||||
return autoUpdateEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update version badge state
|
||||
* @param {string} state - 'checking', 'has-update', 'none'
|
||||
*/
|
||||
function updateVersionBadge(state) {
|
||||
const badge = document.getElementById('versionBadge');
|
||||
if (!badge) return;
|
||||
|
||||
// Remove all state classes
|
||||
badge.classList.remove('has-update', 'checking');
|
||||
badge.textContent = '';
|
||||
|
||||
switch (state) {
|
||||
case 'checking':
|
||||
badge.classList.add('checking');
|
||||
break;
|
||||
case 'has-update':
|
||||
badge.classList.add('has-update');
|
||||
badge.textContent = '!';
|
||||
break;
|
||||
case 'none':
|
||||
default:
|
||||
// Hide badge
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +185,14 @@ function renderIssueView() {
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<!-- Pull from GitHub Button -->
|
||||
<button class="issue-pull-btn" onclick="showPullIssuesModal()" title="Pull issues from GitHub repository">
|
||||
<svg class="w-4 h-4 mr-1.5" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v 3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
<span>Pull from GitHub</span>
|
||||
</button>
|
||||
|
||||
<!-- Create Button -->
|
||||
<button class="issue-create-btn" onclick="showCreateIssueModal()">
|
||||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||||
@@ -281,6 +289,59 @@ function renderIssueView() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pull Issues Modal -->
|
||||
<div id="pullIssuesModal" class="issue-modal hidden">
|
||||
<div class="issue-modal-backdrop" onclick="hidePullIssuesModal()"></div>
|
||||
<div class="issue-modal-content">
|
||||
<div class="issue-modal-header">
|
||||
<h3>
|
||||
<svg class="w-5 h-5 inline mr-2 -mt-1" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
Pull Issues from GitHub
|
||||
</h3>
|
||||
<button class="btn-icon" onclick="hidePullIssuesModal()">
|
||||
<i data-lucide="x" class="w-5 h-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="issue-modal-body">
|
||||
<div class="form-group">
|
||||
<label>Issue State</label>
|
||||
<select id="pullIssueState">
|
||||
<option value="open" selected>Open</option>
|
||||
<option value="closed">Closed</option>
|
||||
<option value="all">All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Maximum Issues</label>
|
||||
<input type="number" id="pullIssueLimit" value="20" min="1" max="100" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Labels (optional)</label>
|
||||
<input type="text" id="pullIssueLabels" placeholder="bug, enhancement (comma-separated)" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="pullDownloadImages" checked />
|
||||
<span>Download images to local</span>
|
||||
</label>
|
||||
<p class="form-hint text-xs text-muted-foreground mt-1">Images will be saved to .workflow/issues/images/ and links updated in issue context</p>
|
||||
</div>
|
||||
<div id="pullIssueResult" class="pull-result hidden mt-4 p-3 rounded-md bg-muted"></div>
|
||||
</div>
|
||||
<div class="issue-modal-footer">
|
||||
<button class="btn-secondary" onclick="hidePullIssuesModal()">Cancel</button>
|
||||
<button class="btn-primary" id="pullIssuesBtn" onclick="pullGitHubIssues()">
|
||||
<svg class="w-4 h-4 mr-1 inline" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||
</svg>
|
||||
Pull Issues
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -2627,6 +2688,127 @@ function hideCreateIssueModal() {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Pull Issues Modal ==========
|
||||
function showPullIssuesModal() {
|
||||
const modal = document.getElementById('pullIssuesModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
// Reset result area
|
||||
const resultDiv = document.getElementById('pullIssueResult');
|
||||
if (resultDiv) {
|
||||
resultDiv.classList.add('hidden');
|
||||
resultDiv.innerHTML = '';
|
||||
}
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
function hidePullIssuesModal() {
|
||||
const modal = document.getElementById('pullIssuesModal');
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
// Clear form
|
||||
const stateSelect = document.getElementById('pullIssueState');
|
||||
const limitInput = document.getElementById('pullIssueLimit');
|
||||
const labelsInput = document.getElementById('pullIssueLabels');
|
||||
const downloadImagesCheck = document.getElementById('pullDownloadImages');
|
||||
if (stateSelect) stateSelect.value = 'open';
|
||||
if (limitInput) limitInput.value = '20';
|
||||
if (labelsInput) labelsInput.value = '';
|
||||
if (downloadImagesCheck) downloadImagesCheck.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function pullGitHubIssues() {
|
||||
const stateSelect = document.getElementById('pullIssueState');
|
||||
const limitInput = document.getElementById('pullIssueLimit');
|
||||
const labelsInput = document.getElementById('pullIssueLabels');
|
||||
const downloadImagesCheck = document.getElementById('pullDownloadImages');
|
||||
const resultDiv = document.getElementById('pullIssueResult');
|
||||
const pullBtn = document.getElementById('pullIssuesBtn');
|
||||
|
||||
const state = stateSelect?.value || 'open';
|
||||
const limit = parseInt(limitInput?.value || '20');
|
||||
const labels = labelsInput?.value?.trim();
|
||||
const downloadImages = downloadImagesCheck?.checked || false;
|
||||
|
||||
// Disable button and show loading
|
||||
if (pullBtn) {
|
||||
pullBtn.disabled = true;
|
||||
pullBtn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 mr-1 animate-spin"></i>' + (t('common.loading') || 'Loading...');
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
path: projectPath,
|
||||
state: state,
|
||||
limit: limit.toString(),
|
||||
downloadImages: downloadImages.toString()
|
||||
});
|
||||
if (labels) params.set('labels', labels);
|
||||
|
||||
const response = await fetch('/api/issues/pull?' + params.toString(), {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || result.error) {
|
||||
showNotification(result.error || 'Failed to pull issues', 'error');
|
||||
if (resultDiv) {
|
||||
resultDiv.classList.remove('hidden');
|
||||
resultDiv.innerHTML = `<p class="text-destructive">${result.error || 'Failed to pull issues'}</p>`;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Show results
|
||||
if (resultDiv) {
|
||||
resultDiv.classList.remove('hidden');
|
||||
resultDiv.innerHTML = `
|
||||
<div class="flex items-start gap-2">
|
||||
<i data-lucide="check-circle" class="w-5 h-5 text-success mt-0.5"></i>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium mb-2">${t('issues.pullSuccess') || 'GitHub Issues Pulled Successfully'}</p>
|
||||
<div class="text-sm text-muted-foreground space-y-1">
|
||||
<p>✓ Imported: <strong>${result.imported || 0}</strong> new issues</p>
|
||||
<p>✓ Updated: <strong>${result.updated || 0}</strong> existing issues</p>
|
||||
<p>✓ Skipped: <strong>${result.skipped || 0}</strong> unchanged issues</p>
|
||||
${result.images_downloaded > 0 ? `<p>✓ Downloaded: <strong>${result.images_downloaded}</strong> images</p>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
showNotification(`Pulled ${result.imported + result.updated} issues from GitHub`, 'success');
|
||||
|
||||
// Reload data after 1 second
|
||||
setTimeout(async () => {
|
||||
await loadIssueData();
|
||||
renderIssueView();
|
||||
hidePullIssuesModal();
|
||||
}, 1500);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to pull issues:', err);
|
||||
showNotification('Failed to pull issues', 'error');
|
||||
if (resultDiv) {
|
||||
resultDiv.classList.remove('hidden');
|
||||
resultDiv.innerHTML = `<p class="text-destructive">${err.message || 'Unknown error occurred'}</p>`;
|
||||
}
|
||||
} finally {
|
||||
// Re-enable button
|
||||
if (pullBtn) {
|
||||
pullBtn.disabled = false;
|
||||
pullBtn.innerHTML = '<i data-lucide="download" class="w-4 h-4 mr-1"></i>' + (t('issues.pull') || 'Pull');
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function createIssue() {
|
||||
const idInput = document.getElementById('newIssueId');
|
||||
const titleInput = document.getElementById('newIssueTitle');
|
||||
|
||||
@@ -1012,15 +1012,24 @@ async function saveTaskOrder(loopId, newOrder) {
|
||||
|
||||
/**
|
||||
* Show add task modal
|
||||
* Loads enabled tools before displaying modal to prevent race conditions
|
||||
*/
|
||||
async function showAddTaskModal(loopId) {
|
||||
// Get enabled tools
|
||||
const enabledTools = await getEnabledTools();
|
||||
// Find and disable the "Add Task" button to prevent multiple clicks during loading
|
||||
const addTaskButton = event?.target;
|
||||
if (addTaskButton) {
|
||||
addTaskButton.disabled = true;
|
||||
const originalText = addTaskButton.innerHTML;
|
||||
addTaskButton.innerHTML = '<i class="spinner"></i> ' + (t('common.loading') || 'Loading...');
|
||||
|
||||
// Build tool options HTML
|
||||
const toolOptions = enabledTools.map(tool =>
|
||||
`<option value="${tool}">${tool.charAt(0).toUpperCase() + tool.slice(1)}</option>`
|
||||
).join('');
|
||||
try {
|
||||
// Get enabled tools (this ensures tools are loaded before modal opens)
|
||||
const enabledTools = await getEnabledTools();
|
||||
|
||||
// Build tool options HTML
|
||||
const toolOptions = enabledTools.map(tool =>
|
||||
`<option value="${tool}">${tool.charAt(0).toUpperCase() + tool.slice(1)}</option>`
|
||||
).join('');
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'addTaskModal';
|
||||
@@ -1075,11 +1084,103 @@ async function showAddTaskModal(loopId) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
document.body.appendChild(modal);
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
|
||||
// Focus on description field
|
||||
setTimeout(() => document.getElementById('taskDescription').focus(), 100);
|
||||
// Focus on description field
|
||||
setTimeout(() => document.getElementById('taskDescription').focus(), 100);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Failed to show add task modal:', err);
|
||||
alert(t('loop.loadToolsError') || 'Failed to load available tools. Please try again.');
|
||||
} finally {
|
||||
// Restore button state
|
||||
if (addTaskButton) {
|
||||
addTaskButton.disabled = false;
|
||||
addTaskButton.innerHTML = originalText;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Fallback if event is not available (shouldn't happen normally)
|
||||
const enabledTools = await getEnabledTools();
|
||||
const toolOptions = enabledTools.map(tool =>
|
||||
`<option value="${tool}">${tool.charAt(0).toUpperCase() + tool.slice(1)}</option>`
|
||||
).join('');
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'addTaskModal';
|
||||
modal.className = 'modal-overlay';
|
||||
modal.innerHTML = `
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h3><i data-lucide="plus-circle" class="w-5 h-5"></i> ${t('loop.addTask') || 'Add Task'}</h3>
|
||||
<button class="modal-close" onclick="closeTaskModal()">
|
||||
<i data-lucide="x" class="w-5 h-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="addTaskForm" onsubmit="handleAddTask(event, '${loopId}')">
|
||||
<div id="addTaskError" class="alert alert-error" style="display: none;"></div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="form-group">
|
||||
<label for="taskDescription">${t('loop.taskDescription') || 'Task Description'} <span class="required">*</span></label>
|
||||
<textarea id="taskDescription" name="description" rows="3" required
|
||||
placeholder="${t('loop.taskDescriptionPlaceholder') || 'Describe what this task should do...'}"
|
||||
class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Tool -->
|
||||
<div class="form-group">
|
||||
<label for="taskTool">${t('loop.tool') || 'Tool'}</label>
|
||||
<select id="taskTool" name="tool" class="form-control">
|
||||
${toolOptions}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Mode -->
|
||||
<div class="form-group">
|
||||
<label for="taskMode">${t('loop.mode') || 'Mode'}</label>
|
||||
<select id="taskMode" name="mode" class="form-control">
|
||||
<option value="analysis">${t('loop.modeAnalysis') || 'Analysis'}</option>
|
||||
<option value="write">${t('loop.modeWrite') || 'Write'}</option>
|
||||
<option value="review">${t('loop.modeReview') || 'Review'}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Prompt Template -->
|
||||
<div class="form-group">
|
||||
<label for="taskPrompt">${t('loop.promptTemplate') || 'Prompt Template'} <span class="required">*</span></label>
|
||||
<textarea id="taskPrompt" name="prompt_template" rows="5" required
|
||||
placeholder="${t('loop.promptPlaceholder') || 'Enter the prompt to execute...'}"
|
||||
class="form-control"></textarea>
|
||||
<small class="form-help">${t('loop.promptHelp') || 'Variables: {iteration}, {output_prev}'}</small>
|
||||
</div>
|
||||
|
||||
<!-- Error Handling -->
|
||||
<div class="form-group">
|
||||
<label for="taskOnError">${t('loop.onError') || 'On Error'}</label>
|
||||
<select id="taskOnError" name="on_error" class="form-control">
|
||||
<option value="continue">${t('loop.errorContinue') || 'Continue'}</option>
|
||||
<option value="pause">${t('loop.errorPause') || 'Pause'}</option>
|
||||
<option value="fail_fast">${t('loop.errorFailFast') || 'Fail Fast'}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Form Actions -->
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeTaskModal()">${t('loop.cancel') || 'Cancel'}</button>
|
||||
<button type="submit" class="btn btn-primary">${t('loop.add') || 'Add'}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
setTimeout(() => document.getElementById('taskDescription').focus(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -278,6 +278,50 @@
|
||||
display: block;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
.check-icon-loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Version Badge */
|
||||
.version-badge {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -2px;
|
||||
right: -2px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
background: hsl(var(--destructive));
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
.version-badge.has-update {
|
||||
display: flex;
|
||||
animation: badgePulse 2s ease-in-out infinite;
|
||||
}
|
||||
.version-badge.checking {
|
||||
display: flex;
|
||||
background: hsl(var(--muted) / 0.8);
|
||||
}
|
||||
.version-badge.checking::after {
|
||||
content: '...';
|
||||
animation: dots 1.5s steps(4, end) infinite;
|
||||
}
|
||||
@keyframes badgePulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.15); }
|
||||
}
|
||||
@keyframes dots {
|
||||
0%, 20% { content: '.'; }
|
||||
40% { content: '..'; }
|
||||
60%, 100% { content: '...'; }
|
||||
}
|
||||
|
||||
/* Auto-Update Toggle Switch */
|
||||
.auto-update-switch {
|
||||
@@ -390,7 +434,7 @@
|
||||
<!-- Auto-Update Controls -->
|
||||
<div class="flex items-center gap-2 border-l border-border pl-2">
|
||||
<!-- Check Now Button -->
|
||||
<button class="tooltip-bottom p-1.5 text-muted-foreground hover:text-foreground hover:bg-hover rounded" id="checkUpdateNow" data-tooltip="Check for updates now" onclick="checkForUpdatesNow()">
|
||||
<button class="tooltip-bottom p-1.5 text-muted-foreground hover:text-foreground hover:bg-hover rounded relative" id="checkUpdateNow" data-tooltip="Check for updates now" onclick="checkForUpdatesNow()">
|
||||
<!-- Download Icon (default) -->
|
||||
<svg class="check-icon-default" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
@@ -398,9 +442,11 @@
|
||||
<line x1="12" y1="15" x2="12" y2="3"/>
|
||||
</svg>
|
||||
<!-- Loading Icon (checking state) -->
|
||||
<svg class="check-icon-loading hidden" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg class="check-icon-loading" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
||||
</svg>
|
||||
<!-- Version Available Badge -->
|
||||
<span class="version-badge" id="versionBadge"></span>
|
||||
</button>
|
||||
<!-- Auto-Update Toggle Switch -->
|
||||
<label class="tooltip-bottom auto-update-switch" data-tooltip="Auto-update">
|
||||
|
||||
Reference in New Issue
Block a user