-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathappFeatures.js
More file actions
169 lines (147 loc) · 7.84 KB
/
Copy pathappFeatures.js
File metadata and controls
169 lines (147 loc) · 7.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// ========== FILE: appFeatures.js (FULL, UNABRIDGED, UPDATED) ==========
import { appState, domRefs } from './state.js';
import { GITHUB_RELEASES_API, CURRENT_VERSION } from './config.js';
// Import the specific dialog function and message function
import { showMessage, showUpdateInfoDialog } from './uiUtils.js';
import { escapeHTML } from './flowCore.js'; // Import escapeHTML
import { logger } from './logger.js';
/**
* Toggles the collapsed state of the left sidebar.
*/
export function handleToggleSidebarCollapse() {
if (!domRefs.sidebar || !domRefs.sidebarToggleBtn) return;
appState.isSidebarCollapsed = !appState.isSidebarCollapsed;
domRefs.sidebar.classList.toggle('collapsed', appState.isSidebarCollapsed);
// Rail flows button reflects drawer state (Sentinel v2).
domRefs.sidebarToggleBtn.setAttribute('aria-pressed', String(!appState.isSidebarCollapsed));
try { localStorage.setItem('sidebarCollapsed', appState.isSidebarCollapsed); } catch (e) { logger.warn("Could not persist sidebar state:", e); }
logger.info(`Sidebar collapsed state: ${appState.isSidebarCollapsed}`);
}
/**
* Toggles the collapsed state of the right runner panel.
*/
export function handleToggleRunnerCollapse() {
if (!domRefs.runnerPanel || !domRefs.runnerToggleBtn) return;
appState.isRunnerCollapsed = !appState.isRunnerCollapsed;
domRefs.runnerPanel.classList.toggle('collapsed', appState.isRunnerCollapsed);
// Collapsing always exits the expanded work-mode (a collapsed strip can't
// be wide); keep the expand toggle in sync.
if (appState.isRunnerCollapsed) {
document.querySelector('.app-container')?.classList.remove('inspector-expanded');
const expandBtn = document.getElementById('inspector-expand-btn');
if (expandBtn) {
expandBtn.setAttribute('aria-pressed', 'false');
expandBtn.querySelector('use')?.setAttribute('href', '#i-expand-h');
}
}
try { localStorage.setItem('runnerCollapsed', appState.isRunnerCollapsed); } catch (e) { logger.warn("Could not persist runner state:", e); }
logger.info(`Runner collapsed state: ${appState.isRunnerCollapsed}`);
}
// --- Update Notification on App Open ---
export async function checkForUpdate() {
try {
logger.info("[Update Check - Startup] Checking for updates...");
const response = await fetch(GITHUB_RELEASES_API, {
headers: { 'Accept': 'application/vnd.github.v3+json' },
cache: 'no-cache' // Try to avoid caching issues
});
// --- IMPORTANT: Handle non-OK responses silently ---
if (!response.ok) {
logger.warn(`[Update Check - Startup] GitHub response not OK: ${response.status}`);
return; // Do nothing visible to the user
}
const data = await response.json();
const latestTag = data.tag_name?.replace(/^v/, ''); // Remove leading 'v' if present
const releaseUrl = data.html_url; // Get the URL to the release page
if (!latestTag) {
logger.warn("[Update Check - Startup] Could not find tag_name in response.");
return; // Do nothing visible
}
if (!releaseUrl) {
logger.warn("[Update Check - Startup] Could not find html_url in response.");
// Decide if we should still show the message without a link, or just return. Let's return.
return;
}
logger.info(`[Update Check - Startup] Current: ${CURRENT_VERSION}, Latest: ${latestTag}`);
if (compareVersions(latestTag, CURRENT_VERSION) > 0) {
logger.info("[Update Check - Startup] Newer version found!");
// --- MODIFICATION: Use allowHTML ---
// Use escapeHTML for dynamic parts, but allow the <a> tag itself
const messageHTML = `A newer version (${escapeHTML(latestTag)}) is available. <a href="${escapeHTML(releaseUrl)}" target="_blank">View Release</a>`;
showMessage(
messageHTML,
'info',
domRefs.builderMessages, // Show in the main message area
'Update Available',
true // Allow HTML rendering for the link
);
} else {
logger.info("[Update Check - Startup] Already on latest version or newer.");
}
} catch (e) {
// --- IMPORTANT: Catch *all* errors silently for startup check ---
logger.warn("[Update Check - Startup] Failed:", e);
// Do nothing visible to the user
}
}
// --- NEW: Manual Update Check Function ---
export async function manualCheckForUpdate() {
try {
showUpdateInfoDialog("Checking for Updates...", "Contacting GitHub...", false); // Show initial dialog
logger.info("[Update Check - Manual] Checking for updates...");
const response = await fetch(GITHUB_RELEASES_API, {
headers: { 'Accept': 'application/vnd.github.v3+json' },
cache: 'no-cache'
});
if (!response.ok) {
// --- Handle non-OK responses by showing an error in the dialog ---
logger.error(`[Update Check - Manual] GitHub response not OK: ${response.status}`);
showUpdateInfoDialog('Update Check Failed', `Could not check for updates. GitHub returned status: ${response.status} ${response.statusText}`, false);
return;
}
const data = await response.json();
const latestTag = data.tag_name?.replace(/^v/, '');
const releaseUrl = data.html_url;
if (!latestTag) {
logger.error("[Update Check - Manual] Could not find tag_name in response.");
showUpdateInfoDialog('Update Check Error', 'Could not determine the latest version from GitHub response.', false);
return;
}
if (!releaseUrl) {
logger.warn("[Update Check - Manual] Could not find html_url in response.");
// Show message but indicate link is missing
if (compareVersions(latestTag, CURRENT_VERSION) > 0) {
showUpdateInfoDialog('Update Available', `A newer version (${escapeHTML(latestTag)}) is available, but the download link could not be retrieved. Please check the GitHub repository manually.`, false);
} else {
showUpdateInfoDialog('Up to Date', `You are running the latest version (v${CURRENT_VERSION}). (Could not verify release link).`, false);
}
return;
}
logger.info(`[Update Check - Manual] Current: ${CURRENT_VERSION}, Latest: ${latestTag}`);
if (compareVersions(latestTag, CURRENT_VERSION) > 0) {
logger.info("[Update Check - Manual] Newer version found!");
// Use escapeHTML for dynamic parts, but allow the <a> tag itself
const messageHTML = `A newer version (v${escapeHTML(latestTag)}) is available.\n\n<a href="${escapeHTML(releaseUrl)}" target="_blank" class="btn btn-primary" style="margin-top: 10px;">View Release on GitHub</a>`;
showUpdateInfoDialog('Update Available', messageHTML, true); // Allow HTML for the button/link
} else {
logger.info("[Update Check - Manual] Already on latest version.");
showUpdateInfoDialog('Up to Date', `You are running the latest version (v${CURRENT_VERSION}).`, false);
}
} catch (e) {
// --- Catch errors and show them in the dialog ---
logger.error("[Update Check - Manual] Failed:", e);
showUpdateInfoDialog('Update Check Error', `Could not check for updates. Error: ${e.message}`, false);
}
}
// --- Version Comparison (Keep as is) ---
export function compareVersions(a, b) {
const pa = a.split('.').map(Number);
const pb = b.split('.').map(Number);
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const na = pa[i] || 0;
const nb = pb[i] || 0;
if (na > nb) return 1;
if (na < nb) return -1;
}
return 0;
}