@perspective-dev/viewer 5.1.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 (245) hide show
  1. package/dist/cdn/perspective-viewer.js +9 -9
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  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/intl/de.css +1 -1
  9. package/dist/css/intl/es.css +1 -1
  10. package/dist/css/intl/fr.css +1 -1
  11. package/dist/css/intl/ja.css +1 -1
  12. package/dist/css/intl/pt.css +1 -1
  13. package/dist/css/intl/zh.css +1 -1
  14. package/dist/css/intl.css +1 -1
  15. package/dist/css/monokai.css +1 -1
  16. package/dist/css/phosphor.css +1 -1
  17. package/dist/css/pro-dark.css +1 -1
  18. package/dist/css/pro.css +1 -1
  19. package/dist/css/solarized-dark.css +1 -1
  20. package/dist/css/solarized.css +1 -1
  21. package/dist/css/themes.css +1 -1
  22. package/dist/css/vaporwave.css +1 -1
  23. package/dist/docs/perspective-docs.json +1 -0
  24. package/dist/esm/column-format.d.ts +19 -28
  25. package/dist/esm/perspective-viewer.d.ts +3 -0
  26. package/dist/esm/perspective-viewer.inline.js +9 -9
  27. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  28. package/dist/esm/perspective-viewer.js +9 -9
  29. package/dist/esm/perspective-viewer.js.map +4 -4
  30. package/dist/esm/plugin.d.ts +10 -0
  31. package/dist/esm/providers.d.ts +79 -0
  32. package/dist/esm/ts-rs/CompactDisplay.d.ts +5 -0
  33. package/dist/esm/ts-rs/CurrencyCode.d.ts +1 -0
  34. package/dist/esm/ts-rs/CurrencyDisplay.d.ts +1 -0
  35. package/dist/esm/ts-rs/CurrencyNumberFormatStyle.d.ts +8 -0
  36. package/dist/esm/ts-rs/CurrencySign.d.ts +1 -0
  37. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
  38. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +54 -0
  39. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +34 -0
  40. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
  41. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
  42. package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
  43. package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
  44. package/dist/esm/ts-rs/Notation.d.ts +17 -0
  45. package/dist/esm/ts-rs/NumberFormatStyle.d.ts +18 -0
  46. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +42 -2
  47. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +35 -0
  48. package/dist/esm/ts-rs/RestoreOptions.d.ts +19 -0
  49. package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
  50. package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
  51. package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
  52. package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
  53. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
  54. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +24 -0
  55. package/dist/esm/ts-rs/SplitRollupMode.d.ts +9 -0
  56. package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
  57. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
  58. package/dist/esm/ts-rs/Unit.d.ts +1 -0
  59. package/dist/esm/ts-rs/UnitDisplay.d.ts +1 -0
  60. package/dist/esm/ts-rs/UnitNumberFormatStyle.d.ts +6 -0
  61. package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
  62. package/dist/esm/ts-rs/ViewerConfig.d.ts +42 -2
  63. package/dist/esm/ts-rs/ViewerConfigInitial.d.ts +175 -0
  64. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +63 -0
  65. package/dist/esm/ts-rs/WindowSpec.d.ts +3 -2
  66. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
  67. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +20 -9
  68. package/dist/wasm/perspective-viewer.d.ts +157 -41
  69. package/dist/wasm/perspective-viewer.js +452 -151
  70. package/dist/wasm/perspective-viewer.wasm +0 -0
  71. package/dist/wasm/perspective-viewer.wasm.d.ts +22 -17
  72. package/package.json +2 -1
  73. package/src/css/_viewer_bundle.css +1 -0
  74. package/src/css/column-selector.css +8 -0
  75. package/src/css/column-settings-panel.css +9 -5
  76. package/src/css/column-style.css +209 -31
  77. package/src/css/config-selector.css +38 -3
  78. package/src/css/dom/select.css +2 -1
  79. package/src/css/form/code-editor.css +6 -7
  80. package/src/css/form/mirrored-textarea.css +81 -0
  81. package/src/css/viewer.css +328 -0
  82. package/src/rust/agent/client/mod.rs +19 -0
  83. package/src/rust/agent/client/protocol.rs +320 -0
  84. package/src/rust/agent/client/transport.rs +287 -0
  85. package/src/rust/agent/client/turn.rs +174 -0
  86. package/src/rust/agent/config.rs +159 -0
  87. package/src/rust/agent/docs.rs +293 -0
  88. package/src/rust/agent/markdown/block.rs +509 -0
  89. package/src/rust/agent/markdown/entity.rs +52 -0
  90. package/src/rust/agent/markdown/events.rs +71 -0
  91. package/src/rust/agent/markdown/inline.rs +497 -0
  92. package/src/rust/agent/markdown/mod.rs +466 -0
  93. package/src/rust/agent/mod.rs +23 -0
  94. package/src/rust/agent/preamble.md +16 -0
  95. package/src/rust/agent/runtime.rs +201 -0
  96. package/src/rust/agent/slot.rs +236 -0
  97. package/src/rust/agent/tools.rs +1141 -0
  98. package/src/rust/components/chat_panel.rs +289 -0
  99. package/src/rust/components/column_selector/active_column.rs +2 -2
  100. package/src/rust/components/column_selector/add_expression_button.rs +3 -3
  101. package/src/rust/components/column_selector/aggregate_selector.rs +2 -3
  102. package/src/rust/components/column_selector/config_selector.rs +84 -7
  103. package/src/rust/components/column_selector/expr_edit_button.rs +4 -10
  104. package/src/rust/components/column_selector/inactive_column.rs +2 -2
  105. package/src/rust/components/column_selector.rs +55 -4
  106. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +293 -126
  107. package/src/rust/components/column_settings_sidebar/style_tab.rs +262 -225
  108. package/src/rust/components/column_settings_sidebar.rs +88 -69
  109. package/src/rust/components/containers/dragdrop_list.rs +37 -0
  110. package/src/rust/components/editable_header.rs +85 -144
  111. package/src/rust/components/expression_editor.rs +9 -16
  112. package/src/rust/components/form/code_editor.rs +29 -28
  113. package/src/rust/components/form/debug.rs +9 -5
  114. package/src/rust/components/form/mirrored_textarea.rs +165 -0
  115. package/src/rust/components/form/mod.rs +4 -1
  116. package/src/rust/components/form/multi_stop_gradient_selector.rs +476 -0
  117. package/src/rust/components/form/named_value_picker.rs +86 -0
  118. package/src/rust/components/form/palette_selector.rs +440 -0
  119. package/src/rust/components/main_panel/msg.rs +2 -2
  120. package/src/rust/components/main_panel/reconcile.rs +4 -4
  121. package/src/rust/components/mod.rs +2 -0
  122. package/src/rust/components/plugin_tab.rs +4 -28
  123. package/src/rust/components/settings_panel.rs +105 -22
  124. package/src/rust/components/viewer/msg.rs +46 -7
  125. package/src/rust/components/viewer/panels.rs +17 -20
  126. package/src/rust/components/viewer/render.rs +10 -3
  127. package/src/rust/components/viewer/settings.rs +163 -23
  128. package/src/rust/components/viewer/snapshots.rs +46 -21
  129. package/src/rust/components/viewer/wiring.rs +14 -25
  130. package/src/rust/components/viewer.rs +12 -8
  131. package/src/rust/components/window_editor.rs +75 -103
  132. package/src/rust/config/column_config_schema.rs +100 -41
  133. package/src/rust/config/css_scale.rs +882 -0
  134. package/src/rust/config/datetime_column_style/custom.rs +17 -0
  135. package/src/rust/config/datetime_column_style/simple.rs +10 -0
  136. package/src/rust/config/mod.rs +2 -0
  137. package/src/rust/config/number_string_format.rs +33 -0
  138. package/src/rust/config/options.rs +28 -0
  139. package/src/rust/config/plugin_static_config.rs +120 -5
  140. package/src/rust/config/viewer_config.rs +216 -3
  141. package/src/rust/config/workspace_config.rs +138 -10
  142. package/src/rust/custom_elements/viewer.rs +369 -119
  143. package/src/rust/custom_events.rs +43 -1
  144. package/src/rust/js/regular_layout.rs +3 -3
  145. package/src/rust/lib.rs +15 -0
  146. package/src/rust/presentation/column_locator.rs +19 -5
  147. package/src/rust/presentation.rs +200 -36
  148. package/src/rust/queries/column_locator.rs +74 -44
  149. package/src/rust/queries/get_viewer_config.rs +2 -1
  150. package/src/rust/queries/mod.rs +2 -0
  151. package/src/rust/queries/palette_set.rs +82 -0
  152. package/src/rust/queries/plugin_column_styles.rs +6 -2
  153. package/src/rust/renderer/dispatch.rs +16 -4
  154. package/src/rust/renderer/plugin_config.rs +309 -47
  155. package/src/rust/renderer.rs +15 -42
  156. package/src/rust/session/column_defaults_update.rs +32 -7
  157. package/src/rust/session/metadata.rs +23 -21
  158. package/src/rust/session/props.rs +1 -0
  159. package/src/rust/session/replace_expression_update.rs +1 -0
  160. package/src/rust/session.rs +9 -6
  161. package/src/rust/tasks/create_panel.rs +46 -49
  162. package/src/rust/tasks/edit_expression.rs +19 -23
  163. package/src/rust/tasks/edit_window.rs +9 -11
  164. package/src/rust/tasks/eject.rs +4 -1
  165. package/src/rust/tasks/pipeline.rs +2 -2
  166. package/src/rust/tasks/presize_panels.rs +44 -5
  167. package/src/rust/tasks/reset_all.rs +4 -4
  168. package/src/rust/tasks/restore_and_render.rs +17 -44
  169. package/src/rust/tasks/restore_panel.rs +37 -22
  170. package/src/rust/tasks/set_edit_mode.rs +3 -1
  171. package/src/rust/tasks/sync_update_panels.rs +18 -24
  172. package/src/rust/tasks/update_theme.rs +23 -18
  173. package/src/rust/utils/css_vars.rs +26 -0
  174. package/src/rust/utils/mod.rs +2 -0
  175. package/src/rust/utils/spawn.rs +1 -1
  176. package/src/rust/workspace.rs +454 -199
  177. package/src/svg/lock.svg +7 -0
  178. package/src/themes/botanical.css +10 -0
  179. package/src/themes/defaults.css +12 -0
  180. package/src/themes/dracula.css +17 -0
  181. package/src/themes/gruvbox-dark.css +20 -0
  182. package/src/themes/gruvbox.css +10 -0
  183. package/src/themes/icons.css +1 -0
  184. package/src/themes/intl/de.css +16 -0
  185. package/src/themes/intl/es.css +16 -0
  186. package/src/themes/intl/fr.css +16 -0
  187. package/src/themes/intl/ja.css +16 -0
  188. package/src/themes/intl/pt.css +16 -0
  189. package/src/themes/intl/zh.css +16 -0
  190. package/src/themes/intl.css +16 -0
  191. package/src/themes/monokai.css +22 -0
  192. package/src/themes/phosphor.css +5 -0
  193. package/src/themes/pro-dark.css +10 -0
  194. package/src/themes/solarized-dark.css +7 -0
  195. package/src/themes/solarized.css +7 -0
  196. package/src/themes/vaporwave.css +20 -0
  197. package/src/ts/column-format.ts +67 -46
  198. package/src/ts/perspective-viewer.ts +3 -0
  199. package/src/ts/plugin.ts +14 -0
  200. package/src/ts/providers.ts +96 -0
  201. package/src/ts/ts-rs/CompactDisplay.ts +3 -0
  202. package/src/ts/ts-rs/CurrencyCode.ts +3 -0
  203. package/src/ts/ts-rs/CurrencyDisplay.ts +3 -0
  204. package/src/ts/ts-rs/CurrencyNumberFormatStyle.ts +6 -0
  205. package/src/ts/ts-rs/CurrencySign.ts +3 -0
  206. package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
  207. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +54 -0
  208. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +25 -0
  209. package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
  210. package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
  211. package/src/ts/ts-rs/FormatMode.ts +3 -0
  212. package/src/ts/ts-rs/FormatUnit.ts +3 -0
  213. package/src/ts/ts-rs/Notation.ts +11 -0
  214. package/src/ts/ts-rs/NumberFormatStyle.ts +12 -0
  215. package/src/ts/ts-rs/PanelViewerConfig.ts +48 -2
  216. package/src/ts/ts-rs/PluginStaticConfig.ts +36 -1
  217. package/src/ts/ts-rs/RestoreOptions.ts +20 -0
  218. package/src/ts/ts-rs/RoundingMode.ts +3 -0
  219. package/src/ts/ts-rs/RoundingPriority.ts +3 -0
  220. package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
  221. package/src/ts/ts-rs/SignDisplay.ts +3 -0
  222. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
  223. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +25 -0
  224. package/src/ts/ts-rs/SplitRollupMode.ts +11 -0
  225. package/src/ts/ts-rs/StringColorMode.ts +3 -0
  226. package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
  227. package/src/ts/ts-rs/Unit.ts +3 -0
  228. package/src/ts/ts-rs/UnitDisplay.ts +3 -0
  229. package/src/ts/ts-rs/UnitNumberFormatStyle.ts +5 -0
  230. package/src/ts/ts-rs/UseGrouping.ts +3 -0
  231. package/src/ts/ts-rs/ViewerConfig.ts +48 -2
  232. package/src/ts/ts-rs/ViewerConfigInitial.ts +164 -0
  233. package/src/ts/ts-rs/ViewerConfigUpdate.ts +72 -2
  234. package/src/ts/ts-rs/WindowSpec.ts +3 -2
  235. package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
  236. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +18 -9
  237. package/dist/esm/ts-rs/WindowAggregate.d.ts +0 -1
  238. package/src/rust/components/form/color_range_selector.rs +0 -137
  239. package/src/ts/ts-rs/WindowAggregate.ts +0 -3
  240. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
  241. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
  242. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
  243. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
  244. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
  245. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
