diff --git a/web/app.js b/web/app.js index a34351d..5d2904e 100644 --- a/web/app.js +++ b/web/app.js @@ -636,14 +636,45 @@ const affHtml = affs.map(a => `${AFF_LABELS[a]}`).join(''); return `
- ${escHtml(title)} - - ${preview} -
- ${affHtml} - ${tags} - ${e.pinned ? '' : ''} - ${e.use_count > 0 ? `${e.use_count}×` : ''} +
+ ${escHtml(title)} + + ${preview} +
+ ${affHtml} + ${tags} + ${e.pinned ? '' : ''} + ${e.use_count > 0 ? `${e.use_count}×` : ''} +
+
+
+
+ ${renderInlineDetail(e)} +
+
+
`; + } + + 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 `
+
${renderMd(e.body || '')}
+ ${tags ? `
${tags}
` : ''} +
${actions}
+
+ +
`; } @@ -668,11 +699,18 @@ } return `
- ${glyph} - ${label} - ${time} - ${tags}${cardBadge} - ${useBadge} +
+ ${glyph} + ${label} + ${time} + ${tags}${cardBadge} + ${useBadge} +
+
+
+ ${renderInlineDetail(e)} +
+
`; } @@ -1100,6 +1138,20 @@ // ========== Actions ========== function selectEntity(idx) { + if (isMobileBreakpoint()) { + const prev = state.selectedIndex; + if (prev === idx) { + state.selectedIndex = -1; + } else { + state.selectedIndex = idx; + } + $$('.entity-item.selected, .card-row.selected').forEach(el => el.classList.remove('selected')); + if (state.selectedIndex >= 0) { + const target = $(`.entity-item[data-index="${state.selectedIndex}"], .card-row[data-index="${state.selectedIndex}"]`); + if (target) target.classList.add('selected'); + } + return; + } state.selectedIndex = idx; state.peekMode = 'preview'; state.runChecked = new Set(); @@ -1401,6 +1453,38 @@ if (idx >= 0) { state.selectedIndex = idx; renderEntityList(); renderDetailPane(); } showToast(e.pinned ? 'unpinned' : 'pinned'); }, + + 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, .card-row.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, .card-row.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; + renderEntityList(); + }, }; // ========== Promote modal ========== @@ -1451,12 +1535,26 @@ return; } - if (ev.key === 'Escape' && $('main').classList.contains('focus-peek')) { - exitFocusPeek(); - state.selectedIndex = -1; - renderEntityList(); - renderDetailPane(); - return; + if (ev.key === 'Escape') { + if (isMobileBreakpoint()) { + if (state.selectedIndex >= 0) { + const sel = $(`.entity-item.selected, .card-row.selected`); + if (sel) sel.classList.remove('selected', 'exp-full'); + state.selectedIndex = -1; + return; + } + } + const pane = $('#detail-pane'); + if ($('main').classList.contains('focus-peek')) { + exitFocusPeek(); + } + if (pane.classList.contains('visible')) { + pane.classList.remove('visible', 'peek-full'); + state.selectedIndex = -1; + renderEntityList(); + renderDetailPane(); + return; + } } const sel = state.entities[state.selectedIndex]; @@ -1540,7 +1638,16 @@ localStorage.setItem('nib:' + cls, m.classList.contains(cls) ? '1' : ''); } + function isMobileBreakpoint() { + return window.matchMedia('(max-width: 900px)').matches; + } + function toggleZen() { + if (isMobileBreakpoint()) { + if (state.selectedIndex >= 0) nibApp.togglePeekFull(); + return; + } + const m = $('main'); if (m.classList.contains('focus-peek')) { diff --git a/web/style.css b/web/style.css index 2e3863a..07fb835 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); } @@ -556,20 +600,23 @@ main.focus-peek .resize-handle { visibility: hidden; } /* ── CARD ROWS ──────────────────────────────────────── */ .card-row { - display: flex; - align-items: center; - gap: 7px; - padding: 9px 12px 9px 14px; margin: 2px 10px; background: var(--surf); border: 1px solid var(--border); border-radius: var(--r2); cursor: pointer; - min-height: 40px; position: relative; transition: border-color var(--t-fast), background var(--t-fast); } +.card-head { + display: flex; + align-items: center; + gap: 7px; + padding: 9px 12px 9px 14px; + min-height: 40px; +} + .card-row:hover { border-color: var(--muted); } .card-row.selected { border-color: var(--accent); background: var(--a-bg); } .card-row.pinned { border-left: 2px solid var(--accent); } @@ -785,6 +832,21 @@ main.focus-peek .resize-handle { visibility: hidden; } overflow: hidden; } +.peek-mobile-btn { + font-family: var(--mono); + font-size: 14px; + color: var(--muted); + background: none; + border: 1px solid var(--border); + border-radius: 4px; + width: 28px; + height: 28px; + cursor: pointer; + transition: color var(--t-fast), border-color var(--t-fast); +} + +.peek-mobile-btn:hover { color: var(--accent); border-color: var(--accent); } + .peek-scroll { flex: 1; overflow-y: auto; @@ -1443,8 +1505,17 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius: /* ── RESPONSIVE ─────────────────────────────────────── */ @media (max-width: 900px) { - main { grid-template-columns: 1fr; } - #tag-rail { display: none; } + main, + main.hide-rail, + main.hide-peek, + main.hide-rail.hide-peek, + main.focus-peek { + grid-template-columns: 1fr !important; + transition: none !important; + } + #tag-rail { display: none !important; } + .resize-handle { display: none !important; } + #entity-panel { overflow: auto; } #detail-pane { position: fixed; inset: 0; @@ -1457,5 +1528,31 @@ 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 { display: none !important; } + .entity-exp { + display: grid; + grid-template-rows: 0fr; + transition: grid-template-rows .2s ease; + } + .entity-item.selected .entity-exp, + .card-row.selected .entity-exp { grid-template-rows: 1fr; } + .entity-item.selected .exp-toolbar, + .card-row.selected .exp-toolbar { display: flex; } + .entity-item.exp-full, + .entity-item.is-card.exp-full, + .card-row.exp-full { + position: fixed; + inset: 0; + z-index: 60; + background: var(--bg); + overflow-y: auto; + border: none; + margin: 0; + border-radius: 0; + } + .entity-item.exp-full .entity-exp, + .card-row.exp-full .entity-exp { grid-template-rows: 1fr; } + .entity-item.exp-full .exp-inner, + .card-row.exp-full .exp-inner { padding-top: 1rem; padding-bottom: 2rem; } + main.focus-peek #entity-panel { display: block; overflow: auto; min-width: 0; } }