@perspective-dev/viewer 5.3.1 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (291) hide show
  1. package/dist/cdn/perspective-viewer.js +6 -6
  2. package/dist/cdn/perspective-viewer.js.map +2 -2
  3. package/dist/css/blueprint.css +1 -1
  4. package/dist/css/botanical.css +1 -1
  5. package/dist/css/dracula.css +1 -1
  6. package/dist/css/eggplant.css +1 -1
  7. package/dist/css/gruvbox-dark.css +1 -1
  8. package/dist/css/gruvbox.css +1 -1
  9. package/dist/css/icons.css +1 -1
  10. package/dist/css/intl/ar.css +1 -0
  11. package/dist/css/intl/de.css +1 -1
  12. package/dist/css/intl/es.css +1 -1
  13. package/dist/css/intl/fr.css +1 -1
  14. package/dist/css/intl/ja.css +1 -1
  15. package/dist/css/intl/pt.css +1 -1
  16. package/dist/css/intl/zh.css +1 -1
  17. package/dist/css/intl.css +1 -1
  18. package/dist/css/ledger.css +1 -1
  19. package/dist/css/monokai.css +1 -1
  20. package/dist/css/nord.css +1 -1
  21. package/dist/css/phosphor.css +1 -1
  22. package/dist/css/pro-dark.css +1 -1
  23. package/dist/css/pro.css +1 -1
  24. package/dist/css/solarized-dark.css +1 -1
  25. package/dist/css/solarized.css +1 -1
  26. package/dist/css/themes.css +1 -1
  27. package/dist/css/vaporwave.css +1 -1
  28. package/dist/css/velvet.css +1 -1
  29. package/dist/docs/perspective-docs.json +1 -1
  30. package/dist/esm/extensions.d.ts +0 -5
  31. package/dist/esm/perspective-viewer.d.ts +2 -0
  32. package/dist/esm/perspective-viewer.inline.js +8 -8
  33. package/dist/esm/perspective-viewer.inline.js.map +2 -2
  34. package/dist/esm/perspective-viewer.js +5 -5
  35. package/dist/esm/perspective-viewer.js.map +2 -2
  36. package/dist/esm/plugin.d.ts +9 -0
  37. package/dist/esm/ts-rs/AddPanelOptions.d.ts +9 -0
  38. package/dist/esm/ts-rs/GetViewMode.d.ts +4 -0
  39. package/dist/esm/ts-rs/GetViewOptions.d.ts +8 -0
  40. package/dist/esm/ts-rs/RestoreOptions.d.ts +6 -0
  41. package/dist/esm/ts-rs/RestoreWorkspaceOptions.d.ts +9 -0
  42. package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +1 -1
  43. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +30 -22
  44. package/dist/wasm/perspective-viewer.d.ts +137 -104
  45. package/dist/wasm/perspective-viewer.js +337 -269
  46. package/dist/wasm/perspective-viewer.wasm +0 -0
  47. package/dist/wasm/perspective-viewer.wasm.d.ts +20 -30
  48. package/package.json +7 -7
  49. package/src/css/_context_menu.css +1 -1
  50. package/src/css/_dropdown_menu.css +1 -1
  51. package/src/css/_viewer_bundle.css +7 -8
  52. package/src/css/column-dropdown.css +0 -3
  53. package/src/css/column-selector.css +18 -7
  54. package/src/css/column-settings-panel.css +0 -225
  55. package/src/css/column-style.css +175 -0
  56. package/src/css/config-selector.css +19 -11
  57. package/src/css/filter-dropdown.css +5 -5
  58. package/src/css/form/debug.css +37 -4
  59. package/src/css/function-dropdown.css +5 -5
  60. package/src/css/panel-tab.css +23 -0
  61. package/src/css/plugin-selector.css +5 -5
  62. package/src/css/render-warning.css +1 -1
  63. package/src/css/status-bar.css +63 -21
  64. package/src/css/type-icon.css +1 -3
  65. package/src/css/{containers → ui/containers}/context-menu.css +22 -16
  66. package/src/css/ui/containers/dropdown-menu-items.css +66 -0
  67. package/src/css/{containers → ui/containers}/dropdown-menu.css +8 -53
  68. package/src/css/{containers → ui/containers}/tabs.css +2 -1
  69. package/src/css/viewer.css +32 -26
  70. package/src/rust/agent/tools.rs +80 -7
  71. package/src/rust/components/chat_panel.rs +1 -1
  72. package/src/rust/components/{form/code_editor.rs → code_editor.rs} +4 -2
  73. package/src/rust/components/column_dropdown.rs +2 -2
  74. package/src/rust/components/column_selector/active_column.rs +24 -9
  75. package/src/rust/components/column_selector/aggregate_selector.rs +1 -1
  76. package/src/rust/components/column_selector/config_selector.rs +2 -2
  77. package/src/rust/components/column_selector/filter_column.rs +8 -5
  78. package/src/rust/components/column_selector/inactive_column.rs +4 -2
  79. package/src/rust/components/column_selector/pivot_column.rs +4 -3
  80. package/src/rust/components/column_selector/sort_column.rs +7 -4
  81. package/src/rust/components/column_selector.rs +14 -3
  82. package/src/rust/components/column_settings_sidebar/style_tab/agg_depth_selector.rs +1 -1
  83. package/src/rust/components/column_settings_sidebar/style_tab/alignment_field.rs +90 -0
  84. package/src/rust/components/column_settings_sidebar/style_tab/font_field.rs +267 -0
  85. package/src/rust/components/column_settings_sidebar/style_tab/primitive_field.rs +16 -18
  86. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +1 -1
  87. package/src/rust/components/column_settings_sidebar/style_tab.rs +70 -12
  88. package/src/rust/components/column_settings_sidebar.rs +3 -4
  89. package/src/rust/components/copy_dropdown.rs +1 -1
  90. package/src/rust/components/datetime_column_style/custom.rs +1 -3
  91. package/src/rust/components/datetime_column_style/simple.rs +1 -2
  92. package/src/rust/components/datetime_column_style.rs +1 -2
  93. package/src/rust/components/{form/debug.rs → debug_panel.rs} +112 -55
  94. package/src/rust/components/{containers/dragdrop_list.rs → dragdrop_list.rs} +2 -10
  95. package/src/rust/components/export_dropdown.rs +1 -1
  96. package/src/rust/components/expression_editor.rs +1 -1
  97. package/src/rust/components/filter_dropdown.rs +2 -2
  98. package/src/rust/components/function_dropdown.rs +2 -2
  99. package/src/rust/components/main_panel/frame_theme.rs +3 -59
  100. package/src/rust/components/main_panel/msg.rs +4 -0
  101. package/src/rust/components/main_panel/reconcile.rs +9 -4
  102. package/src/rust/components/main_panel/render.rs +62 -11
  103. package/src/rust/components/main_panel/update.rs +39 -10
  104. package/src/rust/components/main_panel.rs +40 -2
  105. package/src/rust/components/mod.rs +5 -8
  106. package/src/rust/components/new_panel_menu.rs +97 -0
  107. package/src/rust/components/number_series_style.rs +2 -3
  108. package/src/rust/components/panel_menu.rs +61 -87
  109. package/src/rust/components/panel_tab.rs +100 -43
  110. package/src/rust/components/plugin_tab.rs +58 -4
  111. package/src/rust/components/render_warning.rs +2 -2
  112. package/src/rust/components/{status_bar_counter.rs → rows_counter.rs} +10 -10
  113. package/src/rust/components/settings_panel.rs +8 -2
  114. package/src/rust/components/status_bar.rs +113 -19
  115. package/src/rust/components/status_indicator.rs +9 -14
  116. package/src/rust/components/{style/mod.rs → style.rs} +32 -15
  117. package/src/rust/components/style_controls/number_string_format/digits_section.rs +3 -5
  118. package/src/rust/components/style_controls/number_string_format/misc_section.rs +1 -1
  119. package/src/rust/components/style_controls/number_string_format/style_section.rs +1 -1
  120. package/src/rust/components/viewer/msg.rs +43 -30
  121. package/src/rust/components/viewer/panels.rs +53 -19
  122. package/src/rust/components/viewer/render.rs +16 -13
  123. package/src/rust/components/viewer/settings.rs +137 -68
  124. package/src/rust/components/viewer/snapshots.rs +5 -10
  125. package/src/rust/components/viewer/wiring.rs +11 -22
  126. package/src/rust/components/viewer.rs +15 -10
  127. package/src/rust/components/window_editor.rs +8 -9
  128. package/src/rust/config/column_config_schema.rs +312 -33
  129. package/src/rust/config/datetime_column_style.rs +0 -11
  130. package/src/rust/config/mod.rs +0 -4
  131. package/src/rust/config/options.rs +50 -0
  132. package/src/rust/config/workspace_config.rs +70 -24
  133. package/src/rust/custom_elements/debug_plugin.rs +1 -10
  134. package/src/rust/custom_elements/mod.rs +0 -3
  135. package/src/rust/custom_elements/viewer.rs +194 -115
  136. package/src/rust/js/plugin.rs +3 -87
  137. package/src/rust/lib.rs +19 -18
  138. package/src/rust/presentation/column_locator.rs +3 -7
  139. package/src/rust/presentation.rs +68 -38
  140. package/src/rust/queries/hosted_tables.rs +38 -0
  141. package/src/rust/queries/mod.rs +2 -0
  142. package/src/rust/queries/palette_set.rs +1 -1
  143. package/src/rust/queries/plugin_column_styles.rs +6 -5
  144. package/src/rust/renderer/activate.rs +1 -3
  145. package/src/rust/renderer/dispatch.rs +83 -22
  146. package/src/rust/renderer/limits.rs +24 -5
  147. package/src/rust/renderer/plugin_config.rs +201 -41
  148. package/src/rust/renderer/plugin_store.rs +2 -1
  149. package/src/rust/renderer/props.rs +7 -1
  150. package/src/rust/renderer.rs +23 -27
  151. package/src/rust/session/props.rs +8 -3
  152. package/src/rust/session/view_subscription.rs +19 -6
  153. package/src/rust/session.rs +189 -150
  154. package/src/rust/tasks/create_panel.rs +5 -2
  155. package/src/rust/tasks/dismiss_render_warning.rs +1 -1
  156. package/src/rust/tasks/eject.rs +1 -0
  157. package/src/rust/tasks/pipeline.rs +18 -67
  158. package/src/rust/tasks/presize_panels.rs +24 -1
  159. package/src/rust/tasks/restore_and_render.rs +0 -17
  160. package/src/rust/tasks/restore_panel.rs +129 -30
  161. package/src/rust/tasks/send_column_config.rs +4 -1
  162. package/src/rust/tasks/send_plugin_config.rs +1 -1
  163. package/src/rust/tasks/set_edit_mode.rs +1 -1
  164. package/src/rust/tasks/sync_update_panels.rs +147 -67
  165. package/src/rust/tasks/table_lifecycle.rs +7 -1
  166. package/src/rust/{components → ui}/containers/control_group.rs +25 -3
  167. package/src/rust/{components → ui}/containers/dropdown_menu.rs +5 -2
  168. package/src/rust/{components → ui}/containers/mod.rs +0 -4
  169. package/src/rust/{components → ui}/containers/scroll_panel.rs +0 -22
  170. package/src/rust/{components → ui}/containers/scroll_panel_item.rs +0 -13
  171. package/src/rust/{components → ui}/containers/select.rs +0 -6
  172. package/src/rust/{components → ui}/containers/sidebar.rs +5 -32
  173. package/src/rust/{components → ui}/containers/split_panel.rs +8 -80
  174. package/src/rust/{components → ui}/containers/tab_list.rs +15 -9
  175. package/src/rust/{components → ui}/context_menu.rs +8 -13
  176. package/src/rust/{presentation/drag_helpers.rs → ui/dragdrop.rs} +26 -31
  177. package/src/rust/{components → ui}/font_loader.rs +0 -26
  178. package/src/rust/{components → ui}/form/color_selector.rs +4 -2
  179. package/src/rust/{config/datetime_column_style/color_mode.rs → ui/form/intl_label.rs} +42 -42
  180. package/src/rust/{components → ui}/form/mirrored_textarea.rs +0 -53
  181. package/src/rust/{components → ui}/form/mod.rs +1 -10
  182. package/src/rust/{components → ui}/form/multi_stop_gradient_selector.rs +4 -5
  183. package/src/rust/{components → ui}/form/named_value_picker.rs +1 -6
  184. package/src/rust/{components → ui}/form/number_field.rs +1 -1
  185. package/src/rust/{components → ui}/form/number_range_field.rs +1 -1
  186. package/src/rust/{components → ui}/form/optional_field.rs +3 -1
  187. package/src/rust/{components → ui}/form/palette_selector.rs +4 -5
  188. package/src/rust/{components → ui}/form/select_enum_field.rs +2 -2
  189. package/src/rust/{components → ui}/form/select_value_field.rs +2 -2
  190. package/src/rust/ui/mod.rs +56 -0
  191. package/src/rust/ui/modal.rs +61 -0
  192. package/src/rust/{components → ui}/portal.rs +175 -79
  193. package/src/rust/{components/style/style_provider.rs → ui/style.rs} +4 -34
  194. package/src/rust/utils/completion.rs +0 -6
  195. package/src/rust/{config/css_scale.rs → utils/css.rs} +38 -35
  196. package/src/rust/utils/css_vars.rs +13 -0
  197. package/src/rust/utils/local_fonts.rs +395 -0
  198. package/src/rust/utils/mod.rs +4 -0
  199. package/src/rust/workspace.rs +47 -0
  200. package/src/svg/boolean-type.svg +3 -2
  201. package/src/svg/checkbox-checked-icon.svg +4 -7
  202. package/src/svg/checkbox-hover.svg +3 -3
  203. package/src/svg/checkbox-off.svg +2 -3
  204. package/src/svg/checkbox-on.svg +3 -3
  205. package/src/svg/checkbox-unchecked-icon.svg +3 -3
  206. package/src/svg/datagrid-select-column.svg +8 -15
  207. package/src/svg/datagrid-select-region.svg +8 -15
  208. package/src/svg/datagrid-select-row-tree.svg +7 -12
  209. package/src/svg/datagrid-select-row.svg +8 -13
  210. package/src/svg/date-type.svg +4 -23
  211. package/src/svg/drawer-tab-hover.svg +4 -6
  212. package/src/svg/drawer-tab-invert-hover.svg +5 -5
  213. package/src/svg/drawer-tab-invert.svg +3 -5
  214. package/src/svg/drawer-tab.svg +5 -5
  215. package/src/svg/mega-menu-icons-candlestick.svg +5 -37
  216. package/src/svg/mega-menu-icons-ohlc.svg +4 -39
  217. package/src/svg/mega-menu-icons-sunburst.svg +4 -38
  218. package/src/svg/mega-menu-icons-treemap.svg +3 -43
  219. package/src/svg/mega-menu-icons-y-scatter.svg +12 -72
  220. package/src/svg/new-panel-icon.svg +4 -0
  221. package/src/svg/number-type.svg +3 -3
  222. package/src/svg/radio-hover.svg +3 -3
  223. package/src/svg/radio-off.svg +3 -3
  224. package/src/svg/radio-on.svg +3 -3
  225. package/src/svg/status_ok.svg +3 -26
  226. package/src/svg/status_pending.svg +3 -0
  227. package/src/svg/string-type.svg +4 -8
  228. package/src/svg/theme-icon.svg +5 -5
  229. package/src/themes/blueprint.css +0 -57
  230. package/src/themes/botanical.css +0 -36
  231. package/src/themes/defaults.css +1 -24
  232. package/src/themes/dracula.css +0 -58
  233. package/src/themes/eggplant.css +0 -58
  234. package/src/themes/gruvbox-dark.css +0 -65
  235. package/src/themes/gruvbox.css +1 -39
  236. package/src/themes/icons.css +2 -8
  237. package/src/themes/intl/ar.css +198 -0
  238. package/src/themes/intl/de.css +63 -40
  239. package/src/themes/intl/es.css +58 -35
  240. package/src/themes/intl/fr.css +57 -34
  241. package/src/themes/intl/ja.css +59 -36
  242. package/src/themes/intl/pt.css +54 -31
  243. package/src/themes/intl/zh.css +65 -42
  244. package/src/themes/intl.css +34 -8
  245. package/src/themes/ledger.css +1 -59
  246. package/src/themes/monokai.css +0 -51
  247. package/src/themes/nord.css +0 -55
  248. package/src/themes/phosphor.css +0 -48
  249. package/src/themes/pro-dark.css +0 -37
  250. package/src/themes/pro.css +3 -23
  251. package/src/themes/solarized-dark.css +0 -31
  252. package/src/themes/solarized.css +1 -24
  253. package/src/themes/vaporwave.css +0 -44
  254. package/src/themes/velvet.css +0 -60
  255. package/src/ts/extensions.ts +0 -13
  256. package/src/ts/perspective-viewer.ts +2 -0
  257. package/src/ts/plugin.ts +9 -0
  258. package/src/ts/ts-rs/AddPanelOptions.ts +10 -0
  259. package/src/ts/ts-rs/{DatetimeColorMode.ts → GetViewMode.ts} +4 -1
  260. package/src/ts/ts-rs/GetViewOptions.ts +7 -0
  261. package/src/ts/ts-rs/RestoreOptions.ts +7 -1
  262. package/src/ts/ts-rs/RestoreWorkspaceOptions.ts +10 -0
  263. package/src/ts/ts-rs/ViewOnUpdateResp.ts +1 -1
  264. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +31 -20
  265. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +0 -1
  266. package/dist/esm/ts-rs/FormatMode.d.ts +0 -1
  267. package/dist/esm/ts-rs/StringColorMode.d.ts +0 -1
  268. package/src/css/plugin-settings-panel.css +0 -171
  269. package/src/rust/components/modal.rs +0 -177
  270. package/src/rust/components/string_column_style.rs +0 -121
  271. package/src/rust/config/string_column_style.rs +0 -135
  272. package/src/rust/custom_elements/copy_dropdown.rs +0 -219
  273. package/src/rust/custom_elements/export_dropdown.rs +0 -216
  274. package/src/rust/custom_elements/modal.rs +0 -299
  275. package/src/ts/ts-rs/FormatMode.ts +0 -3
  276. package/src/ts/ts-rs/StringColorMode.ts +0 -3
  277. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline0.js +0 -0
  278. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline1.js +0 -0
  279. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline2.js +0 -0
  280. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline3.js +0 -0
  281. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline4.js +0 -0
  282. /package/dist/wasm/snippets/{perspective-viewer-87bd21b215d0631a → perspective-viewer-ea71a3990cad35d7}/inline5.js +0 -0
  283. /package/src/css/{containers → ui/containers}/scroll-panel.css +0 -0
  284. /package/src/css/{containers → ui/containers}/split-panel.css +0 -0
  285. /package/src/css/{dom → ui/dom}/checkbox.css +0 -0
  286. /package/src/css/{dom → ui/dom}/scrollbar.css +0 -0
  287. /package/src/css/{dom → ui/dom}/select.css +0 -0
  288. /package/src/css/{form → ui/form}/mirrored-textarea.css +0 -0
  289. /package/src/rust/components/{form → code_editor}/highlight.rs +0 -0
  290. /package/src/rust/{components → ui}/containers/sidebar_close_button.rs +0 -0
  291. /package/src/rust/{components → ui}/form/number_input.rs +0 -0
