@perspective-dev/viewer 5.2.0 → 5.3.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.
Files changed (133) hide show
  1. package/dist/cdn/perspective-viewer.js +2 -2
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/monokai.css +1 -1
  9. package/dist/css/phosphor.css +1 -1
  10. package/dist/css/pro-dark.css +1 -1
  11. package/dist/css/pro.css +1 -1
  12. package/dist/css/solarized-dark.css +1 -1
  13. package/dist/css/solarized.css +1 -1
  14. package/dist/css/themes.css +1 -1
  15. package/dist/css/vaporwave.css +1 -1
  16. package/dist/docs/perspective-docs.json +1 -1
  17. package/dist/esm/perspective-viewer.d.ts +1 -0
  18. package/dist/esm/perspective-viewer.inline.js +9 -9
  19. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  20. package/dist/esm/perspective-viewer.js +4 -4
  21. package/dist/esm/perspective-viewer.js.map +2 -2
  22. package/dist/esm/plugin.d.ts +10 -0
  23. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +1 -1
  24. package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
  25. package/dist/esm/ts-rs/ViewerConfig.d.ts +1 -1
  26. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
  27. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +13 -3
  28. package/dist/wasm/perspective-viewer.d.ts +43 -36
  29. package/dist/wasm/perspective-viewer.js +194 -136
  30. package/dist/wasm/perspective-viewer.wasm +0 -0
  31. package/dist/wasm/perspective-viewer.wasm.d.ts +19 -17
  32. package/package.json +1 -1
  33. package/src/css/column-settings-panel.css +9 -5
  34. package/src/css/column-style.css +209 -31
  35. package/src/css/dom/select.css +2 -1
  36. package/src/css/form/code-editor.css +4 -4
  37. package/src/rust/agent/tools.rs +48 -17
  38. package/src/rust/components/column_selector/active_column.rs +2 -2
  39. package/src/rust/components/column_selector/add_expression_button.rs +3 -3
  40. package/src/rust/components/column_selector/aggregate_selector.rs +2 -3
  41. package/src/rust/components/column_selector/config_selector.rs +8 -0
  42. package/src/rust/components/column_selector/expr_edit_button.rs +4 -10
  43. package/src/rust/components/column_selector/inactive_column.rs +2 -2
  44. package/src/rust/components/column_selector.rs +48 -4
  45. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +293 -126
  46. package/src/rust/components/column_settings_sidebar/style_tab.rs +262 -225
  47. package/src/rust/components/column_settings_sidebar.rs +88 -69
  48. package/src/rust/components/editable_header.rs +85 -144
  49. package/src/rust/components/expression_editor.rs +9 -16
  50. package/src/rust/components/form/code_editor.rs +16 -6
  51. package/src/rust/components/form/debug.rs +1 -0
  52. package/src/rust/components/form/mod.rs +3 -1
  53. package/src/rust/components/form/multi_stop_gradient_selector.rs +476 -0
  54. package/src/rust/components/form/named_value_picker.rs +86 -0
  55. package/src/rust/components/form/palette_selector.rs +440 -0
  56. package/src/rust/components/main_panel/msg.rs +2 -2
  57. package/src/rust/components/main_panel/reconcile.rs +4 -4
  58. package/src/rust/components/plugin_tab.rs +4 -28
  59. package/src/rust/components/settings_panel.rs +25 -20
  60. package/src/rust/components/viewer/msg.rs +46 -7
  61. package/src/rust/components/viewer/panels.rs +11 -9
  62. package/src/rust/components/viewer/render.rs +8 -3
  63. package/src/rust/components/viewer/settings.rs +163 -23
  64. package/src/rust/components/viewer/snapshots.rs +46 -21
  65. package/src/rust/components/viewer/wiring.rs +14 -25
  66. package/src/rust/components/viewer.rs +12 -8
  67. package/src/rust/config/column_config_schema.rs +100 -41
  68. package/src/rust/config/css_scale.rs +882 -0
  69. package/src/rust/config/mod.rs +2 -0
  70. package/src/rust/config/options.rs +10 -0
  71. package/src/rust/config/viewer_config.rs +49 -55
  72. package/src/rust/config/workspace_config.rs +132 -5
  73. package/src/rust/custom_elements/viewer.rs +214 -155
  74. package/src/rust/custom_events.rs +14 -1
  75. package/src/rust/js/regular_layout.rs +3 -3
  76. package/src/rust/lib.rs +2 -0
  77. package/src/rust/presentation/column_locator.rs +19 -5
  78. package/src/rust/presentation.rs +192 -36
  79. package/src/rust/queries/column_locator.rs +74 -44
  80. package/src/rust/queries/get_viewer_config.rs +0 -3
  81. package/src/rust/queries/mod.rs +2 -0
  82. package/src/rust/queries/palette_set.rs +82 -0
  83. package/src/rust/queries/plugin_column_styles.rs +6 -2
  84. package/src/rust/renderer/dispatch.rs +16 -4
  85. package/src/rust/renderer/plugin_config.rs +309 -47
  86. package/src/rust/renderer.rs +15 -42
  87. package/src/rust/session/column_defaults_update.rs +15 -8
  88. package/src/rust/session/metadata.rs +5 -1
  89. package/src/rust/session/props.rs +1 -0
  90. package/src/rust/session.rs +9 -6
  91. package/src/rust/tasks/create_panel.rs +44 -57
  92. package/src/rust/tasks/edit_expression.rs +19 -23
  93. package/src/rust/tasks/edit_window.rs +8 -10
  94. package/src/rust/tasks/eject.rs +4 -1
  95. package/src/rust/tasks/pipeline.rs +2 -2
  96. package/src/rust/tasks/presize_panels.rs +44 -5
  97. package/src/rust/tasks/reset_all.rs +4 -4
  98. package/src/rust/tasks/restore_and_render.rs +17 -44
  99. package/src/rust/tasks/restore_panel.rs +11 -13
  100. package/src/rust/tasks/set_edit_mode.rs +3 -1
  101. package/src/rust/tasks/sync_update_panels.rs +11 -1
  102. package/src/rust/tasks/update_theme.rs +23 -18
  103. package/src/rust/utils/css_vars.rs +26 -0
  104. package/src/rust/utils/mod.rs +2 -0
  105. package/src/rust/utils/spawn.rs +1 -1
  106. package/src/rust/workspace.rs +435 -272
  107. package/src/svg/lock.svg +7 -0
  108. package/src/themes/botanical.css +10 -0
  109. package/src/themes/defaults.css +12 -0
  110. package/src/themes/dracula.css +17 -0
  111. package/src/themes/gruvbox-dark.css +20 -0
  112. package/src/themes/gruvbox.css +10 -0
  113. package/src/themes/icons.css +1 -0
  114. package/src/themes/monokai.css +22 -0
  115. package/src/themes/phosphor.css +5 -0
  116. package/src/themes/pro-dark.css +10 -0
  117. package/src/themes/solarized-dark.css +7 -0
  118. package/src/themes/solarized.css +7 -0
  119. package/src/themes/vaporwave.css +20 -0
  120. package/src/ts/perspective-viewer.ts +1 -0
  121. package/src/ts/plugin.ts +14 -0
  122. package/src/ts/ts-rs/PanelViewerConfig.ts +1 -1
  123. package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
  124. package/src/ts/ts-rs/ViewerConfig.ts +1 -1
  125. package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
  126. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +11 -3
  127. package/src/rust/components/form/color_range_selector.rs +0 -137
  128. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
  129. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
  130. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
  131. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
  132. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
  133. /package/dist/wasm/snippets/{perspective-viewer-02e461f3d7062b26 → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
@@ -166,7 +166,7 @@ impl Component for PerspectiveViewer {
166
166
  let renderer_props = active_renderer.to_props(None);
167
167
  let presentation_props = ctx.props().presentation.to_props(PtrEqRc::new(vec![]));
168
168
  let on_close_column_settings = ctx.link().callback(|_| OpenColumnSettings {
169
- locator: None,
169
+ target: None,
170
170
  sender: None,
171
171
  toggle: false,
172
172
  });
@@ -319,8 +319,8 @@ impl Component for PerspectiveViewer {
319
319
  ClearGlobalFilters => self.on_clear_global_filters(ctx),
320
320
 
321
321
  // Settings sidebar + divider pump + column settings (`settings.rs`)
322
- ToggleSettingsInit(update, resolve) => {
323
- self.on_toggle_settings_init(ctx, update, resolve)
322
+ ToggleSettingsInit(update, announce, resolve) => {
323
+ self.on_toggle_settings_init(ctx, update, announce, resolve)
324
324
  },
325
325
  ToggleSettingsComplete(update, resolve) => {
326
326
  self.on_toggle_settings_complete(ctx, update, resolve)
@@ -333,25 +333,29 @@ impl Component for PerspectiveViewer {
333
333
  SettingsPanelTabChanged(tab) => self.on_settings_panel_tab_changed(tab),
334
334
  SettingsPanelAutoWidth(w) => self.on_settings_panel_auto_width(w),
335
335
  OpenColumnSettings {
336
- locator,
336
+ target,
337
337
  sender,
338
338
  toggle,
339
- } => self.on_open_column_settings(ctx, locator, sender, toggle),
339
+ } => self.on_open_column_settings(ctx, target, sender, toggle),
340
340
  ColumnSettingsPanelSizeUpdate(x) => self.on_column_settings_panel_size_update(x),
341
341
  ColumnSettingsPanelAutoWidth(w) => self.on_column_settings_panel_auto_width(w),
342
- ToggleColumnSettingsPin => self.on_toggle_column_settings_pin(),
342
+ ToggleColumnSettingsPin => self.on_toggle_column_settings_pin(ctx),
343
+ ToggleColumnSettingsPinComplete(resolve) => {
344
+ self.on_toggle_column_settings_pin_complete(resolve)
345
+ },
343
346
  ColumnSettingsTabChanged(tab) => self.on_column_settings_tab_changed(ctx, tab),
344
347
  ToggleDebug => self.on_toggle_debug(ctx),
345
348
 
346
349
  // Value-semantic snapshot plumbing (`snapshots.rs`)
347
- UpdateSession(props) => self.on_update_session(*props),
350
+ UpdateSession(props) => self.on_update_session(ctx, *props),
348
351
  UpdateSessionStats(stats, has_table) => self.on_update_session_stats(stats, has_table),
349
352
  UpdateGlobalFilters => self.on_update_global_filters(ctx),
350
353
  UpdateRenderer(props) => self.on_update_renderer(*props),
351
354
  UpdatePresentation(props) => self.on_update_presentation(ctx, *props),
352
355
  UpdateSettingsOpen(open) => self.on_update_settings_open(open),
353
356
  UpdateIsWorkspace(is_workspace) => self.on_update_is_workspace(is_workspace),
354
- UpdateColumnSettings(ocs) => self.on_update_column_settings(*ocs),
357
+ UpdateColumnSettings(ocs) => self.on_update_column_settings(ctx, *ocs),
358
+ UpdateColumnSettingsCommit(resolve) => self.on_update_column_settings_commit(resolve),
355
359
  UpdateDragDrop(props) => self.on_update_dragdrop(*props),
356
360
  UpdateInFlight(count) => self.on_update_in_flight(count),
357
361
  }
@@ -15,18 +15,12 @@ use std::collections::HashSet;
15
15
  use serde::{Deserialize, Serialize};
16
16
  use serde_json::Value;
17
17
 
18
- use super::{KeyValueOpts, NumberSeriesStyleDefaultConfig};
18
+ use super::{CssKind, KeyValueOpts, NumberSeriesStyleDefaultConfig};
19
19
 
20
20
  /// The full schema for one column at one point in time. Plugins may return
21
21
  /// different schemas for the same column based on the column's current
22
22
  /// stored value (e.g. to hide dependent fields), so this is re-queried on
23
23
  /// every field update.
24
- ///
25
- /// Each entry is a [`ControlSpec`]. Primitive variants carry their own
26
- /// `key` (JSON storage key) inline; the sidebar UI label is supplied by
27
- /// CSS via `--psp-label--<key>--content`. Composite variants render a
28
- /// self-contained Yew component that supplies its own labels and owns a
29
- /// fixed key namespace via [`ControlSpec::serialized_keys`].
30
24
  #[derive(Clone, Debug, Default, Deserialize, Serialize)]
31
25
  pub struct ColumnConfigSchema {
32
26
  pub fields: Vec<ControlSpec>,
@@ -93,33 +87,21 @@ pub enum ControlSpec {
93
87
  key: String,
94
88
  default: String,
95
89
  },
96
- /// Paired pos/neg color picker rendered as a single horizontal
97
- /// gradient/range bar. Used to expose the existing
98
- /// [`crate::components::form::color_range_selector::ColorRangeSelector`]
99
- /// widget at primitive granularity. Owns two top-level keys
100
- /// (`key_pos` + `key_neg`); the visible label is derived from
101
- /// `key_pos`.
102
- ColorRange {
103
- key_pos: String,
104
- key_neg: String,
105
- default_pos: String,
106
- default_neg: String,
107
- /// When `true`, the bar renders as a continuous gradient
108
- /// (e.g. for `gradient` color modes); when `false`, the bar
109
- /// renders as a hard pos/neg split. Currently only changes the
110
- /// visual; pos/neg semantics are unchanged.
111
- #[serde(default)]
112
- is_gradient: bool,
90
+ Palette {
91
+ key: String,
92
+ default: String,
93
+
94
+ #[serde(default, skip_serializing_if = "Option::is_none")]
95
+ max: Option<usize>,
113
96
  },
97
+ GradientStops {
98
+ key: String,
99
+ default: String,
114
100
 
115
- /// Residual format-only widget for datetime columns — owns
116
- /// `date_format` only. Pair with primitive `Enum` and `Color` fields
117
- /// for `datetime_color_mode` + `color` to fully decompose datetime
118
- /// styling.
101
+ #[serde(default, skip_serializing_if = "std::ops::Not::not")]
102
+ discrete: bool,
103
+ },
119
104
  DatetimeFormat,
120
- /// Residual format-only widget for string columns — owns `format`
121
- /// only. Pair with primitive `Enum` and `Color` fields for
122
- /// `string_color_mode` + `color` to fully decompose string styling.
123
105
  StringFormat,
124
106
  NumberSeriesStyle {
125
107
  default: NumberSeriesStyleDefaultConfig,
@@ -138,6 +120,90 @@ pub struct EnumVariant {
138
120
  pub label: Option<String>,
139
121
  }
140
122
 
123
+ /// One stop of a [`ControlSpec::GradientStops`] value in its in-memory
124
+ /// form: a `#rrggbb` color at `offset` ∈ `[0, 1]`.
125
+ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
126
+ pub struct GradientStopSpec {
127
+ pub color: String,
128
+ pub offset: f64,
129
+ }
130
+
131
+ /// The [`ControlSpec::GradientStops`] canonical stop order: offsets
132
+ /// clamped to `[0, 1]` and rounded to 3 decimals, stops sorted stably
133
+ /// by offset.
134
+ pub fn canonicalize_gradient_stops(mut stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
135
+ for stop in &mut stops {
136
+ stop.offset = (stop.offset.clamp(0.0, 1.0) * 1000.0).round() / 1000.0;
137
+ }
138
+
139
+ stops.sort_by(|a, b| {
140
+ a.offset
141
+ .partial_cmp(&b.offset)
142
+ .unwrap_or(std::cmp::Ordering::Equal)
143
+ });
144
+
145
+ stops
146
+ }
147
+
148
+ /// Fit `stops` to a `discrete` field's fixed pair: an over-length value
149
+ /// keeps only its two end colors, pinned to `0`/`1`.
150
+ pub fn discrete_pair(stops: Vec<GradientStopSpec>) -> Vec<GradientStopSpec> {
151
+ let stops = canonicalize_gradient_stops(stops);
152
+ match (stops.first(), stops.last()) {
153
+ (Some(first), Some(last)) if stops.len() > 2 => vec![
154
+ GradientStopSpec {
155
+ color: first.color.clone(),
156
+ offset: 0.0,
157
+ },
158
+ GradientStopSpec {
159
+ color: last.color.clone(),
160
+ offset: 1.0,
161
+ },
162
+ ],
163
+ _ => stops,
164
+ }
165
+ }
166
+
167
+ impl ColumnConfigSchema {
168
+ /// Canonicalize every CSS-valued default at schema ingest, dropping
169
+ /// (and logging) fields whose default fails its kind's reader.
170
+ pub fn canonicalize_defaults(mut self) -> Self {
171
+ self.fields.retain_mut(|spec| {
172
+ let (kind, key, default) = match spec {
173
+ ControlSpec::Color { key, default } => (CssKind::Color, key, default),
174
+ ControlSpec::Palette { key, default, .. } => (CssKind::Palette, key, default),
175
+ ControlSpec::GradientStops { key, default, .. } => {
176
+ (CssKind::Gradient, key, default)
177
+ },
178
+ _ => return true,
179
+ };
180
+
181
+ match kind.canonicalize(default) {
182
+ Ok(canonical) => {
183
+ *default = canonical;
184
+ true
185
+ },
186
+ Err(error) => {
187
+ tracing::error!("Dropping `{key}` — invalid schema default: {error}");
188
+ false
189
+ },
190
+ }
191
+ });
192
+
193
+ self
194
+ }
195
+
196
+ /// The CSS kind of the control owning `key`, if it is CSS-valued.
197
+ pub fn css_kind_of(&self, key: &str) -> Option<CssKind> {
198
+ self.fields.iter().find_map(|spec| match spec {
199
+ ControlSpec::Color { key: k, .. } if k == key => Some(CssKind::Color),
200
+ ControlSpec::Palette { key: k, .. } if k == key => Some(CssKind::Palette),
201
+ ControlSpec::GradientStops { key: k, .. } if k == key => Some(CssKind::Gradient),
202
+ _ => None,
203
+ })
204
+ }
205
+ }
206
+
141
207
  impl ControlSpec {
142
208
  /// Top-level JSON keys this control owns when its value is serialized
143
209
  /// into a column's config map. For primitives this is just `[key]`;
@@ -152,14 +218,13 @@ impl ControlSpec {
152
218
  ControlSpec::Symbols { .. } => vec!["symbols"],
153
219
  ControlSpec::NumberFormat => vec!["number_format"],
154
220
  ControlSpec::AggregateDepth => vec!["aggregate_depth"],
155
- ControlSpec::ColorRange {
156
- key_pos, key_neg, ..
157
- } => vec![key_pos.as_str(), key_neg.as_str()],
158
221
  ControlSpec::Enum { key, .. }
159
222
  | ControlSpec::Bool { key, .. }
160
223
  | ControlSpec::Number { key, .. }
161
224
  | ControlSpec::String { key, .. }
162
- | ControlSpec::Color { key, .. } => vec![key.as_str()],
225
+ | ControlSpec::Color { key, .. }
226
+ | ControlSpec::Palette { key, .. }
227
+ | ControlSpec::GradientStops { key, .. } => vec![key.as_str()],
163
228
  }
164
229
  }
165
230
  }
@@ -168,12 +233,6 @@ impl ControlSpec {
168
233
  /// declares which top-level keys the update is allowed to write
169
234
  /// (`keys` — equivalent to the field's [`ControlSpec::serialized_keys`])
170
235
  /// and a partial new sub-state (`value`).
171
- ///
172
- /// Apply semantics: keys in `keys` are *cleared* from the column's config
173
- /// map, then keys present in `value` are *inserted*. Defaults are
174
- /// pre-stripped by the caller (typically via `skip_serializing_if`), so
175
- /// "no value set for key K" means the schema default applies and K is
176
- /// not serialized.
177
236
  #[derive(Clone, Debug, Deserialize, Serialize)]
178
237
  pub struct ColumnConfigFieldUpdate {
179
238
  pub keys: Vec<String>,