@aws/nx-plugin 0.104.1 → 0.106.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 (51) hide show
  1. package/LICENSE-THIRD-PARTY +406 -436
  2. package/package.json +12 -12
  3. package/src/infra/app/__snapshots__/generator.spec.ts.snap +21 -21
  4. package/src/mcp-server/tools/create-workspace-command.js +9 -5
  5. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  6. package/src/open-api/ts-client/__snapshots__/generator.composite-types.spec.ts.snap +438 -0
  7. package/src/open-api/ts-client/files/client.gen.ts.template +20 -0
  8. package/src/open-api/utils/codegen-data/languages.js +7 -3
  9. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  10. package/src/open-api/utils/normalise.js +17 -0
  11. package/src/open-api/utils/normalise.js.map +1 -1
  12. package/src/preset/__snapshots__/generator.spec.ts.snap +4 -4
  13. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +4 -4
  14. package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +9 -9
  15. package/src/py/strands-agent/react-connection/__snapshots__/generator.spec.ts.snap +129 -4
  16. package/src/py/strands-agent/react-connection/serve-local.js +1 -1
  17. package/src/py/strands-agent/react-connection/serve-local.js.map +1 -1
  18. package/src/trpc/react/__snapshots__/generator.spec.ts.snap +4 -1
  19. package/src/ts/lib/__snapshots__/generator.spec.ts.snap +3 -1
  20. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +3 -3
  21. package/src/ts/mcp-server/generator.js +1 -0
  22. package/src/ts/mcp-server/generator.js.map +1 -1
  23. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/CloudscapeAssistantMessage.tsx.template +71 -0
  24. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/CloudscapeChatInput.tsx.template +65 -0
  25. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/CloudscapeCursor.tsx.template +10 -0
  26. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/CloudscapeUserMessage.tsx.template +42 -0
  27. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/copilot.css.template +128 -0
  28. package/src/ts/react-website/agui/files/cloudscape/src/components/copilot/index.tsx.template +46 -0
  29. package/src/ts/react-website/agui/files/{src → common/src}/components/AguiProvider.tsx.template +10 -2
  30. package/src/ts/react-website/agui/files/default/src/components/copilot/index.tsx.template +5 -0
  31. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/ShadcnAssistantMessage.tsx.template +71 -0
  32. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/ShadcnChatInput.tsx.template +117 -0
  33. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/ShadcnCursor.tsx.template +23 -0
  34. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/ShadcnUserMessage.tsx.template +49 -0
  35. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/copilot.css.template +116 -0
  36. package/src/ts/react-website/agui/files/shadcn/src/components/copilot/index.tsx.template +46 -0
  37. package/src/ts/react-website/agui/generator.d.ts +3 -0
  38. package/src/ts/react-website/agui/generator.js +28 -1
  39. package/src/ts/react-website/agui/generator.js.map +1 -1
  40. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +42 -38
  41. package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +1 -1
  42. package/src/ts/strands-agent/generator.js +1 -0
  43. package/src/ts/strands-agent/generator.js.map +1 -1
  44. package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +1 -1
  45. package/src/utils/files/common/shadcn/src/components/ui/avatar.tsx.template +53 -0
  46. package/src/utils/files/common/shadcn/src/components/ui/textarea.tsx.template +18 -0
  47. package/src/utils/files/website/hooks/sigv4/useSigV4.tsx.template +4 -1
  48. package/src/utils/versions.d.ts +59 -58
  49. package/src/utils/versions.js +58 -57
  50. package/src/utils/versions.js.map +1 -1
  51. /package/src/ts/react-website/agui/files/{src → common/src}/hooks/useAgui__agentNameClassName__.tsx.template +0 -0
