flexdesk 0.16.1 → 0.17.1
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 +112 -0
- package/LICENSE +0 -0
- package/README.md +2435 -42
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/canvas.js +18 -0
- package/charts.js +21 -0
- package/core.js +14 -0
- package/css/base.css +1834 -75
- package/css/flexdesk.css +3165 -159
- package/css/overrides.css +16 -0
- package/css/reset.css +0 -0
- package/css/tokens.css +0 -0
- package/dist/canvas.js +6 -239
- package/dist/canvas.js.map +3 -3
- package/dist/charts.js +707 -18
- package/dist/charts.js.map +4 -4
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-4A7SJC3T.js +503 -0
- package/dist/chunk-4A7SJC3T.js.map +7 -0
- package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
- package/dist/chunk-CVMCZZX2.js.map +7 -0
- package/dist/chunk-D3COLZZ2.js +1311 -0
- package/dist/chunk-D3COLZZ2.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
- package/dist/chunk-GOQLR5WR.js.map +7 -0
- package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
- package/dist/chunk-HNU6MM2B.js.map +7 -0
- package/dist/chunk-HYJYYEPK.js +375 -0
- package/dist/chunk-HYJYYEPK.js.map +7 -0
- package/dist/chunk-JKZLNKG6.js +2041 -0
- package/dist/chunk-JKZLNKG6.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/{chunk-FBGP5KN5.js → chunk-LWV23YVE.js} +5073 -532
- package/dist/chunk-LWV23YVE.js.map +7 -0
- package/dist/chunk-N7HC3ADV.js +30 -0
- package/dist/chunk-N7HC3ADV.js.map +7 -0
- package/dist/{chunk-RQM5Z2MU.js → chunk-RMTRWWEO.js} +633 -311
- package/dist/chunk-RMTRWWEO.js.map +7 -0
- package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
- package/dist/chunk-UURZ37SD.js.map +7 -0
- package/dist/{chunk-B5Q53LAJ.js → chunk-UWO43BZU.js} +2 -2
- package/dist/chunk-UWO43BZU.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +0 -0
- package/dist/chunk-WVFGV5FT.js.map +0 -0
- package/dist/core.js +170 -118
- package/dist/core.js.map +2 -2
- package/dist/editor.js +156 -70
- package/dist/editor.js.map +2 -2
- package/dist/flexdesk.css +3165 -159
- package/dist/flow.js +2789 -306
- package/dist/flow.js.map +4 -4
- package/dist/host.js +109 -3
- 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 +657 -234
- package/dist/tiles.js.map +4 -4
- package/dist/tokens.css +0 -0
- package/dist/widgets.js +3290 -2124
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +1227 -347
- package/dist/wm.js.map +4 -4
- package/editor.js +20 -0
- package/flow.js +21 -0
- package/host.js +14 -0
- package/package.json +12 -1
- package/src/canvas/canvas_adapter.js +5 -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 +1 -0
- package/src/canvas/layered_layout.js +244 -0
- package/src/charts/chart_types.js +0 -0
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +2 -2
- package/src/charts/plot_config.js +53 -29
- package/src/charts/plot_config_editor.js +627 -0
- package/src/charts/plot_grid.js +449 -0
- package/src/charts/plot_popout_window.js +103 -317
- package/src/charts/plotly_wrapper.js +22 -2
- package/src/charts/series_transforms.js +206 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +1 -1
- package/src/core/settings.js +105 -19
- package/src/core/state_guard.js +93 -51
- package/src/core/state_guard_config.js +85 -74
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +3 -3
- package/src/editor/monaco_editor_factory.js +124 -60
- package/src/editor/monaco_loader.js +103 -28
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_tab_bar.js +38 -52
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +6 -3
- package/src/flow/canvas_editor/connections.js +90 -0
- package/src/flow/canvas_editor/editor.js +2351 -153
- package/src/flow/canvas_editor/index.js +2 -1
- package/src/flow/canvas_editor/layout.js +119 -12
- package/src/flow/canvas_editor/strings.js +80 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +97 -3
- 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 +3 -3
- package/src/flow/kit/keys.js +7 -1
- package/src/flow/kit/logic_graph.js +97 -15
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +2 -1
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +3 -1
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +86 -109
- package/src/flow/lanes/editor.js +136 -14
- package/src/flow/lanes/index.js +3 -1
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +44 -2
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +1 -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 +168 -42
- package/src/help/help_registry.js +12 -1
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +117 -0
- package/src/host/host.js +10 -2
- package/src/host/photino_host.js +9 -0
- package/src/host/photino_host.md +21 -3
- package/src/host/pywebview_host.js +10 -0
- package/src/tiles/chart_tiles.js +53 -245
- package/src/tiles/config_schema.js +8 -0
- package/src/tiles/figure_tile.js +322 -0
- package/src/tiles/layout_persistence.js +3 -3
- package/src/tiles/list_tiles.js +7 -7
- package/src/tiles/scope_picker.js +0 -0
- package/src/tiles/tile_base.js +1 -1
- package/src/tiles/tile_config_form.js +442 -0
- package/src/tiles/tile_grid.js +2 -0
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +67 -99
- package/src/tiling/command_palette.js +64 -19
- package/src/tiling/content_registry.js +4 -1
- package/src/tiling/desktops.js +11 -1
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +403 -128
- package/src/tiling/kind_taxonomy.js +78 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +59 -10
- package/src/tiling/page_factory.js +5 -5
- package/src/tiling/panel_availability.js +49 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +114 -12
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +105 -9
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +0 -0
- package/src/tiling/wm.js +562 -30
- 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 +30 -2
- 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 +293 -0
- package/src/ui/components/cell_editors.js +480 -0
- package/src/ui/components/cell_grid.js +761 -0
- package/src/ui/components/cell_paste.js +243 -0
- package/src/ui/components/cell_selection.js +220 -0
- package/src/ui/components/choice_controls.js +351 -0
- package/src/ui/components/color_field.js +211 -0
- package/src/ui/components/color_list_field.js +210 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +314 -0
- package/src/ui/components/column_order.js +142 -0
- package/src/ui/components/computing_status_window.js +98 -25
- package/src/ui/components/confirm_dialog.js +81 -38
- package/src/ui/components/context_menu.js +2 -2
- package/src/ui/components/conversation_view.js +440 -0
- package/src/ui/components/crosstab.js +289 -0
- package/src/ui/components/data_table.js +696 -71
- package/src/ui/components/detail_header.js +22 -11
- package/src/ui/components/document_outline.js +397 -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 +322 -0
- package/src/ui/components/gallery_picker.js +19 -8
- package/src/ui/components/header_actions.js +55 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +51 -0
- package/src/ui/components/managed_window.js +27 -4
- package/src/ui/components/menu_bar.js +745 -0
- package/src/ui/components/modal.js +449 -75
- package/src/ui/components/notification_history.js +3 -3
- package/src/ui/components/progress.js +483 -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 +298 -0
- package/src/ui/components/series.js +173 -0
- package/src/ui/components/settings_view.js +634 -0
- package/src/ui/components/slide_out_panel.js +43 -6
- package/src/ui/components/slider_field.js +275 -18
- package/src/ui/components/sortable_list.js +0 -0
- package/src/ui/components/staged_edits.js +361 -0
- package/src/ui/components/staged_history.js +172 -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 +182 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +497 -0
- package/src/ui/components/tag_input.js +215 -0
- package/src/ui/components/toast.js +25 -8
- package/src/ui/components/toggle_field.js +175 -0
- package/src/ui/components/tree_view.js +55 -34
- package/src/ui/components/tsv.js +74 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +563 -0
- package/src/ui/controllers/window_chrome_controller.js +2 -2
- package/src/ui/notification_center.js +375 -119
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +709 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +16 -12
- 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 +55 -0
- package/src/ui/utils/tooltip_service.js +0 -8
- package/tiles.js +27 -0
- package/widgets.js +69 -0
- package/wm.js +28 -0
- package/dist/chunk-6JSOVNID.js.map +0 -7
- package/dist/chunk-B5Q53LAJ.js.map +0 -7
- package/dist/chunk-FBGP5KN5.js.map +0 -7
- package/dist/chunk-FL5KFNQH.js.map +0 -7
- package/dist/chunk-NLTOY57M.js.map +0 -7
- package/dist/chunk-NNFTETK3.js +0 -633
- package/dist/chunk-NNFTETK3.js.map +0 -7
- package/dist/chunk-O5OHMWBB.js +0 -284
- package/dist/chunk-O5OHMWBB.js.map +0 -7
- package/dist/chunk-RQM5Z2MU.js.map +0 -7
- package/dist/chunk-SN4LSOAR.js.map +0 -7
- package/dist/chunk-W6FBJLPW.js +0 -367
- package/dist/chunk-W6FBJLPW.js.map +0 -7
- package/src/ui/controllers/panel_state_machine.js +0 -634
package/src/core/state_guard.js
CHANGED
|
@@ -19,8 +19,12 @@ const HOOK_REGISTRY = {
|
|
|
19
19
|
datasetProxyCache: new WeakMap(),
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const STACK_CACHE_LIMIT = 500;
|
|
23
|
+
/** `url:line:col` at the end of a frame, in V8 (`at fn (url:1:2)`) and in Firefox/WebKit (`fn@url:1:2`). */
|
|
24
|
+
const FRAME_LOCATION = /(?:\(|@|\bat\s+)([^()\s@][^()\s]*?):\d+(?::\d+)?\)?\s*$/;
|
|
23
25
|
const DATA_ATTRIBUTE_PREFIX = 'data-';
|
|
26
|
+
/** Set by the DOM hooks below: one hook frame sits between the writer and handle*Mutation. */
|
|
27
|
+
const VIA_DOM_HOOK = Symbol('state-guard:via-dom-hook');
|
|
24
28
|
const EVENT_CHANNELS = Object.freeze({
|
|
25
29
|
READY: 'state-guard:ready',
|
|
26
30
|
VIOLATION: 'state-guard:violation',
|
|
@@ -41,13 +45,15 @@ export class StateGuardService {
|
|
|
41
45
|
this.mode = this.config.mode;
|
|
42
46
|
this.allowedAttributes = new Set(this.config.allowedAttributes.map((attr) => attr.toLowerCase()));
|
|
43
47
|
this.allowedAttributePrefixes = this.config.allowedAttributePrefixes.map((attr) => attr.toLowerCase());
|
|
44
|
-
this.allowedDatasetKeys = new Set(this.config.allowedDatasetKeys.map(
|
|
48
|
+
this.allowedDatasetKeys = new Set(this.config.allowedDatasetKeys.map(normalizeDatasetKey));
|
|
45
49
|
this.moduleOverrides = (this.config.moduleOverrides ?? []).map((entry) => ({
|
|
46
50
|
pattern: entry.pattern,
|
|
47
51
|
attributes: new Set(entry.attributes.map((attr) => attr.toLowerCase())),
|
|
48
|
-
datasetKeys: new Set(entry.datasetKeys.map(
|
|
52
|
+
datasetKeys: new Set(entry.datasetKeys.map(normalizeDatasetKey)),
|
|
49
53
|
attributePrefixes: entry.attributePrefixes.map((prefix) => prefix.toLowerCase()),
|
|
50
54
|
}));
|
|
55
|
+
this.moduleFramePattern = this.config.moduleFramePattern;
|
|
56
|
+
this.stackCache = new Map();
|
|
51
57
|
this.#installHooks();
|
|
52
58
|
}
|
|
53
59
|
|
|
@@ -78,7 +84,7 @@ export class StateGuardService {
|
|
|
78
84
|
const entry = {
|
|
79
85
|
pattern,
|
|
80
86
|
attributes: new Set(attributes.map((attr) => String(attr).toLowerCase())),
|
|
81
|
-
datasetKeys: new Set(datasetKeys.map(
|
|
87
|
+
datasetKeys: new Set(datasetKeys.map(normalizeDatasetKey)),
|
|
82
88
|
attributePrefixes: attributePrefixes.map((prefix) => String(prefix).toLowerCase()),
|
|
83
89
|
};
|
|
84
90
|
this.moduleOverrides.push(entry);
|
|
@@ -121,7 +127,11 @@ export class StateGuardService {
|
|
|
121
127
|
}
|
|
122
128
|
}
|
|
123
129
|
|
|
124
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Check one attribute write. The write is attributed to the caller of this
|
|
132
|
+
* method, or to the caller of the patched DOM API when it came through one.
|
|
133
|
+
*/
|
|
134
|
+
handleAttributeMutation(element, action, attribute, value, options = {}) {
|
|
125
135
|
const attrName = this.#normalizeAttribute(attribute);
|
|
126
136
|
if (!this.#shouldGuard(attrName)) {
|
|
127
137
|
return;
|
|
@@ -132,15 +142,19 @@ export class StateGuardService {
|
|
|
132
142
|
element,
|
|
133
143
|
value,
|
|
134
144
|
source: 'attribute',
|
|
145
|
+
stack: new Error().stack,
|
|
146
|
+
writerFrame: options?.[VIA_DOM_HOOK] ? 2 : 1,
|
|
135
147
|
});
|
|
136
148
|
}
|
|
137
149
|
|
|
138
|
-
handleDatasetMutation(element, key, value, { deleting = false } = {}) {
|
|
139
|
-
const attrName =
|
|
150
|
+
handleDatasetMutation(element, key, value, { deleting = false, [VIA_DOM_HOOK]: viaHook = false } = {}) {
|
|
151
|
+
const attrName = datasetKeyToAttribute(key);
|
|
140
152
|
if (!this.#shouldGuard(attrName)) {
|
|
141
153
|
return;
|
|
142
154
|
}
|
|
143
155
|
this.#evaluateMutation({
|
|
156
|
+
stack: new Error().stack,
|
|
157
|
+
writerFrame: viaHook ? 2 : 1,
|
|
144
158
|
action: deleting ? 'dataset:delete' : 'dataset:set',
|
|
145
159
|
attribute: attrName,
|
|
146
160
|
element,
|
|
@@ -190,8 +204,8 @@ export class StateGuardService {
|
|
|
190
204
|
return true;
|
|
191
205
|
}
|
|
192
206
|
|
|
193
|
-
#evaluateMutation({ action, attribute, element, value, source }) {
|
|
194
|
-
const moduleKey = this.#resolveModuleFromStack();
|
|
207
|
+
#evaluateMutation({ action, attribute, element, value, source, stack, writerFrame }) {
|
|
208
|
+
const moduleKey = this.#resolveModuleFromStack(stack, writerFrame);
|
|
195
209
|
if (this.#isAllowedForModule(moduleKey, attribute)) {
|
|
196
210
|
return;
|
|
197
211
|
}
|
|
@@ -246,44 +260,47 @@ export class StateGuardService {
|
|
|
246
260
|
return String(attribute).toLowerCase();
|
|
247
261
|
}
|
|
248
262
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
.replace(/-+$/, '');
|
|
260
|
-
if (!normalized) {
|
|
261
|
-
return '';
|
|
262
|
-
}
|
|
263
|
-
return `${DATA_ATTRIBUTE_PREFIX}${normalized}`;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
#resolveModuleFromStack() {
|
|
267
|
-
const stack = new Error().stack;
|
|
263
|
+
/**
|
|
264
|
+
* The module a write is attributed to. `stack` was captured in a
|
|
265
|
+
* handle*Mutation method (frame 0); `writerFrame` is the index of the frame
|
|
266
|
+
* that made the write (1 for a direct call, 2 through a DOM hook). Without a
|
|
267
|
+
* `moduleFramePattern` that writer's location (its URL or path, line and
|
|
268
|
+
* column dropped) is the module. With one, the first frame from the writer
|
|
269
|
+
* up whose location matches is used, and the module is the pattern's first
|
|
270
|
+
* capture group, or that frame's whole location when it has none.
|
|
271
|
+
*/
|
|
272
|
+
#resolveModuleFromStack(stack, writerFrame) {
|
|
268
273
|
if (!stack) {
|
|
269
274
|
return null;
|
|
270
275
|
}
|
|
271
|
-
|
|
272
|
-
|
|
276
|
+
const cacheKey = `${writerFrame}\n${stack}`;
|
|
277
|
+
if (this.stackCache.has(cacheKey)) {
|
|
278
|
+
return this.stackCache.get(cacheKey);
|
|
273
279
|
}
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const match = line.match(/(js_new\/[^)\s]+)/);
|
|
280
|
+
const locations = [];
|
|
281
|
+
for (const line of String(stack).split('\n')) {
|
|
282
|
+
const match = line.match(FRAME_LOCATION);
|
|
278
283
|
if (match) {
|
|
279
|
-
|
|
280
|
-
|
|
284
|
+
locations.push(match[1]);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const callers = locations.slice(writerFrame);
|
|
288
|
+
let modulePath = null;
|
|
289
|
+
if (!this.moduleFramePattern) {
|
|
290
|
+
modulePath = callers[0] ?? null;
|
|
291
|
+
} else {
|
|
292
|
+
for (const location of callers) {
|
|
293
|
+
const match = location.match(this.moduleFramePattern);
|
|
294
|
+
if (match) {
|
|
295
|
+
modulePath = match[1] ?? location;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
281
298
|
}
|
|
282
299
|
}
|
|
283
|
-
if (
|
|
284
|
-
|
|
300
|
+
if (this.stackCache.size > STACK_CACHE_LIMIT) {
|
|
301
|
+
this.stackCache.clear();
|
|
285
302
|
}
|
|
286
|
-
|
|
303
|
+
this.stackCache.set(cacheKey, modulePath);
|
|
287
304
|
return modulePath;
|
|
288
305
|
}
|
|
289
306
|
|
|
@@ -299,6 +316,36 @@ export class StateGuardService {
|
|
|
299
316
|
}
|
|
300
317
|
}
|
|
301
318
|
|
|
319
|
+
/**
|
|
320
|
+
* The attribute a dataset key writes: `fooBar` -> `data-foo-bar`, as the DOM
|
|
321
|
+
* maps it. Empty for a key that names nothing.
|
|
322
|
+
*/
|
|
323
|
+
function datasetKeyToAttribute(key) {
|
|
324
|
+
if (!key && key !== 0) {
|
|
325
|
+
return '';
|
|
326
|
+
}
|
|
327
|
+
const normalized = String(key)
|
|
328
|
+
.replace(/([A-Z])/g, '-$1')
|
|
329
|
+
.replace(/[^a-zA-Z0-9_-]/g, '-')
|
|
330
|
+
.toLowerCase()
|
|
331
|
+
.replace(/-{2,}/g, '-')
|
|
332
|
+
.replace(/^-+/, '')
|
|
333
|
+
.replace(/-+$/, '');
|
|
334
|
+
if (!normalized) {
|
|
335
|
+
return '';
|
|
336
|
+
}
|
|
337
|
+
return `${DATA_ATTRIBUTE_PREFIX}${normalized}`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* An allow-list dataset key in the one spelling the guard compares: the
|
|
342
|
+
* attribute's, without `data-`. `fooBar` and `foo-bar` are the same key, so an
|
|
343
|
+
* allow-list may use either.
|
|
344
|
+
*/
|
|
345
|
+
function normalizeDatasetKey(key) {
|
|
346
|
+
return datasetKeyToAttribute(key).slice(DATA_ATTRIBUTE_PREFIX.length);
|
|
347
|
+
}
|
|
348
|
+
|
|
302
349
|
function installDomHooks(service) {
|
|
303
350
|
if (HOOK_REGISTRY.installed) {
|
|
304
351
|
HOOK_REGISTRY.service = service;
|
|
@@ -316,21 +363,13 @@ function installDomHooks(service) {
|
|
|
316
363
|
|
|
317
364
|
Element.prototype.setAttribute = function patchedSetAttribute(name, value) {
|
|
318
365
|
const guard = HOOK_REGISTRY.service;
|
|
319
|
-
|
|
320
|
-
guard?.handleAttributeMutation(this, 'setAttribute', name, value);
|
|
321
|
-
} catch (error) {
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
366
|
+
guard?.handleAttributeMutation(this, 'setAttribute', name, value, { [VIA_DOM_HOOK]: true });
|
|
324
367
|
return nativeSetAttribute.call(this, name, value);
|
|
325
368
|
};
|
|
326
369
|
|
|
327
370
|
Element.prototype.removeAttribute = function patchedRemoveAttribute(name) {
|
|
328
371
|
const guard = HOOK_REGISTRY.service;
|
|
329
|
-
|
|
330
|
-
guard?.handleAttributeMutation(this, 'removeAttribute', name, undefined);
|
|
331
|
-
} catch (error) {
|
|
332
|
-
throw error;
|
|
333
|
-
}
|
|
372
|
+
guard?.handleAttributeMutation(this, 'removeAttribute', name, undefined, { [VIA_DOM_HOOK]: true });
|
|
334
373
|
return nativeRemoveAttribute.call(this, name);
|
|
335
374
|
};
|
|
336
375
|
|
|
@@ -362,7 +401,7 @@ function wrapDataset(service) {
|
|
|
362
401
|
const guard = HOOK_REGISTRY.service;
|
|
363
402
|
const key = typeof prop === 'string' ? prop : null;
|
|
364
403
|
if (guard && key) {
|
|
365
|
-
guard.handleDatasetMutation(proxyState.element, key, value);
|
|
404
|
+
guard.handleDatasetMutation(proxyState.element, key, value, { [VIA_DOM_HOOK]: true });
|
|
366
405
|
}
|
|
367
406
|
return Reflect.set(target, prop, value);
|
|
368
407
|
},
|
|
@@ -370,7 +409,10 @@ function wrapDataset(service) {
|
|
|
370
409
|
const guard = HOOK_REGISTRY.service;
|
|
371
410
|
const key = typeof prop === 'string' ? prop : null;
|
|
372
411
|
if (guard && key) {
|
|
373
|
-
guard.handleDatasetMutation(proxyState.element, key, undefined, {
|
|
412
|
+
guard.handleDatasetMutation(proxyState.element, key, undefined, {
|
|
413
|
+
deleting: true,
|
|
414
|
+
[VIA_DOM_HOOK]: true,
|
|
415
|
+
});
|
|
374
416
|
}
|
|
375
417
|
return Reflect.deleteProperty(target, prop);
|
|
376
418
|
},
|
|
@@ -16,7 +16,6 @@ const DEFAULT_ALLOWED_ATTRIBUTES = Object.freeze([
|
|
|
16
16
|
'aria-busy',
|
|
17
17
|
'role',
|
|
18
18
|
'title',
|
|
19
|
-
'twm-has-tooltip',
|
|
20
19
|
'data-tooltip',
|
|
21
20
|
'data-help-topic',
|
|
22
21
|
]);
|
|
@@ -31,7 +30,6 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
|
|
|
31
30
|
'deleting',
|
|
32
31
|
// Notification center
|
|
33
32
|
'notification-id',
|
|
34
|
-
'notificationId',
|
|
35
33
|
// Plotly.js internal attributes (third-party charting library)
|
|
36
34
|
'unformatted',
|
|
37
35
|
'toggle',
|
|
@@ -46,56 +44,37 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
|
|
|
46
44
|
'group',
|
|
47
45
|
'trace',
|
|
48
46
|
'point',
|
|
49
|
-
'
|
|
50
|
-
'
|
|
47
|
+
'curve-number',
|
|
48
|
+
'point-number',
|
|
51
49
|
// UI state keys used by original HTML structure
|
|
52
50
|
'active-content',
|
|
53
|
-
'activeContent',
|
|
54
51
|
'panel-content',
|
|
55
|
-
'panelContent',
|
|
56
52
|
'collapsible-id',
|
|
57
|
-
'collapsibleId',
|
|
58
53
|
'collapsible-default',
|
|
59
|
-
'
|
|
60
|
-
'
|
|
61
|
-
'collapsibleHeader',
|
|
62
|
-
'twm-collapsible-content',
|
|
63
|
-
'collapsibleContent',
|
|
54
|
+
'collapsible-header',
|
|
55
|
+
'collapsible-content',
|
|
64
56
|
'collapsible-group',
|
|
65
|
-
'collapsibleGroup',
|
|
66
57
|
'collapsible-exclusive',
|
|
67
|
-
'collapsibleExclusive',
|
|
68
58
|
'collapsible-float',
|
|
69
|
-
'collapsibleFloat',
|
|
70
59
|
'collapsible-fill',
|
|
71
|
-
'collapsibleFill',
|
|
72
60
|
'tool',
|
|
73
61
|
'tab',
|
|
74
62
|
'role',
|
|
75
63
|
'op',
|
|
76
64
|
'func-name',
|
|
77
|
-
'funcName',
|
|
78
65
|
'func-expr',
|
|
79
|
-
'funcExpr',
|
|
80
66
|
'arity',
|
|
81
67
|
'tooltip',
|
|
82
|
-
'
|
|
83
|
-
'latexTooltip',
|
|
68
|
+
'latex-tooltip',
|
|
84
69
|
'node-type',
|
|
85
|
-
'
|
|
86
|
-
'twm-node-name',
|
|
87
|
-
'nodeName',
|
|
70
|
+
'node-name',
|
|
88
71
|
'lang',
|
|
89
72
|
'playback-control',
|
|
90
|
-
'playbackControl',
|
|
91
73
|
// Simulation/scenario state keys
|
|
92
74
|
'status',
|
|
93
75
|
'scenario-id',
|
|
94
|
-
'scenarioId',
|
|
95
76
|
'run-id',
|
|
96
|
-
'runId',
|
|
97
77
|
'run-state',
|
|
98
|
-
'runState',
|
|
99
78
|
'selected',
|
|
100
79
|
'active',
|
|
101
80
|
'disabled',
|
|
@@ -105,53 +84,33 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
|
|
|
105
84
|
'hidden',
|
|
106
85
|
// Node/canvas state keys
|
|
107
86
|
'node-id',
|
|
108
|
-
'nodeId',
|
|
109
87
|
'connector-id',
|
|
110
|
-
'connectorId',
|
|
111
88
|
'connector-type',
|
|
112
|
-
'connectorType',
|
|
113
89
|
'connection-id',
|
|
114
|
-
'connectionId',
|
|
115
90
|
'input-index',
|
|
116
|
-
'inputIndex',
|
|
117
91
|
'output-index',
|
|
118
|
-
'outputIndex',
|
|
119
92
|
'port-index',
|
|
120
|
-
'portIndex',
|
|
121
93
|
// Scenario editor keys
|
|
122
94
|
'source',
|
|
123
95
|
'series-id',
|
|
124
|
-
'seriesId',
|
|
125
96
|
'dataset-id',
|
|
126
|
-
'datasetId',
|
|
127
97
|
'mirror-input-bound',
|
|
128
|
-
'mirrorInputBound',
|
|
129
98
|
'mirror-change-bound',
|
|
130
|
-
'mirrorChangeBound',
|
|
131
99
|
'mirror-input',
|
|
132
|
-
'mirrorInput',
|
|
133
100
|
'bound',
|
|
134
101
|
'field',
|
|
135
102
|
'key',
|
|
136
103
|
'namespace',
|
|
137
104
|
'namespace-id',
|
|
138
|
-
'namespaceId',
|
|
139
105
|
'tab-id',
|
|
140
|
-
'tabId',
|
|
141
106
|
'param-id',
|
|
142
|
-
'paramId',
|
|
143
107
|
'target-id',
|
|
144
|
-
'targetId',
|
|
145
108
|
'override-type',
|
|
146
|
-
'overrideType',
|
|
147
109
|
// Tree view component keys
|
|
148
110
|
'item-id',
|
|
149
|
-
'itemId',
|
|
150
111
|
'category-id',
|
|
151
|
-
'categoryId',
|
|
152
112
|
// Calibration page keys
|
|
153
113
|
'info-for',
|
|
154
|
-
'infoFor',
|
|
155
114
|
'column',
|
|
156
115
|
// Data page keys
|
|
157
116
|
'readonly',
|
|
@@ -174,53 +133,102 @@ const DEFAULT_ALLOWED_DATASET_KEYS = Object.freeze([
|
|
|
174
133
|
'description',
|
|
175
134
|
// DSL generator keys
|
|
176
135
|
'dsl-state',
|
|
177
|
-
'dslState',
|
|
178
136
|
'dsl-section-registered',
|
|
179
|
-
'dslSectionRegistered',
|
|
180
137
|
'dsl-scrollbars-installed',
|
|
181
|
-
'dslScrollbarsInstalled',
|
|
182
138
|
'dsl-tabs-bound',
|
|
183
|
-
'dslTabsBound',
|
|
184
139
|
'dsl-controls-bound',
|
|
185
|
-
'dslControlsBound',
|
|
186
140
|
// Inline highlighter keys
|
|
187
141
|
'inline-hl',
|
|
188
|
-
'inlineHl',
|
|
189
|
-
'tooltip',
|
|
190
142
|
// Overlay scrollbar keys (UI-only, not state storage)
|
|
191
143
|
'scrollable',
|
|
192
144
|
'scrollable-x',
|
|
193
|
-
'scrollableX',
|
|
194
145
|
'scrollable-y',
|
|
195
|
-
'scrollableY',
|
|
196
146
|
// ManagedWindow component keys
|
|
197
147
|
'window-id',
|
|
198
|
-
'windowId',
|
|
199
148
|
// Help system keys
|
|
200
149
|
'help-topic',
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
150
|
+
// FlexDesk widgets: every literal data key written under src/ is listed
|
|
151
|
+
// here (tests/state_guard_defaults.test.mjs scans for them), so the
|
|
152
|
+
// shell, dialogs, forms, tooltips and editors pass a strict guard.
|
|
153
|
+
'accent',
|
|
154
|
+
'act',
|
|
155
|
+
'action-idx',
|
|
156
|
+
'action-value',
|
|
157
|
+
'add',
|
|
158
|
+
'category',
|
|
159
|
+
'code',
|
|
160
|
+
'data',
|
|
161
|
+
'direction',
|
|
162
|
+
'dismiss',
|
|
163
|
+
'entry',
|
|
164
|
+
'error',
|
|
165
|
+
'extra',
|
|
166
|
+
'flow',
|
|
167
|
+
'from',
|
|
168
|
+
'gap',
|
|
169
|
+
'group-id',
|
|
170
|
+
'item',
|
|
171
|
+
'kind',
|
|
172
|
+
'lane',
|
|
173
|
+
'leaf-id',
|
|
174
|
+
'level',
|
|
175
|
+
'list-action',
|
|
176
|
+
'match',
|
|
177
|
+
'mode',
|
|
178
|
+
'node',
|
|
179
|
+
'note-id',
|
|
180
|
+
'notification-summary',
|
|
181
|
+
'open',
|
|
182
|
+
'pager',
|
|
183
|
+
'pane',
|
|
184
|
+
'pane-id',
|
|
185
|
+
'parameter',
|
|
186
|
+
'placeholder',
|
|
187
|
+
'placement',
|
|
188
|
+
'port',
|
|
189
|
+
'record-key',
|
|
190
|
+
'ref',
|
|
191
|
+
'refusal',
|
|
192
|
+
'refusal-for',
|
|
193
|
+
'renameable',
|
|
194
|
+
'row-key',
|
|
195
|
+
'rule',
|
|
196
|
+
'scope',
|
|
197
|
+
'shape',
|
|
198
|
+
'slot',
|
|
199
|
+
'slot-idx',
|
|
200
|
+
'split-id',
|
|
201
|
+
'state',
|
|
202
|
+
'step',
|
|
203
|
+
'syntax',
|
|
204
|
+
'tile-id',
|
|
205
|
+
'tile-type',
|
|
206
|
+
'tip',
|
|
207
|
+
'to',
|
|
208
|
+
'tone',
|
|
209
|
+
'tooltip-max-width',
|
|
210
|
+
'tooltip-placement',
|
|
211
|
+
'twm-flow-item',
|
|
212
|
+
'twm-flow-title',
|
|
213
|
+
'twm-icon',
|
|
214
|
+
'twm-required',
|
|
215
|
+
'twm-tabs',
|
|
216
|
+
'twm-tail',
|
|
217
|
+
'verb',
|
|
212
218
|
]);
|
|
213
219
|
|
|
214
|
-
const DEFAULT_MODE = typeof window !== 'undefined' && Boolean(window.__ECOSIM_DEBUG__)
|
|
215
|
-
? 'strict'
|
|
216
|
-
: 'warn';
|
|
217
|
-
|
|
218
220
|
export const DEFAULT_STATE_GUARD_CONFIG = Object.freeze({
|
|
219
|
-
mode:
|
|
221
|
+
mode: 'warn',
|
|
220
222
|
allowedAttributes: DEFAULT_ALLOWED_ATTRIBUTES,
|
|
221
223
|
allowedAttributePrefixes: DEFAULT_ALLOWED_ATTRIBUTE_PREFIXES,
|
|
222
224
|
allowedDatasetKeys: DEFAULT_ALLOWED_DATASET_KEYS,
|
|
223
|
-
|
|
225
|
+
// FlexDesk ships no module overrides: which of YOUR modules may write which
|
|
226
|
+
// keys is yours to say, through `moduleOverrides` or `allow()`.
|
|
227
|
+
moduleOverrides: Object.freeze([]),
|
|
228
|
+
// null: a write is attributed to the frame that made it. A RegExp (or a
|
|
229
|
+
// literal string) picks the first frame from there up whose location
|
|
230
|
+
// matches instead; its first capture group, if any, names the module.
|
|
231
|
+
moduleFramePattern: null,
|
|
224
232
|
});
|
|
225
233
|
|
|
226
234
|
export function normalizeStateGuardConfig(config = {}) {
|
|
@@ -241,6 +249,9 @@ export function normalizeStateGuardConfig(config = {}) {
|
|
|
241
249
|
moduleOverrides: normalizeOverrides(
|
|
242
250
|
config.moduleOverrides ?? DEFAULT_STATE_GUARD_CONFIG.moduleOverrides,
|
|
243
251
|
),
|
|
252
|
+
moduleFramePattern: config.moduleFramePattern == null || config.moduleFramePattern === ''
|
|
253
|
+
? DEFAULT_STATE_GUARD_CONFIG.moduleFramePattern
|
|
254
|
+
: patternToRegExp(config.moduleFramePattern),
|
|
244
255
|
};
|
|
245
256
|
}
|
|
246
257
|
|
|
File without changes
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* └──────────────────────────────┘
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import { NotebookTabBar } from './notebook_tab_bar.js';
|
|
24
|
+
import { NotebookTabBar, getTabDragMime } from './notebook_tab_bar.js';
|
|
25
25
|
|
|
26
26
|
export class EditorPane {
|
|
27
27
|
/** @type {string} */
|
|
@@ -141,7 +141,7 @@ export class EditorPane {
|
|
|
141
141
|
if (this.#isExternalTabDrag(e)) {
|
|
142
142
|
e.preventDefault();
|
|
143
143
|
this.#editorContainer.classList.remove('notebook-editor-container--drop-target');
|
|
144
|
-
const filePath = e.dataTransfer.getData(
|
|
144
|
+
const filePath = e.dataTransfer.getData(getTabDragMime())
|
|
145
145
|
|| e.dataTransfer.getData('text/plain');
|
|
146
146
|
if (filePath) {
|
|
147
147
|
this.#callbacks.onDropFromOtherPane?.(this.#id, filePath);
|
|
@@ -248,7 +248,7 @@ export class EditorPane {
|
|
|
248
248
|
|
|
249
249
|
/** Check if a drag event is carrying a tab from another pane. */
|
|
250
250
|
#isExternalTabDrag(e) {
|
|
251
|
-
try { return e.dataTransfer.types.includes(
|
|
251
|
+
try { return e.dataTransfer.types.includes(getTabDragMime()); } catch { return false; }
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
dispose() {
|