Skip to content

Commit 9a04052

Browse files
Fix claude css line issue
1 parent eb17766 commit 9a04052

2 files changed

Lines changed: 55 additions & 13 deletions

File tree

static/css/style.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,37 @@ h3 { font-size: 1.15rem; font-weight: 600; }
279279
flex-wrap: wrap;
280280
}
281281

282+
/* Projects home: title + actions on one row; intro + last export on the next row (same baseline). */
283+
.page-header--projects {
284+
flex-direction: column;
285+
align-items: stretch;
286+
}
287+
.page-header--projects .page-header__top {
288+
display: flex;
289+
align-items: center;
290+
justify-content: space-between;
291+
gap: 1rem;
292+
flex-wrap: wrap;
293+
}
294+
.page-header--projects .page-header__subrow {
295+
display: flex;
296+
align-items: baseline;
297+
justify-content: space-between;
298+
gap: 1rem;
299+
flex-wrap: wrap;
300+
}
301+
.page-header--projects .page-header__intro {
302+
margin: 0;
303+
flex: 1 1 12rem;
304+
}
305+
.page-header--projects .page-header__export-meta {
306+
flex: 0 1 auto;
307+
text-align: right;
308+
}
309+
.page-header--projects .page-header__export-meta p {
310+
margin: 0;
311+
}
312+
282313
/* ---------- Utility ---------- */
283314
.flex-between { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
284315

static/js/projects.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ export async function showProjects() {
2020
loadingBar.start();
2121

2222
try {
23-
const [projRes, stateRes] = await Promise.all([
24-
fetch('/api/projects'),
25-
fetch('/api/export/state').catch(() => null),
26-
]);
23+
// Load projects first. Do not Promise.all with /api/export/state: that endpoint
24+
// takes a file lock; if it blocks (stale lock, slow disk), the UI would hang here
25+
// forever even though /api/projects already succeeded.
26+
const projRes = await fetch('/api/projects');
2727
if (!projRes.ok) {
2828
let msg = `Failed to load projects (${projRes.status})`;
2929
try { const body = await projRes.json(); if (body.error) msg = body.error; } catch { try { msg = await projRes.text() || msg; } catch { /* ignore */ } }
@@ -32,9 +32,9 @@ export async function showProjects() {
3232
return;
3333
}
3434
const projects = await projRes.json();
35-
loadingBar.done();
3635

3736
if (!projects.length) {
37+
loadingBar.done();
3838
smoothSet(content, '<div class="empty-state">No Claude Code projects found.<br>Make sure Claude Code has been used on this machine.</div>');
3939
return;
4040
}
@@ -44,15 +44,25 @@ export async function showProjects() {
4444

4545
let lastExportHtml = '';
4646
let hasPreviousExport = false;
47-
if (stateRes) {
47+
let stateRes = null;
48+
const exportCtrl = new AbortController();
49+
const exportTid = setTimeout(() => exportCtrl.abort(), 5000);
50+
try {
51+
stateRes = await fetch('/api/export/state', { signal: exportCtrl.signal });
52+
} catch {
53+
/* timeout or network — still render project list */
54+
} finally {
55+
clearTimeout(exportTid);
56+
}
57+
if (stateRes && stateRes.ok) {
4858
try {
4959
const exportState = await stateRes.json();
5060
if (exportState.last_export_time) {
5161
const d = new Date(exportState.last_export_time);
5262
if (!isNaN(d.getTime())) {
5363
hasPreviousExport = true;
5464
const sessionCount = Math.max(0, parseInt(exportState.last_export_session_count ?? exportState.export_count ?? 0, 10) || 0);
55-
lastExportHtml = `<p class="text-muted text-sm" style="margin:0">Last export: ${d.toLocaleString()} (${sessionCount} sessions in last export)</p>`;
65+
lastExportHtml = `<p class="text-muted text-sm">Last export: ${d.toLocaleString()} (${sessionCount} sessions in last export)</p>`;
5666
}
5767
}
5868
} catch(e) {}
@@ -65,20 +75,20 @@ export async function showProjects() {
6575
</button>`
6676
: '';
6777

68-
let html = `<div class="page-header">
69-
<div>
78+
let html = `<div class="page-header page-header--projects">
79+
<div class="page-header__top">
7080
<h1>Projects</h1>
71-
<p class="text-muted">Browse your Claude Code conversations by project. Click on a project to view its sessions.</p>
72-
</div>
73-
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:0.4rem">
7481
<div class="btn-group">
7582
${sinceBtnHtml}
7683
<button class="btn btn-outline btn-sm" id="btn-export-all" onclick="bulkExport('all')">
7784
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
7885
Export all
7986
</button>
8087
</div>
81-
${lastExportHtml}
88+
</div>
89+
<div class="page-header__subrow">
90+
<p class="text-muted page-header__intro">Browse your Claude Code conversations by project. Click on a project to view its sessions.</p>
91+
${lastExportHtml ? `<div class="page-header__export-meta">${lastExportHtml}</div>` : ''}
8292
</div>
8393
</div>`;
8494

@@ -128,6 +138,7 @@ export async function showProjects() {
128138
html += `</tbody></table></div></div>`;
129139
}
130140

141+
loadingBar.done();
131142
smoothSet(content, html);
132143
} catch (e) {
133144
loadingBar.done();

0 commit comments

Comments
 (0)