@contractspec/module.ai-chat 0.0.0-canary-20260113170453

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -0
  3. package/dist/ai-chat.feature.d.ts +12 -0
  4. package/dist/ai-chat.feature.d.ts.map +1 -0
  5. package/dist/ai-chat.feature.js +102 -0
  6. package/dist/ai-chat.feature.js.map +1 -0
  7. package/dist/ai-chat.operations.d.ts +243 -0
  8. package/dist/ai-chat.operations.d.ts.map +1 -0
  9. package/dist/ai-chat.operations.js +172 -0
  10. package/dist/ai-chat.operations.js.map +1 -0
  11. package/dist/context/context-builder.d.ts +57 -0
  12. package/dist/context/context-builder.d.ts.map +1 -0
  13. package/dist/context/context-builder.js +148 -0
  14. package/dist/context/context-builder.js.map +1 -0
  15. package/dist/context/file-operations.d.ts +100 -0
  16. package/dist/context/file-operations.d.ts.map +1 -0
  17. package/dist/context/file-operations.js +175 -0
  18. package/dist/context/file-operations.js.map +1 -0
  19. package/dist/context/index.d.ts +4 -0
  20. package/dist/context/index.js +5 -0
  21. package/dist/context/workspace-context.d.ts +117 -0
  22. package/dist/context/workspace-context.d.ts.map +1 -0
  23. package/dist/context/workspace-context.js +124 -0
  24. package/dist/context/workspace-context.js.map +1 -0
  25. package/dist/core/chat-service.d.ts +73 -0
  26. package/dist/core/chat-service.d.ts.map +1 -0
  27. package/dist/core/chat-service.js +227 -0
  28. package/dist/core/chat-service.js.map +1 -0
  29. package/dist/core/conversation-store.d.ts +74 -0
  30. package/dist/core/conversation-store.d.ts.map +1 -0
  31. package/dist/core/conversation-store.js +109 -0
  32. package/dist/core/conversation-store.js.map +1 -0
  33. package/dist/core/index.d.ts +4 -0
  34. package/dist/core/index.js +4 -0
  35. package/dist/core/message-types.d.ts +150 -0
  36. package/dist/core/message-types.d.ts.map +1 -0
  37. package/dist/events.d.ts +115 -0
  38. package/dist/events.d.ts.map +1 -0
  39. package/dist/events.js +98 -0
  40. package/dist/events.js.map +1 -0
  41. package/dist/index.d.ts +21 -0
  42. package/dist/index.js +23 -0
  43. package/dist/presentation/components/ChatContainer.d.ts +21 -0
  44. package/dist/presentation/components/ChatContainer.d.ts.map +1 -0
  45. package/dist/presentation/components/ChatContainer.js +63 -0
  46. package/dist/presentation/components/ChatContainer.js.map +1 -0
  47. package/dist/presentation/components/ChatInput.d.ts +35 -0
  48. package/dist/presentation/components/ChatInput.d.ts.map +1 -0
  49. package/dist/presentation/components/ChatInput.js +149 -0
  50. package/dist/presentation/components/ChatInput.js.map +1 -0
  51. package/dist/presentation/components/ChatMessage.d.ts +24 -0
  52. package/dist/presentation/components/ChatMessage.d.ts.map +1 -0
  53. package/dist/presentation/components/ChatMessage.js +136 -0
  54. package/dist/presentation/components/ChatMessage.js.map +1 -0
  55. package/dist/presentation/components/CodePreview.d.ts +40 -0
  56. package/dist/presentation/components/CodePreview.d.ts.map +1 -0
  57. package/dist/presentation/components/CodePreview.js +127 -0
  58. package/dist/presentation/components/CodePreview.js.map +1 -0
  59. package/dist/presentation/components/ContextIndicator.d.ts +26 -0
  60. package/dist/presentation/components/ContextIndicator.d.ts.map +1 -0
  61. package/dist/presentation/components/ContextIndicator.js +97 -0
  62. package/dist/presentation/components/ContextIndicator.js.map +1 -0
  63. package/dist/presentation/components/ModelPicker.d.ts +39 -0
  64. package/dist/presentation/components/ModelPicker.d.ts.map +1 -0
  65. package/dist/presentation/components/ModelPicker.js +202 -0
  66. package/dist/presentation/components/ModelPicker.js.map +1 -0
  67. package/dist/presentation/components/index.d.ts +7 -0
  68. package/dist/presentation/components/index.js +8 -0
  69. package/dist/presentation/hooks/index.d.ts +3 -0
  70. package/dist/presentation/hooks/index.js +4 -0
  71. package/dist/presentation/hooks/useChat.d.ts +67 -0
  72. package/dist/presentation/hooks/useChat.d.ts.map +1 -0
  73. package/dist/presentation/hooks/useChat.js +172 -0
  74. package/dist/presentation/hooks/useChat.js.map +1 -0
  75. package/dist/presentation/hooks/useProviders.d.ts +38 -0
  76. package/dist/presentation/hooks/useProviders.d.ts.map +1 -0
  77. package/dist/presentation/hooks/useProviders.js +41 -0
  78. package/dist/presentation/hooks/useProviders.js.map +1 -0
  79. package/dist/presentation/index.d.ts +11 -0
  80. package/dist/presentation/index.js +12 -0
  81. package/dist/providers/chat-utilities.d.ts +15 -0
  82. package/dist/providers/chat-utilities.d.ts.map +1 -0
  83. package/dist/providers/chat-utilities.js +17 -0
  84. package/dist/providers/chat-utilities.js.map +1 -0
  85. package/dist/providers/index.d.ts +3 -0
  86. package/dist/providers/index.js +4 -0
  87. package/dist/schema.d.ts +222 -0
  88. package/dist/schema.d.ts.map +1 -0
  89. package/dist/schema.js +100 -0
  90. package/dist/schema.js.map +1 -0
  91. package/package.json +87 -0
