diff --git a/docs/architecture.md b/docs/architecture.md
index b6f188d..dbf552b 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -239,10 +239,11 @@ Success or failure of a `tool_use` event is indicated by a Unicode check mark (U
Each event is rendered as a `
` containing:
-- `.event-header`: always visible; shows timestamp, tool label, truncated input (120 chars), duration, and status icon.
+- `.event-header`: always visible; shows timestamp, tool label, truncated input (120 chars), duration, status icon, and a permalink anchor.
- `.event-body`: hidden by default; shown on click via the `toggle(i)` JavaScript function which toggles the `open` class.
The arrow indicator rotates 90 degrees (CSS `transform: rotate(90deg)`) when the panel is open. Output text in `.event-body` is capped at 400px height with `overflow-y: auto`.
+If the page loads with an `#evt-N` fragment, that event is opened automatically and scrolled into view.
Input label semantics vary per tool:
diff --git a/docs/history.md b/docs/history.md
index 479285c..dfa8ebc 100644
--- a/docs/history.md
+++ b/docs/history.md
@@ -76,5 +76,5 @@ The following have been identified as potential improvements but are not current
- **Parallel search**: fan out `ParseTranscript` calls across goroutines with a result channel to reduce wall time for large directories.
- **Persistent index**: a lightweight SQLite index or binary cache per session file to avoid re-parsing on every `Search` or `ListSessions` call.
- **Additional tool types**: the parser's `extractToolInput` fallback handles any unknown tool by listing its JSON keys. Dedicated handling could be added for `WebFetch`, `WebSearch`, `NotebookEdit`, and other tools that appear in Claude Code sessions.
-- **HTML export options**: configurable truncation limits, optional full-output display, and per-event direct links (anchor IDs already exist as `evt-{i}`).
+- **HTML export options**: configurable truncation limits and optional full-output display remain open; per-event direct links are now available via `#evt-{i}` permalinks.
- **VHS alternative**: a pure-Go terminal animation renderer to eliminate the `vhs` dependency for MP4 output.
diff --git a/html.go b/html.go
index fa5c2ee..431ee5e 100644
--- a/html.go
+++ b/html.go
@@ -71,6 +71,8 @@ body { background: var(--bg); color: var(--fg); font-family: var(--font); font-s
.event-header .input { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-header .dur { color: var(--dim); font-size: 11px; min-width: 50px; text-align: right; }
.event-header .status { font-size: 14px; min-width: 20px; text-align: center; }
+.event-header .permalink { color: var(--dim); font-size: 12px; min-width: 16px; text-align: center; text-decoration: none; }
+.event-header .permalink:hover { color: var(--accent); }
.event-header .arrow { color: var(--dim); font-size: 10px; transition: transform 0.15s; min-width: 16px; }
.event.open .arrow { transform: rotate(90deg); }
.event-body { display: none; padding: 12px; background: var(--bg); border-top: 1px solid var(--border); }
@@ -160,6 +162,7 @@ body { background: var(--bg); color: var(--fg); font-family: var(--font); font-s
%s
%s
%s
+
#
`,
@@ -174,7 +177,8 @@ body { background: var(--bg); color: var(--fg); font-family: var(--font); font-s
html.EscapeString(toolLabel),
html.EscapeString(truncate(evt.Input, 120)),
durStr,
- statusIcon))
+ statusIcon,
+ i))
if evt.Input != "" {
label := "Command"
@@ -227,12 +231,22 @@ function filterEvents() {
el.classList.toggle('hidden', !show);
});
}
+function openHashEvent() {
+ const hash = window.location.hash;
+ if (!hash || !hash.startsWith('#evt-')) return;
+ const el = document.getElementById(hash.slice(1));
+ if (!el) return;
+ el.classList.add('open');
+ el.scrollIntoView({block: 'start'});
+}
document.addEventListener('keydown', e => {
if (e.key === '/' && document.activeElement.tagName !== 'INPUT') {
e.preventDefault();
document.getElementById('search').focus();
}
});
+window.addEventListener('hashchange', openHashEvent);
+document.addEventListener('DOMContentLoaded', openHashEvent);