@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.
Files changed (291) hide show
  1. package/dist/cdn/perspective-viewer.js +6 -6
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/blueprint.css +1 -1
  4. package/dist/css/botanical.css +1 -1
  5. package/dist/css/dracula.css +1 -1
  6. package/dist/css/eggplant.css +1 -1
  7. package/dist/css/gruvbox-dark.css +1 -1
  8. package/dist/css/gruvbox.css +1 -1
  9. package/dist/css/icons.css +1 -1
  10. package/dist/css/intl/ar.css +1 -0
  11. package/dist/css/intl/de.css +1 -1
  12. package/dist/css/intl/es.css +1 -1
  13. package/dist/css/intl/fr.css +1 -1
  14. package/dist/css/intl/ja.css +1 -1
  15. package/dist/css/intl/pt.css +1 -1
  16. package/dist/css/intl/zh.css +1 -1
  17. package/dist/css/intl.css +1 -1
  18. package/dist/css/ledger.css +1 -1
  19. package/dist/css/monokai.css +1 -1
  20. package/dist/css/nord.css +1 -1
  21. package/dist/css/phosphor.css +1 -1
  22. package/dist/css/pro-dark.css +1 -1
  23. package/dist/css/pro.css +1 -1
  24. package/dist/css/solarized-dark.css +1 -1
  25. package/dist/css/solarized.css +1 -1
  26. package/dist/css/themes.css +1 -1
  27. package/dist/css/vaporwave.css +1 -1
  28. package/dist/css/velvet.css +1 -1
  29. package/dist/docs/perspective-docs.json +1 -1
  30. package/dist/esm/extensions.d.ts +0 -5
  31. package/dist/esm/perspective-viewer.d.ts +2 -0
  32. package/dist/esm/perspective-viewer.inline.js +8 -8
  33. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  34. package/dist/esm/perspective-viewer.js +5 -5
  35. package/dist/esm/perspective-viewer.js.map +2 -2
  36. package/dist/esm/plugin.d.ts +9 -0
  37. package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
  38. package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
  39. package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
  40. package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
  41. package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
  42. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
  43. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
  44. package/dist/wasm/perspective-viewer.d.ts +137 -104
  45. package/dist/wasm/perspective-viewer.js +337 -269
  46. package/dist/wasm/perspective-viewer.wasm +0 -0
  47. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
  48. package/package.json +7 -7
  49. package/src/css/_context_menu.css +1 -1
  50. package/src/css/_dropdown_menu.css +1 -1
  51. package/src/css/_viewer_bundle.css +7 -8
  52. package/src/css/column-dropdown.css +0 -3
  53. package/src/css/column-selector.css +18 -7
  54. package/src/css/column-settings-panel.css +0 -225
  55. package/src/css/column-style.css +175 -0
  56. package/src/css/config-selector.css +19 -11
  57. package/src/css/filter-dropdown.css +5 -5
  58. package/src/css/form/debug.css +37 -4
  59. package/src/css/function-dropdown.css +5 -5
  60. package/src/css/panel-tab.css +23 -0
  61. package/src/css/plugin-selector.css +5 -5
  62. package/src/css/render-warning.css +1 -1
  63. package/src/css/status-bar.css +63 -21
  64. package/src/css/type-icon.css +1 -3
  65. package/src/css/{containers → ui/containers}/context-menu.css +22 -16
  66. package/src/css/ui/containers/dropdown-menu-items.css +66 -0
  67. package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
  68. package/src/css/{containers → ui/containers}/tabs.css +2 -1
  69. package/src/css/viewer.css +32 -26
  70. package/src/rust/agent/tools.rs +80 -7
  71. package/src/rust/components/chat_panel.rs +1 -1
  72. package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
  73. package/src/rust/components/column_dropdown.rs +2 -2
  74. package/src/rust/components/column_selector/active_column.rs +24 -9
  75. package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
  76. package/src/rust/components/column_selector/config_selector.rs +2 -2
  77. package/src/rust/components/column_selector/filter_column.rs +8 -5
  78. package/src/rust/components/column_selector/inactive_column.rs +4 -2
  79. package/src/rust/components/column_selector/pivot_column.rs +4 -3
  80. package/src/rust/components/column_selector/sort_column.rs +7 -4
  81. package/src/rust/components/column_selector.rs +14 -3
  82. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
  83. package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
  84. package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
  85. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
  86. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
  87. package/src/rust/components/column_settings_sidebar/style_tab.rs +70 -12
  88. package/src/rust/components/column_settings_sidebar.rs +3 -4
  89. package/src/rust/components/copy_dropdown.rs +1 -1
  90. package/src/rust/components/datetime_column_style/custom.rs +1 -3
  91. package/src/rust/components/datetime_column_style/simple.rs +1 -2
  92. package/src/rust/components/datetime_column_style.rs +1 -2
  93. package/src/rust/components/{form/debug.rs → debug_panel.rs} +112 -55
  94. package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
  95. package/src/rust/components/export_dropdown.rs +1 -1
  96. package/src/rust/components/expression_editor.rs +1 -1
  97. package/src/rust/components/filter_dropdown.rs +2 -2
  98. package/src/rust/components/function_dropdown.rs +2 -2
  99. package/src/rust/components/main_panel/frame_theme.rs +3 -59
  100. package/src/rust/components/main_panel/msg.rs +4 -0
  101. package/src/rust/components/main_panel/reconcile.rs +9 -4
  102. package/src/rust/components/main_panel/render.rs +62 -11
  103. package/src/rust/components/main_panel/update.rs +39 -10
  104. package/src/rust/components/main_panel.rs +40 -2
  105. package/src/rust/components/mod.rs +5 -8
  106. package/src/rust/components/new_panel_menu.rs +97 -0
  107. package/src/rust/components/number_series_style.rs +2 -3
  108. package/src/rust/components/panel_menu.rs +61 -87
  109. package/src/rust/components/panel_tab.rs +100 -43
  110. package/src/rust/components/plugin_tab.rs +58 -4
  111. package/src/rust/components/render_warning.rs +2 -2
  112. package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
  113. package/src/rust/components/settings_panel.rs +8 -2
  114. package/src/rust/components/status_bar.rs +113 -19
  115. package/src/rust/components/status_indicator.rs +9 -14
  116. package/src/rust/components/{style/mod.rs → style.rs} +32 -15
  117. package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
  118. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  119. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  120. package/src/rust/components/viewer/msg.rs +43 -30
  121. package/src/rust/components/viewer/panels.rs +53 -19
  122. package/src/rust/components/viewer/render.rs +16 -13
  123. package/src/rust/components/viewer/settings.rs +137 -68
  124. package/src/rust/components/viewer/snapshots.rs +5 -10
  125. package/src/rust/components/viewer/wiring.rs +11 -22
  126. package/src/rust/components/viewer.rs +15 -10
  127. package/src/rust/components/window_editor.rs +8 -9
  128. package/src/rust/config/column_config_schema.rs +312 -33
  129. package/src/rust/config/datetime_column_style.rs +0 -11
  130. package/src/rust/config/mod.rs +0 -4
  131. package/src/rust/config/options.rs +50 -0
  132. package/src/rust/config/workspace_config.rs +70 -24
  133. package/src/rust/custom_elements/debug_plugin.rs +1 -10
  134. package/src/rust/custom_elements/mod.rs +0 -3
  135. package/src/rust/custom_elements/viewer.rs +194 -115
  136. package/src/rust/js/plugin.rs +3 -87
  137. package/src/rust/lib.rs +19 -18
  138. package/src/rust/presentation/column_locator.rs +3 -7
  139. package/src/rust/presentation.rs +68 -38
  140. package/src/rust/queries/hosted_tables.rs +38 -0
  141. package/src/rust/queries/mod.rs +2 -0
  142. package/src/rust/queries/palette_set.rs +1 -1
  143. package/src/rust/queries/plugin_column_styles.rs +6 -5
  144. package/src/rust/renderer/activate.rs +1 -3
  145. package/src/rust/renderer/dispatch.rs +83 -22
  146. package/src/rust/renderer/limits.rs +24 -5
  147. package/src/rust/renderer/plugin_config.rs +201 -41
  148. package/src/rust/renderer/plugin_store.rs +2 -1
  149. package/src/rust/renderer/props.rs +7 -1
  150. package/src/rust/renderer.rs +23 -27
  151. package/src/rust/session/props.rs +8 -3
  152. package/src/rust/session/view_subscription.rs +19 -6
  153. package/src/rust/session.rs +189 -150
  154. package/src/rust/tasks/create_panel.rs +5 -2
  155. package/src/rust/tasks/dismiss_render_warning.rs +1 -1
  156. package/src/rust/tasks/eject.rs +1 -0
  157. package/src/rust/tasks/pipeline.rs +18 -67
  158. package/src/rust/tasks/presize_panels.rs +24 -1
  159. package/src/rust/tasks/restore_and_render.rs +0 -17
  160. package/src/rust/tasks/restore_panel.rs +129 -30
  161. package/src/rust/tasks/send_column_config.rs +4 -1
  162. package/src/rust/tasks/send_plugin_config.rs +1 -1
  163. package/src/rust/tasks/set_edit_mode.rs +1 -1
  164. package/src/rust/tasks/sync_update_panels.rs +147 -67
  165. package/src/rust/tasks/table_lifecycle.rs +7 -1
  166. package/src/rust/{components → ui}/containers/control_group.rs +25 -3
  167. package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
  168. package/src/rust/{components → ui}/containers/mod.rs +0 -4
  169. package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
  170. package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
  171. package/src/rust/{components → ui}/containers/select.rs +0 -6
  172. package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
  173. package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
  174. package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
  175. package/src/rust/{components → ui}/context_menu.rs +8 -13
  176. package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
  177. package/src/rust/{components → ui}/font_loader.rs +0 -26
  178. package/src/rust/{components → ui}/form/color_selector.rs +4 -2
  179. package/src/rust/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
  180. package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
  181. package/src/rust/{components → ui}/form/mod.rs +1 -10
  182. package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
  183. package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
  184. package/src/rust/{components → ui}/form/number_field.rs +1 -1
  185. package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
  186. package/src/rust/{components → ui}/form/optional_field.rs +3 -1
  187. package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
  188. package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
  189. package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
  190. package/src/rust/ui/mod.rs +56 -0
  191. package/src/rust/ui/modal.rs +61 -0
  192. package/src/rust/{components → ui}/portal.rs +175 -79
  193. package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
  194. package/src/rust/utils/completion.rs +0 -6
  195. package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
  196. package/src/rust/utils/css_vars.rs +13 -0
  197. package/src/rust/utils/local_fonts.rs +395 -0
  198. package/src/rust/utils/mod.rs +4 -0
  199. package/src/rust/workspace.rs +47 -0
  200. package/src/svg/boolean-type.svg +3 -2
  201. package/src/svg/checkbox-checked-icon.svg +4 -7
  202. package/src/svg/checkbox-hover.svg +3 -3
  203. package/src/svg/checkbox-off.svg +2 -3
  204. package/src/svg/checkbox-on.svg +3 -3
  205. package/src/svg/checkbox-unchecked-icon.svg +3 -3
  206. package/src/svg/datagrid-select-column.svg +8 -15
  207. package/src/svg/datagrid-select-region.svg +8 -15
  208. package/src/svg/datagrid-select-row-tree.svg +7 -12
  209. package/src/svg/datagrid-select-row.svg +8 -13
  210. package/src/svg/date-type.svg +4 -23
  211. package/src/svg/drawer-tab-hover.svg +4 -6
  212. package/src/svg/drawer-tab-invert-hover.svg +5 -5
  213. package/src/svg/drawer-tab-invert.svg +3 -5
  214. package/src/svg/drawer-tab.svg +5 -5
  215. package/src/svg/mega-menu-icons-candlestick.svg +5 -37
  216. package/src/svg/mega-menu-icons-ohlc.svg +4 -39
  217. package/src/svg/mega-menu-icons-sunburst.svg +4 -38
  218. package/src/svg/mega-menu-icons-treemap.svg +3 -43
  219. package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
  220. package/src/svg/new-panel-icon.svg +4 -0
  221. package/src/svg/number-type.svg +3 -3
  222. package/src/svg/radio-hover.svg +3 -3
  223. package/src/svg/radio-off.svg +3 -3
  224. package/src/svg/radio-on.svg +3 -3
  225. package/src/svg/status_ok.svg +3 -26
  226. package/src/svg/status_pending.svg +3 -0
  227. package/src/svg/string-type.svg +4 -8
  228. package/src/svg/theme-icon.svg +5 -5
  229. package/src/themes/blueprint.css +0 -57
  230. package/src/themes/botanical.css +0 -36
  231. package/src/themes/defaults.css +1 -24
  232. package/src/themes/dracula.css +0 -58
  233. package/src/themes/eggplant.css +0 -58
  234. package/src/themes/gruvbox-dark.css +0 -65
  235. package/src/themes/gruvbox.css +1 -39
  236. package/src/themes/icons.css +2 -8
  237. package/src/themes/intl/ar.css +198 -0
  238. package/src/themes/intl/de.css +63 -40
  239. package/src/themes/intl/es.css +58 -35
  240. package/src/themes/intl/fr.css +57 -34
  241. package/src/themes/intl/ja.css +59 -36
  242. package/src/themes/intl/pt.css +54 -31
  243. package/src/themes/intl/zh.css +65 -42
  244. package/src/themes/intl.css +34 -8
  245. package/src/themes/ledger.css +1 -59
  246. package/src/themes/monokai.css +0 -51
  247. package/src/themes/nord.css +0 -55
  248. package/src/themes/phosphor.css +0 -48
  249. package/src/themes/pro-dark.css +0 -37
  250. package/src/themes/pro.css +3 -23
  251. package/src/themes/solarized-dark.css +0 -31
  252. package/src/themes/solarized.css +1 -24
  253. package/src/themes/vaporwave.css +0 -44
  254. package/src/themes/velvet.css +0 -60
  255. package/src/ts/extensions.ts +0 -13
  256. package/src/ts/perspective-viewer.ts +2 -0
  257. package/src/ts/plugin.ts +9 -0
  258. package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
  259. package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
  260. package/src/ts/ts-rs/GetViewOptions.ts +7 -0
  261. package/src/ts/ts-rs/RestoreOptions.ts +7 -1
  262. package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
  263. package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
  264. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
  265. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  266. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  267. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  268. package/src/css/plugin-settings-panel.css +0 -171
  269. package/src/rust/components/modal.rs +0 -177
  270. package/src/rust/components/string_column_style.rs +0 -121
  271. package/src/rust/config/string_column_style.rs +0 -135
  272. package/src/rust/custom_elements/copy_dropdown.rs +0 -219
  273. package/src/rust/custom_elements/export_dropdown.rs +0 -216
  274. package/src/rust/custom_elements/modal.rs +0 -299
  275. package/src/ts/ts-rs/FormatMode.ts +0 -3
  276. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  277. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
  278. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
  279. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
  280. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
  281. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
  282. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline5.js +0 -0
  283. /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
  284. /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
  285. /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
  286. /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
  287. /package/src/css/{dom → ui/dom}/select.css +0 -0
  288. /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
  289. /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
  290. /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
  291. /package/src/rust/{components → ui}/form/number_input.rs +0 -0
