@a83/orbiter-admin 0.3.45 → 0.3.47

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.45",
3
+ "version": "0.3.47",
4
4
  "description": "Standalone admin server for Orbiter CMS",
5
5
  "type": "module",
6
6
  "main": "./src/server.js",
package/public/style.css CHANGED
@@ -2023,6 +2023,12 @@ a.xfce-sb-logo:hover { opacity: .8; }
2023
2023
  font-family: var(--mono); transition: background .15s;
2024
2024
  }
2025
2025
  .xfce-sb-palette-btn:hover { background: color-mix(in srgb, var(--accent) 14%, transparent); }
2026
+ .xfce-sb-cheat {
2027
+ background: none; border: none; cursor: pointer; padding: 0 3px;
2028
+ font-size: 13px; color: var(--muted); font-family: var(--mono); line-height: 1;
2029
+ transition: color .15s;
2030
+ }
2031
+ .xfce-sb-cheat:hover { color: var(--accent); }
2026
2032
 
2027
2033
  /* Draft badge on dock items */
2028
2034
  .xfce-dock-badge {
@@ -2335,6 +2341,32 @@ html[data-zen="1"] .main {
2335
2341
  .xfce-notif-err .xfce-notif-msg { color: var(--red); }
2336
2342
  .xfce-notif-empty { padding: 20px 12px; color: var(--muted); font-size: 10px; text-align: center; }
2337
2343
 
2344
+ /* ── Build indicator — running state ────────────────────── */
2345
+ .xfce-sb-build--running {
2346
+ color: var(--accent) !important;
2347
+ animation: xfce-blink .9s step-end infinite;
2348
+ }
2349
+
2350
+ /* ── Status bar breadcrumb ───────────────────────────────── */
2351
+ .xfce-sb-crumb-link {
2352
+ color: var(--accent); text-decoration: none; font-family: var(--mono); font-size: 11px;
2353
+ }
2354
+ .xfce-sb-crumb-link:hover { text-decoration: underline; }
2355
+ .xfce-sb-crumb-sep { color: var(--muted); font-size: 10px; margin: 0 2px; }
2356
+ .xfce-sb-crumb-page { color: var(--text); font-family: var(--mono); font-size: 11px; }
2357
+
2358
+ /* ── HUD Activity feed ───────────────────────────────────── */
2359
+ .xfce-hud-act-row {
2360
+ display: flex; align-items: baseline; justify-content: space-between;
2361
+ gap: 8px; padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,.04);
2362
+ font-size: 10px; font-family: var(--mono);
2363
+ }
2364
+ .xfce-hud-act-row:last-child { border-bottom: none; }
2365
+ .xfce-hud-act-msg { flex: 1; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
2366
+ .xfce-hud-act-time { color: var(--line); font-size: 9px; flex-shrink: 0; }
2367
+ .xfce-hud-act-ok .xfce-hud-act-msg { color: var(--jade, #5dc97e); }
2368
+ .xfce-hud-act-err .xfce-hud-act-msg { color: var(--red, #e06c6c); }
2369
+
2338
2370
  /* ── HUD Drafts section ──────────────────────────────────── */
2339
2371
  .xfce-hud-draft-row {
2340
2372
  display: flex; align-items: baseline; justify-content: space-between;
package/public/xfce.js CHANGED
@@ -64,9 +64,11 @@
64
64
  '</div>',
65
65
  '<div class="xfce-sb-center" id="xfce-sb-title"></div>',
66
66
  '<div class="xfce-sb-right">',
67
- '<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
+ '<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 c=schema h=HUD a=account">g ›</span>',
68
68
  '<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>',
69
69
  '<span class="xfce-sb-div">·</span>',
70
+ '<button id="xfce-sb-cheat" class="xfce-sb-cheat" title="Shortcuts (?)">?</button>',
71
+ '<span class="xfce-sb-div">·</span>',
70
72
  '<button id="xfce-sb-palette-btn" class="xfce-sb-palette-btn" title="Command palette (⌘K)">⌘</button>',
71
73
  '<span class="xfce-sb-div">·</span>',
72
74
  '<span id="xfce-sb-build" class="xfce-sb-build" title="Last build"></span>',
@@ -80,10 +82,18 @@
80
82
  ].join('');
81
83
  document.body.insertBefore(sb, document.body.firstChild);
82
84
 
83
- // Page title from document.title
84
- var title = document.title.replace(/\s*—\s*Orbiter.*$/, '').trim();
85
+ // Breadcrumb / page title
85
86
  var titleEl = document.getElementById('xfce-sb-title');
86
- if (titleEl && title) titleEl.textContent = title;
87
+ if (titleEl) {
88
+ if (activeCol && (page === 'entries' || page === 'editor')) {
89
+ // will be filled in by loadInfo once we know the collection label
90
+ titleEl.dataset.col = activeCol;
91
+ titleEl.dataset.page = page;
92
+ } else {
93
+ var title = document.title.replace(/\s*—\s*Orbiter.*$/, '').trim();
94
+ if (title) titleEl.textContent = title;
95
+ }
96
+ }
87
97
 
88
98
  // Clock
89
99
  function tick() {
@@ -111,6 +121,12 @@
111
121
  e.stopPropagation();
112
122
  toggleNotifPanel();
113
123
  });
124
+
125
+ // Cheatsheet
126
+ document.getElementById('xfce-sb-cheat').addEventListener('click', function (e) {
127
+ e.stopPropagation();
128
+ toggleCheatsheet();
129
+ });
114
130
  }
115
131
 
116
132
  // ── HUD Meta Panel ────────────────────────────────────────────────────
@@ -131,6 +147,8 @@
131
147
  '<div id="xfce-hud-cols" class="xfce-hud-rows"></div>',
132
148
  '<div class="xfce-hud-section-label" style="margin-top:16px">Drafts</div>',
133
149
  '<div id="xfce-hud-drafts" class="xfce-hud-rows xfce-hud-drafts"></div>',
150
+ '<div class="xfce-hud-section-label" style="margin-top:16px">Activity</div>',
151
+ '<div id="xfce-hud-activity" class="xfce-hud-rows xfce-hud-activity"></div>',
134
152
  '<div class="xfce-hud-section-label" style="margin-top:16px">Navigation</div>',
135
153
  '<div class="xfce-hud-nav-links" id="xfce-hud-nav"></div>',
136
154
  '</div>',
@@ -155,6 +173,7 @@
155
173
  function toggleHUD() {
156
174
  if (!metaPanel) return;
157
175
  metaPanel.classList.toggle('open');
176
+ if (metaPanel.classList.contains('open')) refreshHUDActivity();
158
177
  }
159
178
 
160
179
  // ── Tools popup ───────────────────────────────────────────────────────
@@ -561,6 +580,8 @@
561
580
  case 'search': palSearch(args.join(' ')); break;
562
581
  case 'build': palBuild(); break;
563
582
  case 'export': palExport(args); break;
583
+ case 'random': palRandom(); break;
584
+ case '=': palMath(args.join(' ')); break;
564
585
  default: palPrint('unknown: <b>' + escHtml(cmd) + '</b> &mdash; try <b>&gt; help</b>', 'err');
565
586
  }
566
587
  }
@@ -574,6 +595,8 @@
574
595
  '<code>info</code> &mdash; pod &amp; version info',
575
596
  '<code>build</code> &mdash; trigger deploy',
576
597
  '<code>export &lt;col&gt; [--md] [--drafts]</code> &mdash; download',
598
+ '<code>random</code> &mdash; jump to a random entry',
599
+ '<code>= &lt;expr&gt;</code> &mdash; evaluate math expression',
577
600
  ].join('<br>'), 'dim');
