@a83/orbiter-admin 0.3.39 → 0.3.41
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 +97 -11
- package/public/xfce.js +285 -48
- package/src/routes/search.js +13 -2
package/package.json
CHANGED
package/public/style.css
CHANGED
|
@@ -2028,23 +2028,47 @@ a.xfce-sb-logo:hover { opacity: .8; }
|
|
|
2028
2028
|
}
|
|
2029
2029
|
.xfce-dock-item { position: relative; }
|
|
2030
2030
|
|
|
2031
|
-
/* Hover
|
|
2032
|
-
.xfce-col-
|
|
2031
|
+
/* Hover preview card above collection dock items */
|
|
2032
|
+
.xfce-col-preview {
|
|
2033
2033
|
position: fixed; z-index: 99990;
|
|
2034
|
-
width: 22px; height: 22px; border-radius: 50%;
|
|
2035
|
-
background: var(--accent);
|
|
2036
|
-
color: color-mix(in srgb, var(--bg1) 15%, #000);
|
|
2037
|
-
font-size: 17px; line-height: 22px; font-weight: 300;
|
|
2038
|
-
text-align: center; text-decoration: none;
|
|
2039
2034
|
transform: translateX(-50%) translateY(6px);
|
|
2040
2035
|
opacity: 0; pointer-events: none;
|
|
2041
|
-
transition: opacity .
|
|
2042
|
-
|
|
2043
|
-
|
|
2036
|
+
transition: opacity .15s, transform .15s cubic-bezier(.34,1.4,.64,1);
|
|
2037
|
+
background: var(--bg1); border: 1px solid var(--line);
|
|
2038
|
+
border-top: 2px solid var(--accent);
|
|
2039
|
+
border-radius: 10px; min-width: 180px; max-width: 240px;
|
|
2040
|
+
box-shadow: 0 4px 24px rgba(0,0,0,.25);
|
|
2041
|
+
overflow: hidden; font-family: var(--mono);
|
|
2044
2042
|
}
|
|
2045
|
-
.xfce-col-
|
|
2043
|
+
.xfce-col-preview.visible {
|
|
2046
2044
|
opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto;
|
|
2047
2045
|
}
|
|
2046
|
+
html[data-dock-pos="left"] .xfce-col-preview {
|
|
2047
|
+
transform: translateX(0) translateY(-50%);
|
|
2048
|
+
}
|
|
2049
|
+
html[data-dock-pos="left"] .xfce-col-preview.visible {
|
|
2050
|
+
transform: translateX(6px) translateY(-50%);
|
|
2051
|
+
}
|
|
2052
|
+
.xfce-preview-head { padding: 7px 12px; border-bottom: 1px solid var(--line); }
|
|
2053
|
+
.xfce-preview-head a { font-size: 10px; font-weight: 600; color: var(--accent); text-decoration: none; text-transform: uppercase; letter-spacing: .06em; }
|
|
2054
|
+
.xfce-preview-head a:hover { text-decoration: underline; }
|
|
2055
|
+
.xfce-preview-entries { padding: 4px 0; }
|
|
2056
|
+
.xfce-preview-row {
|
|
2057
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
2058
|
+
padding: 4px 12px; gap: 8px; text-decoration: none;
|
|
2059
|
+
transition: background .1s;
|
|
2060
|
+
}
|
|
2061
|
+
.xfce-preview-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
2062
|
+
.xfce-preview-slug { font-size: 10px; color: var(--body); flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2063
|
+
.xfce-preview-date { font-size: 9px; color: var(--muted); flex-shrink: 0; }
|
|
2064
|
+
.xfce-preview-empty { padding: 6px 12px; font-size: 10px; color: var(--muted); }
|
|
2065
|
+
.xfce-preview-loading { padding: 10px 12px; font-size: 10px; color: var(--muted); }
|
|
2066
|
+
.xfce-preview-new {
|
|
2067
|
+
display: block; padding: 6px 12px; font-size: 10px;
|
|
2068
|
+
color: var(--accent); text-decoration: none; border-top: 1px solid var(--line);
|
|
2069
|
+
transition: background .1s;
|
|
2070
|
+
}
|
|
2071
|
+
.xfce-preview-new:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
2048
2072
|
|
|
2049
2073
|
/* ── Command Palette ─────────────────────────────────────── */
|
|
2050
2074
|
.xfce-palette {
|
|
@@ -2115,6 +2139,68 @@ a.xfce-sb-logo:hover { opacity: .8; }
|
|
|
2115
2139
|
.xfce-pal-muted { color: var(--muted); opacity: .7; }
|
|
2116
2140
|
.xfce-pal-cmd-hint { color: var(--accent); }
|
|
2117
2141
|
|
|
2142
|
+
/* search result with snippet */
|
|
2143
|
+
.xfce-pal-item--rich { align-items: flex-start; }
|
|
2144
|
+
.xfce-pal-item--rich .xfce-pal-icon { margin-top: 2px; }
|
|
2145
|
+
.xfce-pal-item-body { flex: 1; display: flex; flex-direction: column; gap: 1px; min-width: 0; }
|
|
2146
|
+
.xfce-pal-snippet { font-size: 10px; color: var(--muted); font-family: var(--mono); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
2147
|
+
|
|
2148
|
+
/* ── Status bar build indicator ──────────────────────────── */
|
|
2149
|
+
.xfce-sb-build { font-size: 9px; font-family: var(--mono); color: var(--muted); white-space: nowrap; }
|
|
2150
|
+
.xfce-sb-g-ind { font-size: 10px; font-family: var(--mono); color: var(--accent); font-weight: 600; letter-spacing: .05em; animation: xfce-blink .7s step-end infinite; }
|
|
2151
|
+
@keyframes xfce-blink { 0%,100% { opacity:1; } 50% { opacity:.3; } }
|
|
2152
|
+
|
|
2153
|
+
/* ── Left dock mode ───────────────────────────────────────── */
|
|
2154
|
+
html[data-style="xfce"][data-dock-pos="left"] .main {
|
|
2155
|
+
padding-bottom: 20px !important;
|
|
2156
|
+
padding-left: 100px !important;
|
|
2157
|
+
}
|
|
2158
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock {
|
|
2159
|
+
bottom: auto; left: 16px; top: 50%;
|
|
2160
|
+
transform: translateY(-50%);
|
|
2161
|
+
padding: 10px 6px;
|
|
2162
|
+
align-items: center;
|
|
2163
|
+
}
|
|
2164
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-inner {
|
|
2165
|
+
flex-direction: column; align-items: center; gap: 4px;
|
|
2166
|
+
}
|
|
2167
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-group {
|
|
2168
|
+
flex-direction: column; align-items: center; gap: 4px;
|
|
2169
|
+
}
|
|
2170
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-sep {
|
|
2171
|
+
width: 28px; height: 1px; margin: 2px 0;
|
|
2172
|
+
}
|
|
2173
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-item {
|
|
2174
|
+
transform-origin: left center;
|
|
2175
|
+
}
|
|
2176
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-toast-host {
|
|
2177
|
+
bottom: 20px; left: 110px; transform: none;
|
|
2178
|
+
}
|
|
2179
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-palette {
|
|
2180
|
+
padding-bottom: 20px; padding-left: 100px; align-items: center;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/* ── Dock context menu ───────────────────────────────────── */
|
|
2184
|
+
.xfce-ctx-menu {
|
|
2185
|
+
display: none; position: fixed; z-index: 100000;
|
|
2186
|
+
background: var(--bg1); border: 1px solid var(--line);
|
|
2187
|
+
border-top: 2px solid var(--accent);
|
|
2188
|
+
border-radius: 8px; padding: 4px 0;
|
|
2189
|
+
box-shadow: 0 4px 24px rgba(0,0,0,.25);
|
|
2190
|
+
min-width: 160px;
|
|
2191
|
+
opacity: 0; transform: scale(.96); transform-origin: top left;
|
|
2192
|
+
transition: opacity .1s, transform .1s;
|
|
2193
|
+
}
|
|
2194
|
+
.xfce-ctx-menu.open { opacity: 1; transform: scale(1); }
|
|
2195
|
+
.xfce-ctx-item {
|
|
2196
|
+
display: flex; align-items: center; gap: 10px;
|
|
2197
|
+
width: 100%; background: none; border: none; cursor: pointer;
|
|
2198
|
+
padding: 7px 14px; font-size: 12px; font-family: var(--mono);
|
|
2199
|
+
color: var(--body); text-align: left; transition: background .1s;
|
|
2200
|
+
}
|
|
2201
|
+
.xfce-ctx-item:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--heading); }
|
|
2202
|
+
.xfce-ctx-icon { width: 16px; text-align: center; color: var(--accent); flex-shrink: 0; }
|
|
2203
|
+
|
|
2118
2204
|
/* ── Toast host (above dock) ─────────────────────────────── */
|
|
2119
2205
|
.xfce-toast-host {
|
|
2120
2206
|
position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%);
|
package/public/xfce.js
CHANGED
|
@@ -63,8 +63,11 @@
|
|
|
63
63
|
'</div>',
|
|
64
64
|
'<div class="xfce-sb-center" id="xfce-sb-title"></div>',
|
|
65
65
|
'<div class="xfce-sb-right">',
|
|
66
|
+
'<span id="xfce-sb-g-ind" class="xfce-sb-g-ind" style="display:none" title="g — type destination key">g_</span>',
|
|
66
67
|
'<button id="xfce-sb-palette-btn" class="xfce-sb-palette-btn" title="Command palette (⌘K)">⌘</button>',
|
|
67
68
|
'<span class="xfce-sb-div">·</span>',
|
|
69
|
+
'<span id="xfce-sb-build" class="xfce-sb-build" title="Last build"></span>',
|
|
70
|
+
'<span id="xfce-sb-build-sep" class="xfce-sb-div" style="display:none">·</span>',
|
|
68
71
|
'<a id="xfce-sb-user" href="/account.html" class="xfce-sb-user-link"></a>',
|
|
69
72
|
'<span class="xfce-sb-div">·</span>',
|
|
70
73
|
'<button id="xfce-sb-logout" class="xfce-sb-logout" title="Log out">⏻</button>',
|
|
@@ -166,6 +169,30 @@
|
|
|
166
169
|
openPalette();
|
|
167
170
|
});
|
|
168
171
|
toolsPopup.appendChild(palBtn);
|
|
172
|
+
|
|
173
|
+
var dockSep2 = document.createElement('div');
|
|
174
|
+
dockSep2.className = 'xfce-tools-sep';
|
|
175
|
+
toolsPopup.appendChild(dockSep2);
|
|
176
|
+
|
|
177
|
+
var dockPosBtn = el('button', 'xfce-tools-item');
|
|
178
|
+
dockPosBtn.id = 'xfce-dock-pos-btn';
|
|
179
|
+
function updateDockPosLabel() {
|
|
180
|
+
var pos = document.documentElement.dataset.dockPos || 'bottom';
|
|
181
|
+
dockPosBtn.innerHTML = '<span class="xfce-tools-icon">' + (pos === 'left' ? '⬌' : '⬍') + '</span>'
|
|
182
|
+
+ '<span>Dock: ' + (pos === 'left' ? 'left' : 'bottom') + '</span>';
|
|
183
|
+
}
|
|
184
|
+
updateDockPosLabel();
|
|
185
|
+
dockPosBtn.addEventListener('click', function (e) {
|
|
186
|
+
e.stopPropagation();
|
|
187
|
+
var cur = document.documentElement.dataset.dockPos || 'bottom';
|
|
188
|
+
var next = cur === 'bottom' ? 'left' : 'bottom';
|
|
189
|
+
localStorage.setItem('orb_dock_pos', next);
|
|
190
|
+
document.documentElement.dataset.dockPos = next;
|
|
191
|
+
updateDockPosLabel();
|
|
192
|
+
toolsPopup.classList.remove('open');
|
|
193
|
+
_previewCache = {};
|
|
194
|
+
});
|
|
195
|
+
toolsPopup.appendChild(dockPosBtn);
|
|
169
196
|
document.body.appendChild(toolsPopup);
|
|
170
197
|
document.addEventListener('click', function () {
|
|
171
198
|
toolsPopup.classList.remove('open');
|
|
@@ -185,36 +212,91 @@
|
|
|
185
212
|
toolsPopup.classList.toggle('open');
|
|
186
213
|
}
|
|
187
214
|
|
|
188
|
-
// ── Hover
|
|
189
|
-
var
|
|
215
|
+
// ── Hover preview card above collection items ────────────────────────
|
|
216
|
+
var _previewEl = null, _previewTimer = null, _previewCache = {};
|
|
217
|
+
|
|
218
|
+
function buildPreview() {
|
|
219
|
+
_previewEl = el('div', 'xfce-col-preview');
|
|
220
|
+
_previewEl.id = 'xfce-col-preview';
|
|
221
|
+
_previewEl.addEventListener('mouseenter', function () { clearTimeout(_previewTimer); });
|
|
222
|
+
_previewEl.addEventListener('mouseleave', function () { _previewTimer = setTimeout(hideColPreview, 150); });
|
|
223
|
+
document.body.appendChild(_previewEl);
|
|
224
|
+
}
|
|
190
225
|
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
colCreateEl.addEventListener('mouseenter', function () { clearTimeout(colCreateTimer); });
|
|
196
|
-
colCreateEl.addEventListener('mouseleave', function () { colCreateTimer = setTimeout(hideColCreate, 120); });
|
|
197
|
-
document.body.appendChild(colCreateEl);
|
|
226
|
+
function showColPreview(col, itemEl) {
|
|
227
|
+
if (!_previewEl) buildPreview();
|
|
228
|
+
clearTimeout(_previewTimer);
|
|
229
|
+
_previewTimer = setTimeout(function () { _renderPreview(col, itemEl); }, 280);
|
|
198
230
|
}
|
|
199
231
|
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
var dock
|
|
204
|
-
var
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
232
|
+
function _renderPreview(col, itemEl) {
|
|
233
|
+
var dock = document.getElementById('xfce-dock');
|
|
234
|
+
var isLeft = document.documentElement.dataset.dockPos === 'left';
|
|
235
|
+
var dockR = dock ? dock.getBoundingClientRect() : { top: 0, right: 0 };
|
|
236
|
+
var itemR = itemEl.getBoundingClientRect();
|
|
237
|
+
|
|
238
|
+
function place() {
|
|
239
|
+
if (isLeft) {
|
|
240
|
+
_previewEl.style.left = Math.round(dockR.right + 10) + 'px';
|
|
241
|
+
_previewEl.style.top = Math.round(itemR.top + itemR.height / 2 - _previewEl.offsetHeight / 2) + 'px';
|
|
242
|
+
_previewEl.style.bottom = 'auto';
|
|
243
|
+
} else {
|
|
244
|
+
var cx = Math.round(itemR.left + itemR.width / 2);
|
|
245
|
+
_previewEl.style.left = cx + 'px';
|
|
246
|
+
_previewEl.style.bottom = Math.round(window.innerHeight - dockR.top + 10) + 'px';
|
|
247
|
+
_previewEl.style.top = 'auto';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var newHref = '/editor.html?collection=' + encodeURIComponent(col.id);
|
|
252
|
+
var entriesHref = col.singleton
|
|
253
|
+
? newHref + '&singleton=1'
|
|
254
|
+
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
255
|
+
|
|
256
|
+
function render(entries) {
|
|
257
|
+
var rows = entries.length
|
|
258
|
+
? entries.slice(0, 3).map(function (e) {
|
|
259
|
+
var slug = e.slug || e.id || '';
|
|
260
|
+
var date = (e.updated_at || e.created_at || '').substring(5, 10);
|
|
261
|
+
var href = '/editor.html?collection=' + encodeURIComponent(col.id) + '&slug=' + encodeURIComponent(slug);
|
|
262
|
+
return '<a class="xfce-preview-row" href="' + href + '">'
|
|
263
|
+
+ '<span class="xfce-preview-slug">' + escHtml(slug) + '</span>'
|
|
264
|
+
+ '<span class="xfce-preview-date">' + date + '</span>'
|
|
265
|
+
+ '</a>';
|
|
266
|
+
}).join('')
|
|
267
|
+
: '<div class="xfce-preview-empty">no entries yet</div>';
|
|
268
|
+
_previewEl.innerHTML =
|
|
269
|
+
'<div class="xfce-preview-head"><a href="' + entriesHref + '">' + escHtml(col.label) + '</a></div>'
|
|
270
|
+
+ '<div class="xfce-preview-entries">' + rows + '</div>'
|
|
271
|
+
+ '<a class="xfce-preview-new" href="' + newHref + '">+ new entry</a>';
|
|
272
|
+
_previewEl.classList.add('visible');
|
|
273
|
+
place();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (_previewCache[col.id]) {
|
|
277
|
+
render(_previewCache[col.id]);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
_previewEl.innerHTML = '<div class="xfce-preview-loading">…</div>';
|
|
281
|
+
_previewEl.classList.add('visible');
|
|
282
|
+
place();
|
|
283
|
+
fetch('/api/collections/' + encodeURIComponent(col.id) + '/entries?limit=3', { credentials: 'include' })
|
|
284
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
285
|
+
.then(function (d) {
|
|
286
|
+
var entries = d ? (d.entries || (Array.isArray(d) ? d : [])) : [];
|
|
287
|
+
_previewCache[col.id] = entries;
|
|
288
|
+
if (_previewEl.classList.contains('visible')) render(entries);
|
|
289
|
+
});
|
|
210
290
|
}
|
|
211
291
|
|
|
212
|
-
function
|
|
213
|
-
|
|
292
|
+
function hideColPreview() {
|
|
293
|
+
clearTimeout(_previewTimer);
|
|
294
|
+
if (_previewEl) _previewEl.classList.remove('visible');
|
|
214
295
|
}
|
|
215
296
|
|
|
216
297
|
// ── Command Palette ───────────────────────────────────────────────────
|
|
217
298
|
var palette, paletteInp, paletteResults, palActive = -1;
|
|
299
|
+
var _cmdHistory = [], _cmdHistIdx = -1;
|
|
218
300
|
|
|
219
301
|
var NAV_DEST = {
|
|
220
302
|
dashboard: '/dashboard.html', media: '/media.html', settings: '/settings.html',
|
|
@@ -247,10 +329,18 @@
|
|
|
247
329
|
|
|
248
330
|
paletteInp.addEventListener('keydown', function (e) {
|
|
249
331
|
var val = paletteInp.value.trim();
|
|
332
|
+
var inCmdMode = val.startsWith('>');
|
|
333
|
+
|
|
250
334
|
if (e.key === 'Enter') {
|
|
251
|
-
if (
|
|
335
|
+
if (inCmdMode) {
|
|
252
336
|
e.preventDefault();
|
|
253
|
-
|
|
337
|
+
var cmd = val.slice(1).trim();
|
|
338
|
+
if (cmd) {
|
|
339
|
+
if (!_cmdHistory.length || _cmdHistory[0] !== cmd) _cmdHistory.unshift(cmd);
|
|
340
|
+
if (_cmdHistory.length > 30) _cmdHistory.pop();
|
|
341
|
+
}
|
|
342
|
+
_cmdHistIdx = -1;
|
|
343
|
+
execPaletteCmd(cmd);
|
|
254
344
|
} else {
|
|
255
345
|
var active = paletteResults.querySelector('.xfce-pal-item.pal-active');
|
|
256
346
|
if (active && active.dataset.href) { location.href = active.dataset.href; closePalette(); }
|
|
@@ -259,16 +349,28 @@
|
|
|
259
349
|
if (first) { location.href = first.dataset.href; closePalette(); }
|
|
260
350
|
}
|
|
261
351
|
}
|
|
262
|
-
} else if (e.key === 'ArrowDown') {
|
|
263
|
-
e.preventDefault();
|
|
264
|
-
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
265
|
-
palActive = Math.min(palActive + 1, items.length - 1);
|
|
266
|
-
updatePalActive(items);
|
|
267
352
|
} else if (e.key === 'ArrowUp') {
|
|
268
353
|
e.preventDefault();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
354
|
+
if (inCmdMode && _cmdHistory.length) {
|
|
355
|
+
_cmdHistIdx = Math.min(_cmdHistIdx + 1, _cmdHistory.length - 1);
|
|
356
|
+
paletteInp.value = '> ' + _cmdHistory[_cmdHistIdx];
|
|
357
|
+
renderPalette(paletteInp.value);
|
|
358
|
+
} else if (!inCmdMode) {
|
|
359
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
360
|
+
palActive = Math.max(palActive - 1, 0);
|
|
361
|
+
updatePalActive(items);
|
|
362
|
+
}
|
|
363
|
+
} else if (e.key === 'ArrowDown') {
|
|
364
|
+
e.preventDefault();
|
|
365
|
+
if (inCmdMode && _cmdHistory.length) {
|
|
366
|
+
_cmdHistIdx = Math.max(_cmdHistIdx - 1, -1);
|
|
367
|
+
paletteInp.value = _cmdHistIdx < 0 ? '> ' : '> ' + _cmdHistory[_cmdHistIdx];
|
|
368
|
+
renderPalette(paletteInp.value);
|
|
369
|
+
} else if (!inCmdMode) {
|
|
370
|
+
var items = paletteResults.querySelectorAll('.xfce-pal-item[data-href]');
|
|
371
|
+
palActive = Math.min(palActive + 1, items.length - 1);
|
|
372
|
+
updatePalActive(items);
|
|
373
|
+
}
|
|
272
374
|
} else if (e.key === 'Escape') {
|
|
273
375
|
closePalette();
|
|
274
376
|
}
|
|
@@ -480,11 +582,15 @@
|
|
|
480
582
|
var results = d.results || (Array.isArray(d) ? d : []);
|
|
481
583
|
if (!results.length) { palPrint('no results for “' + escHtml(term) + '”', 'muted'); return; }
|
|
482
584
|
var html = results.slice(0, 15).map(function (r) {
|
|
483
|
-
var href = '/editor.html?collection=' + encodeURIComponent(r.collection || '') + '&
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
+ '<span class
|
|
487
|
-
+ '<span class
|
|
585
|
+
var href = '/editor.html?collection=' + encodeURIComponent(r.collection || '') + '&slug=' + encodeURIComponent(r.slug || '');
|
|
586
|
+
var snippet = r.snippet ? '<div class=”xfce-pal-snippet”>' + escHtml(r.snippet) + '</div>' : '';
|
|
587
|
+
return '<div class=”xfce-pal-item xfce-pal-item--rich” data-href=”' + href + '”>'
|
|
588
|
+
+ '<span class=”xfce-pal-icon”>⌕</span>'
|
|
589
|
+
+ '<span class=”xfce-pal-item-body”>'
|
|
590
|
+
+ '<span class=”xfce-pal-label”>' + escHtml(r.title || r.slug || '') + '</span>'
|
|
591
|
+
+ snippet
|
|
592
|
+
+ '</span>'
|
|
593
|
+
+ '<span class=”xfce-pal-hint-r”>' + escHtml(r.label || r.collection || '') + '</span>'
|
|
488
594
|
+ '</div>';
|
|
489
595
|
}).join('');
|
|
490
596
|
palSetItems(html);
|
|
@@ -494,7 +600,7 @@
|
|
|
494
600
|
|
|
495
601
|
function palBuild() {
|
|
496
602
|
palPrint('triggering build…', 'muted');
|
|
497
|
-
fetch('/api/build', { method: 'POST', credentials: 'include' })
|
|
603
|
+
fetch('/api/build/trigger', { method: 'POST', credentials: 'include' })
|
|
498
604
|
.then(function (r) { return r.json(); })
|
|
499
605
|
.then(function (d) {
|
|
500
606
|
paletteResults.innerHTML = '';
|
|
@@ -973,19 +1079,25 @@
|
|
|
973
1079
|
if (focusedEl) exitFocusMode();
|
|
974
1080
|
}, true);
|
|
975
1081
|
|
|
1082
|
+
// Apply saved dock position
|
|
1083
|
+
var _savedDockPos = localStorage.getItem('orb_dock_pos') || 'bottom';
|
|
1084
|
+
document.documentElement.dataset.dockPos = _savedDockPos;
|
|
1085
|
+
|
|
976
1086
|
// ── Magnification ─────────────────────────────────────────────────
|
|
977
|
-
function applyMag(cx) {
|
|
978
|
-
var
|
|
1087
|
+
function applyMag(cx, cy) {
|
|
1088
|
+
var isLeft = document.documentElement.dataset.dockPos === 'left';
|
|
1089
|
+
var items = dockInner.querySelectorAll('.xfce-dock-item');
|
|
979
1090
|
items.forEach(function (item) {
|
|
980
1091
|
var r = item.getBoundingClientRect();
|
|
981
|
-
var mid = r.left + r.width / 2;
|
|
982
|
-
var
|
|
1092
|
+
var mid = isLeft ? (r.top + r.height / 2) : (r.left + r.width / 2);
|
|
1093
|
+
var pos = isLeft ? cy : cx;
|
|
1094
|
+
var d = Math.abs(pos - mid);
|
|
983
1095
|
var s = d < 80 ? 1 + (1 - d / 80) * 0.50 : 1;
|
|
984
1096
|
item.style.setProperty('--ds', s.toFixed(3));
|
|
985
1097
|
});
|
|
986
1098
|
}
|
|
987
1099
|
|
|
988
|
-
dock.addEventListener('mousemove', function (e) { applyMag(e.clientX); });
|
|
1100
|
+
dock.addEventListener('mousemove', function (e) { applyMag(e.clientX, e.clientY); });
|
|
989
1101
|
dock.addEventListener('mouseleave', function () {
|
|
990
1102
|
dockInner.querySelectorAll('.xfce-dock-item').forEach(function (item) {
|
|
991
1103
|
item.style.setProperty('--ds', '1');
|
|
@@ -1029,12 +1141,12 @@
|
|
|
1029
1141
|
item.appendChild(badge);
|
|
1030
1142
|
}
|
|
1031
1143
|
|
|
1032
|
-
// Hover shows
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
item
|
|
1144
|
+
// Hover shows preview card with recent entries
|
|
1145
|
+
item.addEventListener('mouseenter', function () { showColPreview(col, item); });
|
|
1146
|
+
item.addEventListener('mouseleave', function () { _previewTimer = setTimeout(hideColPreview, 150); });
|
|
1147
|
+
|
|
1148
|
+
// Right-click context menu
|
|
1149
|
+
addDockCtxMenu(item, col);
|
|
1038
1150
|
|
|
1039
1151
|
colGroup.appendChild(item);
|
|
1040
1152
|
|
|
@@ -1094,6 +1206,17 @@
|
|
|
1094
1206
|
}
|
|
1095
1207
|
|
|
1096
1208
|
// ── Keyboard shortcuts ────────────────────────────────────────────────
|
|
1209
|
+
var _gPending = false, _gTimer = null;
|
|
1210
|
+
var G_MAP = { d: '/dashboard.html', m: '/media.html', s: '/settings.html',
|
|
1211
|
+
u: '/users.html', b: '/build.html', i: '/import.html',
|
|
1212
|
+
h: '/schema.html', a: '/account.html' };
|
|
1213
|
+
|
|
1214
|
+
function setGMode(on) {
|
|
1215
|
+
_gPending = on;
|
|
1216
|
+
var ind = document.getElementById('xfce-sb-g-ind');
|
|
1217
|
+
if (ind) ind.style.display = on ? '' : 'none';
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1097
1220
|
function bindKeys() {
|
|
1098
1221
|
// Capture-phase ⌘K: fires before admin-utils.js bubble-phase listener, stops it
|
|
1099
1222
|
document.addEventListener('keydown', function (e) {
|
|
@@ -1116,11 +1239,26 @@
|
|
|
1116
1239
|
// / — open palette (when not typing in an input; Shift+7 on DE keyboard also produces '/')
|
|
1117
1240
|
if (!mod && !e.altKey && e.key === '/' && !isEditing(e.target)) {
|
|
1118
1241
|
e.preventDefault();
|
|
1242
|
+
setGMode(false); clearTimeout(_gTimer);
|
|
1119
1243
|
if (palette && palette.classList.contains('open')) closePalette();
|
|
1120
1244
|
else openPalette();
|
|
1121
1245
|
return;
|
|
1122
1246
|
}
|
|
1123
1247
|
|
|
1248
|
+
// g — vim-style navigation prefix (g d = dashboard, g m = media, …)
|
|
1249
|
+
if (!mod && !e.shiftKey && !e.altKey && e.key === 'g' && !isEditing(e.target)) {
|
|
1250
|
+
e.preventDefault();
|
|
1251
|
+
setGMode(true);
|
|
1252
|
+
clearTimeout(_gTimer);
|
|
1253
|
+
_gTimer = setTimeout(function () { setGMode(false); }, 1500);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
if (_gPending && !isEditing(e.target)) {
|
|
1257
|
+
clearTimeout(_gTimer); setGMode(false);
|
|
1258
|
+
if (G_MAP[e.key]) { e.preventDefault(); location.href = G_MAP[e.key]; }
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1124
1262
|
// ⌘⇧D — toggle HUD
|
|
1125
1263
|
if (mod && e.shiftKey && (e.key === 'd' || e.key === 'D')) {
|
|
1126
1264
|
e.preventDefault();
|
|
@@ -1154,6 +1292,104 @@
|
|
|
1154
1292
|
});
|
|
1155
1293
|
}
|
|
1156
1294
|
|
|
1295
|
+
// ── Build status in status bar ────────────────────────────────────────
|
|
1296
|
+
function loadBuildStatus() {
|
|
1297
|
+
fetch('/api/build/status', { credentials: 'include' })
|
|
1298
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
1299
|
+
.then(function (d) {
|
|
1300
|
+
if (!d || !d.lastTriggered) return;
|
|
1301
|
+
var el2 = document.getElementById('xfce-sb-build');
|
|
1302
|
+
var sep = document.getElementById('xfce-sb-build-sep');
|
|
1303
|
+
if (!el2) return;
|
|
1304
|
+
var dt = new Date(d.lastTriggered.replace(' ', 'T'));
|
|
1305
|
+
var now = new Date();
|
|
1306
|
+
var diffM = Math.floor((now - dt) / 60000);
|
|
1307
|
+
var label = diffM < 1 ? 'built now'
|
|
1308
|
+
: diffM < 60 ? 'built ' + diffM + 'm ago'
|
|
1309
|
+
: diffM < 1440 ? 'built ' + Math.floor(diffM / 60) + 'h ago'
|
|
1310
|
+
: 'built ' + dt.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
|
1311
|
+
el2.textContent = '◉ ' + label;
|
|
1312
|
+
el2.title = 'Last build: ' + d.lastTriggered;
|
|
1313
|
+
if (sep) sep.style.display = '';
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// ── Dock right-click context menu ─────────────────────────────────────
|
|
1318
|
+
var _ctxMenu = null;
|
|
1319
|
+
|
|
1320
|
+
function buildCtxMenu() {
|
|
1321
|
+
_ctxMenu = document.createElement('div');
|
|
1322
|
+
_ctxMenu.className = 'xfce-ctx-menu';
|
|
1323
|
+
_ctxMenu.id = 'xfce-ctx-menu';
|
|
1324
|
+
document.body.appendChild(_ctxMenu);
|
|
1325
|
+
document.addEventListener('click', function (e) {
|
|
1326
|
+
if (_ctxMenu && !_ctxMenu.contains(e.target)) closeCtxMenu();
|
|
1327
|
+
}, true);
|
|
1328
|
+
document.addEventListener('keydown', function (e) {
|
|
1329
|
+
if (e.key === 'Escape') closeCtxMenu();
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
function openCtxMenu(x, y, items) {
|
|
1334
|
+
if (!_ctxMenu) buildCtxMenu();
|
|
1335
|
+
_ctxMenu.innerHTML = items.map(function (it) {
|
|
1336
|
+
return '<button class="xfce-ctx-item" data-href="' + (it.href || '') + '">'
|
|
1337
|
+
+ '<span class="xfce-ctx-icon">' + it.icon + '</span>'
|
|
1338
|
+
+ '<span>' + it.label + '</span>'
|
|
1339
|
+
+ '</button>';
|
|
1340
|
+
}).join('');
|
|
1341
|
+
_ctxMenu.querySelectorAll('.xfce-ctx-item').forEach(function (btn) {
|
|
1342
|
+
btn.addEventListener('click', function () {
|
|
1343
|
+
var href = btn.dataset.href;
|
|
1344
|
+
closeCtxMenu();
|
|
1345
|
+
if (href) location.href = href;
|
|
1346
|
+
});
|
|
1347
|
+
});
|
|
1348
|
+
var vw = window.innerWidth, vh = window.innerHeight;
|
|
1349
|
+
_ctxMenu.style.display = 'block';
|
|
1350
|
+
var w = _ctxMenu.offsetWidth, h = _ctxMenu.offsetHeight;
|
|
1351
|
+
_ctxMenu.style.left = Math.min(x, vw - w - 8) + 'px';
|
|
1352
|
+
_ctxMenu.style.top = Math.min(y, vh - h - 8) + 'px';
|
|
1353
|
+
_ctxMenu.classList.add('open');
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
function closeCtxMenu() {
|
|
1357
|
+
if (!_ctxMenu) return;
|
|
1358
|
+
_ctxMenu.classList.remove('open');
|
|
1359
|
+
setTimeout(function () { if (_ctxMenu) _ctxMenu.style.display = 'none'; }, 120);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
function addDockCtxMenu(item, col) {
|
|
1363
|
+
item.addEventListener('contextmenu', function (e) {
|
|
1364
|
+
e.preventDefault();
|
|
1365
|
+
var entriesHref = col.singleton
|
|
1366
|
+
? '/editor.html?collection=' + encodeURIComponent(col.id) + '&singleton=1'
|
|
1367
|
+
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
1368
|
+
var newHref = '/editor.html?collection=' + encodeURIComponent(col.id);
|
|
1369
|
+
openCtxMenu(e.clientX, e.clientY, [
|
|
1370
|
+
{ icon: '◫', label: 'View entries', href: entriesHref },
|
|
1371
|
+
{ icon: '+', label: 'New entry', href: newHref },
|
|
1372
|
+
{ icon: '↓', label: 'Export JSON', href: '' },
|
|
1373
|
+
]);
|
|
1374
|
+
// Wire export separately (needs fetch, not href)
|
|
1375
|
+
var exportBtn = _ctxMenu.querySelectorAll('.xfce-ctx-item')[2];
|
|
1376
|
+
if (exportBtn) {
|
|
1377
|
+
exportBtn.addEventListener('click', function (ev) {
|
|
1378
|
+
ev.stopImmediatePropagation();
|
|
1379
|
+
closeCtxMenu();
|
|
1380
|
+
fetch('/api/terminal/export?col=' + encodeURIComponent(col.id) + '&format=json&drafts=0', { credentials: 'include' })
|
|
1381
|
+
.then(function (r) { return r.blob(); })
|
|
1382
|
+
.then(function (blob) {
|
|
1383
|
+
var a = document.createElement('a');
|
|
1384
|
+
a.href = URL.createObjectURL(blob);
|
|
1385
|
+
a.download = col.id + '.json';
|
|
1386
|
+
document.body.appendChild(a); a.click(); a.remove();
|
|
1387
|
+
});
|
|
1388
|
+
}, { once: true });
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1157
1393
|
// ── Init ──────────────────────────────────────────────────────────────
|
|
1158
1394
|
function init() {
|
|
1159
1395
|
buildStatusBar();
|
|
@@ -1161,6 +1397,7 @@
|
|
|
1161
1397
|
buildDock();
|
|
1162
1398
|
buildToastHost();
|
|
1163
1399
|
loadInfo();
|
|
1400
|
+
loadBuildStatus();
|
|
1164
1401
|
bindKeys();
|
|
1165
1402
|
initFocusMode();
|
|
1166
1403
|
observeSavedFlash();
|
package/src/routes/search.js
CHANGED
|
@@ -29,6 +29,16 @@ searchRoutes.get('/recent', (c) => {
|
|
|
29
29
|
return c.json(results);
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
function makeSnippet(body, q, maxLen) {
|
|
33
|
+
if (!body) return '';
|
|
34
|
+
const lower = body.toLowerCase();
|
|
35
|
+
const idx = lower.indexOf(q);
|
|
36
|
+
if (idx < 0) return body.slice(0, maxLen) + (body.length > maxLen ? '…' : '');
|
|
37
|
+
const start = Math.max(0, idx - 30);
|
|
38
|
+
const end = Math.min(body.length, idx + q.length + 60);
|
|
39
|
+
return (start > 0 ? '…' : '') + body.slice(start, end).trim() + (end < body.length ? '…' : '');
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
// GET /api/search?q=
|
|
33
43
|
searchRoutes.get('/', (c) => {
|
|
34
44
|
const q = (c.req.query('q') ?? '').trim().toLowerCase();
|
|
@@ -42,8 +52,8 @@ searchRoutes.get('/', (c) => {
|
|
|
42
52
|
const entries = db.getEntries(col.id);
|
|
43
53
|
for (const entry of entries) {
|
|
44
54
|
const title = (entry.data?.title ?? entry.slug ?? '').toLowerCase();
|
|
45
|
-
const body =
|
|
46
|
-
if (title.includes(q) || body.includes(q) || entry.slug.includes(q)) {
|
|
55
|
+
const body = entry.data?.body ?? '';
|
|
56
|
+
if (title.includes(q) || body.toLowerCase().includes(q) || entry.slug.includes(q)) {
|
|
47
57
|
results.push({
|
|
48
58
|
type: 'entry',
|
|
49
59
|
collection: col.id,
|
|
@@ -51,6 +61,7 @@ searchRoutes.get('/', (c) => {
|
|
|
51
61
|
slug: entry.slug,
|
|
52
62
|
title: entry.data?.title ?? entry.slug,
|
|
53
63
|
status: entry.status,
|
|
64
|
+
snippet: makeSnippet(body, q, 100),
|
|
54
65
|
});
|
|
55
66
|
if (results.length >= 20) break;
|
|
56
67
|
}
|