@@ -44,6 +44,28 @@ pub struct RestoreOptions {
44
44
  /// known-good config to recover exactly.
45
45
  #[ts(optional)]
46
46
  pub suppress_errors: Option<bool>,
47
+
48
+ /// When `true`, a `table` no loaded client hosts yet leaves the panel
49
+ /// empty and pending until the table is created, instead of the default
50
+ /// error.
51
+ #[ts(optional)]
52
+ pub wait_for_table: Option<bool>,
53
+ }
54
+
55
+ /// Options for the `restoreWorkspace()` method.
56
+ #[derive(Deserialize, Default, TS)]
57
+ pub struct RestoreWorkspaceOptions {
58
+ /// As `RestoreOptions::wait_for_table`, applied to every panel entry.
59
+ #[ts(optional)]
60
+ pub wait_for_table: Option<bool>,
61
+ }
62
+
63
+ /// Options for the `addPanel()` method.
64
+ #[derive(Deserialize, Default, TS)]
65
+ pub struct AddPanelOptions {
66
+ /// As `RestoreOptions::wait_for_table`.
67
+ #[ts(optional)]
68
+ pub wait_for_table: Option<bool>,
47
69
  }
48
70
 
49
71
  /// The `eject` argument: the loaded client to remove by name; the active
@@ -77,6 +99,34 @@ pub struct GetTableOptions {
77
99
  pub panel: Option<String>,
78
100
  }