@@ -0,0 +1,65 @@
1
+ import { useState } from 'react';
2
+ import {
3
+ CopilotChatInput,
4
+ type CopilotChatInputProps,
5
+ } from '@copilotkit/react-core/v2';
6
+ import PromptInput from '@cloudscape-design/components/prompt-input';
7
+
8
+ const CloudscapeChatInputImpl = ({
9
+ value,
10
+ onChange,
11
+ onSubmitMessage,
12
+ onStop,
13
+ isRunning,
14
+ autoFocus,
15
+ mode,
16
+ }: CopilotChatInputProps) => {
17
+ const [internalValue, setInternalValue] = useState('');
18
+ const currentValue = value ?? internalValue;
19
+ const disabled = mode === 'processing';
20
+
21
+ const handleChange = (next: string) => {
22
+ onChange?.(next);
23
+ if (value === undefined) {
24
+ setInternalValue(next);
25
+ }
26
+ };
27
+
28
+ const handleAction = () => {
29
+ if (isRunning) {
30
+ onStop?.();
31
+ return;
32
+ }
33
+ const trimmed = currentValue.trim();
34
+ if (!trimmed) return;
35
+ onSubmitMessage?.(trimmed);
36
+ if (value === undefined) {
37
+ setInternalValue('');
38
+ }
39
+ };
40
+
41
+ return (
42
+ <div className="copilotkit-cloudscape-input">
43
+ <PromptInput
44
+ value={currentValue}
45
+ onChange={({ detail }) => handleChange(detail.value)}
46
+ onAction={handleAction}
47
+ actionButtonAriaLabel={isRunning ? 'Stop' : 'Send message'}
48
+ actionButtonIconName={isRunning ? 'close' : 'send'}
49
+ ariaLabel="Chat input"
50
+ placeholder="Ask me anything..."
51
+ autoFocus={autoFocus}
52
+ disabled={disabled}
53
+ minRows={2}
54
+ maxRows={6}
55
+ />
56
+ </div>
57
+ );
58
+ };
59
+
60
+ export const CloudscapeChatInput = Object.assign(
61
+ CloudscapeChatInputImpl,
62
+ CopilotChatInput,
63
+ );
64
+
65
+ export default CloudscapeChatInput;
@@ -0,0 +1,10 @@
1
+ import { CopilotChatMessageView } from '@copilotkit/react-core/v2';
2
+ import LoadingBar from '@cloudscape-design/chat-components/loading-bar';
3
+
4
+ export const CloudscapeCursor: typeof CopilotChatMessageView.Cursor = () => (
5
+ <div aria-label="Generating response" role="status">
6
+ <LoadingBar variant="gen-ai-masked" />
7
+ </div>
8
+ );
9
+
10
+ export default CloudscapeCursor;
@@ -0,0 +1,42 @@
1
+ import { CopilotChatUserMessage } from '@copilotkit/react-core/v2';
2
+ import type { CopilotChatUserMessageProps } from '@copilotkit/react-core/v2';
3
+ import ChatBubble from '@cloudscape-design/chat-components/chat-bubble';
4
+ import Avatar from '@cloudscape-design/chat-components/avatar';
5
+
6
+ const flattenContent = (
7
+ content: CopilotChatUserMessageProps['message']['content'],
8
+ ): string => {
9
+ if (!content) return '';
10
+ if (typeof content === 'string') return content;
11
+ return content
12
+ .filter(
13
+ (part): part is { type: 'text'; text: string } =>
14
+ !!part &&
15
+ typeof part === 'object' &&
16
+ 'type' in part &&
17
+ (part as { type?: unknown }).type === 'text' &&
18
+ 'text' in part &&
19
+ typeof (part as { text?: unknown }).text === 'string',
20
+ )
21
+ .map((part) => part.text)
22
+ .join('\n');
23
+ };
24
+
25
+ const CloudscapeUserMessageImpl = ({ message }: CopilotChatUserMessageProps) => (
26
+ <ChatBubble
27
+ ariaLabel="Your message"
28
+ type="outgoing"
29
+ avatar={<Avatar ariaLabel="You" tooltipText="You" initials="Me" />}
30
+ >
31
+ <div style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
32
+ {flattenContent(message.content)}
33
+ </div>
34
+ </ChatBubble>
35
+ );
36
+
37
+ export const CloudscapeUserMessage = Object.assign(
38
+ CloudscapeUserMessageImpl,
39
+ CopilotChatUserMessage,
40
+ );
41
+
42
+ export default CloudscapeUserMessage;
@@ -0,0 +1,128 @@
1
+ /* Explicit styles for the Streamdown markdown output (CopilotKit emits
2
+ * unprefixed Tailwind utilities that the website's Tailwind build can't see). */
3
+
4
+ .copilotkit-cloudscape-messages {
5
+ display: flex;
6
+ flex-direction: column;
7
+ gap: 12px;
8
+ padding: 16px;
9
+ }
10
+
11
+ .copilotkit-cloudscape-input {
12
+ padding: 12px 16px 16px;
13
+ background: var(--awsui-color-background-container-content, #ffffff);
14
+ border-top: 1px solid var(--awsui-color-border-divider-default, #dfe3e6);
15
+ }
16
+
17
+ .copilotkit-cloudscape-messages [data-streamdown='strong'] {
18
+ font-weight: 600;
19
+ }
20
+ .copilotkit-cloudscape-messages [data-streamdown='emphasis'] {
21
+ font-style: italic;
22
+ }
23
+ .copilotkit-cloudscape-messages [data-streamdown='paragraph'],
24
+ .copilotkit-cloudscape-messages > p {
25
+ margin: 0.5em 0;
26
+ }
27
+
28
+ .copilotkit-cloudscape-messages [data-streamdown='heading-1'],
29
+ .copilotkit-cloudscape-messages [data-streamdown='heading-2'],
30
+ .copilotkit-cloudscape-messages [data-streamdown='heading-3'],
31
+ .copilotkit-cloudscape-messages [data-streamdown='heading-4'],
32
+ .copilotkit-cloudscape-messages [data-streamdown='heading-5'],
33
+ .copilotkit-cloudscape-messages [data-streamdown='heading-6'] {
34
+ font-weight: 600;
35
+ line-height: 1.3;
36
+ margin: 0.8em 0 0.4em;
37
+ }
38
+ .copilotkit-cloudscape-messages [data-streamdown='heading-1'] {
39
+ font-size: 1.5em;
40
+ }
41
+ .copilotkit-cloudscape-messages [data-streamdown='heading-2'] {
42
+ font-size: 1.3em;
43
+ }
44
+ .copilotkit-cloudscape-messages [data-streamdown='heading-3'] {
45
+ font-size: 1.15em;
46
+ }
47
+
48
+ .copilotkit-cloudscape-messages [data-streamdown='unordered-list'] {
49
+ list-style: disc;
50
+ padding-inline-start: 1.5em;
51
+ margin: 0.5em 0;
52
+ }
53
+ .copilotkit-cloudscape-messages [data-streamdown='ordered-list'] {
54
+ list-style: decimal;
55
+ padding-inline-start: 1.5em;
56
+ margin: 0.5em 0;
57
+ }
58
+ .copilotkit-cloudscape-messages [data-streamdown='list-item'] {
59
+ margin: 0.25em 0;
60
+ }
61
+
62
+ .copilotkit-cloudscape-messages [data-streamdown='inline-code'],
63
+ .copilotkit-cloudscape-messages code {
64
+ background: rgba(0, 0, 0, 0.06);
65
+ border-radius: 4px;
66
+ padding: 0.1em 0.35em;
67
+ font-family: var(--font-family-monospace, ui-monospace, monospace);
68
+ font-size: 0.9em;
69
+ }
70
+
71
+ .copilotkit-cloudscape-messages [data-streamdown='code-block'] {
72
+ background: rgba(0, 0, 0, 0.04);
73
+ border: 1px solid rgba(0, 0, 0, 0.08);
74
+ border-radius: 8px;
75
+ margin: 0.75em 0;
76
+ max-width: 100%;
77
+ overflow: hidden;
78
+ }
79
+ .copilotkit-cloudscape-messages [data-streamdown='code-block'] pre {
80
+ margin: 0;
81
+ padding: 12px 16px;
82
+ overflow-x: auto;
83
+ white-space: pre;
84
+ font-family: var(--font-family-monospace, ui-monospace, monospace);
85
+ font-size: 0.85em;
86
+ line-height: 1.5;
87
+ }
88
+ .copilotkit-cloudscape-messages [data-streamdown='code-block'] code {
89
+ background: transparent;
90
+ padding: 0;
91
+ white-space: pre;
92
+ }
93
+
94
+ .copilotkit-cloudscape-messages [data-streamdown='blockquote'] {
95
+ border-inline-start: 3px solid rgba(0, 0, 0, 0.15);
96
+ margin: 0.5em 0;
97
+ padding-inline-start: 1em;
98
+ color: rgba(0, 0, 0, 0.65);
99
+ }
100
+
101
+ .copilotkit-cloudscape-messages table[data-streamdown='table-wrapper'] {
102
+ border-collapse: collapse;
103
+ margin: 0.75em 0;
104
+ max-width: 100%;
105
+ overflow-x: auto;
106
+ width: auto;
107
+ }
108
+ .copilotkit-cloudscape-messages table[data-streamdown='table-wrapper'] th,
109
+ .copilotkit-cloudscape-messages table[data-streamdown='table-wrapper'] td {
110
+ border: 1px solid rgba(0, 0, 0, 0.12);
111
+ padding: 6px 10px;
112
+ text-align: start;
113
+ }
114
+ .copilotkit-cloudscape-messages table[data-streamdown='table-wrapper'] th {
115
+ background: rgba(0, 0, 0, 0.04);
116
+ font-weight: 600;
117
+ }
118
+
119
+ .copilotkit-cloudscape-messages [data-streamdown='link'] {
120
+ color: var(--awsui-color-text-link-default, #0972d3);
121
+ text-decoration: underline;
122
+ }
123
+
124
+ .copilotkit-cloudscape-messages [data-streamdown='horizontal-rule'] {
125
+ border: 0;
126
+ border-top: 1px solid rgba(0, 0, 0, 0.12);
127
+ margin: 1em 0;
128
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ CopilotChat as CopilotChatBase,
3
+ CopilotSidebar as CopilotSidebarBase,
4
+ CopilotPopup as CopilotPopupBase,
5
+ CopilotChatView,
6
+ type CopilotChatProps,
7
+ type CopilotSidebarProps,
8
+ type CopilotPopupProps,
9
+ } from '@copilotkit/react-core/v2';
10
+ import { CloudscapeAssistantMessage } from './CloudscapeAssistantMessage';
11
+ import { CloudscapeUserMessage } from './CloudscapeUserMessage';
12
+ import { CloudscapeChatInput } from './CloudscapeChatInput';
13
+ import { CloudscapeCursor } from './CloudscapeCursor';
14
+ import './copilot.css';
15
+
16
+ const NoopFeather: typeof CopilotChatView.Feather = () => null;
17
+
18
+ export const cloudscapeCopilotTheme = {
19
+ messageView: {
20
+ assistantMessage: CloudscapeAssistantMessage,
21
+ userMessage: CloudscapeUserMessage,
22
+ cursor: CloudscapeCursor,
23
+ className: 'copilotkit-cloudscape-messages',
24
+ },
25
+ input: CloudscapeChatInput,
26
+ scrollView: { feather: NoopFeather },
27
+ } as const;
28
+
29
+ export const CopilotChat = (props: CopilotChatProps) => (
30
+ <CopilotChatBase {...cloudscapeCopilotTheme} {...props} />
31
+ );
32
+
33
+ export const CopilotSidebar = (props: CopilotSidebarProps) => (
34
+ <CopilotSidebarBase {...cloudscapeCopilotTheme} {...props} />
35
+ );
36
+
37
+ export const CopilotPopup = (props: CopilotPopupProps) => (
38
+ <CopilotPopupBase {...cloudscapeCopilotTheme} {...props} />
39
+ );
40
+
41
+ export {
42
+ CloudscapeAssistantMessage,
43
+ CloudscapeUserMessage,
44
+ CloudscapeChatInput,
45
+ CloudscapeCursor,
46
+ };
@@ -1,8 +1,13 @@
1
- import { CopilotKitProvider } from '@copilotkit/react-core/v2';
1
+ import {
2
+ CopilotKitProvider,
3
+ WildcardToolCallRender,
4
+ } from '@copilotkit/react-core/v2';
2
5
  import '@copilotkit/react-core/v2/styles.css';
3
6
  import { type FC, type PropsWithChildren, useMemo } from 'react';
4
7
  import type { AbstractAgent } from '@ag-ui/client';
5
8
 
9
+ const renderToolCalls = [WildcardToolCallRender];
10
+
6
11
  export const AguiProvider: FC<PropsWithChildren> = ({ children }) => {
7
12
  const selfManagedAgents = useMemo<Record<string, AbstractAgent>>(
8
13
  () => ({}),
@@ -10,7 +15,10 @@ export const AguiProvider: FC<PropsWithChildren> = ({ children }) => {
10
15
  );
11
16
 
12
17
  return (
13
- <CopilotKitProvider selfManagedAgents={selfManagedAgents}>
18
+ <CopilotKitProvider
19
+ selfManagedAgents={selfManagedAgents}
20
+ renderToolCalls={renderToolCalls}
21
+ >
14
22
  {children}
15
23
  </CopilotKitProvider>
16
24
  );
@@ -0,0 +1,5 @@
1
+ export {
2
+ CopilotChat,
3
+ CopilotSidebar,
4
+ CopilotPopup,
5
+ } from '@copilotkit/react-core/v2';
@@ -0,0 +1,71 @@
1
+ import {
2
+ CopilotChatAssistantMessage,
3
+ CopilotChatToolCallsView,
4
+ } from '@copilotkit/react-core/v2';
5
+ import type { CopilotChatAssistantMessageProps } from '@copilotkit/react-core/v2';
6
+ import { Copy, Sparkles } from 'lucide-react';
7
+ import {
8
+ Avatar,
9
+ AvatarFallback,
10
+ } from '<%= scopeAlias %>common-shadcn/components/ui/avatar';
11
+ import { Button } from '<%= scopeAlias %>common-shadcn/components/ui/button';
12
+ import { cn } from '<%= scopeAlias %>common-shadcn/lib/utils';
13
+
14
+ const ShadcnAssistantMessageImpl = ({
15
+ message,
16
+ messages,
17
+ }: CopilotChatAssistantMessageProps) => {
18
+ const hasContent = !!(message.content && message.content.trim().length > 0);
19
+
20
+ const handleCopy = () => {
21
+ if (typeof message.content === 'string') {
22
+ void navigator.clipboard?.writeText(message.content);
23
+ }
24
+ };
25
+
26
+ return (
27
+ <div className="group flex items-start gap-3 py-2">
28
+ <Avatar className="bg-primary/10 border-primary/20 mt-0.5 border">
29
+ <AvatarFallback className="bg-transparent">
30
+ <Sparkles className="text-primary size-4" />
31
+ </AvatarFallback>
32
+ </Avatar>
33
+ <div className="flex min-w-0 flex-1 flex-col gap-1.5">
34
+ <div
35
+ className={cn(
36
+ 'bg-muted/60 text-foreground rounded-2xl rounded-tl-sm px-4 py-3',
37
+ 'copilotkit-shadcn-markdown',
38
+ )}
39
+ >
40
+ <CopilotChatAssistantMessage.MarkdownRenderer
41
+ content={message.content ?? ''}
42
+ />
43
+ </div>
44
+ {message.toolCalls && message.toolCalls.length > 0 && (
45
+ <CopilotChatToolCallsView message={message} messages={messages} />
46
+ )}
47
+ {hasContent && (
48
+ <div className="text-muted-foreground flex items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
49
+ <Button
50
+ type="button"
51
+ variant="ghost"
52
+ size="icon"
53
+ className="size-7"
54
+ aria-label="Copy message"
55
+ onClick={handleCopy}
56
+ >
57
+ <Copy className="size-3.5" />
58
+ </Button>
59
+ </div>
60
+ )}
61
+ </div>
62
+ </div>
63
+ );
64
+ };
65
+
66
+ export const ShadcnAssistantMessage = Object.assign(
67
+ ShadcnAssistantMessageImpl,
68
+ CopilotChatAssistantMessage,
69
+ );
70
+
71
+ export default ShadcnAssistantMessage;
@@ -0,0 +1,117 @@
1
+ import {
2
+ useRef,
3
+ useState,
4
+ useEffect,
5
+ type KeyboardEvent,
6
+ } from 'react';
7
+ import {
8
+ CopilotChatInput,
9
+ type CopilotChatInputProps,
10
+ } from '@copilotkit/react-core/v2';
11
+ import { ArrowUp, Square } from 'lucide-react';
12
+ import { Button } from '<%= scopeAlias %>common-shadcn/components/ui/button';
13
+ import { Textarea } from '<%= scopeAlias %>common-shadcn/components/ui/textarea';
14
+ import { cn } from '<%= scopeAlias %>common-shadcn/lib/utils';
15
+
16
+ const MAX_ROWS = 8;
17
+
18
+ const ShadcnChatInputImpl = ({
19
+ value,
20
+ onChange,
21
+ onSubmitMessage,
22
+ onStop,
23
+ isRunning,
24
+ autoFocus,
25
+ mode,
26
+ }: CopilotChatInputProps) => {
27
+ const [internalValue, setInternalValue] = useState('');
28
+ const textareaRef = useRef<HTMLTextAreaElement | null>(null);
29
+ const currentValue = value ?? internalValue;
30
+ const disabled = mode === 'processing';
31
+
32
+ useEffect(() => {
33
+ const el = textareaRef.current;
34
+ if (!el) return;
35
+ el.style.height = 'auto';
36
+ const lineHeight = parseFloat(getComputedStyle(el).lineHeight || '20');
37
+ el.style.height = `${Math.min(el.scrollHeight, lineHeight * MAX_ROWS)}px`;
38
+ }, [currentValue]);
39
+
40
+ const setValue = (next: string) => {
41
+ onChange?.(next);
42
+ if (value === undefined) setInternalValue(next);
43
+ };
44
+
45
+ const submit = () => {
46
+ const trimmed = currentValue.trim();
47
+ if (!trimmed) return;
48
+ onSubmitMessage?.(trimmed);
49
+ if (value === undefined) setInternalValue('');
50
+ };
51
+
52
+ const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
53
+ if (event.key === 'Enter' && !event.shiftKey) {
54
+ event.preventDefault();
55
+ submit();
56
+ }
57
+ };
58
+
59
+ return (
60
+ <div className="px-4 pb-4 pt-2">
61
+ <div
62
+ className={cn(
63
+ 'bg-background border-input focus-within:border-ring focus-within:ring-ring/30',
64
+ 'flex items-end gap-2 rounded-3xl border px-3 py-2',
65
+ 'shadow-[0_2px_10px_rgba(0,0,0,0.06)]',
66
+ 'focus-within:ring-4 transition-[border,box-shadow]',
67
+ )}
68
+ >
69
+ <Textarea
70
+ ref={textareaRef}
71
+ value={currentValue}
72
+ onChange={(event) => setValue(event.target.value)}
73
+ onKeyDown={handleKeyDown}
74
+ disabled={disabled}
75
+ autoFocus={autoFocus}
76
+ placeholder="Ask me anything..."
77
+ rows={1}
78
+ className={cn(
79
+ 'flex-1 resize-none border-0 bg-transparent p-2 shadow-none',
80
+ 'focus-visible:ring-0 focus-visible:ring-offset-0',
81
+ 'min-h-0 max-h-48 leading-6',
82
+ )}
83
+ />
84
+ {isRunning ? (
85
+ <Button
86
+ type="button"
87
+ variant="secondary"
88
+ size="icon"
89
+ onClick={onStop}
90
+ aria-label="Stop"
91
+ className="size-9 rounded-full"
92
+ >
93
+ <Square className="size-4" fill="currentColor" />
94
+ </Button>
95
+ ) : (
96
+ <Button
97
+ type="button"
98
+ size="icon"
99
+ onClick={submit}
100
+ disabled={disabled || !currentValue.trim()}
101
+ aria-label="Send message"
102
+ className="size-9 rounded-full"
103
+ >
104
+ <ArrowUp className="size-4" />
105
+ </Button>
106
+ )}
107
+ </div>
108
+ </div>
109
+ );
110
+ };
111
+
112
+ export const ShadcnChatInput = Object.assign(
113
+ ShadcnChatInputImpl,
114
+ CopilotChatInput,
115
+ );
116
+
117
+ export default ShadcnChatInput;
@@ -0,0 +1,23 @@
1
+ import { CopilotChatMessageView } from '@copilotkit/react-core/v2';
2
+ import { cn } from '<%= scopeAlias %>common-shadcn/lib/utils';
3
+
4
+ export const ShadcnCursor: typeof CopilotChatMessageView.Cursor = () => (
5
+ <div
6
+ role="status"
7
+ aria-label="Generating response"
8
+ className="flex items-center gap-1 px-4 py-2"
9
+ >
10
+ {[0, 1, 2].map((i) => (
11
+ <span
12
+ key={i}
13
+ className={cn(
14
+ 'bg-muted-foreground/60 size-1.5 rounded-full',
15
+ 'animate-bounce',
16
+ )}
17
+ style={{ animationDelay: `${i * 120}ms` }}
18
+ />
19
+ ))}
20
+ </div>
21
+ );
22
+
23
+ export default ShadcnCursor;
@@ -0,0 +1,49 @@
1
+ import { CopilotChatUserMessage } from '@copilotkit/react-core/v2';
2
+ import type {
3
+ CopilotChatUserMessageProps,
4
+ UserMessage,
5
+ } from '@copilotkit/react-core/v2';
6
+ import { User } from 'lucide-react';
7
+ import {
8
+ Avatar,
9
+ AvatarFallback,
10
+ } from '<%= scopeAlias %>common-shadcn/components/ui/avatar';
11
+
12
+ const flattenContent = (content: UserMessage['content']): string => {
13
+ if (!content) return '';
14
+ if (typeof content === 'string') return content;
15
+ return content
16
+ .filter(
17
+ (part): part is { type: 'text'; text: string } =>
18
+ !!part &&
19
+ typeof part === 'object' &&
20
+ 'type' in part &&
21
+ (part as { type?: unknown }).type === 'text' &&
22
+ 'text' in part &&
23
+ typeof (part as { text?: unknown }).text === 'string',
24
+ )
25
+ .map((part) => part.text)
26
+ .join('\n');
27
+ };
28
+
29
+ const ShadcnUserMessageImpl = ({ message }: CopilotChatUserMessageProps) => (
30
+ <div className="flex items-start justify-end gap-3 py-2">
31
+ <div className="flex min-w-0 max-w-[85%] flex-col items-end gap-1.5">
32
+ <div className="bg-primary text-primary-foreground rounded-2xl rounded-tr-sm px-4 py-2.5 whitespace-pre-wrap break-words">
33
+ {flattenContent(message.content)}
34
+ </div>
35
+ </div>
36
+ <Avatar className="bg-secondary mt-0.5">
37
+ <AvatarFallback className="bg-transparent">
38
+ <User className="text-secondary-foreground size-4" />
39
+ </AvatarFallback>
40
+ </Avatar>
41
+ </div>
42
+ );
43
+
44
+ export const ShadcnUserMessage = Object.assign(
45
+ ShadcnUserMessageImpl,
46
+ CopilotChatUserMessage,
47
+ );
48
+
49
+ export default ShadcnUserMessage;