flexdesk 0.17.13 → 0.17.15
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 +43 -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 +58 -7
- package/css/flexdesk.css +90 -8
- package/css/overrides.css +21 -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-IG74S3ZH.js → chunk-AD7FIA3Z.js} +2 -2
- 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/{chunk-ZMH3OC7H.js → chunk-OEG6NPAN.js} +3 -1
- package/dist/chunk-OEG6NPAN.js.map +7 -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 +90 -8
- package/dist/flow.js +1 -1
- 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/tiles.js +2 -2
- package/dist/widgets.js +29 -20
- package/dist/widgets.js.map +2 -2
- package/dist/wm.js +205 -35
- 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 +19 -2
- 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 +15 -0
- package/docs/components/slide-out-panel.md +15 -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 +12 -4
- 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 +11 -2
- 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/chunk-ZMH3OC7H.js.map +0 -7
- /package/dist/{chunk-IG74S3ZH.js.map → chunk-AD7FIA3Z.js.map} +0 -0
package/dist/wm.js
CHANGED
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
} from "./chunk-REJ5HLIM.js";
|
|
9
9
|
import {
|
|
10
10
|
showContextMenu
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-AD7FIA3Z.js";
|
|
12
12
|
import {
|
|
13
13
|
openForm
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-OEG6NPAN.js";
|
|
15
15
|
import {
|
|
16
16
|
ManagedWindow,
|
|
17
17
|
isRowActivation
|
|
@@ -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
|
}
|
|
@@ -3331,7 +3471,11 @@ function mountTileBreadcrumb(kind, props, ctx) {
|
|
|
3331
3471
|
const rootCrumb = ctx?.rootCrumb || null;
|
|
3332
3472
|
if (!taxonomy) {
|
|
3333
3473
|
console.error("[breadcrumb] no taxonomy in ctx \u2014 cannot render");
|
|
3334
|
-
|
|
3474
|
+
const el = document.createElement("nav");
|
|
3475
|
+
el.className = "twm-topbar-breadcrumb twm-tile-breadcrumb";
|
|
3476
|
+
el.hidden = true;
|
|
3477
|
+
return { el, refresh: () => {
|
|
3478
|
+
}, destroy: () => {
|
|
3335
3479
|
} };
|
|
3336
3480
|
}
|
|
3337
3481
|
const root = document.createElement("nav");
|
|
@@ -3365,10 +3509,9 @@ function mountTileBreadcrumb(kind, props, ctx) {
|
|
|
3365
3509
|
}
|
|
3366
3510
|
};
|
|
3367
3511
|
const render = () => {
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
);
|
|
3512
|
+
const segments = _segments(kind, props, taxonomy, rootCrumb, rootLabel, navigate, trailOf());
|
|
3513
|
+
_renderInto(root, segments);
|
|
3514
|
+
root.hidden = segments.length < 2;
|
|
3372
3515
|
};
|
|
3373
3516
|
render();
|
|
3374
3517
|
let unsubscribe = null;
|
|
@@ -5131,6 +5274,7 @@ var WindowManager = class _WindowManager {
|
|
|
5131
5274
|
taxonomy,
|
|
5132
5275
|
events,
|
|
5133
5276
|
content,
|
|
5277
|
+
logger = null,
|
|
5134
5278
|
panelDefaults = null,
|
|
5135
5279
|
snapPromotion = false,
|
|
5136
5280
|
promoteInPlace = false,
|
|
@@ -5151,6 +5295,7 @@ var WindowManager = class _WindowManager {
|
|
|
5151
5295
|
this.events = events || {};
|
|
5152
5296
|
this.ctx = ctx || {};
|
|
5153
5297
|
this.eventBus = eventBus || null;
|
|
5298
|
+
this.logger = logger || null;
|
|
5154
5299
|
this.onChange = onChange || (() => {
|
|
5155
5300
|
});
|
|
5156
5301
|
this._panelOverrides = {};
|
|
@@ -5287,12 +5432,20 @@ var WindowManager = class _WindowManager {
|
|
|
5287
5432
|
// ── Persistence ─────────────────────────────────────────────────
|
|
5288
5433
|
async load() {
|
|
5289
5434
|
const blob = await loadDesktops(this.host?.state);
|
|
5290
|
-
if (blob) {
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5435
|
+
if (blob != null) {
|
|
5436
|
+
let restored = null;
|
|
5437
|
+
try {
|
|
5438
|
+
restored = DesktopManager.deserialize(blob, {
|
|
5439
|
+
seed: this._rootLeaf,
|
|
5440
|
+
panelDefaults: this.panelDefaults
|
|
5441
|
+
});
|
|
5442
|
+
} catch (err) {
|
|
5443
|
+
(this.logger ?? console).warn?.(`[wm] bad layout for state key ${DESKTOPS_KEY} (opening the default layout): ${err?.message ?? err}`);
|
|
5444
|
+
}
|
|
5445
|
+
if (restored) {
|
|
5446
|
+
this.desktops = restored;
|
|
5447
|
+
this.renderer.tree = this.desktops.active().tree;
|
|
5448
|
+
}
|
|
5296
5449
|
}
|
|
5297
5450
|
for (const d of this.desktops.desktops) {
|
|
5298
5451
|
for (const leaf of d.tree.leaves()) {
|
|
@@ -8299,13 +8452,28 @@ var WindowManager = class _WindowManager {
|
|
|
8299
8452
|
this._notifyChange();
|
|
8300
8453
|
return true;
|
|
8301
8454
|
}
|
|
8455
|
+
/**
|
|
8456
|
+
* Move the focused tile, with every tab it holds, to desktop `idx` (from 0).
|
|
8457
|
+
* A refused move returns `undefined` and changes nothing; the callers (the
|
|
8458
|
+
* keymap's `toDesktop:N`, the tile menu's *Move to desktop*) read no result.
|
|
8459
|
+
*
|
|
8460
|
+
* The tile menu (`shell.js`, `_tileContextMenu`) never offers a panel, a
|
|
8461
|
+
* fixed desktop or the desktop you are on, and this method refuses all
|
|
8462
|
+
* three itself, because the menu is one door and Alt+Shift+N is another.
|
|
8463
|
+
* A fixed desktop it always refused; a panel and the desktop you are on it
|
|
8464
|
+
* did not, and the move to the desktop you are on destroys the tile: source
|
|
8465
|
+
* and target are one tree, so the tabs are appended to its primary tile and
|
|
8466
|
+
* then the focused tile is closed. A stray Shift on Alt+N reaches that.
|
|
8467
|
+
*/
|
|
8302
8468
|
moveFocusedToDesktop(idx) {
|
|
8303
8469
|
const tree = this._tree();
|
|
8304
8470
|
const focused = tree.focused();
|
|
8305
8471
|
if (!focused || !focused.content) return;
|
|
8472
|
+
if (String(focused.content.kind || "").startsWith("panel:")) return;
|
|
8306
8473
|
const closeChrome = this.renderer?.leafChrome?.(focused.id)?.close;
|
|
8307
8474
|
if (closeChrome === false || closeChrome?.disabled === true) return;
|
|
8308
8475
|
if (this.desktops.desktops[idx]?.fixed) return;
|
|
8476
|
+
if (idx === this.desktops.activeIdx) return;
|
|
8309
8477
|
this.desktops.ensureCount(idx + 1);
|
|
8310
8478
|
const target = this.desktops.desktops[idx];
|
|
8311
8479
|
const tabs = Array.isArray(focused.tabs) && focused.tabs.length ? focused.tabs : [{ kind: focused.content.kind, props: focused.content.props, title: focused.title }];
|
|
@@ -9396,6 +9564,7 @@ async function createShell({
|
|
|
9396
9564
|
host,
|
|
9397
9565
|
taxonomy,
|
|
9398
9566
|
events,
|
|
9567
|
+
logger,
|
|
9399
9568
|
panelDefaults: panels,
|
|
9400
9569
|
snapPromotion,
|
|
9401
9570
|
promoteInPlace,
|
|
@@ -9837,6 +10006,7 @@ function _tileContextMenu(wm, leafId, x, y) {
|
|
|
9837
10006
|
}
|
|
9838
10007
|
export {
|
|
9839
10008
|
BADGE_LEVELS,
|
|
10009
|
+
DESKTOPS_KEY,
|
|
9840
10010
|
DesktopManager,
|
|
9841
10011
|
KEYBOARD_SHORTCUTS_TOPIC_ID,
|
|
9842
10012
|
KEYMAP_SHORTCUTS,
|