codedash-app 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/data.js +16 -2
- package/src/frontend/app.js +52 -1
- package/src/frontend/index.html +6 -0
- package/src/frontend/styles.css +46 -0
- package/src/server.js +38 -0
- package/src/terminals.js +1 -1
package/package.json
CHANGED
package/src/data.js
CHANGED
|
@@ -65,16 +65,30 @@ function scanCodexSessions() {
|
|
|
65
65
|
const uuidMatch = basename.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/);
|
|
66
66
|
if (!uuidMatch) continue;
|
|
67
67
|
const sid = uuidMatch[1];
|
|
68
|
+
// Try to extract cwd from session_meta
|
|
69
|
+
let cwd = '';
|
|
70
|
+
try {
|
|
71
|
+
const firstLine = fs.readFileSync(f, 'utf8').split('\n')[0];
|
|
72
|
+
const meta = JSON.parse(firstLine);
|
|
73
|
+
if (meta.type === 'session_meta' && meta.payload && meta.payload.cwd) {
|
|
74
|
+
cwd = meta.payload.cwd;
|
|
75
|
+
}
|
|
76
|
+
} catch {}
|
|
77
|
+
|
|
68
78
|
const existing = sessions.find(s => s.id === sid);
|
|
69
79
|
if (existing) {
|
|
70
80
|
existing.has_detail = true;
|
|
71
81
|
existing.file_size = stat.size;
|
|
82
|
+
if (cwd && !existing.project) {
|
|
83
|
+
existing.project = cwd;
|
|
84
|
+
existing.project_short = cwd.replace(os.homedir(), '~');
|
|
85
|
+
}
|
|
72
86
|
} else {
|
|
73
87
|
sessions.push({
|
|
74
88
|
id: sid,
|
|
75
89
|
tool: 'codex',
|
|
76
|
-
project:
|
|
77
|
-
project_short: '',
|
|
90
|
+
project: cwd,
|
|
91
|
+
project_short: cwd ? cwd.replace(os.homedir(), '~') : '',
|
|
78
92
|
first_ts: stat.mtimeMs,
|
|
79
93
|
last_ts: stat.mtimeMs,
|
|
80
94
|
messages: 0,
|
package/src/frontend/app.js
CHANGED
|
@@ -219,6 +219,14 @@ function applyFilters() {
|
|
|
219
219
|
return true;
|
|
220
220
|
});
|
|
221
221
|
|
|
222
|
+
// Starred sessions first
|
|
223
|
+
filteredSessions.sort(function(a, b) {
|
|
224
|
+
var aStarred = stars.indexOf(a.id) >= 0 ? 1 : 0;
|
|
225
|
+
var bStarred = stars.indexOf(b.id) >= 0 ? 1 : 0;
|
|
226
|
+
if (aStarred !== bStarred) return bStarred - aStarred;
|
|
227
|
+
return b.last_ts - a.last_ts;
|
|
228
|
+
});
|
|
229
|
+
|
|
222
230
|
render();
|
|
223
231
|
}
|
|
224
232
|
|
|
@@ -821,7 +829,7 @@ function launchSession(sessionId, tool, project) {
|
|
|
821
829
|
|
|
822
830
|
function copyResume(sessionId, tool) {
|
|
823
831
|
var cmd = tool === 'codex'
|
|
824
|
-
? 'codex
|
|
832
|
+
? 'codex resume ' + sessionId
|
|
825
833
|
: 'claude --resume ' + sessionId;
|
|
826
834
|
navigator.clipboard.writeText(cmd).then(function() {
|
|
827
835
|
showToast('Copied: ' + cmd);
|
|
@@ -863,6 +871,17 @@ async function confirmDelete() {
|
|
|
863
871
|
if (data.ok) {
|
|
864
872
|
showToast('Session deleted');
|
|
865
873
|
allSessions = allSessions.filter(function(s) { return s.id !== pendingDelete.id; });
|
|
874
|
+
// Clear search if no more results
|
|
875
|
+
if (searchQuery) {
|
|
876
|
+
var remaining = allSessions.filter(function(s) {
|
|
877
|
+
return (s.project || '').toLowerCase().indexOf(searchQuery.toLowerCase()) >= 0 ||
|
|
878
|
+
(s.first_message || '').toLowerCase().indexOf(searchQuery.toLowerCase()) >= 0;
|
|
879
|
+
});
|
|
880
|
+
if (remaining.length === 0) {
|
|
881
|
+
searchQuery = '';
|
|
882
|
+
document.querySelector('.search-box').value = '';
|
|
883
|
+
}
|
|
884
|
+
}
|
|
866
885
|
closeConfirm();
|
|
867
886
|
closeDetail();
|
|
868
887
|
applyFilters();
|
|
@@ -1080,12 +1099,44 @@ document.addEventListener('keydown', function(e) {
|
|
|
1080
1099
|
}
|
|
1081
1100
|
});
|
|
1082
1101
|
|
|
1102
|
+
// ── Update check ──────────────────────────────────────────────
|
|
1103
|
+
|
|
1104
|
+
async function checkForUpdates() {
|
|
1105
|
+
try {
|
|
1106
|
+
var resp = await fetch('/api/version');
|
|
1107
|
+
var data = await resp.json();
|
|
1108
|
+
if (data.updateAvailable) {
|
|
1109
|
+
var banner = document.getElementById('updateBanner');
|
|
1110
|
+
var text = document.getElementById('updateText');
|
|
1111
|
+
if (banner && text) {
|
|
1112
|
+
text.textContent = 'Update available: v' + data.current + ' → v' + data.latest;
|
|
1113
|
+
banner.style.display = 'flex';
|
|
1114
|
+
banner.dataset.cmd = 'npm update -g codedash-app && codedash run';
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
} catch {}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function copyUpdate() {
|
|
1121
|
+
var banner = document.getElementById('updateBanner');
|
|
1122
|
+
var cmd = banner ? banner.dataset.cmd : 'npm update -g codedash-app';
|
|
1123
|
+
navigator.clipboard.writeText(cmd).then(function() {
|
|
1124
|
+
showToast('Copied: ' + cmd);
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function dismissUpdate() {
|
|
1129
|
+
var banner = document.getElementById('updateBanner');
|
|
1130
|
+
if (banner) banner.style.display = 'none';
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1083
1133
|
// ── Initialization ─────────────────────────────────────────────
|
|
1084
1134
|
|
|
1085
1135
|
(function init() {
|
|
1086
1136
|
// Load data
|
|
1087
1137
|
loadSessions();
|
|
1088
1138
|
loadTerminals();
|
|
1139
|
+
checkForUpdates();
|
|
1089
1140
|
|
|
1090
1141
|
// Apply saved theme
|
|
1091
1142
|
var savedTheme = localStorage.getItem('codedash-theme') || 'dark';
|
package/src/frontend/index.html
CHANGED
|
@@ -111,6 +111,12 @@
|
|
|
111
111
|
|
|
112
112
|
<div class="toast" id="toast"></div>
|
|
113
113
|
|
|
114
|
+
<div class="update-banner" id="updateBanner" style="display:none">
|
|
115
|
+
<span id="updateText"></span>
|
|
116
|
+
<button class="update-btn" onclick="copyUpdate()">Copy update command</button>
|
|
117
|
+
<button class="update-dismiss" onclick="dismissUpdate()">×</button>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
114
120
|
<script>{{SCRIPT}}</script>
|
|
115
121
|
</body>
|
|
116
122
|
</html>
|
package/src/frontend/styles.css
CHANGED
|
@@ -1370,6 +1370,52 @@ body {
|
|
|
1370
1370
|
color: #fff;
|
|
1371
1371
|
}
|
|
1372
1372
|
|
|
1373
|
+
/* ── Update banner ──────────────────────────────────────────── */
|
|
1374
|
+
|
|
1375
|
+
.update-banner {
|
|
1376
|
+
position: fixed;
|
|
1377
|
+
top: 0;
|
|
1378
|
+
left: 200px;
|
|
1379
|
+
right: 0;
|
|
1380
|
+
background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple));
|
|
1381
|
+
color: #fff;
|
|
1382
|
+
padding: 10px 20px;
|
|
1383
|
+
display: flex;
|
|
1384
|
+
align-items: center;
|
|
1385
|
+
gap: 12px;
|
|
1386
|
+
font-size: 13px;
|
|
1387
|
+
z-index: 200;
|
|
1388
|
+
animation: slideDown 0.3s ease;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
@keyframes slideDown {
|
|
1392
|
+
from { transform: translateY(-100%); }
|
|
1393
|
+
to { transform: translateY(0); }
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
.update-btn {
|
|
1397
|
+
background: rgba(255,255,255,0.2);
|
|
1398
|
+
border: 1px solid rgba(255,255,255,0.3);
|
|
1399
|
+
color: #fff;
|
|
1400
|
+
padding: 4px 12px;
|
|
1401
|
+
border-radius: 6px;
|
|
1402
|
+
font-size: 12px;
|
|
1403
|
+
cursor: pointer;
|
|
1404
|
+
white-space: nowrap;
|
|
1405
|
+
}
|
|
1406
|
+
.update-btn:hover { background: rgba(255,255,255,0.3); }
|
|
1407
|
+
|
|
1408
|
+
.update-dismiss {
|
|
1409
|
+
background: none;
|
|
1410
|
+
border: none;
|
|
1411
|
+
color: rgba(255,255,255,0.7);
|
|
1412
|
+
font-size: 18px;
|
|
1413
|
+
cursor: pointer;
|
|
1414
|
+
margin-left: auto;
|
|
1415
|
+
padding: 0 4px;
|
|
1416
|
+
}
|
|
1417
|
+
.update-dismiss:hover { color: #fff; }
|
|
1418
|
+
|
|
1373
1419
|
/* ── List view ──────────────────────────────────────────────── */
|
|
1374
1420
|
|
|
1375
1421
|
.list-view {
|
package/src/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// HTTP server + API routes
|
|
2
2
|
const http = require('http');
|
|
3
|
+
const https = require('https');
|
|
3
4
|
const { URL } = require('url');
|
|
4
5
|
const { exec } = require('child_process');
|
|
5
6
|
const { loadSessions, loadSessionDetail, deleteSession, getGitCommits, exportSessionMarkdown } = require('./data');
|
|
@@ -103,6 +104,18 @@ function startServer(port, openBrowser = true) {
|
|
|
103
104
|
json(res, commits);
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
// ── Version check ────────────────────────
|
|
108
|
+
else if (req.method === 'GET' && pathname === '/api/version') {
|
|
109
|
+
const pkg = require('../package.json');
|
|
110
|
+
const current = pkg.version;
|
|
111
|
+
// Fetch latest from npm registry
|
|
112
|
+
fetchLatestVersion(pkg.name).then(latest => {
|
|
113
|
+
json(res, { current, latest, updateAvailable: latest && latest !== current && isNewer(latest, current) });
|
|
114
|
+
}).catch(() => {
|
|
115
|
+
json(res, { current, latest: null, updateAvailable: false });
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
106
119
|
// ── 404 ─────────────────────────────────
|
|
107
120
|
else {
|
|
108
121
|
res.writeHead(404);
|
|
@@ -139,4 +152,29 @@ function readBody(req, cb) {
|
|
|
139
152
|
req.on('end', () => cb(body));
|
|
140
153
|
}
|
|
141
154
|
|
|
155
|
+
// ── npm version check ───────────────────
|
|
156
|
+
function fetchLatestVersion(packageName) {
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
https.get(`https://registry.npmjs.org/${packageName}/latest`, { timeout: 5000 }, (res) => {
|
|
159
|
+
let data = '';
|
|
160
|
+
res.on('data', chunk => data += chunk);
|
|
161
|
+
res.on('end', () => {
|
|
162
|
+
try {
|
|
163
|
+
resolve(JSON.parse(data).version);
|
|
164
|
+
} catch { reject(); }
|
|
165
|
+
});
|
|
166
|
+
}).on('error', reject);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isNewer(latest, current) {
|
|
171
|
+
const l = latest.split('.').map(Number);
|
|
172
|
+
const c = current.split('.').map(Number);
|
|
173
|
+
for (let i = 0; i < 3; i++) {
|
|
174
|
+
if ((l[i] || 0) > (c[i] || 0)) return true;
|
|
175
|
+
if ((l[i] || 0) < (c[i] || 0)) return false;
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
142
180
|
module.exports = { startServer };
|
package/src/terminals.js
CHANGED
|
@@ -70,7 +70,7 @@ function openInTerminal(sessionId, tool, flags, projectDir, terminalId) {
|
|
|
70
70
|
let cmd;
|
|
71
71
|
|
|
72
72
|
if (tool === 'codex') {
|
|
73
|
-
cmd = `codex
|
|
73
|
+
cmd = `codex resume ${sessionId}`;
|
|
74
74
|
} else {
|
|
75
75
|
cmd = `claude --resume ${sessionId}`;
|
|
76
76
|
if (skipPerms) cmd += ' --dangerously-skip-permissions';
|