@a83/orbiter-admin 0.3.45 → 0.3.46

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.46",
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
@@ -67,6 +67,8 @@
67
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>',
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
 
@@ -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>',
@@ -1573,19 +1666,40 @@
1573
1666
  }
1574
1667
 
1575
1668
  // ── Build status in status bar ────────────────────────────────────────
1669
+ var _buildPolling = false;
1670
+
1576
1671
  function loadBuildStatus() {
1577
1672
  fetch('/api/build/status', { credentials: 'include' })
1578
1673
  .then(function (r) { return r.ok ? r.json() : null; })
1579
1674
  .then(function (d) {
1580
- if (!d || !d.lastTriggered) return;
1675
+ if (!d) return;
1581
1676
  var el2 = document.getElementById('xfce-sb-build');
1582
1677
  var sep = document.getElementById('xfce-sb-build-sep');
1583
1678
  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);
1679
+
1680
+ var running = d.running === true;
1681
+ el2.classList.toggle('xfce-sb-build--running', running);
1682
+
1683
+ if (running) {
1684
+ el2.textContent = '◉ building…';
1685
+ el2.title = 'Build in progress';
1686
+ if (sep) sep.style.display = '';
1687
+ if (!_buildPolling) {
1688
+ _buildPolling = true;
1689
+ setTimeout(function poll() {
1690
+ loadBuildStatus();
1691
+ if (_buildPolling) setTimeout(poll, 4000);
1692
+ }, 4000);
1693
+ }
1694
+ return;
1695
+ }
1696
+
1697
+ _buildPolling = false;
1698
+ if (!d.lastTriggered) return;
1699
+ var dt = new Date(d.lastTriggered.replace(' ', 'T'));
1700
+ var diffM = Math.floor((Date.now() - dt) / 60000);
1587
1701
  var label = diffM < 1 ? 'built now'
1588
- : diffM < 60 ? 'built ' + diffM + 'm ago'
1702
+ : diffM < 60 ? 'built ' + diffM + 'm ago'
1589
1703
  : diffM < 1440 ? 'built ' + Math.floor(diffM / 60) + 'h ago'
1590
1704
  : 'built ' + dt.toLocaleDateString([], { month: 'short', day: 'numeric' });
1591
1705
  el2.textContent = '◉ ' + label;
@@ -1594,6 +1708,18 @@
1594
1708
  });
1595
1709
  }
1596
1710
 
1711
+ function startBuildPoll() {
1712
+ _buildPolling = true;
1713
+ var el2 = document.getElementById('xfce-sb-build');
1714
+ var sep = document.getElementById('xfce-sb-build-sep');
1715
+ if (el2) { el2.textContent = '◉ building…'; el2.classList.add('xfce-sb-build--running'); }
1716
+ if (sep) sep.style.display = '';
1717
+ setTimeout(function poll() {
1718
+ loadBuildStatus();
1719
+ if (_buildPolling) setTimeout(poll, 4000);
1720
+ }, 3000);
1721
+ }
1722
+
1597
1723
 
1598
1724
  // ── Init ──────────────────────────────────────────────────────────────
1599
1725
  function init() {