@jsenv/navi 0.30.24 → 0.30.26
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/dist/dev/jsenv_navi.js +194 -24
- package/dist/dev/jsenv_navi.js.map +5 -5
- package/dist/jsenv_navi.js +194 -24
- package/dist/jsenv_navi.js.map +5 -5
- package/docs/AI_INSTRUCTIONS.md +5 -1
- package/docs/actions.md +4 -0
- package/docs/dynamic_import.md +18 -0
- package/docs/resource.md +119 -10
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -3752,6 +3752,7 @@ ${lines.join("\n")}`);
|
|
|
3752
3752
|
|
|
3753
3753
|
const actionToRunPrivateProperties =
|
|
3754
3754
|
getActionPrivateProperties(actionToPrerunOrRun);
|
|
3755
|
+
actionToRunPrivateProperties.answeredAsync = false;
|
|
3755
3756
|
let performRunResult;
|
|
3756
3757
|
try {
|
|
3757
3758
|
performRunResult = actionToRunPrivateProperties.performRun({
|
|
@@ -3776,6 +3777,7 @@ ${lines.join("\n")}`);
|
|
|
3776
3777
|
activationWeakSet.add(actionToPrerunOrRun);
|
|
3777
3778
|
|
|
3778
3779
|
if (performRunResult && typeof performRunResult.then === "function") {
|
|
3780
|
+
actionToRunPrivateProperties.answeredAsync = true;
|
|
3779
3781
|
actionPromiseMap.set(actionToPrerunOrRun, performRunResult);
|
|
3780
3782
|
allThenableArray.push(performRunResult);
|
|
3781
3783
|
hasAsync = true;
|
|
@@ -3805,7 +3807,9 @@ ${lines.join("\n")}`);
|
|
|
3805
3807
|
|
|
3806
3808
|
// Execute promotions (prerun -> run requested)
|
|
3807
3809
|
for (const actionToPromote of willPromoteSet) {
|
|
3808
|
-
|
|
3810
|
+
writeActionState(() => {
|
|
3811
|
+
actionToPromote.isPrerunSignal.value = false;
|
|
3812
|
+
});
|
|
3809
3813
|
}
|
|
3810
3814
|
}
|
|
3811
3815
|
|
|
@@ -3814,13 +3818,21 @@ ${lines.join("\n")}`);
|
|
|
3814
3818
|
// that IS carrying it. `await action.run()` then means the same thing to
|
|
3815
3819
|
// every caller: the second one waits for the first one's request and reads
|
|
3816
3820
|
// the same data, instead of being handed `undefined` before the network has
|
|
3817
|
-
// said anything.
|
|
3821
|
+
// said anything. It answers in the tense of that run: the answer of a run
|
|
3822
|
+
// that answered with a promise comes back as one, so `run().catch()` holds
|
|
3823
|
+
// whatever state the instance is in. See the run() JSDoc.
|
|
3818
3824
|
{
|
|
3819
3825
|
for (const actionToJoin of willJoinSet) {
|
|
3820
3826
|
const runningPromise = actionPromiseMap.get(actionToJoin);
|
|
3821
3827
|
if (runningPromise) {
|
|
3822
3828
|
hasAsync = true;
|
|
3823
3829
|
resultArray.push({ type: "async", promise: runningPromise });
|
|
3830
|
+
} else if (getActionPrivateProperties(actionToJoin).answeredAsync) {
|
|
3831
|
+
hasAsync = true;
|
|
3832
|
+
resultArray.push({
|
|
3833
|
+
type: "async",
|
|
3834
|
+
promise: Promise.resolve(actionToJoin.dataSignal.peek()),
|
|
3835
|
+
});
|
|
3824
3836
|
} else {
|
|
3825
3837
|
resultArray.push({
|
|
3826
3838
|
type: "sync",
|
|
@@ -4065,6 +4077,10 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4065
4077
|
* the one that asked first or the one that arrived while the request was
|
|
4066
4078
|
* out. Use `rerun()` to force a fresh run ("refresh", "check now", any
|
|
4067
4079
|
* explicit user intent to go back to the network).
|
|
4080
|
+
*
|
|
4081
|
+
* An asynchronous callback answers with a promise, joined or not:
|
|
4082
|
+
* `ACTION.run().catch(…)` holds in every state. A synchronous one answers
|
|
4083
|
+
* synchronously, and throws its failure.
|
|
4068
4084
|
*/
|
|
4069
4085
|
const run = (options) => {
|
|
4070
4086
|
action.debug(`${action}.run(${stringifyForDisplay(options)})`);
|
|
@@ -4524,7 +4540,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4524
4540
|
// another run's, or idle.
|
|
4525
4541
|
return;
|
|
4526
4542
|
}
|
|
4527
|
-
|
|
4543
|
+
writeActionState(() => {
|
|
4544
|
+
runningStateSignal.value = ABORTED;
|
|
4545
|
+
});
|
|
4528
4546
|
internalAbortController.abort(abortReason);
|
|
4529
4547
|
actionAbortMap.delete(action);
|
|
4530
4548
|
if (isPrerun && (globalAbortSignal.aborted || abortSignal.aborted)) {
|
|
@@ -4551,7 +4569,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4551
4569
|
|
|
4552
4570
|
actionAbortMap.set(action, abort);
|
|
4553
4571
|
|
|
4554
|
-
|
|
4572
|
+
writeActionState(() => {
|
|
4555
4573
|
if (valueSignal.peek() === undefined) {
|
|
4556
4574
|
// An action saying nothing of its own may still have a value the
|
|
4557
4575
|
// page kept from the previous document (see kept_values.js).
|
|
@@ -4630,7 +4648,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4630
4648
|
* before the UI attempts to render the now-missing resource.
|
|
4631
4649
|
*/
|
|
4632
4650
|
|
|
4633
|
-
|
|
4651
|
+
writeActionState(() => {
|
|
4634
4652
|
const value = resultToValue
|
|
4635
4653
|
? resultToValue(runResult, action)
|
|
4636
4654
|
: runResult;
|
|
@@ -4662,7 +4680,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4662
4680
|
error === internalAbortSignal.reason) ||
|
|
4663
4681
|
error.name === "AbortError";
|
|
4664
4682
|
if (isAbort) {
|
|
4665
|
-
|
|
4683
|
+
writeActionState(() => {
|
|
4684
|
+
runningStateSignal.value = ABORTED;
|
|
4685
|
+
});
|
|
4666
4686
|
if (isPrerun && abortSignal.aborted) {
|
|
4667
4687
|
prerunProtectionRegistry.unprotect(action);
|
|
4668
4688
|
}
|
|
@@ -4676,7 +4696,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4676
4696
|
if (isPrerun) {
|
|
4677
4697
|
markErrorAsPrerunFailure(error);
|
|
4678
4698
|
}
|
|
4679
|
-
|
|
4699
|
+
writeActionState(() => {
|
|
4680
4700
|
errorSignal.value = error;
|
|
4681
4701
|
runningStateSignal.value = FAILED;
|
|
4682
4702
|
onError?.(error, { event, action, args });
|
|
@@ -4771,7 +4791,7 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4771
4791
|
}
|
|
4772
4792
|
|
|
4773
4793
|
actionPromiseMap.delete(action);
|
|
4774
|
-
|
|
4794
|
+
writeActionState(() => {
|
|
4775
4795
|
if (!willRunOrPrerun) {
|
|
4776
4796
|
errorSignal.value = undefined;
|
|
4777
4797
|
valueSignal.value = valueInitial;
|
|
@@ -4789,6 +4809,9 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
4789
4809
|
provisionalValue,
|
|
4790
4810
|
inheritData,
|
|
4791
4811
|
keepsAnswers: keptAnswerRegistry !== null,
|
|
4812
|
+
// Whether the last run answered with a promise, written by
|
|
4813
|
+
// updateActions: the tense a run joining this instance answers in.
|
|
4814
|
+
answeredAsync: false,
|
|
4792
4815
|
|
|
4793
4816
|
performRun,
|
|
4794
4817
|
performReset,
|
|
@@ -5272,6 +5295,35 @@ const isPlainObject$2 = (obj) => {
|
|
|
5272
5295
|
);
|
|
5273
5296
|
};
|
|
5274
5297
|
|
|
5298
|
+
// Every state write of a run (start, end, failure, abort, reset), batched. At
|
|
5299
|
+
// the end of the batch preact runs the effects those writes woke up, anywhere
|
|
5300
|
+
// in the app, and rethrows the first one that threw: from there it would fail
|
|
5301
|
+
// this run, or leave it RUNNING, or stop the update half way. That throw is
|
|
5302
|
+
// the effect's, so it is reported on its own, as action_error_report.js
|
|
5303
|
+
// reports an error (thrown from a timer), and the action carries on. What
|
|
5304
|
+
// `write` throws (resultToValue, onComplete…) is the run's, and goes on.
|
|
5305
|
+
const writeActionState = (write) => {
|
|
5306
|
+
let writeThrew = false;
|
|
5307
|
+
let writeError;
|
|
5308
|
+
try {
|
|
5309
|
+
batch(() => {
|
|
5310
|
+
try {
|
|
5311
|
+
write();
|
|
5312
|
+
} catch (e) {
|
|
5313
|
+
writeThrew = true;
|
|
5314
|
+
writeError = e;
|
|
5315
|
+
}
|
|
5316
|
+
});
|
|
5317
|
+
} catch (effectError) {
|
|
5318
|
+
setTimeout(() => {
|
|
5319
|
+
throw effectError;
|
|
5320
|
+
});
|
|
5321
|
+
}
|
|
5322
|
+
if (writeThrew) {
|
|
5323
|
+
throw writeError;
|
|
5324
|
+
}
|
|
5325
|
+
};
|
|
5326
|
+
|
|
5275
5327
|
/**
|
|
5276
5328
|
* Reactively runs an action whenever the params derived from signals change.
|
|
5277
5329
|
*
|
|
@@ -28608,7 +28660,11 @@ ${[idKey, ...uniqueKeys].join(", ")}`,
|
|
|
28608
28660
|
* (docs/resource.md).
|
|
28609
28661
|
*/
|
|
28610
28662
|
const upsertAnswer = (...args) => upsertWith(args, true);
|
|
28611
|
-
|
|
28663
|
+
// Batched like upsertWith: a removal observer may drop rows of another
|
|
28664
|
+
// store (a `.one()` declared with `onDelete: "cascade"`), and an effect
|
|
28665
|
+
// reading both runs once, never between the child leaving and its parents.
|
|
28666
|
+
const drop = (...args) => batch(() => dropNow(...args));
|
|
28667
|
+
const dropNow = (...args) => {
|
|
28612
28668
|
const removedItemArray = [];
|
|
28613
28669
|
const triggerRemovedMutations = () => {
|
|
28614
28670
|
if (removedItemArray.length === 0) {
|
|
@@ -28643,6 +28699,7 @@ ${[idKey, ...uniqueKeys].join(", ")}`,
|
|
|
28643
28699
|
hasFound = true;
|
|
28644
28700
|
idToRemoveSet.delete(existingItemId);
|
|
28645
28701
|
idRemovedArray.push(existingItemId);
|
|
28702
|
+
removedItemArray.push(existingItem);
|
|
28646
28703
|
} else {
|
|
28647
28704
|
arrayWithoutDroppedItems.push(existingItem);
|
|
28648
28705
|
}
|
|
@@ -30753,6 +30810,15 @@ const createResource = (
|
|
|
30753
30810
|
* The property is reactive: updating the child item anywhere propagates immediately.
|
|
30754
30811
|
* The child resource exists independently — it is not owned by, nor deleted with, the parent.
|
|
30755
30812
|
*
|
|
30813
|
+
* The other way round is declared with `onDelete`. Left out, a parent outlives its
|
|
30814
|
+
* child: once the child leaves its store, the property reads an empty object (truthy,
|
|
30815
|
+
* `valueOf()` is `null`, no fields) until the parent is read again — a game whose
|
|
30816
|
+
* organizer is deleted is still a game. `onDelete: "cascade"` is for a parent that is
|
|
30817
|
+
* nothing without its child (an address-book row, the person in it): when the child
|
|
30818
|
+
* leaves its store, the parents pointing at it leave theirs in the same write, and
|
|
30819
|
+
* with them every list holding them. Which one a relation needs is decided in
|
|
30820
|
+
* docs/resource.md, "When the child is deleted".
|
|
30821
|
+
*
|
|
30756
30822
|
* Use it when the child is a first-class entity with its own store, shared across
|
|
30757
30823
|
* parents (a user referenced by many games). When the child only exists inside its
|
|
30758
30824
|
* owner, use `.scopedOne()` instead.
|
|
@@ -30772,7 +30838,12 @@ const createResource = (
|
|
|
30772
30838
|
*
|
|
30773
30839
|
* @param {string} propertyName - property holding the child on each parent item
|
|
30774
30840
|
* @param {Object} childResource - the independent resource created by `resource()`
|
|
30775
|
-
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, PUT, DELETE }`
|
|
30841
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, onDelete, GET, PUT, DELETE }`
|
|
30842
|
+
* @param {"cascade"} [restCallbacks.onDelete] - `"cascade"`: a parent leaves its store
|
|
30843
|
+
* when its child leaves the child's (the child's `DELETE`, `DELETE_MANY`, a write
|
|
30844
|
+
* named with verb `DELETE`, a cascade of its own). Not the relation's own `DELETE`,
|
|
30845
|
+
* which unlinks: the property becomes `null` and the parent stays. Throws on a
|
|
30846
|
+
* relation declared on a scoped child.
|
|
30776
30847
|
* @see docs/resource.md
|
|
30777
30848
|
*/
|
|
30778
30849
|
stateFacade.one = (
|
|
@@ -30781,6 +30852,7 @@ const createResource = (
|
|
|
30781
30852
|
{
|
|
30782
30853
|
rerunOn: oneRerunOn,
|
|
30783
30854
|
dependencies: oneDependencies,
|
|
30855
|
+
onDelete,
|
|
30784
30856
|
|
|
30785
30857
|
GET,
|
|
30786
30858
|
PUT,
|
|
@@ -30788,16 +30860,54 @@ const createResource = (
|
|
|
30788
30860
|
} = {},
|
|
30789
30861
|
) => {
|
|
30790
30862
|
const declarationSite = getDeclarationSite();
|
|
30863
|
+
const relationLabel = `${name}.one("${propertyName}")`;
|
|
30791
30864
|
if (ownerName) {
|
|
30792
30865
|
refuseCallbacksNamingScopedItem(
|
|
30793
|
-
|
|
30866
|
+
relationLabel,
|
|
30794
30867
|
{ GET, PUT, DELETE },
|
|
30795
30868
|
{ name, ownerName, propertyName, declarationSite },
|
|
30796
30869
|
);
|
|
30797
30870
|
}
|
|
30871
|
+
if (onDelete !== undefined && onDelete !== "cascade") {
|
|
30872
|
+
throw new TypeError(
|
|
30873
|
+
`${relationLabel}: onDelete must be "cascade" or left out, got ${onDelete}.
|
|
30874
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
30875
|
+
);
|
|
30876
|
+
}
|
|
30877
|
+
const cascade = onDelete === "cascade";
|
|
30878
|
+
if (cascade && ownerName) {
|
|
30879
|
+
// A scoped item lives in its owner's collection, not in a store this
|
|
30880
|
+
// relation could drop it from.
|
|
30881
|
+
throw new Error(
|
|
30882
|
+
`${relationLabel} cannot declare onDelete: "cascade": a "${name}" item exists only inside its "${ownerName}", and leaves through the "${ownerName}" callbacks.
|
|
30883
|
+
Read the "${ownerName}" again after the "${childResource.name}" is deleted (dependencies).
|
|
30884
|
+
${relationLabel} source location: ${declarationSite}`,
|
|
30885
|
+
);
|
|
30886
|
+
}
|
|
30798
30887
|
const childName = `${name}.${propertyName}`;
|
|
30799
30888
|
const childIdKey = childResource.idKey;
|
|
30800
30889
|
const childStore = childResource.store;
|
|
30890
|
+
// The id each parent points at, carried by the parent row (copies of a row
|
|
30891
|
+
// keep it, a spread does not): a removal is matched against the parents
|
|
30892
|
+
// the store holds at that moment, under the ids they have then.
|
|
30893
|
+
const childIdSymbol = cascade ? Symbol(`${childName} id`) : undefined;
|
|
30894
|
+
if (cascade) {
|
|
30895
|
+
childStore.observeRemovals((removedChildArray) => {
|
|
30896
|
+
const removedChildIdSet = new Set();
|
|
30897
|
+
for (const removedChild of removedChildArray) {
|
|
30898
|
+
removedChildIdSet.add(removedChild[childIdKey]);
|
|
30899
|
+
}
|
|
30900
|
+
const orphanIdArray = [];
|
|
30901
|
+
for (const parentItem of store.arraySignal.peek()) {
|
|
30902
|
+
if (removedChildIdSet.has(parentItem[childIdSymbol])) {
|
|
30903
|
+
orphanIdArray.push(parentItem[idKey]);
|
|
30904
|
+
}
|
|
30905
|
+
}
|
|
30906
|
+
if (orphanIdArray.length > 0) {
|
|
30907
|
+
store.drop(orphanIdArray);
|
|
30908
|
+
}
|
|
30909
|
+
});
|
|
30910
|
+
}
|
|
30801
30911
|
addItemSetup((item, answer) => {
|
|
30802
30912
|
const childItemIdSignal = signal();
|
|
30803
30913
|
const updateChildItemId = (value, answer) => {
|
|
@@ -30852,6 +30962,11 @@ const createResource = (
|
|
|
30852
30962
|
get: () => childItemFacadeSignal.value,
|
|
30853
30963
|
set: updateChildItemId,
|
|
30854
30964
|
});
|
|
30965
|
+
if (cascade) {
|
|
30966
|
+
Object.defineProperty(item, childIdSymbol, {
|
|
30967
|
+
get: () => childItemIdSignal.peek(),
|
|
30968
|
+
});
|
|
30969
|
+
}
|
|
30855
30970
|
debug$2(
|
|
30856
30971
|
`setup ${item}.${propertyName} is one "${childResource.name}" (current value: ${childItemSignal.peek()})`,
|
|
30857
30972
|
);
|
|
@@ -39453,10 +39568,12 @@ const barsOutside = (areaElement) => {
|
|
|
39453
39568
|
};
|
|
39454
39569
|
|
|
39455
39570
|
// Room around what a bar's picture holds for what paints past every box — a
|
|
39456
|
-
// shadow, the
|
|
39571
|
+
// shadow, the bar's own filter — which no API measures: a filter cuts whatever
|
|
39457
39572
|
// falls outside its region.
|
|
39458
39573
|
const WALL_FILTER_INK_ROOM = 100;
|
|
39459
39574
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
39575
|
+
// A blur() in a computed filter, its radius in px.
|
|
39576
|
+
const BLUR_FUNCTION_REGEXP = /blur\(([^)]*)\)/g;
|
|
39460
39577
|
|
|
39461
39578
|
// The bar's picture dimmed, not a rectangle over it: each wall's colour is
|
|
39462
39579
|
// flooded atop the bar's own pixels (Porter-Duff source-atop, which no blend
|
|
@@ -39484,11 +39601,17 @@ const dimBarPicture = (bar, sources) => {
|
|
|
39484
39601
|
const ownFilter = getComputedStyle(bar).filter;
|
|
39485
39602
|
const chain = ownFilter === "none" ? [] : [ownFilter];
|
|
39486
39603
|
for (const source of sources) {
|
|
39487
|
-
const backdropFilter =
|
|
39488
|
-
|
|
39489
|
-
|
|
39490
|
-
|
|
39491
|
-
chain.push(
|
|
39604
|
+
const backdropFilter = readBackdropFilter(source);
|
|
39605
|
+
if (backdropFilter !== "none") {
|
|
39606
|
+
// Blurred alone, the bar would fade to transparent over its edges and
|
|
39607
|
+
// show the live document through them: its blur stays inside its shape.
|
|
39608
|
+
chain.push(
|
|
39609
|
+
backdropFilter.replace(
|
|
39610
|
+
BLUR_FUNCTION_REGEXP,
|
|
39611
|
+
(_, radius) =>
|
|
39612
|
+
`url(#${createShapeBlurFilter(parseFloat(radius), region).id})`,
|
|
39613
|
+
),
|
|
39614
|
+
);
|
|
39492
39615
|
}
|
|
39493
39616
|
chain.push(`url(#${createWallFilter(source, region).id})`);
|
|
39494
39617
|
}
|
|
@@ -39497,7 +39620,59 @@ const dimBarPicture = (bar, sources) => {
|
|
|
39497
39620
|
dimmedBars.push(bar);
|
|
39498
39621
|
};
|
|
39499
39622
|
|
|
39623
|
+
// The wall's backdrop-filter as computed, so a blur radius written in em or
|
|
39624
|
+
// rem reads in px, as the wall resolves it where it paints.
|
|
39625
|
+
const readBackdropFilter = (source) => {
|
|
39626
|
+
const sourceStyle = getComputedStyle(source);
|
|
39627
|
+
const backdropFilter = sourceStyle
|
|
39628
|
+
.getPropertyValue("--backdrop-filter")
|
|
39629
|
+
.trim();
|
|
39630
|
+
if (!backdropFilter || backdropFilter === "none") {
|
|
39631
|
+
return "none";
|
|
39632
|
+
}
|
|
39633
|
+
const probe = document.createElement("div");
|
|
39634
|
+
probe.style.fontSize = sourceStyle.fontSize;
|
|
39635
|
+
probe.style.filter = backdropFilter;
|
|
39636
|
+
document.body.append(probe);
|
|
39637
|
+
const filter = getComputedStyle(probe).filter;
|
|
39638
|
+
probe.remove();
|
|
39639
|
+
return filter;
|
|
39640
|
+
};
|
|
39641
|
+
|
|
39500
39642
|
const createWallFilter = (source, region) => {
|
|
39643
|
+
const filter = createFilter(region);
|
|
39644
|
+
const flood = document.createElementNS(SVG_NS, "feFlood");
|
|
39645
|
+
flood.setAttribute("flood-color", readBackdropColor(source));
|
|
39646
|
+
const composite = document.createElementNS(SVG_NS, "feComposite");
|
|
39647
|
+
composite.setAttribute("in2", "SourceGraphic");
|
|
39648
|
+
composite.setAttribute("operator", "atop");
|
|
39649
|
+
filter.append(flood, composite);
|
|
39650
|
+
return filter;
|
|
39651
|
+
};
|
|
39652
|
+
|
|
39653
|
+
// Colour kept, alpha set to 1.
|
|
39654
|
+
const OPAQUE_MATRIX = "1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1";
|
|
39655
|
+
|
|
39656
|
+
// The wall's blur kept inside the bar. At rest the window is blurred as one
|
|
39657
|
+
// image; a CSS blur() on the bar sees only the bar, so its edges go half
|
|
39658
|
+
// transparent and its colour spreads past its box. Made opaque, each pixel is
|
|
39659
|
+
// the mean of the bar's own pixels around it (feColorMatrix works on
|
|
39660
|
+
// unpremultiplied colours), then cut back to the bar's own alpha.
|
|
39661
|
+
const createShapeBlurFilter = (stdDeviation, region) => {
|
|
39662
|
+
const filter = createFilter(region);
|
|
39663
|
+
const blur = document.createElementNS(SVG_NS, "feGaussianBlur");
|
|
39664
|
+
blur.setAttribute("stdDeviation", stdDeviation);
|
|
39665
|
+
const opaque = document.createElementNS(SVG_NS, "feColorMatrix");
|
|
39666
|
+
opaque.setAttribute("type", "matrix");
|
|
39667
|
+
opaque.setAttribute("values", OPAQUE_MATRIX);
|
|
39668
|
+
const clip = document.createElementNS(SVG_NS, "feComposite");
|
|
39669
|
+
clip.setAttribute("in2", "SourceAlpha");
|
|
39670
|
+
clip.setAttribute("operator", "in");
|
|
39671
|
+
filter.append(blur, opaque, clip);
|
|
39672
|
+
return filter;
|
|
39673
|
+
};
|
|
39674
|
+
|
|
39675
|
+
const createFilter = (region) => {
|
|
39501
39676
|
if (!wallFilterSvg) {
|
|
39502
39677
|
wallFilterSvg = document.createElementNS(SVG_NS, "svg");
|
|
39503
39678
|
wallFilterSvg.setAttribute(WALL_FILTERS_ATTRIBUTE, "");
|
|
@@ -39507,18 +39682,13 @@ const createWallFilter = (source, region) => {
|
|
|
39507
39682
|
wallFilterCount++;
|
|
39508
39683
|
const filter = document.createElementNS(SVG_NS, "filter");
|
|
39509
39684
|
filter.id = `navi-transition-wall-${wallFilterCount}`;
|
|
39510
|
-
// linearRGB, the default, floods another colour than the wall's
|
|
39685
|
+
// linearRGB, the default, floods another colour than the wall's and blurs
|
|
39686
|
+
// in another space than its backdrop-filter.
|
|
39511
39687
|
filter.setAttribute("color-interpolation-filters", "sRGB");
|
|
39512
39688
|
filter.setAttribute("x", region.x);
|
|
39513
39689
|
filter.setAttribute("y", region.y);
|
|
39514
39690
|
filter.setAttribute("width", region.width);
|
|
39515
39691
|
filter.setAttribute("height", region.height);
|
|
39516
|
-
const flood = document.createElementNS(SVG_NS, "feFlood");
|
|
39517
|
-
flood.setAttribute("flood-color", readBackdropColor(source));
|
|
39518
|
-
const composite = document.createElementNS(SVG_NS, "feComposite");
|
|
39519
|
-
composite.setAttribute("in2", "SourceGraphic");
|
|
39520
|
-
composite.setAttribute("operator", "atop");
|
|
39521
|
-
filter.append(flood, composite);
|
|
39522
39692
|
wallFilterSvg.appendChild(filter);
|
|
39523
39693
|
return filter;
|
|
39524
39694
|
};
|