@concepttocloud/saiku-embed 3.18.2 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,6 +50,91 @@ globally — no further setup.
50
50
  ></saiku-embed>
51
51
  ```
52
52
 
53
+ ### A saved query, rendered as a hierarchical matrix (v3.19)
54
+
55
+ Matrix mode preserves the row / column axis structure — measures on
56
+ columns, dimension members on rows — instead of flattening to a single
57
+ row-key map like `render="table"` does. Useful for pivot-style reports.
58
+
59
+ ```html
60
+ <saiku-embed
61
+ server="..."
62
+ token="..."
63
+ path="homes/admin/Examples/Sales.saiku"
64
+ render="matrix"
65
+ height="500px"
66
+ ></saiku-embed>
67
+ ```
68
+
69
+ ### An AI ask widget over a cube (v3.19, `kind="ai"`)
70
+
71
+ Point the token at a cube (rather than a saved query) and drop in a
72
+ plain-English ask box. Behind the scenes it POSTs to
73
+ `/rest/saiku/api/embed/ai/{cubeId}/ask`, which runs the question through
74
+ the server's configured LLM provider under the pinned owner's data
75
+ scope. Requires an AI-kind token (see "Minting an AI token" below).
76
+
77
+ ```html
78
+ <saiku-embed
79
+ server="..."
80
+ token="..."
81
+ kind="ai"
82
+ path="foodmart/FoodMart/FoodMart/Sales"
83
+ height="200px"
84
+ ></saiku-embed>
85
+ ```
86
+
87
+ ### A single KPI tile (v3.20, `render="kpi"`)
88
+
89
+ The most common embed shape — one governed figure rendered large. Derives its
90
+ value from the same records response as `render="table"`, so it needs no server
91
+ change. Point it at a saved query whose last measure is the headline number;
92
+ when the query carries a prior measure column, a delta chip is shown.
93
+
94
+ ```html
95
+ <saiku-embed
96
+ server="..."
97
+ token="..."
98
+ path="homes/admin/Examples/NetRevenue.saiku"
99
+ render="kpi"
100
+ height="160px"
101
+ ></saiku-embed>
102
+ ```
103
+
104
+ ### A saved query, sliced at embed time (v3.20, `filter`)
105
+
106
+ Pass slicer overrides as a JSON array. They ride the same validated slicer path
107
+ the dashboard filter tiles use — the saved query's cube binding and axes are
108
+ untouched, so a host can parameterise an embed without re-authoring the query.
109
+
110
+ ```html
111
+ <saiku-embed
112
+ server="..."
113
+ token="..."
114
+ path="homes/admin/Examples/Sales.saiku"
115
+ filter='[{"dimension":"Time","level":"Year","members":["[Time].[2024]"]}]'
116
+ ></saiku-embed>
117
+ ```
118
+
119
+ ### A persona-scoped AI ask (v3.20, `space`)
120
+
121
+ Add a `space` to a `kind="ai"` embed to scope the assistant to an admin-authored
122
+ [Agent Space](../../../docs/AGENT-SPACES-SPEC.md) persona. The persona's system
123
+ prompt, skill filter, and cube allowlist apply server-side. The cube stays
124
+ pinned by the token, so a space can only **narrow** what the guest reaches — if
125
+ the space's allowlist excludes the pinned cube, the ask fails closed.
126
+
127
+ ```html
128
+ <saiku-embed
129
+ server="..."
130
+ token="..."
131
+ kind="ai"
132
+ path="foodmart/FoodMart/FoodMart/Sales"
133
+ space="foodmart-sales-analyst"
134
+ height="240px"
135
+ ></saiku-embed>
136
+ ```
137
+
53
138
  ### A saved dashboard
54
139
 
55
140
  ```html
@@ -79,18 +164,43 @@ If the resource is marked publicly embeddable on the server
79
164
 
80
165
  | Attribute | Default | Notes |
81
166
  |-----------|-------------|------------------------------------------------------------------------|
82
- | `server` | _(required)_| Origin of the Saiku launcher, e.g. `https://demo.saiku.bi` |
83
- | `path` | _(required)_| Repository path of the saved query (`.saiku`) or dashboard (`.saikudash`) |
84
- | `kind` | `query` | `query` or `dashboard` |
167
+ | `server` | _(optional)_| Origin of the Saiku launcher, e.g. `https://demo.saiku.bi`. Leave empty for same-origin (v3.19+) |
168
+ | `path` | _(required)_| `kind=query`: saved query path (`.saiku`) `kind=dashboard`: dashboard path (`.saikudash`) — `kind=ai`: cube ref `connection/catalog/schema/cubeName` |
169
+ | `kind` | `query` | `query`, `dashboard`, or `ai` |
85
170
  | `token` | _(none)_ | Embed token from `POST /saiku/api/embed/tokens`. Omit for public reads |
86
- | `render` | `table` | For `kind=query`: `table` or `chart` |
171
+ | `render` | `table` | For `kind=query`: `table`, `matrix`, `chart`, or `kpi` (v3.20) |
87
172
  | `mode` | `bar` | For `render=chart`: `bar`, `line`, or `pie` |
88
173
  | `height` | `400px` | CSS height of the rendered surface |
174
+ | `space` | _(none)_ | For `kind=ai`: Agent Space persona id — scopes the ask server-side (v3.20) |
175
+ | `filter` | _(none)_ | For `kind=query`: JSON array of slicer overrides applied at embed time (v3.20) |
176
+ | `theme` | _(light)_ | `light`, `dark`, or `auto` (follow `prefers-color-scheme`) (v3.20) |
89
177
 