@@ -30,92 +30,6 @@ extern "C" {
30
30
  pub type JsPerspectiveViewer;
31
31
 
32
32
  /// A `<perspective-viewer>` plugin custom element.
33
- ///
34
- /// # Capability tiers
35
- ///
36
- /// Only `get_static_config`, `draw`, `save`/`restore` and `delete` are
37
- /// REQUIRED. Every other method is optional — detected once per element
38
- /// tag ([`PluginCapabilities`], methods live on the class prototype) —
39
- /// with a defined host fallback, so a minimal plugin renders correctly
40
- /// and each added method buys performance or polish, never correctness:
41
- ///
42
- /// | method | host fallback when absent |
43
- /// |------------|--------------------------------------------------------|
44
- /// | `update` | `draw` — every repaint is a full render (such plugins |
45
- /// | | must NOT treat `draw` as a state-reset license) |
46
- /// | `resize` | no-op — CSS reflows the box; next dispatch repaints |
47
- /// | `restyle` | no-op — the plugin re-reads CSS at its next render |
48
- /// | `clear` | no-op |
49
- /// | `deselect` | no-op |
50
- /// | `presize` | held style-override presize (`Renderer` resize paths) |
51
- /// | `render` | image export reports unsupported |
52
- ///
53
- /// # Call discipline
54
- ///
55
- /// Plugin implementations assume the host NEVER overlaps calls on the
56
- /// same element: each of the rendering methods (`draw`, `update`,
57
- /// `render`, `clear`, `resize`) and `delete` must run to completion
58
- /// before the next begins. Every call site must therefore hold that
59
- /// plugin's per-`Renderer` draw lock, witnessed by
60
- /// [`crate::utils::RenderGuard`] — the guard-taking dispatch helpers
61
- /// (`Renderer::draw_fresh`/`update_bound`,
62
- /// `renderer::activate::activate_plugin`) and the already-locked
63
- /// `render_task`/`update_lazy`/`resize`/`restyle_all`/`dispose`
64
- /// entrypoints do not compile from an unlocked context. Synchronous
65
- /// calls (`restore`, `restyle`, the schema queries) are exempt, though
66
- /// `restyle` should ride inside a locked restyle-then-update sequence
67
- /// (see `Renderer::restyle_all`).
68
- ///
69
- /// # Dispatch semantics (`PLUGIN_DRAW_INVARIANT_PLAN.md`, amended
70
- /// 2026-07-16)
71
- ///
72
- /// The host invokes `draw` **iff there is a NEW `View` for this
73
- /// plugin** — `bind_view` REBUILT the engine `View`, or a
74
- /// freshly-selected plugin element owes its first paint of the bound
75
- /// one — so implementations may treat `draw` as "new data shape"
76
- /// (reset zoom/scroll/domain state). The witness enforcing this is
77
- /// [`crate::session::FreshView`], which only the rebuild and
78
- /// first-paint paths mint.
79
- ///
80
- /// `update` is a repaint of the SAME `View`, invoked iff a
81
- /// plugin-visible SOURCE changed — one of exactly six: (1) data
82
- /// (`View::on_update`), (2) an adopted placeholder-config delta,
83
- /// (3) a genuinely CHANGED `plugin_config`/`columns_config` the host
84
- /// just delivered via `restore` (so `update`-after-`restore` is the
85
- /// sanctioned delivery pairing), (4) genuinely changed CSS vars just
86
- /// applied via `restyle` (`Renderer::restyle_all`, or the fused
87
- /// stale-capture restyle immediately preceding a dispatch inside
88
- /// `draw_view` — gated by `Renderer::needs_restyle`), (5) a
89
- /// render-limits change (warning dismissed), (6) an explicit PUBLIC
90
- /// element-API call that reconciled as a no-op
91
- /// ([`crate::tasks::RunOrigin::Public`] — the documented
92
- /// `viewer.restore({})` refresh affordance). An INTERNAL run that
93
- /// changed none of these dispatches nothing.
94
- ///
95
- /// `resize` is geometry/chrome only, from retained state (and is also
96
- /// the activation-chrome nudge — implementations should no-op it
97
- /// while hidden).
98
- ///
99
- /// `restore`/`save` are state transfer, NOT rendering: plugins must
100
- /// not render internally from them, and must not call host APIs from
101
- /// inside `restore` (a `restore`-triggered `restorePanel` echo is how
102
- /// the initial-load double render happened — user gestures may echo,
103
- /// host-delivered restores must not). (The built-in `Debug` plugin's
104
- /// own `resize()` delegating to its `draw()` is plugin-INTERNAL and
105
- /// exempt.)
106
- ///
107
- /// # Render-callable contract (invariant I5,
108
- /// `SESSION_CONFIG_COHERENCE_PLAN.md`)
109
- ///
110
- /// From inside `draw`/`update`/`render`/`resize`, a plugin may call
111
- /// back into its host `<perspective-viewer>` ONLY the per-panel
112
- /// snapshot getters — `getViewConfigPanel`, `getTablePanel`,
113
- /// `getClientPanel`, `getEditPortPanel` — which answer from the run's
114
- /// pinned `RenderContext`, so plugin-visible state always equals the
115
- /// snapshot being drawn, never a fresher in-flight commit.
116
- /// Lock-acquiring host methods (`savePanel`, `restorePanel`, `flush`,
117
- /// `download`/`export`/`copy`, …) DEADLOCK on the non-reentrant draw
118
- /// lock if called from a render and are forbidden.
119
33
  #[derive(Clone)]
120
34
  pub type JsPerspectiveViewerPlugin;
121
35
 
@@ -139,8 +53,10 @@ extern "C" {
139
53
  #[wasm_bindgen(method, catch, js_name = column_config_schema)]
140
54
  pub fn _column_config_schema(this: &JsPerspectiveViewerPlugin, view_type: &str, group: Option<&str>, column_name: &str, current_value: &JsValue, view_config: &JsValue, column_stats: &JsValue) -> ApiResult<JsValue>;
141
55
 
56
+ /// The plugin-level settings schema as it applies to `current_value`, the
57
+ /// `plugin_config` state the schema should describe.
142
58
  #[wasm_bindgen(method, catch, js_name = plugin_config_schema)]
143
- pub fn _plugin_config_schema(this: &JsPerspectiveViewerPlugin, view_config: &JsValue) -> ApiResult<JsValue>;
59
+ pub fn _plugin_config_schema(this: &JsPerspectiveViewerPlugin, view_config: &JsValue, current_value: &JsValue) -> ApiResult<JsValue>;
144
60
 
145
61
  /// STATE TRANSFER, not rendering (dispatch semantics, rule for
146
62
  /// `restore`/`save`): deliver a `plugin_config` + `columns_config`
package/src/rust/lib.rs CHANGED
@@ -26,9 +26,6 @@
26
26
  clippy::fallible_impl_from,
27
27
  clippy::unneeded_field_pattern
28
28
  )]