@@ -0,0 +1,115 @@
1
+ import * as _contractspec_lib_contracts0 from "@contractspec/lib.contracts";
2
+ import * as _contractspec_lib_schema0 from "@contractspec/lib.schema";
3
+
4
+ //#region src/events.d.ts
5
+ declare const MessageSentEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
6
+ id: {
7
+ type: _contractspec_lib_schema0.FieldType<string, string>;
8
+ isOptional: false;
9
+ };
10
+ role: {
11
+ type: _contractspec_lib_schema0.FieldType<string, string>;
12
+ isOptional: false;
13
+ };
14
+ content: {
15
+ type: _contractspec_lib_schema0.FieldType<string, string>;
16
+ isOptional: false;
17
+ };
18
+ status: {
19
+ type: _contractspec_lib_schema0.FieldType<string, string>;
20
+ isOptional: false;
21
+ };
22
+ createdAt: {
23
+ type: _contractspec_lib_schema0.FieldType<Date, string>;
24
+ isOptional: false;
25
+ };
26
+ }>>;
27
+ declare const MessageReceivedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
28
+ id: {
29
+ type: _contractspec_lib_schema0.FieldType<string, string>;
30
+ isOptional: false;
31
+ };
32
+ role: {
33
+ type: _contractspec_lib_schema0.FieldType<string, string>;
34
+ isOptional: false;
35
+ };
36
+ content: {
37
+ type: _contractspec_lib_schema0.FieldType<string, string>;
38
+ isOptional: false;
39
+ };
40
+ status: {
41
+ type: _contractspec_lib_schema0.FieldType<string, string>;
42
+ isOptional: false;
43
+ };
44
+ createdAt: {
45
+ type: _contractspec_lib_schema0.FieldType<Date, string>;
46
+ isOptional: false;
47
+ };
48
+ }>>;
49
+ declare const ConversationCreatedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
50
+ id: {
51
+ type: _contractspec_lib_schema0.FieldType<string, string>;
52
+ isOptional: false;
53
+ };
54
+ title: {
55
+ type: _contractspec_lib_schema0.FieldType<string, string>;
56
+ isOptional: true;
57
+ };
58
+ status: {
59
+ type: _contractspec_lib_schema0.FieldType<string, string>;
60
+ isOptional: false;
61
+ };
62
+ messages: {
63
+ type: _contractspec_lib_schema0.SchemaModel<{
64
+ id: {
65
+ type: _contractspec_lib_schema0.FieldType<string, string>;
66
+ isOptional: false;
67
+ };
68
+ role: {
69
+ type: _contractspec_lib_schema0.FieldType<string, string>;
70
+ isOptional: false;
71
+ };
72
+ content: {
73
+ type: _contractspec_lib_schema0.FieldType<string, string>;
74
+ isOptional: false;
75
+ };
76
+ status: {
77
+ type: _contractspec_lib_schema0.FieldType<string, string>;
78
+ isOptional: false;
79
+ };
80
+ createdAt: {
81
+ type: _contractspec_lib_schema0.FieldType<Date, string>;
82
+ isOptional: false;
83
+ };
84
+ }>;
85
+ isArray: true;
86
+ isOptional: false;
87
+ };
88
+ provider: {
89
+ type: _contractspec_lib_schema0.FieldType<string, string>;
90
+ isOptional: false;
91
+ };
92
+ model: {
93
+ type: _contractspec_lib_schema0.FieldType<string, string>;
94
+ isOptional: false;
95
+ };
96
+ }>>;
97
+ declare const ConversationDeletedEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
98
+ id: {
99
+ type: _contractspec_lib_schema0.FieldType<string, string>;
100
+ isOptional: false;
101
+ };
102
+ }>>;
103
+ declare const ChatErrorEvent: _contractspec_lib_contracts0.EventSpec<_contractspec_lib_schema0.SchemaModel<{
104
+ code: {
105
+ type: _contractspec_lib_schema0.FieldType<string, string>;
106
+ isOptional: false;
107
+ };
108
+ message: {
109
+ type: _contractspec_lib_schema0.FieldType<string, string>;
110
+ isOptional: false;
111
+ };
112
+ }>>;
113
+ //#endregion
114
+ export { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent };
115
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","names":[],"sources":["../src/events.ts"],"sourcesContent":[],"mappings":";;;;cAIa,kBAAgB,4BAAA,CAAA,oCAAA;;UAU3B,yBAAA,CAAA;;EAVW,CAAA;EAUX,IAAA,EAAA;;;;;;;EAV2B,CAAA;EAAA,MAAA,EAAA;IAYhB,IAAA,qCAUX,CAAA,MAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;;;;;;AAV+B,cAApB,oBAAoB,EAAA,4BAAA,CAAA,SAAA,2BAAA,WAAA,CAAA;EAAA,EAAA,EAAA;IAYpB,IAAA,EAFX,yBAAA,CAAA,SAYA,CAAA,MAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;;;;;;;;;;;;EAVmC,CAAA;EAAA,SAAA,EAAA;IAYxB,IAAA,qCAeX,KAAA,EAAA,MAAA,CAAA;IAAA,UAAA,EAAA,KAAA;;CAfmC,CAAA,CAAA;AAAA,cAZxB,wBAYwB,EAZA,4BAAA,CAAA,SAYA,2BAZA,WAYA,CAAA;EAiBxB,EAAA,EAAA;IAgBX,IAAA,EAnCA,yBAAA,CAAA,SAmCA,CAAA,MAAA,EAAA,MAAA,CAAA;;;EAhByB,KAAA,EAAA;IAAA,IAAA,qCAAA,CAAA,MAAA,EAAA,MAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAjBd,0BAAwB,4BAAA,CAAA,oCAAA;;UAenC,yBAAA,CAAA;;;;cAEW,gBAAc,4BAAA,CAAA,oCAAA;;UAgBzB,yBAAA,CAAA"}
package/dist/events.js ADDED
@@ -0,0 +1,98 @@
1
+ import { ChatConversationModel, ChatMessageModel } from "./schema.js";
2
+ import { defineEvent } from "@contractspec/lib.contracts";
3
+ import { ScalarTypeEnum, defineSchemaModel } from "@contractspec/lib.schema";
4
+
5
+ //#region src/events.ts
6
+ const MessageSentEvent = defineEvent({
7
+ meta: {
8
+ key: "ai-chat.message.sent",
9
+ version: "1.0.0",
10
+ description: "Message sent by user",
11
+ stability: "stable",
12
+ owners: ["@ai-chat"],
13
+ tags: [
14
+ "ai-chat",
15
+ "message",
16
+ "sent"
17
+ ]
18
+ },
19
+ payload: ChatMessageModel
20
+ });
21
+ const MessageReceivedEvent = defineEvent({
22
+ meta: {
23
+ key: "ai-chat.message.received",
24
+ version: "1.0.0",
25
+ description: "Message received from AI",
26
+ stability: "stable",
27
+ owners: ["@ai-chat"],
28
+ tags: [
29
+ "ai-chat",
30
+ "message",
31
+ "received"
32
+ ]
33
+ },
34
+ payload: ChatMessageModel
35
+ });
36
+ const ConversationCreatedEvent = defineEvent({
37
+ meta: {
38
+ key: "ai-chat.conversation.created",
39
+ version: "1.0.0",
40
+ description: "New conversation created",
41
+ stability: "stable",
42
+ owners: ["@ai-chat"],
43
+ tags: [
44
+ "ai-chat",
45
+ "conversation",
46
+ "created"
47
+ ]
48
+ },
49
+ payload: ChatConversationModel
50
+ });
51
+ const ConversationDeletedEvent = defineEvent({
52
+ meta: {
53
+ key: "ai-chat.conversation.deleted",
54
+ version: "1.0.0",
55
+ description: "Conversation deleted",
56
+ stability: "stable",
57
+ owners: ["@ai-chat"],
58
+ tags: [
59
+ "ai-chat",
60
+ "conversation",
61
+ "deleted"
62
+ ]
63
+ },
64
+ payload: defineSchemaModel({
65
+ name: "ConversationDeletedPayload",
66
+ fields: { id: {
67
+ type: ScalarTypeEnum.String_unsecure(),
68
+ isOptional: false
69
+ } }
70
+ })
71
+ });
72
+ const ChatErrorEvent = defineEvent({
73
+ meta: {
74
+ key: "ai-chat.error",
75
+ version: "1.0.0",
76
+ description: "Chat error occurred",
77
+ stability: "stable",
78
+ owners: ["@ai-chat"],
79
+ tags: ["ai-chat", "error"]
80
+ },
81
+ payload: defineSchemaModel({
82
+ name: "ChatErrorPayload",
83
+ fields: {
84
+ code: {
85
+ type: ScalarTypeEnum.String_unsecure(),
86
+ isOptional: false
87
+ },
88
+ message: {
89
+ type: ScalarTypeEnum.String_unsecure(),
90
+ isOptional: false
91
+ }
92
+ }
93
+ })
94
+ });
95
+
96
+ //#endregion
97
+ export { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent };
98
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","names":[],"sources":["../src/events.ts"],"sourcesContent":["import { defineEvent } from '@contractspec/lib.contracts';\nimport { defineSchemaModel, ScalarTypeEnum } from '@contractspec/lib.schema';\nimport { ChatMessageModel, ChatConversationModel } from './schema';\n\nexport const MessageSentEvent = defineEvent({\n meta: {\n key: 'ai-chat.message.sent',\n version: '1.0.0',\n description: 'Message sent by user',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'message', 'sent'],\n },\n payload: ChatMessageModel,\n});\n\nexport const MessageReceivedEvent = defineEvent({\n meta: {\n key: 'ai-chat.message.received',\n version: '1.0.0',\n description: 'Message received from AI',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'message', 'received'],\n },\n payload: ChatMessageModel,\n});\n\nexport const ConversationCreatedEvent = defineEvent({\n meta: {\n key: 'ai-chat.conversation.created',\n version: '1.0.0',\n description: 'New conversation created',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'conversation', 'created'],\n },\n payload: ChatConversationModel,\n});\n\nexport const ConversationDeletedEvent = defineEvent({\n meta: {\n key: 'ai-chat.conversation.deleted',\n version: '1.0.0',\n description: 'Conversation deleted',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'conversation', 'deleted'],\n },\n payload: defineSchemaModel({\n name: 'ConversationDeletedPayload',\n fields: {\n id: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n }),\n});\n\nexport const ChatErrorEvent = defineEvent({\n meta: {\n key: 'ai-chat.error',\n version: '1.0.0',\n description: 'Chat error occurred',\n stability: 'stable',\n owners: ['@ai-chat'],\n tags: ['ai-chat', 'error'],\n },\n payload: defineSchemaModel({\n name: 'ChatErrorPayload',\n fields: {\n code: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n message: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n }),\n});\n"],"mappings":";;;;;AAIA,MAAa,mBAAmB,YAAY;CAC1C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAW;GAAO;EACrC;CACD,SAAS;CACV,CAAC;AAEF,MAAa,uBAAuB,YAAY;CAC9C,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAW;GAAW;EACzC;CACD,SAAS;CACV,CAAC;AAEF,MAAa,2BAA2B,YAAY;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAgB;GAAU;EAC7C;CACD,SAAS;CACV,CAAC;AAEF,MAAa,2BAA2B,YAAY;CAClD,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM;GAAC;GAAW;GAAgB;GAAU;EAC7C;CACD,SAAS,kBAAkB;EACzB,MAAM;EACN,QAAQ,EACN,IAAI;GAAE,MAAM,eAAe,iBAAiB;GAAE,YAAY;GAAO,EAClE;EACF,CAAC;CACH,CAAC;AAEF,MAAa,iBAAiB,YAAY;CACxC,MAAM;EACJ,KAAK;EACL,SAAS;EACT,aAAa;EACb,WAAW;EACX,QAAQ,CAAC,WAAW;EACpB,MAAM,CAAC,WAAW,QAAQ;EAC3B;CACD,SAAS,kBAAkB;EACzB,MAAM;EACN,QAAQ;GACN,MAAM;IAAE,MAAM,eAAe,iBAAiB;IAAE,YAAY;IAAO;GACnE,SAAS;IAAE,MAAM,eAAe,iBAAiB;IAAE,YAAY;IAAO;GACvE;EACF,CAAC;CACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { FileInfo, SpecInfo, WorkspaceContext, WorkspaceContextConfig, WorkspaceSummary, createWorkspaceContext } from "./context/workspace-context.js";
2
+ import { BuiltContext, ContextBuilder, ContextBuilderOptions, ContextEntry, createContextBuilder } from "./context/context-builder.js";
3
+ import { FileOperation, FileOperationResult, FileOperations, FileReadResult, FileSystem, FileWriteResult, createNodeFileOperations } from "./context/file-operations.js";
4
+ import { AiChatFeature } from "./ai-chat.feature.js";
5
+ import { ChatContainer } from "./presentation/components/ChatContainer.js";
6
+ import { ChatMessage } from "./presentation/components/ChatMessage.js";
7
+ import { ChatInput } from "./presentation/components/ChatInput.js";
8
+ import { ModelPicker } from "./presentation/components/ModelPicker.js";
9
+ import { ContextIndicator } from "./presentation/components/ContextIndicator.js";
10
+ import { CodePreview } from "./presentation/components/CodePreview.js";
11
+ import "./presentation/components/index.js";
12
+ import { UseChatOptions, UseChatReturn, useChat } from "./presentation/hooks/useChat.js";
13
+ import { UseProvidersReturn, useProviders } from "./presentation/hooks/useProviders.js";
14
+ import "./presentation/hooks/index.js";
15
+ import "./presentation/index.js";
16
+ import { isStudioAvailable, supportsLocalMode } from "./providers/chat-utilities.js";
17
+ import { ChatModelInfo, ChatProvider, ChatProviderConfig, ChatProviderMode, ChatProviderName, DEFAULT_MODELS, MODELS, ModelCapabilities, ProviderAvailability, createProvider, createProviderFromEnv, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, listOllamaModels, validateProvider } from "./providers/index.js";
18
+ import { ChatConversationModel, ChatMessageModel, ListConversationsOutputModel, SendMessageInputModel, SendMessageOutputModel } from "./schema.js";
19
+ import { DeleteConversationContract, GetConversationContract, ListConversationsContract, ListProvidersContract, ScanContextContract, SendMessageContract, StreamMessageContract } from "./ai-chat.operations.js";
20
+ import { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent } from "./events.js";
21
+ export { AiChatFeature, BuiltContext, ChatContainer, ChatConversationModel, ChatErrorEvent, ChatInput, ChatMessage, ChatMessage as ChatMessageComponent, ChatMessageModel, ChatModelInfo, ChatProvider, ChatProviderConfig, ChatProviderMode, ChatProviderName, CodePreview, ContextBuilder, ContextBuilderOptions, ContextEntry, ContextIndicator, ConversationCreatedEvent, ConversationDeletedEvent, DEFAULT_MODELS, DeleteConversationContract, FileInfo, FileOperation, FileOperationResult, FileOperations, FileReadResult, FileSystem, FileWriteResult, GetConversationContract, ListConversationsContract, ListConversationsOutputModel, ListProvidersContract, MODELS, MessageReceivedEvent, MessageSentEvent, ModelCapabilities, ModelPicker, ProviderAvailability, ScanContextContract, SendMessageContract, SendMessageInputModel, SendMessageOutputModel, SpecInfo, StreamMessageContract, UseChatOptions, UseChatReturn, UseProvidersReturn, WorkspaceContext, WorkspaceContextConfig, WorkspaceSummary, createContextBuilder, createNodeFileOperations, createProvider, createProviderFromEnv, createWorkspaceContext, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, isStudioAvailable, listOllamaModels, supportsLocalMode, useChat, useProviders, validateProvider };
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ import { AiChatFeature } from "./ai-chat.feature.js";
2
+ import { ChatContainer } from "./presentation/components/ChatContainer.js";
3
+ import { CodePreview } from "./presentation/components/CodePreview.js";
4
+ import { ChatMessage } from "./presentation/components/ChatMessage.js";
5
+ import { ChatInput } from "./presentation/components/ChatInput.js";
6
+ import { ModelPicker } from "./presentation/components/ModelPicker.js";
7
+ import { ContextIndicator } from "./presentation/components/ContextIndicator.js";
8
+ import "./presentation/components/index.js";
9
+ import { useChat } from "./presentation/hooks/useChat.js";
10
+ import { useProviders } from "./presentation/hooks/useProviders.js";
11
+ import "./presentation/hooks/index.js";
12
+ import "./presentation/index.js";
13
+ import { isStudioAvailable, supportsLocalMode } from "./providers/chat-utilities.js";
14
+ import { DEFAULT_MODELS, MODELS, createProvider, createProviderFromEnv, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, listOllamaModels, validateProvider } from "./providers/index.js";
15
+ import { WorkspaceContext, createWorkspaceContext } from "./context/workspace-context.js";
16
+ import { ContextBuilder, createContextBuilder } from "./context/context-builder.js";
17
+ import { FileOperations, createNodeFileOperations } from "./context/file-operations.js";
18
+ import "./context/index.js";
19
+ import { ChatConversationModel, ChatMessageModel, ListConversationsOutputModel, SendMessageInputModel, SendMessageOutputModel } from "./schema.js";
20
+ import { DeleteConversationContract, GetConversationContract, ListConversationsContract, ListProvidersContract, ScanContextContract, SendMessageContract, StreamMessageContract } from "./ai-chat.operations.js";
21
+ import { ChatErrorEvent, ConversationCreatedEvent, ConversationDeletedEvent, MessageReceivedEvent, MessageSentEvent } from "./events.js";
22
+
23
+ export { AiChatFeature, ChatContainer, ChatConversationModel, ChatErrorEvent, ChatInput, ChatMessage, ChatMessage as ChatMessageComponent, ChatMessageModel, CodePreview, ContextBuilder, ContextIndicator, ConversationCreatedEvent, ConversationDeletedEvent, DEFAULT_MODELS, DeleteConversationContract, FileOperations, GetConversationContract, ListConversationsContract, ListConversationsOutputModel, ListProvidersContract, MODELS, MessageReceivedEvent, MessageSentEvent, ModelPicker, ScanContextContract, SendMessageContract, SendMessageInputModel, SendMessageOutputModel, StreamMessageContract, WorkspaceContext, createContextBuilder, createNodeFileOperations, createProvider, createProviderFromEnv, createWorkspaceContext, getAvailableProviders, getDefaultModel, getEnvVarName, getModelInfo, getModelsForProvider, getRecommendedModels, hasCredentials, isOllamaRunning, isStudioAvailable, listOllamaModels, supportsLocalMode, useChat, useProviders, validateProvider };
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/presentation/components/ChatContainer.d.ts
5
+ interface ChatContainerProps {
6
+ children: React.ReactNode;
7
+ className?: string;
8
+ /** Show scroll-to-bottom button when scrolled up */
9
+ showScrollButton?: boolean;
10
+ }
11
+ /**
12
+ * Container component for chat messages with scrolling
13
+ */
14
+ declare function ChatContainer({
15
+ children,
16
+ className,
17
+ showScrollButton
18
+ }: ChatContainerProps): react_jsx_runtime0.JSX.Element;
19
+ //#endregion
20
+ export { ChatContainer };
21
+ //# sourceMappingURL=ChatContainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatContainer.d.ts","names":[],"sources":["../../../src/presentation/components/ChatContainer.tsx"],"sourcesContent":[],"mappings":";;;;UAMiB,kBAAA;YACL,KAAA,CAAM;;EADD;EAUD,gBAAa,CAAA,EAAA,OAAA;;;;;AAIR,iBAJL,aAAA,CAIK;EAAA,QAAA;EAAA,SAAA;EAAA;AAAA,CAAA,EAAlB,kBAAkB,CAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+
3
+ import * as React from "react";
4
+ import { ScrollArea } from "@contractspec/lib.ui-kit-web/ui/scroll-area";
5
+ import { cn } from "@contractspec/lib.ui-kit-web/ui/utils";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ //#region src/presentation/components/ChatContainer.tsx
9
+ /**
10
+ * Container component for chat messages with scrolling
11
+ */
12
+ function ChatContainer({ children, className, showScrollButton = true }) {
13
+ const scrollRef = React.useRef(null);
14
+ const [showScrollDown, setShowScrollDown] = React.useState(false);
15
+ React.useEffect(() => {
16
+ const container = scrollRef.current;
17
+ if (!container) return;
18
+ if (container.scrollHeight - container.scrollTop <= container.clientHeight + 100) container.scrollTop = container.scrollHeight;
19
+ }, [children]);
20
+ const handleScroll = React.useCallback((event) => {
21
+ const container = event.currentTarget;
22
+ setShowScrollDown(!(container.scrollHeight - container.scrollTop <= container.clientHeight + 100));
23
+ }, []);
24
+ const scrollToBottom = React.useCallback(() => {
25
+ const container = scrollRef.current;
26
+ if (container) container.scrollTo({
27
+ top: container.scrollHeight,
28
+ behavior: "smooth"
29
+ });
30
+ }, []);
31
+ return /* @__PURE__ */ jsxs("div", {
32
+ className: cn("relative flex flex-1 flex-col", className),
33
+ children: [/* @__PURE__ */ jsx(ScrollArea, {
34
+ ref: scrollRef,
35
+ className: "flex-1",
36
+ onScroll: handleScroll,
37
+ children: /* @__PURE__ */ jsx("div", {
38
+ className: "flex flex-col gap-4 p-4",
39
+ children
40
+ })
41
+ }), showScrollButton && showScrollDown && /* @__PURE__ */ jsxs("button", {
42
+ onClick: scrollToBottom,
43
+ className: cn("absolute bottom-4 left-1/2 -translate-x-1/2", "bg-primary text-primary-foreground", "rounded-full px-3 py-1.5 text-sm font-medium shadow-lg", "hover:bg-primary/90 transition-colors", "flex items-center gap-1.5"),
44
+ "aria-label": "Scroll to bottom",
45
+ children: [/* @__PURE__ */ jsx("svg", {
46
+ xmlns: "http://www.w3.org/2000/svg",
47
+ width: "16",
48
+ height: "16",
49
+ viewBox: "0 0 24 24",
50
+ fill: "none",
51
+ stroke: "currentColor",
52
+ strokeWidth: "2",
53
+ strokeLinecap: "round",
54
+ strokeLinejoin: "round",
55
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
56
+ }), "New messages"]
57
+ })]
58
+ });
59
+ }
60
+
61
+ //#endregion
62
+ export { ChatContainer };
63
+ //# sourceMappingURL=ChatContainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatContainer.js","names":[],"sources":["../../../src/presentation/components/ChatContainer.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { ScrollArea } from '@contractspec/lib.ui-kit-web/ui/scroll-area';\nimport { cn } from '@contractspec/lib.ui-kit-web/ui/utils';\n\nexport interface ChatContainerProps {\n children: React.ReactNode;\n className?: string;\n /** Show scroll-to-bottom button when scrolled up */\n showScrollButton?: boolean;\n}\n\n/**\n * Container component for chat messages with scrolling\n */\nexport function ChatContainer({\n children,\n className,\n showScrollButton = true,\n}: ChatContainerProps) {\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const [showScrollDown, setShowScrollDown] = React.useState(false);\n\n // Auto-scroll to bottom when children change\n React.useEffect(() => {\n const container = scrollRef.current;\n if (!container) return;\n\n // Check if user has scrolled up\n const isAtBottom =\n container.scrollHeight - container.scrollTop <=\n container.clientHeight + 100;\n\n if (isAtBottom) {\n container.scrollTop = container.scrollHeight;\n }\n }, [children]);\n\n // Track scroll position for scroll-to-bottom button\n const handleScroll = React.useCallback(\n (event: React.UIEvent<HTMLDivElement>) => {\n const container = event.currentTarget;\n const isAtBottom =\n container.scrollHeight - container.scrollTop <=\n container.clientHeight + 100;\n setShowScrollDown(!isAtBottom);\n },\n []\n );\n\n const scrollToBottom = React.useCallback(() => {\n const container = scrollRef.current;\n if (container) {\n container.scrollTo({\n top: container.scrollHeight,\n behavior: 'smooth',\n });\n }\n }, []);\n\n return (\n <div className={cn('relative flex flex-1 flex-col', className)}>\n <ScrollArea ref={scrollRef} className=\"flex-1\" onScroll={handleScroll}>\n <div className=\"flex flex-col gap-4 p-4\">{children}</div>\n </ScrollArea>\n\n {showScrollButton && showScrollDown && (\n <button\n onClick={scrollToBottom}\n className={cn(\n 'absolute bottom-4 left-1/2 -translate-x-1/2',\n 'bg-primary text-primary-foreground',\n 'rounded-full px-3 py-1.5 text-sm font-medium shadow-lg',\n 'hover:bg-primary/90 transition-colors',\n 'flex items-center gap-1.5'\n )}\n aria-label=\"Scroll to bottom\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n New messages\n </button>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAgBA,SAAgB,cAAc,EAC5B,UACA,WACA,mBAAmB,QACE;CACrB,MAAM,YAAY,MAAM,OAAuB,KAAK;CACpD,MAAM,CAAC,gBAAgB,qBAAqB,MAAM,SAAS,MAAM;AAGjE,OAAM,gBAAgB;EACpB,MAAM,YAAY,UAAU;AAC5B,MAAI,CAAC,UAAW;AAOhB,MAHE,UAAU,eAAe,UAAU,aACnC,UAAU,eAAe,IAGzB,WAAU,YAAY,UAAU;IAEjC,CAAC,SAAS,CAAC;CAGd,MAAM,eAAe,MAAM,aACxB,UAAyC;EACxC,MAAM,YAAY,MAAM;AAIxB,oBAAkB,EAFhB,UAAU,eAAe,UAAU,aACnC,UAAU,eAAe,KACG;IAEhC,EAAE,CACH;CAED,MAAM,iBAAiB,MAAM,kBAAkB;EAC7C,MAAM,YAAY,UAAU;AAC5B,MAAI,UACF,WAAU,SAAS;GACjB,KAAK,UAAU;GACf,UAAU;GACX,CAAC;IAEH,EAAE,CAAC;AAEN,QACE,qBAAC;EAAI,WAAW,GAAG,iCAAiC,UAAU;aAC5D,oBAAC;GAAW,KAAK;GAAW,WAAU;GAAS,UAAU;aACvD,oBAAC;IAAI,WAAU;IAA2B;KAAe;IAC9C,EAEZ,oBAAoB,kBACnB,qBAAC;GACC,SAAS;GACT,WAAW,GACT,+CACA,sCACA,0DACA,yCACA,4BACD;GACD,cAAW;cAEX,oBAAC;IACC,OAAM;IACN,OAAM;IACN,QAAO;IACP,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;cAEf,oBAAC,UAAK,GAAE,iBAAiB;KACrB;IAEC;GAEP"}
@@ -0,0 +1,35 @@
1
+ import { ChatAttachment } from "../../core/message-types.js";
2
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
3
+
4
+ //#region src/presentation/components/ChatInput.d.ts
5
+ interface ChatInputProps {
6
+ /** Called when a message is sent */
7
+ onSend: (content: string, attachments?: ChatAttachment[]) => void;
8
+ /** Whether input is disabled (e.g., during streaming) */
9
+ disabled?: boolean;
10
+ /** Whether currently loading/streaming */
11
+ isLoading?: boolean;
12
+ /** Placeholder text */
13
+ placeholder?: string;
14
+ /** Additional class name */
15
+ className?: string;
16
+ /** Show attachment button */
17
+ showAttachments?: boolean;
18
+ /** Max attachments allowed */
19
+ maxAttachments?: number;
20
+ }
21
+ /**
22
+ * Chat input component with attachment support
23
+ */
24
+ declare function ChatInput({
25
+ onSend,
26
+ disabled,
27
+ isLoading,
28
+ placeholder,
29
+ className,
30
+ showAttachments,
31
+ maxAttachments
32
+ }: ChatInputProps): react_jsx_runtime2.JSX.Element;
33
+ //#endregion
34
+ export { ChatInput };
35
+ //# sourceMappingURL=ChatInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInput.d.ts","names":[],"sources":["../../../src/presentation/components/ChatInput.tsx"],"sourcesContent":[],"mappings":";;;;UASiB,cAAA;;0CAEyB;EAFzB;EAoBD,QAAA,CAAA,EAAA,OAAS;EACvB;EACA,SAAA,CAAA,EAAA,OAAA;EACA;EACA,WAAA,CAAA,EAAA,MAAA;EACA;EACA,SAAA,CAAA,EAAA,MAAA;EACA;EACC,eAAA,CAAA,EAAA,OAAA;EAAc;EAAA,cAAA,CAAA,EAAA,MAAA;;;;;iBARD,SAAA;;;;;;;;GAQb,iBAAc,kBAAA,CAAA,GAAA,CAAA"}
@@ -0,0 +1,149 @@
1
+ 'use client';
2
+
3
+ import * as React from "react";
4
+ import { cn } from "@contractspec/lib.ui-kit-web/ui/utils";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Code, FileText, Loader2, Paperclip, Send, X } from "lucide-react";
7
+ import { Button, Textarea } from "@contractspec/lib.design-system";
8
+
9
+ //#region src/presentation/components/ChatInput.tsx
10
+ /**
11
+ * Chat input component with attachment support
12
+ */
13
+ function ChatInput({ onSend, disabled = false, isLoading = false, placeholder = "Type a message...", className, showAttachments = true, maxAttachments = 5 }) {
14
+ const [content, setContent] = React.useState("");
15
+ const [attachments, setAttachments] = React.useState([]);
16
+ const textareaRef = React.useRef(null);
17
+ const fileInputRef = React.useRef(null);
18
+ const canSend = content.trim().length > 0 || attachments.length > 0;
19
+ const handleSubmit = React.useCallback((e) => {
20
+ e?.preventDefault();
21
+ if (!canSend || disabled || isLoading) return;
22
+ onSend(content.trim(), attachments.length > 0 ? attachments : void 0);
23
+ setContent("");
24
+ setAttachments([]);
25
+ textareaRef.current?.focus();
26
+ }, [
27
+ canSend,
28
+ content,
29
+ attachments,
30
+ disabled,
31
+ isLoading,
32
+ onSend
33
+ ]);
34
+ const handleKeyDown = React.useCallback((e) => {
35
+ if (e.key === "Enter" && !e.shiftKey) {
36
+ e.preventDefault();
37
+ handleSubmit();
38
+ }
39
+ }, [handleSubmit]);
40
+ const handleFileSelect = React.useCallback(async (e) => {
41
+ const files = e.target.files;
42
+ if (!files) return;
43
+ const newAttachments = [];
44
+ for (const file of Array.from(files)) {
45
+ if (attachments.length + newAttachments.length >= maxAttachments) break;
46
+ const content$1 = await file.text();
47
+ const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
48
+ const isCode = [
49
+ "ts",
50
+ "tsx",
51
+ "js",
52
+ "jsx",
53
+ "py",
54
+ "go",
55
+ "rs",
56
+ "java"
57
+ ].includes(extension);
58
+ newAttachments.push({
59
+ id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
60
+ type: isCode ? "code" : "file",
61
+ name: file.name,
62
+ content: content$1,
63
+ mimeType: file.type,
64
+ size: file.size
65
+ });
66
+ }
67
+ setAttachments((prev) => [...prev, ...newAttachments]);
68
+ e.target.value = "";
69
+ }, [attachments.length, maxAttachments]);
70
+ const removeAttachment = React.useCallback((id) => {
71
+ setAttachments((prev) => prev.filter((a) => a.id !== id));
72
+ }, []);
73
+ return /* @__PURE__ */ jsxs("div", {
74
+ className: cn("flex flex-col gap-2", className),
75
+ children: [
76
+ attachments.length > 0 && /* @__PURE__ */ jsx("div", {
77
+ className: "flex flex-wrap gap-2",
78
+ children: attachments.map((attachment) => /* @__PURE__ */ jsxs("div", {
79
+ className: cn("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
80
+ children: [
81
+ attachment.type === "code" ? /* @__PURE__ */ jsx(Code, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(FileText, { className: "h-3.5 w-3.5" }),
82
+ /* @__PURE__ */ jsx("span", {
83
+ className: "max-w-[150px] truncate",
84
+ children: attachment.name
85
+ }),
86
+ /* @__PURE__ */ jsx("button", {
87
+ type: "button",
88
+ onClick: () => removeAttachment(attachment.id),
89
+ className: "hover:text-foreground",
90
+ "aria-label": `Remove ${attachment.name}`,
91
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
92
+ })
93
+ ]
94
+ }, attachment.id))
95
+ }),
96
+ /* @__PURE__ */ jsxs("form", {
97
+ onSubmit: handleSubmit,
98
+ className: "flex items-end gap-2",
99
+ children: [
100
+ showAttachments && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("input", {
101
+ ref: fileInputRef,
102
+ type: "file",
103
+ multiple: true,
104
+ accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
105
+ onChange: handleFileSelect,
106
+ className: "hidden",
107
+ "aria-label": "Attach files"
108
+ }), /* @__PURE__ */ jsx(Button, {
109
+ type: "button",
110
+ variant: "ghost",
111
+ size: "sm",
112
+ onPress: () => fileInputRef.current?.click(),
113
+ disabled: disabled || attachments.length >= maxAttachments,
114
+ "aria-label": "Attach files",
115
+ children: /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" })
116
+ })] }),
117
+ /* @__PURE__ */ jsx("div", {
118
+ className: "relative flex-1",
119
+ children: /* @__PURE__ */ jsx(Textarea, {
120
+ value: content,
121
+ onChange: (e) => setContent(e.target.value),
122
+ onKeyDown: handleKeyDown,
123
+ placeholder,
124
+ disabled,
125
+ className: cn("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
126
+ rows: 1,
127
+ "aria-label": "Chat message"
128
+ })
129
+ }),
130
+ /* @__PURE__ */ jsx(Button, {
131
+ type: "submit",
132
+ disabled: !canSend || disabled || isLoading,
133
+ size: "sm",
134
+ "aria-label": isLoading ? "Sending..." : "Send message",
135
+ children: isLoading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
136
+ })
137
+ ]
138
+ }),
139
+ /* @__PURE__ */ jsx("p", {
140
+ className: "text-muted-foreground text-xs",
141
+ children: "Press Enter to send, Shift+Enter for new line"
142
+ })
143
+ ]
144
+ });
145
+ }
146
+
147
+ //#endregion
148
+ export { ChatInput };
149
+ //# sourceMappingURL=ChatInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatInput.js","names":["content"],"sources":["../../../src/presentation/components/ChatInput.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { cn } from '@contractspec/lib.ui-kit-web/ui/utils';\nimport { Textarea } from '@contractspec/lib.design-system';\nimport { Button } from '@contractspec/lib.design-system';\nimport { Send, Paperclip, X, Loader2, FileText, Code } from 'lucide-react';\nimport type { ChatAttachment } from '../../core/message-types';\n\nexport interface ChatInputProps {\n /** Called when a message is sent */\n onSend: (content: string, attachments?: ChatAttachment[]) => void;\n /** Whether input is disabled (e.g., during streaming) */\n disabled?: boolean;\n /** Whether currently loading/streaming */\n isLoading?: boolean;\n /** Placeholder text */\n placeholder?: string;\n /** Additional class name */\n className?: string;\n /** Show attachment button */\n showAttachments?: boolean;\n /** Max attachments allowed */\n maxAttachments?: number;\n}\n\n/**\n * Chat input component with attachment support\n */\nexport function ChatInput({\n onSend,\n disabled = false,\n isLoading = false,\n placeholder = 'Type a message...',\n className,\n showAttachments = true,\n maxAttachments = 5,\n}: ChatInputProps) {\n const [content, setContent] = React.useState('');\n const [attachments, setAttachments] = React.useState<ChatAttachment[]>([]);\n const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n const fileInputRef = React.useRef<HTMLInputElement>(null);\n\n const canSend = content.trim().length > 0 || attachments.length > 0;\n\n const handleSubmit = React.useCallback(\n (e?: React.FormEvent) => {\n e?.preventDefault();\n if (!canSend || disabled || isLoading) return;\n\n onSend(content.trim(), attachments.length > 0 ? attachments : undefined);\n setContent('');\n setAttachments([]);\n\n // Focus back on textarea\n textareaRef.current?.focus();\n },\n [canSend, content, attachments, disabled, isLoading, onSend]\n );\n\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent) => {\n // Submit on Enter (without Shift)\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit]\n );\n\n const handleFileSelect = React.useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (!files) return;\n\n const newAttachments: ChatAttachment[] = [];\n\n for (const file of Array.from(files)) {\n if (attachments.length + newAttachments.length >= maxAttachments) break;\n\n const content = await file.text();\n const extension = file.name.split('.').pop()?.toLowerCase() ?? '';\n const isCode = [\n 'ts',\n 'tsx',\n 'js',\n 'jsx',\n 'py',\n 'go',\n 'rs',\n 'java',\n ].includes(extension);\n\n newAttachments.push({\n id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,\n type: isCode ? 'code' : 'file',\n name: file.name,\n content,\n mimeType: file.type,\n size: file.size,\n });\n }\n\n setAttachments((prev) => [...prev, ...newAttachments]);\n\n // Reset file input\n e.target.value = '';\n },\n [attachments.length, maxAttachments]\n );\n\n const removeAttachment = React.useCallback((id: string) => {\n setAttachments((prev) => prev.filter((a) => a.id !== id));\n }, []);\n\n return (\n <div className={cn('flex flex-col gap-2', className)}>\n {/* Attachments preview */}\n {attachments.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {attachments.map((attachment) => (\n <div\n key={attachment.id}\n className={cn(\n 'flex items-center gap-1.5 rounded-md px-2 py-1',\n 'bg-muted text-muted-foreground text-sm'\n )}\n >\n {attachment.type === 'code' ? (\n <Code className=\"h-3.5 w-3.5\" />\n ) : (\n <FileText className=\"h-3.5 w-3.5\" />\n )}\n <span className=\"max-w-[150px] truncate\">{attachment.name}</span>\n <button\n type=\"button\"\n onClick={() => removeAttachment(attachment.id)}\n className=\"hover:text-foreground\"\n aria-label={`Remove ${attachment.name}`}\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input form */}\n <form onSubmit={handleSubmit} className=\"flex items-end gap-2\">\n {/* Attachment button */}\n {showAttachments && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml\"\n onChange={handleFileSelect}\n className=\"hidden\"\n aria-label=\"Attach files\"\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onPress={() => fileInputRef.current?.click()}\n disabled={disabled || attachments.length >= maxAttachments}\n aria-label=\"Attach files\"\n >\n <Paperclip className=\"h-4 w-4\" />\n </Button>\n </>\n )}\n\n {/* Text input */}\n <div className=\"relative flex-1\">\n <Textarea\n value={content}\n onChange={(e) => setContent(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n className={cn(\n 'max-h-[200px] min-h-[44px] resize-none pr-12',\n 'focus-visible:ring-1'\n )}\n rows={1}\n aria-label=\"Chat message\"\n />\n </div>\n\n {/* Send button */}\n <Button\n type=\"submit\"\n disabled={!canSend || disabled || isLoading}\n size=\"sm\"\n aria-label={isLoading ? 'Sending...' : 'Send message'}\n >\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Send className=\"h-4 w-4\" />\n )}\n </Button>\n </form>\n\n {/* Helper text */}\n <p className=\"text-muted-foreground text-xs\">\n Press Enter to send, Shift+Enter for new line\n </p>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { ChatMessage as ChatMessage$1 } from "../../core/message-types.js";
2
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
3
+
4
+ //#region src/presentation/components/ChatMessage.d.ts
5
+ interface ChatMessageProps {
6
+ message: ChatMessage$1;
7
+ className?: string;
8
+ /** Show copy button */
9
+ showCopy?: boolean;
10
+ /** Show avatar */
11
+ showAvatar?: boolean;
12
+ }
13
+ /**
14
+ * Chat message component
15
+ */
16
+ declare function ChatMessage({
17
+ message,
18
+ className,
19
+ showCopy,
20
+ showAvatar
21
+ }: ChatMessageProps): react_jsx_runtime1.JSX.Element;
22
+ //#endregion
23
+ export { ChatMessage };
24
+ //# sourceMappingURL=ChatMessage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatMessage.d.ts","names":[],"sources":["../../../src/presentation/components/ChatMessage.tsx"],"sourcesContent":[],"mappings":";;;;UAWiB,gBAAA;WACN;;EADM;EA+ED,QAAA,CAAA,EAAA,OAAW;EACzB;EACA,UAAA,CAAA,EAAA,OAAA;;;;;AAGiB,iBALH,WAAA,CAKG;EAAA,OAAA;EAAA,SAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EAAhB,gBAAgB,CAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,OAAA"}