@a83/orbiter-admin 0.3.40 → 0.3.42
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 +81 -30
- package/public/xfce.js +173 -114
package/package.json
CHANGED
package/public/style.css
CHANGED
|
@@ -2028,23 +2028,52 @@ 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-actions {
|
|
2067
|
+
display: flex; border-top: 1px solid var(--line);
|
|
2068
|
+
}
|
|
2069
|
+
.xfce-preview-action {
|
|
2070
|
+
flex: 1; padding: 6px 8px; font-size: 10px; font-family: var(--mono);
|
|
2071
|
+
color: var(--accent); text-decoration: none; text-align: center;
|
|
2072
|
+
background: none; border: none; cursor: pointer;
|
|
2073
|
+
transition: background .1s; white-space: nowrap;
|
|
2074
|
+
}
|
|
2075
|
+
.xfce-preview-action + .xfce-preview-action { border-left: 1px solid var(--line); }
|
|
2076
|
+
.xfce-preview-action:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
2048
2077
|
|
|
2049
2078
|
/* ── Command Palette ─────────────────────────────────────── */
|
|
2050
2079
|
.xfce-palette {
|
|
@@ -2123,27 +2152,49 @@ a.xfce-sb-logo:hover { opacity: .8; }
|
|
|
2123
2152
|
|
|
2124
2153
|
/* ── Status bar build indicator ──────────────────────────── */
|
|
2125
2154
|
.xfce-sb-build { font-size: 9px; font-family: var(--mono); color: var(--muted); white-space: nowrap; }
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2155
|
+
.xfce-sb-g-ind {
|
|
2156
|
+
font-size: 10px; font-family: var(--mono); font-weight: 700; white-space: nowrap;
|
|
2157
|
+
color: var(--bg1); background: var(--accent);
|
|
2158
|
+
border-radius: 4px; padding: 1px 6px; letter-spacing: .04em;
|
|
2159
|
+
animation: xfce-blink .6s step-end infinite;
|
|
2160
|
+
}
|
|
2161
|
+
@keyframes xfce-blink { 0%,100% { opacity:1; } 50% { opacity:.4; } }
|
|
2162
|
+
|
|
2163
|
+
/* ── Left dock mode ───────────────────────────────────────── */
|
|
2164
|
+
html[data-style="xfce"][data-dock-pos="left"] .main {
|
|
2165
|
+
padding-bottom: 20px !important;
|
|
2166
|
+
padding-left: 100px !important;
|
|
2167
|
+
}
|
|
2168
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock {
|
|
2169
|
+
bottom: auto; left: 16px; top: 50%;
|
|
2170
|
+
transform: translateY(-50%);
|
|
2171
|
+
padding: 10px 6px;
|
|
2172
|
+
align-items: center;
|
|
2137
2173
|
}
|
|
2138
|
-
.xfce-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2174
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-inner {
|
|
2175
|
+
flex-direction: column; align-items: center; gap: 4px;
|
|
2176
|
+
}
|
|
2177
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-group {
|
|
2178
|
+
flex-direction: column; align-items: center; gap: 4px;
|
|
2179
|
+
}
|
|
2180
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-sep {
|
|
2181
|
+
width: 28px; height: 1px; margin: 2px 0;
|
|
2182
|
+
}
|
|
2183
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-dock-item {
|
|
2184
|
+
transform-origin: left center;
|
|
2185
|
+
}
|
|
2186
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-toast-host {
|
|
2187
|
+
bottom: 20px; left: 110px; transform: none;
|
|
2188
|
+
}
|
|
2189
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-palette {
|
|
2190
|
+
padding-bottom: 20px; padding-left: 100px; align-items: center;
|
|
2191
|
+
}
|
|
2192
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-tools-popup {
|
|
2193
|
+
bottom: auto;
|
|
2194
|
+
}
|
|
2195
|
+
html[data-style="xfce"][data-dock-pos="left"] .xfce-ws-overlay {
|
|
2196
|
+
bottom: auto; left: 110px; top: 50%; transform: translateY(-50%);
|
|
2144
2197
|
}
|
|
2145
|
-
.xfce-ctx-item:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--heading); }
|
|
2146
|
-
.xfce-ctx-icon { width: 16px; text-align: center; color: var(--accent); flex-shrink: 0; }
|
|
2147
2198
|
|
|
2148
2199
|
/* ── Toast host (above dock) ─────────────────────────────── */
|
|
2149
2200
|
.xfce-toast-host {
|
package/public/xfce.js
CHANGED
|
@@ -63,6 +63,7 @@
|
|
|
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 mode: d=dashboard m=media s=settings u=users b=build i=import h=schema a=account">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>',
|
|
68
69
|
'<span id="xfce-sb-build" class="xfce-sb-build" title="Last build"></span>',
|
|
@@ -168,6 +169,30 @@
|
|
|
168
169
|
openPalette();
|
|
169
170
|
});
|
|
170
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);
|
|
171
196
|
document.body.appendChild(toolsPopup);
|
|
172
197
|
document.addEventListener('click', function () {
|
|
173
198
|
toolsPopup.classList.remove('open');
|
|
@@ -179,40 +204,123 @@
|
|
|
179
204
|
|
|
180
205
|
function toggleToolsPopup() {
|
|
181
206
|
if (!toolsPopup) buildToolsPopup();
|
|
182
|
-
var btn
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
207
|
+
var btn = document.getElementById('xfce-tools-btn');
|
|
208
|
+
var dock = document.getElementById('xfce-dock');
|
|
209
|
+
var isLeft = document.documentElement.dataset.dockPos === 'left';
|
|
210
|
+
if (btn && dock) {
|
|
211
|
+
var bRect = btn.getBoundingClientRect();
|
|
212
|
+
var dRect = dock.getBoundingClientRect();
|
|
213
|
+
if (isLeft) {
|
|
214
|
+
toolsPopup.style.left = Math.round(dRect.right + 10) + 'px';
|
|
215
|
+
toolsPopup.style.top = Math.round(bRect.top + bRect.height / 2) + 'px';
|
|
216
|
+
toolsPopup.style.bottom = 'auto';
|
|
217
|
+
toolsPopup.style.transform = 'translateY(-50%)';
|
|
218
|
+
} else {
|
|
219
|
+
toolsPopup.style.left = Math.round(bRect.left + bRect.width / 2) + 'px';
|
|
220
|
+
toolsPopup.style.top = '';
|
|
221
|
+
toolsPopup.style.bottom = '';
|
|
222
|
+
toolsPopup.style.transform = '';
|
|
223
|
+
}
|
|
186
224
|
}
|
|
187
225
|
toolsPopup.classList.toggle('open');
|
|
188
226
|
}
|
|
189
227
|
|
|
190
|
-
// ── Hover
|
|
191
|
-
var
|
|
228
|
+
// ── Hover preview card above collection items ────────────────────────
|
|
229
|
+
var _previewEl = null, _previewTimer = null, _previewCache = {};
|
|
192
230
|
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
document.body.appendChild(colCreateEl);
|
|
231
|
+
function buildPreview() {
|
|
232
|
+
_previewEl = el('div', 'xfce-col-preview');
|
|
233
|
+
_previewEl.id = 'xfce-col-preview';
|
|
234
|
+
_previewEl.addEventListener('mouseenter', function () { clearTimeout(_previewTimer); });
|
|
235
|
+
_previewEl.addEventListener('mouseleave', function () { _previewTimer = setTimeout(hideColPreview, 150); });
|
|
236
|
+
document.body.appendChild(_previewEl);
|
|
200
237
|
}
|
|
201
238
|
|
|
202
|
-
function
|
|
203
|
-
if (!
|
|
204
|
-
clearTimeout(
|
|
205
|
-
|
|
206
|
-
var dockTop = dock ? dock.getBoundingClientRect().top : 0;
|
|
207
|
-
var itemRect = itemEl.getBoundingClientRect();
|
|
208
|
-
colCreateEl.href = href;
|
|
209
|
-
colCreateEl.style.left = Math.round(itemRect.left + itemRect.width / 2) + 'px';
|
|
210
|
-
colCreateEl.style.top = Math.round(dockTop - 34) + 'px';
|
|
211
|
-
colCreateEl.classList.add('visible');
|
|
239
|
+
function showColPreview(col, itemEl) {
|
|
240
|
+
if (!_previewEl) buildPreview();
|
|
241
|
+
clearTimeout(_previewTimer);
|
|
242
|
+
_previewTimer = setTimeout(function () { _renderPreview(col, itemEl); }, 280);
|
|
212
243
|
}
|
|
213
244
|
|
|
214
|
-
function
|
|
215
|
-
|
|
245
|
+
function _renderPreview(col, itemEl) {
|
|
246
|
+
var dock = document.getElementById('xfce-dock');
|
|
247
|
+
var isLeft = document.documentElement.dataset.dockPos === 'left';
|
|
248
|
+
var dockR = dock ? dock.getBoundingClientRect() : { top: 0, right: 0 };
|
|
249
|
+
var itemR = itemEl.getBoundingClientRect();
|
|
250
|
+
|
|
251
|
+
function place() {
|
|
252
|
+
if (isLeft) {
|
|
253
|
+
_previewEl.style.left = Math.round(dockR.right + 10) + 'px';
|
|
254
|
+
_previewEl.style.top = Math.round(itemR.top + itemR.height / 2 - _previewEl.offsetHeight / 2) + 'px';
|
|
255
|
+
_previewEl.style.bottom = 'auto';
|
|
256
|
+
} else {
|
|
257
|
+
var cx = Math.round(itemR.left + itemR.width / 2);
|
|
258
|
+
_previewEl.style.left = cx + 'px';
|
|
259
|
+
_previewEl.style.bottom = Math.round(window.innerHeight - dockR.top + 10) + 'px';
|
|
260
|
+
_previewEl.style.top = 'auto';
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
var newHref = '/editor.html?collection=' + encodeURIComponent(col.id);
|
|
265
|
+
var entriesHref = col.singleton
|
|
266
|
+
? newHref + '&singleton=1'
|
|
267
|
+
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
268
|
+
|
|
269
|
+
function render(entries) {
|
|
270
|
+
var rows = entries.length
|
|
271
|
+
? entries.slice(0, 3).map(function (e) {
|
|
272
|
+
var slug = e.slug || e.id || '';
|
|
273
|
+
var date = (e.updated_at || e.created_at || '').substring(5, 10);
|
|
274
|
+
var href = '/editor.html?collection=' + encodeURIComponent(col.id) + '&slug=' + encodeURIComponent(slug);
|
|
275
|
+
return '<a class="xfce-preview-row" href="' + href + '">'
|
|
276
|
+
+ '<span class="xfce-preview-slug">' + escHtml(slug) + '</span>'
|
|
277
|
+
+ '<span class="xfce-preview-date">' + date + '</span>'
|
|
278
|
+
+ '</a>';
|
|
279
|
+
}).join('')
|
|
280
|
+
: '<div class="xfce-preview-empty">no entries yet</div>';
|
|
281
|
+
_previewEl.innerHTML =
|
|
282
|
+
'<div class="xfce-preview-head"><a href="' + entriesHref + '">' + escHtml(col.label) + '</a></div>'
|
|
283
|
+
+ '<div class="xfce-preview-entries">' + rows + '</div>'
|
|
284
|
+
+ '<div class="xfce-preview-actions">'
|
|
285
|
+
+ '<a class="xfce-preview-action" href="' + newHref + '">+ new entry</a>'
|
|
286
|
+
+ '<a class="xfce-preview-action" href="' + entriesHref + '">◫ view all</a>'
|
|
287
|
+
+ '<button class="xfce-preview-action xfce-preview-export" data-col="' + escHtml(col.id) + '">↓ export</button>'
|
|
288
|
+
+ '</div>';
|
|
289
|
+
var expBtn = _previewEl.querySelector('.xfce-preview-export');
|
|
290
|
+
if (expBtn) expBtn.addEventListener('click', function (ev) {
|
|
291
|
+
ev.preventDefault();
|
|
292
|
+
fetch('/api/terminal/export?col=' + encodeURIComponent(col.id) + '&format=json&drafts=0', { credentials: 'include' })
|
|
293
|
+
.then(function (r) { return r.blob(); })
|
|
294
|
+
.then(function (blob) {
|
|
295
|
+
var a = document.createElement('a');
|
|
296
|
+
a.href = URL.createObjectURL(blob); a.download = col.id + '.json';
|
|
297
|
+
document.body.appendChild(a); a.click(); a.remove();
|
|
298
|
+
});
|
|
299
|
+
hideColPreview();
|
|
300
|
+
});
|
|
301
|
+
_previewEl.classList.add('visible');
|
|
302
|
+
place();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (_previewCache[col.id]) {
|
|
306
|
+
render(_previewCache[col.id]);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
_previewEl.innerHTML = '<div class="xfce-preview-loading">…</div>';
|
|
310
|
+
_previewEl.classList.add('visible');
|
|
311
|
+
place();
|
|
312
|
+
fetch('/api/collections/' + encodeURIComponent(col.id) + '/entries?limit=3', { credentials: 'include' })
|
|
313
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
314
|
+
.then(function (d) {
|
|
315
|
+
var entries = d ? (d.entries || (Array.isArray(d) ? d : [])) : [];
|
|
316
|
+
_previewCache[col.id] = entries;
|
|
317
|
+
if (_previewEl.classList.contains('visible')) render(entries);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function hideColPreview() {
|
|
322
|
+
clearTimeout(_previewTimer);
|
|
323
|
+
if (_previewEl) _previewEl.classList.remove('visible');
|
|
216
324
|
}
|
|
217
325
|
|
|
218
326
|
// ── Command Palette ───────────────────────────────────────────────────
|
|
@@ -1000,19 +1108,25 @@
|
|
|
1000
1108
|
if (focusedEl) exitFocusMode();
|
|
1001
1109
|
}, true);
|
|
1002
1110
|
|
|
1111
|
+
// Apply saved dock position
|
|
1112
|
+
var _savedDockPos = localStorage.getItem('orb_dock_pos') || 'bottom';
|
|
1113
|
+
document.documentElement.dataset.dockPos = _savedDockPos;
|
|
1114
|
+
|
|
1003
1115
|
// ── Magnification ─────────────────────────────────────────────────
|
|
1004
|
-
function applyMag(cx) {
|
|
1005
|
-
var
|
|
1116
|
+
function applyMag(cx, cy) {
|
|
1117
|
+
var isLeft = document.documentElement.dataset.dockPos === 'left';
|
|
1118
|
+
var items = dockInner.querySelectorAll('.xfce-dock-item');
|
|
1006
1119
|
items.forEach(function (item) {
|
|
1007
1120
|
var r = item.getBoundingClientRect();
|
|
1008
|
-
var mid = r.left + r.width / 2;
|
|
1009
|
-
var
|
|
1121
|
+
var mid = isLeft ? (r.top + r.height / 2) : (r.left + r.width / 2);
|
|
1122
|
+
var pos = isLeft ? cy : cx;
|
|
1123
|
+
var d = Math.abs(pos - mid);
|
|
1010
1124
|
var s = d < 80 ? 1 + (1 - d / 80) * 0.50 : 1;
|
|
1011
1125
|
item.style.setProperty('--ds', s.toFixed(3));
|
|
1012
1126
|
});
|
|
1013
1127
|
}
|
|
1014
1128
|
|
|
1015
|
-
dock.addEventListener('mousemove', function (e) { applyMag(e.clientX); });
|
|
1129
|
+
dock.addEventListener('mousemove', function (e) { applyMag(e.clientX, e.clientY); });
|
|
1016
1130
|
dock.addEventListener('mouseleave', function () {
|
|
1017
1131
|
dockInner.querySelectorAll('.xfce-dock-item').forEach(function (item) {
|
|
1018
1132
|
item.style.setProperty('--ds', '1');
|
|
@@ -1056,15 +1170,9 @@
|
|
|
1056
1170
|
item.appendChild(badge);
|
|
1057
1171
|
}
|
|
1058
1172
|
|
|
1059
|
-
// Hover shows
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
: '/editor.html?collection=' + encodeURIComponent(col.id);
|
|
1063
|
-
item.addEventListener('mouseenter', function () { showColCreate(createHref, item); });
|
|
1064
|
-
item.addEventListener('mouseleave', function () { colCreateTimer = setTimeout(hideColCreate, 120); });
|
|
1065
|
-
|
|
1066
|
-
// Right-click context menu
|
|
1067
|
-
addDockCtxMenu(item, col);
|
|
1173
|
+
// Hover shows preview card with recent entries
|
|
1174
|
+
item.addEventListener('mouseenter', function () { showColPreview(col, item); });
|
|
1175
|
+
item.addEventListener('mouseleave', function () { _previewTimer = setTimeout(hideColPreview, 150); });
|
|
1068
1176
|
|
|
1069
1177
|
colGroup.appendChild(item);
|
|
1070
1178
|
|
|
@@ -1124,6 +1232,17 @@
|
|
|
1124
1232
|
}
|
|
1125
1233
|
|
|
1126
1234
|
// ── Keyboard shortcuts ────────────────────────────────────────────────
|
|
1235
|
+
var _gPending = false, _gTimer = null;
|
|
1236
|
+
var G_MAP = { d: '/dashboard.html', m: '/media.html', s: '/settings.html',
|
|
1237
|
+
u: '/users.html', b: '/build.html', i: '/import.html',
|
|
1238
|
+
h: '/schema.html', a: '/account.html' };
|
|
1239
|
+
|
|
1240
|
+
function setGMode(on) {
|
|
1241
|
+
_gPending = on;
|
|
1242
|
+
var ind = document.getElementById('xfce-sb-g-ind');
|
|
1243
|
+
if (ind) ind.style.display = on ? '' : 'none';
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1127
1246
|
function bindKeys() {
|
|
1128
1247
|
// Capture-phase ⌘K: fires before admin-utils.js bubble-phase listener, stops it
|
|
1129
1248
|
document.addEventListener('keydown', function (e) {
|
|
@@ -1146,11 +1265,26 @@
|
|
|
1146
1265
|
// / — open palette (when not typing in an input; Shift+7 on DE keyboard also produces '/')
|
|
1147
1266
|
if (!mod && !e.altKey && e.key === '/' && !isEditing(e.target)) {
|
|
1148
1267
|
e.preventDefault();
|
|
1268
|
+
setGMode(false); clearTimeout(_gTimer);
|
|
1149
1269
|
if (palette && palette.classList.contains('open')) closePalette();
|
|
1150
1270
|
else openPalette();
|
|
1151
1271
|
return;
|
|
1152
1272
|
}
|
|
1153
1273
|
|
|
1274
|
+
// g — vim-style navigation prefix (g d = dashboard, g m = media, …)
|
|
1275
|
+
if (!mod && !e.shiftKey && !e.altKey && e.key === 'g' && !isEditing(e.target)) {
|
|
1276
|
+
e.preventDefault();
|
|
1277
|
+
setGMode(true);
|
|
1278
|
+
clearTimeout(_gTimer);
|
|
1279
|
+
_gTimer = setTimeout(function () { setGMode(false); }, 1500);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (_gPending && !isEditing(e.target)) {
|
|
1283
|
+
clearTimeout(_gTimer); setGMode(false);
|
|
1284
|
+
if (G_MAP[e.key]) { e.preventDefault(); location.href = G_MAP[e.key]; }
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1154
1288
|
// ⌘⇧D — toggle HUD
|
|
1155
1289
|
if (mod && e.shiftKey && (e.key === 'd' || e.key === 'D')) {
|
|
1156
1290
|
e.preventDefault();
|
|
@@ -1206,81 +1340,6 @@
|
|
|
1206
1340
|
});
|
|
1207
1341
|
}
|
|
1208
1342
|
|
|
1209
|
-
// ── Dock right-click context menu ─────────────────────────────────────
|
|
1210
|
-
var _ctxMenu = null;
|
|
1211
|
-
|
|
1212
|
-
function buildCtxMenu() {
|
|
1213
|
-
_ctxMenu = document.createElement('div');
|
|
1214
|
-
_ctxMenu.className = 'xfce-ctx-menu';
|
|
1215
|
-
_ctxMenu.id = 'xfce-ctx-menu';
|
|
1216
|
-
document.body.appendChild(_ctxMenu);
|
|
1217
|
-
document.addEventListener('click', function (e) {
|
|
1218
|
-
if (_ctxMenu && !_ctxMenu.contains(e.target)) closeCtxMenu();
|
|
1219
|
-
}, true);
|
|
1220
|
-
document.addEventListener('keydown', function (e) {
|
|
1221
|
-
if (e.key === 'Escape') closeCtxMenu();
|
|
1222
|
-
});
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
function openCtxMenu(x, y, items) {
|
|
1226
|
-
if (!_ctxMenu) buildCtxMenu();
|
|
1227
|
-
_ctxMenu.innerHTML = items.map(function (it) {
|
|
1228
|
-
return '<button class="xfce-ctx-item" data-href="' + (it.href || '') + '">'
|
|
1229
|
-
+ '<span class="xfce-ctx-icon">' + it.icon + '</span>'
|
|
1230
|
-
+ '<span>' + it.label + '</span>'
|
|
1231
|
-
+ '</button>';
|
|
1232
|
-
}).join('');
|
|
1233
|
-
_ctxMenu.querySelectorAll('.xfce-ctx-item').forEach(function (btn) {
|
|
1234
|
-
btn.addEventListener('click', function () {
|
|
1235
|
-
var href = btn.dataset.href;
|
|
1236
|
-
closeCtxMenu();
|
|
1237
|
-
if (href) location.href = href;
|
|
1238
|
-
});
|
|
1239
|
-
});
|
|
1240
|
-
var vw = window.innerWidth, vh = window.innerHeight;
|
|
1241
|
-
_ctxMenu.style.display = 'block';
|
|
1242
|
-
var w = _ctxMenu.offsetWidth, h = _ctxMenu.offsetHeight;
|
|
1243
|
-
_ctxMenu.style.left = Math.min(x, vw - w - 8) + 'px';
|
|
1244
|
-
_ctxMenu.style.top = Math.min(y, vh - h - 8) + 'px';
|
|
1245
|
-
_ctxMenu.classList.add('open');
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
function closeCtxMenu() {
|
|
1249
|
-
if (!_ctxMenu) return;
|
|
1250
|
-
_ctxMenu.classList.remove('open');
|
|
1251
|
-
setTimeout(function () { if (_ctxMenu) _ctxMenu.style.display = 'none'; }, 120);
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
function addDockCtxMenu(item, col) {
|
|
1255
|
-
item.addEventListener('contextmenu', function (e) {
|
|
1256
|
-
e.preventDefault();
|
|
1257
|
-
var entriesHref = col.singleton
|
|
1258
|
-
? '/editor.html?collection=' + encodeURIComponent(col.id) + '&singleton=1'
|
|
1259
|
-
: '/entries.html?col=' + encodeURIComponent(col.id) + '&label=' + encodeURIComponent(col.label);
|
|
1260
|
-
var newHref = '/editor.html?collection=' + encodeURIComponent(col.id);
|
|
1261
|
-
openCtxMenu(e.clientX, e.clientY, [
|
|
1262
|
-
{ icon: '◫', label: 'View entries', href: entriesHref },
|
|
1263
|
-
{ icon: '+', label: 'New entry', href: newHref },
|
|
1264
|
-
{ icon: '↓', label: 'Export JSON', href: '' },
|
|
1265
|
-
]);
|
|
1266
|
-
// Wire export separately (needs fetch, not href)
|
|
1267
|
-
var exportBtn = _ctxMenu.querySelectorAll('.xfce-ctx-item')[2];
|
|
1268
|
-
if (exportBtn) {
|
|
1269
|
-
exportBtn.addEventListener('click', function (ev) {
|
|
1270
|
-
ev.stopImmediatePropagation();
|
|
1271
|
-
closeCtxMenu();
|
|
1272
|
-
fetch('/api/terminal/export?col=' + encodeURIComponent(col.id) + '&format=json&drafts=0', { credentials: 'include' })
|
|
1273
|
-
.then(function (r) { return r.blob(); })
|
|
1274
|
-
.then(function (blob) {
|
|
1275
|
-
var a = document.createElement('a');
|
|
1276
|
-
a.href = URL.createObjectURL(blob);
|
|
1277
|
-
a.download = col.id + '.json';
|
|
1278
|
-
document.body.appendChild(a); a.click(); a.remove();
|
|
1279
|
-
});
|
|
1280
|
-
}, { once: true });
|
|
1281
|
-
}
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
1343
|
|
|
1285
1344
|
// ── Init ──────────────────────────────────────────────────────────────
|
|
1286
1345
|
function init() {
|