From 54ad67a2f880dec309282bbf2fcd4ced45a9b4ea Mon Sep 17 00:00:00 2001
From: makoto-soracom
Date: Sat, 7 Mar 2026 14:42:46 +0900
Subject: [PATCH 1/2] Update session copy icons and inline code padding
---
internal/render/templates/session.html | 86 +++++++-----
internal/render/templates/style.html | 173 +++++++++++++++++++++++--
2 files changed, 213 insertions(+), 46 deletions(-)
diff --git a/internal/render/templates/session.html b/internal/render/templates/session.html
index cc8755c..cb351b5 100644
--- a/internal/render/templates/session.html
+++ b/internal/render/templates/session.html
@@ -9,13 +9,12 @@
@@ -39,7 +38,13 @@ {{ .File.Name }}
{{ if .Items }}
{{ range .Items }}
-
+
+ {{ if eq .Line $.LastUserLine }} {{ end }}
+ {{ if eq .Line $.LastAgentLine }} {{ end }}
+ {{ if and (gt $.LastUserLine 0) (eq .Line $.LastUserLine) }} {{ else if and (eq $.LastUserLine 0) (eq .Line $.LastItemLine) }} {{ end }}
+ {{ if .IsTurnAborted }}
+ {{ .TurnAbortedMessage }}
+ {{ else }}
{{ if eq .Subtype "reasoning" }}
@@ -59,6 +63,7 @@ {{ .File.Name }}
{{ .HTML }}
{{ end }}
+ {{ end }}
{{ end }}
{{ else }}
@@ -77,6 +82,26 @@ {{ .File.Name }}
updateStickyOffset();
window.addEventListener("resize", updateStickyOffset);
+ function getStickyOffset() {
+ return stickyHeader ? stickyHeader.offsetHeight : 0;
+ }
+ function getElementTop(element) {
+ var rect = element.getBoundingClientRect();
+ var scrollY = window.scrollY || window.pageYOffset || 0;
+ return rect.top + scrollY;
+ }
+ function scrollToElement(element) {
+ if (!element) return;
+ var offset = getStickyOffset() + 8;
+ var top = getElementTop(element) - offset;
+ if (top < 0) top = 0;
+ if (typeof window.scrollTo === "function") {
+ window.scrollTo({ top: top, behavior: "smooth" });
+ } else {
+ window.scrollTop = top;
+ }
+ }
+
function copyText(text, target) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).catch(function () {});
@@ -140,32 +165,13 @@ {{ .File.Name }}
var userSections = Array.prototype.slice.call(
document.querySelectorAll("section.session-item.role-user")
);
- var getStickyOffset = function () {
- return stickyHeader ? stickyHeader.offsetHeight : 0;
- };
- var getSectionTop = function (section) {
- var rect = section.getBoundingClientRect();
- var scrollY = window.scrollY || window.pageYOffset || 0;
- return rect.top + scrollY;
- };
- var scrollToSection = function (section) {
- if (!section) return;
- var offset = getStickyOffset() + 8;
- var top = getSectionTop(section) - offset;
- if (top < 0) top = 0;
- if (typeof window.scrollTo === "function") {
- window.scrollTo({ top: top, behavior: "smooth" });
- } else {
- window.scrollTop = top;
- }
- };
var findCurrentIndex = function () {
var offset = getStickyOffset() + 8;
var scrollY = window.scrollY || window.pageYOffset || 0;
var position = scrollY + offset + 1;
var current = -1;
for (var i = 0; i < userSections.length; i++) {
- if (getSectionTop(userSections[i]) <= position) {
+ if (getElementTop(userSections[i]) <= position) {
current = i;
} else {
break;
@@ -178,19 +184,35 @@ {{ .File.Name }}
if (!userSections.length) return;
var index = findCurrentIndex();
var target = index <= 0 ? 0 : index - 1;
- scrollToSection(userSections[target]);
+ scrollToElement(userSections[target]);
};
var goNext = function (event) {
if (event) event.preventDefault();
if (!userSections.length) return;
var index = findCurrentIndex();
var target = index < 0 ? 0 : Math.min(index + 1, userSections.length - 1);
- scrollToSection(userSections[target]);
+ scrollToElement(userSections[target]);
};
if (jumpPrev) jumpPrev.addEventListener("click", goPrev);
if (jumpNext) jumpNext.addEventListener("click", goNext);
}
+ function handleInitialJump() {
+ var hash = window.location.hash || "";
+ if (hash.length > 1) {
+ var target = document.getElementById(hash.slice(1));
+ if (target) {
+ setTimeout(function () { scrollToElement(target); }, 0);
+ return;
+ }
+ }
+ var lastItem = document.getElementById("last-item");
+ if (lastItem) {
+ setTimeout(function () { scrollToElement(lastItem); }, 0);
+ }
+ }
+ handleInitialJump();
+
})();
diff --git a/internal/render/templates/style.html b/internal/render/templates/style.html
index 5e07766..22d35de 100644
--- a/internal/render/templates/style.html
+++ b/internal/render/templates/style.html
@@ -7,8 +7,9 @@
--muted: #8fa6a3;
--accent: #49c1b5;
--border: #243033;
+ --border-strong: #3b4a4f;
--user: #15333a;
- --assistant: #171f21;
+ --assistant: #223034;
--tool: #1a2b27;
--system: #1a2123;
--error: #3a1f22;
@@ -23,8 +24,9 @@
--muted: #9aa5b1;
--accent: #7fb6ff;
--border: #263243;
+ --border-strong: #3b4a60;
--user: #1e2f4d;
- --assistant: #1a2230;
+ --assistant: #243041;
--tool: #1b2b2a;
--system: #1c2330;
--error: #3a1f25;
@@ -39,8 +41,9 @@
--muted: #b09b84;
--accent: #e0a35c;
--border: #332a21;
+ --border-strong: #4a3c2d;
--user: #2c2318;
- --assistant: #201b14;
+ --assistant: #2a241b;
--tool: #2a2319;
--system: #251f17;
--error: #3a1f1a;
@@ -55,8 +58,9 @@
--muted: #8fa6a3;
--accent: #49c1b5;
--border: #243033;
+ --border-strong: #3b4a4f;
--user: #15333a;
- --assistant: #171f21;
+ --assistant: #223034;
--tool: #1a2b27;
--system: #1a2123;
--error: #3a1f22;
@@ -71,8 +75,9 @@
--muted: #9eb39a;
--accent: #9ad36a;
--border: #243228;
+ --border-strong: #3b4b3c;
--user: #1f2f23;
- --assistant: #18211b;
+ --assistant: #223025;
--tool: #1d2a1f;
--system: #1b231d;
--error: #3b1f20;
@@ -87,8 +92,9 @@
--muted: #b59bb0;
--accent: #e06c9f;
--border: #33263a;
+ --border-strong: #4a3b4f;
--user: #2f1c2a;
- --assistant: #211820;
+ --assistant: #2b202a;
--tool: #2a1c27;
--system: #241a22;
--error: #3b1f27;
@@ -103,8 +109,9 @@
--muted: #9aaab5;
--accent: #5cc7e6;
--border: #24313a;
+ --border-strong: #3a4a56;
--user: #1d2e3a;
- --assistant: #181f24;
+ --assistant: #222c33;
--tool: #1b2a2f;
--system: #1b2226;
--error: #3a1f23;
@@ -142,6 +149,31 @@
color: var(--accent);
text-decoration: none;
}
+.cwd-link {
+ color: var(--accent);
+ text-decoration: none;
+}
+.nav-btn {
+ border: 1px solid var(--border);
+ background: transparent;
+ color: var(--accent);
+ padding: 2px 8px;
+ border-radius: 999px;
+ font-size: 12px;
+ cursor: pointer;
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+}
+.nav-btn:hover {
+ background: var(--border);
+}
+.nav-btn.disabled {
+ opacity: 0.45;
+ cursor: default;
+ pointer-events: none;
+}
.tabs {
margin-top: 10px;
display: flex;
@@ -189,13 +221,22 @@
font-size: 32px;
margin: 0 0 8px;
}
+.page-title .title-links {
+ font-size: 14px;
+ font-weight: normal;
+ margin-left: 6px;
+}
+.page-title .title-link {
+ font-size: 14px;
+ font-weight: normal;
+}
.subtitle {
color: var(--muted);
margin: 0;
}
.card {
background: var(--panel);
- border: 1px solid var(--border);
+ border: 1px solid var(--border-strong);
border-radius: 12px;
padding: 16px 20px;
margin: 16px 0;
@@ -262,15 +303,11 @@
margin: 0;
}
.list li {
- padding: 10px 0;
border-bottom: 1px dashed var(--border);
}
.list li:last-child {
border-bottom: none;
}
-.link-list li {
- padding: 0;
-}
.dir-item {
padding: 0;
margin: 8px 0;
@@ -325,8 +362,81 @@
text-decoration: none;
transition: background-color 0.15s ease;
}
+.session-snippet {
+ margin-top: 10px;
+}
+.session-snippet-thread {
+ display: grid;
+ gap: 10px;
+}
+.snippet-divider {
+ text-align: center;
+ color: var(--muted);
+ font-size: 14px;
+ letter-spacing: 0.2em;
+}
+.snippet-link {
+ display: block;
+ color: inherit;
+ text-decoration: none;
+}
+.snippet-link:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+.session-snippet-thread .session-item {
+ margin-bottom: 0;
+ padding: 10px 12px;
+}
+.session-snippet-thread .session-content {
+ font-size: 13px;
+ line-height: 1.5;
+}
+.session-list-item {
+ padding: 10px;
+ border-radius: 10px;
+ transition: box-shadow 0.15s ease;
+}
+.session-list-item.search-result {
+ border: 1px solid var(--border-strong);
+ margin: 12px 0;
+}
+.session-list-item:hover,
+.session-list-item:focus-within {
+ box-shadow: inset 0 0 0 9999px rgba(255, 255, 255, 0.05);
+ cursor: pointer;
+}
+.pagination {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin: 10px 0 14px;
+ flex-wrap: wrap;
+}
+.page-meta {
+ color: var(--muted);
+ font-size: 12px;
+}
+.filter-form {
+ margin-top: 6px;
+}
+.filter-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ color: var(--muted);
+}
+.filter-checkbox {
+ width: 16px;
+ height: 16px;
+ accent-color: var(--accent);
+}
+.filter-label {
+ user-select: none;
+}
.link-item-link:hover {
- background: rgba(255, 255, 255, 0.05);
+ background: transparent;
}
.link-item-link:focus-visible {
outline: 2px solid var(--accent);
@@ -344,6 +454,9 @@
color: var(--muted);
font-size: 14px;
}
+summary.meta {
+ cursor: pointer;
+}
.session-item {
padding: 16px;
border-radius: 10px;
@@ -352,6 +465,26 @@
background: var(--panel);
scroll-margin-top: calc(var(--sticky-offset, 180px) + 8px);
}
+.turn-aborted-item {
+ margin: 8px 0 16px;
+ margin-left: 33%;
+ padding: 0;
+ border: none;
+ background: transparent;
+ scroll-margin-top: calc(var(--sticky-offset, 180px) + 8px);
+}
+.turn-aborted-text {
+ margin: 0;
+ color: #ff6b6b;
+ font-size: 14px;
+ line-height: 1.6;
+}
+.session-anchor {
+ display: block;
+ height: 0;
+ width: 0;
+ scroll-margin-top: calc(var(--sticky-offset, 180px) + 8px);
+}
.session-item.role-user { background: var(--user); }
.session-item.role-user .session-content {
white-space: pre-wrap;
@@ -463,7 +596,7 @@
}
.session-content code {
background: var(--code);
- padding: 2px 6px;
+ padding: 4px 8px;
border-radius: 6px;
border: 1px solid var(--border);
}
@@ -478,6 +611,18 @@
border-left: 3px solid var(--border);
color: var(--muted);
}
+.session-content table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ margin: 0 0 0.9em;
+}
+.session-content th,
+.session-content td {
+ border: 1px solid rgba(255, 255, 255, 0.18);
+ padding: 6px 8px;
+ text-align: left;
+ vertical-align: top;
+}
.tag {
display: inline-block;
padding: 2px 8px;
From 5c95e1082be549dd9b2a48a3bc9a072e281c9e72 Mon Sep 17 00:00:00 2001
From: makoto-soracom
Date: Sat, 7 Mar 2026 14:43:34 +0900
Subject: [PATCH 2/2] Commit remaining session parsing, search, and web updates
---
internal/render/templates/day.html | 362 +++++++++++++++++-
internal/render/templates/dir.html | 67 +++-
internal/render/templates/index.html | 66 +++-
internal/search/index.go | 292 ++++++++++++--
internal/search/index_test.go | 80 ++++
internal/sessions/parser.go | 39 ++
internal/sessions/parser_test.go | 58 +++
internal/sessions/turn_aborted.go | 30 ++
internal/web/server.go | 548 ++++++++++++++++++++++++---
internal/web/share.go | 11 +-
10 files changed, 1439 insertions(+), 114 deletions(-)
create mode 100644 internal/sessions/turn_aborted.go
diff --git a/internal/render/templates/day.html b/internal/render/templates/day.html
index fa8fce1..6113dd3 100644
--- a/internal/render/templates/day.html
+++ b/internal/render/templates/day.html
@@ -12,12 +12,99 @@
All dates {{ if .SelectedCwd }} / All directories / Directory dates {{ end }}
Sessions on {{ .Date.Label }}{{ if .SelectedCwdLabel }} – {{ .SelectedCwdLabel }}{{ end }}
{{ if .SelectedCwd }}
- Directory filter active. Clear filter / View directory dates
+ Directory filter active. Clear directory filter
{{ end }}
+
+ {{ if .SelectedCwd }}
+
+
Search in {{ .SelectedCwdLabel }}
+
+
+
+
+ {{ end }}
+ {{ if .FallbackDate }}
+
+
No sessions found for this directory today.
+
+
+ --- Previous Day ---
+
+
Filter by directory (click to filter sessions below)
+
+
+ {{ if or .HasPrev .HasNext (gt .TotalPages 1) }}
+
+ {{ end }}
+
+ {{ if .FallbackSessions }}
+
+ {{ else }}
+
No sessions found for this directory.
+ {{ end }}
+
+ {{ else }}
{{ if .Dirs }}
-
+
Filter by directory (click to filter sessions below)
{{ range .Dirs }}
@@ -32,15 +119,49 @@ Sessions on {{ .Date.Label }}{{ if .SelectedCwdLabel }}
{{ end }}
-
+ {{ if or .HasPrev .HasNext (gt .TotalPages 1) }}
+
+ {{ end }}
+
{{ if .Sessions }}
@@ -48,7 +169,238 @@
Sessions on {{ .Date.Label }}{{ if .SelectedCwdLabel }}
No sessions found{{ if .SelectedCwdLabel }} for this directory{{ end }}.
{{ end }}
+ {{ end }}
+