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
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Handles:
|
|
6
6
|
* - Creating full editors (multi-line, for CodeCells)
|
|
7
7
|
* - Creating single-line inline editors (for ParameterCell values, expression fields)
|
|
8
|
-
* - Theme registration (
|
|
8
|
+
* - Theme registration (built-in dark and light themes, names set by
|
|
9
|
+
* setEditorThemeNames(); default flexdesk-dark, flexdesk-light)
|
|
9
10
|
* - Shared editor options
|
|
10
11
|
* - Instance lifecycle (dispose on cell removal)
|
|
11
12
|
*/
|
|
@@ -28,7 +29,7 @@ function safeDispose(editor) {
|
|
|
28
29
|
|
|
29
30
|
// ─── Theme definitions ─────────────────────────────────────────────────────────
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
+
const DARK_THEME = {
|
|
32
33
|
base: 'vs-dark',
|
|
33
34
|
inherit: true,
|
|
34
35
|
rules: [
|
|
@@ -74,7 +75,7 @@ const ECOSIM_DARK_THEME = {
|
|
|
74
75
|
},
|
|
75
76
|
};
|
|
76
77
|
|
|
77
|
-
const
|
|
78
|
+
const LIGHT_THEME = {
|
|
78
79
|
base: 'vs',
|
|
79
80
|
inherit: true,
|
|
80
81
|
rules: [
|
|
@@ -108,13 +109,47 @@ const ECOSIM_LIGHT_THEME = {
|
|
|
108
109
|
},
|
|
109
110
|
};
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
/** The names the built-in themes are registered under when the embedder
|
|
113
|
+
* names none. */
|
|
114
|
+
export const DEFAULT_EDITOR_THEME_NAMES = Object.freeze({
|
|
115
|
+
dark: 'flexdesk-dark',
|
|
116
|
+
light: 'flexdesk-light',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
let _themeNames = DEFAULT_EDITOR_THEME_NAMES;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Name the built-in dark and light Monaco themes. Call before the first
|
|
123
|
+
* factory is created: a factory starts on the dark name, and init() registers
|
|
124
|
+
* the built-in themes under the names current at that moment. An embedder with
|
|
125
|
+
* its own theme defines it with `monaco.editor.defineTheme(name, …)` and passes
|
|
126
|
+
* that name to `factory.setTheme(name)` instead.
|
|
127
|
+
*
|
|
128
|
+
* @param {{ dark?: string, light?: string }} [names]
|
|
129
|
+
*/
|
|
130
|
+
export function setEditorThemeNames({ dark, light } = {}) {
|
|
131
|
+
_themeNames = Object.freeze({
|
|
132
|
+
dark: (typeof dark === 'string' && dark) ? dark : DEFAULT_EDITOR_THEME_NAMES.dark,
|
|
133
|
+
light: (typeof light === 'string' && light) ? light : DEFAULT_EDITOR_THEME_NAMES.light,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** @returns {{ dark: string, light: string }} The built-in themes' names. */
|
|
138
|
+
export function getEditorThemeNames() {
|
|
139
|
+
return _themeNames;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Monaco instance -> names already defined on it, so each name is defined once. */
|
|
143
|
+
const _definedThemes = new WeakMap();
|
|
112
144
|
|
|
113
145
|
function ensureThemes(monaco) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
146
|
+
let defined = _definedThemes.get(monaco);
|
|
147
|
+
if (!defined) { defined = new Set(); _definedThemes.set(monaco, defined); }
|
|
148
|
+
for (const [name, theme] of [[_themeNames.dark, DARK_THEME], [_themeNames.light, LIGHT_THEME]]) {
|
|
149
|
+
if (defined.has(name)) continue;
|
|
150
|
+
defined.add(name);
|
|
151
|
+
monaco.editor.defineTheme(name, theme);
|
|
152
|
+
}
|
|
118
153
|
}
|
|
119
154
|
|
|
120
155
|
// ─── Shared editor options ─────────────────────────────────────────────────────
|
|
@@ -122,7 +157,7 @@ function ensureThemes(monaco) {
|
|
|
122
157
|
function baseOptions(overrides = {}) {
|
|
123
158
|
return {
|
|
124
159
|
language: _defaultLanguage,
|
|
125
|
-
theme:
|
|
160
|
+
theme: _themeNames.dark,
|
|
126
161
|
fontFamily: 'Consolas, "Courier New", monospace',
|
|
127
162
|
fontSize: 13,
|
|
128
163
|
lineHeight: 19.5,
|
|
@@ -177,15 +212,23 @@ function singleLineOptions(overrides = {}) {
|
|
|
177
212
|
});
|
|
178
213
|
}
|
|
179
214
|
|
|
215
|
+
/** A cap is a positive number of px, or `Infinity` for none. */
|
|
216
|
+
function checkMaxHeight(px) {
|
|
217
|
+
if (typeof px !== 'number' || Number.isNaN(px) || px <= 0) {
|
|
218
|
+
throw new RangeError(`maxHeight must be a positive number of px or Infinity, got ${String(px)}`);
|
|
219
|
+
}
|
|
220
|
+
return px;
|
|
221
|
+
}
|
|
222
|
+
|
|
180
223
|
// ─── Factory ───────────────────────────────────────────────────────────────────
|
|
181
224
|
|
|
182
225
|
/**
|
|
183
|
-
* @typedef {{ editor: object, dispose: () => void, getValue: () => string, setValue: (v: string) => void, onDidChange: (cb: () => void) => object }} EditorHandle
|
|
226
|
+
* @typedef {{ editor: object, dispose: () => void, getValue: () => string, setValue: (v: string) => void, onDidChange: (cb: () => void) => object, setMaxHeight?: (px: number) => void }} EditorHandle
|
|
184
227
|
*/
|
|
185
228
|
|
|
186
229
|
export class MonacoEditorFactory {
|
|
187
230
|
#monaco = null;
|
|
188
|
-
#currentTheme =
|
|
231
|
+
#currentTheme = _themeNames.dark;
|
|
189
232
|
|
|
190
233
|
constructor() {}
|
|
191
234
|
|
|
@@ -199,7 +242,8 @@ export class MonacoEditorFactory {
|
|
|
199
242
|
|
|
200
243
|
/**
|
|
201
244
|
* Set global theme for all editors.
|
|
202
|
-
* @param {
|
|
245
|
+
* @param {string} theme A theme name Monaco knows: one of
|
|
246
|
+
* getEditorThemeNames(), or one the embedder defined itself.
|
|
203
247
|
*/
|
|
204
248
|
setTheme(theme) {
|
|
205
249
|
this.#currentTheme = theme;
|
|
@@ -207,17 +251,26 @@ export class MonacoEditorFactory {
|
|
|
207
251
|
}
|
|
208
252
|
|
|
209
253
|
/**
|
|
210
|
-
* Create a full multi-line editor
|
|
254
|
+
* Create a full multi-line editor.
|
|
255
|
+
*
|
|
256
|
+
* Unless `noAutoHeight` is set, the editor sizes `container` to its content,
|
|
257
|
+
* never below `setMinHeight()` (80px by default) and never above `maxHeight`.
|
|
258
|
+
* Past the cap, Monaco scrolls internally and handles the mouse wheel; below
|
|
259
|
+
* it, Monaco leaves the wheel alone, so it reaches whatever scrolls around
|
|
260
|
+
* the editor.
|
|
211
261
|
*
|
|
212
262
|
* @param {HTMLElement} container
|
|
213
263
|
* @param {string} initialValue
|
|
214
|
-
* @param {{ language?: string, readOnly?: boolean, lineNumbers?: string } & object} [overrides]
|
|
264
|
+
* @param {{ language?: string, readOnly?: boolean, lineNumbers?: string, maxHeight?: number, noAutoHeight?: boolean } & object} [overrides]
|
|
265
|
+
* `maxHeight`: the auto-height cap in px, `Infinity` (the default) for none.
|
|
266
|
+
* `noAutoHeight`: leave the container's size to its layout; the wheel always scrolls the editor.
|
|
215
267
|
* @returns {EditorHandle}
|
|
216
268
|
*/
|
|
217
269
|
createEditor(container, initialValue = '', overrides = {}) {
|
|
218
270
|
if (!this.#monaco) throw new Error('MonacoEditorFactory not initialized');
|
|
219
271
|
|
|
220
|
-
const { noAutoHeight, ...editorOverrides } = overrides;
|
|
272
|
+
const { noAutoHeight, maxHeight: maxHeightOption = Infinity, ...editorOverrides } = overrides;
|
|
273
|
+
let maxHeight = checkMaxHeight(maxHeightOption);
|
|
221
274
|
|
|
222
275
|
const editor = this.#monaco.editor.create(container, {
|
|
223
276
|
...baseOptions({ theme: this.#currentTheme }),
|
|
@@ -225,17 +278,9 @@ export class MonacoEditorFactory {
|
|
|
225
278
|
...editorOverrides,
|
|
226
279
|
});
|
|
227
280
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
// Otherwise, grow to fit all content with no upper bound.
|
|
232
|
-
//
|
|
233
|
-
// Monaco's handleMouseWheel is toggled dynamically: disabled when the editor
|
|
234
|
-
// auto-sizes to fit content (so wheel events propagate to the notebook scroller),
|
|
235
|
-
// enabled only when content is capped and there is actual internal overflow.
|
|
236
|
-
//
|
|
237
|
-
// When noAutoHeight is true, skip auto-sizing entirely and let the container
|
|
238
|
-
// control dimensions (e.g. flex layout). Mouse wheel scrolling stays enabled.
|
|
281
|
+
// Monaco's handleMouseWheel is toggled with the height: off while the
|
|
282
|
+
// editor fits its content (the wheel scrolls the page around it), on only
|
|
283
|
+
// when the content overflows the cap and the editor has something to scroll.
|
|
239
284
|
let minHeight = 80;
|
|
240
285
|
let heightDisposable = null;
|
|
241
286
|
let updateHeight = null;
|
|
@@ -243,29 +288,16 @@ export class MonacoEditorFactory {
|
|
|
243
288
|
editor.updateOptions({ scrollbar: { handleMouseWheel: true } });
|
|
244
289
|
} else {
|
|
245
290
|
updateHeight = () => {
|
|
246
|
-
const maxHeightEnabled = container.closest('.notebook-editor-container')?.dataset.codeMaxHeight === 'true';
|
|
247
|
-
const maxH = maxHeightEnabled ? 400 : Infinity;
|
|
248
291
|
const rawContent = editor.getContentHeight();
|
|
249
|
-
const contentHeight = Math.min(
|
|
292
|
+
const contentHeight = Math.min(maxHeight, Math.max(minHeight, rawContent));
|
|
250
293
|
container.style.height = `${contentHeight}px`;
|
|
251
294
|
editor.layout();
|
|
252
|
-
|
|
253
|
-
// Enable Monaco wheel handling only when content overflows the capped height
|
|
254
|
-
const needsInternalScroll = maxHeightEnabled && rawContent > maxH;
|
|
255
|
-
editor.updateOptions({ scrollbar: { handleMouseWheel: needsInternalScroll } });
|
|
295
|
+
editor.updateOptions({ scrollbar: { handleMouseWheel: rawContent > maxHeight } });
|
|
256
296
|
};
|
|
257
297
|
heightDisposable = editor.onDidContentSizeChange(updateHeight);
|
|
258
298
|
updateHeight();
|
|
259
299
|
}
|
|
260
300
|
|
|
261
|
-
// Re-evaluate height when the code-max-height setting changes on the ancestor
|
|
262
|
-
const editorContainer = container.closest('.notebook-editor-container');
|
|
263
|
-
let maxHeightObserver = null;
|
|
264
|
-
if (updateHeight && editorContainer) {
|
|
265
|
-
maxHeightObserver = new MutationObserver(() => updateHeight());
|
|
266
|
-
maxHeightObserver.observe(editorContainer, { attributes: true, attributeFilter: ['data-code-max-height'] });
|
|
267
|
-
}
|
|
268
|
-
|
|
269
301
|
// Scroll the find widget into view when it becomes visible.
|
|
270
302
|
// The editor auto-sizes to content (no internal scroll), so the notebook
|
|
271
303
|
// container scrolls instead. The find widget sits at top:0 of the editor,
|
|
@@ -296,10 +328,14 @@ export class MonacoEditorFactory {
|
|
|
296
328
|
focus: () => editor.focus(),
|
|
297
329
|
layout: () => editor.layout(),
|
|
298
330
|
setMinHeight: (h) => { minHeight = h; },
|
|
331
|
+
/** The auto-height cap in px, `Infinity` for none; applies at once. No effect with `noAutoHeight`. */
|
|
332
|
+
setMaxHeight: (px) => {
|
|
333
|
+
maxHeight = checkMaxHeight(px);
|
|
334
|
+
updateHeight?.();
|
|
335
|
+
},
|
|
299
336
|
updateHeight,
|
|
300
337
|
dispose: () => {
|
|
301
338
|
findWidgetObserver?.disconnect();
|
|
302
|
-
maxHeightObserver?.disconnect();
|
|
303
339
|
heightDisposable?.dispose();
|
|
304
340
|
safeDispose(editor);
|
|
305
341
|
},
|
|
@@ -391,8 +427,12 @@ export class MonacoEditorFactory {
|
|
|
391
427
|
const monaco = this.#monaco;
|
|
392
428
|
const language = options.language || 'plaintext';
|
|
393
429
|
|
|
394
|
-
|
|
395
|
-
|
|
430
|
+
// The pair currently attached to the diff editor. setModels() replaces it, and
|
|
431
|
+
// dispose() frees whichever pair is attached at that moment — not the first one.
|
|
432
|
+
let models = {
|
|
433
|
+
original: monaco.editor.createModel(originalContent, language),
|
|
434
|
+
modified: monaco.editor.createModel(modifiedContent, language),
|
|
435
|
+
};
|
|
396
436
|
|
|
397
437
|
const diffEditor = monaco.editor.createDiffEditor(container, {
|
|
398
438
|
theme: this.#currentTheme,
|
|
@@ -408,28 +448,37 @@ export class MonacoEditorFactory {
|
|
|
408
448
|
enableSplitViewResizing: true,
|
|
409
449
|
});
|
|
410
450
|
|
|
411
|
-
diffEditor.setModel(
|
|
451
|
+
diffEditor.setModel(models);
|
|
412
452
|
|
|
413
453
|
return {
|
|
414
454
|
editor: diffEditor,
|
|
415
455
|
setModels(original, modified, lang) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
456
|
+
const previous = models;
|
|
457
|
+
models = {
|
|
458
|
+
original: monaco.editor.createModel(original, lang || language),
|
|
459
|
+
modified: monaco.editor.createModel(modified, lang || language),
|
|
460
|
+
};
|
|
461
|
+
// Attach the new pair first: a model must not be disposed while the
|
|
462
|
+
// diff editor still holds it.
|
|
463
|
+
diffEditor.setModel(models);
|
|
464
|
+
safeDispose(previous.original);
|
|
465
|
+
safeDispose(previous.modified);
|
|
421
466
|
},
|
|
422
467
|
dispose() {
|
|
423
468
|
safeDispose(diffEditor);
|
|
424
|
-
safeDispose(
|
|
425
|
-
safeDispose(
|
|
469
|
+
safeDispose(models.original);
|
|
470
|
+
safeDispose(models.modified);
|
|
426
471
|
},
|
|
427
472
|
};
|
|
428
473
|
}
|
|
429
474
|
}
|
|
430
475
|
|
|
431
|
-
/**
|
|
432
|
-
|
|
476
|
+
/**
|
|
477
|
+
* The singleton, as a promise: concurrent callers share one load, and a FAILED
|
|
478
|
+
* load clears it so the next call can retry.
|
|
479
|
+
* @type {Promise<MonacoEditorFactory> | null}
|
|
480
|
+
*/
|
|
481
|
+
let _factoryPromise = null;
|
|
433
482
|
|
|
434
483
|
// Where the app keeps Monaco. Set once, at boot.
|
|
435
484
|
let _monacoBasePath = null;
|
|
@@ -439,11 +488,26 @@ export function setMonacoBasePath(basePath) {
|
|
|
439
488
|
if (typeof basePath === 'string' && basePath) _monacoBasePath = basePath;
|
|
440
489
|
}
|
|
441
490
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
491
|
+
/**
|
|
492
|
+
* The shared editor factory, once Monaco has loaded.
|
|
493
|
+
*
|
|
494
|
+
* Uses a load already started by initMonaco(path), so a consumer that calls
|
|
495
|
+
* initMonaco() itself needs no setMonacoBasePath(). Otherwise it starts one
|
|
496
|
+
* from setMonacoBasePath()'s path.
|
|
497
|
+
*
|
|
498
|
+
* Rejects — rather than pending forever (BUG-0023) — with initMonaco()'s error
|
|
499
|
+
* when no path was set and no load was started, or when Monaco cannot be
|
|
500
|
+
* loaded. A later call asks initMonaco() again: after a loader.js failure that
|
|
501
|
+
* is a fresh attempt (fix the path first); after an AMD-level failure it is
|
|
502
|
+
* the same rejection, because that one is final for the page.
|
|
503
|
+
*
|
|
504
|
+
* @returns {Promise<MonacoEditorFactory>}
|
|
505
|
+
*/
|
|
506
|
+
export function getEditorFactory() {
|
|
507
|
+
if (!_factoryPromise) {
|
|
508
|
+
_factoryPromise = initMonaco(_monacoBasePath)
|
|
509
|
+
.then(() => new MonacoEditorFactory().init())
|
|
510
|
+
.catch((err) => { _factoryPromise = null; throw err; });
|
|
447
511
|
}
|
|
448
|
-
return
|
|
512
|
+
return _factoryPromise;
|
|
449
513
|
}
|
|
@@ -8,9 +8,34 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
let _resolveMonaco;
|
|
11
|
-
let _monacoReady = false;
|
|
12
11
|
|
|
13
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The attempt in flight, the one that succeeded, or one that failed for good.
|
|
14
|
+
*
|
|
15
|
+
* Whether a failure is retryable depends on how far the attempt got:
|
|
16
|
+
*
|
|
17
|
+
* - vs/loader.js did not load (a 404, a wrong path): nothing is left behind,
|
|
18
|
+
* so the attempt is cleared and the next initMonaco() starts over. The flag
|
|
19
|
+
* this replaced was set BEFORE loader.js had loaded, so one 404 blocked
|
|
20
|
+
* every retry for the life of the page (BUG-0023).
|
|
21
|
+
* - Monaco's AMD loader reported an error (vs/editor/editor.main or one of
|
|
22
|
+
* its dependencies failed): FINAL for the page. window.require is now
|
|
23
|
+
* Monaco's loader, which records the failed module as complete-with-error
|
|
24
|
+
* and hands every later require() of it the same error — it has no undef.
|
|
25
|
+
* Retrying cannot help, so the rejected attempt is kept and every caller
|
|
26
|
+
* gets that one error. Recovering takes a page reload.
|
|
27
|
+
* @type {Promise<typeof monaco> | null}
|
|
28
|
+
*/
|
|
29
|
+
let _attempt = null;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolves with the `monaco` namespace once any initMonaco() call has loaded it.
|
|
33
|
+
*
|
|
34
|
+
* It never rejects, because a failed attempt is not final — a later
|
|
35
|
+
* initMonaco() may still succeed. To learn that an attempt FAILED, await
|
|
36
|
+
* initMonaco() (or getEditorFactory()), which rejects.
|
|
37
|
+
* @type {Promise<typeof monaco>}
|
|
38
|
+
*/
|
|
14
39
|
export const monacoReady = new Promise(resolve => { _resolveMonaco = resolve; });
|
|
15
40
|
|
|
16
41
|
function injectLoaderScript(src) {
|
|
@@ -18,21 +43,67 @@ function injectLoaderScript(src) {
|
|
|
18
43
|
const s = document.createElement('script');
|
|
19
44
|
s.src = src;
|
|
20
45
|
s.onload = resolve;
|
|
21
|
-
s.onerror = () =>
|
|
46
|
+
s.onerror = () => {
|
|
47
|
+
// Take the dead tag out, so a retry is a fresh request and not a
|
|
48
|
+
// second tag beside the first.
|
|
49
|
+
s.remove?.();
|
|
50
|
+
reject(new Error(`Failed to load Monaco loader: ${src}`));
|
|
51
|
+
};
|
|
22
52
|
document.head.appendChild(s);
|
|
23
53
|
});
|
|
24
54
|
}
|
|
25
55
|
|
|
26
56
|
function injectMonacoCss(basePath) {
|
|
27
57
|
const id = 'monaco-editor-css';
|
|
28
|
-
|
|
58
|
+
const href = `${basePath}/vs/editor/editor.main.css`;
|
|
59
|
+
const existing = document.getElementById(id);
|
|
60
|
+
if (existing) {
|
|
61
|
+
// A retry with a corrected path must not keep the stylesheet of the bad one.
|
|
62
|
+
if (existing.getAttribute?.('href') !== href) existing.setAttribute?.('href', href);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
29
65
|
const link = document.createElement('link');
|
|
30
66
|
link.id = id;
|
|
31
67
|
link.rel = 'stylesheet';
|
|
32
|
-
link.href =
|
|
68
|
+
link.href = href;
|
|
33
69
|
document.head.appendChild(link);
|
|
34
70
|
}
|
|
35
71
|
|
|
72
|
+
function requireEditorMain(basePath) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
window.require.config({
|
|
75
|
+
paths: { vs: `${basePath}/vs` },
|
|
76
|
+
'vs/nls': { availableLanguages: {} },
|
|
77
|
+
});
|
|
78
|
+
window.require(['vs/editor/editor.main'], () => {
|
|
79
|
+
if (window.monaco) resolve(window.monaco);
|
|
80
|
+
else reject(new Error(
|
|
81
|
+
`[twm/editor] vs/editor/editor.main loaded from ${basePath}/vs but did not `
|
|
82
|
+
+ 'set window.monaco.'));
|
|
83
|
+
}, (err) => {
|
|
84
|
+
reject(new Error(
|
|
85
|
+
`[twm/editor] Failed to load Monaco from ${basePath}/vs: ${err?.message ?? err}. `
|
|
86
|
+
+ 'Monaco\'s AMD loader caches this failure, so it is final for this page: '
|
|
87
|
+
+ 'fix the path and reload.'));
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function loadMonaco(basePath) {
|
|
93
|
+
// Inject CSS and loader.js dynamically — kept out of the HTML to avoid global bleed
|
|
94
|
+
injectMonacoCss(basePath);
|
|
95
|
+
if (!window.require) {
|
|
96
|
+
const src = `${basePath}/vs/loader.js`;
|
|
97
|
+
await injectLoaderScript(src);
|
|
98
|
+
if (!window.require) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`[twm/editor] ${src} loaded but did not define window.require; `
|
|
101
|
+
+ 'is it Monaco\'s vs/loader.js?');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return requireEditorMain(basePath);
|
|
105
|
+
}
|
|
106
|
+
|
|
36
107
|
/**
|
|
37
108
|
* Call once when the notebook page initialises.
|
|
38
109
|
* Where Monaco lives is the CONSUMER's business.
|
|
@@ -43,32 +114,36 @@ function injectMonacoCss(basePath) {
|
|
|
43
114
|
*
|
|
44
115
|
* Monaco is a PEER dependency — 14 MB this bundle deliberately does not contain.
|
|
45
116
|
*
|
|
46
|
-
*
|
|
117
|
+
* Idempotent: while an attempt is in flight or has succeeded, every caller
|
|
118
|
+
* gets the same promise, whatever path it passes or whether it passes one —
|
|
119
|
+
* so getEditorFactory() piggybacks on a load the consumer started here.
|
|
120
|
+
*
|
|
121
|
+
* The returned promise REJECTS when no path is given and no attempt exists,
|
|
122
|
+
* when loader.js fails to load, or when Monaco's AMD loader reports an error.
|
|
123
|
+
* After a loader.js failure the next call tries again (with the path it is
|
|
124
|
+
* given). An AMD-level failure is final for the page: every later call gets
|
|
125
|
+
* the same rejection, and only a reload recovers (see `_attempt`).
|
|
126
|
+
*
|
|
127
|
+
* @param {string} [basePath] Path prefix for the vs/ directory. REQUIRED
|
|
128
|
+
* unless an attempt is already in flight or has succeeded.
|
|
129
|
+
* @returns {Promise<typeof monaco>}
|
|
47
130
|
*/
|
|
48
|
-
export
|
|
131
|
+
export function initMonaco(basePath) {
|
|
132
|
+
if (_attempt) return _attempt;
|
|
49
133
|
if (!basePath) {
|
|
50
|
-
|
|
134
|
+
return Promise.reject(new Error(
|
|
51
135
|
'[twm/editor] initMonaco() needs the path to your copy of Monaco, e.g. '
|
|
52
136
|
+ "initMonaco('vendor/monaco'). Monaco is a peer dependency; the library "
|
|
53
|
-
+ 'does not guess where you put it.');
|
|
137
|
+
+ 'does not guess where you put it.'));
|
|
54
138
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
paths: { vs: `${basePath}/vs` },
|
|
66
|
-
'vs/nls': { availableLanguages: {} },
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
window.require(['vs/editor/editor.main'], () => {
|
|
70
|
-
_resolveMonaco(window.monaco);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
return monacoReady;
|
|
139
|
+
const attempt = loadMonaco(basePath).then(
|
|
140
|
+
(m) => { _resolveMonaco(m); return m; },
|
|
141
|
+
(err) => {
|
|
142
|
+
// Monaco's AMD loader is installed only once loader.js has run; a
|
|
143
|
+
// failure before that leaves nothing cached and can be retried.
|
|
144
|
+
if (!window.require && _attempt === attempt) _attempt = null;
|
|
145
|
+
throw err;
|
|
146
|
+
});
|
|
147
|
+
_attempt = attempt;
|
|
148
|
+
return attempt;
|
|
74
149
|
}
|
|
File without changes
|
|
@@ -23,9 +23,29 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { DragReorder } from '../ui/components/drag_reorder.js';
|
|
26
|
+
import { revealInStrip, wheelStripSideways } from '../ui/utils/strip_scroll.js';
|
|
26
27
|
|
|
27
|
-
/**
|
|
28
|
-
const
|
|
28
|
+
/** The MIME type a dragged editor tab carries when the embedder names none. */
|
|
29
|
+
export const DEFAULT_TAB_DRAG_MIME = 'application/x-flexdesk-tab';
|
|
30
|
+
|
|
31
|
+
/** Custom MIME type so tab bars and editor panes recognise cross-pane drags. */
|
|
32
|
+
let _tabMime = DEFAULT_TAB_DRAG_MIME;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Name the MIME type editor tabs carry while dragged. Every NotebookTabBar and
|
|
36
|
+
* EditorPane in the document reads it at drag time, so they always agree.
|
|
37
|
+
* Call at boot, before the first drag.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} [mime=DEFAULT_TAB_DRAG_MIME]
|
|
40
|
+
*/
|
|
41
|
+
export function setTabDragMime(mime = DEFAULT_TAB_DRAG_MIME) {
|
|
42
|
+
_tabMime = (typeof mime === 'string' && mime) ? mime : DEFAULT_TAB_DRAG_MIME;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @returns {string} The MIME type editor tabs carry while dragged. */
|
|
46
|
+
export function getTabDragMime() {
|
|
47
|
+
return _tabMime;
|
|
48
|
+
}
|
|
29
49
|
|
|
30
50
|
export class NotebookTabBar {
|
|
31
51
|
/** @type {HTMLElement} */
|
|
@@ -75,7 +95,7 @@ export class NotebookTabBar {
|
|
|
75
95
|
},
|
|
76
96
|
onDragStart: (e, key, item) => {
|
|
77
97
|
// Carry the cross-pane MIME so other panes accept this tab.
|
|
78
|
-
try { e.dataTransfer.setData(
|
|
98
|
+
try { e.dataTransfer.setData(_tabMime, key); } catch (_) {}
|
|
79
99
|
// C33. …AND LET THE HOST ADD ITS OWN, which is the whole of
|
|
80
100
|
// what a second consumer of this strip needs from it.
|
|
81
101
|
//
|
|
@@ -87,7 +107,7 @@ export class NotebookTabBar {
|
|
|
87
107
|
// tabs — a different question from "is this an editor tab",
|
|
88
108
|
// asked of the same gesture.
|
|
89
109
|
//
|
|
90
|
-
// ADDITIVE BY CONSTRUCTION:
|
|
110
|
+
// ADDITIVE BY CONSTRUCTION: the tab MIME is set first and
|
|
91
111
|
// unchanged, so `EditorPane`'s existing cross-pane drop cannot
|
|
92
112
|
// notice that anyone else is listening.
|
|
93
113
|
try { this.#callbacks.onDragStart?.(e, key, item); }
|
|
@@ -259,59 +279,25 @@ export class NotebookTabBar {
|
|
|
259
279
|
/** @type {number} a pending retry, for a strip not yet laid out */
|
|
260
280
|
#revealFrame = 0;
|
|
261
281
|
|
|
262
|
-
/** Bring the active tab fully into the strip
|
|
263
|
-
* `scrollIntoView`, which scrolls every scrollable ancestor as well and
|
|
264
|
-
* would drag the tile, the window or the page along with the tab. */
|
|
282
|
+
/** Bring the active tab fully into the strip (`revealInStrip`). */
|
|
265
283
|
#revealActive(retry = false) {
|
|
266
284
|
const el = this.#container;
|
|
267
285
|
if (!el) return;
|
|
268
286
|
const active = el.querySelector(':scope > .tab.active');
|
|
269
287
|
if (!active) return;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
return;
|
|
288
|
+
// Mounted but not laid out yet (the renderer updates a strip before it
|
|
289
|
+
// attaches it). Once, on the next frame — not a loop.
|
|
290
|
+
if (!revealInStrip(el, active) && retry && !this.#revealFrame
|
|
291
|
+
&& typeof requestAnimationFrame === 'function') {
|
|
292
|
+
this.#revealFrame = requestAnimationFrame(() => {
|
|
293
|
+
this.#revealFrame = 0;
|
|
294
|
+
this.#revealActive(false);
|
|
295
|
+
});
|
|
280
296
|
}
|
|
281
|
-
const box = el.getBoundingClientRect();
|
|
282
|
-
const tab = active.getBoundingClientRect();
|
|
283
|
-
// Client pixels to the strip's own: 1 unless something above it zooms.
|
|
284
|
-
const scale = (el.offsetWidth ? box.width / el.offsetWidth : 1) || 1;
|
|
285
|
-
const left = (tab.left - box.left) / scale + el.scrollLeft;
|
|
286
|
-
const right = left + tab.width / scale;
|
|
287
|
-
if (left < el.scrollLeft) el.scrollLeft = left;
|
|
288
|
-
else if (right > el.scrollLeft + el.clientWidth) el.scrollLeft = right - el.clientWidth;
|
|
289
297
|
}
|
|
290
298
|
|
|
291
|
-
/**
|
|
292
|
-
|
|
293
|
-
* an element that overflows only horizontally with Shift+wheel or a
|
|
294
|
-
* touchpad, and not with the plain wheel most people have — so with the
|
|
295
|
-
* scrollbar hidden, every tab past the edge was unreachable with a mouse.
|
|
296
|
-
*
|
|
297
|
-
* The wheel is taken from the page only while the strip can actually move
|
|
298
|
-
* that way: at either end it is left alone, so a wheel over a strip that
|
|
299
|
-
* has nothing more to show still scrolls whatever is behind it. A gesture
|
|
300
|
-
* that is already horizontal (a touchpad, Shift+wheel) and Ctrl+wheel (zoom)
|
|
301
|
-
* are the browser's, untouched.
|
|
302
|
-
*/
|
|
303
|
-
#onWheel = (e) => {
|
|
304
|
-
const el = this.#container;
|
|
305
|
-
if (!el || e.ctrlKey || e.shiftKey) return;
|
|
306
|
-
if (Math.abs(e.deltaX) >= Math.abs(e.deltaY)) return;
|
|
307
|
-
const room = el.scrollWidth - el.clientWidth;
|
|
308
|
-
if (room <= 0) return;
|
|
309
|
-
const unit = e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? el.clientWidth : 1;
|
|
310
|
-
const next = Math.max(0, Math.min(room, el.scrollLeft + e.deltaY * unit));
|
|
311
|
-
if (Math.abs(next - el.scrollLeft) < 0.5) return;
|
|
312
|
-
e.preventDefault();
|
|
313
|
-
el.scrollLeft = next;
|
|
314
|
-
};
|
|
299
|
+
/** A vertical wheel scrolls the strip sideways (`wheelStripSideways`). */
|
|
300
|
+
#onWheel = (e) => wheelStripSideways(this.#container, e);
|
|
315
301
|
|
|
316
302
|
// ═════════════════════════════════════════════════════════════════════════
|
|
317
303
|
// Drag and drop reordering (same-pane + cross-pane)
|
|
@@ -335,7 +321,7 @@ export class NotebookTabBar {
|
|
|
335
321
|
*
|
|
336
322
|
* C33. A HOST MAY NAME A SECOND MIME, at mount, as
|
|
337
323
|
* `callbacks.externalTabMimes` — the tile renderer's tabs are not editor
|
|
338
|
-
* tabs and do not carry
|
|
324
|
+
* tabs and do not carry the editor tab MIME (getTabDragMime()), so without this the
|
|
339
325
|
* strip in tile B would refuse a tab dragged out of tile A's strip. Empty
|
|
340
326
|
* by default, so every existing consumer keeps exactly today's admission
|
|
341
327
|
* test.
|
|
@@ -353,7 +339,7 @@ export class NotebookTabBar {
|
|
|
353
339
|
? this.#callbacks.externalTabMimes : [];
|
|
354
340
|
try {
|
|
355
341
|
const types = e.dataTransfer.types;
|
|
356
|
-
return types.includes(
|
|
342
|
+
return types.includes(_tabMime) || extra.some((m) => types.includes(m));
|
|
357
343
|
} catch { return false; }
|
|
358
344
|
}
|
|
359
345
|
|
|
@@ -374,7 +360,7 @@ export class NotebookTabBar {
|
|
|
374
360
|
if (this.#isExternalTabDrag(e)) {
|
|
375
361
|
e.preventDefault();
|
|
376
362
|
this.#container.classList.remove('notebook-tabs--drop-target');
|
|
377
|
-
const filePath = e.dataTransfer.getData(
|
|
363
|
+
const filePath = e.dataTransfer.getData(_tabMime) || e.dataTransfer.getData('text/plain');
|
|
378
364
|
if (filePath) {
|
|
379
365
|
this.#callbacks.onDropFromOtherPane?.(filePath);
|
|
380
366
|
}
|
|
File without changes
|
|
@@ -114,17 +114,20 @@ export class SplitPaneContainer {
|
|
|
114
114
|
|
|
115
115
|
if (!this.#pane1) return;
|
|
116
116
|
|
|
117
|
+
// ADD the class, never assign it: a pane is the caller's element
|
|
118
|
+
// (an EditorPane root is `.editor-pane`), and its own classes are
|
|
119
|
+
// what its stylesheet reaches it by.
|
|
117
120
|
if (!this.#isSplit) {
|
|
118
121
|
// Single pane: fills container
|
|
119
|
-
this.#pane1.
|
|
122
|
+
this.#pane1.classList.add('split-pane');
|
|
120
123
|
this.#pane1.style.flex = '1 1 0';
|
|
121
124
|
this.#pane1.style.minWidth = '0';
|
|
122
125
|
this.#container.appendChild(this.#pane1);
|
|
123
126
|
this.#container.classList.remove('split-pane-container--split');
|
|
124
127
|
} else {
|
|
125
128
|
// Split view
|
|
126
|
-
this.#pane1.
|
|
127
|
-
this.#pane2.
|
|
129
|
+
this.#pane1.classList.add('split-pane');
|
|
130
|
+
this.#pane2.classList.add('split-pane');
|
|
128
131
|
this.#pane1.style.minWidth = '0';
|
|
129
132
|
this.#pane2.style.minWidth = '0';
|
|
130
133
|
|