29
- // Holding a `Ref` across an `await` is the sibling footgun of the config
30
- // write-back race this crate was rearchitected against (see
31
- // `SESSION_CONFIG_COHERENCE_PLAN.md`) — deny, don't warn.
32
29
  #![deny(clippy::await_holding_refcell_ref)]
33
30
 
34
31
  #[cfg(feature = "llm-agent")]
@@ -49,6 +46,7 @@ mod session;
49
46
 
50
47
  #[doc(hidden)]
51
48
  pub mod tasks;
49
+ pub mod ui;
52
50
  pub mod utils;
53
51
  mod workspace;
54
52
 
@@ -61,9 +59,7 @@ use std::cell::RefCell;
61
59
  use perspective_js::utils::*;
62
60
  use wasm_bindgen::prelude::*;
63
61
 
64
- use crate::custom_elements::copy_dropdown::CopyDropDownMenuElement;
65
62
  use crate::custom_elements::debug_plugin::PerspectiveDebugPluginElement;
66
- use crate::custom_elements::export_dropdown::ExportDropDownMenuElement;
67
63
  use crate::custom_elements::viewer::PerspectiveViewerElement;
68
64
  use crate::utils::define_web_component;
69
65
 
@@ -71,19 +67,21 @@ use crate::utils::define_web_component;
71
67
  const TS_APPEND_CONTENT: &'static str = r#"
