@perspective-dev/viewer 4.5.2 → 5.0.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 (178) hide show
  1. package/dist/cdn/perspective-viewer.js +20 -1
  2. package/dist/cdn/perspective-viewer.js.map +4 -4
  3. package/dist/css/botanical.css +1 -1
  4. package/dist/css/dracula.css +1 -1
  5. package/dist/css/gruvbox-dark.css +1 -1
  6. package/dist/css/gruvbox.css +1 -1
  7. package/dist/css/icons.css +1 -1
  8. package/dist/css/monokai.css +1 -1
  9. package/dist/css/phosphor.css +1 -1
  10. package/dist/css/pro-dark.css +1 -1
  11. package/dist/css/pro.css +1 -1
  12. package/dist/css/solarized-dark.css +1 -1
  13. package/dist/css/solarized.css +1 -1
  14. package/dist/css/themes.css +1 -1
  15. package/dist/css/vaporwave.css +1 -1
  16. package/dist/esm/extensions.d.ts +22 -3
  17. package/dist/esm/perspective-viewer.d.ts +9 -0
  18. package/dist/esm/perspective-viewer.inline.js +20 -1
  19. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  20. package/dist/esm/perspective-viewer.js +20 -1
  21. package/dist/esm/perspective-viewer.js.map +4 -4
  22. package/dist/esm/plugin.d.ts +71 -5
  23. package/dist/esm/ts-rs/ClientOptions.d.ts +7 -0
  24. package/dist/esm/ts-rs/ExportMethod.d.ts +1 -0
  25. package/dist/esm/ts-rs/ExportOptions.d.ts +9 -0
  26. package/dist/esm/ts-rs/GetClientOptions.d.ts +8 -0
  27. package/dist/esm/ts-rs/GetTableOptions.d.ts +8 -0
  28. package/dist/esm/ts-rs/Layout.d.ts +11 -0
  29. package/dist/esm/ts-rs/Orientation.d.ts +4 -0
  30. package/dist/esm/ts-rs/PanelOptions.d.ts +7 -0
  31. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +39 -0
  32. package/dist/esm/ts-rs/SplitLayout.d.ts +7 -0
  33. package/dist/esm/ts-rs/TabLayout.d.ts +10 -0
  34. package/dist/esm/ts-rs/ViewerConfig.d.ts +6 -2
  35. package/dist/esm/ts-rs/WorkspaceConfig.d.ts +40 -0
  36. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +28 -0
  37. package/dist/wasm/perspective-viewer.d.ts +211 -77
  38. package/dist/wasm/perspective-viewer.js +528 -180
  39. package/dist/wasm/perspective-viewer.wasm +0 -0
  40. package/dist/wasm/perspective-viewer.wasm.d.ts +34 -26
  41. package/dist/wasm/snippets/perspective-viewer-daeef4ceb066e6c7/inline0.js +6 -0
  42. package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline3.js → perspective-viewer-daeef4ceb066e6c7/inline4.js} +5 -0
  43. package/package.json +3 -2
  44. package/src/css/_context_menu.css +14 -0
  45. package/src/css/column-settings-panel.css +1 -1
  46. package/src/css/containers/context-menu.css +101 -0
  47. package/src/css/panel-tab.css +193 -0
  48. package/src/css/plugin-selector.css +7 -10
  49. package/src/css/status-bar.css +75 -81
  50. package/src/css/viewer.css +180 -70
  51. package/src/rust/components/column_dropdown.rs +40 -30
  52. package/src/rust/components/column_selector/active_column.rs +7 -18
  53. package/src/rust/components/column_selector/aggregate_selector.rs +4 -7
  54. package/src/rust/components/column_selector/config_selector.rs +33 -81
  55. package/src/rust/components/column_selector/filter_column.rs +5 -15
  56. package/src/rust/components/column_selector/inactive_column.rs +3 -8
  57. package/src/rust/components/column_selector/sort_column.rs +3 -6
  58. package/src/rust/components/column_selector.rs +5 -11
  59. package/src/rust/components/containers/split_panel.rs +175 -62
  60. package/src/rust/components/context_menu.rs +130 -0
  61. package/src/rust/components/form/debug.rs +3 -0
  62. package/src/rust/components/global_filter_bar.rs +76 -0
  63. package/src/rust/components/main_panel/frame_theme.rs +186 -0
  64. package/src/rust/components/main_panel/msg.rs +62 -0
  65. package/src/rust/components/main_panel/presize.rs +220 -0
  66. package/src/rust/components/main_panel/reconcile.rs +224 -0
  67. package/src/rust/components/main_panel/render.rs +385 -0
  68. package/src/rust/components/main_panel/update.rs +184 -0
  69. package/src/rust/components/main_panel.rs +311 -71
  70. package/src/rust/components/mod.rs +4 -0
  71. package/src/rust/components/panel_menu.rs +486 -0
  72. package/src/rust/components/panel_tab.rs +505 -0
  73. package/src/rust/components/plugin_tab.rs +1 -1
  74. package/src/rust/components/settings_panel.rs +13 -5
  75. package/src/rust/components/status_bar.rs +110 -178
  76. package/src/rust/components/status_indicator.rs +6 -9
  77. package/src/rust/components/style/mod.rs +5 -0
  78. package/src/rust/components/style/style_provider.rs +2 -0
  79. package/src/rust/components/viewer/filters.rs +133 -0
  80. package/src/rust/components/viewer/msg.rs +180 -0
  81. package/src/rust/components/viewer/panels.rs +326 -0
  82. package/src/rust/components/viewer/render.rs +243 -0
  83. package/src/rust/components/viewer/settings.rs +402 -0
  84. package/src/rust/components/viewer/snapshots.rs +134 -0
  85. package/src/rust/components/viewer/wiring.rs +505 -0
  86. package/src/rust/components/viewer.rs +243 -756
  87. package/src/rust/config/export_method.rs +1 -1
  88. package/src/rust/config/mod.rs +4 -0
  89. package/src/rust/config/options.rs +71 -0
  90. package/src/rust/config/viewer_config.rs +24 -2
  91. package/src/rust/config/workspace_config.rs +92 -0
  92. package/src/rust/custom_elements/copy_dropdown.rs +3 -1
  93. package/src/rust/custom_elements/debug_plugin.rs +20 -1
  94. package/src/rust/custom_elements/export_dropdown.rs +3 -1
  95. package/src/rust/custom_elements/viewer.rs +1015 -286
  96. package/src/rust/custom_events.rs +212 -61
  97. package/src/rust/js/mod.rs +2 -0
  98. package/src/rust/js/plugin.rs +297 -10
  99. package/src/rust/js/regular_layout.rs +258 -0
  100. package/src/rust/lib.rs +20 -1
  101. package/src/rust/presentation.rs +66 -33
  102. package/src/rust/queries/get_viewer_config.rs +29 -11
  103. package/src/rust/queries/plugin_column_styles.rs +2 -2
  104. package/src/rust/renderer/activate.rs +41 -14
  105. package/src/rust/renderer/dispatch.rs +436 -0
  106. package/src/rust/renderer/plugin_config.rs +495 -0
  107. package/src/rust/renderer/registry.rs +18 -6
  108. package/src/rust/renderer/render_timer.rs +29 -10
  109. package/src/rust/renderer.rs +350 -837
  110. package/src/rust/session/column_defaults_update.rs +26 -8
  111. package/src/rust/session/view_subscription.rs +30 -15
  112. package/src/rust/session.rs +753 -264
  113. package/src/rust/tasks/apply_global_filters.rs +89 -0
  114. package/src/rust/tasks/auto_pause.rs +171 -0
  115. package/src/rust/tasks/copy_export.rs +29 -19
  116. package/src/rust/tasks/create_panel.rs +243 -0
  117. package/src/rust/tasks/dismiss_render_warning.rs +3 -1
  118. package/src/rust/tasks/edit_expression.rs +4 -4
  119. package/src/rust/tasks/eject.rs +64 -23
  120. package/src/rust/tasks/mod.rs +28 -4
  121. package/src/rust/tasks/pipeline.rs +411 -0
  122. package/src/rust/tasks/presize_panels.rs +324 -0
  123. package/src/rust/tasks/reset_all.rs +22 -12
  124. package/src/rust/tasks/resize_observer.rs +124 -87
  125. package/src/rust/tasks/restore_and_render.rs +80 -86
  126. package/src/rust/tasks/restore_panel.rs +145 -0
  127. package/src/rust/tasks/send_column_config.rs +5 -2
  128. package/src/rust/tasks/send_plugin_config.rs +5 -2
  129. package/src/rust/tasks/set_edit_mode.rs +59 -0
  130. package/src/rust/tasks/sync_update_panels.rs +152 -0
  131. package/src/rust/tasks/table_lifecycle.rs +158 -0
  132. package/src/rust/tasks/update_theme.rs +56 -12
  133. package/src/rust/utils/completion.rs +56 -0
  134. package/src/rust/utils/custom_element.rs +5 -0
  135. package/src/rust/utils/debounce.rs +130 -7
  136. package/src/rust/utils/mod.rs +6 -0
  137. package/src/rust/utils/pending_effects.rs +62 -0
  138. package/src/rust/utils/spawn.rs +33 -0
  139. package/src/rust/utils/weak_scope.rs +0 -10
  140. package/src/rust/workspace.rs +846 -0
  141. package/src/svg/active-indicator.svg +3 -0
  142. package/src/themes/botanical.css +5 -2
  143. package/src/themes/defaults.css +54 -2
  144. package/src/themes/dracula.css +6 -3
  145. package/src/themes/gruvbox-dark.css +5 -2
  146. package/src/themes/gruvbox.css +5 -2
  147. package/src/themes/icons.css +4 -2
  148. package/src/themes/monokai.css +5 -2
  149. package/src/themes/phosphor.css +5 -2
  150. package/src/themes/pro-dark.css +5 -2
  151. package/src/themes/pro.css +2 -1
  152. package/src/themes/solarized-dark.css +5 -2
  153. package/src/themes/solarized.css +5 -2
  154. package/src/themes/vaporwave.css +5 -2
  155. package/src/ts/bootstrap.ts +8 -7
  156. package/src/ts/extensions.ts +30 -2
  157. package/src/ts/perspective-viewer.ts +10 -0
  158. package/src/ts/plugin.ts +72 -5
  159. package/src/ts/ts-rs/ClientOptions.ts +7 -0
  160. package/src/ts/ts-rs/ExportMethod.ts +3 -0
  161. package/src/ts/ts-rs/ExportOptions.ts +8 -0
  162. package/src/ts/ts-rs/GetClientOptions.ts +7 -0
  163. package/src/ts/ts-rs/GetTableOptions.ts +7 -0
  164. package/src/ts/ts-rs/Layout.ts +9 -0
  165. package/src/ts/ts-rs/Orientation.ts +6 -0
  166. package/src/ts/ts-rs/PanelOptions.ts +7 -0
  167. package/src/ts/ts-rs/PanelViewerConfig.ts +15 -0
  168. package/src/ts/ts-rs/SplitLayout.ts +5 -0
  169. package/src/ts/ts-rs/TabLayout.ts +11 -0
  170. package/src/ts/ts-rs/ViewerConfig.ts +6 -2
  171. package/src/ts/ts-rs/WorkspaceConfig.ts +36 -0
  172. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +24 -0
  173. package/src/rust/tasks/intersection_observer.rs +0 -98
  174. package/src/rust/tasks/update_and_render.rs +0 -110
  175. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline0.js → perspective-viewer-daeef4ceb066e6c7/inline1.js} +0 -0
  176. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline1.js → perspective-viewer-daeef4ceb066e6c7/inline2.js} +0 -0
  177. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline2.js → perspective-viewer-daeef4ceb066e6c7/inline3.js} +0 -0
  178. /package/dist/wasm/snippets/{perspective-viewer-93064d0d79855d97/inline4.js → perspective-viewer-daeef4ceb066e6c7/inline5.js} +0 -0
