@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
@@ -0,0 +1,13 @@
1
+ export declare const i18n: ((key: "error" | "loading" | "retry" | "search" | "empty" | "send-error", params?: import("@gravity-ui/i18n").Params) => string) & {
2
+ Translation: import("react").ComponentType<{
3
+ children: (props: {
4
+ t: (key: "error" | "loading" | "retry" | "search" | "empty" | "send-error", params?: import("@gravity-ui/i18n").Params) => string;
5
+ }) => React.ReactNode;
6
+ }>;
7
+ useTranslation: () => {
8
+ t: (key: "error" | "loading" | "retry" | "search" | "empty" | "send-error", params?: import("@gravity-ui/i18n").Params) => string;
9
+ };
10
+ keysetData: {
11
+ "g-aikit-PromptInput": Record<"error" | "loading" | "retry" | "search" | "empty" | "send-error", import("@gravity-ui/i18n").KeyData>;
12
+ };
13
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.i18n = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const i18n_1 = require("@gravity-ui/uikit/i18n");
6
+ const cn_1 = require("../../../../utils/cn.js");
7
+ const en_json_1 = tslib_1.__importDefault(require("./en.json"));
8
+ const ru_json_1 = tslib_1.__importDefault(require("./ru.json"));
9
+ exports.i18n = (0, i18n_1.addComponentKeysets)({ en: en_json_1.default, ru: ru_json_1.default }, `${cn_1.NAMESPACE}PromptInput`);
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../../../../src","sources":["components/organisms/PromptInput/i18n/index.ts"],"names":[],"mappings":";;;;AAAA,iDAA2D;AAE3D,gDAA+C;AAE/C,gEAA2B;AAC3B,gEAA2B;AACd,QAAA,IAAI,GAAG,IAAA,0BAAmB,EAAC,EAAC,EAAE,EAAF,iBAAE,EAAE,EAAE,EAAF,iBAAE,EAAC,EAAE,GAAG,cAAS,aAAa,CAAC,CAAC","sourcesContent":["import {addComponentKeysets} from '@gravity-ui/uikit/i18n';\n\nimport {NAMESPACE} from '../../../../utils/cn';\n\nimport en from './en.json';\nimport ru from './ru.json';\nexport const i18n = addComponentKeysets({en, ru}, `${NAMESPACE}PromptInput`);\n"]}
@@ -0,0 +1,8 @@
1
+ {
2
+ "loading": "Загрузка…",
3
+ "empty": "Ничего не найдено",
4
+ "error": "Не удалось выбрать или загрузить элементы. Попробуйте ещё раз.",
5
+ "retry": "Повторить",
6
+ "search": "Поиск",
7
+ "send-error": "Не удалось отправить сообщение. Черновик сохранён."
8
+ }
@@ -1,4 +1,4 @@
1
- export { PromptInput, type PromptInputProps } from "./PromptInput.js";
1
+ export { PromptInput, type PromptInputProps, type PromptInputHandle } from "./PromptInput.js";
2
2
  export { PromptInputFull, type PromptInputFullProps } from "./PromptInputFull.js";
3
3
  export { PromptInputSimple, type PromptInputSimpleProps } from "./PromptInputSimple.js";
4
4
  export { PromptInputWithPanels, type PromptInputWithPanelsProps } from "./PromptInputWithPanels.js";
@@ -6,3 +6,4 @@ export { PromptInputWithSuggestions, type PromptInputWithSuggestionsProps, } fro
6
6
  export { type PromptInputBodyConfig, type PromptInputFooterConfig, type PromptInputHeaderConfig, type PromptInputPanelConfig, type PromptInputSuggestionsConfig, } from "./types.js";
7
7
  export { usePromptInput, type UsePromptInputProps, type UsePromptInputReturn, } from "./usePromptInput.js";
8
8
  export { useDelayedUnmount } from "./useDelayedUnmount.js";
9
+ export type { PromptCommandsConfig, PromptCommandSource, PromptCommandItem, PromptSelection, } from "../../../types/promptCommands.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/index.ts"],"names":[],"mappings":";;;AAAA,gDAAiE;AAAzD,0GAAA,WAAW,OAAA;AACnB,wDAA6E;AAArE,kHAAA,eAAe,OAAA;AACvB,4DAAmF;AAA3E,sHAAA,iBAAiB,OAAA;AACzB,oEAA+F;AAAvF,8HAAA,qBAAqB,OAAA;AAC7B,8EAGsC;AAFlC,wIAAA,0BAA0B,OAAA;AAU9B,sDAI0B;AAHtB,gHAAA,cAAc,OAAA;AAIlB,4DAAsD;AAA9C,sHAAA,iBAAiB,OAAA","sourcesContent":["export {PromptInput, type PromptInputProps} from './PromptInput';\nexport {PromptInputFull, type PromptInputFullProps} from './PromptInputFull';\nexport {PromptInputSimple, type PromptInputSimpleProps} from './PromptInputSimple';\nexport {PromptInputWithPanels, type PromptInputWithPanelsProps} from './PromptInputWithPanels';\nexport {\n PromptInputWithSuggestions,\n type PromptInputWithSuggestionsProps,\n} from './PromptInputWithSuggestions';\nexport {\n type PromptInputBodyConfig,\n type PromptInputFooterConfig,\n type PromptInputHeaderConfig,\n type PromptInputPanelConfig,\n type PromptInputSuggestionsConfig,\n} from './types';\nexport {\n usePromptInput,\n type UsePromptInputProps,\n type UsePromptInputReturn,\n} from './usePromptInput';\nexport {useDelayedUnmount} from './useDelayedUnmount';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/index.ts"],"names":[],"mappings":";;;AAAA,gDAAyF;AAAjF,0GAAA,WAAW,OAAA;AACnB,wDAA6E;AAArE,kHAAA,eAAe,OAAA;AACvB,4DAAmF;AAA3E,sHAAA,iBAAiB,OAAA;AACzB,oEAA+F;AAAvF,8HAAA,qBAAqB,OAAA;AAC7B,8EAGsC;AAFlC,wIAAA,0BAA0B,OAAA;AAU9B,sDAI0B;AAHtB,gHAAA,cAAc,OAAA;AAIlB,4DAAsD;AAA9C,sHAAA,iBAAiB,OAAA","sourcesContent":["export {PromptInput, type PromptInputProps, type PromptInputHandle} from './PromptInput';\nexport {PromptInputFull, type PromptInputFullProps} from './PromptInputFull';\nexport {PromptInputSimple, type PromptInputSimpleProps} from './PromptInputSimple';\nexport {PromptInputWithPanels, type PromptInputWithPanelsProps} from './PromptInputWithPanels';\nexport {\n PromptInputWithSuggestions,\n type PromptInputWithSuggestionsProps,\n} from './PromptInputWithSuggestions';\nexport {\n type PromptInputBodyConfig,\n type PromptInputFooterConfig,\n type PromptInputHeaderConfig,\n type PromptInputPanelConfig,\n type PromptInputSuggestionsConfig,\n} from './types';\nexport {\n usePromptInput,\n type UsePromptInputProps,\n type UsePromptInputReturn,\n} from './usePromptInput';\nexport {useDelayedUnmount} from './useDelayedUnmount';\n\nexport type {\n PromptCommandsConfig,\n PromptCommandSource,\n PromptCommandItem,\n PromptSelection,\n} from '../../../types/promptCommands';\n"]}
@@ -44,6 +44,7 @@ export type PromptInputHeaderConfig = {
44
44
  * Props for the body/textarea section of PromptInput
45
45
  */
46
46
  export type PromptInputBodyConfig = {
47
+ controlProps?: TextAreaProps['controlProps'];
47
48
  /** QA/test identifier for body wrapper */
48
49
  qa?: string;
49
50
  /** Ref to the textarea input */
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/types.ts"],"names":[],"mappings":"","sourcesContent":["import {ReactNode, type Ref} from 'react';\n\nimport type {ButtonButtonProps, TextAreaProps} from '@gravity-ui/uikit';\n\nimport type {SuggestionClickHandler, SuggestionsItem} from '../../../types/common';\nimport type {SubmitButtonProps} from '../../atoms/SubmitButton';\nimport {PromptInputHeaderProps} from '../../molecules/PromptInputHeader';\nimport type {SuggestionsProps} from '../../molecules/Suggestions';\n\n/**\n * Props for a panel (top or bottom)\n */\nexport type PromptInputPanelConfig = {\n /** Is panel open */\n isOpen: boolean;\n /** Panel content */\n children?: ReactNode;\n};\n\n/**\n * Props for the header section of PromptInput.\n * Custom content (`topContent`) and default header props are mutually exclusive:\n * `topContent` replaces the default header entirely.\n */\nexport type PromptInputHeaderConfig = {\n /** QA/test identifier for header wrapper */\n qa?: string;\n} & (\n | {\n /** Custom content for header area (replaces the default header) */\n topContent?: ReactNode;\n /** Not allowed together with custom content */\n contextItems?: never;\n /** Not allowed together with custom content */\n showContextIndicator?: never;\n /** Not allowed together with custom content */\n contextIndicatorProps?: never;\n }\n | {\n /** Not allowed together with default header props */\n topContent?: never;\n /** Array of context items to display on the left */\n contextItems?: PromptInputHeaderProps['contextItems'];\n /** Show context indicator in header */\n showContextIndicator?: boolean;\n /** Props for context indicator */\n contextIndicatorProps?: PromptInputHeaderProps['contextIndicatorProps'];\n }\n);\n\n/**\n * Props for the body/textarea section of PromptInput\n */\nexport type PromptInputBodyConfig = {\n /** QA/test identifier for body wrapper */\n qa?: string;\n /** Ref to the textarea input */\n inputRef?: Ref<HTMLTextAreaElement>;\n /** Placeholder text for textarea */\n placeholder?: string;\n /** Size of the textarea. Defaults to `l`, or `xl` in mobile mode */\n size?: TextAreaProps['size'];\n /** Minimum number of textarea rows */\n minRows?: number;\n /** Maximum number of textarea rows */\n maxRows?: number;\n /** Auto focus textarea on mount */\n autoFocus?: boolean;\n /** Disabled state for textarea */\n disabledInput?: boolean;\n /**\n * Auto focus textarea when a new chat is opened via the plus icon.\n * Only applies inside ChatContainer. Defaults to false.\n */\n autoFocusOnNewChat?: boolean;\n /**\n * Auto focus textarea when a chat is selected from history.\n * Only applies inside ChatContainer. Defaults to false.\n */\n autoFocusOnChatSelect?: boolean;\n};\n\n/**\n * Props for the footer section of PromptInput\n */\nexport type PromptInputFooterConfig = {\n /** QA/test identifier for footer wrapper */\n qa?: string;\n /** Additional CSS class for the footer wrapper */\n className?: string;\n /** Additional CSS class for the custom content wrapper */\n contentClassName?: string;\n /** Size of footer action buttons */\n buttonSize?: ButtonButtonProps['size'];\n /** Props for the submit button. State, click handling, and size are managed by PromptInput. */\n submitButtonProps?: Omit<SubmitButtonProps, 'state' | 'onClick' | 'size'>;\n /** Custom content for footer area (SubmitButton will still be shown) */\n bottomContent?: ReactNode;\n /** Show settings icon in footer */\n showSettings?: boolean;\n /** Settings icon click handler */\n onSettingsClick?: () => void;\n /** Show attachment icon in footer */\n showAttachment?: boolean;\n /** Attachment icon click handler */\n onAttachmentClick?: () => void;\n /**\n * Replaces the built-in attachment icon button with a custom node (e.g. AttachmentPicker).\n * When set, `showAttachment` and `onAttachmentClick` are ignored.\n */\n attachmentContent?: ReactNode;\n /** Show microphone icon in footer */\n showMicrophone?: boolean;\n /** Microphone icon click handler */\n onMicrophoneClick?: () => void;\n /** Custom tooltip for submit button in enabled state */\n submitButtonTooltipSend?: string;\n /** Custom tooltip for submit button in cancelable state */\n submitButtonTooltipCancel?: string;\n /** Custom cancelable text (if provided, will be shown in cancelable state) */\n submitButtonCancelableText?: string;\n /** QA/test identifier for submit button */\n submitButtonQa?: string;\n};\n\n/**\n * Props for the suggestions section of PromptInput\n */\nexport type PromptInputSuggestionsConfig = {\n /** Submit suggestions array */\n suggestions?: SuggestionsItem[];\n /** Show submit suggestions */\n showSuggestions?: boolean;\n /** Title for the suggestions section - can be string or custom React element */\n suggestTitle?: string | ReactNode;\n /** Layout orientation for suggestions: 'grid' for horizontal, 'list' for vertical */\n suggestionsLayout?: SuggestionsProps['layout'];\n /** Text alignment inside suggestion buttons */\n suggestionsTextAlign?: SuggestionsProps['textAlign'];\n /** Callback when suggestion is clicked */\n onSuggestionClick?: SuggestionClickHandler;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/types.ts"],"names":[],"mappings":"","sourcesContent":["import {ReactNode, type Ref} from 'react';\n\nimport type {ButtonButtonProps, TextAreaProps} from '@gravity-ui/uikit';\n\nimport type {SuggestionClickHandler, SuggestionsItem} from '../../../types/common';\nimport type {SubmitButtonProps} from '../../atoms/SubmitButton';\nimport {PromptInputHeaderProps} from '../../molecules/PromptInputHeader';\nimport type {SuggestionsProps} from '../../molecules/Suggestions';\n\n/**\n * Props for a panel (top or bottom)\n */\nexport type PromptInputPanelConfig = {\n /** Is panel open */\n isOpen: boolean;\n /** Panel content */\n children?: ReactNode;\n};\n\n/**\n * Props for the header section of PromptInput.\n * Custom content (`topContent`) and default header props are mutually exclusive:\n * `topContent` replaces the default header entirely.\n */\nexport type PromptInputHeaderConfig = {\n /** QA/test identifier for header wrapper */\n qa?: string;\n} & (\n | {\n /** Custom content for header area (replaces the default header) */\n topContent?: ReactNode;\n /** Not allowed together with custom content */\n contextItems?: never;\n /** Not allowed together with custom content */\n showContextIndicator?: never;\n /** Not allowed together with custom content */\n contextIndicatorProps?: never;\n }\n | {\n /** Not allowed together with default header props */\n topContent?: never;\n /** Array of context items to display on the left */\n contextItems?: PromptInputHeaderProps['contextItems'];\n /** Show context indicator in header */\n showContextIndicator?: boolean;\n /** Props for context indicator */\n contextIndicatorProps?: PromptInputHeaderProps['contextIndicatorProps'];\n }\n);\n\n/**\n * Props for the body/textarea section of PromptInput\n */\nexport type PromptInputBodyConfig = {\n controlProps?: TextAreaProps['controlProps'];\n /** QA/test identifier for body wrapper */\n qa?: string;\n /** Ref to the textarea input */\n inputRef?: Ref<HTMLTextAreaElement>;\n /** Placeholder text for textarea */\n placeholder?: string;\n /** Size of the textarea. Defaults to `l`, or `xl` in mobile mode */\n size?: TextAreaProps['size'];\n /** Minimum number of textarea rows */\n minRows?: number;\n /** Maximum number of textarea rows */\n maxRows?: number;\n /** Auto focus textarea on mount */\n autoFocus?: boolean;\n /** Disabled state for textarea */\n disabledInput?: boolean;\n /**\n * Auto focus textarea when a new chat is opened via the plus icon.\n * Only applies inside ChatContainer. Defaults to false.\n */\n autoFocusOnNewChat?: boolean;\n /**\n * Auto focus textarea when a chat is selected from history.\n * Only applies inside ChatContainer. Defaults to false.\n */\n autoFocusOnChatSelect?: boolean;\n};\n\n/**\n * Props for the footer section of PromptInput\n */\nexport type PromptInputFooterConfig = {\n /** QA/test identifier for footer wrapper */\n qa?: string;\n /** Additional CSS class for the footer wrapper */\n className?: string;\n /** Additional CSS class for the custom content wrapper */\n contentClassName?: string;\n /** Size of footer action buttons */\n buttonSize?: ButtonButtonProps['size'];\n /** Props for the submit button. State, click handling, and size are managed by PromptInput. */\n submitButtonProps?: Omit<SubmitButtonProps, 'state' | 'onClick' | 'size'>;\n /** Custom content for footer area (SubmitButton will still be shown) */\n bottomContent?: ReactNode;\n /** Show settings icon in footer */\n showSettings?: boolean;\n /** Settings icon click handler */\n onSettingsClick?: () => void;\n /** Show attachment icon in footer */\n showAttachment?: boolean;\n /** Attachment icon click handler */\n onAttachmentClick?: () => void;\n /**\n * Replaces the built-in attachment icon button with a custom node (e.g. AttachmentPicker).\n * When set, `showAttachment` and `onAttachmentClick` are ignored.\n */\n attachmentContent?: ReactNode;\n /** Show microphone icon in footer */\n showMicrophone?: boolean;\n /** Microphone icon click handler */\n onMicrophoneClick?: () => void;\n /** Custom tooltip for submit button in enabled state */\n submitButtonTooltipSend?: string;\n /** Custom tooltip for submit button in cancelable state */\n submitButtonTooltipCancel?: string;\n /** Custom cancelable text (if provided, will be shown in cancelable state) */\n submitButtonCancelableText?: string;\n /** QA/test identifier for submit button */\n submitButtonQa?: string;\n};\n\n/**\n * Props for the suggestions section of PromptInput\n */\nexport type PromptInputSuggestionsConfig = {\n /** Submit suggestions array */\n suggestions?: SuggestionsItem[];\n /** Show submit suggestions */\n showSuggestions?: boolean;\n /** Title for the suggestions section - can be string or custom React element */\n suggestTitle?: string | ReactNode;\n /** Layout orientation for suggestions: 'grid' for horizontal, 'list' for vertical */\n suggestionsLayout?: SuggestionsProps['layout'];\n /** Text alignment inside suggestion buttons */\n suggestionsTextAlign?: SuggestionsProps['textAlign'];\n /** Callback when suggestion is clicked */\n onSuggestionClick?: SuggestionClickHandler;\n};\n"]}
@@ -0,0 +1,41 @@
1
+ import type { KeyboardEvent, RefObject } from 'react';
2
+ import type { PromptCommandItem, PromptCommandSource, PromptCommandsConfig, PromptSelection } from "../../../types/index.js";
3
+ import { type CommandMatch } from "./commandUtils.js";
4
+ type Session = CommandMatch & {
5
+ origin: 'input' | 'button';
6
+ };
7
+ export declare function usePromptCommands({ config, resetKey, value, onChange, inputRef, disabled, maxLength, }: {
8
+ config?: PromptCommandsConfig;
9
+ resetKey?: string | number;
10
+ value: string;
11
+ onChange: (value: string) => void;
12
+ inputRef: RefObject<HTMLTextAreaElement>;
13
+ disabled: boolean;
14
+ maxLength?: number;
15
+ }): {
16
+ selections: readonly PromptSelection[];
17
+ clearSubmitted: (submitted: readonly PromptSelection[]) => void;
18
+ captureSubmission: () => {
19
+ selections: PromptSelection[];
20
+ hasChanged: () => boolean;
21
+ restore: () => void;
22
+ clear: () => void;
23
+ };
24
+ open: boolean;
25
+ source: PromptCommandSource | undefined;
26
+ session: Session | undefined;
27
+ items: readonly PromptCommandItem[];
28
+ loading: boolean;
29
+ error: boolean | undefined;
30
+ listId: string;
31
+ activeIndex: number;
32
+ select: (item: PromptCommandItem) => void;
33
+ close: () => void;
34
+ handleKeyDown: (event: KeyboardEvent<HTMLElement>) => boolean;
35
+ detect: (text: string, start: number, end: number, edited?: boolean) => void;
36
+ remove: (selection: PromptSelection) => void;
37
+ openSource: (sourceId: string) => void;
38
+ search: (query: string) => void;
39
+ retry: () => void;
40
+ };
41
+ export {};
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePromptCommands = usePromptCommands;
4
+ const react_1 = require("react");
5
+ const commandUtils_1 = require("./commandUtils.js");
6
+ const EMPTY_SELECTIONS = [];
7
+ function usePromptCommands({ config, resetKey, value, onChange, inputRef, disabled, maxLength, }) {
8
+ var _a;
9
+ const [internalSelections, setInternalSelections] = (0, react_1.useState)([]);
10
+ const selections = config ? ((_a = config.selections) !== null && _a !== void 0 ? _a : internalSelections) : EMPTY_SELECTIONS;
11
+ const [session, setSession] = (0, react_1.useState)();
12
+ const [result, setResult] = (0, react_1.useState)();
13
+ const [activeIndex, setActiveIndex] = (0, react_1.useState)(0);
14
+ const [selectionError, setSelectionError] = (0, react_1.useState)(false);
15
+ const suppressed = (0, react_1.useRef)();
16
+ const selectionRevision = (0, react_1.useRef)(0);
17
+ const previousResetKey = (0, react_1.useRef)(resetKey);
18
+ (0, react_1.useEffect)(() => {
19
+ if (previousResetKey.current === resetKey)
20
+ return;
21
+ previousResetKey.current = resetKey;
22
+ selectionRevision.current++;
23
+ setInternalSelections([]);
24
+ setSession(undefined);
25
+ setResult(undefined);
26
+ setSelectionError(false);
27
+ suppressed.current = undefined;
28
+ }, [resetKey]);
29
+ const listId = (0, react_1.useId)();
30
+ const source = config === null || config === void 0 ? void 0 : config.sources.find((item) => item.id === (session === null || session === void 0 ? void 0 : session.sourceId));
31
+ const open = Boolean(session && source && !disabled);
32
+ const items = result && result.source === source && result.session === session ? result.items : [];
33
+ const loading = open && ((result === null || result === void 0 ? void 0 : result.session) !== session || (result === null || result === void 0 ? void 0 : result.source) !== source);
34
+ const error = selectionError ||
35
+ (result && result.source === source && result.session === session && result.error);
36
+ const mounted = (0, react_1.useRef)(true);
37
+ (0, react_1.useEffect)(() => {
38
+ mounted.current = true;
39
+ return () => {
40
+ mounted.current = false;
41
+ };
42
+ }, []);
43
+ const latest = (0, react_1.useRef)({ selections, config });
44
+ if (latest.current.selections.length !== selections.length ||
45
+ latest.current.selections.some((item, index) => item !== selections[index])) {
46
+ selectionRevision.current++;
47
+ }
48
+ latest.current = { selections, config };
49
+ const updateSelections = (next) => {
50
+ var _a, _b;
51
+ if (!mounted.current)
52
+ return;
53
+ selectionRevision.current++;
54
+ latest.current.selections = next;
55
+ setInternalSelections(next);
56
+ (_b = (_a = latest.current.config) === null || _a === void 0 ? void 0 : _a.onSelectionsChange) === null || _b === void 0 ? void 0 : _b.call(_a, next);
57
+ };
58
+ const clearSubmitted = (submitted) => {
59
+ updateSelections(latest.current.selections.filter((item) => !submitted.includes(item)));
60
+ };
61
+ const close = () => {
62
+ suppressed.current = value;
63
+ setSession(undefined);
64
+ };
65
+ const detect = (text, start, end, edited = false) => {
66
+ if (disabled || !config || (!edited && suppressed.current !== undefined))
67
+ return;
68
+ if (edited)
69
+ suppressed.current = undefined;
70
+ const match = (0, commandUtils_1.findCommandMatch)(text, start, end, config.sources);
71
+ setSession((previous) => {
72
+ if (!match)
73
+ return undefined;
74
+ if ((previous === null || previous === void 0 ? void 0 : previous.origin) === 'input' &&
75
+ previous.sourceId === match.sourceId &&
76
+ previous.query === match.query &&
77
+ previous.start === match.start &&
78
+ previous.end === match.end)
79
+ return previous;
80
+ return Object.assign(Object.assign({}, match), { origin: 'input' });
81
+ });
82
+ };
83
+ (0, react_1.useEffect)(() => {
84
+ if (disabled)
85
+ setSession(undefined);
86
+ }, [disabled]);
87
+ (0, react_1.useEffect)(() => {
88
+ if (!session || !source || !open)
89
+ return undefined;
90
+ const controller = new AbortController();
91
+ setActiveIndex(0);
92
+ setSelectionError(false);
93
+ const load = async () => {
94
+ var _a;
95
+ try {
96
+ const matches = source.getItems
97
+ ? await source.getItems(session.query, { signal: controller.signal })
98
+ : ((_a = source.items) !== null && _a !== void 0 ? _a : []).filter((item) => {
99
+ var _a;
100
+ return [item.title, item.description, ...((_a = item.keywords) !== null && _a !== void 0 ? _a : [])].some((text) => text === null || text === void 0 ? void 0 : text.toLocaleLowerCase().includes(session.query.toLocaleLowerCase()));
101
+ });
102
+ if (!controller.signal.aborted) {
103
+ setResult({ session, source, items: matches });
104
+ setActiveIndex(Math.max(0, matches.findIndex((item) => !item.disabled)));
105
+ }
106
+ }
107
+ catch (_b) {
108
+ if (!controller.signal.aborted)
109
+ setResult({ session, source, items: [], error: true });
110
+ }
111
+ };
112
+ load();
113
+ return () => controller.abort();
114
+ }, [session, source, open]);
115
+ const select = (item) => {
116
+ var _a;
117
+ if (!session || !source || item.disabled || disabled)
118
+ return;
119
+ const replacement = session.origin === 'button' && item.insertText === undefined
120
+ ? { value, caret: session.start }
121
+ : (0, commandUtils_1.replaceCommand)(value, session, item.insertText);
122
+ if (maxLength !== undefined && replacement.value.length > maxLength) {
123
+ setSelectionError(true);
124
+ return;
125
+ }
126
+ try {
127
+ (_a = source.onSelect) === null || _a === void 0 ? void 0 : _a.call(source, item, { query: session.query, origin: session.origin });
128
+ }
129
+ catch (_b) {
130
+ setSelectionError(true);
131
+ return;
132
+ }
133
+ if (item.keepSelected !== false) {
134
+ const selection = Object.assign({ sourceId: source.id, id: item.id, title: item.title }, (item.data && { data: item.data }));
135
+ updateSelections([
136
+ ...selections.filter((entry) => entry.sourceId !== source.id || entry.id !== item.id),
137
+ selection,
138
+ ]);
139
+ }
140
+ onChange(replacement.value);
141
+ suppressed.current = replacement.value;
142
+ setSession(undefined);
143
+ requestAnimationFrame(() => {
144
+ const input = inputRef.current;
145
+ if (!(input === null || input === void 0 ? void 0 : input.isConnected))
146
+ return;
147
+ input.focus();
148
+ input.setSelectionRange(replacement.caret, replacement.caret);
149
+ });
150
+ };
151
+ const handleKeyDown = (event) => {
152
+ if (!open || event.nativeEvent.isComposing)
153
+ return false;
154
+ if (event.key === 'Escape' || event.key === 'Tab') {
155
+ if (event.key === 'Escape')
156
+ event.preventDefault();
157
+ close();
158
+ return true;
159
+ }
160
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
161
+ event.preventDefault();
162
+ const direction = event.key === 'ArrowDown' ? 1 : -1;
163
+ for (let step = 1; step <= items.length; step++) {
164
+ const index = (activeIndex + direction * step + items.length) % items.length;
165
+ if (!items[index].disabled) {
166
+ setActiveIndex(index);
167
+ break;
168
+ }
169
+ }
170
+ return true;
171
+ }
172
+ if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey) {
173
+ event.preventDefault();
174
+ if (items[activeIndex])
175
+ select(items[activeIndex]);
176
+ return true;
177
+ }
178
+ return false;
179
+ };
180
+ return {
181
+ selections,
182
+ clearSubmitted,
183
+ captureSubmission: () => {
184
+ const submitted = [...latest.current.selections];
185
+ clearSubmitted(submitted);
186
+ let expectedRevision = selectionRevision.current;
187
+ return {
188
+ selections: submitted,
189
+ hasChanged: () => selectionRevision.current !== expectedRevision,
190
+ restore: () => {
191
+ updateSelections(submitted);
192
+ expectedRevision = selectionRevision.current;
193
+ },
194
+ clear: () => clearSubmitted(submitted),
195
+ };
196
+ },
197
+ open,
198
+ source,
199
+ session,
200
+ items,
201
+ loading,
202
+ error,
203
+ listId,
204
+ activeIndex,
205
+ select,
206
+ close,
207
+ handleKeyDown,
208
+ detect,
209
+ remove: (selection) => updateSelections(selections.filter((item) => item !== selection)),
210
+ openSource: (sourceId) => {
211
+ var _a, _b, _c, _d;
212
+ if (disabled)
213
+ return;
214
+ const start = (_b = (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.selectionStart) !== null && _b !== void 0 ? _b : value.length;
215
+ const end = (_d = (_c = inputRef.current) === null || _c === void 0 ? void 0 : _c.selectionEnd) !== null && _d !== void 0 ? _d : start;
216
+ setSession({ sourceId, query: '', start, end, origin: 'button' });
217
+ },
218
+ search: (query) => setSession((previous) => (previous ? Object.assign(Object.assign({}, previous), { query }) : previous)),
219
+ retry: () => setSession((previous) => (previous ? Object.assign({}, previous) : previous)),
220
+ };
221
+ }
222
+ //# sourceMappingURL=usePromptCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePromptCommands.js","sourceRoot":"../../../../../src","sources":["components/organisms/PromptInput/usePromptCommands.ts"],"names":[],"mappings":"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","sourcesContent":["import {useEffect, useId, useRef, useState} from 'react';\nimport type {KeyboardEvent, RefObject} from 'react';\n\nimport type {\n PromptCommandItem,\n PromptCommandSource,\n PromptCommandsConfig,\n PromptSelection,\n} from '../../../types';\n\nimport {type CommandMatch, findCommandMatch, replaceCommand} from './commandUtils';\n\ntype Session = CommandMatch & {origin: 'input' | 'button'};\nconst EMPTY_SELECTIONS: readonly PromptSelection[] = [];\n\nexport function usePromptCommands({\n config,\n resetKey,\n value,\n onChange,\n inputRef,\n disabled,\n maxLength,\n}: {\n config?: PromptCommandsConfig;\n resetKey?: string | number;\n value: string;\n onChange: (value: string) => void;\n inputRef: RefObject<HTMLTextAreaElement>;\n disabled: boolean;\n maxLength?: number;\n}) {\n const [internalSelections, setInternalSelections] = useState<PromptSelection[]>([]);\n const selections = config ? (config.selections ?? internalSelections) : EMPTY_SELECTIONS;\n const [session, setSession] = useState<Session>();\n const [result, setResult] = useState<{\n session: Session;\n source: PromptCommandSource;\n items: readonly PromptCommandItem[];\n error?: boolean;\n }>();\n const [activeIndex, setActiveIndex] = useState(0);\n const [selectionError, setSelectionError] = useState(false);\n const suppressed = useRef<string>();\n const selectionRevision = useRef(0);\n const previousResetKey = useRef(resetKey);\n useEffect(() => {\n if (previousResetKey.current === resetKey) return;\n previousResetKey.current = resetKey;\n selectionRevision.current++;\n setInternalSelections([]);\n setSession(undefined);\n setResult(undefined);\n setSelectionError(false);\n suppressed.current = undefined;\n }, [resetKey]);\n const listId = useId();\n const source = config?.sources.find((item) => item.id === session?.sourceId);\n const open = Boolean(session && source && !disabled);\n const items =\n result && result.source === source && result.session === session ? result.items : [];\n const loading = open && (result?.session !== session || result?.source !== source);\n const error =\n selectionError ||\n (result && result.source === source && result.session === session && result.error);\n const mounted = useRef(true);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n const latest = useRef({selections, config});\n if (\n latest.current.selections.length !== selections.length ||\n latest.current.selections.some((item, index) => item !== selections[index])\n ) {\n selectionRevision.current++;\n }\n latest.current = {selections, config};\n\n const updateSelections = (next: PromptSelection[]) => {\n if (!mounted.current) return;\n selectionRevision.current++;\n latest.current.selections = next;\n setInternalSelections(next);\n latest.current.config?.onSelectionsChange?.(next);\n };\n const clearSubmitted = (submitted: readonly PromptSelection[]) => {\n updateSelections(latest.current.selections.filter((item) => !submitted.includes(item)));\n };\n const close = () => {\n suppressed.current = value;\n setSession(undefined);\n };\n const detect = (text: string, start: number, end: number, edited = false) => {\n if (disabled || !config || (!edited && suppressed.current !== undefined)) return;\n if (edited) suppressed.current = undefined;\n const match = findCommandMatch(text, start, end, config.sources);\n setSession((previous) => {\n if (!match) return undefined;\n if (\n previous?.origin === 'input' &&\n previous.sourceId === match.sourceId &&\n previous.query === match.query &&\n previous.start === match.start &&\n previous.end === match.end\n )\n return previous;\n return {...match, origin: 'input'};\n });\n };\n useEffect(() => {\n if (disabled) setSession(undefined);\n }, [disabled]);\n useEffect(() => {\n if (!session || !source || !open) return undefined;\n const controller = new AbortController();\n setActiveIndex(0);\n setSelectionError(false);\n const load = async () => {\n try {\n const matches = source.getItems\n ? await source.getItems(session.query, {signal: controller.signal})\n : (source.items ?? []).filter((item) =>\n [item.title, item.description, ...(item.keywords ?? [])].some((text) =>\n text?.toLocaleLowerCase().includes(session.query.toLocaleLowerCase()),\n ),\n );\n if (!controller.signal.aborted) {\n setResult({session, source, items: matches});\n setActiveIndex(\n Math.max(\n 0,\n matches.findIndex((item) => !item.disabled),\n ),\n );\n }\n } catch {\n if (!controller.signal.aborted)\n setResult({session, source, items: [], error: true});\n }\n };\n load();\n return () => controller.abort();\n }, [session, source, open]);\n\n const select = (item: PromptCommandItem) => {\n if (!session || !source || item.disabled || disabled) return;\n const replacement =\n session.origin === 'button' && item.insertText === undefined\n ? {value, caret: session.start}\n : replaceCommand(value, session, item.insertText);\n if (maxLength !== undefined && replacement.value.length > maxLength) {\n setSelectionError(true);\n return;\n }\n try {\n source.onSelect?.(item, {query: session.query, origin: session.origin});\n } catch {\n setSelectionError(true);\n return;\n }\n if (item.keepSelected !== false) {\n const selection: PromptSelection = {\n sourceId: source.id,\n id: item.id,\n title: item.title,\n ...(item.data && {data: item.data}),\n };\n updateSelections([\n ...selections.filter(\n (entry) => entry.sourceId !== source.id || entry.id !== item.id,\n ),\n selection,\n ]);\n }\n onChange(replacement.value);\n suppressed.current = replacement.value;\n setSession(undefined);\n requestAnimationFrame(() => {\n const input = inputRef.current;\n if (!input?.isConnected) return;\n input.focus();\n input.setSelectionRange(replacement.caret, replacement.caret);\n });\n };\n const handleKeyDown = (event: KeyboardEvent<HTMLElement>) => {\n if (!open || event.nativeEvent.isComposing) return false;\n if (event.key === 'Escape' || event.key === 'Tab') {\n if (event.key === 'Escape') event.preventDefault();\n close();\n return true;\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n const direction = event.key === 'ArrowDown' ? 1 : -1;\n for (let step = 1; step <= items.length; step++) {\n const index = (activeIndex + direction * step + items.length) % items.length;\n if (!items[index].disabled) {\n setActiveIndex(index);\n break;\n }\n }\n return true;\n }\n if (event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey) {\n event.preventDefault();\n if (items[activeIndex]) select(items[activeIndex]);\n return true;\n }\n return false;\n };\n return {\n selections,\n clearSubmitted,\n captureSubmission: () => {\n const submitted = [...latest.current.selections];\n clearSubmitted(submitted);\n let expectedRevision = selectionRevision.current;\n return {\n selections: submitted,\n hasChanged: () => selectionRevision.current !== expectedRevision,\n restore: () => {\n updateSelections(submitted);\n expectedRevision = selectionRevision.current;\n },\n clear: () => clearSubmitted(submitted),\n };\n },\n open,\n source,\n session,\n items,\n loading,\n error,\n listId,\n activeIndex,\n select,\n close,\n handleKeyDown,\n detect,\n remove: (selection: PromptSelection) =>\n updateSelections(selections.filter((item) => item !== selection)),\n openSource: (sourceId: string) => {\n if (disabled) return;\n const start = inputRef.current?.selectionStart ?? value.length;\n const end = inputRef.current?.selectionEnd ?? start;\n setSession({sourceId, query: '', start, end, origin: 'button'});\n },\n search: (query: string) =>\n setSession((previous) => (previous ? {...previous, query} : previous)),\n retry: () => setSession((previous) => (previous ? {...previous} : previous)),\n };\n}\n"]}
@@ -4,7 +4,13 @@ import { ChatStatus, TSubmitData } from "../../../types/index.js";
4
4
  */
5
5
  export type UsePromptInputProps = {
6
6
  /** Callback when message is sent */
7
- onSend: (data: TSubmitData) => Promise<void>;
7
+ onSend: (data: TSubmitData) => Promise<void | false>;
8
+ resetKey?: string | number;
9
+ captureSubmission?: () => {
10
+ restore: () => void;
11
+ hasChanged: () => boolean;
12
+ clear: () => void;
13
+ };
8
14
  /** Callback when sending is cancelled */
9
15
  onCancel?: () => Promise<void>;
10
16
  /** Initial value */
@@ -22,6 +28,7 @@ export type UsePromptInputProps = {
22
28
  * Hook return type for PromptInput state management
23
29
  */
24
30
  export type UsePromptInputReturn = {
31
+ sendError?: boolean;
25
32
  /** Current input value */
26
33
  value: string;
27
34
  /** Can submit the form */
@@ -36,6 +43,8 @@ export type UsePromptInputReturn = {
36
43
  handleKeyDown: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
37
44
  /** Handle submit */
38
45
  handleSubmit: () => Promise<void>;
46
+ submit: (data?: TSubmitData) => Promise<void>;
47
+ clearFailedDraft: () => boolean;
39
48
  /** Attachments */
40
49
  attachments: File[];
41
50
  /** Set attachments */
@@ -9,14 +9,36 @@ const react_1 = require("react");
9
9
  * @returns Hook return value with state and handlers
10
10
  */
11
11
  function usePromptInput(props) {
12
- const { onSend, onCancel, initialValue = '', maxLength, disabled = false, status = 'ready', onValueChange, } = props;
12
+ const { onSend, onCancel, initialValue = '', maxLength, disabled = false, status = 'ready', onValueChange, resetKey, captureSubmission, } = props;
13
13
  const [value, setValue] = (0, react_1.useState)(initialValue);
14
- const [attachments, setAttachments] = (0, react_1.useState)([]);
14
+ const [sending, setSending] = (0, react_1.useState)(false);
15
+ const [sendError, setSendError] = (0, react_1.useState)(false);
16
+ const mounted = (0, react_1.useRef)(true);
17
+ (0, react_1.useEffect)(() => {
18
+ mounted.current = true;
19
+ return () => {
20
+ mounted.current = false;
21
+ };
22
+ }, []);
23
+ const revision = (0, react_1.useRef)(0);
24
+ const inFlight = (0, react_1.useRef)(false);
25
+ const [attachments, updateAttachments] = (0, react_1.useState)([]);
26
+ const setAttachments = (0, react_1.useCallback)((files) => {
27
+ revision.current++;
28
+ updateAttachments(files);
29
+ }, []);
30
+ const failedDraft = (0, react_1.useRef)(null);
31
+ (0, react_1.useEffect)(() => {
32
+ revision.current++;
33
+ failedDraft.current = null;
34
+ }, [resetKey]);
15
35
  const updateValue = (0, react_1.useCallback)((nextValue) => {
36
+ revision.current++;
37
+ setSendError(false);
16
38
  setValue(nextValue);
17
39
  onValueChange === null || onValueChange === void 0 ? void 0 : onValueChange(nextValue);
18
40
  }, [onValueChange]);
19
- const isSubmitted = status === 'submitted';
41
+ const isSubmitted = status === 'submitted' || sending;
20
42
  const trimmedValue = value.trim();
21
43
  const canSubmit = !disabled && !isSubmitted && trimmedValue.length > 0;
22
44
  // Map ChatStatus to submit button state
@@ -51,6 +73,8 @@ function usePromptInput(props) {
51
73
  }
52
74
  }
53
75
  // Input is disabled when loading
76
+ if (sending && submitButtonState !== 'cancelable')
77
+ submitButtonState = 'loading';
54
78
  const isInputDisabled = submitButtonState === 'loading';
55
79
  const handleChange = (0, react_1.useCallback)((newValue) => {
56
80
  if (maxLength && newValue.length > maxLength) {
@@ -58,20 +82,82 @@ function usePromptInput(props) {
58
82
  }
59
83
  updateValue(newValue);
60
84
  }, [maxLength, updateValue]);
85
+ const submit = (0, react_1.useCallback)(async (override) => {
86
+ var _a;
87
+ const content = (_a = override === null || override === void 0 ? void 0 : override.content.trim()) !== null && _a !== void 0 ? _a : trimmedValue;
88
+ if (disabled || isSubmitted || inFlight.current || !content)
89
+ return;
90
+ const draftValue = value;
91
+ const submitData = Object.assign(Object.assign(Object.assign({}, override), { content }), (attachments.length > 0 && { attachments }));
92
+ inFlight.current = true;
93
+ setSending(true);
94
+ setSendError(false);
95
+ failedDraft.current = null;
96
+ const selectionSnapshot = captureSubmission === null || captureSubmission === void 0 ? void 0 : captureSubmission();
97
+ updateValue('');
98
+ updateAttachments([]);
99
+ const clearedRevision = revision.current;
100
+ const restore = () => {
101
+ if (!mounted.current ||
102
+ revision.current !== clearedRevision ||
103
+ (selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.hasChanged()))
104
+ return;
105
+ updateValue(draftValue);
106
+ updateAttachments(attachments);
107
+ selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.restore();
108
+ const restoredRevision = revision.current;
109
+ failedDraft.current = () => {
110
+ if (!mounted.current ||
111
+ revision.current !== restoredRevision ||
112
+ (selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.hasChanged()))
113
+ return false;
114
+ updateValue('');
115
+ updateAttachments([]);
116
+ selectionSnapshot === null || selectionSnapshot === void 0 ? void 0 : selectionSnapshot.clear();
117
+ return true;
118
+ };
119
+ return true;
120
+ };
121
+ try {
122
+ const result = await onSend(submitData);
123
+ if (result === false)
124
+ restore();
125
+ }
126
+ catch (_b) {
127
+ if (restore())
128
+ setSendError(true);
129
+ }
130
+ finally {
131
+ inFlight.current = false;
132
+ if (mounted.current)
133
+ setSending(false);
134
+ }
135
+ }, [
136
+ disabled,
137
+ isSubmitted,
138
+ trimmedValue,
139
+ value,
140
+ attachments,
141
+ onSend,
142
+ updateValue,
143
+ captureSubmission,
144
+ ]);
145
+ const clearFailedDraft = (0, react_1.useCallback)(() => {
146
+ var _a;
147
+ const clear = failedDraft.current;
148
+ failedDraft.current = null;
149
+ return (_a = clear === null || clear === void 0 ? void 0 : clear()) !== null && _a !== void 0 ? _a : false;
150
+ }, []);
61
151
  const handleSubmit = (0, react_1.useCallback)(async () => {
62
152
  if (submitButtonState === 'cancelable' && onCancel) {
63
153
  await onCancel();
64
154
  return;
65
155
  }
66
- if (!canSubmit) {
67
- return;
68
- }
69
- const submitData = Object.assign({ content: trimmedValue }, (attachments.length > 0 && { attachments }));
70
- onSend(submitData);
71
- updateValue('');
72
- setAttachments([]);
73
- }, [submitButtonState, canSubmit, trimmedValue, attachments, onSend, onCancel, updateValue]);
156
+ await submit();
157
+ }, [submitButtonState, onCancel, submit]);
74
158
  const handleKeyDown = (0, react_1.useCallback)((event) => {
159
+ if (event.nativeEvent.isComposing || event.defaultPrevented)
160
+ return;
75
161
  const isEnter = event.code === 'Enter' || event.code === 'NumpadEnter';
76
162
  // Insert new line on Ctrl/Cmd + Enter
77
163
  if (isEnter && (event.ctrlKey || event.metaKey)) {
@@ -92,12 +178,15 @@ function usePromptInput(props) {
92
178
  }, [canSubmit, handleSubmit, updateValue, value]);
93
179
  return {
94
180
  value,
181
+ sendError,
95
182
  canSubmit,
96
183
  submitButtonState,
97
184
  isInputDisabled,
98
185
  handleChange,
99
186
  handleKeyDown,
100
187
  handleSubmit,
188
+ submit,
189
+ clearFailedDraft,
101
190
  attachments,
102
191
  setAttachments,
103
192
  };