From e3ca1735b94a83c2ed04e86ac3ca26c0a790029a Mon Sep 17 00:00:00 2001 From: tommy0103 Date: Wed, 17 Jun 2026 01:15:16 +0800 Subject: [PATCH] feat(app): link memory to source session with focus-scroll navigation Add a clickable session link in MemoryList detail panel that navigates to the source session and scrolls to the originating message. Refactor SessionDetail focus logic into a reusable focusPendingMessage() that reads ?focus= from the route query, scrolls the target into view, and applies a pulse animation that fades out. --- .gitignore | 2 + app/renderer/src/views/MemoryList.vue | 38 +++++++++++++++++ app/renderer/src/views/SessionDetail.vue | 54 ++++++++++++++++++------ app/renderer/styles/detail.css | 6 +++ 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index d7bb805..e7fa23d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ tests/ node_modules/ dist-renderer/ release/ +docs/ +.claude/ diff --git a/app/renderer/src/views/MemoryList.vue b/app/renderer/src/views/MemoryList.vue index 5d7e153..0ea0ef9 100644 --- a/app/renderer/src/views/MemoryList.vue +++ b/app/renderer/src/views/MemoryList.vue @@ -75,6 +75,21 @@ function summaryHTML(m) { return highlightPlain(m.summary || '', state.query.trim()); } +function sourceSessionTitle(m) { + if (!m.session_id) return ''; + const s = state.sessions.find(x => x.id === m.session_id); + return s?.title || m.session_id.slice(0, 8); +} + +function openSourceSession(m) { + if (!m.session_id) return; + if (m.message_start) { + router.push({ path: `/sessions/${m.session_id}`, query: { focus: m.message_start } }); + } else { + router.push(`/sessions/${m.session_id}`); + } +} + function timeLabel(m) { return fmtListTime(m.ts); } @@ -316,6 +331,18 @@ onUnmounted(() => {
{{ relativePath(detailMemory) }}
{{ detailMemory.summary }}
+ + {{ fmtRelative(detailMemory.ts) }}
@@ -659,6 +686,17 @@ onUnmounted(() => { color: var(--muted); font-variant-numeric: tabular-nums; padding-bottom: 16px; border-bottom: 1px solid var(--hairline); } +.detail-meta .dot { width: 2px; height: 2px; background: var(--muted-2); border-radius: 50%; flex-shrink: 0; } +.session-link { + color: var(--accent-2); border: 0; background: transparent; + padding: 2px 5px; margin: -2px 0; border-radius: 3px; + font: inherit; cursor: pointer; transition: all 0.1s; + text-decoration: underline; text-decoration-color: rgba(167,139,250,0.25); + text-underline-offset: 3px; + display: inline-flex; align-items: center; gap: 5px; +} +.session-link:hover { background: rgba(167,139,250,0.12); color: var(--accent-2); text-decoration-color: var(--accent-2); } +.session-link svg { width: 11px; height: 11px; } .markdown-section { margin: 28px 0 8px; } .markdown-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; } diff --git a/app/renderer/src/views/SessionDetail.vue b/app/renderer/src/views/SessionDetail.vue index 005fad2..e7fb41f 100644 --- a/app/renderer/src/views/SessionDetail.vue +++ b/app/renderer/src/views/SessionDetail.vue @@ -1,6 +1,6 @@