@sweetoburrito/backstage-plugin-ai-assistant 0.5.0 → 0.7.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 (31) hide show
  1. package/dist/api/chat.esm.js +20 -1
  2. package/dist/api/chat.esm.js.map +1 -1
  3. package/dist/api/mcp.esm.js +73 -0
  4. package/dist/api/mcp.esm.js.map +1 -0
  5. package/dist/api/summarizer.esm.js +47 -0
  6. package/dist/api/summarizer.esm.js.map +1 -0
  7. package/dist/components/AiAssistantChatModal/AiAssistantChatModal.esm.js +194 -0
  8. package/dist/components/AiAssistantChatModal/AiAssistantChatModal.esm.js.map +1 -0
  9. package/dist/components/AiAssistantChatModal/index.esm.js +2 -0
  10. package/dist/components/AiAssistantChatModal/index.esm.js.map +1 -0
  11. package/dist/components/AiAssistantPage/AiAssistantPage.esm.js +31 -8
  12. package/dist/components/AiAssistantPage/AiAssistantPage.esm.js.map +1 -1
  13. package/dist/components/Conversation/Conversation.esm.js +48 -13
  14. package/dist/components/Conversation/Conversation.esm.js.map +1 -1
  15. package/dist/components/Conversation/McpServersTab.esm.js +303 -0
  16. package/dist/components/Conversation/McpServersTab.esm.js.map +1 -0
  17. package/dist/components/Conversation/SettingsModal.esm.js +134 -0
  18. package/dist/components/Conversation/SettingsModal.esm.js.map +1 -0
  19. package/dist/components/MessageCard/FeedbackButtons.esm.js +64 -0
  20. package/dist/components/MessageCard/FeedbackButtons.esm.js.map +1 -0
  21. package/dist/components/MessageCard/MessageCard.esm.js +64 -14
  22. package/dist/components/MessageCard/MessageCard.esm.js.map +1 -1
  23. package/dist/hooks/use-chat-settings.esm.js +24 -0
  24. package/dist/hooks/use-chat-settings.esm.js.map +1 -0
  25. package/dist/hooks/use-page-summary.esm.js +27 -0
  26. package/dist/hooks/use-page-summary.esm.js.map +1 -0
  27. package/dist/index.d.ts +9 -1
  28. package/dist/index.esm.js +1 -1
  29. package/dist/plugin.esm.js +33 -2
  30. package/dist/plugin.esm.js.map +1 -1
  31. package/package.json +4 -3
@@ -53,7 +53,26 @@ const createChatService = ({
53
53
  const data = await response.json();
54
54
  return data.conversations;
55
55
  };
56
- return { getModels, getConversation, sendMessage, getConversations };
56
+ const scoreMessage = async (messageId, score) => {
57
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
58
+ await fetchApi.fetch(
59
+ `${assistantBaseUrl}/chat/message/${messageId}/score`,
60
+ {
61
+ method: "POST",
62
+ body: JSON.stringify({ score }),
63
+ headers: {
64
+ "Content-Type": "application/json"
65
+ }
66
+ }
67
+ );
68
+ };
69
+ return {
70
+ getModels,
71
+ getConversation,
72
+ sendMessage,
73
+ getConversations,
74
+ scoreMessage
75
+ };
57
76
  };
58
77
 
59
78
  export { chatApiRef, createChatService };
