@aganzefelicite/responsekit-react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,304 +1,8 @@
1
1
  import { __export, formatValue, isDev, rowsToCsv } from './chunk-3FSZRQQU.js';
2
+ import { lazy, Suspense, Component, useMemo, useReducer, useState, useRef, useCallback, useEffect } from 'react';
2
3
  import ReactMarkdown from 'react-markdown';
3
4
  import remarkGfm from 'remark-gfm';
4
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { lazy, Suspense, Component, useReducer, useState, useRef, useCallback, useEffect } from 'react';
6
-
7
- // src/actions.ts
8
- var noopAction = () => {
9
- };
10
- function Markdown({ children, className }) {
11
- return /* @__PURE__ */ jsx("div", { className: className ?? "rk-markdown", children: /* @__PURE__ */ jsx(
12
- ReactMarkdown,
13
- {
14
- remarkPlugins: [remarkGfm],
15
- components: {
16
- a: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("a", { ...props, target: "_blank", rel: "noopener noreferrer" })
17
- },
18
- children
19
- }
20
- ) });
21
- }
22
- function TextBlock({ block }) {
23
- return /* @__PURE__ */ jsx("div", { className: "rk-block rk-block-text", children: /* @__PURE__ */ jsx(Markdown, { children: block.content }) });
24
- }
25
- var TREND_GLYPH = { UP: "\u25B2", DOWN: "\u25BC", STABLE: "\u2192" };
26
- function KpiBlock({ block }) {
27
- const items = Array.isArray(block.items) ? block.items : [];
28
- return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-kpi", "aria-label": block.title ?? "KPIs", children: [
29
- block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
30
- /* @__PURE__ */ jsx("div", { className: "rk-kpi-grid", children: items.map((item, i) => {
31
- const display = item.formattedValue ?? formatValue(item.value);
32
- const trend = item.trend;
33
- return /* @__PURE__ */ jsxs("div", { className: "rk-kpi-item", children: [
34
- /* @__PURE__ */ jsx("div", { className: "rk-kpi-label", children: item.label }),
35
- /* @__PURE__ */ jsx("div", { className: "rk-kpi-value", children: display }),
36
- trend || typeof item.percentageChange === "number" ? /* @__PURE__ */ jsxs(
37
- "div",
38
- {
39
- className: `rk-kpi-trend rk-kpi-trend-${(trend ?? "STABLE").toLowerCase()}`,
40
- children: [
41
- trend ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: TREND_GLYPH[trend] }) : null,
42
- typeof item.percentageChange === "number" ? /* @__PURE__ */ jsxs("span", { children: [
43
- item.percentageChange > 0 ? "+" : "",
44
- item.percentageChange,
45
- "%"
46
- ] }) : null
47
- ]
48
- }
49
- ) : null
50
- ] }, `${item.label}-${i}`);
51
- }) })
52
- ] });
53
- }
54
- var ChartRenderer = lazy(() => import('./ChartRenderer-URPZESVN.js'));
55
- function ChartBlock({ block }) {
56
- return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-chart", children: [
57
- block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
58
- /* @__PURE__ */ jsx(
59
- Suspense,
60
- {
61
- fallback: /* @__PURE__ */ jsx("div", { className: "rk-chart-placeholder", "aria-busy": "true", children: "Loading chart\u2026" }),
62
- children: /* @__PURE__ */ jsx(ChartRenderer, { block })
63
- }
64
- )
65
- ] });
66
- }
67
- function TableBlock({ block, onAction }) {
68
- const columns = Array.isArray(block.columns) ? block.columns : [];
69
- const rows = Array.isArray(block.rows) ? block.rows : [];
70
- const download = () => onAction({
71
- type: "DOWNLOAD",
72
- format: "CSV",
73
- filename: `${block.title ?? "table"}.csv`,
74
- data: rowsToCsv(columns, rows),
75
- blockId: block.id
76
- });
77
- return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-table", children: [
78
- /* @__PURE__ */ jsxs("div", { className: "rk-block-header", children: [
79
- block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
80
- rows.length > 0 ? /* @__PURE__ */ jsx("button", { type: "button", className: "rk-action", onClick: download, children: "Download CSV" }) : null
81
- ] }),
82
- /* @__PURE__ */ jsxs("div", { className: "rk-table-scroll", children: [
83
- /* @__PURE__ */ jsxs("table", { className: "rk-table", children: [
84
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("th", { scope: "col", children: col.label }, col.key)) }) }),
85
- /* @__PURE__ */ jsx("tbody", { children: rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("td", { children: formatValue(row[col.key]) }, col.key)) }, r)) })
86
- ] }),
87
- rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rk-empty", children: "No data." }) : null
88
- ] })
89
- ] });
90
- }
91
- var SEVERITY_GLYPH = {
92
- INFO: "\u2139",
93
- SUCCESS: "\u2713",
94
- WARNING: "\u26A0",
95
- CRITICAL: "\u2715"
96
- };
97
- function InsightBlock({ block }) {
98
- const severity = ["INFO", "SUCCESS", "WARNING", "CRITICAL"].includes(
99
- block.severity
100
- ) ? block.severity : "INFO";
101
- return /* @__PURE__ */ jsxs(
102
- "aside",
103
- {
104
- className: `rk-block rk-block-insight rk-severity-${severity.toLowerCase()}`,
105
- role: "note",
106
- children: [
107
- /* @__PURE__ */ jsx("span", { className: "rk-insight-icon", "aria-hidden": true, children: SEVERITY_GLYPH[severity] }),
108
- /* @__PURE__ */ jsxs("div", { className: "rk-insight-body", children: [
109
- /* @__PURE__ */ jsx("div", { className: "rk-insight-title", children: block.title }),
110
- /* @__PURE__ */ jsx("div", { className: "rk-insight-desc", children: block.description })
111
- ] })
112
- ]
113
- }
114
- );
115
- }
116
- function RecommendationBlock({
117
- block
118
- }) {
119
- const items = Array.isArray(block.items) ? block.items : [];
120
- return /* @__PURE__ */ jsx("section", { className: "rk-block rk-block-recommendation", children: /* @__PURE__ */ jsx("ul", { className: "rk-recommendation-list", children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "rk-recommendation-item", children: [
121
- /* @__PURE__ */ jsx("div", { className: "rk-recommendation-title", children: item.title }),
122
- /* @__PURE__ */ jsx("div", { className: "rk-recommendation-desc", children: item.description })
123
- ] }, `${item.title}-${i}`)) }) });
124
- }
125
- function FilterControl({ filter }) {
126
- const options = Array.isArray(filter.options) ? filter.options : [];
127
- switch (filter.filterType) {
128
- case "SELECT":
129
- case "MULTI_SELECT":
130
- return /* @__PURE__ */ jsx("select", { disabled: true, multiple: filter.filterType === "MULTI_SELECT", "aria-label": filter.label, children: options.map((o) => /* @__PURE__ */ jsx("option", { value: o, children: o }, o)) });
131
- case "DATE_RANGE":
132
- return /* @__PURE__ */ jsxs("span", { className: "rk-filter-range", children: [
133
- /* @__PURE__ */ jsx("input", { type: "date", disabled: true, "aria-label": `${filter.label} from` }),
134
- /* @__PURE__ */ jsx("input", { type: "date", disabled: true, "aria-label": `${filter.label} to` })
135
- ] });
136
- case "NUMBER_RANGE":
137
- return /* @__PURE__ */ jsxs("span", { className: "rk-filter-range", children: [
138
- /* @__PURE__ */ jsx("input", { type: "number", disabled: true, "aria-label": `${filter.label} min`, placeholder: "min" }),
139
- /* @__PURE__ */ jsx("input", { type: "number", disabled: true, "aria-label": `${filter.label} max`, placeholder: "max" })
140
- ] });
141
- case "SEARCH":
142
- default:
143
- return /* @__PURE__ */ jsx("input", { type: "search", disabled: true, "aria-label": filter.label, placeholder: "Search\u2026" });
144
- }
145
- }
146
- function FilterBlock({ block }) {
147
- const filters = Array.isArray(block.filters) ? block.filters : [];
148
- return /* @__PURE__ */ jsx("section", { className: "rk-block rk-block-filter", "aria-label": "Filters (read-only)", children: /* @__PURE__ */ jsx("div", { className: "rk-filter-row", children: filters.map((filter, i) => /* @__PURE__ */ jsxs("label", { className: "rk-filter", children: [
149
- /* @__PURE__ */ jsx("span", { className: "rk-filter-label", children: filter.label }),
150
- /* @__PURE__ */ jsx(FilterControl, { filter })
151
- ] }, `${filter.field}-${i}`)) }) });
152
- }
153
- function FallbackBlock({ block }) {
154
- const type = typeof block?.type === "string" ? block.type : "unknown";
155
- return /* @__PURE__ */ jsxs("div", { className: "rk-block rk-block-fallback", role: "note", children: [
156
- /* @__PURE__ */ jsxs("div", { className: "rk-fallback-label", children: [
157
- "Unsupported block: ",
158
- type
159
- ] }),
160
- isDev() ? /* @__PURE__ */ jsxs("details", { className: "rk-fallback-raw", children: [
161
- /* @__PURE__ */ jsx("summary", { children: "Raw payload (dev only)" }),
162
- /* @__PURE__ */ jsx("pre", { children: safeStringify(block) })
163
- ] }) : null
164
- ] });
165
- }
166
- function safeStringify(value) {
167
- try {
168
- return JSON.stringify(value, null, 2);
169
- } catch {
170
- return String(value);
171
- }
172
- }
173
-
174
- // src/blocks/index.ts
175
- var defaultBlockComponents = {
176
- TEXT: TextBlock,
177
- KPI: KpiBlock,
178
- CHART: ChartBlock,
179
- TABLE: TableBlock,
180
- INSIGHT: InsightBlock,
181
- RECOMMENDATION: RecommendationBlock,
182
- FILTER: FilterBlock
183
- };
184
-
185
- // src/registry.ts
186
- var globalRegistry = /* @__PURE__ */ new Map();
187
- function registerBlock(options) {
188
- if (!options.override && globalRegistry.has(options.type)) return;
189
- globalRegistry.set(options.type, options.component);
190
- }
191
- function unregisterBlock(type) {
192
- globalRegistry.delete(type);
193
- }
194
- function registeredBlockTypes() {
195
- return [...globalRegistry.keys()];
196
- }
197
- function resolveBlockComponent(type, overrides) {
198
- return overrides?.[type] ?? globalRegistry.get(type) ?? defaultBlockComponents[type] ?? FallbackBlock;
199
- }
200
- var ErrorBoundary = class extends Component {
201
- constructor() {
202
- super(...arguments);
203
- this.state = { error: null };
204
- }
205
- static getDerivedStateFromError(error) {
206
- return { error };
207
- }
208
- componentDidCatch(error, info) {
209
- this.props.onError?.(error, info);
210
- }
211
- render() {
212
- if (this.state.error) return this.props.fallback(this.state.error);
213
- return this.props.children;
214
- }
215
- };
216
- function AiRenderer({
217
- response,
218
- components,
219
- onAction = noopAction,
220
- className
221
- }) {
222
- if (!response || typeof response !== "object" || !("type" in response)) {
223
- return /* @__PURE__ */ jsx(ErrorView, { message: "No response to display." });
224
- }
225
- return /* @__PURE__ */ jsxs("div", { className: className ?? "rk-response", "data-response-type": response.type, children: [
226
- renderBody(response, components, onAction),
227
- /* @__PURE__ */ jsx(MetadataFooter, { metadata: response.metadata, onAction, responseId: response.id })
228
- ] });
229
- }
230
- function renderBody(response, components, onAction) {
231
- switch (response.type) {
232
- case "TEXT": {
233
- const { format, value } = response.content;
234
- return format === "MARKDOWN" ? /* @__PURE__ */ jsx(Markdown, { children: value }) : /* @__PURE__ */ jsx("p", { className: "rk-text-plain", children: value });
235
- }
236
- case "ANALYTICS": {
237
- const { title, blocks } = response.content;
238
- const list = Array.isArray(blocks) ? blocks : [];
239
- return /* @__PURE__ */ jsxs(Fragment, { children: [
240
- title ? /* @__PURE__ */ jsx("h2", { className: "rk-response-title", children: title }) : null,
241
- /* @__PURE__ */ jsx("div", { className: "rk-blocks", children: list.map((block, i) => /* @__PURE__ */ jsx(
242
- BlockSlot,
243
- {
244
- block,
245
- components,
246
- onAction
247
- },
248
- blockKey(block, i)
249
- )) })
250
- ] });
251
- }
252
- case "ERROR":
253
- return /* @__PURE__ */ jsx(ErrorView, { message: response.content.message });
254
- default:
255
- return /* @__PURE__ */ jsx(ErrorView, { message: "Unsupported response type." });
256
- }
257
- }
258
- function BlockSlot({
259
- block,
260
- components,
261
- onAction
262
- }) {
263
- const type = typeof block?.type === "string" ? block.type : "UNKNOWN";
264
- const Component2 = resolveBlockComponent(type, components);
265
- return /* @__PURE__ */ jsx(ErrorBoundary, { fallback: () => /* @__PURE__ */ jsx(FallbackBlock, { block, onAction }), children: /* @__PURE__ */ jsx(Component2, { block, onAction }) });
266
- }
267
- function ErrorView({ message }) {
268
- return /* @__PURE__ */ jsxs("div", { className: "rk-block rk-error", role: "alert", children: [
269
- /* @__PURE__ */ jsx("span", { className: "rk-error-icon", "aria-hidden": true, children: "\u2715" }),
270
- /* @__PURE__ */ jsx("span", { className: "rk-error-message", children: message })
271
- ] });
272
- }
273
- function MetadataFooter({
274
- metadata,
275
- onAction,
276
- responseId
277
- }) {
278
- if (!metadata) return null;
279
- const { generatedAt, dataSource, refreshable } = metadata;
280
- if (!generatedAt && !dataSource && !refreshable) return null;
281
- return /* @__PURE__ */ jsxs("footer", { className: "rk-meta", children: [
282
- generatedAt ? /* @__PURE__ */ jsx("span", { className: "rk-meta-time", children: formatTime(generatedAt) }) : null,
283
- dataSource ? /* @__PURE__ */ jsx("span", { className: "rk-meta-source", children: dataSource }) : null,
284
- refreshable ? /* @__PURE__ */ jsx(
285
- "button",
286
- {
287
- type: "button",
288
- className: "rk-action rk-meta-refresh",
289
- onClick: () => onAction({ type: "REFRESH", responseId }),
290
- children: "Refresh"
291
- }
292
- ) : null
293
- ] });
294
- }
295
- function formatTime(iso) {
296
- const d = new Date(iso);
297
- return Number.isNaN(d.getTime()) ? iso : d.toLocaleString();
298
- }
299
- function blockKey(block, index) {
300
- return typeof block?.id === "string" && block.id ? block.id : `block-${index}`;
301
- }
302
6
 
