@a83/orbiter-admin 0.3.43 → 0.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/style.css +68 -0
- package/public/xfce.js +104 -0
- package/src/routes/search.js +9 -7
package/package.json
CHANGED
package/public/style.css
CHANGED
|
@@ -2196,6 +2196,74 @@ html[data-style="xfce"][data-dock-pos="left"] .xfce-ws-overlay {
|
|
|
2196
2196
|
bottom: auto; left: 110px; top: 50%; transform: translateY(-50%);
|
|
2197
2197
|
}
|
|
2198
2198
|
|
|
2199
|
+
/* ── Bell / Notification Center ─────────────────────────── */
|
|
2200
|
+
.xfce-sb-bell {
|
|
2201
|
+
background: none; border: none; cursor: pointer; padding: 0 2px;
|
|
2202
|
+
color: var(--muted); font-size: 12px; line-height: 1;
|
|
2203
|
+
position: relative; display: flex; align-items: center; gap: 2px;
|
|
2204
|
+
transition: color .15s;
|
|
2205
|
+
}
|
|
2206
|
+
.xfce-sb-bell:hover { color: var(--accent); }
|
|
2207
|
+
.xfce-sb-bell-badge {
|
|
2208
|
+
position: absolute; top: -3px; right: -4px;
|
|
2209
|
+
background: var(--accent); color: var(--bg1);
|
|
2210
|
+
font-size: 8px; font-family: var(--mono); font-weight: 700;
|
|
2211
|
+
border-radius: 6px; padding: 0 3px; min-width: 12px; text-align: center;
|
|
2212
|
+
line-height: 13px; pointer-events: none;
|
|
2213
|
+
}
|
|
2214
|
+
.xfce-notif-panel {
|
|
2215
|
+
position: fixed; top: calc(var(--sb-h) + 6px); right: 12px;
|
|
2216
|
+
width: 300px; max-height: 340px;
|
|
2217
|
+
background: var(--bg1); border: 1px solid var(--line);
|
|
2218
|
+
border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,.45);
|
|
2219
|
+
z-index: 100000; display: none; flex-direction: column; overflow: hidden;
|
|
2220
|
+
font-family: var(--mono); font-size: 11px;
|
|
2221
|
+
}
|
|
2222
|
+
.xfce-notif-panel.open { display: flex; }
|
|
2223
|
+
.xfce-notif-bar {
|
|
2224
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2225
|
+
padding: 8px 12px; border-bottom: 1px solid var(--line);
|
|
2226
|
+
background: var(--bg2); flex-shrink: 0;
|
|
2227
|
+
}
|
|
2228
|
+
.xfce-notif-title { color: var(--accent); font-weight: 700; font-size: 10px; letter-spacing: .06em; }
|
|
2229
|
+
.xfce-notif-clear {
|
|
2230
|
+
background: none; border: 1px solid var(--line); border-radius: 4px;
|
|
2231
|
+
color: var(--muted); font-size: 9px; font-family: var(--mono);
|
|
2232
|
+
padding: 1px 7px; cursor: pointer; transition: color .15s, border-color .15s;
|
|
2233
|
+
}
|
|
2234
|
+
.xfce-notif-clear:hover { color: var(--accent); border-color: var(--accent); }
|
|
2235
|
+
.xfce-notif-list {
|
|
2236
|
+
overflow-y: auto; flex: 1; padding: 4px 0;
|
|
2237
|
+
}
|
|
2238
|
+
.xfce-notif-list::-webkit-scrollbar { width: 4px; }
|
|
2239
|
+
.xfce-notif-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 2px; }
|
|
2240
|
+
.xfce-notif-item {
|
|
2241
|
+
display: flex; align-items: flex-start; justify-content: space-between;
|
|
2242
|
+
gap: 8px; padding: 6px 12px;
|
|
2243
|
+
border-bottom: 1px solid rgba(255,255,255,.04);
|
|
2244
|
+
}
|
|
2245
|
+
.xfce-notif-item:last-child { border-bottom: none; }
|
|
2246
|
+
.xfce-notif-msg { flex: 1; color: var(--text); font-size: 10px; line-height: 1.4; }
|
|
2247
|
+
.xfce-notif-time { color: var(--muted); font-size: 9px; white-space: nowrap; flex-shrink: 0; margin-top: 1px; }
|
|
2248
|
+
.xfce-notif-ok .xfce-notif-msg { color: var(--jade); }
|
|
2249
|
+
.xfce-notif-err .xfce-notif-msg { color: var(--red); }
|
|
2250
|
+
.xfce-notif-empty { padding: 20px 12px; color: var(--muted); font-size: 10px; text-align: center; }
|
|
2251
|
+
|
|
2252
|
+
/* ── HUD Drafts section ──────────────────────────────────── */
|
|
2253
|
+
.xfce-hud-draft-row {
|
|
2254
|
+
display: flex; align-items: baseline; justify-content: space-between;
|
|
2255
|
+
gap: 8px; padding: 4px 0; border-bottom: 1px solid rgba(255,255,255,.04);
|
|
2256
|
+
}
|
|
2257
|
+
.xfce-hud-draft-row:last-child { border-bottom: none; }
|
|
2258
|
+
.xfce-hud-draft-link {
|
|
2259
|
+
color: var(--accent); text-decoration: none; font-size: 10px;
|
|
2260
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1;
|
|
2261
|
+
max-width: 160px;
|
|
2262
|
+
}
|
|
2263
|
+
.xfce-hud-draft-link:hover { text-decoration: underline; }
|
|
2264
|
+
.xfce-hud-draft-meta { color: var(--muted); font-size: 9px; white-space: nowrap; flex-shrink: 0; }
|
|
2265
|
+
.xfce-hud-empty { color: var(--muted); font-size: 10px; padding: 4px 0; }
|
|
2266
|
+
|
|
2199
2267
|
/* ── Toast host (above dock) ─────────────────────────────── */
|
|
2200
2268
|
.xfce-toast-host {
|
|
2201
2269
|
position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%);
|
package/public/xfce.js
CHANGED
|
@@ -64,6 +64,8 @@
|
|
|
64
64
|
'<div class="xfce-sb-center" id="xfce-sb-title"></div>',
|
|
65
65
|
'<div class="xfce-sb-right">',
|
|
66
66
|
'<span id="xfce-sb-g-ind" class="xfce-sb-g-ind" style="display:none" title="g mode: d=dashboard m=media s=settings u=users b=build i=import h=schema a=account">g ›</span>',
|
|
67
|
+
'<button id="xfce-sb-bell" class="xfce-sb-bell" title="Notifications"><span id="xfce-sb-bell-icon">○</span><span id="xfce-sb-bell-badge" class="xfce-sb-bell-badge" style="display:none"></span></button>',
|
|
68
|
+
'<span class="xfce-sb-div">·</span>',
|
|
67
69
|
'<button id="xfce-sb-palette-btn" class="xfce-sb-palette-btn" title="Command palette (⌘K)">⌘</button>',
|
|
68
70
|
'<span class="xfce-sb-div">·</span>',
|
|
69
71
|
'<span id="xfce-sb-build" class="xfce-sb-build" title="Last build"></span>',
|
|
@@ -102,6 +104,12 @@
|
|
|
102
104
|
e.stopPropagation();
|
|
103
105
|
openPalette();
|
|
104
106
|
});
|
|
107
|
+
|
|
108
|
+
// Bell / notification center
|
|
109
|
+
document.getElementById('xfce-sb-bell').addEventListener('click', function (e) {
|
|
110
|
+
e.stopPropagation();
|
|
111
|
+
toggleNotifPanel();
|
|
112
|
+
});
|
|
105
113
|
}
|
|
106
114
|
|
|
107
115
|
// ── HUD Meta Panel ────────────────────────────────────────────────────
|
|
@@ -120,6 +128,8 @@
|
|
|
120
128
|
'<div id="xfce-hud-pod" class="xfce-hud-rows"></div>',
|
|
121
129
|
'<div class="xfce-hud-section-label" style="margin-top:16px">Collections</div>',
|
|
122
130
|
'<div id="xfce-hud-cols" class="xfce-hud-rows"></div>',
|
|
131
|
+
'<div class="xfce-hud-section-label" style="margin-top:16px">Drafts</div>',
|
|
132
|
+
'<div id="xfce-hud-drafts" class="xfce-hud-rows xfce-hud-drafts"></div>',
|
|
123
133
|
'<div class="xfce-hud-section-label" style="margin-top:16px">Navigation</div>',
|
|
124
134
|
'<div class="xfce-hud-nav-links" id="xfce-hud-nav"></div>',
|
|
125
135
|
'</div>',
|
|
@@ -714,6 +724,7 @@
|
|
|
714
724
|
t.classList.remove('show');
|
|
715
725
|
setTimeout(function () { t.remove(); }, 300);
|
|
716
726
|
}, 2500);
|
|
727
|
+
pushNotif(msg, type);
|
|
717
728
|
};
|
|
718
729
|
|
|
719
730
|
function observeSavedFlash() {
|
|
@@ -726,6 +737,77 @@
|
|
|
726
737
|
}).observe(flash, { attributes: true, attributeFilter: ['style'] });
|
|
727
738
|
}
|
|
728
739
|
|
|
740
|
+
// ── Notification Center ───────────────────────────────────────────────
|
|
741
|
+
var _notifications = [], _notifUnread = 0, _notifPanel = null;
|
|
742
|
+
|
|
743
|
+
function pushNotif(msg, type) {
|
|
744
|
+
_notifications.unshift({ msg: msg, type: type || 'info', time: Date.now() });
|
|
745
|
+
if (_notifications.length > 40) _notifications.pop();
|
|
746
|
+
_notifUnread++;
|
|
747
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
748
|
+
var icon = document.getElementById('xfce-sb-bell-icon');
|
|
749
|
+
if (badge) { badge.textContent = _notifUnread > 9 ? '9+' : _notifUnread; badge.style.display = ''; }
|
|
750
|
+
if (icon) { icon.textContent = '●'; }
|
|
751
|
+
if (_notifPanel && _notifPanel.classList.contains('open')) renderNotifList();
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function buildNotifPanel() {
|
|
755
|
+
_notifPanel = el('div', 'xfce-notif-panel');
|
|
756
|
+
_notifPanel.id = 'xfce-notif-panel';
|
|
757
|
+
_notifPanel.innerHTML = '<div class="xfce-notif-bar">'
|
|
758
|
+
+ '<span class="xfce-notif-title">Notifications</span>'
|
|
759
|
+
+ '<button id="xfce-notif-clear" class="xfce-notif-clear">Clear all</button>'
|
|
760
|
+
+ '</div>'
|
|
761
|
+
+ '<div id="xfce-notif-list" class="xfce-notif-list"></div>';
|
|
762
|
+
document.body.appendChild(_notifPanel);
|
|
763
|
+
document.getElementById('xfce-notif-clear').addEventListener('click', function () {
|
|
764
|
+
_notifications = []; _notifUnread = 0;
|
|
765
|
+
renderNotifList();
|
|
766
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
767
|
+
var icon = document.getElementById('xfce-sb-bell-icon');
|
|
768
|
+
if (badge) badge.style.display = 'none';
|
|
769
|
+
if (icon) icon.textContent = '○';
|
|
770
|
+
});
|
|
771
|
+
document.addEventListener('click', function (e) {
|
|
772
|
+
if (_notifPanel && _notifPanel.classList.contains('open')
|
|
773
|
+
&& !_notifPanel.contains(e.target)
|
|
774
|
+
&& e.target.id !== 'xfce-sb-bell') closeNotifPanel();
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function renderNotifList() {
|
|
779
|
+
var list = document.getElementById('xfce-notif-list');
|
|
780
|
+
if (!list) return;
|
|
781
|
+
if (!_notifications.length) {
|
|
782
|
+
list.innerHTML = '<div class="xfce-notif-empty">No notifications yet</div>';
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
list.innerHTML = _notifications.map(function (n) {
|
|
786
|
+
var ago = Math.floor((Date.now() - n.time) / 1000);
|
|
787
|
+
var t = ago < 60 ? ago + 's' : ago < 3600 ? Math.floor(ago/60) + 'm' : Math.floor(ago/3600) + 'h';
|
|
788
|
+
var cls = n.type === 'success' ? 'ok' : n.type === 'error' ? 'err' : 'info';
|
|
789
|
+
return '<div class="xfce-notif-item xfce-notif-' + cls + '">'
|
|
790
|
+
+ '<span class="xfce-notif-msg">' + escHtml(n.msg) + '</span>'
|
|
791
|
+
+ '<span class="xfce-notif-time">' + t + '</span>'
|
|
792
|
+
+ '</div>';
|
|
793
|
+
}).join('');
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function toggleNotifPanel() {
|
|
797
|
+
if (!_notifPanel) buildNotifPanel();
|
|
798
|
+
var isOpen = _notifPanel.classList.toggle('open');
|
|
799
|
+
if (isOpen) {
|
|
800
|
+
_notifUnread = 0;
|
|
801
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
802
|
+
if (badge) badge.style.display = 'none';
|
|
803
|
+
renderNotifList();
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function closeNotifPanel() {
|
|
808
|
+
if (_notifPanel) _notifPanel.classList.remove('open');
|
|
809
|
+
}
|
|
810
|
+
|
|
729
811
|
// ── Workspace overlay (Notes + To-do) ────────────────────────────────
|
|
730
812
|
var wsOverlay, wsActivePane = 'notes', wsNotesTimer, wsTodosData = [];
|
|
731
813
|
|
|
@@ -1213,6 +1295,28 @@
|
|
|
1213
1295
|
})
|
|
1214
1296
|
.catch(function () {});
|
|
1215
1297
|
|
|
1298
|
+
// HUD Drafts
|
|
1299
|
+
fetch('/api/search/recent?status=draft&limit=10', { credentials: 'include' })
|
|
1300
|
+
.then(function (r) { return r.ok ? r.json() : []; })
|
|
1301
|
+
.then(function (drafts) {
|
|
1302
|
+
var hudDrafts = document.getElementById('xfce-hud-drafts');
|
|
1303
|
+
if (!hudDrafts) return;
|
|
1304
|
+
if (!drafts.length) {
|
|
1305
|
+
hudDrafts.innerHTML = '<div class="xfce-hud-empty">No drafts</div>';
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
hudDrafts.innerHTML = drafts.map(function (d) {
|
|
1309
|
+
var href = '/collections/' + encodeURIComponent(d.collection) + '/entries/' + encodeURIComponent(d.slug);
|
|
1310
|
+
var ago = Math.floor((Date.now() - new Date(d.updated_at).getTime()) / 60000);
|
|
1311
|
+
var t = ago < 60 ? ago + 'm' : Math.floor(ago / 60) + 'h';
|
|
1312
|
+
return '<div class="xfce-hud-draft-row">'
|
|
1313
|
+
+ '<a class="xfce-hud-draft-link" href="' + href + '">' + escHtml(d.title || d.slug) + '</a>'
|
|
1314
|
+
+ '<span class="xfce-hud-draft-meta">' + escHtml(d.label) + ' · ' + t + '</span>'
|
|
1315
|
+
+ '</div>';
|
|
1316
|
+
}).join('');
|
|
1317
|
+
})
|
|
1318
|
+
.catch(function () {});
|
|
1319
|
+
|
|
1216
1320
|
// Site meta
|
|
1217
1321
|
fetch('/api/meta/site~name', { credentials: 'include' })
|
|
1218
1322
|
.then(function (r) { return r.ok ? r.json() : null; })
|
package/src/routes/search.js
CHANGED
|
@@ -5,17 +5,19 @@ export const searchRoutes = new Hono();
|
|
|
5
5
|
|
|
6
6
|
// GET /api/search/recent — last N entries across all collections
|
|
7
7
|
searchRoutes.get('/recent', (c) => {
|
|
8
|
-
const limit
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
const limit = Math.min(parseInt(c.req.query('limit') ?? '10', 10), 50);
|
|
9
|
+
const status = c.req.query('status') ?? null;
|
|
10
|
+
const db = openPod(c.get('podPath'));
|
|
11
|
+
const cols = db.getCollections();
|
|
11
12
|
const colMap = Object.fromEntries(cols.map(c => [c.id, c.label]));
|
|
12
13
|
|
|
13
|
-
const rows =
|
|
14
|
-
.prepare(`SELECT
|
|
15
|
-
.all(limit);
|
|
14
|
+
const rows = status
|
|
15
|
+
? db.db.prepare(`SELECT * FROM _entries WHERE status = ? ORDER BY updated_at DESC LIMIT ?`).all(status, limit)
|
|
16
|
+
: db.db.prepare(`SELECT * FROM _entries ORDER BY updated_at DESC LIMIT ?`).all(limit);
|
|
16
17
|
|
|
17
18
|
const results = rows.map(r => {
|
|
18
|
-
|
|
19
|
+
let data = {};
|
|
20
|
+
try { data = JSON.parse(r.data); } catch {}
|
|
19
21
|
return {
|
|
20
22
|
collection: r.collection_id,
|
|
21
23
|
label: colMap[r.collection_id] ?? r.collection_id,
|