79
101
 
102
+ /// How `getView` resolves the `View` it returns.
103
+ #[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq, TS)]
104
+ #[serde(rename_all = "lowercase")]
105
+ pub enum GetViewMode {
106
+ /// The panel's own bound `View`, which the viewer replaces on config
107
+ /// change and deletes on auto-pause or `delete()`; rejects when none is
108
+ /// bound.
109
+ #[default]
110
+ Live,
111
+
112
+ /// A caller-owned `View` built from the panel's effective config,
113
+ /// independent of the render lifecycle.
114
+ Clone,
115
+
116
+ /// `live` when the panel has a bound `View`, else `clone`.
117
+ Auto,
118
+ }
119
+
120
+ /// The `getView` argument: the `mode` and the target panel.
121
+ #[derive(Deserialize, Default, TS)]
122
+ pub struct GetViewOptions {
123
+ #[ts(optional)]
124
+ pub mode: Option<GetViewMode>,
125
+
126
+ #[ts(optional)]
127
+ pub panel: Option<String>,
128
+ }
129
+
80
130
  /// The `getClient` argument: whether to `wait` for a `Client`, and the target
81
131
  /// panel.
82
132
  #[derive(Deserialize, Default, TS)]
@@ -14,7 +14,8 @@ use std::collections::BTreeMap;
14
14
 
