@perspective-dev/viewer 5.1.0 → 5.2.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 (175) hide show
  1. package/dist/cdn/perspective-viewer.js +9 -9
  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/intl/de.css +1 -1
  8. package/dist/css/intl/es.css +1 -1
  9. package/dist/css/intl/fr.css +1 -1
  10. package/dist/css/intl/ja.css +1 -1
  11. package/dist/css/intl/pt.css +1 -1
  12. package/dist/css/intl/zh.css +1 -1
  13. package/dist/css/intl.css +1 -1
  14. package/dist/css/monokai.css +1 -1
  15. package/dist/css/phosphor.css +1 -1
  16. package/dist/css/pro-dark.css +1 -1
  17. package/dist/css/pro.css +1 -1
  18. package/dist/css/solarized-dark.css +1 -1
  19. package/dist/css/solarized.css +1 -1
  20. package/dist/css/themes.css +1 -1
  21. package/dist/css/vaporwave.css +1 -1
  22. package/dist/docs/perspective-docs.json +1 -0
  23. package/dist/esm/column-format.d.ts +19 -28
  24. package/dist/esm/perspective-viewer.d.ts +2 -0
  25. package/dist/esm/perspective-viewer.inline.js +8 -8
  26. package/dist/esm/perspective-viewer.inline.js.map +4 -4
  27. package/dist/esm/perspective-viewer.js +9 -9
  28. package/dist/esm/perspective-viewer.js.map +4 -4
  29. package/dist/esm/providers.d.ts +79 -0
  30. package/dist/esm/ts-rs/CompactDisplay.d.ts +5 -0
  31. package/dist/esm/ts-rs/CurrencyCode.d.ts +1 -0
  32. package/dist/esm/ts-rs/CurrencyDisplay.d.ts +1 -0
  33. package/dist/esm/ts-rs/CurrencyNumberFormatStyle.d.ts +8 -0
  34. package/dist/esm/ts-rs/CurrencySign.d.ts +1 -0
  35. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
  36. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +54 -0
  37. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +34 -0
  38. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
  39. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
  40. package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
  41. package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
  42. package/dist/esm/ts-rs/Notation.d.ts +17 -0
  43. package/dist/esm/ts-rs/NumberFormatStyle.d.ts +18 -0
  44. package/dist/esm/ts-rs/PanelViewerConfig.d.ts +41 -1
  45. package/dist/esm/ts-rs/PluginStaticConfig.d.ts +35 -0
  46. package/dist/esm/ts-rs/RestoreOptions.d.ts +19 -0
  47. package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
  48. package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
  49. package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
  50. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
  51. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +24 -0
  52. package/dist/esm/ts-rs/SplitRollupMode.d.ts +9 -0
  53. package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
  54. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
  55. package/dist/esm/ts-rs/Unit.d.ts +1 -0
  56. package/dist/esm/ts-rs/UnitDisplay.d.ts +1 -0
  57. package/dist/esm/ts-rs/UnitNumberFormatStyle.d.ts +6 -0
  58. package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
  59. package/dist/esm/ts-rs/ViewerConfig.d.ts +41 -1
  60. package/dist/esm/ts-rs/ViewerConfigInitial.d.ts +175 -0
  61. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +63 -0
  62. package/dist/esm/ts-rs/WindowSpec.d.ts +3 -2
  63. package/dist/esm/ts-rs/WorkspaceConfigUpdate.d.ts +7 -6
  64. package/dist/wasm/perspective-viewer.d.ts +134 -25
  65. package/dist/wasm/perspective-viewer.js +367 -124
  66. package/dist/wasm/perspective-viewer.wasm +0 -0
  67. package/dist/wasm/perspective-viewer.wasm.d.ts +18 -15
  68. package/package.json +2 -1
  69. package/src/css/_viewer_bundle.css +1 -0
  70. package/src/css/column-selector.css +8 -0
  71. package/src/css/config-selector.css +38 -3
  72. package/src/css/form/code-editor.css +2 -3
  73. package/src/css/form/mirrored-textarea.css +81 -0
  74. package/src/css/viewer.css +328 -0
  75. package/src/rust/agent/client/mod.rs +19 -0
  76. package/src/rust/agent/client/protocol.rs +320 -0
  77. package/src/rust/agent/client/transport.rs +287 -0
  78. package/src/rust/agent/client/turn.rs +174 -0
  79. package/src/rust/agent/config.rs +159 -0
  80. package/src/rust/agent/docs.rs +293 -0
  81. package/src/rust/agent/markdown/block.rs +509 -0
  82. package/src/rust/agent/markdown/entity.rs +52 -0
  83. package/src/rust/agent/markdown/events.rs +71 -0
  84. package/src/rust/agent/markdown/inline.rs +497 -0
  85. package/src/rust/agent/markdown/mod.rs +466 -0
  86. package/src/rust/agent/mod.rs +23 -0
  87. package/src/rust/agent/preamble.md +16 -0
  88. package/src/rust/agent/runtime.rs +201 -0
  89. package/src/rust/agent/slot.rs +236 -0
  90. package/src/rust/agent/tools.rs +1110 -0
  91. package/src/rust/components/chat_panel.rs +289 -0
  92. package/src/rust/components/column_selector/config_selector.rs +76 -7
  93. package/src/rust/components/column_selector.rs +7 -0
  94. package/src/rust/components/containers/dragdrop_list.rs +37 -0
  95. package/src/rust/components/form/code_editor.rs +14 -23
  96. package/src/rust/components/form/debug.rs +8 -5
  97. package/src/rust/components/form/mirrored_textarea.rs +165 -0
  98. package/src/rust/components/form/mod.rs +1 -0
  99. package/src/rust/components/mod.rs +2 -0
  100. package/src/rust/components/settings_panel.rs +80 -2
  101. package/src/rust/components/viewer/panels.rs +9 -14
  102. package/src/rust/components/viewer/render.rs +2 -0
  103. package/src/rust/components/window_editor.rs +75 -103
  104. package/src/rust/config/datetime_column_style/custom.rs +17 -0
  105. package/src/rust/config/datetime_column_style/simple.rs +10 -0
  106. package/src/rust/config/number_string_format.rs +33 -0
  107. package/src/rust/config/options.rs +18 -0
  108. package/src/rust/config/plugin_static_config.rs +120 -5
  109. package/src/rust/config/viewer_config.rs +220 -1
  110. package/src/rust/config/workspace_config.rs +7 -6
  111. package/src/rust/custom_elements/viewer.rs +209 -18
  112. package/src/rust/custom_events.rs +29 -0
  113. package/src/rust/lib.rs +13 -0
  114. package/src/rust/presentation.rs +8 -0
  115. package/src/rust/queries/get_viewer_config.rs +5 -1
  116. package/src/rust/session/column_defaults_update.rs +18 -0
  117. package/src/rust/session/metadata.rs +18 -20
  118. package/src/rust/session/replace_expression_update.rs +1 -0
  119. package/src/rust/tasks/create_panel.rs +18 -8
  120. package/src/rust/tasks/edit_window.rs +1 -1
  121. package/src/rust/tasks/restore_panel.rs +34 -17
  122. package/src/rust/tasks/sync_update_panels.rs +7 -23
  123. package/src/rust/workspace.rs +112 -20
  124. package/src/themes/intl/de.css +16 -0
  125. package/src/themes/intl/es.css +16 -0
  126. package/src/themes/intl/fr.css +16 -0
  127. package/src/themes/intl/ja.css +16 -0
  128. package/src/themes/intl/pt.css +16 -0
  129. package/src/themes/intl/zh.css +16 -0
  130. package/src/themes/intl.css +16 -0
  131. package/src/ts/column-format.ts +67 -46
  132. package/src/ts/perspective-viewer.ts +2 -0
  133. package/src/ts/providers.ts +96 -0
  134. package/src/ts/ts-rs/CompactDisplay.ts +3 -0
  135. package/src/ts/ts-rs/CurrencyCode.ts +3 -0
  136. package/src/ts/ts-rs/CurrencyDisplay.ts +3 -0
  137. package/src/ts/ts-rs/CurrencyNumberFormatStyle.ts +6 -0
  138. package/src/ts/ts-rs/CurrencySign.ts +3 -0
  139. package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
  140. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +54 -0
  141. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +25 -0
  142. package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
  143. package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
  144. package/src/ts/ts-rs/FormatMode.ts +3 -0
  145. package/src/ts/ts-rs/FormatUnit.ts +3 -0
  146. package/src/ts/ts-rs/Notation.ts +11 -0
  147. package/src/ts/ts-rs/NumberFormatStyle.ts +12 -0
  148. package/src/ts/ts-rs/PanelViewerConfig.ts +47 -1
  149. package/src/ts/ts-rs/PluginStaticConfig.ts +36 -1
  150. package/src/ts/ts-rs/RestoreOptions.ts +20 -0
  151. package/src/ts/ts-rs/RoundingMode.ts +3 -0
  152. package/src/ts/ts-rs/RoundingPriority.ts +3 -0
  153. package/src/ts/ts-rs/SignDisplay.ts +3 -0
  154. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
  155. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +25 -0
  156. package/src/ts/ts-rs/SplitRollupMode.ts +11 -0
  157. package/src/ts/ts-rs/StringColorMode.ts +3 -0
  158. package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
  159. package/src/ts/ts-rs/Unit.ts +3 -0
  160. package/src/ts/ts-rs/UnitDisplay.ts +3 -0
  161. package/src/ts/ts-rs/UnitNumberFormatStyle.ts +5 -0
  162. package/src/ts/ts-rs/UseGrouping.ts +3 -0
  163. package/src/ts/ts-rs/ViewerConfig.ts +47 -1
  164. package/src/ts/ts-rs/ViewerConfigInitial.ts +164 -0
  165. package/src/ts/ts-rs/ViewerConfigUpdate.ts +72 -2
  166. package/src/ts/ts-rs/WindowSpec.ts +3 -2
  167. package/src/ts/ts-rs/WorkspaceConfigUpdate.ts +7 -6
  168. package/dist/esm/ts-rs/WindowAggregate.d.ts +0 -1
  169. package/src/ts/ts-rs/WindowAggregate.ts +0 -3
  170. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline0.js +0 -0
  171. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline1.js +0 -0
  172. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline2.js +0 -0
  173. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline3.js +0 -0
  174. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline4.js +0 -0
  175. /package/dist/wasm/snippets/{perspective-viewer-8a8c7d5d696b43ae → perspective-viewer-02e461f3d7062b26}/inline5.js +0 -0