@@ -0,0 +1,3 @@
1
+ <svg width="12" height="12" viewBox="0 -0.5 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M15.1593 1.64958L10.3424 7.71878V7.81838L18 6.84731V10.1465L10.3424 9.27499V9.37459L15.2113 15.201L12.0195 16.9502L8.97725 10.1527H8.87974L5.58397 17L2.69772 15.2508L7.50813 9.33101V9.23142L0 10.1402L0 6.84108L7.44962 7.76858V7.66899L2.68472 1.74918L5.77898 0.0497986L8.91874 6.79751H9.02275L12.117 0L15.1593 1.64958Z" fill="#042121"/>
3
+ </svg>
@@ -14,11 +14,13 @@
14
14
 
15
15
  /* Register theme for auto-detection */
16
16
  perspective-viewer,
17
- perspective-viewer[theme="Botanical"] {
17
+ perspective-viewer[theme="Botanical"],
18
+ perspective-viewer [theme="Botanical"] {
18
19
  --psp-theme-name: "Botanical";
19
20
  }
20
21
 
21
- perspective-viewer[theme="Botanical"] {
22
+ perspective-viewer[theme="Botanical"],
23
+ perspective-viewer [theme="Botanical"] {
22
24
  /* perspective-viewer-botanical--colors */
23
25
  background-color: #1a2e1a;
24
26
  color: #e0ead8;
@@ -90,6 +92,7 @@ perspective-viewer[theme="Botanical"] {
90
92
  --psp-charts--map-tiles--attribution-: "I <3 Open Source";
91
93
  }
92
94
 
95
+ perspective-context-menu[theme="Botanical"],
93
96
  perspective-copy-menu[theme="Botanical"],
94
97
  perspective-export-menu[theme="Botanical"],
95
98
  perspective-dropdown[theme="Botanical"],
@@ -14,7 +14,21 @@
14
14
  @import "./intl.css";
15
15
 
16
16
  perspective-viewer,
17
- perspective-workspace,
17
+ /* Per-panel light-DOM descendants (the plugin + `<PanelTab>`, both carrying a
18
+ `theme` attribute) get the complete base too — so each panel resolves real
19
+ `color`/`font-family` and every `--psp-*` from its OWN theme instead of
20
+ inheriting the active panel's. Theme overrides refine this via the matching
21
+ `perspective-viewer [theme="X"]` rule.
22
+
23
+ `:where()` gives this clause ZERO specificity, so the panel's own
24
+ `[theme="X"]` override (specificity 0,1,1) ALWAYS wins over this default base.
25
+ Without it the two would tie at (0,1,1) and import order would decide — and
26
+ because every theme file `@import`s `defaults.css`, the base is inlined after
27
+ later overrides and the default would leak over the panel's theme. A
28
+ zero-specificity rule still beats inheritance, so the base values apply where
29
+ the override is silent. */
30
+ :where(perspective-viewer [theme]),
31
+ perspective-context-menu,
18
32
  perspective-copy-menu,
19
33
  perspective-export-menu,
20
34
  perspective-dropdown,
@@ -26,7 +40,6 @@ perspective-string-column-style {
26
40
  --psp--color: #161616;
27
41
 
28
42
  color: var(--psp--color);
29
- background-color: transparent;
30
43
 
31
44
  --psp-inactive--color: #ababab;
32
45
  --psp-inactive--border-color: #dadada;
@@ -127,9 +140,48 @@ perspective-string-column-style {
127
140
  #42b3d5 67.5%,
128
141
  #1a237e 100%
129
142
  );
143
+
144
+ /* Completeness anchors for per-panel theming. These are color/appearance
145
+ vars that some themes (e.g. Pro Dark) set but the light default does not
146
+ — so without a base value an unthemed/light panel would *inherit* them
147
+ from a differently-themed (active) panel, mixing themes. `initial` makes
148
+ the custom property the guaranteed-invalid value: consumers fall back to
149
+ their own default (identical to the current unthemed rendering) AND the
150
+ element no longer inherits a neighboring panel's value. A theme that
151
+ wants a real value overrides it in its `[theme="X"]` block as usual. */
152
+ --psp-active--background: initial;
153
+ --psp-calendar--filter: initial;
154
+ --psp-charts--legend--color: initial;
155
+ --psp-charts--tooltip--background: initial;
156
+ --psp-charts--treemap--hover-highlight: initial;
157
+ --psp-charts--treemap--labels: initial;
158
+ --psp-code-editor--column--color: initial;
159
+ --psp-code-editor--comment--color: initial;
160
+ --psp-code-editor--literal--color: initial;
161
+ --psp-code-editor--operator--color: initial;
162
+ --psp-code-editor--symbol--color: initial;
163
+ --psp-expression--error--color: initial;
164
+ --psp-expression--function--color: initial;
165
+ --psp-expression--operator--color: initial;
166
+ --psp-input--filter: initial;
167
+ --psp-openlayers--attribution--filter: initial;
168
+ --psp-charts--series-11--color: initial;
169
+ --psp-charts--series-12--color: initial;
170
+ --psp-charts--map-tiles--url: initial;
171
+ --psp-column-type--boolean--color: initial;
172
+ --psp-column-type--date--color: initial;
173
+ --psp-column-type--float--color: initial;
174
+ --psp-column-type--string--color: initial;
175
+ --psp-datagrid--border-color: initial;
176
+ --psp-datagrid--hover--border-color: initial;
177
+ --psp-expression--psp-error--color: initial;
178
+ --psp-status-icon--connected--color: initial;
179
+ --psp-status-icon--initializing--color: initial;
180
+ --psp-status-icon--psp-error--color: initial;
130
181
  }
131
182
 
132
183
  /* Modal defaults */
184
+ perspective-context-menu,
133
185
  perspective-copy-menu,
134
186
  perspective-export-menu,
135
187
  perspective-dropdown,
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Dracula"] {
16
+ perspective-viewer[theme="Dracula"],
17
+ perspective-viewer [theme="Dracula"] {
17
18
  --psp-theme-name: "Dracula";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Dracula"] {
21
+ perspective-viewer[theme="Dracula"],
22
+ perspective-viewer [theme="Dracula"] {
21
23
  /* perspective-viewer-pro-dark--colors (overrides) */
22
24
  background-color: #242526;
23
25
  color: white;
@@ -122,7 +124,7 @@ perspective-viewer[theme="Dracula"] {
122
124
  --theme-pink: #ff79c6;
123
125
 
124
126
  color: var(--theme-fg0);
125
- background-color: var(--theme-bg0);
127
+ background-color: var(--theme-bg1);
126
128
  --psp--color: var(--theme-fg1);
127
129
  --psp-inactive--color: var(--theme-fg2);
128
130
  --psp-inactive--border-color: var(--theme-bg2);
@@ -154,6 +156,7 @@ perspective-viewer[theme="Dracula"] {
154
156
  ) !important;
155
157
  }
156
158
 
159
+ perspective-context-menu[theme="Dracula"],
157
160
  perspective-copy-menu[theme="Dracula"],
158
161
  perspective-export-menu[theme="Dracula"],
159
162
  perspective-dropdown[theme="Dracula"] {
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Gruvbox Dark"] {
16
+ perspective-viewer[theme="Gruvbox Dark"],
17
+ perspective-viewer [theme="Gruvbox Dark"] {
17
18
  --psp-theme-name: "Gruvbox Dark";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Gruvbox Dark"] {
21
+ perspective-viewer[theme="Gruvbox Dark"],
22
+ perspective-viewer [theme="Gruvbox Dark"] {
21
23
  /* perspective-viewer-pro-dark--colors (overrides) */
22
24
  background-color: #242526;
23
25
  color: white;
@@ -167,6 +169,7 @@ perspective-viewer[theme="Gruvbox Dark"] {
167
169
  ) !important;
168
170
  }
169
171
 
172
+ perspective-context-menu[theme="Gruvbox Dark"],
170
173
  perspective-copy-menu[theme="Gruvbox Dark"],
171
174
  perspective-export-menu[theme="Gruvbox Dark"],
172
175
  perspective-dropdown[theme="Gruvbox Dark"] {
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Gruvbox Light"] {
16
+ perspective-viewer[theme="Gruvbox Light"],
17
+ perspective-viewer [theme="Gruvbox Light"] {
17
18
  --psp-theme-name: "Gruvbox Light";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Gruvbox Light"] {
21
+ perspective-viewer[theme="Gruvbox Light"],
22
+ perspective-viewer [theme="Gruvbox Light"] {
21
23
  /* perspective-viewer-gruvbox-light--colors */
22
24
  --theme-fg0: #282828;
23
25
  --theme-fg1: #3c3836;
@@ -83,6 +85,7 @@ perspective-viewer[theme="Gruvbox Light"] {
83
85
  ) !important;
84
86
  }
85
87
 
88
+ perspective-context-menu[theme="Gruvbox Light"],
86
89
  perspective-copy-menu[theme="Gruvbox Light"],
87
90
  perspective-export-menu[theme="Gruvbox Light"],
88
91
  perspective-dropdown[theme="Gruvbox Light"] {
@@ -11,14 +11,15 @@
11
11
  * ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ */
12
12
 
13
13
  perspective-viewer,
14
- perspective-workspace,
14
+ perspective-context-menu,
15
15
  perspective-copy-menu,
16
16
  perspective-export-menu,
17
17
  perspective-dropdown,
18
18
  perspective-date-column-style,
19
19
  perspective-datetime-column-style,
20
20
  perspective-number-column-style,
21
- perspective-string-column-style {
21
+ perspective-string-column-style,
22
+ perspective-viewer-tab {
22
23
  /* Icons */
23
24
  --psp-column-type--integer--mask-image: url("../svg/number-type.svg");
24
25
  --psp-column-type--float--mask-image: var(
@@ -55,6 +56,7 @@ perspective-string-column-style {
55
56
  --psp-label--active-column-selector--content: url("../svg/checkbox-checked-icon.svg");
56
57
  --psp-icon--select-arrow-light--mask-image: url("../svg/dropdown-selector-light.svg");
57
58
  --psp-icon--select-arrow-dark--mask-image: url("../svg/dropdown-selector.svg");
59
+ --psp-icon--active-indicator--mask-image: url("../svg/active-indicator.svg");
58
60
  --psp-icon-overflow-hint--content: "!";
59
61
  --psp-label--reset-button-icon--content: "refresh";
60
62
  --psp-label--save-button-icon--content: "save";
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Monokai"] {
16
+ perspective-viewer[theme="Monokai"],
17
+ perspective-viewer [theme="Monokai"] {
17
18
  --psp-theme-name: "Monokai";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Monokai"] {
21
+ perspective-viewer[theme="Monokai"],
22
+ perspective-viewer [theme="Monokai"] {
21
23
  /* perspective-viewer-pro-dark--colors (overrides) */
22
24
  background-color: #242526;
23
25
  color: white;
@@ -163,6 +165,7 @@ perspective-viewer[theme="Monokai"] {
163
165
  );
164
166
  }
165
167
 
168
+ perspective-context-menu[theme="Monokai"],
166
169
  perspective-copy-menu[theme="Monokai"],
167
170
  perspective-export-menu[theme="Monokai"],
168
171
  perspective-dropdown[theme="Monokai"],
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Phosphor"] {
16
+ perspective-viewer[theme="Phosphor"],
17
+ perspective-viewer [theme="Phosphor"] {
17
18
  --psp-theme-name: "Phosphor";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Phosphor"] {
21
+ perspective-viewer[theme="Phosphor"],
22
+ perspective-viewer [theme="Phosphor"] {
21
23
  /* perspective-viewer-phosphor--colors */
22
24
  --theme-bg0: #0a0a0a;
23
25
  --theme-bg1: #0d0d0d;
@@ -124,6 +126,7 @@ perspective-viewer[theme="Phosphor"] {
124
126
  );
125
127
  }
126
128
 
129
+ perspective-context-menu[theme="Phosphor"],
127
130
  perspective-copy-menu[theme="Phosphor"],
128
131
  perspective-export-menu[theme="Phosphor"],
129
132
  perspective-dropdown[theme="Phosphor"],
@@ -14,11 +14,13 @@
14
14
 
15
15
  /* Register theme for auto-detection */
16
16
  perspective-viewer,
17
- perspective-viewer[theme="Pro Dark"] {
17
+ perspective-viewer[theme="Pro Dark"],
18
+ perspective-viewer [theme="Pro Dark"] {
18
19
  --psp-theme-name: "Pro Dark";
19
20
  }
20
21
 
21
- perspective-viewer[theme="Pro Dark"] {
22
+ perspective-viewer[theme="Pro Dark"],
23
+ perspective-viewer [theme="Pro Dark"] {
22
24
  /* perspective-viewer-pro-dark--colors (overrides) */
23
25
  background-color: #242526;
24
26
  color: white;
@@ -107,6 +109,7 @@ perspective-viewer[theme="Pro Dark"] {
107
109
  );
108
110
  }
109
111
 
112
+ perspective-context-menu[theme="Pro Dark"],
110
113
  perspective-copy-menu[theme="Pro Dark"],
111
114
  perspective-export-menu[theme="Pro Dark"],
112
115
  perspective-dropdown[theme="Pro Dark"],
@@ -13,6 +13,7 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Pro Light"] {
16
+ perspective-viewer[theme="Pro Light"],
17
+ perspective-viewer [theme="Pro Light"] {
17
18
  --psp-theme-name: "Pro Light";
18
19
  }
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Solarized Dark"] {
16
+ perspective-viewer[theme="Solarized Dark"],
17
+ perspective-viewer [theme="Solarized Dark"] {
17
18
  --psp-theme-name: "Solarized Dark";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Solarized Dark"] {
21
+ perspective-viewer[theme="Solarized Dark"],
22
+ perspective-viewer [theme="Solarized Dark"] {
21
23
  /* perspective-viewer-solarized--colors */
22
24
  color: #586e75;
23
25
  background-color: #eee8d5;
@@ -82,6 +84,7 @@ perspective-viewer[theme="Solarized Dark"] {
82
84
  );
83
85
  }
84
86
 
87
+ perspective-context-menu[theme="Solarized Dark"],
85
88
  perspective-copy-menu[theme="Solarized Dark"],
86
89
  perspective-export-menu[theme="Solarized Dark"],
87
90
  perspective-dropdown[theme="Solarized Dark"],
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Solarized"] {
16
+ perspective-viewer[theme="Solarized"],
17
+ perspective-viewer [theme="Solarized"] {
17
18
  --psp-theme-name: "Solarized";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Solarized"] {
21
+ perspective-viewer[theme="Solarized"],
22
+ perspective-viewer [theme="Solarized"] {
21
23
  /* perspective-viewer-solarized--colors */
22
24
  color: #586e75;
23
25
  background-color: #eee8d5;
@@ -61,6 +63,7 @@ x {
61
63
  color: #cb4b16;
62
64
  }
63
65
 
66
+ perspective-context-menu[theme="Solarized"],
64
67
  perspective-copy-menu[theme="Solarized"],
65
68
  perspective-export-menu[theme="Solarized"],
66
69
  perspective-dropdown[theme="Solarized"],
@@ -13,11 +13,13 @@
13
13
  @import "./defaults.css";
14
14
 
15
15
  perspective-viewer,
16
- perspective-viewer[theme="Vaporwave"] {
16
+ perspective-viewer[theme="Vaporwave"],
17
+ perspective-viewer [theme="Vaporwave"] {
17
18
  --psp-theme-name: "Vaporwave";
18
19
  }
19
20
 
20
- perspective-viewer[theme="Vaporwave"] {
21
+ perspective-viewer[theme="Vaporwave"],
22
+ perspective-viewer [theme="Vaporwave"] {
21
23
  /* perspective-viewer-pro-dark--colors (overrides) */
22
24
  background-color: #242526;
23
25
  color: white;
@@ -175,6 +177,7 @@ perspective-viewer[theme="Vaporwave"] {
175
177
  ) !important;
176
178
  }
177
179
 
180
+ perspective-context-menu[theme="Vaporwave"],
178
181
  perspective-copy-menu[theme="Vaporwave"],
179
182
  perspective-export-menu[theme="Vaporwave"],
180
183
  perspective-dropdown[theme="Vaporwave"],
@@ -26,12 +26,13 @@ export async function init_client(
26
26
  ) {
27
27
  // @ts-ignore
28
28
  const url = await worker_url.getWorkerURL();
29
- if (wasm_module === undefined) {
30
- wasm_module = (await import(url)) as typeof wasm_module_type;
31
- }
29
+ const [wasm_module2, module_or_path] = await Promise.all([
30
+ wasm_module === undefined ? import(url) : wasm_module,
31
+ load_wasm_stage_0(wasm_binary).then((unzipped) =>
32
+ WebAssembly.compile(unzipped as BufferSource),
33
+ ),
34
+ ]);
32
35
 
33
- const unzipped = await load_wasm_stage_0(wasm_binary);
34
- const module_or_path = await WebAssembly.compile(unzipped as BufferSource);
35
- await wasm_module.default({ module_or_path });
36
- await wasm_module.init(module_or_path, url);
36
+ await wasm_module2.default({ module_or_path });
37
+ await wasm_module2.init(module_or_path, url);
37
38
  }
@@ -26,18 +26,21 @@ export class PerspectiveSelectDetail {
26
26
  column_names?: string[];
27
27
  removeConfigs: ViewConfigUpdate[];
28
28
  insertConfigs: ViewConfigUpdate[];
29
+ panel?: string;
29
30
  constructor(
30
31
  selected: boolean,
31
32
  row: Record<string, unknown>,
32
33
  column_names: string[],
33
34
  removeConfigs: ViewConfigUpdate[],
34
35
  insertConfigs: ViewConfigUpdate[],
36
+ panel?: string,
35
37
  ) {
36
38
  this.selected = selected;
37
39
  this.row = row;
38
40
  this.column_names = column_names;
39
41
  this.removeConfigs = removeConfigs;
40
42
  this.insertConfigs = insertConfigs;
43
+ this.panel = panel;
41
44
  }
42
45
 
43
46
  get removeFilters(): Filter[] {
@@ -72,12 +75,27 @@ export type PerspectiveClickEventDetail = {
72
75
  config: ViewerConfigUpdate;
73
76
  row: Record<string, any>;
74
77
  column_names: Array<string | Array<string>>;
78
+ panel?: string;
75
79
  };
76
80
 
77
81
  export type PerspectiveSelectEventDetail = {
78
82
  view_window: ViewWindow;
79
83
  };
80
84
 
85
+ export type PerspectiveConfigUpdateEventDetail = {
86
+ /**
87
+ * Serialize the panel config that triggered this event, ON DEMAND. The
88
+ * serialization is deferred so a config change with no interested listener
89
+ * costs nothing; call `getConfig()` only if you need the config.
90
+ *
91
+ * Valid ONLY synchronously within the event callback — the backing closure
92
+ * is released immediately after dispatch, so stashing `getConfig` and
93
+ * calling it later throws. To read config outside the callback, use
94
+ * `viewer.save()` / `viewer.saveWorkspace()`.
95
+ */
96
+ getConfig(): ViewerConfigUpdate;
97
+ };
98
+
81
99
  // JSX / React extensions
82
100
 
83
101
  type ReactPerspectiveViewerAttributes<T> = React.HTMLAttributes<T>;
@@ -196,7 +214,13 @@ export interface PerspectiveViewerElementExt {
196
214
 
197
215
  addEventListener(
198
216
  name: "perspective-global-filter",
199
- cb: (e: CustomEvent<PerspectiveSelectEventDetail>) => void,
217
+ cb: (e: CustomEvent<PerspectiveSelectDetail>) => void,
218
+ options?: { signal: AbortSignal },
219
+ ): void;
220
+
221
+ addEventListener(
222
+ name: "perspective-global-filter-update",
223
+ cb: (e: CustomEvent<Filter[]>) => void,
200
224
  options?: { signal: AbortSignal },
201
225
  ): void;
202
226
 
@@ -214,7 +238,7 @@ export interface PerspectiveViewerElementExt {
214
238
 
215
239
  addEventListener(
216
240
  name: "perspective-config-update",
217
- cb: (e: CustomEvent) => void,
241
+ cb: (e: CustomEvent<PerspectiveConfigUpdateEventDetail>) => void,
218
242
  options?: { signal: AbortSignal },
219
243
  ): void;
220
244
 
@@ -239,6 +263,10 @@ export interface PerspectiveViewerElementExt {
239
263
  removeEventListener(name: "perspective-click", cb: any): void;
240
264
  removeEventListener(name: "perspective-select", cb: any): void;
241
265
  removeEventListener(name: "perspective-global-filter", cb: any): void;
266
+ removeEventListener(
267
+ name: "perspective-global-filter-update",
268
+ cb: any,
269
+ ): void;
242
270
  removeEventListener(name: "perspective-toggle-settings", cb: any): void;
243
271
  removeEventListener(
244
272
  name: "perspective-toggle-settings-before",
@@ -31,6 +31,8 @@
31
31
  * @module perspective-viewer
32
32
  */
33
33
 
34
+ import "regular-layout";
35
+
34
36
  export { IPerspectiveViewerPlugin } from "./plugin";
35
37
  export { HTMLPerspectiveViewerPluginElement } from "./plugin";
36
38
 
@@ -46,10 +48,18 @@ export type * from "./extensions.ts";
46
48
  export { PerspectiveSelectDetail } from "./extensions.ts";
47
49
  export type * from "./ts-rs/ViewerConfigUpdate.d.ts";
48
50
  export type * from "./ts-rs/ViewerConfig.d.ts";
51
+ export type * from "./ts-rs/WorkspaceConfigUpdate.d.ts";
52
+ export type * from "./ts-rs/WorkspaceConfig.d.ts";
49
53
  export type * from "./ts-rs/Filter.d.ts";
50
54
  export type * from "./ts-rs/FilterTerm.d.ts";
51
55
  export type * from "./ts-rs/FilterReducer.d.ts";
52
56
  export type * from "./ts-rs/PluginStaticConfig.ts";
57
+ export type * from "./ts-rs/ExportMethod.d.ts";
58
+ export type * from "./ts-rs/PanelOptions.d.ts";
59
+ export type * from "./ts-rs/ClientOptions.d.ts";
60
+ export type * from "./ts-rs/ExportOptions.d.ts";
61
+ export type * from "./ts-rs/GetTableOptions.d.ts";
62
+ export type * from "./ts-rs/GetClientOptions.d.ts";
53
63
 
54
64
  export { init_client } from "./bootstrap";
55
65
  import { init_client } from "./bootstrap";
package/src/ts/plugin.ts CHANGED
@@ -28,6 +28,36 @@ import { PluginStaticConfig } from "./ts-rs/PluginStaticConfig.js";
28
28
  * level (if packaged as a library), or during application init to ensure global
29
29
  * availability of a plugin.
30
30
  *
31
+ * ## Dispatch contract
32
+ *
33
+ * The host dispatches each method for exactly one reason, and never
34
+ * defensively — implementations may rely on these meanings:
35
+ *
36
+ * - `draw(view)` — `view` is NEW to this plugin: a new engine `View` was
37
+ * constructed, or this plugin was freshly selected and owes its first
38
+ * render. Safe to reset zoom/scroll/selection state.
39
+ * - `update(view)` — same `View`, but a plugin-visible input genuinely
40
+ * changed: new data (`View.on_update`), an effective view-config delta
41
+ * without a rebuild, a *changed* plugin/columns config just delivered
42
+ * via `restore()`, changed CSS just applied via `restyle()`, a
43
+ * render-limits change, or an explicit public `viewer.restore()` call
44
+ * (the no-op-restore refresh affordance). Host-internal operations that
45
+ * change none of these dispatch nothing.
46
+ * - `resize(view)` — geometry or visibility changed (including panel
47
+ * activation); repaint from retained state, no data or CSS re-read.
48
+ * - `restyle()` — the effective theme genuinely changed; when a
49
+ * `draw`/`update` is part of the same operation, `restyle()` is called
50
+ * immediately BEFORE it, so one render pass paints in the new theme.
51
+ * - `restore()`/`save()` — state transfer only. Plugins must NOT render
52
+ * from `restore()` (a changed restore is always followed by exactly one
53
+ * `update`) and must NOT call host APIs from inside it — a
54
+ * `restore()` echo re-enters the host's public surface and forces
55
+ * a redundant render (the classic double-render-on-load bug).
56
+ *
57
+ * Rendering methods (`draw`, `update`, `resize`, `render`, `clear`) and
58
+ * `delete` are serialized per element — the host never overlaps them, and
59
+ * each call runs to completion before the next begins.
60
+ *
31
61
  * @example
32
62
  * ```javascript
33
63
  * const BasePlugin = customElements.get("perspective-viewer-plugin");
@@ -68,6 +98,14 @@ export interface IPerspectiveViewerPlugin {
68
98
  * provision to cancel a render in progress per se, calling a method on
69
99
  * a `View` which has been deleted will throw an exception.
70
100
  *
101
+ * The host invokes `draw` ONLY when `view` is NEW to this plugin — a
102
+ * new engine `View` was constructed for a config change, or this plugin
103
+ * element was freshly selected and owes its first render of the bound
104
+ * `View` — so implementations may treat it as "new data shape" and
105
+ * reset zoom/scroll/selection-domain state. Repaints of the same
106
+ * `View` arrive as `update()` (data or style refresh) or `resize()`
107
+ * (geometry/chrome), never `draw()`.
108
+ *
71
109
  * @example
72
110
  * ```
73
111
  * async draw(view: perspective.View): Promise<void> {
@@ -79,9 +117,9 @@ export interface IPerspectiveViewerPlugin {
79
117
  draw(view: View): Promise<void>;
80
118
 
81
119
  /**
82
- * Draw under the assumption that the `ViewConfig` has not changed since
83
- * the previous call to `draw()`, but the underlying data has. Defaults to
84
- * dispatch to `draw()`.
120
+ * Draw under the assumption that the `View` has not changed since the
121
+ * previous call to `draw()`, but some plugin-visible input has.
122
+ * Defaults to dispatch to `draw()`.
85
123
  *
86
124
  * @example
87
125
  * ```javascript
@@ -109,21 +147,50 @@ export interface IPerspectiveViewerPlugin {
109
147
 
110
148
  /**
111
149
  * Like `update()`, but for when the dimensions of the plugin have changed
112
- * and the underlying data has not.
150
+ * and the underlying data has not — a repaint from retained state,
151
+ * invoked ONLY when geometry or visibility changed: box resizes,
152
+ * presize sweeps, and the panel-ACTIVATION chrome nudge (multi-panel),
153
+ * so implementations should repaint activation-dependent chrome here
154
+ * and no-op while hidden (`offsetParent == null`).
113
155
  */
114
156
  resize(view: View): Promise<void>;
115
157
 
116
158
  /**
117
159
  * Notify the plugin that the style environment has changed. Useful for
118
- * plugins which read CSS styles via `window.getComputedStyle()`.
160
+ * plugins which read CSS styles via `window.getComputedStyle()` — this
161
+ * is the ONLY point at which a plugin is expected to (re-)read them
162
+ * (besides its first `draw()`), so implementations may cache computed
163
+ * styles between `restyle()` calls.
119
164
  */
120
165
  restyle(): void;
121
166
 
122
167
  /**
123
168
  * Restore this plugin to a state previously returned by `save()`.
169
+ *
170
+ * State transfer ONLY — implementations must not render from
171
+ * `restore()` (the host follows a restore that genuinely changed
172
+ * plugin state with exactly one `update()` in the same serialized
173
+ * sequence), and must not call host `<perspective-viewer>` APIs from
174
+ * inside it: an echoed `restore()` re-enters the host's public
175
+ * surface where it is indistinguishable from a user call and forces a
176
+ * redundant render. Host API calls to persist plugin state belong to
177
+ * genuine user-gesture handlers (e.g. a toolbar click), never to the
178
+ * `restore()` delivery path.
124
179
  */
125
180
  restore(config: any): void;
126
181
 
182
+ /**
183
+ * OPTIONAL — clear any visible user selection state (highlighted rows,
184
+ * pinned tooltips) WITHOUT emitting selection events. The host
185
+ * `<perspective-viewer>` invokes this when an element-level global
186
+ * filter contributed by this plugin's selection is removed from the
187
+ * global filter bar, so the selection visual can't outlive the filter
188
+ * it produced. Called under the host's per-panel render serialization
189
+ * (like `draw`), so implementations may redraw. Plugins without a
190
+ * selection UI may omit it.
191
+ */
192
+ deselect?(): Promise<void>;
193
+
127
194
  /**
128
195
  * Free any resources acquired by this plugin and prepare to be deleted.
129
196
  */
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The `eject` argument: the loaded client to remove by name; the active
5
+ * panel's client when omitted.
6
+ */
7
+ export type ClientOptions = { client?: string, };
@@ -0,0 +1,3 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ export type ExportMethod = "csv" | "csv-all" | "csv-selected" | "json" | "json-all" | "json-selected" | "ndjson" | "ndjson-all" | "ndjson-selected" | "html" | "plugin" | "arrow" | "arrow-selected" | "arrow-all" | "json-config";
@@ -0,0 +1,8 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { ExportMethod } from "./ExportMethod.js";
3
+
4
+ /**
5
+ * The `download` / `export` / `copy` argument: the `ExportMethod` and target
6
+ * panel.
7
+ */
8
+ export type ExportOptions = { method?: ExportMethod, panel?: string, };
@@ -0,0 +1,7 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+
3
+ /**
4
+ * The `getClient` argument: whether to `wait` for a `Client`, and the target
5
+ * panel.
6
+ */
7
+ export type GetClientOptions = { wait?: boolean, panel?: string, };