@cntyclub/agent-react 0.2.0 → 0.4.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,5 +1,5 @@
1
- import { ChatMessage, ChatMessageBubble, Markdown, DataTablePaged, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Button, Spinner, Tooltip, TooltipTrigger, TooltipContent, Chat, ChatMessages, ChatTypingIndicator, cn, ChatInput, useMediaQuery, TooltipProvider, Fab } from '@cntyclub/ui-react';
2
- import { WrenchIcon, CheckCircle2Icon, ShieldAlertIcon, SparklesIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, Maximize2Icon, XIcon, Trash2Icon } from 'lucide-react';
1
+ import { ChatMessage, ChatMessageBubble, Markdown, ChatToolChip, DataTablePaged, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Button, Spinner, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Tooltip, TooltipTrigger, TooltipContent, Chat, ChatMessages, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, ChatTypingIndicator, cn, ChatInput, useMediaQuery, TooltipProvider, Fab } from '@cntyclub/ui-react';
2
+ import { WrenchIcon, CheckIcon, ShieldAlertIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, Maximize2Icon, XIcon, Trash2Icon, SparklesIcon } from 'lucide-react';
3
3
  import * as React2 from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -77,6 +77,144 @@ var AgentApiClient = class {
77
77
  });
78
78
  }
79
79
  };