90
178
  The component re-renders whenever an attribute changes, so frameworks
91
179
  binding state to attrs (React's JSX, Vue's `:server="..."`, etc.) just
92
180
  work.
93
181
 
182
+ ## Events (v3.20)
183
+
184
+ The element emits namespaced `CustomEvent`s so the host page can react to what
185
+ happens inside the embed. All bubble and are `composed`, so a listener on the
186
+ `<saiku-embed>` element receives them:
187
+
188
+ | Event | `detail` | Fires when |
189
+ |-------------------|-----------------------------------|-----------------------------------------|
190
+ | `saiku:load` | `{ kind, rows }` | a query / matrix / kpi surface loads |
191
+ | `saiku:error` | `{ message }` | a query load fails (friendly message) |
192
+ | `saiku:select` | `{ row }` | a table row is clicked (`render=table`) |
193
+ | `saiku:ai-query` | `{ question, degraded }` | an AI ask resolves (`kind=ai`) |
194
+
195
+ ```js
196
+ const el = document.querySelector("saiku-embed");
197
+ el.addEventListener("saiku:load", (e) => console.log("loaded", e.detail.rows, "rows"));
198
+ el.addEventListener("saiku:select", (e) => showDetail(e.detail.row));
199
+ ```
200
+
201
+ In React (via `@concepttocloud/saiku-embed-react`) the same events are exposed
202
+ as `onLoad` / `onError` / `onSelect` / `onAiQuery` callback props.
203
+
94
204
  ## Server-side: minting a token
95
205
 
96
206
  Authenticated as a user who has GRANT on the saved query / dashboard:
@@ -108,6 +218,28 @@ curl -X POST 'https://YOUR-SAIKU/rest/saiku/api/embed/tokens' \
108
218
  # → { "status": "OK", "token": "tx-...", "expiresAt": ... }
109
219
  ```
110
220
 
221
+ ### Minting an AI token (v3.19)
222
+
223
+ For `kind="ai"` embeds. `resourcePath` is a cube ref rather than a
224
+ file path. Mint is admin-only for v1 (cube-level ACLs are a follow-up).
225
+
226
+ ```bash
227
+ curl -X POST 'https://YOUR-SAIKU/rest/saiku/api/embed/tokens' \
228
+ -u admin:admin \
229
+ -H 'Content-Type: application/json' \
230
+ -d '{
231
+ "resourceKind": "ai",
232
+ "resourcePath": "foodmart/FoodMart/FoodMart/Sales",
233
+ "ttlHours": 72,
234
+ "label": "DimSum widget on marketing site"
235
+ }'
236
+ ```
237
+
238
+ Server-side, an AI ask requires the launcher to have an LLM provider
239
+ configured (`saiku.ai.ask.provider = anthropic | openai` plus the
240
+ matching API key). Without it, the widget renders a degraded message
241
+ rather than an error.
242
+
111
243
  Paste the `token` into the host page's `<saiku-embed token="...">`.
112
244
  Tokens are server-authoritative — revoke any time via:
113
245
 
@@ -144,8 +276,14 @@ curl -X DELETE 'https://YOUR-SAIKU/rest/saiku/api/embed/public?kind=query&path=s
144
276
 
145
277
  The embed lives inside an
146
278
  [open shadow root](https://developer.mozilla.org/docs/Web/API/ShadowRoot),
147
- so host page CSS can't leak in and vice versa. To recolour the embed,
148
- set CSS variables on the host page:
279
+ so host page CSS can't leak in and vice versa.
280
+
281
+ For a quick dark surface, set `theme="dark"` (or `theme="auto"` to follow the
282
+ viewer's `prefers-color-scheme`) — it swaps the whole palette without the host
283
+ having to set each variable (v3.20). Leaving `theme` unset keeps the original
284
+ light palette, so existing embeds are unchanged.
285
+
286
+ To fine-tune individual colours, set CSS variables on the host page:
149
287
 
150
288
  ```css
151
289
  saiku-embed {
@@ -161,6 +299,28 @@ saiku-embed {
161
299
  }
162
300
  ```
163
301
 
302
+ ### Theming the chart itself
303
+
304
+ The variables above style the embed **chrome** (frame, header, table). To brand
305
+ the **chart series + axes**, set these (custom properties inherit through the
306
+ shadow boundary, so the canvas chart picks them up):
307
+
308
+ ```css
309
+ saiku-embed {
310
+ /* Series colour cycle — set as many as you need, 1..8, contiguously.
311
+ Any unset → the chart falls back to the built-in palette. */
312
+ --saiku-embed-chart-1: #2563eb;
313
+ --saiku-embed-chart-2: #16a34a;
314
+ --saiku-embed-chart-3: #dc2626;
315
+ /* …up to --saiku-embed-chart-8 */
316
+
317
+ /* Axis labels / legend / titles use --saiku-embed-fg;
318
+ axis + split lines use --saiku-embed-muted (both shared with the chrome). */
319
+ }
320
+ ```
321
+
322
+ An embed with none of these set renders exactly as before (ECharts defaults).
323
+
164
324
  ## Security model
165
325
 
166
326
  - **Header-only token transport.** The token travels as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@concepttocloud/saiku-embed",
3
- "version": "3.18.2",
3
+ "version": "3.20.0",
4
4
  "description": "<saiku-embed> Web Component — drop a saved Saiku query or dashboard into any page.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",