72
68
  import type {
73
69
  ColumnType,
74
- TableInitOptions,
75
70
  ColumnWindow,
76
- ViewWindow,
77
- TypedArrayWindow,
78
- OnUpdateOptions,
71
+ DeleteOptions,
72
+ Features,
79
73
  JoinOptions,
74
+ OnRemoveData,
75
+ OnUpdateData,
76
+ OnUpdateOptions,
77
+ Scalar,
78
+ SystemInfo,
79
+ TableInitOptions,
80
+ TypedArrayWindow,
80
81
  UpdateOptions,
81
- DeleteOptions,
82
82
  ViewConfig,
83
83
  ViewConfigUpdate,
84
- SystemInfo,
85
- Scalar,
86
- Features,
84
+ ViewWindow,
87
85
  } from "@perspective-dev/client";
88
86
 
89
87
  export type * from "../../src/ts/ts-rs/ViewerConfig.d.ts";
@@ -95,24 +93,29 @@ export type * from "../../src/ts/ts-rs/WorkspaceConfigUpdate.d.ts";
95
93
  export type * from "../../src/ts/ts-rs/ExportMethod.d.ts";
96
94
  export type * from "../../src/ts/ts-rs/PanelOptions.d.ts";
97
95
  export type * from "../../src/ts/ts-rs/RestoreOptions.d.ts";