@@ -1 +1 @@
1
- {"version":3,"file":"chat.esm.js","sources":["../../src/api/chat.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport {\n Conversation,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\ntype SendMessageOptions = {\n conversationId?: string;\n modelId: string;\n messages: Message[];\n stream?: boolean;\n};\n\nexport type ChatApi = {\n getModels: () => Promise<string[]>;\n getConversation: (id?: string) => Promise<Message[]>;\n sendMessage: (options: SendMessageOptions) => Promise<{\n messages: Message[];\n conversationId: string;\n }>;\n getConversations: () => Promise<Conversation[]>;\n};\n\ntype ChatApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const chatApiRef = createApiRef<ChatApi>({\n id: 'plugin.ai-assistant.chat',\n});\n\nexport const createChatService = ({\n fetchApi,\n discoveryApi,\n}: ChatApiOptions): ChatApi => {\n const getModels: ChatApi['getModels'] = async (): Promise<string[]> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/models`);\n const data = await response.json();\n return data.models;\n };\n\n const getConversation: ChatApi['getConversation'] = async id => {\n if (!id) return [];\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/${id}`);\n\n const data = await response.json();\n\n return data.conversation as Message[];\n };\n\n const sendMessage: ChatApi['sendMessage'] = async ({\n conversationId,\n modelId,\n messages,\n stream,\n }) => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/message`, {\n method: 'POST',\n body: JSON.stringify({\n conversationId,\n modelId,\n messages,\n stream,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const data = await response.json();\n\n return {\n messages: data.messages as Message[],\n conversationId: data.conversationId as string,\n };\n };\n\n const getConversations: ChatApi['getConversations'] = async () => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(\n `${assistantBaseUrl}/chat/conversations`,\n );\n\n const data = await response.json();\n\n return data.conversations as Conversation[];\n };\n\n return { getModels, getConversation, sendMessage, getConversations };\n};\n"],"names":[],"mappings":";;AA6BO,MAAM,aAAa,YAAA,CAAsB;AAAA,EAC9C,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,oBAAoB,CAAC;AAAA,EAChC,QAAA;AAAA,EACA;AACF,CAAA,KAA+B;AAC7B,EAAA,MAAM,YAAkC,YAA+B;AACrE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,OAAA,CAAS,CAAA;AAClE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,eAAA,GAA8C,OAAM,EAAA,KAAM;AAC9D,IAAA,IAAI,CAAC,EAAA,EAAI,OAAO,EAAC;AACjB,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA,CAAM,GAAG,gBAAgB,CAAA,MAAA,EAAS,EAAE,CAAA,CAAE,CAAA;AAEtE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,cAAsC,OAAO;AAAA,IACjD,cAAA;AAAA,IACA,OAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF,KAAM;AACJ,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AACrE,IAAA,MAAM,WAAW,MAAM,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,gBAAgB,CAAA,aAAA,CAAA,EAAiB;AAAA,MACxE,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,KAAK,SAAA,CAAU;AAAA,QACnB,cAAA;AAAA,QACA,OAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACD,CAAA;AAAA,MACD,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA;AAClB,KACD,CAAA;AAED,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO;AAAA,MACL,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,gBAAgB,IAAA,CAAK;AAAA,KACvB;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,mBAAgD,YAAY;AAChE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA;AAAA,MAC9B,GAAG,gBAAgB,CAAA,mBAAA;AAAA,KACrB;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAEjC,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd,CAAA;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,eAAA,EAAiB,WAAA,EAAa,gBAAA,EAAiB;AACrE;;;;"}
1
+ {"version":3,"file":"chat.esm.js","sources":["../../src/api/chat.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport {\n Conversation,\n Message,\n} from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\ntype SendMessageOptions = {\n conversationId?: string;\n modelId: string;\n messages: Message[];\n stream?: boolean;\n};\n\nexport type ChatApi = {\n getModels: () => Promise<string[]>;\n getConversation: (id?: string) => Promise<Message[]>;\n sendMessage: (options: SendMessageOptions) => Promise<{\n messages: Message[];\n conversationId: string;\n }>;\n getConversations: () => Promise<Conversation[]>;\n scoreMessage: (messageId: string, score: number) => Promise<void>;\n};\n\ntype ChatApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const chatApiRef = createApiRef<ChatApi>({\n id: 'plugin.ai-assistant.chat',\n});\n\nexport const createChatService = ({\n fetchApi,\n discoveryApi,\n}: ChatApiOptions): ChatApi => {\n const getModels: ChatApi['getModels'] = async (): Promise<string[]> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/models`);\n const data = await response.json();\n return data.models;\n };\n\n const getConversation: ChatApi['getConversation'] = async id => {\n if (!id) return [];\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/${id}`);\n\n const data = await response.json();\n\n return data.conversation as Message[];\n };\n\n const sendMessage: ChatApi['sendMessage'] = async ({\n conversationId,\n modelId,\n messages,\n stream,\n }) => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n const response = await fetchApi.fetch(`${assistantBaseUrl}/chat/message`, {\n method: 'POST',\n body: JSON.stringify({\n conversationId,\n modelId,\n messages,\n stream,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n const data = await response.json();\n\n return {\n messages: data.messages as Message[],\n conversationId: data.conversationId as string,\n };\n };\n\n const getConversations: ChatApi['getConversations'] = async () => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(\n `${assistantBaseUrl}/chat/conversations`,\n );\n\n const data = await response.json();\n\n return data.conversations as Conversation[];\n };\n\n const scoreMessage: ChatApi['scoreMessage'] = async (messageId, score) => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n await fetchApi.fetch(\n `${assistantBaseUrl}/chat/message/${messageId}/score`,\n {\n method: 'POST',\n body: JSON.stringify({ score }),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n );\n };\n\n return {\n getModels,\n getConversation,\n sendMessage,\n getConversations,\n scoreMessage,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { createApiRef } from '@backstage/core-plugin-api';
2
+
3
+ const mcpApiRef = createApiRef({
4
+ id: "plugin.ai-assistant.mcp"
5
+ });
6
+ const createMcpService = ({ fetchApi, discoveryApi }) => {
7
+ const getUserDefinedMcpConfigs = async () => {
8
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
9
+ const response = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`);
10
+ const data = await response.json();
11
+ return data;
12
+ };
13
+ const createMcpConfig = async (config) => {
14
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
15
+ const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {
16
+ method: "POST",
17
+ body: JSON.stringify(config),
18
+ headers: {
19
+ "Content-Type": "application/json"
20
+ }
21
+ });
22
+ if (!res.ok) {
23
+ const errorData = await res.json();
24
+ const { error } = errorData;
25
+ throw new Error(
26
+ `Failed to create MCP config: ${error || res.statusText}`
27
+ );
28
+ }
29
+ };
30
+ const updateMcpConfig = async (config) => {
31
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
32
+ const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {
33
+ method: "PATCH",
34
+ body: JSON.stringify(config),
35
+ headers: {
36
+ "Content-Type": "application/json"
37
+ }
38
+ });
39
+ if (!res.ok) {
40
+ const errorData = await res.json();
41
+ const { error } = errorData;
42
+ throw new Error(
43
+ `Failed to update MCP config: ${error || res.statusText}`
44
+ );
45
+ }
46
+ };
47
+ const deleteMcpConfig = async (configName) => {
48
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
49
+ const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {
50
+ method: "DELETE",
51
+ body: JSON.stringify({ name: configName }),
52
+ headers: {
53
+ "Content-Type": "application/json"
54
+ }
55
+ });
56
+ if (!res.ok) {
57
+ const errorData = await res.json();
58
+ const { error } = errorData;
59
+ throw new Error(
60
+ `Failed to delete MCP config: ${error || res.statusText}`
61
+ );
62
+ }
63
+ };
64
+ return {
65
+ getUserDefinedMcpConfigs,
66
+ createMcpConfig,
67
+ updateMcpConfig,
68
+ deleteMcpConfig
69
+ };
70
+ };
71
+
72
+ export { createMcpService, mcpApiRef };
73
+ //# sourceMappingURL=mcp.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.esm.js","sources":["../../src/api/mcp.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { McpServerConfig } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\nexport type McpApi = Awaited<ReturnType<typeof createMcpService>>;\n\ntype McpApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const mcpApiRef = createApiRef<McpApi>({\n id: 'plugin.ai-assistant.mcp',\n});\n\nexport const createMcpService = ({ fetchApi, discoveryApi }: McpApiOptions) => {\n const getUserDefinedMcpConfigs = async (): Promise<{ names: string[] }> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`);\n const data = await response.json();\n return data;\n };\n\n const createMcpConfig = async (config: McpServerConfig): Promise<void> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {\n method: 'POST',\n body: JSON.stringify(config),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!res.ok) {\n const errorData = await res.json();\n\n const { error } = errorData;\n\n throw new Error(\n `Failed to create MCP config: ${error || res.statusText}`,\n );\n }\n };\n\n const updateMcpConfig = async (config: McpServerConfig): Promise<void> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {\n method: 'PATCH',\n body: JSON.stringify(config),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!res.ok) {\n const errorData = await res.json();\n\n const { error } = errorData;\n\n throw new Error(\n `Failed to update MCP config: ${error || res.statusText}`,\n );\n }\n };\n\n const deleteMcpConfig = async (configName: string): Promise<void> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const res = await fetchApi.fetch(`${assistantBaseUrl}/mcp/config`, {\n method: 'DELETE',\n body: JSON.stringify({ name: configName }),\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!res.ok) {\n const errorData = await res.json();\n\n const { error } = errorData;\n\n throw new Error(\n `Failed to delete MCP config: ${error || res.statusText}`,\n );\n }\n };\n\n return {\n getUserDefinedMcpConfigs,\n createMcpConfig,\n updateMcpConfig,\n deleteMcpConfig,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { createApiRef } from '@backstage/core-plugin-api';
2
+
3
+ const summarizerApiRef = createApiRef({
4
+ id: "plugin.ai-assistant.summarizer"
5
+ });
6
+ const createSummarizerService = ({
7
+ fetchApi,
8
+ discoveryApi
9
+ }) => {
10
+ const summarizeContent = async (content) => {
11
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
12
+ const response = await fetchApi.fetch(
13
+ `${assistantBaseUrl}/summary/content`,
14
+ {
15
+ method: "POST",
16
+ body: JSON.stringify({ content }),
17
+ headers: {
18
+ "Content-Type": "application/json"
19
+ }
20
+ }
21
+ );
22
+ const data = await response.json();
23
+ return data.summary;
24
+ };
25
+ const summarizeConversation = async (messages) => {
26
+ const assistantBaseUrl = await discoveryApi.getBaseUrl("ai-assistant");
27
+ const response = await fetchApi.fetch(
28
+ `${assistantBaseUrl}/summary/conversation`,
29
+ {
30
+ method: "POST",
31
+ body: JSON.stringify({ messages }),
32
+ headers: {
33
+ "Content-Type": "application/json"
34
+ }
35
+ }
36
+ );
37
+ const data = await response.json();
38
+ return data.summary;
39
+ };
40
+ return {
41
+ summarizeContent,
42
+ summarizeConversation
43
+ };
44
+ };
45
+
46
+ export { createSummarizerService, summarizerApiRef };
47
+ //# sourceMappingURL=summarizer.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarizer.esm.js","sources":["../../src/api/summarizer.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\nexport type SummarizerApi = Awaited<ReturnType<typeof createSummarizerService>>;\n\ntype SummarizerApiOptions = {\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n};\n\nexport const summarizerApiRef = createApiRef<SummarizerApi>({\n id: 'plugin.ai-assistant.summarizer',\n});\n\nexport const createSummarizerService = ({\n fetchApi,\n discoveryApi,\n}: SummarizerApiOptions) => {\n const summarizeContent = async (content: string): Promise<string> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n\n const response = await fetchApi.fetch(\n `${assistantBaseUrl}/summary/content`,\n {\n method: 'POST',\n body: JSON.stringify({ content }),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n );\n\n const data = await response.json();\n return data.summary as string;\n };\n\n const summarizeConversation = async (\n messages: Message[],\n ): Promise<string> => {\n const assistantBaseUrl = await discoveryApi.getBaseUrl('ai-assistant');\n const response = await fetchApi.fetch(\n `${assistantBaseUrl}/summary/conversation`,\n {\n method: 'POST',\n body: JSON.stringify({ messages }),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n );\n\n const data = await response.json();\n return data.summary as string;\n };\n\n return {\n summarizeContent,\n summarizeConversation,\n };\n};\n"],"names":[],"mappings":";;AAWO,MAAM,mBAAmB,YAAA,CAA4B;AAAA,EAC1D,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,0BAA0B,CAAC;AAAA,EACtC,QAAA;AAAA,EACA;AACF,CAAA,KAA4B;AAC1B,EAAA,MAAM,gBAAA,GAAmB,OAAO,OAAA,KAAqC;AACnE,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AAErE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA;AAAA,MAC9B,GAAG,gBAAgB,CAAA,gBAAA,CAAA;AAAA,MACnB;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,SAAS,CAAA;AAAA,QAChC,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB;AAAA;AAClB;AACF,KACF;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,qBAAA,GAAwB,OAC5B,QAAA,KACoB;AACpB,IAAA,MAAM,gBAAA,GAAmB,MAAM,YAAA,CAAa,UAAA,CAAW,cAAc,CAAA;AACrE,IAAA,MAAM,QAAA,GAAW,MAAM,QAAA,CAAS,KAAA;AAAA,MAC9B,GAAG,gBAAgB,CAAA,qBAAA,CAAA;AAAA,MACnB;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,UAAU,CAAA;AAAA,QACjC,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB;AAAA;AAClB;AACF,KACF;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,gBAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,194 @@
1
+ import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
+ import { useState, useEffect, useMemo } from 'react';
3
+ import { Conversation } from '../Conversation/Conversation.esm.js';
4
+ import '@mui/material/Dialog';
5
+ import '@mui/material/DialogTitle';
6
+ import '@mui/material/DialogContent';
7
+ import '@mui/material/DialogActions';
8
+ import Button from '@mui/material/Button';
9
+ import '@mui/material/Tab';
10
+ import '@mui/material/Tabs';
11
+ import Box from '@mui/material/Box';
12
+ import Typography from '@mui/material/Typography';
13
+ import Stack from '@mui/material/Stack';
14
+ import IconButton from '@mui/material/IconButton';
15
+ import '@mui/icons-material/Close';
16
+ import '@mui/icons-material/Settings';
17
+ import '@backstage/core-plugin-api';
18
+ import 'react-use';
19
+ import '@mui/material/TextField';
20
+ import '@mui/material/Paper';
21
+ import '@mui/material/Divider';
22
+ import '@mui/material/Card';
23
+ import '@mui/material/CardContent';
24
+ import '@mui/material/CardActions';
25
+ import '@mui/material/Alert';
26
+ import '@mui/icons-material/Delete';
27
+ import AddIcon from '@mui/icons-material/Add';
28
+ import Tooltip from '@mui/material/Tooltip';
29
+ import '../../api/mcp.esm.js';
30
+ import Modal from '@mui/material/Modal';
31
+ import Chip from '@mui/material/Chip';
32
+ import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
33
+ import { useChatSettings } from '../../hooks/use-chat-settings.esm.js';
34
+ import { useLocation } from 'react-router-dom';
35
+ import { usePageSummary } from '../../hooks/use-page-summary.esm.js';
36
+
37
+ const AiAssistantChatModal = ({
38
+ open: controlledOpen,
39
+ onClose: controlledOnClose,
40
+ conversationId: controlledConversationId,
41
+ setConversationId: controlledSetConversationId
42
+ }) => {
43
+ const [modalOpen, setModalOpen] = useState(false);
44
+ const [modalConversationId, setModalConversationId] = useState();
45
+ const { modalVisible, setModalVisible, setSummaryEnabled, summaryEnabled } = useChatSettings();
46
+ const location = useLocation();
47
+ const { summary, loading, error } = usePageSummary();
48
+ useEffect(() => {
49
+ setModalVisible(true);
50
+ }, [location, setModalVisible, setSummaryEnabled]);
51
+ useEffect(() => {
52
+ if (!modalOpen) return;
53
+ setSummaryEnabled(true);
54
+ }, [modalOpen, setSummaryEnabled]);
55
+ const additionalSystemMessages = useMemo(() => {
56
+ if (!summaryEnabled || !summary) return [];
57
+ const path = location.pathname;
58
+ const title = document.title;
59
+ const content = `The user is currently on the page titled "${title}" located at "${path}". The page has the following context: ${summary}. Use this context to inform your responses where relevant.`;
60
+ return [
61
+ {
62
+ role: "system",
63
+ content,
64
+ metadata: {},
65
+ score: 0
66
+ }
67
+ ];
68
+ }, [summaryEnabled, summary, location.pathname]);
69
+ if (!modalVisible) {
70
+ return /* @__PURE__ */ jsx(Fragment, {});
71
+ }
72
+ const getChipColor = () => {
73
+ if (loading) return "primary";
74
+ if (error) return "error";
75
+ if (summary) return "success";
76
+ return void 0;
77
+ };
78
+ const getChipLabel = () => {
79
+ if (loading) return "Loading page context...";
80
+ if (error) return "Error loading page context";
81
+ if (summary) return "Page Context Loaded";
82
+ return "No page context available";
83
+ };
84
+ const isOpen = controlledOpen !== void 0 ? controlledOpen : modalOpen;
85
+ const conversationId = controlledConversationId !== void 0 ? controlledConversationId : modalConversationId;
86
+ const setConversationId = controlledSetConversationId || setModalConversationId;
87
+ const handleModalOpen = () => {
88
+ if (controlledOpen === void 0) {
89
+ setModalOpen(true);
90
+ }
91
+ };
92
+ const handleModalClose = () => {
93
+ if (controlledOnClose) {
94
+ controlledOnClose();
95
+ } else {
96
+ setModalOpen(false);
97
+ }
98
+ };
99
+ const openNewModalChat = () => {
100
+ setConversationId(void 0);
101
+ };
102
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
103
+ /* @__PURE__ */ jsx(
104
+ Box,
105
+ {
106
+ sx: {
107
+ position: "absolute",
108
+ bottom: (theme) => theme.spacing(2),
109
+ right: (theme) => theme.spacing(2),
110
+ borderRadius: "50%",
111
+ boxShadow: (theme) => theme.shadows[4],
112
+ bgcolor: (theme) => theme.palette.background.paper,
113
+ border: (theme) => `1px solid ${theme.palette.primary.main}`,
114
+ width: 50,
115
+ height: 50,
116
+ padding: (theme) => theme.spacing(1),
117
+ display: "flex",
118
+ justifyContent: "center",
119
+ alignItems: "center",
120
+ zIndex: (theme) => theme.zIndex.modal - 1,
121
+ ":hover": {
122
+ cursor: "pointer",
123
+ boxShadow: (theme) => theme.shadows[6],
124
+ bgcolor: (theme) => theme.palette.background.default
125
+ }
126
+ },
127
+ onClick: handleModalOpen,
128
+ children: /* @__PURE__ */ jsx(AutoAwesomeIcon, {})
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsx(Modal, { open: isOpen, onClose: handleModalClose, children: /* @__PURE__ */ jsx(
132
+ Box,
133
+ {
134
+ sx: {
135
+ position: "absolute",
136
+ top: "50%",
137
+ left: "50%",
138
+ transform: "translate(-50%, -50%)",
139
+ width: "60vw",
140
+ height: "60vh",
141
+ bgcolor: "background.paper",
142
+ border: "1px solid #000",
143
+ padding: 2,
144
+ display: "flex",
145
+ flexDirection: "column"
146
+ },
147
+ children: /* @__PURE__ */ jsxs(Stack, { spacing: 2, flex: 1, height: "100%", children: [
148
+ /* @__PURE__ */ jsxs(
149
+ Stack,
150
+ {
151
+ direction: "row",
152
+ spacing: 2,
153
+ justifyContent: "space-between",
154
+ alignItems: "center",
155
+ children: [
156
+ /* @__PURE__ */ jsx(Typography, { variant: "h6", component: "h2", children: "AI Assistant" }),
157
+ summaryEnabled && /* @__PURE__ */ jsx(
158
+ Tooltip,
159
+ {
160
+ title: summary ?? "No summary available",
161
+ placement: "bottom",
162
+ children: /* @__PURE__ */ jsx(
163
+ Chip,
164
+ {
165
+ label: getChipLabel(),
166
+ color: getChipColor(),
167
+ variant: "outlined"
168
+ }
169
+ )
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1, children: [
173
+ /* @__PURE__ */ jsx(Tooltip, { title: "New Chat", placement: "bottom", children: /* @__PURE__ */ jsx(IconButton, { onClick: openNewModalChat, children: /* @__PURE__ */ jsx(AddIcon, {}) }) }),
174
+ /* @__PURE__ */ jsx(Button, { variant: "outlined", onClick: handleModalClose, children: "Close" })
175
+ ] })
176
+ ]
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(
180
+ Conversation,
181
+ {
182
+ conversationId,
183
+ setConversationId,
184
+ additionalSystemMessages
185
+ }
186
+ )
187
+ ] })
188
+ }
189
+ ) })
190
+ ] });
191
+ };
192
+
193
+ export { AiAssistantChatModal };
194
+ //# sourceMappingURL=AiAssistantChatModal.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AiAssistantChatModal.esm.js","sources":["../../../src/components/AiAssistantChatModal/AiAssistantChatModal.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Conversation } from '../Conversation';\nimport Stack from '@mui/material/Stack';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport Modal from '@mui/material/Modal';\nimport Button from '@mui/material/Button';\nimport Chip from '@mui/material/Chip';\nimport AddIcon from '@mui/icons-material/Add';\nimport AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';\nimport { useChatSettings } from '../../hooks/use-chat-settings';\nimport { useLocation } from 'react-router-dom';\nimport { usePageSummary } from '../../hooks/use-page-summary';\nimport { Message } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\n\nexport interface AiAssistantChatModalProps {\n open?: boolean;\n onClose?: () => void;\n conversationId?: string;\n setConversationId?: (id: string | undefined) => void;\n}\n\nexport const AiAssistantChatModal = ({\n open: controlledOpen,\n onClose: controlledOnClose,\n conversationId: controlledConversationId,\n setConversationId: controlledSetConversationId,\n}: AiAssistantChatModalProps) => {\n const [modalOpen, setModalOpen] = useState(false);\n const [modalConversationId, setModalConversationId] = useState<string>();\n\n const { modalVisible, setModalVisible, setSummaryEnabled, summaryEnabled } =\n useChatSettings();\n\n const location = useLocation();\n\n const { summary, loading, error } = usePageSummary();\n\n // Update visibility based on route changes\n useEffect(() => {\n setModalVisible(true);\n }, [location, setModalVisible, setSummaryEnabled]);\n\n useEffect(() => {\n if (!modalOpen) return;\n setSummaryEnabled(true);\n }, [modalOpen, setSummaryEnabled]);\n\n const additionalSystemMessages: Message[] = useMemo(() => {\n if (!summaryEnabled || !summary) return [];\n\n const path = location.pathname;\n const title = document.title;\n const content = `The user is currently on the page titled \"${title}\" located at \"${path}\". The page has the following context: ${summary}. Use this context to inform your responses where relevant.`;\n\n return [\n {\n role: 'system',\n content,\n metadata: {},\n score: 0,\n },\n ];\n }, [summaryEnabled, summary, location.pathname]);\n\n // If the context says the modal is not visible, don't render anything\n if (!modalVisible) {\n return <></>;\n }\n\n const getChipColor = () => {\n if (loading) return 'primary';\n if (error) return 'error';\n if (summary) return 'success';\n return undefined;\n };\n\n const getChipLabel = () => {\n if (loading) return 'Loading page context...';\n if (error) return 'Error loading page context';\n if (summary) return 'Page Context Loaded';\n return 'No page context available';\n };\n\n // Use controlled props if provided, otherwise use internal state\n const isOpen = controlledOpen !== undefined ? controlledOpen : modalOpen;\n const conversationId =\n controlledConversationId !== undefined\n ? controlledConversationId\n : modalConversationId;\n const setConversationId =\n controlledSetConversationId || setModalConversationId;\n\n const handleModalOpen = () => {\n if (controlledOpen === undefined) {\n setModalOpen(true);\n }\n };\n\n const handleModalClose = () => {\n if (controlledOnClose) {\n controlledOnClose();\n } else {\n setModalOpen(false);\n }\n };\n\n const openNewModalChat = () => {\n setConversationId(undefined);\n };\n\n return (\n <>\n <Box\n sx={{\n position: 'absolute',\n bottom: theme => theme.spacing(2),\n right: theme => theme.spacing(2),\n borderRadius: '50%',\n boxShadow: theme => theme.shadows[4],\n bgcolor: theme => theme.palette.background.paper,\n border: theme => `1px solid ${theme.palette.primary.main}`,\n width: 50,\n height: 50,\n padding: theme => theme.spacing(1),\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: theme => theme.zIndex.modal - 1,\n ':hover': {\n cursor: 'pointer',\n boxShadow: theme => theme.shadows[6],\n bgcolor: theme => theme.palette.background.default,\n },\n }}\n onClick={handleModalOpen}\n >\n <AutoAwesomeIcon />\n </Box>\n\n <Modal open={isOpen} onClose={handleModalClose}>\n <Box\n sx={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n width: '60vw',\n height: '60vh',\n bgcolor: 'background.paper',\n border: '1px solid #000',\n padding: 2,\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n <Stack spacing={2} flex={1} height=\"100%\">\n <Stack\n direction=\"row\"\n spacing={2}\n justifyContent=\"space-between\"\n alignItems=\"center\"\n >\n <Typography variant=\"h6\" component=\"h2\">\n AI Assistant\n </Typography>\n {summaryEnabled && (\n <Tooltip\n title={summary ?? 'No summary available'}\n placement=\"bottom\"\n >\n <Chip\n label={getChipLabel()}\n color={getChipColor()}\n variant=\"outlined\"\n />\n </Tooltip>\n )}\n <Stack direction=\"row\" spacing={1}>\n <Tooltip title=\"New Chat\" placement=\"bottom\">\n <IconButton onClick={openNewModalChat}>\n <AddIcon />\n </IconButton>\n </Tooltip>\n <Button variant=\"outlined\" onClick={handleModalClose}>\n Close\n </Button>\n </Stack>\n </Stack>\n <Conversation\n conversationId={conversationId}\n setConversationId={setConversationId}\n additionalSystemMessages={additionalSystemMessages}\n />\n </Stack>\n </Box>\n </Modal>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { AiAssistantChatModal } from './AiAssistantChatModal.esm.js';
2
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -1,27 +1,50 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
- import { useState, useEffect } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Conversation } from '../Conversation/Conversation.esm.js';
4
- import { useAsync, useList } from 'react-use';
5
- import { chatApiRef } from '../../api/chat.esm.js';
6
- import { useTheme } from '@mui/material/styles';
4
+ import '@mui/material/Dialog';
5
+ import '@mui/material/DialogTitle';
6
+ import '@mui/material/DialogContent';
7
+ import '@mui/material/DialogActions';
8
+ import '@mui/material/Button';
9
+ import '@mui/material/Tab';
10
+ import '@mui/material/Tabs';
11
+ import Box from '@mui/material/Box';
12
+ import Typography from '@mui/material/Typography';
7
13
  import Stack from '@mui/material/Stack';
8
14
  import IconButton from '@mui/material/IconButton';
15
+ import '@mui/icons-material/Close';
16
+ import '@mui/icons-material/Settings';
17
+ import { useApi } from '@backstage/core-plugin-api';
18
+ import { useAsync, useList } from 'react-use';
19
+ import '@mui/material/TextField';
20
+ import '@mui/material/Paper';
21
+ import '@mui/material/Divider';
22
+ import '@mui/material/Card';
23
+ import '@mui/material/CardContent';
24
+ import '@mui/material/CardActions';
25
+ import '@mui/material/Alert';
26
+ import '@mui/icons-material/Delete';
27
+ import AddIcon from '@mui/icons-material/Add';
9
28
  import Tooltip from '@mui/material/Tooltip';
29
+ import '../../api/mcp.esm.js';
30
+ import { chatApiRef } from '../../api/chat.esm.js';
31
+ import { useTheme } from '@mui/material/styles';
10
32
  import Drawer from '@mui/material/Drawer';
11
33
  import List from '@mui/material/List';
12
34
  import ListItemButton from '@mui/material/ListItemButton';
13
35
  import ListItem from '@mui/material/ListItem';
14
- import Box from '@mui/material/Box';
15
- import Typography from '@mui/material/Typography';
16
36
  import MenuIcon from '@mui/icons-material/Menu';
17
- import AddIcon from '@mui/icons-material/Add';
18
- import { useApi } from '@backstage/core-plugin-api';
19
37
  import { signalApiRef } from '@backstage/plugin-signals-react';
38
+ import { useChatSettings } from '../../hooks/use-chat-settings.esm.js';
20
39
 
21
40
  const AiAssistantPage = () => {
22
41
  const chatApi = useApi(chatApiRef);
23
42
  const signalApi = useApi(signalApiRef);
24
43
  const theme = useTheme();
44
+ const chatSettings = useChatSettings();
45
+ useEffect(() => {
46
+ chatSettings.setModalVisible(false);
47
+ }, [chatSettings]);
25
48
  const [conversationId, setConversationId] = useState();
26
49
  const { value: conversationHistory } = useAsync(
27
50
  () => chatApi.getConversations(),
@@ -1 +1 @@
1
- {"version":3,"file":"AiAssistantPage.esm.js","sources":["../../../src/components/AiAssistantPage/AiAssistantPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Conversation } from '../Conversation';\nimport type { Conversation as ConversationType } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { useAsync, useList } from 'react-use';\nimport { chatApiRef } from '../../api/chat';\nimport { useTheme } from '@mui/material/styles';\n\nimport Stack from '@mui/material/Stack';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Drawer from '@mui/material/Drawer';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItem from '@mui/material/ListItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nimport MenuIcon from '@mui/icons-material/Menu';\nimport AddIcon from '@mui/icons-material/Add';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { signalApiRef } from '@backstage/plugin-signals-react';\n\nexport const AiAssistantPage = () => {\n const chatApi = useApi(chatApiRef);\n const signalApi = useApi(signalApiRef);\n\n const theme = useTheme();\n\n const [conversationId, setConversationId] = useState<string>();\n\n const { value: conversationHistory } = useAsync(\n () => chatApi.getConversations(),\n [chatApi],\n );\n\n const [conversations, { set, updateAt }] = useList<ConversationType>([]);\n\n useEffect(() => {\n if (!conversationHistory) {\n return;\n }\n\n set(conversationHistory);\n }, [conversationHistory, set]);\n\n useEffect(() => {\n const subscription = signalApi.subscribe<{\n conversation: ConversationType;\n }>(`ai-assistant.chat.conversation-details-update`, ({ conversation }) => {\n set(currentConversations => {\n const index = currentConversations.findIndex(\n c => c.id === conversation.id,\n );\n\n if (index !== -1) {\n const newConversations = [...currentConversations];\n newConversations[index] = conversation;\n return newConversations;\n }\n return [conversation, ...currentConversations];\n });\n });\n\n return () => subscription.unsubscribe();\n }, [signalApi, set, updateAt]);\n\n const [open, setOpen] = useState(false);\n\n const toggleDrawer = (drawerOpen: boolean) => () => {\n setOpen(drawerOpen);\n };\n\n const openNewChat = () => {\n setConversationId(undefined);\n };\n\n return (\n <>\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n height=\"100vh\"\n maxHeight=\"100vh\"\n boxSizing=\"border-box\"\n >\n <Stack direction=\"row\" spacing={2} justifyContent=\"flex-end\">\n <Tooltip title=\"New Chat\">\n <IconButton onClick={openNewChat}>\n <AddIcon />\n </IconButton>\n </Tooltip>\n\n {conversations.length > 0 && (\n <Tooltip title=\"Chat History\">\n <IconButton onClick={toggleDrawer(true)}>\n <MenuIcon />\n </IconButton>\n </Tooltip>\n )}\n </Stack>\n <Conversation\n conversationId={conversationId}\n setConversationId={setConversationId}\n />\n </Stack>\n <Drawer anchor=\"right\" open={open} onClose={toggleDrawer(false)}>\n <Box\n sx={{ width: 300 }}\n role=\"presentation\"\n onClick={toggleDrawer(false)}\n >\n <List>\n {conversations.map(conversation => (\n <ListItem\n key={conversation.id}\n sx={{\n fontSize: theme.typography.body1.fontSize,\n }}\n >\n <ListItemButton\n sx={{\n justifyContent: 'flex-start !important',\n padding: `${theme.spacing(1)} !important`,\n borderRadius: `${theme.spacing(1)} !important`,\n backgroundColor:\n conversationId === conversation.id\n ? `${theme.palette.action.selected} !important`\n : 'transparent !important',\n }}\n onClick={() => setConversationId(conversation.id)}\n >\n <Typography\n variant=\"body1\"\n sx={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n width: '100%',\n }}\n >\n {conversation.title}\n </Typography>\n </ListItemButton>\n </ListItem>\n ))}\n </List>\n </Box>\n </Drawer>\n </>\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AiAssistantPage.esm.js","sources":["../../../src/components/AiAssistantPage/AiAssistantPage.tsx"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { Conversation } from '../Conversation';\nimport type { Conversation as ConversationType } from '@sweetoburrito/backstage-plugin-ai-assistant-common';\nimport { useAsync, useList } from 'react-use';\nimport { chatApiRef } from '../../api/chat';\nimport { useTheme } from '@mui/material/styles';\n\nimport Stack from '@mui/material/Stack';\nimport IconButton from '@mui/material/IconButton';\nimport Tooltip from '@mui/material/Tooltip';\nimport Drawer from '@mui/material/Drawer';\nimport List from '@mui/material/List';\nimport ListItemButton from '@mui/material/ListItemButton';\nimport ListItem from '@mui/material/ListItem';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nimport MenuIcon from '@mui/icons-material/Menu';\nimport AddIcon from '@mui/icons-material/Add';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { signalApiRef } from '@backstage/plugin-signals-react';\nimport { useChatSettings } from '../../hooks/use-chat-settings';\n\nexport const AiAssistantPage = () => {\n const chatApi = useApi(chatApiRef);\n const signalApi = useApi(signalApiRef);\n\n const theme = useTheme();\n\n const chatSettings = useChatSettings();\n\n useEffect(() => {\n chatSettings.setModalVisible(false);\n }, [chatSettings]);\n\n const [conversationId, setConversationId] = useState<string>();\n\n const { value: conversationHistory } = useAsync(\n () => chatApi.getConversations(),\n [chatApi],\n );\n\n const [conversations, { set, updateAt }] = useList<ConversationType>([]);\n\n useEffect(() => {\n if (!conversationHistory) {\n return;\n }\n\n set(conversationHistory);\n }, [conversationHistory, set]);\n\n useEffect(() => {\n const subscription = signalApi.subscribe<{\n conversation: ConversationType;\n }>(`ai-assistant.chat.conversation-details-update`, ({ conversation }) => {\n set(currentConversations => {\n const index = currentConversations.findIndex(\n c => c.id === conversation.id,\n );\n\n if (index !== -1) {\n const newConversations = [...currentConversations];\n newConversations[index] = conversation;\n return newConversations;\n }\n return [conversation, ...currentConversations];\n });\n });\n\n return () => subscription.unsubscribe();\n }, [signalApi, set, updateAt]);\n\n const [open, setOpen] = useState(false);\n\n const toggleDrawer = (drawerOpen: boolean) => () => {\n setOpen(drawerOpen);\n };\n\n const openNewChat = () => {\n setConversationId(undefined);\n };\n\n return (\n <>\n <Stack\n padding={2}\n spacing={2}\n flex={1}\n height=\"100vh\"\n maxHeight=\"100vh\"\n boxSizing=\"border-box\"\n >\n <Stack direction=\"row\" spacing={2} justifyContent=\"flex-end\">\n <Tooltip title=\"New Chat\">\n <IconButton onClick={openNewChat}>\n <AddIcon />\n </IconButton>\n </Tooltip>\n\n {conversations.length > 0 && (\n <Tooltip title=\"Chat History\">\n <IconButton onClick={toggleDrawer(true)}>\n <MenuIcon />\n </IconButton>\n </Tooltip>\n )}\n </Stack>\n <Conversation\n conversationId={conversationId}\n setConversationId={setConversationId}\n />\n </Stack>\n <Drawer anchor=\"right\" open={open} onClose={toggleDrawer(false)}>\n <Box\n sx={{ width: 300 }}\n role=\"presentation\"\n onClick={toggleDrawer(false)}\n >\n <List>\n {conversations.map(conversation => (\n <ListItem\n key={conversation.id}\n sx={{\n fontSize: theme.typography.body1.fontSize,\n }}\n >\n <ListItemButton\n sx={{\n justifyContent: 'flex-start !important',\n padding: `${theme.spacing(1)} !important`,\n borderRadius: `${theme.spacing(1)} !important`,\n backgroundColor:\n conversationId === conversation.id\n ? `${theme.palette.action.selected} !important`\n : 'transparent !important',\n }}\n onClick={() => setConversationId(conversation.id)}\n >\n <Typography\n variant=\"body1\"\n sx={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n width: '100%',\n }}\n >\n {conversation.title}\n </Typography>\n </ListItemButton>\n </ListItem>\n ))}\n </List>\n </Box>\n </Drawer>\n </>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBO,MAAM,kBAAkB,MAAM;AACnC,EAAA,MAAM,OAAA,GAAU,OAAO,UAAU,CAAA;AACjC,EAAA,MAAM,SAAA,GAAY,OAAO,YAAY,CAAA;AAErC,EAAA,MAAM,QAAQ,QAAA,EAAS;AAEvB,EAAA,MAAM,eAAe,eAAA,EAAgB;AAErC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,CAAa,gBAAgB,KAAK,CAAA;AAAA,EACpC,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAEjB,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAI,QAAA,EAAiB;AAE7D,EAAA,MAAM,EAAE,KAAA,EAAO,mBAAA,EAAoB,GAAI,QAAA;AAAA,IACrC,MAAM,QAAQ,gBAAA,EAAiB;AAAA,IAC/B,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,CAAC,eAAe,EAAE,GAAA,EAAK,UAAU,CAAA,GAAI,OAAA,CAA0B,EAAE,CAAA;AAEvE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,mBAAA,EAAqB;AACxB,MAAA;AAAA,IACF;AAEA,IAAA,GAAA,CAAI,mBAAmB,CAAA;AAAA,EACzB,CAAA,EAAG,CAAC,mBAAA,EAAqB,GAAG,CAAC,CAAA;AAE7B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,eAAe,SAAA,CAAU,SAAA,CAE5B,iDAAiD,CAAC,EAAE,cAAa,KAAM;AACxE,MAAA,GAAA,CAAI,CAAA,oBAAA,KAAwB;AAC1B,QAAA,MAAM,QAAQ,oBAAA,CAAqB,SAAA;AAAA,UACjC,CAAA,CAAA,KAAK,CAAA,CAAE,EAAA,KAAO,YAAA,CAAa;AAAA,SAC7B;AAEA,QAAA,IAAI,UAAU,EAAA,EAAI;AAChB,UAAA,MAAM,gBAAA,GAAmB,CAAC,GAAG,oBAAoB,CAAA;AACjD,UAAA,gBAAA,CAAiB,KAAK,CAAA,GAAI,YAAA;AAC1B,UAAA,OAAO,gBAAA;AAAA,QACT;AACA,QAAA,OAAO,CAAC,YAAA,EAAc,GAAG,oBAAoB,CAAA;AAAA,MAC/C,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,OAAO,MAAM,aAAa,WAAA,EAAY;AAAA,EACxC,CAAA,EAAG,CAAC,SAAA,EAAW,GAAA,EAAK,QAAQ,CAAC,CAAA;AAE7B,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,KAAK,CAAA;AAEtC,EAAA,MAAM,YAAA,GAAe,CAAC,UAAA,KAAwB,MAAM;AAClD,IAAA,OAAA,CAAQ,UAAU,CAAA;AAAA,EACpB,CAAA;AAEA,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,iBAAA,CAAkB,MAAS,CAAA;AAAA,EAC7B,CAAA;AAEA,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,OAAA,EAAS,CAAA;AAAA,QACT,OAAA,EAAS,CAAA;AAAA,QACT,IAAA,EAAM,CAAA;AAAA,QACN,MAAA,EAAO,OAAA;AAAA,QACP,SAAA,EAAU,OAAA;AAAA,QACV,SAAA,EAAU,YAAA;AAAA,QAEV,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,SAAM,SAAA,EAAU,KAAA,EAAM,OAAA,EAAS,CAAA,EAAG,gBAAe,UAAA,EAChD,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,KAAA,EAAM,UAAA,EACb,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,SAAS,WAAA,EACnB,QAAA,kBAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,CAAA,EACX,CAAA,EACF,CAAA;AAAA,YAEC,cAAc,MAAA,GAAS,CAAA,oBACtB,GAAA,CAAC,OAAA,EAAA,EAAQ,OAAM,cAAA,EACb,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,OAAA,EAAS,aAAa,IAAI,CAAA,EACpC,QAAA,kBAAA,GAAA,CAAC,QAAA,EAAA,EAAS,GACZ,CAAA,EACF;AAAA,WAAA,EAEJ,CAAA;AAAA,0BACA,GAAA;AAAA,YAAC,YAAA;AAAA,YAAA;AAAA,cACC,cAAA;AAAA,cACA;AAAA;AAAA;AACF;AAAA;AAAA,KACF;AAAA,oBACA,GAAA,CAAC,UAAO,MAAA,EAAO,OAAA,EAAQ,MAAY,OAAA,EAAS,YAAA,CAAa,KAAK,CAAA,EAC5D,QAAA,kBAAA,GAAA;AAAA,MAAC,GAAA;AAAA,MAAA;AAAA,QACC,EAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAI;AAAA,QACjB,IAAA,EAAK,cAAA;AAAA,QACL,OAAA,EAAS,aAAa,KAAK,CAAA;AAAA,QAE3B,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EACE,QAAA,EAAA,aAAA,CAAc,GAAA,CAAI,CAAA,YAAA,qBACjB,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YAEC,EAAA,EAAI;AAAA,cACF,QAAA,EAAU,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM;AAAA,aACnC;AAAA,YAEA,QAAA,kBAAA,GAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBACC,EAAA,EAAI;AAAA,kBACF,cAAA,EAAgB,uBAAA;AAAA,kBAChB,OAAA,EAAS,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,WAAA,CAAA;AAAA,kBAC5B,YAAA,EAAc,CAAA,EAAG,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,WAAA,CAAA;AAAA,kBACjC,eAAA,EACE,mBAAmB,YAAA,CAAa,EAAA,GAC5B,GAAG,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,QAAQ,CAAA,WAAA,CAAA,GAChC;AAAA,iBACR;AAAA,gBACA,OAAA,EAAS,MAAM,iBAAA,CAAkB,YAAA,CAAa,EAAE,CAAA;AAAA,gBAEhD,QAAA,kBAAA,GAAA;AAAA,kBAAC,UAAA;AAAA,kBAAA;AAAA,oBACC,OAAA,EAAQ,OAAA;AAAA,oBACR,EAAA,EAAI;AAAA,sBACF,QAAA,EAAU,QAAA;AAAA,sBACV,YAAA,EAAc,UAAA;AAAA,sBACd,UAAA,EAAY,QAAA;AAAA,sBACZ,KAAA,EAAO;AAAA,qBACT;AAAA,oBAEC,QAAA,EAAA,YAAA,CAAa;AAAA;AAAA;AAChB;AAAA;AACF,WAAA;AAAA,UA5BK,YAAA,CAAa;AAAA,SA8BrB,CAAA,EACH;AAAA;AAAA,KACF,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}