@@ -62,14 +62,22 @@ const fn numeric_default() -> u32 {
62
62
  0
63
63
  }
64
64
 
65
+ /// A datetime column's `date_format` in its `Custom` form: per-part
66
+ /// `Intl.DateTimeFormatOptions` overrides, discriminated from the `Simple`
67
+ /// preset form by the required `format: "custom"` key. Each part is a
68
+ /// [`CustomDatetimeFormat`] variant valid for that `Intl` option;
69
+ /// `"disabled"` omits the part from the formatted output entirely.
65
70
  #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, TS)]
66
71
  pub struct CustomDatetimeStyleConfig {
67
72
  format: FormatUnit,
68
73
 
74
+ /// An IANA time zone name (e.g. `"America/New_York"`); defaults to the
75
+ /// browser's local time zone.
69
76
  #[serde(default)]
70
77
  #[serde(rename = "timeZone", skip_serializing_if = "Option::is_none")]
71
78
  pub time_zone: Option<String>,
72
79
 
80
+ /// Sub-second digits to display, 0-3 (0 = none).
73
81
  #[serde(
74
82
  skip_serializing_if = "is_zero",
75
83
  rename = "fractionalSecondDigits",
@@ -77,27 +85,36 @@ pub struct CustomDatetimeStyleConfig {
77
85
  )]
