@a83/orbiter-admin 0.3.43 → 0.3.45
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 +168 -14
- package/public/xfce.js +267 -17
- package/src/routes/search.js +9 -7
package/package.json
CHANGED
package/public/style.css
CHANGED
|
@@ -1158,8 +1158,8 @@ html[data-style="xfce"] .editor-shell {
|
|
|
1158
1158
|
flex-direction: column;
|
|
1159
1159
|
align-items: center;
|
|
1160
1160
|
justify-content: flex-end;
|
|
1161
|
-
width: var(--dock-item-base, 50px);
|
|
1162
|
-
padding:
|
|
1161
|
+
min-width: var(--dock-item-base, 50px);
|
|
1162
|
+
padding: 10px 12px 9px;
|
|
1163
1163
|
background: transparent;
|
|
1164
1164
|
border: none;
|
|
1165
1165
|
border-radius: 12px;
|
|
@@ -1196,21 +1196,25 @@ html[data-style="xfce"] .editor-shell {
|
|
|
1196
1196
|
}
|
|
1197
1197
|
.xfce-dock-item.active {
|
|
1198
1198
|
color: var(--accent);
|
|
1199
|
-
background: color-mix(in srgb, var(--accent)
|
|
1199
|
+
background: color-mix(in srgb, var(--accent) 14%, transparent);
|
|
1200
|
+
box-shadow:
|
|
1201
|
+
0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent),
|
|
1202
|
+
0 0 14px color-mix(in srgb, var(--accent) 22%, transparent),
|
|
1203
|
+
inset 0 1px 0 color-mix(in srgb, white 18%, transparent);
|
|
1200
1204
|
}
|
|
1201
|
-
.xfce-dock-item.active::before { opacity: .
|
|
1202
|
-
/* active indicator: glowing
|
|
1205
|
+
.xfce-dock-item.active::before { opacity: .65; }
|
|
1206
|
+
/* active indicator: glowing dot at bottom */
|
|
1203
1207
|
.xfce-dock-item.active::after {
|
|
1204
1208
|
content: '';
|
|
1205
1209
|
position: absolute;
|
|
1206
|
-
bottom:
|
|
1210
|
+
bottom: 4px;
|
|
1207
1211
|
left: 50%;
|
|
1208
1212
|
transform: translateX(-50%);
|
|
1209
|
-
width:
|
|
1210
|
-
height:
|
|
1211
|
-
border-radius:
|
|
1212
|
-
background:
|
|
1213
|
-
box-shadow: 0 0
|
|
1213
|
+
width: 4px;
|
|
1214
|
+
height: 4px;
|
|
1215
|
+
border-radius: 50%;
|
|
1216
|
+
background: var(--accent);
|
|
1217
|
+
box-shadow: 0 0 6px 2px color-mix(in srgb, var(--accent) 80%, transparent);
|
|
1214
1218
|
}
|
|
1215
1219
|
|
|
1216
1220
|
.xfce-dock-icon {
|
|
@@ -1227,9 +1231,10 @@ html[data-style="xfce"] .editor-shell {
|
|
|
1227
1231
|
transform: scale(1.14);
|
|
1228
1232
|
}
|
|
1229
1233
|
.xfce-dock-item.active .xfce-dock-icon {
|
|
1230
|
-
opacity:
|
|
1234
|
+
opacity: 1;
|
|
1231
1235
|
color: var(--accent);
|
|
1232
|
-
filter: drop-shadow(0 0
|
|
1236
|
+
filter: drop-shadow(0 0 8px color-mix(in srgb, var(--accent) 80%, transparent));
|
|
1237
|
+
transform: scale(1.08);
|
|
1233
1238
|
}
|
|
1234
1239
|
.xfce-dock-lbl {
|
|
1235
1240
|
font-family: var(--mono);
|
|
@@ -1246,8 +1251,10 @@ html[data-style="xfce"] .editor-shell {
|
|
|
1246
1251
|
color: color-mix(in srgb, var(--accent) 80%, var(--heading));
|
|
1247
1252
|
}
|
|
1248
1253
|
.xfce-dock-item.active .xfce-dock-lbl {
|
|
1249
|
-
opacity:
|
|
1254
|
+
opacity: 1;
|
|
1250
1255
|
color: var(--accent);
|
|
1256
|
+
font-weight: 700;
|
|
1257
|
+
letter-spacing: .08em;
|
|
1251
1258
|
}
|
|
1252
1259
|
|
|
1253
1260
|
/* ── HUD Panel ────────────────────────────────────────────── */
|
|
@@ -2150,6 +2157,20 @@ html[data-dock-pos="left"] .xfce-col-preview.visible {
|
|
|
2150
2157
|
.xfce-pal-item-body { flex: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
2151
2158
|
.xfce-pal-snippet { font-size: 10px; color: var(--muted); font-family: var(--mono); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2152
2159
|
|
|
2160
|
+
/* palette — recent entries */
|
|
2161
|
+
.xfce-pal-icon-sm { width: 20px; text-align: center; font-size: 10px; color: var(--muted); flex-shrink: 0; }
|
|
2162
|
+
.xfce-pal-group-sub {
|
|
2163
|
+
font-size: 8px; font-family: var(--mono); color: var(--line);
|
|
2164
|
+
letter-spacing: .08em; text-transform: uppercase;
|
|
2165
|
+
padding: 8px 14px 4px; border-top: 1px solid var(--line);
|
|
2166
|
+
}
|
|
2167
|
+
.xfce-pal-status {
|
|
2168
|
+
font-size: 9px; font-family: var(--mono); border-radius: 4px;
|
|
2169
|
+
padding: 1px 6px; border: 1px solid currentColor; white-space: nowrap;
|
|
2170
|
+
}
|
|
2171
|
+
.xfce-pal-status-published { color: var(--jade, #5dc97e); }
|
|
2172
|
+
.xfce-pal-status-draft { color: var(--muted); }
|
|
2173
|
+
|
|
2153
2174
|
/* ── Status bar build indicator ──────────────────────────── */
|
|
2154
2175
|
.xfce-sb-build { font-size: 9px; font-family: var(--mono); color: var(--muted); white-space: nowrap; }
|
|
2155
2176
|
.xfce-sb-g-ind {
|
|
@@ -2196,6 +2217,139 @@ html[data-style="xfce"][data-dock-pos="left"] .xfce-ws-overlay {
|
|
|
2196
2217
|
bottom: auto; left: 110px; top: 50%; transform: translateY(-50%);
|
|
2197
2218
|
}
|
|
2198
2219
|
|
|
2220
|
+
/* ── Zen / Focus mode ────────────────────────────────────── */
|
|
2221
|
+
html[data-zen="1"] .xfce-sb {
|
|
2222
|
+
opacity: 0; pointer-events: none;
|
|
2223
|
+
transform: translateY(-100%);
|
|
2224
|
+
transition: opacity .3s, transform .3s;
|
|
2225
|
+
}
|
|
2226
|
+
html[data-zen="1"] .xfce-dock {
|
|
2227
|
+
opacity: 0; pointer-events: none;
|
|
2228
|
+
transform: translateY(30px);
|
|
2229
|
+
transition: opacity .3s, transform .3s;
|
|
2230
|
+
}
|
|
2231
|
+
html[data-zen="1"][data-dock-pos="left"] .xfce-dock {
|
|
2232
|
+
transform: translateX(-30px);
|
|
2233
|
+
}
|
|
2234
|
+
html[data-zen="1"] .main {
|
|
2235
|
+
padding-top: 8px !important;
|
|
2236
|
+
padding-bottom: 20px !important;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
/* ── Shortcut Cheatsheet ─────────────────────────────────── */
|
|
2240
|
+
.xfce-cheat-overlay {
|
|
2241
|
+
display: none; position: fixed; inset: 0;
|
|
2242
|
+
background: rgba(0,0,0,.55); backdrop-filter: blur(4px);
|
|
2243
|
+
z-index: 100001; align-items: center; justify-content: center;
|
|
2244
|
+
}
|
|
2245
|
+
.xfce-cheat-overlay.open { display: flex; }
|
|
2246
|
+
.xfce-cheat-panel {
|
|
2247
|
+
background: var(--bg1); border: 1px solid var(--line);
|
|
2248
|
+
border-radius: 14px; box-shadow: 0 16px 48px rgba(0,0,0,.55);
|
|
2249
|
+
width: 560px; max-width: calc(100vw - 40px); font-family: var(--mono);
|
|
2250
|
+
overflow: hidden;
|
|
2251
|
+
}
|
|
2252
|
+
.xfce-cheat-bar {
|
|
2253
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2254
|
+
padding: 12px 16px; border-bottom: 1px solid var(--line);
|
|
2255
|
+
background: var(--bg2);
|
|
2256
|
+
}
|
|
2257
|
+
.xfce-cheat-title { color: var(--accent); font-size: 11px; font-weight: 700; letter-spacing: .07em; }
|
|
2258
|
+
.xfce-cheat-close {
|
|
2259
|
+
background: none; border: none; color: var(--muted); cursor: pointer;
|
|
2260
|
+
font-size: 13px; line-height: 1; padding: 2px 4px;
|
|
2261
|
+
}
|
|
2262
|
+
.xfce-cheat-close:hover { color: var(--text); }
|
|
2263
|
+
.xfce-cheat-body {
|
|
2264
|
+
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
|
2265
|
+
padding: 16px; gap: 0 24px; max-height: 70vh; overflow-y: auto;
|
|
2266
|
+
}
|
|
2267
|
+
.xfce-cheat-section {
|
|
2268
|
+
font-size: 9px; letter-spacing: .1em; color: var(--accent);
|
|
2269
|
+
text-transform: uppercase; font-weight: 700;
|
|
2270
|
+
margin-bottom: 8px; margin-top: 4px;
|
|
2271
|
+
}
|
|
2272
|
+
.xfce-cheat-row {
|
|
2273
|
+
display: flex; align-items: baseline; gap: 10px;
|
|
2274
|
+
padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,.04);
|
|
2275
|
+
}
|
|
2276
|
+
.xfce-cheat-row:last-child { border-bottom: none; }
|
|
2277
|
+
.xfce-cheat-key {
|
|
2278
|
+
background: var(--bg2); border: 1px solid var(--line);
|
|
2279
|
+
border-radius: 4px; padding: 1px 6px; font-size: 10px;
|
|
2280
|
+
color: var(--accent); font-family: var(--mono); white-space: nowrap;
|
|
2281
|
+
flex-shrink: 0; min-width: 80px; text-align: center;
|
|
2282
|
+
}
|
|
2283
|
+
.xfce-cheat-desc { font-size: 10px; color: var(--text); }
|
|
2284
|
+
|
|
2285
|
+
/* ── Bell / Notification Center ─────────────────────────── */
|
|
2286
|
+
.xfce-sb-bell {
|
|
2287
|
+
background: none; border: none; cursor: pointer; padding: 0 2px;
|
|
2288
|
+
color: var(--muted); font-size: 12px; line-height: 1;
|
|
2289
|
+
position: relative; display: flex; align-items: center; gap: 2px;
|
|
2290
|
+
transition: color .15s;
|
|
2291
|
+
}
|
|
2292
|
+
.xfce-sb-bell:hover { color: var(--accent); }
|
|
2293
|
+
.xfce-sb-bell-badge {
|
|
2294
|
+
position: absolute; top: -3px; right: -4px;
|
|
2295
|
+
background: var(--accent); color: var(--bg1);
|
|
2296
|
+
font-size: 8px; font-family: var(--mono); font-weight: 700;
|
|
2297
|
+
border-radius: 6px; padding: 0 3px; min-width: 12px; text-align: center;
|
|
2298
|
+
line-height: 13px; pointer-events: none;
|
|
2299
|
+
}
|
|
2300
|
+
.xfce-notif-panel {
|
|
2301
|
+
position: fixed; top: calc(var(--sb-h) + 6px); right: 12px;
|
|
2302
|
+
width: 300px; max-height: 340px;
|
|
2303
|
+
background: var(--bg1); border: 1px solid var(--line);
|
|
2304
|
+
border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,.45);
|
|
2305
|
+
z-index: 100000; display: none; flex-direction: column; overflow: hidden;
|
|
2306
|
+
font-family: var(--mono); font-size: 11px;
|
|
2307
|
+
}
|
|
2308
|
+
.xfce-notif-panel.open { display: flex; }
|
|
2309
|
+
.xfce-notif-bar {
|
|
2310
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2311
|
+
padding: 8px 12px; border-bottom: 1px solid var(--line);
|
|
2312
|
+
background: var(--bg2); flex-shrink: 0;
|
|
2313
|
+
}
|
|
2314
|
+
.xfce-notif-title { color: var(--accent); font-weight: 700; font-size: 10px; letter-spacing: .06em; }
|
|
2315
|
+
.xfce-notif-clear {
|
|
2316
|
+
background: none; border: 1px solid var(--line); border-radius: 4px;
|
|
2317
|
+
color: var(--muted); font-size: 9px; font-family: var(--mono);
|
|
2318
|
+
padding: 1px 7px; cursor: pointer; transition: color .15s, border-color .15s;
|
|
2319
|
+
}
|
|
2320
|
+
.xfce-notif-clear:hover { color: var(--accent); border-color: var(--accent); }
|
|
2321
|
+
.xfce-notif-list {
|
|
2322
|
+
overflow-y: auto; flex: 1; padding: 4px 0;
|
|
2323
|
+
}
|
|
2324
|
+
.xfce-notif-list::-webkit-scrollbar { width: 4px; }
|
|
2325
|
+
.xfce-notif-list::-webkit-scrollbar-thumb { background: var(--line); border-radius: 2px; }
|
|
2326
|
+
.xfce-notif-item {
|
|
2327
|
+
display: flex; align-items: flex-start; justify-content: space-between;
|
|
2328
|
+
gap: 8px; padding: 6px 12px;
|
|
2329
|
+
border-bottom: 1px solid rgba(255,255,255,.04);
|
|
2330
|
+
}
|
|
2331
|
+
.xfce-notif-item:last-child { border-bottom: none; }
|
|
2332
|
+
.xfce-notif-msg { flex: 1; color: var(--text); font-size: 10px; line-height: 1.4; }
|
|
2333
|
+
.xfce-notif-time { color: var(--muted); font-size: 9px; white-space: nowrap; flex-shrink: 0; margin-top: 1px; }
|
|
2334
|
+
.xfce-notif-ok .xfce-notif-msg { color: var(--jade); }
|
|
2335
|
+
.xfce-notif-err .xfce-notif-msg { color: var(--red); }
|
|
2336
|
+
.xfce-notif-empty { padding: 20px 12px; color: var(--muted); font-size: 10px; text-align: center; }
|
|
2337
|
+
|
|
2338
|
+
/* ── HUD Drafts section ──────────────────────────────────── */
|
|
2339
|
+
.xfce-hud-draft-row {
|
|
2340
|
+
display: flex; align-items: baseline; justify-content: space-between;
|
|
2341
|
+
gap: 8px; padding: 4px 0; border-bottom: 1px solid rgba(255,255,255,.04);
|
|
2342
|
+
}
|
|
2343
|
+
.xfce-hud-draft-row:last-child { border-bottom: none; }
|
|
2344
|
+
.xfce-hud-draft-link {
|
|
2345
|
+
color: var(--accent); text-decoration: none; font-size: 10px;
|
|
2346
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1;
|
|
2347
|
+
max-width: 160px;
|
|
2348
|
+
}
|
|
2349
|
+
.xfce-hud-draft-link:hover { text-decoration: underline; }
|
|
2350
|
+
.xfce-hud-draft-meta { color: var(--muted); font-size: 9px; white-space: nowrap; flex-shrink: 0; }
|
|
2351
|
+
.xfce-hud-empty { color: var(--muted); font-size: 10px; padding: 4px 0; }
|
|
2352
|
+
|
|
2199
2353
|
/* ── Toast host (above dock) ─────────────────────────────── */
|
|
2200
2354
|
.xfce-toast-host {
|
|
2201
2355
|
position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%);
|
package/public/xfce.js
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
var NAV = [
|
|
14
14
|
{ icon: '⬡', label: 'Dashboard', href: '/dashboard.html', key: 'dashboard' },
|
|
15
15
|
{ icon: '◫', label: 'Media', href: '/media.html', key: 'media' },
|
|
16
|
-
{ icon: '⚙', label: 'Settings', href: '/settings.html', key: 'settings' },
|
|
17
16
|
{ icon: '⊛', label: 'Users', href: '/users.html', key: 'users' },
|
|
18
17
|
];
|
|
19
18
|
|
|
20
19
|
var TOOLS = [
|
|
21
|
-
{ icon: '▦', label: 'Schema',
|
|
22
|
-
{ icon: '◉', label: 'Build',
|
|
23
|
-
{ icon: '↓', label: 'Import',
|
|
20
|
+
{ icon: '▦', label: 'Schema', href: '/schema.html', key: 'schema' },
|
|
21
|
+
{ icon: '◉', label: 'Build', href: '/build.html', key: 'build' },
|
|
22
|
+
{ icon: '↓', label: 'Import', href: '/import.html', key: 'import' },
|
|
23
|
+
{ icon: '⚙', label: 'Settings', href: '/settings.html', key: 'settings' },
|
|
24
24
|
];
|
|
25
25
|
|
|
26
26
|
var WORKSPACE = [
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
|
|
31
31
|
// palette items — nav + tools pre-seeded; collections appended after /api/info
|
|
32
32
|
var _palItems = NAV.concat(TOOLS).map(function (n) {
|
|
33
|
-
return { icon: n.icon, label: n.label, href: n.href, group: n.key in { schema:1, build:1, import:1 } ? 'Tools' : 'Nav' };
|
|
33
|
+
return { icon: n.icon, label: n.label, href: n.href, group: n.key in { schema:1, build:1, import:1, settings:1 } ? 'Tools' : 'Nav' };
|
|
34
34
|
});
|
|
35
|
-
var _termCols
|
|
35
|
+
var _termCols = []; // collection metadata for palette commands
|
|
36
|
+
var _palRecents = []; // prefetched recent entries for empty palette
|
|
36
37
|
|
|
37
38
|
// ── Helpers ───────────────────────────────────────────────────────────
|
|
38
39
|
function el(tag, cls, html) {
|
|
@@ -64,6 +65,8 @@
|
|
|
64
65
|
'<div class="xfce-sb-center" id="xfce-sb-title"></div>',
|
|
65
66
|
'<div class="xfce-sb-right">',
|
|
66
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
|
+
'<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
|
+
'<span class="xfce-sb-div">·</span>',
|
|
67
70
|
'<button id="xfce-sb-palette-btn" class="xfce-sb-palette-btn" title="Command palette (⌘K)">⌘</button>',
|
|
68
71
|
'<span class="xfce-sb-div">·</span>',
|
|
69
72
|
'<span id="xfce-sb-build" class="xfce-sb-build" title="Last build"></span>',
|
|
@@ -102,6 +105,12 @@
|
|
|
102
105
|
e.stopPropagation();
|
|
103
106
|
openPalette();
|
|
104
107
|
});
|
|
108
|
+
|
|
109
|
+
// Bell / notification center
|
|
110
|
+
document.getElementById('xfce-sb-bell').addEventListener('click', function (e) {
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
toggleNotifPanel();
|
|
113
|
+
});
|
|
105
114
|
}
|
|
106
115
|
|
|
107
116
|
// ── HUD Meta Panel ────────────────────────────────────────────────────
|
|
@@ -120,6 +129,8 @@
|
|
|
120
129
|
'<div id="xfce-hud-pod" class="xfce-hud-rows"></div>',
|
|
121
130
|
'<div class="xfce-hud-section-label" style="margin-top:16px">Collections</div>',
|
|
122
131
|
'<div id="xfce-hud-cols" class="xfce-hud-rows"></div>',
|
|
132
|
+
'<div class="xfce-hud-section-label" style="margin-top:16px">Drafts</div>',
|
|
133
|
+
'<div id="xfce-hud-drafts" class="xfce-hud-rows xfce-hud-drafts"></div>',
|
|
123
134
|
'<div class="xfce-hud-section-label" style="margin-top:16px">Navigation</div>',
|
|
124
135
|
'<div class="xfce-hud-nav-links" id="xfce-hud-nav"></div>',
|
|
125
136
|
'</div>',
|
|
@@ -152,19 +163,12 @@
|
|
|
152
163
|
function buildToolsPopup() {
|
|
153
164
|
toolsPopup = el('div', 'xfce-tools-popup');
|
|
154
165
|
toolsPopup.id = 'xfce-tools-popup';
|
|
155
|
-
TOOLS.forEach(function (t) {
|
|
166
|
+
TOOLS.filter(function (t) { return t.key !== 'settings'; }).forEach(function (t) {
|
|
156
167
|
var a = el('a', 'xfce-tools-item' + (page === t.key ? ' active' : ''));
|
|
157
168
|
a.href = t.href;
|
|
158
169
|
a.innerHTML = '<span class="xfce-tools-icon">' + t.icon + '</span><span>' + t.label + '</span>';
|
|
159
170
|
toolsPopup.appendChild(a);
|
|
160
171
|
});
|
|
161
|
-
var settingsSep = document.createElement('div');
|
|
162
|
-
settingsSep.className = 'xfce-tools-sep';
|
|
163
|
-
toolsPopup.appendChild(settingsSep);
|
|
164
|
-
var settingsLink = el('a', 'xfce-tools-item' + (page === 'settings' ? ' active' : ''));
|
|
165
|
-
settingsLink.href = '/settings.html';
|
|
166
|
-
settingsLink.innerHTML = '<span class="xfce-tools-icon">⚙</span><span>Settings</span>';
|
|
167
|
-
toolsPopup.appendChild(settingsLink);
|
|
168
172
|
var sep = document.createElement('div');
|
|
169
173
|
sep.className = 'xfce-tools-sep';
|
|
170
174
|
toolsPopup.appendChild(sep);
|
|
@@ -436,9 +440,55 @@
|
|
|
436
440
|
}
|
|
437
441
|
|
|
438
442
|
q = q.toLowerCase();
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
443
|
+
|
|
444
|
+
// Empty input → show recents + nav
|
|
445
|
+
if (!q) {
|
|
446
|
+
var html = '';
|
|
447
|
+
if (_palRecents.length) {
|
|
448
|
+
html += '<div class="xfce-pal-group">Recent</div>';
|
|
449
|
+
_palRecents.forEach(function (r) {
|
|
450
|
+
var href = '/collections/' + encodeURIComponent(r.collection) + '/entries/' + encodeURIComponent(r.slug);
|
|
451
|
+
html += '<div class="xfce-pal-item" data-href="' + href + '">'
|
|
452
|
+
+ '<span class="xfce-pal-icon xfce-pal-icon-sm">◈</span>'
|
|
453
|
+
+ '<div class="xfce-pal-item-body">'
|
|
454
|
+
+ '<span class="xfce-pal-label">' + escHtml(r.title || r.slug) + '</span>'
|
|
455
|
+
+ '<span class="xfce-pal-snippet">' + escHtml(r.label) + '</span>'
|
|
456
|
+
+ '</div>'
|
|
457
|
+
+ '<span class="xfce-pal-hint-r xfce-pal-status xfce-pal-status-' + r.status + '">' + r.status + '</span>'
|
|
458
|
+
+ '</div>';
|
|
459
|
+
});
|
|
460
|
+
html += '<div class="xfce-pal-group">Navigation</div>';
|
|
461
|
+
}
|
|
462
|
+
var navGroups = {};
|
|
463
|
+
_palItems.forEach(function (it) {
|
|
464
|
+
var g = it.group || 'Nav';
|
|
465
|
+
if (!navGroups[g]) navGroups[g] = [];
|
|
466
|
+
navGroups[g].push(it);
|
|
467
|
+
});
|
|
468
|
+
Object.keys(navGroups).forEach(function (g) {
|
|
469
|
+
if (_palRecents.length) html += '<div class="xfce-pal-group-sub">' + g + '</div>';
|
|
470
|
+
else html += '<div class="xfce-pal-group">' + g + '</div>';
|
|
471
|
+
navGroups[g].forEach(function (it) {
|
|
472
|
+
html += '<div class="xfce-pal-item" data-href="' + it.href + '">'
|
|
473
|
+
+ '<span class="xfce-pal-icon">' + it.icon + '</span>'
|
|
474
|
+
+ '<span class="xfce-pal-label">' + escHtml(it.label) + '</span>'
|
|
475
|
+
+ (it.meta ? '<span class="xfce-pal-hint-r">' + escHtml(it.meta) + '</span>' : '')
|
|
476
|
+
+ '</div>';
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
paletteResults.innerHTML = html;
|
|
480
|
+
paletteResults.querySelectorAll('.xfce-pal-item[data-href]').forEach(function (item) {
|
|
481
|
+
item.addEventListener('click', function () { location.href = item.dataset.href; closePalette(); });
|
|
482
|
+
item.addEventListener('mouseenter', function () {
|
|
483
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
484
|
+
palActive = Array.from(items).indexOf(item);
|
|
485
|
+
updatePalActive(items);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
var filtered = _palItems.filter(function (it) { return it.label.toLowerCase().includes(q); });
|
|
442
492
|
|
|
443
493
|
if (!filtered.length) {
|
|
444
494
|
paletteResults.innerHTML = '<div class="xfce-pal-empty">No results</div>';
|
|
@@ -714,6 +764,7 @@
|
|
|
714
764
|
t.classList.remove('show');
|
|
715
765
|
setTimeout(function () { t.remove(); }, 300);
|
|
716
766
|
}, 2500);
|
|
767
|
+
pushNotif(msg, type);
|
|
717
768
|
};
|
|
718
769
|
|
|
719
770
|
function observeSavedFlash() {
|
|
@@ -726,6 +777,77 @@
|
|
|
726
777
|
}).observe(flash, { attributes: true, attributeFilter: ['style'] });
|
|
727
778
|
}
|
|
728
779
|
|
|
780
|
+
// ── Notification Center ───────────────────────────────────────────────
|
|
781
|
+
var _notifications = [], _notifUnread = 0, _notifPanel = null;
|
|
782
|
+
|
|
783
|
+
function pushNotif(msg, type) {
|
|
784
|
+
_notifications.unshift({ msg: msg, type: type || 'info', time: Date.now() });
|
|
785
|
+
if (_notifications.length > 40) _notifications.pop();
|
|
786
|
+
_notifUnread++;
|
|
787
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
788
|
+
var icon = document.getElementById('xfce-sb-bell-icon');
|
|
789
|
+
if (badge) { badge.textContent = _notifUnread > 9 ? '9+' : _notifUnread; badge.style.display = ''; }
|
|
790
|
+
if (icon) { icon.textContent = '●'; }
|
|
791
|
+
if (_notifPanel && _notifPanel.classList.contains('open')) renderNotifList();
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function buildNotifPanel() {
|
|
795
|
+
_notifPanel = el('div', 'xfce-notif-panel');
|
|
796
|
+
_notifPanel.id = 'xfce-notif-panel';
|
|
797
|
+
_notifPanel.innerHTML = '<div class="xfce-notif-bar">'
|
|
798
|
+
+ '<span class="xfce-notif-title">Notifications</span>'
|
|
799
|
+
+ '<button id="xfce-notif-clear" class="xfce-notif-clear">Clear all</button>'
|
|
800
|
+
+ '</div>'
|
|
801
|
+
+ '<div id="xfce-notif-list" class="xfce-notif-list"></div>';
|
|
802
|
+
document.body.appendChild(_notifPanel);
|
|
803
|
+
document.getElementById('xfce-notif-clear').addEventListener('click', function () {
|
|
804
|
+
_notifications = []; _notifUnread = 0;
|
|
805
|
+
renderNotifList();
|
|
806
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
807
|
+
var icon = document.getElementById('xfce-sb-bell-icon');
|
|
808
|
+
if (badge) badge.style.display = 'none';
|
|
809
|
+
if (icon) icon.textContent = '○';
|
|
810
|
+
});
|
|
811
|
+
document.addEventListener('click', function (e) {
|
|
812
|
+
if (_notifPanel && _notifPanel.classList.contains('open')
|
|
813
|
+
&& !_notifPanel.contains(e.target)
|
|
814
|
+
&& e.target.id !== 'xfce-sb-bell') closeNotifPanel();
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function renderNotifList() {
|
|
819
|
+
var list = document.getElementById('xfce-notif-list');
|
|
820
|
+
if (!list) return;
|
|
821
|
+
if (!_notifications.length) {
|
|
822
|
+
list.innerHTML = '<div class="xfce-notif-empty">No notifications yet</div>';
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
list.innerHTML = _notifications.map(function (n) {
|
|
826
|
+
var ago = Math.floor((Date.now() - n.time) / 1000);
|
|
827
|
+
var t = ago < 60 ? ago + 's' : ago < 3600 ? Math.floor(ago/60) + 'm' : Math.floor(ago/3600) + 'h';
|
|
828
|
+
var cls = n.type === 'success' ? 'ok' : n.type === 'error' ? 'err' : 'info';
|
|
829
|
+
return '<div class="xfce-notif-item xfce-notif-' + cls + '">'
|
|
830
|
+
+ '<span class="xfce-notif-msg">' + escHtml(n.msg) + '</span>'
|
|
831
|
+
+ '<span class="xfce-notif-time">' + t + '</span>'
|
|
832
|
+
+ '</div>';
|
|
833
|
+
}).join('');
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function toggleNotifPanel() {
|
|
837
|
+
if (!_notifPanel) buildNotifPanel();
|
|
838
|
+
var isOpen = _notifPanel.classList.toggle('open');
|
|
839
|
+
if (isOpen) {
|
|
840
|
+
_notifUnread = 0;
|
|
841
|
+
var badge = document.getElementById('xfce-sb-bell-badge');
|
|
842
|
+
if (badge) badge.style.display = 'none';
|
|
843
|
+
renderNotifList();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
function closeNotifPanel() {
|
|
848
|
+
if (_notifPanel) _notifPanel.classList.remove('open');
|
|
849
|
+
}
|
|
850
|
+
|
|
729
851
|
// ── Workspace overlay (Notes + To-do) ────────────────────────────────
|
|
730
852
|
var wsOverlay, wsActivePane = 'notes', wsNotesTimer, wsTodosData = [];
|
|
731
853
|
|
|
@@ -1091,6 +1213,9 @@
|
|
|
1091
1213
|
});
|
|
1092
1214
|
dockInner.appendChild(toolsBtn);
|
|
1093
1215
|
|
|
1216
|
+
var settingsDockBtn = makeDockItem('⚙', 'Settings', '/settings.html', page === 'settings', false);
|
|
1217
|
+
dockInner.appendChild(settingsDockBtn);
|
|
1218
|
+
|
|
1094
1219
|
dockInner.appendChild(el('div', 'xfce-dock-sep'));
|
|
1095
1220
|
|
|
1096
1221
|
// HUD toggle
|
|
@@ -1213,6 +1338,28 @@
|
|
|
1213
1338
|
})
|
|
1214
1339
|
.catch(function () {});
|
|
1215
1340
|
|
|
1341
|
+
// HUD Drafts
|
|
1342
|
+
fetch('/api/search/recent?status=draft&limit=10', { credentials: 'include' })
|
|
1343
|
+
.then(function (r) { return r.ok ? r.json() : []; })
|
|
1344
|
+
.then(function (drafts) {
|
|
1345
|
+
var hudDrafts = document.getElementById('xfce-hud-drafts');
|
|
1346
|
+
if (!hudDrafts) return;
|
|
1347
|
+
if (!drafts.length) {
|
|
1348
|
+
hudDrafts.innerHTML = '<div class="xfce-hud-empty">No drafts</div>';
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
hudDrafts.innerHTML = drafts.map(function (d) {
|
|
1352
|
+
var href = '/collections/' + encodeURIComponent(d.collection) + '/entries/' + encodeURIComponent(d.slug);
|
|
1353
|
+
var ago = Math.floor((Date.now() - new Date(d.updated_at).getTime()) / 60000);
|
|
1354
|
+
var t = ago < 60 ? ago + 'm' : Math.floor(ago / 60) + 'h';
|
|
1355
|
+
return '<div class="xfce-hud-draft-row">'
|
|
1356
|
+
+ '<a class="xfce-hud-draft-link" href="' + href + '">' + escHtml(d.title || d.slug) + '</a>'
|
|
1357
|
+
+ '<span class="xfce-hud-draft-meta">' + escHtml(d.label) + ' · ' + t + '</span>'
|
|
1358
|
+
+ '</div>';
|
|
1359
|
+
}).join('');
|
|
1360
|
+
})
|
|
1361
|
+
.catch(function () {});
|
|
1362
|
+
|
|
1216
1363
|
// Site meta
|
|
1217
1364
|
fetch('/api/meta/site~name', { credentials: 'include' })
|
|
1218
1365
|
.then(function (r) { return r.ok ? r.json() : null; })
|
|
@@ -1232,12 +1379,98 @@
|
|
|
1232
1379
|
if (sbUser) sbUser.textContent = d.user.username;
|
|
1233
1380
|
})
|
|
1234
1381
|
.catch(function () {});
|
|
1382
|
+
|
|
1383
|
+
// Prefetch recent entries for palette empty state
|
|
1384
|
+
fetch('/api/search/recent?limit=7', { credentials: 'include' })
|
|
1385
|
+
.then(function (r) { return r.ok ? r.json() : []; })
|
|
1386
|
+
.then(function (rows) { _palRecents = rows; })
|
|
1387
|
+
.catch(function () {});
|
|
1235
1388
|
}
|
|
1236
1389
|
|
|
1237
1390
|
function hudRow(label, value) {
|
|
1238
1391
|
return '<div class="xfce-hud-row"><span>' + label + '</span><span>' + value + '</span></div>';
|
|
1239
1392
|
}
|
|
1240
1393
|
|
|
1394
|
+
// ── Zen / Focus mode ─────────────────────────────────────────────────
|
|
1395
|
+
function toggleZen() {
|
|
1396
|
+
var html = document.documentElement;
|
|
1397
|
+
var on = html.dataset.zen !== '1';
|
|
1398
|
+
html.dataset.zen = on ? '1' : '';
|
|
1399
|
+
localStorage.setItem('orb_zen', on ? '1' : '');
|
|
1400
|
+
if (on) window.xfceToast('Focus mode on — ⌘⇧F to exit', 'info');
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// ── Shortcut cheatsheet ───────────────────────────────────────────────
|
|
1404
|
+
var _cheatEl = null;
|
|
1405
|
+
|
|
1406
|
+
function buildCheatsheet() {
|
|
1407
|
+
_cheatEl = el('div', 'xfce-cheat-overlay');
|
|
1408
|
+
_cheatEl.id = 'xfce-cheat';
|
|
1409
|
+
_cheatEl.innerHTML = [
|
|
1410
|
+
'<div class="xfce-cheat-panel">',
|
|
1411
|
+
'<div class="xfce-cheat-bar">',
|
|
1412
|
+
'<span class="xfce-cheat-title">⌨ Shortcuts</span>',
|
|
1413
|
+
'<button class="xfce-cheat-close" id="xfce-cheat-close">✕</button>',
|
|
1414
|
+
'</div>',
|
|
1415
|
+
'<div class="xfce-cheat-body">',
|
|
1416
|
+
'<div class="xfce-cheat-col">',
|
|
1417
|
+
'<div class="xfce-cheat-section">Navigation</div>',
|
|
1418
|
+
cheatRow('⌘K / /', 'Open command palette'),
|
|
1419
|
+
cheatRow('↑ ↓', 'Move selection in palette'),
|
|
1420
|
+
cheatRow('↵', 'Go to selected item'),
|
|
1421
|
+
cheatRow('Esc', 'Close overlay / panel'),
|
|
1422
|
+
cheatRow('g + d', 'Dashboard'),
|
|
1423
|
+
cheatRow('g + m', 'Media'),
|
|
1424
|
+
cheatRow('g + u', 'Users'),
|
|
1425
|
+
cheatRow('g + s', 'Settings'),
|
|
1426
|
+
cheatRow('g + b', 'Build'),
|
|
1427
|
+
cheatRow('g + i', 'Import'),
|
|
1428
|
+
cheatRow('g + h', 'Schema'),
|
|
1429
|
+
cheatRow('g + a', 'Account'),
|
|
1430
|
+
cheatRow('1 – 9', 'Jump to nth dock item'),
|
|
1431
|
+
'</div>',
|
|
1432
|
+
'<div class="xfce-cheat-col">',
|
|
1433
|
+
'<div class="xfce-cheat-section">Panels</div>',
|
|
1434
|
+
cheatRow('⌘⇧D', 'Toggle HUD'),
|
|
1435
|
+
cheatRow('⌘⇧F', 'Focus / Zen mode'),
|
|
1436
|
+
cheatRow('⌘⇧L', 'Switch to Glass mode'),
|
|
1437
|
+
cheatRow('?', 'This cheatsheet'),
|
|
1438
|
+
'<div class="xfce-cheat-section" style="margin-top:14px">Palette commands</div>',
|
|
1439
|
+
cheatRow('> ls', 'List collections'),
|
|
1440
|
+
cheatRow('> go <page>', 'Navigate to page'),
|
|
1441
|
+
cheatRow('> new <col>', 'New entry in collection'),
|
|
1442
|
+
cheatRow('> search <q>', 'Search entries'),
|
|
1443
|
+
cheatRow('> build', 'Trigger site build'),
|
|
1444
|
+
cheatRow('> export <col>', 'Export collection'),
|
|
1445
|
+
cheatRow('> info', 'Show pod info'),
|
|
1446
|
+
cheatRow('> help', 'Show command help'),
|
|
1447
|
+
'</div>',
|
|
1448
|
+
'</div>',
|
|
1449
|
+
'</div>',
|
|
1450
|
+
].join('');
|
|
1451
|
+
document.body.appendChild(_cheatEl);
|
|
1452
|
+
document.getElementById('xfce-cheat-close').addEventListener('click', closeCheatsheet);
|
|
1453
|
+
_cheatEl.addEventListener('click', function (e) {
|
|
1454
|
+
if (e.target === _cheatEl) closeCheatsheet();
|
|
1455
|
+
});
|
|
1456
|
+
document.addEventListener('keydown', function (e) {
|
|
1457
|
+
if (e.key === 'Escape' && _cheatEl && _cheatEl.classList.contains('open')) closeCheatsheet();
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
function cheatRow(key, desc) {
|
|
1462
|
+
return '<div class="xfce-cheat-row"><kbd class="xfce-cheat-key">' + key + '</kbd><span class="xfce-cheat-desc">' + desc + '</span></div>';
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
function toggleCheatsheet() {
|
|
1466
|
+
if (!_cheatEl) buildCheatsheet();
|
|
1467
|
+
_cheatEl.classList.toggle('open');
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function closeCheatsheet() {
|
|
1471
|
+
if (_cheatEl) _cheatEl.classList.remove('open');
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1241
1474
|
// ── Keyboard shortcuts ────────────────────────────────────────────────
|
|
1242
1475
|
var _gPending = false, _gTimer = null;
|
|
1243
1476
|
var G_MAP = { d: '/dashboard.html', m: '/media.html', s: '/settings.html',
|
|
@@ -1299,6 +1532,13 @@
|
|
|
1299
1532
|
return;
|
|
1300
1533
|
}
|
|
1301
1534
|
|
|
1535
|
+
// ⌘⇧F — zen / focus mode
|
|
1536
|
+
if (mod && e.shiftKey && (e.key === 'f' || e.key === 'F')) {
|
|
1537
|
+
e.preventDefault();
|
|
1538
|
+
toggleZen();
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1302
1542
|
// ⌘⇧L — switch back to glass mode
|
|
1303
1543
|
if (mod && e.shiftKey && (e.key === 'l' || e.key === 'L')) {
|
|
1304
1544
|
e.preventDefault();
|
|
@@ -1307,6 +1547,13 @@
|
|
|
1307
1547
|
return;
|
|
1308
1548
|
}
|
|
1309
1549
|
|
|
1550
|
+
// ? — shortcut cheatsheet
|
|
1551
|
+
if (!mod && !e.altKey && e.key === '?' && !isEditing(e.target)) {
|
|
1552
|
+
e.preventDefault();
|
|
1553
|
+
toggleCheatsheet();
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1310
1557
|
// 1–9 — jump to nth dock link (no modifier, not in input)
|
|
1311
1558
|
if (!mod && !e.shiftKey && !e.altKey && !isEditing(e.target)) {
|
|
1312
1559
|
var n = parseInt(e.key);
|
|
@@ -1359,6 +1606,9 @@
|
|
|
1359
1606
|
bindKeys();
|
|
1360
1607
|
initFocusMode();
|
|
1361
1608
|
observeSavedFlash();
|
|
1609
|
+
if (localStorage.getItem('orb_zen') === '1') {
|
|
1610
|
+
document.documentElement.dataset.zen = '1';
|
|
1611
|
+
}
|
|
1362
1612
|
}
|
|
1363
1613
|
|
|
1364
1614
|
if (document.readyState === 'loading') {
|
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,
|