80
+ var NOISE_KEYS = /* @__PURE__ */ new Set([
81
+ "id",
82
+ "pk",
83
+ "uuid",
84
+ "slug",
85
+ "object_id",
86
+ "external_id",
87
+ "client_id",
88
+ "content_type",
89
+ "provider_call_id",
90
+ "tool_call_id"
91
+ ]);
92
+ var NOISE_PATTERNS = [
93
+ /_id$/i,
94
+ /_ids$/i,
95
+ /_key$/i,
96
+ /_hash$/i,
97
+ /embedding/i,
98
+ /vector/i,
99
+ /password/i,
100
+ /secret/i,
101
+ /(^|_)token(_|$)/i
102
+ ];
103
+ var IMAGE_KEY = /(image|logo|avatar|photo|picture|icon|thumbnail|banner|cover|headshot)/i;
104
+ var IMAGE_EXT = /\.(png|jpe?g|gif|webp|svg|avif)(\?|$)/i;
105
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
106
+ var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}(T|$)/;
107
+ var URL_RE = /^https?:\/\//i;
108
+ var PRIORITY_KEYS = ["name", "title", "label", "full_name", "email"];
109
+ var LABEL_KEYS = ["name", "title", "label", "full_name", "email", "code"];
110
+ function isUuid(value) {
111
+ return typeof value === "string" && UUID_RE.test(value);
112
+ }
113
+ function humanize(key) {
114
+ const text = key.replace(/_/g, " ").trim();
115
+ return text.charAt(0).toUpperCase() + text.slice(1);
116
+ }
117
+ function isNoiseColumn(key, rows) {
118
+ if (NOISE_KEYS.has(key.toLowerCase())) return true;
119
+ if (NOISE_PATTERNS.some((pattern) => pattern.test(key))) return true;
120
+ const values = rows.map((row) => row[key]).filter((value) => value !== null && value !== void 0 && value !== "");
121
+ return values.length > 0 && values.every(isUuid);
122
+ }
123
+ function pickLabel(obj) {
124
+ for (const key of LABEL_KEYS) {
125
+ const value = obj[key];
126
+ if (typeof value === "string" && value.trim()) return value;
127
+ if (typeof value === "number") return String(value);
128
+ }
129
+ return null;
130
+ }
131
+ function formatObject(obj) {
132
+ const label = pickLabel(obj);
133
+ if (label) return label;
134
+ const parts = [];
135
+ for (const [key, value] of Object.entries(obj)) {
136
+ if (value === null || value === void 0 || value === "") continue;
137
+ if (typeof value === "object") continue;
138
+ parts.push(`${humanize(key)} ${value}`);
139
+ if (parts.length >= 6) break;
140
+ }
141
+ return parts.join(" \xB7 ") || "\u2014";
142
+ }
143
+ function formatArray(arr) {
144
+ const items = arr.map((item) => {
145
+ if (item === null || item === void 0) return "";
146
+ if (Array.isArray(item)) return "";
147
+ if (typeof item === "object") return pickLabel(item) ?? "";
148
+ return String(item);
149
+ }).filter(Boolean);
150
+ if (items.length === 0) return "\u2014";
151
+ const shown = items.slice(0, 8);
152
+ const extra = items.length - shown.length;
153
+ return shown.join(", ") + (extra > 0 ? ` +${extra} more` : "");
154
+ }
155
+ function renderScalar(key, value) {
156
+ if (typeof value !== "string") return value;
157
+ const text = value.trim();
158
+ if (URL_RE.test(text) && (IMAGE_EXT.test(text) || IMAGE_KEY.test(key))) {
159
+ return /* @__PURE__ */ jsx(
160
+ "img",
161
+ {
162
+ alt: humanize(key),
163
+ className: "h-9 w-9 rounded-md border border-border object-cover",
164
+ loading: "lazy",
165
+ src: text
166
+ }
167
+ );
168
+ }
169
+ if (URL_RE.test(text)) {
170
+ let label = text;
171
+ try {
172
+ label = new URL(text).hostname.replace(/^www\./, "");
173
+ } catch {
174
+ }
175
+ return /* @__PURE__ */ jsx(
176
+ "a",
177
+ {
178
+ className: "text-primary underline underline-offset-2",
179
+ href: text,
180
+ rel: "noreferrer",
181
+ target: "_blank",
182
+ children: label
183
+ }
184
+ );
185
+ }
186
+ if (ISO_DATE_RE.test(text)) {
187
+ const date = new Date(text);
188
+ if (!Number.isNaN(date.getTime())) return date.toLocaleDateString();
189
+ }
190
+ return text;
191
+ }
192
+ function formatCellValue(key, value) {
193
+ if (value === null || value === void 0 || value === "") return value;
194
+ if (Array.isArray(value)) return formatArray(value);
195
+ if (typeof value === "object") return formatObject(value);
196
+ return renderScalar(key, value);
197
+ }
198
+ function toDisplayTable(block) {
199
+ const rows = block.rows ?? [];
200
+ const kept = (block.columns ?? []).filter((key) => !isNoiseColumn(key, rows));
201
+ const columnKeys = kept.length > 0 ? kept : block.columns ?? [];
202
+ const ordered = [...columnKeys].sort((a, b) => {
203
+ const ra = PRIORITY_KEYS.indexOf(a.toLowerCase());
204
+ const rb = PRIORITY_KEYS.indexOf(b.toLowerCase());
205
+ const pa = ra === -1 ? PRIORITY_KEYS.length : ra;
206
+ const pb = rb === -1 ? PRIORITY_KEYS.length : rb;
207
+ if (pa !== pb) return pa - pb;
208
+ return columnKeys.indexOf(a) - columnKeys.indexOf(b);
209
+ });
210
+ const columns = ordered.map((key) => ({ key, label: humanize(key) }));
211
+ const displayRows = rows.map((row) => {
212
+ const out = {};
213
+ for (const key of ordered) out[key] = formatCellValue(key, row[key]);
214
+ return out;
215
+ });
216
+ return { columns, rows: displayRows };
217
+ }
80
218
  function humanizeToolName(toolName) {
81
219
  return toolName.replace(/^[a-z]+_/, "").replaceAll("_", " ");
82
220
  }
