@perspective-dev/viewer 5.3.1 → 5.5.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/dist/cdn/perspective-viewer.js +6 -6
- package/dist/cdn/perspective-viewer.js.map +2 -2
- package/dist/css/blueprint.css +1 -1
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/ar.css +1 -0
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/ledger.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -1
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/css/velvet.css +1 -1
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/extensions.d.ts +0 -5
- package/dist/esm/perspective-viewer.d.ts +2 -0
- package/dist/esm/perspective-viewer.inline.js +8 -8
- package/dist/esm/perspective-viewer.inline.js.map +2 -2
- package/dist/esm/perspective-viewer.js +5 -5
- package/dist/esm/perspective-viewer.js.map +2 -2
- package/dist/esm/plugin.d.ts +9 -0
- package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
- package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
- package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
- package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
- package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
- package/dist/wasm/perspective-viewer.d.ts +137 -104
- package/dist/wasm/perspective-viewer.js +337 -269
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
- package/package.json +7 -7
- package/src/css/_context_menu.css +1 -1
- package/src/css/_dropdown_menu.css +1 -1
- package/src/css/_viewer_bundle.css +7 -8
- package/src/css/column-dropdown.css +0 -3
- package/src/css/column-selector.css +18 -7
- package/src/css/column-settings-panel.css +0 -225
- package/src/css/column-style.css +175 -0
- package/src/css/config-selector.css +19 -11
- package/src/css/filter-dropdown.css +5 -5
- package/src/css/form/debug.css +37 -4
- package/src/css/function-dropdown.css +5 -5
- package/src/css/panel-tab.css +23 -0
- package/src/css/plugin-selector.css +5 -5
- package/src/css/render-warning.css +1 -1
- package/src/css/status-bar.css +63 -21
- package/src/css/type-icon.css +1 -3
- package/src/css/{containers → ui/containers}/context-menu.css +22 -16
- package/src/css/ui/containers/dropdown-menu-items.css +66 -0
- package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
- package/src/css/{containers → ui/containers}/tabs.css +2 -1
- package/src/css/viewer.css +32 -26
- package/src/rust/agent/tools.rs +80 -7
- package/src/rust/components/chat_panel.rs +1 -1
- package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
- package/src/rust/components/column_dropdown.rs +2 -2
- package/src/rust/components/column_selector/active_column.rs +24 -9
- package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
- package/src/rust/components/column_selector/config_selector.rs +2 -2
- package/src/rust/components/column_selector/filter_column.rs +8 -5
- package/src/rust/components/column_selector/inactive_column.rs +4 -2
- package/src/rust/components/column_selector/pivot_column.rs +4 -3
- package/src/rust/components/column_selector/sort_column.rs +7 -4
- package/src/rust/components/column_selector.rs +14 -3
- package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
- package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
- package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
- package/src/rust/components/column_settings_sidebar/style_tab.rs +70 -12
- package/src/rust/components/column_settings_sidebar.rs +3 -4
- package/src/rust/components/copy_dropdown.rs +1 -1
- package/src/rust/components/datetime_column_style/custom.rs +1 -3
- package/src/rust/components/datetime_column_style/simple.rs +1 -2
- package/src/rust/components/datetime_column_style.rs +1 -2
- package/src/rust/components/{form/debug.rs → debug_panel.rs} +112 -55
- package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
- package/src/rust/components/export_dropdown.rs +1 -1
- package/src/rust/components/expression_editor.rs +1 -1
- package/src/rust/components/filter_dropdown.rs +2 -2
- package/src/rust/components/function_dropdown.rs +2 -2
- package/src/rust/components/main_panel/frame_theme.rs +3 -59
- package/src/rust/components/main_panel/msg.rs +4 -0
- package/src/rust/components/main_panel/reconcile.rs +9 -4
- package/src/rust/components/main_panel/render.rs +62 -11
- package/src/rust/components/main_panel/update.rs +39 -10
- package/src/rust/components/main_panel.rs +40 -2
- package/src/rust/components/mod.rs +5 -8
- package/src/rust/components/new_panel_menu.rs +97 -0
- package/src/rust/components/number_series_style.rs +2 -3
- package/src/rust/components/panel_menu.rs +61 -87
- package/src/rust/components/panel_tab.rs +100 -43
- package/src/rust/components/plugin_tab.rs +58 -4
- package/src/rust/components/render_warning.rs +2 -2
- package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
- package/src/rust/components/settings_panel.rs +8 -2
- package/src/rust/components/status_bar.rs +113 -19
- package/src/rust/components/status_indicator.rs +9 -14
- package/src/rust/components/{style/mod.rs → style.rs} +32 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
- package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
- package/src/rust/components/viewer/msg.rs +43 -30
- package/src/rust/components/viewer/panels.rs +53 -19
- package/src/rust/components/viewer/render.rs +16 -13
- package/src/rust/components/viewer/settings.rs +137 -68
- package/src/rust/components/viewer/snapshots.rs +5 -10
- package/src/rust/components/viewer/wiring.rs +11 -22
- package/src/rust/components/viewer.rs +15 -10
- package/src/rust/components/window_editor.rs +8 -9
- package/src/rust/config/column_config_schema.rs +312 -33
- package/src/rust/config/datetime_column_style.rs +0 -11
- package/src/rust/config/mod.rs +0 -4
- package/src/rust/config/options.rs +50 -0
- package/src/rust/config/workspace_config.rs +70 -24
- package/src/rust/custom_elements/debug_plugin.rs +1 -10
- package/src/rust/custom_elements/mod.rs +0 -3
- package/src/rust/custom_elements/viewer.rs +194 -115
- package/src/rust/js/plugin.rs +3 -87
- package/src/rust/lib.rs +19 -18
- package/src/rust/presentation/column_locator.rs +3 -7
- package/src/rust/presentation.rs +68 -38
- package/src/rust/queries/hosted_tables.rs +38 -0
- package/src/rust/queries/mod.rs +2 -0
- package/src/rust/queries/palette_set.rs +1 -1
- package/src/rust/queries/plugin_column_styles.rs +6 -5
- package/src/rust/renderer/activate.rs +1 -3
- package/src/rust/renderer/dispatch.rs +83 -22
- package/src/rust/renderer/limits.rs +24 -5
- package/src/rust/renderer/plugin_config.rs +201 -41
- package/src/rust/renderer/plugin_store.rs +2 -1
- package/src/rust/renderer/props.rs +7 -1
- package/src/rust/renderer.rs +23 -27
- package/src/rust/session/props.rs +8 -3
- package/src/rust/session/view_subscription.rs +19 -6
- package/src/rust/session.rs +189 -150
- package/src/rust/tasks/create_panel.rs +5 -2
- package/src/rust/tasks/dismiss_render_warning.rs +1 -1
- package/src/rust/tasks/eject.rs +1 -0
- package/src/rust/tasks/pipeline.rs +18 -67
- package/src/rust/tasks/presize_panels.rs +24 -1
- package/src/rust/tasks/restore_and_render.rs +0 -17
- package/src/rust/tasks/restore_panel.rs +129 -30
- package/src/rust/tasks/send_column_config.rs +4 -1
- package/src/rust/tasks/send_plugin_config.rs +1 -1
- package/src/rust/tasks/set_edit_mode.rs +1 -1
- package/src/rust/tasks/sync_update_panels.rs +147 -67
- package/src/rust/tasks/table_lifecycle.rs +7 -1
- package/src/rust/{components → ui}/containers/control_group.rs +25 -3
- package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
- package/src/rust/{components → ui}/containers/mod.rs +0 -4
- package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
- package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
- package/src/rust/{components → ui}/containers/select.rs +0 -6
- package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
- package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
- package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
- package/src/rust/{components → ui}/context_menu.rs +8 -13
- package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
- package/src/rust/{components → ui}/font_loader.rs +0 -26
- package/src/rust/{components → ui}/form/color_selector.rs +4 -2
- package/src/rust/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
- package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
- package/src/rust/{components → ui}/form/mod.rs +1 -10
- package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
- package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
- package/src/rust/{components → ui}/form/number_field.rs +1 -1
- package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
- package/src/rust/{components → ui}/form/optional_field.rs +3 -1
- package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
- package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
- package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
- package/src/rust/ui/mod.rs +56 -0
- package/src/rust/ui/modal.rs +61 -0
- package/src/rust/{components → ui}/portal.rs +175 -79
- package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
- package/src/rust/utils/completion.rs +0 -6
- package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
- package/src/rust/utils/css_vars.rs +13 -0
- package/src/rust/utils/local_fonts.rs +395 -0
- package/src/rust/utils/mod.rs +4 -0
- package/src/rust/workspace.rs +47 -0
- package/src/svg/boolean-type.svg +3 -2
- package/src/svg/checkbox-checked-icon.svg +4 -7
- package/src/svg/checkbox-hover.svg +3 -3
- package/src/svg/checkbox-off.svg +2 -3
- package/src/svg/checkbox-on.svg +3 -3
- package/src/svg/checkbox-unchecked-icon.svg +3 -3
- package/src/svg/datagrid-select-column.svg +8 -15
- package/src/svg/datagrid-select-region.svg +8 -15
- package/src/svg/datagrid-select-row-tree.svg +7 -12
- package/src/svg/datagrid-select-row.svg +8 -13
- package/src/svg/date-type.svg +4 -23
- package/src/svg/drawer-tab-hover.svg +4 -6
- package/src/svg/drawer-tab-invert-hover.svg +5 -5
- package/src/svg/drawer-tab-invert.svg +3 -5
- package/src/svg/drawer-tab.svg +5 -5
- package/src/svg/mega-menu-icons-candlestick.svg +5 -37
- package/src/svg/mega-menu-icons-ohlc.svg +4 -39
- package/src/svg/mega-menu-icons-sunburst.svg +4 -38
- package/src/svg/mega-menu-icons-treemap.svg +3 -43
- package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
- package/src/svg/new-panel-icon.svg +4 -0
- package/src/svg/number-type.svg +3 -3
- package/src/svg/radio-hover.svg +3 -3
- package/src/svg/radio-off.svg +3 -3
- package/src/svg/radio-on.svg +3 -3
- package/src/svg/status_ok.svg +3 -26
- package/src/svg/status_pending.svg +3 -0
- package/src/svg/string-type.svg +4 -8
- package/src/svg/theme-icon.svg +5 -5
- package/src/themes/blueprint.css +0 -57
- package/src/themes/botanical.css +0 -36
- package/src/themes/defaults.css +1 -24
- package/src/themes/dracula.css +0 -58
- package/src/themes/eggplant.css +0 -58
- package/src/themes/gruvbox-dark.css +0 -65
- package/src/themes/gruvbox.css +1 -39
- package/src/themes/icons.css +2 -8
- package/src/themes/intl/ar.css +198 -0
- package/src/themes/intl/de.css +63 -40
- package/src/themes/intl/es.css +58 -35
- package/src/themes/intl/fr.css +57 -34
- package/src/themes/intl/ja.css +59 -36
- package/src/themes/intl/pt.css +54 -31
- package/src/themes/intl/zh.css +65 -42
- package/src/themes/intl.css +34 -8
- package/src/themes/ledger.css +1 -59
- package/src/themes/monokai.css +0 -51
- package/src/themes/nord.css +0 -55
- package/src/themes/phosphor.css +0 -48
- package/src/themes/pro-dark.css +0 -37
- package/src/themes/pro.css +3 -23
- package/src/themes/solarized-dark.css +0 -31
- package/src/themes/solarized.css +1 -24
- package/src/themes/vaporwave.css +0 -44
- package/src/themes/velvet.css +0 -60
- package/src/ts/extensions.ts +0 -13
- package/src/ts/perspective-viewer.ts +2 -0
- package/src/ts/plugin.ts +9 -0
- package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
- package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
- package/src/ts/ts-rs/GetViewOptions.ts +7 -0
- package/src/ts/ts-rs/RestoreOptions.ts +7 -1
- package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
- package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
- package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
- package/src/css/plugin-settings-panel.css +0 -171
- package/src/rust/components/modal.rs +0 -177
- package/src/rust/components/string_column_style.rs +0 -121
- package/src/rust/config/string_column_style.rs +0 -135
- package/src/rust/custom_elements/copy_dropdown.rs +0 -219
- package/src/rust/custom_elements/export_dropdown.rs +0 -216
- package/src/rust/custom_elements/modal.rs +0 -299
- package/src/ts/ts-rs/FormatMode.ts +0 -3
- package/src/ts/ts-rs/StringColorMode.ts +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline5.js +0 -0
- /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
- /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
- /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
- /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
- /package/src/css/{dom → ui/dom}/select.css +0 -0
- /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
- /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
- /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
- /package/src/rust/{components → ui}/form/number_input.rs +0 -0
|
@@ -10,21 +10,6 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
//! The render pipeline (see `SESSION_CONFIG_COHERENCE_PLAN.md`) — the ONE
|
|
14
|
-
//! review surface for how config-driven renders happen.
|
|
15
|
-
//!
|
|
16
|
-
//! Invariants enforced here and by the types this composes:
|
|
17
|
-
//! - **I1** config mutation is a synchronous commit
|
|
18
|
-
//! ([`Session::commit_view_config`]) — no async writer exists.
|
|
19
|
-
//! - **I2** every run consumes an immutable snapshot taken inside the draw
|
|
20
|
-
//! lock; async stages never re-read the live config (the `ValidatedSnapshot`
|
|
21
|
-
//! type-state token).
|
|
22
|
-
//! - **I3** runs serialize on the per-`Renderer` draw lock (the [`RenderGuard`]
|
|
23
|
-
//! witness — plugin dispatch without it does not compile) and every commit
|
|
24
|
-
//! schedules a run, so the final render always reflects the final commit.
|
|
25
|
-
//! - **I5** the run pins a [`RenderContext`] so plugin read-backs mid-render
|
|
26
|
-
//! answer from the run's snapshot, never live state.
|
|
27
|
-
|
|
28
13
|
use std::rc::Rc;
|
|
29
14
|
|
|
30
15
|
use futures::future::LocalBoxFuture;
|
|
@@ -79,30 +64,10 @@ pub async fn bind_snapshot(
|
|
|
79
64
|
/// legitimate concurrent public calls).
|
|
80
65
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
81
66
|
pub enum RunOrigin {
|
|
82
|
-
/// A public element API entry (`restore`, `restorePanel`, `reset`,
|
|
83
|
-
/// `load`, …): an explicit external request, so a run that reconciles
|
|
84
|
-
/// `Unchanged` with no state delta STILL repaints — the documented
|
|
85
|
-
/// no-op-restore refresh affordance (`update` source 6, "the user
|
|
86
|
-
/// asked" — `PLUGIN_DRAW_INVARIANT_PLAN.md` amendment).
|
|
87
67
|
Public,
|
|
88
|
-
/// A host-initiated run (UI commits, unpause resumes, subscriptions):
|
|
89
|
-
/// a reconciled no-op dispatches nothing.
|
|
90
68
|
Internal,
|
|
91
69
|
}
|
|
92
70
|
|
|
93
|
-
/// The ONE decision table mapping a run's [`BindDisposition`] to its plugin
|
|
94
|
-
/// dispatch (`PLUGIN_DRAW_INVARIANT_PLAN.md`, as amended 2026-07-16):
|
|
95
|
-
/// `plugin.draw` fires iff there is a NEW `View` — a REBUILD, or a
|
|
96
|
-
/// freshly-selected plugin's first paint of the bound `View`
|
|
97
|
-
/// (`promote_first_paint`). `plugin.update` fires iff the `View` is
|
|
98
|
-
/// unchanged but a plugin-visible SOURCE changed: the `Adopted`
|
|
99
|
-
/// config delta, a genuinely changed plugin/columns bucket this run
|
|
100
|
-
/// delivered via `plugin.restore` (`plugin_state_changed`), or an explicit
|
|
101
|
-
/// public API request ([`RunOrigin::Public`]). An INTERNAL run that
|
|
102
|
-
/// reconciled `Unchanged` with no state delta dispatches NOTHING — no
|
|
103
|
-
/// defensive repaints (this is what makes an inert plugin echo, e.g. the
|
|
104
|
-
/// datagrid's `toggle_edit_mode` `restorePanel`, render-silent). Deferred
|
|
105
|
-
/// binds dispatch nothing.
|
|
106
71
|
pub async fn dispatch_bound(
|
|
107
72
|
guard: &RenderGuard,
|
|
108
73
|
renderer: &Renderer,
|
|
@@ -286,6 +251,18 @@ pub(crate) async fn locked_run(
|
|
|
286
251
|
let view_config_snapshot = session.get_view_config().clone();
|
|
287
252
|
let plugin_config_changed =
|
|
288
253
|
renderer.update_plugin_config(&view_config_snapshot, spec.plugin_config)?;
|
|
254
|
+
let plugin_config_snapshot = renderer.get_plugin_config();
|
|
255
|
+
let plugin_update =
|
|
256
|
+
JsValue::from_serde_ext(&plugin_config_snapshot).unwrap_or(JsValue::NULL);
|
|
257
|
+
|
|
258
|
+
let plugin_restored = plugin_config_changed || plugin_swapped;
|
|
259
|
+
if plugin_restored {
|
|
260
|
+
let columns_config = renderer
|
|
261
|
+
.all_columns_configs_materialized(&view_config_snapshot, &session)
|
|
262
|
+
.await;
|
|
263
|
+
plugin.restore(&plugin_update, Some(&columns_config))?;
|
|
264
|
+
}
|
|
265
|
+
|
|
289
266
|
let columns_config_changed = renderer.update_columns_configs(
|
|
290
267
|
&view_config_snapshot,
|
|
291
268
|
&session,
|
|
@@ -293,17 +270,16 @@ pub(crate) async fn locked_run(
|
|
|
293
270
|
)?;
|
|
294
271
|
|
|
295
272
|
let changed = plugin_config_changed || columns_config_changed;
|
|
296
|
-
if
|
|
297
|
-
let plugin_config_snapshot = renderer.get_plugin_config();
|
|
298
|
-
let plugin_update =
|
|
299
|
-
JsValue::from_serde_ext(&plugin_config_snapshot).unwrap_or(JsValue::NULL);
|
|
273
|
+
if columns_config_changed {
|
|
300
274
|
let columns_config = renderer
|
|
301
275
|
.all_columns_configs_materialized(&view_config_snapshot, &session)
|
|
302
276
|
.await;
|
|
303
277
|
plugin.restore(&plugin_update, Some(&columns_config))?;
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
278
|
+
renderer.columns_config_changed.emit(columns_config);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if plugin_config_changed {
|
|
282
|
+
renderer.plugin_config_changed.emit(plugin_config_snapshot);
|
|
307
283
|
}
|
|
308
284
|
|
|
309
285
|
if spec
|
|
@@ -343,31 +319,6 @@ async fn render_run(
|
|
|
343
319
|
}
|
|
344
320
|
}
|
|
345
321
|
|
|
346
|
-
/// One TRANSACTIONAL activation repaint (see the I5 audit gap in
|
|
347
|
-
/// `SESSION_CONFIG_COHERENCE_PLAN.md` §4): under the panel's draw lock,
|
|
348
|
-
/// stamp the `active` class + theme and `plugin.resize()` in ONE dispatch,
|
|
349
|
-
/// so the activation-dependent chrome (e.g. the datagrid's edit
|
|
350
|
-
/// column-header row, decided by its style listener reading that class) and
|
|
351
|
-
/// the class that styles it land in a single paint commit, never split.
|
|
352
|
-
///
|
|
353
|
-
/// `resize`, NOT `draw`/`update`: activation creates no new `View` and
|
|
354
|
-
/// changes no data (`plugin.draw` ⇔ new `View` —
|
|
355
|
-
/// `PLUGIN_DRAW_INVARIANT_PLAN.md`), and a full dispatch on charts is a
|
|
356
|
-
/// fetch + multi-blit repaint — the stacked-tab two-stage regression, paid
|
|
357
|
-
/// by BOTH sides of every tab switch. `resize` is each plugin's cheap
|
|
358
|
-
/// repaint-from-retained-state, and both built-ins skip it while hidden, so
|
|
359
|
-
/// the OUTGOING (unslotted) panel's nudge costs nothing. Deliberately NOT
|
|
360
|
-
/// debounced/throttled: activation is a one-shot interaction, not a data
|
|
361
|
-
/// stream, and the throttle timer is a task boundary the browser paints
|
|
362
|
-
/// across.
|
|
363
|
-
///
|
|
364
|
-
/// EXCEPTION: a panel whose `table_updated` redraws were dropped while it
|
|
365
|
-
/// was hidden ([`Renderer::take_data_stale`]) HAS changed data — its
|
|
366
|
-
/// retained frame is stale, so this activation dispatches one full
|
|
367
|
-
/// `plugin.update` (same `View`, fresh data; `draw` stays reserved for new
|
|
368
|
-
/// `View`s) instead of the chrome-only repaint. `draw_view` stamps the
|
|
369
|
-
/// activation class + theme itself, so the atomicity contract above holds
|
|
370
|
-
/// on this branch too.
|
|
371
322
|
pub async fn activation_render(session: Session, renderer: Renderer) -> ApiResult<()> {
|
|
372
323
|
let result = {
|
|
373
324
|
clone!(session, renderer);
|
|
@@ -166,13 +166,14 @@ pub async fn presize_visible_panels_open(
|
|
|
166
166
|
pub async fn presize_visible_panels_pane_width(
|
|
167
167
|
workspace: &Workspace,
|
|
168
168
|
elem: &web_sys::HtmlElement,
|
|
169
|
+
pane_sel: &str,
|
|
169
170
|
pane_width: f64,
|
|
170
171
|
) -> StagedPresents {
|
|
171
172
|
let Some(mpc) = shadow_rect(elem, "#main_panel_container") else {
|
|
172
173
|
return StagedPresents::default();
|
|
173
174
|
};
|
|
174
175
|
|
|
175
|
-
let Some(pane) = shadow_rect(elem,
|
|
176
|
+
let Some(pane) = shadow_rect(elem, pane_sel) else {
|
|
176
177
|
return StagedPresents::default();
|
|
177
178
|
};
|
|
178
179
|
|
|
@@ -308,6 +309,28 @@ fn shadow_rect(elem: &web_sys::HtmlElement, sel: &str) -> Option<web_sys::DomRec
|
|
|
308
309
|
)
|
|
309
310
|
}
|
|
310
311
|
|
|
312
|
+
/// The width the `SplitPanel` pane at `pane_sel` takes with no divider
|
|
313
|
+
/// override, measured under one forced synchronous layout that no paint or
|
|
314
|
+
/// `ResizeObserver` can observe, or `None` when the pane isn't in the DOM.
|
|
315
|
+
pub fn measure_pane_natural_width(elem: &web_sys::HtmlElement, pane_sel: &str) -> Option<f64> {
|
|
316
|
+
let pane = elem
|
|
317
|
+
.shadow_root()?
|
|
318
|
+
.query_selector(pane_sel)
|
|
319
|
+
.ok()??
|
|
320
|
+
.dyn_into::<web_sys::HtmlElement>()
|
|
321
|
+
.ok()?;
|
|
322
|
+
|
|
323
|
+
let style = pane.style();
|
|
324
|
+
let css_text = style.css_text();
|
|
325
|
+
let class_name = pane.class_name();
|
|
326
|
+
style.set_css_text("");
|
|
327
|
+
let _ = pane.class_list().remove_1("is-width-override");
|
|
328
|
+
let width = pane.get_bounding_client_rect().width();
|
|
329
|
+
style.set_css_text(&css_text);
|
|
330
|
+
pane.set_class_name(&class_name);
|
|
331
|
+
Some(width)
|
|
332
|
+
}
|
|
333
|
+
|
|
311
334
|
/// The open-state geometry deltas cached for the next settings *open* (P2):
|
|
312
335
|
/// `(layout_area.w − mpc.w, main_column.h − mpc.h)`. Only meaningful while the
|
|
313
336
|
/// settings pane is open (i.e. measured at close time).
|
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
//! Apply a full [`ViewerConfigUpdate`] (settings, theme, title, plugin,
|
|
14
|
-
//! plugin_config, columns_config, view_config) and re-draw, on the snapshot
|
|
15
|
-
//! pipeline (see `tasks/pipeline.rs` / `SESSION_CONFIG_COHERENCE_PLAN.md`).
|
|
16
|
-
|
|
17
13
|
use futures::Future;
|
|
18
14
|
use perspective_client::clone;
|
|
19
15
|
|
|
@@ -28,19 +24,6 @@ use crate::*;
|
|
|
28
24
|
/// Apply a full [`ViewerConfigUpdate`] (theme, title, plugin selection,
|
|
29
25
|
/// plugin config, columns config, view config) to the engines and re-draw.
|
|
30
26
|
/// Returns an [`ApiFuture<()>`] which resolves when the draw completes.
|
|
31
|
-
///
|
|
32
|
-
/// `origin` says who initiated the restore ([`RunOrigin`]): a `Public`
|
|
33
|
-
/// element-API call keeps the no-op-restore refresh affordance (an
|
|
34
|
-
/// `Unchanged` reconcile still repaints via `update`); an `Internal`
|
|
35
|
-
/// restore that reconciles `Unchanged` and changes no plugin state
|
|
36
|
-
/// dispatches nothing.
|
|
37
|
-
///
|
|
38
|
-
/// This function owns the PRE-LOCK prologue only (element-level settings /
|
|
39
|
-
/// title / host-theme mirror, plugin resolution, the synchronous config
|
|
40
|
-
/// commit and spinner token); the run itself is
|
|
41
|
-
/// [`locked_run`] with the caller's `task` awaited inside the lock. The
|
|
42
|
-
/// `update`'s `table` field is NOT applied here — table binding is the
|
|
43
|
-
/// `task`'s job (see `restore_panel` / `table_lifecycle`).
|
|
44
27
|
pub fn restore_and_render(
|
|
45
28
|
session: &Session,
|
|
46
29
|
renderer: &Renderer,
|
|
@@ -16,7 +16,7 @@ use perspective_client::utils::PerspectiveResultExt;
|
|
|
16
16
|
use crate::config::*;
|
|
17
17
|
use crate::presentation::Presentation;
|
|
18
18
|
use crate::renderer::Renderer;
|
|
19
|
-
use crate::session::{ResetOptions, Session};
|
|
19
|
+
use crate::session::{MissingTable, ResetOptions, Session, TableIntermediateState, probe_table};
|
|
20
20
|
use crate::tasks::*;
|
|
21
21
|
use crate::workspace::Workspace;
|
|
22
22
|
use crate::*;
|
|
@@ -38,27 +38,107 @@ pub(crate) enum RestoreErrors {
|
|
|
38
38
|
Suppress,
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/// The client to bind and the opened `Table`, or `None` when pending.
|
|
42
|
+
type Probed = (
|
|
43
|
+
perspective_client::Client,
|
|
44
|
+
Option<perspective_client::Table>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
/// Resolve and probe the client hosting `name` without touching the
|
|
48
|
+
/// session's own binding.
|
|
49
|
+
async fn probe(
|
|
50
|
+
session: &Session,
|
|
51
|
+
workspace: &Workspace,
|
|
52
|
+
name: &str,
|
|
53
|
+
missing: MissingTable,
|
|
54
|
+
) -> ApiResult<Probed> {
|
|
55
|
+
let current = session.get_client();
|
|
56
|
+
let resolved = workspace
|
|
57
|
+
.resolve_client_for_table(name, current.as_ref())
|
|
58
|
+
.await;
|
|
59
|
+
|
|
60
|
+
let client = resolved.or(current).into_apierror()?;
|
|
61
|
+
let table = probe_table(&client, name, missing).await?;
|
|
62
|
+
Ok((client, table))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// Bind an unbound or same-named session to `name`.
|
|
41
66
|
pub(crate) async fn bind_table_task(
|
|
42
67
|
session: &Session,
|
|
43
68
|
workspace: &Workspace,
|
|
44
69
|
name: String,
|
|
70
|
+
missing: MissingTable,
|
|
45
71
|
) -> ApiResult<()> {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
.await
|
|
72
|
+
if session
|
|
73
|
+
.get_table()
|
|
74
|
+
.is_some_and(|t| t.get_name() == name.as_str())
|
|
50
75
|
{
|
|
51
|
-
|
|
76
|
+
return Ok(());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let (client, table) = probe(session, workspace, &name, missing).await?;
|
|
80
|
+
session.set_client(client);
|
|
81
|
+
match table {
|
|
82
|
+
Some(table) => session.bind_table(table).await?,
|
|
83
|
+
None => session.pend_table(name).await?,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
session.commit_table_defaults();
|
|
87
|
+
Ok(())
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Rebind a session to `name`, probing the incoming table before the
|
|
91
|
+
/// outgoing binding is dropped and replaying the journal opened as
|
|
92
|
+
/// `generation` over the incoming table's defaults.
|
|
93
|
+
async fn rebind_table_task(
|
|
94
|
+
session: &Session,
|
|
95
|
+
renderer: &Renderer,
|
|
96
|
+
workspace: &Workspace,
|
|
97
|
+
name: String,
|
|
98
|
+
missing: MissingTable,
|
|
99
|
+
generation: u32,
|
|
100
|
+
) -> ApiResult<()> {
|
|
101
|
+
let probed = probe(session, workspace, &name, missing).await;
|
|
102
|
+
let journal = session.take_pending_load(generation);
|
|
103
|
+
let (client, table) = probed?;
|
|
104
|
+
let Some(journal) = journal else {
|
|
105
|
+
return Ok(());
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
session
|
|
109
|
+
.reset(ResetOptions {
|
|
110
|
+
config: true,
|
|
111
|
+
expressions: true,
|
|
112
|
+
stats: true,
|
|
113
|
+
table: Some(TableIntermediateState::Reloaded),
|
|
114
|
+
})
|
|
115
|
+
.await?;
|
|
116
|
+
|
|
117
|
+
session.set_client(client);
|
|
118
|
+
match table {
|
|
119
|
+
Some(table) => session.bind_table(table).await?,
|
|
120
|
+
None => session.pend_table(name).await?,
|
|
52
121
|
}
|
|
53
122
|
|
|
54
|
-
session.set_table(name).await?;
|
|
55
123
|
session.commit_table_defaults();
|
|
124
|
+
for delta in journal {
|
|
125
|
+
session.commit_view_config(delta)?;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
session.commit_table_defaults();
|
|
129
|
+
if session.get_table().is_none()
|
|
130
|
+
&& let Some(plugin) = renderer.active_plugin()
|
|
131
|
+
{
|
|
132
|
+
plugin.clear().await?;
|
|
133
|
+
}
|
|
134
|
+
|
|
56
135
|
Ok(())
|
|
57
136
|
}
|
|
58
137
|
|
|
59
138
|
/// Apply a [`ViewerConfigUpdate`] to a single panel and re-draw — the one
|
|
60
139
|
/// pipeline shared by `restorePanel` (an existing panel), `restoreWorkspace`,
|
|
61
140
|
/// and `addPanel` (both fresh panels).
|
|
141
|
+
#[allow(clippy::too_many_arguments)]
|
|
62
142
|
pub(crate) async fn restore_panel(
|
|
63
143
|
session: &Session,
|
|
64
144
|
renderer: &Renderer,
|
|
@@ -67,6 +147,7 @@ pub(crate) async fn restore_panel(
|
|
|
67
147
|
mode: RestoreMode,
|
|
68
148
|
mut update: ViewerConfigUpdate,
|
|
69
149
|
errors: RestoreErrors,
|
|
150
|
+
missing: MissingTable,
|
|
70
151
|
) -> ApiResult<()> {
|
|
71
152
|
let active = matches!(mode, RestoreMode::Existing { active: true });
|
|
72
153
|
let fresh = matches!(mode, RestoreMode::Fresh);
|
|
@@ -100,21 +181,24 @@ pub(crate) async fn restore_panel(
|
|
|
100
181
|
let rollback =
|
|
101
182
|
matches!(errors, RestoreErrors::Suppress).then(|| session.get_view_config().clone());
|
|
102
183
|
|
|
103
|
-
let
|
|
104
|
-
|
|
105
|
-
|
|
184
|
+
let binding_before = (
|
|
185
|
+
session.get_table().map(|t| t.get_name().to_owned()),
|
|
186
|
+
session.pending_table(),
|
|
187
|
+
);
|
|
106
188
|
|
|
107
|
-
let
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
189
|
+
let generation = match &update.table {
|
|
190
|
+
OptionalUpdate::Update(name)
|
|
191
|
+
if session.is_errored()
|
|
192
|
+
|| (!fresh
|
|
193
|
+
&& session
|
|
194
|
+
.get_table()
|
|
195
|
+
.map(|t| t.get_name() != name.as_str())
|
|
196
|
+
.unwrap_or(true)) =>
|
|
197
|
+
{
|
|
198
|
+
Some(session.begin_pending_load())
|
|
199
|
+
},
|
|
200
|
+
_ => None,
|
|
201
|
+
};
|
|
118
202
|
|
|
119
203
|
let result = restore_and_render(
|
|
120
204
|
session,
|
|
@@ -123,27 +207,42 @@ pub(crate) async fn restore_panel(
|
|
|
123
207
|
RunOrigin::Public,
|
|
124
208
|
update.clone(),
|
|
125
209
|
{
|
|
126
|
-
clone!(session, update.table, workspace);
|
|
210
|
+
clone!(session, renderer, update.table, workspace);
|
|
127
211
|
async move {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
212
|
+
let OptionalUpdate::Update(name) = table else {
|
|
213
|
+
return Ok(());
|
|
214
|
+
};
|
|
132
215
|
|
|
133
|
-
|
|
216
|
+
match generation {
|
|
217
|
+
Some(generation) => {
|
|
218
|
+
rebind_table_task(
|
|
219
|
+
&session, &renderer, &workspace, name, missing, generation,
|
|
220
|
+
)
|
|
221
|
+
.await
|
|
222
|
+
},
|
|
223
|
+
None => bind_table_task(&session, &workspace, name, missing).await,
|
|
134
224
|
}
|
|
135
|
-
|
|
136
|
-
Ok(())
|
|
137
225
|
}
|
|
138
226
|
},
|
|
139
227
|
)
|
|
140
228
|
.await;
|
|
141
229
|
|
|
230
|
+
if let Some(generation) = generation {
|
|
231
|
+
session.take_pending_load(generation);
|
|
232
|
+
}
|
|
233
|
+
|
|
142
234
|
if let Err(e) = &result {
|
|
143
235
|
match errors {
|
|
144
236
|
RestoreErrors::Publish => session.set_error(false, e.clone()).await?,
|
|
145
237
|
RestoreErrors::Suppress => {
|
|
146
|
-
|
|
238
|
+
let binding_after = (
|
|
239
|
+
session.get_table().map(|t| t.get_name().to_owned()),
|
|
240
|
+
session.pending_table(),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
if let Some(config) = rollback
|
|
244
|
+
&& binding_after == binding_before
|
|
245
|
+
{
|
|
147
246
|
session.commit_view_config(config.into()).unwrap_or_log();
|
|
148
247
|
}
|
|
149
248
|
},
|
|
@@ -49,8 +49,11 @@ pub fn send_column_config(
|
|
|
49
49
|
|
|
50
50
|
clone!(session);
|
|
51
51
|
renderer
|
|
52
|
-
.update_lazy(async move { Ok(session.
|
|
52
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
53
53
|
.await?;
|
|
54
|
+
renderer
|
|
55
|
+
.columns_config_changed
|
|
56
|
+
.emit(columns_configs.clone());
|
|
54
57
|
renderer.column_style_changed.emit(columns_configs);
|
|
55
58
|
Ok(())
|
|
56
59
|
})
|
|
@@ -44,7 +44,7 @@ pub fn send_plugin_config(session: &Session, renderer: &Renderer, update: Column
|
|
|
44
44
|
.restore(&plugin_token, Some(&columns_configs))?;
|
|
45
45
|
clone!(session);
|
|
46
46
|
renderer
|
|
47
|
-
.update_lazy(async move { Ok(session.
|
|
47
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
48
48
|
.await?;
|
|
49
49
|
renderer.plugin_config_changed.emit(plugin_config);
|
|
50
50
|
}
|
|
@@ -51,7 +51,7 @@ pub fn set_edit_mode(session: &Session, renderer: &Renderer, mode: &str) {
|
|
|
51
51
|
.restore(&plugin_token, Some(&columns_configs))?;
|
|
52
52
|
clone!(session);
|
|
53
53
|
renderer
|
|
54
|
-
.update_lazy(async move { Ok(session.
|
|
54
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
55
55
|
.await?;
|
|
56
56
|
renderer.plugin_config_changed.emit(plugin_config);
|
|
57
57
|
}
|