Skip to content

Commit d568e20

Browse files
committed
Fix Breadcrumbs webview
1 parent c36ff36 commit d568e20

2 files changed

Lines changed: 131 additions & 82 deletions

File tree

src/webviews/assets/breadcrumbs.css

Lines changed: 72 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -74,90 +74,112 @@
7474
.crumb-list {
7575
display: flex;
7676
flex-direction: column;
77-
gap: 1.2rem;
77+
gap: 0.9rem;
7878
}
7979

8080
.crumb-item {
81-
display: grid;
82-
grid-template-columns: 32px 1fr;
81+
border: 1px solid var(--vscode-editorWidget-border);
82+
border-radius: 8px;
83+
background: var(--vscode-editorWidget-background);
84+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
85+
}
86+
87+
.crumb-item[open] {
88+
border-color: var(--vscode-focusBorder);
89+
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
90+
}
91+
92+
.crumb-item summary {
93+
list-style: none;
94+
display: flex;
95+
align-items: center;
8396
gap: 0.75rem;
84-
position: relative;
97+
padding: 0.85rem 1rem;
98+
cursor: pointer;
8599
}
86100

87-
.crumb-item__marker {
88-
position: relative;
89-
width: 24px;
90-
height: 24px;
91-
border-radius: 50%;
92-
background: var(--vscode-button-secondaryBackground);
93-
color: var(--vscode-button-secondaryForeground);
94-
font-size: 0.85rem;
101+
.crumb-item summary::-webkit-details-marker {
102+
display: none;
103+
}
104+
105+
.crumb-summary__meta {
95106
display: flex;
96107
align-items: center;
97-
justify-content: center;
98-
margin-top: 0.2rem;
108+
gap: 0.75rem;
109+
flex: 1;
110+
min-width: 0;
99111
}
100112

101-
.crumb-item__marker::after {
102-
content: '';
103-
position: absolute;
104-
top: 24px;
105-
left: 50%;
106-
transform: translateX(-50%);
107-
width: 2px;
108-
height: calc(100% + 18px);
109-
background: var(--vscode-editorLineNumber-activeForeground, rgba(128, 128, 128, 0.6));
113+
.crumb-step {
114+
font-size: 0.75rem;
115+
letter-spacing: 0.06em;
116+
text-transform: uppercase;
117+
background: var(--vscode-textLink-activeForeground, rgba(100, 149, 237, 0.25));
118+
color: var(--vscode-textLink-foreground);
119+
border-radius: 999px;
120+
padding: 0.1rem 0.6rem;
121+
font-weight: 600;
110122
}
111123

112-
.crumb-item--last .crumb-item__marker::after {
113-
display: none;
124+
.crumb-title {
125+
font-weight: 600;
126+
color: var(--vscode-editor-foreground);
127+
overflow: hidden;
128+
text-overflow: ellipsis;
129+
white-space: nowrap;
114130
}
115131

116-
.crumb-item__body {
117-
border: 1px solid var(--vscode-editorWidget-border);
118-
background: var(--vscode-editorWidget-background);
119-
border-radius: 6px;
120-
padding: 0.75rem;
121-
cursor: pointer;
122-
transition: border-color 0.2s ease, box-shadow 0.2s ease;
132+
.crumb-preview {
133+
color: var(--vscode-descriptionForeground);
134+
font-size: 0.85rem;
135+
flex: 1;
136+
overflow: hidden;
137+
text-overflow: ellipsis;
138+
white-space: nowrap;
123139
}
124140

125-
.crumb-item__body:hover {
126-
border-color: var(--vscode-focusBorder);
127-
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
141+
.crumb-chevron {
142+
color: var(--vscode-descriptionForeground);
143+
font-size: 0.85rem;
128144
}
129145

130-
.crumb-item__header {
146+
.crumb-body {
147+
padding: 0 1rem 1rem;
131148
display: flex;
132-
justify-content: space-between;
133-
align-items: baseline;
134-
gap: 0.5rem;
149+
flex-direction: column;
150+
gap: 0.75rem;
151+
border-top: 1px solid var(--vscode-editorWidget-border);
135152
}
136153

137-
.crumb-item__path {
138-
font-weight: 600;
154+
.crumb-meta {
155+
margin: 0.5rem 0 0;
156+
font-size: 0.75rem;
157+
color: var(--vscode-descriptionForeground);
139158
}
140159

141-
.crumb-item__note {
142-
margin: 0.5rem 0 0;
160+
.crumb-note {
161+
margin: 0;
162+
padding-left: 0.75rem;
163+
border-left: 3px solid var(--vscode-textPreformat-foreground);
143164
color: var(--vscode-descriptionForeground);
144165
font-style: italic;
145166
}
146167

147-
.crumb-item__snippet {
148-
margin: 0.75rem 0 0;
168+
.crumb-snippet {
169+
margin: 0;
149170
background: var(--vscode-editor-background);
150171
border: 1px solid var(--vscode-editorWidget-border);
151172
border-radius: 4px;
152-
padding: 0.5rem;
173+
padding: 0.6rem;
153174
white-space: pre-wrap;
154175
word-break: break-word;
155176
font-family: var(--vscode-editor-font-family, monospace);
156177
font-size: 0.85rem;
178+
cursor: pointer;
179+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
157180
}
158181

159-
.crumb-item__meta {
160-
margin: 0.75rem 0 0;
161-
font-size: 0.75rem;
162-
color: var(--vscode-descriptionForeground);
182+
.crumb-snippet:hover {
183+
border-color: var(--vscode-focusBorder);
184+
box-shadow: 0 0 0 1px var(--vscode-focusBorder);
163185
}

src/webviews/assets/breadcrumbs.js

Lines changed: 59 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -129,61 +129,88 @@
129129
list.className = 'crumb-list';
130130

131131
trail.crumbs.forEach((crumb, index) => {
132-
list.appendChild(renderCrumb(crumb, index === trail.crumbs.length - 1, trail.id));
132+
list.appendChild(renderCrumb(crumb, index, trail.id));
133133
});
134134

135135
content.appendChild(list);
136136
}
137137

138-
function renderCrumb(crumb, isLast, trailId) {
139-
const item = document.createElement('div');
140-
item.className = `crumb-item${isLast ? ' crumb-item--last' : ''}`;
141-
item.dataset.crumbId = crumb.id;
142-
item.dataset.trailId = trailId;
138+
function renderCrumb(crumb, index, trailId) {
139+
const details = document.createElement('details');
140+
details.className = 'crumb-item';
141+
details.dataset.crumbId = crumb.id;
142+
details.dataset.trailId = trailId;
143+
if (index === 0) {
144+
details.open = true;
145+
}
146+
147+
const summary = document.createElement('summary');
148+
summary.className = 'crumb-summary';
149+
150+
const summaryMeta = document.createElement('div');
151+
summaryMeta.className = 'crumb-summary__meta';
152+
153+
const step = document.createElement('span');
154+
step.className = 'crumb-step';
155+
step.textContent = `Step ${index + 1}`;
156+
summaryMeta.appendChild(step);
157+
158+
const title = document.createElement('span');
159+
title.className = 'crumb-title';
160+
title.textContent = `${crumb.filePath}:${crumb.rangeLabel}`;
161+
summaryMeta.appendChild(title);
162+
163+
summary.appendChild(summaryMeta);
143164

144-
const marker = document.createElement('div');
145-
marker.className = 'crumb-item__marker';
146-
const markerLabel = document.createElement('span');
147-
markerLabel.textContent = String(crumb.index + 1);
148-
marker.appendChild(markerLabel);
149-
item.appendChild(marker);
165+
const preview = document.createElement('span');
166+
preview.className = 'crumb-preview';
167+
preview.textContent = crumb.note || crumb.snippetPreview || '(no preview)';
168+
summary.appendChild(preview);
169+
170+
const chevron = document.createElement('span');
171+
chevron.className = 'crumb-chevron';
172+
chevron.innerHTML = '▾';
173+
summary.appendChild(chevron);
174+
175+
details.appendChild(summary);
150176

151177
const body = document.createElement('div');
152-
body.className = 'crumb-item__body';
178+
body.className = 'crumb-body';
153179

154-
const header = document.createElement('div');
155-
header.className = 'crumb-item__header';
156-
const path = document.createElement('span');
157-
path.className = 'crumb-item__path';
158-
path.textContent = `${crumb.filePath}:${crumb.rangeLabel}`;
159-
header.appendChild(path);
160-
body.appendChild(header);
180+
const meta = document.createElement('p');
181+
meta.className = 'crumb-meta';
182+
const created = new Date(crumb.createdAt).toLocaleString();
183+
meta.textContent = `Captured ${created}`;
184+
body.appendChild(meta);
161185

162186
if (crumb.note) {
163187
const note = document.createElement('p');
164-
note.className = 'crumb-item__note';
188+
note.className = 'crumb-note';
165189
note.textContent = crumb.note;
166190
body.appendChild(note);
167191
}
168192

169193
const snippet = document.createElement('pre');
170-
snippet.className = 'crumb-item__snippet';
194+
snippet.className = 'crumb-snippet';
171195
snippet.textContent = crumb.snippet;
196+
snippet.addEventListener('click', (event) => {
197+
event.stopPropagation();
198+
vscode.postMessage({ type: 'openCrumb', trailId, crumbId: crumb.id });
199+
});
172200
body.appendChild(snippet);
173201

174-
const meta = document.createElement('p');
175-
meta.className = 'crumb-item__meta';
176-
const created = new Date(crumb.createdAt).toLocaleString();
177-
meta.textContent = `Captured ${created}`;
178-
body.appendChild(meta);
179-
180-
body.addEventListener('click', () => {
181-
vscode.postMessage({ type: 'openCrumb', trailId, crumbId: crumb.id });
202+
details.addEventListener('toggle', () => {
203+
if (details.open) {
204+
chevron.innerHTML = '▾';
205+
} else {
206+
chevron.innerHTML = '▸';
207+
}
182208
});
183209

184-
item.appendChild(body);
210+
chevron.innerHTML = details.open ? '▾' : '▸';
211+
details.appendChild(body);
185212

186-
return item;
213+
return details;
187214
}
188215

189216
vscode.postMessage({ type: 'ready' });

0 commit comments

Comments
 (0)