@@ -89,32 +227,26 @@ function MessageItem({ message }) {
89
227
  if (!message.content && requestedTools.length === 0) return null;
90
228
  return /* @__PURE__ */ jsxs(ChatMessage, { from: "assistant", children: [
91
229
  message.content ? /* @__PURE__ */ jsx(ChatMessageBubble, { from: "assistant", children: /* @__PURE__ */ jsx(Markdown, { children: message.content }) }) : null,
92
- requestedTools.map((toolCall) => /* @__PURE__ */ jsxs(
93
- "div",
94
- {
95
- className: "flex items-center gap-1.5 px-1 text-muted-foreground text-xs",
96
- children: [
97
- /* @__PURE__ */ jsx(WrenchIcon, { className: "size-3" }),
98
- "Using ",
99
- humanizeToolName(toolCall.name ?? "tool"),
100
- "\u2026"
101
- ]
102
- },
103
- toolCall.id ?? toolCall.name
104
- ))
230
+ requestedTools.map((toolCall) => /* @__PURE__ */ jsxs(ChatToolChip, { icon: /* @__PURE__ */ jsx(WrenchIcon, {}), children: [
231
+ "Using ",
232
+ humanizeToolName(toolCall.name ?? "tool"),
233
+ "\u2026"
234
+ ] }, toolCall.id ?? toolCall.name))
105
235
  ] });
106
236
  }
107
237
  const tableBlocks = (message.blocks ?? []).filter((block) => block.type === "table");
108
- return /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: tableBlocks.length > 0 ? tableBlocks.map((block, index) => /* @__PURE__ */ jsx("div", { className: "w-full max-w-full", children: /* @__PURE__ */ jsx(
109
- DataTablePaged,
110
- {
111
- columns: block.columns.map((key) => ({ key })),
112
- pageSize: 8,
113
- rows: block.rows,
114
- totalCount: typeof block.pagination?.count === "number" ? block.pagination.count : block.row_count
115
- }
116
- ) }, `${message.id}-${index}`)) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-1 text-muted-foreground text-xs", children: [
117
- /* @__PURE__ */ jsx(CheckCircle2Icon, { className: "size-3" }),
238
+ return /* @__PURE__ */ jsx(ChatMessage, { from: "assistant", children: tableBlocks.length > 0 ? tableBlocks.map((block, index) => {
239
+ const { columns, rows } = toDisplayTable(block);
240
+ return /* @__PURE__ */ jsx("div", { className: "w-full max-w-full", children: /* @__PURE__ */ jsx(
241
+ DataTablePaged,
242
+ {
243
+ columns,
244
+ pageSize: 8,
245
+ rows,
246
+ totalCount: typeof block.pagination?.count === "number" ? block.pagination.count : block.row_count
247
+ }
248
+ ) }, `${message.id}-${index}`);
249
+ }) : /* @__PURE__ */ jsxs(ChatToolChip, { icon: /* @__PURE__ */ jsx(CheckIcon, {}), children: [
118
250
  humanizeToolName(message.tool_name ?? "tool"),
119
251
  " finished"
120
252
  ] }) });
@@ -205,72 +337,74 @@ function AgentPanel({
205
337
  ),
206
338
  "data-slot": "agent-panel",
207
339
  children: [
208
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1 border-border border-b px-3 py-2", children: [
209
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
210
- /* @__PURE__ */ jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(SparklesIcon, { className: "size-4" }) }),
211
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
340
+ /* @__PURE__ */ jsxs(ChatHeader, { children: [
341
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2.5", children: [
342
+ /* @__PURE__ */ jsx(ChatHeaderAvatar, {}),
343
+ /* @__PURE__ */ jsxs(ChatHeaderTitle, { children: [
212
344
  /* @__PURE__ */ jsx("div", { className: "truncate font-medium text-sm", children: agentName }),
213
345
  config.tagline ? /* @__PURE__ */ jsx("div", { className: "truncate text-muted-foreground text-xs", children: config.tagline }) : null
214
346
  ] })
215
347
  ] }),