78
86
  pub fractional_seconds: u32,
79
87
 
88
+ /// Defaults to `"numeric"`.
80
89
  #[serde(skip_serializing_if = "is_numeric", default = "second_default")]
81
90
  pub second: CustomDatetimeFormat,
82
91
 
92
+ /// Defaults to `"numeric"`.
83
93
  #[serde(skip_serializing_if = "is_numeric", default = "second_default")]
84
94
  pub minute: CustomDatetimeFormat,
85
95
 
96
+ /// Defaults to `"numeric"`.
86
97
  #[serde(skip_serializing_if = "is_numeric", default = "second_default")]
87
98
  pub hour: CustomDatetimeFormat,
88
99
 
100
+ /// Defaults to `"numeric"`.
89
101
  #[serde(skip_serializing_if = "is_numeric", default = "second_default")]
90
102
  pub day: CustomDatetimeFormat,
91
103
 
104
+ /// A name form (`"long"`/`"short"`/`"narrow"`); defaults to
105
+ /// `"disabled"` (weekday not shown).
92
106
  #[serde(skip_serializing_if = "is_disabled", default = "weekday_default")]
93
107
  pub weekday: CustomDatetimeFormat,
94
108
 
109
+ /// Numeric or name form; defaults to `"numeric"`.
95
110
  #[serde(skip_serializing_if = "is_numeric", default = "second_default")]
