@perspective-dev/viewer 5.4.0 → 5.5.1

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 (142) hide show
  1. package/dist/cdn/perspective-viewer.js +4 -4
  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 -1
  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/perspective-viewer.d.ts +2 -0
  31. package/dist/esm/perspective-viewer.inline.js +9 -9
  32. package/dist/esm/perspective-viewer.inline.js.map +3 -3
  33. package/dist/esm/perspective-viewer.js +4 -4
  34. package/dist/esm/perspective-viewer.js.map +2 -2
  35. package/dist/esm/plugin.d.ts +9 -0
  36. package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
  37. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -1
  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/WorkspaceConfigUpdate.d.ts +30 -22
  43. package/dist/wasm/perspective-viewer.d.ts +102 -47
  44. package/dist/wasm/perspective-viewer.js +233 -142
  45. package/dist/wasm/perspective-viewer.wasm +0 -0
  46. package/dist/wasm/perspective-viewer.wasm.d.ts +18 -18
  47. package/package.json +7 -7
  48. package/src/css/column-selector.css +11 -3
  49. package/src/css/column-style.css +162 -0
  50. package/src/css/config-selector.css +1 -1
  51. package/src/css/form/debug.css +9 -0
  52. package/src/css/status-bar.css +11 -0
  53. package/src/css/ui/containers/tabs.css +2 -1
  54. package/src/rust/agent/tools.rs +80 -7
  55. package/src/rust/components/column_selector/active_column.rs +9 -0
  56. package/src/rust/components/column_selector.rs +11 -0
  57. package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
  58. package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
  59. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +1 -1
  60. package/src/rust/components/column_settings_sidebar/style_tab.rs +68 -9
  61. package/src/rust/components/debug_panel.rs +107 -50
  62. package/src/rust/components/export_dropdown.rs +6 -0
  63. package/src/rust/components/main_panel/msg.rs +4 -0
  64. package/src/rust/components/main_panel.rs +14 -0
  65. package/src/rust/components/mod.rs +0 -1
  66. package/src/rust/components/plugin_tab.rs +57 -3
  67. package/src/rust/components/settings_panel.rs +6 -0
  68. package/src/rust/components/status_bar.rs +12 -3
  69. package/src/rust/components/status_indicator.rs +4 -0
  70. package/src/rust/components/viewer/filters.rs +2 -2
  71. package/src/rust/components/viewer/msg.rs +4 -0
  72. package/src/rust/components/viewer/panels.rs +6 -3
  73. package/src/rust/components/viewer/render.rs +1 -0
  74. package/src/rust/components/viewer/wiring.rs +14 -2
  75. package/src/rust/components/viewer.rs +2 -2
  76. package/src/rust/config/column_config_schema.rs +312 -8
  77. package/src/rust/config/datetime_column_style.rs +0 -11
  78. package/src/rust/config/export_method.rs +24 -0
  79. package/src/rust/config/mod.rs +0 -2
  80. package/src/rust/config/options.rs +50 -0
  81. package/src/rust/config/workspace_config.rs +69 -24
  82. package/src/rust/custom_elements/viewer.rs +214 -113
  83. package/src/rust/js/plugin.rs +3 -1
  84. package/src/rust/lib.rs +7 -3
  85. package/src/rust/queries/plugin_column_styles.rs +6 -5
  86. package/src/rust/renderer/plugin_config.rs +200 -41
  87. package/src/rust/renderer/plugin_store.rs +2 -1
  88. package/src/rust/renderer/props.rs +5 -0
  89. package/src/rust/renderer.rs +9 -0
  90. package/src/rust/session/metadata.rs +5 -10
  91. package/src/rust/session/props.rs +4 -0
  92. package/src/rust/session.rs +273 -195
  93. package/src/rust/tasks/copy_export.rs +67 -0
  94. package/src/rust/tasks/create_panel.rs +16 -13
  95. package/src/rust/tasks/pipeline.rs +26 -8
  96. package/src/rust/tasks/restore_panel.rs +129 -30
  97. package/src/rust/tasks/send_column_config.rs +3 -0
  98. package/src/rust/tasks/sync_update_panels.rs +148 -68
  99. package/src/rust/tasks/table_lifecycle.rs +7 -1
  100. package/src/rust/ui/containers/tab_list.rs +1 -1
  101. package/src/rust/ui/form/intl_label.rs +4 -23
  102. package/src/rust/ui/portal.rs +1 -0
  103. package/src/rust/utils/css_vars.rs +13 -0
  104. package/src/rust/{config/datetime_column_style/color_mode.rs → utils/in_flight.rs} +47 -35
  105. package/src/rust/utils/local_fonts.rs +395 -0
  106. package/src/rust/utils/mod.rs +4 -0
  107. package/src/rust/utils/pending_effects.rs +6 -30
  108. package/src/rust/workspace.rs +12 -0
  109. package/src/svg/status_pending.svg +3 -0
  110. package/src/themes/icons.css +1 -0
  111. package/src/themes/intl/ar.css +20 -7
  112. package/src/themes/intl/de.css +20 -7
  113. package/src/themes/intl/es.css +20 -7
  114. package/src/themes/intl/fr.css +20 -7
  115. package/src/themes/intl/ja.css +20 -7
  116. package/src/themes/intl/pt.css +20 -7
  117. package/src/themes/intl/zh.css +20 -7
  118. package/src/themes/intl.css +20 -7
  119. package/src/themes/monokai.css +1 -1
  120. package/src/themes/vaporwave.css +1 -1
  121. package/src/ts/perspective-viewer.ts +2 -0
  122. package/src/ts/plugin.ts +9 -0
  123. package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
  124. package/src/ts/ts-rs/ExportMethod.ts +1 -1
  125. package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
  126. package/src/ts/ts-rs/GetViewOptions.ts +7 -0
  127. package/src/ts/ts-rs/RestoreOptions.ts +7 -1
  128. package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
  129. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
  130. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  131. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  132. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  133. package/src/rust/components/string_column_style.rs +0 -120
  134. package/src/rust/config/string_column_style.rs +0 -135
  135. package/src/ts/ts-rs/FormatMode.ts +0 -3
  136. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  137. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline0.js +0 -0
  138. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline1.js +0 -0
  139. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline2.js +0 -0
  140. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline3.js +0 -0
  141. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline4.js +0 -0
  142. /package/dist/wasm/snippets/{perspective-viewer-adc6f3f79141112b → perspective-viewer-6813b568219c6763}/inline5.js +0 -0
