telygent-ui 0.1.6 → 0.1.8

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
@@ -18,9 +18,9 @@ import {ChatProvider, type SendMessageInput, type SendMessageResult, type ChatMe
18
18
  import {ChatInterface} from "@/components/ai/ChatInterface";
19
19
 
20
20
  const adapter = {
21
- async sendMessage({message, conversationId}: SendMessageInput): Promise<SendMessageResult> {
21
+ async sendMessage({message, conversationId, messageContext}: SendMessageInput): Promise<SendMessageResult> {
22
22
  // Use React Query, Axios, RTK Query, etc.
23
- const response = await api.send({message, conversationId});
23
+ const response = await api.send({message, conversationId, messageContext});
24
24
  return {
25
25
  conversationId: response.conversationId,
26
26
  message: {
@@ -45,9 +45,10 @@ const adapter = {
45
45
 
46
46
  export default function Page() {
47
47
  const conversationId = "your-conversation-id";
48
+ const messageContext = "Viewing document id: 3283823883";
48
49
  return (
49
50
  <ChatProvider adapter={adapter}>
50
- <ChatInterface conversationId={conversationId} aiName="Telygent" />
51
+ <ChatInterface conversationId={conversationId} aiName="Telygent" messageContext={messageContext} />
51
52
  </ChatProvider>
52
53
  );
53
54
  }
@@ -68,6 +69,7 @@ async function* streamChat(input: SendMessageInput): AsyncIterable<ChatStreamEve
68
69
  body: JSON.stringify({
69
70
  question: input.message,
70
71
  conversationId: input.conversationId,
72
+ messageContext: input.messageContext,
71
73
  }),
72
74
  });
73
75
 
@@ -111,7 +113,7 @@ const adapter = {
111
113
  export default function Page() {
112
114
  return (
113
115
  <ChatProvider adapter={adapter}>
114
- <ChatInterface conversationId="conv_123" />
116
+ <ChatInterface conversationId="conv_123" messageContext="Viewing transaction id: txn_123" />
115
117
  </ChatProvider>
116
118
  );
117
119
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telygent-ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Telygent UI CLI",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
@@ -6,9 +6,9 @@ import {MarkdownRenderer} from "./MarkdownRenderer";
6
6
  import AIWriter from "react-aiwriter";
7
7
  import * as echarts from "echarts";
8
8
  import type {EChartsOption} from "echarts";
9
- import {Check, Copy} from "lucide-react";
9
+ import {Check, Copy, Download} from "lucide-react";
10
10
 
11
- import {cn} from "@/lib/utils";
11
+ import {cn} from "./utils";
12
12
  import {type ChatMessage, type ChatSummaryCard, type ChatVisualization} from "./ChatProvider";
13
13
  import {useDatabaseChat} from "../hooks/use-database-chat";
14
14
 
@@ -18,8 +18,17 @@ const MemoizedAIWriter = React.memo(({children}: {children: React.ReactNode}) =>
18
18
  });
19
19
  MemoizedAIWriter.displayName = "MemoizedAIWriter";
20
20
 
21
- const EChart = ({option}: {option: EChartsOption}) => {
21
+ const getChartFilename = (seed: string) => {
22
+ const normalized = seed
23
+ .toLowerCase()
24
+ .replace(/[^a-z0-9]+/g, "-")
25
+ .replace(/^-+|-+$/g, "");
26
+ return normalized || "chart";
27
+ };
28
+
29
+ const EChart = ({option, fileName}: {option: EChartsOption; fileName: string}) => {
22
30
  const chartRef = React.useRef<HTMLDivElement>(null);
31
+ const chartInstanceRef = React.useRef<echarts.ECharts | null>(null);
23
32
 
24
33
  React.useEffect(() => {
25
34
  if (!chartRef.current) {
@@ -27,6 +36,7 @@ const EChart = ({option}: {option: EChartsOption}) => {
27
36
  }
28
37
 
29
38
  const instance = echarts.init(chartRef.current);
39
+ chartInstanceRef.current = instance;
30
40
  instance.setOption(option);
31
41
 
32
42
  const handleResize = () => instance.resize();
@@ -35,10 +45,48 @@ const EChart = ({option}: {option: EChartsOption}) => {
35
45
  return () => {
36
46
  window.removeEventListener("resize", handleResize);
37
47
  instance.dispose();
48
+ chartInstanceRef.current = null;
38
49
  };
39
50
  }, [option]);
40
51
 
41
- return <div ref={chartRef} className="h-64 w-full" />;
52
+ const handleExportChart = React.useCallback(() => {
53
+ const instance = chartInstanceRef.current;
54
+ if (!instance) {
55
+ return;
56
+ }
57
+
58
+ try {
59
+ const dataUrl = instance.getDataURL({
60
+ type: "png",
61
+ pixelRatio: 2,
62
+ backgroundColor: "#ffffff",
63
+ });
64
+
65
+ const link = document.createElement("a");
66
+ link.href = dataUrl;
67
+ link.download = `${getChartFilename(fileName)}.png`;
68
+ document.body.appendChild(link);
69
+ link.click();
70
+ document.body.removeChild(link);
71
+ } catch (error) {
72
+ console.error("Unable to export chart image.", error);
73
+ }
74
+ }, [fileName]);
75
+
76
+ return (
77
+ <div>
78
+ <div ref={chartRef} className="h-64 w-full" />
79
+ <button
80
+ type="button"
81
+ onClick={handleExportChart}
82
+ className="mt-2 inline-flex items-center gap-1 text-xs font-medium text-slate-600 transition hover:text-slate-700"
83
+ aria-label="Export chart as PNG"
84
+ >
85
+ <Download className="h-4 w-4" />
86
+ <span>Export PNG</span>
87
+ </button>
88
+ </div>
89
+ );
42
90
  };
43
91
 
44
92
  const inlineComputedStyles = (source: Element, target: Element) => {
@@ -128,12 +176,13 @@ const renderVisualization = (viz: ChatVisualization, key: string) => {
128
176
  return null;
129
177
  }
130
178
 
179
+ const chartName = viz.title || key || "chart";
131
180
  return (
132
181
  <div key={key} className="rounded-2xl border border-slate-200 bg-white p-3">
133
182
  {viz.title ? (
134
183
  <p className="mb-2 text-sm font-semibold text-slate-700">{viz.title}</p>
135
184
  ) : null}
136
- <EChart option={viz.options as EChartsOption} />
185
+ <EChart option={viz.options as EChartsOption} fileName={chartName} />
137
186
  </div>
138
187
  );
139
188
  };
@@ -336,7 +385,7 @@ UserMessageBody.displayName = "UserMessageBody";
336
385
 
337
386
  export type ChatInterfaceProps = {
338
387
  className?: string;
339
- viewContext?: string;
388
+ messageContext?: string;
340
389
  placeholder?: string;
341
390
  defaultPrompts?: {label: string; accent?: string}[];
342
391
  conversationId: string;
@@ -347,7 +396,7 @@ export type ChatInterfaceProps = {
347
396
 
348
397
  export function ChatInterface({
349
398
  className,
350
- viewContext,
399
+ messageContext,
351
400
  placeholder,
352
401
  defaultPrompts,
353
402
  conversationId,
@@ -364,7 +413,7 @@ export function ChatInterface({
364
413
  loading,
365
414
  loadingHistory,
366
415
  prompts,
367
- } = useDatabaseChat({viewContext, defaultPrompts, conversationId});
416
+ } = useDatabaseChat({messageContext, defaultPrompts, conversationId});
368
417
 
369
418
  const containerRef = React.useRef<HTMLDivElement>(null);
370
419
  const scrollRef = React.useRef<HTMLDivElement>(null);
@@ -571,10 +620,10 @@ export function ChatInterface({
571
620
  </div>
572
621
 
573
622
  <div className="border-t border-slate-200 bg-white px-6 py-4">
574
- {viewContext ? (
623
+ {messageContext ? (
575
624
  <div className="mb-2 flex w-6/12">
576
625
  <div className="truncate rounded-full border border-[dodgerblue] bg-[dodgerblue]/10 px-2 text-xs">
577
- {viewContext}
626
+ {messageContext}
578
627
  </div>
579
628
  </div>
580
629
  ) : null}
@@ -38,7 +38,7 @@ export type ChatMessage = {
38
38
  export type SendMessageInput = {
39
39
  message: string;
40
40
  conversationId: string;
41
- context?: string;
41
+ messageContext?: string;
42
42
  };
43
43
 
44
44
  export type SendMessageResult = {
@@ -0,0 +1,6 @@
1
+ import {type ClassValue, clsx} from "clsx";
2
+ import {twMerge} from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -5,12 +5,12 @@ import {useChatAdapter, type ChatMessage, type ChatStreamEvent} from "../compone
5
5
 
6
6
  export type UseDatabaseChatOptions = {
7
7
  conversationId: string;
8
- viewContext?: string;
8
+ messageContext?: string;
9
9
  defaultPrompts?: {label: string; accent?: string}[];
10
10
  };
11
11
 
12
12
  export function useDatabaseChat(options: UseDatabaseChatOptions) {
13
- const {conversationId: initialConversationId, viewContext, defaultPrompts} = options;
13
+ const {conversationId: initialConversationId, messageContext, defaultPrompts} = options;
14
14
  const adapter = useChatAdapter();
15
15
 
16
16
  const [conversationId] = React.useState<string>(initialConversationId);
@@ -74,7 +74,6 @@ export function useDatabaseChat(options: UseDatabaseChatOptions) {
74
74
  return;
75
75
  }
76
76
 
77
- const withContext = `${viewContext ?? ""} ${outgoing}`.trim();
78
77
  addMessage({role: "user", content: outgoing, createdAt: new Date()});
79
78
  setMessage("");
80
79
 
@@ -93,9 +92,9 @@ export function useDatabaseChat(options: UseDatabaseChatOptions) {
93
92
 
94
93
  const stream =
95
94
  (await adapter.sendMessageStream({
96
- message: withContext,
95
+ message: outgoing,
97
96
  conversationId,
98
- context: viewContext,
97
+ messageContext,
99
98
  })) as AsyncIterable<ChatStreamEvent>;
100
99
 
101
100
  for await (const event of stream) {
@@ -135,9 +134,9 @@ export function useDatabaseChat(options: UseDatabaseChatOptions) {
135
134
  }
136
135
  } else {
137
136
  const result = await adapter.sendMessage({
138
- message: withContext,
137
+ message: outgoing,
139
138
  conversationId,
140
- context: viewContext,
139
+ messageContext,
141
140
  });
142
141
  addMessage(result.message);
143
142
  }
@@ -145,7 +144,7 @@ export function useDatabaseChat(options: UseDatabaseChatOptions) {
145
144
  setLoading(false);
146
145
  }
147
146
  },
148
- [adapter, addMessage, conversationId, message, viewContext]
147
+ [adapter, addMessage, conversationId, message, messageContext]
149
148
  );
150
149
 
151
150
  return {
@@ -7,6 +7,7 @@
7
7
  "components/ChatProvider.tsx",
8
8
  "components/ChatInterface.tsx",
9
9
  "components/MarkdownRenderer.tsx",
10
+ "components/utils.ts",
10
11
  "components/ai-mdx.css",
11
12
  "hooks/use-database-chat.ts",
12
13
  "hooks/use-rtk-stream-adapter.ts"
@@ -16,7 +17,9 @@
16
17
  "markdown-to-jsx",
17
18
  "react-aiwriter",
18
19
  "moment",
19
- "lucide-react"
20
+ "lucide-react",
21
+ "clsx",
22
+ "tailwind-merge"
20
23
  ],
21
24
  "peerDependencies": [
22
25
  "@reduxjs/toolkit"