96
111
  pub month: CustomDatetimeFormat,
97
112
 
113
+ /// Defaults to `"2-digit"`.
98
114
  #[serde(skip_serializing_if = "is_two_digit", default = "year_default")]
99
115
  pub year: CustomDatetimeFormat,
100
116
 
117
+ /// 12-hour clock; defaults to `true`.
101
118
  #[serde(skip_serializing_if = "is_true", default = "hour12_default")]
102
119
  pub hour12: bool,
103
120
  }
@@ -23,12 +23,20 @@ const fn time_style_default() -> SimpleDatetimeFormat {
23
23
  SimpleDatetimeFormat::Medium
24
24
  }
25
25
 
26
+ /// A datetime column's `date_format` in its `Simple` preset form:
27
+ /// `Intl.DateTimeFormatOptions`' `dateStyle`/`timeStyle` presets. This is
28
+ /// the default form (no `format` key); setting `format: "custom"` selects
29
+ /// the per-part `CustomDatetimeStyleConfig` instead.
26
30
  #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, TS)]
27
31
  pub struct SimpleDatetimeStyleConfig {
32
+ /// An IANA time zone name (e.g. `"America/New_York"`); defaults to the
33
+ /// browser's local time zone.
28
34
  #[serde(default)]
29
35
  #[serde(rename = "timeZone", skip_serializing_if = "Option::is_none")]
30
36
  pub time_zone: Option<String>,
31
37
 
38
+ /// Date preset breadth, or `"disabled"` to omit the date entirely;
39
+ /// defaults to `"short"`.
32
40
  #[serde(
33
41
  default = "date_style_default",
34
42
  rename = "dateStyle",
@@ -36,6 +44,8 @@ pub struct SimpleDatetimeStyleConfig {
36
44
  )]
