flexdesk 0.6.3 → 0.8.0
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 +339 -0
- package/css/base.css +408 -0
- package/css/flexdesk.css +408 -0
- package/dist/charts.js +2 -2
- package/dist/{chunk-WLASPRGP.js → chunk-JLCJWCRS.js} +1577 -56
- package/dist/chunk-JLCJWCRS.js.map +7 -0
- package/dist/{chunk-WAVLVXYV.js → chunk-WKTYKXWF.js} +2 -2
- package/dist/flexdesk.css +408 -0
- package/dist/flow.js +24 -8
- package/dist/flow.js.map +2 -2
- package/dist/tiles.js +2 -2
- package/dist/widgets.js +672 -1
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +86 -13
- package/dist/wm.js.map +2 -2
- package/package.json +1 -1
- package/src/flow/outline/editor.js +46 -8
- package/src/tiling/shell.js +6 -2
- package/src/tiling/tile_renderer.js +116 -18
- package/src/tiling/wm.js +24 -0
- package/src/ui/components/data_table.js +1650 -66
- package/src/ui/components/fact_table.js +342 -0
- package/src/ui/components/record_list.js +594 -0
- package/src/ui/components/table_filter_chips.js +153 -0
- package/dist/chunk-WLASPRGP.js.map +0 -7
- /package/dist/{chunk-WAVLVXYV.js.map → chunk-WKTYKXWF.js.map} +0 -0
package/dist/wm.js
CHANGED
|
@@ -2689,7 +2689,7 @@ function createPageFactories({ eventBus = null, events = {}, refreshOn = {} } =
|
|
|
2689
2689
|
|
|
2690
2690
|
// src/tiling/tile_renderer.js
|
|
2691
2691
|
var SPLITTER_PX = 4;
|
|
2692
|
-
var TAB_LAYOUTS = ["bottom", "top"];
|
|
2692
|
+
var TAB_LAYOUTS = ["bottom", "top", "header"];
|
|
2693
2693
|
var DEFAULT_TAB_LAYOUT = "bottom";
|
|
2694
2694
|
function _normalizeTabLayout(value) {
|
|
2695
2695
|
return TAB_LAYOUTS.includes(value) ? value : DEFAULT_TAB_LAYOUT;
|
|
@@ -2702,7 +2702,7 @@ function _tabKeyIndex(key) {
|
|
|
2702
2702
|
return Number.isInteger(n) && n >= 0 ? n : -1;
|
|
2703
2703
|
}
|
|
2704
2704
|
var TILE_LIFT_PX = 24;
|
|
2705
|
-
var CHROME_NO_FLOAT = "button, .twm-leaf__tab";
|
|
2705
|
+
var CHROME_NO_FLOAT = "button, .twm-leaf__tab, .notebook-tabs .tab";
|
|
2706
2706
|
function _isDownwardPull(down, sideways) {
|
|
2707
2707
|
return down > 0 && sideways <= down;
|
|
2708
2708
|
}
|
|
@@ -2877,6 +2877,7 @@ var TileRenderer = class {
|
|
|
2877
2877
|
if (entry && entry.kindKey === kindKey) {
|
|
2878
2878
|
entry.titleEl.textContent = leaf.title || (leaf.content ? leaf.content.kind : "empty");
|
|
2879
2879
|
_paintLeafIcon(entry.iconEl, leaf, entry.content, this.ctx);
|
|
2880
|
+
if (this.tabLayout === "header") this._refreshHeaderLabels(leaf, entry);
|
|
2880
2881
|
return entry.wrapEl;
|
|
2881
2882
|
}
|
|
2882
2883
|
if (entry) {
|
|
@@ -2928,7 +2929,14 @@ var TileRenderer = class {
|
|
|
2928
2929
|
if (!Array.isArray(leaf.tabs) || leaf.tabs.length <= 1) {
|
|
2929
2930
|
tabBar.classList.add("twm-leaf__tabbar--hidden");
|
|
2930
2931
|
}
|
|
2931
|
-
wrap.append(chrome,
|
|
2932
|
+
wrap.append(chrome, body, tabBar);
|
|
2933
|
+
this._placeTabBar({
|
|
2934
|
+
wrapEl: wrap,
|
|
2935
|
+
bodyEl: body,
|
|
2936
|
+
chromeEl: chrome,
|
|
2937
|
+
titleEl: title,
|
|
2938
|
+
tabBarEl: tabBar
|
|
2939
|
+
});
|
|
2932
2940
|
wrap.addEventListener("mousedown", (e) => {
|
|
2933
2941
|
if (e.target.closest(".twm-splitter")) return;
|
|
2934
2942
|
this.tree.focus(leaf.id);
|
|
@@ -3148,9 +3156,10 @@ var TileRenderer = class {
|
|
|
3148
3156
|
* attached element is a re-parent, not a rebuild — the body keeps its
|
|
3149
3157
|
* DOM, its listeners and its scroll. */
|
|
3150
3158
|
_placeTabBar(entry) {
|
|
3151
|
-
const { wrapEl, bodyEl, tabBarEl } = entry;
|
|
3159
|
+
const { wrapEl, bodyEl, tabBarEl, chromeEl, titleEl } = entry;
|
|
3152
3160
|
if (!wrapEl || !tabBarEl || !bodyEl) return;
|
|
3153
|
-
if (this.tabLayout === "
|
|
3161
|
+
if (this.tabLayout === "header" && chromeEl && titleEl) titleEl.after(tabBarEl);
|
|
3162
|
+
else if (this.tabLayout === "top") wrapEl.insertBefore(tabBarEl, bodyEl);
|
|
3154
3163
|
else wrapEl.appendChild(tabBarEl);
|
|
3155
3164
|
}
|
|
3156
3165
|
/** Tear down the `top` layout's component, if this leaf has one. Safe to
|
|
@@ -3173,16 +3182,42 @@ var TileRenderer = class {
|
|
|
3173
3182
|
const bar = entry.tabBarEl;
|
|
3174
3183
|
if (!bar) return;
|
|
3175
3184
|
const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
|
|
3185
|
+
const header = this.tabLayout === "header";
|
|
3176
3186
|
bar.classList.toggle("twm-leaf__tabbar--top", this.tabLayout === "top");
|
|
3177
|
-
|
|
3187
|
+
bar.classList.toggle("twm-leaf__tabbar--header", header);
|
|
3188
|
+
const isPanel = String(leaf.content?.kind || "").startsWith("panel:");
|
|
3189
|
+
const inHeader = header && !isPanel && !!leaf.content && tabs.length >= 1;
|
|
3190
|
+
entry.wrapEl?.classList.toggle("twm-leaf--tabs-in-header", inHeader);
|
|
3191
|
+
if (!inHeader && tabs.length <= 1) {
|
|
3178
3192
|
bar.classList.add("twm-leaf__tabbar--hidden");
|
|
3179
3193
|
this._disposeTabStrip(entry);
|
|
3180
3194
|
bar.innerHTML = "";
|
|
3181
3195
|
return;
|
|
3182
3196
|
}
|
|
3183
3197
|
bar.classList.remove("twm-leaf__tabbar--hidden");
|
|
3184
|
-
if (this.tabLayout === "
|
|
3185
|
-
else this.
|
|
3198
|
+
if (this.tabLayout === "bottom") this._renderBottomTabBar(leaf, entry, tabs);
|
|
3199
|
+
else this._renderTopTabBar(leaf, entry, tabs);
|
|
3200
|
+
}
|
|
3201
|
+
/** C38. The labels the `header` strip shows for `leaf`, in one string, so
|
|
3202
|
+
* a repaint can tell whether anything it draws has changed. */
|
|
3203
|
+
_headerLabels(leaf, entry) {
|
|
3204
|
+
const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
|
|
3205
|
+
const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));
|
|
3206
|
+
return tabs.map((t, i) => this._tabLabel(t, i === activeIdx, entry)).join("\n");
|
|
3207
|
+
}
|
|
3208
|
+
/** C38. A cached leaf is not re-mounted, so a tab renamed in the tree
|
|
3209
|
+
* (`TileTree.renameMatchingTabs`) reaches the strip only through here. */
|
|
3210
|
+
_refreshHeaderLabels(leaf, entry) {
|
|
3211
|
+
const labels = this._headerLabels(leaf, entry);
|
|
3212
|
+
if (entry.headerLabels === labels) return;
|
|
3213
|
+
this._renderTabBar(leaf, entry);
|
|
3214
|
+
}
|
|
3215
|
+
/** What a tab in the `top` or `header` strip is called. A tab with no
|
|
3216
|
+
* title of its own falls back, when it is the active one, to the title
|
|
3217
|
+
* its content gave at mount — the string the chrome title would have
|
|
3218
|
+
* shown, and under `header` the chrome title is hidden (C38). */
|
|
3219
|
+
_tabLabel(t, active, entry) {
|
|
3220
|
+
return t.title || active && entry?.content?.title || t.kind || "";
|
|
3186
3221
|
}
|
|
3187
3222
|
/**
|
|
3188
3223
|
* C33. THE CHROME IS THE DRAG SOURCE FOR A LEAF THAT HAS ONE TAB.
|
|
@@ -3218,12 +3253,13 @@ var TileRenderer = class {
|
|
|
3218
3253
|
const tabs = Array.isArray(leaf.tabs) ? leaf.tabs : [];
|
|
3219
3254
|
const isPanel = String(leaf.content?.kind || "").startsWith("panel:");
|
|
3220
3255
|
const vetoed = this.leafChrome(leaf.id)?.promote === false;
|
|
3221
|
-
const on = tabs.length === 1 && !isPanel && !vetoed;
|
|
3256
|
+
const on = tabs.length === 1 && !isPanel && !vetoed && this.tabLayout !== "header";
|
|
3222
3257
|
if (on) chrome.setAttribute("draggable", "true");
|
|
3223
3258
|
else chrome.removeAttribute("draggable");
|
|
3224
3259
|
if (chrome.__twmTabDragBound) return;
|
|
3225
3260
|
chrome.__twmTabDragBound = true;
|
|
3226
3261
|
chrome.addEventListener("dragstart", (ev) => {
|
|
3262
|
+
if (entry.tabBarEl?.contains(ev.target)) return;
|
|
3227
3263
|
if (ev.target?.closest?.(CHROME_NO_FLOAT)) {
|
|
3228
3264
|
ev.preventDefault();
|
|
3229
3265
|
return;
|
|
@@ -3282,11 +3318,27 @@ var TileRenderer = class {
|
|
|
3282
3318
|
);
|
|
3283
3319
|
}, true);
|
|
3284
3320
|
}
|
|
3321
|
+
if (this.tabLayout === "header" && !bar.querySelector(".twm-leaf__tab-new")) {
|
|
3322
|
+
const add = document.createElement("button");
|
|
3323
|
+
add.type = "button";
|
|
3324
|
+
add.className = "twm-leaf__tab-new";
|
|
3325
|
+
add.dataset.action = "tab-new";
|
|
3326
|
+
add.title = "New tab";
|
|
3327
|
+
add.setAttribute("aria-label", "New tab");
|
|
3328
|
+
add.innerHTML = '<span class="material-symbols-outlined">add</span>';
|
|
3329
|
+
add.addEventListener("click", (ev) => {
|
|
3330
|
+
ev.preventDefault();
|
|
3331
|
+
ev.stopPropagation();
|
|
3332
|
+
this.ctx.onLeafTabAction?.(leaf.id, "new", {});
|
|
3333
|
+
});
|
|
3334
|
+
bar.appendChild(add);
|
|
3335
|
+
}
|
|
3285
3336
|
const activeIdx = Math.max(0, Math.min(tabs.length - 1, leaf.activeTabIdx || 0));
|
|
3337
|
+
entry.headerLabels = this._headerLabels(leaf, entry);
|
|
3286
3338
|
entry.tabStrip.update(
|
|
3287
3339
|
tabs.map((t, i) => ({
|
|
3288
3340
|
filePath: _tabKey(i),
|
|
3289
|
-
label: t
|
|
3341
|
+
label: this._tabLabel(t, i === activeIdx, entry),
|
|
3290
3342
|
// The component's `fileType` picks a glyph out of a static map
|
|
3291
3343
|
// of FILE kinds. A tile's kinds are the embedder's, and the
|
|
3292
3344
|
// taxonomy already answers for them — see `_paintTopTabs`.
|
|
@@ -3299,7 +3351,7 @@ var TileRenderer = class {
|
|
|
3299
3351
|
})),
|
|
3300
3352
|
_tabKey(activeIdx)
|
|
3301
3353
|
);
|
|
3302
|
-
this._paintTopTabs(entry.tabStripHostEl, tabs);
|
|
3354
|
+
this._paintTopTabs(entry.tabStripHostEl, tabs, activeIdx, entry);
|
|
3303
3355
|
}
|
|
3304
3356
|
/**
|
|
3305
3357
|
* The two things `NotebookTabBar` derives from a vocabulary a tile does not
|
|
@@ -3318,13 +3370,13 @@ var TileRenderer = class {
|
|
|
3318
3370
|
* rendered last. The class names used here are the component's published
|
|
3319
3371
|
* contract, stated in its header comment.
|
|
3320
3372
|
*/
|
|
3321
|
-
_paintTopTabs(hostEl, tabs) {
|
|
3373
|
+
_paintTopTabs(hostEl, tabs, activeIdx = -1, entry = null) {
|
|
3322
3374
|
if (!hostEl) return;
|
|
3323
3375
|
const els = hostEl.querySelectorAll(".tab");
|
|
3324
3376
|
els.forEach((el, i) => {
|
|
3325
3377
|
const spec = tabs[i];
|
|
3326
3378
|
if (!spec) return;
|
|
3327
|
-
el.title = spec
|
|
3379
|
+
el.title = this._tabLabel(spec, i === activeIdx, entry);
|
|
3328
3380
|
const icon = spec.kind ? this.ctx?.taxonomy?.meta?.(spec.kind)?.icon : null;
|
|
3329
3381
|
const glyph = el.querySelector(".tab-icon");
|
|
3330
3382
|
if (glyph && icon) glyph.textContent = icon;
|
|
@@ -4089,6 +4141,23 @@ var WindowManager = class _WindowManager {
|
|
|
4089
4141
|
setTabLayout(layout) {
|
|
4090
4142
|
this.renderer.setTabLayout(layout);
|
|
4091
4143
|
}
|
|
4144
|
+
/** C38. Open a new tab in a tile and show it. It holds the taxonomy root —
|
|
4145
|
+
* what a split seeds its new pane with, so "a new tab" and "a new pane"
|
|
4146
|
+
* start in the same place. Defaults to the focused tile. A panel tile
|
|
4147
|
+
* grows no tabs (`TileTree.appendLeafTab`); then this does nothing and
|
|
4148
|
+
* answers -1, else the new tab's index. */
|
|
4149
|
+
newTab(leafId = null) {
|
|
4150
|
+
const tree = this._tree();
|
|
4151
|
+
const id = leafId ?? tree.focusedLeafId;
|
|
4152
|
+
const seed = this._rootLeaf();
|
|
4153
|
+
const idx = tree.appendLeafTab(id, seed.content, seed.title);
|
|
4154
|
+
if (idx < 0) return -1;
|
|
4155
|
+
tree.focus(id);
|
|
4156
|
+
this.renderer.render();
|
|
4157
|
+
this._persist();
|
|
4158
|
+
this._notifyChange("tab-new");
|
|
4159
|
+
return idx;
|
|
4160
|
+
}
|
|
4092
4161
|
/** Emit on bus + call onChange. Use this instead of the bare callback
|
|
4093
4162
|
* so other surfaces (palette, top-bar toggles, page shortcuts) can
|
|
4094
4163
|
* subscribe through the existing event system. */
|
|
@@ -6706,6 +6775,10 @@ var WindowManager = class _WindowManager {
|
|
|
6706
6775
|
this._notifyChange("twm-tab-close");
|
|
6707
6776
|
return;
|
|
6708
6777
|
}
|
|
6778
|
+
if (action === "new") {
|
|
6779
|
+
this.newTab(leafId);
|
|
6780
|
+
return;
|
|
6781
|
+
}
|
|
6709
6782
|
if (action === "move") {
|
|
6710
6783
|
tree.moveLeafTab(leafId, data.from, data.to);
|
|
6711
6784
|
this.renderer.render();
|