96
+ export type * from "../../src/ts/ts-rs/RestoreWorkspaceOptions.d.ts";
97
+ export type * from "../../src/ts/ts-rs/AddPanelOptions.d.ts";
98
98
  export type * from "../../src/ts/ts-rs/SaveWorkspaceOptions.d.ts";
99
99
  export type * from "../../src/ts/ts-rs/ClientOptions.d.ts";
100
100
  export type * from "../../src/ts/ts-rs/ExportOptions.d.ts";
101
101
  export type * from "../../src/ts/ts-rs/GetTableOptions.d.ts";
102
102
  export type * from "../../src/ts/ts-rs/GetClientOptions.d.ts";
103
+ export type * from "../../src/ts/ts-rs/GetViewOptions.d.ts";
104
+ export type * from "../../src/ts/ts-rs/GetViewMode.d.ts";
103
105
  export type * from "../../src/ts/ts-rs/CustomNumberFormatConfig.d.ts";
104
106
  export type * from "../../src/ts/ts-rs/NumberFormatStyle.d.ts";
105
107
  export type * from "../../src/ts/ts-rs/Notation.d.ts";
106
108
  export type * from "../../src/ts/ts-rs/DatetimeFormatType.d.ts";
107
- export type * from "../../src/ts/ts-rs/StringColorMode.d.ts";
108
- export type * from "../../src/ts/ts-rs/DatetimeColorMode.d.ts";
109
- export type * from "../../src/ts/ts-rs/FormatMode.d.ts";
109
+
110
110
  import type {GetTableOptions} from "../../src/ts/ts-rs/GetTableOptions.d.ts";
