@perspective-dev/viewer 5.0.0 → 5.1.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 (106) 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/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/esm/perspective-viewer.inline.js +9 -9
  24. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  25. package/dist/esm/perspective-viewer.js +1 -1
  26. package/dist/esm/perspective-viewer.js.map +2 -2
  27. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +2 -0
  28. package/dist/esm/ts-rs/ViewerConfig.d.ts +2 -0
  29. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +9 -0
  30. package/dist/esm/ts-rs/WindowAggregate.d.ts +1 -0
  31. package/dist/esm/ts-rs/WindowSort.d.ts +7 -0
  32. package/dist/esm/ts-rs/WindowSortDir.d.ts +6 -0
  33. package/dist/esm/ts-rs/WindowSpec.d.ts +46 -0
  34. package/dist/esm/ts-rs/Windows.d.ts +10 -0
  35. package/dist/wasm/perspective-viewer.d.ts +103 -16
  36. package/dist/wasm/perspective-viewer.js +118 -108
  37. package/dist/wasm/perspective-viewer.wasm +0 -0
  38. package/dist/wasm/perspective-viewer.wasm.d.ts +15 -15
  39. package/package.json +1 -1
  40. package/src/css/column-selector.css +1 -0
  41. package/src/css/column-settings-panel.css +67 -2
  42. package/src/css/column-style.css +4 -3
  43. package/src/css/config-selector.css +22 -1
  44. package/src/css/containers/context-menu.css +12 -6
  45. package/src/css/containers/tabs.css +2 -2
  46. package/src/css/viewer.css +8 -0
  47. package/src/rust/components/column_selector/active_column.rs +21 -27
  48. package/src/rust/components/column_selector/column_selector_column_row.rs +73 -0
  49. package/src/rust/components/column_selector/config_selector.rs +7 -1
  50. package/src/rust/components/column_selector/expr_edit_button.rs +7 -1
  51. package/src/rust/components/column_selector/inactive_column.rs +8 -1
  52. package/src/rust/components/column_selector.rs +20 -3
  53. package/src/rust/components/column_settings_sidebar/style_tab.rs +2 -1
  54. package/src/rust/components/column_settings_sidebar/window_tab.rs +34 -0
  55. package/src/rust/components/column_settings_sidebar.rs +163 -16
  56. package/src/rust/components/containers/dragdrop_list.rs +73 -3
  57. package/src/rust/components/containers/sidebar.rs +99 -22
  58. package/src/rust/components/containers/split_panel.rs +6 -0
  59. package/src/rust/components/editable_header.rs +2 -0
  60. package/src/rust/components/main_panel.rs +5 -0
  61. package/src/rust/components/mod.rs +1 -0
  62. package/src/rust/components/panel_menu.rs +7 -0
  63. package/src/rust/components/panel_tab.rs +5 -0
  64. package/src/rust/components/portal.rs +7 -0
  65. package/src/rust/components/viewer/msg.rs +2 -0
  66. package/src/rust/components/viewer/render.rs +12 -0
  67. package/src/rust/components/viewer/settings.rs +33 -1
  68. package/src/rust/components/viewer.rs +2 -0
  69. package/src/rust/components/window_editor.rs +1013 -0
  70. package/src/rust/lib.rs +4 -1
  71. package/src/rust/presentation/column_locator.rs +16 -1
  72. package/src/rust/queries/columns_iter_set.rs +7 -1
  73. package/src/rust/renderer/dispatch.rs +85 -58
  74. package/src/rust/renderer.rs +34 -1
  75. package/src/rust/session/drag_drop_update.rs +18 -0
  76. package/src/rust/session/metadata.rs +66 -9
  77. package/src/rust/session/replace_expression_update.rs +1 -0
  78. package/src/rust/session.rs +7 -5
  79. package/src/rust/tasks/edit_window.rs +155 -0
  80. package/src/rust/tasks/mod.rs +2 -0
  81. package/src/rust/utils/browser/dragdrop.rs +12 -0
  82. package/src/rust/utils/browser/tests/debounce.rs +49 -0
  83. package/src/rust/utils/debounce.rs +76 -27
  84. package/src/svg/pin-icon.svg +5 -0
  85. package/src/themes/icons.css +1 -0
  86. package/src/themes/intl/de.css +7 -0
  87. package/src/themes/intl/es.css +7 -0
  88. package/src/themes/intl/fr.css +7 -0
  89. package/src/themes/intl/ja.css +7 -0
  90. package/src/themes/intl/pt.css +7 -0
  91. package/src/themes/intl/zh.css +7 -0
  92. package/src/themes/intl.css +10 -1
  93. package/src/ts/ts-rs/PanelViewerConfig.ts +2 -1
  94. package/src/ts/ts-rs/ViewerConfig.ts +2 -1
  95. package/src/ts/ts-rs/ViewerConfigUpdate.ts +9 -0
  96. package/src/ts/ts-rs/WindowAggregate.ts +3 -0
  97. package/src/ts/ts-rs/WindowSort.ts +9 -0
  98. package/src/ts/ts-rs/WindowSortDir.ts +8 -0
  99. package/src/ts/ts-rs/WindowSpec.ts +46 -0
  100. package/src/ts/ts-rs/Windows.ts +10 -0
  101. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline0.js +0 -0
  102. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline1.js +0 -0
  103. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline2.js +0 -0
  104. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline3.js +0 -0
  105. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline4.js +0 -0
  106. /package/dist/wasm/snippets/{perspective-viewer-daeef4ceb066e6c7 → perspective-viewer-8a8c7d5d696b43ae}/inline5.js +0 -0