15
15
  use perspective_client::config::Filter;
16
16
 
17
- use crate::config::{CssKind, PanelViewerConfig, ViewerConfigInitial};
17
+ use crate::config::{OptionalUpdate, PanelViewerConfig, ViewerConfigInitial};
18
+ use crate::utils::CssKind;
18
19
 
19
20
  /// The workspace config format (`{version, active?, layout, panels}`) —
20
21
  /// the multi-panel counterpart of the single-panel [`ViewerConfig`] — as
@@ -66,48 +67,52 @@ pub struct WorkspaceConfig {
66
67
  pub palette: BTreeMap<String, String>,
67
68
  }
68
69
 
69
- /// The parse target of a workspace config in
70
- /// [`PerspectiveViewerElement::restoreWorkspace`]. Mirrors
71
- /// [`WorkspaceConfig`], but `panels` entries are [`ViewerConfigInitial`]s —
72
- /// every entry creates a NEW panel, so `table` is required by type (a
73
- /// stray per-panel `settings` key is ignored; it is element-level state,
74
- /// carried by the top-level `active` field).
70
+ /// The [`PerspectiveViewerElement::restoreWorkspace`] argument: a field-wise
71
+ /// update of the element's workspace state, like [`ViewerConfigUpdate`] for a
72
+ /// panel, where an absent key is unchanged, `null` resets and a value
73
+ /// replaces.
75
74
  #[derive(serde::Deserialize, ts_rs::TS)]