111
111
  import type {PanelOptions} from "../../src/ts/ts-rs/PanelOptions.d.ts";
112
112
  import type {RestoreOptions} from "../../src/ts/ts-rs/RestoreOptions.d.ts";
113
+ import type {RestoreWorkspaceOptions} from "../../src/ts/ts-rs/RestoreWorkspaceOptions.d.ts";
114
+ import type {AddPanelOptions} from "../../src/ts/ts-rs/AddPanelOptions.d.ts";
113
115
  import type {SaveWorkspaceOptions} from "../../src/ts/ts-rs/SaveWorkspaceOptions.d.ts";
114
116
  import type {ExportOptions} from "../../src/ts/ts-rs/ExportOptions.d.ts";
115
117
  import type {GetClientOptions} from "../../src/ts/ts-rs/GetClientOptions.d.ts";
118
+ import type {GetViewOptions} from "../../src/ts/ts-rs/GetViewOptions.d.ts";
116
119
  import type {ClientOptions} from "../../src/ts/ts-rs/ClientOptions.d.ts";
117
120
  import type {ViewerConfig} from "../../src/ts/ts-rs/ViewerConfig.d.ts";
118
121
  import type {ViewerConfigUpdate} from "../../src/ts/ts-rs/ViewerConfigUpdate.d.ts";
@@ -177,8 +180,6 @@ pub fn js_get_worker_url() -> Result<web_sys::Url, JsValue> {
177
180
  pub fn bootstrap_web_components(psp: &JsValue) {
178
181
  define_web_component::<PerspectiveViewerElement>(psp);
179
182
  define_web_component::<PerspectiveDebugPluginElement>(psp);
180
- define_web_component::<CopyDropDownMenuElement>(psp);
181
- define_web_component::<ExportDropDownMenuElement>(psp);
182
183
  }
183
184
 
184
185
  /// Defining the web components needs an extern struct to reference the
@@ -12,6 +12,8 @@
12
12
 
13
13
  use std::fmt::Display;
14
14
 
15
+ use crate::ui::TabItem;
16
+
15
17
  /// Locates a view column.
16
18
  /// Table columns are those defined on the table, but their types will reflect
17
19
  /// the view type, not the table type.
@@ -110,10 +112,4 @@ impl OpenColumnSettings {
110
112
  }
111
113
  }
112
114
 
113
- pub trait ColumnTab: PartialEq + Display + Clone + Default + 'static {}
114
-
115
- impl ColumnTab for String {}
116
-
117
- impl ColumnTab for &'static str {}
118
-
119
- impl ColumnTab for ColumnSettingsTab {}
115
+ impl TabItem for ColumnSettingsTab {}
@@ -11,7 +11,6 @@
11
11
  // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
12
 
13
13
  mod column_locator;
14
- pub mod drag_helpers;
15
14
  mod props;
16
15
  mod sheets;
17
16
 
@@ -28,13 +27,14 @@ use yew::html::ImplicitClone;
28
27
  use yew::prelude::*;
29
28
 
30
29
  pub use self::column_locator::{
31
- ColumnLocator, ColumnSettingsTab, ColumnSettingsTarget, ColumnTab, OpenColumnSettings,
30
+ ColumnLocator, ColumnSettingsTab, ColumnSettingsTarget, OpenColumnSettings,
32
31
  };
33
- use self::drag_helpers::DragTargetState;
34
- pub use self::drag_helpers::{DragDropContainer, DragEndCallback};
35
32
  pub use self::props::{DragDropProps, PresentationProps};
36
- use crate::config::{CssKind, NamedValue, assign_palette_names};
37
- use crate::utils::*;
33
+ pub use crate::ui::{DragDropContainer, DragEndCallback};
34
+ use crate::ui::{
35
+ DragTargetState, PointerDownCallback, clear_document_selection, closest_draggable,
36
+ };
37
+ use crate::utils::{CssKind, NamedValue, assign_palette_names, *};
38
38
 
39
39
  #[derive(Clone, Debug)]