37
45
  pub date_style: SimpleDatetimeFormat,
38
46
 
47
+ /// Time preset breadth, or `"disabled"` to omit the time entirely;
48
+ /// defaults to `"medium"`.
39
49
  #[serde(
40
50
  default = "time_style_default",
41
51
  rename = "timeStyle",
@@ -15,6 +15,7 @@
15
15
 
16
16
  mod column_config_schema;
17
17
  mod columns_config;
18
+ mod css_scale;
18
19
  mod datetime_column_style;
19
20
  mod export_method;
20
21
  mod kvpair;
@@ -29,6 +30,7 @@ mod workspace_config;
29
30
 
30
31
  pub use column_config_schema::*;
31
32
  pub use columns_config::*;
33
+ pub use css_scale::*;
32
34
  pub use datetime_column_style::*;
33
35
  pub use export_method::*;
34
36
  pub use number_series_style::*;
@@ -16,6 +16,11 @@ use serde::{Deserialize, Serialize};
16
16
  use strum::{Display, EnumIter};
17
17
  use ts_rs::TS;
18
18
 
19
+ /// The `style` family of a numeric column's `number_format` — serialized
20
+ /// FLATTENED into [`CustomNumberFormatConfig`]'s object, discriminated by
21
+ /// the `style` key (`"decimal"` default, `"currency"` + `currency`/
22
+ /// `currencyDisplay`/`currencySign`, `"percent"`, `"unit"` + `unit`/
23
+ /// `unitDisplay`), mirroring `Intl.NumberFormat` options.
19
24
  #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, TS)]
20
25
  #[serde(rename_all = "camelCase", tag = "style")]
