@perspective-dev/viewer 5.5.1 → 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/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 +115 -35
- package/dist/wasm/perspective-viewer.js +314 -190
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +21 -17
- package/package.json +31 -18
- 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/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 +74 -24
- package/src/rust/components/status_indicator.rs +8 -1
- package/src/rust/components/viewer/filters.rs +13 -1
- package/src/rust/components/viewer/msg.rs +4 -0
- package/src/rust/components/viewer/panels.rs +41 -1
- 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 +14 -0
- package/src/rust/components/viewer.rs +45 -25
- package/src/rust/components/window_editor.rs +5 -4
- package/src/rust/config/column_config_schema.rs +38 -377
- 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 +187 -131
- 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 +30 -9
- 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 +900 -395
- 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 +5 -0
- package/src/rust/tasks/create_panel.rs +34 -9
- package/src/rust/tasks/derive.rs +312 -0
- package/src/rust/tasks/mod.rs +6 -2
- package/src/rust/tasks/pipeline.rs +145 -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 +39 -12
- 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/local_fonts.rs +0 -36
- package/src/rust/utils/mod.rs +2 -3
- package/src/rust/utils/modal_position.rs +15 -21
- 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/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/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/mod.rs +0 -14
- package/src/rust/utils/tests/pubsub.rs +0 -95
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-6813b568219c6763 → perspective-viewer-d0d525977a1acba2}/inline5.js +0 -0
|
@@ -10,17 +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::{
|
|
20
|
-
|
|
21
|
-
};
|
|
21
|
+
use crate::session::{BindPlan, MissingTable, OpCtx, OpKind, Session, probe_table};
|
|
22
|
+
use crate::tasks::transactional_restore::{Outcome, commit_and_render, prepare};
|
|
22
23
|
use crate::tasks::*;
|
|
23
|
-
use crate::workspace::Workspace;
|
|
24
|
+
use crate::workspace::{PanelId, Workspace};
|
|
24
25
|
use crate::*;
|
|
25
26
|
|
|
26
27
|
/// How a [`restore_panel`] call reached the pipeline — the only two genuine
|
|
@@ -30,7 +31,7 @@ pub(crate) enum RestoreMode {
|
|
|
30
31
|
Fresh,
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/// Where a failed
|
|
34
|
+
/// Where the error of a restore that COMMITTED and then failed to render goes.
|
|
34
35
|
#[derive(Clone, Copy)]
|
|
35
36
|
pub(crate) enum RestoreErrors {
|
|
36
37
|
// Raise errors in the UI.
|
|
@@ -64,77 +65,67 @@ async fn probe(
|
|
|
64
65
|
Ok((client, table))
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
///
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
) -> ApiResult<()> {
|
|
74
|
-
if session
|
|
75
|
-
.get_table()
|
|
76
|
-
.is_some_and(|t| t.get_name() == name.as_str())
|
|
77
|
-
{
|
|
78
|
-
return Ok(());
|
|
79
|
-
}
|
|
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,
|
|
80
74
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Some(table) => session.bind_table(table).await?,
|
|
85
|
-
None => session.pend_table(name).await?,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
session.commit_table_defaults();
|
|
89
|
-
Ok(())
|
|
75
|
+
/// The table lifecycle completing a pending bind: the committed config is
|
|
76
|
+
/// the intent the bind exists to honor.
|
|
77
|
+
Complete,
|
|
90
78
|
}
|
|
91
79
|
|
|
92
|
-
///
|
|
93
|
-
///
|
|
94
|
-
|
|
95
|
-
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,
|
|
96
85
|
session: &Session,
|
|
97
86
|
renderer: &Renderer,
|
|
98
87
|
workspace: &Workspace,
|
|
99
|
-
|
|
88
|
+
table: &TableUpdate,
|
|
89
|
+
fresh: bool,
|
|
90
|
+
rebind: Rebind,
|
|
100
91
|
missing: MissingTable,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
let journal = load.claim();
|
|
105
|
-
let (client, table) = probed?;
|
|
106
|
-
let Some(journal) = journal else {
|
|
107
|
-
return Ok(());
|
|
92
|
+
) -> ApiResult<Option<BindPlan>> {
|
|
93
|
+
let OptionalUpdate::Update(table) = table else {
|
|
94
|
+
return Ok(Some(BindPlan::Keep));
|
|
108
95
|
};
|
|
109
96
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
table: Some(TableIntermediateState::Reloaded),
|
|
116
|
-
})
|
|
117
|
-
.await?;
|
|
118
|
-
|
|
119
|
-
session.set_client(client);
|
|
120
|
-
match table {
|
|
121
|
-
Some(table) => session.bind_table(table).await?,
|
|
122
|
-
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));
|
|
123
102
|
}
|
|
124
103
|
|
|
125
|
-
session.
|
|
126
|
-
|
|
127
|
-
|
|
104
|
+
let (client, table) = probe(session, workspace, name, missing).await?;
|
|
105
|
+
if !fresh && ctx.is_superseded() {
|
|
106
|
+
return Ok(None);
|
|
128
107
|
}
|
|
129
108
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
&&
|
|
109
|
+
if table.is_none()
|
|
110
|
+
&& rebind == Rebind::Complete
|
|
111
|
+
&& session.pending_table().as_deref() == Some(name)
|
|
133
112
|
{
|
|
134
|
-
|
|
113
|
+
return Ok(None);
|
|
135
114
|
}
|
|
136
115
|
|
|
137
|
-
|
|
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
|
+
}))
|
|
138
129
|
}
|
|
139
130
|
|
|
140
131
|
/// Apply a [`ViewerConfigUpdate`] to a single panel and re-draw — the one
|
|
@@ -147,109 +138,130 @@ pub(crate) async fn restore_panel(
|
|
|
147
138
|
presentation: &Presentation,
|
|
148
139
|
workspace: &Workspace,
|
|
149
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,
|
|
150
193
|
mut update: ViewerConfigUpdate,
|
|
151
194
|
errors: RestoreErrors,
|
|
152
195
|
missing: MissingTable,
|
|
153
196
|
) -> ApiResult<()> {
|
|
154
197
|
let active = matches!(mode, RestoreMode::Existing { active: true });
|
|
155
198
|
let fresh = matches!(mode, RestoreMode::Fresh);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
162
212
|
},
|
|
163
|
-
|
|
164
|
-
}
|
|
213
|
+
_ => missing,
|
|
214
|
+
};
|
|
165
215
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
};
|
|
169
231
|
|
|
170
232
|
if !fresh {
|
|
171
233
|
tracing::info!("Restoring {update}");
|
|
172
234
|
}
|
|
173
235
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// this pipeline stays per-panel. Applying it here reached only the
|
|
178
|
-
// `Existing { active: true }` mode, which is why a freshly created
|
|
179
|
-
// panel silently ignored it.
|
|
180
|
-
|
|
181
|
-
// Under `Suppress` the restore is TRANSACTIONAL, so snapshot the config
|
|
182
|
-
// it is about to overwrite — see the failure tail below for why.
|
|
183
|
-
let rollback =
|
|
184
|
-
matches!(errors, RestoreErrors::Suppress).then(|| session.get_view_config().clone());
|
|
185
|
-
|
|
186
|
-
let binding_before = (
|
|
187
|
-
session.get_table().map(|t| t.get_name().to_owned()),
|
|
188
|
-
session.pending_table(),
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
let load = match &update.table {
|
|
192
|
-
OptionalUpdate::Update(name)
|
|
193
|
-
if session.is_errored()
|
|
194
|
-
|| (!fresh
|
|
195
|
-
&& session
|
|
196
|
-
.get_table()
|
|
197
|
-
.map(|t| t.get_name() != name.as_str())
|
|
198
|
-
.unwrap_or(true)) =>
|
|
199
|
-
{
|
|
200
|
-
Some(session.begin_pending_load())
|
|
201
|
-
},
|
|
202
|
-
_ => None,
|
|
203
|
-
};
|
|
236
|
+
let overlay = renderer
|
|
237
|
+
.slot_name()
|
|
238
|
+
.map(|id| overlay_for(workspace, &PanelId::from(id)));
|
|
204
239
|
|
|
205
|
-
let
|
|
240
|
+
let rebound = !matches!(plan, BindPlan::Keep);
|
|
241
|
+
let prepared = prepare(
|
|
206
242
|
session,
|
|
207
243
|
renderer,
|
|
208
244
|
presentation,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
async move {
|
|
214
|
-
let OptionalUpdate::Update(name) = table else {
|
|
215
|
-
return Ok(());
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
match &load {
|
|
219
|
-
Some(load) => {
|
|
220
|
-
rebind_table_task(&session, &renderer, &workspace, name, missing, load)
|
|
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
|
-
load.close();
|
|
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
|
}
|