@perspective-dev/viewer 5.3.0 → 5.4.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 +8 -8
- package/dist/cdn/perspective-viewer.js.map +3 -3
- package/dist/css/blueprint.css +1 -0
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -0
- 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 -0
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -0
- 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 -0
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/column-format.d.ts +16 -3
- package/dist/esm/extensions.d.ts +0 -5
- package/dist/esm/perspective-viewer.inline.js +7 -7
- package/dist/esm/perspective-viewer.inline.js.map +3 -3
- package/dist/esm/perspective-viewer.js +8 -8
- package/dist/esm/perspective-viewer.js.map +3 -3
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
- package/dist/wasm/perspective-viewer.d.ts +62 -73
- package/dist/wasm/perspective-viewer.js +230 -238
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +18 -28
- 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 +8 -5
- package/src/css/column-settings-panel.css +0 -206
- package/src/css/column-style.css +95 -0
- package/src/css/config-selector.css +18 -10
- package/src/css/filter-dropdown.css +5 -5
- package/src/css/form/debug.css +28 -4
- package/src/css/function-dropdown.css +5 -5
- package/src/css/panel-tab.css +23 -0
- package/src/css/plugin-selector.css +6 -5
- package/src/css/render-warning.css +1 -1
- package/src/css/status-bar.css +52 -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 +13 -3
- package/src/css/{dom → ui/dom}/select.css +19 -8
- package/src/css/viewer.css +32 -31
- package/src/rust/agent/tools.rs +5 -2
- 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 +15 -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 +3 -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/primitive_field.rs +15 -17
- 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 +296 -238
- 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 +16 -6
- package/src/rust/components/datetime_column_style.rs +51 -12
- package/src/rust/components/{form/debug.rs → debug_panel.rs} +5 -5
- 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/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 +26 -2
- package/src/rust/components/mod.rs +5 -7
- 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 +125 -87
- 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 +3 -2
- package/src/rust/components/status_bar.rs +113 -20
- package/src/rust/components/status_indicator.rs +5 -14
- package/src/rust/components/string_column_style.rs +1 -2
- package/src/rust/components/{style/mod.rs → style.rs} +32 -15
- package/src/rust/components/style_controls/number_string_format/digits_section.rs +25 -15
- package/src/rust/components/style_controls/number_string_format/misc_section.rs +7 -3
- package/src/rust/components/style_controls/number_string_format/style_section.rs +2 -1
- package/src/rust/components/style_controls/number_string_format/types.rs +24 -0
- package/src/rust/components/style_controls/number_string_format.rs +37 -14
- package/src/rust/components/viewer/msg.rs +43 -30
- package/src/rust/components/viewer/panels.rs +50 -19
- package/src/rust/components/viewer/render.rs +15 -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 +1 -21
- 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 +358 -52
- package/src/rust/config/datetime_column_style.rs +4 -21
- package/src/rust/config/mod.rs +0 -2
- package/src/rust/config/number_string_format.rs +211 -18
- package/src/rust/config/workspace_config.rs +2 -1
- 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 +0 -18
- package/src/rust/js/plugin.rs +0 -86
- package/src/rust/lib.rs +12 -15
- package/src/rust/presentation/column_locator.rs +3 -7
- package/src/rust/presentation.rs +85 -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 +2 -2
- package/src/rust/renderer/activate.rs +1 -3
- package/src/rust/renderer/dispatch.rs +117 -30
- package/src/rust/renderer/limits.rs +24 -5
- package/src/rust/renderer/plugin_config.rs +56 -8
- package/src/rust/renderer/props.rs +2 -1
- package/src/rust/renderer.rs +14 -27
- package/src/rust/session/props.rs +4 -3
- package/src/rust/session/view_subscription.rs +19 -6
- package/src/rust/session.rs +38 -70
- package/src/rust/tasks/create_panel.rs +1 -1
- package/src/rust/tasks/dismiss_render_warning.rs +1 -1
- package/src/rust/tasks/eject.rs +1 -0
- package/src/rust/tasks/pipeline.rs +0 -60
- package/src/rust/tasks/presize_panels.rs +24 -1
- package/src/rust/tasks/restore_and_render.rs +0 -17
- package/src/rust/tasks/send_column_config.rs +1 -1
- package/src/rust/tasks/send_plugin_config.rs +1 -1
- package/src/rust/tasks/set_edit_mode.rs +1 -1
- package/src/rust/ui/containers/control_group.rs +113 -0
- package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
- package/src/rust/{components → ui}/containers/mod.rs +1 -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 +8 -8
- 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/ui/form/intl_label.rs +81 -0
- 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 +174 -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/mod.rs +2 -0
- package/src/rust/workspace.rs +35 -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/string-type.svg +4 -8
- package/src/svg/theme-icon.svg +5 -5
- package/src/themes/blueprint.css +133 -0
- package/src/themes/botanical.css +3 -52
- package/src/themes/defaults.css +3 -144
- package/src/themes/dracula.css +1 -71
- package/src/themes/eggplant.css +131 -0
- package/src/themes/gruvbox-dark.css +2 -79
- package/src/themes/gruvbox.css +2 -40
- package/src/themes/icons.css +2 -10
- package/src/themes/intl/ar.css +185 -0
- package/src/themes/intl/de.css +65 -33
- package/src/themes/intl/es.css +60 -28
- package/src/themes/intl/fr.css +60 -28
- package/src/themes/intl/ja.css +62 -30
- package/src/themes/intl/pt.css +58 -26
- package/src/themes/intl/zh.css +68 -36
- package/src/themes/intl.css +42 -7
- package/src/themes/ledger.css +137 -0
- package/src/themes/monokai.css +1 -64
- package/src/themes/nord.css +131 -0
- package/src/themes/phosphor.css +1 -61
- package/src/themes/pro-dark.css +1 -52
- package/src/themes/pro.css +98 -0
- package/src/themes/solarized-dark.css +1 -46
- package/src/themes/solarized.css +1 -24
- package/src/themes/themes.css +10 -5
- package/src/themes/vaporwave.css +1 -57
- package/src/themes/velvet.css +132 -0
- package/src/ts/column-format.ts +52 -2
- package/src/ts/extensions.ts +0 -13
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
- package/src/css/plugin-settings-panel.css +0 -99
- package/src/rust/components/modal.rs +0 -177
- 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/svg/dropdown-selector-light.svg +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-39c3c3b28e3cbd48 → perspective-viewer-adc6f3f79141112b}/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/{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
|
@@ -26,6 +26,7 @@ use super::Renderer;
|
|
|
26
26
|
use crate::config::*;
|
|
27
27
|
use crate::queries::resolve_abs_max;
|
|
28
28
|
use crate::session::Session;
|
|
29
|
+
use crate::utils::{CssKind, CssLiteralUse, parse_var_ref, resolve_css_refs};
|
|
29
30
|
|
|
30
31
|
/// A per-column config map. Each inner [`serde_json::Map`] is a flat collection
|
|
31
32
|
/// of plugin-defined JSON keys whose shape is dictated by the active plugin's
|
|
@@ -133,7 +134,7 @@ impl Renderer {
|
|
|
133
134
|
else {
|
|
134
135
|
continue;
|
|
135
136
|
};
|
|
136
|
-
let needs_warm = schema.
|
|
137
|
+
let needs_warm = schema.leaf_fields().into_iter().any(|f| {
|
|
137
138
|
matches!(
|
|
138
139
|
f,
|
|
139
140
|
ControlSpec::Number {
|
|
@@ -164,7 +165,7 @@ impl Renderer {
|
|
|
164
165
|
continue;
|
|
165
166
|
};
|
|
166
167
|
|
|
167
|
-
for field in
|
|
168
|
+
for field in schema.leaf_fields() {
|
|
168
169
|
let ControlSpec::Number {
|
|
169
170
|
key,
|
|
170
171
|
default,
|
|
@@ -380,7 +381,7 @@ impl Renderer {
|
|
|
380
381
|
let view_config_js = JsValue::from_serde_ext(view_config).unwrap_or(JsValue::NULL);
|
|
381
382
|
let raw = plugin._plugin_config_schema(&view_config_js)?;
|
|
382
383
|
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
383
|
-
.map(|schema| schema.
|
|
384
|
+
.map(|schema| schema.canonicalize())
|
|
384
385
|
.map_err(|e| e.into())
|
|
385
386
|
}
|
|
386
387
|
|
|
@@ -429,7 +430,7 @@ impl Renderer {
|
|
|
429
430
|
)?;
|
|
430
431
|
|
|
431
432
|
serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
|
|
432
|
-
.map(|schema| schema.
|
|
433
|
+
.map(|schema| schema.canonicalize())
|
|
433
434
|
.map_err(|e| e.into())
|
|
434
435
|
}
|
|
435
436
|
|
|
@@ -470,9 +471,9 @@ impl Renderer {
|
|
|
470
471
|
))));
|
|
471
472
|
}
|
|
472
473
|
|
|
474
|
+
let leaves = s.leaf_fields();
|
|
473
475
|
map.retain(|key, value| {
|
|
474
|
-
let is_default =
|
|
475
|
-
.fields
|
|
476
|
+
let is_default = leaves
|
|
476
477
|
.iter()
|
|
477
478
|
.any(|spec| matches_declared_default(spec, key, value));
|
|
478
479
|
if is_default {
|
|
@@ -567,9 +568,9 @@ fn strip_default_values(
|
|
|
567
568
|
schema: &ColumnConfigSchema,
|
|
568
569
|
map: &mut serde_json::Map<String, serde_json::Value>,
|
|
569
570
|
) {
|
|
571
|
+
let leaves = schema.leaf_fields();
|
|
570
572
|
map.retain(|key, value| {
|
|
571
|
-
!
|
|
572
|
-
.fields
|
|
573
|
+
!leaves
|
|
573
574
|
.iter()
|
|
574
575
|
.any(|spec| matches_declared_default(spec, key, value))
|
|
575
576
|
});
|
|
@@ -702,6 +703,53 @@ mod tests {
|
|
|
702
703
|
));
|
|
703
704
|
}
|
|
704
705
|
|
|
706
|
+
#[test]
|
|
707
|
+
fn strip_default_values_sees_through_groups() {
|
|
708
|
+
let leaves = vec![
|
|
709
|
+
ControlSpec::Bool {
|
|
710
|
+
key: "flag".to_owned(),
|
|
711
|
+
default: false,
|
|
712
|
+
},
|
|
713
|
+
ControlSpec::Number {
|
|
714
|
+
key: "size".to_owned(),
|
|
715
|
+
default: 3.0,
|
|
716
|
+
include: None,
|
|
717
|
+
min: None,
|
|
718
|
+
max: None,
|
|
719
|
+
step: None,
|
|
720
|
+
},
|
|
721
|
+
];
|
|
722
|
+
|
|
723
|
+
let flat = ColumnConfigSchema {
|
|
724
|
+
fields: leaves.clone(),
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
let grouped = ColumnConfigSchema {
|
|
728
|
+
fields: vec![ControlSpec::Group {
|
|
729
|
+
key: "section".to_owned(),
|
|
730
|
+
fields: leaves,
|
|
731
|
+
}],
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
let src = json!({ "flag": false, "size": 4.0, "foreign": 1 })
|
|
735
|
+
.as_object()
|
|
736
|
+
.unwrap()
|
|
737
|
+
.clone();
|
|
738
|
+
|
|
739
|
+
let mut a = src.clone();
|
|
740
|
+
let mut b = src;
|
|
741
|
+
strip_default_values(&flat, &mut a);
|
|
742
|
+
strip_default_values(&grouped, &mut b);
|
|
743
|
+
assert_eq!(a, b);
|
|
744
|
+
assert_eq!(
|
|
745
|
+
a,
|
|
746
|
+
json!({ "size": 4.0, "foreign": 1 })
|
|
747
|
+
.as_object()
|
|
748
|
+
.unwrap()
|
|
749
|
+
.clone()
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
|
|
705
753
|
#[test]
|
|
706
754
|
fn normalize_css_values_canonicalizes_and_reports() {
|
|
707
755
|
let schema = ColumnConfigSchema {
|
|
@@ -30,7 +30,8 @@ pub struct RendererProps {
|
|
|
30
30
|
/// internal `metadata` — `Rc::ptr_eq` works.
|
|
31
31
|
pub config: Rc<PluginStaticConfig>,
|
|
32
32
|
|
|
33
|
-
///
|
|
33
|
+
/// The most recently emitted render limits, only while a cap is in
|
|
34
|
+
/// effect.
|
|
34
35
|
pub render_limits: Option<RenderLimits>,
|
|
35
36
|
|
|
36
37
|
/// Names of all registered plugins, in registration order.
|
package/src/rust/renderer.rs
CHANGED
|
@@ -120,6 +120,10 @@ pub struct RendererData {
|
|
|
120
120
|
/// [`Renderer::presize_with_box`] presize calls.
|
|
121
121
|
presize_pending: Cell<u32>,
|
|
122
122
|
|
|
123
|
+
/// The `(width, height)` the plugin last painted at through a presize,
|
|
124
|
+
/// consumed by the post-commit reactive resize.
|
|
125
|
+
presized_box: Cell<Option<(f64, f64)>>,
|
|
126
|
+
|
|
123
127
|
/// Debounce slot for geometry tasks (`resize` / presize), separate from
|
|
124
128
|
/// the default slot data-update redraws coalesce on. Invariant: a
|
|
125
129
|
/// geometry request must never resolve via a parked DATA task — a
|
|
@@ -160,14 +164,7 @@ pub struct RendererData {
|
|
|
160
164
|
|
|
161
165
|
/// The effective theme stamped at the active plugin's last `--psp-*`
|
|
162
166
|
/// CSS CAPTURE — its first paint ([`Renderer::draw_view`]) or last
|
|
163
|
-
/// [`Renderer::restyle_all`].
|
|
164
|
-
/// state behind [`Renderer::needs_restyle`]: restyle necessity is a
|
|
165
|
-
/// comparison against what the plugin actually captured, never
|
|
166
|
-
/// inferred from which call site mutated a theme record (the
|
|
167
|
-
/// raycasting boot double render — see
|
|
168
|
-
/// `PLUGIN_DRAW_INVARIANT_PLAN.md`, captured-theme revision). Same
|
|
169
|
-
/// lifecycle as [`Self::has_drawn`]: cleared on plugin swap,
|
|
170
|
-
/// `dispose` and `delete`; written only inside locked dispatches.
|
|
167
|
+
/// [`Renderer::restyle_all`].
|
|
171
168
|
captured_theme: RefCell<Option<Option<String>>>,
|
|
172
169
|
|
|
173
170
|
/// The [`RenderContext`] of the currently-bound `View` (built at bind
|
|
@@ -268,6 +265,7 @@ impl Renderer {
|
|
|
268
265
|
plugin_config_changed: Default::default(),
|
|
269
266
|
render_warning: Cell::new(true),
|
|
270
267
|
presize_pending: Cell::new(0),
|
|
268
|
+
presized_box: Cell::new(None),
|
|
271
269
|
on_render_limits_changed: Default::default(),
|
|
272
270
|
slot_name: Default::default(),
|
|
273
271
|
theme: Default::default(),
|
|
@@ -315,14 +313,7 @@ impl Renderer {
|
|
|
315
313
|
|
|
316
314
|
/// Whether the active plugin's captured `--psp-*` CSS is STALE — the
|
|
317
315
|
/// effective theme differs from the one stamped at the plugin's last
|
|
318
|
-
/// CSS capture (first paint / last restyle).
|
|
319
|
-
/// every theme-driven `restyle_all` (the `has_drawn` analog for
|
|
320
|
-
/// `restyle` — `PLUGIN_DRAW_INVARIANT_PLAN.md`, captured-theme
|
|
321
|
-
/// revision): a caller that just mutated a theme RECORD may not need a
|
|
322
|
-
/// restyle at all if the plugin's capture already reflects the new
|
|
323
|
-
/// value (e.g. the same restore also performed the first paint,
|
|
324
|
-
/// post-stamp). `false` when no capture exists yet — the owed first
|
|
325
|
-
/// paint captures fresh by construction ("stamp before draw").
|
|
316
|
+
/// CSS capture (first paint / last restyle).
|
|
326
317
|
pub fn needs_restyle(&self) -> bool {
|
|
327
318
|
match &*self.0.captured_theme.borrow() {
|
|
328
319
|
Some(captured) => *captured != self.theme(),
|
|
@@ -336,17 +327,6 @@ impl Renderer {
|
|
|
336
327
|
/// [`Self::delete`], which clears the entire light DOM). Removes the
|
|
337
328
|
/// plugin (`slot=<id>`) and any panel-scoped aux element it mounted
|
|
338
329
|
/// (e.g. the datagrid toolbar, `slot=statusbar-extra-<id>`).
|
|
339
|
-
///
|
|
340
|
-
/// The teardown is DEFERRED through this renderer's draw lock: spawned
|
|
341
|
-
/// draws are uncancellable, so a synchronous `plugin.delete()` here could
|
|
342
|
-
/// land mid-`plugin.draw()`, which the serialized-call contract forbids
|
|
343
|
-
/// (see [`crate::js::plugin`]). Deferral is invisible — the caller
|
|
344
|
-
/// (`eject_panel`) has already removed the panel from the `Workspace`, so
|
|
345
|
-
/// the same commit unmounts its frame and the plugin element sits
|
|
346
|
-
/// unslotted (unrendered) until teardown lands; panel ids are never
|
|
347
|
-
/// reused, so it can't collide with a replacement panel's slot. Draws
|
|
348
|
-
/// still queued behind the lock no-op via the synchronous session reset
|
|
349
|
-
/// (`get_view()` → `None`).
|
|
350
330
|
pub fn dispose(&self) -> ApiFuture<()> {
|
|
351
331
|
self.0.has_drawn.set(false);
|
|
352
332
|
self.0.captured_theme.borrow_mut().take();
|
|
@@ -488,6 +468,12 @@ impl Renderer {
|
|
|
488
468
|
Ok(self.0.has_drawn.get())
|
|
489
469
|
}
|
|
490
470
|
|
|
471
|
+
/// Take the box the plugin last painted at through a presize (see
|
|
472
|
+
/// `RendererData::presized_box`).
|
|
473
|
+
pub fn take_presized_box(&self) -> Option<(f64, f64)> {
|
|
474
|
+
self.0.presized_box.take()
|
|
475
|
+
}
|
|
476
|
+
|
|
491
477
|
/// Mount the selected plugin element into the viewer's light DOM without
|
|
492
478
|
/// drawing it (see [`activate::mount_plugin`] — idempotent). Pure query:
|
|
493
479
|
/// a renderer with no selection is a no-op, deferring to the draw path's
|
|
@@ -695,6 +681,7 @@ impl Renderer {
|
|
|
695
681
|
/// suitable for passing as a Yew prop. Called by the root component
|
|
696
682
|
/// whenever a renderer-related PubSub event fires.
|
|
697
683
|
pub fn to_props(&self, render_limits: Option<RenderLimits>) -> RendererProps {
|
|
684
|
+
let render_limits = render_limits.filter(RenderLimits::is_capped);
|
|
698
685
|
let has_plugin = self.active_plugin().is_some();
|
|
699
686
|
if has_plugin {
|
|
700
687
|
let config = self.metadata();
|
|
@@ -19,7 +19,7 @@ use crate::session::column_defaults_update::ViewConfigUpdateExt;
|
|
|
19
19
|
use crate::session::drag_drop_update::ViewConfigExt as DragDropExt;
|
|
20
20
|
use crate::session::metadata::SessionMetadataRc;
|
|
21
21
|
use crate::session::replace_expression_update::ViewConfigExt as ReplaceExprExt;
|
|
22
|
-
use crate::session::{ColumnStats, TableErrorState
|
|
22
|
+
use crate::session::{ColumnStats, TableErrorState};
|
|
23
23
|
use crate::utils::*;
|
|
24
24
|
|
|
25
25
|
#[derive(Clone, Debug, PartialEq, Default)]
|
|
@@ -39,8 +39,9 @@ pub struct SessionProps {
|
|
|
39
39
|
/// The current `ViewConfig` driving the active `View`.
|
|
40
40
|
pub config: PtrEqRc<ViewConfig>,
|
|
41
41
|
|
|
42
|
-
///
|
|
43
|
-
|
|
42
|
+
/// Whether the view stats carry table dimensions yet, the status
|
|
43
|
+
/// indicator's loaded/uninitialized split.
|
|
44
|
+
pub has_table_cells: bool,
|
|
44
45
|
|
|
45
46
|
/// `true` if a `Table` has been loaded into this session.
|
|
46
47
|
pub has_table: Option<TableLoadState>,
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use std::cell::Cell;
|
|
13
|
+
use std::cell::{Cell, RefCell};
|
|
14
14
|
use std::rc::Rc;
|
|
15
15
|
|
|
16
16
|
use perspective_client::config::*;
|
|
17
|
+
use perspective_client::proto::ViewDimensionsResp;
|
|
17
18
|
use perspective_client::{OnUpdateOptions, View};
|
|
18
19
|
use wasm_bindgen::prelude::*;
|
|
19
20
|
use yew::prelude::*;
|
|
@@ -41,6 +42,10 @@ struct ViewSubscriptionData {
|
|
|
41
42
|
on_stats: Callback<ViewStats>,
|
|
42
43
|
on_update: Option<Callback<()>>,
|
|
43
44
|
is_deleted: Rc<Cell<bool>>,
|
|
45
|
+
|
|
46
|
+
/// The bound `View`'s latest known dimensions, written before
|
|
47
|
+
/// `on_update`/`on_stats` fire.
|
|
48
|
+
dimensions: Rc<RefCell<Option<ViewDimensionsResp>>>,
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/// A subscription to `on_update()` events from a Perspective `View()`, managing
|
|
@@ -52,20 +57,21 @@ pub struct ViewSubscription {
|
|
|
52
57
|
impl ViewSubscriptionData {
|
|
53
58
|
/// Main handler when underlying `View()` calls `on_update()`.
|
|
54
59
|
async fn on_view_update(self) -> ApiResult<JsValue> {
|
|
60
|
+
self.clone().update_view_stats().await?;
|
|
55
61
|
if let Some(on_update) = &self.on_update {
|
|
56
62
|
on_update.emit(());
|
|
57
63
|
};
|
|
58
64
|
|
|
59
|
-
self.clone().update_view_stats().await?;
|
|
60
65
|
Ok(JsValue::UNDEFINED)
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
async fn update_view_stats(self) -> ApiResult<JsValue> {
|
|
64
69
|
let dimensions = self.view.dimensions().await?;
|
|
65
|
-
let num_rows = dimensions.num_table_rows
|
|
66
|
-
let num_cols = dimensions.num_table_columns
|
|
67
|
-
let virtual_rows = dimensions.num_view_rows
|
|
68
|
-
let virtual_cols = dimensions.num_view_columns
|
|
70
|
+
let num_rows = dimensions.num_table_rows;
|
|
71
|
+
let num_cols = dimensions.num_table_columns;
|
|
72
|
+
let virtual_rows = dimensions.num_view_rows;
|
|
73
|
+
let virtual_cols = dimensions.num_view_columns;
|
|
74
|
+
*self.dimensions.borrow_mut() = Some(dimensions);
|
|
69
75
|
let stats = ViewStats {
|
|
70
76
|
num_table_cells: Some((num_rows, num_cols)),
|
|
71
77
|
num_view_cells: Some((virtual_rows, virtual_cols)),
|
|
@@ -119,6 +125,7 @@ impl ViewSubscription {
|
|
|
119
125
|
callback_id: Rc::default(),
|
|
120
126
|
on_update,
|
|
121
127
|
is_deleted: Rc::default(),
|
|
128
|
+
dimensions: Rc::default(),
|
|
122
129
|
};
|
|
123
130
|
|
|
124
131
|
if data.on_update.is_some() {
|
|
@@ -160,6 +167,12 @@ impl ViewSubscription {
|
|
|
160
167
|
&self.data.view
|
|
161
168
|
}
|
|
162
169
|
|
|
170
|
+
/// The bound `View`'s latest known dimensions (`None` until the first
|
|
171
|
+
/// fetch resolves).
|
|
172
|
+
pub fn dimensions(&self) -> Option<ViewDimensionsResp> {
|
|
173
|
+
self.data.dimensions.borrow().clone()
|
|
174
|
+
}
|
|
175
|
+
|
|
163
176
|
/// User-facing snapshot of the [`ViewConfig`] the bound `View` was
|
|
164
177
|
/// constructed from (the persisted config at build time — global-filter
|
|
165
178
|
/// overlay excluded). This is the [`ViewConfig`] consistent with the
|
package/src/rust/session.rs
CHANGED
|
@@ -24,6 +24,7 @@ use std::ops::Deref;
|
|
|
24
24
|
use std::rc::Rc;
|
|
25
25
|
|
|
26
26
|
use perspective_client::config::*;
|
|
27
|
+
use perspective_client::proto::ViewDimensionsResp;
|
|
27
28
|
use perspective_client::{Client, ClientError, ReconnectCallback, View};
|
|
28
29
|
use perspective_js::apierror;
|
|
29
30
|
use perspective_js::utils::*;
|
|
@@ -56,11 +57,7 @@ pub struct SessionHandle {
|
|
|
56
57
|
pub table_loaded: PubSub<()>,
|
|
57
58
|
pub table_unloaded: PubSub<bool>,
|
|
58
59
|
|
|
59
|
-
/// Fires when a `View` was CREATED — literally
|
|
60
|
-
/// path constructed and bound a new engine `View`. Nothing else may
|
|
61
|
-
/// emit this (a SKIP/REUSE/paused reconcile is [`Self::commit_reconciled`],
|
|
62
|
-
/// which REBUILD also emits) — overloading it broke consumers that
|
|
63
|
-
/// relied on the name's meaning (see `UPDATE_COUNT_REGRESSION_PLAN.md`).
|
|
60
|
+
/// Fires when a `View` was CREATED — literally.
|
|
64
61
|
pub view_created: PubSub<()>,
|
|
65
62
|
|
|
66
63
|
/// Fires exactly once per locked run that RECONCILED the committed
|
|
@@ -74,43 +71,23 @@ pub struct SessionHandle {
|
|
|
74
71
|
pub view_config_changed: PubSub<()>,
|
|
75
72
|
pub title_changed: PubSub<Option<String>>,
|
|
76
73
|
|
|
77
|
-
/// Count of in-flight CONFIG-DRIVEN pipeline runs
|
|
78
|
-
/// settled), incremented by [`Session::begin_config_run`] and
|
|
79
|
-
/// decremented when the returned [`ConfigRunToken`] drops. Drives the
|
|
80
|
-
/// `StatusIndicator` "updating" spinner via [`Self::run_state_changed`].
|
|
81
|
-
/// Pure redraws (resize, activation nudges, `just_render`) do NOT
|
|
82
|
-
/// count — the spinner means "a config requery is in flight".
|
|
74
|
+
/// Count of in-flight CONFIG-DRIVEN pipeline runs.
|
|
83
75
|
in_flight_config_runs: Cell<u32>,
|
|
84
76
|
|
|
85
77
|
/// Fires with the ABSOLUTE [`Self::in_flight_config_runs`] count after
|
|
86
|
-
/// every change.
|
|
87
|
-
/// payload rather than accumulate deltas, so a dropped or reordered
|
|
88
|
-
/// notification is corrected by the next one — the delta-counting
|
|
89
|
-
/// (`view_config_changed`+1 / `view_created`−1) design this replaces
|
|
90
|
-
/// drifted whenever those streams weren't perfectly paired (see
|
|
91
|
-
/// `UPDATE_COUNT_REGRESSION_PLAN.md`).
|
|
78
|
+
/// every change.
|
|
92
79
|
pub run_state_changed: PubSub<u32>,
|
|
93
80
|
|
|
94
81
|
/// Fires when the user clicks the status indicator while in
|
|
95
|
-
/// [`StatusIconState::Normal`].
|
|
96
|
-
/// listener and fans this out as the `perspective-status-indicator-click`
|
|
97
|
-
/// `CustomEvent`.
|
|
82
|
+
/// [`StatusIconState::Normal`].
|
|
98
83
|
pub status_indicator_clicked: PubSub<()>,
|
|
99
84
|
|
|
100
|
-
/// Per-column numeric stats cache.
|
|
101
|
-
/// `fetch_column_abs_max` task and consumed by the schema-query
|
|
102
|
-
/// path so plugins emit gradient defaults without
|
|
103
|
-
/// per-render `View::get_min_max` round trips. Cleared
|
|
104
|
-
/// synchronously inside [`Session::commit_view_config`].
|
|
85
|
+
/// Per-column numeric stats cache.
|
|
105
86
|
column_stats: RefCell<HashMap<String, ColumnStats>>,
|
|
106
87
|
|
|
107
88
|
/// Memoized snapshots used by [`Session::to_props`] to keep
|
|
108
89
|
/// `PtrEqRc` identity stable across repeated `to_props()` calls
|
|
109
|
-
/// when the underlying value hasn't changed.
|
|
110
|
-
/// `PubSub` fire (including `column_stats_changed`) produces a
|
|
111
|
-
/// fresh `Rc` for `config` / `metadata`, triggering downstream
|
|
112
|
-
/// `use_effect_with(view_config, ...)` effects to spuriously
|
|
113
|
-
/// refire — closing a loop with the stats fetch path.
|
|
90
|
+
/// when the underlying value hasn't changed.
|
|
114
91
|
cached_config: RefCell<Option<PtrEqRc<ViewConfig>>>,
|
|
115
92
|
cached_metadata: RefCell<Option<SessionMetadataRc>>,
|
|
116
93
|
|
|
@@ -131,18 +108,7 @@ pub struct SessionHandle {
|
|
|
131
108
|
load_generation: Cell<u32>,
|
|
132
109
|
|
|
133
110
|
/// Open between a `load()` call and its payload's classification as
|
|
134
|
-
/// `Table`/`Client` ([`PendingLoad`]).
|
|
135
|
-
/// unknown, so its RESET/no-RESET disposition cannot be decided yet — but
|
|
136
|
-
/// the window's POSITION on the config-commit stream is fixed at the
|
|
137
|
-
/// `load()` call site. During the window, config commits still apply live
|
|
138
|
-
/// (so `save()` stays coherent — I1) but ALSO append their raw delta to
|
|
139
|
-
/// the journal, and config-driven binds DEFER ([`Session::snapshot`]'s
|
|
140
|
-
/// caller [`crate::tasks::bind_snapshot`]) — the incoming config must
|
|
141
|
-
/// never draw against the still-bound outgoing table (hold the last
|
|
142
|
-
/// frame). Classified as `Table` the journal replays over a reset base
|
|
143
|
-
/// (`reset ∘ Δ₁ ∘ Δ₂ …`, the program-order result the old synchronous
|
|
144
|
-
/// reset guaranteed); as `Client` it is dropped (no reset — live already
|
|
145
|
-
/// carries every Δ). See `SESSION_CONFIG_COHERENCE_PLAN.md`.
|
|
111
|
+
/// `Table`/`Client` ([`PendingLoad`]).
|
|
146
112
|
pending_load: RefCell<Option<PendingLoad>>,
|
|
147
113
|
|
|
148
114
|
/// Coalesces `view_config_changed`: multiple synchronous commits in one
|
|
@@ -165,8 +131,11 @@ pub struct SessionHandle {
|
|
|
165
131
|
/// new value.
|
|
166
132
|
pub column_stats_changed: PubSub<()>,
|
|
167
133
|
|
|
168
|
-
///
|
|
169
|
-
|
|
134
|
+
/// Fires when view stats are updated.
|
|
135
|
+
pub stats_changed: PubSub<()>,
|
|
136
|
+
|
|
137
|
+
/// Injected callback from the root component, driving
|
|
138
|
+
/// `session_props.has_table_cells` for the active panel.
|
|
170
139
|
pub on_stats_changed: RefCell<Option<Callback<()>>>,
|
|
171
140
|
|
|
172
141
|
/// Injected callback from the root component, replacing the former
|
|
@@ -279,13 +248,7 @@ pub struct ResetOptions {
|
|
|
279
248
|
#[derive(Clone)]
|
|
280
249
|
pub struct Session(Rc<SessionHandle>);
|
|
281
250
|
|
|
282
|
-
/// RAII spinner accounting for one config-driven pipeline run
|
|
283
|
-
/// [`Session::begin_config_run`] and `UPDATE_COUNT_REGRESSION_PLAN.md`):
|
|
284
|
-
/// created when the run is scheduled — immediately after its commit — and
|
|
285
|
-
/// moved INTO the run future, so `Drop` settles the count on every exit
|
|
286
|
-
/// path: completion, error, cancellation, and runs that never reach
|
|
287
|
-
/// `bind_view` (the deferred-draw restore). A stranded count is
|
|
288
|
-
/// unrepresentable; there is no other writer.
|
|
251
|
+
/// RAII spinner accounting for one config-driven pipeline run.
|
|
289
252
|
pub struct ConfigRunToken(Session);
|
|
290
253
|
|
|
291
254
|
impl Drop for ConfigRunToken {
|
|
@@ -664,10 +627,27 @@ impl Session {
|
|
|
664
627
|
.map(|sub| sub.get_view().clone())
|
|
665
628
|
}
|
|
666
629
|
|
|
630
|
+
/// The bound `View` together with its latest known dimensions, read under
|
|
631
|
+
/// one borrow so they always belong together.
|
|
632
|
+
pub fn get_view_with_dimensions(&self) -> Option<(View, Option<ViewDimensionsResp>)> {
|
|
633
|
+
self.borrow()
|
|
634
|
+
.view_sub
|
|
635
|
+
.as_ref()
|
|
636
|
+
.map(|sub| (sub.get_view().clone(), sub.dimensions()))
|
|
637
|
+
}
|
|
638
|
+
|
|
667
639
|
pub(crate) fn get_table_stats(&self) -> Option<ViewStats> {
|
|
668
640
|
self.borrow().stats.clone()
|
|
669
641
|
}
|
|
670
642
|
|
|
643
|
+
/// Whether the stats snapshot carries table dimensions.
|
|
644
|
+
pub(crate) fn has_table_cells(&self) -> bool {
|
|
645
|
+
self.borrow()
|
|
646
|
+
.stats
|
|
647
|
+
.as_ref()
|
|
648
|
+
.is_some_and(|s| s.num_table_cells.is_some())
|
|
649
|
+
}
|
|
650
|
+
|
|
671
651
|
pub fn get_view_config(&'_ self) -> Ref<'_, ViewConfig> {
|
|
672
652
|
Ref::map(self.borrow(), |x| &x.config)
|
|
673
653
|
}
|
|
@@ -924,15 +904,6 @@ impl Session {
|
|
|
924
904
|
/// Emit `view_config_changed` coalesced to one event per microtask batch.
|
|
925
905
|
/// Stats are cleared synchronously per commit (idempotent — an
|
|
926
906
|
/// already-empty cache no-ops, so rapid commit sequences fetch once).
|
|
927
|
-
///
|
|
928
|
-
/// CONTRACT: this channel is COALESCED and DEFERRED — N same-batch
|
|
929
|
-
/// commits produce ONE emission, on a microtask AFTER the mutation.
|
|
930
|
-
/// Subscribers must be batch-tolerant snapshot refreshers (they re-read
|
|
931
|
-
/// live state; today: the root `SessionProps` snapshot and the debug
|
|
932
|
-
/// panel). It is NOT suitable for per-commit accounting — pairing its
|
|
933
|
-
/// edges against any other event stream WILL drift (the stuck-spinner
|
|
934
|
-
/// regression, `UPDATE_COUNT_REGRESSION_PLAN.md`); run accounting is
|
|
935
|
-
/// [`Self::begin_config_run`]'s RAII token instead.
|
|
936
907
|
fn notify_view_config_changed(&self) {
|
|
937
908
|
self.clear_column_stats();
|
|
938
909
|
if !self.0.config_event_scheduled.replace(true) {
|
|
@@ -1218,6 +1189,8 @@ impl Session {
|
|
|
1218
1189
|
if let Some(cb) = self.on_stats_changed.borrow().as_ref() {
|
|
1219
1190
|
cb.emit(());
|
|
1220
1191
|
}
|
|
1192
|
+
|
|
1193
|
+
self.stats_changed.emit(());
|
|
1221
1194
|
}
|
|
1222
1195
|
|
|
1223
1196
|
fn all_columns(&self) -> Vec<String> {
|
|
@@ -1258,7 +1231,10 @@ impl Session {
|
|
|
1258
1231
|
|
|
1259
1232
|
SessionProps {
|
|
1260
1233
|
config,
|
|
1261
|
-
|
|
1234
|
+
has_table_cells: data
|
|
1235
|
+
.stats
|
|
1236
|
+
.as_ref()
|
|
1237
|
+
.is_some_and(|s| s.num_table_cells.is_some()),
|
|
1262
1238
|
has_table: if data.table.is_some() {
|
|
1263
1239
|
Some(TableLoadState::Loaded)
|
|
1264
1240
|
} else if data.is_loading {
|
|
@@ -1290,15 +1266,7 @@ pub struct ConfigSnapshot {
|
|
|
1290
1266
|
pub struct ValidatedSnapshot(ConfigSnapshot);
|
|
1291
1267
|
|
|
1292
1268
|
/// Type-state witness that a `View` is NEW for the plugin about to render
|
|
1293
|
-
/// it
|
|
1294
|
-
/// `draw` fires iff there is a new `View`, never otherwise). Minted in
|
|
1295
|
-
/// exactly two places: [`Session::bind_view`]'s REBUILD arm (a new engine
|
|
1296
|
-
/// `View` was constructed) and
|
|
1297
|
-
/// [`crate::renderer::Renderer::promote_first_paint`] (a freshly-selected
|
|
1298
|
-
/// plugin owes its first paint of the bound `View`). Do NOT construct it
|
|
1299
|
-
/// anywhere else — `Renderer::draw_fresh` is the only consumer, and this
|
|
1300
|
-
/// witness is what makes "full draw without a new `View`" (the stacked-chart
|
|
1301
|
-
/// activation regression) uncompilable.
|
|
1269
|
+
/// it.
|
|
1302
1270
|
pub struct FreshView(View);
|
|
1303
1271
|
|
|
1304
1272
|
impl FreshView {
|
|
@@ -60,7 +60,7 @@ fn wire_panel_render_sub(session: &Session, renderer: &Renderer) -> Subscription
|
|
|
60
60
|
clone!(renderer, session);
|
|
61
61
|
ApiFuture::spawn(async move {
|
|
62
62
|
renderer
|
|
63
|
-
.update_lazy(async move { Ok(session.
|
|
63
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
64
64
|
.await
|
|
65
65
|
.ignore_view_delete()
|
|
66
66
|
.map(|_| ())
|
|
@@ -28,7 +28,7 @@ pub fn dismiss_render_warning_callback(session: &Session, renderer: &Renderer) -
|
|
|
28
28
|
ApiFuture::spawn(async move {
|
|
29
29
|
renderer.disable_active_plugin_render_warning();
|
|
30
30
|
renderer
|
|
31
|
-
.update_lazy(async move { Ok(session.
|
|
31
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
32
32
|
.await
|
|
33
33
|
});
|
|
34
34
|
})
|
package/src/rust/tasks/eject.rs
CHANGED
|
@@ -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,
|
|
@@ -343,31 +308,6 @@ async fn render_run(
|
|
|
343
308
|
}
|
|
344
309
|
}
|
|
345
310
|
|
|
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
311
|
pub async fn activation_render(session: Session, renderer: Renderer) -> ApiResult<()> {
|
|
372
312
|
let result = {
|
|
373
313
|
clone!(session, renderer);
|