Spaces:
Sleeping
Sleeping
| import { STATE, removeBookmark } from '../../core/State.js'; | |
| let updateCallback = null; | |
| /** | |
| * Build the Bookmarks list card. | |
| * @param {import('../types.js').PanelContext} ctx | |
| * @returns {HTMLDivElement} | |
| */ | |
| export function buildBookmarksPanel(ctx) { | |
| const card = document.createElement('div'); | |
| card.className = 'section-card'; | |
| card.innerHTML = ` | |
| <div class="section-title" style="color: var(--color-brand-primary);" title="List of saved relief coordinates for quick jump to Mode C">📌 Navigation Bookmarks</div> | |
| <ul id="bookmarks-list-ul" class="list-container" style="max-height: 180px; overflow-y: auto;"></ul> | |
| `; | |
| updateCallback = () => { | |
| const ul = card.querySelector('#bookmarks-list-ul'); | |
| if (!ul) return; | |
| ul.innerHTML = ''; | |
| if (!STATE.bookmarks || STATE.bookmarks.length === 0) { | |
| ul.innerHTML = `<li class="list-item" style="color: var(--color-neutral-400); justify-content: center; font-size: var(--font-size-small); font-style: italic; text-align: center; padding: var(--spacing-sm) 0;"> | |
| No bookmarks yet. Point at a relief and press 'B' to save | |
| </li>`; | |
| return; | |
| } | |
| STATE.bookmarks.forEach(bm => { | |
| const li = document.createElement('li'); | |
| li.className = 'list-item'; | |
| li.style.cssText = 'cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: var(--spacing-xs);'; | |
| // User name, metadata badges (chunk index, dimension), and a delete icon. | |
| li.innerHTML = ` | |
| <div class="bookmark-info" style="display: flex; flex-direction: column; gap: 2px; overflow: hidden; flex: 1; padding-right: var(--spacing-xs);"> | |
| <span style="font-weight: 700; color: var(--color-neutral-50); font-size: var(--font-size-small); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${bm.name}">${bm.name}</span> | |
| <span style="font-size: 10px; color: var(--color-neutral-400); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="${bm.docId}">${bm.docId}</span> | |
| <div style="display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px;"> | |
| <span class="badge" style="background: oklch(from var(--color-neutral-50) l c h / 0.05); border: 1px solid oklch(from var(--color-neutral-50) l c h / 0.1); padding: 1px 4px; border-radius: 4px; font-size: 9px; font-family: var(--font-mono); color: var(--color-brand-secondary);" title="Fragment index (Chunk Z)">Chunk: ${bm.chunkIndex}</span> | |
| <span class="badge" style="background: oklch(from var(--color-neutral-50) l c h / 0.05); border: 1px solid oklch(from var(--color-neutral-50) l c h / 0.1); padding: 1px 4px; border-radius: 4px; font-size: 9px; font-family: var(--font-mono); color: var(--color-brand-primary);" title="Latent dimension index (Dim X)">Dim: ${bm.dimIndex}</span> | |
| <span class="badge" style="background: oklch(from var(--color-neutral-50) l c h / 0.05); border: 1px solid oklch(from var(--color-neutral-50) l c h / 0.1); padding: 1px 4px; border-radius: 4px; font-size: 9px; font-family: var(--font-mono); color: var(--color-semantic-success);" title="Numeric activation value (Y)">Val: ${bm.value.toFixed(4)}</span> | |
| </div> | |
| </div> | |
| <button class="btn-delete btn btn-danger" style="width: 22px; height: 22px; min-height: 22px; padding: 0; font-size: 10px; border-radius: 4px; display: flex; align-items: center; justify-content: center; flex-shrink: 0;" title="Remove bookmark">🗑️</button> | |
| `; | |
| // Click handlers | |
| li.onclick = (e) => { | |
| // If clicked on delete button, do not navigate | |
| if (e.target.closest('.btn-delete')) return; | |
| // On click on the item body: Set STATE.comparisonMode = 'C', set STATE.reliefInspectDim = dimIndex, | |
| // and open the fullscreen Mode C inspector, focusing directly on the marked dimension. | |
| STATE.comparisonMode = 'C'; | |
| STATE.reliefInspectDim = bm.dimIndex; | |
| // Add the dimension to comparison dims if not already there | |
| if (!STATE.reliefInspectDims) STATE.reliefInspectDims = []; | |
| if (!STATE.reliefInspectDims.some(d => d.dim === bm.dimIndex)) { | |
| STATE.reliefInspectDims.push({ dim: bm.dimIndex, visible: true }); | |
| } | |
| // Call applyComparison | |
| if (typeof ctx.applyComparison === 'function') { | |
| ctx.applyComparison(); | |
| } else if (typeof window.applyComparison === 'function') { | |
| window.applyComparison(); | |
| } | |
| }; | |
| const btnDelete = li.querySelector('.btn-delete'); | |
| if (btnDelete) { | |
| btnDelete.onclick = (e) => { | |
| e.stopPropagation(); | |
| removeBookmark(bm.id); | |
| updateBookmarksUI(); | |
| }; | |
| } | |
| ul.appendChild(li); | |
| }); | |
| }; | |
| // Register refresh | |
| ctx.refresh.bookmarks = updateCallback; | |
| // Render initially | |
| setTimeout(() => { | |
| updateBookmarksUI(); | |
| }, 0); | |
| return card; | |
| } | |
| export function updateBookmarksUI() { | |
| if (typeof updateCallback === 'function') { | |
| updateCallback(); | |
| } | |
| } | |