feat(ui): absorb button in peek, preserve newlines, demote promoted items
Stream peek now shows absorb button for unpromoted entries. Promoted items in stream show demote instead of delete. d double-tap demotes any card_type entity regardless of view. Parsers preserve newlines from Shift+Enter. Absorb popup truncates to first non-empty line.
This commit is contained in:
+35
-25
@@ -229,36 +229,40 @@
|
||||
|
||||
// Steps 6-8: Extract flags, tags, time, card suffix
|
||||
function extractModifiers(text, handleFlags) {
|
||||
const tokens = text.split(/\s+/).filter(Boolean);
|
||||
const parts = [];
|
||||
let localTime = timeAnchor, localPin = pin, localCard = cardSuffix;
|
||||
const localTags = [...tags];
|
||||
const localSeen = { ...seenTags };
|
||||
|
||||
for (const tok of tokens) {
|
||||
if (handleFlags && tok.toLowerCase() === '!pin') {
|
||||
localPin = true;
|
||||
} else if (tok.startsWith('##') && tok.length > 2) {
|
||||
parts.push('#' + tok.slice(2));
|
||||
} else if (tok.startsWith('@') && tok.length > 1) {
|
||||
const ts = tok.slice(1);
|
||||
if (validateTime(ts) && localTime === null) {
|
||||
localTime = ts;
|
||||
const outLines = [];
|
||||
for (const line of text.split('\n')) {
|
||||
const tokens = line.split(/[ \t]+/).filter(Boolean);
|
||||
const lineParts = [];
|
||||
for (const tok of tokens) {
|
||||
if (handleFlags && tok.toLowerCase() === '!pin') {
|
||||
localPin = true;
|
||||
} else if (tok.startsWith('##') && tok.length > 2) {
|
||||
lineParts.push('#' + tok.slice(2));
|
||||
} else if (tok.startsWith('@') && tok.length > 1) {
|
||||
const ts = tok.slice(1);
|
||||
if (validateTime(ts) && localTime === null) {
|
||||
localTime = ts;
|
||||
} else {
|
||||
lineParts.push(tok);
|
||||
}
|
||||
} else if (tok.startsWith('#') && tok.length > 1) {
|
||||
const tag = tok.slice(1).toLowerCase();
|
||||
if (!localSeen[tag]) { localTags.push(tag); localSeen[tag] = true; }
|
||||
} else if (tok.startsWith('^') && tok.length > 1) {
|
||||
const suffix = tok.slice(1);
|
||||
if (VALID_CARDS[suffix] && localCard === null) localCard = VALID_CARDS[suffix];
|
||||
else lineParts.push(tok);
|
||||
} else {
|
||||
parts.push(tok);
|
||||
lineParts.push(tok);
|
||||
}
|
||||
} else if (tok.startsWith('#') && tok.length > 1) {
|
||||
const tag = tok.slice(1).toLowerCase();
|
||||
if (!localSeen[tag]) { localTags.push(tag); localSeen[tag] = true; }
|
||||
} else if (tok.startsWith('^') && tok.length > 1) {
|
||||
const suffix = tok.slice(1);
|
||||
if (VALID_CARDS[suffix] && localCard === null) localCard = VALID_CARDS[suffix];
|
||||
else parts.push(tok);
|
||||
} else {
|
||||
parts.push(tok);
|
||||
}
|
||||
outLines.push(lineParts.join(' '));
|
||||
}
|
||||
return { body: parts.join(' '), timeAnchor: localTime, tags: localTags, seen: localSeen, cardSuffix: localCard, pin: localPin };
|
||||
return { body: outLines.join('\n'), timeAnchor: localTime, tags: localTags, seen: localSeen, cardSuffix: localCard, pin: localPin };
|
||||
}
|
||||
|
||||
let title = null, description = null;
|
||||
@@ -737,8 +741,13 @@
|
||||
let actions = '';
|
||||
if (!e.card_type) {
|
||||
actions += `<button class="action-btn primary" onclick="nibApp.showPromote('${e.id}')">promote →</button>`;
|
||||
actions += `<button class="action-btn" onclick="nibApp.showAbsorb('${e.id}')">absorb <kbd>a</kbd></button>`;
|
||||
}
|
||||
if (e.card_type) {
|
||||
actions += `<button class="action-btn danger" onclick="nibApp.demoteEntity('${e.id}')">demote</button>`;
|
||||
} else {
|
||||
actions += `<button class="action-btn danger" onclick="nibApp.deleteEntity('${e.id}')">delete</button>`;
|
||||
}
|
||||
actions += `<button class="action-btn danger" onclick="nibApp.deleteEntity('${e.id}')">delete</button>`;
|
||||
|
||||
return `<div class="peek-scroll">
|
||||
<div class="peek-brow">
|
||||
@@ -1271,7 +1280,8 @@
|
||||
list.innerHTML = sources.map(e => {
|
||||
const g = displayGlyph(e);
|
||||
const gc = glyphClass(e);
|
||||
const label = e.title ? escHtml(e.title) : escHtml(e.body);
|
||||
const rawLabel = e.title || (e.body || '').split('\n').find(l => l.trim()) || '';
|
||||
const label = escHtml(rawLabel.length > 80 ? rawLabel.slice(0, 80) + '…' : rawLabel);
|
||||
return `<div class="absorb-source-item" data-id="${e.id}">
|
||||
<span class="entity-glyph ${gc}">${g}</span>
|
||||
<span class="entity-body">${label}</span>
|
||||
@@ -1460,7 +1470,7 @@
|
||||
const now = Date.now();
|
||||
if (now - lastDTime < 400) {
|
||||
if (sel) {
|
||||
if (sel.card_type && state.view === 'cards') nibApp.demoteEntity(sel.id);
|
||||
if (sel.card_type) nibApp.demoteEntity(sel.id);
|
||||
else nibApp.deleteEntity(sel.id);
|
||||
}
|
||||
lastDTime = 0;
|
||||
|
||||
Reference in New Issue
Block a user