@@ -17,6 +17,18 @@ pub enum DragTarget {
17
17
  SplitBy,
18
18
  Sort,
19
19
  Filter,
20
+ WindowSource,
21
+ WindowOrderBy,
22
+ WindowPartitionBy,
23
+ }
24
+
25
+ impl DragTarget {
26
+ pub fn is_staged(&self) -> bool {
27
+ matches!(
28
+ self,
29
+ Self::WindowSource | Self::WindowOrderBy | Self::WindowPartitionBy
30
+ )
31
+ }
20
32
  }
21
33
 
22
34
  #[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -86,6 +86,55 @@ pub async fn test_lock_seq() {
86
86
  assert_eq!(cell.get(), 10);
87
87
  }
88
88
 
89
+ /// A dedicated [`DebounceSlot`]'s task must RUN even when the default slot
90
+ /// has a parked runner — coalescing is scoped per slot. Pre-slot (one
91
+ /// shared parked flag), `t4` would resolve via `t2`'s settle without its
92
+ /// closure ever running: the resize-eaten-by-parked-update bug.
93
+ #[wasm_bindgen_test]
94
+ pub async fn test_slot_isolated_from_default_coalescing() {
95
+ let debounce_mutex = DebounceMutex::default();
96
+ let slot = debounce_mutex.slot();
97
+ let defaults: Rc<Cell<u32>> = Rc::new(Cell::new(0));
98
+ let slots: Rc<Cell<u32>> = Rc::new(Cell::new(0));
99
+
100
+ let t1 = debounce_mutex.lock(async {
101
+ set_timeout(10).await.unwrap();
102
+ });
103
+
104
+ let t2 = {
105
+ let defaults = defaults.clone();
106
+ debounce_mutex.debounce(async move {
107
+ defaults.set(defaults.get() + 1);
108
+ Ok(())
109
+ })
110
+ };
111
+
112
+ let t3 = {
113
+ let defaults = defaults.clone();
114
+ debounce_mutex.debounce(async move {
115
+ defaults.set(defaults.get() + 100);
116
+ Ok(())
117
+ })
118
+ };
119
+
120
+ let t4 = {
121
+ let slots = slots.clone();
122
+ slot.debounce_with(|_| async move {
123
+ slots.set(slots.get() + 1);
124
+ Ok(())
125
+ })
126
+ };
127
+
128
+ let (_, r2, r3, r4) = futures::join!(t1, t2, t3, t4);
129
+ r2.unwrap();
130
+ r3.unwrap();
131
+ r4.unwrap();
132
+
133
+ // `t3` coalesced onto the parked `t2`; `t4` ran its own closure.
134
+ assert_eq!(defaults.get(), 1);
135
+ assert_eq!(slots.get(), 1);
136
+ }
137
+
89
138
  #[wasm_bindgen_test]
