@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.
- package/README.md +82 -3
- package/dist/cdn/perspective-viewer.js +9 -9
- package/dist/cdn/perspective-viewer.js.map +3 -3
- package/dist/css/blueprint.css +1 -1
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.css +1 -1
- package/dist/css/eggplant.css +1 -1
- package/dist/css/gruvbox-dark.css +1 -1
- package/dist/css/gruvbox.css +1 -1
- package/dist/css/icons.css +1 -1
- package/dist/css/intl/ar.css +1 -1
- package/dist/css/intl/de.css +1 -1
- package/dist/css/intl/es.css +1 -1
- package/dist/css/intl/fr.css +1 -1
- package/dist/css/intl/ja.css +1 -1
- package/dist/css/intl/pt.css +1 -1
- package/dist/css/intl/zh.css +1 -1
- package/dist/css/intl.css +1 -1
- package/dist/css/ledger.css +1 -1
- package/dist/css/monokai.css +1 -1
- package/dist/css/nord.css +1 -1
- package/dist/css/phosphor.css +1 -1
- package/dist/css/pro-dark.css +1 -1
- package/dist/css/pro.css +1 -1
- package/dist/css/solarized-dark.css +1 -1
- package/dist/css/solarized.css +1 -1
- package/dist/css/themes.css +1 -1
- package/dist/css/vaporwave.css +1 -1
- package/dist/css/velvet.css +1 -1
- package/dist/docs/perspective-docs.json +1 -1
- package/dist/esm/column-format.js +2 -0
- package/dist/esm/column-format.js.map +7 -0
- package/dist/esm/extensions.d.ts +3 -13
- package/dist/esm/perspective-viewer.d.ts +1 -1
- package/dist/esm/perspective-viewer.js +9 -9
- package/dist/esm/perspective-viewer.js.map +3 -3
- package/dist/esm/plugin.d.ts +4 -1
- package/dist/esm/select-detail.d.ts +15 -0
- package/dist/esm/select-detail.js +2 -0
- package/dist/esm/select-detail.js.map +7 -0
- package/dist/esm/ts-rs/DerivedTableConfig.d.ts +35 -0
- package/dist/esm/ts-rs/ExportMethod.d.ts +1 -1
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/RestoreOptions.d.ts +3 -7
- package/dist/esm/ts-rs/TableRef.d.ts +9 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +7 -6
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +7 -5
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +9 -0
- package/dist/wasm/perspective-viewer.d.ts +126 -35
- package/dist/wasm/perspective-viewer.js +320 -185
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
- package/package.json +33 -20
- package/src/css/column-selector.css +205 -50
- package/src/css/ui/containers/context-menu.css +12 -1
- package/src/css/ui/containers/dropdown-menu-items.css +29 -0
- package/src/css/ui/containers/scroll-panel.css +4 -1
- package/src/rust/agent/client/protocol.rs +0 -89
- package/src/rust/agent/markdown/mod.rs +0 -238
- package/src/rust/agent/preamble.md +1 -0
- package/src/rust/agent/tools.rs +2 -16
- package/src/rust/components/column_selector/active_column.rs +15 -5
- package/src/rust/components/column_selector/add_expression_button.rs +9 -26
- package/src/rust/components/column_selector/config_selector.rs +34 -6
- package/src/rust/components/column_selector/derived_control.rs +229 -0
- package/src/rust/components/column_selector/expr_edit_button.rs +3 -2
- package/src/rust/components/column_selector/filter_column.rs +0 -2
- package/src/rust/components/column_selector/inactive_column.rs +4 -5
- package/src/rust/components/column_selector/pivot_column.rs +21 -1
- package/src/rust/components/column_selector/sort_column.rs +4 -3
- package/src/rust/components/column_selector.rs +21 -1
- package/src/rust/components/column_settings_sidebar/save_settings.rs +5 -3
- package/src/rust/components/column_settings_sidebar/style_tab.rs +1 -0
- package/src/rust/components/column_settings_sidebar.rs +25 -19
- package/src/rust/components/debug_panel.rs +38 -29
- package/src/rust/components/dragdrop_list.rs +8 -9
- package/src/rust/components/export_dropdown.rs +6 -0
- package/src/rust/components/main_panel/msg.rs +6 -4
- package/src/rust/components/main_panel/reconcile.rs +6 -1
- package/src/rust/components/main_panel/render.rs +70 -51
- package/src/rust/components/main_panel/update.rs +39 -1
- package/src/rust/components/main_panel.rs +97 -59
- package/src/rust/components/new_panel_menu.rs +69 -1
- package/src/rust/components/panel_menu.rs +63 -12
- package/src/rust/components/plugin_tab.rs +3 -2
- package/src/rust/components/render_warning.rs +2 -1
- package/src/rust/components/settings_panel.rs +4 -4
- package/src/rust/components/status_bar.rs +80 -21
- package/src/rust/components/status_indicator.rs +8 -1
- package/src/rust/components/viewer/filters.rs +15 -3
- package/src/rust/components/viewer/msg.rs +8 -0
- package/src/rust/components/viewer/panels.rs +44 -4
- package/src/rust/components/viewer/render.rs +1 -0
- package/src/rust/components/viewer/settings.rs +0 -22
- package/src/rust/components/viewer/snapshots.rs +2 -2
- package/src/rust/components/viewer/wiring.rs +18 -1
- package/src/rust/components/viewer.rs +47 -27
- package/src/rust/components/window_editor.rs +5 -4
- package/src/rust/config/column_config_schema.rs +38 -377
- package/src/rust/config/export_method.rs +24 -0
- package/src/rust/config/number_string_format.rs +0 -94
- package/src/rust/config/options.rs +3 -7
- package/src/rust/config/plugin_static_config.rs +0 -42
- package/src/rust/config/viewer_config.rs +47 -17
- package/src/rust/config/workspace_config.rs +158 -128
- package/src/rust/custom_elements/viewer.rs +189 -129
- package/src/rust/custom_events.rs +33 -18
- package/src/rust/derived.rs +266 -0
- package/src/rust/exprtk/tokenize.rs +0 -54
- package/src/rust/js/plugin.rs +1 -1
- package/src/rust/lib.rs +3 -0
- package/src/rust/presentation.rs +23 -4
- package/src/rust/queries/column_locator.rs +55 -48
- package/src/rust/queries/get_viewer_config.rs +1 -1
- package/src/rust/queries/plugin_column_styles.rs +22 -23
- package/src/rust/renderer/dispatch.rs +2 -0
- package/src/rust/renderer/plugin_config.rs +652 -487
- package/src/rust/renderer/props.rs +4 -0
- package/src/rust/renderer/registry.rs +2 -6
- package/src/rust/renderer/state.rs +205 -0
- package/src/rust/renderer.rs +201 -59
- package/src/rust/session/metadata.rs +35 -19
- package/src/rust/session/op_queue.rs +430 -0
- package/src/rust/session/panel_state.rs +327 -0
- package/src/rust/session/view_subscription.rs +42 -3
- package/src/rust/session.rs +931 -419
- package/src/rust/tasks/apply_global_filters.rs +49 -35
- package/src/rust/tasks/auto_pause.rs +22 -12
- package/src/rust/tasks/copy_export.rs +72 -0
- package/src/rust/tasks/create_panel.rs +46 -21
- package/src/rust/tasks/derive.rs +312 -0
- package/src/rust/tasks/mod.rs +6 -2
- package/src/rust/tasks/pipeline.rs +152 -89
- package/src/rust/tasks/reset_all.rs +31 -20
- package/src/rust/tasks/restore_derived.rs +200 -0
- package/src/rust/tasks/restore_panel.rs +153 -141
- package/src/rust/tasks/send_column_config.rs +56 -29
- package/src/rust/tasks/send_plugin_config.rs +52 -23
- package/src/rust/tasks/set_edit_mode.rs +33 -25
- package/src/rust/tasks/sync_update_panels.rs +40 -13
- package/src/rust/tasks/table_lifecycle.rs +70 -34
- package/src/rust/tasks/transactional_restore.rs +387 -0
- package/src/rust/tasks/update_theme.rs +45 -6
- package/src/rust/ui/containers/scroll_panel.rs +17 -10
- package/src/rust/ui/containers/split_panel.rs +0 -41
- package/src/rust/ui/context_menu.rs +157 -18
- package/src/rust/ui/font_loader.rs +23 -35
- package/src/rust/ui/form/mirrored_textarea.rs +0 -12
- package/src/rust/ui/form/multi_stop_gradient_selector.rs +0 -89
- package/src/rust/ui/form/palette_selector.rs +0 -51
- package/src/rust/ui/portal.rs +4 -10
- package/src/rust/utils/browser/mod.rs +2 -3
- package/src/{ts/perspective-viewer.inline.ts → rust/utils/browser/mouse.rs} +11 -10
- package/src/rust/utils/browser/request_animation_frame.rs +27 -8
- package/src/rust/utils/css.rs +0 -360
- package/src/rust/utils/{tests/mod.rs → in_flight.rs} +62 -2
- package/src/rust/utils/local_fonts.rs +0 -36
- package/src/rust/utils/mod.rs +4 -3
- package/src/rust/utils/modal_position.rs +15 -21
- package/src/rust/utils/pending_effects.rs +6 -30
- package/src/rust/utils/{browser/tests/mod.rs → plugin_host_state.rs} +31 -1
- package/src/rust/utils/pubsub.rs +0 -1
- package/src/rust/workspace.rs +187 -140
- package/src/svg/derived.svg +6 -0
- package/src/themes/icons.css +1 -0
- package/src/themes/intl/ar.css +10 -0
- package/src/themes/intl/de.css +10 -0
- package/src/themes/intl/es.css +10 -0
- package/src/themes/intl/fr.css +10 -0
- package/src/themes/intl/ja.css +10 -0
- package/src/themes/intl/pt.css +10 -0
- package/src/themes/intl/zh.css +10 -0
- package/src/themes/intl.css +10 -0
- package/src/themes/monokai.css +1 -1
- package/src/themes/vaporwave.css +1 -1
- package/src/ts/extensions.ts +3 -36
- package/src/ts/perspective-viewer.ts +1 -1
- package/src/ts/plugin.ts +4 -1
- package/src/{rust/utils/tests/request_animation_frame.rs → ts/select-detail.ts} +32 -26
- package/src/ts/ts-rs/DerivedTableConfig.ts +22 -0
- package/src/ts/ts-rs/ExportMethod.ts +1 -1
- package/src/ts/ts-rs/PanelViewerConfig.ts +7 -6
- package/src/ts/ts-rs/RestoreOptions.ts +3 -7
- package/src/ts/ts-rs/TableRef.ts +9 -0
- package/src/ts/ts-rs/ViewerConfig.ts +7 -6
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +7 -5
- package/src/ts/ts-rs/WorkspaceConfig.ts +7 -1
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +7 -0
- package/dist/esm/perspective-viewer.inline.d.ts +0 -3
- package/dist/esm/perspective-viewer.inline.js +0 -21
- package/dist/esm/perspective-viewer.inline.js.map +0 -7
- package/src/rust/tasks/restore_and_render.rs +0 -107
- package/src/rust/utils/browser/tests/debounce.rs +0 -163
- package/src/rust/utils/tests/pubsub.rs +0 -95
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-ea71a3990cad35d7 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use std::rc::Rc;
|
|
14
|
+
|
|
13
15
|
use perspective_client::clone;
|
|
14
16
|
use perspective_client::utils::PerspectiveResultExt;
|
|
15
17
|
|
|
16
18
|
use crate::config::*;
|
|
17
19
|
use crate::presentation::Presentation;
|
|
18
20
|
use crate::renderer::Renderer;
|
|
19
|
-
use crate::session::{MissingTable,
|
|
21
|
+
use crate::session::{BindPlan, MissingTable, OpCtx, OpKind, Session, probe_table};
|
|
22
|
+
use crate::tasks::transactional_restore::{Outcome, commit_and_render, prepare};
|
|
20
23
|
use crate::tasks::*;
|
|
21
|
-
use crate::workspace::Workspace;
|
|
24
|
+
use crate::workspace::{PanelId, Workspace};
|
|
22
25
|
use crate::*;
|
|
23
26
|
|
|
24
27
|
/// How a [`restore_panel`] call reached the pipeline — the only two genuine
|
|
@@ -28,7 +31,7 @@ pub(crate) enum RestoreMode {
|
|
|
28
31
|
Fresh,
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
/// Where a failed
|
|
34
|
+
/// Where the error of a restore that COMMITTED and then failed to render goes.
|
|
32
35
|
#[derive(Clone, Copy)]
|
|
33
36
|
pub(crate) enum RestoreErrors {
|
|
34
37
|
// Raise errors in the UI.
|
|
@@ -62,77 +65,67 @@ async fn probe(
|
|
|
62
65
|
Ok((client, table))
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
///
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
) -> ApiResult<()> {
|
|
72
|
-
if session
|
|
73
|
-
.get_table()
|
|
74
|
-
.is_some_and(|t| t.get_name() == name.as_str())
|
|
75
|
-
{
|
|
76
|
-
return Ok(());
|
|
77
|
-
}
|
|
68
|
+
/// What a restore naming `table` does to the panel's binding.
|
|
69
|
+
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
70
|
+
pub(crate) enum Rebind {
|
|
71
|
+
/// A caller's restore: a table that REPLACES what the panel showed (or
|
|
72
|
+
/// recovers an errored panel) starts from a default config.
|
|
73
|
+
Replace,
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Some(table) => session.bind_table(table).await?,
|
|
83
|
-
None => session.pend_table(name).await?,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
session.commit_table_defaults();
|
|
87
|
-
Ok(())
|
|
75
|
+
/// The table lifecycle completing a pending bind: the committed config is
|
|
76
|
+
/// the intent the bind exists to honor.
|
|
77
|
+
Complete,
|
|
88
78
|
}
|
|
89
79
|
|
|
90
|
-
///
|
|
91
|
-
///
|
|
92
|
-
|
|
93
|
-
async fn
|
|
80
|
+
/// Decide a restore's [`BindPlan`], probing the incoming table — without
|
|
81
|
+
/// touching the session's own binding.
|
|
82
|
+
#[allow(clippy::too_many_arguments)]
|
|
83
|
+
async fn plan_binding(
|
|
84
|
+
ctx: &OpCtx,
|
|
94
85
|
session: &Session,
|
|
95
86
|
renderer: &Renderer,
|
|
96
87
|
workspace: &Workspace,
|
|
97
|
-
|
|
88
|
+
table: &TableUpdate,
|
|
89
|
+
fresh: bool,
|
|
90
|
+
rebind: Rebind,
|
|
98
91
|
missing: MissingTable,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
let journal = session.take_pending_load(generation);
|
|
103
|
-
let (client, table) = probed?;
|
|
104
|
-
let Some(journal) = journal else {
|
|
105
|
-
return Ok(());
|
|
92
|
+
) -> ApiResult<Option<BindPlan>> {
|
|
93
|
+
let OptionalUpdate::Update(table) = table else {
|
|
94
|
+
return Ok(Some(BindPlan::Keep));
|
|
106
95
|
};
|
|
107
96
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
table: Some(TableIntermediateState::Reloaded),
|
|
114
|
-
})
|
|
115
|
-
.await?;
|
|
116
|
-
|
|
117
|
-
session.set_client(client);
|
|
118
|
-
match table {
|
|
119
|
-
Some(table) => session.bind_table(table).await?,
|
|
120
|
-
None => session.pend_table(name).await?,
|
|
97
|
+
let name = table.name()?;
|
|
98
|
+
let same = session.get_table().is_some_and(|t| t.get_name() == name);
|
|
99
|
+
|
|
100
|
+
if same && !session.is_errored() && renderer.failure().is_none() {
|
|
101
|
+
return Ok(Some(BindPlan::Keep));
|
|
121
102
|
}
|
|
122
103
|
|
|
123
|
-
session.
|
|
124
|
-
|
|
125
|
-
|
|
104
|
+
let (client, table) = probe(session, workspace, name, missing).await?;
|
|
105
|
+
if !fresh && ctx.is_superseded() {
|
|
106
|
+
return Ok(None);
|
|
126
107
|
}
|
|
127
108
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
&&
|
|
109
|
+
if table.is_none()
|
|
110
|
+
&& rebind == Rebind::Complete
|
|
111
|
+
&& session.pending_table().as_deref() == Some(name)
|
|
131
112
|
{
|
|
132
|
-
|
|
113
|
+
return Ok(None);
|
|
133
114
|
}
|
|
134
115
|
|
|
135
|
-
|
|
116
|
+
let reset = !fresh && rebind == Rebind::Replace;
|
|
117
|
+
Ok(Some(match table {
|
|
118
|
+
Some(table) => BindPlan::Bind {
|
|
119
|
+
client,
|
|
120
|
+
table: Box::new(table),
|
|
121
|
+
reset,
|
|
122
|
+
},
|
|
123
|
+
None => BindPlan::Pend {
|
|
124
|
+
client,
|
|
125
|
+
name: name.to_owned(),
|
|
126
|
+
reset,
|
|
127
|
+
},
|
|
128
|
+
}))
|
|
136
129
|
}
|
|
137
130
|
|
|
138
131
|
/// Apply a [`ViewerConfigUpdate`] to a single panel and re-draw — the one
|
|
@@ -145,111 +138,130 @@ pub(crate) async fn restore_panel(
|
|
|
145
138
|
presentation: &Presentation,
|
|
146
139
|
workspace: &Workspace,
|
|
147
140
|
mode: RestoreMode,
|
|
141
|
+
update: ViewerConfigUpdate,
|
|
142
|
+
errors: RestoreErrors,
|
|
143
|
+
missing: MissingTable,
|
|
144
|
+
preamble: Option<futures::future::LocalBoxFuture<'static, ApiResult<()>>>,
|
|
145
|
+
) -> ApiResult<()> {
|
|
146
|
+
let update = Rc::new(update);
|
|
147
|
+
let kind = OpKind::Restore {
|
|
148
|
+
update: (!matches!(mode, RestoreMode::Fresh)).then(|| update.clone()),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
let ticket = session.submit(kind, {
|
|
152
|
+
clone!(session, renderer, presentation, workspace);
|
|
153
|
+
move |ctx| {
|
|
154
|
+
Box::pin(async move {
|
|
155
|
+
if let Some(preamble) = preamble {
|
|
156
|
+
preamble.await?;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
restore_panel_step(
|
|
160
|
+
&ctx,
|
|
161
|
+
&session,
|
|
162
|
+
&renderer,
|
|
163
|
+
&presentation,
|
|
164
|
+
&workspace,
|
|
165
|
+
mode,
|
|
166
|
+
Rebind::Replace,
|
|
167
|
+
RunOrigin::Public,
|
|
168
|
+
Rc::unwrap_or_clone(update),
|
|
169
|
+
errors,
|
|
170
|
+
missing,
|
|
171
|
+
)
|
|
172
|
+
.await?;
|
|
173
|
+
|
|
174
|
+
Ok(None)
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
ticket.settle().await
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/// The body of [`restore_panel`], to be called only from a running op's step.
|
|
183
|
+
#[allow(clippy::too_many_arguments)]
|
|
184
|
+
pub(crate) async fn restore_panel_step(
|
|
185
|
+
ctx: &OpCtx,
|
|
186
|
+
session: &Session,
|
|
187
|
+
renderer: &Renderer,
|
|
188
|
+
presentation: &Presentation,
|
|
189
|
+
workspace: &Workspace,
|
|
190
|
+
mode: RestoreMode,
|
|
191
|
+
rebind: Rebind,
|
|
192
|
+
origin: RunOrigin,
|
|
148
193
|
mut update: ViewerConfigUpdate,
|
|
149
194
|
errors: RestoreErrors,
|
|
150
195
|
missing: MissingTable,
|
|
151
196
|
) -> ApiResult<()> {
|
|
152
197
|
let active = matches!(mode, RestoreMode::Existing { active: true });
|
|
153
198
|
let fresh = matches!(mode, RestoreMode::Fresh);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
199
|
+
|
|
200
|
+
crate::tasks::resolve_table_ref(workspace, &mut update, missing).await?;
|
|
201
|
+
|
|
202
|
+
// A derived definition parked in the pending registry is a known-future
|
|
203
|
+
// table, never an unknown name — bind as PEND whatever the caller's
|
|
204
|
+
// missing-table mode.
|
|
205
|
+
let missing = match &update.table {
|
|
206
|
+
OptionalUpdate::Update(table)
|
|
207
|
+
if table
|
|
208
|
+
.name()
|
|
209
|
+
.is_ok_and(|name| workspace.is_derived_pending(name)) =>
|
|
210
|
+
{
|
|
211
|
+
MissingTable::Pend
|
|
160
212
|
},
|
|
161
|
-
|
|
162
|
-
}
|
|
213
|
+
_ => missing,
|
|
214
|
+
};
|
|
163
215
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
216
|
+
renderer.check_plugin_update(&update.plugin)?;
|
|
217
|
+
let Some(plan) = plan_binding(
|
|
218
|
+
ctx,
|
|
219
|
+
session,
|
|
220
|
+
renderer,
|
|
221
|
+
workspace,
|
|
222
|
+
&update.table,
|
|
223
|
+
fresh,
|
|
224
|
+
rebind,
|
|
225
|
+
missing,
|
|
226
|
+
)
|
|
227
|
+
.await?
|
|
228
|
+
else {
|
|
229
|
+
return Ok(());
|
|
230
|
+
};
|
|
167
231
|
|
|
168
232
|
if !fresh {
|
|
169
233
|
tracing::info!("Restoring {update}");
|
|
170
234
|
}
|
|
171
235
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// this pipeline stays per-panel. Applying it here reached only the
|
|
176
|
-
// `Existing { active: true }` mode, which is why a freshly created
|
|
177
|
-
// panel silently ignored it.
|
|
178
|
-
|
|
179
|
-
// Under `Suppress` the restore is TRANSACTIONAL, so snapshot the config
|
|
180
|
-
// it is about to overwrite — see the failure tail below for why.
|
|
181
|
-
let rollback =
|
|
182
|
-
matches!(errors, RestoreErrors::Suppress).then(|| session.get_view_config().clone());
|
|
183
|
-
|
|
184
|
-
let binding_before = (
|
|
185
|
-
session.get_table().map(|t| t.get_name().to_owned()),
|
|
186
|
-
session.pending_table(),
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
let generation = match &update.table {
|
|
190
|
-
OptionalUpdate::Update(name)
|
|
191
|
-
if session.is_errored()
|
|
192
|
-
|| (!fresh
|
|
193
|
-
&& session
|
|
194
|
-
.get_table()
|
|
195
|
-
.map(|t| t.get_name() != name.as_str())
|
|
196
|
-
.unwrap_or(true)) =>
|
|
197
|
-
{
|
|
198
|
-
Some(session.begin_pending_load())
|
|
199
|
-
},
|
|
200
|
-
_ => None,
|
|
201
|
-
};
|
|
236
|
+
let overlay = renderer
|
|
237
|
+
.slot_name()
|
|
238
|
+
.map(|id| overlay_for(workspace, &PanelId::from(id)));
|
|
202
239
|
|
|
203
|
-
let
|
|
240
|
+
let rebound = !matches!(plan, BindPlan::Keep);
|
|
241
|
+
let prepared = prepare(
|
|
204
242
|
session,
|
|
205
243
|
renderer,
|
|
206
244
|
presentation,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
async move {
|
|
212
|
-
let OptionalUpdate::Update(name) = table else {
|
|
213
|
-
return Ok(());
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
match generation {
|
|
217
|
-
Some(generation) => {
|
|
218
|
-
rebind_table_task(
|
|
219
|
-
&session, &renderer, &workspace, name, missing, generation,
|
|
220
|
-
)
|
|
221
|
-
.await
|
|
222
|
-
},
|
|
223
|
-
None => bind_table_task(&session, &workspace, name, missing).await,
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
},
|
|
245
|
+
active,
|
|
246
|
+
plan,
|
|
247
|
+
overlay,
|
|
248
|
+
update,
|
|
227
249
|
)
|
|
228
|
-
.await
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
session.take_pending_load(generation);
|
|
232
|
-
}
|
|
250
|
+
.await?;
|
|
251
|
+
let Outcome { committed, result } =
|
|
252
|
+
commit_and_render(session, renderer, presentation, origin, prepared).await;
|
|
233
253
|
|
|
234
|
-
if let Err(e) = &result
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
session.get_table().map(|t| t.get_name().to_owned()),
|
|
240
|
-
session.pending_table(),
|
|
241
|
-
);
|
|
242
|
-
|
|
243
|
-
if let Some(config) = rollback
|
|
244
|
-
&& binding_after == binding_before
|
|
245
|
-
{
|
|
246
|
-
session.commit_view_config(config.into()).unwrap_or_log();
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
}
|
|
254
|
+
if let Err(e) = &result
|
|
255
|
+
&& committed
|
|
256
|
+
&& matches!(errors, RestoreErrors::Publish)
|
|
257
|
+
{
|
|
258
|
+
let _ = renderer.fail(e.clone());
|
|
250
259
|
}
|
|
251
260
|
|
|
252
261
|
result?;
|
|
262
|
+
if rebound {
|
|
263
|
+
workspace.bindings_changed().emit(());
|
|
264
|
+
}
|
|
253
265
|
|
|
254
266
|
if fresh {
|
|
255
267
|
renderer.resize().await.unwrap_or_log();
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use futures::FutureExt;
|
|
13
14
|
use perspective_client::clone;
|
|
14
15
|
use perspective_js::utils::*;
|
|
15
16
|
|
|
16
17
|
use crate::config::ColumnConfigFieldUpdate;
|
|
17
|
-
use crate::renderer::Renderer;
|
|
18
|
-
use crate::session::Session;
|
|
18
|
+
use crate::renderer::{Renderer, StagedEdit};
|
|
19
|
+
use crate::session::{EditDelta, OpKind, Session};
|
|
19
20
|
|
|
20
21
|
/// Apply a [`ColumnConfigFieldUpdate`] from a column-style sidebar
|
|
21
22
|
/// control to the active plugin's per-column config bucket on
|
|
@@ -30,31 +31,57 @@ pub fn send_column_config(
|
|
|
30
31
|
column_name: &str,
|
|
31
32
|
update: ColumnConfigFieldUpdate,
|
|
32
33
|
) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
34
|
+
let column = column_name.to_string();
|
|
35
|
+
let staged = renderer.stage(StagedEdit::ColumnField {
|
|
36
|
+
column: column.clone(),
|
|
37
|
+
update: update.clone(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
let kind = OpKind::Edit {
|
|
41
|
+
delta: EditDelta::Renderer,
|
|
42
|
+
swaps_plugin: false,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
let ticket = session.submit(kind, {
|
|
46
|
+
clone!(session, renderer);
|
|
47
|
+
move |_ctx| {
|
|
48
|
+
Box::pin(async move {
|
|
49
|
+
let view_config = session.committed_view_config().clone();
|
|
50
|
+
renderer.update_columns_config_field(&view_config, &session, column, update);
|
|
51
|
+
drop(staged);
|
|
52
|
+
Ok(Some(
|
|
53
|
+
async move {
|
|
54
|
+
let view_config_snapshot = session.committed_view_config().clone();
|
|
55
|
+
let columns_configs = renderer
|
|
56
|
+
.all_columns_configs_materialized(&view_config_snapshot, &session)
|
|
57
|
+
.await;
|
|
58
|
+
|
|
59
|
+
let plugin_token = wasm_bindgen::JsValue::from_serde_ext(
|
|
60
|
+
&renderer.committed_plugin_config(),
|
|
61
|
+
)
|
|
62
|
+
.unwrap();
|
|
63
|
+
|
|
64
|
+
renderer
|
|
65
|
+
.ensure_plugin_selected()?
|
|
66
|
+
.restore(&plugin_token, Some(&columns_configs))?;
|
|
67
|
+
|
|
68
|
+
clone!(session);
|
|
69
|
+
renderer
|
|
70
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
71
|
+
.await?;
|
|
72
|
+
|
|
73
|
+
renderer
|
|
74
|
+
.columns_config_changed
|
|
75
|
+
.emit(columns_configs.clone());
|
|
76
|
+
|
|
77
|
+
renderer.column_style_changed.emit(columns_configs);
|
|
78
|
+
Ok(())
|
|
79
|
+
}
|
|
80
|
+
.boxed_local(),
|
|
81
|
+
))
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
ApiFuture::spawn(ticket.settle());
|
|
60
87
|
}
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use futures::FutureExt;
|
|
13
14
|
use perspective_client::clone;
|
|
14
15
|
use perspective_js::utils::*;
|
|
15
16
|
|
|
16
17
|
use crate::config::ColumnConfigFieldUpdate;
|
|
17
|
-
use crate::renderer::Renderer;
|
|
18
|
-
use crate::session::Session;
|
|
18
|
+
use crate::renderer::{Renderer, StagedEdit};
|
|
19
|
+
use crate::session::{EditDelta, OpKind, Session};
|
|
19
20
|
|
|
20
21
|
/// Apply a [`ColumnConfigFieldUpdate`] from the Plugin-settings tab to
|
|
21
22
|
/// the active plugin's bucket on [`Renderer`], then re-`restore` the
|
|
@@ -28,27 +29,55 @@ use crate::session::Session;
|
|
|
28
29
|
///
|
|
29
30
|
/// Column-style updates go through [`super::send_column_config`].
|
|
30
31
|
pub fn send_plugin_config(session: &Session, renderer: &Renderer, update: ColumnConfigFieldUpdate) {
|
|
31
|
-
let
|
|
32
|
-
let
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
let staged = renderer.stage(StagedEdit::PluginField(update.clone()));
|
|
33
|
+
let kind = OpKind::Edit {
|
|
34
|
+
delta: EditDelta::Renderer,
|
|
35
|
+
swaps_plugin: false,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
let ticket = session.submit(kind, {
|
|
39
|
+
clone!(session, renderer);
|
|
40
|
+
move |_ctx| {
|
|
41
|
+
Box::pin(async move {
|
|
42
|
+
let view_config = session.committed_view_config().clone();
|
|
43
|
+
let changed = renderer.update_plugin_config_field(&view_config, update);
|
|
44
|
+
drop(staged);
|
|
45
|
+
Ok(Some(
|
|
46
|
+
async move {
|
|
47
|
+
if changed {
|
|
48
|
+
deliver_plugin_config(&session, &renderer).await?;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Ok(())
|
|
52
|
+
}
|
|
53
|
+
.boxed_local(),
|
|
54
|
+
))
|
|
55
|
+
})
|
|
50
56
|
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
ApiFuture::spawn(ticket.settle());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Re-`restore` the active plugin with its committed buckets, repaint, and
|
|
63
|
+
/// announce the plugin-level change.
|
|
64
|
+
pub(super) async fn deliver_plugin_config(session: &Session, renderer: &Renderer) -> ApiResult<()> {
|
|
65
|
+
let plugin_config = renderer.committed_plugin_config();
|
|
66
|
+
let plugin_token = wasm_bindgen::JsValue::from_serde_ext(&plugin_config).unwrap();
|
|
67
|
+
let view_config_snapshot = session.committed_view_config().clone();
|
|
68
|
+
let columns_configs = renderer
|
|
69
|
+
.all_columns_configs_materialized(&view_config_snapshot, session)
|
|
70
|
+
.await;
|
|
71
|
+
|
|
72
|
+
renderer
|
|
73
|
+
.ensure_plugin_selected()?
|
|
74
|
+
.restore(&plugin_token, Some(&columns_configs))?;
|
|
75
|
+
|
|
76
|
+
clone!(session);
|
|
77
|
+
renderer
|
|
78
|
+
.update_lazy(async move { Ok(session.get_view_with_dimensions()) })
|
|
79
|
+
.await?;
|
|
51
80
|
|
|
52
|
-
|
|
53
|
-
|
|
81
|
+
renderer.plugin_config_changed.emit(plugin_config);
|
|
82
|
+
Ok(())
|
|
54
83
|
}
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
+
use futures::FutureExt;
|
|
13
14
|
use perspective_client::clone;
|
|
14
15
|
use perspective_js::utils::*;
|
|
15
16
|
|
|
16
17
|
use crate::config::*;
|
|
17
|
-
use crate::renderer::Renderer;
|
|
18
|
-
use crate::session::Session;
|
|
18
|
+
use crate::renderer::{Renderer, StagedEdit};
|
|
19
|
+
use crate::session::{EditDelta, OpKind, Session};
|
|
19
20
|
|
|
20
21
|
/// Set the active plugin's `edit_mode`, persisting it in the [`Renderer`]'s
|
|
21
22
|
/// plugin bucket and re-`restore`+rendering (the same merged-token path as
|
|
@@ -27,35 +28,42 @@ use crate::session::Session;
|
|
|
27
28
|
/// (e.g. charts) schema-gate the key out in [`Renderer::update_plugin_config`],
|
|
28
29
|
/// making this a no-op for them.
|
|
29
30
|
pub fn set_edit_mode(session: &Session, renderer: &Renderer, mode: &str) {
|
|
30
|
-
let view_config = session.get_view_config().clone();
|
|
31
31
|
let mut map = serde_json::Map::new();
|
|
32
32
|
map.insert(
|
|
33
33
|
"edit_mode".to_owned(),
|
|
34
34
|
serde_json::Value::String(mode.to_owned()),
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
let staged = renderer.stage(StagedEdit::PluginConfig(map.clone()));
|
|
38
|
+
let kind = OpKind::Edit {
|
|
39
|
+
delta: EditDelta::Renderer,
|
|
40
|
+
swaps_plugin: false,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
let ticket = session.submit(kind, {
|
|
44
|
+
clone!(session, renderer);
|
|
45
|
+
move |_ctx| {
|
|
46
|
+
Box::pin(async move {
|
|
47
|
+
let view_config = session.committed_view_config().clone();
|
|
48
|
+
let changed = renderer
|
|
49
|
+
.update_plugin_config(&view_config, OptionalUpdate::Update(map))
|
|
50
|
+
.unwrap_or_default();
|
|
51
|
+
|
|
52
|
+
drop(staged);
|
|
53
|
+
Ok(Some(
|
|
54
|
+
async move {
|
|
55
|
+
if changed {
|
|
56
|
+
super::send_plugin_config::deliver_plugin_config(&session, &renderer)
|
|
57
|
+
.await?;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
Ok(())
|
|
61
|
+
}
|
|
62
|
+
.boxed_local(),
|
|
63
|
+
))
|
|
64
|
+
})
|
|
57
65
|
}
|
|
66
|
+
});
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
})
|
|
68
|
+
ApiFuture::spawn(ticket.settle());
|
|
61
69
|
}
|