diff --git a/web/app.js b/web/app.js index be5246c..42078f6 100644 --- a/web/app.js +++ b/web/app.js @@ -648,6 +648,30 @@ `; } + function renderInlineDetail(e) { + const tags = (e.tags || []).map(t => `#${t}`).join(''); + let actions = ''; + actions += ``; + if (!e.card_type) { + actions += ``; + actions += ``; + } + if (e.card_type) { + actions += ``; + } else { + actions += ``; + } + return `
+
${escHtml(e.body || '')}
+ ${tags ? `
${tags}
` : ''} +
${actions}
+
+ + +
+
`; + } + function renderEntityItem(e, idx) { const glyph = displayGlyph(e); const gc = glyphClass(e); @@ -668,11 +692,18 @@ } return `
- ${glyph} - ${label} - ${time} - ${tags}${cardBadge} - ${useBadge} +
+ ${glyph} + ${label} + ${time} + ${tags}${cardBadge} + ${useBadge} +
+
+
+ ${renderInlineDetail(e)} +
+
`; } @@ -694,21 +725,16 @@ pane.classList.add('visible'); - const mobileBar = `
- - -
`; - if (state.peekMode === 'edit') { - pane.innerHTML = mobileBar + renderEditMode(e); + pane.innerHTML = renderEditMode(e); } else if (state.view === 'stream' || !e.card_type) { - pane.innerHTML = mobileBar + renderStreamPeek(e); + pane.innerHTML = renderStreamPeek(e); } else if (state.peekMode === 'run') { - pane.innerHTML = mobileBar + renderRunMode(e); + pane.innerHTML = renderRunMode(e); } else if (state.peekMode === 'fill') { - pane.innerHTML = mobileBar + renderFillMode(e); + pane.innerHTML = renderFillMode(e); } else { - pane.innerHTML = mobileBar + renderCardPeek(e); + pane.innerHTML = renderCardPeek(e); } bindPeekEvents(e); @@ -1105,6 +1131,20 @@ // ========== Actions ========== function selectEntity(idx) { + if (isMobileBreakpoint()) { + const prev = state.selectedIndex; + if (prev === idx) { + state.selectedIndex = -1; + } else { + state.selectedIndex = idx; + } + $$('.entity-item.selected').forEach(el => el.classList.remove('selected')); + if (state.selectedIndex >= 0) { + const target = $(`.entity-item[data-index="${state.selectedIndex}"]`); + if (target) target.classList.add('selected'); + } + return; + } state.selectedIndex = idx; state.peekMode = 'preview'; state.runChecked = new Set(); @@ -1408,13 +1448,31 @@ }, togglePeekFull() { + if (isMobileBreakpoint()) { + this.expandInline(); + return; + } const pane = $('#detail-pane'); pane.classList.toggle('peek-full'); const btn = pane.querySelector('.peek-mobile-btn'); if (btn) btn.textContent = pane.classList.contains('peek-full') ? '↓' : '↑'; }, + expandInline() { + const sel = $(`.entity-item.selected`); + if (!sel) return; + sel.classList.toggle('exp-full'); + const btn = sel.querySelector('.exp-toolbar .peek-mobile-btn'); + if (btn) btn.textContent = sel.classList.contains('exp-full') ? '↓' : '↑'; + }, + dismissPeek() { + if (isMobileBreakpoint()) { + const sel = $(`.entity-item.selected`); + if (sel) sel.classList.remove('selected', 'exp-full'); + state.selectedIndex = -1; + return; + } const pane = $('#detail-pane'); pane.classList.remove('visible', 'peek-full'); state.selectedIndex = -1; @@ -1471,6 +1529,14 @@ } if (ev.key === 'Escape') { + if (isMobileBreakpoint()) { + if (state.selectedIndex >= 0) { + const sel = $(`.entity-item.selected`); + if (sel) sel.classList.remove('selected', 'exp-full'); + state.selectedIndex = -1; + return; + } + } const pane = $('#detail-pane'); if ($('main').classList.contains('focus-peek')) { exitFocusPeek(); diff --git a/web/style.css b/web/style.css index fd71a34..fb7636f 100644 --- a/web/style.css +++ b/web/style.css @@ -375,28 +375,72 @@ main.focus-peek .resize-handle { visibility: hidden; } } .entity-item { + cursor: pointer; + border-left: 2px solid transparent; + transition: background var(--t-fast), border-left-color var(--t-fast); +} + +.entity-head { display: flex; align-items: center; gap: 8px; padding: 5px 16px 5px 20px; - cursor: pointer; - border-left: 2px solid transparent; min-height: 32px; - transition: background var(--t-fast), border-left-color var(--t-fast); } .entity-item:hover { background: var(--surf); } .entity-item.selected { background: var(--surf); border-left-color: var(--accent); } +.entity-exp { display: none; } + +.entity-exp-clip { overflow: hidden; } + +.exp-inner { + padding: .6rem 1rem .7rem calc(20px + 14px + 8px); + border-top: 1px solid var(--border); +} + +.exp-body { + font-family: var(--mono); + font-size: 11px; + color: var(--text); + line-height: 1.7; + white-space: pre-wrap; + word-break: break-word; + margin-bottom: .5rem; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.entity-item.exp-full .exp-body { + -webkit-line-clamp: unset; + overflow: visible; +} + +.exp-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: .5rem; } + +.exp-acts { display: flex; flex-wrap: wrap; gap: 4px; } + +.exp-toolbar { + display: none; + justify-content: space-between; + margin-top: .5rem; + padding-top: .5rem; + border-top: 1px solid var(--border); +} + .entity-item.is-card { background: var(--surf); margin: 2px 10px; border-radius: var(--r2); border: 1px solid var(--border); border-left-width: 1px; - padding: 7px 12px; } +.entity-item.is-card .entity-head { padding: 7px 12px; } + .entity-item.is-card:hover { border-color: var(--muted); } .entity-item.is-card.selected { border-color: var(--accent); background: var(--a-bg); } @@ -785,13 +829,6 @@ main.focus-peek .resize-handle { visibility: hidden; } overflow: hidden; } -.peek-mobile-bar { - display: none; - justify-content: space-between; - padding: 6px 12px; - border-bottom: 1px solid var(--border); -} - .peek-mobile-btn { font-family: var(--mono); font-size: 14px; @@ -1488,8 +1525,23 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius: transition: transform var(--t-base); z-index: 50; } - #detail-pane.visible { transform: translateY(0); } - #detail-pane.peek-full { height: 100vh; height: 100dvh; top: 0; } - .peek-mobile-bar { display: flex; } + #detail-pane { display: none !important; } + .entity-exp { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows .2s ease; + } + .entity-item.selected .entity-exp { grid-template-rows: 1fr; } + .entity-item.selected .exp-toolbar { display: flex; } + .entity-item.exp-full { + position: fixed; + inset: 0; + z-index: 60; + background: var(--bg); + overflow-y: auto; + border-left: none; + } + .entity-item.exp-full .entity-exp { grid-template-rows: 1fr; } + .entity-item.exp-full .exp-inner { padding-top: 1rem; padding-bottom: 2rem; } main.focus-peek #entity-panel { display: block; overflow: auto; min-width: 0; } }