mnfst 0.5.217 → 0.5.219
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/lib/manifest.appwrite.data.js +1 -1
- package/lib/manifest.code.js +120 -46
- package/lib/manifest.css +83 -222
- package/lib/manifest.d.ts +2 -0
- package/lib/manifest.data.js +26 -3
- package/lib/manifest.defer.js +145 -34
- package/lib/manifest.edit.css +63 -27
- package/lib/manifest.edit.js +213 -28
- package/lib/manifest.integrity.json +11 -11
- package/lib/manifest.js +43 -2
- package/lib/manifest.markdown.js +11 -7
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.router.js +58 -69
- package/lib/manifest.sw.js +1 -1
- package/lib/manifest.text.edit.css +20 -0
- package/lib/manifest.text.edit.js +151 -25
- package/lib/manifest.utilities.js +246 -100
- package/lib/manifest.utilities.node.mjs +195 -35
- package/package.json +1 -1
package/lib/manifest.data.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
4
|
|
|
5
|
-
const MANIFEST_BUILD_VERSION = '0.5.
|
|
5
|
+
const MANIFEST_BUILD_VERSION = '0.5.219';
|
|
6
6
|
|
|
7
7
|
/* Manifest Data Sources - Configuration */
|
|
8
8
|
|
|
@@ -1326,6 +1326,13 @@ function setupLocaleChangeListener() {
|
|
|
1326
1326
|
store._localeChanging = false;
|
|
1327
1327
|
bumpAllVersions();
|
|
1328
1328
|
}
|
|
1329
|
+
} finally {
|
|
1330
|
+
// Early exits (no manifest.data) must not strand the render-ready gate
|
|
1331
|
+
const store = Alpine.store('data');
|
|
1332
|
+
if (store?._localeChanging) {
|
|
1333
|
+
store._localeChanging = false;
|
|
1334
|
+
checkAndDispatchRenderReady();
|
|
1335
|
+
}
|
|
1329
1336
|
}
|
|
1330
1337
|
});
|
|
1331
1338
|
}
|
|
@@ -11340,7 +11347,7 @@ function registerFilesDirective() {
|
|
|
11340
11347
|
// sending a broken/empty-equal query. Retries once when auth settles further;
|
|
11341
11348
|
// dedup by key so a source pending across repeated loads doesn't stack listeners.
|
|
11342
11349
|
const pendingAuthRetries = new Set();
|
|
11343
|
-
const AUTH_RETRY_EVENTS = ['manifest:auth:initialized', 'manifest:auth:teams-loaded', 'manifest:auth:login'];
|
|
11350
|
+
const AUTH_RETRY_EVENTS = ['manifest:auth:initialized', 'manifest:auth:teams-loaded', 'manifest:auth:login', 'manifest:auth:anonymous'];
|
|
11344
11351
|
function scheduleAuthRetry(dataSourceName, locale) {
|
|
11345
11352
|
if (typeof window === 'undefined') return;
|
|
11346
11353
|
const key = `${dataSourceName}:${locale}`;
|
|
@@ -11354,6 +11361,13 @@ function scheduleAuthRetry(dataSourceName, locale) {
|
|
|
11354
11361
|
AUTH_RETRY_EVENTS.forEach(type => window.addEventListener(type, retry));
|
|
11355
11362
|
}
|
|
11356
11363
|
|
|
11364
|
+
// Auth settled with no identity (or no auth plugin): a scoped read can't resolve until sign-in
|
|
11365
|
+
function authSettledSignedOut() {
|
|
11366
|
+
const auth = typeof Alpine !== 'undefined' ? Alpine.store('auth') : null;
|
|
11367
|
+
if (!auth) return true;
|
|
11368
|
+
return auth._initialized === true && auth.isAuthenticated !== true;
|
|
11369
|
+
}
|
|
11370
|
+
|
|
11357
11371
|
// Client-side scope filter: Appwrite returns all accessible files, so narrow to
|
|
11358
11372
|
// the current team to match database team-scope behavior.
|
|
11359
11373
|
async function filterFilesByScope(files, scope) {
|
|
@@ -11857,6 +11871,13 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
|
|
|
11857
11871
|
const generation = sourceGeneration ? sourceGeneration(dataSourceName) : 0;
|
|
11858
11872
|
const superseded = () => sourceGeneration && sourceGeneration(dataSourceName) !== generation;
|
|
11859
11873
|
|
|
11874
|
+
// Signed out: settle empty (not $loading forever); sign-in reloads via the auth retry
|
|
11875
|
+
const settleSignedOut = () => {
|
|
11876
|
+
if (isInitializing || superseded() || !authSettledSignedOut()) return false;
|
|
11877
|
+
updateStore(dataSourceName, [], { loading: false, error: null, ready: true });
|
|
11878
|
+
return true;
|
|
11879
|
+
};
|
|
11880
|
+
|
|
11860
11881
|
return runDeduped(cacheKey, async () => {
|
|
11861
11882
|
let landed = false;
|
|
11862
11883
|
try {
|
|
@@ -11933,9 +11954,10 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
|
|
|
11933
11954
|
? await window.ManifestDataQueries.buildAppwriteQueries(queriesConfig.default || queriesConfig, scope, scopeColumns)
|
|
11934
11955
|
: await window.ManifestDataQueries.buildAppwriteQueries([], scope, scopeColumns);
|
|
11935
11956
|
|
|
11936
|
-
// Not ready (auth/scope unresolved): skip this read,
|
|
11957
|
+
// Not ready (auth/scope unresolved): skip this read, retry on an auth event
|
|
11937
11958
|
if (queries === null) {
|
|
11938
11959
|
scheduleAuthRetry(dataSourceName, locale);
|
|
11960
|
+
if (settleSignedOut()) landed = true;
|
|
11939
11961
|
return null;
|
|
11940
11962
|
}
|
|
11941
11963
|
|
|
@@ -11974,6 +11996,7 @@ async function loadDataSource(dataSourceName, locale = 'en', options = {}) {
|
|
|
11974
11996
|
// Not ready (a $auth. arg in queriesConfig unresolved): skip, retry on an auth event
|
|
11975
11997
|
if (queries === null) {
|
|
11976
11998
|
scheduleAuthRetry(dataSourceName, locale);
|
|
11999
|
+
if (settleSignedOut()) landed = true;
|
|
11977
12000
|
return null;
|
|
11978
12001
|
}
|
|
11979
12002
|
|
package/lib/manifest.defer.js
CHANGED
|
@@ -135,7 +135,7 @@ function register(el, rule, opts) {
|
|
|
135
135
|
};
|
|
136
136
|
wire(rec);
|
|
137
137
|
if (rule === 'route') routes.add(rec);
|
|
138
|
-
else if (!rec.lazy) {
|
|
138
|
+
else if (!rec.lazy) { addPending(rec); if (!el.closest('[x-route][hidden]')) track(rec); schedule(); }
|
|
139
139
|
return rec;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -143,7 +143,7 @@ function render(rec, viaPrewarm) {
|
|
|
143
143
|
if (rec.rendered) { if (!viaPrewarm && warm.delete(rec)) schedule(); return; }
|
|
144
144
|
rec.rendered = true;
|
|
145
145
|
pending.delete(rec);
|
|
146
|
-
if (viaPrewarm) warm.add(rec); else warm.delete(rec);
|
|
146
|
+
if (viaPrewarm) warm.add(rec); else { warm.delete(rec); untrack(rec); }
|
|
147
147
|
const { el, tpl } = rec;
|
|
148
148
|
const content = rec.discard ? tpl.content.cloneNode(true) : tpl.content;
|
|
149
149
|
Alpine.mutateDom(() => {
|
|
@@ -165,11 +165,12 @@ function teardown(rec) {
|
|
|
165
165
|
});
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
// Evict a warm, still-closed, never-opened container back to its stash (least reachable first)
|
|
168
|
+
// Evict a warm, still-closed, never-opened container back to its stash (non-urgent, least reachable first)
|
|
169
169
|
function restash(rec) {
|
|
170
170
|
if (!rec.rendered) return;
|
|
171
171
|
rec.rendered = false;
|
|
172
172
|
rec.near = undefined;
|
|
173
|
+
rec.urgent = false; // evicted: back to plain prewarm order, or it re-renders and evicts forever
|
|
173
174
|
const { el, tpl } = rec;
|
|
174
175
|
Alpine.mutateDom(() => {
|
|
175
176
|
Array.from(el.children).forEach((child) => Alpine.destroyTree(child));
|
|
@@ -177,7 +178,7 @@ function restash(rec) {
|
|
|
177
178
|
el.appendChild(tpl);
|
|
178
179
|
});
|
|
179
180
|
warm.delete(rec);
|
|
180
|
-
|
|
181
|
+
addPending(rec);
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
function evict() {
|
|
@@ -186,10 +187,9 @@ function evict() {
|
|
|
186
187
|
while (warm.size > PREWARM_CAP) {
|
|
187
188
|
let victim = null, score = -1;
|
|
188
189
|
for (const rec of warm) {
|
|
189
|
-
if (!rec.el.isConnected) { warm.delete(rec); continue; }
|
|
190
|
+
if (!rec.el.isConnected) { warm.delete(rec); untrack(rec); continue; }
|
|
190
191
|
if (rec.rule === 'popover' && isPopoverOpen(rec.el)) continue;
|
|
191
|
-
|
|
192
|
-
const sc = (reachable(rec.el) ? 0 : 2) + (rec.near ? 0 : 1);
|
|
192
|
+
const sc = (rec.urgent ? 0 : 4) + (reachable(rec) ? 0 : 2) + (isNear(rec) ? 0 : 1);
|
|
193
193
|
if (sc > score) { victim = rec; score = sc; }
|
|
194
194
|
}
|
|
195
195
|
if (!victim) return;
|
|
@@ -261,15 +261,15 @@ function cancelIdle() {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// A gesture that revealed a pane (tab switch, panel open) makes its containers urgent
|
|
264
|
-
function promoteNearViewport() {
|
|
264
|
+
function promoteNearViewport(first) {
|
|
265
265
|
if (!ready || !bootDrained) return;
|
|
266
|
+
if (first) refreshInvokers();
|
|
266
267
|
// The on-screen containers nearest the gesture point (a revealed pane sits under the click)
|
|
267
268
|
const candidates = [];
|
|
268
|
-
for (const rec of
|
|
269
|
-
if (rec.urgent || !rec
|
|
270
|
-
const r =
|
|
271
|
-
|
|
272
|
-
if (!rec.near) continue;
|
|
269
|
+
for (const rec of reachablePending()) {
|
|
270
|
+
if (rec.urgent || !pending.has(rec) || !rec.el.isConnected) continue;
|
|
271
|
+
const r = nearRect(rec);
|
|
272
|
+
if (!r) continue;
|
|
273
273
|
const dx = Math.max(r.left - gestureX, 0, gestureX - r.right);
|
|
274
274
|
const dy = Math.max(r.top - gestureY, 0, gestureY - r.bottom);
|
|
275
275
|
candidates.push([dx * dx + dy * dy, rec]);
|
|
@@ -283,49 +283,159 @@ function promoteNearViewport() {
|
|
|
283
283
|
}
|
|
284
284
|
function onGesture(e) {
|
|
285
285
|
lastGestureAt = now();
|
|
286
|
+
bumpReach();
|
|
286
287
|
if (e && typeof e.clientX === 'number' && (e.clientX || e.clientY)) { gestureX = e.clientX; gestureY = e.clientY; }
|
|
287
288
|
if (now() - lastPromoteAt < 300) return;
|
|
288
289
|
clearTimeout(promoteTimer);
|
|
289
290
|
lastPromoteAt = now();
|
|
290
291
|
// Three passes: a revealed pane lays out and mounts its menus over the next few hundred ms
|
|
291
|
-
promoteTimer = setTimeout(() => { promoteNearViewport(); promoteTimer = setTimeout(() => { promoteNearViewport(); promoteTimer = setTimeout(promoteNearViewport, 400); }, 150); }, 50);
|
|
292
|
+
promoteTimer = setTimeout(() => { promoteNearViewport(true); promoteTimer = setTimeout(() => { promoteNearViewport(); promoteTimer = setTimeout(promoteNearViewport, 400); }, 150); }, 50);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Only containers the user can reach: not under a hidden route (cached until the next gesture or route change)
|
|
296
|
+
let reachEpoch = 0;
|
|
297
|
+
let pool = null, poolEpoch = -1; // reachable subset of pending, rebuilt once per epoch
|
|
298
|
+
const bumpReach = () => { reachEpoch++; };
|
|
299
|
+
function reachablePending() {
|
|
300
|
+
if (pool && poolEpoch === reachEpoch && pool.length < pending.size * 2 + 64) return pool;
|
|
301
|
+
poolEpoch = reachEpoch;
|
|
302
|
+
pool = [];
|
|
303
|
+
for (const rec of pending) {
|
|
304
|
+
if (!rec.el.isConnected) { pending.delete(rec); untrack(rec); continue; }
|
|
305
|
+
if (reachable(rec)) { track(rec); pool.push(rec); } else untrack(rec);
|
|
306
|
+
}
|
|
307
|
+
return pool;
|
|
308
|
+
}
|
|
309
|
+
function addPending(rec) {
|
|
310
|
+
pending.add(rec);
|
|
311
|
+
if (pool && poolEpoch === reachEpoch && reachable(rec)) { track(rec); pool.push(rec); }
|
|
312
|
+
}
|
|
313
|
+
function reachable(rec) {
|
|
314
|
+
if (rec.reachAt !== reachEpoch) { rec.reachAt = reachEpoch; rec.reach = !rec.el.closest('[x-route][hidden]'); }
|
|
315
|
+
return rec.reach;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ---- Geometry: IntersectionObserver keeps near/box per record; sync reads only until it reports ----
|
|
319
|
+
|
|
320
|
+
let io = null;
|
|
321
|
+
const anchors = new Map(); // anchor el -> { recs, box, near, seen }
|
|
322
|
+
const MAX_DEPTH = 6;
|
|
323
|
+
|
|
324
|
+
function ioReady() {
|
|
325
|
+
if (io === null) io = typeof IntersectionObserver === 'function' ? new IntersectionObserver(onIntersect) : false;
|
|
326
|
+
return io;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function applyAnchor(rec, a) {
|
|
330
|
+
if (!a.box && rec.depth + 1 < MAX_DEPTH && rec.anchor.parentElement) { anchorTo(rec, rec.anchor.parentElement, rec.depth + 1); return; }
|
|
331
|
+
rec.box = a.box;
|
|
332
|
+
rec.ioNear = a.near;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function onIntersect(entries) {
|
|
336
|
+
for (const e of entries) {
|
|
337
|
+
const a = anchors.get(e.target);
|
|
338
|
+
if (!a) continue;
|
|
339
|
+
const r = e.boundingClientRect;
|
|
340
|
+
a.seen = true;
|
|
341
|
+
a.box = (r.width || r.height) ? r : null;
|
|
342
|
+
a.near = !!a.box && e.isIntersecting;
|
|
343
|
+
for (const rec of Array.from(a.recs)) applyAnchor(rec, a);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function anchorTo(rec, el, depth) {
|
|
348
|
+
untrack(rec);
|
|
349
|
+
rec.anchor = el; rec.depth = depth; rec.box = undefined; rec.ioNear = undefined;
|
|
350
|
+
let a = anchors.get(el);
|
|
351
|
+
if (!a) { anchors.set(el, a = { recs: new Set(), box: undefined, near: false, seen: false }); io.observe(el); }
|
|
352
|
+
a.recs.add(rec);
|
|
353
|
+
if (a.seen) applyAnchor(rec, a);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function untrack(rec) {
|
|
357
|
+
const el = rec.anchor;
|
|
358
|
+
if (!el) return;
|
|
359
|
+
rec.anchor = null; rec.box = undefined; rec.ioNear = undefined;
|
|
360
|
+
const a = anchors.get(el);
|
|
361
|
+
if (!a) return;
|
|
362
|
+
a.recs.delete(rec);
|
|
363
|
+
if (!a.recs.size) { anchors.delete(el); io.unobserve(el); }
|
|
292
364
|
}
|
|
293
365
|
|
|
294
|
-
// Only containers the user can reach: not under a hidden route; visible parents first
|
|
295
|
-
const reachable = (el) => !el.closest('[x-route][hidden]');
|
|
296
366
|
// Judge by the invoker (popovertarget / aria-controls), else the nearest ancestor with a box
|
|
367
|
+
function invokerFor(rec, map) {
|
|
368
|
+
const el = rec.el;
|
|
369
|
+
if (!el.id) return null;
|
|
370
|
+
if (map) return map.get(el.id) || null;
|
|
371
|
+
if (!window.CSS || !CSS.escape) return null;
|
|
372
|
+
const id = CSS.escape(el.id);
|
|
373
|
+
return document.querySelector(`[popovertarget="${id}"], [aria-controls="${id}"]`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function track(rec) {
|
|
377
|
+
if (rec.anchor || !ioReady() || !rec.el.isConnected) return;
|
|
378
|
+
if (rec.invoker === undefined) rec.invoker = invokerFor(rec);
|
|
379
|
+
const start = (rec.invoker && rec.invoker.isConnected) ? rec.invoker : rec.el.parentElement;
|
|
380
|
+
if (start) anchorTo(rec, start, 0);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Late invokers / late-bound ids: once per gesture, re-anchor tracked records that had none
|
|
384
|
+
function refreshInvokers() {
|
|
385
|
+
if (!io) return;
|
|
386
|
+
const orphans = [];
|
|
387
|
+
for (const rec of reachablePending()) if (!rec.invoker && rec.anchor && rec.el.id && pending.has(rec)) orphans.push(rec);
|
|
388
|
+
if (!orphans.length) return;
|
|
389
|
+
let map = null;
|
|
390
|
+
if (orphans.length > 8) {
|
|
391
|
+
map = new Map();
|
|
392
|
+
for (const inv of document.querySelectorAll('[popovertarget], [aria-controls]')) {
|
|
393
|
+
const id = inv.getAttribute('popovertarget') || inv.getAttribute('aria-controls');
|
|
394
|
+
if (id && !map.has(id)) map.set(id, inv);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
for (const rec of orphans) {
|
|
398
|
+
const inv = invokerFor(rec, map);
|
|
399
|
+
if (inv) { rec.invoker = inv; anchorTo(rec, inv, 0); }
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
297
403
|
// Rect of the invoker or the nearest boxed ancestor (null when nothing has a box)
|
|
298
404
|
function anchorRect(rec) {
|
|
299
405
|
const el = rec.el;
|
|
300
|
-
if (
|
|
301
|
-
const id = CSS.escape(el.id);
|
|
302
|
-
rec.invoker = document.querySelector(`[popovertarget="${id}"], [aria-controls="${id}"]`) || null;
|
|
303
|
-
}
|
|
406
|
+
if (rec.invoker === undefined || (rec.invoker === null && !io)) rec.invoker = invokerFor(rec);
|
|
304
407
|
let p = (rec.invoker && rec.invoker.isConnected) ? rec.invoker : el.parentElement;
|
|
305
|
-
for (let depth = 0; p && depth <
|
|
408
|
+
for (let depth = 0; p && depth < MAX_DEPTH; p = p.parentElement, depth++) {
|
|
306
409
|
const r = p.getBoundingClientRect();
|
|
307
410
|
if (r.width || r.height) return r;
|
|
308
411
|
}
|
|
309
412
|
return null;
|
|
310
413
|
}
|
|
311
|
-
|
|
414
|
+
const inViewport = (r) => !!r && r.bottom > 0 && r.top < window.innerHeight && r.right > 0 && r.left < window.innerWidth;
|
|
415
|
+
|
|
416
|
+
// On-screen rect for ranking, or null when off-screen / boxless
|
|
417
|
+
function nearRect(rec) {
|
|
418
|
+
if (rec.box !== undefined) return rec.ioNear ? rec.box : null;
|
|
312
419
|
const r = anchorRect(rec);
|
|
313
|
-
|
|
420
|
+
rec.near = inViewport(r);
|
|
421
|
+
return rec.near ? r : null;
|
|
422
|
+
}
|
|
423
|
+
function isNear(rec) {
|
|
424
|
+
if (rec.box !== undefined) return !!rec.ioNear;
|
|
425
|
+
if (rec.near === undefined) rec.near = inViewport(anchorRect(rec));
|
|
426
|
+
return rec.near;
|
|
314
427
|
}
|
|
315
428
|
function rank(rec) {
|
|
316
|
-
|
|
317
|
-
return (rec.urgent ? 0 : 4) + (rec.near ? 0 : 2);
|
|
429
|
+
return (rec.urgent ? 0 : 4) + (isNear(rec) ? 0 : 2);
|
|
318
430
|
}
|
|
319
431
|
function next() {
|
|
320
432
|
let best = null, bestRank = 0;
|
|
321
|
-
for (const rec of
|
|
322
|
-
if (!
|
|
323
|
-
if (!
|
|
433
|
+
for (const rec of reachablePending()) {
|
|
434
|
+
if (!pending.has(rec)) continue;
|
|
435
|
+
if (!rec.el.isConnected) { pending.delete(rec); untrack(rec); continue; }
|
|
324
436
|
const r = rank(rec);
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|| (r === bestRank && (rec.priority < best.priority
|
|
328
|
-
|| (rec.priority === best.priority && (best.el.compareDocumentPosition(rec.el) & Node.DOCUMENT_POSITION_PRECEDING))))) {
|
|
437
|
+
// Ties keep registration order: document order for the initial walk, mount order after
|
|
438
|
+
if (!best || r < bestRank || (r === bestRank && rec.priority < best.priority)) {
|
|
329
439
|
best = rec; bestRank = r;
|
|
330
440
|
}
|
|
331
441
|
}
|
|
@@ -347,7 +457,7 @@ function schedule() {
|
|
|
347
457
|
const rec = next();
|
|
348
458
|
if (!rec) break;
|
|
349
459
|
if (!rec.urgent && warm.size >= PREWARM_CAP) break; // cap = max warm; only urgent renders may exceed it
|
|
350
|
-
for (const other of pending) other.near = undefined;
|
|
460
|
+
if (!io) for (const other of pending) other.near = undefined;
|
|
351
461
|
render(rec, true);
|
|
352
462
|
n++;
|
|
353
463
|
}
|
|
@@ -394,6 +504,7 @@ function initializeDeferPlugin() {
|
|
|
394
504
|
Alpine.directive('defer', () => { });
|
|
395
505
|
if (!killed) Alpine.interceptInit(Alpine.skipDuringClone(intercept));
|
|
396
506
|
['pointerdown', 'keydown'].forEach((type) => document.addEventListener(type, onGesture, { capture: true, passive: true }));
|
|
507
|
+
['manifest:route-change', 'manifest:components-processed'].forEach((type) => window.addEventListener(type, bumpReach));
|
|
397
508
|
armReady();
|
|
398
509
|
}
|
|
399
510
|
|
|
@@ -403,7 +514,7 @@ window.ManifestDefer = {
|
|
|
403
514
|
render: (el) => { if (el && el.__mnfstDefer) render(el.__mnfstDefer); },
|
|
404
515
|
isPending: (el) => !!(el && el.__mnfstDefer && !el.__mnfstDefer.rendered),
|
|
405
516
|
stats: () => ({
|
|
406
|
-
pending: pending.size, warm: warm.size, cap: PREWARM_CAP, ready, bootDrained, armed: idleHandle !== null,
|
|
517
|
+
pending: pending.size, warm: warm.size, cap: PREWARM_CAP, ready, bootDrained, armed: idleHandle !== null, observed: anchors.size,
|
|
407
518
|
head: (() => { const h = next(); return h ? (h.el.id || h.el.tagName) : null; })(),
|
|
408
519
|
routes: { enabled: routesOn(), stashed: Array.from(routes).filter((r) => !r.rendered).length, rendered: Array.from(routes).filter((r) => r.rendered).length }
|
|
409
520
|
})
|
package/lib/manifest.edit.css
CHANGED
|
@@ -9,34 +9,44 @@
|
|
|
9
9
|
|
|
10
10
|
@layer utilities {
|
|
11
11
|
|
|
12
|
-
/* ----
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
the region
|
|
12
|
+
/* ---- Editing UI scope ----
|
|
13
|
+
ALL chrome — region outline + label, sortable grips, movable/text outlines,
|
|
14
|
+
size-handle hairlines, the floating toolbar — lives under ONE scope:
|
|
15
|
+
[data-edit-ui], on the region itself or any ancestor (e.g. <body data-edit-ui>
|
|
16
|
+
for a whole editor view). The plugin sets it on `.authoring` regions
|
|
17
|
+
automatically; everything else opts in with the attribute. A region outside
|
|
18
|
+
the scope is just as editable — it simply carries no visual cues. */
|
|
19
|
+
|
|
20
|
+
/* ---- Editable area (armed) ---- */
|
|
16
21
|
:where([data-edit-armed]) {
|
|
17
22
|
position: relative;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
/* Quiet until touched. A page covered in dashed boxes reads as a diagram of
|
|
21
|
-
itself; the affordance only has to be there when you go looking for it.
|
|
22
|
-
|
|
26
|
+
itself; the affordance only has to be there when you go looking for it.
|
|
27
|
+
While an inner affordance is engaged (text caret, handle, row), the region
|
|
28
|
+
outline stands down so only the element being edited is highlighted. */
|
|
29
|
+
:where([data-edit-armed]):where([data-edit-ui], [data-edit-ui] *) {
|
|
23
30
|
outline: 1px dashed transparent;
|
|
24
31
|
outline-offset: 6px;
|
|
25
32
|
border-radius: var(--radius);
|
|
26
33
|
transition: outline-color 0.12s ease;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
:where([data-edit-armed][data-edit-
|
|
30
|
-
:where([data-edit-armed][data-edit-authoring]):focus-within {
|
|
36
|
+
:where([data-edit-armed]):where([data-edit-ui], [data-edit-ui] *):is(:hover, :focus-within):not(:has([contenteditable]:is(:hover, :focus), [data-edit-handle]:is(:hover, :focus), [data-edit-sortable]:is(:hover, :focus-within), [data-edit-movable]:is(:hover, :focus))) {
|
|
31
37
|
outline-color: var(--color-line);
|
|
32
38
|
}
|
|
33
39
|
|
|
34
|
-
:where([data-edit-armed][data-edit-
|
|
40
|
+
:where([data-edit-armed]):where([data-edit-ui], [data-edit-ui] *)::before {
|
|
35
41
|
content: attr(data-edit-label);
|
|
36
42
|
position: absolute;
|
|
37
|
-
inset-block-start: -
|
|
38
|
-
inset-inline-start: 0;
|
|
43
|
+
inset-block-start: calc(-0.6em - 6px);
|
|
44
|
+
inset-inline-start: 0.5rem;
|
|
45
|
+
padding-inline: 0.3rem;
|
|
46
|
+
background: var(--color-page);
|
|
47
|
+
border-radius: 3px;
|
|
39
48
|
font-size: 0.7rem;
|
|
49
|
+
line-height: 1.2;
|
|
40
50
|
letter-spacing: 0.04em;
|
|
41
51
|
text-transform: uppercase;
|
|
42
52
|
color: var(--color-content-subtle);
|
|
@@ -45,8 +55,7 @@
|
|
|
45
55
|
transition: opacity 0.12s ease;
|
|
46
56
|
}
|
|
47
57
|
|
|
48
|
-
:where([data-edit-armed][data-edit-
|
|
49
|
-
:where([data-edit-armed][data-edit-authoring]):focus-within::before {
|
|
58
|
+
:where([data-edit-armed]):where([data-edit-ui], [data-edit-ui] *):is(:hover, :focus-within):not(:has([contenteditable]:is(:hover, :focus), [data-edit-handle]:is(:hover, :focus), [data-edit-sortable]:is(:hover, :focus-within), [data-edit-movable]:is(:hover, :focus)))::before {
|
|
50
59
|
opacity: 1;
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -57,10 +66,28 @@
|
|
|
57
66
|
transition: opacity 0.12s ease, outline-color 0.12s ease;
|
|
58
67
|
}
|
|
59
68
|
|
|
60
|
-
:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
69
|
+
/* The grip: the plugin's own inline drag affordance, sitting inside the row
|
|
70
|
+
at its inline start. A dot grid, revealed on approach. --edit-grip sizes
|
|
71
|
+
it; `--edit-grip: 0` removes it (e.g. for grid rows where an extra flex
|
|
72
|
+
item would take a cell). No full-row outline — the grip and the cursor
|
|
73
|
+
say "draggable" without boxing the content. */
|
|
74
|
+
:where([data-edit-ui] [data-edit-sortable], [data-edit-ui][data-edit-sortable])::before {
|
|
75
|
+
content: '';
|
|
76
|
+
align-self: center;
|
|
77
|
+
flex: 0 0 auto;
|
|
78
|
+
width: var(--edit-grip, 0.6rem);
|
|
79
|
+
height: 1rem;
|
|
80
|
+
margin-inline-end: calc(var(--edit-grip, 0.6rem) / 2);
|
|
81
|
+
background-image: radial-gradient(circle, var(--color-content-subtle) 1px, transparent 1px);
|
|
82
|
+
background-size: 0.3rem 0.3rem;
|
|
83
|
+
background-position: center;
|
|
84
|
+
opacity: 0;
|
|
85
|
+
transition: opacity 0.12s ease;
|
|
86
|
+
pointer-events: none;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
:where([data-edit-ui] [data-edit-sortable], [data-edit-ui][data-edit-sortable]):is(:hover, :focus-visible)::before {
|
|
90
|
+
opacity: 0.9;
|
|
64
91
|
}
|
|
65
92
|
|
|
66
93
|
:where([data-edit-sortable], [data-edit-movable]):focus-visible {
|
|
@@ -105,25 +132,30 @@
|
|
|
105
132
|
touch-action: none;
|
|
106
133
|
}
|
|
107
134
|
|
|
108
|
-
:where([data-edit-movable]):hover {
|
|
135
|
+
:where([data-edit-ui] [data-edit-movable], [data-edit-ui][data-edit-movable]):hover {
|
|
109
136
|
outline: 1px solid var(--edit-accent, var(--color-brand-content));
|
|
110
137
|
outline-offset: 2px;
|
|
111
138
|
}
|
|
112
139
|
|
|
113
|
-
/* ---- Inline text ----
|
|
114
|
-
|
|
140
|
+
/* ---- Inline text ----
|
|
141
|
+
One weight everywhere: hover hints and the focused caret both use the same
|
|
142
|
+
1px line, so focusing doesn't make the page jump a weight class. */
|
|
143
|
+
:where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"],
|
|
144
|
+
:where([data-edit-ui])[data-edit-armed] [contenteditable="true"] {
|
|
115
145
|
outline: 1px solid transparent;
|
|
116
146
|
outline-offset: 2px;
|
|
117
147
|
border-radius: 3px;
|
|
118
148
|
transition: outline-color 0.12s ease;
|
|
119
149
|
}
|
|
120
150
|
|
|
121
|
-
:where([data-edit-armed]) [contenteditable="true"]:hover
|
|
151
|
+
:where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"]:hover,
|
|
152
|
+
:where([data-edit-ui])[data-edit-armed] [contenteditable="true"]:hover {
|
|
122
153
|
outline-color: color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
|
|
123
154
|
}
|
|
124
155
|
|
|
125
|
-
:where([data-edit-armed]) [contenteditable="true"]:focus
|
|
126
|
-
|
|
156
|
+
:where([data-edit-ui]) :where([data-edit-armed]) [contenteditable="true"]:focus,
|
|
157
|
+
:where([data-edit-ui])[data-edit-armed] [contenteditable="true"]:focus {
|
|
158
|
+
outline-color: var(--edit-accent, var(--color-brand-content));
|
|
127
159
|
}
|
|
128
160
|
|
|
129
161
|
/* ---- Size handles ----
|
|
@@ -133,14 +165,16 @@
|
|
|
133
165
|
position: relative;
|
|
134
166
|
}
|
|
135
167
|
|
|
136
|
-
/* Handles reveal on approach rather than sitting on the element permanently
|
|
168
|
+
/* Handles reveal on approach rather than sitting on the element permanently.
|
|
169
|
+
The hairline is chrome, so it follows the [data-edit-ui] scope like every
|
|
170
|
+
other affordance; outside the scope the handles still work (hit areas and
|
|
171
|
+
resize cursors stay), they just draw nothing. */
|
|
137
172
|
:where([data-edit-sizable]) :where([data-edit-handle])::before {
|
|
138
173
|
opacity: 0;
|
|
139
174
|
transition: opacity 0.12s ease, background-color 0.15s ease;
|
|
140
175
|
}
|
|
141
176
|
|
|
142
|
-
:where([data-edit-sizable]):hover :where([data-edit-handle])::before
|
|
143
|
-
:where([data-edit-sizable]):focus-within :where([data-edit-handle])::before {
|
|
177
|
+
:where([data-edit-ui] [data-edit-sizable], [data-edit-ui][data-edit-sizable]):is(:hover, :focus-within) :where([data-edit-handle])::before {
|
|
144
178
|
opacity: 1;
|
|
145
179
|
}
|
|
146
180
|
|
|
@@ -300,8 +334,10 @@
|
|
|
300
334
|
}
|
|
301
335
|
}
|
|
302
336
|
|
|
337
|
+
/* --edit-toolbar: none (any scope up to :root) suppresses it — e.g. a site
|
|
338
|
+
that demos .authoring regions but has its own publish flow. */
|
|
303
339
|
:root[data-edit-active] :where([data-edit-toolbar]) {
|
|
304
|
-
display: flex;
|
|
340
|
+
display: var(--edit-toolbar, flex);
|
|
305
341
|
}
|
|
306
342
|
|
|
307
343
|
/* A popover: the top layer sits above every stacking context, so no handle,
|