flexdesk 0.15.1 → 0.16.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/LICENSE +0 -0
- package/README.md +26 -0
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/css/base.css +0 -0
- package/css/flexdesk.css +0 -0
- package/css/overrides.css +0 -0
- package/css/reset.css +0 -0
- package/css/tokens.css +0 -0
- package/dist/canvas.js +0 -0
- package/dist/canvas.js.map +0 -0
- package/dist/charts.js +0 -0
- package/dist/charts.js.map +0 -0
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-6JSOVNID.js +0 -0
- package/dist/chunk-6JSOVNID.js.map +0 -0
- package/dist/chunk-CWPMLX67.js +0 -0
- package/dist/chunk-CWPMLX67.js.map +0 -0
- package/dist/chunk-FBGP5KN5.js +0 -0
- package/dist/chunk-FBGP5KN5.js.map +0 -0
- package/dist/chunk-FL5KFNQH.js +0 -0
- package/dist/chunk-FL5KFNQH.js.map +0 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/chunk-NLTOY57M.js +0 -0
- package/dist/chunk-NLTOY57M.js.map +0 -0
- package/dist/chunk-NNFTETK3.js +0 -0
- package/dist/chunk-NNFTETK3.js.map +0 -0
- package/dist/chunk-O5OHMWBB.js +0 -0
- package/dist/chunk-O5OHMWBB.js.map +0 -0
- package/dist/chunk-RQM5Z2MU.js +0 -0
- package/dist/chunk-RQM5Z2MU.js.map +0 -0
- package/dist/chunk-SN4LSOAR.js +0 -0
- package/dist/chunk-SN4LSOAR.js.map +0 -0
- package/dist/chunk-WVFGV5FT.js +0 -0
- package/dist/chunk-WVFGV5FT.js.map +0 -0
- package/dist/core.js +0 -0
- package/dist/core.js.map +0 -0
- package/dist/editor.js +0 -0
- package/dist/editor.js.map +0 -0
- package/dist/flexdesk.css +0 -0
- package/dist/flow.js +0 -0
- package/dist/flow.js.map +0 -0
- package/dist/host.js +80 -1
- package/dist/host.js.map +3 -3
- 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 +0 -0
- package/dist/tiles.js.map +0 -0
- package/dist/tokens.css +0 -0
- package/dist/widgets.js +62 -0
- package/dist/widgets.js.map +3 -3
- package/dist/wm.js +0 -0
- package/dist/wm.js.map +0 -0
- package/package.json +1 -1
- 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/charts/chart_types.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/plot_popout_window.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 +0 -0
- 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_tab_bar.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/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 +94 -0
- package/src/host/host.js +4 -1
- package/src/host/photino_host.js +7 -0
- package/src/host/photino_host.md +18 -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/layout_persistence.js +0 -0
- package/src/tiles/tile_base.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 +0 -0
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +0 -0
- 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_keys.js +0 -0
- package/src/tiling/shell.js +0 -0
- 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 +0 -0
- package/src/tiling/wm.js +0 -0
- 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/action_dropdown.js +0 -0
- package/src/ui/components/alert_log.js +0 -0
- package/src/ui/components/alerts_pane.js +16 -0
- package/src/ui/components/attribute_list_editor.js +0 -0
- package/src/ui/components/autocomplete_field.js +0 -0
- package/src/ui/components/column_fit.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/context_menu.js +0 -0
- package/src/ui/components/data_table.js +0 -0
- package/src/ui/components/detail_header.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/gallery_picker.js +0 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/managed_window.js +0 -0
- package/src/ui/components/modal.js +0 -0
- package/src/ui/components/notification_history.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/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/table_filter_chips.js +0 -0
- package/src/ui/components/table_state_store.js +0 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/toast.js +0 -0
- package/src/ui/components/tree_view.js +0 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/controllers/panel_state_machine.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/native_context_menu.js +61 -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/tooltip_service.js +0 -0
package/dist/wm.js
CHANGED
|
File without changes
|
package/dist/wm.js.map
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/canvas/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/charts/downsample.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/core/event_bus.js
CHANGED
|
File without changes
|
package/src/core/logging.js
CHANGED
|
File without changes
|
package/src/core/settings.js
CHANGED
|
File without changes
|
package/src/core/state_guard.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/flow/kit/dom.js
CHANGED
|
File without changes
|
package/src/flow/kit/findings.js
CHANGED
|
File without changes
|
package/src/flow/kit/history.js
CHANGED
|
File without changes
|
package/src/flow/kit/index.js
CHANGED
|
File without changes
|
package/src/flow/kit/keys.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/flow/kit/strings.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/flow/kit/widgets.js
CHANGED
|
File without changes
|
package/src/flow/lanes/editor.js
CHANGED
|
File without changes
|
package/src/flow/lanes/index.js
CHANGED
|
File without changes
|
package/src/flow/lanes/layout.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/flow/outline/menu.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/help/help_modal.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* external_links.js — links that leave the app open in the system browser.
|
|
3
|
+
*
|
|
4
|
+
* In a browser tab, `target="_blank"` and `window.open()` open a new tab, which
|
|
5
|
+
* is what everyone expects. In a native window (Photino, a webview) they open
|
|
6
|
+
* either nothing or a bare popup with no address bar and no way back. The host
|
|
7
|
+
* knows how to hand a URL to the OS (`host.shell.openExternal`); this routes
|
|
8
|
+
* the page's outbound links there.
|
|
9
|
+
*
|
|
10
|
+
* Without a `shell` capability it changes nothing, so an app can call it
|
|
11
|
+
* unconditionally at boot: in a browser tab it is a no-op.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const SCHEMES = new Set(['http:', 'https:', 'mailto:']);
|
|
15
|
+
|
|
16
|
+
/** The URL as an absolute URL when it is one we would send out, else null. */
|
|
17
|
+
function outbound(href, base) {
|
|
18
|
+
let url;
|
|
19
|
+
try { url = new URL(String(href), base); } catch { return null; }
|
|
20
|
+
return SCHEMES.has(url.protocol) ? url : null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Send external link clicks and `window.open` calls to `host.shell.openExternal`.
|
|
25
|
+
*
|
|
26
|
+
* A link goes out when it is http(s) or mailto AND it has `target="_blank"`,
|
|
27
|
+
* points to another origin, or is mailto. A same-origin link without a target
|
|
28
|
+
* is the app's own navigation and is left alone; a same-origin `_blank` goes
|
|
29
|
+
* out too, because in a native window the alternative is a bare popup. A
|
|
30
|
+
* handler that already called `preventDefault()` on the click keeps it.
|
|
31
|
+
*
|
|
32
|
+
* When `openExternal` resolves false (the native side refused it), the original
|
|
33
|
+
* `window.open` runs instead, so the click still does something.
|
|
34
|
+
*
|
|
35
|
+
* @param {object} opts
|
|
36
|
+
* @param {object} opts.host a Host; does nothing without `host.shell`
|
|
37
|
+
* @param {EventTarget} [opts.root=document] where clicks are listened for
|
|
38
|
+
* @param {Window} [opts.win=window] whose `open` is wrapped
|
|
39
|
+
* @returns {{ dispose: () => void }}
|
|
40
|
+
*/
|
|
41
|
+
export function routeExternalLinks({ host, root, win } = {}) {
|
|
42
|
+
const w = win ?? (typeof window !== 'undefined' ? window : null);
|
|
43
|
+
const r = root ?? (typeof document !== 'undefined' ? document : null);
|
|
44
|
+
const shell = host?.shell;
|
|
45
|
+
if (!shell || typeof shell.openExternal !== 'function' || !w || !r) return { dispose() {} };
|
|
46
|
+
|
|
47
|
+
const base = () => w.location?.href;
|
|
48
|
+
const origin = () => { try { return new URL(base()).origin; } catch { return null; } };
|
|
49
|
+
const nativeOpen = w.open;
|
|
50
|
+
const fallback = (href) => { try { nativeOpen?.call(w, href, '_blank', 'noopener'); } catch { /* blocked */ } };
|
|
51
|
+
const send = (href) => {
|
|
52
|
+
Promise.resolve()
|
|
53
|
+
.then(() => shell.openExternal(href))
|
|
54
|
+
.then((opened) => { if (opened !== true) fallback(href); }, () => fallback(href));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const onClick = (e) => {
|
|
58
|
+
if (e.defaultPrevented) return;
|
|
59
|
+
// Left click, or a middle click (auxclick): both open a new window.
|
|
60
|
+
if (e.type === 'click' ? e.button !== 0 : e.button !== 1) return;
|
|
61
|
+
const a = e.target?.closest?.('a[href]');
|
|
62
|
+
if (!a || a.hasAttribute('download')) return;
|
|
63
|
+
const url = outbound(a.getAttribute('href'), base());
|
|
64
|
+
if (!url) return;
|
|
65
|
+
const leaves = url.protocol === 'mailto:'
|
|
66
|
+
|| (a.getAttribute('target') || '').toLowerCase() === '_blank'
|
|
67
|
+
|| url.origin !== origin()
|
|
68
|
+
|| e.button === 1;
|
|
69
|
+
if (!leaves) return;
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
send(url.href);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const routedOpen = function open(href, target, features) {
|
|
75
|
+
const url = href == null ? null : outbound(href, base());
|
|
76
|
+
if (url && (url.protocol === 'mailto:' || url.origin !== origin())) {
|
|
77
|
+
send(url.href);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
return nativeOpen.call(w, href, target, features);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
r.addEventListener('click', onClick);
|
|
84
|
+
r.addEventListener('auxclick', onClick);
|
|
85
|
+
w.open = routedOpen;
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
dispose() {
|
|
89
|
+
r.removeEventListener('click', onClick);
|
|
90
|
+
r.removeEventListener('auxclick', onClick);
|
|
91
|
+
if (w.open === routedOpen) w.open = nativeOpen;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
package/src/host/host.js
CHANGED
|
@@ -24,6 +24,9 @@ export const HOST_CONTRACT = Object.freeze({
|
|
|
24
24
|
}),
|
|
25
25
|
dialogs: Object.freeze({ required: Object.freeze(['saveFile']), optional: Object.freeze([]) }),
|
|
26
26
|
state: Object.freeze({ required: Object.freeze(['read', 'write']), optional: Object.freeze([]) }),
|
|
27
|
+
// `openExternal(url)` hands a web address to the OS's default browser and
|
|
28
|
+
// resolves true when it did. See external_links.js.
|
|
29
|
+
shell: Object.freeze({ required: Object.freeze(['openExternal']), optional: Object.freeze([]) }),
|
|
27
30
|
});
|
|
28
31
|
|
|
29
32
|
/**
|
|
@@ -49,7 +52,7 @@ export function validateCapability(name, cap) {
|
|
|
49
52
|
* Throws at boot on an incomplete capability — a half-wired host is a bug,
|
|
50
53
|
* and a bug at boot beats a silent degrade at click time.
|
|
51
54
|
*
|
|
52
|
-
* @param {{ window?: object, dialogs?: object, state?: object }} caps
|
|
55
|
+
* @param {{ window?: object, dialogs?: object, state?: object, shell?: object }} caps
|
|
53
56
|
* @returns {Readonly<object>} the Host
|
|
54
57
|
*/
|
|
55
58
|
export function createHost(caps = {}) {
|
package/src/host/photino_host.js
CHANGED
|
@@ -106,6 +106,7 @@ export function createPhotinoTransport({ external, timeoutMs = 10000, onEvent =
|
|
|
106
106
|
* @param {object|null} [opts.window] replace the window capability, or `null` to leave it out
|
|
107
107
|
* @param {object|null} [opts.dialogs] replace the dialogs capability, or `null` to leave it out
|
|
108
108
|
* @param {object|null} [opts.state] replace the state capability, or `null` to leave it out
|
|
109
|
+
* @param {object|null} [opts.shell] replace the shell capability, or `null` to leave it out
|
|
109
110
|
* @returns {Readonly<object>} a Host, or NULL_HOST outside a Photino window
|
|
110
111
|
*/
|
|
111
112
|
export function createPhotinoHost(opts = {}) {
|
|
@@ -182,6 +183,12 @@ export function createPhotinoHost(opts = {}) {
|
|
|
182
183
|
return r && typeof r === 'object' ? r : null;
|
|
183
184
|
},
|
|
184
185
|
},
|
|
186
|
+
shell: {
|
|
187
|
+
// True when the native side opened it in the system browser; false
|
|
188
|
+
// when it refused (a scheme it won't open, an older native side
|
|
189
|
+
// without the op): the caller falls back to window.open.
|
|
190
|
+
openExternal: async (url) => (await soft(false, 'shell', 'openExternal', String(url || ''))) === true,
|
|
191
|
+
},
|
|
185
192
|
state: {
|
|
186
193
|
async read(key) {
|
|
187
194
|
const v = await soft(null, 'state', 'read', key);
|
package/src/host/photino_host.md
CHANGED
|
@@ -40,6 +40,7 @@ host → page {"event": "window.maximizedChanged", "value": true}
|
|
|
40
40
|
| `window.startDrag` | `[]` | anything, **after mouse-up** | custom chrome only: the OS move loop (below) |
|
|
41
41
|
| `window.startResize` | `[edge]`: `n` `s` `e` `w` `ne` `nw` `se` `sw` | anything, **after mouse-up** | custom chrome only: the OS size loop (below) |
|
|
42
42
|
| `dialogs.saveFile` | `[{data, filename, kind}]` | see below | `ShowSaveFile(...)` + write |
|
|
43
|
+
| `shell.openExternal` | `[url]` | `true` when opened | the OS's default browser (below) |
|
|
43
44
|
| `state.read` | `[key]` | the stored value, or `null` | a file per key |
|
|
44
45
|
| `state.write` | `[key, value]` | `true` | a file per key |
|
|
45
46
|
|
|
@@ -149,3 +150,20 @@ window.RegisterWebMessageReceivedHandler((sender, message) =>
|
|
|
149
150
|
|
|
150
151
|
The handler runs on the window's thread; a save dialog shown from it is modal to
|
|
151
152
|
the window, which is what you want.
|
|
153
|
+
|
|
154
|
+
## Opening links in the system browser
|
|
155
|
+
|
|
156
|
+
`shell.openExternal` takes one absolute URL and opens it with the OS's default
|
|
157
|
+
handler: `Process.Start(new ProcessStartInfo(url) { UseShellExecute = true })`
|
|
158
|
+
on Windows, `xdg-open` on Linux, `open` on macOS. Answer `true` once it has
|
|
159
|
+
been handed over.
|
|
160
|
+
|
|
161
|
+
**Refuse anything but `http:`, `https:` and `mailto:`** with `ok: false`. The
|
|
162
|
+
URL comes from page content (a link in a record somebody else wrote), and a
|
|
163
|
+
`file:` path or a custom protocol handed to the shell runs whatever the OS
|
|
164
|
+
associates with it. Refused, the page falls back to `window.open`, which is
|
|
165
|
+
no worse than a native side that doesn't have the op.
|
|
166
|
+
|
|
167
|
+
The page side is `routeExternalLinks({ host })` (`external_links.js`). It
|
|
168
|
+
sends external link clicks and `window.open` calls here; without a `shell`
|
|
169
|
+
capability it changes nothing.
|
|
File without changes
|
package/src/tiles/chart_tiles.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiles/tile_base.js
CHANGED
|
File without changes
|
package/src/tiles/tile_grid.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/desktops.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/tiling/keymap.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/nav_panel.js
CHANGED
|
File without changes
|
|
File without changes
|
package/src/tiling/panel_keys.js
CHANGED
|
File without changes
|
package/src/tiling/shell.js
CHANGED
|
File without changes
|
package/src/tiling/tab_strip.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/tiling/tile_tree.js
CHANGED
|
File without changes
|
package/src/tiling/wm.js
CHANGED
|
File without changes
|
package/src/tiling/zoom.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/ui/base/page_base.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -22,12 +22,20 @@
|
|
|
22
22
|
* button is down inside the pane a repaint waits, and runs once it is up.
|
|
23
23
|
* The bar (chips, the filter, the buttons) is built once and updated in
|
|
24
24
|
* place, so typing in the filter never loses the caret either.
|
|
25
|
+
*
|
|
26
|
+
* OLDER ENTRIES LOAD AS YOU SCROLL (Tables STORY-0293): when the list is
|
|
27
|
+
* scrolled to within :data:`MORE_WITHIN_PX` of its end and the source has
|
|
28
|
+
* more, the next page is asked for — once at a time. *Load older* stays at the
|
|
29
|
+
* end, for a list too short to scroll and for the keyboard.
|
|
25
30
|
*/
|
|
26
31
|
|
|
27
32
|
import { ALERT_LEVELS, ago, dayLabel, normalizeLevel } from './alert_log.js';
|
|
28
33
|
|
|
29
34
|
const CHIPS = [['all', 'All'], ['alert', 'Alert'], ['warning', 'Warning'], ['info', 'Info']];
|
|
30
35
|
|
|
36
|
+
/** How near the end of the list a scroll asks for the next page. */
|
|
37
|
+
export const MORE_WITHIN_PX = 48;
|
|
38
|
+
|
|
31
39
|
/**
|
|
32
40
|
* @param {HTMLElement} host
|
|
33
41
|
* @param {object} opts
|
|
@@ -271,6 +279,14 @@ export function mountAlertsPane(host, {
|
|
|
271
279
|
});
|
|
272
280
|
input.addEventListener('input', () => { q = input.value; paint(); });
|
|
273
281
|
|
|
282
|
+
let loadingMore = false;
|
|
283
|
+
list.addEventListener('scroll', () => {
|
|
284
|
+
if (loadingMore || !log.hasMore()) return;
|
|
285
|
+
if (list.scrollTop + list.clientHeight < list.scrollHeight - MORE_WITHIN_PX) return;
|
|
286
|
+
loadingMore = true;
|
|
287
|
+
Promise.resolve(log.loadMore()).catch(() => {}).finally(() => { loadingMore = false; });
|
|
288
|
+
});
|
|
289
|
+
|
|
274
290
|
const unsubscribe = log.subscribe(() => paint());
|
|
275
291
|
// "5 min ago" moves on its own.
|
|
276
292
|
const timer = setInterval(paint, 60000);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* native_context_menu.js — the browser's own right-click menu, only where it helps.
|
|
3
|
+
*
|
|
4
|
+
* A FlexDesk app draws its own context menus (DataTable rows and headers, tabs,
|
|
5
|
+
* tiles, the top bar). Everywhere else a right-click shows the browser's or the
|
|
6
|
+
* webview's menu: Back, Reload, Save as, Print, Inspect. In a desktop window
|
|
7
|
+
* that makes the app look like a web page and offers moves that break it.
|
|
8
|
+
*
|
|
9
|
+
* This keeps the native menu where it is the useful one, editable text (Paste)
|
|
10
|
+
* and selected text (Copy), and prevents it everywhere nobody else claimed the
|
|
11
|
+
* right-click.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Input types that take typed text, where Paste belongs. */
|
|
15
|
+
const TEXT_INPUTS = new Set(['', 'text', 'search', 'url', 'tel', 'email', 'password', 'number',
|
|
16
|
+
'date', 'datetime-local', 'month', 'week', 'time']);
|
|
17
|
+
|
|
18
|
+
function isEditable(el) {
|
|
19
|
+
if (!el || el.nodeType !== 1) return false;
|
|
20
|
+
const tag = el.tagName;
|
|
21
|
+
if (tag === 'TEXTAREA' || tag === 'SELECT') return true;
|
|
22
|
+
if (tag === 'INPUT') return TEXT_INPUTS.has(String(el.getAttribute('type') || '').toLowerCase());
|
|
23
|
+
return !!el.closest?.('[contenteditable=""],[contenteditable="true"],[contenteditable="plaintext-only"]');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A non-empty selection that the click landed inside. */
|
|
27
|
+
function onSelection(target, doc) {
|
|
28
|
+
const sel = doc?.getSelection?.();
|
|
29
|
+
if (!sel || sel.isCollapsed || !String(sel).trim()) return false;
|
|
30
|
+
for (let i = 0; i < sel.rangeCount; i += 1) {
|
|
31
|
+
if (sel.getRangeAt(i).intersectsNode?.(target)) return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Prevent the native context menu wherever no handler claimed the right-click.
|
|
38
|
+
*
|
|
39
|
+
* Listens in the bubble phase, so every element handler runs first: one that
|
|
40
|
+
* shows its own menu calls `preventDefault()`, and this leaves it alone. A
|
|
41
|
+
* handler that stops propagation never reaches here at all.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} [opts]
|
|
44
|
+
* @param {EventTarget} [opts.root=document]
|
|
45
|
+
* @param {(e: MouseEvent) => boolean} [opts.allow] true keeps the native menu for that event
|
|
46
|
+
* @returns {{ dispose: () => void }}
|
|
47
|
+
*/
|
|
48
|
+
export function suppressNativeContextMenu({ root, allow } = {}) {
|
|
49
|
+
const r = root ?? (typeof document !== 'undefined' ? document : null);
|
|
50
|
+
if (!r) return { dispose() {} };
|
|
51
|
+
const onMenu = (e) => {
|
|
52
|
+
if (e.defaultPrevented) return;
|
|
53
|
+
const t = e.target;
|
|
54
|
+
if (isEditable(t)) return;
|
|
55
|
+
if (onSelection(t, t?.ownerDocument)) return;
|
|
56
|
+
if (allow?.(e)) return;
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
};
|
|
59
|
+
r.addEventListener('contextmenu', onMenu);
|
|
60
|
+
return { dispose() { r.removeEventListener('contextmenu', onMenu); } };
|
|
61
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|