@@ -74,6 +74,38 @@ pub enum ControlSpec {
74
74
  variants: Vec<EnumVariant>,
75
75
  default: String,
76
76
  },
77
+
78
+ /// A CSS `font-family` picked from the generic keywords plus the families
79
+ /// the host can enumerate, with optional size, bold and italic inputs
80
+ /// each bound to its own key.
81
+ Font {
82
+ key: String,
83
+ default: String,
84
+
85
+ #[serde(default, skip_serializing_if = "Option::is_none")]
86
+ size: Option<FontSize>,
87
+
88
+ #[serde(default, skip_serializing_if = "Option::is_none")]
89
+ bold: Option<FontToggle>,
90
+
91
+ #[serde(default, skip_serializing_if = "Option::is_none")]
92
+ italic: Option<FontToggle>,
93
+ },
94
+ /// A 3×3 anchor picker whose value is one of the nine [`Alignment`]
95
+ /// tokens.
96
+ Alignment {
97
+ key: String,
98
+
99
+ /// The cell shown as the unmodified value and elided from serialized
100
+ /// configs, or `None` when an unset key stands for something no cell
101
+ /// can show.
102
+ #[serde(default, skip_serializing_if = "Option::is_none")]
103
+ default: Option<Alignment>,
104
+
105
+ /// Only the four corner cells are selectable.
106
+ #[serde(default, skip_serializing_if = "std::ops::Not::not")]
107
+ corners: bool,
108
+ },
77
109
  Bool {
78
110
  key: String,
79
111
  default: bool,
@@ -125,7 +157,6 @@ pub enum ControlSpec {
125
157
  #[serde(default, skip_serializing_if = "Option::is_none")]
126
158
  default: Option<DatetimeFormatType>,
127
159
  },
128
- StringFormat,
129
160
  NumberSeriesStyle {
130
161
  default: NumberSeriesStyleDefaultConfig,
131
162
  },
@@ -147,6 +178,108 @@ pub enum ControlSpec {
147
178
  },
148
179
  }
149
180
 