578
601
  }
579
602
 
@@ -690,12 +713,50 @@
690
713
  .then(function (r) { return r.json(); })
691
714
  .then(function (d) {
692
715
  paletteResults.innerHTML = '';
693
- if (d.ok || d.message) { palPrint('✓ ' + (d.message || 'build triggered'), 'ok'); setTimeout(closePalette, 900); }
694
- else palPrint('build error: ' + escHtml(d.error || JSON.stringify(d)), 'err');
716
+ if (d.ok || d.message) {
717
+ palPrint(' ' + (d.message || 'build triggered'), 'ok');
718
+ startBuildPoll();
719
+ setTimeout(closePalette, 900);
720
+ } else palPrint('build error: ' + escHtml(d.error || JSON.stringify(d)), 'err');
695
721
  })
696
722
  .catch(function () { paletteResults.innerHTML = ''; palPrint('build request failed', 'err'); });
697
723
  }
698
724
 
725
+ function palMath(expr) {
726
+ if (!expr) { palPrint('usage: <code>= 2 * 450 + 12</code>', 'dim'); return; }
727
+ try {
728
+ // only allow safe math chars
729
+ if (!/^[\d\s\+\-\*\/\.\(\)%,^]+$/.test(expr)) throw new Error('unsafe');
730
+ // replace ^ with ** for exponentiation
731
+ var safe = expr.replace(/\^/g, '**');
732
+ // eslint-disable-next-line no-new-func
733
+ var result = Function('"use strict"; return (' + safe + ')')();
734
+ if (typeof result !== 'number' || !isFinite(result)) throw new Error('not a number');
735
+ palPrint('<span style="font-size:15px;color:var(--accent)">' + result + '</span>'
736
+ + ' <span style="color:var(--muted);font-size:10px">= ' + escHtml(expr) + '</span>', '');
737
+ } catch (e) {
738
+ palPrint('invalid expression', 'err');
739
+ }
740
+ }
741
+
742
+ function palRandom() {
743
+ palPrint('picking a random entry…', 'muted');
744
+ fetch('/api/search/recent?limit=50', { credentials: 'include' })
745
+ .then(function (r) { return r.ok ? r.json() : []; })
746
+ .then(function (rows) {
747
+ var published = rows.filter(function (r) { return r.status === 'published'; });
748
+ if (!published.length) { paletteResults.innerHTML = ''; palPrint('no published entries found', 'muted'); return; }
749
+ var pick = published[Math.floor(Math.random() * published.length)];
750
+ var href = '/collections/' + encodeURIComponent(pick.collection) + '/entries/' + encodeURIComponent(pick.slug);
751
+ paletteResults.innerHTML = '';
752
+ palPrint('✦ <a href="' + href + '" style="color:var(--accent)">' + escHtml(pick.title || pick.slug) + '</a>'
753
+ + ' <span style="color:var(--muted)">in ' + escHtml(pick.label) + '</span>', '');
754
+ setTimeout(function () { location.href = href; closePalette(); }, 1200);
755
+ })
756
+ .catch(function () { paletteResults.innerHTML = ''; palPrint('error fetching entries', 'err'); });
757
+ }
758
+
759
+
699
760
  function palExport(args) {
700
761
  if (!args.length) { palPrint('usage: export &lt;collection&gt; [--md] [--drafts]', 'err'); return; }
701
762
  var colId = args[0];
@@ -789,6 +850,7 @@
789
850
  if (badge) { badge.textContent = _notifUnread > 9 ? '9+' : _notifUnread; badge.style.display = ''; }
790
851
  if (icon) { icon.textContent = '●'; }
791
852
  if (_notifPanel && _notifPanel.classList.contains('open')) renderNotifList();
853
+ refreshHUDActivity();
792
854
  }