@@ -118,6 +118,22 @@ perspective-dropdown {
118
118
  --psp-label--query-tab--content: "Requête";
119
119
  --psp-label--plugin-tab--content: "Plugin";
120
120
  --psp-label--debug-tab--content: "JSON";
121
+ --psp-label--chat-tab--content: "Discussion";
122
+
123
+ /* Chat panel */
124
+ --psp-label--chat-reasoning--content: "Raisonnement";
125
+ --psp-label--chat-send--content: "Envoyer";
126
+ --psp-label--chat-stop--content: "Arrêter";
127
+ --psp-label--chat-placeholder--content: "Question sur vos données…";
128
+
129
+ /* Plugin-declared pivot roles (PluginStaticConfig) */
130
+ --psp-label--role--x-axis--content: "Axe X";
131
+ --psp-label--role--y-axis--content: "Axe Y";
132
+ --psp-label--role--series--content: "Séries";
133
+ --psp-label--role--hierarchy--content: "Hiérarchie";
134
+ --psp-label--role--facets--content: "Facettes";
135
+ --psp-label--role--row-groups--content: "Groupes de lignes";
136
+ --psp-label--role--column-groups--content: "Groupes de colonnes";
121
137
 
122
138
  /* Plugin settings */
123
139
  --psp-label--edit-mode--content: "Mode édition";
@@ -118,6 +118,22 @@ perspective-dropdown {
118
118
  --psp-label--query-tab--content: "クエリ";
119
119
  --psp-label--plugin-tab--content: "プラグイン";
120
120
  --psp-label--debug-tab--content: "JSON";
121
+ --psp-label--chat-tab--content: "チャット";
122
+
123
+ /* Chat panel */
124
+ --psp-label--chat-reasoning--content: "推論";
125
+ --psp-label--chat-send--content: "送信";
126
+ --psp-label--chat-stop--content: "停止";
127
+ --psp-label--chat-placeholder--content: "データについて質問…";
128
+
129
+ /* Plugin-declared pivot roles (PluginStaticConfig) */
130
+ --psp-label--role--x-axis--content: "X軸";
131
+ --psp-label--role--y-axis--content: "Y軸";
132
+ --psp-label--role--series--content: "系列";
133
+ --psp-label--role--hierarchy--content: "階層";
134
+ --psp-label--role--facets--content: "ファセット";
135
+ --psp-label--role--row-groups--content: "行グループ";
136
+ --psp-label--role--column-groups--content: "列グループ";
121
137
 
122
138
  /* Plugin settings */
123
139
  --psp-label--edit-mode--content: "編集モード";
@@ -118,6 +118,22 @@ perspective-dropdown {
118
118
  --psp-label--query-tab--content: "Consulta";
119
119
  --psp-label--plugin-tab--content: "Plugin";
120
120
  --psp-label--debug-tab--content: "JSON";
121
+ --psp-label--chat-tab--content: "Conversa";
122
+
123
+ /* Chat panel */
124
+ --psp-label--chat-reasoning--content: "Raciocínio";
125
+ --psp-label--chat-send--content: "Enviar";
126
+ --psp-label--chat-stop--content: "Parar";
127
+ --psp-label--chat-placeholder--content: "Pergunte sobre seus dados…";
128
+
129
+ /* Plugin-declared pivot roles (PluginStaticConfig) */
130
+ --psp-label--role--x-axis--content: "Eixo X";
131
+ --psp-label--role--y-axis--content: "Eixo Y";
132
+ --psp-label--role--series--content: "Séries";
133
+ --psp-label--role--hierarchy--content: "Hierarquia";
134
+ --psp-label--role--facets--content: "Facetas";
135
+ --psp-label--role--row-groups--content: "Grupos de linhas";
136
+ --psp-label--role--column-groups--content: "Grupos de colunas";
121
137
 
122
138
  /* Plugin settings */
123
139
  --psp-label--edit-mode--content: "Modo de edição";
@@ -118,6 +118,22 @@ perspective-dropdown {
118
118
  --psp-label--query-tab--content: "查询";
119
119
  --psp-label--plugin-tab--content: "插件";
120
120
  --psp-label--debug-tab--content: "JSON";
121
+ --psp-label--chat-tab--content: "聊天";
122
+
123
+ /* Chat panel */
124
+ --psp-label--chat-reasoning--content: "推理";
125
+ --psp-label--chat-send--content: "发送";
126
+ --psp-label--chat-stop--content: "停止";
127
+ --psp-label--chat-placeholder--content: "询问您的数据…";
128
+
129
+ /* Plugin-declared pivot roles (PluginStaticConfig) */
130
+ --psp-label--role--x-axis--content: "X 轴";
131
+ --psp-label--role--y-axis--content: "Y 轴";
132
+ --psp-label--role--series--content: "系列";
133
+ --psp-label--role--hierarchy--content: "层级";
134
+ --psp-label--role--facets--content: "分面";
135
+ --psp-label--role--row-groups--content: "行分组";
136
+ --psp-label--role--column-groups--content: "列分组";
121
137
 
122
138
  /* Plugin settings */
123
139
  --psp-label--edit-mode--content: "编辑模式";
@@ -108,6 +108,22 @@ perspective-dropdown {
108
108
  --psp-label--query-tab--content: "Query";
109
109
  --psp-label--plugin-tab--content: "Plugin";
110
110
  --psp-label--debug-tab--content: "JSON";
111
+ --psp-label--chat-tab--content: "Chat";
112
+
113
+ /* Chat panel */
114
+ --psp-label--chat-reasoning--content: "Reasoning";
115
+ --psp-label--chat-send--content: "Send";
116
+ --psp-label--chat-stop--content: "Stop";
117
+ --psp-label--chat-placeholder--content: "Ask Perspective ...";
118
+
119
+ /* Plugin-declared pivot roles (PluginStaticConfig) */
120
+ --psp-label--role--x-axis--content: "X Axis";
121
+ --psp-label--role--y-axis--content: "Y Axis";
122
+ --psp-label--role--series--content: "Series";
123
+ --psp-label--role--hierarchy--content: "Hierarchy";
124
+ --psp-label--role--facets--content: "Facets";
125
+ --psp-label--role--row-groups--content: "Row Groups";
126
+ --psp-label--role--column-groups--content: "Column Groups";
111
127
 
112
128
  /* Window editor */
113
129
  --psp-label--window-source--content: "Column";
@@ -24,34 +24,36 @@
24
24
  * per-field overrides (year / month / day / ...).
25
25
  */
26
26
 
27
- export interface NumberFormatConfig {
28
- style?: "decimal" | "currency" | "percent" | "unit";
29
- minimumFractionDigits?: number;
30
- maximumFractionDigits?: number;
31
- minimumIntegerDigits?: number;
32
- minimumSignificantDigits?: number;
33
- maximumSignificantDigits?: number;
34
- currency?: string;
35
- currencyDisplay?: "code" | "symbol" | "narrowSymbol" | "name";
36
- notation?: "standard" | "scientific" | "engineering" | "compact";
37
- compactDisplay?: "short" | "long";
38
- useGrouping?: boolean;
39
- }
27
+ import type { CustomNumberFormatConfig } from "./ts-rs/CustomNumberFormatConfig.d.ts";
28
+ import type { NumberFormatStyle } from "./ts-rs/NumberFormatStyle.d.ts";
29
+ import type { Notation } from "./ts-rs/Notation.d.ts";
30
+ import type { DatetimeFormatType } from "./ts-rs/DatetimeFormatType.d.ts";
31
+ import type { SimpleDatetimeStyleConfig } from "./ts-rs/SimpleDatetimeStyleConfig.d.ts";
32
+
33
+ /**
34
+ * A numeric column's `number_format` (`columns_config` value), exactly as
35
+ * the Style tab's editor WRITES it — the ts-rs projection of the Rust
36
+ * `CustomNumberFormatConfig`, re-composed with its serde-flattened
37
+ * `style` and `notation` families (ts-rs cannot flatten `Option<enum>`,
38
+ * so the Rust type `#[ts(skip)]`s them and they export separately).
39
+ */
40
+ export type NumberFormatConfig = CustomNumberFormatConfig &
41
+ Partial<NumberFormatStyle> &
42
+ Partial<Notation>;
40
43
 
41
- export interface DateFormatConfig {
42
- format?: "custom" | string;
43
- timeZone?: string;
44
- dateStyle?: "short" | "medium" | "long" | "full" | "disabled";
45
- timeStyle?: "short" | "medium" | "long" | "full" | "disabled";
46
- second?: "numeric" | "2-digit" | "disabled";
47
- minute?: "numeric" | "2-digit" | "disabled";
48
- hour?: "numeric" | "2-digit" | "disabled";
49
- day?: "numeric" | "2-digit" | "disabled";
50
- weekday?: "narrow" | "short" | "long" | "disabled";
51
- month?: "numeric" | "2-digit" | "narrow" | "short" | "long" | "disabled";
52
- year?: "numeric" | "2-digit" | "disabled";
53
- hour12?: boolean;
54
- fractionalSecondDigits?: 1 | 2 | 3;
44
+ /**
45
+ * A datetime column's `date_format` (`columns_config` value) — the Rust
46
+ * `DatetimeFormatType` union: a `Simple` preset (`dateStyle` /
47
+ * `timeStyle`), or per-part custom fields discriminated by
48
+ * `format: "custom"`.
49
+ */
50
+ export type DateFormatConfig = DatetimeFormatType;
51
+
52
+ /** Narrow a [`DateFormatConfig`] to its `Simple` arm, if it is one. */
53
+ function simple_config(
54
+ cfg?: DateFormatConfig,
55
+ ): SimpleDatetimeStyleConfig | undefined {
56
+ return cfg && !("format" in cfg) ? cfg : undefined;
55
57
  }
56
58
 
57
59
  /**
@@ -85,55 +87,73 @@ export function createNumberFormatter(
85
87
  type: string,
86
88
  cfg?: NumberFormatConfig,
87
89
  ): Intl.NumberFormat {
88
- const opts: Intl.NumberFormatOptions =
89
- cfg ?? NUMERIC_LEGACY_DEFAULTS[type] ?? {};
90
+ // ts-rs renders serde-absent keys as `T | null`; the wire never
91
+ // carries an explicit `null` (`skip_serializing_if`), and
92
+ // `Intl.NumberFormat` treats both absent and `undefined` the same, so
93
+ // the cast is presentation-only.
94
+ const opts = (cfg ??
95
+ NUMERIC_LEGACY_DEFAULTS[type] ??
96
+ {}) as Intl.NumberFormatOptions;
90
97
  return new Intl.NumberFormat(navigator.languages as string[], opts);
91
98
  }
92
99
 
93
100
  export function createDatetimeFormatter(
94
101
  cfg?: DateFormatConfig,
95
102
  ): Intl.DateTimeFormat {
96
- if (!cfg || cfg.format !== "custom") {
103
+ if (!cfg || !("format" in cfg)) {
104
+ const preset = simple_config(cfg);
97
105
  const opts: Intl.DateTimeFormatOptions = {
98
- timeZone: cfg?.timeZone,
106
+ timeZone: preset?.timeZone ?? undefined,
99
107
  dateStyle:
100
- cfg?.dateStyle === "disabled"
108
+ preset?.dateStyle === "disabled"
101
109
  ? undefined
102
- : (cfg?.dateStyle ?? DATETIME_LEGACY_DEFAULTS.dateStyle),
110
+ : (preset?.dateStyle ?? DATETIME_LEGACY_DEFAULTS.dateStyle),
103
111
  timeStyle:
104
- cfg?.timeStyle === "disabled"
112
+ preset?.timeStyle === "disabled"
105
113
  ? undefined
106
- : (cfg?.timeStyle ?? DATETIME_LEGACY_DEFAULTS.timeStyle),
114
+ : (preset?.timeStyle ?? DATETIME_LEGACY_DEFAULTS.timeStyle),
107
115
  };
108
116
 
109
117
  return new Intl.DateTimeFormat(navigator.languages as string[], opts);
110
118
  }
111
119
 
120
+ // Per-part fields are the shared `CustomDatetimeFormat` enum; the
121
+ // editor only writes each part's `Intl`-valid subset, so the
122
+ // narrowing casts below are presentation-only.
112
123
  const opts: Intl.DateTimeFormatOptions = {
113
- timeZone: cfg.timeZone,
124
+ timeZone: cfg.timeZone ?? undefined,
114
125
  hour12: cfg.hour12 ?? true,
115
- fractionalSecondDigits: cfg.fractionalSecondDigits,
126
+ fractionalSecondDigits: cfg.fractionalSecondDigits as
127
+ | 1
128
+ | 2
129
+ | 3
130
+ | undefined,
116
131
  };
117
132
  if (cfg.year !== "disabled") {
118
- opts.year = cfg.year ?? "2-digit";
133
+ opts.year = (cfg.year ??
134
+ "2-digit") as Intl.DateTimeFormatOptions["year"];
119
135
  }
120
136
  if (cfg.month !== "disabled") {
121
- opts.month = cfg.month ?? "numeric";
137
+ opts.month = (cfg.month ??
138
+ "numeric") as Intl.DateTimeFormatOptions["month"];
122
139
  }
123
140
  if (cfg.day !== "disabled") {
124
- opts.day = cfg.day ?? "numeric";
141
+ opts.day = (cfg.day ?? "numeric") as Intl.DateTimeFormatOptions["day"];
125
142
  }
126
143
  if (cfg.weekday && cfg.weekday !== "disabled") {
127
- opts.weekday = cfg.weekday;
144
+ opts.weekday = cfg.weekday as Intl.DateTimeFormatOptions["weekday"];
128
145
  }
129
146
  if (cfg.hour !== "disabled") {
130
- opts.hour = cfg.hour ?? "numeric";
147
+ opts.hour = (cfg.hour ??
148
+ "numeric") as Intl.DateTimeFormatOptions["hour"];
131
149
  }
132
150
  if (cfg.minute !== "disabled") {
133
- opts.minute = cfg.minute ?? "numeric";
151
+ opts.minute = (cfg.minute ??
152
+ "numeric") as Intl.DateTimeFormatOptions["minute"];
134
153
  }
135
154
  if (cfg.second !== "disabled") {
136
- opts.second = cfg.second ?? "numeric";
155
+ opts.second = (cfg.second ??
156
+ "numeric") as Intl.DateTimeFormatOptions["second"];
137
157
  }
138
158
  return new Intl.DateTimeFormat(navigator.languages as string[], opts);
139
159
  }
@@ -141,12 +161,13 @@ export function createDatetimeFormatter(
141
161
  export function createDateFormatter(
142
162
  cfg?: DateFormatConfig,
143
163
  ): Intl.DateTimeFormat {
164
+ const preset = simple_config(cfg);
144
165
  const opts: Intl.DateTimeFormatOptions = {
145
166
  timeZone: "utc",
146
167
  dateStyle:
147
- cfg?.dateStyle === "disabled"
168
+ preset?.dateStyle === "disabled"
148
169
  ? undefined
149
- : (cfg?.dateStyle ?? DATE_LEGACY_DEFAULTS.dateStyle),
170
+ : (preset?.dateStyle ?? DATE_LEGACY_DEFAULTS.dateStyle),
150
171
  };
151
172
  return new Intl.DateTimeFormat(navigator.languages as string[], opts);
152
173
  }
@@ -35,6 +35,8 @@ import "regular-layout";
35
35
 
36
36
  export { IPerspectiveViewerPlugin } from "./plugin";
37
37
  export { HTMLPerspectiveViewerPluginElement } from "./plugin";
38
+ export { providers } from "./providers";
39
+ export type { AgentProviderPreset } from "./providers";
38
40
 
39
41
  export {
40
42
  createNumberFormatter,
@@ -0,0 +1,96 @@
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
+ /**
14
+ * LLM provider presets for `viewer.agentConfig()`. These are plain data —
15
+ * the agent core is provider-agnostic (one OpenAI-chat-completions protocol
16
+ * over primitive `url`/`headers`/`apiKey` connection fields), so a
17
+ * "provider" is just a spreadable collection of those fields:
18
+ *
19
+ * ```javascript
20
+ * import { providers } from "@perspective-dev/viewer";
21
+ * viewer.agentConfig({
22
+ * ...providers.anthropic,
23
+ * apiKey: "sk-ant-...",
24
+ * model: "claude-haiku-4-5", // spread order = override order
25
+ * });
26
+ * ```
27
+ *
28
+ * Any OpenAI-compatible service works without a preset — pass its full
29
+ * chat-completions `url` (and `apiKey`/`headers` as needed) directly.
30
+ * Cloud presets call the provider from the browser tab; suitable for local
31
+ * development, or route `url` through your own proxy for production.
32
+ */
33
+
34
+ /**
35
+ * A spreadable connection preset for `viewer.agentConfig()`.
36
+ */
37
+ export interface AgentProviderPreset {
38
+ name: string;
39
+ url: string;
40
+ headers?: Record<string, string>;
41
+ model?: string;
42
+ }
43
+
44
+ export const providers = {
45
+ /**
46
+ * Anthropic's OpenAI-compatibility endpoint. `apiKey` required.
47
+ */
48
+ anthropic: {
49
+ name: "anthropic",
50
+ url: "https://api.anthropic.com/v1/chat/completions",
51
+ headers: { "anthropic-dangerous-direct-browser-access": "true" },
52
+ model: "claude-opus-5",
53
+ },
54
+
55
+ /**
56
+ * Google Gemini's OpenAI-compatibility endpoint. `apiKey` required.
57
+ */
58
+ gemini: {
59
+ name: "gemini",
60
+ url: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
61
+ model: "gemini-2.5-flash",
62
+ },
63
+
64
+ /**
65
+ * LM Studio's local developer server (enable CORS in its settings).
66
+ */
67
+ lmstudio: {
68
+ name: "lmstudio",
69
+ url: "http://localhost:1234/v1/chat/completions",
70
+ },
71
+
72
+ /**
73
+ * Local Ollama (set `OLLAMA_ORIGINS` for CORS).
74
+ */
75
+ ollama: {
76
+ name: "ollama",
77
+ url: "http://localhost:11434/v1/chat/completions",
78
+ },
79
+
80
+ /**
81
+ * OpenRouter. `apiKey` required.
82
+ */
83
+ openrouter: {
84
+ name: "openrouter",
85
+ url: "https://openrouter.ai/api/v1/chat/completions",
86
+ },
87
+
88
+ /**
89
+ * OpenAI. `apiKey` required.
90
+ */
91
+ openai: {
92
+ name: "openai",
93
+ url: "https://api.openai.com/v1/chat/completions",
94
+ model: "gpt-5.2",
95
+ },
96
+ } as const satisfies Record<string, AgentProviderPreset>;
@@ -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 CompactDisplay = { "compactDisplay": "short" } | { "compactDisplay": "long" };
@@ -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 CurrencyCode = "AED" | "AFN" | "ALL" | "AMD" | "ANG" | "AOA" | "ARS" | "AUD" | "AWG" | "AZN" | "BAM" | "BBD" | "BDT" | "BGN" | "BHD" | "BIF" | "BMD" | "BND" | "BOB" | "BOV" | "BRL" | "BSD" | "BTN" | "BWP" | "BYR" | "BZD" | "CAD" | "CDF" | "CHE" | "CHF" | "CHW" | "CLF" | "CLP" | "CNY" | "COP" | "COU" | "CRC" | "CUC" | "CUP" | "CVE" | "CZK" | "DJF" | "DKK" | "DOP" | "DZD" | "EGP" | "ERN" | "ETB" | "EUR" | "FJD" | "FKP" | "GBP" | "GEL" | "GHS" | "GIP" | "GMD" | "GNF" | "GTQ" | "GYD" | "HKD" | "HNL" | "HRK" | "HTG" | "HUF" | "IDR" | "ILS" | "INR" | "IQD" | "IRR" | "ISK" | "JMD" | "JOD" | "JPY" | "KES" | "KGS" | "KHR" | "KMF" | "KPW" | "KRW" | "KWD" | "KYD" | "KZT" | "LAK" | "LBP" | "LKR" | "LRD" | "LSL" | "LTL" | "LVL" | "LYD" | "MAD" | "MDL" | "MGA" | "MKD" | "MMK" | "MNT" | "MOP" | "MRO" | "MUR" | "MVR" | "MWK" | "MXN" | "MXV" | "MYR" | "MZN" | "NAD" | "NGN" | "NIO" | "NOK" | "NPR" | "NZD" | "OMR" | "PAB" | "PEN" | "PGK" | "PHP" | "PKR" | "PLN" | "PYG" | "QAR" | "RON" | "RSD" | "RUB" | "RWF" | "SAR" | "SBD" | "SCR" | "SDG" | "SEK" | "SGD" | "SHP" | "SLL" | "SOS" | "SRD" | "SSP" | "STD" | "SYP" | "SZL" | "THB" | "TJS" | "TMT" | "TND" | "TOP" | "TRY" | "TTD" | "TWD" | "TZS" | "UAH" | "UGX" | "USD" | "USN" | "USS" | "UYI" | "UYU" | "UZS" | "VEF" | "VND" | "VUV" | "WST" | "XAF" | "XAG" | "XAU" | "XBA" | "XBB" | "XBC" | "XBD" | "XCD" | "XDR" | "XFU" | "XOF" | "XPD" | "XPF" | "XPT" | "XTS" | "XXX" | "YER" | "ZAR" | "ZMW";
@@ -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 CurrencyDisplay = "code" | "symbol" | "narrowSymbol" | "name";
@@ -0,0 +1,6 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { CurrencyCode } from "./CurrencyCode.js";
3
+ import type { CurrencyDisplay } from "./CurrencyDisplay.js";
4
+ import type { CurrencySign } from "./CurrencySign.js";
5
+
6
+ export type CurrencyNumberFormatStyle = { currency: CurrencyCode, currencyDisplay: CurrencyDisplay | null, currencySign: CurrencySign | null, };
@@ -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 CurrencySign = "standard" | "accounting";
@@ -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 CustomDatetimeFormat = "long" | "short" | "narrow" | "numeric" | "2-digit" | "disabled";
@@ -0,0 +1,54 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { CustomDatetimeFormat } from "./CustomDatetimeFormat.js";
3
+ import type { FormatUnit } from "./FormatUnit.js";
4
+
5
+ /**
6
+ * A datetime column's `date_format` in its `Custom` form: per-part
7
+ * `Intl.DateTimeFormatOptions` overrides, discriminated from the `Simple`
8
+ * preset form by the required `format: "custom"` key. Each part is a
9
+ * [`CustomDatetimeFormat`] variant valid for that `Intl` option;
10
+ * `"disabled"` omits the part from the formatted output entirely.
11
+ */
12
+ export type CustomDatetimeStyleConfig = { format: FormatUnit,
13
+ /**
14
+ * An IANA time zone name (e.g. `"America/New_York"`); defaults to the
15
+ * browser's local time zone.
16
+ */
17
+ timeZone?: string | null,
18
+ /**
19
+ * Sub-second digits to display, 0-3 (0 = none).
20
+ */
21
+ fractionalSecondDigits?: number,
22
+ /**
23
+ * Defaults to `"numeric"`.
24
+ */
25
+ second?: CustomDatetimeFormat,
26
+ /**
27
+ * Defaults to `"numeric"`.
28
+ */
29
+ minute?: CustomDatetimeFormat,
30
+ /**
31
+ * Defaults to `"numeric"`.
32
+ */
33
+ hour?: CustomDatetimeFormat,
34
+ /**
35
+ * Defaults to `"numeric"`.
36
+ */
37
+ day?: CustomDatetimeFormat,
38
+ /**
39
+ * A name form (`"long"`/`"short"`/`"narrow"`); defaults to
40
+ * `"disabled"` (weekday not shown).
41
+ */
42
+ weekday?: CustomDatetimeFormat,
43
+ /**
44
+ * Numeric or name form; defaults to `"numeric"`.
45
+ */
46
+ month?: CustomDatetimeFormat,
47
+ /**
48
+ * Defaults to `"2-digit"`.
49
+ */
50
+ year?: CustomDatetimeFormat,
51
+ /**
52
+ * 12-hour clock; defaults to `true`.
53
+ */
54
+ hour12?: boolean, };
@@ -0,0 +1,25 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { RoundingMode } from "./RoundingMode.js";
3
+ import type { RoundingPriority } from "./RoundingPriority.js";
4
+ import type { SignDisplay } from "./SignDisplay.js";
5
+ import type { TrailingZeroDisplay } from "./TrailingZeroDisplay.js";
6
+ import type { UseGrouping } from "./UseGrouping.js";
7
+
8
+ /**
9
+ * A numeric column's `number_format` (`columns_config` value) —
10
+ * `Intl.NumberFormat`-shaped options, written by the Style tab's number
11
+ * format editor and read by `createNumberFormatter`. The `style` and
12
+ * `notation` families ([`NumberFormatStyle`] / [`Notation`]) are serde-
13
+ * FLATTENED into this object but `#[ts(skip)]`'d (ts-rs cannot flatten
14
+ * `Option<enum>`) — the package's `NumberFormatConfig` re-composes the
15
+ * full wire type as `CustomNumberFormatConfig & Partial<NumberFormatStyle>
16
+ * & Partial<Notation>` (see `column-format.ts`).
17
+ */
18
+ export type CustomNumberFormatConfig = { minimumIntegerDigits?: number | null, minimumFractionDigits?: number | null, maximumFractionDigits?: number | null, minimumSignificantDigits?: number | null, maximumSignificantDigits?: number | null, roundingPriority?: RoundingPriority | null, roundingIncrement?: number | null, roundingMode?: RoundingMode | null, trailingZeroDisplay?: TrailingZeroDisplay | null,
19
+ /**
20
+ * NOTE (audit 2026-08-05): serialized values are the STRINGS
21
+ * `"always"`/`"auto"`/`"min2"` or the untagged BOOLEAN `false` —
22
+ * the former hand-written TS `useGrouping?: boolean` was wrong for
23
+ * the string cases.
24
+ */
25
+ useGrouping?: UseGrouping | null, signDisplay?: SignDisplay | null, };
@@ -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 DatetimeColorMode = "none" | "foreground" | "background";
@@ -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 { CustomDatetimeStyleConfig } from "./CustomDatetimeStyleConfig.js";
3
+ import type { SimpleDatetimeStyleConfig } from "./SimpleDatetimeStyleConfig.js";
4
+
5
+ /**
6
+ * `Simple` case has all default-able keys and must be last!
7
+ */
8
+ export type DatetimeFormatType = CustomDatetimeStyleConfig | SimpleDatetimeStyleConfig;
@@ -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 FormatMode = "none" | "link" | "bold" | "italics";
@@ -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 FormatUnit = "custom";
@@ -0,0 +1,11 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { CompactDisplay } from "./CompactDisplay.js";
3
+
4
+ /**
5
+ * The `notation` family of a numeric column's `number_format` —
6
+ * serialized FLATTENED into [`CustomNumberFormatConfig`]'s object,
7
+ * discriminated by the `notation` key (`"standard"` default,
8
+ * `"scientific"`, `"engineering"`, `"compact"` + `compactDisplay`),
9
+ * mirroring `Intl.NumberFormat` options.
10
+ */
11
+ export type Notation = { "notation": "standard" } | { "notation": "scientific" } | { "notation": "engineering" } | { "notation": "compact" } & CompactDisplay;
@@ -0,0 +1,12 @@
1
+ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
2
+ import type { CurrencyNumberFormatStyle } from "./CurrencyNumberFormatStyle.js";
3
+ import type { UnitNumberFormatStyle } from "./UnitNumberFormatStyle.js";
4
+
5
+ /**
6
+ * The `style` family of a numeric column's `number_format` — serialized
7
+ * FLATTENED into [`CustomNumberFormatConfig`]'s object, discriminated by
8
+ * the `style` key (`"decimal"` default, `"currency"` + `currency`/
9
+ * `currencyDisplay`/`currencySign`, `"percent"`, `"unit"` + `unit`/
10
+ * `unitDisplay`), mirroring `Intl.NumberFormat` options.
11
+ */
12
+ export type NumberFormatStyle = { "style": "decimal" } | { "style": "currency" } & CurrencyNumberFormatStyle | { "style": "percent" } | { "style": "unit" } & UnitNumberFormatStyle;
@@ -5,6 +5,7 @@ import type { Filter } from "./Filter.js";
5
5
  import type { FilterReducer } from "./FilterReducer.js";
6
6
  import type { GroupRollupMode } from "./GroupRollupMode.js";
7
7
  import type { Sort } from "./Sort.js";
8
+ import type { SplitRollupMode } from "./SplitRollupMode.js";
8
9
  import type { Windows } from "./Windows.js";
9
10
  import type { JsonValue } from "./serde_json/JsonValue.js";
10
11
 
@@ -13,4 +14,49 @@ import type { JsonValue } from "./serde_json/JsonValue.js";
13
14
  * element-level `settings` flag. This is the `panels` entry type of the
14
15
  * whole-element config format.
15
16
  */
16
- export type PanelViewerConfig<V = string> = { version: V, columns_config: { [key in string]?: { [key in string]?: JsonValue } }, plugin: string, plugin_config: { [key in string]?: JsonValue }, table: string | null, theme: string | null, title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };
17
+ export type PanelViewerConfig<V = string> = {
18
+ /**
19
+ * The `@perspective-dev/viewer` version that wrote this config,
20
+ * stamped on save and used to migrate older tokens. Callers do not
21
+ * set it.
22
+ */
23
+ version: V,
24
+ /**
25
+ * Per-column styling, keyed by column name. The viewer treats the
26
+ * values as opaque — their shape is defined by the ACTIVE plugin,
27
+ * so a config written for one plugin may carry keys another
28
+ * ignores. Query the live shape with the `get_style_schema` agent
29
+ * tool, or the plugin's `column_config_schema()`.
30
+ */
31
+ columns_config: { [key in string]?: { [key in string]?: JsonValue } },
32
+ /**
33
+ * Name of the visualization plugin, from the set registered on the
34
+ * page (the `list_plugins` agent tool, or the plugin picker). This
35
+ * also decides what the view fields MEAN visually: `columns` is
36
+ * positional and each plugin reads the positions differently, and
37
+ * `group_by`/`split_by` draw different things per plugin.
38
+ */
39
+ plugin: string,
40
+ /**
41
+ * Plugin-wide settings (as opposed to the per-column
42
+ * [`Self::columns_config`]). Opaque to the viewer and defined by
43
+ * the active plugin; see `get_style_schema`.
44
+ */
45
+ plugin_config: { [key in string]?: JsonValue },
46
+ /**
47
+ * Name of the `Table` this panel renders, as hosted on the panel's
48
+ * `Client`. Every placed panel has a table binding (creation
49
+ * requires one by type — [`ViewerConfigInitial`]), so the saved
50
+ * config carries it unconditionally.
51
+ */
52
+ table: string,
53
+ /**
54
+ * Selected theme NAME (e.g. `"Pro Dark"`) — not a CSS value. Valid
55
+ * names are the Perspective themes loaded on the page, which
56
+ * `resetThemes()` re-scans. `None` selects the first available.
57
+ */
58
+ theme: string | null,
59
+ /**
60
+ * Panel title, shown in its tab. `None` renders the default title.
61
+ */
62
+ title: string | null, group_by: Array<string>, split_by: Array<string>, sort: Array<Sort>, filter: Array<Filter>, group_rollup_mode: GroupRollupMode, split_rollup_mode: SplitRollupMode, filter_op?: FilterReducer, expressions: Expressions, windows?: Windows, columns: Array<string | null>, aggregates: { [key in string]?: Aggregate }, group_by_depth?: number | null, };