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/README.md +24 -0
- package/dist/charts.js +2 -2
- package/dist/{chunk-XKDTIT4Q.js → chunk-4RGQSRMC.js} +2 -2
- package/dist/{chunk-QIU5S2RU.js → chunk-P4AALM3A.js} +2 -2
- package/dist/{chunk-QIU5S2RU.js.map → chunk-P4AALM3A.js.map} +2 -2
- package/dist/tiles.js +2 -2
- package/dist/widgets.js +1 -1
- package/dist/wm.js +232 -107
- package/dist/wm.js.map +3 -3
- package/package.json +1 -1
- package/src/tiling/shell.js +8 -1
- package/src/tiling/tile_breadcrumb.js +9 -8
- package/src/tiling/tile_renderer.js +1 -1
- package/src/tiling/wm.js +258 -118
- package/src/ui/components/data_table.js +5 -1
- /package/dist/{chunk-XKDTIT4Q.js.map → chunk-4RGQSRMC.js.map} +0 -0
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-
|
|
7
|
-
import "./chunk-
|
|
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
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.
|
|
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
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
const
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
const
|
|
4161
|
-
if (
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
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
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
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
|
|
4238
|
-
*
|
|
4239
|
-
* The gesture this is for: a list
|
|
4240
|
-
*
|
|
4241
|
-
*
|
|
4242
|
-
* the
|
|
4243
|
-
*
|
|
4244
|
-
*
|
|
4245
|
-
*
|
|
4246
|
-
*
|
|
4247
|
-
*
|
|
4248
|
-
*
|
|
4249
|
-
*
|
|
4250
|
-
*
|
|
4251
|
-
*
|
|
4252
|
-
*
|
|
4253
|
-
* record
|
|
4254
|
-
*
|
|
4255
|
-
*
|
|
4256
|
-
*
|
|
4257
|
-
*
|
|
4258
|
-
*
|
|
4259
|
-
* list
|
|
4260
|
-
*
|
|
4261
|
-
*
|
|
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
|
-
|
|
4271
|
-
|
|
4272
|
-
const
|
|
4273
|
-
if (!
|
|
4274
|
-
const
|
|
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
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
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
|