flexdesk 0.17.13 → 0.17.14
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/CHANGELOG.md +27 -0
- package/LICENSE +0 -0
- package/README.md +0 -0
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/canvas.js +0 -0
- package/charts.js +0 -0
- package/core.js +0 -0
- package/css/base.css +45 -3
- package/css/flexdesk.css +60 -3
- package/css/overrides.css +15 -0
- package/css/reset.css +0 -0
- package/dist/canvas.js +0 -0
- package/dist/canvas.js.map +0 -0
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/chunk-GOQLR5WR.js +0 -0
- package/dist/chunk-GOQLR5WR.js.map +0 -0
- package/dist/chunk-HNU6MM2B.js +0 -0
- package/dist/chunk-HNU6MM2B.js.map +0 -0
- package/dist/chunk-JKZLNKG6.js +0 -0
- package/dist/chunk-JKZLNKG6.js.map +0 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/chunk-N7HC3ADV.js +0 -0
- package/dist/chunk-N7HC3ADV.js.map +0 -0
- package/dist/core.js +3 -18
- package/dist/core.js.map +2 -2
- package/dist/editor.js.map +0 -0
- package/dist/flexdesk.css +60 -3
- package/dist/flow.js.map +0 -0
- package/dist/host.js +0 -0
- package/dist/host.js.map +0 -0
- package/dist/reset.css +0 -0
- package/dist/tile_registry-6WZPTUZV.js +0 -0
- package/dist/tile_registry-6WZPTUZV.js.map +0 -0
- package/dist/widgets.js +27 -18
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +195 -28
- package/dist/wm.js.map +3 -3
- package/docs/README.md +1 -1
- package/docs/best-practices.md +1 -1
- package/docs/components/command-palette.md +0 -0
- package/docs/components/conversation.md +0 -0
- package/docs/components/data-table-grid.md +0 -0
- package/docs/components/data-table.md +0 -0
- package/docs/components/editor.md +0 -0
- package/docs/components/flow-canvas-editor.md +0 -0
- package/docs/components/flow-editors.md +0 -0
- package/docs/components/flow-lane-editor.md +0 -0
- package/docs/components/flow-outline-editor.md +0 -0
- package/docs/components/forms-and-dialogs.md +0 -0
- package/docs/components/input-fields.md +0 -0
- package/docs/components/keymap.md +66 -3
- package/docs/components/layout-widgets.md +32 -0
- package/docs/components/markdown-and-outline.md +0 -0
- package/docs/components/menus.md +9 -5
- package/docs/components/notifications.md +0 -0
- package/docs/components/panels-and-desktops.md +56 -0
- package/docs/components/record-list-and-fact-table.md +0 -0
- package/docs/components/settings-view.md +0 -0
- package/docs/components/shell.md +0 -0
- package/docs/components/slide-out-panel.md +0 -0
- package/docs/components/wizard.md +0 -0
- package/docs/concepts/building-and-packaging.md +0 -0
- package/docs/concepts/domain-neutrality.md +0 -0
- package/docs/concepts/host-port.md +0 -0
- package/docs/concepts/packages-and-entry-points.md +0 -0
- package/docs/concepts/peer-dependencies.md +0 -0
- package/docs/concepts/state-guard.md +65 -2
- package/docs/concepts/styling.md +24 -6
- package/docs/quickstart.md +0 -0
- package/editor.js +0 -0
- package/flow.js +0 -0
- package/host.js +0 -0
- package/package.json +2 -2
- package/src/canvas/canvas_adapter.js +0 -0
- package/src/canvas/edge_router.js +0 -0
- package/src/canvas/graph/connector_router.js +0 -0
- package/src/canvas/graph/history_service.js +0 -0
- package/src/canvas/graph/node_platform.js +0 -0
- package/src/canvas/index.js +0 -0
- package/src/canvas/layered_layout.js +0 -0
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +0 -0
- package/src/charts/plot_config.js +0 -0
- package/src/charts/plotly_wrapper.js +0 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +0 -0
- package/src/core/settings.js +0 -0
- package/src/core/state_guard.js +0 -0
- package/src/core/state_guard_config.js +19 -18
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +0 -0
- package/src/editor/monaco_editor_factory.js +0 -0
- package/src/editor/monaco_loader.js +0 -0
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +0 -0
- package/src/flow/canvas_editor/connections.js +0 -0
- package/src/flow/canvas_editor/editor.js +0 -0
- package/src/flow/canvas_editor/index.js +0 -0
- package/src/flow/canvas_editor/layout.js +0 -0
- package/src/flow/canvas_editor/strings.js +0 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +0 -0
- package/src/flow/kit/chip_input.js +0 -0
- package/src/flow/kit/dom.js +0 -0
- package/src/flow/kit/findings.js +0 -0
- package/src/flow/kit/history.js +0 -0
- package/src/flow/kit/index.js +0 -0
- package/src/flow/kit/keys.js +0 -0
- package/src/flow/kit/logic_graph.js +0 -0
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +0 -0
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +0 -0
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +0 -0
- package/src/flow/lanes/editor.js +0 -0
- package/src/flow/lanes/index.js +0 -0
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +0 -0
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +0 -0
- package/src/flow/outline/editor.js +0 -0
- package/src/flow/outline/index.js +0 -0
- package/src/flow/outline/mapping.js +0 -0
- package/src/flow/outline/menu.js +0 -0
- package/src/flow/outline/operations.js +0 -0
- package/src/flow/outline/recognise.js +0 -0
- package/src/flow/outline/strings.js +0 -0
- package/src/help/help_modal.js +0 -0
- package/src/help/help_registry.js +0 -0
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +0 -0
- package/src/host/host.js +0 -0
- package/src/host/photino_host.js +0 -0
- package/src/host/photino_host.md +0 -0
- package/src/host/pywebview_host.js +0 -0
- package/src/tiles/chart_tiles.js +0 -0
- package/src/tiles/config_schema.js +0 -0
- package/src/tiles/figure_tile.js +0 -0
- package/src/tiles/layout_persistence.js +0 -0
- package/src/tiles/list_tiles.js +0 -0
- package/src/tiles/scope_picker.js +0 -0
- package/src/tiles/tile_config_form.js +0 -0
- package/src/tiles/tile_grid.js +0 -0
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +0 -0
- package/src/tiling/command_palette.js +0 -0
- package/src/tiling/content_registry.js +0 -0
- package/src/tiling/desktops.js +79 -16
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +70 -2
- package/src/tiling/kind_taxonomy.js +0 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +0 -0
- package/src/tiling/page_factory.js +0 -0
- package/src/tiling/panel_availability.js +0 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +9 -2
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +0 -0
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +63 -5
- package/src/tiling/wm.js +59 -6
- package/src/tiling/zoom.js +0 -0
- package/src/ui/base/component_base.js +0 -0
- package/src/ui/base/controller_base.js +0 -0
- package/src/ui/base/page_base.js +0 -0
- package/src/ui/components/about_dialog.js +0 -0
- package/src/ui/components/alert_log.js +0 -0
- package/src/ui/components/alerts_pane.js +0 -0
- package/src/ui/components/attribute_list_editor.js +0 -0
- package/src/ui/components/autocomplete_field.js +0 -0
- package/src/ui/components/cell_editor_host.js +0 -0
- package/src/ui/components/cell_editors.js +0 -0
- package/src/ui/components/cell_grid.js +0 -0
- package/src/ui/components/cell_paste.js +0 -0
- package/src/ui/components/cell_selection.js +0 -0
- package/src/ui/components/choice_controls.js +0 -0
- package/src/ui/components/color_field.js +0 -0
- package/src/ui/components/color_list_field.js +0 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +0 -0
- package/src/ui/components/column_order.js +0 -0
- package/src/ui/components/computing_status_window.js +0 -0
- package/src/ui/components/confirm_dialog.js +0 -0
- package/src/ui/components/conversation_view.js +0 -0
- package/src/ui/components/crosstab.js +0 -0
- package/src/ui/components/detail_header.js +0 -0
- package/src/ui/components/document_outline.js +0 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/fact_table.js +0 -0
- package/src/ui/components/fill_handle.js +0 -0
- package/src/ui/components/gallery_picker.js +0 -0
- package/src/ui/components/header_actions.js +0 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +0 -0
- package/src/ui/components/menu_bar.js +0 -0
- package/src/ui/components/modal.js +0 -0
- package/src/ui/components/notification_history.js +0 -0
- package/src/ui/components/progress.js +0 -0
- package/src/ui/components/record_list.js +0 -0
- package/src/ui/components/row_activation.js +0 -0
- package/src/ui/components/section_stack.js +0 -0
- package/src/ui/components/series.js +0 -0
- package/src/ui/components/settings_view.js +0 -0
- package/src/ui/components/slide_out_panel.js +0 -0
- package/src/ui/components/slider_field.js +0 -0
- package/src/ui/components/sortable_list.js +0 -0
- package/src/ui/components/staged_edits.js +0 -0
- package/src/ui/components/staged_history.js +0 -0
- package/src/ui/components/table_filter_chips.js +0 -0
- package/src/ui/components/table_state_store.js +0 -0
- package/src/ui/components/table_structure.js +0 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +0 -0
- package/src/ui/components/tag_input.js +0 -0
- package/src/ui/components/toast.js +0 -0
- package/src/ui/components/toggle_field.js +0 -0
- package/src/ui/components/tree_view.js +41 -18
- package/src/ui/components/tsv.js +0 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +0 -0
- package/src/ui/controllers/window_chrome_controller.js +0 -0
- package/src/ui/notification_center.js +0 -0
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +0 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +0 -0
- package/src/ui/utils/raf_resize_observer.js +0 -0
- package/src/ui/utils/scroll_lock.js +0 -0
- package/src/ui/utils/strip_scroll.js +0 -0
- package/src/ui/utils/tooltip_service.js +0 -0
- package/tiles.js +0 -0
- package/widgets.js +0 -0
- package/wm.js +0 -0
package/dist/wm.js
CHANGED
|
@@ -342,6 +342,17 @@ function _compose(mods, key) {
|
|
|
342
342
|
const used = MODIFIER_ORDER.filter((m) => mods.has(m) && (m !== "Shift" || _shiftCounts(key)));
|
|
343
343
|
return [...used, k].join("+");
|
|
344
344
|
}
|
|
345
|
+
var TEXT_FIELDS = 'input, textarea, select, [contenteditable="true"]';
|
|
346
|
+
function _altTypes() {
|
|
347
|
+
const nav = globalThis.navigator;
|
|
348
|
+
return /^(mac|iphone|ipad|ipod)/i.test(nav?.userAgentData?.platform || nav?.platform || "");
|
|
349
|
+
}
|
|
350
|
+
function _digitOfEvent(e) {
|
|
351
|
+
const typesCharacter = e.getModifierState?.("AltGraph") || e.ctrlKey && e.altKey || _altTypes() && !!e.target?.closest?.(TEXT_FIELDS);
|
|
352
|
+
const byPosition = typesCharacter ? null : /^Digit([1-9])$/.exec(e.code ?? "");
|
|
353
|
+
if (byPosition) return byPosition[1];
|
|
354
|
+
return /^[1-9]$/.test(e.key) ? e.key : null;
|
|
355
|
+
}
|
|
345
356
|
function _chordOfEvent(e) {
|
|
346
357
|
const key = e.key;
|
|
347
358
|
if (!key || key === "Control" || key === "Shift" || key === "Alt" || key === "Meta") return null;
|
|
@@ -363,7 +374,8 @@ function _chordOfEvent(e) {
|
|
|
363
374
|
return null;
|
|
364
375
|
}
|
|
365
376
|
const sh = shift ? "Shift+" : "";
|
|
366
|
-
|
|
377
|
+
const digit = _digitOfEvent(e);
|
|
378
|
+
if (digit) return `Alt+${sh}${digit}`;
|
|
367
379
|
if (lower === "h" || lower === "v") return `Alt+${sh}${lower.toUpperCase()}`;
|
|
368
380
|
if (lower === "t" || lower === "n" || lower === "w" || lower === "f") return `Alt+${lower.toUpperCase()}`;
|
|
369
381
|
if (ARROWS.includes(key)) return `Alt+${sh}${key}`;
|
|
@@ -450,7 +462,7 @@ function installKeymap({ wm, palette, navSelector = null, bindings = null, logge
|
|
|
450
462
|
if (!chord) return;
|
|
451
463
|
const b = table.get(chord);
|
|
452
464
|
if (!b || b.disabled) return;
|
|
453
|
-
if (!b.inField && e.target?.closest?.(
|
|
465
|
+
if (!b.inField && e.target?.closest?.(TEXT_FIELDS)) return;
|
|
454
466
|
if (b.run(ctx, e) !== false) e.preventDefault();
|
|
455
467
|
};
|
|
456
468
|
document.addEventListener("keydown", onKeyDown);
|
|
@@ -1301,6 +1313,23 @@ var _idCounter = 1;
|
|
|
1301
1313
|
function _newId(prefix) {
|
|
1302
1314
|
return `${prefix}-${_idCounter++}`;
|
|
1303
1315
|
}
|
|
1316
|
+
function _isRecord(v) {
|
|
1317
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1318
|
+
}
|
|
1319
|
+
function _expect(ok, path, want, got) {
|
|
1320
|
+
if (ok) return;
|
|
1321
|
+
let shown;
|
|
1322
|
+
if (got === void 0) shown = "nothing";
|
|
1323
|
+
else {
|
|
1324
|
+
try {
|
|
1325
|
+
shown = JSON.stringify(got);
|
|
1326
|
+
} catch {
|
|
1327
|
+
shown = String(got);
|
|
1328
|
+
}
|
|
1329
|
+
if (shown.length > 40) shown = `${shown.slice(0, 37)}...`;
|
|
1330
|
+
}
|
|
1331
|
+
throw new TypeError(`${path}: expected ${want}, found ${shown}`);
|
|
1332
|
+
}
|
|
1304
1333
|
function _isPanel(leaf) {
|
|
1305
1334
|
return String(leaf?.content?.kind || "").startsWith("panel:");
|
|
1306
1335
|
}
|
|
@@ -2075,11 +2104,44 @@ var TileTree = class _TileTree {
|
|
|
2075
2104
|
focusedTitle: this.focused()?.title || null
|
|
2076
2105
|
};
|
|
2077
2106
|
}
|
|
2078
|
-
|
|
2107
|
+
/** Rebuild a tree from what `serialize()` wrote. A blob with no root is an
|
|
2108
|
+
* empty tree, as it always was. Anything else is read STRICTLY: a node that
|
|
2109
|
+
* `serialize()` could not have written throws a `TypeError` naming where
|
|
2110
|
+
* (`at` is the path of the blob, for the message only) and what it wanted,
|
|
2111
|
+
* rather than building half a tree or failing later, inside the renderer,
|
|
2112
|
+
* with `node.sizes.reduce is not a function`. The caller decides what a
|
|
2113
|
+
* tree that cannot be read means; see `WindowManager.load`. */
|
|
2114
|
+
static deserialize(blob, at = "tree") {
|
|
2079
2115
|
const t = new _TileTree();
|
|
2080
2116
|
if (!blob || !blob.root) return t;
|
|
2081
|
-
const
|
|
2117
|
+
const checkTab = (tab, path) => {
|
|
2118
|
+
_expect(_isRecord(tab) && typeof tab.kind === "string", path, "a tab, { kind, props, title }", tab);
|
|
2119
|
+
};
|
|
2120
|
+
const build = (raw, path) => {
|
|
2121
|
+
_expect(_isRecord(raw), path, "a node, an object", raw);
|
|
2122
|
+
_expect(raw.kind === "leaf" || raw.kind === "split", `${path}.kind`, '"leaf" or "split"', raw.kind);
|
|
2082
2123
|
if (raw.kind === "leaf") {
|
|
2124
|
+
if (raw.content != null) checkTab(raw.content, `${path}.content`);
|
|
2125
|
+
_expect(raw.tabs == null || Array.isArray(raw.tabs), `${path}.tabs`, "an array of tabs", raw.tabs);
|
|
2126
|
+
(raw.tabs || []).forEach((tab, i) => checkTab(tab, `${path}.tabs[${i}]`));
|
|
2127
|
+
_expect(
|
|
2128
|
+
raw.activeTabIdx == null || Number.isFinite(raw.activeTabIdx),
|
|
2129
|
+
`${path}.activeTabIdx`,
|
|
2130
|
+
"a number",
|
|
2131
|
+
raw.activeTabIdx
|
|
2132
|
+
);
|
|
2133
|
+
_expect(raw.pageTabs == null || _isRecord(raw.pageTabs), `${path}.pageTabs`, "an object", raw.pageTabs);
|
|
2134
|
+
for (const [page, arc] of Object.entries(raw.pageTabs || {})) {
|
|
2135
|
+
const where = `${path}.pageTabs[${JSON.stringify(page)}]`;
|
|
2136
|
+
_expect(_isRecord(arc) && Array.isArray(arc.tabs), where, "{ tabs, activeTabIdx }", arc);
|
|
2137
|
+
arc.tabs.forEach((tab, i) => checkTab(tab, `${where}.tabs[${i}]`));
|
|
2138
|
+
_expect(
|
|
2139
|
+
arc.activeTabIdx == null || Number.isFinite(arc.activeTabIdx),
|
|
2140
|
+
`${where}.activeTabIdx`,
|
|
2141
|
+
"a number",
|
|
2142
|
+
arc.activeTabIdx
|
|
2143
|
+
);
|
|
2144
|
+
}
|
|
2083
2145
|
const leaf = makeLeaf({ content: raw.content, title: raw.title });
|
|
2084
2146
|
if (Array.isArray(raw.tabs) && raw.tabs.length > 0) {
|
|
2085
2147
|
leaf.tabs = raw.tabs.map((tab) => ({
|
|
@@ -2096,7 +2158,6 @@ var TileTree = class _TileTree {
|
|
|
2096
2158
|
if (raw.pageTabs && typeof raw.pageTabs === "object") {
|
|
2097
2159
|
leaf.pageTabs = {};
|
|
2098
2160
|
for (const [k, v] of Object.entries(raw.pageTabs)) {
|
|
2099
|
-
if (!v || !Array.isArray(v.tabs)) continue;
|
|
2100
2161
|
leaf.pageTabs[k] = {
|
|
2101
2162
|
tabs: v.tabs.map((t2) => ({
|
|
2102
2163
|
kind: t2.kind,
|
|
@@ -2109,7 +2170,20 @@ var TileTree = class _TileTree {
|
|
|
2109
2170
|
}
|
|
2110
2171
|
return leaf;
|
|
2111
2172
|
}
|
|
2112
|
-
|
|
2173
|
+
_expect(raw.dir === "h" || raw.dir === "v", `${path}.dir`, '"h" or "v"', raw.dir);
|
|
2174
|
+
_expect(
|
|
2175
|
+
Array.isArray(raw.children) && raw.children.length > 0,
|
|
2176
|
+
`${path}.children`,
|
|
2177
|
+
"a non-empty array of nodes",
|
|
2178
|
+
raw.children
|
|
2179
|
+
);
|
|
2180
|
+
_expect(
|
|
2181
|
+
Array.isArray(raw.sizes) && raw.sizes.length === raw.children.length && raw.sizes.every((n) => Number.isFinite(n) && n >= 0),
|
|
2182
|
+
`${path}.sizes`,
|
|
2183
|
+
`${raw.children.length} non-negative numbers, one per child`,
|
|
2184
|
+
raw.sizes
|
|
2185
|
+
);
|
|
2186
|
+
const children = raw.children.map((c, i) => build(c, `${path}.children[${i}]`));
|
|
2113
2187
|
const split = makeSplit({
|
|
2114
2188
|
dir: raw.dir,
|
|
2115
2189
|
children: children.map((c) => c.id),
|
|
@@ -2118,7 +2192,7 @@ var TileTree = class _TileTree {
|
|
|
2118
2192
|
children.forEach((c) => t._register(c));
|
|
2119
2193
|
return split;
|
|
2120
2194
|
};
|
|
2121
|
-
const root = build(blob.root);
|
|
2195
|
+
const root = build(blob.root, `${at}.root`);
|
|
2122
2196
|
t.setRoot(root);
|
|
2123
2197
|
return t;
|
|
2124
2198
|
}
|
|
@@ -2130,6 +2204,23 @@ var DEFAULT_PANEL_STATE = {
|
|
|
2130
2204
|
right: true,
|
|
2131
2205
|
bottom: true
|
|
2132
2206
|
};
|
|
2207
|
+
function _isRecord2(v) {
|
|
2208
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
2209
|
+
}
|
|
2210
|
+
function _expect2(ok, path, want, got) {
|
|
2211
|
+
if (ok) return;
|
|
2212
|
+
let shown;
|
|
2213
|
+
if (got === void 0) shown = "nothing";
|
|
2214
|
+
else {
|
|
2215
|
+
try {
|
|
2216
|
+
shown = JSON.stringify(got);
|
|
2217
|
+
} catch {
|
|
2218
|
+
shown = String(got);
|
|
2219
|
+
}
|
|
2220
|
+
if (shown.length > 40) shown = `${shown.slice(0, 37)}...`;
|
|
2221
|
+
}
|
|
2222
|
+
throw new TypeError(`${path}: expected ${want}, found ${shown}`);
|
|
2223
|
+
}
|
|
2133
2224
|
function _makeDesktop(label, seed, panelDefaults = DEFAULT_PANEL_STATE) {
|
|
2134
2225
|
const tree = new TileTree();
|
|
2135
2226
|
tree.setRoot(makeLeaf(seed()));
|
|
@@ -2282,23 +2373,72 @@ var DesktopManager = class _DesktopManager {
|
|
|
2282
2373
|
}))
|
|
2283
2374
|
};
|
|
2284
2375
|
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Rebuild the manager from what `serialize()` wrote. `null`/`undefined` is
|
|
2378
|
+
* nothing saved and gives the fresh manager.
|
|
2379
|
+
*
|
|
2380
|
+
* The blob is whatever the host's `state` held under `DESKTOPS_KEY`: a file
|
|
2381
|
+
* anyone can truncate or edit, which is valid JSON of any shape. So it is
|
|
2382
|
+
* read STRICTLY, and a blob that `serialize()` could not have written throws
|
|
2383
|
+
* a `TypeError` naming the path and what was expected there. It builds only
|
|
2384
|
+
* objects of its own and touches nothing its caller holds, which is what lets
|
|
2385
|
+
* `WindowManager.load` treat a throw as "nothing saved" with nothing to undo.
|
|
2386
|
+
*
|
|
2387
|
+
* Strict about SHAPE, not about every value. What the loader, the tree
|
|
2388
|
+
* operations and the renderer index, iterate or branch on has to be what they
|
|
2389
|
+
* need (containers, node and tab kinds, `dir`, `children`, `sizes`,
|
|
2390
|
+
* `activeTabIdx`). What is only drawn or coerced is taken as it is: a desktop's
|
|
2391
|
+
* `id`, `label`, `group` and `fixed`, a panel's flag, a tab's title and props,
|
|
2392
|
+
* `activeIdx`. A layout that fails on one of those would be thrown away for
|
|
2393
|
+
* nothing, and an app can put almost anything in a title.
|
|
2394
|
+
*
|
|
2395
|
+
* And strict is not hostile to an old blob. A key the loader has always given
|
|
2396
|
+
* a default may be absent: a desktop's `id`, `label` and `panels`, the keys
|
|
2397
|
+
* later releases added (`panelShares`, `group`, `fixed`), and a leaf's `tabs`
|
|
2398
|
+
* and `pageTabs`. What `serialize()` has always written and the loader cannot
|
|
2399
|
+
* do without (`desktops`, a desktop's `tree`, a split's `dir`, `children` and
|
|
2400
|
+
* `sizes`) may not.
|
|
2401
|
+
*/
|
|
2285
2402
|
static deserialize(blob, { seed, panelDefaults = null } = {}) {
|
|
2286
2403
|
const m = new _DesktopManager({ seed, panelDefaults });
|
|
2287
|
-
if (
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2404
|
+
if (blob == null) return m;
|
|
2405
|
+
_expect2(
|
|
2406
|
+
Array.isArray(blob.desktops) && blob.desktops.length > 0,
|
|
2407
|
+
"desktops",
|
|
2408
|
+
"a non-empty array of desktops",
|
|
2409
|
+
blob.desktops
|
|
2410
|
+
);
|
|
2411
|
+
m.desktops = blob.desktops.map((raw, i) => {
|
|
2412
|
+
const at = `desktops[${i}]`;
|
|
2413
|
+
_expect2(_isRecord2(raw), at, "a desktop, an object", raw);
|
|
2414
|
+
_expect2(
|
|
2415
|
+
raw.tree === null || _isRecord2(raw.tree) && _isRecord2(raw.tree.root),
|
|
2416
|
+
`${at}.tree`,
|
|
2417
|
+
"{ root }, or null for an empty desktop",
|
|
2418
|
+
raw.tree
|
|
2419
|
+
);
|
|
2420
|
+
_expect2(raw.panels == null || _isRecord2(raw.panels), `${at}.panels`, "an object", raw.panels);
|
|
2421
|
+
_expect2(
|
|
2422
|
+
raw.panelShares == null || _isRecord2(raw.panelShares),
|
|
2423
|
+
`${at}.panelShares`,
|
|
2424
|
+
"an object",
|
|
2425
|
+
raw.panelShares
|
|
2426
|
+
);
|
|
2427
|
+
return {
|
|
2428
|
+
id: raw.id || `desk-${Math.random().toString(36).slice(2, 8)}`,
|
|
2429
|
+
label: raw.label || "?",
|
|
2430
|
+
tree: raw.tree ? TileTree.deserialize(raw.tree, `${at}.tree`) : new TileTree(),
|
|
2431
|
+
windows: [],
|
|
2432
|
+
// A RESTORED desktop's own answer wins over the default: the user
|
|
2433
|
+
// closed that panel, and re-opening it on every reload is the bug
|
|
2434
|
+
// this merge order avoids. The default only fills a key the stored
|
|
2435
|
+
// blob predates.
|
|
2436
|
+
panels: { ...m.panelDefaults, ...raw.panels || {} },
|
|
2437
|
+
panelShares: { ...raw.panelShares || {} },
|
|
2438
|
+
group: raw.group || null,
|
|
2439
|
+
fixed: !!raw.fixed
|
|
2440
|
+
};
|
|
2441
|
+
});
|
|
2302
2442
|
for (const d of m.desktops) {
|
|
2303
2443
|
if (!d.tree.rootId) d.tree.setRoot(makeLeaf(seed()));
|
|
2304
2444
|
}
|
|
@@ -5131,6 +5271,7 @@ var WindowManager = class _WindowManager {
|
|
|
5131
5271
|
taxonomy,
|
|
5132
5272
|
events,
|
|
5133
5273
|
content,
|
|
5274
|
+
logger = null,
|
|
5134
5275
|
panelDefaults = null,
|
|
5135
5276
|
snapPromotion = false,
|
|
5136
5277
|
promoteInPlace = false,
|
|
@@ -5151,6 +5292,7 @@ var WindowManager = class _WindowManager {
|
|
|
5151
5292
|
this.events = events || {};
|
|
5152
5293
|
this.ctx = ctx || {};
|
|
5153
5294
|
this.eventBus = eventBus || null;
|
|
5295
|
+
this.logger = logger || null;
|
|
5154
5296
|
this.onChange = onChange || (() => {
|
|
5155
5297
|
});
|
|
5156
5298
|
this._panelOverrides = {};
|
|
@@ -5287,12 +5429,20 @@ var WindowManager = class _WindowManager {
|
|
|
5287
5429
|
// ── Persistence ─────────────────────────────────────────────────
|
|
5288
5430
|
async load() {
|
|
5289
5431
|
const blob = await loadDesktops(this.host?.state);
|
|
5290
|
-
if (blob) {
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5432
|
+
if (blob != null) {
|
|
5433
|
+
let restored = null;
|
|
5434
|
+
try {
|
|
5435
|
+
restored = DesktopManager.deserialize(blob, {
|
|
5436
|
+
seed: this._rootLeaf,
|
|
5437
|
+
panelDefaults: this.panelDefaults
|
|
5438
|
+
});
|
|
5439
|
+
} catch (err) {
|
|
5440
|
+
(this.logger ?? console).warn?.(`[wm] bad layout for state key ${DESKTOPS_KEY} (opening the default layout): ${err?.message ?? err}`);
|
|
5441
|
+
}
|
|
5442
|
+
if (restored) {
|
|
5443
|
+
this.desktops = restored;
|
|
5444
|
+
this.renderer.tree = this.desktops.active().tree;
|
|
5445
|
+
}
|
|
5296
5446
|
}
|
|
5297
5447
|
for (const d of this.desktops.desktops) {
|
|
5298
5448
|
for (const leaf of d.tree.leaves()) {
|
|
@@ -8299,13 +8449,28 @@ var WindowManager = class _WindowManager {
|
|
|
8299
8449
|
this._notifyChange();
|
|
8300
8450
|
return true;
|
|
8301
8451
|
}
|
|
8452
|
+
/**
|
|
8453
|
+
* Move the focused tile, with every tab it holds, to desktop `idx` (from 0).
|
|
8454
|
+
* A refused move returns `undefined` and changes nothing; the callers (the
|
|
8455
|
+
* keymap's `toDesktop:N`, the tile menu's *Move to desktop*) read no result.
|
|
8456
|
+
*
|
|
8457
|
+
* The tile menu (`shell.js`, `_tileContextMenu`) never offers a panel, a
|
|
8458
|
+
* fixed desktop or the desktop you are on, and this method refuses all
|
|
8459
|
+
* three itself, because the menu is one door and Alt+Shift+N is another.
|
|
8460
|
+
* A fixed desktop it always refused; a panel and the desktop you are on it
|
|
8461
|
+
* did not, and the move to the desktop you are on destroys the tile: source
|
|
8462
|
+
* and target are one tree, so the tabs are appended to its primary tile and
|
|
8463
|
+
* then the focused tile is closed. A stray Shift on Alt+N reaches that.
|
|
8464
|
+
*/
|
|
8302
8465
|
moveFocusedToDesktop(idx) {
|
|
8303
8466
|
const tree = this._tree();
|
|
8304
8467
|
const focused = tree.focused();
|
|
8305
8468
|
if (!focused || !focused.content) return;
|
|
8469
|
+
if (String(focused.content.kind || "").startsWith("panel:")) return;
|
|
8306
8470
|
const closeChrome = this.renderer?.leafChrome?.(focused.id)?.close;
|
|
8307
8471
|
if (closeChrome === false || closeChrome?.disabled === true) return;
|
|
8308
8472
|
if (this.desktops.desktops[idx]?.fixed) return;
|
|
8473
|
+
if (idx === this.desktops.activeIdx) return;
|
|
8309
8474
|
this.desktops.ensureCount(idx + 1);
|
|
8310
8475
|
const target = this.desktops.desktops[idx];
|
|
8311
8476
|
const tabs = Array.isArray(focused.tabs) && focused.tabs.length ? focused.tabs : [{ kind: focused.content.kind, props: focused.content.props, title: focused.title }];
|
|
@@ -9396,6 +9561,7 @@ async function createShell({
|
|
|
9396
9561
|
host,
|
|
9397
9562
|
taxonomy,
|
|
9398
9563
|
events,
|
|
9564
|
+
logger,
|
|
9399
9565
|
panelDefaults: panels,
|
|
9400
9566
|
snapPromotion,
|
|
9401
9567
|
promoteInPlace,
|
|
@@ -9837,6 +10003,7 @@ function _tileContextMenu(wm, leafId, x, y) {
|
|
|
9837
10003
|
}
|
|
9838
10004
|
export {
|
|
9839
10005
|
BADGE_LEVELS,
|
|
10006
|
+
DESKTOPS_KEY,
|
|
9840
10007
|
DesktopManager,
|
|
9841
10008
|
KEYBOARD_SHORTCUTS_TOPIC_ID,
|
|
9842
10009
|
KEYMAP_SHORTCUTS,
|