flexdesk 0.4.2 → 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);
@@ -4158,56 +4160,42 @@ var WindowManager = class _WindowManager {
4158
4160
  * methods (openFromContext / openInLeaf / navigateActiveTab) —
4159
4161
  * they all record history by default. */
4160
4162
  navigateBack() {
4161
- if (this._navigateBackInWindow()) return;
4162
- const tree = this._tree();
4163
- const focusedId = tree.focusedLeafId;
4164
- const primaryId = tree.primaryLeafId();
4165
- const focusedLeaf = focusedId ? tree.get(focusedId) : null;
4166
- const focusedKind = focusedLeaf?.content?.kind || "";
4167
- const id = focusedId && focusedKind && !focusedKind.startsWith("panel:") && focusedKind !== "window-placeholder" ? focusedId : primaryId;
4168
- if (!id) return;
4169
- const prior = tree.popActiveTabHistory?.(id);
4170
- if (prior && prior.kind) {
4171
- const leafNow = tree.get(id);
4172
- const curKind = leafNow?.content?.kind;
4173
- const curTopNav = curKind ? this.taxonomy.topNavFor(curKind) : null;
4174
- if (prior.topNav && curTopNav && prior.topNav !== curTopNav) {
4175
- tree.swapToPage?.(
4176
- id,
4177
- {
4178
- kind: prior.kind,
4179
- props: prior.props || {},
4180
- title: prior.title || _tabTitle(prior.kind, prior.props)
4181
- },
4182
- curTopNav,
4183
- prior.topNav,
4184
- { recordHistory: false }
4185
- );
4186
- } else {
4187
- tree.replaceActiveTabContent(
4188
- id,
4189
- { kind: prior.kind, props: prior.props || {} },
4190
- prior.title || _tabTitle(prior.kind, prior.props),
4191
- { recordHistory: false }
4192
- );
4193
- }
4194
- tree.focus(id);
4195
- this.renderer.render();
4196
- this._persist();
4197
- this._notifyChange("navigate-back");
4198
- return;
4199
- }
4200
- if (this.backToOpenList && this._backToOpenList(id)) return;
4201
- const leaf = tree.get(id);
4202
- if (!leaf?.content) return;
4203
- const { kind, props } = leaf.content;
4204
- const up = this.taxonomy.parentOf(kind, props);
4205
- if (up) {
4206
- this.navigateActiveTab(id, up.kind, up.props);
4207
- return;
4208
- }
4209
- const parent = this.taxonomy.parentKindFor(kind);
4210
- 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);
4211
4199
  }
4212
4200
  /** True when `navigateBack` would do something user-visible — i.e.
4213
4201
  * the active tab has per-tab history, OR the current kind has a
@@ -4239,135 +4227,178 @@ var WindowManager = class _WindowManager {
4239
4227
  this.renderer?.rebaselineLeaf?.(leafId, expected);
4240
4228
  this._persist();
4241
4229
  }
4242
- /** Navigate inside the current tab — preserves every other tab in
4243
- * the leaf. Used by Backspace + breadcrumb segments + anything
4244
- * else that should walk WITHIN the tile rather than reset it. */
4245
- /**
4246
- * C32. BACK FROM AN OPENED RECORD RETURNS TO THE LIST, IT DOES NOT MAKE ONE.
4247
- *
4248
- * The gesture this is for: a list tab, a record opened from it into a NEW
4249
- * tab, and Backspace in that record. With nothing left in the record tab's
4250
- * own history, step (3) of `navigateBack` walks the taxonomy up and REWRITES
4251
- * the record tab into its parent — so the tile ends up showing the list
4252
- * twice, once in the tab you started from and once in the tab you just left.
4253
- * The user's model is a browser's: you came from the list, the list is still
4254
- * open, so going back means closing this and being on the list again.
4255
- *
4256
- * So: when the active tab is an ENTITY — its props carry an `id`, the same
4257
- * test `swapToPage` uses for "the caller asked for a specific entity" — look
4258
- * for an open tab of the SAME section that is NOT one. That is the list or
4259
- * landing page the record belongs to. Close the record and activate it.
4260
- *
4261
- * NEAREST TO THE LEFT FIRST. New tabs are appended at the end, so the tab a
4262
- * record was opened from sits to its left; when several lists of the section
4263
- * are open, the nearest one on that side is the one it most plausibly came
4264
- * from. The right side is searched only after the left has nothing.
4265
- *
4266
- * DECLINES, and leaves step (3) to walk up in place, whenever there is no
4267
- * such tab: a record that is the tile's only tab, or one whose section has no
4268
- * list open. That keeps the one guarantee the whole rule exists for — Back
4269
- * never leaves two copies of a list — without ever closing a record into a
4270
- * tile that has nothing of its own to show.
4271
- *
4272
- * Matching is by SECTION (`topNavFor`) and not by kind, because the list a
4273
- * record was opened from is often not the record's taxonomy parent: a root
4274
- * landing page that renders the section, or a list that a flattened taxonomy
4275
- * files beside the record rather than above it.
4276
- *
4277
- * @returns {boolean} whether it closed a tab
4278
- */
4279
- _backToOpenList(leafId) {
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;
4280
4245
  const tree = this._tree();
4281
- const leaf = tree.get(leafId);
4282
- if (!leaf || leaf.kind !== "leaf") return false;
4283
- const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
4284
- if (tabs.length < 2) return false;
4285
- const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));
4286
- const active = tabs[activeIdx];
4287
- if (!_isEntityTab(active)) return false;
4288
- const section = this.taxonomy.topNavFor(active.kind);
4289
- if (!section) return false;
4290
- const isList = (t) => t && !_isEntityTab(t) && this.taxonomy.topNavFor(t.kind) === section;
4291
- let target = -1;
4292
- for (let i = activeIdx - 1; i >= 0 && target < 0; i--) if (isList(tabs[i])) target = i;
4293
- for (let i = activeIdx + 1; i < tabs.length && target < 0; i++) if (isList(tabs[i])) target = i;
4294
- if (target < 0) return false;
4295
- tree.removeLeafTab(leafId, activeIdx);
4296
- tree.setActiveLeafTab(leafId, target > activeIdx ? target - 1 : target);
4297
- tree.focus(leafId);
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);
4298
4309
  this.renderer.render();
