@perspective-dev/viewer 5.5.0 → 5.6.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 (201) hide show
  1. package/README.md +82 -3
  2. package/dist/cdn/perspective-viewer.js +9 -9
  3. package/dist/cdn/perspective-viewer.js.map +3 -3
  4. package/dist/css/blueprint.css +1 -1
  5. package/dist/css/botanical.css +1 -1
  6. package/dist/css/dracula.css +1 -1
  7. package/dist/css/eggplant.css +1 -1
  8. package/dist/css/gruvbox-dark.css +1 -1
  9. package/dist/css/gruvbox.css +1 -1
  10. package/dist/css/icons.css +1 -1
  11. package/dist/css/intl/ar.css +1 -1
  12. package/dist/css/intl/de.css +1 -1
  13. package/dist/css/intl/es.css +1 -1
  14. package/dist/css/intl/fr.css +1 -1
  15. package/dist/css/intl/ja.css +1 -1
  16. package/dist/css/intl/pt.css +1 -1
  17. package/dist/css/intl/zh.css +1 -1
  18. package/dist/css/intl.css +1 -1
  19. package/dist/css/ledger.css +1 -1
  20. package/dist/css/monokai.css +1 -1
  21. package/dist/css/nord.css +1 -1
  22. package/dist/css/phosphor.css +1 -1
  23. package/dist/css/pro-dark.css +1 -1
  24. package/dist/css/pro.css +1 -1
  25. package/dist/css/solarized-dark.css +1 -1
  26. package/dist/css/solarized.css +1 -1
  27. package/dist/css/themes.css +1 -1
  28. package/dist/css/vaporwave.css +1 -1
  29. package/dist/css/velvet.css +1 -1
  30. package/dist/docs/perspective-docs.json +1 -1
  31. package/dist/esm/column-format.js +2 -0
  32. package/dist/esm/column-format.js.map +7 -0
  33. package/dist/esm/extensions.d.ts +3 -13
  34. package/dist/esm/perspective-viewer.d.ts +1 -1
  35. package/dist/esm/perspective-viewer.js +9 -9
  36. package/dist/esm/perspective-viewer.js.map +3 -3
  37. package/dist/esm/plugin.d.ts +4 -1
  38. package/dist/esm/select-detail.d.ts +15 -0
  39. package/dist/esm/select-detail.js +2 -0
  40. package/dist/esm/select-detail.js.map +7 -0
  41. package/dist/esm/ts-rs/DerivedTableConfig.d.ts +35 -0
  42. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -1
  43. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +7 -6
  44. package/dist/esm/ts-rs/RestoreOptions.d.ts +3 -7
  45. package/dist/esm/ts-rs/TableRef.d.ts +9 -0
  46. package/dist/esm/ts-rs/ViewerConfig.d.ts +7 -6
  47. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -5
  48. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
  49. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +9 -0
  50. package/dist/wasm/perspective-viewer.d.ts +126 -35
  51. package/dist/wasm/perspective-viewer.js +320 -185
  52. package/dist/wasm/perspective-viewer.wasm +0 -0
  53. package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
  54. package/package.json +33 -20
  55. package/src/css/column-selector.css +205 -50
  56. package/src/css/ui/containers/context-menu.css +12 -1
  57. package/src/css/ui/containers/dropdown-menu-items.css +29 -0
  58. package/src/css/ui/containers/scroll-panel.css +4 -1
  59. package/src/rust/agent/client/protocol.rs +0 -89
  60. package/src/rust/agent/markdown/mod.rs +0 -238
  61. package/src/rust/agent/preamble.md +1 -0
  62. package/src/rust/agent/tools.rs +2 -16
  63. package/src/rust/components/column_selector/active_column.rs +15 -5
  64. package/src/rust/components/column_selector/add_expression_button.rs +9 -26
  65. package/src/rust/components/column_selector/config_selector.rs +34 -6
  66. package/src/rust/components/column_selector/derived_control.rs +229 -0
  67. package/src/rust/components/column_selector/expr_edit_button.rs +3 -2
  68. package/src/rust/components/column_selector/filter_column.rs +0 -2
  69. package/src/rust/components/column_selector/inactive_column.rs +4 -5
  70. package/src/rust/components/column_selector/pivot_column.rs +21 -1
  71. package/src/rust/components/column_selector/sort_column.rs +4 -3
  72. package/src/rust/components/column_selector.rs +21 -1
  73. package/src/rust/components/column_settings_sidebar/save_settings.rs +5 -3
  74. package/src/rust/components/column_settings_sidebar/style_tab.rs +1 -0
  75. package/src/rust/components/column_settings_sidebar.rs +25 -19
  76. package/src/rust/components/debug_panel.rs +38 -29
  77. package/src/rust/components/dragdrop_list.rs +8 -9
  78. package/src/rust/components/export_dropdown.rs +6 -0
  79. package/src/rust/components/main_panel/msg.rs +6 -4
  80. package/src/rust/components/main_panel/reconcile.rs +6 -1
  81. package/src/rust/components/main_panel/render.rs +70 -51
  82. package/src/rust/components/main_panel/update.rs +39 -1
  83. package/src/rust/components/main_panel.rs +97 -59
  84. package/src/rust/components/new_panel_menu.rs +69 -1
  85. package/src/rust/components/panel_menu.rs +63 -12
  86. package/src/rust/components/plugin_tab.rs +3 -2
  87. package/src/rust/components/render_warning.rs +2 -1
  88. package/src/rust/components/settings_panel.rs +4 -4
  89. package/src/rust/components/status_bar.rs +80 -21
  90. package/src/rust/components/status_indicator.rs +8 -1
  91. package/src/rust/components/viewer/filters.rs +15 -3
  92. package/src/rust/components/viewer/msg.rs +8 -0
  93. package/src/rust/components/viewer/panels.rs +44 -4
  94. package/src/rust/components/viewer/render.rs +1 -0
  95. package/src/rust/components/viewer/settings.rs +0 -22
  96. package/src/rust/components/viewer/snapshots.rs +2 -2
  97. package/src/rust/components/viewer/wiring.rs +18 -1
  98. package/src/rust/components/viewer.rs +47 -27
  99. package/src/rust/components/window_editor.rs +5 -4
  100. package/src/rust/config/column_config_schema.rs +38 -377
  101. package/src/rust/config/export_method.rs +24 -0
  102. package/src/rust/config/number_string_format.rs +0 -94
  103. package/src/rust/config/options.rs +3 -7
  104. package/src/rust/config/plugin_static_config.rs +0 -42
  105. package/src/rust/config/viewer_config.rs +47 -17
  106. package/src/rust/config/workspace_config.rs +158 -128
  107. package/src/rust/custom_elements/viewer.rs +189 -129
  108. package/src/rust/custom_events.rs +33 -18
  109. package/src/rust/derived.rs +266 -0
  110. package/src/rust/exprtk/tokenize.rs +0 -54
  111. package/src/rust/js/plugin.rs +1 -1
  112. package/src/rust/lib.rs +3 -0
  113. package/src/rust/presentation.rs +23 -4
  114. package/src/rust/queries/column_locator.rs +55 -48
  115. package/src/rust/queries/get_viewer_config.rs +1 -1
  116. package/src/rust/queries/plugin_column_styles.rs +22 -23
  117. package/src/rust/renderer/dispatch.rs +2 -0
  118. package/src/rust/renderer/plugin_config.rs +652 -487
  119. package/src/rust/renderer/props.rs +4 -0
  120. package/src/rust/renderer/registry.rs +2 -6
  121. package/src/rust/renderer/state.rs +205 -0
  122. package/src/rust/renderer.rs +201 -59
  123. package/src/rust/session/metadata.rs +35 -19
  124. package/src/rust/session/op_queue.rs +430 -0
  125. package/src/rust/session/panel_state.rs +327 -0
  126. package/src/rust/session/view_subscription.rs +42 -3
  127. package/src/rust/session.rs +931 -419
  128. package/src/rust/tasks/apply_global_filters.rs +49 -35
  129. package/src/rust/tasks/auto_pause.rs +22 -12
  130. package/src/rust/tasks/copy_export.rs +72 -0
  131. package/src/rust/tasks/create_panel.rs +46 -21
  132. package/src/rust/tasks/derive.rs +312 -0
  133. package/src/rust/tasks/mod.rs +6 -2
  134. package/src/rust/tasks/pipeline.rs +152 -89
  135. package/src/rust/tasks/reset_all.rs +31 -20
  136. package/src/rust/tasks/restore_derived.rs +200 -0
  137. package/src/rust/tasks/restore_panel.rs +153 -141
  138. package/src/rust/tasks/send_column_config.rs +56 -29
  139. package/src/rust/tasks/send_plugin_config.rs +52 -23
  140. package/src/rust/tasks/set_edit_mode.rs +33 -25
  141. package/src/rust/tasks/sync_update_panels.rs +40 -13
  142. package/src/rust/tasks/table_lifecycle.rs +70 -34
  143. package/src/rust/tasks/transactional_restore.rs +387 -0
  144. package/src/rust/tasks/update_theme.rs +45 -6
  145. package/src/rust/ui/containers/scroll_panel.rs +17 -10
  146. package/src/rust/ui/containers/split_panel.rs +0 -41
  147. package/src/rust/ui/context_menu.rs +157 -18
  148. package/src/rust/ui/font_loader.rs +23 -35
  149. package/src/rust/ui/form/mirrored_textarea.rs +0 -12
  150. package/src/rust/ui/form/multi_stop_gradient_selector.rs +0 -89
  151. package/src/rust/ui/form/palette_selector.rs +0 -51
  152. package/src/rust/ui/portal.rs +4 -10
  153. package/src/rust/utils/browser/mod.rs +2 -3
  154. package/src/{ts/perspective-viewer.inline.ts → rust/utils/browser/mouse.rs} +11 -10
  155. package/src/rust/utils/browser/request_animation_frame.rs +27 -8
  156. package/src/rust/utils/css.rs +0 -360
  157. package/src/rust/utils/{tests/mod.rs → in_flight.rs} +62 -2
  158. package/src/rust/utils/local_fonts.rs +0 -36
  159. package/src/rust/utils/mod.rs +4 -3
  160. package/src/rust/utils/modal_position.rs +15 -21
  161. package/src/rust/utils/pending_effects.rs +6 -30
  162. package/src/rust/utils/{browser/tests/mod.rs → plugin_host_state.rs} +31 -1
  163. package/src/rust/utils/pubsub.rs +0 -1
  164. package/src/rust/workspace.rs +187 -140
  165. package/src/svg/derived.svg +6 -0
  166. package/src/themes/icons.css +1 -0
  167. package/src/themes/intl/ar.css +10 -0
  168. package/src/themes/intl/de.css +10 -0
  169. package/src/themes/intl/es.css +10 -0
  170. package/src/themes/intl/fr.css +10 -0
  171. package/src/themes/intl/ja.css +10 -0
  172. package/src/themes/intl/pt.css +10 -0
  173. package/src/themes/intl/zh.css +10 -0
  174. package/src/themes/intl.css +10 -0
  175. package/src/themes/monokai.css +1 -1
  176. package/src/themes/vaporwave.css +1 -1
  177. package/src/ts/extensions.ts +3 -36
  178. package/src/ts/perspective-viewer.ts +1 -1
  179. package/src/ts/plugin.ts +4 -1
  180. package/src/{rust/utils/tests/request_animation_frame.rs → ts/select-detail.ts} +32 -26
  181. package/src/ts/ts-rs/DerivedTableConfig.ts +22 -0
  182. package/src/ts/ts-rs/ExportMethod.ts +1 -1
  183. package/src/ts/ts-rs/PanelViewerConfig.ts +7 -6
  184. package/src/ts/ts-rs/RestoreOptions.ts +3 -7
  185. package/src/ts/ts-rs/TableRef.ts +9 -0
  186. package/src/ts/ts-rs/ViewerConfig.ts +7 -6
  187. package/src/ts/ts-rs/ViewerConfigUpdate.ts +7 -5
  188. package/src/ts/ts-rs/WorkspaceConfig.ts +7 -1
  189. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +7 -0
  190. package/dist/esm/perspective-viewer.inline.d.ts +0 -3
  191. package/dist/esm/perspective-viewer.inline.js +0 -21
  192. package/dist/esm/perspective-viewer.inline.js.map +0 -7
  193. package/src/rust/tasks/restore_and_render.rs +0 -107
  194. package/src/rust/utils/browser/tests/debounce.rs +0 -163
  195. package/src/rust/utils/tests/pubsub.rs +0 -95
  196. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
  197. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
  198. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
  199. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
  200. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
  201. /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