76
75
  pub struct WorkspaceConfigUpdate {
76
+ /// The panel to activate with the settings sidebar open, by `panels` key
77
+ /// or existing panel id; `null` closes the sidebar.
77
78
  #[serde(default)]
79
+ #[ts(as = "Option<_>")]
78
80
  #[ts(optional)]
79
- pub active: Option<String>,
81
+ pub active: OptionalUpdate<String>,
80
82
 
83
+ /// The layout tree to stage, naming `panels` keys or existing panel ids.
81
84
  #[serde(default)]
82
85
  #[ts(optional)]
83
86
  pub layout: Option<crate::js::Layout>,
84
87
 
85
- pub panels: BTreeMap<String, ViewerConfigInitial>,
88
+ /// The complete replacement panel set, each [`ViewerConfigInitial`] entry
89
+ /// creating a new panel (`{}` empties the element); absent keeps the
90
+ /// existing panels.
91
+ #[serde(default)]
92
+ #[ts(optional)]
93
+ pub panels: Option<BTreeMap<String, ViewerConfigInitial>>,
86
94
 
87
- /// The element-level global (master/detail cross-) filters to re-apply as
88
- /// a transient overlay on every DETAIL panel. Restored as one
89
- /// unattributed bucket: the next selection on any master replaces it.
90
- /// `Option` so an explicit `undefined` property deserializes as
91
- /// `None` like an absent key (also `masters` / `palette` below).
95
+ /// The element-level cross-filter set applied to every detail panel as
96
+ /// one unattributed bucket, replacing the current set; `null` clears it.
92
97
  #[serde(default)]
93
98
  #[ts(as = "Option<_>")]
94
99
  #[ts(optional)]
95
- pub global_filters: Option<Vec<Filter>>,
100
+ pub global_filters: OptionalUpdate<Vec<Filter>>,
96
101
 
97
- /// The master (filter-source) panels, by saved `panels` key. An id not in
98
- /// `panels` warns and is dropped.
102
+ /// The master (filter-source) panels by `panels` key or existing panel
103
+ /// id, unknown ids dropped with a warning; `null` demotes every panel.
99
104
  #[serde(default)]
100
105
  #[ts(as = "Option<_>")]
101
106
  #[ts(optional)]
102
- pub masters: Option<Vec<String>>,
107
+ pub masters: OptionalUpdate<Vec<String>>,
103
108
 
104
- /// Named color-scale definitions to apply to the host (see
105
- /// [`WorkspaceConfig::palette`]), replacing any previously restored
106
- /// palette.
109
+ /// Named color-scale definitions applied to the host (see
110
+ /// [`WorkspaceConfig::palette`]), replacing the previous palette; `null`
111
+ /// clears it.
107
112
  #[serde(default)]
108
113
  #[ts(as = "Option<_>")]
109
114
  #[ts(optional)]
110
- pub palette: Option<BTreeMap<String, String>>,
115
+ pub palette: OptionalUpdate<BTreeMap<String, String>>,
111
116
  }