40
40
  struct DragFrom {
@@ -73,14 +73,7 @@ pub struct PresentationHandle {
73
73
  pub agent: crate::agent::AgentSlot,
74
74
 
75
75
  /// The available themes as detected in the browser environment or set
76
- /// explicitly when CORS prevents detection — a MEMO of a document
77
- /// external, not component state. `None` until first parsed (detection
78
- /// is expensive and must wait for `document.styleSheets`). Sync-readable
79
- /// (a `RefCell`, never borrowed across an await) so synchronous paths
80
- /// ("stamp with commit" — panel creation, theme mutation sites) can
81
- /// derive the registry default without awaiting registry init; derived
82
- /// values (e.g. the default theme = first registered) are computed from
83
- /// it on demand, never mirrored.
76
+ /// explicitly when CORS prevents detection.
84
77
  themes: RefCell<Option<Vec<String>>>,
85
78
 
86
79
  /// Single-flight guard for the stylesheet parse that populates
@@ -91,12 +84,6 @@ pub struct PresentationHandle {
91
84
 
92
85
  /// Whether the host's theme was ever EXPLICITLY chosen — authored as a
93
86
  /// `theme` attribute, or set by name through [`Self::set_theme_name`].
94
- ///
95
- /// `false` means the host merely displays the registry default, which
96
- /// [`Self::reset_themes`] is free to move; `true` pins the selection
97
- /// until it leaves the registry. The `theme` attribute alone cannot
98
- /// carry this, because `init` stamps it unconditionally so the document
99
- /// cascade has a theme to match.
100
87
  theme_selected: Cell<bool>,
101
88
 
102
89
  palette: RefCell<BTreeMap<String, String>>,
@@ -126,32 +113,22 @@ pub struct PresentationHandle {
126
113
  /// dragged element from the shadow tree.
127
114
  host_dragend: RefCell<Option<DragEndCallback>>,
128
115
 
116
+ /// Host-level `pointerdown` listener that clears a stale page selection
117
+ /// before it can turn a row drag into a browser selection drag.
118
+ host_pointerdown: RefCell<Option<PointerDownCallback>>,
119
+
129
120
  source_dragend: RefCell<Option<(web_sys::EventTarget, DragEndCallback)>>,
130
121
 
131
122
  /// IntersectionObserver-based fallback for the drag image, kept alive for
132
123
  /// the duration of the drag.
133
124
  drag_target: RefCell<Option<DragTargetState>>,
134
125
 
135
- /// `(open, announce)` — `announce` says this toggle is the SOLE carrier
136
- /// of the config change (a user gesture: toolbar, `toggleConfig`), so
137
- /// the element must emit `toggle-settings` + a config-update for it. An
138
- /// API restore's toggle is announced by its own view-config commit
139
- /// dispatch instead — announcing here too would double-emit, the first
140
- /// carrying the intermediate config (settings flipped, view fields not
141
- /// yet committed).
142
126
  pub settings_open_changed: PubSub<(bool, bool)>,
143
-
144
- /// Injected callback from the root component, replacing the former
145
- /// `is_workspace_changed: PubSub` field.
146
127
  pub on_is_workspace_changed: RefCell<Option<Callback<bool>>>,
147
128
  pub settings_before_open_changed: PubSub<bool>,
148
129
  pub column_settings_open_changed: PubSub<(bool, Option<String>)>,
149
130
  pub theme_config_updated: PubSub<(PtrEqRc<Vec<String>>, Option<usize>)>,
150
131
  pub on_eject: PubSub<()>,
151
-
152
- /// Fires for status-bar / main-panel pointer events that target the
153
- /// statusbar element. `wire_element_events` formats the `PointerEvent`'s
154
- /// `type_()` into a `perspective-statusbar-{type}` `CustomEvent` name.
155
132
  pub statusbar_pointer_event: PubSub<PointerEvent>,
156
133
  }
157
134
 
@@ -202,10 +179,12 @@ impl Presentation {
202
179
  on_dragstart: Default::default(),
203
180
  on_dragend: Default::default(),
204
181
  host_dragend: Default::default(),
182
+ host_pointerdown: Default::default(),
205
183
  source_dragend: Default::default(),
206
184
  drag_target: Default::default(),
207
185
  }));
208
186
 
187
+ theme.register_host_pointerdown();
209
188
  ApiFuture::spawn(theme.clone().init());
210
189
  theme
211
190
  }
@@ -577,18 +556,53 @@ impl Presentation {
577
556
  }
578
557
  }
579
558
 
580
- pub fn set_drag_image(&self, event: &DragEvent) -> ApiResult<()> {
559
+ /// Claim a `dragstart` for the column drag machinery, returning `false`
560
+ /// (cancelling the native drag) when it did not originate on a
561
+ /// `draggable="true"` row or installation failed.
562
+ pub fn set_drag_image(&self, event: &DragEvent) -> bool {
563
+ match self.try_set_drag_image(event) {
564
+ Ok(true) => true,
565
+ Ok(false) => {
566
+ event.prevent_default();
567
+ if let Err(e) = clear_document_selection() {
568
+ web_sys::console::warn_1(&e.into());
569
+ }
570
+
571
+ false
572
+ },
573
+ Err(e) => {
574
+ event.prevent_default();
575
+ web_sys::console::warn_1(&e.into());
576
+ false
577
+ },
578
+ }
579
+ }
580
+
581
+ fn try_set_drag_image(&self, event: &DragEvent) -> ApiResult<bool> {
581
582
  event.stop_propagation();
583
+ let Some(original) = closest_draggable(event) else {
584
+ return Ok(false);
585
+ };
586
+
587
+ let is_row_drag = event
588
+ .target()
589
+ .and_then(|target| target.dyn_into::<Node>().ok())
590
+ .map(|target| original.is_same_node(Some(&target)))
591
+ .unwrap_or(false);
592
+
593
+ if !is_row_drag {
594
+ return Ok(false);
595
+ }
596
+
582
597
  self.register_source_dragend(event)?;
583
598
  if let Some(dt) = event.data_transfer() {
584
599
  dt.set_drop_effect("move");
585
600
  }
586
601
 
587
- let original: HtmlElement = event.target().into_apierror()?.unchecked_into();
588
602
  let elem: HtmlElement = original
589
603
  .children()
590
604
  .get_with_index(0)
591
- .unwrap()
605
+ .into_apierror()?
592
606
  .clone_node_with_deep(true)?
593
607
  .unchecked_into();
594
608
 
@@ -612,7 +626,7 @@ impl Presentation {
612
626
  Ok(())
613
627
  });
614
628
 
615
- Ok(())
629
+ Ok(true)
616
630
  }