@@ -0,0 +1,312 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ //! Create a derived `Table` from a panel's view, and release a panel back to
14
+ //! the `Table` its own derived one reads.
15
+
16
+ use perspective_client::config::{ViewConfig, ViewConfigUpdate};
17
+ use perspective_client::{Client, DeleteOptions, TableData, TableInitOptions};
18
+ use perspective_js::utils::*;
19
+
20
+ use crate::config::{DerivedTableConfig, OptionalUpdate, TableRef, ViewerConfigUpdate};
21
+ use crate::derived::{DerivedSource, DerivedTable, suggest_name};
22
+ use crate::presentation::Presentation;
23
+ use crate::queries::fetch_hosted_tables;
24
+ use crate::renderer::Renderer;
25
+ use crate::session::{MissingTable, Session, probe_table};
26
+ use crate::tasks::{RestoreErrors, RestoreMode, restore_panel};
27
+ use crate::utils::PtrEqRc;
28
+ use crate::workspace::Workspace;
29
+
30
+ /// Whether this panel's `Client` can derive a `Table` from a `View`.
31
+ pub fn can_derive(session: &Session) -> bool {
32
+ session
33
+ .metadata()
34
+ .get_features()
35
+ .is_some_and(|x| x.view_derivations)
36
+ }
37
+
38
+ /// Rewrite an inline derived-table definition in `update.table` to a concrete
39
+ /// hosted name, rebinding to a structurally identical live derivation where
40
+ /// one exists and building (or pending, per `missing`) the rest.
41
+ pub async fn resolve_table_ref(
42
+ workspace: &Workspace,
43
+ update: &mut ViewerConfigUpdate,
44
+ missing: MissingTable,
45
+ ) -> ApiResult<()> {
46
+ if !matches!(&update.table, OptionalUpdate::Update(TableRef::Derived(_))) {
47
+ return Ok(());
48
+ }
49
+
50
+ let OptionalUpdate::Update(table_ref) =
51
+ std::mem::replace(&mut update.table, OptionalUpdate::Missing)
52
+ else {
53
+ return Ok(());
54
+ };
55
+
56
+ let mut levels = vec![];
57
+ let mut cursor = table_ref;
58
+ let base = loop {
59
+ match cursor {
60
+ TableRef::Name(name) => break name,
61
+ TableRef::Derived(config) => {
62
+ levels.push(config.view_config);
63
+ cursor = config.table;
64
+ },
65
+ }
66
+ };
67
+
68
+ let mut pending = workspace.derived_pending();
69
+ let mut taken = fetch_hosted_tables(workspace)
70
+ .await
71
+ .into_iter()
72
+ .flat_map(|(_, tables)| tables)
73
+ .collect::<std::collections::BTreeSet<_>>();
74
+
75
+ taken.extend(pending.keys().cloned());
76
+ let mut parent = base;
77
+ for view_config in levels.into_iter().rev() {
78
+ let config = DerivedTableConfig {
79
+ table: parent.clone(),
80
+ view_config,
81
+ };
82
+
83
+ if let Some(existing) = workspace.derived_find_structural(&config) {
84
+ parent = existing;
85
+ continue;
86
+ }
87
+
88
+ if let Some(existing) = pending.iter().find(|(_, x)| **x == config) {
89
+ parent = existing.0.clone();
90
+ continue;
91
+ }
92
+
93
+ let name = suggest_name(
94
+ &taken,
95
+ &PtrEqRc::new(config.view_config.clone()),
96
+ &Some(config.table.clone()),
97
+ );
98
+
99
+ taken.insert(name.clone());
100
+ let built = match workspace
101
+ .resolve_client_for_table(&config.table, None)
102
+ .await
103
+ {
104
+ Some(client) => {
105
+ build_derived(
106
+ &client,
107
+ workspace,
108
+ &name,
109
+ &config.table,
110
+ &config.view_config,
111
+ )
112
+ .await?
113
+ },
114
+ None => false,
115
+ };
116
+
117
+ if !built {
118
+ if matches!(missing, MissingTable::Error) {
119
+ return Err(ApiError::new(format!("Unknown table \"{}\"", config.table)));
120
+ }
121
+
122
+ pending.insert(name.clone(), config);
123
+ }
124
+
125
+ parent = name;
126
+ }
127
+
128
+ workspace.set_derived_pending(pending);
129
+ update.table = OptionalUpdate::Update(TableRef::Name(parent));
130
+ Ok(())
131
+ }
132
+
133
+ /// Build a derived `Table` named `name` from `parent`'s `view_config`, and
134
+ /// register it on `workspace`.
135
+ pub async fn build_derived(
136
+ client: &Client,
137
+ workspace: &Workspace,
138
+ name: &str,
139
+ parent: &str,
140
+ view_config: &ViewConfig,
141
+ ) -> ApiResult<bool> {
142
+ let Some(parent_table) = probe_table(client, parent, MissingTable::Pend).await? else {
143
+ return Ok(false);
144
+ };
145
+
146
+ // An empty `columns` in a derived DEFINITION means "all columns" (the
147
+ // engine's absent-columns default), not a key-only view — `ViewConfig`'s
148
+ // serde default materializes `[]` for definitions that omit the key.
149
+ let mut update: ViewConfigUpdate = view_config.clone().into();
150
+ if update.columns.as_ref().is_none_or(|x| x.is_empty()) {
151
+ update.columns = None;
152
+ }
153
+
154
+ let view = parent_table.view(Some(update)).await?;
155
+ let derived = match client
156
+ .table(TableData::View(view.clone()), TableInitOptions {
157
+ name: Some(name.to_owned()),
158
+ ..TableInitOptions::default()
159
+ })
160
+ .await
161
+ {
162
+ Ok(derived) => derived,
163
+ Err(err) => {
164
+ view.delete().await?;
165
+ return Err(err.into());
166
+ },
167
+ };
168
+
169
+ workspace.register_derived(
170
+ name.to_owned(),
171
+ DerivedTable::new(
172
+ parent.to_owned(),
173
+ view_config.clone(),
174
+ DerivedSource::Restored(view),
175
+ derived,
176
+ ),
177
+ );
178
+
179
+ Ok(true)
180
+ }
181
+
182
+ /// Promote the panel's current `View` to a derived `Table` named `name`,
183
+ /// consuming that `View` and rebinding the panel to the result.
184
+ pub async fn create_derived(
185
+ session: &Session,
186
+ renderer: &Renderer,
187
+ presentation: &Presentation,
188
+ workspace: &Workspace,
189
+ name: String,
190
+ ) -> ApiResult<()> {
191
+ if !can_derive(session) {
192
+ return Err("This data source cannot derive a Table from a View".into());
193
+ }
194
+
195
+ let client = session.get_client().ok_or("`load()` has not been called")?;
196
+ let table = session.get_table().ok_or("`load()` has not been called")?;
197
+ let parent = table.get_name().to_owned();
198
+ let plugin = renderer.metadata().name.clone();
199
+ let sub = session
200
+ .take_view_sub()
201
+ .ok_or("Panel has no View to derive from")?;
202
+
203
+ let view_config = (*sub.get_view_config()).clone();
204
+ let derived = match client
205
+ .table(TableData::View(sub.get_view().clone()), TableInitOptions {
206
+ name: Some(name.clone()),
207
+ ..TableInitOptions::default()
208
+ })
209
+ .await
210
+ {
211
+ Ok(derived) => derived,
212
+ Err(err) => {
213
+ session.put_view_sub(sub);
214
+ return Err(err.into());
215
+ },
216
+ };
217
+
218
+ // The promoted `View` now feeds the derived `Table`, not this panel —
219
+ // silence its Session-bound stats/update channels, or every PARENT tick
220
+ // keeps writing the parent's stats over the panel's.
221
+ if let Err(err) = sub.quiesce().await {
222
+ session.put_view_sub(sub);
223
+ derived.delete(DeleteOptions::default()).await?;
224
+ return Err(err);
225
+ }
226
+
227
+ workspace.register_derived(
228
+ name.clone(),
229
+ DerivedTable::new(parent, view_config, DerivedSource::Panel(sub), derived),
230
+ );
231
+
232
+ let update = ViewerConfigUpdate {
233
+ table: OptionalUpdate::Update(name.clone().into()),
234
+ plugin: OptionalUpdate::Update(plugin),
235
+ ..ViewerConfigUpdate::default()
236
+ };
237
+
238
+ let restored = restore_panel(
239
+ session,
240
+ renderer,
241
+ presentation,
242
+ workspace,
243
+ RestoreMode::Existing { active: true },
244
+ update,
245
+ RestoreErrors::Publish,
246
+ MissingTable::Error,
247
+ None,
248
+ )
249
+ .await;
250
+
251
+ if restored.is_err()
252
+ && let Some(derived) = workspace.take_derived(&name)
253
+ {
254
+ derived.delete().await?;
255
+ }
256
+
257
+ restored
258
+ }
259
+
260
+ /// Rebind the panel to the `Table` its derived one reads, deleting that
261
+ /// derivation when `delete` and nothing else consumes it.
262
+ pub async fn release_derived(
263
+ session: &Session,
264
+ renderer: &Renderer,
265
+ presentation: &Presentation,
266
+ workspace: &Workspace,
267
+ delete: bool,
268
+ ) -> ApiResult<()> {
269
+ let table = session.get_table().ok_or("Panel has no table")?;
270
+ let name = table.get_name().to_owned();
271
+ let config = workspace
272
+ .derived_config(&name)
273
+ .ok_or("Panel is not bound to a derived table")?;
274
+
275
+ let update = ViewerConfigUpdate {
276
+ table: OptionalUpdate::Update(config.table.clone().into()),
277
+ view_config: config.view_config.into(),
278
+ ..ViewerConfigUpdate::default()
279
+ };
280
+
281
+ restore_panel(
282
+ session,
283
+ renderer,
284
+ presentation,
285
+ workspace,
286
+ RestoreMode::Existing { active: true },
287
+ update,
288
+ RestoreErrors::Publish,
289
+ MissingTable::Error,
290
+ None,
291
+ )
292
+ .await?;
293
+
294
+ if delete {
295
+ delete_derived(workspace, &name).await?;
296
+ }
297
+
298
+ Ok(())
299
+ }
300
+
301
+ /// Delete a derived `Table` that nothing consumes.
302
+ pub async fn delete_derived(workspace: &Workspace, name: &str) -> ApiResult<()> {
303
+ let consumers = workspace.derived_consumers(name);
304
+ if consumers > 0 {
305
+ return Err(format!("`{name}` is still read by {consumers} panels or tables").into());
306
+ }
307
+
308
+ match workspace.take_derived(name) {
309
+ Some(derived) => derived.delete().await,
310
+ None => Err(format!("`{name}` is not a derived table").into()),
311
+ }
312
+ }
@@ -25,6 +25,7 @@ mod apply_global_filters;
25
25
  mod auto_pause;