793
855
 
794
856
  function buildNotifPanel() {
@@ -833,6 +895,24 @@
833
895
  }).join('');
834
896
  }
835
897
 
898
+ function refreshHUDActivity() {
899
+ var el2 = document.getElementById('xfce-hud-activity');
900
+ if (!el2) return;
901
+ if (!_notifications.length) {
902
+ el2.innerHTML = '<div class="xfce-hud-empty">No activity yet</div>';
903
+ return;
904
+ }
905
+ el2.innerHTML = _notifications.slice(0, 8).map(function (n) {
906
+ var ago = Math.floor((Date.now() - n.time) / 1000);
907
+ var t = ago < 60 ? ago + 's' : ago < 3600 ? Math.floor(ago / 60) + 'm' : Math.floor(ago / 3600) + 'h';
908
+ var cls = n.type === 'success' ? 'xfce-hud-act-ok' : n.type === 'error' ? 'xfce-hud-act-err' : '';
909
+ return '<div class="xfce-hud-act-row' + (cls ? ' ' + cls : '') + '">'
910
+ + '<span class="xfce-hud-act-msg">' + escHtml(n.msg) + '</span>'
911
+ + '<span class="xfce-hud-act-time">' + t + '</span>'
912
+ + '</div>';
913
+ }).join('');
914
+ }
915
+
836
916
  function toggleNotifPanel() {
837
917
  if (!_notifPanel) buildNotifPanel();
838
918
  var isOpen = _notifPanel.classList.toggle('open');
@@ -1335,6 +1415,17 @@
1335
1415
  return hudRow(col.label, col.total + ' published' + draftTxt);
1336
1416
  }).join('');
