From b20ed7c925eb3da70ba89b82cfbb614fb0c1b20f Mon Sep 17 00:00:00 2001 From: Snow Lee Date: Sun, 12 Jul 2026 12:54:02 -0700 Subject: [PATCH] feat(web): expandable history notes; Insights as project landing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit History notes still clamp to one line so the feed stays scannable, but clicking (or Enter on) the note expands the full text and back — links inside keep working. Selecting a project now lands admins/org owners on the Insights dashboard instead of the empty "select a file" pane; members keep the placeholder, and routes that carry a path still open that file or folder. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt --- internal/webapp/static/app.js | 19 ++++++++++++++++++- internal/webapp/static/style.css | 4 +++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/internal/webapp/static/app.js b/internal/webapp/static/app.js index c56800e..6b3de33 100644 --- a/internal/webapp/static/app.js +++ b/internal/webapp/static/app.js @@ -166,7 +166,12 @@ function selectProject(p, path) { initUpload(); initHistory(); updateShareButton(); - refreshTree().then(() => { if (path) openPath(path); }); + refreshTree().then(() => { + if (path) openPath(path); + // Landing view: admins/owners get the Insights dashboard instead of an + // empty "select a file" pane; members keep the placeholder. + else if (canSeeInsights()) showInsights(); + }); if (!path) pushURL("/" + p.id); } @@ -1543,6 +1548,18 @@ function historyEntryRow(e) { note.append(tok); } } + // Long notes clamp to one line; clicking the note (links still work) + // expands to the full text and back. + note.tabIndex = 0; + note.setAttribute("role", "button"); + note.title = "Show full note"; + note.onclick = (ev) => { + if (ev.target.tagName === "A") return; + const open = note.classList.toggle("open"); + note.title = open ? "Collapse note" : "Show full note"; + note.setAttribute("aria-expanded", String(open)); + }; + note.onkeydown = (ev) => { if (ev.key === "Enter" || ev.key === " ") { ev.preventDefault(); note.click(); } }; row.appendChild(note); } const p = e.path; diff --git a/internal/webapp/static/style.css b/internal/webapp/static/style.css index c29f073..7ccaf05 100644 --- a/internal/webapp/static/style.css +++ b/internal/webapp/static/style.css @@ -347,7 +347,9 @@ button, input, a.btn { font-family: inherit; } .hact { margin-left: auto; } .hact a { color: var(--accent-bright); text-decoration: none; margin-left: 10px; } .hact a:hover { text-decoration: underline; } -.hnote { margin-top: 4px; padding-left: 23px; font-size: 12px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.hnote { margin-top: 4px; padding-left: 23px; font-size: 12px; color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; } +.hnote:hover { color: var(--text); } +.hnote.open { white-space: normal; overflow-wrap: anywhere; } .hnote::before { content: "› "; color: var(--text-ghost); } .hnote a { color: var(--accent-bright); text-decoration: none; } .hnote a:hover { text-decoration: underline; }