@loomweaver/shell 0.7.6 → 0.7.7

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.
@@ -4300,6 +4300,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4300
4300
  type: Service
4301
4301
  }], ctorParameters: () => [] });
4302
4302
 
4303
+ function popoutNavigationRefusal(path) {
4304
+ return (`Content navigation to "${path}" was ignored: this is a pop-out window, which shows one ` +
4305
+ `surface and has no content area to navigate. A command reaches a pop-out's palette only ` +
4306
+ `if it declares popout: true, so leave that off anything that navigates.`);
4307
+ }
4308
+
4303
4309
  class OpenTabsService {
4304
4310
  router = inject(Router);
4305
4311
  registry = inject(ContributionRegistry);
@@ -4438,9 +4444,11 @@ class OpenTabsService {
4438
4444
  this.paneTree.hydrated();
4439
4445
  untracked(() => {
4440
4446
  if (url !== this.lastUrl) {
4447
+ const previous = this.lastUrl;
4441
4448
  this.lastUrl = url;
4442
4449
  if (this.ownNavigation !== normalizePath(url)) {
4443
4450
  this.viewTabSelection.set(null);
4451
+ this.focusHolderOf(path, this.rootFor(previous).root);
4444
4452
  }
4445
4453
  this.ownNavigation = null;
4446
4454
  }
@@ -4459,14 +4467,12 @@ class OpenTabsService {
4459
4467
  navigate(path) {
4460
4468
  if (this.inPopout) {
4461
4469
  if (isDevMode()) {
4462
- console.warn(`Content navigation to "${path}" was ignored: this is a pop-out window, which shows one ` +
4463
- `surface and has no content area to navigate. A command reaches a pop-out's palette only ` +
4464
- `if it declares popout: true, so leave that off anything that navigates.`);
4470
+ console.warn(popoutNavigationRefusal(path));
4465
4471
  }
4466
4472
  return Promise.resolve(false);
4467
4473
  }
4468
4474
  const target = normalizePath(path);
4469
- this.focusHolderOf(target);
4475
+ this.focusHolderOf(target, this.activeTabRoot());
4470
4476
  this.ownNavigation = target;
4471
4477
  this.viewTabSelection.set(null);
4472
4478
  return this.router.navigateByUrl('/' + target + suffixOf(path));
@@ -4522,7 +4528,7 @@ class OpenTabsService {
4522
4528
  return (owner.rest === true ||
4523
4529
  segmentsOf(owner.path).length === segmentsOf(address).length);
4524
4530
  }
4525
- focusHolderOf(target) {
4531
+ focusHolderOf(target, previousContent) {
4526
4532
  const routes = this.registry.contentRoutes();
4527
4533
  const root = tabRootOf(routes, target);
4528
4534
  if (root === '') {
@@ -4540,7 +4546,7 @@ class OpenTabsService {
4540
4546
  return;
4541
4547
  }
4542
4548
  this.paneTree.setActiveTab(CONTENT_DOCK, holder.id, held.path);
4543
- this.paneTree.focusPane(CONTENT_DOCK, holder.id, this.activeTabRoot());
4549
+ this.paneTree.focusPane(CONTENT_DOCK, holder.id, previousContent);
4544
4550
  }
4545
4551
  stampActive(root) {
4546
4552
  const next = new Map(this.lastActive());
@@ -11363,7 +11369,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
11363
11369
  // GENERATED — do not edit by hand.
11364
11370
  // Written by tools/stamp-version.mjs from <Version> in Directory.Build.props.
11365
11371
  // Single source of truth: Directory.Build.props (bump via scripts/bump-version.sh).
11366
- const APP_VERSION = '0.7.6';
11372
+ const APP_VERSION = '0.7.7';
11367
11373
 
11368
11374
  /**
11369
11375
  * The running build's version, sourced from `<Version>` in Directory.Build.props
@@ -13861,6 +13867,8 @@ class CommandPalette {
13861
13867
  ? 'tabs'
13862
13868
  : 'commands', /* @ts-ignore */
13863
13869
  ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
13870
+ title = computed(() => this.mode() === 'tabs' ? 'palette.quickOpenTitle' : 'palette.title', /* @ts-ignore */
13871
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
13864
13872
  query = signal('', /* @ts-ignore */
13865
13873
  ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
13866
13874
  rawIndex = signal(0, /* @ts-ignore */
@@ -14026,11 +14034,11 @@ class CommandPalette {
14026
14034
  }
14027
14035
  }
14028
14036
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CommandPalette, deps: [], target: i0.ɵɵFactoryTarget.Component });
14029
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: CommandPalette, isStandalone: true, selector: "lw-command-palette", ngImport: i0, template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"'palette.title' | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14037
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: CommandPalette, isStandalone: true, selector: "lw-command-palette", ngImport: i0, template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"title() | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n", dependencies: [{ kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14030
14038
  }
14031
14039
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: CommandPalette, decorators: [{
14032
14040
  type: Component,
14033
- args: [{ selector: 'lw-command-palette', schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [TranslocoPipe], template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"'palette.title' | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n" }]
14041
+ args: [{ selector: 'lw-command-palette', schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [TranslocoPipe], template: "<div class=\"flex max-h-[60vh] w-full flex-col overflow-hidden\">\n <div class=\"flex items-center gap-2 border-b border-border px-4\">\n <lw-icon name=\"search\" size=\"1.1rem\" class=\"text-content-muted\" />\n <input\n data-lw-autofocus\n type=\"text\"\n role=\"combobox\"\n aria-controls=\"lw-palette-list\"\n [attr.aria-expanded]=\"true\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-label]=\"title() | transloco\"\n [value]=\"query()\"\n [placeholder]=\"\n (mode() === 'tabs' ? 'palette.tabsPlaceholder' : 'palette.placeholder')\n | transloco\n \"\n class=\"flex-1 bg-transparent py-3 text-sm text-content outline-none placeholder:text-content-faint\"\n (input)=\"onQuery($event)\"\n (keydown.arrowdown)=\"move($event, 1)\"\n (keydown.arrowup)=\"move($event, -1)\"\n (keydown.arrowright)=\"openTabActions($event)\"\n (keydown.enter)=\"runActive($event)\"\n />\n </div>\n\n <ul\n id=\"lw-palette-list\"\n role=\"listbox\"\n class=\"min-h-0 flex-1 overflow-y-auto p-1\"\n >\n @for (entry of results(); track entry.id; let i = $index) {\n @if (recentCount() > 0 && i === 0) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-recent\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.recent' | transloco }}\n </li>\n }\n @if (recentCount() > 0 && i === recentCount()) {\n <li\n role=\"presentation\"\n data-testid=\"palette-section-all\"\n class=\"px-3 pt-2 pb-1 text-xs font-medium text-content-faint\"\n >\n {{ 'palette.all' | transloco }}\n </li>\n }\n <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <li\n role=\"option\"\n [id]=\"optionId(i)\"\n [attr.aria-selected]=\"i === activeIndex()\"\n class=\"flex cursor-pointer items-center gap-3 rounded-md px-3 py-2 text-sm\"\n [class]=\"\n i === activeIndex()\n ? 'bg-brand/10 text-brand-text'\n : 'text-content hover:bg-surface-overlay'\n \"\n (click)=\"select(entry)\"\n (mouseenter)=\"setActive(i)\"\n >\n @if (entry.icon; as icon) {\n <lw-icon [name]=\"icon\" size=\"1rem\" class=\"text-content-muted\" />\n }\n <span class=\"flex-1 truncate\">{{ entry.label }}</span>\n @if (timeOf(entry); as time) {\n <span class=\"shrink-0 text-xs text-content-faint\">{{ time }}</span>\n }\n @if (shortcutOf(entry); as shortcut) {\n <kbd\n class=\"rounded border border-border px-1.5 py-0.5 text-xs text-content-muted\"\n >{{ shortcut }}</kbd\n >\n }\n </li>\n } @empty {\n <li class=\"px-3 py-6 text-center text-sm text-content-muted\">\n {{\n (mode() === 'tabs' ? 'palette.tabsEmpty' : 'palette.empty') | transloco\n }}\n </li>\n }\n </ul>\n\n <div\n data-testid=\"palette-footer\"\n class=\"flex items-center gap-4 border-t border-border px-4 py-2 text-xs text-content-faint\"\n >\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2191\u2193</kbd>\n {{ 'palette.hint.navigate' | transloco }}\n </span>\n <span class=\"flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u21B5</kbd>\n {{ 'palette.hint.run' | transloco }}\n </span>\n @if (mode() === 'tabs') {\n <span class=\"flex items-center gap-1.5\" data-testid=\"palette-hint-actions\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">\u2192</kbd>\n {{ 'palette.hint.actions' | transloco }}\n </span>\n }\n <span class=\"ml-auto flex items-center gap-1.5\">\n <kbd class=\"rounded border border-border px-1 py-0.5\">Esc</kbd>\n {{ 'palette.hint.close' | transloco }}\n </span>\n </div>\n</div>\n" }]
14034
14042
  }] });
14035
14043
 
14036
14044
  class WorkspaceDialog {
@@ -14264,7 +14272,12 @@ function seedHostCommands(registry, layout, deps) {
14264
14272
  shortcut: 'mod+k',
14265
14273
  popout: true,
14266
14274
  run: () => {
14267
- dialogs.open(CommandPalette, { bare: true, size: 'lg', align: 'top' });
14275
+ dialogs.open(CommandPalette, {
14276
+ bare: true,
14277
+ size: 'lg',
14278
+ align: 'top',
14279
+ title: 'palette.title',
14280
+ });
14268
14281
  },
14269
14282
  });
14270
14283
  registry.addCommand({
@@ -14289,6 +14302,7 @@ function seedHostCommands(registry, layout, deps) {
14289
14302
  bare: true,
14290
14303
  size: 'lg',
14291
14304
  align: 'top',
14305
+ title: 'palette.quickOpenTitle',
14292
14306
  data: { mode: 'tabs' },
14293
14307
  });
14294
14308
  },
@@ -14801,6 +14815,16 @@ function accessCanMatch(access) {
14801
14815
  };
14802
14816
  }
14803
14817
 
14818
+ const keepPopout = (_route, state) => {
14819
+ if (!isPopoutUrl(inject(BootAddress).path)) {
14820
+ return true;
14821
+ }
14822
+ if (isDevMode()) {
14823
+ console.warn(popoutNavigationRefusal(normalizePath(state.url)));
14824
+ }
14825
+ return false;
14826
+ };
14827
+
14804
14828
  const settleWorkspace = async (_route, state) => {
14805
14829
  const claims = inject(WORKSPACE_CLAIMS);
14806
14830
  await claims.settle(normalizePath(state.url));
@@ -14841,6 +14865,7 @@ function buildContentRoutes(contentRoutes, omitted = [], retention = 'destroy')
14841
14865
  const placeholders = omitted.map((route) => ({
14842
14866
  path: route.path,
14843
14867
  component: RouteUnavailableView,
14868
+ canActivate: [keepPopout],
14844
14869
  data: { content: true, routePlaceholder: true },
14845
14870
  }));
14846
14871
  return [...buildRegisteredRoutes(contentRoutes, retention), ...placeholders];
@@ -14886,7 +14911,7 @@ function buildRegisteredRoutes(contentRoutes, retention) {
14886
14911
  const angular = {
14887
14912
  path: route.path,
14888
14913
  ...surfaceRoute(route, retained),
14889
- canActivate: [settleWorkspace],
14914
+ canActivate: [keepPopout, settleWorkspace],
14890
14915
  data: {
14891
14916
  content: true,
14892
14917
  chromeless: route.chromeless,
@@ -14905,6 +14930,7 @@ function buildRegisteredRoutes(contentRoutes, retention) {
14905
14930
  const placeholder = {
14906
14931
  path: route.path,
14907
14932
  component: AuthRequiredView,
14933
+ canActivate: [keepPopout],
14908
14934
  data: {
14909
14935
  content: true,
14910
14936
  authPlaceholder: true,