4299
4310
  this._persist();
4300
- this._notifyChange("navigate-back-close");
4311
+ this._notifyChange("navigate-back");
4301
4312
  return true;
4302
4313
  }
4303
4314
  /**
4304
- * C34. Back while a floating window is the thing being read. Returns true
4305
- * when it handled the press, false to let the tile path run.
4306
- *
4307
- * Only the window's ACTIVE tab is considered, in this order:
4308
- * (1) with `backToOpenList`, a record closes onto an open list of its
4309
- * section: first among the window's own tabs, then any tile of the
4310
- * window's desktop, primary tile first. Closing the last tab closes
4311
- * the window, which discards it (a plain close never docks back).
4312
- * (2) otherwise it walks up IN the window, exactly as a tile does.
4313
- * A window whose content has nowhere to go still counts as handled: the
4314
- * press was aimed at it, and moving the tile behind it would be the bug.
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
4315
4340
  */
4316
- _navigateBackInWindow() {
4317
- const winId = this._backWindowId;
4318
- const rec = winId ? this._windowToLeaf.get(winId) : null;
4319
- if (!rec) {
4320
- this._backWindowId = null;
4321
- return false;
4322
- }
4323
- const tabs = Array.isArray(rec.tabs) && rec.tabs.length ? rec.tabs : [rec.original].filter(Boolean);
4324
- const activeIdx = Math.max(0, Math.min(tabs.length - 1, rec.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;
4325
4346
  const active = tabs[activeIdx];
4326
- if (!active) return true;
4327
- const section = this.backToOpenList && _isEntityTab(active) ? this.taxonomy.topNavFor(active.kind) : null;
4347
+ if (!_isEntityTab(active)) return false;
4348
+ const section = this.taxonomy.topNavFor(active.kind);
4349
+ if (!section) return false;
4328
4350
  const isList = (t) => !!t && !_isEntityTab(t) && this.taxonomy.topNavFor(t.kind) === section;
4329
- if (section) {
4330
- let own = -1;
4331
- for (let i = activeIdx - 1; i >= 0 && own < 0; i--) if (isList(tabs[i])) own = i;
4332
- for (let i = activeIdx + 1; i < tabs.length && own < 0; i++) if (isList(tabs[i])) own = i;
4333
- if (own >= 0 && Array.isArray(rec.tabs) && rec.tabs.length > 1) {
4334
- this._windowTabAction(winId, "close", { idx: activeIdx });
4335
- this.showWindowTab(winId, own > activeIdx ? own - 1 : own);
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);
4336
4357
  return true;
4337
4358
  }
4338
- const desk = this.desktops.desktops[rec.desktopIdx] || this.desktops.active();
4339
- const tree = desk.tree;
4340
- const leaves = tree.leaves?.() || [];
4341
- const primaryId = tree.primaryLeafId();
4342
- const ordered = [...leaves.filter((l) => l.id === primaryId), ...leaves.filter((l) => l.id !== primaryId)];
4343
- for (const leaf of ordered) {
4344
- const ltabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
4345
- const cur = Math.max(0, Math.min(ltabs.length - 1, leaf.activeTabIdx || 0));
4346
- const hit = isList(ltabs[cur]) ? cur : ltabs.findIndex(isList);
4347
- if (hit < 0) continue;
4348
- this._backWindowId = null;
4349
- if (Array.isArray(rec.tabs) && rec.tabs.length > 1) this._windowTabAction(winId, "close", { idx: activeIdx });
4350
- else {
4351
- try {
4352
- rec.window.close({ force: true });
4353
- } catch {
4354
- }
4355
- }
4356
- if (this.desktops.active().tree === tree) {
4357
- tree.setActiveLeafTab(leaf.id, hit);
4358
- tree.focus(leaf.id);
4359
- this.renderer.render();
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 {
4360
4386
  }
4361
- this._persist();
4362
- this._notifyChange("navigate-back-close");
4363
- return true;
4364
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;
4365
4396
  }
4366
- const up = this.taxonomy.parentOf?.(active.kind, active.props || {});
4367
- const parentKind = up ? up.kind : this.taxonomy.parentKindFor(active.kind);
4368
- if (parentKind) this.openInWindow(winId, parentKind, up ? up.props || {} : {});
4369
- return true;
4397
+ return false;
4370
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. */
4371
4402
  navigateActiveTab(leafId, kind, props = {}) {
4372
4403
  const tree = this._tree();
4373
4404
  const leaf = tree.get(leafId);
@@ -4494,6 +4525,11 @@ var WindowManager = class _WindowManager {
4494
4525
  currentTopNav,
4495
4526
  targetTopNav
4496
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
+ }
4497
4533
  tree.focus(leafId);
4498
4534
  if (spawned) this._canonicalize(tree, this.desktops.active());
4499
4535
  this.renderer.render();
@@ -6986,6 +7022,11 @@ function _swapSiblings(tree, aId, bId) {
6986
7022
  parent.sizes[j] = size;
6987
7023
  return true;
6988
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
+ }
6989
7030
  function _isEntityTab(tab) {
6990
7031
  return !!tab && tab.props != null && tab.props.id != null && tab.props.id !== "";
6991
7032
  }