181
+ /// One cell of a 3×3 anchor grid: a corner `top-left`/`top-right`/
182
+ /// `bottom-left`/`bottom-right`, an edge `top`/`left`/`right`/`bottom`, or
183
+ /// `center`.
184
+ #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
185
+ #[serde(rename_all = "kebab-case")]
186
+ pub enum Alignment {
187
+ TopLeft,
188
+ Top,
189
+ TopRight,
190
+ Left,
191
+ Center,
192
+ Right,
193
+ BottomLeft,
194
+ Bottom,
195
+ BottomRight,
196
+ }
197
+
198
+ impl Alignment {
199
+ /// Every cell in row-major order.
200
+ pub const ALL: [Alignment; 9] = [
201
+ Alignment::TopLeft,
202
+ Alignment::Top,
203
+ Alignment::TopRight,
204
+ Alignment::Left,
205
+ Alignment::Center,
206
+ Alignment::Right,
207
+ Alignment::BottomLeft,
208
+ Alignment::Bottom,
209
+ Alignment::BottomRight,
210
+ ];
211
+ pub const CORNERS: [Alignment; 4] = [
212
+ Alignment::TopLeft,
213
+ Alignment::TopRight,
214
+ Alignment::BottomLeft,
215
+ Alignment::BottomRight,
216
+ ];
217
+
218
+ pub fn is_corner(self) -> bool {
219
+ Self::CORNERS.contains(&self)
220
+ }
221
+
222
+ /// The serialized token.
223
+ pub fn as_str(self) -> &'static str {
224
+ match self {
225
+ Alignment::TopLeft => "top-left",
226
+ Alignment::Top => "top",
227
+ Alignment::TopRight => "top-right",
228
+ Alignment::Left => "left",
229
+ Alignment::Center => "center",
230
+ Alignment::Right => "right",
231
+ Alignment::BottomLeft => "bottom-left",
232
+ Alignment::Bottom => "bottom",
233
+ Alignment::BottomRight => "bottom-right",
234
+ }
235
+ }
236
+
237
+ pub fn parse(src: &str) -> Option<Self> {
238
+ Self::ALL.into_iter().find(|x| x.as_str() == src)
239
+ }
240
+
241
+ /// `Top Left`-style text for accessible names.
242
+ pub fn humanized(self) -> String {
243
+ self.as_str()
244
+ .split('-')
245
+ .map(|word| {
246
+ let mut chars = word.chars();
247
+ match chars.next() {
248
+ Some(first) => first.to_uppercase().chain(chars).collect::<String>(),
249
+ None => String::new(),
250
+ }
251
+ })
252
+ .collect::<Vec<_>>()
253
+ .join(" ")
254
+ }
255
+ }
256
+
257
+ /// One boolean style toggle of a [`ControlSpec::Font`] control: the
258
+ /// config key it writes and the value that counts as "not set".
259
+ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
260
+ pub struct FontToggle {
261
+ pub key: String,
262
+ #[serde(default)]
263
+ pub default: bool,
264
+ }
265
+
266
+ /// The size input of a [`ControlSpec::Font`] control: a number (CSS px)
267
+ /// under its own key, elided from serialized configs at `default`.
268
+ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
269
+ pub struct FontSize {
270
+ pub key: String,
271
+ pub default: f64,
272
+
273
+ #[serde(default, skip_serializing_if = "Option::is_none")]
274
+ pub min: Option<f64>,
275
+
276
+ #[serde(default, skip_serializing_if = "Option::is_none")]
277
+ pub max: Option<f64>,
278
+
279
+ #[serde(default, skip_serializing_if = "Option::is_none")]
280
+ pub step: Option<f64>,
281
+ }
282
+
150
283
  #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