617
631
 
618
632
  /// Is the drag/drop state currently in `action`?
@@ -695,6 +709,22 @@ impl Presentation {
695
709
  Ok(())
696
710
  }
697
711
 
712
+ fn register_host_pointerdown(&self) {
713
+ let closure = Closure::wrap(Box::new(move |event: PointerEvent| {
714
+ if closest_draggable(&event).is_some()
715
+ && let Err(e) = clear_document_selection()
716
+ {
717
+ web_sys::console::warn_1(&e.into());
718
+ }
719
+ }) as Box<dyn FnMut(PointerEvent)>);
720
+
721
+ self.viewer_elem
722
+ .add_event_listener_with_callback("pointerdown", closure.as_ref().unchecked_ref())
723
+ .unwrap();
724
+
725
+ *self.host_pointerdown.borrow_mut() = Some(closure);
726
+ }
727
+
698
728
  fn register_host_dragend(&self) {
699
729
  if let Some(prev) = self.host_dragend.borrow_mut().take() {
700
730
  let _ = self
@@ -0,0 +1,38 @@
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 crate::workspace::Workspace;
14
+
15
+ /// Every hosted `Table` name per loaded `Client`, as `(client name, sorted
16
+ /// table names)` in registration order.
17
+ pub async fn fetch_hosted_tables(workspace: &Workspace) -> Vec<(String, Vec<String>)> {
18
+ let clients = workspace.clients();
19
+ let mut tables = Vec::with_capacity(clients.len());
20
+ for client in clients {
21
+ let mut names = match client.get_hosted_table_names().await {
22
+ Ok(names) => names,
23
+ Err(err) => {
24
+ tracing::warn!(
25
+ "Failed to list tables for `Client` \"{}\": {err}",
26
+ client.get_name()
27
+ );
28
+
29
+ vec![]
30
+ },
31
+ };
32
+
33
+ names.sort_unstable();
34
+ tables.push((client.get_name().to_owned(), names));
35
+ }
36
+
37
+ tables
38
+ }
@@ -28,6 +28,7 @@ pub mod export_app;
28
28
  mod exports;
29
29
  mod fetch_column_stats;
30
30
  mod get_viewer_config;
31
+ mod hosted_tables;
31
32
  mod is_invalid_drop;
32
33
  mod palette_set;
33
34
  mod plugin_column_styles;
@@ -40,6 +41,7 @@ pub use self::export_app::*;
40
41
  pub use self::exports::*;
41
42
  pub use self::fetch_column_stats::*;
42
43
  pub use self::get_viewer_config::*;
44
+ pub use self::hosted_tables::*;
43
45
  pub use self::is_invalid_drop::*;
44
46
  pub use self::palette_set::*;
45
47
  pub use self::plugin_column_styles::*;
@@ -12,8 +12,8 @@
12
12
 
13
13
  use std::collections::BTreeMap;
14
14
 
15
- use crate::config::{CssKind, CssLiteralUse, NamedValue, assign_palette_names};
16
15
  use crate::presentation::Presentation;
16
+ use crate::utils::{CssKind, CssLiteralUse, NamedValue, assign_palette_names};
17
17
  use crate::workspace::{PanelId, Workspace};
18
18
 
19
19
  /// Every CSS literal in use across the workspace, as `(panel id, use)`,
@@ -29,18 +29,19 @@ struct ColumnStats {
29
29
  abs_max: Option<f64>,
30
30
  }
31
31
 
32
- /// Queries the active plugin for its plugin-scoped
33
- /// [`ColumnConfigSchema`]. Mirrors [`get_column_config_schema`] but
34
- /// drops the column-specific args — the schema describes the shape
35
- /// of the plugin's own config bucket on the renderer.
32
+ /// Queries the active plugin for its plugin-scoped [`ColumnConfigSchema`] as
33
+ /// it applies to `current_value`.
36
34
  pub fn get_plugin_config_schema(
37
35
  renderer: &Renderer,
38
36
  view_config: &ViewConfig,
37
+ current_value: Option<&serde_json::Map<String, serde_json::Value>>,
39
38
  ) -> ApiResult<ColumnConfigSchema> {
40
39
  let plugin = renderer.ensure_plugin_selected()?;
41
40
  let view_config_js =
42
41
  wasm_bindgen::JsValue::from_serde_ext(view_config).unwrap_or(wasm_bindgen::JsValue::NULL);
43
- let raw = plugin._plugin_config_schema(&view_config_js)?;
42
+ let current_js = wasm_bindgen::JsValue::from_serde_ext(&current_value)
43
+ .unwrap_or(wasm_bindgen::JsValue::NULL);
44
+ let raw = plugin._plugin_config_schema(&view_config_js, &current_js)?;
44
45
  serde_wasm_bindgen::from_value::<ColumnConfigSchema>(raw)
45
46
  .map(|schema| schema.canonicalize())
46
47
  .map_err(|e| e.into())
@@ -51,9 +51,7 @@ pub fn mount_plugin(
51
51
  }
52
52
 
53
53
  /// Given an async `task` which draws `plugin`, [`mount_plugin`] it, then run
54
- /// `task`. Requires the [`RenderGuard`] lock witness — plugin dispatch
55
- /// outside a locked run does not compile (invariants I3/I6 of the merge;
56
- /// see `SESSION_CONFIG_COHERENCE_PLAN.md`).
54
+ /// `task`.
57
55
  pub async fn activate_plugin<T>(
58
56
  _guard: &RenderGuard,
59
57
  viewer: &HtmlElement,