flexdesk 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tiles.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  } from "./chunk-O5OHMWBB.js";
4
4
  import {
5
5
  openRawTracesWindow
6
- } from "./chunk-XKDTIT4Q.js";
7
- import "./chunk-QIU5S2RU.js";
6
+ } from "./chunk-4RGQSRMC.js";
7
+ import "./chunk-P4AALM3A.js";
8
8
  import "./chunk-LH5TSOZW.js";
9
9
  import "./chunk-FL5KFNQH.js";
10
10
  import {
package/dist/widgets.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  DataTable,
23
23
  createRafResizeObserver
24
- } from "./chunk-QIU5S2RU.js";
24
+ } from "./chunk-P4AALM3A.js";
25
25
  import {
26
26
  ManagedWindow
27
27
  } from "./chunk-LH5TSOZW.js";
package/dist/wm.js CHANGED
@@ -2335,7 +2335,9 @@ function mountTileBreadcrumb(kind, props, ctx) {
2335
2335
  console.error("[breadcrumb] no WM available \u2014 click ignored", k);
2336
2336
  return;
2337
2337
  }
2338
- if (typeof wm.navigate === "function") {
2338
+ if (typeof wm.navigateUp === "function") {
2339
+ wm.navigateUp(k, p, { ctx });
2340
+ } else if (typeof wm.navigate === "function") {
2339
2341
  wm.navigate(k, p, { ctx, dest: "origin" });
2340
2342
  } else if (typeof wm.openInPrimary === "function") {
2341
2343
  wm.openInPrimary(k, p);
@@ -2902,7 +2904,7 @@ var TileRenderer = class {
2902
2904
  <button class="twm-leaf__btn" data-action="split-v" title="Split vertically (Alt+V)">
2903
2905
  <span class="material-symbols-outlined">splitscreen_add</span>
2904
2906
  </button>
2905
- <button class="twm-leaf__btn" data-action="promote" title="Float this pane as a window (Alt+F)">
2907
+ <button class="twm-leaf__btn" data-action="promote" title="Float this ${this.ctx.wm?.floatActiveTab ? "tab" : "pane"} as a window (Alt+F)">
2906
2908
  <span class="material-symbols-outlined">web_asset</span>
2907
2909
  </button>`}
2908
2910
  <button class="twm-leaf__btn" data-action="close" title="Close (Alt+W)">
@@ -3979,7 +3981,8 @@ var WindowManager = class _WindowManager {
3979
3981
  snapPromotion = false,
3980
3982
  promoteInPlace = false,
3981
3983
  tabLayout = null,
3982
- backToOpenList = false
3984
+ backToOpenList = false,
3985
+ floatActiveTab = false
3983
3986
  }) {
3984
3987
  if (!taxonomy) throw new Error("WindowManager: a taxonomy is required");
3985
3988
  if (!content || typeof content.mount !== "function") {
@@ -3995,6 +3998,7 @@ var WindowManager = class _WindowManager {
3995
3998
  this.eventBus = eventBus || null;
3996
3999
  this.onChange = onChange || (() => {
3997
4000
  });
4001
+ this.floatActiveTab = !!floatActiveTab;
3998
4002
  this._rootLeaf = () => {
3999
4003
  const kind = this.taxonomy.root;
4000
4004
  return {
@@ -4006,6 +4010,12 @@ var WindowManager = class _WindowManager {
4006
4010
  this.snapPromotion = !!snapPromotion;
4007
4011
  this.promoteInPlace = !!promoteInPlace;
4008
4012
  this.backToOpenList = !!backToOpenList;
4013
+ this._backWindowId = null;
4014
+ const doc = rootEl?.ownerDocument || globalThis.document;
4015
+ doc?.addEventListener?.("pointerdown", (e) => {
4016
+ const el = e.target?.closest?.("[data-window-id]");
4017
+ this._backWindowId = el && this._windowToLeaf.has(el.getAttribute("data-window-id")) ? el.getAttribute("data-window-id") : null;
4018
+ }, true);
4009
4019
  this._snapCtl = null;
4010
4020
  this.desktops = new DesktopManager({
4011
4021
  seed: this._rootLeaf,
@@ -4150,55 +4160,42 @@ var WindowManager = class _WindowManager {
4150
4160
  * methods (openFromContext / openInLeaf / navigateActiveTab) —
4151
4161
  * they all record history by default. */
4152
4162
  navigateBack() {
4153
- const tree = this._tree();
4154
- const focusedId = tree.focusedLeafId;
4155
- const primaryId = tree.primaryLeafId();
4156
- const focusedLeaf = focusedId ? tree.get(focusedId) : null;
4157
- const focusedKind = focusedLeaf?.content?.kind || "";
4158
- const id = focusedId && focusedKind && !focusedKind.startsWith("panel:") && focusedKind !== "window-placeholder" ? focusedId : primaryId;
4159
- if (!id) return;
4160
- const prior = tree.popActiveTabHistory?.(id);
4161
- if (prior && prior.kind) {
4162
- const leafNow = tree.get(id);
4163
- const curKind = leafNow?.content?.kind;
4164
- const curTopNav = curKind ? this.taxonomy.topNavFor(curKind) : null;
4165
- if (prior.topNav && curTopNav && prior.topNav !== curTopNav) {
4166
- tree.swapToPage?.(
4167
- id,
4168
- {
4169
- kind: prior.kind,
4170
- props: prior.props || {},
4171
- title: prior.title || _tabTitle(prior.kind, prior.props)
4172
- },
4173
- curTopNav,
4174
- prior.topNav,
4175
- { recordHistory: false }
4176
- );
4177
- } else {
4178
- tree.replaceActiveTabContent(
4179
- id,
4180
- { kind: prior.kind, props: prior.props || {} },
4181
- prior.title || _tabTitle(prior.kind, prior.props),
4182
- { recordHistory: false }
4183
- );
4184
- }
4185
- tree.focus(id);
4186
- this.renderer.render();
4187
- this._persist();
4188
- this._notifyChange("navigate-back");
4189
- return;
4190
- }
4191
- if (this.backToOpenList && this._backToOpenList(id)) return;
4192
- const leaf = tree.get(id);
4193
- if (!leaf?.content) return;
4194
- const { kind, props } = leaf.content;
4195
- const up = this.taxonomy.parentOf(kind, props);
4196
- if (up) {
4197
- this.navigateActiveTab(id, up.kind, up.props);
4198
- return;
4199
- }
4200
- const parent = this.taxonomy.parentKindFor(kind);
4201
- if (parent) this.navigateActiveTab(id, parent);
4163
+ const scope = this._backScope();
4164
+ if (!scope) return;
4165
+ if (scope.leafId && this._popTabHistory(scope.leafId)) return;
4166
+ if (this._returnToOpenList(scope)) return;
4167
+ const active = this._scopeActiveTab(scope);
4168
+ if (!active) return;
4169
+ const up = this.taxonomy.parentOf(active.kind, active.props || {});
4170
+ const parentKind = up ? up.kind : this.taxonomy.parentKindFor(active.kind);
4171
+ if (parentKind) this._navigateInScope(scope, parentKind, up ? up.props || {} : {});
4172
+ }
4173
+ /**
4174
+ * C35. CLIMB TO A LEVEL: what a breadcrumb crumb means.
4175
+ *
4176
+ * `navigate` goes to exactly the target. A crumb says something different:
4177
+ * "take me up to this level of where I am". From a record, the level it
4178
+ * names is usually a list of the record's own section, and when such a list
4179
+ * is already open that is the SAME request Backspace makes, so it goes
4180
+ * through the same rule, `_returnToOpenList`, and closes the record onto the
4181
+ * open list instead of turning the record into a second copy of it.
4182
+ *
4183
+ * Every crumb calls this, so no crumb needs to know whether it is the
4184
+ * immediate parent. A crumb that names an entity (an epic above a story), a
4185
+ * record with no list of its section open, or content that is not a record
4186
+ * navigates in place, exactly as `navigate(…, { dest: 'origin' })` would.
4187
+ *
4188
+ * @param {string} kind
4189
+ * @param {object} [props]
4190
+ * @param {{ctx?: object}} [opts] the crumb's tile or window context
4191
+ */
4192
+ navigateUp(kind, props = {}, { ctx = null } = {}) {
4193
+ const scope = this._scopeFromCtx(ctx);
4194
+ if (!scope) return this.navigate(kind, props, { ctx, dest: "origin" });
4195
+ const active = this._scopeActiveTab(scope);
4196
+ const aimsAtOwnList = !_isEntityTab({ props }) && _isEntityTab(active) && this.taxonomy.topNavFor(kind) === this.taxonomy.topNavFor(active.kind);
4197
+ if (aimsAtOwnList && this._returnToOpenList(scope)) return;
4198
+ this._navigateInScope(scope, kind, props);
4202
4199
  }
4203
4200
  /** True when `navigateBack` would do something user-visible — i.e.
4204
4201
  * the active tab has per-tab history, OR the current kind has a
@@ -4230,67 +4227,178 @@ var WindowManager = class _WindowManager {
4230
4227
  this.renderer?.rebaselineLeaf?.(leafId, expected);
4231
4228
  this._persist();
4232
4229
  }
4233
- /** Navigate inside the current tab — preserves every other tab in
4234
- * the leaf. Used by Backspace + breadcrumb segments + anything
4235
- * else that should walk WITHIN the tile rather than reset it. */
4230
+ // ── Back and up: one scope model, one rule ─────────────────────
4231
+ //
4232
+ // A SCOPE is where a navigation happens: `{ leafId }` for a tile or
4233
+ // `{ windowId }` for a floating window. Backspace and `navigateUp` both
4234
+ // resolve one, and everything below works on a scope, so tiles and windows
4235
+ // cannot drift into two versions of the same behaviour.
4236
+ /** The scope Backspace acts on. C34: a floating window the user last
4237
+ * clicked in, because the tree's focus never moves to a window; else the
4238
+ * focused content tile; else the primary tile. Panels are never a scope:
4239
+ * Backspace from the nav panel walks the content area. */
4240
+ _backScope() {
4241
+ if (this._backWindowId && this._windowToLeaf.has(this._backWindowId)) {
4242
+ return { windowId: this._backWindowId };
4243
+ }
4244
+ this._backWindowId = null;
4245
+ const tree = this._tree();
4246
+ const focusedId = tree.focusedLeafId;
4247
+ const focusedKind = focusedId ? tree.get(focusedId)?.content?.kind || "" : "";
4248
+ const id = focusedId && focusedKind && !focusedKind.startsWith("panel:") && focusedKind !== "window-placeholder" ? focusedId : tree.primaryLeafId();
4249
+ return id ? { leafId: id } : null;
4250
+ }
4251
+ /** The scope a content context names: its window, or its tile. Null for a
4252
+ * panel or a context the WM does not know. */
4253
+ _scopeFromCtx(ctx) {
4254
+ if (ctx?.windowId && this._windowToLeaf.has(ctx.windowId)) return { windowId: ctx.windowId };
4255
+ const leaf = ctx?.leafId ? this._tree().get(ctx.leafId) : null;
4256
+ const k = leaf?.content?.kind || "";
4257
+ if (leaf && leaf.kind === "leaf" && k && !k.startsWith("panel:")) return { leafId: leaf.id };
4258
+ return null;
4259
+ }
4260
+ /** A scope's tab list and active index. A window promoted with no strip, or
4261
+ * a tile with no tab array, reads as a single tab. */
4262
+ _scopeTabs(scope) {
4263
+ if (scope?.windowId) {
4264
+ const rec = this._windowToLeaf.get(scope.windowId);
4265
+ if (!rec) return null;
4266
+ const tabs2 = Array.isArray(rec.tabs) && rec.tabs.length ? rec.tabs : [rec.original].filter(Boolean);
4267
+ return { tabs: tabs2, activeIdx: Math.max(0, Math.min(tabs2.length - 1, rec.activeTabIdx || 0)) };
4268
+ }
4269
+ const leaf = scope?.leafId ? this._tree().get(scope.leafId) : null;
4270
+ if (!leaf || leaf.kind !== "leaf") return null;
4271
+ const tabs = Array.isArray(leaf.tabs) && leaf.tabs.length ? leaf.tabs : [leaf.content].filter(Boolean);
4272
+ return { tabs, activeIdx: Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0)) };
4273
+ }
4274
+ _scopeActiveTab(scope) {
4275
+ const src = this._scopeTabs(scope);
4276
+ return src ? src.tabs[src.activeIdx] || null : null;
4277
+ }
4278
+ /** Replace what a scope shows, in place: the tile's active tab (recording
4279
+ * history) or the window's content. */
4280
+ _navigateInScope(scope, kind, props = {}) {
4281
+ if (scope.windowId) return this.openInWindow(scope.windowId, kind, props);
4282
+ return this.navigateActiveTab(scope.leafId, kind, props);
4283
+ }
4284
+ /** Pop the active tab's history, if it has any. */
4285
+ _popTabHistory(id) {
4286
+ const tree = this._tree();
4287
+ const prior = tree.popActiveTabHistory?.(id);
4288
+ if (!prior || !prior.kind) return false;
4289
+ const curKind = tree.get(id)?.content?.kind;
4290
+ const curTopNav = curKind ? this.taxonomy.topNavFor(curKind) : null;
4291
+ const title = prior.title || _tabTitle(prior.kind, prior.props);
4292
+ if (prior.topNav && curTopNav && prior.topNav !== curTopNav) {
4293
+ tree.swapToPage?.(
4294
+ id,
4295
+ { kind: prior.kind, props: prior.props || {}, title },
4296
+ curTopNav,
4297
+ prior.topNav,
4298
+ { recordHistory: false }
4299
+ );
4300
+ } else {
4301
+ tree.replaceActiveTabContent(
4302
+ id,
4303
+ { kind: prior.kind, props: prior.props || {} },
4304
+ title,
4305
+ { recordHistory: false }
4306
+ );
4307
+ }
4308
+ tree.focus(id);
4309
+ this.renderer.render();
4310
+ this._persist();
4311
+ this._notifyChange("navigate-back");
4312
+ return true;
4313
+ }
4236
4314
  /**
4237
- * C32. BACK FROM AN OPENED RECORD RETURNS TO THE LIST, IT DOES NOT MAKE ONE.
4238
- *
4239
- * The gesture this is for: a list tab, a record opened from it into a NEW
4240
- * tab, and Backspace in that record. With nothing left in the record tab's
4241
- * own history, step (3) of `navigateBack` walks the taxonomy up and REWRITES
4242
- * the record tab into its parent — so the tile ends up showing the list
4243
- * twice, once in the tab you started from and once in the tab you just left.
4244
- * The user's model is a browser's: you came from the list, the list is still
4245
- * open, so going back means closing this and being on the list again.
4246
- *
4247
- * So: when the active tab is an ENTITY — its props carry an `id`, the same
4248
- * test `swapToPage` uses for "the caller asked for a specific entity" — look
4249
- * for an open tab of the SAME section that is NOT one. That is the list or
4250
- * landing page the record belongs to. Close the record and activate it.
4251
- *
4252
- * NEAREST TO THE LEFT FIRST. New tabs are appended at the end, so the tab a
4253
- * record was opened from sits to its left; when several lists of the section
4254
- * are open, the nearest one on that side is the one it most plausibly came
4255
- * from. The right side is searched only after the left has nothing.
4256
- *
4257
- * DECLINES, and leaves step (3) to walk up in place, whenever there is no
4258
- * such tab: a record that is the tile's only tab, or one whose section has no
4259
- * list open. That keeps the one guarantee the whole rule exists for — Back
4260
- * never leaves two copies of a list — without ever closing a record into a
4261
- * tile that has nothing of its own to show.
4262
- *
4263
- * Matching is by SECTION (`topNavFor`) and not by kind, because the list a
4264
- * record was opened from is often not the record's taxonomy parent: a root
4265
- * landing page that renders the section, or a list that a flattened taxonomy
4266
- * files beside the record rather than above it.
4267
- *
4268
- * @returns {boolean} whether it closed a tab
4315
+ * C32 / C34 / C35. BACK FROM A RECORD RETURNS TO THE LIST; IT DOES NOT MAKE ONE.
4316
+ *
4317
+ * The gesture this is for: a list, a record opened from it into a new tab,
4318
+ * then Backspace or the list's crumb in that record. Rewriting the record
4319
+ * into its parent would leave the list open twice. The user's model is a
4320
+ * browser's: the list is still open, so going back closes the record and
4321
+ * shows the list again. This function is the ONLY place that rule lives.
4322
+ *
4323
+ * Applies when `backToOpenList` is on and the scope's active tab is a RECORD
4324
+ * (its props carry an `id`). The list is an open tab that is not a record
4325
+ * and belongs to the same SECTION (`topNavFor`), matched by section and not
4326
+ * by kind because a record's list is often not its taxonomy parent: a root
4327
+ * landing that renders the section, or a list filed beside the record.
4328
+ *
4329
+ * Where it looks, in order:
4330
+ * - the scope's own tabs, nearest to the LEFT first (new tabs are appended,
4331
+ * so the tab a record came from sits to its left), then to the right;
4332
+ * - for a WINDOW only, then any tile of the window's desktop, primary tile
4333
+ * first, activating a list tab there. Closing a window's last tab closes
4334
+ * the window, which discards it; a plain close never docks back.
4335
+ *
4336
+ * Declines, leaving the caller to navigate in place, when there is no such
4337
+ * list, so Back never closes a record into a tile with nothing to show.
4338
+ *
4339
+ * @returns {boolean} whether it closed the record
4269
4340
  */
4270
- _backToOpenList(leafId) {
4271
- const tree = this._tree();
4272
- const leaf = tree.get(leafId);
4273
- if (!leaf || leaf.kind !== "leaf") return false;
4274
- const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
4275
- if (tabs.length < 2) return false;
4276
- const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));
4341
+ _returnToOpenList(scope) {
4342
+ if (!this.backToOpenList) return false;
4343
+ const src = this._scopeTabs(scope);
4344
+ if (!src) return false;
4345
+ const { tabs, activeIdx } = src;
4277
4346
  const active = tabs[activeIdx];
4278
4347
  if (!_isEntityTab(active)) return false;
4279
4348
  const section = this.taxonomy.topNavFor(active.kind);
4280
4349
  if (!section) return false;
4281
- const isList = (t) => t && !_isEntityTab(t) && this.taxonomy.topNavFor(t.kind) === section;
4282
- let target = -1;
4283
- for (let i = activeIdx - 1; i >= 0 && target < 0; i--) if (isList(tabs[i])) target = i;
4284
- for (let i = activeIdx + 1; i < tabs.length && target < 0; i++) if (isList(tabs[i])) target = i;
4285
- if (target < 0) return false;
4286
- tree.removeLeafTab(leafId, activeIdx);
4287
- tree.setActiveLeafTab(leafId, target > activeIdx ? target - 1 : target);
4288
- tree.focus(leafId);
4289
- this.renderer.render();
4290
- this._persist();
4291
- this._notifyChange("navigate-back-close");
4292
- return true;
4350
+ const isList = (t) => !!t && !_isEntityTab(t) && this.taxonomy.topNavFor(t.kind) === section;
4351
+ const own = _nearestIndex(tabs, activeIdx, isList);
4352
+ if (own >= 0 && tabs.length > 1) {
4353
+ const landing = own > activeIdx ? own - 1 : own;
4354
+ if (scope.windowId) {
4355
+ this._windowTabAction(scope.windowId, "close", { idx: activeIdx });
4356
+ this.showWindowTab(scope.windowId, landing);
4357
+ return true;
4358
+ }
4359
+ const tree2 = this._tree();
4360
+ tree2.removeLeafTab(scope.leafId, activeIdx);
4361
+ tree2.setActiveLeafTab(scope.leafId, landing);
4362
+ tree2.focus(scope.leafId);
4363
+ this.renderer.render();
4364
+ this._persist();
4365
+ this._notifyChange("navigate-back-close");
4366
+ return true;
4367
+ }
4368
+ if (!scope.windowId) return false;
4369
+ const rec = this._windowToLeaf.get(scope.windowId);
4370
+ const tree = (this.desktops.desktops[rec.desktopIdx] || this.desktops.active()).tree;
4371
+ const primaryId = tree.primaryLeafId();
4372
+ const leaves = tree.leaves?.() || [];
4373
+ const ordered = [...leaves.filter((l) => l.id === primaryId), ...leaves.filter((l) => l.id !== primaryId)];
4374
+ for (const leaf of ordered) {
4375
+ const ltabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
4376
+ const cur = Math.max(0, Math.min(ltabs.length - 1, leaf.activeTabIdx || 0));
4377
+ const hit = isList(ltabs[cur]) ? cur : ltabs.findIndex(isList);
4378
+ if (hit < 0) continue;
4379
+ if (this._backWindowId === scope.windowId) this._backWindowId = null;
4380
+ if (Array.isArray(rec.tabs) && rec.tabs.length > 1) {
4381
+ this._windowTabAction(scope.windowId, "close", { idx: activeIdx });
4382
+ } else {
4383
+ try {
4384
+ rec.window.close({ force: true });
4385
+ } catch {
4386
+ }
4387
+ }
4388
+ if (this.desktops.active().tree === tree) {
4389
+ tree.setActiveLeafTab(leaf.id, hit);
4390
+ tree.focus(leaf.id);
4391
+ this.renderer.render();
4392
+ }
4393
+ this._persist();
4394
+ this._notifyChange("navigate-back-close");
4395
+ return true;
4396
+ }
4397
+ return false;
4293
4398
  }
4399
+ /** Navigate inside the current tab — preserves every other tab in
4400
+ * the leaf. Used by Backspace + breadcrumb segments + anything
4401
+ * else that should walk WITHIN the tile rather than reset it. */
4294
4402
  navigateActiveTab(leafId, kind, props = {}) {
4295
4403
  const tree = this._tree();
4296
4404
  const leaf = tree.get(leafId);
@@ -4417,6 +4525,11 @@ var WindowManager = class _WindowManager {
4417
4525
  currentTopNav,
4418
4526
  targetTopNav
4419
4527
  );
4528
+ const _leaf = tree.get(leafId);
4529
+ const _active = _leaf?.tabs?.[_leaf.activeTabIdx];
4530
+ if (_active && _active.kind === kind && Object.keys(props || {}).length > 0 && JSON.stringify(_active.props || {}) !== JSON.stringify(props || {})) {
4531
+ tree.replaceActiveTabContent(leafId, { kind, props }, title, { recordHistory: false });
4532
+ }
4420
4533
  tree.focus(leafId);
4421
4534
  if (spawned) this._canonicalize(tree, this.desktops.active());
4422
4535
  this.renderer.render();
@@ -4632,6 +4745,11 @@ var WindowManager = class _WindowManager {
4632
4745
  const tree = this._tree();
4633
4746
  const focused = tree.focused();
4634
4747
  if (!focused) return null;
4748
+ const tabs = Array.isArray(focused.tabs) ? focused.tabs : [];
4749
+ if (this.floatActiveTab && tabs.length > 1) {
4750
+ const idx = Math.max(0, Math.min(tabs.length - 1, focused.activeTabIdx || 0));
4751
+ return this.floatTabAsWindow(focused.id, idx);
4752
+ }
4635
4753
  return this.floatPane(focused.id);
4636
4754
  }
4637
4755
  /** R8. Float a pane — every tab, with the strip — into a managed window.
@@ -6904,6 +7022,11 @@ function _swapSiblings(tree, aId, bId) {
6904
7022
  parent.sizes[j] = size;
6905
7023
  return true;
6906
7024
  }
7025
+ function _nearestIndex(tabs, from, test) {
7026
+ for (let i = from - 1; i >= 0; i--) if (test(tabs[i])) return i;
7027
+ for (let i = from + 1; i < tabs.length; i++) if (test(tabs[i])) return i;
7028
+ return -1;
7029
+ }
6907
7030
  function _isEntityTab(tab) {
6908
7031
  return !!tab && tab.props != null && tab.props.id != null && tab.props.id !== "";
6909
7032
  }
@@ -7421,6 +7544,7 @@ async function createShell({
7421
7544
  snapPromotion = false,
7422
7545
  promoteInPlace = false,
7423
7546
  backToOpenList = false,
7547
+ floatActiveTab = false,
7424
7548
  tabLayout = null,
7425
7549
  chrome = {},
7426
7550
  // An embedder that moved its sections out of the top bar — into an icon
@@ -7461,6 +7585,7 @@ async function createShell({
7461
7585
  snapPromotion,
7462
7586
  promoteInPlace,
7463
7587
  backToOpenList,
7588
+ floatActiveTab,
7464
7589
  tabLayout,
7465
7590
  // `ctx` is the delivery vehicle for leaf-mounted chrome: tile_renderer
7466
7591
  // spreads it into every content factory, which is how the breadcrumb
@@ -7788,7 +7913,7 @@ function _tileContextMenu(wm, leafId, x, y) {
7788
7913
  // direction is a dead control; in the mean direction it is a missing
7789
7914
  // feature, and this file has shipped one of each.
7790
7915
  {
7791
- label: "Float this pane as a window",
7916
+ label: wm.floatActiveTab ? "Float this tab as a window" : "Float this pane as a window",
7792
7917
  icon: "web_asset",
7793
7918
  action: "promote",
7794
7919
  disabled: isPanel || !leaf.content || wm.renderer?.leafChrome?.(leafId)?.promote === false