26
26
  mod copy_export;
27
27
  mod create_panel;
28
+ mod derive;
28
29
  mod dismiss_render_warning;
29
30
  mod edit_expression;
30
31
  mod edit_window;
@@ -33,13 +34,14 @@ mod pipeline;
33
34
  mod presize_panels;
34
35
  mod reset_all;
35
36
  mod resize_observer;
36
- mod restore_and_render;
37
+ mod restore_derived;
37
38
  mod restore_panel;
38
39
  mod send_column_config;
39
40
  mod send_plugin_config;
40
41
  mod set_edit_mode;
41
42
  mod sync_update_panels;
42
43
  mod table_lifecycle;
44
+ mod transactional_restore;
43
45
  mod update_theme;
44
46
  mod validate_expression;
45
47
 
@@ -57,6 +59,7 @@ pub use self::apply_global_filters::*;
57
59
  pub use self::auto_pause::*;
58
60
  pub use self::copy_export::*;
59
61
  pub(crate) use self::create_panel::*;
62
+ pub use self::derive::*;
60
63
  pub use self::dismiss_render_warning::*;
61
64
  pub use self::edit_expression::*;
62
65
  pub use self::edit_window::*;
@@ -65,12 +68,13 @@ pub use self::pipeline::*;
65
68
  pub use self::presize_panels::*;
66
69
  pub use self::reset_all::*;
67
70
  pub use self::resize_observer::*;
68
- pub use self::restore_and_render::*;
71
+ pub use self::restore_derived::*;
69
72
  pub(crate) use self::restore_panel::*;
70
73
  pub use self::send_column_config::*;
71
74
  pub use self::send_plugin_config::*;
72
75
  pub use self::set_edit_mode::*;
73
76
  pub(crate) use self::sync_update_panels::*;
74
77
  pub(crate) use self::table_lifecycle::*;
78
+ pub(crate) use self::transactional_restore::{commit_and_render_locked, prepare};
75
79
  pub use self::update_theme::*;
76
80
  pub use self::validate_expression::*;