1337
1417
  }
1418
+
1419
+ // Breadcrumb in status bar center
1420
+ var titleEl = document.getElementById('xfce-sb-title');
1421
+ if (titleEl && titleEl.dataset.col) {
1422
+ var colMeta = (info.collections || []).find(function (c) { return c.id === titleEl.dataset.col; });
1423
+ var colLabel = colMeta ? colMeta.label : titleEl.dataset.col;
1424
+ var crumbPage = titleEl.dataset.page === 'editor' ? 'Editor' : 'Entries';
1425
+ titleEl.innerHTML = '<a href="/entries.html?col=' + encodeURIComponent(titleEl.dataset.col) + '" class="xfce-sb-crumb-link">' + escHtml(colLabel) + '</a>'
1426
+ + '<span class="xfce-sb-crumb-sep"> › </span>'
1427
+ + '<span class="xfce-sb-crumb-page">' + crumbPage + '</span>';
1428
+ }
1338
1429
  })
1339
1430
  .catch(function () {});
1340
1431
 
@@ -1425,13 +1516,13 @@
1425
1516
  cheatRow('g &nbsp;+&nbsp; s', 'Settings'),
1426
1517
  cheatRow('g &nbsp;+&nbsp; b', 'Build'),
1427
1518
  cheatRow('g &nbsp;+&nbsp; i', 'Import'),
1428
- cheatRow('g &nbsp;+&nbsp; h', 'Schema'),
1519
+ cheatRow('g &nbsp;+&nbsp; c', 'Schema'),
1520
+ cheatRow('g &nbsp;+&nbsp; h', 'Toggle HUD'),
1429
1521
  cheatRow('g &nbsp;+&nbsp; a', 'Account'),
1430
1522
  cheatRow('1 – 9', 'Jump to nth dock item'),
1431
1523
  '</div>',
1432
1524
  '<div class="xfce-cheat-col">',
1433
1525
  '<div class="xfce-cheat-section">Panels</div>',
1434
- cheatRow('⌘⇧D', 'Toggle HUD'),
1435
1526
  cheatRow('⌘⇧F', 'Focus / Zen mode'),
1436
1527
  cheatRow('⌘⇧L', 'Switch to Glass mode'),
1437
1528
  cheatRow('?', 'This cheatsheet'),
@@ -1442,6 +1533,8 @@
1442
1533
  cheatRow('&gt; search &lt;q&gt;', 'Search entries'),
1443
1534
  cheatRow('&gt; build', 'Trigger site build'),
1444
1535
  cheatRow('&gt; export &lt;col&gt;', 'Export collection'),
1536
+ cheatRow('&gt; random', 'Jump to random entry'),
1537
+ cheatRow('&gt; = &lt;expr&gt;', 'Evaluate math'),
1445
1538
  cheatRow('&gt; info', 'Show pod info'),
1446
1539
  cheatRow('&gt; help', 'Show command help'),
1447
1540
  '</div>',
@@ -1475,7 +1568,7 @@
1475
1568
  var _gPending = false, _gTimer = null;
1476
1569
  var G_MAP = { d: '/dashboard.html', m: '/media.html', s: '/settings.html',
1477
1570
  u: '/users.html', b: '/build.html', i: '/import.html',
1478
- h: '/schema.html', a: '/account.html' };
1571
+ c: '/schema.html', a: '/account.html' };
1479
1572
 
1480
1573
  function setGMode(on) {
1481
1574
  _gPending = on;
@@ -1521,16 +1614,11 @@
1521
1614
  }