112
117
 
113
118
  /// Validate a restored palette map: each key's `--psp-user--<kind>-`
@@ -172,11 +177,48 @@ mod tests {
172
177
  );
173
178
  }
174
179
 
180
+ #[test]
181
+ fn update_absent_fields_are_missing_and_null_resets() {
182
+ let update: WorkspaceConfigUpdate = serde_json::from_value(json!({})).unwrap();
183
+ assert!(update.panels.is_none());
184
+ assert!(update.layout.is_none());
185
+ assert_eq!(update.active, OptionalUpdate::Missing);
186
+ assert_eq!(update.global_filters, OptionalUpdate::Missing);
187
+ assert_eq!(update.masters, OptionalUpdate::Missing);
188
+ assert_eq!(update.palette, OptionalUpdate::Missing);
189
+
190
+ let update: WorkspaceConfigUpdate = serde_json::from_value(json!({
191
+ "panels": null,
192
+ "active": null,
193
+ "global_filters": null,
194
+ "masters": null,
195
+ "palette": null,
196
+ }))
197
+ .unwrap();
198
+
199
+ assert!(update.panels.is_none());
200
+ assert_eq!(update.active, OptionalUpdate::SetDefault);
201
+ assert_eq!(update.global_filters, OptionalUpdate::SetDefault);
202
+ assert_eq!(update.masters, OptionalUpdate::SetDefault);
203
+ assert_eq!(update.palette, OptionalUpdate::SetDefault);
204
+
205
+ let update: WorkspaceConfigUpdate = serde_json::from_value(json!({
206
+ "panels": {},
207
+ "global_filters": [["Region", "==", "West"]],
208
+ "masters": ["a"],
209
+ }))
210
+ .unwrap();
211
+
212
+ assert_eq!(update.panels.map(|p| p.len()), Some(0));
213
+ assert_eq!(update.masters, OptionalUpdate::Update(vec!["a".to_owned()]));
214
+ assert!(matches!(update.global_filters, OptionalUpdate::Update(ref f) if f.len() == 1));
215
+ }
216
+
175
217
  #[test]
