@loomweaver/shell 0.7.5 → 0.7.6
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.
|
@@ -65,8 +65,15 @@ function withoutQuery(url) {
|
|
|
65
65
|
const end = url.search(/[?#]/);
|
|
66
66
|
return end === -1 ? url : url.slice(0, end);
|
|
67
67
|
}
|
|
68
|
+
function withoutTrailingSlashes$1(path) {
|
|
69
|
+
let end = path.length;
|
|
70
|
+
while (end > 0 && path[end - 1] === '/') {
|
|
71
|
+
end -= 1;
|
|
72
|
+
}
|
|
73
|
+
return path.slice(0, end);
|
|
74
|
+
}
|
|
68
75
|
function bare(url) {
|
|
69
|
-
return withoutQuery(url).replace(/^\/+/, '')
|
|
76
|
+
return withoutTrailingSlashes$1(withoutQuery(url).replace(/^\/+/, ''));
|
|
70
77
|
}
|
|
71
78
|
function isPopoutUrl(url) {
|
|
72
79
|
const path = bare(url);
|
|
@@ -94,7 +101,7 @@ function popoutUrlFor(paneTarget) {
|
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
function upsertBy(items, item, matches) {
|
|
97
|
-
const index = items.findIndex(matches);
|
|
104
|
+
const index = items.findIndex((existing) => matches(existing));
|
|
98
105
|
if (index === -1) {
|
|
99
106
|
return [...items, item];
|
|
100
107
|
}
|
|
@@ -310,7 +317,7 @@ class ContributionRegistry {
|
|
|
310
317
|
const visible = omitted.size === 0
|
|
311
318
|
? docked
|
|
312
319
|
: docked.filter((entry) => entry.id === undefined || !omitted.has(entry.id));
|
|
313
|
-
return visible.map(entryToView);
|
|
320
|
+
return visible.map((entry) => entryToView(entry));
|
|
314
321
|
}, /* @ts-ignore */
|
|
315
322
|
...(ngDevMode ? [{ debugName: "views" }] : /* istanbul ignore next */ []));
|
|
316
323
|
barItems = this.visible(this.barItemsSignal);
|
|
@@ -321,7 +328,7 @@ class ContributionRegistry {
|
|
|
321
328
|
* picker, `matchRoute` — drops them without knowing about omission.
|
|
322
329
|
*/
|
|
323
330
|
contentRoutes = computed(() => {
|
|
324
|
-
const routes = this.routableSurfaces().map(entryToContentRoute);
|
|
331
|
+
const routes = this.routableSurfaces().map((entry) => entryToContentRoute(entry));
|
|
325
332
|
const omitted = this.omittedSignal();
|
|
326
333
|
if (omitted.size === 0) {
|
|
327
334
|
return routes;
|
|
@@ -340,7 +347,7 @@ class ContributionRegistry {
|
|
|
340
347
|
return NO_ROUTES;
|
|
341
348
|
}
|
|
342
349
|
return this.routableSurfaces()
|
|
343
|
-
.map(entryToContentRoute)
|
|
350
|
+
.map((entry) => entryToContentRoute(entry))
|
|
344
351
|
.filter((route) => isRouteOmitted(route, omitted));
|
|
345
352
|
}, /* @ts-ignore */
|
|
346
353
|
...(ngDevMode ? [{ debugName: "omittedContentRoutes" }] : /* istanbul ignore next */ []));
|
|
@@ -436,13 +443,13 @@ class ContributionRegistry {
|
|
|
436
443
|
}
|
|
437
444
|
/** Adds a menu-slot item. With an `id` a re-registration replaces in place (last-in wins); without one the item is additive and dispose removes this exact contribution. */
|
|
438
445
|
addMenuItem(item) {
|
|
439
|
-
this.menuItemsSignal.update((items) => upsertBy(items, item, (
|
|
446
|
+
this.menuItemsSignal.update((items) => upsertBy(items, item, (index) => item.id !== undefined && index.id === item.id));
|
|
440
447
|
return {
|
|
441
|
-
dispose: () => this.menuItemsSignal.update((items) => items.filter((
|
|
448
|
+
dispose: () => this.menuItemsSignal.update((items) => items.filter((index) => index !== item)),
|
|
442
449
|
};
|
|
443
450
|
}
|
|
444
451
|
removeMenuItemById(id) {
|
|
445
|
-
this.menuItemsSignal.update((items) => items.filter((
|
|
452
|
+
this.menuItemsSignal.update((items) => items.filter((index) => index.id !== id));
|
|
446
453
|
}
|
|
447
454
|
addSurface(entry) {
|
|
448
455
|
this.surfacesSignal.update((entries) => upsertBy(entries, entry, sameSlotAs(entry)));
|
|
@@ -462,11 +469,11 @@ class ContributionRegistry {
|
|
|
462
469
|
add(target, item) {
|
|
463
470
|
target.update((items) => upsertById(items, item));
|
|
464
471
|
return {
|
|
465
|
-
dispose: () => target.update((items) => items.filter((
|
|
472
|
+
dispose: () => target.update((items) => items.filter((index) => index !== item)),
|
|
466
473
|
};
|
|
467
474
|
}
|
|
468
475
|
removeById(target, id) {
|
|
469
|
-
target.update((items) => items.filter((
|
|
476
|
+
target.update((items) => items.filter((index) => index.id !== id));
|
|
470
477
|
}
|
|
471
478
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContributionRegistry, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
472
479
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: ContributionRegistry });
|
|
@@ -485,7 +492,7 @@ function sameSlotAs(entry) {
|
|
|
485
492
|
}
|
|
486
493
|
|
|
487
494
|
function normalizePath(url) {
|
|
488
|
-
return url.split(/[?#]
|
|
495
|
+
return url.split(/[?#]/, 1)[0].replace(/^\/+/, '');
|
|
489
496
|
}
|
|
490
497
|
function segmentsOf(path) {
|
|
491
498
|
return normalizePath(path).split('/').filter(Boolean);
|
|
@@ -538,11 +545,11 @@ function paramsOfPattern(pattern, path) {
|
|
|
538
545
|
const parts = segmentsOf(pattern);
|
|
539
546
|
const segments = segmentsOf(path);
|
|
540
547
|
const params = {};
|
|
541
|
-
parts.
|
|
548
|
+
for (const [index, part] of parts.entries()) {
|
|
542
549
|
if (part.startsWith(':') && segments[index] !== undefined) {
|
|
543
550
|
params[part.slice(1)] = segments[index];
|
|
544
551
|
}
|
|
545
|
-
}
|
|
552
|
+
}
|
|
546
553
|
return params;
|
|
547
554
|
}
|
|
548
555
|
function routeParams(route, path) {
|
|
@@ -647,7 +654,7 @@ function reusableRoute(route) {
|
|
|
647
654
|
return route.routeConfig?.data?.['chromeless'] !== true;
|
|
648
655
|
}
|
|
649
656
|
function effectiveRetain(declared, fallback) {
|
|
650
|
-
return declared
|
|
657
|
+
return declared === undefined ? fallback === 'retain' : declared === 'always';
|
|
651
658
|
}
|
|
652
659
|
function routeRetains(route, fallback) {
|
|
653
660
|
if (route.container !== undefined) {
|
|
@@ -672,7 +679,7 @@ function surfaceRetentionMode(routes, path) {
|
|
|
672
679
|
if (!route || route.container !== undefined) {
|
|
673
680
|
return 'rebuild';
|
|
674
681
|
}
|
|
675
|
-
return route.iframe
|
|
682
|
+
return route.iframe === undefined ? 'move' : 'in-place';
|
|
676
683
|
}
|
|
677
684
|
function containerChildInstances(entries, tabPath) {
|
|
678
685
|
const scoped = containerDockFor(tabPath) + ':';
|
|
@@ -732,11 +739,11 @@ class ContentReuseStrategy {
|
|
|
732
739
|
changes = signal(0, /* @ts-ignore */
|
|
733
740
|
...(ngDevMode ? [{ debugName: "changes" }] : /* istanbul ignore next */ []));
|
|
734
741
|
version = this.changes.asReadonly();
|
|
735
|
-
shouldReuseRoute(future,
|
|
736
|
-
if (!isContentRoute(future) || !isContentRoute(
|
|
737
|
-
return future.routeConfig ===
|
|
742
|
+
shouldReuseRoute(future, current) {
|
|
743
|
+
if (!isContentRoute(future) || !isContentRoute(current)) {
|
|
744
|
+
return future.routeConfig === current.routeConfig;
|
|
738
745
|
}
|
|
739
|
-
return future.routeConfig ===
|
|
746
|
+
return future.routeConfig === current.routeConfig && sameParams(future, current);
|
|
740
747
|
}
|
|
741
748
|
shouldDetach(route) {
|
|
742
749
|
return reusableRoute(route);
|
|
@@ -782,11 +789,12 @@ class ContentReuseStrategy {
|
|
|
782
789
|
}
|
|
783
790
|
pruneExcept(isLive) {
|
|
784
791
|
for (const [key, handle] of this.handles) {
|
|
785
|
-
if (
|
|
786
|
-
|
|
787
|
-
this.handles.delete(key);
|
|
788
|
-
this.changes.update((value) => value + 1);
|
|
792
|
+
if (isLive(key)) {
|
|
793
|
+
continue;
|
|
789
794
|
}
|
|
795
|
+
handle.componentRef?.destroy();
|
|
796
|
+
this.handles.delete(key);
|
|
797
|
+
this.changes.update((value) => value + 1);
|
|
790
798
|
}
|
|
791
799
|
}
|
|
792
800
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentReuseStrategy, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
@@ -884,7 +892,7 @@ class StateSyncChannel {
|
|
|
884
892
|
}
|
|
885
893
|
try {
|
|
886
894
|
const opened = new BroadcastChannel(CHANNEL_NAME);
|
|
887
|
-
opened.
|
|
895
|
+
opened.addEventListener('message', (event) => this.receive(event.data));
|
|
888
896
|
return opened;
|
|
889
897
|
}
|
|
890
898
|
catch {
|
|
@@ -1024,7 +1032,7 @@ function cleanArea(area, options, problems, depth) {
|
|
|
1024
1032
|
problems.push(`${options.context}: the arrangement nests deeper than ${MAX_DEPTH} levels — the deeper areas are dropped.`);
|
|
1025
1033
|
return null;
|
|
1026
1034
|
}
|
|
1027
|
-
const kinds = ['tabs', 'rows', 'columns'].filter((key) => key
|
|
1035
|
+
const kinds = ['tabs', 'rows', 'columns'].filter((key) => Object.hasOwn(area, key));
|
|
1028
1036
|
if (kinds.length !== 1) {
|
|
1029
1037
|
problems.push(`${options.context}: a pane area must be exactly one of tabs, rows or columns.`);
|
|
1030
1038
|
return null;
|
|
@@ -1093,7 +1101,7 @@ function cleanTabsArea(area, size, options, problems) {
|
|
|
1093
1101
|
function collectTabs$1(area) {
|
|
1094
1102
|
return area.kind === 'tabs'
|
|
1095
1103
|
? [...area.tabs]
|
|
1096
|
-
: area.children.flatMap(collectTabs$1);
|
|
1104
|
+
: area.children.flatMap((child) => collectTabs$1(child));
|
|
1097
1105
|
}
|
|
1098
1106
|
function buildNode(area, idPrefix, ctx, indexPath) {
|
|
1099
1107
|
if (area.kind === 'tabs') {
|
|
@@ -1122,6 +1130,12 @@ function buildNode(area, idPrefix, ctx, indexPath) {
|
|
|
1122
1130
|
};
|
|
1123
1131
|
return chain(0, 1);
|
|
1124
1132
|
}
|
|
1133
|
+
function evenShareOf(remainder, declaredSum, children, unspecified) {
|
|
1134
|
+
if (unspecified === 0) {
|
|
1135
|
+
return 0;
|
|
1136
|
+
}
|
|
1137
|
+
return remainder > 0 ? remainder / unspecified : declaredSum / children;
|
|
1138
|
+
}
|
|
1125
1139
|
function fractionsOf(children) {
|
|
1126
1140
|
const declared = children
|
|
1127
1141
|
.map((child) => child.size)
|
|
@@ -1129,11 +1143,7 @@ function fractionsOf(children) {
|
|
|
1129
1143
|
const declaredSum = declared.reduce((sum, size) => sum + size, 0);
|
|
1130
1144
|
const unspecified = children.length - declared.length;
|
|
1131
1145
|
const remainder = Math.max(0, 100 - declaredSum);
|
|
1132
|
-
const evenShare =
|
|
1133
|
-
? remainder > 0
|
|
1134
|
-
? remainder / unspecified
|
|
1135
|
-
: declaredSum / children.length
|
|
1136
|
-
: 0;
|
|
1146
|
+
const evenShare = evenShareOf(remainder, declaredSum, children.length, unspecified);
|
|
1137
1147
|
const weights = children.map((child) => child.size ?? evenShare);
|
|
1138
1148
|
const total = weights.reduce((sum, weight) => sum + weight, 0);
|
|
1139
1149
|
return weights.map((weight) => weight / total);
|
|
@@ -1168,7 +1178,12 @@ function claimFor(claims, path) {
|
|
|
1168
1178
|
if (matching.length === 0) {
|
|
1169
1179
|
return null;
|
|
1170
1180
|
}
|
|
1171
|
-
|
|
1181
|
+
let best = matching[0];
|
|
1182
|
+
for (const claim of matching) {
|
|
1183
|
+
if (narrower(claim.pattern, best.pattern) > 0) {
|
|
1184
|
+
best = claim;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1172
1187
|
const tied = matching.filter((claim) => claim.workspaceId !== best.workspaceId &&
|
|
1173
1188
|
narrower(claim.pattern, best.pattern) === 0);
|
|
1174
1189
|
return tied.length === 0 ? best : null;
|
|
@@ -1186,9 +1201,12 @@ function contestedShapes(claims) {
|
|
|
1186
1201
|
return new Map([...byShape].filter(([, owners]) => owners.length > 1));
|
|
1187
1202
|
}
|
|
1188
1203
|
function conflictingClaims(claims) {
|
|
1189
|
-
return [...contestedShapes(claims)].map(([shape, owners]) =>
|
|
1190
|
-
|
|
1191
|
-
`
|
|
1204
|
+
return [...contestedShapes(claims)].map(([shape, owners]) => {
|
|
1205
|
+
const named = owners.map((id) => `"${id}"`).join(' and ');
|
|
1206
|
+
return (`Workspaces ${named} both claim "${shape}" — ` +
|
|
1207
|
+
`neither is narrower than the other, so the claim is dropped and that address ` +
|
|
1208
|
+
`behaves as though nothing claimed it. Give the address one home.`);
|
|
1209
|
+
});
|
|
1192
1210
|
}
|
|
1193
1211
|
function withoutConflicts(claims) {
|
|
1194
1212
|
const contested = contestedShapes(claims);
|
|
@@ -1230,8 +1248,8 @@ function leafWith(id, tabs, candidateActive, declared) {
|
|
|
1230
1248
|
kind: 'leaf',
|
|
1231
1249
|
id,
|
|
1232
1250
|
tabs,
|
|
1233
|
-
...(active
|
|
1234
|
-
...(declared
|
|
1251
|
+
...(active !== undefined && { active }),
|
|
1252
|
+
...(declared && { declared: true }),
|
|
1235
1253
|
};
|
|
1236
1254
|
}
|
|
1237
1255
|
|
|
@@ -1324,15 +1342,13 @@ function normalizeTab(value) {
|
|
|
1324
1342
|
}
|
|
1325
1343
|
return {
|
|
1326
1344
|
path: tab['path'],
|
|
1327
|
-
...(tab['pinned'] === true
|
|
1328
|
-
...(tab['preview'] === true
|
|
1329
|
-
...(tab['closable'] === false
|
|
1330
|
-
...(typeof tab['title'] === 'string'
|
|
1331
|
-
...(tab['literalTitle'] === true
|
|
1332
|
-
...(typeof tab['icon'] === 'string'
|
|
1333
|
-
...(typeof tab['instance'] === 'string'
|
|
1334
|
-
? { instance: tab['instance'] }
|
|
1335
|
-
: {}),
|
|
1345
|
+
...(tab['pinned'] === true && { pinned: true }),
|
|
1346
|
+
...(tab['preview'] === true && { preview: true }),
|
|
1347
|
+
...(tab['closable'] === false && { closable: false }),
|
|
1348
|
+
...(typeof tab['title'] === 'string' && { title: tab['title'] }),
|
|
1349
|
+
...(tab['literalTitle'] === true && { literalTitle: true }),
|
|
1350
|
+
...(typeof tab['icon'] === 'string' && { icon: tab['icon'] }),
|
|
1351
|
+
...(typeof tab['instance'] === 'string' && { instance: tab['instance'] }),
|
|
1336
1352
|
};
|
|
1337
1353
|
}
|
|
1338
1354
|
function normalizeLeafNode(id, node) {
|
|
@@ -1342,7 +1358,7 @@ function normalizeLeafNode(id, node) {
|
|
|
1342
1358
|
}
|
|
1343
1359
|
const rawTabs = node['tabs'];
|
|
1344
1360
|
const tabs = Array.isArray(rawTabs)
|
|
1345
|
-
? rawTabs.map(normalizeTab).filter((tab) => tab !== null)
|
|
1361
|
+
? rawTabs.map((value) => normalizeTab(value)).filter((tab) => tab !== null)
|
|
1346
1362
|
: [];
|
|
1347
1363
|
const rawActive = node['active'];
|
|
1348
1364
|
return leafWith(id, tabs, typeof rawActive === 'string' ? rawActive : undefined, node['declared'] === true);
|
|
@@ -1415,11 +1431,11 @@ function auditWorkspaceDefinitions(definitions, panelRegions) {
|
|
|
1415
1431
|
}
|
|
1416
1432
|
seen.add(definition.id);
|
|
1417
1433
|
if (definition.initial) {
|
|
1418
|
-
if (initial
|
|
1419
|
-
|
|
1434
|
+
if (initial === null) {
|
|
1435
|
+
initial = definition.id;
|
|
1420
1436
|
}
|
|
1421
1437
|
else {
|
|
1422
|
-
|
|
1438
|
+
problems.push(`Workspace "${definition.id}" also declares initial: true — "${initial}" already does, so this one is ignored.`);
|
|
1423
1439
|
}
|
|
1424
1440
|
}
|
|
1425
1441
|
auditDefinition(definition, panelRegions, problems);
|
|
@@ -1477,13 +1493,13 @@ function baselineSidebars(definition, deps) {
|
|
|
1477
1493
|
if (sidebars === undefined) {
|
|
1478
1494
|
return {};
|
|
1479
1495
|
}
|
|
1480
|
-
const listed = deps.panelRegions.filter((region) => region
|
|
1496
|
+
const listed = deps.panelRegions.filter((region) => Object.hasOwn(sidebars, region));
|
|
1481
1497
|
const hidden = listed
|
|
1482
1498
|
.flatMap((region) => deps
|
|
1483
1499
|
.declaredPaths(region)
|
|
1484
1500
|
.map((path) => path.slice(VIEW_PANE_PREFIX.length))
|
|
1485
1501
|
.filter((id) => !sidebars[region].includes(id)))
|
|
1486
|
-
.
|
|
1502
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
1487
1503
|
return hidden.length === 0 ? {} : { hiddenViews: JSON.stringify(hidden) };
|
|
1488
1504
|
}
|
|
1489
1505
|
function declaredTabPaths(definition) {
|
|
@@ -1511,7 +1527,7 @@ function bakeTab(definitionId, entry, problems) {
|
|
|
1511
1527
|
return {
|
|
1512
1528
|
tab: {
|
|
1513
1529
|
path: tab.path,
|
|
1514
|
-
...(tab.closable === false
|
|
1530
|
+
...(tab.closable === false && { closable: false }),
|
|
1515
1531
|
},
|
|
1516
1532
|
active: tab.active === true,
|
|
1517
1533
|
};
|
|
@@ -1592,9 +1608,16 @@ function movable(parent) {
|
|
|
1592
1608
|
function supportsAtomicMove(document) {
|
|
1593
1609
|
return movable(document.body ?? document.documentElement);
|
|
1594
1610
|
}
|
|
1611
|
+
function insertNode(parent, node, before) {
|
|
1612
|
+
if (before === null) {
|
|
1613
|
+
parent.append(node);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
before.before(node);
|
|
1617
|
+
}
|
|
1595
1618
|
function moveNode(parent, node, before) {
|
|
1596
1619
|
if (!movable(parent) || !node.isConnected) {
|
|
1597
|
-
parent
|
|
1620
|
+
insertNode(parent, node, before);
|
|
1598
1621
|
return false;
|
|
1599
1622
|
}
|
|
1600
1623
|
try {
|
|
@@ -1602,7 +1625,7 @@ function moveNode(parent, node, before) {
|
|
|
1602
1625
|
return true;
|
|
1603
1626
|
}
|
|
1604
1627
|
catch {
|
|
1605
|
-
parent
|
|
1628
|
+
insertNode(parent, node, before);
|
|
1606
1629
|
return false;
|
|
1607
1630
|
}
|
|
1608
1631
|
}
|
|
@@ -1652,10 +1675,11 @@ class RetainedViewStash {
|
|
|
1652
1675
|
},
|
|
1653
1676
|
stale: () => entry.tracked && (!owns() || this.entries.get(key) !== entry),
|
|
1654
1677
|
describe: (mode, retain) => {
|
|
1655
|
-
if (owns()) {
|
|
1656
|
-
|
|
1657
|
-
entry.keep = retain;
|
|
1678
|
+
if (!owns()) {
|
|
1679
|
+
return;
|
|
1658
1680
|
}
|
|
1681
|
+
entry.mode = mode;
|
|
1682
|
+
entry.keep = retain;
|
|
1659
1683
|
},
|
|
1660
1684
|
release: (retained) => {
|
|
1661
1685
|
if (owns()) {
|
|
@@ -1722,14 +1746,16 @@ class RetainedViewStash {
|
|
|
1722
1746
|
}
|
|
1723
1747
|
}
|
|
1724
1748
|
evictWorkspace(workspaceId) {
|
|
1725
|
-
|
|
1749
|
+
const snapshot = [...this.entries.values()];
|
|
1750
|
+
for (const entry of snapshot) {
|
|
1726
1751
|
if (!entry.inUse && entry.workspace === workspaceId) {
|
|
1727
1752
|
this.destroyEntry(entry);
|
|
1728
1753
|
}
|
|
1729
1754
|
}
|
|
1730
1755
|
}
|
|
1731
1756
|
ngOnDestroy() {
|
|
1732
|
-
|
|
1757
|
+
const snapshot = [...this.entries.values()];
|
|
1758
|
+
for (const entry of snapshot) {
|
|
1733
1759
|
this.destroyEntry(entry);
|
|
1734
1760
|
}
|
|
1735
1761
|
this.holdingArea?.remove();
|
|
@@ -1835,7 +1861,7 @@ class RetainedViewStash {
|
|
|
1835
1861
|
const area = this.document.createElement('div');
|
|
1836
1862
|
area.dataset['lwRetentionHold'] = '';
|
|
1837
1863
|
area.style.display = 'none';
|
|
1838
|
-
this.document.body.
|
|
1864
|
+
this.document.body.append(area);
|
|
1839
1865
|
this.holdingArea = area;
|
|
1840
1866
|
return area;
|
|
1841
1867
|
}
|
|
@@ -1846,7 +1872,7 @@ class RetainedViewStash {
|
|
|
1846
1872
|
}
|
|
1847
1873
|
pullNodes(entry) {
|
|
1848
1874
|
for (const node of liveRootNodes(entry)) {
|
|
1849
|
-
node.
|
|
1875
|
+
node.remove();
|
|
1850
1876
|
}
|
|
1851
1877
|
}
|
|
1852
1878
|
destroyEntry(entry) {
|
|
@@ -1870,7 +1896,8 @@ class RetainedViewStash {
|
|
|
1870
1896
|
this.sweepQueued = true;
|
|
1871
1897
|
queueMicrotask(() => {
|
|
1872
1898
|
this.sweepQueued = false;
|
|
1873
|
-
|
|
1899
|
+
const snapshot = [...this.entries.values()];
|
|
1900
|
+
for (const entry of snapshot) {
|
|
1874
1901
|
if (entry.inUse) {
|
|
1875
1902
|
continue;
|
|
1876
1903
|
}
|
|
@@ -1950,7 +1977,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
1950
1977
|
type: Service
|
|
1951
1978
|
}] });
|
|
1952
1979
|
function pathOfStashKey(key) {
|
|
1953
|
-
return key.split('|')[1] ?? '';
|
|
1980
|
+
return key.split('|', 2)[1] ?? '';
|
|
1954
1981
|
}
|
|
1955
1982
|
|
|
1956
1983
|
class RetentionUnloadGuard {
|
|
@@ -1983,7 +2010,6 @@ class RetentionUnloadGuard {
|
|
|
1983
2010
|
return;
|
|
1984
2011
|
}
|
|
1985
2012
|
event.preventDefault();
|
|
1986
|
-
event.returnValue = '';
|
|
1987
2013
|
};
|
|
1988
2014
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: RetentionUnloadGuard, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
1989
2015
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: RetentionUnloadGuard });
|
|
@@ -2169,16 +2195,20 @@ function isMacPlatform() {
|
|
|
2169
2195
|
function normaliseKey(key) {
|
|
2170
2196
|
const lower = key.toLowerCase();
|
|
2171
2197
|
switch (lower) {
|
|
2172
|
-
case 'esc':
|
|
2198
|
+
case 'esc': {
|
|
2173
2199
|
return 'escape';
|
|
2174
|
-
|
|
2200
|
+
}
|
|
2201
|
+
case 'return': {
|
|
2175
2202
|
return 'enter';
|
|
2203
|
+
}
|
|
2176
2204
|
case 'space':
|
|
2177
2205
|
case 'spacebar':
|
|
2178
|
-
case ' ':
|
|
2206
|
+
case ' ': {
|
|
2179
2207
|
return 'space';
|
|
2180
|
-
|
|
2208
|
+
}
|
|
2209
|
+
default: {
|
|
2181
2210
|
return lower;
|
|
2211
|
+
}
|
|
2182
2212
|
}
|
|
2183
2213
|
}
|
|
2184
2214
|
function toSignature(parts) {
|
|
@@ -2202,72 +2232,89 @@ function chordSignature(chord, isMac) {
|
|
|
2202
2232
|
};
|
|
2203
2233
|
for (const raw of chord.split('+')) {
|
|
2204
2234
|
const token = raw.trim().toLowerCase();
|
|
2205
|
-
if (
|
|
2206
|
-
|
|
2235
|
+
if (token) {
|
|
2236
|
+
applyToken(parts, token, isMac);
|
|
2207
2237
|
}
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
case 'ctrl':
|
|
2216
|
-
case 'control':
|
|
2217
|
-
parts.ctrl = true;
|
|
2218
|
-
break;
|
|
2219
|
-
case 'meta':
|
|
2220
|
-
case 'cmd':
|
|
2221
|
-
case 'command':
|
|
2222
|
-
case 'win':
|
|
2238
|
+
}
|
|
2239
|
+
return parts.key ? toSignature(parts) : null;
|
|
2240
|
+
}
|
|
2241
|
+
function applyToken(parts, token, isMac) {
|
|
2242
|
+
switch (token) {
|
|
2243
|
+
case 'mod': {
|
|
2244
|
+
if (isMac)
|
|
2223
2245
|
parts.meta = true;
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2246
|
+
else
|
|
2247
|
+
parts.ctrl = true;
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
case 'ctrl':
|
|
2251
|
+
case 'control': {
|
|
2252
|
+
parts.ctrl = true;
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
case 'meta':
|
|
2256
|
+
case 'cmd':
|
|
2257
|
+
case 'command':
|
|
2258
|
+
case 'win': {
|
|
2259
|
+
parts.meta = true;
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
case 'alt':
|
|
2263
|
+
case 'option': {
|
|
2264
|
+
parts.alt = true;
|
|
2265
|
+
return;
|
|
2266
|
+
}
|
|
2267
|
+
case 'shift': {
|
|
2268
|
+
parts.shift = true;
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
default: {
|
|
2272
|
+
parts.key = normaliseKey(token);
|
|
2234
2273
|
}
|
|
2235
2274
|
}
|
|
2236
|
-
return parts.key ? toSignature(parts) : null;
|
|
2237
2275
|
}
|
|
2238
2276
|
function formatShortcut(chord, isMac) {
|
|
2239
2277
|
const tokens = chord.split('+').map((raw) => {
|
|
2240
2278
|
const token = raw.trim().toLowerCase();
|
|
2241
2279
|
switch (token) {
|
|
2242
|
-
case 'mod':
|
|
2280
|
+
case 'mod': {
|
|
2243
2281
|
return isMac ? '⌘' : 'Ctrl';
|
|
2282
|
+
}
|
|
2244
2283
|
case 'ctrl':
|
|
2245
|
-
case 'control':
|
|
2284
|
+
case 'control': {
|
|
2246
2285
|
return isMac ? '⌃' : 'Ctrl';
|
|
2286
|
+
}
|
|
2247
2287
|
case 'meta':
|
|
2248
2288
|
case 'cmd':
|
|
2249
2289
|
case 'command':
|
|
2250
|
-
case 'win':
|
|
2290
|
+
case 'win': {
|
|
2251
2291
|
return isMac ? '⌘' : 'Meta';
|
|
2292
|
+
}
|
|
2252
2293
|
case 'alt':
|
|
2253
|
-
case 'option':
|
|
2294
|
+
case 'option': {
|
|
2254
2295
|
return isMac ? '⌥' : 'Alt';
|
|
2255
|
-
|
|
2296
|
+
}
|
|
2297
|
+
case 'shift': {
|
|
2256
2298
|
return isMac ? '⇧' : 'Shift';
|
|
2299
|
+
}
|
|
2257
2300
|
case 'enter':
|
|
2258
|
-
case 'return':
|
|
2301
|
+
case 'return': {
|
|
2259
2302
|
return isMac ? '↵' : 'Enter';
|
|
2303
|
+
}
|
|
2260
2304
|
case 'escape':
|
|
2261
|
-
case 'esc':
|
|
2305
|
+
case 'esc': {
|
|
2262
2306
|
return 'Esc';
|
|
2307
|
+
}
|
|
2263
2308
|
case 'space':
|
|
2264
2309
|
case 'spacebar':
|
|
2265
|
-
case ' ':
|
|
2310
|
+
case ' ': {
|
|
2266
2311
|
return 'Space';
|
|
2267
|
-
|
|
2312
|
+
}
|
|
2313
|
+
default: {
|
|
2268
2314
|
return token.length === 1
|
|
2269
2315
|
? token.toUpperCase()
|
|
2270
2316
|
: token.charAt(0).toUpperCase() + token.slice(1);
|
|
2317
|
+
}
|
|
2271
2318
|
}
|
|
2272
2319
|
});
|
|
2273
2320
|
return tokens.join(isMac ? '' : '+');
|
|
@@ -2428,20 +2475,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
2428
2475
|
type: Service
|
|
2429
2476
|
}] });
|
|
2430
2477
|
|
|
2431
|
-
function upgradeElementProperty(
|
|
2432
|
-
const self =
|
|
2433
|
-
if (Object.
|
|
2478
|
+
function upgradeElementProperty(element, name) {
|
|
2479
|
+
const self = element;
|
|
2480
|
+
if (Object.hasOwn(element, name)) {
|
|
2434
2481
|
const value = self[name];
|
|
2435
2482
|
delete self[name];
|
|
2436
2483
|
self[name] = value;
|
|
2437
2484
|
}
|
|
2438
2485
|
}
|
|
2439
|
-
function reflectAttribute(
|
|
2486
|
+
function reflectAttribute(element, name, value) {
|
|
2440
2487
|
if (value === null || value === undefined) {
|
|
2441
|
-
|
|
2488
|
+
element.removeAttribute(name);
|
|
2442
2489
|
}
|
|
2443
2490
|
else {
|
|
2444
|
-
|
|
2491
|
+
element.setAttribute(name, value);
|
|
2445
2492
|
}
|
|
2446
2493
|
}
|
|
2447
2494
|
|
|
@@ -2568,7 +2615,9 @@ class LwMenuElement extends HTMLElement {
|
|
|
2568
2615
|
return;
|
|
2569
2616
|
}
|
|
2570
2617
|
this.active = (index + items.length) % items.length;
|
|
2571
|
-
|
|
2618
|
+
for (const [index_, item] of items.entries()) {
|
|
2619
|
+
item.tabIndex = index_ === this.active ? 0 : -1;
|
|
2620
|
+
}
|
|
2572
2621
|
const item = items[this.active];
|
|
2573
2622
|
item.focus();
|
|
2574
2623
|
item.scrollIntoView?.({ block: 'nearest' });
|
|
@@ -2581,18 +2630,22 @@ class LwMenuElement extends HTMLElement {
|
|
|
2581
2630
|
}
|
|
2582
2631
|
handleKeydown(event) {
|
|
2583
2632
|
switch (event.key) {
|
|
2584
|
-
case 'ArrowDown':
|
|
2633
|
+
case 'ArrowDown': {
|
|
2585
2634
|
this.setActive(this.active < 0 ? 0 : this.active + 1);
|
|
2586
2635
|
break;
|
|
2587
|
-
|
|
2588
|
-
|
|
2636
|
+
}
|
|
2637
|
+
case 'ArrowUp': {
|
|
2638
|
+
this.setActive((this.active < 0 ? this.items().length : this.active) - 1);
|
|
2589
2639
|
break;
|
|
2590
|
-
|
|
2640
|
+
}
|
|
2641
|
+
case 'Home': {
|
|
2591
2642
|
this.setActive(0);
|
|
2592
2643
|
break;
|
|
2593
|
-
|
|
2644
|
+
}
|
|
2645
|
+
case 'End': {
|
|
2594
2646
|
this.setActive(this.items().length - 1);
|
|
2595
2647
|
break;
|
|
2648
|
+
}
|
|
2596
2649
|
case 'Enter':
|
|
2597
2650
|
case ' ': {
|
|
2598
2651
|
const item = this.active >= 0 ? this.items()[this.active] : undefined;
|
|
@@ -2602,11 +2655,13 @@ class LwMenuElement extends HTMLElement {
|
|
|
2602
2655
|
break;
|
|
2603
2656
|
}
|
|
2604
2657
|
case 'Escape':
|
|
2605
|
-
case 'Tab':
|
|
2658
|
+
case 'Tab': {
|
|
2606
2659
|
this.dispatchEvent(new CustomEvent(LW_MENU_DISMISS, { bubbles: true }));
|
|
2607
2660
|
return;
|
|
2608
|
-
|
|
2661
|
+
}
|
|
2662
|
+
default: {
|
|
2609
2663
|
return;
|
|
2664
|
+
}
|
|
2610
2665
|
}
|
|
2611
2666
|
event.preventDefault();
|
|
2612
2667
|
}
|
|
@@ -2685,7 +2740,7 @@ class MenuService {
|
|
|
2685
2740
|
document.body.append(menu);
|
|
2686
2741
|
document.body.classList.add('lw-menu-open');
|
|
2687
2742
|
menu.openAt(at.x, at.y);
|
|
2688
|
-
const listenTimer = setTimeout(() => document.addEventListener('pointerdown', onOutside, true));
|
|
2743
|
+
const listenTimer = setTimeout(() => document.addEventListener('pointerdown', onOutside, { capture: true }), 0);
|
|
2689
2744
|
this.current = { menu, onOutside, restore, listenTimer };
|
|
2690
2745
|
}
|
|
2691
2746
|
resolve(menuIds, context) {
|
|
@@ -2718,7 +2773,7 @@ class MenuService {
|
|
|
2718
2773
|
};
|
|
2719
2774
|
})
|
|
2720
2775
|
.filter((entry) => entry !== null)
|
|
2721
|
-
.
|
|
2776
|
+
.toSorted((a, b) => a.group.localeCompare(b.group) || a.order - b.order);
|
|
2722
2777
|
}
|
|
2723
2778
|
createMenu(resolved) {
|
|
2724
2779
|
const menu = document.createElement(LW_MENU_TAG);
|
|
@@ -2871,7 +2926,7 @@ class ShellBarItem {
|
|
|
2871
2926
|
shortcut = computed(() => {
|
|
2872
2927
|
const button = this.asButton();
|
|
2873
2928
|
if (!button?.showShortcut || !button.command) {
|
|
2874
|
-
return
|
|
2929
|
+
return;
|
|
2875
2930
|
}
|
|
2876
2931
|
return this.commands.shortcutOf(this.commands.commands().find((entry) => entry.id === button.command));
|
|
2877
2932
|
}, /* @ts-ignore */
|
|
@@ -2911,7 +2966,7 @@ class ShellBar {
|
|
|
2911
2966
|
.filter((item) => item.bar === this.region().id && item.slot === slot)
|
|
2912
2967
|
.filter((item) => this.auth.visible(item.access))
|
|
2913
2968
|
.filter((item) => 'component' in item || this.commands.triggerable(item))
|
|
2914
|
-
.
|
|
2969
|
+
.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
2915
2970
|
}
|
|
2916
2971
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ShellBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2917
2972
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ShellBar, isStandalone: true, selector: "lw-shell-bar", inputs: { region: { classPropertyName: "region", publicName: "region", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<header\n class=\"flex items-center gap-2 overflow-hidden border-border bg-surface px-2 md:px-4\"\n [class.border-b]=\"dock() === 'top'\"\n [class.h-12]=\"dock() === 'top'\"\n [class.border-t]=\"dock() === 'bottom' || footer()\"\n [class.py-1]=\"dock() === 'bottom' || footer()\"\n [class.text-xs]=\"dock() === 'bottom'\"\n [class.text-content-muted]=\"dock() === 'bottom'\"\n>\n <div class=\"flex min-w-0 items-center gap-2\">\n @for (item of startItems(); track item.id) {\n <lw-shell-bar-item [item]=\"item\" [dock]=\"dock()\" />\n }\n </div>\n\n @if (centerItems().length) {\n <div class=\"mx-auto flex shrink-0 items-center gap-2\">\n @for (item of centerItems(); track item.id) {\n <lw-shell-bar-item [item]=\"item\" [dock]=\"dock()\" />\n }\n </div>\n }\n\n <div class=\"ml-auto flex shrink-0 items-center gap-2\">\n @for (item of endItems(); track item.id) {\n <lw-shell-bar-item [item]=\"item\" [dock]=\"dock()\" />\n }\n </div>\n</header>\n", dependencies: [{ kind: "component", type: ShellBarItem, selector: "lw-shell-bar-item", inputs: ["item", "dock"] }] });
|
|
@@ -3182,7 +3237,7 @@ class RailItemsService {
|
|
|
3182
3237
|
}
|
|
3183
3238
|
isVisible(itemId) {
|
|
3184
3239
|
if (isWorkspaceRailItem(itemId)) {
|
|
3185
|
-
return
|
|
3240
|
+
return Object.hasOwn(this.state().placed, itemId);
|
|
3186
3241
|
}
|
|
3187
3242
|
return !this.state().hidden.includes(itemId);
|
|
3188
3243
|
}
|
|
@@ -3318,15 +3373,15 @@ class Reorderable {
|
|
|
3318
3373
|
moveByKeyboard(item, direction) {
|
|
3319
3374
|
const id = item.dataset['reorderId'] ?? '';
|
|
3320
3375
|
const band = item.dataset['reorderBand'] ?? '';
|
|
3321
|
-
const inBand = this.items().filter((
|
|
3322
|
-
const bandIds = inBand.map((
|
|
3376
|
+
const inBand = this.items().filter((element) => (element.dataset['reorderBand'] ?? '') === band);
|
|
3377
|
+
const bandIds = inBand.map((element) => element.dataset['reorderId'] ?? '');
|
|
3323
3378
|
const target = bandIds.indexOf(id) + direction;
|
|
3324
3379
|
if (target < 0 || target >= bandIds.length) {
|
|
3325
3380
|
return false;
|
|
3326
3381
|
}
|
|
3327
3382
|
const neighbourId = bandIds[target];
|
|
3328
3383
|
const without = this.items()
|
|
3329
|
-
.map((
|
|
3384
|
+
.map((element) => element.dataset['reorderId'] ?? '')
|
|
3330
3385
|
.filter((x) => x !== id);
|
|
3331
3386
|
const insertAt = without.indexOf(neighbourId) + (direction > 0 ? 1 : 0);
|
|
3332
3387
|
without.splice(insertAt, 0, id);
|
|
@@ -3341,7 +3396,7 @@ class Reorderable {
|
|
|
3341
3396
|
}
|
|
3342
3397
|
focusItem(id) {
|
|
3343
3398
|
this.items()
|
|
3344
|
-
.find((
|
|
3399
|
+
.find((element) => (element.dataset['reorderId'] ?? '') === id)
|
|
3345
3400
|
?.focus();
|
|
3346
3401
|
}
|
|
3347
3402
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Reorderable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
@@ -3392,8 +3447,8 @@ class UserOrderService {
|
|
|
3392
3447
|
const rank = new Map(sequence.map((id, index) => [id, index]));
|
|
3393
3448
|
const ranked = (item) => rank.has(key(item));
|
|
3394
3449
|
const knownInUserOrder = items
|
|
3395
|
-
.filter(ranked)
|
|
3396
|
-
.
|
|
3450
|
+
.filter((item) => ranked(item))
|
|
3451
|
+
.toSorted((a, b) => (rank.get(key(a)) ?? 0) - (rank.get(key(b)) ?? 0));
|
|
3397
3452
|
let next = 0;
|
|
3398
3453
|
return items.map((item) => ranked(item) ? knownInUserOrder[next++] : item);
|
|
3399
3454
|
}
|
|
@@ -3513,7 +3568,7 @@ function defaultTabTitle(route, root) {
|
|
|
3513
3568
|
function withRefreshedPath(tabs, index, path) {
|
|
3514
3569
|
return tabs[index].path === path
|
|
3515
3570
|
? tabs
|
|
3516
|
-
: tabs.map((tab,
|
|
3571
|
+
: tabs.map((tab, index_) => (index_ === index ? { ...tab, path } : tab));
|
|
3517
3572
|
}
|
|
3518
3573
|
function autoOpenedTab(route, root, path) {
|
|
3519
3574
|
if (!route ||
|
|
@@ -3553,11 +3608,11 @@ function toPaneTab(tab) {
|
|
|
3553
3608
|
return {
|
|
3554
3609
|
path: tab.path,
|
|
3555
3610
|
title: tab.title,
|
|
3556
|
-
...(tab.literalTitle
|
|
3557
|
-
...(tab.icon
|
|
3558
|
-
...(tab.pinned
|
|
3559
|
-
...(tab.preview
|
|
3560
|
-
...(tab.closable
|
|
3611
|
+
...(tab.literalTitle && { literalTitle: true }),
|
|
3612
|
+
...(tab.icon !== undefined && { icon: tab.icon }),
|
|
3613
|
+
...(tab.pinned && { pinned: true }),
|
|
3614
|
+
...(tab.preview && { preview: true }),
|
|
3615
|
+
...(!tab.closable && { closable: false }),
|
|
3561
3616
|
};
|
|
3562
3617
|
}
|
|
3563
3618
|
function facetTabViews(routes, addressOf = (route) => route.path) {
|
|
@@ -3565,7 +3620,7 @@ function facetTabViews(routes, addressOf = (route) => route.path) {
|
|
|
3565
3620
|
.filter((route) => route.follows === true)
|
|
3566
3621
|
.map((route) => ({ route, address: addressOf(route) }))
|
|
3567
3622
|
.filter((entry) => entry.address !== null)
|
|
3568
|
-
.
|
|
3623
|
+
.toSorted((a, b) => (a.route.order ?? 0) - (b.route.order ?? 0) ||
|
|
3569
3624
|
a.route.path.localeCompare(b.route.path))
|
|
3570
3625
|
.map(({ route, address }, index) => ({
|
|
3571
3626
|
path: route.path,
|
|
@@ -3656,11 +3711,11 @@ function collidingParam(pattern, other) {
|
|
|
3656
3711
|
function paramPrefixes(pattern) {
|
|
3657
3712
|
const prefixes = new Map();
|
|
3658
3713
|
const segments = segmentsOf(pattern);
|
|
3659
|
-
segments.
|
|
3714
|
+
for (const [index, segment] of segments.entries()) {
|
|
3660
3715
|
if (segment.startsWith(':')) {
|
|
3661
3716
|
prefixes.set(segment.slice(1), segments.slice(0, index).join('/'));
|
|
3662
3717
|
}
|
|
3663
|
-
}
|
|
3718
|
+
}
|
|
3664
3719
|
return prefixes;
|
|
3665
3720
|
}
|
|
3666
3721
|
|
|
@@ -3748,22 +3803,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3748
3803
|
type: Service
|
|
3749
3804
|
}] });
|
|
3750
3805
|
|
|
3751
|
-
function transformLeaf(node, paneId,
|
|
3806
|
+
function transformLeaf(node, paneId, function_) {
|
|
3752
3807
|
if (node.kind === 'leaf') {
|
|
3753
|
-
return node.id === paneId ?
|
|
3808
|
+
return node.id === paneId ? function_(node) : node;
|
|
3754
3809
|
}
|
|
3755
|
-
const first = transformLeaf(node.first, paneId,
|
|
3756
|
-
const second = transformLeaf(node.second, paneId,
|
|
3810
|
+
const first = transformLeaf(node.first, paneId, function_);
|
|
3811
|
+
const second = transformLeaf(node.second, paneId, function_);
|
|
3757
3812
|
return first === node.first && second === node.second
|
|
3758
3813
|
? node
|
|
3759
3814
|
: { ...node, first, second };
|
|
3760
3815
|
}
|
|
3761
|
-
function collapseLeaf(node, paneId,
|
|
3816
|
+
function collapseLeaf(node, paneId, function_) {
|
|
3762
3817
|
if (node.kind === 'leaf') {
|
|
3763
|
-
return node.id === paneId ?
|
|
3818
|
+
return node.id === paneId ? function_(node) : node;
|
|
3764
3819
|
}
|
|
3765
|
-
const first = collapseLeaf(node.first, paneId,
|
|
3766
|
-
const second = collapseLeaf(node.second, paneId,
|
|
3820
|
+
const first = collapseLeaf(node.first, paneId, function_);
|
|
3821
|
+
const second = collapseLeaf(node.second, paneId, function_);
|
|
3767
3822
|
if (first === null) {
|
|
3768
3823
|
return second;
|
|
3769
3824
|
}
|
|
@@ -3878,8 +3933,8 @@ function pruneEmptyLeaves(node, primaryId, spare) {
|
|
|
3878
3933
|
function insertTab(node, paneId, tab, index) {
|
|
3879
3934
|
return transformLeaf(node, paneId, (leaf) => {
|
|
3880
3935
|
const existingIndex = leaf.tabs.findIndex((existing) => existing.path === tab.path);
|
|
3881
|
-
if (existingIndex
|
|
3882
|
-
const tabs = leaf.tabs.map((existing,
|
|
3936
|
+
if (existingIndex !== -1) {
|
|
3937
|
+
const tabs = leaf.tabs.map((existing, index_) => index_ === existingIndex ? tab : existing);
|
|
3883
3938
|
return { ...leaf, tabs, active: tab.path };
|
|
3884
3939
|
}
|
|
3885
3940
|
const at = index === undefined
|
|
@@ -3917,7 +3972,7 @@ function refineLeafTitles(leaf, skipLeaf, matches, patch) {
|
|
|
3917
3972
|
...tab,
|
|
3918
3973
|
title: patch.title,
|
|
3919
3974
|
literalTitle: patch.literalTitle,
|
|
3920
|
-
...(patch.icon
|
|
3975
|
+
...(patch.icon !== undefined && { icon: patch.icon }),
|
|
3921
3976
|
};
|
|
3922
3977
|
});
|
|
3923
3978
|
return found
|
|
@@ -3955,7 +4010,7 @@ function reseatPinned(tabs, index, updated) {
|
|
|
3955
4010
|
function pinTab(node, paneId, tabPath) {
|
|
3956
4011
|
return transformLeaf(node, paneId, (leaf) => {
|
|
3957
4012
|
const index = leaf.tabs.findIndex((tab) => tab.path === tabPath);
|
|
3958
|
-
if (index
|
|
4013
|
+
if (index === -1 || leaf.tabs[index].pinned === true) {
|
|
3959
4014
|
return leaf;
|
|
3960
4015
|
}
|
|
3961
4016
|
const pinned = {
|
|
@@ -3968,7 +4023,7 @@ function pinTab(node, paneId, tabPath) {
|
|
|
3968
4023
|
function unpinTab(node, paneId, tabPath) {
|
|
3969
4024
|
return transformLeaf(node, paneId, (leaf) => {
|
|
3970
4025
|
const index = leaf.tabs.findIndex((tab) => tab.path === tabPath && tab.pinned);
|
|
3971
|
-
if (index
|
|
4026
|
+
if (index === -1) {
|
|
3972
4027
|
return leaf;
|
|
3973
4028
|
}
|
|
3974
4029
|
const unpinned = tabWithout(leaf.tabs[index], 'pinned');
|
|
@@ -4025,7 +4080,7 @@ class PaneTreeService {
|
|
|
4025
4080
|
}
|
|
4026
4081
|
stackView(dock, viewId) {
|
|
4027
4082
|
const segments = paneSegments(this.tree(dock));
|
|
4028
|
-
const last = segments
|
|
4083
|
+
const last = segments.at(-1);
|
|
4029
4084
|
if (last) {
|
|
4030
4085
|
this.splitPane(dock, last.id, 'column', VIEW_PANE_PREFIX + viewId);
|
|
4031
4086
|
}
|
|
@@ -4058,7 +4113,7 @@ class PaneTreeService {
|
|
|
4058
4113
|
return;
|
|
4059
4114
|
}
|
|
4060
4115
|
const rank = new Map(order.map((path, index) => [path, index]));
|
|
4061
|
-
const tabs = [...leaf.tabs].
|
|
4116
|
+
const tabs = [...leaf.tabs].toSorted((a, b) => (rank.get(a.path) ?? leaf.tabs.indexOf(a)) -
|
|
4062
4117
|
(rank.get(b.path) ?? leaf.tabs.indexOf(b)));
|
|
4063
4118
|
this.commit(dock, setTabs(this.tree(dock), paneId, tabs));
|
|
4064
4119
|
}
|
|
@@ -4110,7 +4165,7 @@ class PaneTreeService {
|
|
|
4110
4165
|
const tree = this.tree(dock);
|
|
4111
4166
|
const primary = this.primaryId(dock);
|
|
4112
4167
|
const leaf = paneId === primary ? findLeaf(tree, primary) : undefined;
|
|
4113
|
-
if (leaf
|
|
4168
|
+
if (leaf?.tabs.every((tab) => tab.path !== tabPath)) {
|
|
4114
4169
|
return;
|
|
4115
4170
|
}
|
|
4116
4171
|
if (leaf && !leaf.declared && leaf.tabs.length <= 1) {
|
|
@@ -4165,7 +4220,7 @@ class PaneTreeService {
|
|
|
4165
4220
|
return this.docks()[dock] !== undefined;
|
|
4166
4221
|
}
|
|
4167
4222
|
dropDock(dock) {
|
|
4168
|
-
if (!this.
|
|
4223
|
+
if (!this.hasDock(dock)) {
|
|
4169
4224
|
return;
|
|
4170
4225
|
}
|
|
4171
4226
|
this.docks.update((docks) => {
|
|
@@ -4196,7 +4251,7 @@ class PaneTreeService {
|
|
|
4196
4251
|
this.docks.update((current) => {
|
|
4197
4252
|
const merged = { ...persisted };
|
|
4198
4253
|
for (const [dock, entry] of Object.entries(current)) {
|
|
4199
|
-
if (isContainerDock(dock) && !(dock
|
|
4254
|
+
if (isContainerDock(dock) && !Object.hasOwn(persisted, dock)) {
|
|
4200
4255
|
merged[dock] = entry;
|
|
4201
4256
|
}
|
|
4202
4257
|
}
|
|
@@ -4331,7 +4386,7 @@ class OpenTabsService {
|
|
|
4331
4386
|
activeViewInstance = computed(() => {
|
|
4332
4387
|
const path = this.activeViewPath();
|
|
4333
4388
|
if (path === null) {
|
|
4334
|
-
return
|
|
4389
|
+
return;
|
|
4335
4390
|
}
|
|
4336
4391
|
return this.paneTree
|
|
4337
4392
|
.primaryTabs(CONTENT_DOCK)
|
|
@@ -4367,7 +4422,7 @@ class OpenTabsService {
|
|
|
4367
4422
|
const facets = facetTabViews(routes, (route) => this.addressOf(route));
|
|
4368
4423
|
const open = this.openTabs().filter((tab) => this.strippable(routes, tab));
|
|
4369
4424
|
const dynamics = dynamicTabViews(routes, open);
|
|
4370
|
-
return [...facets, ...dynamics, ...this.viewTabs()].
|
|
4425
|
+
return [...facets, ...dynamics, ...this.viewTabs()].toSorted((a, b) => a.order - b.order);
|
|
4371
4426
|
}, /* @ts-ignore */
|
|
4372
4427
|
...(ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []));
|
|
4373
4428
|
viewTabs = computed(() => viewTabViews(this.paneTree.primaryTabs(CONTENT_DOCK), (id) => this.registry.views().find((view) => view.id === id), (access) => this.auth.meets(access), VIEW_PANE_PREFIX), /* @ts-ignore */
|
|
@@ -4419,9 +4474,9 @@ class OpenTabsService {
|
|
|
4419
4474
|
navigateTo(path) {
|
|
4420
4475
|
this.navigate(path).catch((error) => console.error('Content navigation failed', error));
|
|
4421
4476
|
}
|
|
4422
|
-
updateOpen(
|
|
4477
|
+
updateOpen(function_) {
|
|
4423
4478
|
const current = this.openTabs();
|
|
4424
|
-
const next =
|
|
4479
|
+
const next = function_(current);
|
|
4425
4480
|
if (next === current) {
|
|
4426
4481
|
return;
|
|
4427
4482
|
}
|
|
@@ -4496,7 +4551,7 @@ class OpenTabsService {
|
|
|
4496
4551
|
const routes = this.registry.contentRoutes();
|
|
4497
4552
|
this.updateOpen((tabs) => {
|
|
4498
4553
|
const index = tabs.findIndex((tab) => tabRootOf(routes, tab.path) === root);
|
|
4499
|
-
if (index
|
|
4554
|
+
if (index !== -1) {
|
|
4500
4555
|
return withRefreshedPath(tabs, index, path);
|
|
4501
4556
|
}
|
|
4502
4557
|
const opened = autoOpenedTab(route, root, path);
|
|
@@ -4511,7 +4566,7 @@ class OpenTabsService {
|
|
|
4511
4566
|
return (route !== undefined &&
|
|
4512
4567
|
route.chromeless !== true &&
|
|
4513
4568
|
route.follows !== true &&
|
|
4514
|
-
|
|
4569
|
+
(normalizePath(route.path) !== '' || normalizePath(tab.path) === ''));
|
|
4515
4570
|
}
|
|
4516
4571
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: OpenTabsService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
4517
4572
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: OpenTabsService });
|
|
@@ -4927,7 +4982,7 @@ class SurfaceCloseGuard {
|
|
|
4927
4982
|
}
|
|
4928
4983
|
async saveAll(dirty) {
|
|
4929
4984
|
try {
|
|
4930
|
-
await Promise.all(dirty.map((surface) => surface.surfaceSave?.()));
|
|
4985
|
+
await Promise.all(dirty.map(async (surface) => surface.surfaceSave?.()));
|
|
4931
4986
|
}
|
|
4932
4987
|
catch (error) {
|
|
4933
4988
|
console.error('Save before closing failed', error);
|
|
@@ -5074,9 +5129,17 @@ class TabClosingService {
|
|
|
5074
5129
|
const closing = this.state.openTabs().filter((tab) => roots.has(tabRootOf(routes, tab.path)));
|
|
5075
5130
|
const activeWentAway = roots.has(this.state.activeTabRoot());
|
|
5076
5131
|
this.state.updateOpen((tabs) => tabs.filter((tab) => !roots.has(tabRootOf(routes, tab.path))));
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5132
|
+
for (const tab of closing) {
|
|
5133
|
+
this.closeHooks.runSafely(tab.onClose);
|
|
5134
|
+
}
|
|
5135
|
+
for (const root of roots) {
|
|
5136
|
+
this.closeHooks.delete(root);
|
|
5137
|
+
}
|
|
5138
|
+
const evictAll = () => {
|
|
5139
|
+
for (const root of roots) {
|
|
5140
|
+
this.reuse.evict(root);
|
|
5141
|
+
}
|
|
5142
|
+
};
|
|
5080
5143
|
if (activeWentAway) {
|
|
5081
5144
|
void this.state.navigate(fallbackPath)
|
|
5082
5145
|
.catch((error) => console.error('Content navigation failed', error))
|
|
@@ -5108,7 +5171,7 @@ class TabClosingService {
|
|
|
5108
5171
|
neighbourPath(root) {
|
|
5109
5172
|
const routes = this.registry.contentRoutes();
|
|
5110
5173
|
const siblings = this.state.openTabs().filter((tab) => tabRootOf(routes, tab.path) !== root);
|
|
5111
|
-
return siblings.
|
|
5174
|
+
return siblings.at(-1)?.path ?? '';
|
|
5112
5175
|
}
|
|
5113
5176
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: TabClosingService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
5114
5177
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: TabClosingService });
|
|
@@ -5193,7 +5256,7 @@ class ContentTabsService {
|
|
|
5193
5256
|
const seat = (tab, fallback) => rank.get(tabRootOf(routes, tab.path)) ?? fallback;
|
|
5194
5257
|
this.state.updateOpen((tabs) => tabs
|
|
5195
5258
|
.map((tab, index) => ({ tab, index }))
|
|
5196
|
-
.
|
|
5259
|
+
.toSorted((a, b) => seat(a.tab, a.index) - seat(b.tab, b.index))
|
|
5197
5260
|
.map((entry) => entry.tab));
|
|
5198
5261
|
}
|
|
5199
5262
|
/**
|
|
@@ -5209,7 +5272,7 @@ class ContentTabsService {
|
|
|
5209
5272
|
const { routes, root } = this.state.rootFor(path);
|
|
5210
5273
|
this.state.updateOpen((tabs) => {
|
|
5211
5274
|
const index = tabs.findIndex((tab) => tabRootOf(routes, tab.path) === root);
|
|
5212
|
-
return index
|
|
5275
|
+
return index === -1 || tabs[index].pinned
|
|
5213
5276
|
? tabs
|
|
5214
5277
|
: reseatPinned(tabs, index, tabs[index]);
|
|
5215
5278
|
});
|
|
@@ -5364,7 +5427,7 @@ class ContentTabsService {
|
|
|
5364
5427
|
const { routes, root } = this.state.rootFor(path);
|
|
5365
5428
|
this.state.updateOpen((tabs) => {
|
|
5366
5429
|
const index = tabs.findIndex((tab) => tabRootOf(routes, tab.path) === root);
|
|
5367
|
-
if (index
|
|
5430
|
+
if (index === -1 || tabs[index].pinned === pinned) {
|
|
5368
5431
|
return tabs;
|
|
5369
5432
|
}
|
|
5370
5433
|
const updated = pinned
|
|
@@ -5434,7 +5497,7 @@ class PanelViewsService {
|
|
|
5434
5497
|
const declared = this.registry
|
|
5435
5498
|
.views()
|
|
5436
5499
|
.filter((view) => view.region === regionId)
|
|
5437
|
-
.
|
|
5500
|
+
.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
5438
5501
|
return this.order.applyOrder(panelViewsContainerId(regionId), declared, (view) => view.id);
|
|
5439
5502
|
}
|
|
5440
5503
|
candidatesFor(regionId) {
|
|
@@ -5449,7 +5512,7 @@ class PanelViewsService {
|
|
|
5449
5512
|
(panelRegions.has(view.region) &&
|
|
5450
5513
|
(holder === null || !panelRegions.has(holder))))
|
|
5451
5514
|
.map(({ view, holder }) => ({ view, here: holder === regionId }))
|
|
5452
|
-
.
|
|
5515
|
+
.toSorted((a, b) => Number(b.here) - Number(a.here) ||
|
|
5453
5516
|
(a.view.order ?? 0) - (b.view.order ?? 0));
|
|
5454
5517
|
}
|
|
5455
5518
|
holderOf(viewId) {
|
|
@@ -5485,7 +5548,7 @@ class HiddenViewsService {
|
|
|
5485
5548
|
...(ngDevMode ? [{ debugName: "ids" }] : /* istanbul ignore next */ []));
|
|
5486
5549
|
hidden = this.ids.asReadonly();
|
|
5487
5550
|
constructor() {
|
|
5488
|
-
|
|
5551
|
+
this.hydrateWhenWorkspaceReady();
|
|
5489
5552
|
}
|
|
5490
5553
|
isHidden(viewId) {
|
|
5491
5554
|
return this.ids().has(viewId);
|
|
@@ -5509,7 +5572,7 @@ class HiddenViewsService {
|
|
|
5509
5572
|
void this.store.set(this.storageKey(), this.serialize());
|
|
5510
5573
|
}
|
|
5511
5574
|
serialize() {
|
|
5512
|
-
return JSON.stringify([...this.ids()].
|
|
5575
|
+
return JSON.stringify([...this.ids()].toSorted((a, b) => a.localeCompare(b)));
|
|
5513
5576
|
}
|
|
5514
5577
|
commit(next) {
|
|
5515
5578
|
this.ids.set(next);
|
|
@@ -5518,6 +5581,9 @@ class HiddenViewsService {
|
|
|
5518
5581
|
storageKey() {
|
|
5519
5582
|
return this.workspace.scopedKey(STORAGE_KEY$b);
|
|
5520
5583
|
}
|
|
5584
|
+
hydrateWhenWorkspaceReady() {
|
|
5585
|
+
void this.workspace.ready.then(() => hydrateAsync(this.store, this.storageKey(), (raw) => this.ids.set(parseHiddenViews(raw))));
|
|
5586
|
+
}
|
|
5521
5587
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: HiddenViewsService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
5522
5588
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: HiddenViewsService });
|
|
5523
5589
|
}
|
|
@@ -5624,9 +5690,9 @@ function tabGaps(definition, routes, id) {
|
|
|
5624
5690
|
}
|
|
5625
5691
|
function sidebarGaps(definition, declaredPaths, id) {
|
|
5626
5692
|
return Object.entries(definition.sidebars ?? {}).flatMap(([region, visible]) => {
|
|
5627
|
-
const declared = declaredPaths(region).map((path) => path.slice(VIEW_PANE_PREFIX.length));
|
|
5693
|
+
const declared = new Set(declaredPaths(region).map((path) => path.slice(VIEW_PANE_PREFIX.length)));
|
|
5628
5694
|
return visible
|
|
5629
|
-
.filter((viewId) => !declared.
|
|
5695
|
+
.filter((viewId) => !declared.has(viewId))
|
|
5630
5696
|
.map((viewId) => `Workspace "${id}": sidebar view "${viewId}" is not declared for region "${region}" — the entry has no effect.`);
|
|
5631
5697
|
});
|
|
5632
5698
|
}
|
|
@@ -5643,7 +5709,7 @@ function canonicalState(read, shape) {
|
|
|
5643
5709
|
}
|
|
5644
5710
|
function canonicalValue(key, raw, hidden, shape) {
|
|
5645
5711
|
if (key === shape.hiddenViewsKey) {
|
|
5646
|
-
return JSON.stringify([...hidden].
|
|
5712
|
+
return JSON.stringify([...hidden].toSorted((a, b) => a.localeCompare(b)));
|
|
5647
5713
|
}
|
|
5648
5714
|
if (key === shape.paneTreesKey) {
|
|
5649
5715
|
return canonicalTrees(raw, hidden, shape);
|
|
@@ -5657,7 +5723,8 @@ function canonicalTrees(raw, hidden, shape) {
|
|
|
5657
5723
|
try {
|
|
5658
5724
|
const parsed = JSON.parse(raw);
|
|
5659
5725
|
const out = {};
|
|
5660
|
-
|
|
5726
|
+
const docks = Object.keys(parsed ?? {}).toSorted((a, b) => a.localeCompare(b));
|
|
5727
|
+
for (const dock of docks) {
|
|
5661
5728
|
const entry = normalizeDockEntry(parsed[dock]);
|
|
5662
5729
|
if (entry === null) {
|
|
5663
5730
|
continue;
|
|
@@ -5706,7 +5773,7 @@ function comparableTab(tab) {
|
|
|
5706
5773
|
}
|
|
5707
5774
|
function comparableNode(node) {
|
|
5708
5775
|
if (node.kind === 'leaf') {
|
|
5709
|
-
return { ...node, tabs: node.tabs.map(comparableTab) };
|
|
5776
|
+
return { ...node, tabs: node.tabs.map((tab) => comparableTab(tab)) };
|
|
5710
5777
|
}
|
|
5711
5778
|
return {
|
|
5712
5779
|
...node,
|
|
@@ -5732,7 +5799,7 @@ function baseInitials(name) {
|
|
|
5732
5799
|
if (letters.length === 1) {
|
|
5733
5800
|
return letters[0].toUpperCase();
|
|
5734
5801
|
}
|
|
5735
|
-
return (letters[0] + letters
|
|
5802
|
+
return (letters[0] + (letters.at(-1) ?? '')).toUpperCase();
|
|
5736
5803
|
}
|
|
5737
5804
|
function* candidatesFor(name) {
|
|
5738
5805
|
const base = baseInitials(name);
|
|
@@ -5741,8 +5808,8 @@ function* candidatesFor(name) {
|
|
|
5741
5808
|
}
|
|
5742
5809
|
yield base;
|
|
5743
5810
|
const letters = [...(wordsOf(name)[0] ?? '')];
|
|
5744
|
-
for (let
|
|
5745
|
-
yield (letters[0] + letters[
|
|
5811
|
+
for (let index = 1; index < letters.length; index++) {
|
|
5812
|
+
yield (letters[0] + letters[index]).toUpperCase();
|
|
5746
5813
|
}
|
|
5747
5814
|
for (let digit = 2; digit <= LAST_RESORT_DIGITS; digit++) {
|
|
5748
5815
|
yield letters[0].toUpperCase() + digit;
|
|
@@ -5752,17 +5819,22 @@ function assignWorkspaceInitials(workspaces) {
|
|
|
5752
5819
|
const taken = new Set();
|
|
5753
5820
|
const assigned = new Map();
|
|
5754
5821
|
for (const workspace of workspaces) {
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
continue;
|
|
5758
|
-
}
|
|
5822
|
+
const candidate = firstFree(candidatesFor(workspace.name), taken);
|
|
5823
|
+
if (candidate !== undefined) {
|
|
5759
5824
|
taken.add(candidate);
|
|
5760
5825
|
assigned.set(workspace.id, candidate);
|
|
5761
|
-
break;
|
|
5762
5826
|
}
|
|
5763
5827
|
}
|
|
5764
5828
|
return assigned;
|
|
5765
5829
|
}
|
|
5830
|
+
function firstFree(candidates, taken) {
|
|
5831
|
+
for (const candidate of candidates) {
|
|
5832
|
+
if (!taken.has(candidate)) {
|
|
5833
|
+
return candidate;
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
return undefined;
|
|
5837
|
+
}
|
|
5766
5838
|
|
|
5767
5839
|
const STORAGE_KEY$a = 'lw.shell.workspaces';
|
|
5768
5840
|
const HIDDEN_VIEWS_KEY = 'lw.shell.hidden-views';
|
|
@@ -5849,14 +5921,16 @@ class WorkspaceService {
|
|
|
5849
5921
|
}, /* @ts-ignore */
|
|
5850
5922
|
...(ngDevMode ? [{ debugName: "changedIds" }] : /* istanbul ignore next */ []));
|
|
5851
5923
|
constructor() {
|
|
5852
|
-
|
|
5853
|
-
this.
|
|
5924
|
+
const setList = (raw) => this.list.set(parse(raw));
|
|
5925
|
+
hydrateAsync(this.store, STORAGE_KEY$a, setList);
|
|
5926
|
+
this.sync.register('settings', STORAGE_KEY$a, setList);
|
|
5854
5927
|
if (isDevMode()) {
|
|
5855
|
-
|
|
5928
|
+
const all = this.definitionBatches.flat();
|
|
5929
|
+
for (const problem of auditWorkspaceDefinitions(all, this.panelRegions)) {
|
|
5856
5930
|
console.warn(problem);
|
|
5857
5931
|
}
|
|
5858
5932
|
}
|
|
5859
|
-
|
|
5933
|
+
this.layOutAdoptedWorkspaceWhenReady();
|
|
5860
5934
|
}
|
|
5861
5935
|
async saveCurrent(name) {
|
|
5862
5936
|
const baseline = await this.currentState();
|
|
@@ -5864,7 +5938,7 @@ class WorkspaceService {
|
|
|
5864
5938
|
const origin = this.originOf(this.active.id());
|
|
5865
5939
|
this.commit([
|
|
5866
5940
|
...this.list(),
|
|
5867
|
-
{ id, name, baseline, ...(origin
|
|
5941
|
+
{ id, name, baseline, ...(origin !== null && { origin }) },
|
|
5868
5942
|
]);
|
|
5869
5943
|
this.active.set(id);
|
|
5870
5944
|
this.applyState(baseline);
|
|
@@ -5976,10 +6050,8 @@ class WorkspaceService {
|
|
|
5976
6050
|
declaredPaths: (region) => this.panelGroups.declaredPaths(region),
|
|
5977
6051
|
});
|
|
5978
6052
|
return {
|
|
5979
|
-
...(state.hiddenViews
|
|
5980
|
-
|
|
5981
|
-
: { [HIDDEN_VIEWS_KEY]: state.hiddenViews }),
|
|
5982
|
-
...(state.trees === undefined ? {} : { [PANE_TREES_KEY]: state.trees }),
|
|
6053
|
+
...(state.hiddenViews !== undefined && { [HIDDEN_VIEWS_KEY]: state.hiddenViews }),
|
|
6054
|
+
...(state.trees !== undefined && { [PANE_TREES_KEY]: state.trees }),
|
|
5983
6055
|
};
|
|
5984
6056
|
}
|
|
5985
6057
|
warnDeclarationGaps(id) {
|
|
@@ -6043,6 +6115,9 @@ class WorkspaceService {
|
|
|
6043
6115
|
this.list.set(next);
|
|
6044
6116
|
void this.store.set(STORAGE_KEY$a, JSON.stringify(next));
|
|
6045
6117
|
}
|
|
6118
|
+
layOutAdoptedWorkspaceWhenReady() {
|
|
6119
|
+
void this.active.ready.then(() => this.layOutAdoptedWorkspace());
|
|
6120
|
+
}
|
|
6046
6121
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: WorkspaceService, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
6047
6122
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: WorkspaceService });
|
|
6048
6123
|
}
|
|
@@ -6081,7 +6156,7 @@ class ShellRail {
|
|
|
6081
6156
|
.filter((item) => this.railItems.regionOf(item.id, item.rail) === this.region().id)
|
|
6082
6157
|
.filter((item) => this.auth.visible(item.access))
|
|
6083
6158
|
.filter((item) => item.workspace !== undefined || this.commands.triggerable(item))
|
|
6084
|
-
.
|
|
6159
|
+
.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0)), /* @ts-ignore */
|
|
6085
6160
|
...(ngDevMode ? [{ debugName: "registered" }] : /* istanbul ignore next */ []));
|
|
6086
6161
|
items = computed(() => {
|
|
6087
6162
|
const inRail = this.registered().filter((item) => this.railItems.isVisible(item.id));
|
|
@@ -6089,7 +6164,7 @@ class ShellRail {
|
|
|
6089
6164
|
const id = this.containerId();
|
|
6090
6165
|
const key = (item) => item.id;
|
|
6091
6166
|
const top = this.userOrder.applyOrder(id, inRail.filter((item) => !isBottom(item)), key);
|
|
6092
|
-
const bottom = this.userOrder.applyOrder(id, inRail.filter(isBottom), key);
|
|
6167
|
+
const bottom = this.userOrder.applyOrder(id, inRail.filter((item) => isBottom(item)), key);
|
|
6093
6168
|
return [...top, ...bottom];
|
|
6094
6169
|
}, /* @ts-ignore */
|
|
6095
6170
|
...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
@@ -6234,10 +6309,11 @@ class ViewStateService {
|
|
|
6234
6309
|
hydrateAsync(this.store, key, (raw) => value.set(parseBlob$1(raw)));
|
|
6235
6310
|
let timer;
|
|
6236
6311
|
const cancelPendingSave = () => {
|
|
6237
|
-
if (timer
|
|
6238
|
-
|
|
6239
|
-
timer = undefined;
|
|
6312
|
+
if (timer === undefined) {
|
|
6313
|
+
return;
|
|
6240
6314
|
}
|
|
6315
|
+
clearTimeout(timer);
|
|
6316
|
+
timer = undefined;
|
|
6241
6317
|
};
|
|
6242
6318
|
const save = () => {
|
|
6243
6319
|
cancelPendingSave();
|
|
@@ -6275,12 +6351,12 @@ function parseRecord(viewId, raw) {
|
|
|
6275
6351
|
}
|
|
6276
6352
|
const record = parsed;
|
|
6277
6353
|
const instances = Array.isArray(record.instances)
|
|
6278
|
-
? record.instances.filter((
|
|
6354
|
+
? record.instances.filter((index) => !!index && typeof index.id === 'string' && typeof index.name === 'string')
|
|
6279
6355
|
: [];
|
|
6280
|
-
const withoutDefault = instances.filter((
|
|
6356
|
+
const withoutDefault = instances.filter((index) => index.id !== viewId);
|
|
6281
6357
|
const merged = [{ id: viewId, name: '' }, ...withoutDefault];
|
|
6282
6358
|
const activeId = typeof record.activeId === 'string' &&
|
|
6283
|
-
merged.some((
|
|
6359
|
+
merged.some((index) => index.id === record.activeId)
|
|
6284
6360
|
? record.activeId
|
|
6285
6361
|
: viewId;
|
|
6286
6362
|
return { instances: merged, activeId };
|
|
@@ -6313,7 +6389,7 @@ class ViewInstanceService {
|
|
|
6313
6389
|
}
|
|
6314
6390
|
setActive(viewId, instanceId) {
|
|
6315
6391
|
const record = this.recordFor(viewId);
|
|
6316
|
-
if (record().instances.some((
|
|
6392
|
+
if (record().instances.some((index) => index.id === instanceId)) {
|
|
6317
6393
|
this.commit(viewId, { ...record(), activeId: instanceId });
|
|
6318
6394
|
}
|
|
6319
6395
|
}
|
|
@@ -6332,7 +6408,7 @@ class ViewInstanceService {
|
|
|
6332
6408
|
const record = this.recordFor(viewId);
|
|
6333
6409
|
this.commit(viewId, {
|
|
6334
6410
|
...record(),
|
|
6335
|
-
instances: record().instances.map((
|
|
6411
|
+
instances: record().instances.map((index) => index.id === instanceId ? { ...index, name } : index),
|
|
6336
6412
|
});
|
|
6337
6413
|
}
|
|
6338
6414
|
remove(viewId, instanceId) {
|
|
@@ -6340,7 +6416,7 @@ class ViewInstanceService {
|
|
|
6340
6416
|
return;
|
|
6341
6417
|
}
|
|
6342
6418
|
const record = this.recordFor(viewId);
|
|
6343
|
-
const instances = record().instances.filter((
|
|
6419
|
+
const instances = record().instances.filter((index) => index.id !== instanceId);
|
|
6344
6420
|
const activeId = record().activeId === instanceId ? viewId : record().activeId;
|
|
6345
6421
|
this.commit(viewId, { instances, activeId });
|
|
6346
6422
|
this.viewStates.clear(instanceId);
|
|
@@ -6601,7 +6677,7 @@ class PaneChromeService {
|
|
|
6601
6677
|
clearMinimized(dock) {
|
|
6602
6678
|
const prefix = `${dock}:`;
|
|
6603
6679
|
const current = this.min();
|
|
6604
|
-
if (
|
|
6680
|
+
if ([...current].every((id) => !id.startsWith(prefix))) {
|
|
6605
6681
|
return;
|
|
6606
6682
|
}
|
|
6607
6683
|
this.min.set(new Set([...current].filter((id) => !id.startsWith(prefix))));
|
|
@@ -6668,7 +6744,7 @@ function bakeChild(dock, spec, entry, problems, context) {
|
|
|
6668
6744
|
return {
|
|
6669
6745
|
tab: {
|
|
6670
6746
|
...containerChildTab(dock, spec, declared.surface),
|
|
6671
|
-
...(declared.closable === false
|
|
6747
|
+
...(declared.closable === false && { closable: false }),
|
|
6672
6748
|
},
|
|
6673
6749
|
active: declared.active === true,
|
|
6674
6750
|
};
|
|
@@ -6705,13 +6781,11 @@ class PaneContainersService {
|
|
|
6705
6781
|
const tab = {
|
|
6706
6782
|
path,
|
|
6707
6783
|
instance: `${dock}::${path}`,
|
|
6708
|
-
...(label?.title
|
|
6709
|
-
|
|
6710
|
-
:
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
}),
|
|
6714
|
-
...(label?.icon === undefined ? {} : { icon: label.icon }),
|
|
6784
|
+
...(label?.title !== undefined && {
|
|
6785
|
+
title: label.title,
|
|
6786
|
+
literalTitle: label.titleIsLiteral ?? false,
|
|
6787
|
+
}),
|
|
6788
|
+
...(label?.icon !== undefined && { icon: label.icon }),
|
|
6715
6789
|
};
|
|
6716
6790
|
this.paneTree.commit(dock, setActiveTab(insertTab(tree, target, tab), target, path));
|
|
6717
6791
|
}
|
|
@@ -6737,11 +6811,11 @@ function offRouterPaneTargets(registry, auth) {
|
|
|
6737
6811
|
const routes = registry
|
|
6738
6812
|
.contentRoutes()
|
|
6739
6813
|
.filter((route) => offRouterMountable(registry, auth, route.path))
|
|
6740
|
-
.map(routeTarget);
|
|
6814
|
+
.map((route) => routeTarget(route));
|
|
6741
6815
|
const views = registry
|
|
6742
6816
|
.views()
|
|
6743
6817
|
.filter((view) => auth.meets(view.access))
|
|
6744
|
-
.map(viewTarget);
|
|
6818
|
+
.map((view) => viewTarget(view));
|
|
6745
6819
|
return [...routes, ...views];
|
|
6746
6820
|
}
|
|
6747
6821
|
function containerChildTargets(registry, auth, spec) {
|
|
@@ -6750,14 +6824,14 @@ function containerChildTargets(registry, auth, spec) {
|
|
|
6750
6824
|
.filter((child) => child.segment === undefined || isAddressable(child.segment))
|
|
6751
6825
|
.map((child) => views.find((view) => view.id === child.surface))
|
|
6752
6826
|
.filter((view) => view !== undefined && auth.meets(view.access))
|
|
6753
|
-
.map(viewTarget);
|
|
6827
|
+
.map((view) => viewTarget(view));
|
|
6754
6828
|
}
|
|
6755
6829
|
function routerPaneTargets(registry, auth) {
|
|
6756
6830
|
return registry
|
|
6757
6831
|
.contentRoutes()
|
|
6758
6832
|
.filter((route) => barePathHostableRoute(registry, route.path) !== null &&
|
|
6759
6833
|
auth.meets(route.access))
|
|
6760
|
-
.map(routeTarget);
|
|
6834
|
+
.map((route) => routeTarget(route));
|
|
6761
6835
|
}
|
|
6762
6836
|
function paneTargetEntries(targets, translate) {
|
|
6763
6837
|
return targets.map((target) => ({
|
|
@@ -7126,7 +7200,7 @@ class PaneTabStrip {
|
|
|
7126
7200
|
targetKind: 'view-tab',
|
|
7127
7201
|
viewId: tab.path.slice(VIEW_PANE_PREFIX.length),
|
|
7128
7202
|
region: this.contextGroup(),
|
|
7129
|
-
...(tab.instance
|
|
7203
|
+
...(tab.instance && { instance: tab.instance }),
|
|
7130
7204
|
};
|
|
7131
7205
|
}
|
|
7132
7206
|
return {
|
|
@@ -7180,22 +7254,22 @@ class PaneTabStrip {
|
|
|
7180
7254
|
this.reorderTabs.emit(tabs.filter((tab) => this.canReorder(tab)).map((tab) => tab.path));
|
|
7181
7255
|
}
|
|
7182
7256
|
observeResize() {
|
|
7183
|
-
const
|
|
7184
|
-
if (!
|
|
7257
|
+
const element = this.strip()?.nativeElement;
|
|
7258
|
+
if (!element || typeof ResizeObserver === 'undefined') {
|
|
7185
7259
|
return;
|
|
7186
7260
|
}
|
|
7187
7261
|
const observer = new ResizeObserver(() => this.overflow() && this.measureOverflow());
|
|
7188
|
-
observer.observe(
|
|
7262
|
+
observer.observe(element);
|
|
7189
7263
|
this.destroyRef.onDestroy(() => observer.disconnect());
|
|
7190
7264
|
}
|
|
7191
7265
|
measureOverflow() {
|
|
7192
|
-
const
|
|
7193
|
-
this.overflowing.set(!!
|
|
7266
|
+
const element = this.strip()?.nativeElement;
|
|
7267
|
+
this.overflowing.set(!!element && element.scrollWidth - element.clientWidth > EDGE_TOLERANCE_PX);
|
|
7194
7268
|
}
|
|
7195
7269
|
revealActiveTab() {
|
|
7196
7270
|
const strip = this.strip()?.nativeElement;
|
|
7197
7271
|
const active = this.activeId();
|
|
7198
|
-
const wrapper = strip?.querySelector(`[data-tab-path="${active}"]`)?.parentElement;
|
|
7272
|
+
const wrapper = strip?.querySelector(`[data-tab-path="${CSS.escape(active)}"]`)?.parentElement;
|
|
7199
7273
|
if (!strip || !wrapper) {
|
|
7200
7274
|
return;
|
|
7201
7275
|
}
|
|
@@ -7400,12 +7474,14 @@ class PluginStateService {
|
|
|
7400
7474
|
};
|
|
7401
7475
|
}
|
|
7402
7476
|
removePlugin(pluginId) {
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
this.entries.delete(storageKey);
|
|
7477
|
+
const snapshot = [...this.entries];
|
|
7478
|
+
for (const [storageKey, entry] of snapshot) {
|
|
7479
|
+
if (entry.pluginId !== pluginId) {
|
|
7480
|
+
continue;
|
|
7408
7481
|
}
|
|
7482
|
+
this.cancelPending(entry);
|
|
7483
|
+
entry.value.set(undefined);
|
|
7484
|
+
this.entries.delete(storageKey);
|
|
7409
7485
|
}
|
|
7410
7486
|
this.keysByPlugin.delete(pluginId);
|
|
7411
7487
|
void readStoredValue(this.store, INDEX_PREFIX + pluginId).then((raw) => {
|
|
@@ -7495,10 +7571,11 @@ class PluginStateService {
|
|
|
7495
7571
|
entry.pending = undefined;
|
|
7496
7572
|
}
|
|
7497
7573
|
cancelTimer(entry) {
|
|
7498
|
-
if (entry.timer
|
|
7499
|
-
|
|
7500
|
-
entry.timer = undefined;
|
|
7574
|
+
if (entry.timer === undefined) {
|
|
7575
|
+
return;
|
|
7501
7576
|
}
|
|
7577
|
+
clearTimeout(entry.timer);
|
|
7578
|
+
entry.timer = undefined;
|
|
7502
7579
|
}
|
|
7503
7580
|
withinLimits(pluginId, key, serialised) {
|
|
7504
7581
|
const bytes = serialised.length;
|
|
@@ -7660,10 +7737,9 @@ function ownerByToken(registrations) {
|
|
|
7660
7737
|
...Object.keys(registration.dark ?? {}),
|
|
7661
7738
|
];
|
|
7662
7739
|
for (const name of names) {
|
|
7663
|
-
if (
|
|
7664
|
-
|
|
7740
|
+
if (known.has(name) && !owners.has(name)) {
|
|
7741
|
+
owners.set(name, registration);
|
|
7665
7742
|
}
|
|
7666
|
-
owners.set(name, registration);
|
|
7667
7743
|
}
|
|
7668
7744
|
}
|
|
7669
7745
|
return owners;
|
|
@@ -7702,7 +7778,7 @@ function createPluginLayerRules() {
|
|
|
7702
7778
|
}
|
|
7703
7779
|
const element = document.createElement('style');
|
|
7704
7780
|
element.dataset['lwPluginTheme'] = '';
|
|
7705
|
-
document.head.
|
|
7781
|
+
document.head.append(element);
|
|
7706
7782
|
const sheet = element.sheet;
|
|
7707
7783
|
if (!sheet) {
|
|
7708
7784
|
return null;
|
|
@@ -8009,7 +8085,7 @@ class CapabilityGrantService {
|
|
|
8009
8085
|
})),
|
|
8010
8086
|
}))
|
|
8011
8087
|
.filter((entry) => entry.capabilities.length > 0)
|
|
8012
|
-
.
|
|
8088
|
+
.toSorted((a, b) => a.pluginId.localeCompare(b.pluginId));
|
|
8013
8089
|
}, /* @ts-ignore */
|
|
8014
8090
|
...(ngDevMode ? [{ debugName: "permissions" }] : /* istanbul ignore next */ []));
|
|
8015
8091
|
constructor() {
|
|
@@ -8146,7 +8222,7 @@ class IframeSurface {
|
|
|
8146
8222
|
...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
8147
8223
|
restPath = computed(() => {
|
|
8148
8224
|
if (!this.ownsRest) {
|
|
8149
|
-
return
|
|
8225
|
+
return;
|
|
8150
8226
|
}
|
|
8151
8227
|
return this.hostMounted
|
|
8152
8228
|
? this.hostSub()
|
|
@@ -8177,7 +8253,9 @@ class IframeSurface {
|
|
|
8177
8253
|
queueMicrotask(() => this.push({ ...snapshot, ...this.readResolved() }));
|
|
8178
8254
|
});
|
|
8179
8255
|
inject(DestroyRef).onDestroy(() => {
|
|
8180
|
-
|
|
8256
|
+
for (const entry of this.watched.values()) {
|
|
8257
|
+
entry.stop();
|
|
8258
|
+
}
|
|
8181
8259
|
this.watched.clear();
|
|
8182
8260
|
this.visibility?.disconnect();
|
|
8183
8261
|
this.connection?.destroy();
|
|
@@ -8213,12 +8291,12 @@ class IframeSurface {
|
|
|
8213
8291
|
this.tabs.keep(this.tabRoot);
|
|
8214
8292
|
}
|
|
8215
8293
|
},
|
|
8216
|
-
setDirty: (dirty) => this.dirty.set(dirty
|
|
8217
|
-
stateWatch: (key) => this.watchState(
|
|
8218
|
-
stateSet: (key, value) => this.watched.get(
|
|
8219
|
-
stateClear: (key) => this.watched.get(
|
|
8294
|
+
setDirty: (dirty) => this.dirty.set(dirty),
|
|
8295
|
+
stateWatch: (key) => this.watchState(key),
|
|
8296
|
+
stateSet: (key, value) => this.watched.get(key)?.handle.set(value),
|
|
8297
|
+
stateClear: (key) => this.watched.get(key)?.handle.clear(),
|
|
8220
8298
|
stateUnwatch: (key) => {
|
|
8221
|
-
const name =
|
|
8299
|
+
const name = key;
|
|
8222
8300
|
this.watched.get(name)?.stop();
|
|
8223
8301
|
this.watched.delete(name);
|
|
8224
8302
|
},
|
|
@@ -8228,7 +8306,9 @@ class IframeSurface {
|
|
|
8228
8306
|
.then((remote) => {
|
|
8229
8307
|
this.remote = remote;
|
|
8230
8308
|
this.push({ ...this.reactiveState(), ...this.readResolved() });
|
|
8231
|
-
|
|
8309
|
+
for (const [key, entry] of this.watched) {
|
|
8310
|
+
this.pushState(key, entry.handle.value(), entry.handle.loaded());
|
|
8311
|
+
}
|
|
8232
8312
|
})
|
|
8233
8313
|
.catch(() => undefined);
|
|
8234
8314
|
}
|
|
@@ -8264,7 +8344,7 @@ class IframeSurface {
|
|
|
8264
8344
|
return;
|
|
8265
8345
|
}
|
|
8266
8346
|
this.visibility = new IntersectionObserver((entries) => {
|
|
8267
|
-
const last = entries
|
|
8347
|
+
const last = entries.at(-1);
|
|
8268
8348
|
if (last) {
|
|
8269
8349
|
this.shown.set(last.isIntersecting);
|
|
8270
8350
|
}
|
|
@@ -8287,19 +8367,15 @@ class IframeSurface {
|
|
|
8287
8367
|
theme: this.theme.resolvedTheme(),
|
|
8288
8368
|
preview: this.isPreview(),
|
|
8289
8369
|
shown: this.shown(),
|
|
8290
|
-
...(this.instanceId
|
|
8291
|
-
...(Object.keys(this.routeParams).length > 0
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
roles: this.auth.roles(),
|
|
8300
|
-
},
|
|
8301
|
-
}
|
|
8302
|
-
: {}),
|
|
8370
|
+
...(this.instanceId && { instanceId: this.instanceId }),
|
|
8371
|
+
...(Object.keys(this.routeParams).length > 0 && { params: this.routeParams }),
|
|
8372
|
+
...(rest !== undefined && { rest }),
|
|
8373
|
+
...(this.sessionGranted() && {
|
|
8374
|
+
session: {
|
|
8375
|
+
authenticated: this.auth.authenticated(),
|
|
8376
|
+
roles: this.auth.roles(),
|
|
8377
|
+
},
|
|
8378
|
+
}),
|
|
8303
8379
|
};
|
|
8304
8380
|
}
|
|
8305
8381
|
readResolved() {
|
|
@@ -8312,19 +8388,19 @@ class IframeSurface {
|
|
|
8312
8388
|
return {
|
|
8313
8389
|
tokens,
|
|
8314
8390
|
rootFontSize: styles.fontSize,
|
|
8315
|
-
...(Object.keys(icons).length > 0
|
|
8391
|
+
...(Object.keys(icons).length > 0 && { icons }),
|
|
8316
8392
|
};
|
|
8317
8393
|
}
|
|
8318
8394
|
navigateWithinTabRoot(path) {
|
|
8319
8395
|
if (this.docked) {
|
|
8320
8396
|
if (isDevMode()) {
|
|
8321
|
-
console.warn(`[loom] a docked surface asked to navigate to "${
|
|
8397
|
+
console.warn(`[loom] a docked surface asked to navigate to "${path}" — ignored. ` +
|
|
8322
8398
|
`A docked surface has no address of its own; the channel's navigate is confined to a tab ` +
|
|
8323
8399
|
`root and there is none. Use ctx.navigateContent (the 'navigation' grant) instead.`);
|
|
8324
8400
|
}
|
|
8325
8401
|
return;
|
|
8326
8402
|
}
|
|
8327
|
-
const raw =
|
|
8403
|
+
const raw = path;
|
|
8328
8404
|
const suffix = suffixOf(raw);
|
|
8329
8405
|
const target = normalizePath(raw);
|
|
8330
8406
|
if (target !== this.tabRoot && !target.startsWith(this.tabRoot + '/')) {
|
|
@@ -8393,8 +8469,8 @@ function syntheticDockedRoute(view, instanceId, params = {}) {
|
|
|
8393
8469
|
url: [],
|
|
8394
8470
|
params,
|
|
8395
8471
|
data: {
|
|
8396
|
-
...(view.iframe !== undefined
|
|
8397
|
-
...(view.pluginId
|
|
8472
|
+
...(view.iframe !== undefined && { iframe: view.iframe }),
|
|
8473
|
+
...(view.pluginId && { pluginId: view.pluginId }),
|
|
8398
8474
|
docked: true,
|
|
8399
8475
|
instanceId,
|
|
8400
8476
|
},
|
|
@@ -8410,13 +8486,13 @@ function syntheticRouteFor(route, path, options = {}) {
|
|
|
8410
8486
|
url: segments.map((segment) => new UrlSegment(segment, {})),
|
|
8411
8487
|
params,
|
|
8412
8488
|
data: {
|
|
8413
|
-
...('iframe' in route
|
|
8414
|
-
...('container' in route
|
|
8415
|
-
...(route.pluginId
|
|
8416
|
-
...(route.rest === true
|
|
8417
|
-
...(sub
|
|
8418
|
-
...(options.urlDriven
|
|
8419
|
-
...(options.instanceId
|
|
8489
|
+
...('iframe' in route && { iframe: route.iframe }),
|
|
8490
|
+
...('container' in route && { container: route.container }),
|
|
8491
|
+
...(route.pluginId && { pluginId: route.pluginId }),
|
|
8492
|
+
...(route.rest === true && { rest: true }),
|
|
8493
|
+
...(sub && { sub }),
|
|
8494
|
+
...(options.urlDriven && { urlDriven: true }),
|
|
8495
|
+
...(options.instanceId && { instanceId: options.instanceId }),
|
|
8420
8496
|
},
|
|
8421
8497
|
});
|
|
8422
8498
|
}
|
|
@@ -8652,14 +8728,12 @@ class ContentSecondaryPane {
|
|
|
8652
8728
|
mountParams = computed(() => {
|
|
8653
8729
|
const ctx = this.containerCtx;
|
|
8654
8730
|
if (!ctx) {
|
|
8655
|
-
return
|
|
8731
|
+
return;
|
|
8656
8732
|
}
|
|
8657
8733
|
const match = containerChildForPath(this.registry.contentRoutes(), this.registry.views(), this.path());
|
|
8658
8734
|
return {
|
|
8659
8735
|
...ctx.params,
|
|
8660
|
-
...(match
|
|
8661
|
-
? paramsOfPattern(match.declaration.segment ?? '', match.segmentPath)
|
|
8662
|
-
: {}),
|
|
8736
|
+
...(match && paramsOfPattern(match.declaration.segment ?? '', match.segmentPath)),
|
|
8663
8737
|
};
|
|
8664
8738
|
}, /* @ts-ignore */
|
|
8665
8739
|
...(ngDevMode ? [{ debugName: "mountParams" }] : /* istanbul ignore next */ []));
|
|
@@ -8691,12 +8765,12 @@ class ContentSecondaryPane {
|
|
|
8691
8765
|
...(ngDevMode ? [{ debugName: "activeRoute" }] : /* istanbul ignore next */ []));
|
|
8692
8766
|
iframeSurface = computed(() => {
|
|
8693
8767
|
const route = this.activeRoute();
|
|
8694
|
-
return route?.iframe
|
|
8695
|
-
?
|
|
8768
|
+
return route?.iframe === undefined
|
|
8769
|
+
? null
|
|
8770
|
+
: {
|
|
8696
8771
|
component: IframeSurface,
|
|
8697
8772
|
injector: this.injectorFor(route, this.path(), this.surfaceKey()),
|
|
8698
|
-
}
|
|
8699
|
-
: null;
|
|
8773
|
+
};
|
|
8700
8774
|
}, /* @ts-ignore */
|
|
8701
8775
|
...(ngDevMode ? [{ debugName: "iframeSurface" }] : /* istanbul ignore next */ []));
|
|
8702
8776
|
surface = computed(() => {
|
|
@@ -9148,11 +9222,11 @@ class PaneView {
|
|
|
9148
9222
|
];
|
|
9149
9223
|
}
|
|
9150
9224
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneView, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9151
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneView, isStandalone: true, selector: "lw-pane-view", inputs: { dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null }, leaf: { classPropertyName: "leaf", publicName: "leaf", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex min-h-0 min-w-0 flex-1 flex-col" }, ngImport: i0, template: "<lw-pane-tab-strip\n class=\"shrink-0\"\n [variant]=\"options().variant\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabId()\"\n [urlDriven]=\"pointed()\"\n [source]=\"source()\"\n [reorderable]=\"tabsReorderable()\"\n [draggable]=\"tabsDraggable()\"\n [acceptsTabs]=\"acceptsTabs()\"\n [canAddTab]=\"canAddTab()\"\n [viewContextMenuSlot]=\"viewContextMenu()\"\n [contextGroup]=\"dock()\"\n [paneActions]=\"paneActions\"\n (selectTab)=\"onSelectTab($event)\"\n (escalate)=\"onEscalate($event)\"\n (closeTab)=\"onCloseTab($event)\"\n (unpinTab)=\"onUnpinTab($event)\"\n (addTab)=\"openPicker($event)\"\n (reorderTabs)=\"onReorderTabs($event)\"\n/>\n<ng-template #paneActions>\n <lw-pane-toolbar\n [canSplitRight]=\"canSplitRight()\"\n [canSplitDown]=\"canSplitDown()\"\n [canMinimize]=\"canMinimize()\"\n [canMaximize]=\"canMaximize()\"\n [maximized]=\"maximized()\"\n [canClose]=\"canClose()\"\n [closeLabel]=\"options().closeLabel\"\n (splitRight)=\"splitPane('row')\"\n (splitDown)=\"splitPane('column')\"\n (minimize)=\"minimize()\"\n (toggleMaximize)=\"toggleMaximize()\"\n (closePane)=\"closePane()\"\n />\n</ng-template>\n\n<div class=\"flex min-h-0 min-w-0 flex-1 flex-col\" (pointerdown)=\"onBodyPointerDown()\">\n @if (awaitingContent()) {\n <
|
|
9225
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneView, isStandalone: true, selector: "lw-pane-view", inputs: { dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null }, leaf: { classPropertyName: "leaf", publicName: "leaf", isSignal: true, isRequired: true, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "flex min-h-0 min-w-0 flex-1 flex-col" }, ngImport: i0, template: "<lw-pane-tab-strip\n class=\"shrink-0\"\n [variant]=\"options().variant\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabId()\"\n [urlDriven]=\"pointed()\"\n [source]=\"source()\"\n [reorderable]=\"tabsReorderable()\"\n [draggable]=\"tabsDraggable()\"\n [acceptsTabs]=\"acceptsTabs()\"\n [canAddTab]=\"canAddTab()\"\n [viewContextMenuSlot]=\"viewContextMenu()\"\n [contextGroup]=\"dock()\"\n [paneActions]=\"paneActions\"\n (selectTab)=\"onSelectTab($event)\"\n (escalate)=\"onEscalate($event)\"\n (closeTab)=\"onCloseTab($event)\"\n (unpinTab)=\"onUnpinTab($event)\"\n (addTab)=\"openPicker($event)\"\n (reorderTabs)=\"onReorderTabs($event)\"\n/>\n<ng-template #paneActions>\n <lw-pane-toolbar\n [canSplitRight]=\"canSplitRight()\"\n [canSplitDown]=\"canSplitDown()\"\n [canMinimize]=\"canMinimize()\"\n [canMaximize]=\"canMaximize()\"\n [maximized]=\"maximized()\"\n [canClose]=\"canClose()\"\n [closeLabel]=\"options().closeLabel\"\n (splitRight)=\"splitPane('row')\"\n (splitDown)=\"splitPane('column')\"\n (minimize)=\"minimize()\"\n (toggleMaximize)=\"toggleMaximize()\"\n (closePane)=\"closePane()\"\n />\n</ng-template>\n\n<div class=\"flex min-h-0 min-w-0 flex-1 flex-col\" (pointerdown)=\"onBodyPointerDown()\">\n @if (awaitingContent()) {\n <output\n class=\"flex h-full items-center justify-center p-6 text-center text-sm text-content-faint\"\n data-testid=\"pane-awaiting-content\"\n >\n {{ 'content.split.awaiting' | transloco }}\n </output>\n } @else {\n <lw-content-secondary-pane\n class=\"min-h-0 min-w-0 flex-1\"\n [path]=\"path()\"\n [instanceId]=\"instanceId()\"\n [variant]=\"options().body\"\n [retentionScope]=\"retentionScope()\"\n (instanceReleased)=\"releaseTabInstance()\"\n />\n }\n</div>\n", dependencies: [{ kind: "component", type: ContentSecondaryPane, selector: "lw-content-secondary-pane", inputs: ["path", "variant", "instanceId", "retentionScope"], outputs: ["instanceReleased"] }, { kind: "component", type: PaneTabStrip, selector: "lw-pane-tab-strip", inputs: ["tabs", "activeId", "reorderable", "draggable", "acceptsTabs", "source", "variant", "urlDriven", "contextMenuSlot", "viewContextMenuSlot", "contextGroup", "overflow", "canAddTab", "paneActions"], outputs: ["selectTab", "escalate", "closeTab", "unpinTab", "reorderTabs", "runAction", "addTab", "revealRequest"] }, { kind: "component", type: PaneToolbar, selector: "lw-pane-toolbar", inputs: ["canNewTab", "canSplitRight", "canSplitDown", "canMinimize", "canMaximize", "maximized", "canClose", "closeLabel", "newTabTestId", "splitRightTestId", "splitDownTestId"], outputs: ["newTab", "splitRight", "splitDown", "minimize", "toggleMaximize", "closePane"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9152
9226
|
}
|
|
9153
9227
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneView, decorators: [{
|
|
9154
9228
|
type: Component,
|
|
9155
|
-
args: [{ selector: 'lw-pane-view', imports: [ContentSecondaryPane, PaneTabStrip, PaneToolbar, TranslocoPipe], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'flex min-h-0 min-w-0 flex-1 flex-col' }, template: "<lw-pane-tab-strip\n class=\"shrink-0\"\n [variant]=\"options().variant\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabId()\"\n [urlDriven]=\"pointed()\"\n [source]=\"source()\"\n [reorderable]=\"tabsReorderable()\"\n [draggable]=\"tabsDraggable()\"\n [acceptsTabs]=\"acceptsTabs()\"\n [canAddTab]=\"canAddTab()\"\n [viewContextMenuSlot]=\"viewContextMenu()\"\n [contextGroup]=\"dock()\"\n [paneActions]=\"paneActions\"\n (selectTab)=\"onSelectTab($event)\"\n (escalate)=\"onEscalate($event)\"\n (closeTab)=\"onCloseTab($event)\"\n (unpinTab)=\"onUnpinTab($event)\"\n (addTab)=\"openPicker($event)\"\n (reorderTabs)=\"onReorderTabs($event)\"\n/>\n<ng-template #paneActions>\n <lw-pane-toolbar\n [canSplitRight]=\"canSplitRight()\"\n [canSplitDown]=\"canSplitDown()\"\n [canMinimize]=\"canMinimize()\"\n [canMaximize]=\"canMaximize()\"\n [maximized]=\"maximized()\"\n [canClose]=\"canClose()\"\n [closeLabel]=\"options().closeLabel\"\n (splitRight)=\"splitPane('row')\"\n (splitDown)=\"splitPane('column')\"\n (minimize)=\"minimize()\"\n (toggleMaximize)=\"toggleMaximize()\"\n (closePane)=\"closePane()\"\n />\n</ng-template>\n\n<div class=\"flex min-h-0 min-w-0 flex-1 flex-col\" (pointerdown)=\"onBodyPointerDown()\">\n @if (awaitingContent()) {\n <
|
|
9229
|
+
args: [{ selector: 'lw-pane-view', imports: [ContentSecondaryPane, PaneTabStrip, PaneToolbar, TranslocoPipe], schemas: [CUSTOM_ELEMENTS_SCHEMA], host: { class: 'flex min-h-0 min-w-0 flex-1 flex-col' }, template: "<lw-pane-tab-strip\n class=\"shrink-0\"\n [variant]=\"options().variant\"\n [tabs]=\"stripTabs()\"\n [activeId]=\"activeTabId()\"\n [urlDriven]=\"pointed()\"\n [source]=\"source()\"\n [reorderable]=\"tabsReorderable()\"\n [draggable]=\"tabsDraggable()\"\n [acceptsTabs]=\"acceptsTabs()\"\n [canAddTab]=\"canAddTab()\"\n [viewContextMenuSlot]=\"viewContextMenu()\"\n [contextGroup]=\"dock()\"\n [paneActions]=\"paneActions\"\n (selectTab)=\"onSelectTab($event)\"\n (escalate)=\"onEscalate($event)\"\n (closeTab)=\"onCloseTab($event)\"\n (unpinTab)=\"onUnpinTab($event)\"\n (addTab)=\"openPicker($event)\"\n (reorderTabs)=\"onReorderTabs($event)\"\n/>\n<ng-template #paneActions>\n <lw-pane-toolbar\n [canSplitRight]=\"canSplitRight()\"\n [canSplitDown]=\"canSplitDown()\"\n [canMinimize]=\"canMinimize()\"\n [canMaximize]=\"canMaximize()\"\n [maximized]=\"maximized()\"\n [canClose]=\"canClose()\"\n [closeLabel]=\"options().closeLabel\"\n (splitRight)=\"splitPane('row')\"\n (splitDown)=\"splitPane('column')\"\n (minimize)=\"minimize()\"\n (toggleMaximize)=\"toggleMaximize()\"\n (closePane)=\"closePane()\"\n />\n</ng-template>\n\n<div class=\"flex min-h-0 min-w-0 flex-1 flex-col\" (pointerdown)=\"onBodyPointerDown()\">\n @if (awaitingContent()) {\n <output\n class=\"flex h-full items-center justify-center p-6 text-center text-sm text-content-faint\"\n data-testid=\"pane-awaiting-content\"\n >\n {{ 'content.split.awaiting' | transloco }}\n </output>\n } @else {\n <lw-content-secondary-pane\n class=\"min-h-0 min-w-0 flex-1\"\n [path]=\"path()\"\n [instanceId]=\"instanceId()\"\n [variant]=\"options().body\"\n [retentionScope]=\"retentionScope()\"\n (instanceReleased)=\"releaseTabInstance()\"\n />\n }\n</div>\n" }]
|
|
9156
9230
|
}], propDecorators: { dock: [{ type: i0.Input, args: [{ isSignal: true, alias: "dock", required: true }] }], leaf: [{ type: i0.Input, args: [{ isSignal: true, alias: "leaf", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
|
|
9157
9231
|
|
|
9158
9232
|
class PaneDropZones {
|
|
@@ -9190,13 +9264,12 @@ class PaneDropZones {
|
|
|
9190
9264
|
...(ngDevMode ? [{ debugName: "accepts" }] : /* istanbul ignore next */ []));
|
|
9191
9265
|
constructor() {
|
|
9192
9266
|
effect((onCleanup) => {
|
|
9193
|
-
const
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
onCleanup(() => disposers.forEach((dispose) => dispose()));
|
|
9267
|
+
const disposers = this.zoneIds().map((id) => this.drag.registerZone(id));
|
|
9268
|
+
onCleanup(() => {
|
|
9269
|
+
for (const dispose of disposers) {
|
|
9270
|
+
dispose();
|
|
9271
|
+
}
|
|
9272
|
+
});
|
|
9200
9273
|
});
|
|
9201
9274
|
}
|
|
9202
9275
|
has(edge) {
|
|
@@ -9232,8 +9305,14 @@ class PaneDropZones {
|
|
|
9232
9305
|
this.paneMove.moveToEdge(source, String(event.item.data ?? ''), { dock: this.dock(), paneId: this.paneId() }, edge);
|
|
9233
9306
|
}
|
|
9234
9307
|
}
|
|
9308
|
+
zoneIds() {
|
|
9309
|
+
if (!this.fills()) {
|
|
9310
|
+
return this.edges().map((edge) => this.zoneId(edge));
|
|
9311
|
+
}
|
|
9312
|
+
return this.accepts() ? [this.fillZoneId()] : [];
|
|
9313
|
+
}
|
|
9235
9314
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneDropZones, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9236
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneDropZones, isStandalone: true, selector: "lw-pane-drop-zones", inputs: { dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null }, paneId: { classPropertyName: "paneId", publicName: "paneId", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.opacity-0": "!active()", "class.pointer-events-none": "!active()", "style.grid-template-columns": "\"1fr 2fr 1fr\"", "style.grid-template-rows": "\"1fr 2fr 1fr\"", "attr.aria-hidden": "true" }, classAttribute: "absolute inset-0 z-20 grid transition-opacity" }, ngImport: i0, template: "@if (fills()) {\n @if (accepts()) {\n <div\n class=\"lw-pane-drop-zone col-span-3
|
|
9315
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PaneDropZones, isStandalone: true, selector: "lw-pane-drop-zones", inputs: { dock: { classPropertyName: "dock", publicName: "dock", isSignal: true, isRequired: true, transformFunction: null }, paneId: { classPropertyName: "paneId", publicName: "paneId", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "class.opacity-0": "!active()", "class.pointer-events-none": "!active()", "style.grid-template-columns": "\"1fr 2fr 1fr\"", "style.grid-template-rows": "\"1fr 2fr 1fr\"", "attr.aria-hidden": "true" }, classAttribute: "absolute inset-0 z-20 grid transition-opacity" }, ngImport: i0, template: "@if (fills()) {\n @if (accepts()) {\n <div\n class=\"lw-pane-drop-zone col-span-3 col-start-1 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"fillZoneId()\"\n (cdkDropListDropped)=\"onFill($event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--fill\"></div>\n </div>\n }\n} @else {\n @if (has('left')) {\n <div\n class=\"lw-pane-drop-zone col-start-1 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('left')\"\n (cdkDropListDropped)=\"onDrop('left', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--left\"></div>\n </div>\n }\n @if (has('right')) {\n <div\n class=\"lw-pane-drop-zone col-start-3 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('right')\"\n (cdkDropListDropped)=\"onDrop('right', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--right\"></div>\n </div>\n }\n @if (has('top')) {\n <div\n class=\"lw-pane-drop-zone col-start-2 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('top')\"\n (cdkDropListDropped)=\"onDrop('top', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--top\"></div>\n </div>\n }\n @if (has('bottom')) {\n <div\n class=\"lw-pane-drop-zone col-start-2 row-start-3\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('bottom')\"\n (cdkDropListDropped)=\"onDrop('bottom', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--bottom\"></div>\n </div>\n }\n}\n", dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }] });
|
|
9237
9316
|
}
|
|
9238
9317
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PaneDropZones, decorators: [{
|
|
9239
9318
|
type: Component,
|
|
@@ -9244,7 +9323,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
9244
9323
|
'[style.grid-template-columns]': '"1fr 2fr 1fr"',
|
|
9245
9324
|
'[style.grid-template-rows]': '"1fr 2fr 1fr"',
|
|
9246
9325
|
'[attr.aria-hidden]': 'true',
|
|
9247
|
-
}, template: "@if (fills()) {\n @if (accepts()) {\n <div\n class=\"lw-pane-drop-zone col-span-3
|
|
9326
|
+
}, template: "@if (fills()) {\n @if (accepts()) {\n <div\n class=\"lw-pane-drop-zone col-span-3 col-start-1 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"fillZoneId()\"\n (cdkDropListDropped)=\"onFill($event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--fill\"></div>\n </div>\n }\n} @else {\n @if (has('left')) {\n <div\n class=\"lw-pane-drop-zone col-start-1 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('left')\"\n (cdkDropListDropped)=\"onDrop('left', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--left\"></div>\n </div>\n }\n @if (has('right')) {\n <div\n class=\"lw-pane-drop-zone col-start-3 row-span-3 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('right')\"\n (cdkDropListDropped)=\"onDrop('right', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--right\"></div>\n </div>\n }\n @if (has('top')) {\n <div\n class=\"lw-pane-drop-zone col-start-2 row-start-1\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('top')\"\n (cdkDropListDropped)=\"onDrop('top', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--top\"></div>\n </div>\n }\n @if (has('bottom')) {\n <div\n class=\"lw-pane-drop-zone col-start-2 row-start-3\"\n cdkDropList\n [cdkDropListEnterPredicate]=\"enterPredicate\"\n [id]=\"zoneId('bottom')\"\n (cdkDropListDropped)=\"onDrop('bottom', $event)\"\n >\n <div class=\"lw-pane-drop-preview lw-pane-drop-preview--bottom\"></div>\n </div>\n }\n}\n" }]
|
|
9248
9327
|
}], ctorParameters: () => [], propDecorators: { dock: [{ type: i0.Input, args: [{ isSignal: true, alias: "dock", required: true }] }], paneId: [{ type: i0.Input, args: [{ isSignal: true, alias: "paneId", required: true }] }] } });
|
|
9249
9328
|
|
|
9250
9329
|
class PaneMinimizedStrip {
|
|
@@ -9317,18 +9396,18 @@ class PaneSplitHandle {
|
|
|
9317
9396
|
return;
|
|
9318
9397
|
}
|
|
9319
9398
|
const rect = parent.getBoundingClientRect();
|
|
9320
|
-
const
|
|
9321
|
-
|
|
9399
|
+
const element = this.host.nativeElement;
|
|
9400
|
+
element.setPointerCapture(event.pointerId);
|
|
9322
9401
|
event.preventDefault();
|
|
9323
9402
|
const move = (e) => this.ratioStream.emit(this.fraction(e, rect));
|
|
9324
9403
|
const up = (e) => {
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9404
|
+
element.releasePointerCapture(e.pointerId);
|
|
9405
|
+
element.removeEventListener('pointermove', move);
|
|
9406
|
+
element.removeEventListener('pointerup', up);
|
|
9328
9407
|
this.ratioCommit.emit();
|
|
9329
9408
|
};
|
|
9330
|
-
|
|
9331
|
-
|
|
9409
|
+
element.addEventListener('pointermove', move);
|
|
9410
|
+
element.addEventListener('pointerup', up);
|
|
9332
9411
|
}
|
|
9333
9412
|
onKeydown(event) {
|
|
9334
9413
|
const step = event.shiftKey ? STEP_COARSE$1 : STEP$1;
|
|
@@ -9635,11 +9714,12 @@ function parseWidths(raw) {
|
|
|
9635
9714
|
}
|
|
9636
9715
|
const result = {};
|
|
9637
9716
|
for (const [key, value] of Object.entries(parsed)) {
|
|
9638
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9717
|
+
if (!(typeof value === 'number' && Number.isFinite(value))) {
|
|
9718
|
+
continue;
|
|
9719
|
+
}
|
|
9720
|
+
const clamped = clampWidth(value);
|
|
9721
|
+
if (clamped !== DEFAULT_PANEL_WIDTH) {
|
|
9722
|
+
result[key] = clamped;
|
|
9643
9723
|
}
|
|
9644
9724
|
}
|
|
9645
9725
|
return result;
|
|
@@ -9742,20 +9822,25 @@ class PanelSplitter {
|
|
|
9742
9822
|
const step = (event.shiftKey ? STEP_COARSE : STEP) * this.edgeSign();
|
|
9743
9823
|
let next;
|
|
9744
9824
|
switch (event.key) {
|
|
9745
|
-
case 'ArrowRight':
|
|
9825
|
+
case 'ArrowRight': {
|
|
9746
9826
|
next = this.width() + step;
|
|
9747
9827
|
break;
|
|
9748
|
-
|
|
9828
|
+
}
|
|
9829
|
+
case 'ArrowLeft': {
|
|
9749
9830
|
next = this.width() - step;
|
|
9750
9831
|
break;
|
|
9751
|
-
|
|
9832
|
+
}
|
|
9833
|
+
case 'Home': {
|
|
9752
9834
|
next = this.size.minWidth;
|
|
9753
9835
|
break;
|
|
9754
|
-
|
|
9836
|
+
}
|
|
9837
|
+
case 'End': {
|
|
9755
9838
|
next = this.size.maxWidth;
|
|
9756
9839
|
break;
|
|
9757
|
-
|
|
9840
|
+
}
|
|
9841
|
+
default: {
|
|
9758
9842
|
return;
|
|
9843
|
+
}
|
|
9759
9844
|
}
|
|
9760
9845
|
event.preventDefault();
|
|
9761
9846
|
this.size.setWidth(this.regionId(), next);
|
|
@@ -9812,7 +9897,7 @@ class ShellPanel {
|
|
|
9812
9897
|
activeView = computed(() => {
|
|
9813
9898
|
const path = this.activePath();
|
|
9814
9899
|
if (!path?.startsWith(VIEW_PANE_PREFIX)) {
|
|
9815
|
-
return
|
|
9900
|
+
return;
|
|
9816
9901
|
}
|
|
9817
9902
|
return viewForPanePath(this.registry.views(), path);
|
|
9818
9903
|
}, /* @ts-ignore */
|
|
@@ -9826,7 +9911,7 @@ class ShellPanel {
|
|
|
9826
9911
|
...(ngDevMode ? [{ debugName: "activeContentPath" }] : /* istanbul ignore next */ []));
|
|
9827
9912
|
actions = computed(() => [...(this.activeView()?.actions ?? [])]
|
|
9828
9913
|
.filter((action) => this.auth.visible(action.access))
|
|
9829
|
-
.
|
|
9914
|
+
.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0)), /* @ts-ignore */
|
|
9830
9915
|
...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
|
|
9831
9916
|
panelPaneOptions = PANEL_PANE_OPTIONS;
|
|
9832
9917
|
primaryScope = computed(() => paneRetentionScope(this.region().id, this.paneTree.primaryId(this.region().id)), /* @ts-ignore */
|
|
@@ -10120,10 +10205,14 @@ function registerTabContextMenu(registry, tabs, paneMove, popout, shell) {
|
|
|
10120
10205
|
},
|
|
10121
10206
|
];
|
|
10122
10207
|
const registered = new Set(commands.map((command) => command.id));
|
|
10123
|
-
|
|
10124
|
-
|
|
10125
|
-
|
|
10126
|
-
|
|
10208
|
+
for (const command of commands) {
|
|
10209
|
+
registry.addCommand({ ...command, paletteHidden: true });
|
|
10210
|
+
}
|
|
10211
|
+
for (const item of items) {
|
|
10212
|
+
if (item.command !== undefined && registered.has(item.command)) {
|
|
10213
|
+
registry.addMenuItem(item);
|
|
10214
|
+
}
|
|
10215
|
+
}
|
|
10127
10216
|
}
|
|
10128
10217
|
|
|
10129
10218
|
class ContentArea {
|
|
@@ -10329,7 +10418,7 @@ class ContentGrid {
|
|
|
10329
10418
|
}, /* @ts-ignore */
|
|
10330
10419
|
...(ngDevMode ? [{ debugName: "tree" }] : /* istanbul ignore next */ []));
|
|
10331
10420
|
constructor() {
|
|
10332
|
-
const
|
|
10421
|
+
const document_ = inject(DOCUMENT);
|
|
10333
10422
|
const onKeydown = (event) => {
|
|
10334
10423
|
if (event.key === 'Escape') {
|
|
10335
10424
|
this.chrome.restore();
|
|
@@ -10339,8 +10428,8 @@ class ContentGrid {
|
|
|
10339
10428
|
if (!this.maximized()) {
|
|
10340
10429
|
return;
|
|
10341
10430
|
}
|
|
10342
|
-
|
|
10343
|
-
onCleanup(() =>
|
|
10431
|
+
document_.addEventListener('keydown', onKeydown);
|
|
10432
|
+
onCleanup(() => document_.removeEventListener('keydown', onKeydown));
|
|
10344
10433
|
});
|
|
10345
10434
|
effect(() => {
|
|
10346
10435
|
if (!this.layout.isSplit(CONTENT_DOCK)) {
|
|
@@ -10513,8 +10602,8 @@ class DialogOutlet {
|
|
|
10513
10602
|
if (!this.dialogs().length || !panels.length) {
|
|
10514
10603
|
return;
|
|
10515
10604
|
}
|
|
10516
|
-
const top = panels
|
|
10517
|
-
if (top.contains(this.document.activeElement)) {
|
|
10605
|
+
const top = panels.at(-1)?.nativeElement;
|
|
10606
|
+
if (!top || top.contains(this.document.activeElement)) {
|
|
10518
10607
|
return;
|
|
10519
10608
|
}
|
|
10520
10609
|
(top.querySelector('[data-lw-autofocus]') ?? top).focus();
|
|
@@ -10543,10 +10632,10 @@ class DialogOutlet {
|
|
|
10543
10632
|
return;
|
|
10544
10633
|
}
|
|
10545
10634
|
const first = focusables[0];
|
|
10546
|
-
const last = focusables
|
|
10635
|
+
const last = focusables.at(-1);
|
|
10547
10636
|
const active = this.document.activeElement;
|
|
10548
10637
|
if (backward && active === first) {
|
|
10549
|
-
last
|
|
10638
|
+
last?.focus();
|
|
10550
10639
|
event.preventDefault();
|
|
10551
10640
|
}
|
|
10552
10641
|
else if (!backward && active === last) {
|
|
@@ -10618,7 +10707,7 @@ class DialogOutlet {
|
|
|
10618
10707
|
}
|
|
10619
10708
|
topPanel() {
|
|
10620
10709
|
const panels = this.panels();
|
|
10621
|
-
return panels.
|
|
10710
|
+
return panels.at(-1)?.nativeElement;
|
|
10622
10711
|
}
|
|
10623
10712
|
focusable(root) {
|
|
10624
10713
|
const selector = 'button:not(:disabled), a[href], input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])';
|
|
@@ -10626,7 +10715,7 @@ class DialogOutlet {
|
|
|
10626
10715
|
}
|
|
10627
10716
|
top() {
|
|
10628
10717
|
const list = this.dialogs();
|
|
10629
|
-
return list
|
|
10718
|
+
return list.at(-1);
|
|
10630
10719
|
}
|
|
10631
10720
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogOutlet, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10632
10721
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: DialogOutlet, isStandalone: true, selector: "lw-dialog-outlet", host: { listeners: { "document:keydown.escape": "onEscape()", "document:focusin": "onFocusIn($event)", "document:keydown.tab": "onTab($event, false)", "document:keydown.shift.tab": "onTab($event, true)" } }, viewQueries: [{ propertyName: "panels", predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "@for (d of dialogs(); track d.id) {\n <div [class]=\"wrapperClasses(d)\">\n <button\n type=\"button\"\n class=\"lw-scrim absolute inset-0 cursor-default\"\n [disabled]=\"!d.dismissable\"\n [attr.aria-label]=\"'dialog.close' | transloco\"\n (click)=\"dismiss(d)\"\n ></button>\n\n @if (d.bare) {\n <dialog\n #panel\n open\n class=\"relative z-10 m-0 flex max-h-full w-full flex-col overflow-hidden rounded-t-xl border border-border bg-surface-raised shadow-xl sm:rounded-xl\"\n [class.rounded-xl]=\"d.align === 'top'\"\n [class]=\"panelWidth(d)\"\n aria-modal=\"true\"\n [attr.aria-label]=\"d.title ? (d.title | transloco) : null\"\n tabindex=\"-1\"\n >\n <ng-container\n [ngComponentOutlet]=\"d.component ?? null\"\n [ngComponentOutletInjector]=\"d.injector\"\n />\n </dialog>\n } @else {\n <dialog\n #panel\n open\n class=\"relative z-10 m-0 flex max-h-full w-full flex-col gap-4 overflow-y-auto rounded-t-xl border border-border bg-surface-raised p-5 shadow-xl sm:rounded-xl\"\n [class.rounded-xl]=\"d.align === 'top'\"\n [class]=\"panelWidth(d)\"\n aria-modal=\"true\"\n [attr.aria-label]=\"d.title ? (d.title | transloco) : null\"\n tabindex=\"-1\"\n >\n <div class=\"flex gap-3.5\">\n @if (d.icon; as icon) {\n <span\n class=\"grid h-10 w-10 shrink-0 place-items-center rounded-full\"\n [class]=\"toneCircle(d.tone)\"\n aria-hidden=\"true\"\n >\n <lw-icon [name]=\"icon\" size=\"1.25rem\" />\n </span>\n }\n\n <div class=\"flex min-w-0 flex-1 flex-col gap-3\">\n @if (d.title || (d.kind === 'custom' && d.dismissable)) {\n <div class=\"flex items-start justify-between gap-4\">\n <h2 class=\"min-w-0 text-lg font-semibold text-content\">\n @if (d.title) {\n {{ d.title | transloco }}\n }\n </h2>\n <span class=\"flex shrink-0 items-center gap-1\">\n @if (d.maximizable) {\n <button\n lwButton\n variant=\"ghost\"\n size=\"sm\"\n iconOnly\n type=\"button\"\n class=\"-mt-1\"\n [attr.aria-label]=\"\n (d.ref.maximized() ? 'dialog.restore' : 'dialog.maximize') | transloco\n \"\n (click)=\"d.ref.toggleMaximized()\"\n >\n <lw-icon [name]=\"d.ref.maximized() ? 'restore' : 'maximize'\" size=\"1rem\" />\n </button>\n }\n @if (d.kind === 'custom' && d.dismissable) {\n <button\n lwButton\n variant=\"ghost\"\n size=\"sm\"\n iconOnly\n type=\"button\"\n class=\"-mt-1 -mr-1\"\n [attr.aria-label]=\"'dialog.close' | transloco\"\n (click)=\"dismiss(d)\"\n >\n <lw-icon name=\"close\" size=\"1rem\" />\n </button>\n }\n </span>\n </div>\n }\n\n @if (d.component) {\n <ng-container\n [ngComponentOutlet]=\"d.component\"\n [ngComponentOutletInjector]=\"d.injector\"\n />\n } @else if (d.kind === 'progress') {\n <div class=\"flex items-center gap-3 text-sm text-content\">\n <lw-spinner [label]=\"'progress.busy' | transloco\" />\n @if (d.progressMessage) {\n <span>{{ d.progressMessage() | transloco }}</span>\n }\n </div>\n } @else {\n @if (d.message) {\n <lw-markdown [source]=\"d.message | transloco\" />\n }\n @if (d.requireLabel) {\n <lw-markdown [source]=\"d.requireLabel | transloco\" />\n }\n @if (d.promptValue) {\n <input\n type=\"text\"\n class=\"lw-field\"\n [class.lw-field--invalid]=\"guardError(d)\"\n [value]=\"d.promptValue()\"\n [attr.placeholder]=\"d.placeholder ? (d.placeholder | transloco) : null\"\n (input)=\"onPromptInput(d, $event)\"\n (keydown.enter)=\"onEnter(d)\"\n data-lw-autofocus\n />\n @if (guardError(d); as error) {\n <p class=\"text-xs text-negative\">{{ error | transloco }}</p>\n }\n }\n }\n </div>\n </div>\n\n @if (d.buttons.length) {\n <div class=\"flex justify-end gap-2\">\n @for (b of d.buttons; track $index) {\n <button\n lwButton\n [variant]=\"b.variant\"\n type=\"button\"\n [disabled]=\"confirmBlocked(d, b)\"\n [attr.data-lw-autofocus]=\"b.autofocus ? '' : null\"\n (click)=\"onButton(d, b)\"\n >\n {{ b.label | transloco }}\n </button>\n }\n </div>\n }\n </dialog>\n }\n </div>\n}\n", dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "directive", type: LwButton, selector: "button[lwButton], a[lwButton]", inputs: ["variant", "size", "iconOnly"] }, { kind: "component", type: LwSpinner, selector: "lw-spinner", inputs: ["size", "label"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
@@ -10776,7 +10865,7 @@ class SettingsRegistry {
|
|
|
10776
10865
|
return this.sections()
|
|
10777
10866
|
.map((section) => visibleSection(section, omitted))
|
|
10778
10867
|
.filter((section) => section !== null)
|
|
10779
|
-
.
|
|
10868
|
+
.toSorted((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
10780
10869
|
}, /* @ts-ignore */
|
|
10781
10870
|
...(ngDevMode ? [{ debugName: "all" }] : /* istanbul ignore next */ []));
|
|
10782
10871
|
register(section) {
|
|
@@ -11016,6 +11105,13 @@ function provideTranslationNamespaces(...namespaces) {
|
|
|
11016
11105
|
/** Directory the distribution serves its overlay bundles from, without a trailing slash. */
|
|
11017
11106
|
const TRANSLATION_OVERRIDES = new InjectionToken('TRANSLATION_OVERRIDES');
|
|
11018
11107
|
const DEFAULT_OVERRIDES_PATH = '/i18n/overrides';
|
|
11108
|
+
function withoutTrailingSlashes(path) {
|
|
11109
|
+
let end = path.length;
|
|
11110
|
+
while (end > 0 && path[end - 1] === '/') {
|
|
11111
|
+
end -= 1;
|
|
11112
|
+
}
|
|
11113
|
+
return path.slice(0, end);
|
|
11114
|
+
}
|
|
11019
11115
|
/**
|
|
11020
11116
|
* Load `<basePath>/<lang>.json` and merge it over everything else **key by key**, so a product
|
|
11021
11117
|
* can reword the shell in its own house language ("Save as" rather than "Save as new") without
|
|
@@ -11035,7 +11131,7 @@ const DEFAULT_OVERRIDES_PATH = '/i18n/overrides';
|
|
|
11035
11131
|
* nothing ships is dev-warned too, since a typo there would otherwise be a string that never appears.
|
|
11036
11132
|
*/
|
|
11037
11133
|
function provideTranslationOverrides(basePath = DEFAULT_OVERRIDES_PATH) {
|
|
11038
|
-
const normalized = basePath
|
|
11134
|
+
const normalized = withoutTrailingSlashes(basePath);
|
|
11039
11135
|
if (normalized === '') {
|
|
11040
11136
|
throw new Error('provideTranslationOverrides() needs a directory to load overlays from; ' +
|
|
11041
11137
|
`pass one or omit the argument for "${DEFAULT_OVERRIDES_PATH}".`);
|
|
@@ -11087,7 +11183,9 @@ class TranslocoHttpLoader {
|
|
|
11087
11183
|
return forkJoin([host$, ...namespaced$, this.overrides$(lang)]).pipe(map(([host, ...rest]) => {
|
|
11088
11184
|
const overlay = rest.pop();
|
|
11089
11185
|
const merged = { ...host };
|
|
11090
|
-
|
|
11186
|
+
for (const [index, name] of this.namespaces.entries()) {
|
|
11187
|
+
merged[name] = rest[index];
|
|
11188
|
+
}
|
|
11091
11189
|
return this.applyOverrides(merged, overlay, lang);
|
|
11092
11190
|
}));
|
|
11093
11191
|
}
|
|
@@ -11158,9 +11256,7 @@ class LwIconElement extends HTMLElement {
|
|
|
11158
11256
|
this.render();
|
|
11159
11257
|
}
|
|
11160
11258
|
attributeChangedCallback() {
|
|
11161
|
-
|
|
11162
|
-
this.render();
|
|
11163
|
-
}
|
|
11259
|
+
this.refresh();
|
|
11164
11260
|
}
|
|
11165
11261
|
/**
|
|
11166
11262
|
* Re-draws from the registry without changing the name. A sandboxed surface receives the product's
|
|
@@ -11267,7 +11363,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
11267
11363
|
// GENERATED — do not edit by hand.
|
|
11268
11364
|
// Written by tools/stamp-version.mjs from <Version> in Directory.Build.props.
|
|
11269
11365
|
// Single source of truth: Directory.Build.props (bump via scripts/bump-version.sh).
|
|
11270
|
-
const APP_VERSION = '0.7.
|
|
11366
|
+
const APP_VERSION = '0.7.6';
|
|
11271
11367
|
|
|
11272
11368
|
/**
|
|
11273
11369
|
* The running build's version, sourced from `<Version>` in Directory.Build.props
|
|
@@ -11483,7 +11579,7 @@ class UpdateService {
|
|
|
11483
11579
|
await bestEffort(async () => {
|
|
11484
11580
|
const registrations = (await container?.getRegistrations()) ?? [];
|
|
11485
11581
|
await Promise.all(registrations
|
|
11486
|
-
.filter(isShellWorker)
|
|
11582
|
+
.filter((registration) => isShellWorker(registration))
|
|
11487
11583
|
.map((registration) => registration.unregister()));
|
|
11488
11584
|
});
|
|
11489
11585
|
await bestEffort(async () => {
|
|
@@ -11491,14 +11587,14 @@ class UpdateService {
|
|
|
11491
11587
|
const keys = (await storage?.keys()) ?? [];
|
|
11492
11588
|
await Promise.all(keys
|
|
11493
11589
|
.filter((key) => key.startsWith(WORKER_CACHE_PREFIX))
|
|
11494
|
-
.map((key) => storage?.delete(key)));
|
|
11590
|
+
.map(async (key) => storage?.delete(key)));
|
|
11495
11591
|
});
|
|
11496
11592
|
}
|
|
11497
11593
|
onVersionEvent(event) {
|
|
11498
11594
|
if (event.type === 'VERSION_READY') {
|
|
11499
11595
|
this.onUpdateReady();
|
|
11500
11596
|
}
|
|
11501
|
-
if (event.type === 'VERSION_INSTALLATION_FAILED') {
|
|
11597
|
+
else if (event.type === 'VERSION_INSTALLATION_FAILED') {
|
|
11502
11598
|
this.onUpdateFailed();
|
|
11503
11599
|
}
|
|
11504
11600
|
}
|
|
@@ -11862,7 +11958,7 @@ class CommandInvocationService {
|
|
|
11862
11958
|
.filter((entry) => entry.command.callable === true &&
|
|
11863
11959
|
this.reachable(entry, callerId, granted))
|
|
11864
11960
|
.map((entry) => this.describe(entry.command))
|
|
11865
|
-
.
|
|
11961
|
+
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
11866
11962
|
}
|
|
11867
11963
|
async invoke(callerId, granted, id, args) {
|
|
11868
11964
|
const entry = this.registry
|
|
@@ -12182,17 +12278,21 @@ class LwTooltipElement extends HTMLElement {
|
|
|
12182
12278
|
const centerX = t.left + t.width / 2 - b.width / 2;
|
|
12183
12279
|
const centerY = t.top + t.height / 2 - b.height / 2;
|
|
12184
12280
|
switch (this.position) {
|
|
12185
|
-
case 'bottom':
|
|
12281
|
+
case 'bottom': {
|
|
12186
12282
|
[left, top] = [centerX, t.bottom + TOOLTIP_GAP];
|
|
12187
12283
|
break;
|
|
12188
|
-
|
|
12284
|
+
}
|
|
12285
|
+
case 'left': {
|
|
12189
12286
|
[left, top] = [t.left - b.width - TOOLTIP_GAP, centerY];
|
|
12190
12287
|
break;
|
|
12191
|
-
|
|
12288
|
+
}
|
|
12289
|
+
case 'right': {
|
|
12192
12290
|
[left, top] = [t.right + TOOLTIP_GAP, centerY];
|
|
12193
12291
|
break;
|
|
12194
|
-
|
|
12292
|
+
}
|
|
12293
|
+
default: {
|
|
12195
12294
|
[left, top] = [centerX, t.top - b.height - TOOLTIP_GAP];
|
|
12295
|
+
}
|
|
12196
12296
|
}
|
|
12197
12297
|
}
|
|
12198
12298
|
else {
|
|
@@ -12217,10 +12317,11 @@ class LwTooltipElement extends HTMLElement {
|
|
|
12217
12317
|
}
|
|
12218
12318
|
}
|
|
12219
12319
|
clearTimer() {
|
|
12220
|
-
if (this.showTimer
|
|
12221
|
-
|
|
12222
|
-
this.showTimer = undefined;
|
|
12320
|
+
if (this.showTimer === undefined) {
|
|
12321
|
+
return;
|
|
12223
12322
|
}
|
|
12323
|
+
clearTimeout(this.showTimer);
|
|
12324
|
+
this.showTimer = undefined;
|
|
12224
12325
|
}
|
|
12225
12326
|
}
|
|
12226
12327
|
/** Registers `<lw-tooltip>` once (idempotent) — called from {@link provideShell} at bootstrap. */
|
|
@@ -12231,11 +12332,7 @@ function defineLwTooltip() {
|
|
|
12231
12332
|
}
|
|
12232
12333
|
}
|
|
12233
12334
|
|
|
12234
|
-
const LW_SELECT_TAG = 'lw-select';
|
|
12235
12335
|
const LW_OPTION_TAG = 'lw-option';
|
|
12236
|
-
const LW_SELECT_CHANGE = 'lw-select-change';
|
|
12237
|
-
let nextSelectId = 0;
|
|
12238
|
-
const TYPEAHEAD_RESET_MS = 500;
|
|
12239
12336
|
class LwOptionElement extends HTMLElement {
|
|
12240
12337
|
get value() {
|
|
12241
12338
|
return this.getAttribute('value');
|
|
@@ -12254,6 +12351,87 @@ class LwOptionElement extends HTMLElement {
|
|
|
12254
12351
|
upgradeElementProperty(this, 'icon');
|
|
12255
12352
|
}
|
|
12256
12353
|
}
|
|
12354
|
+
|
|
12355
|
+
function readChoices(host) {
|
|
12356
|
+
return [...host.querySelectorAll(LW_OPTION_TAG)].map((option) => ({
|
|
12357
|
+
value: option.getAttribute('value') ?? '',
|
|
12358
|
+
label: (option.textContent ?? '').trim(),
|
|
12359
|
+
icon: option.getAttribute('icon'),
|
|
12360
|
+
disabled: option.hasAttribute('disabled'),
|
|
12361
|
+
}));
|
|
12362
|
+
}
|
|
12363
|
+
function createTrigger(options) {
|
|
12364
|
+
const trigger = document.createElement('button');
|
|
12365
|
+
trigger.type = 'button';
|
|
12366
|
+
trigger.className = 'lw-select-trigger';
|
|
12367
|
+
trigger.setAttribute('aria-haspopup', 'listbox');
|
|
12368
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
12369
|
+
trigger.setAttribute('aria-controls', options.listboxId);
|
|
12370
|
+
trigger.style.setProperty('anchor-name', options.anchorName);
|
|
12371
|
+
trigger.addEventListener('click', options.onToggle);
|
|
12372
|
+
trigger.addEventListener('keydown', options.onKeydown);
|
|
12373
|
+
const valueSlot = document.createElement('span');
|
|
12374
|
+
valueSlot.className = 'lw-select-value';
|
|
12375
|
+
const chevron = document.createElement('span');
|
|
12376
|
+
chevron.className = 'lw-select-chevron';
|
|
12377
|
+
chevron.setAttribute('aria-hidden', 'true');
|
|
12378
|
+
chevron.textContent = '▾';
|
|
12379
|
+
trigger.append(valueSlot, chevron);
|
|
12380
|
+
return { trigger, valueSlot };
|
|
12381
|
+
}
|
|
12382
|
+
function createListbox(options) {
|
|
12383
|
+
const listbox = document.createElement('div');
|
|
12384
|
+
listbox.id = options.listboxId;
|
|
12385
|
+
listbox.className = 'lw-select-listbox';
|
|
12386
|
+
listbox.setAttribute('role', 'listbox');
|
|
12387
|
+
listbox.hidden = true;
|
|
12388
|
+
listbox.style.setProperty('position-anchor', options.anchorName);
|
|
12389
|
+
listbox.addEventListener('keydown', options.onKeydown);
|
|
12390
|
+
return listbox;
|
|
12391
|
+
}
|
|
12392
|
+
function fillValueSlot(slot, text, icon) {
|
|
12393
|
+
slot.textContent = '';
|
|
12394
|
+
if (icon) {
|
|
12395
|
+
slot.append(createGlyph(icon));
|
|
12396
|
+
}
|
|
12397
|
+
slot.append(document.createTextNode(text));
|
|
12398
|
+
}
|
|
12399
|
+
function createOptionRow(options) {
|
|
12400
|
+
const choice = options.choice;
|
|
12401
|
+
const row = document.createElement('div');
|
|
12402
|
+
row.className = 'lw-select-option';
|
|
12403
|
+
row.setAttribute('role', 'option');
|
|
12404
|
+
row.id = options.id;
|
|
12405
|
+
row.dataset['value'] = choice.value;
|
|
12406
|
+
row.setAttribute('aria-selected', String(options.selected));
|
|
12407
|
+
row.tabIndex = -1;
|
|
12408
|
+
if (choice.disabled) {
|
|
12409
|
+
row.setAttribute('aria-disabled', 'true');
|
|
12410
|
+
}
|
|
12411
|
+
if (choice.icon) {
|
|
12412
|
+
row.append(createGlyph(choice.icon));
|
|
12413
|
+
}
|
|
12414
|
+
row.append(document.createTextNode(choice.label));
|
|
12415
|
+
row.addEventListener('click', () => {
|
|
12416
|
+
if (!choice.disabled) {
|
|
12417
|
+
options.onPick();
|
|
12418
|
+
}
|
|
12419
|
+
});
|
|
12420
|
+
row.addEventListener('pointermove', options.onHover);
|
|
12421
|
+
return row;
|
|
12422
|
+
}
|
|
12423
|
+
function createGlyph(icon) {
|
|
12424
|
+
const glyph = document.createElement('span');
|
|
12425
|
+
glyph.className = 'lw-select-glyph';
|
|
12426
|
+
glyph.setAttribute('aria-hidden', 'true');
|
|
12427
|
+
glyph.textContent = icon;
|
|
12428
|
+
return glyph;
|
|
12429
|
+
}
|
|
12430
|
+
|
|
12431
|
+
const LW_SELECT_TAG = 'lw-select';
|
|
12432
|
+
const LW_SELECT_CHANGE = 'lw-select-change';
|
|
12433
|
+
let nextSelectId = 0;
|
|
12434
|
+
const TYPEAHEAD_RESET_MS = 500;
|
|
12257
12435
|
class LwSelectElement extends HTMLElement {
|
|
12258
12436
|
static observedAttributes = [
|
|
12259
12437
|
'value',
|
|
@@ -12298,10 +12476,7 @@ class LwSelectElement extends HTMLElement {
|
|
|
12298
12476
|
if (!this.trigger) {
|
|
12299
12477
|
return;
|
|
12300
12478
|
}
|
|
12301
|
-
if (name
|
|
12302
|
-
name === 'label' ||
|
|
12303
|
-
name === 'placeholder' ||
|
|
12304
|
-
name === 'disabled') {
|
|
12479
|
+
if (LwSelectElement.observedAttributes.includes(name)) {
|
|
12305
12480
|
this.syncTrigger();
|
|
12306
12481
|
}
|
|
12307
12482
|
if (name === 'disabled' && this.hasAttribute('disabled')) {
|
|
@@ -12321,51 +12496,34 @@ class LwSelectElement extends HTMLElement {
|
|
|
12321
12496
|
attributes: true,
|
|
12322
12497
|
});
|
|
12323
12498
|
}
|
|
12324
|
-
write(
|
|
12499
|
+
write(function_) {
|
|
12325
12500
|
this.observer?.disconnect();
|
|
12326
12501
|
try {
|
|
12327
|
-
|
|
12502
|
+
function_();
|
|
12328
12503
|
}
|
|
12329
12504
|
finally {
|
|
12330
12505
|
this.observe();
|
|
12331
12506
|
}
|
|
12332
12507
|
}
|
|
12333
12508
|
choices() {
|
|
12334
|
-
return
|
|
12335
|
-
value: option.getAttribute('value') ?? '',
|
|
12336
|
-
label: (option.textContent ?? '').trim(),
|
|
12337
|
-
icon: option.getAttribute('icon'),
|
|
12338
|
-
disabled: option.hasAttribute('disabled'),
|
|
12339
|
-
}));
|
|
12509
|
+
return readChoices(this);
|
|
12340
12510
|
}
|
|
12341
12511
|
selectedChoice() {
|
|
12342
12512
|
const value = this.value;
|
|
12343
12513
|
return this.choices().find((choice) => choice.value === value);
|
|
12344
12514
|
}
|
|
12345
12515
|
buildControl() {
|
|
12346
|
-
const trigger =
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
const chevron = document.createElement('span');
|
|
12358
|
-
chevron.className = 'lw-select-chevron';
|
|
12359
|
-
chevron.setAttribute('aria-hidden', 'true');
|
|
12360
|
-
chevron.textContent = '▾';
|
|
12361
|
-
trigger.append(valueSlot, chevron);
|
|
12362
|
-
const listbox = document.createElement('div');
|
|
12363
|
-
listbox.id = this.listboxId;
|
|
12364
|
-
listbox.className = 'lw-select-listbox';
|
|
12365
|
-
listbox.setAttribute('role', 'listbox');
|
|
12366
|
-
listbox.hidden = true;
|
|
12367
|
-
listbox.style.setProperty('position-anchor', this.anchorName);
|
|
12368
|
-
listbox.addEventListener('keydown', (event) => this.onListboxKeydown(event));
|
|
12516
|
+
const { trigger, valueSlot } = createTrigger({
|
|
12517
|
+
anchorName: this.anchorName,
|
|
12518
|
+
listboxId: this.listboxId,
|
|
12519
|
+
onToggle: () => this.toggle(),
|
|
12520
|
+
onKeydown: (event) => this.onTriggerKeydown(event),
|
|
12521
|
+
});
|
|
12522
|
+
const listbox = createListbox({
|
|
12523
|
+
anchorName: this.anchorName,
|
|
12524
|
+
listboxId: this.listboxId,
|
|
12525
|
+
onKeydown: (event) => this.onListboxKeydown(event),
|
|
12526
|
+
});
|
|
12369
12527
|
this.append(trigger, listbox);
|
|
12370
12528
|
this.trigger = trigger;
|
|
12371
12529
|
this.valueSlot = valueSlot;
|
|
@@ -12380,22 +12538,13 @@ class LwSelectElement extends HTMLElement {
|
|
|
12380
12538
|
const label = this.getAttribute('label');
|
|
12381
12539
|
const selected = this.selectedChoice();
|
|
12382
12540
|
const text = selected?.label ?? this.getAttribute('placeholder') ?? '';
|
|
12383
|
-
const icon = selected?.icon ?? null;
|
|
12384
12541
|
this.write(() => {
|
|
12385
12542
|
trigger.disabled = this.hasAttribute('disabled');
|
|
12386
12543
|
if (label !== null) {
|
|
12387
12544
|
trigger.setAttribute('aria-label', label);
|
|
12388
12545
|
this.listbox?.setAttribute('aria-label', label);
|
|
12389
12546
|
}
|
|
12390
|
-
valueSlot
|
|
12391
|
-
if (icon) {
|
|
12392
|
-
const glyph = document.createElement('span');
|
|
12393
|
-
glyph.className = 'lw-select-glyph';
|
|
12394
|
-
glyph.setAttribute('aria-hidden', 'true');
|
|
12395
|
-
glyph.textContent = icon;
|
|
12396
|
-
valueSlot.append(glyph);
|
|
12397
|
-
}
|
|
12398
|
-
valueSlot.append(document.createTextNode(text));
|
|
12547
|
+
fillValueSlot(valueSlot, text, selected?.icon ?? null);
|
|
12399
12548
|
});
|
|
12400
12549
|
}
|
|
12401
12550
|
toggle() {
|
|
@@ -12421,7 +12570,9 @@ class LwSelectElement extends HTMLElement {
|
|
|
12421
12570
|
const choices = this.choices();
|
|
12422
12571
|
const selected = choices.findIndex((choice) => choice.value === this.value);
|
|
12423
12572
|
this.setActive(Math.max(0, selected));
|
|
12424
|
-
document.addEventListener('pointerdown', this.onOutsidePointer,
|
|
12573
|
+
document.addEventListener('pointerdown', this.onOutsidePointer, {
|
|
12574
|
+
capture: true,
|
|
12575
|
+
});
|
|
12425
12576
|
}
|
|
12426
12577
|
close(refocusTrigger = true) {
|
|
12427
12578
|
const listbox = this.listbox;
|
|
@@ -12441,94 +12592,83 @@ class LwSelectElement extends HTMLElement {
|
|
|
12441
12592
|
}
|
|
12442
12593
|
}
|
|
12443
12594
|
renderOptions() {
|
|
12444
|
-
|
|
12595
|
+
const listbox = this.listbox;
|
|
12596
|
+
if (!listbox) {
|
|
12445
12597
|
return;
|
|
12446
12598
|
}
|
|
12447
|
-
const
|
|
12448
|
-
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
if (choice.disabled) {
|
|
12456
|
-
option.setAttribute('aria-disabled', 'true');
|
|
12457
|
-
}
|
|
12458
|
-
if (choice.icon) {
|
|
12459
|
-
const glyph = document.createElement('span');
|
|
12460
|
-
glyph.className = 'lw-select-glyph';
|
|
12461
|
-
glyph.setAttribute('aria-hidden', 'true');
|
|
12462
|
-
glyph.textContent = choice.icon;
|
|
12463
|
-
option.append(glyph);
|
|
12464
|
-
}
|
|
12465
|
-
option.append(document.createTextNode(choice.label));
|
|
12466
|
-
option.addEventListener('click', () => {
|
|
12467
|
-
if (!choice.disabled) {
|
|
12468
|
-
this.commit(choice.value);
|
|
12469
|
-
}
|
|
12470
|
-
});
|
|
12471
|
-
option.addEventListener('pointermove', () => this.setActive(index));
|
|
12472
|
-
return option;
|
|
12473
|
-
});
|
|
12474
|
-
const listbox = this.listbox;
|
|
12475
|
-
this.write(() => listbox.replaceChildren(...items));
|
|
12599
|
+
const rows = this.choices().map((choice, index) => createOptionRow({
|
|
12600
|
+
choice,
|
|
12601
|
+
id: `${this.listboxId}-opt-${index}`,
|
|
12602
|
+
selected: choice.value === this.value,
|
|
12603
|
+
onPick: () => this.commit(choice.value),
|
|
12604
|
+
onHover: () => this.setActive(index),
|
|
12605
|
+
}));
|
|
12606
|
+
this.write(() => listbox.replaceChildren(...rows));
|
|
12476
12607
|
}
|
|
12477
12608
|
setActive(index) {
|
|
12478
12609
|
if (!this.listbox) {
|
|
12479
12610
|
return;
|
|
12480
12611
|
}
|
|
12481
|
-
const
|
|
12482
|
-
if (
|
|
12612
|
+
const rows = [...this.listbox.children];
|
|
12613
|
+
if (rows.length === 0) {
|
|
12483
12614
|
return;
|
|
12484
12615
|
}
|
|
12485
|
-
this.activeIndex = Math.max(0, Math.min(index,
|
|
12616
|
+
this.activeIndex = Math.max(0, Math.min(index, rows.length - 1));
|
|
12486
12617
|
this.write(() => {
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
}
|
|
12618
|
+
for (const [index_, row] of rows.entries()) {
|
|
12619
|
+
row.classList.toggle('is-active', index_ === this.activeIndex);
|
|
12620
|
+
row.tabIndex = index_ === this.activeIndex ? 0 : -1;
|
|
12621
|
+
}
|
|
12491
12622
|
});
|
|
12492
|
-
const active =
|
|
12623
|
+
const active = rows[this.activeIndex];
|
|
12493
12624
|
active.focus();
|
|
12494
12625
|
active.scrollIntoView?.({ block: 'nearest' });
|
|
12495
12626
|
}
|
|
12496
12627
|
onTriggerKeydown(event) {
|
|
12497
|
-
if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {
|
|
12498
|
-
|
|
12499
|
-
this.openListbox();
|
|
12628
|
+
if (!['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {
|
|
12629
|
+
return;
|
|
12500
12630
|
}
|
|
12631
|
+
event.preventDefault();
|
|
12632
|
+
this.openListbox();
|
|
12501
12633
|
}
|
|
12502
12634
|
onListboxKeydown(event) {
|
|
12503
12635
|
const last = this.choices().length - 1;
|
|
12504
12636
|
switch (event.key) {
|
|
12505
|
-
case 'ArrowDown':
|
|
12637
|
+
case 'ArrowDown': {
|
|
12506
12638
|
this.setActive(this.activeIndex >= last ? 0 : this.activeIndex + 1);
|
|
12507
12639
|
break;
|
|
12508
|
-
|
|
12640
|
+
}
|
|
12641
|
+
case 'ArrowUp': {
|
|
12509
12642
|
this.setActive(this.activeIndex <= 0 ? last : this.activeIndex - 1);
|
|
12510
12643
|
break;
|
|
12511
|
-
|
|
12644
|
+
}
|
|
12645
|
+
case 'Home': {
|
|
12512
12646
|
this.setActive(0);
|
|
12513
12647
|
break;
|
|
12514
|
-
|
|
12648
|
+
}
|
|
12649
|
+
case 'End': {
|
|
12515
12650
|
this.setActive(last);
|
|
12516
12651
|
break;
|
|
12652
|
+
}
|
|
12517
12653
|
case 'Enter':
|
|
12518
|
-
case ' ':
|
|
12654
|
+
case ' ': {
|
|
12519
12655
|
this.commitActive();
|
|
12520
12656
|
break;
|
|
12521
|
-
|
|
12657
|
+
}
|
|
12658
|
+
case 'Escape': {
|
|
12522
12659
|
this.close();
|
|
12523
12660
|
break;
|
|
12524
|
-
|
|
12661
|
+
}
|
|
12662
|
+
case 'Tab': {
|
|
12525
12663
|
this.close(false);
|
|
12526
12664
|
return;
|
|
12527
|
-
|
|
12665
|
+
}
|
|
12666
|
+
default: {
|
|
12528
12667
|
if (event.key.length === 1) {
|
|
12529
12668
|
this.onTypeahead(event.key);
|
|
12530
12669
|
}
|
|
12531
12670
|
return;
|
|
12671
|
+
}
|
|
12532
12672
|
}
|
|
12533
12673
|
event.preventDefault();
|
|
12534
12674
|
}
|
|
@@ -12540,7 +12680,7 @@ class LwSelectElement extends HTMLElement {
|
|
|
12540
12680
|
this.typeaheadTimer = setTimeout(() => (this.typeahead = ''), TYPEAHEAD_RESET_MS);
|
|
12541
12681
|
const match = this.choices().findIndex((choice) => !choice.disabled &&
|
|
12542
12682
|
choice.label.toLowerCase().startsWith(this.typeahead));
|
|
12543
|
-
if (match
|
|
12683
|
+
if (match !== -1) {
|
|
12544
12684
|
this.setActive(match);
|
|
12545
12685
|
}
|
|
12546
12686
|
}
|
|
@@ -12689,10 +12829,11 @@ class LwButtonElement extends HTMLElement {
|
|
|
12689
12829
|
}
|
|
12690
12830
|
}
|
|
12691
12831
|
onKeydown = (event) => {
|
|
12692
|
-
if ((event.key === 'Enter' || event.key === ' ')
|
|
12693
|
-
|
|
12694
|
-
this.click();
|
|
12832
|
+
if (!(event.key === 'Enter' || event.key === ' ') || this.disabled) {
|
|
12833
|
+
return;
|
|
12695
12834
|
}
|
|
12835
|
+
event.preventDefault();
|
|
12836
|
+
this.click();
|
|
12696
12837
|
};
|
|
12697
12838
|
render() {
|
|
12698
12839
|
const stale = [...this.classList].filter((cls) => cls.startsWith('lw-btn'));
|
|
@@ -12871,7 +13012,7 @@ class ViewVisibilityService {
|
|
|
12871
13012
|
return this.stash
|
|
12872
13013
|
.keyedInstances()
|
|
12873
13014
|
.filter((entry) => !entry.key.startsWith(CONTAINER_DOCK_PREFIX) &&
|
|
12874
|
-
entry.key.split('|')[1] === path)
|
|
13015
|
+
entry.key.split('|', 2)[1] === path)
|
|
12875
13016
|
.map((entry) => entry.instance);
|
|
12876
13017
|
}
|
|
12877
13018
|
removeTabs(path) {
|
|
@@ -12943,10 +13084,11 @@ class RailWorkspaceEntries {
|
|
|
12943
13084
|
reconcile() {
|
|
12944
13085
|
const wanted = this.wantedItems();
|
|
12945
13086
|
for (const [id, registration] of this.registered) {
|
|
12946
|
-
if (
|
|
12947
|
-
|
|
12948
|
-
this.registered.delete(id);
|
|
13087
|
+
if (wanted.has(id)) {
|
|
13088
|
+
continue;
|
|
12949
13089
|
}
|
|
13090
|
+
registration.disposable.dispose();
|
|
13091
|
+
this.registered.delete(id);
|
|
12950
13092
|
}
|
|
12951
13093
|
for (const [id, item] of wanted) {
|
|
12952
13094
|
const current = this.registered.get(id);
|
|
@@ -13188,10 +13330,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
13188
13330
|
type: Service
|
|
13189
13331
|
}] });
|
|
13190
13332
|
function installCompositionReport(report) {
|
|
13191
|
-
if (
|
|
13333
|
+
if (globalThis.window === undefined) {
|
|
13192
13334
|
return;
|
|
13193
13335
|
}
|
|
13194
|
-
const host =
|
|
13336
|
+
const host = globalThis;
|
|
13195
13337
|
if (host['loomweaver'] !== undefined) {
|
|
13196
13338
|
return;
|
|
13197
13339
|
}
|
|
@@ -13262,7 +13404,7 @@ class PluginEnablementService {
|
|
|
13262
13404
|
const disabled = this.disabledSet();
|
|
13263
13405
|
return [...this.names().entries()]
|
|
13264
13406
|
.map(([id, name]) => ({ id, name, enabled: !disabled.has(id) }))
|
|
13265
|
-
.
|
|
13407
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
13266
13408
|
}, /* @ts-ignore */
|
|
13267
13409
|
...(ngDevMode ? [{ debugName: "plugins" }] : /* istanbul ignore next */ []));
|
|
13268
13410
|
constructor() {
|
|
@@ -13416,7 +13558,7 @@ function parseCatalogEntry(raw) {
|
|
|
13416
13558
|
function dedupeById(items) {
|
|
13417
13559
|
const result = [];
|
|
13418
13560
|
for (const item of items) {
|
|
13419
|
-
if (item &&
|
|
13561
|
+
if (item && result.every((existing) => existing.id !== item.id)) {
|
|
13420
13562
|
result.push(item);
|
|
13421
13563
|
}
|
|
13422
13564
|
}
|
|
@@ -13431,7 +13573,7 @@ function parseInstalledList(raw) {
|
|
|
13431
13573
|
if (!Array.isArray(parsed)) {
|
|
13432
13574
|
return [];
|
|
13433
13575
|
}
|
|
13434
|
-
return dedupeById(parsed.map(parseInstalledPlugin));
|
|
13576
|
+
return dedupeById(parsed.map((raw) => parseInstalledPlugin(raw)));
|
|
13435
13577
|
}
|
|
13436
13578
|
catch {
|
|
13437
13579
|
return [];
|
|
@@ -13441,7 +13583,7 @@ function parseCatalogList(raw) {
|
|
|
13441
13583
|
if (!Array.isArray(raw)) {
|
|
13442
13584
|
return [];
|
|
13443
13585
|
}
|
|
13444
|
-
return dedupeById(raw.map(parseCatalogEntry));
|
|
13586
|
+
return dedupeById(raw.map((entry) => parseCatalogEntry(entry)));
|
|
13445
13587
|
}
|
|
13446
13588
|
|
|
13447
13589
|
const STORAGE_KEY$2 = 'lw.shell.deployed-plugins';
|
|
@@ -13469,7 +13611,7 @@ class PluginDeploymentService {
|
|
|
13469
13611
|
adopt(entries) {
|
|
13470
13612
|
this.persist(entries
|
|
13471
13613
|
.filter((entry) => entry.deployed === true)
|
|
13472
|
-
.map(withoutCatalogMetadata));
|
|
13614
|
+
.map((entry) => withoutCatalogMetadata(entry)));
|
|
13473
13615
|
}
|
|
13474
13616
|
isDeployed(id) {
|
|
13475
13617
|
return this.entries().some((entry) => entry.id === id);
|
|
@@ -13587,10 +13729,10 @@ function registerDefaultSettings(settings) {
|
|
|
13587
13729
|
|
|
13588
13730
|
function fuzzyScore(query, label) {
|
|
13589
13731
|
const needle = query.toLowerCase();
|
|
13590
|
-
const haystack = label.toLowerCase();
|
|
13591
13732
|
if (!needle) {
|
|
13592
13733
|
return 0;
|
|
13593
13734
|
}
|
|
13735
|
+
const haystack = label.toLowerCase();
|
|
13594
13736
|
let score = 0;
|
|
13595
13737
|
let searchFrom = 0;
|
|
13596
13738
|
let previous = -2;
|
|
@@ -13620,7 +13762,7 @@ function formatterFor(locale) {
|
|
|
13620
13762
|
}
|
|
13621
13763
|
catch {
|
|
13622
13764
|
try {
|
|
13623
|
-
return new Intl.RelativeTimeFormat(locale.
|
|
13765
|
+
return new Intl.RelativeTimeFormat(locale.replaceAll('_', '-'), {
|
|
13624
13766
|
numeric: 'auto',
|
|
13625
13767
|
});
|
|
13626
13768
|
}
|
|
@@ -13697,7 +13839,7 @@ function ranked(query, entries) {
|
|
|
13697
13839
|
return entries
|
|
13698
13840
|
.map((entry) => ({ entry, score: fuzzyScore(query, entry.label) }))
|
|
13699
13841
|
.filter((scored) => scored.score !== null)
|
|
13700
|
-
.
|
|
13842
|
+
.toSorted((a, b) => b.score - a.score)
|
|
13701
13843
|
.map((scored) => scored.entry);
|
|
13702
13844
|
}
|
|
13703
13845
|
class CommandPalette {
|
|
@@ -13754,9 +13896,9 @@ class CommandPalette {
|
|
|
13754
13896
|
pinned: tab.pinned,
|
|
13755
13897
|
closable: tab.closable,
|
|
13756
13898
|
lastActive: tab.lastActive,
|
|
13757
|
-
time: tab.lastActive
|
|
13758
|
-
?
|
|
13759
|
-
:
|
|
13899
|
+
time: tab.lastActive === undefined
|
|
13900
|
+
? undefined
|
|
13901
|
+
: formatRelativeTime(locale, tab.lastActive, now),
|
|
13760
13902
|
}));
|
|
13761
13903
|
}, /* @ts-ignore */
|
|
13762
13904
|
...(ngDevMode ? [{ debugName: "tabEntries" }] : /* istanbul ignore next */ []));
|
|
@@ -13785,7 +13927,7 @@ class CommandPalette {
|
|
|
13785
13927
|
const query = this.query().trim();
|
|
13786
13928
|
const entries = this.tabEntries();
|
|
13787
13929
|
if (!query) {
|
|
13788
|
-
return [...entries].
|
|
13930
|
+
return [...entries].toSorted((a, b) => (b.lastActive ?? 0) - (a.lastActive ?? 0));
|
|
13789
13931
|
}
|
|
13790
13932
|
return ranked(query, entries);
|
|
13791
13933
|
}, /* @ts-ignore */
|
|
@@ -13860,7 +14002,7 @@ class CommandPalette {
|
|
|
13860
14002
|
return;
|
|
13861
14003
|
}
|
|
13862
14004
|
const entry = this.results()[this.activeIndex()];
|
|
13863
|
-
if (
|
|
14005
|
+
if (entry?.kind !== 'tab') {
|
|
13864
14006
|
return;
|
|
13865
14007
|
}
|
|
13866
14008
|
event.preventDefault();
|
|
@@ -14261,9 +14403,12 @@ function seedBuiltInMenus(registry, layout, deps) {
|
|
|
14261
14403
|
return;
|
|
14262
14404
|
}
|
|
14263
14405
|
registerTabContextMenu(registry, deps.tabs, deps.paneMove, deps.popout, deps.features);
|
|
14406
|
+
seedRailMenus(registry, layout, deps);
|
|
14407
|
+
seedViewMenus(registry, layout, deps);
|
|
14408
|
+
}
|
|
14409
|
+
function seedRailMenus(registry, layout, deps) {
|
|
14264
14410
|
const railCount = layout.regions.filter((region) => region.type === 'rail').length;
|
|
14265
14411
|
const rail = deps.features.rail;
|
|
14266
|
-
const sidebar = deps.features.sidebar;
|
|
14267
14412
|
if (railCount >= 1 && rail.hideItems) {
|
|
14268
14413
|
registerRailContextMenu(registry, deps.railItems);
|
|
14269
14414
|
}
|
|
@@ -14273,6 +14418,9 @@ function seedBuiltInMenus(registry, layout, deps) {
|
|
|
14273
14418
|
if (railCount >= 1 && rail.curate) {
|
|
14274
14419
|
registerRailCustomizeMenu(registry);
|
|
14275
14420
|
}
|
|
14421
|
+
}
|
|
14422
|
+
function seedViewMenus(registry, layout, deps) {
|
|
14423
|
+
const sidebar = deps.features.sidebar;
|
|
14276
14424
|
if (sidebar.resetViewState) {
|
|
14277
14425
|
registerViewResetMenu(registry, deps.viewStates, deps.viewInstances);
|
|
14278
14426
|
}
|
|
@@ -14421,7 +14569,7 @@ class BootLatchedIdentity {
|
|
|
14421
14569
|
return this.latched;
|
|
14422
14570
|
}
|
|
14423
14571
|
const id = this.read();
|
|
14424
|
-
if (id
|
|
14572
|
+
if (!id) {
|
|
14425
14573
|
return null;
|
|
14426
14574
|
}
|
|
14427
14575
|
this.latched = id;
|
|
@@ -14711,7 +14859,7 @@ function surfaceRoute(route, retained) {
|
|
|
14711
14859
|
function subStub(path, pathMatch) {
|
|
14712
14860
|
return {
|
|
14713
14861
|
path,
|
|
14714
|
-
...(pathMatch
|
|
14862
|
+
...(pathMatch && { pathMatch }),
|
|
14715
14863
|
component: ContentSubStub,
|
|
14716
14864
|
data: { content: true, sub: true },
|
|
14717
14865
|
};
|
|
@@ -15002,13 +15150,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
15002
15150
|
type: Service
|
|
15003
15151
|
}] });
|
|
15004
15152
|
function pathOfKey(key) {
|
|
15005
|
-
return key.split('|')[1] ?? '';
|
|
15153
|
+
return key.split('|', 2)[1] ?? '';
|
|
15006
15154
|
}
|
|
15007
15155
|
function stashKeyLive(key, open, routes, views) {
|
|
15008
15156
|
if (key.startsWith(PRIMARY_RETENTION_PREFIX)) {
|
|
15009
15157
|
return true;
|
|
15010
15158
|
}
|
|
15011
|
-
const [scope, path] = key.split('|');
|
|
15159
|
+
const [scope, path] = key.split('|', 2);
|
|
15012
15160
|
if (!tabOpen(open.get(scope), routes, path)) {
|
|
15013
15161
|
return false;
|
|
15014
15162
|
}
|
|
@@ -15541,7 +15689,8 @@ class IconRegistry {
|
|
|
15541
15689
|
setIcon(name, safe);
|
|
15542
15690
|
added.push(name);
|
|
15543
15691
|
}
|
|
15544
|
-
return { dispose: () =>
|
|
15692
|
+
return { dispose: () => { for (const name of added)
|
|
15693
|
+
removeIcon(name); } };
|
|
15545
15694
|
}
|
|
15546
15695
|
resolve(name) {
|
|
15547
15696
|
return resolveIcon(name);
|
|
@@ -15691,6 +15840,9 @@ function providePlugins(...plugins) {
|
|
|
15691
15840
|
];
|
|
15692
15841
|
}
|
|
15693
15842
|
|
|
15843
|
+
/** Multi-provider token: each contribution adds one sandboxed plugin to load. */
|
|
15844
|
+
const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
|
|
15845
|
+
|
|
15694
15846
|
const STORAGE_KEY = 'lw.shell.installed-plugins';
|
|
15695
15847
|
/**
|
|
15696
15848
|
* The user's installed community plugins. Holds only the state: which catalog entries the
|
|
@@ -15722,7 +15874,7 @@ class PluginInstallService {
|
|
|
15722
15874
|
return this.entries().some((entry) => entry.id === id);
|
|
15723
15875
|
}
|
|
15724
15876
|
/** The installed entry for an id, or `undefined` — the baseline an update is compared against. */
|
|
15725
|
-
|
|
15877
|
+
byId(id) {
|
|
15726
15878
|
return this.entries().find((entry) => entry.id === id);
|
|
15727
15879
|
}
|
|
15728
15880
|
/**
|
|
@@ -15787,202 +15939,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
15787
15939
|
type: Service
|
|
15788
15940
|
}], ctorParameters: () => [] });
|
|
15789
15941
|
|
|
15790
|
-
|
|
15791
|
-
|
|
15792
|
-
if (!Array.isArray(raw)) {
|
|
15793
|
-
return [];
|
|
15794
|
-
}
|
|
15795
|
-
return raw
|
|
15796
|
-
.filter((option) => typeof option === 'object' &&
|
|
15797
|
-
option !== null &&
|
|
15798
|
-
typeof option['value'] === 'string' &&
|
|
15799
|
-
typeof option['label'] === 'string')
|
|
15800
|
-
.map((option) => ({ value: option.value, label: option.label }));
|
|
15801
|
-
}
|
|
15802
|
-
function optionalString(value) {
|
|
15803
|
-
return typeof value === 'string' ? value : undefined;
|
|
15804
|
-
}
|
|
15805
|
-
function optionalNumber(value) {
|
|
15806
|
-
return typeof value === 'number' ? value : undefined;
|
|
15807
|
-
}
|
|
15808
|
-
function buildTextControl(value, control) {
|
|
15809
|
-
return {
|
|
15810
|
-
kind: 'text',
|
|
15811
|
-
value,
|
|
15812
|
-
inputType: INPUT_TYPES.find((type) => type === control['inputType']),
|
|
15813
|
-
placeholder: optionalString(control['placeholder']),
|
|
15814
|
-
};
|
|
15942
|
+
function levelOf(plugin) {
|
|
15943
|
+
return plugin.level ?? DEFAULT_ISOLATION_LEVEL;
|
|
15815
15944
|
}
|
|
15816
|
-
function
|
|
15817
|
-
const
|
|
15818
|
-
|
|
15819
|
-
|
|
15945
|
+
function signatureOf(plugin) {
|
|
15946
|
+
const sorted = (values) => [...(values ?? [])].toSorted((a, b) => a.localeCompare(b)).join(',');
|
|
15947
|
+
return `${plugin.entryUrl}|${sorted(plugin.capabilities)}|${sorted(plugin.granted)}|${plugin.version ?? ''}|${levelOf(plugin)}`;
|
|
15948
|
+
}
|
|
15949
|
+
function runnablePlugins(composed, installed, deployed, catalogCap) {
|
|
15950
|
+
const claimed = new Set(composed.map((plugin) => plugin.id));
|
|
15951
|
+
const provided = new Set(deployed.map((plugin) => plugin.id));
|
|
15952
|
+
const fromCatalog = [];
|
|
15953
|
+
for (const plugin of [...deployed, ...installed]) {
|
|
15954
|
+
if (claimed.has(plugin.id)) {
|
|
15955
|
+
continue;
|
|
15956
|
+
}
|
|
15957
|
+
const asked = plugin.level ?? DEFAULT_ISOLATION_LEVEL;
|
|
15958
|
+
if (exceedsLevel(asked, catalogCap)) {
|
|
15959
|
+
console.error(`Plugin "${plugin.id}" asks to run ${asked}, which this catalog may not confer ` +
|
|
15960
|
+
`(its cap is ${catalogCap}). It is not started.`);
|
|
15961
|
+
continue;
|
|
15962
|
+
}
|
|
15963
|
+
claimed.add(plugin.id);
|
|
15964
|
+
fromCatalog.push({
|
|
15965
|
+
id: plugin.id,
|
|
15966
|
+
entryUrl: plugin.entryUrl,
|
|
15967
|
+
capabilities: plugin.capabilities,
|
|
15968
|
+
name: plugin.name,
|
|
15969
|
+
granted: plugin.capabilities ?? [],
|
|
15970
|
+
version: plugin.version,
|
|
15971
|
+
level: asked,
|
|
15972
|
+
provided: provided.has(plugin.id) || undefined,
|
|
15973
|
+
});
|
|
15820
15974
|
}
|
|
15821
|
-
return
|
|
15975
|
+
return [...composed, ...fromCatalog];
|
|
15822
15976
|
}
|
|
15823
|
-
|
|
15824
|
-
|
|
15825
|
-
|
|
15826
|
-
|
|
15827
|
-
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
|
|
15834
|
-
|
|
15835
|
-
const value = control['value'];
|
|
15836
|
-
if (kind === 'toggle' && typeof value === 'boolean') {
|
|
15837
|
-
return { kind, value };
|
|
15838
|
-
}
|
|
15839
|
-
if (kind === 'text' && typeof value === 'string') {
|
|
15840
|
-
return buildTextControl(value, control);
|
|
15841
|
-
}
|
|
15842
|
-
if (kind === 'select' && typeof value === 'string') {
|
|
15843
|
-
return buildSelectControl(pluginId, value, control);
|
|
15844
|
-
}
|
|
15845
|
-
if (kind === 'slider' && typeof value === 'number') {
|
|
15846
|
-
return buildSliderControl(value, control);
|
|
15847
|
-
}
|
|
15848
|
-
throw new Error(`Sandbox plugin "${pluginId}": a settings control must be toggle/text/select/slider with a matching default 'value'.`);
|
|
15849
|
-
}
|
|
15850
|
-
function sanitizeRow(pluginId, raw) {
|
|
15851
|
-
const row = (raw ?? {});
|
|
15852
|
-
if (typeof row['id'] !== 'string' || row['id'].length === 0) {
|
|
15853
|
-
throw new Error(`Sandbox plugin "${pluginId}": every settings row needs a non-empty 'id'.`);
|
|
15854
|
-
}
|
|
15855
|
-
if (typeof row['label'] !== 'string' || row['label'].length === 0) {
|
|
15856
|
-
throw new Error(`Sandbox plugin "${pluginId}": every settings row needs a non-empty 'label'.`);
|
|
15857
|
-
}
|
|
15858
|
-
return {
|
|
15859
|
-
id: row['id'],
|
|
15860
|
-
label: row['label'],
|
|
15861
|
-
description: typeof row['description'] === 'string' ? row['description'] : undefined,
|
|
15862
|
-
control: sanitizeControl(pluginId, row['control']),
|
|
15863
|
-
};
|
|
15864
|
-
}
|
|
15865
|
-
function sanitizeRpcSettingsSection(pluginId, section) {
|
|
15866
|
-
const raw = (section ?? {});
|
|
15867
|
-
if (typeof raw['id'] !== 'string' || raw['id'].length === 0) {
|
|
15868
|
-
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires a non-empty 'id'.`);
|
|
15869
|
-
}
|
|
15870
|
-
if (typeof raw['title'] !== 'string' || raw['title'].length === 0) {
|
|
15871
|
-
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires a non-empty 'title'.`);
|
|
15872
|
-
}
|
|
15873
|
-
if (!Array.isArray(raw['rows']) || raw['rows'].length === 0) {
|
|
15874
|
-
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires at least one row.`);
|
|
15875
|
-
}
|
|
15876
|
-
return {
|
|
15877
|
-
id: raw['id'],
|
|
15878
|
-
title: raw['title'],
|
|
15879
|
-
order: typeof raw['order'] === 'number' ? raw['order'] : undefined,
|
|
15880
|
-
rows: raw['rows'].map((row) => sanitizeRow(pluginId, row)),
|
|
15881
|
-
};
|
|
15882
|
-
}
|
|
15883
|
-
function defaultsOf(wire) {
|
|
15884
|
-
const defaults = {};
|
|
15885
|
-
for (const row of wire.rows) {
|
|
15886
|
-
defaults[row.id] = row.control.value;
|
|
15887
|
-
}
|
|
15888
|
-
return defaults;
|
|
15889
|
-
}
|
|
15890
|
-
function typedOverlay(defaults, raw) {
|
|
15891
|
-
if (!raw) {
|
|
15892
|
-
return defaults;
|
|
15893
|
-
}
|
|
15894
|
-
try {
|
|
15895
|
-
const parsed = JSON.parse(raw);
|
|
15896
|
-
if (typeof parsed !== 'object' || parsed === null) {
|
|
15897
|
-
return defaults;
|
|
15898
|
-
}
|
|
15899
|
-
const merged = { ...defaults };
|
|
15900
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
15901
|
-
if (key in defaults && typeof value === typeof defaults[key]) {
|
|
15902
|
-
merged[key] = value;
|
|
15903
|
-
}
|
|
15904
|
-
}
|
|
15905
|
-
return merged;
|
|
15906
|
-
}
|
|
15907
|
-
catch {
|
|
15908
|
-
return defaults;
|
|
15909
|
-
}
|
|
15910
|
-
}
|
|
15911
|
-
function buildFrameSection(deps) {
|
|
15912
|
-
const { pluginId, wire, group, store, sync, notify } = deps;
|
|
15913
|
-
const key = `lw.plugin-settings:${pluginId}:${wire.id}`;
|
|
15914
|
-
const defaults = defaultsOf(wire);
|
|
15915
|
-
const values = signal(typedOverlay(defaults, store.peek?.(key)), /* @ts-ignore */
|
|
15916
|
-
...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
|
|
15917
|
-
const applyStored = (raw) => {
|
|
15918
|
-
values.set(typedOverlay(defaults, raw));
|
|
15919
|
-
notify(wire.id, values());
|
|
15920
|
-
};
|
|
15921
|
-
if (store.peek) {
|
|
15922
|
-
notify(wire.id, values());
|
|
15923
|
-
}
|
|
15924
|
-
else {
|
|
15925
|
-
hydrateAsync(store, key, applyStored);
|
|
15926
|
-
}
|
|
15927
|
-
const disposeSync = sync.register('settings', key, applyStored);
|
|
15928
|
-
const set = (rowId, value) => {
|
|
15929
|
-
values.update((current) => ({ ...current, [rowId]: value }));
|
|
15930
|
-
void store.set(key, JSON.stringify(values()));
|
|
15931
|
-
notify(wire.id, values());
|
|
15932
|
-
};
|
|
15933
|
-
const section = {
|
|
15934
|
-
id: `${pluginId}.${wire.id}`,
|
|
15935
|
-
title: wire.title,
|
|
15936
|
-
group,
|
|
15937
|
-
order: wire.order,
|
|
15938
|
-
rows: wire.rows.map((row) => ({
|
|
15939
|
-
id: `${pluginId}.${wire.id}.${row.id}`,
|
|
15940
|
-
label: row.label,
|
|
15941
|
-
description: row.description,
|
|
15942
|
-
control: hostControl(row, values, set),
|
|
15943
|
-
})),
|
|
15944
|
-
};
|
|
15945
|
-
return { section, disposeSync };
|
|
15946
|
-
}
|
|
15947
|
-
function hostControl(row, values, set) {
|
|
15948
|
-
const control = row.control;
|
|
15949
|
-
switch (control.kind) {
|
|
15950
|
-
case 'toggle':
|
|
15951
|
-
return {
|
|
15952
|
-
kind: 'toggle',
|
|
15953
|
-
value: () => values()[row.id] === true,
|
|
15954
|
-
set: (value) => set(row.id, value),
|
|
15955
|
-
};
|
|
15956
|
-
case 'text':
|
|
15957
|
-
return {
|
|
15958
|
-
kind: 'text',
|
|
15959
|
-
inputType: control.inputType,
|
|
15960
|
-
placeholder: control.placeholder,
|
|
15961
|
-
value: () => String(values()[row.id] ?? ''),
|
|
15962
|
-
set: (value) => set(row.id, value),
|
|
15963
|
-
};
|
|
15964
|
-
case 'select':
|
|
15965
|
-
return {
|
|
15966
|
-
kind: 'select',
|
|
15967
|
-
options: control.options,
|
|
15968
|
-
value: () => String(values()[row.id] ?? control.value),
|
|
15969
|
-
set: (value) => set(row.id, value),
|
|
15970
|
-
};
|
|
15971
|
-
case 'slider':
|
|
15972
|
-
return {
|
|
15973
|
-
kind: 'slider',
|
|
15974
|
-
min: control.min,
|
|
15975
|
-
max: control.max,
|
|
15976
|
-
step: control.step,
|
|
15977
|
-
value: () => Number(values()[row.id] ?? control.value),
|
|
15978
|
-
set: (value) => set(row.id, value),
|
|
15979
|
-
};
|
|
15980
|
-
}
|
|
15977
|
+
|
|
15978
|
+
const UNCARRIABLE_ARGUMENTS = {
|
|
15979
|
+
outcome: 'refused',
|
|
15980
|
+
reason: 'invalid-arguments',
|
|
15981
|
+
message: 'Arguments must be an object of single values or lists of them; anything else cannot cross the ' +
|
|
15982
|
+
'sandbox boundary as the value it was.',
|
|
15983
|
+
};
|
|
15984
|
+
function invokeRpcCommand(ctx, id, args) {
|
|
15985
|
+
const carried = args === undefined ? undefined : asCommandArguments(args);
|
|
15986
|
+
return carried === null
|
|
15987
|
+
? Promise.resolve(UNCARRIABLE_ARGUMENTS)
|
|
15988
|
+
: ctx.invokeCommand(String(id), carried);
|
|
15981
15989
|
}
|
|
15982
15990
|
|
|
15983
15991
|
const MAX_RPC_AREA_DEPTH = 8;
|
|
15984
15992
|
function sanitizeRpcSurface(pluginId, surface, permitted) {
|
|
15985
15993
|
const raw = (surface ?? {});
|
|
15994
|
+
const { id, title } = rpcSurfaceIdentity(pluginId, raw);
|
|
15995
|
+
const container = sanitizeRpcContainer(raw['container']);
|
|
15996
|
+
const iframe = container === undefined
|
|
15997
|
+
? rpcIframeUrl(pluginId, raw['iframe'], permitted)
|
|
15998
|
+
: undefined;
|
|
15999
|
+
const routable = sanitizeRpcRoutable(raw['routable']);
|
|
16000
|
+
const docks = sanitizeRpcDocks(raw['docks']);
|
|
16001
|
+
assertRpcSurfaceAddress(pluginId, container, routable, docks);
|
|
16002
|
+
const shared = {
|
|
16003
|
+
id,
|
|
16004
|
+
title,
|
|
16005
|
+
icon: typeof raw['icon'] === 'string' ? raw['icon'] : undefined,
|
|
16006
|
+
order: typeof raw['order'] === 'number' ? raw['order'] : undefined,
|
|
16007
|
+
instanceable: raw['instanceable'] === true ? true : undefined,
|
|
16008
|
+
retain: raw['retain'] === 'always' || raw['retain'] === 'never'
|
|
16009
|
+
? raw['retain']
|
|
16010
|
+
: undefined,
|
|
16011
|
+
saveOn: raw['saveOn'] === 'hide' ? 'hide' : undefined,
|
|
16012
|
+
closable: raw['closable'] === false ? false : undefined,
|
|
16013
|
+
padded: raw['padded'] === false ? false : undefined,
|
|
16014
|
+
routable,
|
|
16015
|
+
docks,
|
|
16016
|
+
};
|
|
16017
|
+
return container === undefined
|
|
16018
|
+
? { ...shared, iframe: iframe }
|
|
16019
|
+
: { ...shared, container };
|
|
16020
|
+
}
|
|
16021
|
+
function rpcSurfaceIdentity(pluginId, raw) {
|
|
15986
16022
|
if (typeof raw['id'] !== 'string' || raw['id'].length === 0) {
|
|
15987
16023
|
throw new Error(`Sandbox plugin "${pluginId}": registerSurface requires a non-empty 'id'.`);
|
|
15988
16024
|
}
|
|
@@ -15997,20 +16033,20 @@ function sanitizeRpcSurface(pluginId, surface, permitted) {
|
|
|
15997
16033
|
throw new Error(`Sandbox plugin "${pluginId}": 'access' does not cross the RPC boundary — ` +
|
|
15998
16034
|
`a sandboxed surface gates itself from the pushed session state.`);
|
|
15999
16035
|
}
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
}
|
|
16006
|
-
const origin = surfaceOrigin(iframe);
|
|
16007
|
-
if (origin === null || !permittedOrigins(permitted).has(origin)) {
|
|
16008
|
-
throw new Error(`Sandbox plugin "${pluginId}": the iframe surface must be served from an origin this ` +
|
|
16009
|
-
`distribution permitted for it, got "${iframe}".`);
|
|
16010
|
-
}
|
|
16036
|
+
return { id: raw['id'], title: raw['title'] };
|
|
16037
|
+
}
|
|
16038
|
+
function rpcIframeUrl(pluginId, value, permitted) {
|
|
16039
|
+
if (typeof value !== 'string') {
|
|
16040
|
+
throw new TypeError(`Sandbox plugin "${pluginId}": registerSurface needs an { iframe } URL or a { container } spec.`);
|
|
16011
16041
|
}
|
|
16012
|
-
const
|
|
16013
|
-
|
|
16042
|
+
const origin = surfaceOrigin(value);
|
|
16043
|
+
if (origin === null || !permittedOrigins(permitted).has(origin)) {
|
|
16044
|
+
throw new Error(`Sandbox plugin "${pluginId}": the iframe surface must be served from an origin this ` +
|
|
16045
|
+
`distribution permitted for it, got "${value}".`);
|
|
16046
|
+
}
|
|
16047
|
+
return value;
|
|
16048
|
+
}
|
|
16049
|
+
function assertRpcSurfaceAddress(pluginId, container, routable, docks) {
|
|
16014
16050
|
if (routable === undefined && docks === undefined) {
|
|
16015
16051
|
throw new Error(`Sandbox plugin "${pluginId}": registerSurface needs 'routable.path' (a URL-addressed surface) ` +
|
|
16016
16052
|
`or 'docks' (a surface hosted at a dock).`);
|
|
@@ -16019,24 +16055,6 @@ function sanitizeRpcSurface(pluginId, surface, permitted) {
|
|
|
16019
16055
|
throw new Error(`Sandbox plugin "${pluginId}": a container surface must be routable — a container tab holds ` +
|
|
16020
16056
|
`its own ':id'.`);
|
|
16021
16057
|
}
|
|
16022
|
-
const shared = {
|
|
16023
|
-
id: raw['id'],
|
|
16024
|
-
title: raw['title'],
|
|
16025
|
-
icon: typeof raw['icon'] === 'string' ? raw['icon'] : undefined,
|
|
16026
|
-
order: typeof raw['order'] === 'number' ? raw['order'] : undefined,
|
|
16027
|
-
instanceable: raw['instanceable'] === true ? true : undefined,
|
|
16028
|
-
retain: raw['retain'] === 'always' || raw['retain'] === 'never'
|
|
16029
|
-
? raw['retain']
|
|
16030
|
-
: undefined,
|
|
16031
|
-
saveOn: raw['saveOn'] === 'hide' ? 'hide' : undefined,
|
|
16032
|
-
closable: raw['closable'] === false ? false : undefined,
|
|
16033
|
-
padded: raw['padded'] === false ? false : undefined,
|
|
16034
|
-
routable,
|
|
16035
|
-
docks,
|
|
16036
|
-
};
|
|
16037
|
-
return container !== undefined
|
|
16038
|
-
? { ...shared, container }
|
|
16039
|
-
: { ...shared, iframe: iframe };
|
|
16040
16058
|
}
|
|
16041
16059
|
function sanitizeRpcRoutable(value) {
|
|
16042
16060
|
if (typeof value !== 'object' || value === null) {
|
|
@@ -16089,7 +16107,7 @@ function sanitizeRpcArea(value, depth) {
|
|
|
16089
16107
|
const raw = value;
|
|
16090
16108
|
const size = typeof raw['size'] === 'number' ? { size: raw['size'] } : {};
|
|
16091
16109
|
if (Array.isArray(raw['tabs'])) {
|
|
16092
|
-
return { ...size, tabs: raw['tabs'].flatMap(sanitizeRpcContainerTab) };
|
|
16110
|
+
return { ...size, tabs: raw['tabs'].flatMap((value) => sanitizeRpcContainerTab(value)) };
|
|
16093
16111
|
}
|
|
16094
16112
|
for (const kind of ['rows', 'columns']) {
|
|
16095
16113
|
const declared = raw[kind];
|
|
@@ -16116,8 +16134,8 @@ function sanitizeRpcContainerTab(value) {
|
|
|
16116
16134
|
return [
|
|
16117
16135
|
{
|
|
16118
16136
|
surface: raw['surface'],
|
|
16119
|
-
...(raw['closable'] === false
|
|
16120
|
-
...(raw['active'] === true
|
|
16137
|
+
...(raw['closable'] === false && { closable: false }),
|
|
16138
|
+
...(raw['active'] === true && { active: true }),
|
|
16121
16139
|
},
|
|
16122
16140
|
];
|
|
16123
16141
|
}
|
|
@@ -16174,11 +16192,14 @@ function sanitizeRpcToastInput(input) {
|
|
|
16174
16192
|
id: typeof raw['id'] === 'string' ? raw['id'] : undefined,
|
|
16175
16193
|
};
|
|
16176
16194
|
}
|
|
16195
|
+
const NOTIFICATION_KINDS = new Set([
|
|
16196
|
+
'info',
|
|
16197
|
+
'success',
|
|
16198
|
+
'warning',
|
|
16199
|
+
'error',
|
|
16200
|
+
]);
|
|
16177
16201
|
function isNotificationKind(value) {
|
|
16178
|
-
return (value
|
|
16179
|
-
value === 'success' ||
|
|
16180
|
-
value === 'warning' ||
|
|
16181
|
-
value === 'error');
|
|
16202
|
+
return NOTIFICATION_KINDS.has(value);
|
|
16182
16203
|
}
|
|
16183
16204
|
function sanitizeRpcMenuItem(item) {
|
|
16184
16205
|
const raw = (item ?? {});
|
|
@@ -16211,29 +16232,267 @@ function sanitizeMenuContext(value) {
|
|
|
16211
16232
|
return clean;
|
|
16212
16233
|
}
|
|
16213
16234
|
|
|
16214
|
-
const
|
|
16215
|
-
|
|
16216
|
-
|
|
16217
|
-
|
|
16218
|
-
|
|
16219
|
-
|
|
16220
|
-
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16235
|
+
const INPUT_TYPES = ['text', 'date', 'email', 'number', 'password'];
|
|
16236
|
+
function sanitizeOptions(raw) {
|
|
16237
|
+
if (!Array.isArray(raw)) {
|
|
16238
|
+
return [];
|
|
16239
|
+
}
|
|
16240
|
+
return raw
|
|
16241
|
+
.filter((option) => typeof option === 'object' &&
|
|
16242
|
+
option !== null &&
|
|
16243
|
+
typeof option['value'] === 'string' &&
|
|
16244
|
+
typeof option['label'] === 'string')
|
|
16245
|
+
.map((option) => ({ value: option.value, label: option.label }));
|
|
16225
16246
|
}
|
|
16226
|
-
|
|
16227
|
-
|
|
16228
|
-
const FRAME_PLUGIN = new InjectionToken('FRAME_PLUGIN');
|
|
16229
|
-
function levelOf(plugin) {
|
|
16230
|
-
return plugin.level ?? DEFAULT_ISOLATION_LEVEL;
|
|
16247
|
+
function optionalString(value) {
|
|
16248
|
+
return typeof value === 'string' ? value : undefined;
|
|
16231
16249
|
}
|
|
16232
|
-
function
|
|
16233
|
-
|
|
16234
|
-
const granted = [...(plugin.granted ?? [])].sort().join(',');
|
|
16235
|
-
return `${plugin.entryUrl}|${caps}|${granted}|${plugin.version ?? ''}|${levelOf(plugin)}`;
|
|
16250
|
+
function optionalNumber(value) {
|
|
16251
|
+
return typeof value === 'number' ? value : undefined;
|
|
16236
16252
|
}
|
|
16253
|
+
function buildTextControl(value, control) {
|
|
16254
|
+
return {
|
|
16255
|
+
kind: 'text',
|
|
16256
|
+
value,
|
|
16257
|
+
inputType: INPUT_TYPES.find((type) => type === control['inputType']),
|
|
16258
|
+
placeholder: optionalString(control['placeholder']),
|
|
16259
|
+
};
|
|
16260
|
+
}
|
|
16261
|
+
function buildSelectControl(pluginId, value, control) {
|
|
16262
|
+
const options = sanitizeOptions(control['options']);
|
|
16263
|
+
if (options.length === 0) {
|
|
16264
|
+
throw new Error(`Sandbox plugin "${pluginId}": a select control needs at least one { value, label } option.`);
|
|
16265
|
+
}
|
|
16266
|
+
return { kind: 'select', value, options };
|
|
16267
|
+
}
|
|
16268
|
+
function buildSliderControl(value, control) {
|
|
16269
|
+
return {
|
|
16270
|
+
kind: 'slider',
|
|
16271
|
+
value,
|
|
16272
|
+
min: optionalNumber(control['min']),
|
|
16273
|
+
max: optionalNumber(control['max']),
|
|
16274
|
+
step: optionalNumber(control['step']),
|
|
16275
|
+
};
|
|
16276
|
+
}
|
|
16277
|
+
function sanitizeControl(pluginId, raw) {
|
|
16278
|
+
const control = (raw ?? {});
|
|
16279
|
+
const kind = control['kind'];
|
|
16280
|
+
const value = control['value'];
|
|
16281
|
+
if (kind === 'toggle' && typeof value === 'boolean') {
|
|
16282
|
+
return { kind, value };
|
|
16283
|
+
}
|
|
16284
|
+
if (kind === 'text' && typeof value === 'string') {
|
|
16285
|
+
return buildTextControl(value, control);
|
|
16286
|
+
}
|
|
16287
|
+
if (kind === 'select' && typeof value === 'string') {
|
|
16288
|
+
return buildSelectControl(pluginId, value, control);
|
|
16289
|
+
}
|
|
16290
|
+
if (kind === 'slider' && typeof value === 'number') {
|
|
16291
|
+
return buildSliderControl(value, control);
|
|
16292
|
+
}
|
|
16293
|
+
throw new Error(`Sandbox plugin "${pluginId}": a settings control must be toggle/text/select/slider with a matching default 'value'.`);
|
|
16294
|
+
}
|
|
16295
|
+
function sanitizeRow(pluginId, raw) {
|
|
16296
|
+
const row = (raw ?? {});
|
|
16297
|
+
if (typeof row['id'] !== 'string' || row['id'].length === 0) {
|
|
16298
|
+
throw new Error(`Sandbox plugin "${pluginId}": every settings row needs a non-empty 'id'.`);
|
|
16299
|
+
}
|
|
16300
|
+
if (typeof row['label'] !== 'string' || row['label'].length === 0) {
|
|
16301
|
+
throw new Error(`Sandbox plugin "${pluginId}": every settings row needs a non-empty 'label'.`);
|
|
16302
|
+
}
|
|
16303
|
+
return {
|
|
16304
|
+
id: row['id'],
|
|
16305
|
+
label: row['label'],
|
|
16306
|
+
description: typeof row['description'] === 'string' ? row['description'] : undefined,
|
|
16307
|
+
control: sanitizeControl(pluginId, row['control']),
|
|
16308
|
+
};
|
|
16309
|
+
}
|
|
16310
|
+
function sanitizeRpcSettingsSection(pluginId, section) {
|
|
16311
|
+
const raw = (section ?? {});
|
|
16312
|
+
if (typeof raw['id'] !== 'string' || raw['id'].length === 0) {
|
|
16313
|
+
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires a non-empty 'id'.`);
|
|
16314
|
+
}
|
|
16315
|
+
if (typeof raw['title'] !== 'string' || raw['title'].length === 0) {
|
|
16316
|
+
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires a non-empty 'title'.`);
|
|
16317
|
+
}
|
|
16318
|
+
if (!Array.isArray(raw['rows']) || raw['rows'].length === 0) {
|
|
16319
|
+
throw new Error(`Sandbox plugin "${pluginId}": registerSettingsSection requires at least one row.`);
|
|
16320
|
+
}
|
|
16321
|
+
return {
|
|
16322
|
+
id: raw['id'],
|
|
16323
|
+
title: raw['title'],
|
|
16324
|
+
order: typeof raw['order'] === 'number' ? raw['order'] : undefined,
|
|
16325
|
+
rows: raw['rows'].map((row) => sanitizeRow(pluginId, row)),
|
|
16326
|
+
};
|
|
16327
|
+
}
|
|
16328
|
+
function defaultsOf(wire) {
|
|
16329
|
+
const defaults = {};
|
|
16330
|
+
for (const row of wire.rows) {
|
|
16331
|
+
defaults[row.id] = row.control.value;
|
|
16332
|
+
}
|
|
16333
|
+
return defaults;
|
|
16334
|
+
}
|
|
16335
|
+
function typedOverlay(defaults, raw) {
|
|
16336
|
+
if (!raw) {
|
|
16337
|
+
return defaults;
|
|
16338
|
+
}
|
|
16339
|
+
try {
|
|
16340
|
+
const parsed = JSON.parse(raw);
|
|
16341
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
16342
|
+
return defaults;
|
|
16343
|
+
}
|
|
16344
|
+
const merged = { ...defaults };
|
|
16345
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
16346
|
+
if (Object.hasOwn(defaults, key) && typeof value === typeof defaults[key]) {
|
|
16347
|
+
merged[key] = value;
|
|
16348
|
+
}
|
|
16349
|
+
}
|
|
16350
|
+
return merged;
|
|
16351
|
+
}
|
|
16352
|
+
catch {
|
|
16353
|
+
return defaults;
|
|
16354
|
+
}
|
|
16355
|
+
}
|
|
16356
|
+
function buildFrameSection(deps) {
|
|
16357
|
+
const { pluginId, wire, group, store, sync, notify } = deps;
|
|
16358
|
+
const key = `lw.plugin-settings:${pluginId}:${wire.id}`;
|
|
16359
|
+
const defaults = defaultsOf(wire);
|
|
16360
|
+
const values = signal(typedOverlay(defaults, store.peek?.(key)), /* @ts-ignore */
|
|
16361
|
+
...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
|
|
16362
|
+
const applyStored = (raw) => {
|
|
16363
|
+
values.set(typedOverlay(defaults, raw));
|
|
16364
|
+
notify(wire.id, values());
|
|
16365
|
+
};
|
|
16366
|
+
if (store.peek) {
|
|
16367
|
+
notify(wire.id, values());
|
|
16368
|
+
}
|
|
16369
|
+
else {
|
|
16370
|
+
hydrateAsync(store, key, applyStored);
|
|
16371
|
+
}
|
|
16372
|
+
const disposeSync = sync.register('settings', key, applyStored);
|
|
16373
|
+
const set = (rowId, value) => {
|
|
16374
|
+
values.update((current) => ({ ...current, [rowId]: value }));
|
|
16375
|
+
void store.set(key, JSON.stringify(values()));
|
|
16376
|
+
notify(wire.id, values());
|
|
16377
|
+
};
|
|
16378
|
+
const section = {
|
|
16379
|
+
id: `${pluginId}.${wire.id}`,
|
|
16380
|
+
title: wire.title,
|
|
16381
|
+
group,
|
|
16382
|
+
order: wire.order,
|
|
16383
|
+
rows: wire.rows.map((row) => ({
|
|
16384
|
+
id: `${pluginId}.${wire.id}.${row.id}`,
|
|
16385
|
+
label: row.label,
|
|
16386
|
+
description: row.description,
|
|
16387
|
+
control: hostControl(row, values, set),
|
|
16388
|
+
})),
|
|
16389
|
+
};
|
|
16390
|
+
return { section, disposeSync };
|
|
16391
|
+
}
|
|
16392
|
+
function hostControl(row, values, set) {
|
|
16393
|
+
const control = row.control;
|
|
16394
|
+
switch (control.kind) {
|
|
16395
|
+
case 'toggle': {
|
|
16396
|
+
return {
|
|
16397
|
+
kind: 'toggle',
|
|
16398
|
+
value: () => values()[row.id] === true,
|
|
16399
|
+
set: (value) => set(row.id, value),
|
|
16400
|
+
};
|
|
16401
|
+
}
|
|
16402
|
+
case 'text': {
|
|
16403
|
+
return {
|
|
16404
|
+
kind: 'text',
|
|
16405
|
+
inputType: control.inputType,
|
|
16406
|
+
placeholder: control.placeholder,
|
|
16407
|
+
value: () => String(values()[row.id] ?? ''),
|
|
16408
|
+
set: (value) => set(row.id, value),
|
|
16409
|
+
};
|
|
16410
|
+
}
|
|
16411
|
+
case 'select': {
|
|
16412
|
+
return {
|
|
16413
|
+
kind: 'select',
|
|
16414
|
+
options: control.options,
|
|
16415
|
+
value: () => String(values()[row.id] ?? control.value),
|
|
16416
|
+
set: (value) => set(row.id, value),
|
|
16417
|
+
};
|
|
16418
|
+
}
|
|
16419
|
+
case 'slider': {
|
|
16420
|
+
return {
|
|
16421
|
+
kind: 'slider',
|
|
16422
|
+
min: control.min,
|
|
16423
|
+
max: control.max,
|
|
16424
|
+
step: control.step,
|
|
16425
|
+
value: () => Number(values()[row.id] ?? control.value),
|
|
16426
|
+
set: (value) => set(row.id, value),
|
|
16427
|
+
};
|
|
16428
|
+
}
|
|
16429
|
+
}
|
|
16430
|
+
}
|
|
16431
|
+
|
|
16432
|
+
function frameRpcMethods(deps) {
|
|
16433
|
+
const { pluginId, ctx, origins, watched } = deps;
|
|
16434
|
+
return reportingRefusals({
|
|
16435
|
+
registerSurface: (surface) => {
|
|
16436
|
+
ctx.registerSurface(sanitizeRpcSurface(pluginId, surface, origins));
|
|
16437
|
+
},
|
|
16438
|
+
registerMenuItem: (item) => {
|
|
16439
|
+
ctx.registerMenuItem(sanitizeRpcMenuItem(item));
|
|
16440
|
+
},
|
|
16441
|
+
registerSettingsSection: (section) => {
|
|
16442
|
+
const built = buildFrameSection({
|
|
16443
|
+
pluginId,
|
|
16444
|
+
wire: sanitizeRpcSettingsSection(pluginId, section),
|
|
16445
|
+
group: deps.install.isInstalled(pluginId)
|
|
16446
|
+
? 'settings.group.community'
|
|
16447
|
+
: 'settings.group.plugins',
|
|
16448
|
+
store: deps.store,
|
|
16449
|
+
sync: deps.sync,
|
|
16450
|
+
notify: (sectionId, values) => deps.notify((remote) => remote.settingsChanged(sectionId, values)),
|
|
16451
|
+
});
|
|
16452
|
+
deps.syncCleanups.push(built.disposeSync);
|
|
16453
|
+
ctx.registerSettingsSection(built.section);
|
|
16454
|
+
},
|
|
16455
|
+
navigateContent: (path) => ctx.navigateContent(path),
|
|
16456
|
+
openContentTab: (input) => {
|
|
16457
|
+
const sanitized = sanitizeRpcTabInput(input);
|
|
16458
|
+
ctx.openContentTab({
|
|
16459
|
+
...sanitized,
|
|
16460
|
+
onClose: () => deps.notify((remote) => remote.contentTabClosed(sanitized.path)),
|
|
16461
|
+
});
|
|
16462
|
+
},
|
|
16463
|
+
keepContentTab: (path) => ctx.keepContentTab(path),
|
|
16464
|
+
pinContentTab: (path) => ctx.pinContentTab(path),
|
|
16465
|
+
unpinContentTab: (path) => ctx.unpinContentTab(path),
|
|
16466
|
+
closeContentTab: (path) => ctx.closeContentTab(path),
|
|
16467
|
+
revealSurface: (id) => ctx.revealSurface(id),
|
|
16468
|
+
invokeCommand: (id, args) => invokeRpcCommand(ctx, id, args),
|
|
16469
|
+
invocableCommands: () => ctx.invocableCommands(),
|
|
16470
|
+
toast: (input) => ctx.ui.toast(sanitizeRpcToastInput(input)),
|
|
16471
|
+
stateWatch: (key) => deps.watchState(key),
|
|
16472
|
+
stateSet: (key, value) => watched.get(key)?.handle.set(value),
|
|
16473
|
+
stateClear: (key) => watched.get(key)?.handle.clear(),
|
|
16474
|
+
stateUnwatch: (key) => {
|
|
16475
|
+
watched.get(key)?.stop();
|
|
16476
|
+
watched.delete(key);
|
|
16477
|
+
},
|
|
16478
|
+
}, deps.reportRefusal);
|
|
16479
|
+
}
|
|
16480
|
+
function reportingRefusals(methods, report) {
|
|
16481
|
+
const reported = Object.entries(methods).map(([name, method]) => [
|
|
16482
|
+
name,
|
|
16483
|
+
(...args) => {
|
|
16484
|
+
try {
|
|
16485
|
+
return method(...args);
|
|
16486
|
+
}
|
|
16487
|
+
catch (error) {
|
|
16488
|
+
report(error);
|
|
16489
|
+
throw error;
|
|
16490
|
+
}
|
|
16491
|
+
},
|
|
16492
|
+
]);
|
|
16493
|
+
return Object.fromEntries(reported);
|
|
16494
|
+
}
|
|
16495
|
+
|
|
16237
16496
|
/**
|
|
16238
16497
|
* Second {@link PluginRuntime} implementation:
|
|
16239
16498
|
* runs each plugin in an isolated `<iframe sandbox="allow-scripts">` and hands it `ctx` over **Penpal**
|
|
@@ -16291,9 +16550,13 @@ class FramePluginRuntime {
|
|
|
16291
16550
|
this.instances.delete(id);
|
|
16292
16551
|
instance.connection.destroy();
|
|
16293
16552
|
instance.frame.remove();
|
|
16294
|
-
|
|
16553
|
+
for (const entry of instance.watched.values()) {
|
|
16554
|
+
entry.stop();
|
|
16555
|
+
}
|
|
16295
16556
|
instance.ctx.disposeAll();
|
|
16296
|
-
instance.syncCleanups
|
|
16557
|
+
for (const cleanup of instance.syncCleanups) {
|
|
16558
|
+
cleanup();
|
|
16559
|
+
}
|
|
16297
16560
|
this.grants.unregister(id);
|
|
16298
16561
|
this.isolation.unregister(id);
|
|
16299
16562
|
}
|
|
@@ -16305,7 +16568,7 @@ class FramePluginRuntime {
|
|
|
16305
16568
|
}
|
|
16306
16569
|
}
|
|
16307
16570
|
reconcile(disabled, installed, deployed) {
|
|
16308
|
-
const runnable = this.
|
|
16571
|
+
const runnable = runnablePlugins(this.plugins, installed, deployed, this.catalogCap);
|
|
16309
16572
|
for (const plugin of runnable) {
|
|
16310
16573
|
this.enablement.register(plugin.id, plugin.name ?? plugin.id);
|
|
16311
16574
|
const enabled = plugin.provided === true || !disabled.has(plugin.id);
|
|
@@ -16323,34 +16586,6 @@ class FramePluginRuntime {
|
|
|
16323
16586
|
}
|
|
16324
16587
|
this.dropUninstalled(runnable);
|
|
16325
16588
|
}
|
|
16326
|
-
runnablePlugins(installed, deployed) {
|
|
16327
|
-
const claimed = new Set(this.plugins.map((plugin) => plugin.id));
|
|
16328
|
-
const provided = new Set(deployed.map((plugin) => plugin.id));
|
|
16329
|
-
const fromCatalog = [];
|
|
16330
|
-
for (const plugin of [...deployed, ...installed]) {
|
|
16331
|
-
if (claimed.has(plugin.id)) {
|
|
16332
|
-
continue;
|
|
16333
|
-
}
|
|
16334
|
-
const asked = plugin.level ?? DEFAULT_ISOLATION_LEVEL;
|
|
16335
|
-
if (exceedsLevel(asked, this.catalogCap)) {
|
|
16336
|
-
console.error(`Plugin "${plugin.id}" asks to run ${asked}, which this catalog may not confer ` +
|
|
16337
|
-
`(its cap is ${this.catalogCap}). It is not started.`);
|
|
16338
|
-
continue;
|
|
16339
|
-
}
|
|
16340
|
-
claimed.add(plugin.id);
|
|
16341
|
-
fromCatalog.push({
|
|
16342
|
-
id: plugin.id,
|
|
16343
|
-
entryUrl: plugin.entryUrl,
|
|
16344
|
-
capabilities: plugin.capabilities,
|
|
16345
|
-
name: plugin.name,
|
|
16346
|
-
granted: plugin.capabilities ?? [],
|
|
16347
|
-
version: plugin.version,
|
|
16348
|
-
level: asked,
|
|
16349
|
-
provided: provided.has(plugin.id) || undefined,
|
|
16350
|
-
});
|
|
16351
|
-
}
|
|
16352
|
-
return [...this.plugins, ...fromCatalog];
|
|
16353
|
-
}
|
|
16354
16589
|
dropUninstalled(runnable) {
|
|
16355
16590
|
const known = new Set(runnable.map((plugin) => plugin.id));
|
|
16356
16591
|
for (const id of this.instances.keys()) {
|
|
@@ -16374,7 +16609,19 @@ class FramePluginRuntime {
|
|
|
16374
16609
|
const watched = new Map();
|
|
16375
16610
|
const connection = connect({
|
|
16376
16611
|
messenger,
|
|
16377
|
-
methods:
|
|
16612
|
+
methods: frameRpcMethods({
|
|
16613
|
+
pluginId: plugin.id,
|
|
16614
|
+
ctx,
|
|
16615
|
+
origins: plugin.origins,
|
|
16616
|
+
install: this.install,
|
|
16617
|
+
store: this.store,
|
|
16618
|
+
sync: this.sync,
|
|
16619
|
+
syncCleanups,
|
|
16620
|
+
watched,
|
|
16621
|
+
watchState: (key) => this.watchState(plugin.id, ctx, watched, key),
|
|
16622
|
+
notify: (send) => this.notify(plugin.id, send),
|
|
16623
|
+
reportRefusal: (error) => this.refusals.report(error),
|
|
16624
|
+
}),
|
|
16378
16625
|
});
|
|
16379
16626
|
this.instances.set(plugin.id, {
|
|
16380
16627
|
ctx,
|
|
@@ -16385,10 +16632,11 @@ class FramePluginRuntime {
|
|
|
16385
16632
|
watched,
|
|
16386
16633
|
});
|
|
16387
16634
|
connection.promise.catch((error) => {
|
|
16388
|
-
if (this.instances.has(plugin.id)) {
|
|
16389
|
-
|
|
16390
|
-
this.deactivate(plugin.id);
|
|
16635
|
+
if (!this.instances.has(plugin.id)) {
|
|
16636
|
+
return;
|
|
16391
16637
|
}
|
|
16638
|
+
console.error(`Sandbox plugin "${plugin.id}" failed to connect`, error);
|
|
16639
|
+
this.deactivate(plugin.id);
|
|
16392
16640
|
});
|
|
16393
16641
|
}
|
|
16394
16642
|
createFrame(entryUrl, level) {
|
|
@@ -16399,71 +16647,9 @@ class FramePluginRuntime {
|
|
|
16399
16647
|
frame.setAttribute('aria-hidden', 'true');
|
|
16400
16648
|
frame.style.display = 'none';
|
|
16401
16649
|
frame.src = entryUrl;
|
|
16402
|
-
document.body.
|
|
16650
|
+
document.body.append(frame);
|
|
16403
16651
|
return frame;
|
|
16404
16652
|
}
|
|
16405
|
-
reportingRefusals(methods) {
|
|
16406
|
-
const reported = Object.entries(methods).map(([name, method]) => [
|
|
16407
|
-
name,
|
|
16408
|
-
(...args) => {
|
|
16409
|
-
try {
|
|
16410
|
-
return method(...args);
|
|
16411
|
-
}
|
|
16412
|
-
catch (error) {
|
|
16413
|
-
this.refusals.report(error);
|
|
16414
|
-
throw error;
|
|
16415
|
-
}
|
|
16416
|
-
},
|
|
16417
|
-
]);
|
|
16418
|
-
return Object.fromEntries(reported);
|
|
16419
|
-
}
|
|
16420
|
-
rpcMethods(pluginId, ctx, syncCleanups, watched, origins) {
|
|
16421
|
-
return {
|
|
16422
|
-
registerSurface: (surface) => {
|
|
16423
|
-
ctx.registerSurface(sanitizeRpcSurface(pluginId, surface, origins));
|
|
16424
|
-
},
|
|
16425
|
-
registerMenuItem: (item) => {
|
|
16426
|
-
ctx.registerMenuItem(sanitizeRpcMenuItem(item));
|
|
16427
|
-
},
|
|
16428
|
-
registerSettingsSection: (section) => {
|
|
16429
|
-
const built = buildFrameSection({
|
|
16430
|
-
pluginId,
|
|
16431
|
-
wire: sanitizeRpcSettingsSection(pluginId, section),
|
|
16432
|
-
group: this.install.isInstalled(pluginId)
|
|
16433
|
-
? 'settings.group.community'
|
|
16434
|
-
: 'settings.group.plugins',
|
|
16435
|
-
store: this.store,
|
|
16436
|
-
sync: this.sync,
|
|
16437
|
-
notify: (sectionId, values) => this.notifySettings(pluginId, sectionId, values),
|
|
16438
|
-
});
|
|
16439
|
-
syncCleanups.push(built.disposeSync);
|
|
16440
|
-
ctx.registerSettingsSection(built.section);
|
|
16441
|
-
},
|
|
16442
|
-
navigateContent: (path) => ctx.navigateContent(path),
|
|
16443
|
-
openContentTab: (input) => {
|
|
16444
|
-
const sanitized = sanitizeRpcTabInput(input);
|
|
16445
|
-
ctx.openContentTab({
|
|
16446
|
-
...sanitized,
|
|
16447
|
-
onClose: () => this.notifyTabClosed(pluginId, sanitized.path),
|
|
16448
|
-
});
|
|
16449
|
-
},
|
|
16450
|
-
keepContentTab: (path) => ctx.keepContentTab(path),
|
|
16451
|
-
pinContentTab: (path) => ctx.pinContentTab(path),
|
|
16452
|
-
unpinContentTab: (path) => ctx.unpinContentTab(path),
|
|
16453
|
-
closeContentTab: (path) => ctx.closeContentTab(path),
|
|
16454
|
-
revealSurface: (id) => ctx.revealSurface(id),
|
|
16455
|
-
invokeCommand: (id, args) => invokeRpcCommand(ctx, id, args),
|
|
16456
|
-
invocableCommands: () => ctx.invocableCommands(),
|
|
16457
|
-
toast: (input) => ctx.ui.toast(sanitizeRpcToastInput(input)),
|
|
16458
|
-
stateWatch: (key) => this.watchState(pluginId, ctx, watched, key),
|
|
16459
|
-
stateSet: (key, value) => watched.get(key)?.handle.set(value),
|
|
16460
|
-
stateClear: (key) => watched.get(key)?.handle.clear(),
|
|
16461
|
-
stateUnwatch: (key) => {
|
|
16462
|
-
watched.get(key)?.stop();
|
|
16463
|
-
watched.delete(key);
|
|
16464
|
-
},
|
|
16465
|
-
};
|
|
16466
|
-
}
|
|
16467
16653
|
watchState(pluginId, ctx, watched, key) {
|
|
16468
16654
|
if (watched.has(key)) {
|
|
16469
16655
|
return;
|
|
@@ -16472,7 +16658,7 @@ class FramePluginRuntime {
|
|
|
16472
16658
|
const ref = effect(() => {
|
|
16473
16659
|
const value = handle.value();
|
|
16474
16660
|
const loaded = handle.loaded();
|
|
16475
|
-
untracked(() => this.
|
|
16661
|
+
untracked(() => this.notify(pluginId, (remote) => remote.stateChanged(key, value, loaded)));
|
|
16476
16662
|
}, { ...(ngDevMode ? { debugName: "ref" } : /* istanbul ignore next */ {}), injector: this.injector });
|
|
16477
16663
|
watched.set(key, {
|
|
16478
16664
|
handle,
|
|
@@ -16482,32 +16668,12 @@ class FramePluginRuntime {
|
|
|
16482
16668
|
},
|
|
16483
16669
|
});
|
|
16484
16670
|
}
|
|
16485
|
-
|
|
16486
|
-
const instance = this.instances.get(pluginId);
|
|
16487
|
-
if (!instance) {
|
|
16488
|
-
return;
|
|
16489
|
-
}
|
|
16490
|
-
void instance.connection.promise
|
|
16491
|
-
.then((remote) => remote.stateChanged(key, value, loaded))
|
|
16492
|
-
.catch(() => undefined);
|
|
16493
|
-
}
|
|
16494
|
-
notifyTabClosed(pluginId, path) {
|
|
16671
|
+
notify(pluginId, send) {
|
|
16495
16672
|
const instance = this.instances.get(pluginId);
|
|
16496
16673
|
if (!instance) {
|
|
16497
16674
|
return;
|
|
16498
16675
|
}
|
|
16499
|
-
void instance.connection.promise
|
|
16500
|
-
.then((remote) => remote.contentTabClosed(path))
|
|
16501
|
-
.catch(() => undefined);
|
|
16502
|
-
}
|
|
16503
|
-
notifySettings(pluginId, sectionId, values) {
|
|
16504
|
-
const instance = this.instances.get(pluginId);
|
|
16505
|
-
if (!instance) {
|
|
16506
|
-
return;
|
|
16507
|
-
}
|
|
16508
|
-
void instance.connection.promise
|
|
16509
|
-
.then((remote) => remote.settingsChanged(sectionId, values))
|
|
16510
|
-
.catch(() => undefined);
|
|
16676
|
+
void instance.connection.promise.then(send).catch(() => undefined);
|
|
16511
16677
|
}
|
|
16512
16678
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FramePluginRuntime, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
16513
16679
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.0", ngImport: i0, type: FramePluginRuntime });
|
|
@@ -16651,12 +16817,12 @@ function availableUpdate(installed, entry) {
|
|
|
16651
16817
|
return isNewerVersion(entry.version, installed.version) ? entry : undefined;
|
|
16652
16818
|
}
|
|
16653
16819
|
function addedCapabilities(entry, installed) {
|
|
16654
|
-
const consented = new Set(installed.capabilities
|
|
16820
|
+
const consented = new Set(installed.capabilities);
|
|
16655
16821
|
return (entry.capabilities ?? []).filter((capability) => !consented.has(capability));
|
|
16656
16822
|
}
|
|
16657
16823
|
|
|
16658
16824
|
async function confirmUpdate(deps, entry) {
|
|
16659
|
-
const installed = deps.installs.
|
|
16825
|
+
const installed = deps.installs.byId(entry.id);
|
|
16660
16826
|
if (!installed) {
|
|
16661
16827
|
return;
|
|
16662
16828
|
}
|
|
@@ -16822,7 +16988,7 @@ class PluginStoreDetail {
|
|
|
16822
16988
|
transloco = inject(TranslocoService);
|
|
16823
16989
|
readme = signal(undefined, /* @ts-ignore */
|
|
16824
16990
|
...(ngDevMode ? [{ debugName: "readme" }] : /* istanbul ignore next */ []));
|
|
16825
|
-
update = computed(() => availableUpdate(this.installs.
|
|
16991
|
+
update = computed(() => availableUpdate(this.installs.byId(this.entry().id), this.entry()), /* @ts-ignore */
|
|
16826
16992
|
...(ngDevMode ? [{ debugName: "update" }] : /* istanbul ignore next */ []));
|
|
16827
16993
|
constructor() {
|
|
16828
16994
|
effect(() => {
|
|
@@ -16908,7 +17074,7 @@ class PluginStoreDialog {
|
|
|
16908
17074
|
...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
|
|
16909
17075
|
filtered = computed(() => {
|
|
16910
17076
|
const list = (this.entries() ?? []).filter((entry) => matchesQuery([entry.name, entry.author, entry.category, entry.description], this.query()));
|
|
16911
|
-
return list.
|
|
17077
|
+
return list.toSorted((a, b) => (b.downloads ?? 0) - (a.downloads ?? 0) || a.name.localeCompare(b.name));
|
|
16912
17078
|
}, /* @ts-ignore */
|
|
16913
17079
|
...(ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []));
|
|
16914
17080
|
selected = computed(() => this.filtered().find((entry) => entry.id === this.selectedId()), /* @ts-ignore */
|
|
@@ -16920,7 +17086,7 @@ class PluginStoreDialog {
|
|
|
16920
17086
|
void confirmInstall(this.consentDeps, entry);
|
|
16921
17087
|
}
|
|
16922
17088
|
hasUpdate(entry) {
|
|
16923
|
-
return availableUpdate(this.installs.
|
|
17089
|
+
return availableUpdate(this.installs.byId(entry.id), entry) !== undefined;
|
|
16924
17090
|
}
|
|
16925
17091
|
requestUpdate(entry) {
|
|
16926
17092
|
void confirmUpdate(this.consentDeps, entry);
|