21
26
  pub enum NumberFormatStyle {
@@ -127,6 +132,11 @@ pub enum TrailingZeroDisplay {
127
132
  StripIfInteger,
128
133
  }
129
134
 
135
+ /// The `notation` family of a numeric column's `number_format` —
136
+ /// serialized FLATTENED into [`CustomNumberFormatConfig`]'s object,
137
+ /// discriminated by the `notation` key (`"standard"` default,
138
+ /// `"scientific"`, `"engineering"`, `"compact"` + `compactDisplay`),
139
+ /// mirroring `Intl.NumberFormat` options.
130
140
  #[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, TS)]
131
141
  #[serde(rename_all = "camelCase", tag = "notation")]
132
142
  pub enum Notation {
@@ -169,6 +179,14 @@ pub enum SignDisplay {
169
179
  Never,
170
180
  }
171
181
 
182
+ /// A numeric column's `number_format` (`columns_config` value) —
183
+ /// `Intl.NumberFormat`-shaped options, written by the Style tab's number
184
+ /// format editor and read by `createNumberFormatter`. The `style` and
185
+ /// `notation` families ([`NumberFormatStyle`] / [`Notation`]) are serde-
186
+ /// FLATTENED into this object but `#[ts(skip)]`'d (ts-rs cannot flatten
187
+ /// `Option<enum>`) — the package's `NumberFormatConfig` re-composes the
188
+ /// full wire type as `CustomNumberFormatConfig & Partial<NumberFormatStyle>
189
+ /// & Partial<Notation>` (see `column-format.ts`).
172
190
  #[derive(Serialize, Deserialize, Debug, Default, PartialEq, Clone, TS)]
173
191
  #[serde(rename_all = "camelCase")]
174
192
  pub struct CustomNumberFormatConfig {
@@ -181,33 +199,42 @@ pub struct CustomNumberFormatConfig {
181
199
  // these min/max props can all be specified but it results in possible conflicts
182
200
  // may consider making them distinct options
183
201
  #[serde(skip_serializing_if = "Option::is_none")]
202
+ #[ts(optional, as = "Option<_>")]
184
203
  pub minimum_integer_digits: Option<f64>,
185
204
 
186
205
  #[serde(skip_serializing_if = "Option::is_none")]
206
+ #[ts(optional, as = "Option<_>")]
187
207
  pub minimum_fraction_digits: Option<f64>,
188
208
 
189
209
  #[serde(skip_serializing_if = "Option::is_none")]
210
+ #[ts(optional, as = "Option<_>")]
190
211
  pub maximum_fraction_digits: Option<f64>,
191
212
 
192
213
  #[serde(skip_serializing_if = "Option::is_none")]
214
+ #[ts(optional, as = "Option<_>")]
193
215
  pub minimum_significant_digits: Option<f64>,
194
216
 
195
217
  #[serde(skip_serializing_if = "Option::is_none")]
218
+ #[ts(optional, as = "Option<_>")]
196
219
  pub maximum_significant_digits: Option<f64>,
197
220
 
198
221
  #[serde(skip_serializing_if = "Option::is_none")]
222
+ #[ts(optional, as = "Option<_>")]
199
223
  pub rounding_priority: Option<RoundingPriority>,
200
224
 
201
225
  // specific values https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#roundingincrement
202
226
  // Only available with automatic rounding priority
203
227
  // Cannot be mixed with sigfig rounding. (Does this mean max/min sigfig must be unset?)
204
228
  #[serde(skip_serializing_if = "Option::is_none")]
229
+ #[ts(optional, as = "Option<_>")]
205
230
  pub rounding_increment: Option<f64>,
206
231
 
207
232
  #[serde(skip_serializing_if = "Option::is_none")]
233
+ #[ts(optional, as = "Option<_>")]
208
234
  pub rounding_mode: Option<RoundingMode>,
209
235
 
210
236
  #[serde(skip_serializing_if = "Option::is_none")]
237
+ #[ts(optional, as = "Option<_>")]
211
238
  pub trailing_zero_display: Option<TrailingZeroDisplay>,
212
239
 
213
240
  #[serde(flatten)]
@@ -215,10 +242,16 @@ pub struct CustomNumberFormatConfig {
215
242
  #[serde(skip_serializing_if = "Option::is_none")]
216
243
  pub _notation: Option<Notation>,
217
244
 
245
+ /// NOTE (audit 2026-08-05): serialized values are the STRINGS
246
+ /// `"always"`/`"auto"`/`"min2"` or the untagged BOOLEAN `false` —
247
+ /// the former hand-written TS `useGrouping?: boolean` was wrong for
248
+ /// the string cases.
218
249
  #[serde(skip_serializing_if = "Option::is_none")]
250
+ #[ts(optional, as = "Option<_>")]
219
251
  pub use_grouping: Option<UseGrouping>,
220
252
 
221
253
  #[serde(skip_serializing_if = "Option::is_none")]
254
+ #[ts(optional, as = "Option<_>")]
222
255
  pub sign_display: Option<SignDisplay>,
223
256
  }
224
257
 
@@ -28,6 +28,24 @@ pub struct PanelOptions {
28
28
  pub panel: Option<String>,
29
29
  }
30
30
 
31
+ /// Options for the `restore()` method.
32
+ #[derive(Deserialize, Default, TS)]
33
+ pub struct RestoreOptions {
34
+ /// The target panel; the active panel when omitted.
35
+ #[ts(optional)]
36
+ pub panel: Option<String>,
37
+
38
+ /// When `true`, a failed restore only REJECTS the returned `Promise` —
39
+ /// the error is not committed to the viewer's visible error state, and
40
+ /// the session remains usable for subsequent calls. For programmatic
41
+ /// callers (e.g. the LLM agent's `set_view_config` tool) for whom a
42
+ /// failed config patch is feedback rather than a user-facing fault.
43
+ /// The config may be partially applied on failure; restore a
44
+ /// known-good config to recover exactly.
45
+ #[ts(optional)]
46
+ pub suppress_errors: Option<bool>,
47
+ }
48
+
31
49
  /// The `eject` argument: the loaded client to remove by name; the active
32
50
  /// panel's client when omitted.
33
51
  #[derive(Deserialize, Default, TS)]
@@ -69,3 +87,13 @@ pub struct GetClientOptions {
69
87
  #[ts(optional)]
70
88
  pub panel: Option<String>,
71
89
  }
90
+
91
+ /// Options for the `saveWorkspace()` method.
92
+ #[derive(Deserialize, Default, TS)]
93
+ pub struct SaveWorkspaceOptions {
94
+ /// When `true`, the emitted `palette` is the full set the element
95
+ /// holds rather than only the values the panels reference.
96
+ #[serde(default)]
97
+ #[ts(optional)]
98
+ pub full_palette: Option<bool>,
99
+ }
@@ -10,7 +10,7 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- use perspective_client::config::GroupRollupMode;
13
+ use perspective_client::config::{GroupRollupMode, SplitRollupMode};
14
14
  use serde::Deserialize;
15
15
  use ts_rs::TS;
16
16
 
@@ -103,6 +103,13 @@ pub struct PluginStaticConfig {
103
103
  #[ts(optional)]
104
104
  pub group_rollup_modes: Option<Vec<GroupRollupMode>>,
105
105
 
106
+ /// Split-rollup modes the plugin accepts, in preference order.
107
+ /// The first entry that matches a feature flag becomes the default.
108
+ #[serde(default)]
109
+ #[ts(as = "Option<_>")]
110
+ #[ts(optional)]
111
+ pub split_rollup_modes: Option<Vec<SplitRollupMode>>,
112
+
106
113
  /// Plugin load priority. Higher numbers win; ties resolve in
107
114
  /// registration order. The highest-priority plugin is loaded by
108
115
  /// default unless `restore({ plugin })` overrides it.
@@ -120,15 +127,70 @@ pub struct PluginStaticConfig {
120
127
  #[ts(as = "Option<_>")]
121
128
  #[ts(optional)]
122
129
  pub can_render_column_styles: bool,
130
+
131
+ /// What `group_by` MEANS visually for this plugin, e.g. `"X Axis"`
132
+ /// for the Y-series charts or `"Hierarchy"` for treemap/sunburst.
133
+ /// `None` where the field has no visual role of its own and is a
134
+ /// plain aggregation key (the X/Y charts, whose axes both come from
135
+ /// `columns`).
136
+ ///
137
+ /// This is the `group_by` counterpart of [`Self::config_column_names`]:
138
+ /// the same declaration that names the positional `columns` slots
139
+ /// should say what the other view fields draw, so that consumers —
140
+ /// the settings UI's field labels, and the agent's `list_plugins`
141
+ /// contract — read one source instead of restating the mapping.
142
+ #[serde(default)]
143
+ #[ts(as = "Option<_>")]
144
+ #[ts(optional)]
145
+ pub group_by_role: Option<String>,
146
+
147
+ /// What `split_by` MEANS visually for this plugin, e.g. `"Series"`.
148
+ /// See [`Self::group_by_role`].
149
+ #[serde(default)]
150
+ #[ts(as = "Option<_>")]
151
+ #[ts(optional)]
152
+ pub split_by_role: Option<String>,
153
+
154
+ /// `true` when this plugin CONNECTS its points in row order, so the
155
+ /// `View`'s row order is visible in the drawing: an unsorted config
156
+ /// renders the table's natural order, which reads as a tangle unless
157
+ /// the rows already arrive ordered along the axis. Declared rather
158
+ /// than inferred, because "unsorted" is not itself a mistake — a
159
+ /// pre-ordered table needs no `sort`, and the point plugins
160
+ /// (scatter, density) do not care at all.
161
+ #[serde(default)]
162
+ #[ts(as = "Option<_>")]
163
+ #[ts(optional)]
164
+ pub connects_row_order: bool,
123
165
  }
124
166
 
125
167
  impl PluginStaticConfig {
168
+ /// The number of leading `columns` slots that are POSITIONAL: a drop
169
+ /// there swaps with the column already present, and the slot's
170
+ /// meaning is fixed by [`Self::config_column_names`]. Everything from
171
+ /// this index on is the insert TAIL, which repeats the last named
172
+ /// role — which is why a `Y Line` (named slots: `["Y Axis"]`) takes
173
+ /// any number of columns as additional Y series, while an
174
+ /// `X/Y Line` (`["X Axis", "Y Axis", "Tooltip"]`) pins its two axes
175
+ /// and treats the rest as further tooltips.
176
+ ///
177
+ /// The single definition of that rule: [`Self::is_swap`] and the
178
+ /// agent's `list_plugins` contract both derive from it, so the
179
+ /// convention cannot be read two different ways.
180
+ pub fn positional_columns(&self) -> usize {
181
+ self.config_column_names.len().saturating_sub(1)
182
+ }
183
+
184
+ /// The role that `columns` past the positional slots repeat, i.e.
185
+ /// the last named slot. `None` when the plugin names no slots.
186
+ pub fn tail_column_role(&self) -> Option<&str> {
187
+ self.config_column_names.last().map(|x| x.as_str())
188
+ }
189
+
126
190
  /// `true` if dropping a column at `index` should swap with the
127
- /// column already there rather than insert. Only the named slots
128
- /// (`config_column_names[..len()-1]`) participate in swap behaviour;
129
- /// the trailing unnamed tail inserts.
191
+ /// column already there rather than insert.
130
192
  pub fn is_swap(&self, index: usize) -> bool {
131
- !self.config_column_names.is_empty() && index < self.config_column_names.len() - 1
193
+ !self.config_column_names.is_empty() && index < self.positional_columns()
132
194
  }
133
195
 
134
196
  pub fn get_group_rollups(&self, rollup_features: &[GroupRollupMode]) -> Vec<GroupRollupMode> {
@@ -141,4 +203,57 @@ impl PluginStaticConfig {
141
203
  })
142
204
  .unwrap_or_default()
143
205
  }
206
+
207
+ pub fn get_split_rollups(&self, rollup_features: &[SplitRollupMode]) -> Vec<SplitRollupMode> {
208
+ self.split_rollup_modes
209
+ .clone()
210
+ .map(|x| {
211
+ x.into_iter()
212
+ .filter(|y| rollup_features.is_empty() || rollup_features.contains(y))
213
+ .collect()
214
+ })
215
+ .unwrap_or_default()
216
+ }
217
+ }
218
+
219
+ #[cfg(test)]
220
+ mod tests {
221
+ use super::*;
222
+
223
+ fn plugin(names: &[&str]) -> PluginStaticConfig {
224
+ PluginStaticConfig {
225
+ config_column_names: names.iter().map(|x| (*x).to_owned()).collect(),
226
+ ..Default::default()
227
+ }
228
+ }
229
+
230
+ /// The tail rule has ONE definition: `is_swap` and the role a
231
+ /// trailing column repeats must never disagree about where the
232
+ /// positional slots end.
233
+ #[test]
234
+ fn positional_slots_and_tail_agree() {
235
+ // `Y Line` — the only named slot IS the tail, so every column
236
+ // is another Y series and none of them swap.
237
+ let y_line = plugin(&["Y Axis"]);
238
+ assert_eq!(y_line.positional_columns(), 0);
239
+ assert_eq!(y_line.tail_column_role(), Some("Y Axis"));
240
+ assert!(!y_line.is_swap(0));
241
+
242
+ // `X/Y Line` — two pinned axes, then tooltips.
243
+ let xy_line = plugin(&["X Axis", "Y Axis", "Tooltip"]);
244
+ assert_eq!(xy_line.positional_columns(), 2);
245
+ assert_eq!(xy_line.tail_column_role(), Some("Tooltip"));
246
+ assert!(xy_line.is_swap(0));
247
+ assert!(xy_line.is_swap(1));
248
+ assert!(!xy_line.is_swap(2));
249
+
250
+ for index in 0..4 {
251
+ assert_eq!(xy_line.is_swap(index), index < xy_line.positional_columns());
252
+ }
253
+
254
+ let unnamed = plugin(&[]);
255
+ assert_eq!(unnamed.positional_columns(), 0);
256
+ assert_eq!(unnamed.tail_column_role(), None);
257
+ assert!(!unnamed.is_swap(0));
258
+ }
144
259
  }