@gravity-ui/aikit 2.24.1-beta.883c85d04b93d8521b2733baa3e86da20249b4e7.0 → 2.25.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 (118) hide show
  1. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.d.ts +1 -0
  2. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.js +2 -8
  3. package/build/cjs/components/molecules/PromptInputBody/PromptInputBody.js.map +1 -1
  4. package/build/cjs/components/organisms/PromptInput/PromptInput.css +37 -0
  5. package/build/cjs/components/organisms/PromptInput/PromptInput.d.ts +13 -2
  6. package/build/cjs/components/organisms/PromptInput/PromptInput.js +64 -3
  7. package/build/cjs/components/organisms/PromptInput/PromptInput.js.map +1 -1
  8. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.d.ts +9 -0
  9. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.js +39 -0
  10. package/build/cjs/components/organisms/PromptInput/PromptInputCommands.js.map +1 -0
  11. package/build/cjs/components/organisms/PromptInput/PromptInputFull.d.ts +2 -0
  12. package/build/cjs/components/organisms/PromptInput/PromptInputFull.js +4 -4
  13. package/build/cjs/components/organisms/PromptInput/PromptInputFull.js.map +1 -1
  14. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.d.ts +2 -0
  15. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.js +3 -3
  16. package/build/cjs/components/organisms/PromptInput/PromptInputSimple.js.map +1 -1
  17. package/build/cjs/components/organisms/PromptInput/commandUtils.d.ts +13 -0
  18. package/build/cjs/components/organisms/PromptInput/commandUtils.js +27 -0
  19. package/build/cjs/components/organisms/PromptInput/commandUtils.js.map +1 -0
  20. package/build/cjs/components/organisms/PromptInput/i18n/en.json +8 -0
  21. package/build/cjs/components/organisms/PromptInput/i18n/index.d.ts +13 -0
  22. package/build/cjs/components/organisms/PromptInput/i18n/index.js +10 -0
  23. package/build/cjs/components/organisms/PromptInput/i18n/index.js.map +1 -0
  24. package/build/cjs/components/organisms/PromptInput/i18n/ru.json +8 -0
  25. package/build/cjs/components/organisms/PromptInput/index.d.ts +2 -1
  26. package/build/cjs/components/organisms/PromptInput/index.js.map +1 -1
  27. package/build/cjs/components/organisms/PromptInput/types.d.ts +1 -0
  28. package/build/cjs/components/organisms/PromptInput/types.js.map +1 -1
  29. package/build/cjs/components/organisms/PromptInput/usePromptCommands.d.ts +41 -0
  30. package/build/cjs/components/organisms/PromptInput/usePromptCommands.js +222 -0
  31. package/build/cjs/components/organisms/PromptInput/usePromptCommands.js.map +1 -0
  32. package/build/cjs/components/organisms/PromptInput/usePromptInput.d.ts +10 -1
  33. package/build/cjs/components/organisms/PromptInput/usePromptInput.js +100 -11
  34. package/build/cjs/components/organisms/PromptInput/usePromptInput.js.map +1 -1
  35. package/build/cjs/components/pages/AIStudioChat/AIStudioChat.js +139 -52
  36. package/build/cjs/components/pages/AIStudioChat/AIStudioChat.js.map +1 -1
  37. package/build/cjs/components/pages/AIStudioChat/request.d.ts +4 -0
  38. package/build/cjs/components/pages/AIStudioChat/request.js +11 -0
  39. package/build/cjs/components/pages/AIStudioChat/request.js.map +1 -0
  40. package/build/cjs/components/pages/AIStudioChat/types.d.ts +5 -5
  41. package/build/cjs/components/pages/AIStudioChat/types.js.map +1 -1
  42. package/build/cjs/components/pages/ChatContainer/ChatContainer.js +10 -4
  43. package/build/cjs/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  44. package/build/cjs/components/pages/ChatContainer/types.d.ts +1 -1
  45. package/build/cjs/components/pages/ChatContainer/types.js.map +1 -1
  46. package/build/cjs/components/pages/ChatContainer/useChatContainer.d.ts +1 -0
  47. package/build/cjs/components/pages/ChatContainer/useChatContainer.js +5 -1
  48. package/build/cjs/components/pages/ChatContainer/useChatContainer.js.map +1 -1
  49. package/build/cjs/package.json +1 -1
  50. package/build/cjs/types/index.d.ts +1 -0
  51. package/build/cjs/types/index.js +1 -0
  52. package/build/cjs/types/index.js.map +1 -1
  53. package/build/cjs/types/messages.d.ts +3 -0
  54. package/build/cjs/types/messages.js.map +1 -1
  55. package/build/cjs/types/promptCommands.d.ts +44 -0
  56. package/build/cjs/types/promptCommands.js +3 -0
  57. package/build/cjs/types/promptCommands.js.map +1 -0
  58. package/build/docs/components/molecules/PromptInputBody.md +16 -15
  59. package/build/docs/components/organisms/PromptInput.md +151 -0
  60. package/build/docs/components/pages/AIStudioChat.md +38 -0
  61. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.d.ts +1 -0
  62. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.js +2 -8
  63. package/build/esm/components/molecules/PromptInputBody/PromptInputBody.js.map +1 -1
  64. package/build/esm/components/organisms/PromptInput/PromptInput.css +37 -0
  65. package/build/esm/components/organisms/PromptInput/PromptInput.d.ts +13 -2
  66. package/build/esm/components/organisms/PromptInput/PromptInput.js +65 -4
  67. package/build/esm/components/organisms/PromptInput/PromptInput.js.map +1 -1
  68. package/build/esm/components/organisms/PromptInput/PromptInputCommands.d.ts +9 -0
  69. package/build/esm/components/organisms/PromptInput/PromptInputCommands.js +36 -0
  70. package/build/esm/components/organisms/PromptInput/PromptInputCommands.js.map +1 -0
  71. package/build/esm/components/organisms/PromptInput/PromptInputFull.d.ts +2 -0
  72. package/build/esm/components/organisms/PromptInput/PromptInputFull.js +4 -4
  73. package/build/esm/components/organisms/PromptInput/PromptInputFull.js.map +1 -1
  74. package/build/esm/components/organisms/PromptInput/PromptInputSimple.d.ts +2 -0
  75. package/build/esm/components/organisms/PromptInput/PromptInputSimple.js +3 -3
  76. package/build/esm/components/organisms/PromptInput/PromptInputSimple.js.map +1 -1
  77. package/build/esm/components/organisms/PromptInput/commandUtils.d.ts +13 -0
  78. package/build/esm/components/organisms/PromptInput/commandUtils.js +23 -0
  79. package/build/esm/components/organisms/PromptInput/commandUtils.js.map +1 -0
  80. package/build/esm/components/organisms/PromptInput/i18n/en.json +8 -0
  81. package/build/esm/components/organisms/PromptInput/i18n/index.d.ts +13 -0
  82. package/build/esm/components/organisms/PromptInput/i18n/index.js +6 -0
  83. package/build/esm/components/organisms/PromptInput/i18n/index.js.map +1 -0
  84. package/build/esm/components/organisms/PromptInput/i18n/ru.json +8 -0
  85. package/build/esm/components/organisms/PromptInput/index.d.ts +2 -1
  86. package/build/esm/components/organisms/PromptInput/index.js.map +1 -1
  87. package/build/esm/components/organisms/PromptInput/types.d.ts +1 -0
  88. package/build/esm/components/organisms/PromptInput/types.js.map +1 -1
  89. package/build/esm/components/organisms/PromptInput/usePromptCommands.d.ts +41 -0
  90. package/build/esm/components/organisms/PromptInput/usePromptCommands.js +219 -0
  91. package/build/esm/components/organisms/PromptInput/usePromptCommands.js.map +1 -0
  92. package/build/esm/components/organisms/PromptInput/usePromptInput.d.ts +10 -1
  93. package/build/esm/components/organisms/PromptInput/usePromptInput.js +101 -12
  94. package/build/esm/components/organisms/PromptInput/usePromptInput.js.map +1 -1
  95. package/build/esm/components/pages/AIStudioChat/AIStudioChat.js +139 -52
  96. package/build/esm/components/pages/AIStudioChat/AIStudioChat.js.map +1 -1
  97. package/build/esm/components/pages/AIStudioChat/request.d.ts +4 -0
  98. package/build/esm/components/pages/AIStudioChat/request.js +8 -0
  99. package/build/esm/components/pages/AIStudioChat/request.js.map +1 -0
  100. package/build/esm/components/pages/AIStudioChat/types.d.ts +5 -5
  101. package/build/esm/components/pages/AIStudioChat/types.js.map +1 -1
  102. package/build/esm/components/pages/ChatContainer/ChatContainer.js +12 -6
  103. package/build/esm/components/pages/ChatContainer/ChatContainer.js.map +1 -1
  104. package/build/esm/components/pages/ChatContainer/types.d.ts +1 -1
  105. package/build/esm/components/pages/ChatContainer/types.js.map +1 -1
  106. package/build/esm/components/pages/ChatContainer/useChatContainer.d.ts +1 -0
  107. package/build/esm/components/pages/ChatContainer/useChatContainer.js +5 -1
  108. package/build/esm/components/pages/ChatContainer/useChatContainer.js.map +1 -1
  109. package/build/esm/package.json +1 -1
  110. package/build/esm/types/index.d.ts +1 -0
  111. package/build/esm/types/index.js +1 -0
  112. package/build/esm/types/index.js.map +1 -1
  113. package/build/esm/types/messages.d.ts +3 -0
  114. package/build/esm/types/messages.js.map +1 -1
  115. package/build/esm/types/promptCommands.d.ts +44 -0
  116. package/build/esm/types/promptCommands.js +2 -0
  117. package/build/esm/types/promptCommands.js.map +1 -0
  118. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"../../../src","sources":["types/messages.ts"],"names":[],"mappings":";;;AAmDA,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC7B,sCAAa,CAAA;IACb,sCAAa,CAAA;IACb,wCAAe,CAAA;IACf,sCAAa,CAAA;IACb,4CAAmB,CAAA;IACnB,0CAAiB,CAAA;AACrB,CAAC,EAPW,qBAAqB,qCAArB,qBAAqB,QAOhC","sourcesContent":["import type React from 'react';\n\nimport type {ButtonView, PopupPlacement} from '@gravity-ui/uikit';\n\nimport {ActionConfig} from './common';\nimport {ToolMessageProps} from './tool';\n\n/**\n * Context object passed to popup content generator function\n */\nexport interface ActionPopupContext {\n /** Update popup content without closing it */\n setContent: (content: React.ReactNode) => void;\n /** Update popup title dynamically */\n setTitle: (title: string | undefined) => void;\n /** Update popup subtitle dynamically */\n setSubtitle: (subtitle: string | undefined) => void;\n /** Programmatically close the popup */\n closePopup: () => void;\n}\n\n/**\n * Configuration for action popup\n */\nexport interface ActionPopupConfig<TMessage> {\n /** Function that generates popup content */\n getContent: (message: TMessage, context: ActionPopupContext) => React.ReactNode;\n /** Optional popup title (can be overridden by actionPopupProps) */\n title?: string;\n /** Optional popup subtitle (can be overridden by actionPopupProps) */\n subtitle?: string;\n /** Optional popup placement (can be overridden by actionPopupProps) */\n placement?: PopupPlacement;\n}\n\nexport type BaseMessageActionConfig<TMessage = unknown> = ActionConfig & {\n /** Optional popup configuration for this action */\n popup?: ActionPopupConfig<TMessage>;\n};\n\n/**\n * BaseMessage action can be either:\n * - BaseMessageActionConfig object with properties (including type for default icons)\n * - React.ReactNode for fully custom content\n */\nexport type BaseMessageAction<TMessage = unknown> =\n | BaseMessageActionConfig<TMessage>\n | React.ReactNode;\n\nexport type UserRating = 'like' | 'dislike';\n\nexport enum BaseMessageActionType {\n Copy = 'copy',\n Edit = 'edit',\n Retry = 'retry',\n Like = 'like',\n Dislike = 'dislike',\n Delete = 'delete',\n}\n\n/**\n * Default action descriptor for message action buttons.\n * Generic over the message type to provide typed onClick handler.\n */\nexport type DefaultMessageAction<TMessage> = {\n type?: string;\n onClick: (message: TMessage) => void;\n icon?: React.ReactNode;\n /**\n * Arbitrary button content (text, badges, or any ReactNode).\n * Takes precedence over `icon` when both are provided.\n * Can be a static ReactNode or a render function that receives the message,\n * allowing dynamic content based on message data (e.g. token count from metadata).\n */\n children?: React.ReactNode | ((message: TMessage) => React.ReactNode);\n label?: string;\n view?: ButtonView;\n /** Optional popup configuration for this action */\n popup?: ActionPopupConfig<TMessage>;\n};\n\n/**\n * React component used to render extra contextual info for a message\n * (e.g. token count, latency). Receives the message as a `message` prop.\n */\nexport type MessageExtraInfoComponent<TMessage> = React.ComponentType<{message: TMessage}>;\n\nexport type BaseMessageProps<TMessage = unknown> = {\n children: React.ReactNode;\n role: TMessageRole;\n actions?: BaseMessageAction<TMessage>[];\n /** Extra info node rendered alongside the action buttons (e.g. token count). */\n extraInfo?: React.ReactNode;\n userRating?: UserRating;\n timestamp?: string;\n showTimestamp?: boolean;\n showActionsOnHover?: boolean;\n className?: string;\n qa?: string;\n /** Callback when action with popup config is clicked */\n onActionPopup?: (action: BaseMessageActionConfig<TMessage>, anchorElement: HTMLElement) => void;\n};\n\nexport type TMessageMetadata = Record<string, unknown>;\n\n/** Context passed when a message is sent via suggestion click */\nexport type TSuggestionContext = {\n id?: string;\n data?: Record<string, unknown>;\n};\n\nexport type TSubmitData = {\n content: string;\n attachments?: File[];\n metadata?: TMessageMetadata;\n /** Set when the message is sent by clicking a suggestion */\n suggestion?: TSuggestionContext;\n};\n\nexport type TMessageRole = 'user' | 'assistant' | 'system';\n\nexport type TMessageContent<Type extends string = string, Data = unknown> = {\n id?: string;\n type: Type;\n data: Data;\n};\n\nexport type TextMessageContentData = {\n text: string;\n};\n\nexport type TextMessageContent = TMessageContent<'text', TextMessageContentData>;\n\nexport type ThinkingMessageContentData = {\n title?: string;\n content: string | string[];\n status: 'thinking' | 'thought';\n defaultExpanded?: boolean;\n showStatusIndicator?: boolean;\n onCopyClick?: () => void;\n enabledCopy?: boolean;\n className?: string;\n qa?: string;\n};\n\nexport type ThinkingMessageContent = TMessageContent<'thinking', ThinkingMessageContentData>;\n\nexport type ToolMessageContentData = ToolMessageProps & {\n /** MCP arguments, pretty-printed JSON. Read by AIStudioChat's tool renderer. */\n mcpRequest?: string;\n /** MCP output (or error), pretty-printed JSON. Read by AIStudioChat's tool renderer. */\n mcpResponse?: string;\n};\n\nexport type ToolMessageContent = TMessageContent<'tool', ToolMessageContentData>;\n\nexport type TDefaultMessageContent =\n | TextMessageContent\n | ThinkingMessageContent\n | ToolMessageContent;\n\nexport type TMessageContentUnion<TCustomMessageContent extends TMessageContent = never> =\n | TDefaultMessageContent\n | TCustomMessageContent;\n\nexport type TAssistantMessageContent<TCustomMessageContent extends TMessageContent = never> =\n | string\n | TMessageContentUnion<TCustomMessageContent>\n | TMessageContentUnion<TCustomMessageContent>[];\n\nexport type TBaseMessage<Metadata = TMessageMetadata> = Pick<\n BaseMessageProps,\n 'actions' | 'timestamp'\n> & {\n id?: string;\n error?: unknown;\n metadata?: Metadata;\n};\n\nexport type FileAttachment = {\n id: string;\n name: string;\n mimeType?: string;\n};\n\nexport type TUserMessage<Metadata = TMessageMetadata> = TBaseMessage<Metadata> & {\n role: 'user';\n content: string;\n format?: 'plain' | 'markdown';\n avatarUrl?: string;\n /** Base64 data URLs or regular image URLs attached to the message */\n images?: string[];\n /** File attachments sent with this message */\n fileAttachments?: FileAttachment[];\n};\n\nexport type TAssistantMessage<\n TCustomMessageContent extends TMessageContent = never,\n Metadata = TMessageMetadata,\n> = TBaseMessage<Metadata> & {\n role: 'assistant';\n content: TAssistantMessageContent<TCustomMessageContent>;\n userRating?: UserRating;\n};\n\nexport type TChatMessage<\n TCustomMessageContent extends TMessageContent = never,\n Metadata = TMessageMetadata,\n> = TUserMessage<Metadata> | TAssistantMessage<TCustomMessageContent, Metadata>;\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"../../../src","sources":["types/messages.ts"],"names":[],"mappings":";;;AAoDA,IAAY,qBAOX;AAPD,WAAY,qBAAqB;IAC7B,sCAAa,CAAA;IACb,sCAAa,CAAA;IACb,wCAAe,CAAA;IACf,sCAAa,CAAA;IACb,4CAAmB,CAAA;IACnB,0CAAiB,CAAA;AACrB,CAAC,EAPW,qBAAqB,qCAArB,qBAAqB,QAOhC","sourcesContent":["import type React from 'react';\n\nimport type {ButtonView, PopupPlacement} from '@gravity-ui/uikit';\n\nimport {ActionConfig} from './common';\nimport type {PromptSelection} from './promptCommands';\nimport {ToolMessageProps} from './tool';\n\n/**\n * Context object passed to popup content generator function\n */\nexport interface ActionPopupContext {\n /** Update popup content without closing it */\n setContent: (content: React.ReactNode) => void;\n /** Update popup title dynamically */\n setTitle: (title: string | undefined) => void;\n /** Update popup subtitle dynamically */\n setSubtitle: (subtitle: string | undefined) => void;\n /** Programmatically close the popup */\n closePopup: () => void;\n}\n\n/**\n * Configuration for action popup\n */\nexport interface ActionPopupConfig<TMessage> {\n /** Function that generates popup content */\n getContent: (message: TMessage, context: ActionPopupContext) => React.ReactNode;\n /** Optional popup title (can be overridden by actionPopupProps) */\n title?: string;\n /** Optional popup subtitle (can be overridden by actionPopupProps) */\n subtitle?: string;\n /** Optional popup placement (can be overridden by actionPopupProps) */\n placement?: PopupPlacement;\n}\n\nexport type BaseMessageActionConfig<TMessage = unknown> = ActionConfig & {\n /** Optional popup configuration for this action */\n popup?: ActionPopupConfig<TMessage>;\n};\n\n/**\n * BaseMessage action can be either:\n * - BaseMessageActionConfig object with properties (including type for default icons)\n * - React.ReactNode for fully custom content\n */\nexport type BaseMessageAction<TMessage = unknown> =\n | BaseMessageActionConfig<TMessage>\n | React.ReactNode;\n\nexport type UserRating = 'like' | 'dislike';\n\nexport enum BaseMessageActionType {\n Copy = 'copy',\n Edit = 'edit',\n Retry = 'retry',\n Like = 'like',\n Dislike = 'dislike',\n Delete = 'delete',\n}\n\n/**\n * Default action descriptor for message action buttons.\n * Generic over the message type to provide typed onClick handler.\n */\nexport type DefaultMessageAction<TMessage> = {\n type?: string;\n onClick: (message: TMessage) => void;\n icon?: React.ReactNode;\n /**\n * Arbitrary button content (text, badges, or any ReactNode).\n * Takes precedence over `icon` when both are provided.\n * Can be a static ReactNode or a render function that receives the message,\n * allowing dynamic content based on message data (e.g. token count from metadata).\n */\n children?: React.ReactNode | ((message: TMessage) => React.ReactNode);\n label?: string;\n view?: ButtonView;\n /** Optional popup configuration for this action */\n popup?: ActionPopupConfig<TMessage>;\n};\n\n/**\n * React component used to render extra contextual info for a message\n * (e.g. token count, latency). Receives the message as a `message` prop.\n */\nexport type MessageExtraInfoComponent<TMessage> = React.ComponentType<{message: TMessage}>;\n\nexport type BaseMessageProps<TMessage = unknown> = {\n children: React.ReactNode;\n role: TMessageRole;\n actions?: BaseMessageAction<TMessage>[];\n /** Extra info node rendered alongside the action buttons (e.g. token count). */\n extraInfo?: React.ReactNode;\n userRating?: UserRating;\n timestamp?: string;\n showTimestamp?: boolean;\n showActionsOnHover?: boolean;\n className?: string;\n qa?: string;\n /** Callback when action with popup config is clicked */\n onActionPopup?: (action: BaseMessageActionConfig<TMessage>, anchorElement: HTMLElement) => void;\n};\n\nexport type TMessageMetadata = Record<string, unknown>;\n\n/** Context passed when a message is sent via suggestion click */\nexport type TSuggestionContext = {\n id?: string;\n data?: Record<string, unknown>;\n};\n\nexport type TSubmitData = {\n /** Snapshot of commands and context selected for this message. */\n selections?: PromptSelection[];\n content: string;\n attachments?: File[];\n metadata?: TMessageMetadata;\n /** Set when the message is sent by clicking a suggestion */\n suggestion?: TSuggestionContext;\n};\n\nexport type TMessageRole = 'user' | 'assistant' | 'system';\n\nexport type TMessageContent<Type extends string = string, Data = unknown> = {\n id?: string;\n type: Type;\n data: Data;\n};\n\nexport type TextMessageContentData = {\n text: string;\n};\n\nexport type TextMessageContent = TMessageContent<'text', TextMessageContentData>;\n\nexport type ThinkingMessageContentData = {\n title?: string;\n content: string | string[];\n status: 'thinking' | 'thought';\n defaultExpanded?: boolean;\n showStatusIndicator?: boolean;\n onCopyClick?: () => void;\n enabledCopy?: boolean;\n className?: string;\n qa?: string;\n};\n\nexport type ThinkingMessageContent = TMessageContent<'thinking', ThinkingMessageContentData>;\n\nexport type ToolMessageContentData = ToolMessageProps & {\n /** MCP arguments, pretty-printed JSON. Read by AIStudioChat's tool renderer. */\n mcpRequest?: string;\n /** MCP output (or error), pretty-printed JSON. Read by AIStudioChat's tool renderer. */\n mcpResponse?: string;\n};\n\nexport type ToolMessageContent = TMessageContent<'tool', ToolMessageContentData>;\n\nexport type TDefaultMessageContent =\n | TextMessageContent\n | ThinkingMessageContent\n | ToolMessageContent;\n\nexport type TMessageContentUnion<TCustomMessageContent extends TMessageContent = never> =\n | TDefaultMessageContent\n | TCustomMessageContent;\n\nexport type TAssistantMessageContent<TCustomMessageContent extends TMessageContent = never> =\n | string\n | TMessageContentUnion<TCustomMessageContent>\n | TMessageContentUnion<TCustomMessageContent>[];\n\nexport type TBaseMessage<Metadata = TMessageMetadata> = Pick<\n BaseMessageProps,\n 'actions' | 'timestamp'\n> & {\n id?: string;\n error?: unknown;\n metadata?: Metadata;\n};\n\nexport type FileAttachment = {\n id: string;\n name: string;\n mimeType?: string;\n};\n\nexport type TUserMessage<Metadata = TMessageMetadata> = TBaseMessage<Metadata> & {\n role: 'user';\n content: string;\n format?: 'plain' | 'markdown';\n avatarUrl?: string;\n /** Base64 data URLs or regular image URLs attached to the message */\n images?: string[];\n /** File attachments sent with this message */\n fileAttachments?: FileAttachment[];\n};\n\nexport type TAssistantMessage<\n TCustomMessageContent extends TMessageContent = never,\n Metadata = TMessageMetadata,\n> = TBaseMessage<Metadata> & {\n role: 'assistant';\n content: TAssistantMessageContent<TCustomMessageContent>;\n userRating?: UserRating;\n};\n\nexport type TChatMessage<\n TCustomMessageContent extends TMessageContent = never,\n Metadata = TMessageMetadata,\n> = TUserMessage<Metadata> | TAssistantMessage<TCustomMessageContent, Metadata>;\n"]}
@@ -0,0 +1,44 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Serializable application data attached to a single submitted draft. */
3
+ export type PromptSelection = {
4
+ sourceId: string;
5
+ id: string;
6
+ title: string;
7
+ data?: Record<string, unknown>;
8
+ };
9
+ export type PromptCommandItem = {
10
+ id: string;
11
+ title: string;
12
+ description?: string;
13
+ icon?: ReactNode;
14
+ keywords?: string[];
15
+ disabled?: boolean;
16
+ data?: Record<string, unknown>;
17
+ /** Replaces the active trigger and query. Omit to remove them. */
18
+ insertText?: string;
19
+ /** Keep this item in the draft and submit it. Defaults to true. */
20
+ keepSelected?: boolean;
21
+ };
22
+ export type PromptCommandSource = {
23
+ id: string;
24
+ title: string;
25
+ trigger?: '/' | '@';
26
+ /** Also expose a button opening this source. */
27
+ buttonLabel?: string;
28
+ items?: readonly PromptCommandItem[];
29
+ /** Overrides items; the application owns remote filtering. */
30
+ getItems?: (query: string, options: {
31
+ signal: AbortSignal;
32
+ }) => Promise<readonly PromptCommandItem[]>;
33
+ /** Called before committing the selection. Throw to keep the picker open. */
34
+ onSelect?: (item: PromptCommandItem, context: {
35
+ query: string;
36
+ origin: 'input' | 'button';
37
+ }) => void;
38
+ };
39
+ export type PromptCommandsConfig = {
40
+ sources: readonly PromptCommandSource[];
41
+ /** Controlled selections. Reset these when switching conversations. */
42
+ selections?: readonly PromptSelection[];
43
+ onSelectionsChange?: (selections: PromptSelection[]) => void;
44
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=promptCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promptCommands.js","sourceRoot":"../../../src","sources":["types/promptCommands.ts"],"names":[],"mappings":"","sourcesContent":["import type {ReactNode} from 'react';\n\n/** Serializable application data attached to a single submitted draft. */\nexport type PromptSelection = {\n sourceId: string;\n id: string;\n title: string;\n data?: Record<string, unknown>;\n};\n\nexport type PromptCommandItem = {\n id: string;\n title: string;\n description?: string;\n icon?: ReactNode;\n keywords?: string[];\n disabled?: boolean;\n data?: Record<string, unknown>;\n /** Replaces the active trigger and query. Omit to remove them. */\n insertText?: string;\n /** Keep this item in the draft and submit it. Defaults to true. */\n keepSelected?: boolean;\n};\n\nexport type PromptCommandSource = {\n id: string;\n title: string;\n trigger?: '/' | '@';\n /** Also expose a button opening this source. */\n buttonLabel?: string;\n items?: readonly PromptCommandItem[];\n /** Overrides items; the application owns remote filtering. */\n getItems?: (\n query: string,\n options: {signal: AbortSignal},\n ) => Promise<readonly PromptCommandItem[]>;\n /** Called before committing the selection. Throw to keep the picker open. */\n onSelect?: (\n item: PromptCommandItem,\n context: {query: string; origin: 'input' | 'button'},\n ) => void;\n};\n\nexport type PromptCommandsConfig = {\n sources: readonly PromptCommandSource[];\n /** Controlled selections. Reset these when switching conversations. */\n selections?: readonly PromptSelection[];\n onSelectionsChange?: (selections: PromptSelection[]) => void;\n};\n"]}
@@ -49,21 +49,22 @@ import {PromptInputBody} from '@gravity-ui/aikit';
49
49
 
50
50
  ## Props
51
51
 
52
- | Prop | Type | Required | Default | Description |
53
- | --------------- | ----------------------------------------------------------- | -------- | ----------------------------- | ---------------------------------------------- |
54
- | `value` | `string` | - | - | Value of the textarea |
55
- | `placeholder` | `string` | - | - | Placeholder text |
56
- | `maxLength` | `number` | - | - | Maximum length of input |
57
- | `size` | `TextAreaProps['size']` | - | `'l'` (`'xl'` in mobile mode) | Size of the textarea |
58
- | `minRows` | `number` | - | `1` | Minimum number of rows |
59
- | `maxRows` | `number` | - | `15` | Maximum number of rows |
60
- | `autoFocus` | `boolean` | - | `false` | Auto focus on mount |
61
- | `disabledInput` | `boolean` | - | `false` | Disabled state for input |
62
- | `onChange` | `(value: string) => void` | - | - | Change handler |
63
- | `onKeyDown` | `(event: React.KeyboardEvent<HTMLTextAreaElement>) => void` | - | - | Key down handler |
64
- | `children` | `ReactNode` | - | - | Custom content to replace the default textarea |
65
- | `className` | `string` | - | - | Additional CSS class |
66
- | `qa` | `string` | - | - | QA/test identifier |
52
+ | Prop | Type | Required | Default | Description |
53
+ | --------------- | ----------------------------------------------------------- | -------- | ----------------------------- | --------------------------------------------------------------- |
54
+ | `controlProps` | `TextAreaProps['controlProps']` | - | - | Native textarea attributes, including ARIA and selection events |
55
+ | `value` | `string` | - | - | Value of the textarea |
56
+ | `placeholder` | `string` | - | - | Placeholder text |
57
+ | `maxLength` | `number` | - | - | Maximum length of input |
58
+ | `size` | `TextAreaProps['size']` | - | `'l'` (`'xl'` in mobile mode) | Size of the textarea |
59
+ | `minRows` | `number` | - | `1` | Minimum number of rows |
60
+ | `maxRows` | `number` | - | `15` | Maximum number of rows |
61
+ | `autoFocus` | `boolean` | - | `false` | Auto focus on mount |
62
+ | `disabledInput` | `boolean` | - | `false` | Disabled state for input |
63
+ | `onChange` | `(value: string) => void` | - | - | Change handler |
64
+ | `onKeyDown` | `(event: React.KeyboardEvent<HTMLTextAreaElement>) => void` | - | - | Key down handler |
65
+ | `children` | `ReactNode` | - | - | Custom content to replace the default textarea |
66
+ | `className` | `string` | - | - | Additional CSS class |
67
+ | `qa` | `string` | - | - | QA/test identifier |
67
68
 
68
69
  ## Styling
69
70
 
@@ -4,6 +4,8 @@ A flexible input component for chat interfaces with support for simple and full
4
4
 
5
5
  ## Features
6
6
 
7
+ - **Application Commands**: Trigger-based and button-based selection with async search and per-message data
8
+
7
9
  - **Two View Modes**: Simple view with minimal UI, Full view with header and footer
8
10
  - **Expandable Panels**: Top and bottom panels with smooth animation for additional content
9
11
  - **Context Indicator**: Show usage context with percentage or number display
@@ -116,6 +118,7 @@ import {PromptInput} from '@gravity-ui/aikit';
116
118
  | Prop | Type | Required | Default | Description |
117
119
  | ------------------ | -------------------------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------- |
118
120
  | `view` | `'full' \| 'simple'` | - | `'simple'` | View variant |
121
+ | `commands` | `PromptCommandsConfig` | - | - | Application command and context sources |
119
122
  | `onSend` | `(data: TSubmitData) => Promise<void>` | ✓ | - | Callback when message is sent |
120
123
  | `onCancel` | `() => Promise<void>` | - | - | Callback when sending is cancelled |
121
124
  | `disabled` | `boolean` | - | `false` | Disabled state |
@@ -351,3 +354,151 @@ function MyComponent({isOpen}) {
351
354
  - `shouldRender` (boolean): Whether the element should be rendered in DOM
352
355
 
353
356
  The hook is particularly useful when you have CSS transitions that need to complete before removing elements from DOM.
357
+
358
+ ## Commands and context
359
+
360
+ Pass `commands` directly to `PromptInput`, or through `promptInputProps` on
361
+ `ChatContainer` and `AIStudioChat`. Sources are owned by the application and are
362
+ scoped to this input. Use unique source IDs and at most one source per trigger.
363
+ Item IDs must be unique within a source.
364
+
365
+ ```tsx
366
+ import {PromptInput, type PromptCommandSource} from '@gravity-ui/aikit';
367
+
368
+ const sources: PromptCommandSource[] = [
369
+ {
370
+ id: 'commands',
371
+ title: 'Commands',
372
+ trigger: '/',
373
+ items: [
374
+ {id: 'review', title: '/review', data: {mode: 'review'}},
375
+ {
376
+ id: 'summary',
377
+ title: '/summary',
378
+ insertText: 'Summarize this document.',
379
+ keepSelected: false,
380
+ },
381
+ ],
382
+ },
383
+ {
384
+ id: 'documents',
385
+ title: 'Document context',
386
+ trigger: '@',
387
+ buttonLabel: 'Add context',
388
+ getItems: async (query, {signal}) => {
389
+ const response = await fetch(`/api/documents?q=${encodeURIComponent(query)}`, {signal});
390
+ if (!response.ok) throw new Error('Could not search documents');
391
+ return response.json();
392
+ },
393
+ },
394
+ ];
395
+
396
+ <PromptInput
397
+ commands={{sources}}
398
+ onSend={async ({content, selections}) => {
399
+ const response = await fetch('/api/chat', {
400
+ method: 'POST',
401
+ headers: {'Content-Type': 'application/json'},
402
+ body: JSON.stringify({message: content, context: selections}),
403
+ });
404
+ if (!response.ok) throw new Error('Could not send message');
405
+ }}
406
+ />;
407
+ ```
408
+
409
+ ### Configuration
410
+
411
+ | Field | Type | Default | Description |
412
+ | -------------------- | ------------------------------------ | -------------- | ---------------------------------------------- |
413
+ | `sources` | `readonly PromptCommandSource[]` | Required | Available command and context sources |
414
+ | `selections` | `readonly PromptSelection[]` | Internal state | Controlled draft selections |
415
+ | `onSelectionsChange` | `(items: PromptSelection[]) => void` | — | Selection, removal and successful-send updates |
416
+
417
+ Each source accepts `id`, `title`, optional `/` or `@` `trigger`, optional
418
+ `buttonLabel`, `items`, `getItems(query, {signal})`, and synchronous
419
+ `onSelect(item, {query, origin})`. `getItems` takes precedence over `items` and
420
+ owns filtering; static items are filtered by title, description and keywords.
421
+ Keep source objects stable while searching. The search signal is aborted when
422
+ the query/source changes or the picker closes. Stale results are ignored even
423
+ when the application does not honor cancellation.
424
+
425
+ Items accept `id`, `title`, `description`, `icon`, `keywords`, `disabled`, `data`,
426
+ `insertText` and `keepSelected`. By default, a selection is shown as a removable
427
+ chip and passed to `onSend` as `{sourceId, id, title, data}`. Selecting the same
428
+ source/item replaces its previous selection. Different items may coexist.
429
+ `data` should contain serializable application data, not UI nodes or credentials.
430
+
431
+ `insertText` replaces the active token, preserving surrounding text. With
432
+ `keepSelected: false`, the item only inserts text and/or invokes `onSelect`.
433
+ Choosing context via a button preserves the input text. Throwing from `onSelect`
434
+ keeps the picker open; asynchronous preparation belongs in `getItems` or the
435
+ send handler. Trigger characters inside URLs and email addresses do not open
436
+ the picker. The first matching source owns a trigger.
437
+
438
+ ### Programmatic submission
439
+
440
+ Pass an `apiRef` to access `PromptInputHandle`. `submit(data?)` uses the same
441
+ submission lifecycle as the send button and includes current attachments and
442
+ command selections. With no argument it submits the current text. `ChatContainer`
443
+ uses this method for welcome suggestions, preserving suggestion metadata.
444
+ `clearFailedDraft()` clears an unchanged restored draft after a successful external
445
+ retry and returns whether it was cleared. `AIStudioChat` uses it to reconcile Retry
446
+ without erasing a newer draft.
447
+
448
+ ### Sending and lifecycle
449
+
450
+ `onSend` receives a snapshot of the current selection array. Return `false` to
451
+ cancel or reject to report an error. Text, attachments and selections clear immediately,
452
+ including when the send callback awaits a complete stream. A failed or cancelled
453
+ send restores the snapshot only if the draft and conversation have not changed.
454
+ New typing and selections are never overwritten by a late result. Treat selection
455
+ objects and their data as immutable. Text is required to submit, including when
456
+ only a command or context has been selected.
457
+
458
+ `ChatContainer` resets uncontrolled selections and closes pending searches on
459
+ conversation changes, independently of autofocus settings. Active-conversation
460
+ deletion also resets the command state. `AIStudioChat` preserves the draft when
461
+ it automatically creates a conversation for the first send.
462
+
463
+ Applications using controlled `selections` must reset or restore them with their
464
+ own conversation state. A standalone `PromptInput` accepts `commandsResetKey`
465
+ (`string | number`): changing it clears only uncontrolled command state without
466
+ moving focus. `ChatContainer` derives this key from conversation identity and
467
+ explicit new/select actions unless the application supplies its own key.
468
+
469
+ In `AIStudioChat`, `onBeforeSend` receives all submit data, including selections.
470
+ Use `transformRequest(request, data)` to map them into any field of the final
471
+ request body after file processing. Return a new request object, or `false` to
472
+ cancel. No command data is sent automatically. Retries reuse the serialized
473
+ request from the original send, without rerunning the mapping callbacks.
474
+
475
+ ```tsx
476
+ const transformRequest = (request, {selections = []}) => ({
477
+ ...request,
478
+ metadata: {
479
+ commands: selections.filter((item) => item.sourceId === 'commands'),
480
+ },
481
+ retrieval: {
482
+ documents: selections.filter((item) => item.sourceId === 'documents'),
483
+ },
484
+ });
485
+ ```
486
+
487
+ See **Commands And Context** in Storybook for a complete `ChatContainer`
488
+ integration with request inspection, controlled selections, simulated failures
489
+ and both input layouts.
490
+
491
+ ### Picker keyboard support
492
+
493
+ Arrow keys navigate available options; Enter selects without sending the
494
+ message. Escape dismisses suggestions until the text changes. Tab closes the
495
+ picker and moves focus normally. Shift+Enter remains a newline. IME composition
496
+ never submits or selects a command.
497
+
498
+ ### Command picker styling
499
+
500
+ The picker uses `--g-spacing-1`, `--g-spacing-2`, `--g-spacing-3`,
501
+ `--g-border-radius-m`, `--g-color-base-simple-hover`, `--g-color-text-secondary`
502
+ and `--g-text-body-1-font-size` for spacing, corner radius, active background,
503
+ description color and description typography respectively. Its width is limited
504
+ to the viewport and long result lists scroll.
@@ -186,3 +186,41 @@ On every `onSendMessage` call the component:
186
186
  7. If `showHistory` is enabled and no active chat exists, a new chat is created automatically using the first message as its name.
187
187
 
188
188
  On **retry**, the original user message text and stored `images` are re-sent without requiring the user to re-attach files.
189
+
190
+ ## Application commands and context
191
+
192
+ Configure command sources through `promptInputProps.commands`. See the
193
+ [PromptInput command guide](../organisms/PromptInput.md#commands-and-context).
194
+ `onBeforeSend(data)` receives the complete `TSubmitData`, including `selections`.
195
+ It can contribute request body fields or return `false`/`null` to cancel.
196
+ Returning `undefined` contributes no extra fields and continues sending.
197
+
198
+ `transformRequest(request, data)` runs after `extraRequestParams`, per-send
199
+ parameters, file parameters and conversation continuity have been assembled.
200
+ Return the final request body, synchronously or asynchronously, or `false` to
201
+ cancel without clearing the draft. The application determines exactly where
202
+ command instructions and context belong. Selection data is not automatically
203
+ serialized into the request. UI message text remains the user's input even if
204
+ the mapping adds instructions to the API messages.
205
+
206
+ ```tsx
207
+ <AIStudioChat
208
+ apiUrl="/api/chat"
209
+ fileUpload={fileUpload}
210
+ promptInputProps={{commands: {sources}}}
211
+ transformRequest={(request, {selections = []}) => ({
212
+ ...request,
213
+ context: selections.map(({sourceId, id, data}) => ({sourceId, id, data})),
214
+ })}
215
+ />
216
+ ```
217
+
218
+ Retry reuses the original serialized request body. Later draft selections and
219
+ changed mapping callbacks do not alter the retried request. HTTP headers still
220
+ come from the current `requestInit` configuration.
221
+
222
+ Failed HTTP attempts are displayed separately from model history. Technical
223
+ errors are shown through the chat error state and are never added as assistant
224
+ messages. Sending the retained draft again uses the accepted history and the
225
+ current draft exactly once, even when the draft has been edited. Retry reuses
226
+ the failed attempt's original request snapshot.
@@ -21,6 +21,7 @@ export type PromptInputBodyProps = {
21
21
  autoFocus?: boolean;
22
22
  /** Disabled state for input */
23
23
  disabledInput?: boolean;
24
+ controlProps?: TextAreaProps['controlProps'];
24
25
  /** Change handler */
25
26
  onChange?: (value: string) => void;
26
27
  /** Key down handler */
@@ -23,7 +23,7 @@ const scheduleCaretAtEndIfCollapsed = (textarea) => {
23
23
  * @returns React component
24
24
  */
25
25
  export const PromptInputBody = forwardRef((props, ref) => {
26
- const { value, placeholder, maxLength, size, minRows = 1, maxRows = 15, autoFocus = false, disabledInput = false, onChange, onKeyDown, children, className, inputClassName, qa, } = props;
26
+ const { controlProps, value, placeholder, maxLength, size, minRows = 1, maxRows = 15, autoFocus = false, disabledInput = false, onChange, onKeyDown, children, className, inputClassName, qa, } = props;
27
27
  const resolvedSize = useMobileControlSize(size, 'l', 'xl');
28
28
  const hasHandledInitialFocusRef = useRef(false);
29
29
  const pendingInitialPointerIdRef = useRef(null);
@@ -64,13 +64,7 @@ export const PromptInputBody = forwardRef((props, ref) => {
64
64
  return (_jsx("div", { className: b(null, className), "data-qa": qa, children: children }));
65
65
  }
66
66
  // Render default textarea
67
- return (_jsx("div", { className: b(null, className), "data-qa": qa, children: _jsx(TextArea, { controlRef: ref, size: resolvedSize, value: value, placeholder: placeholder, minRows: minRows, maxRows: maxRows, autoFocus: autoFocus, disabled: disabledInput, onUpdate: onChange, onFocus: handleFocus, onKeyDown: onKeyDown, view: "clear", className: b('textarea'), controlProps: {
68
- className: b('textarea-control', inputClassName),
69
- maxLength,
70
- onPointerDown: handlePointerDown,
71
- onPointerUp: handlePointerUp,
72
- onPointerCancel: handlePointerCancel,
73
- } }) }));
67
+ return (_jsx("div", { className: b(null, className), "data-qa": qa, children: _jsx(TextArea, { controlRef: ref, size: resolvedSize, value: value, placeholder: placeholder, minRows: minRows, maxRows: maxRows, autoFocus: autoFocus, disabled: disabledInput, onUpdate: onChange, onFocus: handleFocus, onKeyDown: onKeyDown, view: "clear", className: b('textarea'), controlProps: Object.assign(Object.assign({}, controlProps), { className: b('textarea-control', inputClassName), maxLength, onPointerDown: handlePointerDown, onPointerUp: handlePointerUp, onPointerCancel: handlePointerCancel }) }) }));
74
68
  });
75
69
  PromptInputBody.displayName = 'PromptInputBody';
76
70
  //# sourceMappingURL=PromptInputBody.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PromptInputBody.js","sourceRoot":"../../../../../src","sources":["components/molecules/PromptInputBody/PromptInputBody.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ReactNode, forwardRef, useRef} from 'react';\n\nimport type {TextAreaProps} from '@gravity-ui/uikit';\nimport {TextArea} from '@gravity-ui/uikit';\n\nimport {useMobileControlSize} from '../../../hooks/useMobileControlSize';\nimport {block} from '../../../utils/cn';\n\nimport './PromptInputBody.scss';\n\nconst b = block('prompt-input-body');\n\nconst scheduleCaretAtEndIfCollapsed = (textarea: HTMLTextAreaElement) => {\n requestAnimationFrame(() => {\n if (\n textarea.isConnected &&\n textarea.ownerDocument.activeElement === textarea &&\n textarea.selectionStart === textarea.selectionEnd\n ) {\n const caretPosition = textarea.value.length;\n textarea.setSelectionRange(caretPosition, caretPosition);\n }\n });\n};\n\n/**\n * Props for the PromptInputBody component\n */\nexport type PromptInputBodyProps = {\n /** Value of the textarea */\n value?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Maximum length of input */\n maxLength?: number;\n /** Size of the textarea. Defaults to `l`, or `xl` in mobile mode */\n size?: TextAreaProps['size'];\n /** Minimum number of rows */\n minRows?: number;\n /** Maximum number of rows */\n maxRows?: number;\n /** Auto focus on mount */\n autoFocus?: boolean;\n /** Disabled state for input */\n disabledInput?: boolean;\n /** Change handler */\n onChange?: (value: string) => void;\n /** Key down handler */\n onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n /** Custom content to replace the default textarea */\n children?: ReactNode;\n /** Additional CSS class */\n className?: string;\n /** Additional CSS class for input element */\n inputClassName?: string;\n /** QA/test identifier */\n qa?: string;\n};\n\n/**\n * PromptInputBody component displays the main input area\n * with textarea or custom content\n *\n * @param props - Component props\n * @returns React component\n */\nexport const PromptInputBody = forwardRef<HTMLTextAreaElement, PromptInputBodyProps>(\n (props, ref) => {\n const {\n value,\n placeholder,\n maxLength,\n size,\n minRows = 1,\n maxRows = 15,\n autoFocus = false,\n disabledInput = false,\n onChange,\n onKeyDown,\n children,\n className,\n inputClassName,\n qa,\n } = props;\n\n const resolvedSize = useMobileControlSize(size, 'l', 'xl');\n const hasHandledInitialFocusRef = useRef(false);\n const pendingInitialPointerIdRef = useRef<number | null>(null);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n pendingInitialPointerIdRef.current =\n !hasHandledInitialFocusRef.current && event.isPrimary && event.button === 0\n ? event.pointerId\n : null;\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n if (pendingInitialPointerIdRef.current !== event.pointerId) {\n return;\n }\n\n pendingInitialPointerIdRef.current = null;\n scheduleCaretAtEndIfCollapsed(event.currentTarget);\n };\n\n const handlePointerCancel = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n if (pendingInitialPointerIdRef.current === event.pointerId) {\n pendingInitialPointerIdRef.current = null;\n }\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLTextAreaElement>) => {\n if (hasHandledInitialFocusRef.current) {\n return;\n }\n\n hasHandledInitialFocusRef.current = true;\n\n const textarea = event.currentTarget;\n if (!textarea.value) {\n pendingInitialPointerIdRef.current = null;\n return;\n }\n\n if (pendingInitialPointerIdRef.current === null) {\n scheduleCaretAtEndIfCollapsed(textarea);\n }\n };\n\n // If custom content is provided, render it\n if (children) {\n return (\n <div className={b(null, className)} data-qa={qa}>\n {children}\n </div>\n );\n }\n\n // Render default textarea\n return (\n <div className={b(null, className)} data-qa={qa}>\n <TextArea\n controlRef={ref}\n size={resolvedSize}\n value={value}\n placeholder={placeholder}\n minRows={minRows}\n maxRows={maxRows}\n autoFocus={autoFocus}\n disabled={disabledInput}\n onUpdate={onChange}\n onFocus={handleFocus}\n onKeyDown={onKeyDown}\n view=\"clear\"\n className={b('textarea')}\n controlProps={{\n className: b('textarea-control', inputClassName),\n maxLength,\n onPointerDown: handlePointerDown,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerCancel,\n }}\n />\n </div>\n );\n },\n);\n\nPromptInputBody.displayName = 'PromptInputBody';\n"]}
1
+ {"version":3,"file":"PromptInputBody.js","sourceRoot":"../../../../../src","sources":["components/molecules/PromptInputBody/PromptInputBody.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ReactNode, forwardRef, useRef} from 'react';\n\nimport type {TextAreaProps} from '@gravity-ui/uikit';\nimport {TextArea} from '@gravity-ui/uikit';\n\nimport {useMobileControlSize} from '../../../hooks/useMobileControlSize';\nimport {block} from '../../../utils/cn';\n\nimport './PromptInputBody.scss';\n\nconst b = block('prompt-input-body');\n\nconst scheduleCaretAtEndIfCollapsed = (textarea: HTMLTextAreaElement) => {\n requestAnimationFrame(() => {\n if (\n textarea.isConnected &&\n textarea.ownerDocument.activeElement === textarea &&\n textarea.selectionStart === textarea.selectionEnd\n ) {\n const caretPosition = textarea.value.length;\n textarea.setSelectionRange(caretPosition, caretPosition);\n }\n });\n};\n\n/**\n * Props for the PromptInputBody component\n */\nexport type PromptInputBodyProps = {\n /** Value of the textarea */\n value?: string;\n /** Placeholder text */\n placeholder?: string;\n /** Maximum length of input */\n maxLength?: number;\n /** Size of the textarea. Defaults to `l`, or `xl` in mobile mode */\n size?: TextAreaProps['size'];\n /** Minimum number of rows */\n minRows?: number;\n /** Maximum number of rows */\n maxRows?: number;\n /** Auto focus on mount */\n autoFocus?: boolean;\n /** Disabled state for input */\n disabledInput?: boolean;\n controlProps?: TextAreaProps['controlProps'];\n /** Change handler */\n onChange?: (value: string) => void;\n /** Key down handler */\n onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n /** Custom content to replace the default textarea */\n children?: ReactNode;\n /** Additional CSS class */\n className?: string;\n /** Additional CSS class for input element */\n inputClassName?: string;\n /** QA/test identifier */\n qa?: string;\n};\n\n/**\n * PromptInputBody component displays the main input area\n * with textarea or custom content\n *\n * @param props - Component props\n * @returns React component\n */\nexport const PromptInputBody = forwardRef<HTMLTextAreaElement, PromptInputBodyProps>(\n (props, ref) => {\n const {\n controlProps,\n value,\n placeholder,\n maxLength,\n size,\n minRows = 1,\n maxRows = 15,\n autoFocus = false,\n disabledInput = false,\n onChange,\n onKeyDown,\n children,\n className,\n inputClassName,\n qa,\n } = props;\n\n const resolvedSize = useMobileControlSize(size, 'l', 'xl');\n const hasHandledInitialFocusRef = useRef(false);\n const pendingInitialPointerIdRef = useRef<number | null>(null);\n\n const handlePointerDown = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n pendingInitialPointerIdRef.current =\n !hasHandledInitialFocusRef.current && event.isPrimary && event.button === 0\n ? event.pointerId\n : null;\n };\n\n const handlePointerUp = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n if (pendingInitialPointerIdRef.current !== event.pointerId) {\n return;\n }\n\n pendingInitialPointerIdRef.current = null;\n scheduleCaretAtEndIfCollapsed(event.currentTarget);\n };\n\n const handlePointerCancel = (event: React.PointerEvent<HTMLTextAreaElement>) => {\n if (pendingInitialPointerIdRef.current === event.pointerId) {\n pendingInitialPointerIdRef.current = null;\n }\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLTextAreaElement>) => {\n if (hasHandledInitialFocusRef.current) {\n return;\n }\n\n hasHandledInitialFocusRef.current = true;\n\n const textarea = event.currentTarget;\n if (!textarea.value) {\n pendingInitialPointerIdRef.current = null;\n return;\n }\n\n if (pendingInitialPointerIdRef.current === null) {\n scheduleCaretAtEndIfCollapsed(textarea);\n }\n };\n\n // If custom content is provided, render it\n if (children) {\n return (\n <div className={b(null, className)} data-qa={qa}>\n {children}\n </div>\n );\n }\n\n // Render default textarea\n return (\n <div className={b(null, className)} data-qa={qa}>\n <TextArea\n controlRef={ref}\n size={resolvedSize}\n value={value}\n placeholder={placeholder}\n minRows={minRows}\n maxRows={maxRows}\n autoFocus={autoFocus}\n disabled={disabledInput}\n onUpdate={onChange}\n onFocus={handleFocus}\n onKeyDown={onKeyDown}\n view=\"clear\"\n className={b('textarea')}\n controlProps={{\n ...controlProps,\n className: b('textarea-control', inputClassName),\n maxLength,\n onPointerDown: handlePointerDown,\n onPointerUp: handlePointerUp,\n onPointerCancel: handlePointerCancel,\n }}\n />\n </div>\n );\n },\n);\n\nPromptInputBody.displayName = 'PromptInputBody';\n"]}
@@ -47,4 +47,41 @@
47
47
  }
48
48
  .g-aikit-prompt-input__suggestions {
49
49
  padding-bottom: var(--g-spacing-2);
50
+ }
51
+
52
+ .g-aikit-prompt-input__command-buttons {
53
+ display: flex;
54
+ flex-wrap: wrap;
55
+ gap: var(--g-spacing-1);
56
+ }
57
+ .g-aikit-prompt-input__commands {
58
+ width: min(400px, 100vw - 32px);
59
+ padding: var(--g-spacing-3);
60
+ box-sizing: border-box;
61
+ }
62
+ .g-aikit-prompt-input__commands-title {
63
+ font-weight: 600;
64
+ margin-bottom: var(--g-spacing-2);
65
+ }
66
+ .g-aikit-prompt-input__command-list {
67
+ max-height: 240px;
68
+ overflow-y: auto;
69
+ }
70
+ .g-aikit-prompt-input__command {
71
+ display: flex;
72
+ gap: var(--g-spacing-2);
73
+ padding: var(--g-spacing-2);
74
+ border-radius: var(--g-border-radius-m);
75
+ cursor: pointer;
76
+ }
77
+ .g-aikit-prompt-input__command_active, .g-aikit-prompt-input__command:hover {
78
+ background: var(--g-color-base-simple-hover);
79
+ }
80
+ .g-aikit-prompt-input__command_disabled {
81
+ opacity: 0.5;
82
+ cursor: default;
83
+ }
84
+ .g-aikit-prompt-input__command-description {
85
+ color: var(--g-color-text-secondary);
86
+ font-size: var(--g-text-body-1-font-size);
50
87
  }
@@ -1,14 +1,25 @@
1
- import { ChatStatus, TSubmitData } from "../../../types/index.js";
1
+ import { type Ref } from 'react';
2
+ import { ChatStatus, type PromptCommandsConfig, TSubmitData } from "../../../types/index.js";
2
3
  import { PromptInputBodyConfig, PromptInputFooterConfig, PromptInputHeaderConfig, PromptInputPanelConfig, PromptInputSuggestionsConfig } from "./types.js";
3
4
  import "./PromptInput.css";
4
5
  /**
5
6
  * Props for the PromptInput component
6
7
  */
8
+ export type PromptInputHandle = {
9
+ submit: (data?: TSubmitData) => Promise<void>;
10
+ clearFailedDraft: () => boolean;
11
+ };
7
12
  export type PromptInputProps = {
13
+ /** Programmatic submission uses the same draft lifecycle as the send button. */
14
+ apiRef?: Ref<PromptInputHandle>;
15
+ /** Application-defined commands and context sources. */
16
+ commands?: PromptCommandsConfig;
17
+ /** Resets uncontrolled command selections and search without changing focus. */
18
+ commandsResetKey?: string | number;
8
19
  /** View variant: 'full' with all features, 'simple' with minimal UI */
9
20
  view?: 'full' | 'simple';
10
21
  /** Callback when message is sent */
11
- onSend: (data: TSubmitData) => Promise<void>;
22
+ onSend: (data: TSubmitData) => Promise<void | false>;
12
23
  /** Callback when sending is cancelled */
13
24
  onCancel?: () => Promise<void>;
14
25
  /** Initial value */
@@ -1,8 +1,13 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useImperativeHandle, useRef } from 'react';
3
+ import { useForkRef } from '@gravity-ui/uikit';
4
+ import { PromptInputCommands } from "./PromptInputCommands.js";
2
5
  import { PromptInputFull } from "./PromptInputFull.js";
3
6
  import { PromptInputSimple } from "./PromptInputSimple.js";
4
7
  import { PromptInputWithPanels } from "./PromptInputWithPanels.js";
5
8
  import { PromptInputWithSuggestions } from "./PromptInputWithSuggestions.js";
9
+ import { i18n } from "./i18n/index.js";
10
+ import { usePromptCommands } from "./usePromptCommands.js";
6
11
  import { usePromptInput } from "./usePromptInput.js";
7
12
  import "./PromptInput.css";
8
13
  /**
@@ -13,9 +18,23 @@ import "./PromptInput.css";
13
18
  * @returns React component
14
19
  */
15
20
  export function PromptInput(props) {
16
- const { view = 'simple', onSend, onCancel, initialValue, disabled = false, status = 'ready', maxLength, onValueChange, headerProps, bodyProps, footerProps, suggestionsProps, topPanel, bottomPanel, className, qa, } = props;
21
+ var _a;
22
+ const { apiRef, commands: commandsConfig, commandsResetKey, view = 'simple', onSend, onCancel, initialValue, disabled = false, status = 'ready', maxLength, onValueChange, headerProps, bodyProps, footerProps, suggestionsProps, topPanel, bottomPanel, className, qa, } = props;
23
+ const inputRef = useRef(null);
24
+ const mergedInputRef = useForkRef(inputRef, bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.inputRef);
25
+ const commandsRef = useRef(null);
26
+ const submittedSelections = useRef([]);
17
27
  const hookState = usePromptInput({
18
- onSend,
28
+ resetKey: commandsResetKey,
29
+ captureSubmission: () => {
30
+ var _a, _b;
31
+ const snapshot = (_a = commandsRef.current) === null || _a === void 0 ? void 0 : _a.captureSubmission();
32
+ submittedSelections.current = (_b = snapshot === null || snapshot === void 0 ? void 0 : snapshot.selections) !== null && _b !== void 0 ? _b : [];
33
+ return snapshot !== null && snapshot !== void 0 ? snapshot : { restore: () => { }, clear: () => { }, hasChanged: () => false };
34
+ },
35
+ onSend: (data) => onSend(Object.assign(Object.assign({}, data), (submittedSelections.current.length && {
36
+ selections: [...submittedSelections.current],
37
+ }))),
19
38
  onCancel,
20
39
  initialValue,
21
40
  disabled,
@@ -23,6 +42,48 @@ export function PromptInput(props) {
23
42
  maxLength,
24
43
  onValueChange,
25
44
  });
45
+ useImperativeHandle(apiRef, () => ({
46
+ submit: hookState.submit,
47
+ clearFailedDraft: hookState.clearFailedDraft,
48
+ }));
49
+ const commandsDisabled = disabled || hookState.isInputDisabled || Boolean(bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.disabledInput);
50
+ const commands = usePromptCommands({
51
+ config: commandsConfig,
52
+ resetKey: commandsResetKey,
53
+ value: hookState.value,
54
+ onChange: hookState.handleChange,
55
+ inputRef,
56
+ disabled: commandsDisabled,
57
+ maxLength,
58
+ });
59
+ commandsRef.current = commands;
60
+ const inputBodyProps = Object.assign(Object.assign({}, bodyProps), { inputRef: mergedInputRef, controlProps: Object.assign(Object.assign({}, bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.controlProps), (commandsConfig && {
61
+ role: 'combobox',
62
+ 'aria-expanded': commands.open && ((_a = commands.session) === null || _a === void 0 ? void 0 : _a.origin) === 'input',
63
+ 'aria-controls': commands.open ? commands.listId : undefined,
64
+ 'aria-autocomplete': 'list',
65
+ 'aria-activedescendant': commands.open && commands.items[commands.activeIndex]
66
+ ? `${commands.listId}-${commands.activeIndex}`
67
+ : undefined,
68
+ onSelect: (event) => {
69
+ var _a, _b;
70
+ (_b = (_a = bodyProps === null || bodyProps === void 0 ? void 0 : bodyProps.controlProps) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a, event);
71
+ const input = event.currentTarget;
72
+ commands.detect(input.value, input.selectionStart, input.selectionEnd);
73
+ },
74
+ })) });
75
+ const commandHookState = Object.assign(Object.assign({}, hookState), { isInputDisabled: commandsDisabled, handleChange: (value) => {
76
+ if (maxLength !== undefined && value.length > maxLength)
77
+ return;
78
+ hookState.handleChange(value);
79
+ const input = inputRef.current;
80
+ if (input)
81
+ commands.detect(value, input.selectionStart, input.selectionEnd, true);
82
+ }, handleKeyDown: (event) => {
83
+ if (!commands.handleKeyDown(event))
84
+ hookState.handleKeyDown(event);
85
+ } });
86
+ const commandsContent = (_jsxs(_Fragment, { children: [commandsConfig && (_jsx(PromptInputCommands, { commands: commands, config: commandsConfig, inputRef: inputRef, disabled: commandsDisabled })), hookState.sendError && _jsx("div", { role: "alert", children: i18n('send-error') })] }));
26
87
  // Handle suggestion click with custom handler or default to input change
27
88
  const handleSuggestionClick = (content, id, data) => {
28
89
  if (suggestionsProps === null || suggestionsProps === void 0 ? void 0 : suggestionsProps.onSuggestionClick) {
@@ -36,6 +97,6 @@ export function PromptInput(props) {
36
97
  // Prepare suggestions props with wrapped handler
37
98
  const wrappedSuggestionsProps = suggestionsProps
38
99
  ? Object.assign(Object.assign({}, suggestionsProps), { onSuggestionClick: handleSuggestionClick }) : undefined;
39
- return (_jsx(PromptInputWithSuggestions, { suggestionsProps: wrappedSuggestionsProps, children: _jsx(PromptInputWithPanels, { topPanel: topPanel, bottomPanel: bottomPanel, children: isFullView ? (_jsx(PromptInputFull, { hookState: hookState, headerProps: headerProps, bodyProps: bodyProps, footerProps: footerProps, className: className, qa: qa })) : (_jsx(PromptInputSimple, { hookState: hookState, bodyProps: bodyProps, footerProps: footerProps, className: className, qa: qa })) }) }));
100
+ return (_jsx(PromptInputWithSuggestions, { suggestionsProps: wrappedSuggestionsProps, children: _jsx(PromptInputWithPanels, { topPanel: topPanel, bottomPanel: bottomPanel, children: isFullView ? (_jsx(PromptInputFull, { hookState: commandHookState, commandsContent: commandsContent, headerProps: headerProps, bodyProps: inputBodyProps, footerProps: footerProps, className: className, qa: qa })) : (_jsx(PromptInputSimple, { hookState: commandHookState, commandsContent: commandsContent, bodyProps: inputBodyProps, footerProps: footerProps, className: className, qa: qa })) }) }));
40
101
  }
41
102
  //# sourceMappingURL=PromptInput.js.map