303
7
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
304
8
  var external_exports = {};
@@ -4453,198 +4157,495 @@ var toUnknownBlock = (raw) => {
4453
4157
  type: typeof obj.type === "string" ? obj.type : "UNKNOWN"
4454
4158
  };
4455
4159
  };
4456
- var blockSchema = external_exports.union([
4457
- knownBlockSchema,
4458
- external_exports.any().transform(toUnknownBlock)
4459
- ]);
4460
- var textResponseSchema = external_exports.object({
4461
- id: external_exports.string(),
4462
- type: external_exports.literal("TEXT"),
4463
- content: external_exports.object({ format: textFormatSchema, value: external_exports.string() }),
4464
- metadata: responseMetadataSchema.optional()
4465
- });
4466
- var analyticsResponseSchema = external_exports.object({
4467
- id: external_exports.string(),
4468
- type: external_exports.literal("ANALYTICS"),
4469
- content: external_exports.object({
4470
- title: external_exports.string().optional(),
4471
- // Use the lenient block schema so one malformed block can't sink the whole response.
4472
- blocks: external_exports.array(blockSchema)
4473
- }),
4474
- metadata: responseMetadataSchema.optional()
4475
- });
4476
- var errorResponseSchema = external_exports.object({
4477
- id: external_exports.string(),
4478
- type: external_exports.literal("ERROR"),
4479
- content: external_exports.object({ message: external_exports.string() }),
4480
- metadata: responseMetadataSchema.optional()
4481
- });
4482
- var aiResponseSchema = external_exports.discriminatedUnion("type", [
4483
- textResponseSchema,
4484
- analyticsResponseSchema,
4485
- errorResponseSchema
4486
- ]);
4487
- var makeErrorResponse = (message, id) => ({
4488
- id: id ?? genId("error"),
4489
- type: "ERROR",
4490
- content: { message }
4491
- });
4492
- function parseAiResponse(input) {
4493
- const result = aiResponseSchema.safeParse(input);
4494
- if (result.success) return result.data;
4495
- return makeErrorResponse(
4496
- "The response could not be displayed (it did not match the expected format)."
4160
+ var blockSchema = external_exports.union([
4161
+ knownBlockSchema,
4162
+ external_exports.any().transform(toUnknownBlock)
4163
+ ]);
4164
+ var textResponseSchema = external_exports.object({
4165
+ id: external_exports.string(),
4166
+ type: external_exports.literal("TEXT"),
4167
+ content: external_exports.object({ format: textFormatSchema, value: external_exports.string() }),
4168
+ metadata: responseMetadataSchema.optional()
4169
+ });
4170
+ var analyticsResponseSchema = external_exports.object({
4171
+ id: external_exports.string(),
4172
+ type: external_exports.literal("ANALYTICS"),
4173
+ content: external_exports.object({
4174
+ title: external_exports.string().optional(),
4175
+ // Use the lenient block schema so one malformed block can't sink the whole response.
4176
+ blocks: external_exports.array(blockSchema)
4177
+ }),
4178
+ metadata: responseMetadataSchema.optional()
4179
+ });
4180
+ var errorResponseSchema = external_exports.object({
4181
+ id: external_exports.string(),
4182
+ type: external_exports.literal("ERROR"),
4183
+ content: external_exports.object({ message: external_exports.string() }),
4184
+ metadata: responseMetadataSchema.optional()
4185
+ });
4186
+ var aiResponseSchema = external_exports.discriminatedUnion("type", [
4187
+ textResponseSchema,
4188
+ analyticsResponseSchema,
4189
+ errorResponseSchema
4190
+ ]);
4191
+ var makeErrorResponse = (message, id) => ({
4192
+ id: id ?? genId("error"),
4193
+ type: "ERROR",
4194
+ content: { message }
4195
+ });
4196
+ function parseAiResponse(input) {
4197
+ const result = aiResponseSchema.safeParse(input);
4198
+ if (result.success) return result.data;
4199
+ return makeErrorResponse(
4200
+ "The response could not be displayed (it did not match the expected format)."
4201
+ );
4202
+ }
4203
+ function safeParseAiResponse(input) {
4204
+ return aiResponseSchema.safeParse(input);
4205
+ }
4206
+ function parseBlock(input) {
4207
+ return blockSchema.parse(input);
4208
+ }
4209
+ var progressEventSchema = external_exports.object({
4210
+ phase: progressPhaseSchema,
4211
+ message: external_exports.string().optional(),
4212
+ tool: external_exports.string().optional()
4213
+ });
4214
+ var responseStartSchema = external_exports.object({
4215
+ id: external_exports.string(),
4216
+ type: responseTypeSchema,
4217
+ metadata: responseMetadataSchema.optional(),
4218
+ title: external_exports.string().optional()
4219
+ });
4220
+ var contentEventSchema = external_exports.object({
4221
+ format: textFormatSchema.optional(),
4222
+ value: external_exports.string().optional(),
4223
+ message: external_exports.string().optional()
4224
+ });
4225
+ var responseEndSchema = external_exports.object({
4226
+ status: external_exports.enum(["COMPLETED", "ERROR"])
4227
+ });
4228
+ var errorEventSchema = external_exports.object({
4229
+ message: external_exports.string().optional()
4230
+ });
4231
+ function parseSseEvent(eventName, raw) {
4232
+ let json;
4233
+ try {
4234
+ json = raw.length ? JSON.parse(raw) : {};
4235
+ } catch {
4236
+ return { event: "error", data: { message: "Malformed event payload from server." } };
4237
+ }
4238
+ switch (eventName) {
4239
+ case "progress": {
4240
+ const r = progressEventSchema.safeParse(json);
4241
+ return r.success ? { event: "progress", data: r.data } : { event: "progress", data: { phase: "THINKING" } };
4242
+ }
4243
+ case "response_start": {
4244
+ const r = responseStartSchema.safeParse(json);
4245
+ return r.success ? { event: "response_start", data: r.data } : { event: "error", data: { message: "Invalid response_start event." } };
4246
+ }
4247
+ case "content": {
4248
+ const r = contentEventSchema.safeParse(json);
4249
+ return r.success ? { event: "content", data: r.data } : { event: "content", data: {} };
4250
+ }
4251
+ case "block":
4252
+ return { event: "block", data: blockSchema.parse(json) };
4253
+ case "response_end": {
4254
+ const r = responseEndSchema.safeParse(json);
4255
+ return r.success ? { event: "response_end", data: r.data } : { event: "response_end", data: { status: "COMPLETED" } };
4256
+ }
4257
+ case "error":
4258
+ default: {
4259
+ const r = errorEventSchema.safeParse(json);
4260
+ return { event: "error", data: r.success ? r.data : {} };
4261
+ }
4262
+ }
4263
+ }
4264
+ var initialStreamState = { phase: "idle" };
4265
+ function startResponse(data) {
4266
+ const type = data.type;
4267
+ const base = { id: data.id, metadata: data.metadata };
4268
+ switch (type) {
4269
+ case "ANALYTICS":
4270
+ return { ...base, type: "ANALYTICS", content: { title: data.title, blocks: [] } };
4271
+ case "ERROR":
4272
+ return { ...base, type: "ERROR", content: { message: "" } };
4273
+ case "TEXT":
4274
+ default:
4275
+ return { ...base, type: "TEXT", content: { format: "PLAIN_TEXT", value: "" } };
4276
+ }
4277
+ }
4278
+ function streamReducer(state, event) {
4279
+ switch (event.event) {
4280
+ case "progress":
4281
+ return {
4282
+ ...state,
4283
+ phase: event.data.phase,
4284
+ progressMessage: event.data.message ?? state.progressMessage,
4285
+ tool: event.data.tool
4286
+ };
4287
+ case "response_start":
4288
+ return {
4289
+ ...state,
4290
+ phase: "COMPOSING",
4291
+ tool: void 0,
4292
+ response: startResponse(event.data),
4293
+ error: void 0
4294
+ };
4295
+ case "content": {
4296
+ const response = state.response;
4297
+ if (!response) return state;
4298
+ if (response.type === "TEXT") {
4299
+ return {
4300
+ ...state,
4301
+ response: {
4302
+ ...response,
4303
+ content: {
4304
+ format: event.data.format ?? response.content.format,
4305
+ value: event.data.value ?? response.content.value
4306
+ }
4307
+ }
4308
+ };
4309
+ }
4310
+ if (response.type === "ERROR") {
4311
+ return {
4312
+ ...state,
4313
+ response: {
4314
+ ...response,
4315
+ content: { message: event.data.message ?? response.content.message }
4316
+ }
4317
+ };
4318
+ }
4319
+ return state;
4320
+ }
4321
+ case "block": {
4322
+ const response = state.response;
4323
+ if (!response || response.type !== "ANALYTICS") return state;
4324
+ return {
4325
+ ...state,
4326
+ response: {
4327
+ ...response,
4328
+ content: {
4329
+ ...response.content,
4330
+ blocks: [...response.content.blocks, event.data]
4331
+ }
4332
+ }
4333
+ };
4334
+ }
4335
+ case "response_end": {
4336
+ if (event.data.status === "ERROR") {
4337
+ const message = state.response?.type === "ERROR" && state.response.content.message || state.error || "The request failed.";
4338
+ return { ...state, phase: "error", error: message };
4339
+ }
4340
+ return { ...state, phase: "done", progressMessage: void 0, tool: void 0 };
4341
+ }
4342
+ case "error":
4343
+ return {
4344
+ ...state,
4345
+ phase: "error",
4346
+ error: event.data.message ?? "The stream encountered a fatal error."
4347
+ };
4348
+ default:
4349
+ return state;
4350
+ }
4351
+ }
4352
+
4353
+ // src/actions.ts
4354
+ var noopAction = () => {
4355
+ };
4356
+ function Markdown({ children, className }) {
4357
+ return /* @__PURE__ */ jsx("div", { className: className ?? "rk-markdown", children: /* @__PURE__ */ jsx(
4358
+ ReactMarkdown,
4359
+ {
4360
+ remarkPlugins: [remarkGfm],
4361
+ components: {
4362
+ a: ({ node: _node, ...props }) => /* @__PURE__ */ jsx("a", { ...props, target: "_blank", rel: "noopener noreferrer" })
4363
+ },
4364
+ children
4365
+ }
4366
+ ) });
4367
+ }
4368
+ function TextBlock({ block }) {
4369
+ return /* @__PURE__ */ jsx("div", { className: "rk-block rk-block-text", children: /* @__PURE__ */ jsx(Markdown, { children: block.content }) });
4370
+ }
4371
+ var TREND_GLYPH = { UP: "\u25B2", DOWN: "\u25BC", STABLE: "\u2192" };
4372
+ function KpiBlock({ block }) {
4373
+ const items = Array.isArray(block.items) ? block.items : [];
4374
+ return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-kpi", "aria-label": block.title ?? "KPIs", children: [
4375
+ block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
4376
+ /* @__PURE__ */ jsx("div", { className: "rk-kpi-grid", children: items.map((item, i) => {
4377
+ const display = item.formattedValue ?? formatValue(item.value);
4378
+ const trend = item.trend;
4379
+ return /* @__PURE__ */ jsxs("div", { className: "rk-kpi-item", children: [
4380
+ /* @__PURE__ */ jsx("div", { className: "rk-kpi-label", children: item.label }),
4381
+ /* @__PURE__ */ jsx("div", { className: "rk-kpi-value", children: display }),
4382
+ trend || typeof item.percentageChange === "number" ? /* @__PURE__ */ jsxs(
4383
+ "div",
4384
+ {
4385
+ className: `rk-kpi-trend rk-kpi-trend-${(trend ?? "STABLE").toLowerCase()}`,
4386
+ children: [
4387
+ trend ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: TREND_GLYPH[trend] }) : null,
4388
+ typeof item.percentageChange === "number" ? /* @__PURE__ */ jsxs("span", { children: [
4389
+ item.percentageChange > 0 ? "+" : "",
4390
+ item.percentageChange,
4391
+ "%"
4392
+ ] }) : null
4393
+ ]
4394
+ }
4395
+ ) : null
4396
+ ] }, `${item.label}-${i}`);
4397
+ }) })
4398
+ ] });
4399
+ }
4400
+ var ChartRenderer = lazy(() => import('./ChartRenderer-URPZESVN.js'));
4401
+ function ChartBlock({ block }) {
4402
+ return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-chart", children: [
4403
+ block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
4404
+ /* @__PURE__ */ jsx(
4405
+ Suspense,
4406
+ {
4407
+ fallback: /* @__PURE__ */ jsx("div", { className: "rk-chart-placeholder", "aria-busy": "true", children: "Loading chart\u2026" }),
4408
+ children: /* @__PURE__ */ jsx(ChartRenderer, { block })
4409
+ }
4410
+ )
4411
+ ] });
4412
+ }
4413
+ function TableBlock({ block, onAction }) {
4414
+ const columns = Array.isArray(block.columns) ? block.columns : [];
4415
+ const rows = Array.isArray(block.rows) ? block.rows : [];
4416
+ const download = () => onAction({
4417
+ type: "DOWNLOAD",
4418
+ format: "CSV",
4419
+ filename: `${block.title ?? "table"}.csv`,
4420
+ data: rowsToCsv(columns, rows),
4421
+ blockId: block.id
4422
+ });
4423
+ return /* @__PURE__ */ jsxs("section", { className: "rk-block rk-block-table", children: [
4424
+ /* @__PURE__ */ jsxs("div", { className: "rk-block-header", children: [
4425
+ block.title ? /* @__PURE__ */ jsx("h3", { className: "rk-block-title", children: block.title }) : null,
4426
+ rows.length > 0 ? /* @__PURE__ */ jsx("button", { type: "button", className: "rk-action", onClick: download, children: "Download CSV" }) : null
4427
+ ] }),
4428
+ /* @__PURE__ */ jsxs("div", { className: "rk-table-scroll", children: [
4429
+ /* @__PURE__ */ jsxs("table", { className: "rk-table", children: [
4430
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("th", { scope: "col", children: col.label }, col.key)) }) }),
4431
+ /* @__PURE__ */ jsx("tbody", { children: rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("td", { children: formatValue(row[col.key]) }, col.key)) }, r)) })
4432
+ ] }),
4433
+ rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "rk-empty", children: "No data." }) : null
4434
+ ] })
4435
+ ] });
4436
+ }
4437
+ var SEVERITY_GLYPH = {
4438
+ INFO: "\u2139",
4439
+ SUCCESS: "\u2713",
4440
+ WARNING: "\u26A0",
4441
+ CRITICAL: "\u2715"
4442
+ };
4443
+ function InsightBlock({ block }) {
4444
+ const severity = ["INFO", "SUCCESS", "WARNING", "CRITICAL"].includes(
4445
+ block.severity
4446
+ ) ? block.severity : "INFO";
4447
+ return /* @__PURE__ */ jsxs(
4448
+ "aside",
4449
+ {
4450
+ className: `rk-block rk-block-insight rk-severity-${severity.toLowerCase()}`,
4451
+ role: "note",
4452
+ children: [
4453
+ /* @__PURE__ */ jsx("span", { className: "rk-insight-icon", "aria-hidden": true, children: SEVERITY_GLYPH[severity] }),
4454
+ /* @__PURE__ */ jsxs("div", { className: "rk-insight-body", children: [
4455
+ /* @__PURE__ */ jsx("div", { className: "rk-insight-title", children: block.title }),
4456
+ /* @__PURE__ */ jsx("div", { className: "rk-insight-desc", children: block.description })
4457
+ ] })
4458
+ ]
4459
+ }
4497
4460
  );
4498
4461
  }