216
- /* @__PURE__ */ jsxs(Tooltip, { children: [
217
- /* @__PURE__ */ jsx(
218
- TooltipTrigger,
219
- {
220
- render: /* @__PURE__ */ jsx(
221
- Button,
222
- {
223
- "aria-label": "Conversation history",
224
- onClick: view === "history" ? () => setView("chat") : openHistory,
225
- size: "icon-sm",
226
- variant: "ghost",
227
- children: /* @__PURE__ */ jsx(HistoryIcon, {})
228
- }
229
- )
230
- }
231
- ),
232
- /* @__PURE__ */ jsx(TooltipContent, { children: "History" })
233
- ] }),
234
- /* @__PURE__ */ jsxs(Tooltip, { children: [
235
- /* @__PURE__ */ jsx(
236
- TooltipTrigger,
237
- {
238
- render: /* @__PURE__ */ jsx(
239
- Button,
240
- {
241
- "aria-label": "New chat",
242
- onClick: () => {
243
- chat.newChat();
244
- setView("chat");
245
- },
246
- size: "icon-sm",
247
- variant: "ghost",
248
- children: /* @__PURE__ */ jsx(SquarePenIcon, {})
249
- }
250
- )
251
- }
252
- ),
253
- /* @__PURE__ */ jsx(TooltipContent, { children: "New chat" })
254
- ] }),
255
- canToggleAgentMode ? /* @__PURE__ */ jsxs(Tooltip, { children: [
256
- /* @__PURE__ */ jsx(
257
- TooltipTrigger,
258
- {
259
- render: /* @__PURE__ */ jsx(
260
- Button,
261
- {
262
- "aria-label": agentMode ? "Exit Agent Mode" : "Enter Agent Mode",
263
- onClick: onToggleAgentMode,
264
- size: "icon-sm",
265
- variant: "ghost",
266
- children: agentMode ? /* @__PURE__ */ jsx(Minimize2Icon, {}) : /* @__PURE__ */ jsx(Maximize2Icon, {})
267
- }
268
- )
269
- }
270
- ),
271
- /* @__PURE__ */ jsx(TooltipContent, { children: agentMode ? "Exit Agent Mode" : "Agent Mode" })
272
- ] }) : null,
273
- /* @__PURE__ */ jsx(Button, { "aria-label": "Close", onClick: onClose, size: "icon-sm", variant: "ghost", children: /* @__PURE__ */ jsx(XIcon, {}) })
348
+ /* @__PURE__ */ jsxs(ChatHeaderActions, { children: [
349
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
350
+ /* @__PURE__ */ jsx(
351
+ TooltipTrigger,
352
+ {
353
+ render: /* @__PURE__ */ jsx(
354
+ Button,
355
+ {
356
+ "aria-label": "Conversation history",
357
+ onClick: view === "history" ? () => setView("chat") : openHistory,
358
+ size: "icon-sm",
359
+ variant: "ghost",
360
+ children: /* @__PURE__ */ jsx(HistoryIcon, {})
361
+ }
362
+ )
363
+ }
364
+ ),
365
+ /* @__PURE__ */ jsx(TooltipContent, { children: "History" })
366
+ ] }),
367
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
368
+ /* @__PURE__ */ jsx(
369
+ TooltipTrigger,
370
+ {
371
+ render: /* @__PURE__ */ jsx(
372
+ Button,
373
+ {
374
+ "aria-label": "New chat",
375
+ onClick: () => {
376
+ chat.newChat();
377
+ setView("chat");
378
+ },
379
+ size: "icon-sm",
380
+ variant: "ghost",
381
+ children: /* @__PURE__ */ jsx(SquarePenIcon, {})
382
+ }
383
+ )
384
+ }
385
+ ),
386
+ /* @__PURE__ */ jsx(TooltipContent, { children: "New chat" })
387
+ ] }),
388
+ canToggleAgentMode ? /* @__PURE__ */ jsxs(Tooltip, { children: [
389
+ /* @__PURE__ */ jsx(
390
+ TooltipTrigger,
391
+ {
392
+ render: /* @__PURE__ */ jsx(
393
+ Button,
394
+ {
395
+ "aria-label": agentMode ? "Exit Agent Mode" : "Enter Agent Mode",
396
+ onClick: onToggleAgentMode,
397
+ size: "icon-sm",
398
+ variant: "ghost",
399
+ children: agentMode ? /* @__PURE__ */ jsx(Minimize2Icon, {}) : /* @__PURE__ */ jsx(Maximize2Icon, {})
400
+ }
401
+ )
402
+ }
403
+ ),
404
+ /* @__PURE__ */ jsx(TooltipContent, { children: agentMode ? "Exit Agent Mode" : "Agent Mode" })
405
+ ] }) : null,
406
+ /* @__PURE__ */ jsx(Button, { "aria-label": "Close", onClick: onClose, size: "icon-sm", variant: "ghost", children: /* @__PURE__ */ jsx(XIcon, {}) })
407
+ ] })
274
408
  ] }),
