@perspective-dev/viewer 5.1.0 → 5.3.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/dist/cdn/perspective-viewer.js +9 -9
- package/dist/cdn/perspective-viewer.js.map +4 -4
- package/dist/css/botanical.css +1 -1
- package/dist/css/dracula.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/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/monokai.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/docs/perspective-docs.json +1 -0
- package/dist/esm/column-format.d.ts +19 -28
- package/dist/esm/perspective-viewer.d.ts +3 -0
- package/dist/esm/perspective-viewer.inline.js +9 -9
- package/dist/esm/perspective-viewer.inline.js.map +4 -4
- package/dist/esm/perspective-viewer.js +9 -9
- package/dist/esm/perspective-viewer.js.map +4 -4
- package/dist/esm/plugin.d.ts +10 -0
- package/dist/esm/providers.d.ts +79 -0
- package/dist/esm/ts-rs/CompactDisplay.d.ts +5 -0
- package/dist/esm/ts-rs/CurrencyCode.d.ts +1 -0
- package/dist/esm/ts-rs/CurrencyDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/CurrencyNumberFormatStyle.d.ts +8 -0
- package/dist/esm/ts-rs/CurrencySign.d.ts +1 -0
- package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +54 -0
- package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +34 -0
- package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
- package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
- package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
- package/dist/esm/ts-rs/Notation.d.ts +17 -0
- package/dist/esm/ts-rs/NumberFormatStyle.d.ts +18 -0
- package/dist/esm/ts-rs/PanelViewerConfig.d.ts +42 -2
- package/dist/esm/ts-rs/PluginStaticConfig.d.ts +35 -0
- package/dist/esm/ts-rs/RestoreOptions.d.ts +19 -0
- package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
- package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
- package/dist/esm/ts-rs/SaveWorkspaceOptions.d.ts +10 -0
- package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
- package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +24 -0
- package/dist/esm/ts-rs/SplitRollupMode.d.ts +9 -0
- package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
- package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/Unit.d.ts +1 -0
- package/dist/esm/ts-rs/UnitDisplay.d.ts +1 -0
- package/dist/esm/ts-rs/UnitNumberFormatStyle.d.ts +6 -0
- package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
- package/dist/esm/ts-rs/ViewerConfig.d.ts +42 -2
- package/dist/esm/ts-rs/ViewerConfigInitial.d.ts +175 -0
- package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +63 -0
- package/dist/esm/ts-rs/WindowSpec.d.ts +3 -2
- package/dist/esm/ts-rs/WorkspaceConfig.d.ts +9 -1
- package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +20 -9
- package/dist/wasm/perspective-viewer.d.ts +157 -41
- package/dist/wasm/perspective-viewer.js +452 -151
- package/dist/wasm/perspective-viewer.wasm +0 -0
- package/dist/wasm/perspective-viewer.wasm.d.ts +22 -17
- package/package.json +2 -1
- package/src/css/_viewer_bundle.css +1 -0
- package/src/css/column-selector.css +8 -0
- package/src/css/column-settings-panel.css +9 -5
- package/src/css/column-style.css +209 -31
- package/src/css/config-selector.css +38 -3
- package/src/css/dom/select.css +2 -1
- package/src/css/form/code-editor.css +6 -7
- package/src/css/form/mirrored-textarea.css +81 -0
- package/src/css/viewer.css +328 -0
- package/src/rust/agent/client/mod.rs +19 -0
- package/src/rust/agent/client/protocol.rs +320 -0
- package/src/rust/agent/client/transport.rs +287 -0
- package/src/rust/agent/client/turn.rs +174 -0
- package/src/rust/agent/config.rs +159 -0
- package/src/rust/agent/docs.rs +293 -0
- package/src/rust/agent/markdown/block.rs +509 -0
- package/src/rust/agent/markdown/entity.rs +52 -0
- package/src/rust/agent/markdown/events.rs +71 -0
- package/src/rust/agent/markdown/inline.rs +497 -0
- package/src/rust/agent/markdown/mod.rs +466 -0
- package/src/rust/agent/mod.rs +23 -0
- package/src/rust/agent/preamble.md +16 -0
- package/src/rust/agent/runtime.rs +201 -0
- package/src/rust/agent/slot.rs +236 -0
- package/src/rust/agent/tools.rs +1141 -0
- package/src/rust/components/chat_panel.rs +289 -0
- package/src/rust/components/column_selector/active_column.rs +2 -2
- package/src/rust/components/column_selector/add_expression_button.rs +3 -3
- package/src/rust/components/column_selector/aggregate_selector.rs +2 -3
- package/src/rust/components/column_selector/config_selector.rs +84 -7
- package/src/rust/components/column_selector/expr_edit_button.rs +4 -10
- package/src/rust/components/column_selector/inactive_column.rs +2 -2
- package/src/rust/components/column_selector.rs +55 -4
- package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +293 -126
- package/src/rust/components/column_settings_sidebar/style_tab.rs +262 -225
- package/src/rust/components/column_settings_sidebar.rs +88 -69
- package/src/rust/components/containers/dragdrop_list.rs +37 -0
- package/src/rust/components/editable_header.rs +85 -144
- package/src/rust/components/expression_editor.rs +9 -16
- package/src/rust/components/form/code_editor.rs +29 -28
- package/src/rust/components/form/debug.rs +9 -5
- package/src/rust/components/form/mirrored_textarea.rs +165 -0
- package/src/rust/components/form/mod.rs +4 -1
- package/src/rust/components/form/multi_stop_gradient_selector.rs +476 -0
- package/src/rust/components/form/named_value_picker.rs +86 -0
- package/src/rust/components/form/palette_selector.rs +440 -0
- package/src/rust/components/main_panel/msg.rs +2 -2
- package/src/rust/components/main_panel/reconcile.rs +4 -4
- package/src/rust/components/mod.rs +2 -0
- package/src/rust/components/plugin_tab.rs +4 -28
- package/src/rust/components/settings_panel.rs +105 -22
- package/src/rust/components/viewer/msg.rs +46 -7
- package/src/rust/components/viewer/panels.rs +17 -20
- package/src/rust/components/viewer/render.rs +10 -3
- package/src/rust/components/viewer/settings.rs +163 -23
- package/src/rust/components/viewer/snapshots.rs +46 -21
- package/src/rust/components/viewer/wiring.rs +14 -25
- package/src/rust/components/viewer.rs +12 -8
- package/src/rust/components/window_editor.rs +75 -103
- package/src/rust/config/column_config_schema.rs +100 -41
- package/src/rust/config/css_scale.rs +882 -0
- package/src/rust/config/datetime_column_style/custom.rs +17 -0
- package/src/rust/config/datetime_column_style/simple.rs +10 -0
- package/src/rust/config/mod.rs +2 -0
- package/src/rust/config/number_string_format.rs +33 -0
- package/src/rust/config/options.rs +28 -0
- package/src/rust/config/plugin_static_config.rs +120 -5
- package/src/rust/config/viewer_config.rs +216 -3
- package/src/rust/config/workspace_config.rs +138 -10
- package/src/rust/custom_elements/viewer.rs +369 -119
- package/src/rust/custom_events.rs +43 -1
- package/src/rust/js/regular_layout.rs +3 -3
- package/src/rust/lib.rs +15 -0
- package/src/rust/presentation/column_locator.rs +19 -5
- package/src/rust/presentation.rs +200 -36
- package/src/rust/queries/column_locator.rs +74 -44
- package/src/rust/queries/get_viewer_config.rs +2 -1
- package/src/rust/queries/mod.rs +2 -0
- package/src/rust/queries/palette_set.rs +82 -0
- package/src/rust/queries/plugin_column_styles.rs +6 -2
- package/src/rust/renderer/dispatch.rs +16 -4
- package/src/rust/renderer/plugin_config.rs +309 -47
- package/src/rust/renderer.rs +15 -42
- package/src/rust/session/column_defaults_update.rs +32 -7
- package/src/rust/session/metadata.rs +23 -21
- package/src/rust/session/props.rs +1 -0
- package/src/rust/session/replace_expression_update.rs +1 -0
- package/src/rust/session.rs +9 -6
- package/src/rust/tasks/create_panel.rs +46 -49
- package/src/rust/tasks/edit_expression.rs +19 -23
- package/src/rust/tasks/edit_window.rs +9 -11
- package/src/rust/tasks/eject.rs +4 -1
- package/src/rust/tasks/pipeline.rs +2 -2
- package/src/rust/tasks/presize_panels.rs +44 -5
- package/src/rust/tasks/reset_all.rs +4 -4
- package/src/rust/tasks/restore_and_render.rs +17 -44
- package/src/rust/tasks/restore_panel.rs +37 -22
- package/src/rust/tasks/set_edit_mode.rs +3 -1
- package/src/rust/tasks/sync_update_panels.rs +18 -24
- package/src/rust/tasks/update_theme.rs +23 -18
- package/src/rust/utils/css_vars.rs +26 -0
- package/src/rust/utils/mod.rs +2 -0
- package/src/rust/utils/spawn.rs +1 -1
- package/src/rust/workspace.rs +454 -199
- package/src/svg/lock.svg +7 -0
- package/src/themes/botanical.css +10 -0
- package/src/themes/defaults.css +12 -0
- package/src/themes/dracula.css +17 -0
- package/src/themes/gruvbox-dark.css +20 -0
- package/src/themes/gruvbox.css +10 -0
- package/src/themes/icons.css +1 -0
- package/src/themes/intl/de.css +16 -0
- package/src/themes/intl/es.css +16 -0
- package/src/themes/intl/fr.css +16 -0
- package/src/themes/intl/ja.css +16 -0
- package/src/themes/intl/pt.css +16 -0
- package/src/themes/intl/zh.css +16 -0
- package/src/themes/intl.css +16 -0
- package/src/themes/monokai.css +22 -0
- package/src/themes/phosphor.css +5 -0
- package/src/themes/pro-dark.css +10 -0
- package/src/themes/solarized-dark.css +7 -0
- package/src/themes/solarized.css +7 -0
- package/src/themes/vaporwave.css +20 -0
- package/src/ts/column-format.ts +67 -46
- package/src/ts/perspective-viewer.ts +3 -0
- package/src/ts/plugin.ts +14 -0
- package/src/ts/providers.ts +96 -0
- package/src/ts/ts-rs/CompactDisplay.ts +3 -0
- package/src/ts/ts-rs/CurrencyCode.ts +3 -0
- package/src/ts/ts-rs/CurrencyDisplay.ts +3 -0
- package/src/ts/ts-rs/CurrencyNumberFormatStyle.ts +6 -0
- package/src/ts/ts-rs/CurrencySign.ts +3 -0
- package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +54 -0
- package/src/ts/ts-rs/CustomNumberFormatConfig.ts +25 -0
- package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
- package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
- package/src/ts/ts-rs/FormatMode.ts +3 -0
- package/src/ts/ts-rs/FormatUnit.ts +3 -0
- package/src/ts/ts-rs/Notation.ts +11 -0
- package/src/ts/ts-rs/NumberFormatStyle.ts +12 -0
- package/src/ts/ts-rs/PanelViewerConfig.ts +48 -2
- package/src/ts/ts-rs/PluginStaticConfig.ts +36 -1
- package/src/ts/ts-rs/RestoreOptions.ts +20 -0
- package/src/ts/ts-rs/RoundingMode.ts +3 -0
- package/src/ts/ts-rs/RoundingPriority.ts +3 -0
- package/src/ts/ts-rs/SaveWorkspaceOptions.ts +11 -0
- package/src/ts/ts-rs/SignDisplay.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
- package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +25 -0
- package/src/ts/ts-rs/SplitRollupMode.ts +11 -0
- package/src/ts/ts-rs/StringColorMode.ts +3 -0
- package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
- package/src/ts/ts-rs/Unit.ts +3 -0
- package/src/ts/ts-rs/UnitDisplay.ts +3 -0
- package/src/ts/ts-rs/UnitNumberFormatStyle.ts +5 -0
- package/src/ts/ts-rs/UseGrouping.ts +3 -0
- package/src/ts/ts-rs/ViewerConfig.ts +48 -2
- package/src/ts/ts-rs/ViewerConfigInitial.ts +164 -0
- package/src/ts/ts-rs/ViewerConfigUpdate.ts +72 -2
- package/src/ts/ts-rs/WindowSpec.ts +3 -2
- package/src/ts/ts-rs/WorkspaceConfig.ts +8 -2
- package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +18 -9
- package/dist/esm/ts-rs/WindowAggregate.d.ts +0 -1
- package/src/rust/components/form/color_range_selector.rs +0 -137
- package/src/ts/ts-rs/WindowAggregate.ts +0 -3
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline0.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline1.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline2.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline3.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline4.js +0 -0
- /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-39c3c3b28e3cbd48}/inline5.js +0 -0
|
@@ -285,12 +285,12 @@ pub(crate) async fn locked_run(
|
|
|
285
285
|
|
|
286
286
|
let view_config_snapshot = session.get_view_config().clone();
|
|
287
287
|
let plugin_config_changed =
|
|
288
|
-
renderer.update_plugin_config(&view_config_snapshot, spec.plugin_config)
|
|
288
|
+
renderer.update_plugin_config(&view_config_snapshot, spec.plugin_config)?;
|
|
289
289
|
let columns_config_changed = renderer.update_columns_configs(
|
|
290
290
|
&view_config_snapshot,
|
|
291
291
|
&session,
|
|
292
292
|
spec.columns_config,
|
|
293
|
-
)
|
|
293
|
+
)?;
|
|
294
294
|
|
|
295
295
|
let changed = plugin_config_changed || columns_config_changed;
|
|
296
296
|
if changed || plugin_swapped {
|
|
@@ -131,11 +131,14 @@ pub async fn presize_visible_panels_grown(
|
|
|
131
131
|
presize_visible_panels_scaled(workspace, elem, width_ratio, height_ratio).await
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
/// Pre-size each visible plugin to its SHRUNK
|
|
135
|
-
///
|
|
136
|
-
/// the
|
|
137
|
-
///
|
|
138
|
-
///
|
|
134
|
+
/// Pre-size each visible plugin to its cell SHRUNK by `(delta_w, delta_h)` —
|
|
135
|
+
/// GROWN when a delta is negative — *before* the layout change commits. Used
|
|
136
|
+
/// by the settings-pane open (P2, deltas `(layout_area.w − mpc.w,
|
|
137
|
+
/// main_column.h − mpc.h)` cached at the last close — the pane and divider
|
|
138
|
+
/// width and the docked status-bar height, both stable across a close/open
|
|
139
|
+
/// cycle since the pane width persists in the override) and by the
|
|
140
|
+
/// column-settings pin toggle and docked-drawer mount/unmount (signed delta
|
|
141
|
+
/// measured live at toggle time).
|
|
139
142
|
pub async fn presize_visible_panels_open(
|
|
140
143
|
workspace: &Workspace,
|
|
141
144
|
elem: &web_sys::HtmlElement,
|
|
@@ -199,6 +202,7 @@ async fn presize_visible_panels_scaled(
|
|
|
199
202
|
return StagedPresents::default();
|
|
200
203
|
}
|
|
201
204
|
|
|
205
|
+
let mpc = shadow_rect(elem, "#main_panel_container");
|
|
202
206
|
let mut last_chrome: Option<(f64, f64)> = None;
|
|
203
207
|
let targets = workspace
|
|
204
208
|
.panels()
|
|
@@ -222,6 +226,23 @@ async fn presize_visible_panels_scaled(
|
|
|
222
226
|
|
|
223
227
|
last_chrome = chrome.or(last_chrome);
|
|
224
228
|
let (cw, ch) = last_chrome.unwrap_or(super::CHROME_FALLBACK);
|
|
229
|
+
|
|
230
|
+
// Per axis, chrome can never exceed the container's
|
|
231
|
+
// non-plugin remainder: with no sibling panel on that axis,
|
|
232
|
+
// `mpc − box` IS the exact chrome, while the frame-margin
|
|
233
|
+
// measurement overcounts (margins that overlap container
|
|
234
|
+
// padding) — the source of a ~2px target miss that pushed
|
|
235
|
+
// every commit past the plugin transport's ±0.5px resize
|
|
236
|
+
// dedupe, costing an extra corrective render. With siblings
|
|
237
|
+
// the remainder is large and the measured chrome wins.
|
|
238
|
+
let (cw, ch) = match &mpc {
|
|
239
|
+
Some(mpc) => (
|
|
240
|
+
cw.min((mpc.width() - plugin_box.width()).max(0.0)),
|
|
241
|
+
ch.min((mpc.height() - plugin_box.height()).max(0.0)),
|
|
242
|
+
),
|
|
243
|
+
None => (cw, ch),
|
|
244
|
+
};
|
|
245
|
+
|
|
225
246
|
let w = ((plugin_box.width() + cw) * width_ratio - cw).max(0.0);
|
|
226
247
|
let h = ((plugin_box.height() + ch) * height_ratio - ch).max(0.0);
|
|
227
248
|
Some((panel, w, h))
|
|
@@ -300,6 +321,24 @@ pub fn measure_settings_open_deltas(elem: &web_sys::HtmlElement) -> Option<(f64,
|
|
|
300
321
|
))
|
|
301
322
|
}
|
|
302
323
|
|
|
324
|
+
/// The signed `#main_panel_container` width delta a column-settings pin
|
|
325
|
+
/// toggle will cause: the docked `#modal_panel`'s flex box spans its drawer
|
|
326
|
+
/// child plus the overlapping divider (`drawer.right − divider.left`, the
|
|
327
|
+
/// same span in both modes since `.pinned` reparents nothing) — PINNING
|
|
328
|
+
/// takes it from the main column (positive, shrink), UNPINNING releases it
|
|
329
|
+
/// back (negative, grow). `None` when the drawer isn't mounted.
|
|
330
|
+
pub fn measure_column_settings_pin_delta(
|
|
331
|
+
elem: &web_sys::HtmlElement,
|
|
332
|
+
is_pinned: bool,
|
|
333
|
+
) -> Option<f64> {
|
|
334
|
+
let drawer = shadow_rect(elem, "#modal_panel > .split-panel-child:first-child")?;
|
|
335
|
+
let divider_left = shadow_rect(elem, "#modal_panel > .split-panel-divider")
|
|
336
|
+
.map(|r| r.left())
|
|
337
|
+
.unwrap_or_else(|| drawer.left());
|
|
338
|
+
let width = (drawer.right() - divider_left).max(0.0);
|
|
339
|
+
Some(if is_pinned { -width } else { width })
|
|
340
|
+
}
|
|
341
|
+
|
|
303
342
|
/// The frame chrome a plugin's grid cell has that the plugin itself doesn't
|
|
304
343
|
/// occupy, as `(width, height)` px: margin + border + titlebar. Measured live
|
|
305
344
|
/// from a `regular-layout-frame` and its plugin — robust to theme/CSS changes —
|
|
@@ -61,11 +61,11 @@ pub fn reset_all(
|
|
|
61
61
|
|
|
62
62
|
presentation.reset_available_themes(None).await;
|
|
63
63
|
if all {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
renderer.set_theme(None);
|
|
64
|
+
// Put this panel back on the registry default CONCRETELY —
|
|
65
|
+
// `reset_theme` only resets the host, which an explicitly-themed
|
|
66
|
+
// panel would otherwise keep overriding.
|
|
68
67
|
presentation.reset_theme().await?;
|
|
68
|
+
renderer.set_theme(presentation.get_default_theme_name().await);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// For `all = true`, route the bucket clears through `restore_and_render`'s
|
|
@@ -18,6 +18,7 @@ use futures::Future;
|
|
|
18
18
|
use perspective_client::clone;
|
|
19
19
|
|
|
20
20
|
use super::pipeline::{RunOrigin, RunSpec, locked_run};
|
|
21
|
+
use super::update_theme::seed_panel_theme;
|
|
21
22
|
use crate::config::{OptionalUpdate, ViewerConfigUpdate};
|
|
22
23
|
use crate::presentation::Presentation;
|
|
23
24
|
use crate::renderer::Renderer;
|
|
@@ -70,11 +71,6 @@ pub fn restore_and_render(
|
|
|
70
71
|
session.set_title(None);
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
// Mirror a config-carried theme onto the host attribute (the shared
|
|
74
|
-
// chrome). The restyle a theme change requires is owned by the
|
|
75
|
-
// MUTATION SITES (`restorePanel`'s own-theme tail, the theme-picker
|
|
76
|
-
// task), not here — this run's own draw below already stamps the new
|
|
77
|
-
// effective theme before the plugin's first style read.
|
|
78
74
|
match theme_name {
|
|
79
75
|
OptionalUpdate::SetDefault => {
|
|
80
76
|
let current_name = presentation.get_selected_theme_name().await;
|
|
@@ -83,59 +79,31 @@ pub fn restore_and_render(
|
|
|
83
79
|
}
|
|
84
80
|
},
|
|
85
81
|
OptionalUpdate::Update(x) => {
|
|
86
|
-
// No pre-resolution gate: `set_theme_name` stamps the host
|
|
87
|
-
// attribute SYNCHRONOUSLY before its registry await and
|
|
88
|
-
// no-ops on literal equality itself. The old
|
|
89
|
-
// `get_selected_theme_name().await` guard was precisely
|
|
90
|
-
// the former-theme window on a cold registry — the host
|
|
91
|
-
// held the old attribute until stylesheet parsing (and
|
|
92
|
-
// everything queued behind it) resolved.
|
|
93
82
|
presentation.set_theme_name(Some(&x)).await?;
|
|
94
83
|
},
|
|
95
84
|
_ => {},
|
|
96
85
|
};
|
|
97
86
|
|
|
98
|
-
// Resolve the target plugin here (pure — needed now for
|
|
99
|
-
// `set_update_column_defaults`), but COMMIT it only inside the locked
|
|
100
|
-
// run below, atomically with the view rebind it belongs to. No swap
|
|
101
|
-
// intent may exist outside that run: an unrelated run that wins the
|
|
102
|
-
// lock first (e.g. a `table_updated` redraw) must observe either the
|
|
103
|
-
// fully-old or fully-new world, never a staged half of this one.
|
|
104
87
|
let resolved_plugin = renderer.resolve_plugin_update(&plugin);
|
|
105
88
|
if let Some((_, metadata)) = &resolved_plugin {
|
|
106
89
|
session.set_update_column_defaults(&mut view_config, metadata);
|
|
107
90
|
} else {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
session.set_update_rollup_defaults(&mut view_config, &
|
|
91
|
+
let metadata = if renderer.active_plugin().is_none() {
|
|
92
|
+
renderer
|
|
93
|
+
.resolve_plugin_update(&OptionalUpdate::SetDefault)
|
|
94
|
+
.map(|(_, metadata)| metadata)
|
|
95
|
+
.unwrap_or_else(|| renderer.metadata())
|
|
96
|
+
} else {
|
|
97
|
+
renderer.metadata()
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
session.set_update_rollup_defaults(&mut view_config, &metadata);
|
|
118
101
|
}
|
|
119
102
|
|
|
120
103
|
let plugin_idx = resolved_plugin.map(|(idx, _)| idx);
|
|
121
|
-
|
|
122
|
-
// The config COMMIT: synchronous, validated, atomic (I1/I4). Under
|
|
123
|
-
// I2/I3 committing before the lock is safe — whichever queued run
|
|
124
|
-
// snapshots next picks it up, and this run's own snapshot (taken
|
|
125
|
-
// inside the lock below) can only be this commit or fresher.
|
|
126
104
|
session.commit_view_config(view_config)?;
|
|
127
|
-
|
|
128
|
-
// Spinner accounting (RAII): held to the end of this restore —
|
|
129
|
-
// INCLUDING the deferred-draw exit (no table yet → no
|
|
130
|
-
// `bind_snapshot`), which under the old edge-counted scheme
|
|
131
|
-
// stranded the `StatusIndicator` spinner permanently.
|
|
132
105
|
let _run_token = session.begin_config_run();
|
|
133
|
-
|
|
134
|
-
// Awaits theme-registry init, so the stamp below can never observe a
|
|
135
|
-
// pre-init (empty) theme set on a cold first load. Seeds this
|
|
136
|
-
// panel's renderer default-theme cache, which every locked draw
|
|
137
|
-
// stamps the effective theme from.
|
|
138
|
-
renderer.set_default_theme(presentation.get_default_theme_name().await);
|
|
106
|
+
seed_panel_theme(&presentation, &renderer).await;
|
|
139
107
|
locked_run(&session, &renderer, RunSpec {
|
|
140
108
|
origin,
|
|
141
109
|
plugin_idx,
|
|
@@ -146,6 +114,11 @@ pub fn restore_and_render(
|
|
|
146
114
|
})
|
|
147
115
|
.await?;
|
|
148
116
|
|
|
117
|
+
if renderer.needs_restyle() {
|
|
118
|
+
renderer.restyle_all().await?;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
presentation.publish_theme_config().await?;
|
|
149
122
|
Ok(())
|
|
150
123
|
})
|
|
151
124
|
}
|
|
@@ -10,15 +10,12 @@
|
|
|
10
10
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
11
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
12
|
|
|
13
|
-
use futures::channel::oneshot::channel;
|
|
14
13
|
use perspective_client::clone;
|
|
15
14
|
use perspective_client::utils::PerspectiveResultExt;
|
|
16
15
|
|
|
17
|
-
use crate::components::viewer::{PerspectiveViewer, PerspectiveViewerMsg};
|
|
18
16
|
use crate::config::*;
|
|
19
17
|
use crate::presentation::Presentation;
|
|
20
18
|
use crate::renderer::Renderer;
|
|
21
|
-
use crate::root::Root;
|
|
22
19
|
use crate::session::{ResetOptions, Session};
|
|
23
20
|
use crate::tasks::*;
|
|
24
21
|
use crate::workspace::Workspace;
|
|
@@ -31,6 +28,16 @@ pub(crate) enum RestoreMode {
|
|
|
31
28
|
Fresh,
|
|
32
29
|
}
|
|
33
30
|
|
|
31
|
+
/// Where a failed restore's error goes.
|
|
32
|
+
#[derive(Clone, Copy)]
|
|
33
|
+
pub(crate) enum RestoreErrors {
|
|
34
|
+
// Raise errors in the UI.
|
|
35
|
+
Publish,
|
|
36
|
+
|
|
37
|
+
// Report only in the API.
|
|
38
|
+
Suppress,
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
pub(crate) async fn bind_table_task(
|
|
35
42
|
session: &Session,
|
|
36
43
|
workspace: &Workspace,
|
|
@@ -50,22 +57,26 @@ pub(crate) async fn bind_table_task(
|
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
/// Apply a [`ViewerConfigUpdate`] to a single panel and re-draw — the one
|
|
53
|
-
/// pipeline shared by `restorePanel` (an existing panel),
|
|
54
|
-
///
|
|
60
|
+
/// pipeline shared by `restorePanel` (an existing panel), `restoreWorkspace`,
|
|
61
|
+
/// and `addPanel` (both fresh panels).
|
|
55
62
|
pub(crate) async fn restore_panel(
|
|
56
63
|
session: &Session,
|
|
57
64
|
renderer: &Renderer,
|
|
58
65
|
presentation: &Presentation,
|
|
59
66
|
workspace: &Workspace,
|
|
60
|
-
root: Option<&Root<PerspectiveViewer>>,
|
|
61
67
|
mode: RestoreMode,
|
|
62
68
|
mut update: ViewerConfigUpdate,
|
|
69
|
+
errors: RestoreErrors,
|
|
63
70
|
) -> ApiResult<()> {
|
|
64
71
|
let active = matches!(mode, RestoreMode::Existing { active: true });
|
|
65
72
|
let fresh = matches!(mode, RestoreMode::Fresh);
|
|
66
73
|
match &update.theme {
|
|
67
74
|
OptionalUpdate::Update(theme) => renderer.set_theme_stamped(Some(theme.clone())),
|
|
68
|
-
|
|
75
|
+
// `SetDefault` resolves to a CONCRETE registry default here, rather
|
|
76
|
+
// than clearing the panel's theme — nothing downstream re-resolves.
|
|
77
|
+
OptionalUpdate::SetDefault => {
|
|
78
|
+
renderer.set_theme_stamped(presentation.get_default_theme_name().await)
|
|
79
|
+
},
|
|
69
80
|
OptionalUpdate::Missing => {},
|
|
70
81
|
}
|
|
71
82
|
|
|
@@ -77,17 +88,17 @@ pub(crate) async fn restore_panel(
|
|
|
77
88
|
tracing::info!("Restoring {update}");
|
|
78
89
|
}
|
|
79
90
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
// NOTE: `update.settings` is deliberately NOT applied here. It is
|
|
92
|
+
// element-level chrome rather than panel state, so `restore()` — the
|
|
93
|
+
// only caller that can carry it — applies it before dispatching, and
|
|
94
|
+
// this pipeline stays per-panel. Applying it here reached only the
|
|
95
|
+
// `Existing { active: true }` mode, which is why a freshly created
|
|
96
|
+
// panel silently ignored it.
|
|
97
|
+
|
|
98
|
+
// Under `Suppress` the restore is TRANSACTIONAL, so snapshot the config
|
|
99
|
+
// it is about to overwrite — see the failure tail below for why.
|
|
100
|
+
let rollback =
|
|
101
|
+
matches!(errors, RestoreErrors::Suppress).then(|| session.get_view_config().clone());
|
|
91
102
|
|
|
92
103
|
let table_changed = !fresh
|
|
93
104
|
&& matches!(&update.table, OptionalUpdate::Update(name)
|
|
@@ -122,7 +133,6 @@ pub(crate) async fn restore_panel(
|
|
|
122
133
|
bind_table_task(&session, &workspace, name).await?;
|
|
123
134
|
}
|
|
124
135
|
|
|
125
|
-
receiver.await.unwrap_or_log();
|
|
126
136
|
Ok(())
|
|
127
137
|
}
|
|
128
138
|
},
|
|
@@ -130,15 +140,20 @@ pub(crate) async fn restore_panel(
|
|
|
130
140
|
.await;
|
|
131
141
|
|
|
132
142
|
if let Err(e) = &result {
|
|
133
|
-
|
|
143
|
+
match errors {
|
|
144
|
+
RestoreErrors::Publish => session.set_error(false, e.clone()).await?,
|
|
145
|
+
RestoreErrors::Suppress => {
|
|
146
|
+
if let Some(config) = rollback {
|
|
147
|
+
session.commit_view_config(config.into()).unwrap_or_log();
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
}
|
|
134
151
|
}
|
|
135
152
|
|
|
136
153
|
result?;
|
|
137
154
|
|
|
138
155
|
if fresh {
|
|
139
156
|
renderer.resize().await.unwrap_or_log();
|
|
140
|
-
} else if renderer.needs_restyle() {
|
|
141
|
-
renderer.restyle_all().await?;
|
|
142
157
|
}
|
|
143
158
|
|
|
144
159
|
Ok(())
|
|
@@ -34,7 +34,9 @@ pub fn set_edit_mode(session: &Session, renderer: &Renderer, mode: &str) {
|
|
|
34
34
|
serde_json::Value::String(mode.to_owned()),
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
let changed = renderer
|
|
37
|
+
let changed = renderer
|
|
38
|
+
.update_plugin_config(&view_config, OptionalUpdate::Update(map))
|
|
39
|
+
.unwrap_or_default();
|
|
38
40
|
clone!(session, renderer);
|
|
39
41
|
ApiFuture::spawn(async move {
|
|
40
42
|
if changed {
|
|
@@ -46,8 +46,11 @@ pub fn sync_update_panels(
|
|
|
46
46
|
panels,
|
|
47
47
|
global_filters,
|
|
48
48
|
masters,
|
|
49
|
+
palette,
|
|
49
50
|
} = update.into_serde_ext()?;
|
|
50
51
|
|
|
52
|
+
let palette = validate_palette(palette.unwrap_or_default())
|
|
53
|
+
.map_err(|e| ApiError::from(JsValue::from_str(&e)))?;
|
|
51
54
|
let old_ids = this.workspace.panel_ids();
|
|
52
55
|
|
|
53
56
|
// Phase 1 — models only, NO renders and NO draws
|
|
@@ -55,20 +58,19 @@ pub fn sync_update_panels(
|
|
|
55
58
|
let mut fallback_fresh: Option<String> = None;
|
|
56
59
|
let mut active_fresh: Option<String> = None;
|
|
57
60
|
let mut contents = Vec::new();
|
|
61
|
+
// `panels` entries are `ViewerConfigInitial`s — `table` required by
|
|
62
|
+
// TYPE (a placed panel without a binding would be permanently blank),
|
|
63
|
+
// and no per-panel `settings` exists (element-level; carried by the
|
|
64
|
+
// top-level `active` field). An EMPTY `panels` map restores to the
|
|
65
|
+
// zero-panel empty stage — the former table-less fallback panel was
|
|
66
|
+
// exactly the blank-panel state this type exists to preclude.
|
|
58
67
|
for (saved_id, config) in panels {
|
|
59
|
-
if !matches!(config.settings, OptionalUpdate::Missing) {
|
|
60
|
-
#[rustfmt::skip]
|
|
61
|
-
tracing::warn!(
|
|
62
|
-
"`settings` on panel \"{saved_id}\" is ignored in a whole-element config; use the top-level `active` field"
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
68
|
let (fresh, session, renderer, config) = create_panel_model(
|
|
67
69
|
&this.elem,
|
|
68
70
|
&this.presentation,
|
|
69
71
|
&this.workspace,
|
|
70
72
|
None,
|
|
71
|
-
config,
|
|
73
|
+
config.into(),
|
|
72
74
|
None,
|
|
73
75
|
Placement::Placed,
|
|
74
76
|
);
|
|
@@ -82,21 +84,6 @@ pub fn sync_update_panels(
|
|
|
82
84
|
contents.push((fresh, session, renderer, config));
|
|
83
85
|
}
|
|
84
86
|
|
|
85
|
-
if contents.is_empty() {
|
|
86
|
-
let (fresh, session, renderer, config) = create_panel_model(
|
|
87
|
-
&this.elem,
|
|
88
|
-
&this.presentation,
|
|
89
|
-
&this.workspace,
|
|
90
|
-
None,
|
|
91
|
-
ViewerConfigUpdate::default(),
|
|
92
|
-
None,
|
|
93
|
-
Placement::Placed,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
fallback_fresh = Some(fresh.as_str().to_owned());
|
|
97
|
-
contents.push((fresh, session, renderer, config));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
87
|
let mut eject_tasks = Vec::new();
|
|
101
88
|
if let Some(panel) = this.workspace.take_reserved() {
|
|
102
89
|
eject_tasks.push(eject_panel(panel));
|
|
@@ -108,6 +95,8 @@ pub fn sync_update_panels(
|
|
|
108
95
|
}
|
|
109
96
|
}
|
|
110
97
|
|
|
98
|
+
this.presentation.set_palette(palette)?;
|
|
99
|
+
|
|
111
100
|
// Phase 2 — stage the remapped layout tree on the Workspace
|
|
112
101
|
if let Some(layout) = layout {
|
|
113
102
|
this.workspace
|
|
@@ -129,13 +118,17 @@ pub fn sync_update_panels(
|
|
|
129
118
|
}
|
|
130
119
|
|
|
131
120
|
if let Some(app) = this.root.borrow().as_ref() {
|
|
121
|
+
// Silent (`announce: false`): each restored panel's own view-config
|
|
122
|
+
// commit dispatch announces the config, settings field included.
|
|
132
123
|
app.send_message(PerspectiveViewerMsg::ToggleSettingsInit(
|
|
133
124
|
Some(SettingsUpdate::Update(sidebar_open)),
|
|
125
|
+
false,
|
|
134
126
|
None,
|
|
135
127
|
));
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
let masters = masters
|
|
131
|
+
.unwrap_or_default()
|
|
139
132
|
.into_iter()
|
|
140
133
|
.filter_map(|saved| match id_map.get(&saved) {
|
|
141
134
|
Some(fresh) => Some(PanelId::from(fresh.as_str())),
|
|
@@ -147,6 +140,7 @@ pub fn sync_update_panels(
|
|
|
147
140
|
.collect::<Vec<_>>();
|
|
148
141
|
|
|
149
142
|
this.workspace.set_masters(masters);
|
|
150
|
-
this.workspace
|
|
143
|
+
this.workspace
|
|
144
|
+
.set_global_filters(global_filters.unwrap_or_default());
|
|
151
145
|
Ok((contents, eject_tasks))
|
|
152
146
|
}
|
|
@@ -19,20 +19,21 @@ use crate::presentation::Presentation;
|
|
|
19
19
|
use crate::renderer::Renderer;
|
|
20
20
|
use crate::workspace::Workspace;
|
|
21
21
|
|
|
22
|
-
///
|
|
23
|
-
///
|
|
24
|
-
///
|
|
25
|
-
///
|
|
26
|
-
pub(crate) async fn
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
) -> Option<String> {
|
|
30
|
-
let default = presentation.get_default_theme_name().await;
|
|
31
|
-
for panel in workspace.panels() {
|
|
32
|
-
panel.renderer.set_default_theme(default.clone());
|
|
22
|
+
/// Give `renderer` the concrete registry default IF it has none yet — the
|
|
23
|
+
/// cold-boot case, where the panel was created before the registry first
|
|
24
|
+
/// parsed and `active_theme_name_sync` had nothing to resolve. Only ever
|
|
25
|
+
/// FILLS IN, never overwrites, so it cannot repaint a themed panel.
|
|
26
|
+
pub(crate) async fn seed_panel_theme(presentation: &Presentation, renderer: &Renderer) {
|
|
27
|
+
if renderer.theme().is_none() {
|
|
28
|
+
renderer.set_theme(presentation.get_default_theme_name().await);
|
|
33
29
|
}
|
|
30
|
+
}
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
/// [`seed_panel_theme`] for every panel.
|
|
33
|
+
pub(crate) async fn seed_default_themes(presentation: &Presentation, workspace: &Workspace) {
|
|
34
|
+
for panel in workspace.panels() {
|
|
35
|
+
seed_panel_theme(presentation, &panel.renderer).await;
|
|
36
|
+
}
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/// Apply a theme change and restyle the affected panel's view.
|
|
@@ -59,14 +60,17 @@ pub fn update_theme(
|
|
|
59
60
|
workspace: &Workspace,
|
|
60
61
|
theme: Option<String>,
|
|
61
62
|
) {
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
63
|
+
// A NAMED theme needs no registry, so it stamps SYNCHRONOUSLY — no await
|
|
64
|
+
// separates the caller's config commit from the attribute the document
|
|
65
|
+
// cascade styles. Only "reset to default" has to await the registry, and
|
|
66
|
+
// it records the resolved NAME, never an empty theme.
|
|
67
|
+
if let Some(name) = &theme {
|
|
68
|
+
renderer.set_theme_stamped(Some(name.clone()));
|
|
69
|
+
}
|
|
67
70
|
|
|
68
71
|
let presentation = presentation.clone();
|
|
69
72
|
let workspace = workspace.clone();
|
|
73
|
+
let renderer = renderer.clone();
|
|
70
74
|
ApiFuture::spawn(async move {
|
|
71
75
|
match theme {
|
|
72
76
|
Some(name) => {
|
|
@@ -74,10 +78,10 @@ pub fn update_theme(
|
|
|
74
78
|
},
|
|
75
79
|
None => {
|
|
76
80
|
presentation.reset_theme().await?;
|
|
81
|
+
renderer.set_theme_stamped(presentation.get_default_theme_name().await);
|
|
77
82
|
},
|
|
78
83
|
}
|
|
79
84
|
|
|
80
|
-
seed_default_themes(&presentation, &workspace).await;
|
|
81
85
|
let panels = workspace.panels();
|
|
82
86
|
join_all(panels.iter().map(|panel| async move {
|
|
83
87
|
if panel.renderer.needs_restyle() {
|
|
@@ -90,6 +94,7 @@ pub fn update_theme(
|
|
|
90
94
|
.into_iter()
|
|
91
95
|
.collect::<ApiResult<Vec<_>>>()?;
|
|
92
96
|
|
|
97
|
+
presentation.publish_theme_config().await?;
|
|
93
98
|
Ok(())
|
|
94
99
|
});
|
|
95
100
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
2
|
+
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
3
|
+
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
4
|
+
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
5
|
+
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
6
|
+
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
7
|
+
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
8
|
+
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
9
|
+
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
10
|
+
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
11
|
+
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
12
|
+
|
|
13
|
+
use web_sys::Element;
|
|
14
|
+
|
|
15
|
+
/// The computed value of custom property `name` on `elem`, trimmed, or
|
|
16
|
+
/// `None` when undefined/empty.
|
|
17
|
+
pub fn read_custom_property(elem: &Element, name: &str) -> Option<String> {
|
|
18
|
+
let value = web_sys::window()?
|
|
19
|
+
.get_computed_style(elem)
|
|
20
|
+
.ok()??
|
|
21
|
+
.get_property_value(name)
|
|
22
|
+
.ok()?;
|
|
23
|
+
|
|
24
|
+
let value = value.trim();
|
|
25
|
+
(!value.is_empty()).then(|| value.to_owned())
|
|
26
|
+
}
|
package/src/rust/utils/mod.rs
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
mod browser;
|
|
20
20
|
mod completion;
|
|
21
|
+
mod css_vars;
|
|
21
22
|
mod custom_element;
|
|
22
23
|
mod datetime;
|
|
23
24
|
mod debounce;
|
|
@@ -35,6 +36,7 @@ mod tests;
|
|
|
35
36
|
|
|
36
37
|
pub use browser::*;
|
|
37
38
|
pub use completion::*;
|
|
39
|
+
pub use css_vars::*;
|
|
38
40
|
pub use custom_element::*;
|
|
39
41
|
pub use datetime::*;
|
|
40
42
|
pub use debounce::*;
|
package/src/rust/utils/spawn.rs
CHANGED
|
@@ -23,7 +23,7 @@ use perspective_js::utils::*;
|
|
|
23
23
|
/// (invariant I6); use this only at event-listener leaves where no caller
|
|
24
24
|
/// exists.
|
|
25
25
|
pub fn spawn_owned(tag: &'static str, fut: impl Future<Output = ApiResult<()>> + 'static) {
|
|
26
|
-
ApiFuture::
|
|
26
|
+
ApiFuture::spawn_named(tag, async move {
|
|
27
27
|
if let Err(e) = fut.await.ignore_view_delete() {
|
|
28
28
|
tracing::error!("[{}] unhandled task error: {}", tag, e);
|
|
29
29
|
}
|