@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
|
@@ -19,7 +19,6 @@ use futures::channel::oneshot::channel;
|
|
|
19
19
|
use futures::future::join_all;
|
|
20
20
|
use js_sys::{Array, JsString};
|
|
21
21
|
use perspective_client::config::ViewConfigUpdate;
|
|
22
|
-
use perspective_client::utils::PerspectiveResultExt;
|
|
23
22
|
use perspective_js::utils::global;
|
|
24
23
|
use perspective_js::{JsViewConfig, JsViewWindow, Table, View, apierror};
|
|
25
24
|
use wasm_bindgen::JsCast;
|
|
@@ -38,7 +37,7 @@ use crate::js::*;
|
|
|
38
37
|
use crate::presentation::*;
|
|
39
38
|
use crate::queries::*;
|
|
40
39
|
use crate::root::Root;
|
|
41
|
-
use crate::session::{MissingTable, ResetOptions, TableLoadState};
|
|
40
|
+
use crate::session::{BindPlan, Disposal, MissingTable, OpKind, ResetOptions, TableLoadState};
|
|
42
41
|
use crate::tasks::*;
|
|
43
42
|
use crate::utils::*;
|
|
44
43
|
use crate::workspace::{Panel, PanelId, Workspace};
|
|
@@ -212,13 +211,13 @@ impl PerspectiveViewerElement {
|
|
|
212
211
|
.panels()
|
|
213
212
|
.into_iter()
|
|
214
213
|
.filter(|panel| !fresh.contains(&panel.id))
|
|
215
|
-
.filter(|panel| stamp_global_overlay(&self.workspace, &panel.id, &panel.session))
|
|
216
214
|
.collect::<Vec<_>>();
|
|
217
215
|
|
|
218
|
-
join_all(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
join_all(
|
|
217
|
+
retained
|
|
218
|
+
.iter()
|
|
219
|
+
.map(|panel| broadcast_overlay(&self.workspace, panel)),
|
|
220
|
+
)
|
|
222
221
|
.await
|
|
223
222
|
.into_iter()
|
|
224
223
|
.collect::<ApiResult<Vec<_>>>()?;
|
|
@@ -237,7 +236,8 @@ impl PerspectiveViewerElement {
|
|
|
237
236
|
|
|
238
237
|
async fn workspace_config(this: Self, full_palette: bool) -> ApiResult<JsValue> {
|
|
239
238
|
this.workspace.effects().settle().await;
|
|
240
|
-
let mut panels: std::collections::BTreeMap<String, PanelViewerConfig
|
|
239
|
+
let mut panels: std::collections::BTreeMap<String, PanelViewerConfig<String, String>> =
|
|
240
|
+
Default::default();
|
|
241
241
|
for id in &this.workspace.panel_ids() {
|
|
242
242
|
let panel = this.workspace.panel(id).into_apierror()?;
|
|
243
243
|
let config = panel
|
|
@@ -288,6 +288,7 @@ impl PerspectiveViewerElement {
|
|
|
288
288
|
active,
|
|
289
289
|
layout,
|
|
290
290
|
panels,
|
|
291
|
+
derived_tables: this.workspace.derived_configs(),
|
|
291
292
|
global_filters: this.workspace.global_filters(),
|
|
292
293
|
masters: this
|
|
293
294
|
.workspace
|
|
@@ -492,41 +493,17 @@ impl PerspectiveViewerElement {
|
|
|
492
493
|
.dyn_into::<js_sys::Promise>()
|
|
493
494
|
.unwrap_or_else(|_| js_sys::Promise::resolve(&table));
|
|
494
495
|
|
|
495
|
-
// Resolve the target panel. On an EMPTY element (zero panels):
|
|
496
|
-
// - a synchronously-detectable `Client` registers inertly with NO panel (the
|
|
497
|
-
// common `load(client)` — no phantom panel is left behind);
|
|
498
|
-
// - otherwise (a resolved `Table`, or a `Promise` whose type isn't yet known)
|
|
499
|
-
// the first panel is RESERVED synchronously here — a full panel model held
|
|
500
|
-
// in the workspace's reservation slot, NOT placed — so its ordering position
|
|
501
|
-
// is fixed at the call site: a `restore()` fired right after an unawaited
|
|
502
|
-
// `load()` CLAIMS the reservation (placing it) and targets THIS panel, not a
|
|
503
|
-
// second one. The reservation is likewise placed when the payload proves to
|
|
504
|
-
// be a `Table` (or the load fails, surfacing its error), and discarded —
|
|
505
|
-
// only while still unclaimed — for an inert `Client` payload. Placement and
|
|
506
|
-
// discard are both atomic slot transfers (`Workspace::claim_reserved` /
|
|
507
|
-
// `Workspace::take_reserved`), so an inert payload disposing a panel a
|
|
508
|
-
// racing `restore` claimed is unrepresentable.
|
|
509
|
-
// A pre-existing active panel is used as-is (a `Client` registers
|
|
510
|
-
// inertly against it, never clearing its table).
|
|
511
496
|
let (panel, notify) = match self.workspace.active_panel() {
|
|
512
497
|
Some(panel) => (panel, None),
|
|
513
498
|
None => {
|
|
514
|
-
// Empty element — classify the payload synchronously where possible.
|
|
515
499
|
if let Ok(Some(client)) =
|
|
516
500
|
try_from_js_option::<perspective_js::Client>(table.clone())
|
|
517
501
|
{
|
|
518
|
-
// A resolved `Client` registers SYNCHRONOUSLY (so an unawaited
|
|
519
|
-
// `load(client)` is visible to a `restore()` fired right after,
|
|
520
|
-
// which creates the first panel and federates against loaded
|
|
521
|
-
// clients) and creates NO panel — inert.
|
|
522
502
|
self.workspace
|
|
523
503
|
.set_default_client(client.get_client().clone());
|
|
524
504
|
return Ok(ApiFuture::new(async { Ok(()) }));
|
|
525
505
|
}
|
|
526
506
|
|
|
527
|
-
// A resolved `Table` (or a `Promise` whose type isn't yet known)
|
|
528
|
-
// adopts the pending reservation (a second `load()` on a
|
|
529
|
-
// still-empty element), else reserves a fresh panel model.
|
|
530
507
|
let panel = self.workspace.reserved_panel().unwrap_or_else(|| {
|
|
531
508
|
create_panel_model(
|
|
532
509
|
&self.elem,
|
|
@@ -548,18 +525,28 @@ impl PerspectiveViewerElement {
|
|
|
548
525
|
|
|
549
526
|
let session = panel.session;
|
|
550
527
|
let renderer = panel.renderer;
|
|
551
|
-
let generation = session.begin_pending_load();
|
|
552
528
|
clone!(self.workspace, self.presentation);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
529
|
+
enum Discard {
|
|
530
|
+
Unclaimed(Panel),
|
|
531
|
+
Evicted(Panel),
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let table_known = matches!(
|
|
535
|
+
try_from_js_option::<perspective_js::Table>(table.clone()),
|
|
536
|
+
Ok(Some(_))
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
let ticket = session.submit(OpKind::Load { table_known }, {
|
|
540
|
+
clone!(session, renderer);
|
|
541
|
+
move |load| {
|
|
542
|
+
Box::pin(async move {
|
|
543
|
+
let _effect = effect;
|
|
544
|
+
renderer.set_throttle(None);
|
|
545
|
+
let result = {
|
|
546
|
+
clone!(session, renderer, workspace, notify, load);
|
|
547
|
+
renderer
|
|
560
548
|
.clone()
|
|
561
549
|
.render_task(|guard| async move {
|
|
562
|
-
seed_panel_theme(&presentation, &renderer).await;
|
|
563
550
|
renderer.stamp_theme(None);
|
|
564
551
|
let jstable = JsFuture::from(promise)
|
|
565
552
|
.await
|
|
@@ -569,30 +556,17 @@ impl PerspectiveViewerElement {
|
|
|
569
556
|
try_from_js_option::<perspective_js::Table>(jstable.clone())
|
|
570
557
|
{
|
|
571
558
|
tracing::warn!("{}", DEPRECATED_TABLE_MESSAGE);
|
|
572
|
-
|
|
559
|
+
if load.is_superseded() {
|
|
573
560
|
return Ok(None);
|
|
574
|
-
}
|
|
561
|
+
}
|
|
575
562
|
|
|
576
563
|
if let Some(notify) = ¬ify {
|
|
577
564
|
place_reserved(&workspace, notify, true);
|
|
578
565
|
}
|
|
579
566
|
|
|
580
|
-
let _plugin = renderer.ensure_plugin_selected()?;
|
|
581
|
-
let _ = renderer.mount_active_plugin();
|
|
582
|
-
session
|
|
583
|
-
.reset(ResetOptions {
|
|
584
|
-
config: true,
|
|
585
|
-
expressions: true,
|
|
586
|
-
stats: true,
|
|
587
|
-
table: Some(session::TableIntermediateState::Reloaded),
|
|
588
|
-
})
|
|
589
|
-
.await
|
|
590
|
-
.unwrap_or_log();
|
|
591
|
-
|
|
592
567
|
let client = table.get_client().await;
|
|
593
568
|
let inner_client = client.get_client().clone();
|
|
594
|
-
|
|
595
|
-
workspace.set_default_client(inner_client);
|
|
569
|
+
workspace.set_default_client(inner_client.clone());
|
|
596
570
|
let name = table.get_name().await;
|
|
597
571
|
tracing::debug!(
|
|
598
572
|
"Loading {:.0} rows from `Table` {}",
|
|
@@ -600,21 +574,40 @@ impl PerspectiveViewerElement {
|
|
|
600
574
|
name
|
|
601
575
|
);
|
|
602
576
|
|
|
603
|
-
session
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
577
|
+
let plan = match session::probe_table(
|
|
578
|
+
&inner_client,
|
|
579
|
+
&name,
|
|
580
|
+
MissingTable::Pend,
|
|
581
|
+
)
|
|
582
|
+
.await?
|
|
583
|
+
{
|
|
584
|
+
Some(table) => BindPlan::Bind {
|
|
585
|
+
client: inner_client,
|
|
586
|
+
table: Box::new(table),
|
|
587
|
+
reset: true,
|
|
588
|
+
},
|
|
589
|
+
None => BindPlan::Pend {
|
|
590
|
+
client: inner_client,
|
|
591
|
+
name,
|
|
592
|
+
reset: true,
|
|
593
|
+
},
|
|
594
|
+
};
|
|
611
595
|
|
|
612
|
-
crate::tasks::
|
|
613
|
-
&
|
|
596
|
+
let prepared = crate::tasks::prepare(
|
|
597
|
+
&session,
|
|
614
598
|
&renderer,
|
|
615
|
-
|
|
599
|
+
&presentation,
|
|
616
600
|
false,
|
|
617
|
-
|
|
601
|
+
plan,
|
|
602
|
+
renderer
|
|
603
|
+
.slot_name()
|
|
604
|
+
.map(|id| overlay_for(&workspace, &PanelId::from(id))),
|
|
605
|
+
ViewerConfigUpdate::default(),
|
|
606
|
+
)
|
|
607
|
+
.await?;
|
|
608
|
+
|
|
609
|
+
crate::tasks::commit_and_render_locked(
|
|
610
|
+
guard, &session, &renderer, prepared,
|
|
618
611
|
)
|
|
619
612
|
.await?;
|
|
620
613
|
|
|
@@ -626,14 +619,11 @@ impl PerspectiveViewerElement {
|
|
|
626
619
|
// INERT: register the client only — never rebind or
|
|
627
620
|
// reset the active panel (its table is preserved).
|
|
628
621
|
// Panels bind their client lazily at table-resolution
|
|
629
|
-
// time (`Workspace::resolve_client_for_table`).
|
|
630
|
-
|
|
631
|
-
// performs no reset, and any racing `restore`'s
|
|
632
|
-
// commits already applied live (`commit_view_config`).
|
|
633
|
-
let owned_window = session.take_pending_load(generation).is_some();
|
|
622
|
+
// time (`Workspace::resolve_client_for_table`).
|
|
623
|
+
let owned_window = !load.is_superseded();
|
|
634
624
|
let discard = if owned_window && notify.is_some() {
|
|
635
625
|
match workspace.take_reserved() {
|
|
636
|
-
Some(panel) => Some((panel
|
|
626
|
+
Some(panel) => Some(Discard::Unclaimed(panel)),
|
|
637
627
|
// The one-shot claim read: a table-less
|
|
638
628
|
// `restore` claimed the reservation, and no
|
|
639
629
|
// table has bound nor is pending — evict the
|
|
@@ -655,9 +645,7 @@ impl PerspectiveViewerElement {
|
|
|
655
645
|
notify.emit(());
|
|
656
646
|
}
|
|
657
647
|
|
|
658
|
-
evicted.map(
|
|
659
|
-
(panel, Some(ApiError::new(CREATE_REQUIRES_TABLE)))
|
|
660
|
-
})
|
|
648
|
+
evicted.map(Discard::Evicted)
|
|
661
649
|
},
|
|
662
650
|
None => None,
|
|
663
651
|
}
|
|
@@ -668,33 +656,36 @@ impl PerspectiveViewerElement {
|
|
|
668
656
|
workspace.set_default_client(client.get_client().clone());
|
|
669
657
|
Ok(discard)
|
|
670
658
|
} else {
|
|
671
|
-
session.take_pending_load(generation);
|
|
672
659
|
Err(ApiError::new("Invalid argument"))
|
|
673
660
|
}
|
|
674
661
|
})
|
|
675
662
|
.await
|
|
676
|
-
|
|
663
|
+
};
|
|
677
664
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
665
|
+
match result {
|
|
666
|
+
Err(e) => {
|
|
667
|
+
if let Some(notify) = ¬ify {
|
|
668
|
+
place_reserved(&workspace, notify, true);
|
|
669
|
+
}
|
|
684
670
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
671
|
+
session.set_error(false, e.clone()).await?;
|
|
672
|
+
Err(e)
|
|
673
|
+
},
|
|
674
|
+
Ok(Some(Discard::Unclaimed(panel))) => {
|
|
675
|
+
eject_panel(panel, Disposal::Reject).await?;
|
|
676
|
+
Ok(None)
|
|
677
|
+
},
|
|
678
|
+
Ok(Some(Discard::Evicted(panel))) => {
|
|
679
|
+
eject_panel(panel, Disposal::Resolve).await?;
|
|
680
|
+
Err(ApiError::new(CREATE_REQUIRES_TABLE))
|
|
681
|
+
},
|
|
682
|
+
Ok(None) => Ok(None),
|
|
693
683
|
}
|
|
694
|
-
}
|
|
695
|
-
Ok(None) => Ok(()),
|
|
684
|
+
})
|
|
696
685
|
}
|
|
697
|
-
})
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
Ok(ApiFuture::new_throttled(ticket.settle()))
|
|
698
689
|
}
|
|
699
690
|
|
|
700
691
|
/// Delete all internal [`View`]s and all associated state, rendering this
|
|
@@ -1002,6 +993,7 @@ impl PerspectiveViewerElement {
|
|
|
1002
993
|
|
|
1003
994
|
let mut fulfilled = false;
|
|
1004
995
|
for panel in &panels {
|
|
996
|
+
panel.session.settle_ops().await;
|
|
1005
997
|
panel.renderer.clone().with_lock(async { Ok(()) }).await?;
|
|
1006
998
|
panel.renderer.clone().with_lock(async { Ok(()) }).await?;
|
|
1007
999
|
panel.session.settle_dispatches().await?;
|
|
@@ -1009,6 +1001,7 @@ impl PerspectiveViewerElement {
|
|
|
1009
1001
|
&& presentation.is_visible()
|
|
1010
1002
|
&& !panel.renderer.is_plugin_activated()?
|
|
1011
1003
|
&& panel.session.get_error().is_none()
|
|
1004
|
+
&& panel.renderer.failure().is_none()
|
|
1012
1005
|
&& matches!(panel.session.has_table(), Some(TableLoadState::Loaded))
|
|
1013
1006
|
{
|
|
1014
1007
|
set_panel_paused(&panel.session, &panel.renderer, &presentation, true)
|
|
@@ -1060,11 +1053,9 @@ impl PerspectiveViewerElement {
|
|
|
1060
1053
|
/// either "json", "string" or "arraybuffer" format.
|
|
1061
1054
|
/// - `options.panel` - The panel to target, or the active panel when
|
|
1062
1055
|
/// omitted.
|
|
1063
|
-
/// - `options.suppress_errors` - when `true`, a
|
|
1064
|
-
///
|
|
1065
|
-
///
|
|
1066
|
-
/// already applied (theme, title, plugin, a successful `table` change)
|
|
1067
|
-
/// untouched.
|
|
1056
|
+
/// - `options.suppress_errors` - when `true`, a config that was applied and
|
|
1057
|
+
/// then failed to render only rejects the returned `Promise`, without
|
|
1058
|
+
/// raising the panel's visible error state.
|
|
1068
1059
|
/// - `options.wait_for_table` - when `true`, a `table` no loaded client
|
|
1069
1060
|
/// hosts yet leaves the panel empty and pending until the table is
|
|
1070
1061
|
/// created, instead of the default error.
|
|
@@ -1116,6 +1107,7 @@ impl PerspectiveViewerElement {
|
|
|
1116
1107
|
Create(Box<ViewerConfigInitial>),
|
|
1117
1108
|
}
|
|
1118
1109
|
|
|
1110
|
+
crate::tasks::resolve_table_ref(&this.workspace, &mut update, missing).await?;
|
|
1119
1111
|
let notify = this.layout_changed_notify();
|
|
1120
1112
|
let target = match this.workspace.panel_or_active(id.as_ref()) {
|
|
1121
1113
|
// An existing (or the active) panel — update it in place.
|
|
@@ -1138,14 +1130,24 @@ impl PerspectiveViewerElement {
|
|
|
1138
1130
|
},
|
|
1139
1131
|
};
|
|
1140
1132
|
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1133
|
+
let settings_toggle: Option<futures::future::LocalBoxFuture<'static, ApiResult<()>>> =
|
|
1134
|
+
(!matches!(settings, OptionalUpdate::Missing)).then(|| {
|
|
1135
|
+
let this = this.clone();
|
|
1136
|
+
Box::pin(async move {
|
|
1137
|
+
let (sender, receiver) = channel::<ApiResult<JsValue>>();
|
|
1138
|
+
this.root.borrow().as_ref().into_apierror()?.send_message(
|
|
1139
|
+
PerspectiveViewerMsg::ToggleSettingsInit(
|
|
1140
|
+
Some(settings),
|
|
1141
|
+
false,
|
|
1142
|
+
Some(sender),
|
|
1143
|
+
),
|
|
1144
|
+
);
|
|
1146
1145
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1146
|
+
receiver.await.map_err(|_| ApiError::new("Cancelled"))??;
|
|
1147
|
+
Ok(())
|
|
1148
|
+
})
|
|
1149
|
+
as futures::future::LocalBoxFuture<'static, ApiResult<()>>
|
|
1150
|
+
});
|
|
1149
1151
|
|
|
1150
1152
|
match target {
|
|
1151
1153
|
Target::Existing { panel, active } => {
|
|
@@ -1158,6 +1160,7 @@ impl PerspectiveViewerElement {
|
|
|
1158
1160
|
update,
|
|
1159
1161
|
errors,
|
|
1160
1162
|
missing,
|
|
1163
|
+
settings_toggle,
|
|
1161
1164
|
)
|
|
1162
1165
|
.await
|
|
1163
1166
|
},
|
|
@@ -1171,10 +1174,15 @@ impl PerspectiveViewerElement {
|
|
|
1171
1174
|
update,
|
|
1172
1175
|
errors,
|
|
1173
1176
|
missing,
|
|
1177
|
+
settings_toggle,
|
|
1174
1178
|
)
|
|
1175
1179
|
.await
|
|
1176
1180
|
},
|
|
1177
1181
|
Target::Create(config) => {
|
|
1182
|
+
if let Some(settings_toggle) = settings_toggle {
|
|
1183
|
+
settings_toggle.await?;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1178
1186
|
create_panel(
|
|
1179
1187
|
&this.elem,
|
|
1180
1188
|
&this.presentation,
|
|
@@ -1230,7 +1238,22 @@ impl PerspectiveViewerElement {
|
|
|
1230
1238
|
let _effect = effect;
|
|
1231
1239
|
let RestoreWorkspaceOptions { wait_for_table } = parse_options(options)?;
|
|
1232
1240
|
let missing = MissingTable::from_wait(wait_for_table);
|
|
1233
|
-
let (contents, eject_tasks) = sync_update_panels(&this, update)?;
|
|
1241
|
+
let (contents, eject_tasks, derived_tables) = sync_update_panels(&this, update)?;
|
|
1242
|
+
|
|
1243
|
+
// The outgoing panels' ejects must settle BEFORE the derived
|
|
1244
|
+
// pass: a stale derivation is undeletable (and so cannot be
|
|
1245
|
+
// redefined or pruned) while an ejecting panel still holds a
|
|
1246
|
+
// `View` on it. Their errors are surfaced LAST, after the
|
|
1247
|
+
// replacement set is in place.
|
|
1248
|
+
let ejected = join_all(eject_tasks)
|
|
1249
|
+
.await
|
|
1250
|
+
.into_iter()
|
|
1251
|
+
.collect::<ApiResult<Vec<_>>>();
|
|
1252
|
+
|
|
1253
|
+
if let Some(derived_tables) = derived_tables.clone() {
|
|
1254
|
+
crate::tasks::apply_derived_tables(&this.workspace, derived_tables).await?;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1234
1257
|
let fresh = contents
|
|
1235
1258
|
.iter()
|
|
1236
1259
|
.map(|(id, ..)| id.clone())
|
|
@@ -1238,9 +1261,9 @@ impl PerspectiveViewerElement {
|
|
|
1238
1261
|
let results = join_all(contents.into_iter().map(|(id, session, renderer, config)| {
|
|
1239
1262
|
let presentation = this.presentation.clone();
|
|
1240
1263
|
let workspace = this.workspace.clone();
|
|
1264
|
+
let notify = this.layout_changed_notify();
|
|
1241
1265
|
async move {
|
|
1242
|
-
|
|
1243
|
-
restore_panel(
|
|
1266
|
+
let result = restore_panel(
|
|
1244
1267
|
&session,
|
|
1245
1268
|
&renderer,
|
|
1246
1269
|
&presentation,
|
|
@@ -1249,8 +1272,15 @@ impl PerspectiveViewerElement {
|
|
|
1249
1272
|
config,
|
|
1250
1273
|
crate::tasks::RestoreErrors::Publish,
|
|
1251
1274
|
missing,
|
|
1275
|
+
None,
|
|
1252
1276
|
)
|
|
1253
|
-
.await
|
|
1277
|
+
.await;
|
|
1278
|
+
|
|
1279
|
+
if discard_rejected(&workspace, &id, &session, &result).await? {
|
|
1280
|
+
notify.emit(());
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
result?;
|
|
1254
1284
|
if workspace.is_master(&id) {
|
|
1255
1285
|
set_edit_mode(&session, &renderer, "SELECT_ROW_TREE");
|
|
1256
1286
|
}
|
|
@@ -1262,11 +1292,12 @@ impl PerspectiveViewerElement {
|
|
|
1262
1292
|
|
|
1263
1293
|
results.into_iter().collect::<ApiResult<Vec<_>>>()?;
|
|
1264
1294
|
this.rerender_retained_overlays(&fresh).await?;
|
|
1265
|
-
|
|
1266
|
-
.
|
|
1267
|
-
.
|
|
1268
|
-
|
|
1295
|
+
if let Some(derived_tables) = derived_tables {
|
|
1296
|
+
let keep = derived_tables.keys().cloned().collect();
|
|
1297
|
+
crate::tasks::prune_derived(&this.workspace, &keep).await?;
|
|
1298
|
+
}
|
|
1269
1299
|
|
|
1300
|
+
ejected?;
|
|
1270
1301
|
Ok(())
|
|
1271
1302
|
});
|
|
1272
1303
|
|
|
@@ -1282,10 +1313,19 @@ impl PerspectiveViewerElement {
|
|
|
1282
1313
|
};
|
|
1283
1314
|
|
|
1284
1315
|
let reset_effect = self.workspace.effects().guard();
|
|
1285
|
-
let
|
|
1316
|
+
let reset = panel.session.submit(OpKind::Restore { update: None }, {
|
|
1317
|
+
let session = panel.session.clone();
|
|
1318
|
+
move |_ctx| {
|
|
1319
|
+
Box::pin(async move {
|
|
1320
|
+
session.reset(ResetOptions::default()).await?;
|
|
1321
|
+
Ok(None)
|
|
1322
|
+
})
|
|
1323
|
+
}
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1286
1326
|
ApiFuture::spawn(async move {
|
|
1287
1327
|
let _effect = reset_effect;
|
|
1288
|
-
|
|
1328
|
+
reset.settle().await
|
|
1289
1329
|
});
|
|
1290
1330
|
|
|
1291
1331
|
let effect = self.workspace.effects().guard();
|
|
@@ -1338,7 +1378,9 @@ impl PerspectiveViewerElement {
|
|
|
1338
1378
|
})
|
|
1339
1379
|
.await?;
|
|
1340
1380
|
|
|
1341
|
-
viewer_config
|
|
1381
|
+
viewer_config
|
|
1382
|
+
.map_table(|name| this.workspace.inline_table_ref(&name))
|
|
1383
|
+
.encode()
|
|
1342
1384
|
});
|
|
1343
1385
|
|
|
1344
1386
|
js_sys::Promise::from(fut).unchecked_into()
|
|
@@ -1402,9 +1444,14 @@ impl PerspectiveViewerElement {
|
|
|
1402
1444
|
};
|
|
1403
1445
|
|
|
1404
1446
|
let panel = this.resolve_panel(name)?;
|
|
1405
|
-
let blob =
|
|
1406
|
-
|
|
1407
|
-
|
|
1447
|
+
let blob = export_method_to_blob(
|
|
1448
|
+
&panel.session,
|
|
1449
|
+
&panel.renderer,
|
|
1450
|
+
&this.presentation,
|
|
1451
|
+
&this.workspace,
|
|
1452
|
+
method,
|
|
1453
|
+
)
|
|
1454
|
+
.await?;
|
|
1408
1455
|
let is_chart = panel.renderer.is_chart();
|
|
1409
1456
|
download(
|
|
1410
1457
|
format!("untitled{}", method.as_filename(is_chart)).as_ref(),
|
|
@@ -1450,8 +1497,14 @@ impl PerspectiveViewerElement {
|
|
|
1450
1497
|
};
|
|
1451
1498
|
|
|
1452
1499
|
let panel = this.resolve_panel(name)?;
|
|
1453
|
-
export_method_to_jsvalue(
|
|
1454
|
-
.
|
|
1500
|
+
export_method_to_jsvalue(
|
|
1501
|
+
&panel.session,
|
|
1502
|
+
&panel.renderer,
|
|
1503
|
+
&this.presentation,
|
|
1504
|
+
&this.workspace,
|
|
1505
|
+
method,
|
|
1506
|
+
)
|
|
1507
|
+
.await
|
|
1455
1508
|
})
|
|
1456
1509
|
}
|
|
1457
1510
|
|
|
@@ -1489,8 +1542,13 @@ impl PerspectiveViewerElement {
|
|
|
1489
1542
|
};
|
|
1490
1543
|
|
|
1491
1544
|
let panel = this.resolve_panel(name)?;
|
|
1492
|
-
let js_task =
|
|
1493
|
-
|
|
1545
|
+
let js_task = export_method_to_blob(
|
|
1546
|
+
&panel.session,
|
|
1547
|
+
&panel.renderer,
|
|
1548
|
+
&this.presentation,
|
|
1549
|
+
&this.workspace,
|
|
1550
|
+
method,
|
|
1551
|
+
);
|
|
1494
1552
|
copy_to_clipboard(js_task, MimeType::TextPlain).await
|
|
1495
1553
|
})
|
|
1496
1554
|
}
|
|
@@ -1506,7 +1564,9 @@ impl PerspectiveViewerElement {
|
|
|
1506
1564
|
/// # Arguments
|
|
1507
1565
|
///
|
|
1508
1566
|
/// - `reset_all` - If set, will clear expressions and column settings as
|
|
1509
|
-
/// well
|
|
1567
|
+
/// well, and tear down the element's ENTIRE derived-table state: every
|
|
1568
|
+
/// panel bound to a derived `Table` is released to its root table, and
|
|
1569
|
+
/// every derivation (pending ones included) is deleted.
|
|
1510
1570
|
/// - `options` - An optional `{panel?: string}`; the panel to reset, or
|
|
1511
1571
|
/// every panel when omitted.
|
|
1512
1572
|
///
|
|
@@ -1830,7 +1890,7 @@ impl PerspectiveViewerElement {
|
|
|
1830
1890
|
// alone must repaint nothing.
|
|
1831
1891
|
let stale = theme.as_ref().is_none_or(|x| !available.contains(x));
|
|
1832
1892
|
if (stale || theme == previous) && theme != active {
|
|
1833
|
-
panel.renderer
|
|
1893
|
+
submit_theme(&panel.session, &panel.renderer, active.clone());
|
|
1834
1894
|
if panel.renderer.needs_restyle() {
|
|
1835
1895
|
panel.renderer.restyle_all().await?;
|
|
1836
1896
|
}
|