90
139
  pub async fn test_debounce_seq() {
91
140
  let debounce_mutex = DebounceMutex::default();
@@ -33,16 +33,27 @@ pub struct RenderGuard {
33
33
  _private: (),
34
34
  }
35
35
 
36
+ /// The per-slot debounce bookkeeping: one running + one parked evaluation,
37
+ /// with coalesced callers awaiting the parked runner's settle. Kept
38
+ /// per-[`DebounceSlot`] so coalescing is only expressible WITHIN a kind —
39
+ /// a caller can never be absorbed by a parked runner of a different kind
40
+ /// whose work does not subsume it (the resize-eaten-by-parked-update bug).
36
41
  #[derive(Default)]
37
- struct DebounceMutexData {
42
+ struct SlotState {
38
43
  id: Cell<u64>,
39
- held: Cell<bool>,
40
- mutex: Mutex<u64>,
44
+ last: Cell<u64>,
41
45
  parked: Cell<bool>,
42
46
  settled_id: Cell<u64>,
43
47
  on_settle: PubSub<()>,
44
48
  }
45
49
 
50
+ #[derive(Default)]
51
+ struct DebounceMutexData {
52
+ held: Cell<bool>,
53
+ mutex: Mutex<()>,
54
+ default_slot: Rc<SlotState>,
55
+ }
56
+
46
57
  /// Clears the `held` flag on drop, so cancellation of a locked task (its
47
58
  /// future dropped mid-await) can't leave [`DebounceMutex::is_held`] stuck
48
59
  /// `true`.
@@ -67,25 +78,25 @@ impl Drop for HeldFlag<'_> {
67
78
  /// mid-run still frees the parked slot and releases its coalesced waiters,
68
79
  /// so they can never be stranded awaiting a settle that no task will emit.
69
80
  struct SettleGuard<'a> {
70
- data: &'a DebounceMutexData,
81
+ state: &'a SlotState,
71
82
  next: u64,
72
83
  }
73
84
 
74
85
  impl<'a> SettleGuard<'a> {
75
- fn park(data: &'a DebounceMutexData, next: u64) -> Self {
76
- data.parked.set(true);
77
- Self { data, next }
86
+ fn park(state: &'a SlotState, next: u64) -> Self {
87
+ state.parked.set(true);
88
+ Self { state, next }
78
89
  }
79
90
  }
80
91
 
81
92
  impl Drop for SettleGuard<'_> {
82
93
  fn drop(&mut self) {
83
- self.data.parked.set(false);
84
- if self.data.settled_id.get() < self.next {
85
- self.data.settled_id.set(self.next);
94
+ self.state.parked.set(false);
95
+ if self.state.settled_id.get() < self.next {
96
+ self.state.settled_id.set(self.next);
86
97
  }
87
98
 
88
- self.data.on_settle.emit(());
99
+ self.state.on_settle.emit(());
89
100
  }
90
101
  }
91
102
 
@@ -123,12 +134,11 @@ impl DebounceMutex {
123
134
  F: FnOnce(RenderGuard) -> Fut,
124
135
  Fut: Future<Output = T>,
125
136
  {
126
- let mut last = self.0.mutex.lock().await;
127
- let next = self.0.id.get();
137
+ let guard = self.0.mutex.lock().await;
128
138
  let held = HeldFlag::set(&self.0.held);
129
139
  let result = f(RenderGuard { _private: () }).await;
130
140
  drop(held);
131
- *last = next;
141
+ drop(guard);
132
142
  result
133
143
  }
134
144
 
@@ -144,23 +154,62 @@ impl DebounceMutex {
144
154
  F: FnOnce(RenderGuard) -> Fut,
145
155
  Fut: Future<Output = ApiResult<T>>,
146
156
  {
147
- let next = self.0.id.get() + 1;
148
- if self.0.parked.get() {
157
+ DebounceSlot {
158
+ mutex: self.clone(),
159
+ state: self.0.default_slot.clone(),
160
+ }
161
+ .debounce_with(f)
162
+ .await
163
+ }
164
+
165
+ /// A NEW debounce slot over this mutex: its tasks serialize against
166
+ /// every other locked task, but coalesce only among themselves.
167
+ pub fn slot(&self) -> DebounceSlot {
168
+ DebounceSlot {
169
+ mutex: self.clone(),
170
+ state: Default::default(),
171
+ }
172
+ }
173
+ }
174
+
175
+ /// One debounce KIND over a shared [`DebounceMutex`]. Execution is
176
+ /// exclusive across the whole mutex; coalescing (a caller resolving via a
177
+ /// parked runner instead of queueing) is scoped to the slot. A coalesced
178
+ /// caller's work must be subsumable by any same-slot runner that runs no
179
+ /// earlier than the call — which is why parameterized tasks sharing a slot
180
+ /// must read their parameters at RUN time (e.g. the [`Renderer`] geometry
181
+ /// command cell), never capture them at call time.
182
+ #[derive(Clone)]
183
+ pub struct DebounceSlot {
184
+ mutex: DebounceMutex,
185
+ state: Rc<SlotState>,
186
+ }
187
+
188
+ impl DebounceSlot {
189
+ pub async fn debounce_with<T, F, Fut>(&self, f: F) -> ApiResult<T>
190
+ where
191
+ T: Default,
192
+ F: FnOnce(RenderGuard) -> Fut,
193
+ Fut: Future<Output = ApiResult<T>>,
194
+ {
195
+ let state = &self.state;
196
+ let next = state.id.get() + 1;
197
+ if state.parked.get() {
149
198
  self.await_settled(next).await;
150
199
  return Ok(T::default());
151
200
  }
152
201
 
153
- let settle = SettleGuard::park(&self.0, next);
154
- let mut last = self.0.mutex.lock().await;
155
- self.0.parked.set(false);
156
- let result = if *last < next {
157
- let next = self.0.id.get() + 1;
158
- self.0.id.set(next);
159
- let held = HeldFlag::set(&self.0.held);
202
+ let settle = SettleGuard::park(state, next);
203
+ let guard = self.mutex.0.mutex.lock().await;
204
+ state.parked.set(false);
205
+ let result = if state.last.get() < next {
206
+ let next = state.id.get() + 1;
207
+ state.id.set(next);
208
+ let held = HeldFlag::set(&self.mutex.0.held);
160
209
  let result = f(RenderGuard { _private: () }).await;
161
210
  drop(held);
162
211
  if result.is_ok() {
163
- *last = next;
212
+ state.last.set(next);
164
213
  }
165
214
 
166
215
  result
@@ -168,14 +217,14 @@ impl DebounceMutex {
168
217
  Ok(T::default())
169
218
  };
170
219
 
171
- drop(last);
220
+ drop(guard);
172
221
  drop(settle);
173
222
  result
174
223
  }
175
224
 
176
225
  async fn await_settled(&self, next: u64) {
177
- while self.0.settled_id.get() < next {
178
- if self.0.on_settle.read_next().await.is_err() {
226
+ while self.state.settled_id.get() < next {
227
+ if self.state.on_settle.read_next().await.is_err() {
179
228
  break;
180
229
  }
181
230
  }
@@ -0,0 +1,5 @@
1
+ <svg width="14" height="14" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ d="M8 2h6v2l-1 1v4l3 2v2h-4v6l-1 2-1-2v-6H6v-2l3-2V5L8 4z"
4
+ stroke="#042121" />
5
+ </svg>
@@ -52,6 +52,7 @@ perspective-viewer-tab {
52
52
  --psp-icon--sort-none--mask-image: url("../svg/sort-none-icon.svg");
53
53
  --psp-icon--add-expression--mask-image: url("../svg/expression.svg");
54
54
  --psp-icon--close--mask-image: url("../svg/close-icon.svg");
55
+ --psp-icon--pin--mask-image: url("../svg/pin-icon.svg");
55
56
  --psp-label--inactive-column-selector--content: url("../svg/checkbox-unchecked-icon.svg");
56
57
  --psp-label--active-column-selector--content: url("../svg/checkbox-checked-icon.svg");
57
58
  --psp-icon--select-arrow-light--mask-image: url("../svg/dropdown-selector-light.svg");
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "Stil";
110
110
  --psp-label--attributes-tab--content: "Attribute";
111
+ --psp-label--window-tab--content: "Fenster";
112
+ --psp-label--window-source--content: "Spalte";
113
+ --psp-label--window-order-by--content: "Sortieren nach";
114
+ --psp-label--window-partition-by--content: "Partitionieren nach";
115
+ --psp-label--window-frame--content: "Rahmen";
116
+ --psp-label--window-offset--content: "Versatz";
117
+ --psp-label--window-alpha--content: "Alpha";
111
118
  --psp-label--query-tab--content: "Abfrage";
112
119
  --psp-label--plugin-tab--content: "Plugin";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "Estilo";
110
110
  --psp-label--attributes-tab--content: "Atributos";
111
+ --psp-label--window-tab--content: "Ventana";
112
+ --psp-label--window-source--content: "Columna";
113
+ --psp-label--window-order-by--content: "Ordenar por";
114
+ --psp-label--window-partition-by--content: "Particionar por";
115
+ --psp-label--window-frame--content: "Marco";
116
+ --psp-label--window-offset--content: "Desplazamiento";
117
+ --psp-label--window-alpha--content: "Alfa";
111
118
  --psp-label--query-tab--content: "Consulta";
112
119
  --psp-label--plugin-tab--content: "Plugin";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "Style";
110
110
  --psp-label--attributes-tab--content: "Les attributs";
111
+ --psp-label--window-tab--content: "Fenêtre";
112
+ --psp-label--window-source--content: "Colonne";
113
+ --psp-label--window-order-by--content: "Commandé par";
114
+ --psp-label--window-partition-by--content: "Partitionner par";
115
+ --psp-label--window-frame--content: "Cadre";
116
+ --psp-label--window-offset--content: "Décalage";
117
+ --psp-label--window-alpha--content: "Alpha";
111
118
  --psp-label--query-tab--content: "Requête";
112
119
  --psp-label--plugin-tab--content: "Plugin";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "スタイル";
110
110
  --psp-label--attributes-tab--content: "属性";
111
+ --psp-label--window-tab--content: "ウィンドウ";
112
+ --psp-label--window-source--content: "列";
113
+ --psp-label--window-order-by--content: "注文方法";
114
+ --psp-label--window-partition-by--content: "パーティション";
115
+ --psp-label--window-frame--content: "フレーム";
116
+ --psp-label--window-offset--content: "オフセット";
117
+ --psp-label--window-alpha--content: "アルファ";
111
118
  --psp-label--query-tab--content: "クエリ";
112
119
  --psp-label--plugin-tab--content: "プラグイン";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "Estilo";
110
110
  --psp-label--attributes-tab--content: "Atributos";
111
+ --psp-label--window-tab--content: "Janela";
112
+ --psp-label--window-source--content: "Coluna";
113
+ --psp-label--window-order-by--content: "Ordenar por";
114
+ --psp-label--window-partition-by--content: "Particionar por";
115
+ --psp-label--window-frame--content: "Quadro";
116
+ --psp-label--window-offset--content: "Deslocamento";
117
+ --psp-label--window-alpha--content: "Alfa";
111
118
  --psp-label--query-tab--content: "Consulta";
112
119
  --psp-label--plugin-tab--content: "Plugin";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -108,6 +108,13 @@ perspective-dropdown {
108
108
  /* Tabs */
109
109
  --psp-label--style-tab--content: "风格";
110
110
  --psp-label--attributes-tab--content: "属性";
111
+ --psp-label--window-tab--content: "窗口";
112
+ --psp-label--window-source--content: "列";
113
+ --psp-label--window-order-by--content: "订购依据";
114
+ --psp-label--window-partition-by--content: "分区依据";
115
+ --psp-label--window-frame--content: "框架";
116
+ --psp-label--window-offset--content: "偏移";
117
+ --psp-label--window-alpha--content: "Alpha";
111
118
  --psp-label--query-tab--content: "查询";
112
119
  --psp-label--plugin-tab--content: "插件";
113
120
  --psp-label--debug-tab--content: "JSON";
@@ -103,11 +103,20 @@ perspective-dropdown {
103
103
 
104
104
  /* Tabs */
105
105
  --psp-label--style-tab--content: "Style";
106
- --psp-label--attributes-tab--content: "Attributes";
106
+ --psp-label--attributes-tab--content: "Expression";
107
+ --psp-label--window-tab--content: "Window";
107
108
  --psp-label--query-tab--content: "Query";
108
109
  --psp-label--plugin-tab--content: "Plugin";
109
110
  --psp-label--debug-tab--content: "JSON";
110
111
 
112
+ /* Window editor */
113
+ --psp-label--window-source--content: "Column";
114
+ --psp-label--window-order-by--content: "Order By";
115
+ --psp-label--window-partition-by--content: "Partition By";
116
+ --psp-label--window-frame--content: "Frame";
117
+ --psp-label--window-offset--content: "Offset";
118
+ --psp-label--window-alpha--content: "Alpha";
119
+
111
120
  /* Plugin settings */
112
121
  --psp-label--edit-mode--content: "Edit mode";
113
122
  --psp-label--scroll-lock--content: "Scroll lock";
@@ -5,6 +5,7 @@ import type { Filter } from "./Filter.js";
5
5
  import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { Sort } from "./Sort.js";
8
+ import type { Windows } from "./Windows.js";
8
9
  import type { JsonValue } from "./serde_json/JsonValue.js";
9
10
 
10
11
  /**
@@ -12,4 +13,4 @@ import type { JsonValue } from "./serde_json/JsonValue.js";
12
13
  * element-level `settings` flag. This is the `panels` entry type of the
13
14
  * whole-element config format.
14
15
  */
15
- export type PanelViewerConfig<V = string> = { version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
16
+ export type PanelViewerConfig<V = string> = { version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
@@ -5,6 +5,7 @@ import type { Filter } from "./Filter.js";
5
5
  import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { Sort } from "./Sort.js";
8
+ import type { Windows } from "./Windows.js";
8
9
  import type { JsonValue } from "./serde_json/JsonValue.js";
9
10
 
10
11
  /**
@@ -15,4 +16,4 @@ import type { JsonValue } from "./serde_json/JsonValue.js";
15
16
  * state there, carried by the top-level `active` field instead), while the
16
17
  * single-panel format flattens back to the legacy shape.
17
18
  */
18
- export type ViewerConfig<V = string> = { settings: boolean, version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
19
+ export type ViewerConfig<V = string> = { settings: boolean, version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
@@ -6,6 +6,7 @@ import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { OptionalUpdate } from "./OptionalUpdate.js";
8
8
  import type { Sort } from "./Sort.js";
9
+ import type { Windows } from "./Windows.js";
9
10
  import type { JsonValue } from "./serde_json/JsonValue.js";
10
11
 
11
12
  export type ViewerConfigUpdate = { version?: OptionalUpdate<string>, plugin?: OptionalUpdate<string>, title?: OptionalUpdate<string>, table?: OptionalUpdate<string>, theme?: OptionalUpdate<string>, settings?: OptionalUpdate<boolean>, plugin_config?: OptionalUpdate<{ [key in string]?: JsonValue }>, columns_config?: OptionalUpdate<{ [key in string]?: { [key in string]?: JsonValue } }>,
@@ -74,6 +75,14 @@ sort?: Array<Sort>,
74
75
  * panel.
75
76
  */
76
77
  expressions?: Expressions,
78
+ /**
79
+ * The `windows` property declares ordered, partitioned rolling
80
+ * computations (moving aggregates, cumulative sums) as _new_ columns
81
+ * keyed by output alias (`{"name": {...spec}}`, symmetric with
82
+ * `expressions`), analogous to SQL window functions. See
83
+ * [`crate::config::WindowSpec`].
84
+ */
85
+ windows?: Windows,
77
86
  /**
78
87
  * Aggregates perform a calculation over an entire column, and are
79
88
  * displayed when one or more [Group By](#group-by) are applied to the
@@ -0,0 +1,3 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type WindowAggregate = "sum" | "avg" | "count" | "min" | "max" | "stddev" | "var" | "first" | "last" | "lag" | "lead" | "diff" | "rate" | "ema";
@@ -0,0 +1,9 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowSortDir } from "./WindowSortDir.js";
3
+
4
+ /**
5
+ * The `Table` column which orders each partition, with its direction -
6
+ * serialized as a two-element array (`["ts", "desc"]`), symmetric with the
7
+ * `ViewConfig`'s `sort` field.
8
+ */
9
+ export type WindowSort = [string, WindowSortDir];
@@ -0,0 +1,8 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * A window's order direction. A dedicated two-variant enum rather than
5
+ * [`crate::config::SortDir`] - the column-sort extras (`col asc`, `abs`,
6
+ * `none`) are meaningless inside a window frame and unrepresentable here.
7
+ */
8
+ export type WindowSortDir = "asc" | "desc";
@@ -0,0 +1,46 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowAggregate } from "./WindowAggregate.js";
3
+ import type { WindowSort } from "./WindowSort.js";
4
+
5
+ /**
6
+ * The serialized form of [`WindowSpec`].
7
+ */
8
+ export type WindowSpec = {
9
+ /**
10
+ * The input column - either a real `Table` column or an expression
11
+ * alias from the same `ViewConfig`.
12
+ */
13
+ column: string, aggregate: WindowAggregate,
14
+ /**
15
+ * Columns whose distinct value tuples partition the rows; empty
16
+ * partitions the whole `Table` as one group.
17
+ */
18
+ partition_by?: Array<string>,
19
+ /**
20
+ * The `Table` column which orders each partition, and the direction.
21
+ */
22
+ order_by?: WindowSort | null,
23
+ /**
24
+ * A frame of the `rows` preceding each row, plus the row itself.
25
+ * Mutually exclusive with `range` and `cumulative`.
26
+ */
27
+ rows?: number | null,
28
+ /**
29
+ * A frame of the rows whose `order_by` value lies within `range` of
30
+ * each row's, requiring a numeric or temporal `order_by`. Mutually
31
+ * exclusive with `rows` and `cumulative`.
32
+ */
33
+ range?: number | null,
34
+ /**
35
+ * `true` frames all rows from the partition start through each row.
36
+ * Default can be omitted.
37
+ */
38
+ cumulative?: boolean | null,
39
+ /**
40
+ * Row offset for `lag`/`lead` (default 1).
41
+ */
42
+ offset?: number | null,
43
+ /**
44
+ * Smoothing factor in `(0, 1]` for `ema`.
45
+ */
46
+ alpha?: number | null, };
@@ -0,0 +1,10 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { WindowSpec } from "./WindowSpec.js";
3
+
4
+ /**
5
+ * The window columns of a `ViewConfig`, keyed by output column alias -
6
+ * symmetric with `expressions` (`{"name": {...spec}}`). An alias must not
7
+ * collide with a `Table` column, expression alias, or another window's
8
+ * key.
9
+ */
10
+ export type Windows = { [key in string]?: WindowSpec };