@a83/orbiter-admin 0.3.42 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a83/orbiter-admin",
3
- "version": "0.3.42",
3
+ "version": "0.3.44",
4
4
  "description": "Standalone admin server for Orbiter CMS",
5
5
  "type": "module",
6
6
  "main": "./src/server.js",
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>',
@@ -158,6 +168,13 @@
158
168
  a.innerHTML = '<span class="xfce-tools-icon">' + t.icon + '</span><span>' + t.label + '</span>';
159
169
  toolsPopup.appendChild(a);
160
170
  });
171
+ var settingsSep = document.createElement('div');
172
+ settingsSep.className = 'xfce-tools-sep';
173
+ toolsPopup.appendChild(settingsSep);
174
+ var settingsLink = el('a', 'xfce-tools-item' + (page === 'settings' ? ' active' : ''));
175
+ settingsLink.href = '/settings.html';
176
+ settingsLink.innerHTML = '<span class="xfce-tools-icon">⚙</span><span>Settings</span>';
177
+ toolsPopup.appendChild(settingsLink);
161
178
  var sep = document.createElement('div');
162
179
  sep.className = 'xfce-tools-sep';
163
180
  toolsPopup.appendChild(sep);
@@ -707,6 +724,7 @@
707
724
  t.classList.remove('show');
708
725
  setTimeout(function () { t.remove(); }, 300);
709
726
  }, 2500);
727
+ pushNotif(msg, type);
710
728
  };
711
729
 
712
730
  function observeSavedFlash() {
@@ -719,6 +737,77 @@
719
737
  }).observe(flash, { attributes: true, attributeFilter: ['style'] });
720
738
  }
721
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
+
722
811
  // ── Workspace overlay (Notes + To-do) ────────────────────────────────
723
812
  var wsOverlay, wsActivePane = 'notes', wsNotesTimer, wsTodosData = [];
724
813
 
@@ -1206,6 +1295,28 @@
1206
1295
  })
1207
1296
  .catch(function () {});
1208
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
+
1209
1320
  // Site meta
1210
1321
  fetch('/api/meta/site~name', { credentials: 'include' })
1211
1322
  .then(function (r) { return r.ok ? r.json() : null; })
@@ -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 = Math.min(parseInt(c.req.query('limit') ?? '10', 10), 50);
9
- const db = openPod(c.get('podPath'));
10
- const cols = db.getCollections();
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 = db.db
14
- .prepare(`SELECT e.*, e.collection_id as collection FROM _entries e ORDER BY e.updated_at DESC LIMIT ?`)
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
- const data = JSON.parse(r.data);
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,