flexdesk 0.4.5 → 0.4.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.
- package/README.md +301 -276
- package/css/base.css +57 -12
- package/css/flexdesk.css +75 -18
- package/css/overrides.css +15 -5
- package/dist/charts.js +782 -782
- package/dist/{chunk-SWB5ARKN.js → chunk-F5FYDCOA.js} +2373 -2373
- package/dist/{chunk-SWB5ARKN.js.map → chunk-F5FYDCOA.js.map} +7 -7
- package/dist/{chunk-QNQHQ24V.js → chunk-NLTOY57M.js} +72 -1
- package/dist/chunk-NLTOY57M.js.map +7 -0
- package/dist/{chunk-BIAOGX6K.js → chunk-WMGOJ3I6.js} +2628 -2096
- package/dist/chunk-WMGOJ3I6.js.map +7 -0
- package/dist/editor.js +1 -1
- package/dist/flexdesk.css +75 -18
- package/dist/tiles.js +2195 -2195
- package/dist/widgets.js +5796 -5796
- package/dist/wm.js +8074 -8015
- package/dist/wm.js.map +7 -7
- package/package.json +81 -81
- package/src/editor/notebook_tab_bar.js +89 -0
- package/src/tiling/tile_breadcrumb.js +21 -0
- package/src/tiling/tile_renderer.js +11 -0
- package/src/tiling/wm.js +3916 -3872
- package/src/ui/components/data_table.js +3294 -2694
- package/dist/chunk-BIAOGX6K.js.map +0 -7
- package/dist/chunk-QNQHQ24V.js.map +0 -7
package/package.json
CHANGED
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "flexdesk",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+ssh://git@github.com/nor-os/FlexDesk.git"
|
|
9
|
-
},
|
|
10
|
-
"homepage": "https://github.com/nor-os/FlexDesk",
|
|
11
|
-
"bugs": {
|
|
12
|
-
"url": "https://github.com/nor-os/FlexDesk/issues"
|
|
13
|
-
},
|
|
14
|
-
"type": "module",
|
|
15
|
-
"sideEffects": [
|
|
16
|
-
"*.css"
|
|
17
|
-
],
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"src",
|
|
21
|
-
"css",
|
|
22
|
-
"README.md",
|
|
23
|
-
"LICENSE",
|
|
24
|
-
"THIRD_PARTY_LICENSES.md"
|
|
25
|
-
],
|
|
26
|
-
"exports": {
|
|
27
|
-
"./core": {
|
|
28
|
-
"import": "./dist/core.js",
|
|
29
|
-
"development": "./core.js"
|
|
30
|
-
},
|
|
31
|
-
"./host": {
|
|
32
|
-
"import": "./dist/host.js",
|
|
33
|
-
"development": "./host.js"
|
|
34
|
-
},
|
|
35
|
-
"./wm": {
|
|
36
|
-
"import": "./dist/wm.js",
|
|
37
|
-
"development": "./wm.js"
|
|
38
|
-
},
|
|
39
|
-
"./widgets": {
|
|
40
|
-
"import": "./dist/widgets.js",
|
|
41
|
-
"development": "./widgets.js"
|
|
42
|
-
},
|
|
43
|
-
"./css": "./dist/flexdesk.css",
|
|
44
|
-
"./css/tokens": "./css/tokens.css",
|
|
45
|
-
"./charts": {
|
|
46
|
-
"import": "./dist/charts.js",
|
|
47
|
-
"development": "./charts.js"
|
|
48
|
-
},
|
|
49
|
-
"./tiles": {
|
|
50
|
-
"import": "./dist/tiles.js",
|
|
51
|
-
"development": "./tiles.js"
|
|
52
|
-
},
|
|
53
|
-
"./editor": {
|
|
54
|
-
"import": "./dist/editor.js",
|
|
55
|
-
"development": "./editor.js"
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"scripts": {
|
|
59
|
-
"build": "node build.mjs",
|
|
60
|
-
"check": "node build.mjs --check",
|
|
61
|
-
"test": "node tests/run.mjs"
|
|
62
|
-
},
|
|
63
|
-
"devDependencies": {
|
|
64
|
-
"esbuild": "^0.28.1"
|
|
65
|
-
},
|
|
66
|
-
"peerDependenciesMeta": {
|
|
67
|
-
"plotly.js": {
|
|
68
|
-
"optional": true
|
|
69
|
-
},
|
|
70
|
-
"monaco-editor": {
|
|
71
|
-
"optional": true
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
"keywords": [
|
|
75
|
-
"tiling",
|
|
76
|
-
"window-manager",
|
|
77
|
-
"datatable",
|
|
78
|
-
"command-palette",
|
|
79
|
-
"ui"
|
|
80
|
-
]
|
|
81
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "flexdesk",
|
|
3
|
+
"version": "0.4.7",
|
|
4
|
+
"description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+ssh://git@github.com/nor-os/FlexDesk.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/nor-os/FlexDesk",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/nor-os/FlexDesk/issues"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": [
|
|
16
|
+
"*.css"
|
|
17
|
+
],
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"css",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"THIRD_PARTY_LICENSES.md"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
"./core": {
|
|
28
|
+
"import": "./dist/core.js",
|
|
29
|
+
"development": "./core.js"
|
|
30
|
+
},
|
|
31
|
+
"./host": {
|
|
32
|
+
"import": "./dist/host.js",
|
|
33
|
+
"development": "./host.js"
|
|
34
|
+
},
|
|
35
|
+
"./wm": {
|
|
36
|
+
"import": "./dist/wm.js",
|
|
37
|
+
"development": "./wm.js"
|
|
38
|
+
},
|
|
39
|
+
"./widgets": {
|
|
40
|
+
"import": "./dist/widgets.js",
|
|
41
|
+
"development": "./widgets.js"
|
|
42
|
+
},
|
|
43
|
+
"./css": "./dist/flexdesk.css",
|
|
44
|
+
"./css/tokens": "./css/tokens.css",
|
|
45
|
+
"./charts": {
|
|
46
|
+
"import": "./dist/charts.js",
|
|
47
|
+
"development": "./charts.js"
|
|
48
|
+
},
|
|
49
|
+
"./tiles": {
|
|
50
|
+
"import": "./dist/tiles.js",
|
|
51
|
+
"development": "./tiles.js"
|
|
52
|
+
},
|
|
53
|
+
"./editor": {
|
|
54
|
+
"import": "./dist/editor.js",
|
|
55
|
+
"development": "./editor.js"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "node build.mjs",
|
|
60
|
+
"check": "node build.mjs --check",
|
|
61
|
+
"test": "node tests/run.mjs"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"esbuild": "^0.28.1"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"plotly.js": {
|
|
68
|
+
"optional": true
|
|
69
|
+
},
|
|
70
|
+
"monaco-editor": {
|
|
71
|
+
"optional": true
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"keywords": [
|
|
75
|
+
"tiling",
|
|
76
|
+
"window-manager",
|
|
77
|
+
"datatable",
|
|
78
|
+
"command-palette",
|
|
79
|
+
"ui"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
@@ -61,6 +61,8 @@ export class NotebookTabBar {
|
|
|
61
61
|
this.#container.className = 'tabs notebook-tabs';
|
|
62
62
|
this.#boundHideContextMenu = () => this.#hideContextMenu();
|
|
63
63
|
this.#installStripDnDHandlers();
|
|
64
|
+
// NOT PASSIVE: it has to be able to take the wheel from the page.
|
|
65
|
+
this.#container.addEventListener('wheel', this.#onWheel, { passive: false });
|
|
64
66
|
this.#dragReorder = new DragReorder({
|
|
65
67
|
container: this.#container,
|
|
66
68
|
itemSelector: '.tab',
|
|
@@ -118,6 +120,9 @@ export class NotebookTabBar {
|
|
|
118
120
|
dispose() {
|
|
119
121
|
this.#hideContextMenu();
|
|
120
122
|
this.#removeStripDnDHandlers();
|
|
123
|
+
this.#container?.removeEventListener('wheel', this.#onWheel);
|
|
124
|
+
if (this.#revealFrame) cancelAnimationFrame(this.#revealFrame);
|
|
125
|
+
this.#revealFrame = 0;
|
|
121
126
|
this.#dragReorder?.destroy();
|
|
122
127
|
this.#dragReorder = null;
|
|
123
128
|
this.#container = null;
|
|
@@ -161,6 +166,11 @@ export class NotebookTabBar {
|
|
|
161
166
|
#render() {
|
|
162
167
|
if (!this.#container) return;
|
|
163
168
|
|
|
169
|
+
// THE STRIP KEEPS ITS SCROLL ACROSS A REBUILD. Emptying it shrinks its
|
|
170
|
+
// scroll width to nothing, which clamps `scrollLeft` to 0 — so every
|
|
171
|
+
// render (a tab activated, a title changed) threw a strip scrolled to
|
|
172
|
+
// its tenth tab back to its first.
|
|
173
|
+
const keepLeft = this.#container.scrollLeft;
|
|
164
174
|
this.#container.innerHTML = '';
|
|
165
175
|
|
|
166
176
|
for (const tab of this.#tabs) {
|
|
@@ -222,8 +232,87 @@ export class NotebookTabBar {
|
|
|
222
232
|
}
|
|
223
233
|
// Make the freshly-rendered tabs draggable-to-reorder.
|
|
224
234
|
this.#dragReorder?.attach();
|
|
235
|
+
|
|
236
|
+
this.#container.scrollLeft = keepLeft;
|
|
237
|
+
// A tab that has just BECOME active — opened, switched to, moved here
|
|
238
|
+
// from another pane — is scrolled into the strip. Only then: a render
|
|
239
|
+
// for any other reason (a title, a dirty dot) leaves the strip where the
|
|
240
|
+
// reader put it, rather than yanking it back to the active tab.
|
|
241
|
+
const revealKey = `${this.#activeTab}\u0000${this.#tabs.length}`;
|
|
242
|
+
if (revealKey !== this.#revealedKey) {
|
|
243
|
+
this.#revealedKey = revealKey;
|
|
244
|
+
this.#revealActive(true);
|
|
245
|
+
}
|
|
225
246
|
}
|
|
226
247
|
|
|
248
|
+
// ═════════════════════════════════════════════════════════════════════════
|
|
249
|
+
// Scrolling (1.5) — the strip scrolls rather than wrapping or shrinking its
|
|
250
|
+
// tabs, and hides its scrollbar (a bar in a 26px strip eats a third of every
|
|
251
|
+
// label). So it owes a mouse user every other way in: a plain wheel, the
|
|
252
|
+
// active tab brought into view, and — in `DragReorder._autoScrollEdge` —
|
|
253
|
+
// the edges scrolling while a tab is dragged toward them.
|
|
254
|
+
// ═════════════════════════════════════════════════════════════════════════
|
|
255
|
+
|
|
256
|
+
/** @type {string|null} the active tab + count last scrolled into view */
|
|
257
|
+
#revealedKey = null;
|
|
258
|
+
|
|
259
|
+
/** @type {number} a pending retry, for a strip not yet laid out */
|
|
260
|
+
#revealFrame = 0;
|
|
261
|
+
|
|
262
|
+
/** Bring the active tab fully into the strip — and ONLY the strip. Not
|
|
263
|
+
* `scrollIntoView`, which scrolls every scrollable ancestor as well and
|
|
264
|
+
* would drag the tile, the window or the page along with the tab. */
|
|
265
|
+
#revealActive(retry = false) {
|
|
266
|
+
const el = this.#container;
|
|
267
|
+
if (!el) return;
|
|
268
|
+
const active = el.querySelector(':scope > .tab.active');
|
|
269
|
+
if (!active) return;
|
|
270
|
+
if (!el.isConnected || el.clientWidth === 0) {
|
|
271
|
+
// Mounted but not laid out yet (the renderer updates a strip before
|
|
272
|
+
// it attaches it). Once, on the next frame — not a loop.
|
|
273
|
+
if (retry && !this.#revealFrame && typeof requestAnimationFrame === 'function') {
|
|
274
|
+
this.#revealFrame = requestAnimationFrame(() => {
|
|
275
|
+
this.#revealFrame = 0;
|
|
276
|
+
this.#revealActive(false);
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const box = el.getBoundingClientRect();
|
|
282
|
+
const tab = active.getBoundingClientRect();
|
|
283
|
+
// Client pixels to the strip's own: 1 unless something above it zooms.
|
|
284
|
+
const scale = (el.offsetWidth ? box.width / el.offsetWidth : 1) || 1;
|
|
285
|
+
const left = (tab.left - box.left) / scale + el.scrollLeft;
|
|
286
|
+
const right = left + tab.width / scale;
|
|
287
|
+
if (left < el.scrollLeft) el.scrollLeft = left;
|
|
288
|
+
else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* A VERTICAL WHEEL SCROLLS THE STRIP SIDEWAYS. Chromium and Firefox scroll
|
|
293
|
+
* an element that overflows only horizontally with Shift+wheel or a
|
|
294
|
+
* touchpad, and not with the plain wheel most people have — so with the
|
|
295
|
+
* scrollbar hidden, every tab past the edge was unreachable with a mouse.
|
|
296
|
+
*
|
|
297
|
+
* The wheel is taken from the page only while the strip can actually move
|
|
298
|
+
* that way: at either end it is left alone, so a wheel over a strip that
|
|
299
|
+
* has nothing more to show still scrolls whatever is behind it. A gesture
|
|
300
|
+
* that is already horizontal (a touchpad, Shift+wheel) and Ctrl+wheel (zoom)
|
|
301
|
+
* are the browser's, untouched.
|
|
302
|
+
*/
|
|
303
|
+
#onWheel = (e) => {
|
|
304
|
+
const el = this.#container;
|
|
305
|
+
if (!el || e.ctrlKey || e.shiftKey) return;
|
|
306
|
+
if (Math.abs(e.deltaX) >= Math.abs(e.deltaY)) return;
|
|
307
|
+
const room = el.scrollWidth - el.clientWidth;
|
|
308
|
+
if (room <= 0) return;
|
|
309
|
+
const unit = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? el.clientWidth : 1;
|
|
310
|
+
const next = Math.max(0, Math.min(room, el.scrollLeft + e.deltaY * unit));
|
|
311
|
+
if (Math.abs(next - el.scrollLeft) < 0.5) return;
|
|
312
|
+
e.preventDefault();
|
|
313
|
+
el.scrollLeft = next;
|
|
314
|
+
};
|
|
315
|
+
|
|
227
316
|
// ═════════════════════════════════════════════════════════════════════════
|
|
228
317
|
// Drag and drop reordering (same-pane + cross-pane)
|
|
229
318
|
// ═════════════════════════════════════════════════════════════════════════
|
|
@@ -206,6 +206,27 @@ function _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trail)
|
|
|
206
206
|
return segs;
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
/**
|
|
210
|
+
* C37. THE SAME STRIP FOR A CONTENT'S OWN LEVELS — the markup every tile
|
|
211
|
+
* breadcrumb draws, with segments the caller routes itself.
|
|
212
|
+
*
|
|
213
|
+
* `mountTileBreadcrumb` names where the TILE is, and every one of its segments
|
|
214
|
+
* climbs through the window manager. A page with levels that are not tabs (a
|
|
215
|
+
* section's list, one record opened in its place) has a trail the taxonomy
|
|
216
|
+
* cannot know and segments the WM cannot route; it draws them here, in the same
|
|
217
|
+
* markup and so the same look, and pairs it with a `navigateBack()` from its
|
|
218
|
+
* mount so Backspace climbs the same levels (`wm.navigateBack`, step 0).
|
|
219
|
+
*
|
|
220
|
+
* `segments` is `[{label, icon?, onClick?}]`; the last one — and any with no
|
|
221
|
+
* `onClick` — is drawn as the current one, as text. Redraws `container` in full.
|
|
222
|
+
*
|
|
223
|
+
* @param {HTMLElement} container
|
|
224
|
+
* @param {Array<{label: string, icon?: string, onClick?: Function}>} segments
|
|
225
|
+
*/
|
|
226
|
+
export function renderBreadcrumb(container, segments) {
|
|
227
|
+
_renderInto(container, segments);
|
|
228
|
+
}
|
|
229
|
+
|
|
209
230
|
function _renderInto(container, segments) {
|
|
210
231
|
container.innerHTML = '';
|
|
211
232
|
if (!segments?.length) return;
|
|
@@ -839,6 +839,17 @@ export class TileRenderer {
|
|
|
839
839
|
return (leaf && leaf.kind === 'leaf') ? this._leafKindKey(leaf) : null;
|
|
840
840
|
}
|
|
841
841
|
|
|
842
|
+
/**
|
|
843
|
+
* C37. WHAT A LEAF'S CONTENT FACTORY RETURNED for the tab on screen, or
|
|
844
|
+
* `null` — so the window manager can ask the content a question about
|
|
845
|
+
* itself (`wm.navigateBack`: *"have you a level of your own to go up?"*)
|
|
846
|
+
* without reaching into the cache. Read-only; a leaf that has not been
|
|
847
|
+
* rendered yet has none.
|
|
848
|
+
*/
|
|
849
|
+
contentOf(leafId) {
|
|
850
|
+
return this._leafCache.get(leafId)?.content ?? null;
|
|
851
|
+
}
|
|
852
|
+
|
|
842
853
|
/**
|
|
843
854
|
* C34. ACCEPT A PROPS WRITE THE CONTENT MADE ABOUT ITSELF, WITHOUT
|
|
844
855
|
* REBUILDING THE TILE THAT MADE IT.
|