mnfst 0.5.230 → 0.5.231
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.css +20 -2
- package/lib/manifest.data.js +1 -1
- package/lib/manifest.edit.css +7 -1
- package/lib/manifest.edit.js +137 -16
- package/lib/manifest.integrity.json +5 -5
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.sw.js +1 -1
- package/lib/manifest.text.edit.css +13 -1
- package/lib/manifest.text.edit.js +251 -54
- package/package.json +1 -1
package/lib/manifest.css
CHANGED
|
@@ -2956,7 +2956,13 @@
|
|
|
2956
2956
|
|
|
2957
2957
|
/* No injected grip: the row itself is the handle (grab cursor), while text
|
|
2958
2958
|
inside stays a text surface (text cursor, below). Rows that are pure text
|
|
2959
|
-
still reorder by keyboard. */
|
|
2959
|
+
still reorder by keyboard (Alt/Option + arrows). */
|
|
2960
|
+
|
|
2961
|
+
/* Hover hint in the UI scope; quiet while a drag is in progress. */
|
|
2962
|
+
:where([data-edit-ui] [data-edit-sortable], [data-edit-ui][data-edit-sortable]):where(:hover):where(:not([data-edit-dragging], [data-edit-ghost], [data-edit-dragging-in] [data-edit-sortable])) {
|
|
2963
|
+
outline: 1px solid color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
|
|
2964
|
+
outline-offset: 3px;
|
|
2965
|
+
}
|
|
2960
2966
|
|
|
2961
2967
|
:where([data-edit-sortable], [data-edit-movable]):focus-visible {
|
|
2962
2968
|
outline: 2px solid var(--edit-accent, var(--color-brand-content));
|
|
@@ -4245,6 +4251,11 @@
|
|
|
4245
4251
|
}
|
|
4246
4252
|
}
|
|
4247
4253
|
|
|
4254
|
+
/* A .page background applies to an unstyled area too. */
|
|
4255
|
+
:where([data-text-edit].unstyle[style*="--text-edit-background"]) {
|
|
4256
|
+
background: var(--text-edit-background);
|
|
4257
|
+
}
|
|
4258
|
+
|
|
4248
4259
|
/* An unstyled area has no field padding, so its placeholder hugs the line. */
|
|
4249
4260
|
:where([data-text-edit].unstyle)[data-text-edit-empty]::before {
|
|
4250
4261
|
inset-block-start: 0;
|
|
@@ -4320,11 +4331,18 @@
|
|
|
4320
4331
|
}
|
|
4321
4332
|
}
|
|
4322
4333
|
|
|
4323
|
-
|
|
4334
|
+
/* Icon-only buttons; text buttons keep the house padding. */
|
|
4335
|
+
:where(button[data-text-edit-control]:has(> svg:only-child)) {
|
|
4324
4336
|
padding: 0.35rem;
|
|
4325
4337
|
line-height: 0;
|
|
4326
4338
|
}
|
|
4327
4339
|
|
|
4340
|
+
/* The reset drops pointer events on disabled elements; a click would fall
|
|
4341
|
+
through and blur the area. The plugin ignores it instead. */
|
|
4342
|
+
:where([data-text-edit-control][aria-disabled=true]) {
|
|
4343
|
+
pointer-events: auto;
|
|
4344
|
+
}
|
|
4345
|
+
|
|
4328
4346
|
/* ---- Editable content ----
|
|
4329
4347
|
The editor authors real block elements, so style them as elements. Deliberately
|
|
4330
4348
|
lighter than the .prose class, which sets a reading measure this field can't use. */
|
package/lib/manifest.data.js
CHANGED
package/lib/manifest.edit.css
CHANGED
|
@@ -36,7 +36,13 @@
|
|
|
36
36
|
|
|
37
37
|
/* No injected grip: the row itself is the handle (grab cursor), while text
|
|
38
38
|
inside stays a text surface (text cursor, below). Rows that are pure text
|
|
39
|
-
still reorder by keyboard. */
|
|
39
|
+
still reorder by keyboard (Alt/Option + arrows). */
|
|
40
|
+
|
|
41
|
+
/* Hover hint in the UI scope; quiet while a drag is in progress. */
|
|
42
|
+
:where([data-edit-ui] [data-edit-sortable], [data-edit-ui][data-edit-sortable]):where(:hover):where(:not([data-edit-dragging], [data-edit-ghost], [data-edit-dragging-in] [data-edit-sortable])) {
|
|
43
|
+
outline: 1px solid color-mix(in oklch, var(--edit-accent, var(--color-brand-content)) 45%, transparent);
|
|
44
|
+
outline-offset: 3px;
|
|
45
|
+
}
|
|
40
46
|
|
|
41
47
|
:where([data-edit-sortable], [data-edit-movable]):focus-visible {
|
|
42
48
|
outline: 2px solid var(--edit-accent, var(--color-brand-content));
|
package/lib/manifest.edit.js
CHANGED
|
@@ -488,7 +488,7 @@
|
|
|
488
488
|
if (d.kind === 'data-splice') applySplice(d, true);
|
|
489
489
|
else if (d.kind === 'data') { await applySnap(areaByKey(d.region), d.kind, d.before); lastSnap[d.region] = d.before; }
|
|
490
490
|
else dispatchApply(d);
|
|
491
|
-
saveState(); refresh();
|
|
491
|
+
rebaseFocused(); saveState(); refresh();
|
|
492
492
|
}
|
|
493
493
|
async function redo() {
|
|
494
494
|
if (cursor >= log.length) return; const d = log[cursor++];
|
|
@@ -496,7 +496,7 @@
|
|
|
496
496
|
if (d.kind === 'data-splice') applySplice(d, false);
|
|
497
497
|
else if (d.kind === 'data') { await applySnap(areaByKey(d.region), d.kind, d.after); lastSnap[d.region] = d.after; }
|
|
498
498
|
else dispatchApply(d);
|
|
499
|
-
saveState(); refresh();
|
|
499
|
+
rebaseFocused(); saveState(); refresh();
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
/* ---- Projections (static/data); component projections come from componentState() ---- */
|
|
@@ -735,10 +735,13 @@
|
|
|
735
735
|
|
|
736
736
|
/* ---- Drag: reorder (in-flow, pointer+keyboard) OR move (positioned) ----
|
|
737
737
|
Pointer-based so it works with mouse, touch, and pen (HTML5 DnD was mouse-only).
|
|
738
|
-
Keyboard: focus an item, Space/Enter to grab, arrows to move, Enter/Esc to drop/cancel
|
|
738
|
+
Keyboard: focus an item, Space/Enter to grab, arrows to move, Enter/Esc to drop/cancel;
|
|
739
|
+
Alt/Option+arrow moves the focused row one step; in text only Up/Down (Left/Right stay word-jump).
|
|
740
|
+
CSS-var config:
|
|
741
|
+
--edit-drop-reach 48px how far outside a cross-region destination a drop still lands in it */
|
|
739
742
|
const isPositioned = (el) => { const p = getComputedStyle(el).position; return p === 'absolute' || p === 'fixed'; };
|
|
740
743
|
let grabbed = null, liveRegion;
|
|
741
|
-
function announce(msg) { if (!liveRegion) { liveRegion = document.createElement('div'); liveRegion.setAttribute('data-edit-live', ''); liveRegion.setAttribute('aria-live', 'polite'); document.body.appendChild(liveRegion); } liveRegion.textContent = msg; }
|
|
744
|
+
function announce(msg) { if (!liveRegion || !liveRegion.isConnected) { liveRegion = document.createElement('div'); liveRegion.setAttribute('data-edit-live', ''); liveRegion.setAttribute('aria-live', 'polite'); document.body.appendChild(liveRegion); } liveRegion.textContent = msg; }
|
|
742
745
|
|
|
743
746
|
function makeSortable(container) {
|
|
744
747
|
sortableChildren(container).forEach(child => {
|
|
@@ -820,7 +823,7 @@
|
|
|
820
823
|
active = true; lift(ev); paint();
|
|
821
824
|
}
|
|
822
825
|
ev.preventDefault();
|
|
823
|
-
const over = (crossOK && dropArea(px, py)) || ghost.parentElement; // cross-region drop search
|
|
826
|
+
const over = (crossOK && dropArea(px, py, area)) || ghost.parentElement; // cross-region drop search
|
|
824
827
|
if (over !== hovered) {
|
|
825
828
|
if (hovered !== container) hovered.removeAttribute('data-edit-dragging-in');
|
|
826
829
|
hovered = over;
|
|
@@ -874,13 +877,27 @@
|
|
|
874
877
|
if (dragged.parentElement === container && (ref === dragged || ref === dragged.nextElementSibling)) return;
|
|
875
878
|
if (ref == null) container.appendChild(dragged); else container.insertBefore(dragged, ref);
|
|
876
879
|
}
|
|
877
|
-
// Cross-region drop target: the innermost
|
|
878
|
-
|
|
880
|
+
// Cross-region drop target: the innermost valid region under the pointer, else
|
|
881
|
+
// the nearest valid one within reach — regions are often shorter than their column.
|
|
882
|
+
const dropValid = (a) => !!a && !!a._edit && isActive(a) && !locked(a) && capOf(a, 'sort') && classify(a) === 'static' && topArea(a);
|
|
883
|
+
function dropArea(x, y, origin) {
|
|
879
884
|
const hit = document.elementFromPoint ? document.elementFromPoint(x, y) : null;
|
|
880
885
|
for (let a = hit && hit.closest ? hit.closest('[data-edit-area]') : null; a; a = a.parentElement && a.parentElement.closest('[data-edit-area]')) {
|
|
881
|
-
if (
|
|
886
|
+
if (dropValid(a)) return a;
|
|
882
887
|
}
|
|
883
|
-
|
|
888
|
+
const cands = [origin, ...document.querySelectorAll('[data-edit-drop-target]')].filter((a, i, all) => dropValid(a) && all.indexOf(a) === i);
|
|
889
|
+
const inside = (r, pad) => x >= r.left - pad && x <= r.right + pad && y >= r.top - pad && y <= r.bottom + pad;
|
|
890
|
+
let best = null, bestD = Infinity;
|
|
891
|
+
for (const a of cands) {
|
|
892
|
+
const r = a.getBoundingClientRect();
|
|
893
|
+
const d = Math.hypot(Math.max(r.left - x, 0, x - r.right), Math.max(r.top - y, 0, y - r.bottom));
|
|
894
|
+
const reach = toPx(cssVar(a, '--edit-drop-reach', ''), a, 'w') ?? 48;
|
|
895
|
+
// The block wrapping the region (its column) counts as its surface, unless it wraps another candidate too.
|
|
896
|
+
const p = a.parentElement;
|
|
897
|
+
const column = p && p !== document.body && p !== document.documentElement && !cands.some(o => o !== a && p.contains(o)) && inside(p.getBoundingClientRect(), 0);
|
|
898
|
+
if ((inside(r, reach) || column) && d < bestD) { best = a; bestD = d; }
|
|
899
|
+
}
|
|
900
|
+
return best;
|
|
884
901
|
}
|
|
885
902
|
function finishReorder(area) { if (classify(area) === 'data') { applyDataOrder(area, sortableChildren(area).map(c => itemKey(c, area))); commit(area); } else commitStaticOrder(area); }
|
|
886
903
|
// Reading-order insertion: block, inline, flex row/col/wrap, grid auto-flow.
|
|
@@ -897,10 +914,21 @@
|
|
|
897
914
|
}
|
|
898
915
|
return null;
|
|
899
916
|
}
|
|
900
|
-
// Keyboard reorder: grab → arrows move among siblings → drop.
|
|
917
|
+
// Keyboard reorder: grab → arrows move among siblings → drop. Space/Enter belong
|
|
918
|
+
// to text and controls inside the row; Alt+arrow moves the row directly.
|
|
919
|
+
const ownControl = (e, item) => e.target !== item && !!e.target.closest && item.contains(e.target.closest('a[href], button, summary, [role="button"], [role="link"]'));
|
|
901
920
|
function onItemKeydown(e) {
|
|
902
921
|
const item = this, area = item.closest('[data-edit-area]'); if (!isActive(area)) return;
|
|
903
|
-
if (e.
|
|
922
|
+
if (e._editRow) return; // the innermost row claims the key
|
|
923
|
+
const dir = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 }[e.key];
|
|
924
|
+
const wordJump = inText(e) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight');
|
|
925
|
+
if (dir != null && e.altKey && !e.shiftKey && !e.metaKey && !e.ctrlKey && !wordJump) {
|
|
926
|
+
const home = item.parentElement && item.parentElement.closest('[data-edit-area]'); // the row's region, never the row itself
|
|
927
|
+
if (!isActive(home)) return;
|
|
928
|
+
e._editRow = true; e.preventDefault(); stepRow(item, home, dir); return;
|
|
929
|
+
}
|
|
930
|
+
if ((e.key === ' ' || e.key === 'Enter') && !inText(e) && !ownControl(e, item)) {
|
|
931
|
+
e._editRow = true;
|
|
904
932
|
e.preventDefault();
|
|
905
933
|
if (grabbed === item) { grabbed = null; item.removeAttribute('data-edit-grabbed'); finishReorder(area); item.focus(); announce('Dropped'); }
|
|
906
934
|
else { if (grabbed) grabbed.removeAttribute('data-edit-grabbed'); grabbed = item; item.setAttribute('data-edit-grabbed', ''); announce('Grabbed — arrow keys to move, Enter to drop'); }
|
|
@@ -908,14 +936,32 @@
|
|
|
908
936
|
}
|
|
909
937
|
if (grabbed !== item) return;
|
|
910
938
|
if (e.key === 'Escape') { e.preventDefault(); grabbed = null; item.removeAttribute('data-edit-grabbed'); item.focus(); announce('Cancelled'); return; }
|
|
911
|
-
const dir = { ArrowLeft: -1, ArrowUp: -1, ArrowRight: 1, ArrowDown: 1 }[e.key];
|
|
912
939
|
if (dir == null) return;
|
|
940
|
+
e._editRow = true;
|
|
913
941
|
e.preventDefault();
|
|
914
942
|
const sibs = sortableChildren(item.parentElement).filter(c => !locked(c)), i = sibs.indexOf(item), j = i + dir;
|
|
915
943
|
if (j < 0 || j >= sibs.length) return;
|
|
916
944
|
if (dir > 0) sibs[j].after(item); else sibs[j].before(item);
|
|
917
945
|
item.focus(); announce(`Position ${j + 1} of ${sibs.length}`);
|
|
918
946
|
}
|
|
947
|
+
// One step, committed at once. The row itself never leaves the DOM — its
|
|
948
|
+
// neighbours move round it — so focus and the caret in its text survive.
|
|
949
|
+
function stepRow(item, area, dir) {
|
|
950
|
+
const sibs = sortableChildren(item.parentElement).filter(c => !locked(c)), i = sibs.indexOf(item), j = i + dir;
|
|
951
|
+
if (i < 0 || j < 0 || j >= sibs.length) { announce(`Position ${i + 1} of ${sibs.length}`); return; }
|
|
952
|
+
const other = sibs[j], between = [];
|
|
953
|
+
for (let n = dir > 0 ? item.nextElementSibling : item.previousElementSibling; n && n !== other; n = dir > 0 ? n.nextElementSibling : n.previousElementSibling) between.push(n);
|
|
954
|
+
if (dir > 0) { item.before(other); between.forEach(n => item.before(n)); }
|
|
955
|
+
else { item.after(other); between.forEach(n => item.after(n)); }
|
|
956
|
+
const focused = document.activeElement, sel = window.getSelection && window.getSelection();
|
|
957
|
+
const range = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
958
|
+
finishReorder(area);
|
|
959
|
+
if (focused && focused.isConnected && document.activeElement !== focused) {
|
|
960
|
+
focused.focus();
|
|
961
|
+
if (range) try { sel.removeAllRanges(); sel.addRange(range); } catch { }
|
|
962
|
+
}
|
|
963
|
+
announce(`Moved to position ${j + 1} of ${sibs.length}`);
|
|
964
|
+
}
|
|
919
965
|
// Move mode: positioned elements → pointer-drag left/top (touch-capable) + keyboard nudge, unit-preserving.
|
|
920
966
|
function makeMovable(el) {
|
|
921
967
|
el.setAttribute('data-edit-movable', ''); el.tabIndex = 0; el.setAttribute('role', 'application'); el.setAttribute('aria-label', 'Draggable; arrow keys to move');
|
|
@@ -958,7 +1004,7 @@
|
|
|
958
1004
|
--edit-size-snap-distance 1rem magnet tolerance (NOT a grid step)
|
|
959
1005
|
--edit-size-snap-distance-x/-y per-axis tolerance
|
|
960
1006
|
--edit-size-collapse-x/-y 120px collapse below this → [data-edit-collapsed] + 'edit:collapse' event
|
|
961
|
-
(min/max come from the element's native min-/max-width/height.) */
|
|
1007
|
+
(min/max come from the element's native min-/max-width/height; % against the containing block.) */
|
|
962
1008
|
let overlayEl;
|
|
963
1009
|
// Fires while dragging (done:false) and once on commit (done:true), so consumers
|
|
964
1010
|
// that must re-measure — charts, virtual lists — can track the drag and settle.
|
|
@@ -978,6 +1024,45 @@
|
|
|
978
1024
|
const rtl = isRTL(el);
|
|
979
1025
|
return pos.replace(/start/g, rtl ? 'right' : 'left').replace(/end/g, rtl ? 'left' : 'right');
|
|
980
1026
|
}
|
|
1027
|
+
// Containing-block size for % limits: the parent's content box in flow, the
|
|
1028
|
+
// offsetParent's padding box when absolute, the viewport when fixed. An
|
|
1029
|
+
// auto-height parent has no definite height, so % height limits drop (null).
|
|
1030
|
+
function blockSize(el, dim) {
|
|
1031
|
+
const pos = getComputedStyle(el).position;
|
|
1032
|
+
if (pos === 'fixed') return dim === 'w' ? document.documentElement.clientWidth : document.documentElement.clientHeight;
|
|
1033
|
+
const cb = pos === 'absolute' ? el.offsetParent || el.parentElement : el.parentElement;
|
|
1034
|
+
if (!cb) return null;
|
|
1035
|
+
const ps = getComputedStyle(cb), w = dim === 'w';
|
|
1036
|
+
const box = w ? cb.clientWidth : cb.clientHeight;
|
|
1037
|
+
if (pos === 'absolute') return box;
|
|
1038
|
+
if (!w && !definiteHeight(cb)) return null;
|
|
1039
|
+
return box - (parseFloat(w ? ps.paddingLeft : ps.paddingTop) || 0) - (parseFloat(w ? ps.paddingRight : ps.paddingBottom) || 0);
|
|
1040
|
+
}
|
|
1041
|
+
// Specified height via Typed OM; without it (Firefox) only an inline height counts.
|
|
1042
|
+
function definiteHeight(el) {
|
|
1043
|
+
for (let n = el, depth = 0; n && depth < 32; n = n.parentElement, depth++) {
|
|
1044
|
+
if (n === document.documentElement) return true; // % on the root resolves against the viewport
|
|
1045
|
+
const pos = getComputedStyle(n).position;
|
|
1046
|
+
if (pos === 'absolute' || pos === 'fixed') return true;
|
|
1047
|
+
let h;
|
|
1048
|
+
if (n.computedStyleMap) { try { h = String(n.computedStyleMap().get('height')).trim(); } catch { } }
|
|
1049
|
+
if (h == null) h = (n.style && n.style.height || '').trim() || 'auto';
|
|
1050
|
+
if (h === 'auto' || h === '') return false;
|
|
1051
|
+
if (!h.endsWith('%')) return true; // a length (or calc) is definite
|
|
1052
|
+
}
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
function limitPx(v, el, dim, none) {
|
|
1056
|
+
v = String(v || '').trim();
|
|
1057
|
+
if (!v || v === 'none' || v === 'auto') return none;
|
|
1058
|
+
if (/^-?[\d.]+%$/.test(v)) { const b = blockSize(el, dim); return b == null ? none : parseFloat(v) / 100 * b; }
|
|
1059
|
+
const px = toPx(v, el, dim);
|
|
1060
|
+
return px == null ? none : px;
|
|
1061
|
+
}
|
|
1062
|
+
function limits(el, cs) {
|
|
1063
|
+
const minW = limitPx(cs.minWidth, el, 'w', 0), minH = limitPx(cs.minHeight, el, 'h', 0);
|
|
1064
|
+
return { minW, minH, maxW: Math.max(minW, limitPx(cs.maxWidth, el, 'w', Infinity)), maxH: Math.max(minH, limitPx(cs.maxHeight, el, 'h', Infinity)) }; // min wins, as in CSS
|
|
1065
|
+
}
|
|
981
1066
|
const defaultEdges = (axes) => axes === 'x' ? ['left', 'right'] : axes === 'y' ? ['top', 'bottom'] : ['top', 'right', 'bottom', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
|
982
1067
|
|
|
983
1068
|
function armSize(el) {
|
|
@@ -1011,7 +1096,7 @@
|
|
|
1011
1096
|
const [kx, ky] = map[e.key], step = e.shiftKey ? 32 : 8, cs = getComputedStyle(el);
|
|
1012
1097
|
const right = pos.includes('right'), left = pos.includes('left'), top = pos.includes('top'), bottom = pos.includes('bottom');
|
|
1013
1098
|
const wu = unitOf(el.style.width) || 'px', hu = unitOf(el.style.height) || 'px';
|
|
1014
|
-
const minW
|
|
1099
|
+
const { minW, maxW, minH, maxH } = limits(el, cs);
|
|
1015
1100
|
if ((right || left) && kx) { const w = clamp((parseFloat(cs.width) || 0) + kx * step * (right ? 1 : -1), minW, maxW); el.style.width = toUnit(w, wu, el, el.parentElement, 'w') + wu; }
|
|
1016
1101
|
if ((top || bottom) && ky) { const h = clamp((parseFloat(cs.height) || 0) + ky * step * (bottom ? 1 : -1), minH, maxH); el.style.height = toUnit(h, hu, el, el.parentElement, 'h') + hu; }
|
|
1017
1102
|
sizeEvent(el, false);
|
|
@@ -1028,8 +1113,7 @@
|
|
|
1028
1113
|
const cs = getComputedStyle(el), rect = el.getBoundingClientRect();
|
|
1029
1114
|
const wu = unitOf(el.style.width) || 'px', hu = unitOf(el.style.height) || 'px';
|
|
1030
1115
|
const baseW = parseFloat(cs.width) || rect.width, baseH = parseFloat(cs.height) || rect.height, sx = e.clientX, sy = e.clientY;
|
|
1031
|
-
const minW
|
|
1032
|
-
const minH = parseFloat(cs.minHeight) || 0, maxH = parseFloat(cs.maxHeight) || Infinity;
|
|
1116
|
+
const { minW, maxW, minH, maxH } = limits(el, cs);
|
|
1033
1117
|
const right = pos.includes('right'), left = pos.includes('left'), bottom = pos.includes('bottom'), top = pos.includes('top');
|
|
1034
1118
|
const tolX = toPx(cssVar(el, '--edit-size-snap-distance-x', '') || cssVar(el, '--edit-size-snap-distance', '0'), el, 'w') || 0;
|
|
1035
1119
|
const tolY = toPx(cssVar(el, '--edit-size-snap-distance-y', '') || cssVar(el, '--edit-size-snap-distance', '0'), el, 'h') || 0;
|
|
@@ -1560,6 +1644,42 @@
|
|
|
1560
1644
|
bar.querySelector('[data-a="redo"]').disabled = cursor >= log.length;
|
|
1561
1645
|
}
|
|
1562
1646
|
|
|
1647
|
+
/* ---- Undo/redo hotkeys: Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z, Ctrl+Y ----
|
|
1648
|
+
On wherever a region is active; data-edit-hotkeys="off" on a region or any
|
|
1649
|
+
ancestor opts it out. Text that is still being typed keeps the browser's own
|
|
1650
|
+
undo until it commits on blur; fields and x-text-edit keep theirs always. */
|
|
1651
|
+
const leafText = (el) => el.hasAttribute('data-edit-field') && !el.hasAttribute('data-edit-rich') ? el.textContent : el.innerHTML.trim();
|
|
1652
|
+
const hotkeysOff = (el) => !!el.closest('[data-edit-hotkeys="off"]');
|
|
1653
|
+
// A leaf the user was typing in commits on blur against its focus-time value;
|
|
1654
|
+
// after history rewrites it under the caret, that value must follow (again
|
|
1655
|
+
// once Alpine has re-rendered a data field).
|
|
1656
|
+
function rebaseFocused() {
|
|
1657
|
+
const el = document.activeElement;
|
|
1658
|
+
if (!el || el._preEdit === undefined || !el.isContentEditable) return;
|
|
1659
|
+
const sync = () => { if (document.activeElement === el) el._preEdit = leafText(el); };
|
|
1660
|
+
sync(); setTimeout(sync, 0);
|
|
1661
|
+
}
|
|
1662
|
+
function onHistoryKey(e) {
|
|
1663
|
+
if (e.defaultPrevented || e.altKey || !(e.metaKey || e.ctrlKey) || dragged) return; // never mid-drag
|
|
1664
|
+
const k = (e.key || '').toLowerCase();
|
|
1665
|
+
const isRedo = (k === 'z' && e.shiftKey) || (k === 'y' && e.ctrlKey && !e.metaKey && !e.shiftKey);
|
|
1666
|
+
if (!isRedo && !(k === 'z' && !e.shiftKey)) return;
|
|
1667
|
+
const t = e.target && e.target.nodeType === 1 ? e.target : document.body;
|
|
1668
|
+
if (/^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName) || t.closest('[data-text-edit]')) return;
|
|
1669
|
+
const region = t.closest('[data-edit-area]');
|
|
1670
|
+
if (region) {
|
|
1671
|
+
const top = regionOf(region);
|
|
1672
|
+
if (!top || !isActive(top) || hotkeysOff(region)) return;
|
|
1673
|
+
} else {
|
|
1674
|
+
if (t.isContentEditable) return; // someone else's editor
|
|
1675
|
+
if (!areas().some(a => isActive(a) && !hotkeysOff(a))) return;
|
|
1676
|
+
}
|
|
1677
|
+
if (t.isContentEditable && t._preEdit !== undefined && leafText(t) !== t._preEdit) return; // uncommitted typing: native undo
|
|
1678
|
+
if (isRedo ? cursor >= log.length : cursor === 0) return;
|
|
1679
|
+
e.preventDefault();
|
|
1680
|
+
isRedo ? redo() : undo();
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1563
1683
|
|
|
1564
1684
|
/* ---- Restore persisted edits + boot (registers x-edit directive + $edit magic) ---- */
|
|
1565
1685
|
function restore() {
|
|
@@ -1615,6 +1735,7 @@
|
|
|
1615
1735
|
loadState();
|
|
1616
1736
|
setTimeout(() => { armAll(); restore(); }, 450); // arm (captures baselines) THEN re-apply persisted edits
|
|
1617
1737
|
window.addEventListener('manifest:route-change', () => setTimeout(refresh, 0)); // chrome follows the route
|
|
1738
|
+
document.addEventListener('keydown', onHistoryKey);
|
|
1618
1739
|
}
|
|
1619
1740
|
document.addEventListener('alpine:init', init);
|
|
1620
1741
|
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-LoFQ2CaZ+pyCCoUY2+wayzRnujS3dF/2SENBPtEtWTewy4yNUNuC7LvhfwFv+XUJ",
|
|
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,12 +11,12 @@
|
|
|
11
11
|
"manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
|
|
12
12
|
"manifest.components.js": "sha384-4bc3HgPvF9DHMiOG08w+nWXI7Hho01RuAPAUL9xyLxOiS2igeZpulCVcvr5aT9v6",
|
|
13
13
|
"manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
|
|
14
|
-
"manifest.data.js": "sha384-
|
|
14
|
+
"manifest.data.js": "sha384-L9PweXBo47bGZe7wR016DllF8FJiufSINnmTagbs8QgAmC7JMrosI7r45MsIPrUH",
|
|
15
15
|
"manifest.datepicker.js": "sha384-XnbeURni2X91ozVzatQcSjYYIS4MoW12dBRJnYAGBr0nCQ624heH5s+xtQSpLZX6",
|
|
16
16
|
"manifest.defer.js": "sha384-WcCf8YQ1bKIXCLSZ24a0bJwepbYntYc8TzQM50hklo5VMIH8AtzDnwl6WU4prRv1",
|
|
17
17
|
"manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
|
|
18
18
|
"manifest.dropdowns.js": "sha384-0ilj9AvRph0LacIBJSqwpu1fvPRn5gcWbhIG1SOpaoM/Q8idMbKBLupwM+5O0UaX",
|
|
19
|
-
"manifest.edit.js": "sha384-
|
|
19
|
+
"manifest.edit.js": "sha384-RmZQXMw2jazBen79OOZ1dqAY858JIJ1ow3dlMoun8wNeJ7TIRJ85rd2P7xu/ucHR",
|
|
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",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
|
|
28
28
|
"manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
|
|
29
29
|
"manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
|
|
30
|
-
"manifest.sw.js": "sha384-
|
|
30
|
+
"manifest.sw.js": "sha384-ycxQxP+KCw2Vgss5IfUQ7JsNkkMNkN7hs6ND0KQt9BnVQ3VUpAilXnCbdNdt3HUW",
|
|
31
31
|
"manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
|
|
32
32
|
"manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
|
|
33
|
-
"manifest.text.edit.js": "sha384
|
|
33
|
+
"manifest.text.edit.js": "sha384-+VkMe/Ac9mhPu3YWhofY0Wxbm3rAFmdBd/z7oQW+Uu/Q1h5gFBIZ+Mx0ENseLq/o",
|
|
34
34
|
"manifest.toasts.js": "sha384-3zkWxzw693pxg3QDFsoYCA3CqYaLKmBaMArULXODD66Utolpewl0OAeTB92aWUVv",
|
|
35
35
|
"manifest.tooltips.js": "sha384-4b24PH5WsfFsewGea5SMWLwmrhC4kD6eDP/vvjNyjXYnQXXFGi4nLdojrIOwzG8M",
|
|
36
36
|
"manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
|