151
284
  pub struct EnumVariant {
152
285
  pub value: String,
@@ -182,9 +315,7 @@ impl ColumnConfigSchema {
182
315
  fn is_format(spec: &ControlSpec) -> bool {
183
316
  matches!(
184
317
  spec,
185
- ControlSpec::NumberFormat { .. }
186
- | ControlSpec::DatetimeFormat { .. }
187
- | ControlSpec::StringFormat
318
+ ControlSpec::NumberFormat { .. } | ControlSpec::DatetimeFormat { .. }
188
319
  )
189
320
  }
190
321
 
@@ -230,6 +361,18 @@ impl ColumnConfigSchema {
230
361
  canonicalize_specs(fields);
231
362
  return !fields.is_empty();
232
363
  },
364
+ ControlSpec::Alignment {
365
+ key,
366
+ default: Some(default),
367
+ corners: true,
368
+ } if !default.is_corner() => {
369
+ tracing::error!(
370
+ "Dropping `{key}` — default `{}` is not a corner",
371
+ default.as_str()
372
+ );
373
+
374
+ return false;
375
+ },
233
376
  ControlSpec::Color { key, default } => (CssKind::Color, key, default),
234
377
  ControlSpec::Palette { key, default, .. } => (CssKind::Palette, key, default),
235
378
  ControlSpec::GradientStops { key, default, .. } => {
@@ -255,6 +398,16 @@ impl ColumnConfigSchema {
255
398
  self
256
399
  }
257
400
 
401
+ /// The variants of the [`ControlSpec::Enum`] owning `key`, if any.
402
+ pub fn enum_variants_of(&self, key: &str) -> Option<&[EnumVariant]> {
403
+ self.leaf_fields().into_iter().find_map(|spec| match spec {
404
+ ControlSpec::Enum {
405
+ key: k, variants, ..
406
+ } if k == key => Some(variants.as_slice()),
407
+ _ => None,
408
+ })
409
+ }
410
+
258
411
  /// The CSS kind of the control owning `key`, if it is CSS-valued.
259
412
  pub fn css_kind_of(&self, key: &str) -> Option<CssKind> {
260
413
  self.leaf_fields().into_iter().find_map(|spec| match spec {
@@ -275,12 +428,27 @@ impl ControlSpec {
275
428
  pub fn serialized_keys(&self) -> Vec<&str> {
276
429
  match self {
277
430
  ControlSpec::DatetimeFormat { .. } => vec!["date_format"],
278
- ControlSpec::StringFormat => vec!["format"],
431
+ ControlSpec::Font {
432
+ key,
433
+ size,
434
+ bold,
435
+ italic,
436
+ ..
437
+ } => [
438
+ Some(key.as_str()),
439
+ size.as_ref().map(|s| s.key.as_str()),
440
+ bold.as_ref().map(|t| t.key.as_str()),
441
+ italic.as_ref().map(|t| t.key.as_str()),
442
+ ]
443
+ .into_iter()
444
+ .flatten()
445
+ .collect(),
279
446
  ControlSpec::NumberSeriesStyle { .. } => vec!["chart_type", "stack"],
280
447
  ControlSpec::Symbols { .. } => vec!["symbols"],
281
448
  ControlSpec::NumberFormat { .. } => vec!["number_format"],
282
449
  ControlSpec::AggregateDepth => vec!["aggregate_depth"],
283
450
  ControlSpec::Enum { key, .. }
451
+ | ControlSpec::Alignment { key, .. }
284
452
  | ControlSpec::Bool { key, .. }
285
453
  | ControlSpec::Number { key, .. }
286
454
  | ControlSpec::String { key, .. }
@@ -396,13 +564,46 @@ mod tests {
396
564
  assert_eq!(flat.css_kind_of("stack"), grouped.css_kind_of("stack"));
397
565
  }
398
566
 
567
+ #[test]
568
+ fn enum_variants_see_through_groups() {
569
+ let mode = ControlSpec::Enum {
570
+ key: "fg_mode".to_owned(),
571
+ default: "color".to_owned(),
572
+ variants: vec![
573
+ EnumVariant {
574
+ value: "disabled".to_owned(),
575
+ label: None,
576
+ },
577
+ EnumVariant {
578
+ value: "color".to_owned(),
579
+ label: None,
580
+ },
581
+ ],
582
+ };
583
+
584
+ let grouped = ColumnConfigSchema {
585
+ fields: vec![group("color", vec![mode, flag("flag")])],
586
+ };
587
+
588
+ let values: Vec<&str> = grouped
589
+ .enum_variants_of("fg_mode")
590
+ .unwrap()
591
+ .iter()
592
+ .map(|v| v.value.as_str())
593
+ .collect();
594
+
595
+ assert_eq!(values, vec!["disabled", "color"]);
596
+ assert!(grouped.enum_variants_of("flag").is_none());
597
+ assert!(grouped.enum_variants_of("missing").is_none());
598
+ }
599
+
399
600
  #[test]
400
601
  fn format_controls_group_and_merge() {
401
602
  let schema = ColumnConfigSchema {
402
603
  fields: vec![
403
604
  ControlSpec::NumberFormat { default: None },
404
605
  flag("flag"),
405
- ControlSpec::StringFormat,
606
+ ControlSpec::DatetimeFormat { default: None },
406
607
  ],
407
608
  }
408
609
  .group_format_controls();
@@ -414,19 +615,45 @@ mod tests {
414
615
 
415
616
  assert_eq!(key, "format");
416
617
  assert!(matches!(fields[0], ControlSpec::NumberFormat { .. }));
417
- assert!(matches!(fields[1], ControlSpec::StringFormat));
618
+ assert!(matches!(fields[1], ControlSpec::DatetimeFormat { .. }));
418
619
  assert!(matches!(&schema.fields[1], ControlSpec::Bool { .. }));
419
620
 
420
621
  assert_eq!(
421
622
  schema.active_keys(),
422
623
  HashSet::from([
423
624
  "number_format".to_owned(),
424
- "format".to_owned(),
625
+ "date_format".to_owned(),
425
626
  "flag".to_owned()
426
627
  ])
427
628
  );
428
629
  }
429
630
 
631
+ #[test]
632
+ fn font_owns_its_size_and_toggle_keys() {
633
+ let spec = ControlSpec::Font {
634
+ key: "font_family".to_owned(),
635
+ default: "inherit".to_owned(),
636
+ size: Some(FontSize {
637
+ key: "font_size".to_owned(),
638
+ default: 12.0,
639
+ min: None,
640
+ max: None,
641
+ step: None,
642
+ }),
643
+ bold: Some(FontToggle {
644
+ key: "bold".to_owned(),
645
+ default: false,
646
+ }),
647
+ italic: None,
648
+ };
649
+
650
+ assert_eq!(spec.serialized_keys(), vec![
651
+ "font_family",
652
+ "font_size",
653
+ "bold"
654
+ ]);
655
+ }
656
+
430
657
  #[test]
431
658
  fn format_grouping_recurses_but_never_double_wraps() {
432
659
  let schema = ColumnConfigSchema {
@@ -536,6 +763,83 @@ mod tests {
536
763
  );
537
764
  }
538
765
 
766
+ #[test]
767
+ fn alignment_tokens_round_trip() {
768
+ for align in Alignment::ALL {
769
+ assert_eq!(Alignment::parse(align.as_str()), Some(align));
770
+ assert_eq!(serde_json::to_value(align).unwrap(), json!(align.as_str()));
771
+ }
772
+
773
+ for bad in ["middle", "left-top", "top-center", "middle-left", ""] {
774
+ assert_eq!(Alignment::parse(bad), None, "{bad}");
775
+ }
776
+
777
+ assert_eq!(Alignment::ALL.iter().filter(|x| x.is_corner()).count(), 4);
778
+ assert_eq!(Alignment::TopLeft.humanized(), "Top Left");
779
+ assert_eq!(Alignment::Center.humanized(), "Center");
780
+ }
781
+
782
+ #[test]
783
+ fn alignment_deserializes_with_optional_default_and_corners() {
784
+ let schema: ColumnConfigSchema = serde_json::from_value(json!({
785
+ "fields": [
786
+ { "kind": "Alignment", "key": "align" },
787
+ { "kind": "Alignment", "key": "legend_anchor", "default": "top-right", "corners": true }
788
+ ]
789
+ }))
790
+ .unwrap();
791
+
792
+ assert!(matches!(&schema.fields[0], ControlSpec::Alignment {
793
+ default: None,
794
+ corners: false,
795
+ ..
796
+ }));
797
+ assert!(matches!(&schema.fields[1], ControlSpec::Alignment {
798
+ default: Some(Alignment::TopRight),
799
+ corners: true,
800
+ ..
801
+ }));
802
+ assert_eq!(
803
+ schema.active_keys(),
804
+ HashSet::from(["align".to_owned(), "legend_anchor".to_owned()])
805
+ );
806
+ assert!(
807
+ serde_json::from_value::<ColumnConfigSchema>(json!({
808
+ "fields": [{ "kind": "Alignment", "key": "x", "default": "middle" }]
809
+ }))
810
+ .is_err()
811
+ );
812
+ }
813
+
814
+ #[test]
815
+ fn canonicalize_defaults_drops_non_corner_default_of_corners_field() {
816
+ let schema = ColumnConfigSchema {
817
+ fields: vec![
818
+ ControlSpec::Alignment {
819
+ key: "bad".to_owned(),
820
+ default: Some(Alignment::Top),
821
+ corners: true,
822
+ },
823
+ ControlSpec::Alignment {
824
+ key: "ok".to_owned(),
825
+ default: Some(Alignment::Top),
826
+ corners: false,
827
+ },
828
+ ControlSpec::Alignment {
829
+ key: "unset".to_owned(),
830
+ default: None,
831
+ corners: true,
832
+ },
833
+ ],
834
+ }
835
+ .canonicalize_defaults();
836
+
837
+ assert_eq!(
838
+ schema.active_keys(),
839
+ HashSet::from(["ok".to_owned(), "unset".to_owned()])
840
+ );
841
+ }
842
+
539
843
  #[test]
540
844
  fn canonicalize_defaults_recurses_and_drops_empty_groups() {
541
845
  let schema = ColumnConfigSchema {
@@ -10,13 +10,11 @@
10
10
  // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
- mod color_mode;
14
13
  mod custom;
15
14
  mod custom_format;
16
15
  mod simple;
17
16
  mod simple_format;
18
17
 
19
- pub use color_mode::*;
20
18
  pub use custom::*;
21
19
  pub use custom_format::*;
22
20
  use serde::{Deserialize, Serialize};
@@ -65,13 +63,4 @@ pub struct DatetimeColumnStyleConfig {
65
63
  #[serde(skip_serializing_if = "Option::is_none")]
66
64
  #[ts(optional, as = "Option<_>")]
67
65
  pub date_format: Option<DatetimeFormatType>,
68
-
69
- #[serde(default)]
70
- #[serde(skip_serializing_if = "DatetimeColorMode::is_none")]
71
- pub datetime_color_mode: DatetimeColorMode,
72
-
73
- #[serde(default)]
74
- #[serde(skip_serializing_if = "Option::is_none")]
75
- #[ts(skip)]
76
- pub color: Option<String>,
77
66
  }
@@ -69,6 +69,24 @@ pub enum ExportMethod {
69
69
  #[serde(rename = "arrow-all")]
70
70
  ArrowAll,
71
71
 
72
+ #[serde(rename = "arrow-lz4")]
73
+ ArrowLz4,
74
+
75
+ #[serde(rename = "arrow-lz4-selected")]
76
+ ArrowLz4Selected,
77
+
78
+ #[serde(rename = "arrow-lz4-all")]
79
+ ArrowLz4All,
80
+
81
+ #[serde(rename = "arrow-zstd")]
82
+ ArrowZstd,
83
+
84
+ #[serde(rename = "arrow-zstd-selected")]
85
+ ArrowZstdSelected,
86
+
87
+ #[serde(rename = "arrow-zstd-all")]
88
+ ArrowZstdAll,
89
+
72
90
  #[serde(rename = "json-config")]
73
91
  JsonConfig,
74
92
  }
@@ -94,6 +112,12 @@ impl ExportMethod {
94
112
  Self::CsvSelected => ".selected.csv",
95
113
  Self::JsonSelected => ".selected.json",
96
114
  Self::ArrowSelected => ".selected.arrow",
115
+ Self::ArrowLz4 => ".lz4.arrow",
116
+ Self::ArrowLz4Selected => ".selected.lz4.arrow",
117
+ Self::ArrowLz4All => ".all.lz4.arrow",
118
+ Self::ArrowZstd => ".zstd.arrow",
119
+ Self::ArrowZstdSelected => ".selected.zstd.arrow",
120
+ Self::ArrowZstdAll => ".all.zstd.arrow",
97
121
  Self::Ndjson => ".ndjson",
98
122
  Self::NdjsonAll => ".all.ndjson",
99
123
  Self::NdjsonSelected => ".selected.ndjson",
@@ -22,7 +22,6 @@ mod number_series_style;
22
22
  mod number_string_format;
23
23
  mod options;
24
24
  mod plugin_static_config;
25
- mod string_column_style;
26
25
  pub mod view_config;
27
26
  mod viewer_config;
28
27
  mod workspace_config;
@@ -35,7 +34,6 @@ pub use number_series_style::*;
35
34
  pub use number_string_format::*;
36
35
  pub use options::*;
37
36
  pub use plugin_static_config::*;
38
- pub use string_column_style::*;
39
37
  pub use view_config::*;
40
38
  pub use viewer_config::*;
41
39
  pub use workspace_config::*;
@@ -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)]