176
218
  fn update_palette_defaults_empty_and_validates_by_prefix() {
177
219
  let update: WorkspaceConfigUpdate =
178
220
  serde_json::from_value(json!({ "panels": {} })).unwrap();
179
- assert!(update.palette.is_none());
221
+ assert_eq!(update.palette, OptionalUpdate::Missing);
180
222
 
181
223
  let update: WorkspaceConfigUpdate = serde_json::from_value(json!({
182
224
  "panels": {},
@@ -188,7 +230,11 @@ mod tests {
188
230
  }))
189
231
  .unwrap();
190
232
 
191
- let valid = validate_palette(update.palette.unwrap()).unwrap();
233
+ let OptionalUpdate::Update(palette) = update.palette else {
234
+ panic!("palette missing");
235
+ };
236
+
237
+ let valid = validate_palette(palette).unwrap();
192
238
  assert_eq!(
193
239
  valid.get("--psp-user--gradient-1").unwrap(),
194
240
  "linear-gradient(to right, #000000 0%, #ffffff 100%)"
@@ -66,16 +66,7 @@ impl PerspectiveDebugPluginElement {
66
66
  }
67
67
 
68
68
  /// Delegates to `draw()` VIRTUALLY — through the JS element's `draw`
69
- /// property, never `self.draw(view)` (Rust static dispatch). This
70
- /// element is the documented base class for custom plugins
71
- /// (`class MyPlugin extends
72
- /// customElements.get("perspective-viewer-plugin")`), whose contract is
73
- /// "`update()` defaults to dispatch to `draw()`" — a
74
- /// subclass overriding only `draw` must receive `update`-path repaints
75
- /// (`BindDisposition::Unchanged`/`Adopted` runs,
76
- /// `PLUGIN_DRAW_INVARIANT_PLAN.md`); the static call bypassed the
77
- /// override and repainted the Debug CSV instead (the
78
- /// `view_lifecycle.spec` regression).
69
+ /// property, never `self.draw(view)` (Rust static dispatch).
79
70
  pub fn update(&self, view: &perspective_js::View) -> ApiFuture<()> {
80
71
  clone!(self.elem, view);
81
72
  ApiFuture::new(async move {
@@ -13,8 +13,5 @@
13
13
  //! Each file in `custom_elements` exports a single struct which will be the
14
14
  //! public [`wasm_bindgen`] API to a JavaScript Custom Element.
15
15
 
16
- pub mod copy_dropdown;
17
16
  pub mod debug_plugin;
18
- pub mod export_dropdown;
19
- pub mod modal;
20
17
  pub mod viewer;