Merge pull request 'fix/mobile-view' (#22) from fix/stream-zoom-ui into main
Reviewed-on: #22
This commit was merged in pull request #22.
This commit is contained in:
+108
-1
@@ -636,6 +636,7 @@
|
|||||||
const affHtml = affs.map(a => `<span class="aff clickable ${AFF_CLASSES[a]}">${AFF_LABELS[a]}</span>`).join('');
|
const affHtml = affs.map(a => `<span class="aff clickable ${AFF_CLASSES[a]}">${AFF_LABELS[a]}</span>`).join('');
|
||||||
|
|
||||||
return `<div class="card-row${selected}${pinCls}${flashCls}" data-index="${idx}" data-id="${e.id}">
|
return `<div class="card-row${selected}${pinCls}${flashCls}" data-index="${idx}" data-id="${e.id}">
|
||||||
|
<div class="card-head">
|
||||||
<span class="card-row-title">${escHtml(title)}</span>
|
<span class="card-row-title">${escHtml(title)}</span>
|
||||||
<span class="card-row-dash">—</span>
|
<span class="card-row-dash">—</span>
|
||||||
<span class="card-row-preview">${preview}</span>
|
<span class="card-row-preview">${preview}</span>
|
||||||
@@ -645,6 +646,36 @@
|
|||||||
${e.pinned ? '<span class="card-row-pin">★</span>' : ''}
|
${e.pinned ? '<span class="card-row-pin">★</span>' : ''}
|
||||||
${e.use_count > 0 ? `<span class="card-row-use">${e.use_count}×</span>` : ''}
|
${e.use_count > 0 ? `<span class="card-row-use">${e.use_count}×</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="entity-exp">
|
||||||
|
<div class="entity-exp-clip">
|
||||||
|
${renderInlineDetail(e)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderInlineDetail(e) {
|
||||||
|
const tags = (e.tags || []).map(t => `<span class="detail-tag">#${t}</span>`).join('');
|
||||||
|
let actions = '';
|
||||||
|
actions += `<button class="action-btn" onclick="event.stopPropagation();nibApp.enterMode('edit')">edit</button>`;
|
||||||
|
if (!e.card_type) {
|
||||||
|
actions += `<button class="action-btn" onclick="event.stopPropagation();nibApp.showAbsorb('${e.id}')">absorb</button>`;
|
||||||
|
actions += `<button class="action-btn primary" onclick="event.stopPropagation();nibApp.showPromote('${e.id}')">promote</button>`;
|
||||||
|
}
|
||||||
|
if (e.card_type) {
|
||||||
|
actions += `<button class="action-btn danger" onclick="event.stopPropagation();nibApp.demoteEntity('${e.id}')">demote</button>`;
|
||||||
|
} else {
|
||||||
|
actions += `<button class="action-btn danger" onclick="event.stopPropagation();nibApp.deleteEntity('${e.id}')">delete</button>`;
|
||||||
|
}
|
||||||
|
return `<div class="exp-inner">
|
||||||
|
<div class="exp-body md">${renderMd(e.body || '')}</div>
|
||||||
|
${tags ? `<div class="exp-tags">${tags}</div>` : ''}
|
||||||
|
<div class="exp-acts">${actions}</div>
|
||||||
|
<div class="exp-toolbar">
|
||||||
|
<button class="peek-mobile-btn" onclick="event.stopPropagation();nibApp.expandInline()">↑</button>
|
||||||
|
<button class="peek-mobile-btn" onclick="event.stopPropagation();nibApp.dismissPeek()">×</button>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,11 +699,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return `<div class="entity-item${selected}${isCard}" data-index="${idx}" data-id="${e.id}">
|
return `<div class="entity-item${selected}${isCard}" data-index="${idx}" data-id="${e.id}">
|
||||||
|
<div class="entity-head">
|
||||||
<span class="entity-glyph ${gc}">${glyph}</span>
|
<span class="entity-glyph ${gc}">${glyph}</span>
|
||||||
${label}
|
${label}
|
||||||
${time}
|
${time}
|
||||||
<span class="entity-tags">${tags}${cardBadge}</span>
|
<span class="entity-tags">${tags}${cardBadge}</span>
|
||||||
<span class="entity-meta">${useBadge}</span>
|
<span class="entity-meta">${useBadge}</span>
|
||||||
|
</div>
|
||||||
|
<div class="entity-exp">
|
||||||
|
<div class="entity-exp-clip">
|
||||||
|
${renderInlineDetail(e)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1100,6 +1138,20 @@
|
|||||||
// ========== Actions ==========
|
// ========== Actions ==========
|
||||||
|
|
||||||
function selectEntity(idx) {
|
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.selectedIndex = idx;
|
||||||
state.peekMode = 'preview';
|
state.peekMode = 'preview';
|
||||||
state.runChecked = new Set();
|
state.runChecked = new Set();
|
||||||
@@ -1401,6 +1453,38 @@
|
|||||||
if (idx >= 0) { state.selectedIndex = idx; renderEntityList(); renderDetailPane(); }
|
if (idx >= 0) { state.selectedIndex = idx; renderEntityList(); renderDetailPane(); }
|
||||||
showToast(e.pinned ? 'unpinned' : 'pinned');
|
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 ==========
|
// ========== Promote modal ==========
|
||||||
@@ -1451,13 +1535,27 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.key === 'Escape' && $('main').classList.contains('focus-peek')) {
|
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();
|
exitFocusPeek();
|
||||||
|
}
|
||||||
|
if (pane.classList.contains('visible')) {
|
||||||
|
pane.classList.remove('visible', 'peek-full');
|
||||||
state.selectedIndex = -1;
|
state.selectedIndex = -1;
|
||||||
renderEntityList();
|
renderEntityList();
|
||||||
renderDetailPane();
|
renderDetailPane();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const sel = state.entities[state.selectedIndex];
|
const sel = state.entities[state.selectedIndex];
|
||||||
|
|
||||||
@@ -1540,7 +1638,16 @@
|
|||||||
localStorage.setItem('nib:' + cls, m.classList.contains(cls) ? '1' : '');
|
localStorage.setItem('nib:' + cls, m.classList.contains(cls) ? '1' : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMobileBreakpoint() {
|
||||||
|
return window.matchMedia('(max-width: 900px)').matches;
|
||||||
|
}
|
||||||
|
|
||||||
function toggleZen() {
|
function toggleZen() {
|
||||||
|
if (isMobileBreakpoint()) {
|
||||||
|
if (state.selectedIndex >= 0) nibApp.togglePeekFull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const m = $('main');
|
const m = $('main');
|
||||||
|
|
||||||
if (m.classList.contains('focus-peek')) {
|
if (m.classList.contains('focus-peek')) {
|
||||||
|
|||||||
+109
-12
@@ -375,28 +375,72 @@ main.focus-peek .resize-handle { visibility: hidden; }
|
|||||||
}
|
}
|
||||||
|
|
||||||
.entity-item {
|
.entity-item {
|
||||||
|
cursor: pointer;
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
transition: background var(--t-fast), border-left-color var(--t-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 5px 16px 5px 20px;
|
padding: 5px 16px 5px 20px;
|
||||||
cursor: pointer;
|
|
||||||
border-left: 2px solid transparent;
|
|
||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
transition: background var(--t-fast), border-left-color var(--t-fast);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-item:hover { background: var(--surf); }
|
.entity-item:hover { background: var(--surf); }
|
||||||
.entity-item.selected { background: var(--surf); border-left-color: var(--accent); }
|
.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 {
|
.entity-item.is-card {
|
||||||
background: var(--surf);
|
background: var(--surf);
|
||||||
margin: 2px 10px;
|
margin: 2px 10px;
|
||||||
border-radius: var(--r2);
|
border-radius: var(--r2);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-left-width: 1px;
|
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:hover { border-color: var(--muted); }
|
||||||
.entity-item.is-card.selected { border-color: var(--accent); background: var(--a-bg); }
|
.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 ROWS ──────────────────────────────────────── */
|
||||||
.card-row {
|
.card-row {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
padding: 9px 12px 9px 14px;
|
|
||||||
margin: 2px 10px;
|
margin: 2px 10px;
|
||||||
background: var(--surf);
|
background: var(--surf);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: var(--r2);
|
border-radius: var(--r2);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
min-height: 40px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: border-color var(--t-fast), background var(--t-fast);
|
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:hover { border-color: var(--muted); }
|
||||||
.card-row.selected { border-color: var(--accent); background: var(--a-bg); }
|
.card-row.selected { border-color: var(--accent); background: var(--a-bg); }
|
||||||
.card-row.pinned { border-left: 2px solid var(--accent); }
|
.card-row.pinned { border-left: 2px solid var(--accent); }
|
||||||
@@ -785,6 +832,21 @@ main.focus-peek .resize-handle { visibility: hidden; }
|
|||||||
overflow: 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 {
|
.peek-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -1443,8 +1505,17 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius:
|
|||||||
|
|
||||||
/* ── RESPONSIVE ─────────────────────────────────────── */
|
/* ── RESPONSIVE ─────────────────────────────────────── */
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
main { grid-template-columns: 1fr; }
|
main,
|
||||||
#tag-rail { display: none; }
|
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 {
|
#detail-pane {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -1457,5 +1528,31 @@ kbd { background: var(--raised); border: 1px solid var(--border); border-radius:
|
|||||||
transition: transform var(--t-base);
|
transition: transform var(--t-base);
|
||||||
z-index: 50;
|
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; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user