4499
- function safeParseAiResponse(input) {
4500
- return aiResponseSchema.safeParse(input);
4462
+ function RecommendationBlock({
4463
+ block
4464
+ }) {
4465
+ const items = Array.isArray(block.items) ? block.items : [];
4466
+ return /* @__PURE__ */ jsx("section", { className: "rk-block rk-block-recommendation", children: /* @__PURE__ */ jsx("ul", { className: "rk-recommendation-list", children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "rk-recommendation-item", children: [
4467
+ /* @__PURE__ */ jsx("div", { className: "rk-recommendation-title", children: item.title }),
4468
+ /* @__PURE__ */ jsx("div", { className: "rk-recommendation-desc", children: item.description })
4469
+ ] }, `${item.title}-${i}`)) }) });
4501
4470
  }
4502
- function parseBlock(input) {
4503
- return blockSchema.parse(input);
4471
+ function FilterControl({ filter }) {
4472
+ const options = Array.isArray(filter.options) ? filter.options : [];
4473
+ switch (filter.filterType) {
4474
+ case "SELECT":
4475
+ case "MULTI_SELECT":
4476
+ return /* @__PURE__ */ jsx("select", { disabled: true, multiple: filter.filterType === "MULTI_SELECT", "aria-label": filter.label, children: options.map((o) => /* @__PURE__ */ jsx("option", { value: o, children: o }, o)) });
4477
+ case "DATE_RANGE":
4478
+ return /* @__PURE__ */ jsxs("span", { className: "rk-filter-range", children: [
4479
+ /* @__PURE__ */ jsx("input", { type: "date", disabled: true, "aria-label": `${filter.label} from` }),
4480
+ /* @__PURE__ */ jsx("input", { type: "date", disabled: true, "aria-label": `${filter.label} to` })
4481
+ ] });
4482
+ case "NUMBER_RANGE":
4483
+ return /* @__PURE__ */ jsxs("span", { className: "rk-filter-range", children: [
4484
+ /* @__PURE__ */ jsx("input", { type: "number", disabled: true, "aria-label": `${filter.label} min`, placeholder: "min" }),
4485
+ /* @__PURE__ */ jsx("input", { type: "number", disabled: true, "aria-label": `${filter.label} max`, placeholder: "max" })
4486
+ ] });
4487
+ case "SEARCH":
4488
+ default:
4489
+ return /* @__PURE__ */ jsx("input", { type: "search", disabled: true, "aria-label": filter.label, placeholder: "Search\u2026" });
4490
+ }
4491
+ }
4492
+ function FilterBlock({ block }) {
4493
+ const filters = Array.isArray(block.filters) ? block.filters : [];
4494
+ return /* @__PURE__ */ jsx("section", { className: "rk-block rk-block-filter", "aria-label": "Filters (read-only)", children: /* @__PURE__ */ jsx("div", { className: "rk-filter-row", children: filters.map((filter, i) => /* @__PURE__ */ jsxs("label", { className: "rk-filter", children: [
4495
+ /* @__PURE__ */ jsx("span", { className: "rk-filter-label", children: filter.label }),
4496
+ /* @__PURE__ */ jsx(FilterControl, { filter })
4497
+ ] }, `${filter.field}-${i}`)) }) });
4498
+ }
4499
+ function FallbackBlock({ block }) {
4500
+ const type = typeof block?.type === "string" ? block.type : "unknown";
4501
+ return /* @__PURE__ */ jsxs("div", { className: "rk-block rk-block-fallback", role: "note", children: [
4502
+ /* @__PURE__ */ jsxs("div", { className: "rk-fallback-label", children: [
4503
+ "Unsupported block: ",
4504
+ type
4505
+ ] }),
4506
+ isDev() ? /* @__PURE__ */ jsxs("details", { className: "rk-fallback-raw", children: [
4507
+ /* @__PURE__ */ jsx("summary", { children: "Raw payload (dev only)" }),
4508
+ /* @__PURE__ */ jsx("pre", { children: safeStringify(block) })
4509
+ ] }) : null
4510
+ ] });
4504
4511
  }
4505
- var progressEventSchema = external_exports.object({
4506
- phase: progressPhaseSchema,
4507
- message: external_exports.string().optional(),
4508
- tool: external_exports.string().optional()
4509
- });
4510
- var responseStartSchema = external_exports.object({
4511
- id: external_exports.string(),
4512
- type: responseTypeSchema,
4513
- metadata: responseMetadataSchema.optional(),
4514
- title: external_exports.string().optional()
4515
- });
4516
- var contentEventSchema = external_exports.object({
4517
- format: textFormatSchema.optional(),
4518
- value: external_exports.string().optional(),
4519
- message: external_exports.string().optional()
4520
- });
4521
- var responseEndSchema = external_exports.object({
4522
- status: external_exports.enum(["COMPLETED", "ERROR"])
4523
- });
4524
- var errorEventSchema = external_exports.object({
4525
- message: external_exports.string().optional()
4526
- });
4527
- function parseSseEvent(eventName, raw) {
4528
- let json;
4512
+ function safeStringify(value) {
4529
4513
  try {
4530
- json = raw.length ? JSON.parse(raw) : {};
4514
+ return JSON.stringify(value, null, 2);
4531
4515
  } catch {
4532
- return { event: "error", data: { message: "Malformed event payload from server." } };
4533
- }
4534
- switch (eventName) {
4535
- case "progress": {
4536
- const r = progressEventSchema.safeParse(json);
4537
- return r.success ? { event: "progress", data: r.data } : { event: "progress", data: { phase: "THINKING" } };
4538
- }
4539
- case "response_start": {
4540
- const r = responseStartSchema.safeParse(json);
4541
- return r.success ? { event: "response_start", data: r.data } : { event: "error", data: { message: "Invalid response_start event." } };
4542
- }
4543
- case "content": {
4544
- const r = contentEventSchema.safeParse(json);
4545
- return r.success ? { event: "content", data: r.data } : { event: "content", data: {} };
4546
- }
4547
- case "block":
4548
- return { event: "block", data: blockSchema.parse(json) };
4549
- case "response_end": {
4550
- const r = responseEndSchema.safeParse(json);
4551
- return r.success ? { event: "response_end", data: r.data } : { event: "response_end", data: { status: "COMPLETED" } };
4552
- }
4553
- case "error":
4554
- default: {
4555
- const r = errorEventSchema.safeParse(json);
4556
- return { event: "error", data: r.success ? r.data : {} };
4557
- }
4516
+ return String(value);
4558
4517
  }
4559
4518
  }
4560
- var initialStreamState = { phase: "idle" };
4561
- function startResponse(data) {
4562
- const type = data.type;
4563
- const base = { id: data.id, metadata: data.metadata };
4564
- switch (type) {
4565
- case "ANALYTICS":
4566
- return { ...base, type: "ANALYTICS", content: { title: data.title, blocks: [] } };
4567
- case "ERROR":
4568
- return { ...base, type: "ERROR", content: { message: "" } };
4569
- case "TEXT":
4570
- default:
4571
- return { ...base, type: "TEXT", content: { format: "PLAIN_TEXT", value: "" } };
4519
+
4520
+ // src/blocks/index.ts
4521
+ var defaultBlockComponents = {
4522
+ TEXT: TextBlock,
4523
+ KPI: KpiBlock,
4524
+ CHART: ChartBlock,
4525
+ TABLE: TableBlock,
4526
+ INSIGHT: InsightBlock,
4527
+ RECOMMENDATION: RecommendationBlock,
4528
+ FILTER: FilterBlock
4529
+ };
4530
+
4531
+ // src/registry.ts
4532
+ var globalRegistry = /* @__PURE__ */ new Map();
4533
+ function registerBlock(options) {
4534
+ if (!options.override && globalRegistry.has(options.type)) return;
4535
+ globalRegistry.set(options.type, options.component);
4536
+ }
4537
+ function unregisterBlock(type) {
4538
+ globalRegistry.delete(type);
4539
+ }
4540
+ function registeredBlockTypes() {
4541
+ return [...globalRegistry.keys()];
4542
+ }
4543
+ function resolveBlockComponent(type, overrides) {
4544
+ return overrides?.[type] ?? globalRegistry.get(type) ?? defaultBlockComponents[type] ?? FallbackBlock;
4545
+ }
4546
+ var ErrorBoundary = class extends Component {
4547
+ constructor() {
4548
+ super(...arguments);
4549
+ this.state = { error: null };
4550
+ }
4551
+ static getDerivedStateFromError(error) {
4552
+ return { error };
4553
+ }
4554
+ componentDidCatch(error, info) {
4555
+ this.props.onError?.(error, info);
4556
+ }
4557
+ render() {
4558
+ if (this.state.error) return this.props.fallback(this.state.error);
4559
+ return this.props.children;
4560
+ }
4561
+ };
4562
+ function AiRenderer({
4563
+ response,
4564
+ components,
4565
+ onAction = noopAction,
4566
+ className
4567
+ }) {
4568
+ const parsed = useMemo(() => parseAiResponse(response), [response]);
4569
+ if (response == null) {
4570
+ return /* @__PURE__ */ jsx(ErrorView, { message: "No response to display." });
4572
4571
  }
4572
+ return /* @__PURE__ */ jsxs("div", { className: className ?? "rk-response", "data-response-type": parsed.type, children: [
4573
+ renderBody(parsed, components, onAction),
4574
+ /* @__PURE__ */ jsx(MetadataFooter, { metadata: parsed.metadata, onAction, responseId: parsed.id })
4575
+ ] });
4573
4576
  }
4574
- function streamReducer(state, event) {
4575
- switch (event.event) {
4576
- case "progress":
4577
- return {
4578
- ...state,
4579
- phase: event.data.phase,
4580
- progressMessage: event.data.message ?? state.progressMessage,
4581
- tool: event.data.tool
4582
- };
4583
- case "response_start":
4584
- return {
4585
- ...state,
4586
- phase: "COMPOSING",
4587
- tool: void 0,
4588
- response: startResponse(event.data),
4589
- error: void 0
4590
- };
4591
- case "content": {
4592
- const response = state.response;
4593
- if (!response) return state;
4594
- if (response.type === "TEXT") {
4595
- return {
4596
- ...state,
4597
- response: {
4598
- ...response,
4599
- content: {
4600
- format: event.data.format ?? response.content.format,
4601
- value: event.data.value ?? response.content.value
4602
- }
4603
- }
4604
- };
4605
- }
4606
- if (response.type === "ERROR") {
4607
- return {
4608
- ...state,
4609
- response: {
4610
- ...response,
4611
- content: { message: event.data.message ?? response.content.message }
4612
- }
4613
- };
4614
- }
4615
- return state;
4616
- }
4617
- case "block": {
4618
- const response = state.response;
4619
- if (!response || response.type !== "ANALYTICS") return state;
4620
- return {
4621
- ...state,
4622
- response: {
4623
- ...response,
4624
- content: {
4625
- ...response.content,
4626
- blocks: [...response.content.blocks, event.data]
4627
- }
4628
- }
4629
- };
4577
+ function renderBody(response, components, onAction) {
4578
+ switch (response.type) {
4579
+ case "TEXT": {
4580
+ const { format, value } = response.content;
4581
+ return format === "MARKDOWN" ? /* @__PURE__ */ jsx(Markdown, { children: value }) : /* @__PURE__ */ jsx("p", { className: "rk-text-plain", children: value });
4630
4582
  }
4631
- case "response_end": {
4632
- if (event.data.status === "ERROR") {
4633
- const message = state.response?.type === "ERROR" && state.response.content.message || state.error || "The request failed.";
4634
- return { ...state, phase: "error", error: message };
4635
- }
4636
- return { ...state, phase: "done", progressMessage: void 0, tool: void 0 };
4583
+ case "ANALYTICS": {
4584
+ const { title, blocks } = response.content;
4585
+ const list = Array.isArray(blocks) ? blocks : [];
4586
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4587
+ title ? /* @__PURE__ */ jsx("h2", { className: "rk-response-title", children: title }) : null,
4588
+ /* @__PURE__ */ jsx("div", { className: "rk-blocks", children: list.map((block, i) => /* @__PURE__ */ jsx(
4589
+ BlockSlot,
4590
+ {
4591
+ block,
4592
+ components,
4593
+ onAction
4594
+ },
4595
+ blockKey(block, i)
4596
+ )) })
4597
+ ] });
4637
4598
  }
4638
- case "error":
4639
- return {
4640
- ...state,
4641
- phase: "error",
4642
- error: event.data.message ?? "The stream encountered a fatal error."
4643
- };
4599
+ case "ERROR":
4600
+ return /* @__PURE__ */ jsx(ErrorView, { message: response.content.message });
4644
4601
  default:
4645
- return state;
4602
+ return /* @__PURE__ */ jsx(ErrorView, { message: "Unsupported response type." });
4646
4603
  }
4647
4604
  }
4605
+ function BlockSlot({
4606
+ block,
4607
+ components,
4608
+ onAction
4609
+ }) {
4610
+ const type = typeof block?.type === "string" ? block.type : "UNKNOWN";
4611
+ const Component2 = resolveBlockComponent(type, components);
4612
+ return /* @__PURE__ */ jsx(ErrorBoundary, { fallback: () => /* @__PURE__ */ jsx(FallbackBlock, { block, onAction }), children: /* @__PURE__ */ jsx(Component2, { block, onAction }) });
4613
+ }
4614
+ function ErrorView({ message }) {
4615
+ return /* @__PURE__ */ jsxs("div", { className: "rk-block rk-error", role: "alert", children: [
4616
+ /* @__PURE__ */ jsx("span", { className: "rk-error-icon", "aria-hidden": true, children: "\u2715" }),
4617
+ /* @__PURE__ */ jsx("span", { className: "rk-error-message", children: message })
4618
+ ] });
4619
+ }
4620
+ function MetadataFooter({
4621
+ metadata,
4622
+ onAction,
4623
+ responseId
4624
+ }) {
4625
+ if (!metadata) return null;
4626
+ const { generatedAt, dataSource, refreshable } = metadata;
4627
+ if (!generatedAt && !dataSource && !refreshable) return null;
4628
+ return /* @__PURE__ */ jsxs("footer", { className: "rk-meta", children: [
4629
+ generatedAt ? /* @__PURE__ */ jsx("span", { className: "rk-meta-time", children: formatTime(generatedAt) }) : null,
4630
+ dataSource ? /* @__PURE__ */ jsx("span", { className: "rk-meta-source", children: dataSource }) : null,
4631
+ refreshable ? /* @__PURE__ */ jsx(
4632
+ "button",
4633
+ {
4634
+ type: "button",
4635
+ className: "rk-action rk-meta-refresh",
4636
+ onClick: () => onAction({ type: "REFRESH", responseId }),
4637
+ children: "Refresh"
4638
+ }
4639
+ ) : null
4640
+ ] });
4641
+ }
4642
+ function formatTime(iso) {
4643
+ const d = new Date(iso);
4644
+ return Number.isNaN(d.getTime()) ? iso : d.toLocaleString();
4645
+ }
4646
+ function blockKey(block, index) {
4647
+ return typeof block?.id === "string" && block.id ? block.id : `block-${index}`;
4648
+ }
4648
4649
 
4649
4650
  // src/sse.ts
4650
4651
  var trimTrailingSlash = (s) => s.replace(/\/+$/, "");