275
409
  view === "history" ? /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto p-2", children: chat.conversations.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-4 text-center text-muted-foreground text-sm", children: "No conversations yet." }) : chat.conversations.map((conversation) => /* @__PURE__ */ jsxs(
276
410
  "div",
@@ -307,22 +441,21 @@ function AgentPanel({
307
441
  },
308
442
  conversation.id
309
443
  )) }) : /* @__PURE__ */ jsxs(Chat, { children: [
310
- /* @__PURE__ */ jsx(ChatMessages, { className: cn(agentMode && "mx-auto w-full max-w-3xl"), children: chat.messages.length === 0 && !chat.sending ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center", children: [
311
- /* @__PURE__ */ jsx("span", { className: "flex size-12 items-center justify-center rounded-full bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(SparklesIcon, { className: "size-6" }) }),
444
+ /* @__PURE__ */ jsx(ChatMessages, { className: cn(agentMode && "mx-auto w-full max-w-3xl"), children: chat.messages.length === 0 && !chat.sending ? /* @__PURE__ */ jsxs(ChatEmptyState, { children: [
445
+ /* @__PURE__ */ jsx(ChatEmptyStateIcon, {}),
312
446
  /* @__PURE__ */ jsxs("div", { children: [
313
- /* @__PURE__ */ jsxs("p", { className: "font-medium text-sm", children: [
447
+ /* @__PURE__ */ jsxs(ChatEmptyStateTitle, { children: [
314
448
  "Hi! I'm ",
315
449
  agentName,
316
450
  "."
317
451
  ] }),
318
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground text-sm", children: "Ask me anything about your data \u2014 I can look things up and take actions (with your approval)." })
452
+ /* @__PURE__ */ jsx(ChatEmptyStateDescription, { className: "mt-1", children: "Ask me anything about your data \u2014 I can look things up and take actions (with your approval)." })
319
453
  ] }),
320
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: suggestions.slice(0, 3).map((suggestion) => /* @__PURE__ */ jsx(
321
- Button,
454
+ /* @__PURE__ */ jsx(ChatSuggestions, { children: suggestions.slice(0, 4).map((suggestion, index) => /* @__PURE__ */ jsx(
455
+ ChatSuggestion,
322
456
  {
457
+ index,
323
458
  onClick: () => handleSubmit(suggestion),
324
- size: "sm",
325
- variant: "outline",
326
459
  children: suggestion
327
460
  },
328
461
  suggestion
@@ -347,7 +480,7 @@ function AgentPanel({
347
480
  /* @__PURE__ */ jsx(
348
481
  ChatInput,
349
482
  {
350
- className: cn(agentMode && "mx-auto w-full max-w-3xl border-t-0"),
483
+ className: cn(agentMode && "mx-auto w-full max-w-3xl"),
351
484
  disabled: busy || chat.pendingApprovals.length > 0,
352
485
  onSubmit: handleSubmit,
353
486
  onValueChange: setDraft,