mnfst 0.5.232 → 0.5.233
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.d.ts +4 -2
- package/lib/manifest.data.js +1 -1
- package/lib/manifest.edit.js +96 -30
- package/lib/manifest.integrity.json +6 -6
- package/lib/manifest.js +44 -3
- package/lib/manifest.rich.text.js +524 -141
- package/lib/manifest.sw.js +1 -1
- package/package.json +1 -1
package/lib/manifest.d.ts
CHANGED
|
@@ -433,11 +433,13 @@ export interface ManifestEdit {
|
|
|
433
433
|
/** Set an attribute: a string sets it, `true` sets it empty, `null`/`false` removes it. */
|
|
434
434
|
attr(name: string, value: string | boolean | null, el?: Element): boolean;
|
|
435
435
|
classes: ManifestEditClasses;
|
|
436
|
-
/** Component scope of
|
|
436
|
+
/** Component scope of `el`'s area (default `$edit.target`): `scope(el)` reads it.
|
|
437
437
|
* Instance overrides apply to an area that wraps exactly one component with no x-edit region
|
|
438
438
|
* of its own; a component among other content is a host piece of a static area instead, and
|
|
439
439
|
* a component's own region is edited in its own file. */
|
|
440
|
-
scope(
|
|
440
|
+
scope(el?: Element | null): 'instance' | 'main' | null;
|
|
441
|
+
/** Set the component scope of `el`'s area (default `$edit.target`); false if it has none. */
|
|
442
|
+
scope(value: 'instance' | 'main', el?: Element | null): boolean;
|
|
441
443
|
/** Revert a component element's instance overrides (the area element reverts all). */
|
|
442
444
|
revert(el?: Element): boolean;
|
|
443
445
|
}
|
package/lib/manifest.data.js
CHANGED
package/lib/manifest.edit.js
CHANGED
|
@@ -1163,7 +1163,8 @@
|
|
|
1163
1163
|
if (kind === 'component') return false;
|
|
1164
1164
|
const block = asPiece(el);
|
|
1165
1165
|
if (!block) return false;
|
|
1166
|
-
if (action === 'copy'
|
|
1166
|
+
if (action === 'copy') return !isHostEl(block);
|
|
1167
|
+
if (action === 'cut') return canDo('copy', block) && canDo('remove', block); // a cut is a copy and a remove
|
|
1167
1168
|
if (action === 'move') return movable(block);
|
|
1168
1169
|
const lockAs = action === 'duplicate' ? 'duplicate' : 'remove';
|
|
1169
1170
|
return capOf(block.parentElement, 'move') && !lockedFor(block, lockAs);
|
|
@@ -1408,6 +1409,8 @@
|
|
|
1408
1409
|
const region = s.crossOK ? dropArea(x, y, s.area) : s.area;
|
|
1409
1410
|
if (!region) return null;
|
|
1410
1411
|
const hit = document.elementFromPoint ? document.elementFromPoint(x, y) : null;
|
|
1412
|
+
// Over the stand-in itself: it stays where it is
|
|
1413
|
+
if (hit && s.ghost && (hit === s.ghost || s.ghost.contains(hit))) return null;
|
|
1411
1414
|
if (hit && hit !== region && region.contains(hit)) {
|
|
1412
1415
|
// Inside a component host: the host is what is hit (its markup is another file's)
|
|
1413
1416
|
let from = hit;
|
|
@@ -1417,6 +1420,9 @@
|
|
|
1417
1420
|
const parent = el.parentElement;
|
|
1418
1421
|
if (!isContainer(parent)) continue;
|
|
1419
1422
|
if (isHostEl(el)) { if (accepts(parent) && fits(s.item, parent) && !s.item.contains(parent)) return beside(el, x, y, parent); continue; }
|
|
1423
|
+
// Over the container the piece came from, or one holding the stand-in (a gap, its padding):
|
|
1424
|
+
// it stays in, placed by reading order; it leaves only when the pointer does
|
|
1425
|
+
if (s.ghost && (el === s.home || el.contains(s.ghost)) && accepts(el) && fits(s.item, el)) return { container: el, ref: afterElement(el, x, y) };
|
|
1420
1426
|
if (isContainer(el) && accepts(el) && fits(s.item, el) && inMiddle(el, x, y, parent)) {
|
|
1421
1427
|
if (s.ghost && s.ghost.parentElement !== el) s.nested = { el, rect: el.getBoundingClientRect(), horiz: flowAxis(parent) === 'x' };
|
|
1422
1428
|
return { container: el, ref: afterElement(el, x, y) };
|
|
@@ -1429,9 +1435,12 @@
|
|
|
1429
1435
|
}
|
|
1430
1436
|
// Reading-order insertion: the first sibling past the pointer.
|
|
1431
1437
|
function afterElement(c, x, y) {
|
|
1438
|
+
const column = flowAxis(c) === 'y';
|
|
1432
1439
|
for (const el of sortableChildren(c)) {
|
|
1433
1440
|
if (el === dragged || el.hasAttribute('data-edit-dragging') || lockedFor(el, 'move')) continue;
|
|
1434
1441
|
const b = el.getBoundingClientRect(), cx = b.left + b.width / 2, cy = b.top + b.height / 2;
|
|
1442
|
+
// Stacked rows: past a row once the pointer is past its middle (wherever it is across)
|
|
1443
|
+
if (column && !/inline/.test(getComputedStyle(el).display)) { if (cy > y) return el; continue; }
|
|
1435
1444
|
const sameRow = Math.abs(cy - y) < b.height / 2;
|
|
1436
1445
|
if (cy - y > b.height / 2 || (sameRow && cx > x)) return el;
|
|
1437
1446
|
}
|
|
@@ -2332,10 +2341,24 @@
|
|
|
2332
2341
|
});
|
|
2333
2342
|
cssvarControls.forEach(syncCssVarInput);
|
|
2334
2343
|
}
|
|
2335
|
-
function rebaseTheme() {
|
|
2336
|
-
Object.keys(themeBaseline).forEach(kk => { const sep = kk.indexOf('|'); themeBaseline[kk] = themeTargetEl(kk.slice(0, sep) || null).style.getPropertyValue(kk.slice(sep + 1)); });
|
|
2337
|
-
themeTouched.
|
|
2338
|
-
}
|
|
2344
|
+
function rebaseTheme(keep) {
|
|
2345
|
+
Object.keys(themeBaseline).forEach(kk => { if (keep && keep.has('theme|' + kk)) return; const sep = kk.indexOf('|'); themeBaseline[kk] = themeTargetEl(kk.slice(0, sep) || null).style.getPropertyValue(kk.slice(sep + 1)); });
|
|
2346
|
+
[...themeTouched].forEach(kk => { if (!(keep && keep.has('theme|' + kk))) themeTouched.delete(kk); });
|
|
2347
|
+
}
|
|
2348
|
+
// A scoped value lives in its scope element's inline style, and is written there, never to a
|
|
2349
|
+
// stylesheet: with its x-edit area's tree when the element is a static piece of one ('area'),
|
|
2350
|
+
// else found by its own x-edit.theme name ('own'). Inside generated content it can't be (null).
|
|
2351
|
+
function inlineScope(scope) {
|
|
2352
|
+
const el = scope && themeScopes[scope]; if (!el || generatedBy(el)) return null;
|
|
2353
|
+
const a = regionOf(el), k = el.getAttribute('data-edit-key');
|
|
2354
|
+
if (a && classify(a) === 'static' && !sharedKey(a) && k && /^[@~]/.test(k)) return 'area';
|
|
2355
|
+
if (a && a !== el) return null;
|
|
2356
|
+
const named = areaByKey(scope);
|
|
2357
|
+
return !named || named === el ? 'own' : null;
|
|
2358
|
+
}
|
|
2359
|
+
const inlineScoped = () => new Set(Object.values(themeState()).filter(d => d.scope && inlineScope(d.scope) === 'area').map(d => themeScopes[d.scope]));
|
|
2360
|
+
// The component file a scope element is authored in, if any.
|
|
2361
|
+
const scopeComponent = (el) => { for (let n = el; n; n = n.parentElement) if (n._manifestComponent) return n._manifestComponent; return null; };
|
|
2339
2362
|
// A custom-property value must stay inside its declaration (same tokenizer as the
|
|
2340
2363
|
// source writer): strings and url(...) are opaque; script schemes in url() and
|
|
2341
2364
|
// expression() are refused anywhere; data: urls only as images.
|
|
@@ -2383,12 +2406,16 @@
|
|
|
2383
2406
|
const cur = themeState()[kk]; if (cur && cur.value === value) return; // dedupe
|
|
2384
2407
|
pushDelta({ kind: 'theme', scope: scope || null, var: v, value }, !!persist);
|
|
2385
2408
|
}
|
|
2386
|
-
//
|
|
2409
|
+
// Global vars → the page's theme stylesheet (the server's choice); scoped ones inline
|
|
2387
2410
|
function themePatches() {
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2411
|
+
const st = Object.values(themeState()), out = [];
|
|
2412
|
+
st.filter(d => !inlineScope(d.scope)).forEach(d => out.push({ kind: 'theme', scope: d.scope || null, var: d.var, value: d.value, file: null }));
|
|
2413
|
+
// A scope element of its own: its style attribute, as the page has it now
|
|
2414
|
+
new Set(st.filter(d => d.scope && inlineScope(d.scope) === 'own').map(d => d.scope)).forEach(scope => {
|
|
2415
|
+
const el = themeScopes[scope], comp = scopeComponent(el), tree = { key: '', edits: { style: ownStyle(el) || null } };
|
|
2416
|
+
out.push(comp ? { kind: 'static-tree', region: scope, component: comp, tree } : { kind: 'static-tree', region: scope, tree });
|
|
2391
2417
|
});
|
|
2418
|
+
return out;
|
|
2392
2419
|
}
|
|
2393
2420
|
|
|
2394
2421
|
|
|
@@ -2448,8 +2475,21 @@
|
|
|
2448
2475
|
};
|
|
2449
2476
|
walk(area, '');
|
|
2450
2477
|
}
|
|
2451
|
-
const warnedRendered = new Set(), renderedRegions = new Set();
|
|
2478
|
+
const warnedRendered = new Set(), renderedRegions = new Set(), unwritableWhy = new Map();
|
|
2479
|
+
// A list whose records mnfst-run can't write: inline x-data, or a source manifest.json does not declare as a file.
|
|
2480
|
+
function dataUnwritable(area) {
|
|
2481
|
+
if (classify(area) !== 'data') return null;
|
|
2482
|
+
const m = (dataSourceExpr(area) || '').match(/\$x\.([\w$]+)/);
|
|
2483
|
+
if (!m) return 'inline x-data list: mnfst-run writes data only to CSV/JSON/YAML sources declared in manifest.json';
|
|
2484
|
+
const mf = window.ManifestComponentsRegistry && window.ManifestComponentsRegistry.manifest;
|
|
2485
|
+
if (!mf) return null;
|
|
2486
|
+
const src = (mf.data || {})[m[1]];
|
|
2487
|
+
if (src == null) return `data source '${m[1]}' is not declared in manifest.json: mnfst-run writes data only to CSV/JSON/YAML files declared there`;
|
|
2488
|
+
return typeof src === 'string' ? null : `data source '${m[1]}' is not a file (a cloud or remote source): save it with $x.${m[1]}.$update, not mnfst-run`;
|
|
2489
|
+
}
|
|
2452
2490
|
function armArea(area) {
|
|
2491
|
+
const dw = dataUnwritable(area);
|
|
2492
|
+
if (dw) { renderedRegions.add(key(area)); unwritableWhy.set(key(area), dw); if (!warnedRendered.has(key(area))) { warnedRendered.add(key(area)); console.warn(`[edit] x-edit="${key(area)}": ${dw}; its edits stay on the page (and reach onPublish)`); } }
|
|
2453
2493
|
if (area._edit.rendered) renderedRegions.add(key(area));
|
|
2454
2494
|
if (area._edit.rendered && !warnedRendered.has(key(area))) { warnedRendered.add(key(area)); console.warn(`[edit] x-edit="${key(area)}" is inside content ${area._edit.rendered} renders: it edits on the page (and reaches onPublish), but mnfst-run cannot write it to a source file`); }
|
|
2455
2495
|
uneditIn(null);
|
|
@@ -2545,7 +2585,7 @@
|
|
|
2545
2585
|
return Object.keys(e).length ? e : null;
|
|
2546
2586
|
};
|
|
2547
2587
|
const nontrivial = (n) => n.edits || n.children || n.key[0] === '~';
|
|
2548
|
-
const live = keyMap(staticAreas());
|
|
2588
|
+
const live = keyMap(staticAreas()), scoped = inlineScoped();
|
|
2549
2589
|
staticAreas().forEach(area => {
|
|
2550
2590
|
const region = key(area), comp = sourceComponent(area);
|
|
2551
2591
|
const ids = new Proxy({}, { get: (_, k) => { const o = originOf(String(k)), a = areaByKey(decodeURIComponent(o.slice(1, o.indexOf('@', 1)))); const m = baseIds && a && baseIds[sourceFile(a)]; return m ? m[o] : undefined; } });
|
|
@@ -2565,6 +2605,7 @@
|
|
|
2565
2605
|
const at = originAt.get(originOf(id)); if (at != null) n.at = at;
|
|
2566
2606
|
if (ids && ids[originOf(id)]) n.fp = ids[originOf(id)];
|
|
2567
2607
|
const e = editsFor(id); if (e) n.edits = e;
|
|
2608
|
+
if (scoped.has(el)) n.edits = Object.assign({}, n.edits, { style: ownStyle(el) || null });
|
|
2568
2609
|
if (!(e && 'text' in e) && !isRendered(el) && !el.hasAttribute('data-rich-text')) {
|
|
2569
2610
|
const kids = treeKids(el).map(c => build(c)).filter(Boolean);
|
|
2570
2611
|
const structural = ss.touched.has(isRoot ? '#' + region : id);
|
|
@@ -2606,7 +2647,8 @@
|
|
|
2606
2647
|
return e;
|
|
2607
2648
|
};
|
|
2608
2649
|
const dv = dataValueState();
|
|
2609
|
-
|
|
2650
|
+
const dvRegion = {}; log.slice(0, cursor).forEach(d => { if (d.kind === 'data-val') dvRegion[d.source] = d.region; });
|
|
2651
|
+
Object.entries(dv).forEach(([source, recs]) => Object.entries(recs).forEach(([id, fields]) => Object.entries(fields).forEach(([field, value]) => patches.push({ kind: 'data-val', region: dvRegion[source], source, id, field, value }))));
|
|
2610
2652
|
const { main, inst, reverts } = componentState();
|
|
2611
2653
|
Object.entries(main).forEach(([component, paths]) => { const edits = toEdits(paths); if (edits.length) patches.push({ kind: 'component', scope: 'main', component, edits }); });
|
|
2612
2654
|
new Set([...Object.keys(inst), ...Object.keys(reverts)]).forEach(region => {
|
|
@@ -2623,7 +2665,7 @@
|
|
|
2623
2665
|
// `keep` (areas a publish left out), whose identities are left as they are.
|
|
2624
2666
|
function rebase(keep) {
|
|
2625
2667
|
const mounted = (r) => r == null || (!!areaByKey(r) && !(keep && keep.has(r)));
|
|
2626
|
-
const pending = (d) => (d.kind === 'st-move' ? [d.from, d.to] : [d.region]).some(r => !mounted(r));
|
|
2668
|
+
const pending = (d) => d.kind === 'theme' ? !!keep && (keep.has('theme|' + (d.scope || '') + '|' + d.var) || keep.has(d.scope)) : (d.kind === 'st-move' ? [d.from, d.to] : [d.region]).some(r => !mounted(r));
|
|
2627
2669
|
// Runtime locks are app state, not source: carried over by element.
|
|
2628
2670
|
const locks = [];
|
|
2629
2671
|
const lockState = lockStateNow();
|
|
@@ -2657,7 +2699,7 @@
|
|
|
2657
2699
|
if (kind === 'component' && a._armed) armComponent(a);
|
|
2658
2700
|
lastSnap[key(a)] = snapshot(a, kind);
|
|
2659
2701
|
});
|
|
2660
|
-
rebaseTheme();
|
|
2702
|
+
rebaseTheme(keep);
|
|
2661
2703
|
relock();
|
|
2662
2704
|
saveState(); refresh();
|
|
2663
2705
|
}
|
|
@@ -2783,7 +2825,7 @@
|
|
|
2783
2825
|
// fingerprints of keys with twins there (which may not be the elements this page shows).
|
|
2784
2826
|
let overNewer = false;
|
|
2785
2827
|
function publishNow(opts) {
|
|
2786
|
-
const done = (r, keep) => {
|
|
2828
|
+
const done = (r, keep, heldRes) => {
|
|
2787
2829
|
if (r && r.base && baseFiles) Object.entries(r.base).forEach(([f, h]) => { if (!overNewer || !/\.html?$/i.test(f)) baseFiles[f] = h; });
|
|
2788
2830
|
if (r && r.ids && baseIds) {
|
|
2789
2831
|
if (!overNewer) baseIds = r.ids;
|
|
@@ -2792,7 +2834,7 @@
|
|
|
2792
2834
|
const stale = r && Array.isArray(r.stale) ? r.stale : [];
|
|
2793
2835
|
if (estore) estore.stale = stale;
|
|
2794
2836
|
if (stale.length) document.dispatchEvent(new CustomEvent('edit:stale', { detail: { files: stale } }));
|
|
2795
|
-
if (published(keep ? { results: r.results.filter(x => !(x && keep.has(x.region))) } : r)) rebase(keep);
|
|
2837
|
+
if (published(keep ? { results: r.results.filter(x => !(x && (keep.has(x.region) || (heldRes && heldRes.has(x))))) } : r)) rebase(keep);
|
|
2796
2838
|
takeHeldReload();
|
|
2797
2839
|
return r;
|
|
2798
2840
|
};
|
|
@@ -2801,14 +2843,19 @@
|
|
|
2801
2843
|
const leftOut = (r, patches) => {
|
|
2802
2844
|
if (!r || !Array.isArray(r.results) || (Array.isArray(r.stale) && r.stale.length)) return null;
|
|
2803
2845
|
const failed = new Set(r.results.filter(x => x && x.failed != null).map(x => x.failed));
|
|
2804
|
-
|
|
2846
|
+
const own = new Set(patches.filter((p, i) => r.results[i] && r.results[i].status === 'error' && r.results[i].failed == null && p.kind !== 'static-tree'));
|
|
2847
|
+
if ((!failed.size && !own.size) || r.results.some((x, i) => x && x.status === 'error' && !failed.has(patches[i] && patches[i].region) && !own.has(patches[i]))) return null;
|
|
2805
2848
|
tie(failed);
|
|
2806
|
-
|
|
2849
|
+
const out = (p) => own.has(p) || (p.region != null && failed.has(p.region));
|
|
2850
|
+
if (!patches.some(p => !out(p))) return null;
|
|
2851
|
+
return Object.assign(failed, { own, out });
|
|
2807
2852
|
};
|
|
2853
|
+
// What a held-back patch keeps in the log: its region's edits, or its theme value.
|
|
2854
|
+
const keepOf = (p) => p.kind === 'theme' ? 'theme|' + (p.scope || '') + '|' + p.var : p.region;
|
|
2808
2855
|
// An area inside generated content (x-markdown output and the like) edits on the page,
|
|
2809
2856
|
// but its markup is in no file mnfst-run can write: it is refused by name, not sent.
|
|
2810
2857
|
const renderedBy = (region) => { const a = areaByKey(region); return a && a._edit.rendered; };
|
|
2811
|
-
const unwritable = (p) =>
|
|
2858
|
+
const unwritable = (p) => p.region != null && p.kind !== 'theme' && renderedRegions.has(p.region);
|
|
2812
2859
|
const send = () => {
|
|
2813
2860
|
commitFocused(true); // typing under way publishes with the rest (identities are re-derived from the page after)
|
|
2814
2861
|
const patches = buildPatches();
|
|
@@ -2816,24 +2863,25 @@
|
|
|
2816
2863
|
if (!patches.length) { takeHeldReload(); return Promise.resolve({ results: [] }); } // nothing to send: a held reload goes now
|
|
2817
2864
|
const post = (ps) => { inFlight++; return fetch('/__edit/save', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ patches: ps, base: baseFiles }) }).then(r => r.json()).catch(err => { settled(null); throw err; }); };
|
|
2818
2865
|
const off = tie(new Set(patches.filter(unwritable).map(p => p.region)));
|
|
2819
|
-
const offResult = (p) => ({ region: p.region, status: 'error', reason:
|
|
2820
|
-
? `x-edit="${p.region}" is inside content ${renderedBy(p.region)} renders, so mnfst-run can't write it to a source file`
|
|
2866
|
+
const offResult = (p) => ({ region: p.region, status: 'error', reason: unwritableWhy.has(p.region) && unwritable(p) ? `x-edit="${p.region}": ${unwritableWhy.get(p.region)}`
|
|
2867
|
+
: renderedBy(p.region) && unwritable(p) ? `x-edit="${p.region}" is inside content ${renderedBy(p.region)} renders, so mnfst-run can't write it to a source file`
|
|
2821
2868
|
: `not published: tied by a move to x-edit="${off.via.get(p.region)}", which mnfst-run can't write` });
|
|
2822
2869
|
const sendable = patches.filter(p => !off.has(p.region));
|
|
2823
2870
|
// The sent part's answer, merged with what was held back, as one result per patch.
|
|
2824
2871
|
const finish = (r, held, sent) => {
|
|
2825
|
-
const keep = new Set([...off, ...(held || [])]);
|
|
2826
|
-
const
|
|
2827
|
-
const
|
|
2872
|
+
const keep = new Set([...off, ...(held || []), ...(held ? sent.filter(held.out).map(keepOf) : [])]);
|
|
2873
|
+
const heldRes = new Set();
|
|
2874
|
+
const results = patches.map(p => { if (off.has(p.region)) { const x = offResult(p); heldRes.add(x); return x; } const x = r.results[sent.indexOf(p)]; if (held && held.out(p)) heldRes.add(x); return x; });
|
|
2875
|
+
const o = done(Object.assign({}, r, { results }), keep.size ? keep : undefined, heldRes); settled(r, !!(opts && opts.rebase)); return o;
|
|
2828
2876
|
};
|
|
2829
2877
|
if (!sendable.length) { const o = done({ results: patches.map(offResult) }, off); return Promise.resolve(o); }
|
|
2830
2878
|
return post(sendable).then(r => {
|
|
2831
2879
|
const out = leftOut(r, sendable);
|
|
2832
2880
|
if (!out) return finish(r, null, sendable);
|
|
2833
2881
|
settled(r, false);
|
|
2834
|
-
const rest = sendable.filter(p => !out.
|
|
2882
|
+
const rest = sendable.filter(p => !out.out(p));
|
|
2835
2883
|
return post(rest).then(r2 => {
|
|
2836
|
-
const results = sendable.map((p, i) => out.
|
|
2884
|
+
const results = sendable.map((p, i) => out.out(p)
|
|
2837
2885
|
? (r.results[i] && r.results[i].status === 'error' ? r.results[i] : { region: p.region, status: 'error', reason: `not published: tied by a move to x-edit="${out.via.get(p.region) || [...out].find(x => !out.via.has(x))}", which failed` })
|
|
2838
2886
|
: r2.results[rest.indexOf(p)]);
|
|
2839
2887
|
return finish(Object.assign({}, r2, { results }), out, sendable);
|
|
@@ -2935,8 +2983,10 @@
|
|
|
2935
2983
|
refresh();
|
|
2936
2984
|
}
|
|
2937
2985
|
function waitForData(area, cb) { const expr = dataSourceExpr(area), tpl = listTpl(area); let n = 0; const t = setInterval(() => { let r = false; try { r = Array.isArray(window.Alpine.evaluate(tpl, expr)); } catch { } if (r) { clearInterval(t); cb(); } else if (++n > 100) clearInterval(t); }, 50); }
|
|
2986
|
+
let inited = false;
|
|
2938
2987
|
function init() {
|
|
2939
|
-
if (!window.Alpine || !Alpine.directive) return;
|
|
2988
|
+
if (inited || !window.Alpine || !Alpine.directive) return;
|
|
2989
|
+
inited = true;
|
|
2940
2990
|
// Alpine walks only x-data trees: a page with none still gets its areas
|
|
2941
2991
|
if (document.body && !document.body.hasAttribute('x-data')) document.body.setAttribute('x-data', '{}');
|
|
2942
2992
|
Alpine.directive('edit', (el, { modifiers, expression }, { cleanup }) => {
|
|
@@ -2968,14 +3018,20 @@
|
|
|
2968
3018
|
block(node) { return pieceOf(node); },
|
|
2969
3019
|
attr(name, value, el) { return setAttr(name, value, el); },
|
|
2970
3020
|
classes: classesApi,
|
|
2971
|
-
scope(value, el) { void this.version;
|
|
3021
|
+
scope(value, el) { void this.version; if (value && value.nodeType === 1) return scopeOf(value); return value == null ? scopeOf(el) : setScope(value, el); },
|
|
2972
3022
|
revert(el) { return revert(el); },
|
|
2973
3023
|
clearConflicts() { conflicts = []; this.conflicts = []; saveState(); }
|
|
2974
3024
|
});
|
|
2975
3025
|
estore = Alpine.store('edit');
|
|
2976
|
-
|
|
3026
|
+
const editMagic = () => Alpine.store('edit');
|
|
3027
|
+
Alpine.magic('edit', editMagic);
|
|
3028
|
+
// The loader's stand-in answered $edit until now: it hands over, and effects that read it run again
|
|
3029
|
+
window.__manifestEditMagic = editMagic;
|
|
3030
|
+
const kept = window.__manifestEditStubWrites; // writes made on the stand-in ($edit.onPublish = …)
|
|
3031
|
+
if (kept) Object.keys(kept).forEach(k => { try { estore[k] = kept[k]; } catch { } delete kept[k]; });
|
|
3032
|
+
if (window.__manifestEditStub) setTimeout(() => { window.__manifestEditStub.v++; }, 0);
|
|
2977
3033
|
loadState();
|
|
2978
|
-
loadBase();
|
|
3034
|
+
if (window.__mnfstEdit) loadBase(); // only mnfst-run --edit serves the base
|
|
2979
3035
|
setTimeout(() => { armAll(); restore(); }, 450);
|
|
2980
3036
|
document.addEventListener('keydown', onHistoryKey);
|
|
2981
3037
|
window.addEventListener('click', onAreaLinkClick, true);
|
|
@@ -2984,6 +3040,16 @@
|
|
|
2984
3040
|
document.addEventListener('dropdown:context', onContextCapture, true);
|
|
2985
3041
|
// SPA navigation: the state so far is saved under the page it was made on.
|
|
2986
3042
|
window.addEventListener('manifest:route-change', () => { if (lsPath !== location.pathname) { lsPath = location.pathname; saveState(); } });
|
|
3043
|
+
adopt();
|
|
3044
|
+
}
|
|
3045
|
+
// Loaded after Alpine started: x-edit elements its walk passed get their attributes re-set
|
|
3046
|
+
// (Alpine runs them as new directives); those it never walked (no x-data above) are walked now.
|
|
3047
|
+
function adopt() {
|
|
3048
|
+
if (!window.__manifestAlpineStarted && ![...document.querySelectorAll('[x-data]')].some(e => e._x_marker)) return;
|
|
3049
|
+
const isEdit = (n) => /^x-edit(\.|$)/.test(n);
|
|
3050
|
+
const found = [...document.querySelectorAll('*')].filter(el => !el._edit && [...el.attributes].some(a => isEdit(a.name)));
|
|
3051
|
+
found.forEach(el => { if (el._x_marker) [...el.attributes].filter(a => isEdit(a.name)).forEach(a => el.setAttribute(a.name, a.value)); });
|
|
3052
|
+
if (found.some(el => !el._x_marker)) Alpine.initTree(document.body);
|
|
2987
3053
|
}
|
|
2988
3054
|
document.addEventListener('alpine:init', init);
|
|
2989
3055
|
if (window.Alpine && Alpine.directive) setTimeout(init, 0);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest.appwrite.auth.js": "sha384-dsIWnH0qCAGSzAXMGrd8fvOrVl4tOIE6j5tC35JPTrQXZ56qLD36ARyvhR7vi3Id",
|
|
3
|
-
"manifest.appwrite.data.js": "sha384-
|
|
3
|
+
"manifest.appwrite.data.js": "sha384-ovIuPS7AiQP/dP+NHcF9QaB3YK3dlqOLWN9pYX/RHofc2PLrZZuDjfEFiyNReuBu",
|
|
4
4
|
"manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
|
|
5
5
|
"manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
|
|
6
6
|
"manifest.charts.js": "sha384-gEAv/98+29RbhvNkiNWTNyBPbLTFr2Ld15wLu29FLCL57j1cwGChRrL9Pfk7dBtT",
|
|
@@ -11,24 +11,24 @@
|
|
|
11
11
|
"manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
|
|
12
12
|
"manifest.components.js": "sha384-JalISaBBWNFl3ggx92Hb3B52EpwwG3gzgra6Q+js0OgZ8vrFKMD48CuBox2N9A5T",
|
|
13
13
|
"manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
|
|
14
|
-
"manifest.data.js": "sha384-
|
|
14
|
+
"manifest.data.js": "sha384-cemls8IyDcg61WdlIOT60HmQS7aCef4ifwqnXfw7kA6Jp5D2PdXazWRRzR6QHx10",
|
|
15
15
|
"manifest.datepicker.js": "sha384-XnbeURni2X91ozVzatQcSjYYIS4MoW12dBRJnYAGBr0nCQ624heH5s+xtQSpLZX6",
|
|
16
16
|
"manifest.defer.js": "sha384-pMRGqQKiEoJhpUcD40QeM4jEHau7sz9Xn/YCpiUamT1T4xJBZDSiYX+xnVrSWoq+",
|
|
17
17
|
"manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
|
|
18
18
|
"manifest.dropdowns.js": "sha384-acufUps7R9WjqB4XXW5JLACqniuHJGM4CNTb2rF4B2Q/jv7ZRmsAwfr2McanOz3s",
|
|
19
|
-
"manifest.edit.js": "sha384-
|
|
19
|
+
"manifest.edit.js": "sha384-bOvEFgjau1t30LPHKBGkcJ3jH8tRUy1k8qpyYs8GbB6gUJ/R9JQCzeaF5fsKlzu3",
|
|
20
20
|
"manifest.export.js": "sha384-Rx6XvqBZc4apzP/FwcDr6yIln4/zNa08Q4ZqBZcEqUz1aNZQfl4h0+Ae7mqCQyuG",
|
|
21
21
|
"manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
|
|
22
22
|
"manifest.localization.js": "sha384-EImsIw8Z3l13V5eH9ZJF/yfsOfIvR1oPRNSArANkPji7V8O3IRf6zMwsNiAGj3Hk",
|
|
23
23
|
"manifest.markdown.js": "sha384-heyDR40LXHaAmAuA+fa381uFwShk1oAqpFFY6E2Qq4T3VN0TQfRqyc++MF40K5Hj",
|
|
24
24
|
"manifest.native.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
|
|
25
25
|
"manifest.payments.js": "sha384-8pofk2zhxtapPMpwnFyTelPMrYhShNhDCE722uQh/EYFvZFuPLmSdgunqsarVQjv",
|
|
26
|
-
"manifest.rich.text.js": "sha384-
|
|
26
|
+
"manifest.rich.text.js": "sha384-QpPHG4uWjJSJoTPY8yOtoIFoVmHbuYPhtghBU0gc2l0PJBc25cNPuNcD4+55Qv3i",
|
|
27
27
|
"manifest.router.js": "sha384-QkKhRaCbdXEOxhj+ojT4jeLUCtQepBnqP+QWTIWdb6kUWD+mVzPN8hf6jhgschpW",
|
|
28
28
|
"manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
|
|
29
29
|
"manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
|
|
30
30
|
"manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
|
|
31
|
-
"manifest.sw.js": "sha384-
|
|
31
|
+
"manifest.sw.js": "sha384-vXYxaf8oKd2LT8C9xRmFmEXhh0qDLv0set07o/hI4hkOvpS+CTDdC5UNECLRrSWa",
|
|
32
32
|
"manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
|
|
33
33
|
"manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
|
|
34
34
|
"manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
|
|
37
37
|
"manifest.utilities.js": "sha384-L5uXMLFBIF6LUM1haRjd4eGjHSWuqsCYSpPZRPYosNfJsMC5U5TnmjBxIhrZRe2d",
|
|
38
38
|
"manifest.virtual.js": "sha384-ZDNoF9QVhVcVpkjIK6NH15S6I5c6OBshiJpXSOYjNsIB+298I0a7g3aFO+jyq4Vb",
|
|
39
|
-
"manifest.js": "sha384-
|
|
39
|
+
"manifest.js": "sha384-m5WiqHv+23ZnBvvIt9NtUGE2Gupaw9jDpKuonIH9HZCHc+LR8wbitDImJoNPH9CI"
|
|
40
40
|
}
|
package/lib/manifest.js
CHANGED
|
@@ -597,14 +597,55 @@
|
|
|
597
597
|
const A = window.Alpine;
|
|
598
598
|
if (window.ManifestRichText || !A || !A.magic || !A.reactive || window.__manifestRichStub) return;
|
|
599
599
|
const state = window.__manifestRichStub = A.reactive({ v: 0 });
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
600
|
+
// Writes ($rich.value = …, $rich.all = …) are kept per element and applied to its field at handover
|
|
601
|
+
const writes = window.__manifestRichStubWrites = [], perEl = new WeakMap();
|
|
602
|
+
const deref = (r) => r && typeof r.deref === 'function' ? r.deref() : r;
|
|
603
|
+
const base = { value: '', link: '', selection: null, all: {}, active: () => false, can: () => false, markdown: () => '', html: () => '' };
|
|
604
|
+
const idleFor = (el) => {
|
|
605
|
+
if (!perEl.has(el)) perEl.set(el, new Proxy(base, {
|
|
606
|
+
get: (t, k) => { const w = writes.find(x => deref(x[0]) === el && x[1] === k); return w ? w[2] : k in t ? t[k] : (typeof k === 'string' ? () => { } : undefined); },
|
|
607
|
+
// The latest write per element and key; elements gone from the page are let go
|
|
608
|
+
set: (t, k, v) => {
|
|
609
|
+
for (let i = writes.length - 1; i >= 0; i--) { const e = deref(writes[i][0]); if (!e || !e.isConnected || (e === el && writes[i][1] === k)) writes.splice(i, 1); }
|
|
610
|
+
writes.push([typeof WeakRef === 'function' ? new WeakRef(el) : el, k, v]);
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
}));
|
|
614
|
+
return perEl.get(el);
|
|
615
|
+
};
|
|
616
|
+
A.magic('rich', (el, u) => { void state.v; return window.__manifestRichMagic ? window.__manifestRichMagic(el, u) : idleFor(el); });
|
|
603
617
|
};
|
|
604
618
|
document.addEventListener('alpine:init', install, { once: true });
|
|
605
619
|
if (window.Alpine && window.Alpine.magic) install();
|
|
606
620
|
}
|
|
607
621
|
stubRichMagic();
|
|
622
|
+
// $edit likewise: Edit loads behind a gate (data-plugins or Manifest.loadPlugin('edit')), so until
|
|
623
|
+
// it registers, $edit is an idle stand-in; Edit bumps `v` and the stand-in delegates to it.
|
|
624
|
+
let editStubArmed = false;
|
|
625
|
+
function stubEditMagic() {
|
|
626
|
+
if (typeof window === 'undefined' || typeof document === 'undefined' || editStubArmed) return;
|
|
627
|
+
editStubArmed = true;
|
|
628
|
+
const install = () => {
|
|
629
|
+
const A = window.Alpine;
|
|
630
|
+
if (window.__manifestEditMagic || !A || !A.magic || !A.reactive || window.__manifestEditStub) return;
|
|
631
|
+
const state = window.__manifestEditStub = A.reactive({ v: 0 });
|
|
632
|
+
const none = () => false;
|
|
633
|
+
// Writes ($edit.onPublish = …) are kept and applied to the store at handover
|
|
634
|
+
const writes = window.__manifestEditStubWrites = {};
|
|
635
|
+
const idle = new Proxy({
|
|
636
|
+
active: false, canUndo: false, canRedo: false, version: 0, stale: [], conflicts: [], onPublish: null, target: null,
|
|
637
|
+
can: none, scope: () => null, block: () => null, patches: () => [], export: () => ({ log: [], cursor: 0 }),
|
|
638
|
+
publish: () => Promise.resolve({ results: [] }), undo: () => Promise.resolve(false), redo: () => Promise.resolve(false),
|
|
639
|
+
classes: new Proxy({}, { get: () => none })
|
|
640
|
+
}, { get: (t, k) => k in writes ? writes[k] : k in t ? t[k] : (typeof k === 'string' ? none : undefined), set: (t, k, v) => { writes[k] = v; return true; } });
|
|
641
|
+
A.magic('edit', (el, u) => { void state.v; return window.__manifestEditMagic ? window.__manifestEditMagic(el, u) : idle; });
|
|
642
|
+
};
|
|
643
|
+
document.addEventListener('alpine:init', install, { once: true });
|
|
644
|
+
if (window.Alpine && window.Alpine.magic) install();
|
|
645
|
+
}
|
|
646
|
+
stubEditMagic();
|
|
647
|
+
// Plugins loaded after Alpine started adopt what its first walk already passed
|
|
648
|
+
if (typeof document !== 'undefined') document.addEventListener('alpine:initialized', () => { window.__manifestAlpineStarted = true; }, { once: true });
|
|
608
649
|
// Framework web components — <x-*> tags that are NOT project components.
|
|
609
650
|
const FRAMEWORK_TAG_RE = /^x-(code|code-group)$/;
|
|
610
651
|
function detectUsagePlugins(alreadyLoaded, root = typeof document === 'undefined' ? null : document) {
|