1522
1615
  if (_gPending && !isEditing(e.target)) {
1523
1616
  clearTimeout(_gTimer); setGMode(false);
1524
- if (G_MAP[e.key]) { e.preventDefault(); location.href = G_MAP[e.key]; }
1617
+ if (e.key === 'h') { e.preventDefault(); toggleHUD(); }
1618
+ else if (G_MAP[e.key]) { e.preventDefault(); location.href = G_MAP[e.key]; }
1525
1619
  return;
1526
1620
  }
1527
1621
 
1528
- // ⌘⇧D — toggle HUD
1529
- if (mod && e.shiftKey && (e.key === 'd' || e.key === 'D')) {
1530
- e.preventDefault();
1531
- toggleHUD();
1532
- return;
1533
- }
1534
1622
 
1535
1623
  // ⌘⇧F — zen / focus mode
1536
1624
  if (mod && e.shiftKey && (e.key === 'f' || e.key === 'F')) {
@@ -1573,19 +1661,40 @@
1573
1661
  }
1574
1662
 
1575
1663
  // ── Build status in status bar ────────────────────────────────────────
1664
+ var _buildPolling = false;
1665
+
1576
1666
  function loadBuildStatus() {
1577
1667
  fetch('/api/build/status', { credentials: 'include' })
1578
1668
  .then(function (r) { return r.ok ? r.json() : null; })
1579
1669
  .then(function (d) {
1580
- if (!d || !d.lastTriggered) return;
1670
+ if (!d) return;
1581
1671
  var el2 = document.getElementById('xfce-sb-build');
1582
1672
  var sep = document.getElementById('xfce-sb-build-sep');
1583
1673
  if (!el2) return;
1584
- var dt = new Date(d.lastTriggered.replace(' ', 'T'));
1585
- var now = new Date();
1586
- var diffM = Math.floor((now - dt) / 60000);
1674
+
1675
+ var running = d.running === true;
1676
+ el2.classList.toggle('xfce-sb-build--running', running);
1677
+
1678
+ if (running) {
1679
+ el2.textContent = '◉ building…';
1680
+ el2.title = 'Build in progress';
1681
+ if (sep) sep.style.display = '';
1682
+ if (!_buildPolling) {
1683
+ _buildPolling = true;
1684
+ setTimeout(function poll() {
1685
+ loadBuildStatus();
1686
+ if (_buildPolling) setTimeout(poll, 4000);
1687
+ }, 4000);
1688
+ }
1689
+ return;
1690
+ }
1691
+
1692
+ _buildPolling = false;
1693
+ if (!d.lastTriggered) return;
1694
+ var dt = new Date(d.lastTriggered.replace(' ', 'T'));
1695
+ var diffM = Math.floor((Date.now() - dt) / 60000);
1587
1696
  var label = diffM < 1 ? 'built now'
1588
- : diffM < 60 ? 'built ' + diffM + 'm ago'
1697
+ : diffM < 60 ? 'built ' + diffM + 'm ago'
1589
1698
  : diffM < 1440 ? 'built ' + Math.floor(diffM / 60) + 'h ago'
1590
1699
  : 'built ' + dt.toLocaleDateString([], { month: 'short', day: 'numeric' });
1591
1700
  el2.textContent = '◉ ' + label;
@@ -1594,6 +1703,18 @@
1594
1703
  });
1595
1704
  }
1596
1705
 
1706
+ function startBuildPoll() {
1707
+ _buildPolling = true;
1708
+ var el2 = document.getElementById('xfce-sb-build');
1709
+ var sep = document.getElementById('xfce-sb-build-sep');
1710
+ if (el2) { el2.textContent = '◉ building…'; el2.classList.add('xfce-sb-build--running'); }
1711
+ if (sep) sep.style.display = '';
1712
+ setTimeout(function poll() {
1713
+ loadBuildStatus();
1714
+ if (_buildPolling) setTimeout(poll, 4000);
1715
+ }, 3000);
1716
+ }
1717
+
1597
1718
 
1598
1719
  // ── Init ──────────────────────────────────────────────────────────────
1599
1720
  function init() {