@common-origin/design-system 2.20.0 → 2.21.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 (55) hide show
  1. package/dist/client.js +844 -344
  2. package/dist/client.js.map +1 -1
  3. package/dist/components/atoms/AILabel/AILabel.d.mts +30 -0
  4. package/dist/components/atoms/AILabel/AILabel.d.ts +30 -0
  5. package/dist/components/atoms/AILabel/AILabel.mjs +94 -0
  6. package/dist/components/atoms/AILabel/AILabel.mjs.map +1 -0
  7. package/dist/components/atoms/AILabel/index.d.mts +1 -0
  8. package/dist/components/atoms/AILabel/index.d.ts +1 -0
  9. package/dist/components/atoms/Chip/SuggestionChip.d.mts +24 -0
  10. package/dist/components/atoms/Chip/SuggestionChip.d.ts +24 -0
  11. package/dist/components/atoms/Chip/SuggestionChip.mjs +61 -0
  12. package/dist/components/atoms/Chip/SuggestionChip.mjs.map +1 -0
  13. package/dist/components/atoms/Chip/index.d.mts +1 -0
  14. package/dist/components/atoms/Chip/index.d.ts +1 -0
  15. package/dist/components/atoms/index.d.mts +1 -0
  16. package/dist/components/atoms/index.d.ts +1 -0
  17. package/dist/components/molecules/AgentInput/AgentInput.d.mts +63 -1
  18. package/dist/components/molecules/AgentInput/AgentInput.d.ts +63 -1
  19. package/dist/components/molecules/AgentInput/AgentInput.mjs +476 -112
  20. package/dist/components/molecules/AgentInput/agentInputSpeech.d.mts +2 -0
  21. package/dist/components/molecules/AgentInput/agentInputSpeech.d.ts +2 -0
  22. package/dist/components/molecules/AgentInput/agentInputSpeech.mjs +5 -1
  23. package/dist/components/molecules/AgentInput/agentInputSpeech.mjs.map +1 -1
  24. package/dist/components/molecules/AgentInput/index.d.mts +1 -1
  25. package/dist/components/molecules/AgentInput/index.d.ts +1 -1
  26. package/dist/components/molecules/CardLarge/CardLarge.mjs +1 -0
  27. package/dist/components/molecules/ChipGroup/ChipGroup.mjs +2 -0
  28. package/dist/components/molecules/FeatureBlock/FeatureBlock.mjs +1 -0
  29. package/dist/data.js +316 -3
  30. package/dist/data.js.map +1 -1
  31. package/dist/index.js +2 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/index.mjs +2 -0
  34. package/dist/index.mjs.map +1 -1
  35. package/dist/lib/tokenAliases.mjs +1 -0
  36. package/dist/lib/tokenAliases.mjs.map +1 -1
  37. package/dist/styles/icons.json +30 -1
  38. package/dist/styles/icons.json.mjs +33 -2
  39. package/dist/styles/icons.json.mjs.map +1 -1
  40. package/dist/styles/tokens.d.mts +545 -1
  41. package/dist/styles/tokens.d.ts +545 -1
  42. package/dist/styles/tokens.json +284 -2
  43. package/dist/styles/tokens.json.mjs +284 -2
  44. package/dist/styles/tokens.json.mjs.map +1 -1
  45. package/dist/tokens/index.js +284 -2
  46. package/dist/tokens/index.js.map +1 -1
  47. package/dist/tokens/index.mjs +284 -2
  48. package/dist/tokens/index.mjs.map +1 -1
  49. package/dist/tokens/styles/tokens.d.mts +545 -1
  50. package/dist/tokens/styles/tokens.d.ts +545 -1
  51. package/dist/tokens.css +242 -0
  52. package/dist/types/icons.d.mts +1 -1
  53. package/dist/types/icons.d.ts +1 -1
  54. package/dist/types/icons.mjs.map +1 -1
  55. package/package.json +1 -1
@@ -1,106 +1,182 @@
1
1
  'use client';
2
2
  import { __makeTemplateObject, __awaiter, __spreadArray, __generator } from '../../../vendor/tslib/tslib.es6.mjs';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
4
- import { useState, useRef, useId, useCallback, useEffect, useMemo } from 'react';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+ import { forwardRef, useState, useRef, useId, useEffect, useCallback, useImperativeHandle, useMemo, useLayoutEffect } from 'react';
5
5
  import styled$module, { keyframes, css } from 'styled-components';
6
6
  const styled = styled$module.default || styled$module;
7
+ import { Button } from '../../atoms/Button/Button.mjs';
8
+ import '../../atoms/Chip/shared/ChipBase.mjs';
9
+ import '../../atoms/Icon/Icon.mjs';
10
+ import { SuggestionChip } from '../../atoms/Chip/SuggestionChip.mjs';
7
11
  import { IconButton } from '../../atoms/IconButton/IconButton.mjs';
8
12
  import { Typography } from '../../atoms/Typography/Typography.mjs';
9
- import { visuallyHidden } from '../../../lib/styleUtils.mjs';
13
+ import { reducedMotion, visuallyHidden } from '../../../lib/styleUtils.mjs';
14
+ import { usePresence, EXIT_DURATION_MS, useInert } from '../../../lib/usePresence.mjs';
10
15
  import tokens from '../../../lib/cssTokens.mjs';
11
- import { normalizeTranscript, detectSpeechRecognitionConstructor, defaultVoiceErrorMessage, mapSpeechErrorCode } from './agentInputSpeech.mjs';
16
+ import { normalizeTranscript, normalizeMessage, detectSpeechRecognitionConstructor, defaultVoiceErrorMessage, mapSpeechErrorCode } from './agentInputSpeech.mjs';
12
17
  import { agentInputStateTransition } from './agentInputStateMachine.mjs';
13
18
 
14
19
  var semantic = tokens.semantic,
15
20
  component = tokens.component;
16
- var DEFAULT_IDLE_HELPER = 'Ask about transactions, spending, or transfers';
17
21
  var DEFAULT_LISTENING_MESSAGE = 'Listening… speak now';
18
22
  var DEFAULT_PROCESSING_MESSAGE = 'Finishing voice input…';
23
+ // The counter appears from this share of maxLength
24
+ var COUNTER_THRESHOLD = 0.8;
25
+ // Layout effects warn during server rendering in React 18
26
+ var useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
19
27
  var rotateRing = keyframes(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n"], ["\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n"])));
20
28
  var Container = /*#__PURE__*/styled.div.withConfig({
29
+ shouldForwardProp: function shouldForwardProp(prop) {
30
+ return !prop.startsWith('$');
31
+ },
21
32
  displayName: "AgentInput__Container",
22
33
  componentId: "sc-fhl996-0"
23
- })(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n display: flex;\n flex-direction: column;\n gap: ", ";\n width: 100%;\n"], ["\n display: flex;\n flex-direction: column;\n gap: ", ";\n width: 100%;\n"])), semantic.space[200]);
34
+ })(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n display: flex;\n flex-direction: column;\n gap: ", ";\n width: 100%;\n\n ", "\n"], ["\n display: flex;\n flex-direction: column;\n gap: ", ";\n width: 100%;\n\n ", "\n"])), semantic.space[200], function (_a) {
35
+ var $docked = _a.$docked;
36
+ return $docked && css(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n position: sticky;\n bottom: 0;\n z-index: ", ";\n /* The band hides content scrolling past the field and status line (decisions 0006, 0031) */\n background-color: ", ";\n padding-top: ", ";\n /* Clears the home indicator; zero unless the page sets viewport-fit=cover */\n padding-bottom: env(safe-area-inset-bottom, 0px);\n "], ["\n position: sticky;\n bottom: 0;\n z-index: ", ";\n /* The band hides content scrolling past the field and status line (decisions 0006, 0031) */\n background-color: ", ";\n padding-top: ", ";\n /* Clears the home indicator; zero unless the page sets viewport-fit=cover */\n padding-bottom: env(safe-area-inset-bottom, 0px);\n "])), semantic.layer.sticky, semantic.surface.base, semantic.space[300]);
37
+ });
24
38
  var ControlsRow = /*#__PURE__*/styled.div.withConfig({
25
39
  displayName: "AgentInput__ControlsRow",
26
40
  componentId: "sc-fhl996-1"
27
- })(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n width: 100%;\n"], ["\n width: 100%;\n"])));
41
+ })(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n width: 100%;\n"], ["\n width: 100%;\n"])));
28
42
  var InputShell = /*#__PURE__*/styled.div.withConfig({
29
43
  displayName: "AgentInput__InputShell",
30
44
  componentId: "sc-fhl996-2"
31
- })(templateObject_4 || (templateObject_4 = __makeTemplateObject(["\n display: flex;\n align-items: center;\n gap: ", ";\n width: 100%;\n padding: ", " ", " ", " ", ";\n background-color: ", ";\n border: ", " solid ", ";\n border-radius: ", ";\n box-shadow: ", ";\n transition: border-color ", " ", ", outline ", " ", ";\n\n &:focus-within {\n border-color: ", ";\n outline: ", ";\n outline-offset: ", ";\n }\n\n &[data-disabled='true'] {\n background-color: ", ";\n border-color: ", ";\n }\n"], ["\n display: flex;\n align-items: center;\n gap: ", ";\n width: 100%;\n padding: ", " ", " ", " ", ";\n background-color: ", ";\n border: ", " solid ", ";\n border-radius: ", ";\n box-shadow: ", ";\n transition: border-color ", " ", ", outline ", " ", ";\n\n &:focus-within {\n border-color: ", ";\n outline: ", ";\n outline-offset: ", ";\n }\n\n &[data-disabled='true'] {\n background-color: ", ";\n border-color: ", ";\n }\n"])), semantic.space[200], semantic.space[100], semantic.space[200], semantic.space[100], semantic.space.inset.lg, component.input["default"].backgroundColor, component.input["default"].borderWidth, component.input["default"].borderColor, semantic.radius['3xl'], semantic.elevation.raised, semantic.motion.duration.moderate, semantic.motion.easing.standard, semantic.motion.duration.moderate, semantic.motion.easing.standard, component.input.focus.borderColor, component.input.focus.outline, semantic.border.focusOffset, component.input.disabled.backgroundColor, component.input.disabled.borderColor);
32
- var Input = /*#__PURE__*/styled.input.withConfig({
45
+ })(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n display: flex;\n /* Actions sit beside the last line as the field grows */\n align-items: flex-end;\n gap: ", ";\n width: 100%;\n padding: ", " ", " ", " ", ";\n background-color: ", ";\n border: ", " solid ", ";\n border-radius: ", ";\n box-shadow: ", ";\n transition: border-color ", " ", ", outline ", " ", ";\n\n &:focus-within {\n border-color: ", ";\n outline: ", ";\n outline-offset: ", ";\n }\n\n &[data-disabled='true'] {\n background-color: ", ";\n border-color: ", ";\n }\n"], ["\n display: flex;\n /* Actions sit beside the last line as the field grows */\n align-items: flex-end;\n gap: ", ";\n width: 100%;\n padding: ", " ", " ", " ", ";\n background-color: ", ";\n border: ", " solid ", ";\n border-radius: ", ";\n box-shadow: ", ";\n transition: border-color ", " ", ", outline ", " ", ";\n\n &:focus-within {\n border-color: ", ";\n outline: ", ";\n outline-offset: ", ";\n }\n\n &[data-disabled='true'] {\n background-color: ", ";\n border-color: ", ";\n }\n"
46
+ // One row is a control-height field; it grows with its content to six rows, then scrolls.
47
+ // The height is set from scrollHeight in resizeField; 1lh is the body line height.
48
+ ])), semantic.space[200], semantic.space[100], semantic.space[200], semantic.space[100], semantic.space.inset.lg, component.input["default"].backgroundColor, component.input["default"].borderWidth, component.input["default"].borderColor, semantic.radius['3xl'], semantic.elevation.raised, semantic.motion.duration.moderate, semantic.motion.easing.standard, semantic.motion.duration.moderate, semantic.motion.easing.standard, component.input.focus.borderColor, component.input.focus.outline, semantic.border.focusOffset, component.input.disabled.backgroundColor, component.input.disabled.borderColor);
49
+ // One row is a control-height field; it grows with its content to six rows, then scrolls.
50
+ // The height is set from scrollHeight in resizeField; 1lh is the body line height.
51
+ var Input = /*#__PURE__*/styled.textarea.withConfig({
33
52
  displayName: "AgentInput__Input",
34
53
  componentId: "sc-fhl996-3"
35
- })(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n flex: 1;\n min-width: 0;\n min-height: ", ";\n border: none;\n background: transparent;\n font: ", ";\n color: ", ";\n outline: none;\n\n &::placeholder {\n color: ", ";\n /* Firefox fades placeholders to 0.54 by default, which would undo the 4.5:1 contrast */\n opacity: 1;\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n }\n"], ["\n flex: 1;\n min-width: 0;\n min-height: ", ";\n border: none;\n background: transparent;\n font: ", ";\n color: ", ";\n outline: none;\n\n &::placeholder {\n color: ", ";\n /* Firefox fades placeholders to 0.54 by default, which would undo the 4.5:1 contrast */\n opacity: 1;\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n }\n"])), semantic.size.control.lg, semantic.typography.body, semantic.color.text["default"], component.input.placeholder.textColor, semantic.color.text.disabled);
54
+ })(templateObject_6 || (templateObject_6 = __makeTemplateObject(["\n display: block;\n flex: 1;\n min-width: 0;\n box-sizing: border-box;\n height: ", ";\n min-height: ", ";\n max-height: calc(5lh + ", ");\n margin: 0;\n padding: calc((", " - 1lh) / 2) 0;\n resize: none;\n overflow-y: hidden;\n border: none;\n background: transparent;\n font: ", ";\n color: ", ";\n outline: none;\n\n &::placeholder {\n color: ", ";\n /* Firefox fades placeholders to 0.54 by default, which would undo the 4.5:1 contrast */\n opacity: 1;\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n }\n"], ["\n display: block;\n flex: 1;\n min-width: 0;\n box-sizing: border-box;\n height: ", ";\n min-height: ", ";\n max-height: calc(5lh + ", ");\n margin: 0;\n padding: calc((", " - 1lh) / 2) 0;\n resize: none;\n overflow-y: hidden;\n border: none;\n background: transparent;\n font: ", ";\n color: ", ";\n outline: none;\n\n &::placeholder {\n color: ", ";\n /* Firefox fades placeholders to 0.54 by default, which would undo the 4.5:1 contrast */\n opacity: 1;\n }\n\n &:disabled {\n color: ", ";\n cursor: not-allowed;\n }\n"])), semantic.size.control.lg, semantic.size.control.lg, semantic.size.control.lg, semantic.size.control.lg, semantic.typography.body, semantic.color.text["default"], component.input.placeholder.textColor, semantic.color.text.disabled);
36
55
  var InputActions = /*#__PURE__*/styled.div.withConfig({
37
56
  displayName: "AgentInput__InputActions",
38
57
  componentId: "sc-fhl996-4"
39
- })(templateObject_6 || (templateObject_6 = __makeTemplateObject(["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n"], ["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n"])), semantic.space[200]);
58
+ })(templateObject_7 || (templateObject_7 = __makeTemplateObject(["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n /* Centres the buttons on a one-row field, and keeps them level with the last line as it grows */\n padding-bottom: calc((", " - ", ") / 2);\n"], ["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n /* Centres the buttons on a one-row field, and keeps them level with the last line as it grows */\n padding-bottom: calc((", " - ", ") / 2);\n"])), semantic.space[200], semantic.size.control.lg, semantic.size.control.md);
40
59
  var CircularActionIconButton = /*#__PURE__*/styled(IconButton).withConfig({
41
60
  displayName: "AgentInput__CircularActionIconButton",
42
61
  componentId: "sc-fhl996-5"
43
- })(templateObject_7 || (templateObject_7 = __makeTemplateObject(["\n border-radius: ", ";\n"], ["\n border-radius: ", ";\n"])), semantic.radius.full);
62
+ })(templateObject_8 || (templateObject_8 = __makeTemplateObject(["\n border-radius: ", ";\n"], ["\n border-radius: ", ";\n"])), semantic.radius.full);
44
63
  var InputWrapper = /*#__PURE__*/styled.div.withConfig({
45
64
  displayName: "AgentInput__InputWrapper",
46
65
  componentId: "sc-fhl996-6"
47
- })(templateObject_8 || (templateObject_8 = __makeTemplateObject(["\n flex: 1;\n min-width: 0;\n"], ["\n flex: 1;\n min-width: 0;\n"])));
66
+ })(templateObject_9 || (templateObject_9 = __makeTemplateObject(["\n flex: 1;\n min-width: 0;\n"], ["\n flex: 1;\n min-width: 0;\n"])));
48
67
  var MicButtonWrapper = /*#__PURE__*/styled.div.withConfig({
49
68
  shouldForwardProp: function shouldForwardProp(prop) {
50
69
  return !prop.startsWith('$');
51
70
  },
52
71
  displayName: "AgentInput__MicButtonWrapper",
53
72
  componentId: "sc-fhl996-7"
54
- })(templateObject_9 || (templateObject_9 = __makeTemplateObject(["\n position: relative;\n display: inline-flex;\n border-radius: ", ";\n\n & > button {\n position: relative;\n z-index: 1;\n }\n"], ["\n position: relative;\n display: inline-flex;\n border-radius: ", ";\n\n & > button {\n position: relative;\n z-index: 1;\n }\n"
73
+ })(templateObject_10 || (templateObject_10 = __makeTemplateObject(["\n position: relative;\n display: inline-flex;\n border-radius: ", ";\n\n & > button {\n position: relative;\n z-index: 1;\n }\n"], ["\n position: relative;\n display: inline-flex;\n border-radius: ", ";\n\n & > button {\n position: relative;\n z-index: 1;\n }\n"
55
74
  // Masks read only alpha, so the opaque stop is a mask value, not a colour (decision 0010).
56
75
  // eslint-disable-next-line no-restricted-syntax
57
76
  ])), semantic.radius.full);
58
77
  // Masks read only alpha, so the opaque stop is a mask value, not a colour (decision 0010).
59
78
  // eslint-disable-next-line no-restricted-syntax
60
- var ringMask = css(templateObject_10 || (templateObject_10 = __makeTemplateObject(["radial-gradient(farthest-side, transparent calc(100% - var(--ring-thickness)), #000 calc(100% - var(--ring-thickness)))"], ["radial-gradient(farthest-side, transparent calc(100% - var(--ring-thickness)), #000 calc(100% - var(--ring-thickness)))"])));
79
+ var ringMask = css(templateObject_11 || (templateObject_11 = __makeTemplateObject(["radial-gradient(farthest-side, transparent calc(100% - var(--ring-thickness)), #000 calc(100% - var(--ring-thickness)))"], ["radial-gradient(farthest-side, transparent calc(100% - var(--ring-thickness)), #000 calc(100% - var(--ring-thickness)))"])));
61
80
  var MicListeningRing = /*#__PURE__*/styled.span.withConfig({
62
81
  shouldForwardProp: function shouldForwardProp(prop) {
63
82
  return !prop.startsWith('$');
64
83
  },
65
84
  displayName: "AgentInput__MicListeningRing",
66
85
  componentId: "sc-fhl996-8"
67
- })(templateObject_12 || (templateObject_12 = __makeTemplateObject(["\n --ring-thickness: ", ";\n position: absolute;\n inset: calc(-1 * ", " - ", ");\n border-radius: ", ";\n pointer-events: none;\n z-index: 0;\n background: conic-gradient(\n from 0deg,\n ", " 0deg,\n ", " 110deg,\n ", " 210deg,\n ", " 300deg,\n ", " 360deg\n );\n mask: ", ";\n -webkit-mask: ", ";\n\n ", "\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n"], ["\n --ring-thickness: ", ";\n position: absolute;\n inset: calc(-1 * ", " - ", ");\n border-radius: ", ";\n pointer-events: none;\n z-index: 0;\n background: conic-gradient(\n from 0deg,\n ", " 0deg,\n ", " 110deg,\n ", " 210deg,\n ", " 300deg,\n ", " 360deg\n );\n mask: ", ";\n -webkit-mask: ", ";\n\n ", "\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n"])), semantic.border.width.thick, semantic.space[100], semantic.border.focusOffset, semantic.radius.full, semantic.color.fill['accent-muted'], semantic.color.fill.accent, semantic.color.fill['accent-hover'], semantic.color.fill.accent, semantic.color.fill['accent-muted'], ringMask, ringMask, function (_a) {
86
+ })(templateObject_13 || (templateObject_13 = __makeTemplateObject(["\n --ring-thickness: ", ";\n position: absolute;\n inset: calc(-1 * ", " - ", ");\n border-radius: ", ";\n pointer-events: none;\n z-index: 0;\n background: conic-gradient(\n from 0deg,\n ", " 0deg,\n ", " 110deg,\n ", " 210deg,\n ", " 300deg,\n ", " 360deg\n );\n mask: ", ";\n -webkit-mask: ", ";\n\n ", "\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n"], ["\n --ring-thickness: ", ";\n position: absolute;\n inset: calc(-1 * ", " - ", ");\n border-radius: ", ";\n pointer-events: none;\n z-index: 0;\n background: conic-gradient(\n from 0deg,\n ", " 0deg,\n ", " 110deg,\n ", " 210deg,\n ", " 300deg,\n ", " 360deg\n );\n mask: ", ";\n -webkit-mask: ", ";\n\n ", "\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n"
87
+ // Maximum suggestions shown (decision 0046, #280)
88
+ ])), semantic.border.width.thick, semantic.space[100], semantic.border.focusOffset, semantic.radius.full, semantic.color.fill['accent-muted'], semantic.color.fill.accent, semantic.color.fill['accent-hover'], semantic.color.fill.accent, semantic.color.fill['accent-muted'], ringMask, ringMask, function (_a) {
68
89
  var $reducedMotion = _a.$reducedMotion;
69
- return !$reducedMotion && css(templateObject_11 || (templateObject_11 = __makeTemplateObject(["\n animation: ", " 1300ms linear infinite;\n "], ["\n animation: ", " 1300ms linear infinite;\n "])), rotateRing);
90
+ return !$reducedMotion && css(templateObject_12 || (templateObject_12 = __makeTemplateObject(["\n animation: ", " 1300ms linear infinite;\n "], ["\n animation: ", " 1300ms linear infinite;\n "])), rotateRing);
91
+ });
92
+ // Maximum suggestions shown (decision 0046, #280)
93
+ var MAX_SUGGESTIONS = 3;
94
+ var regionEnter = keyframes(templateObject_14 || (templateObject_14 = __makeTemplateObject(["\n from {\n opacity: 0;\n transform: translateY(", ");\n }\n"], ["\n from {\n opacity: 0;\n transform: translateY(", ");\n }\n"])), semantic.motion.distance.sm);
95
+ var regionFadeIn = keyframes(templateObject_15 || (templateObject_15 = __makeTemplateObject(["\n from {\n opacity: 0;\n }\n"], ["\n from {\n opacity: 0;\n }\n"
96
+ // Leaving content fades, then its space closes, as with Alert's dismiss (decision 0015). A grid
97
+ // row from 1fr to 0fr collapses to the content's own height without measuring it.
98
+ ])));
99
+ // Leaving content fades, then its space closes, as with Alert's dismiss (decision 0015). A grid
100
+ // row from 1fr to 0fr collapses to the content's own height without measuring it.
101
+ var regionFadeThenCollapse = keyframes(templateObject_16 || (templateObject_16 = __makeTemplateObject(["\n 0%, 50% {\n grid-template-rows: 1fr;\n }\n 0% {\n opacity: 1;\n }\n 50%, 100% {\n opacity: 0;\n }\n 100% {\n grid-template-rows: 0fr;\n }\n"], ["\n 0%, 50% {\n grid-template-rows: 1fr;\n }\n 0% {\n opacity: 1;\n }\n 50%, 100% {\n opacity: 0;\n }\n 100% {\n grid-template-rows: 0fr;\n }\n"])));
102
+ var regionFadeOut = keyframes(templateObject_17 || (templateObject_17 = __makeTemplateObject(["\n to {\n opacity: 0;\n }\n"], ["\n to {\n opacity: 0;\n }\n"
103
+ // One part of the region above the field: the review bar or the suggestions
104
+ ])));
105
+ // One part of the region above the field: the review bar or the suggestions
106
+ var RegionItem = /*#__PURE__*/styled.div.withConfig({
107
+ shouldForwardProp: function shouldForwardProp(prop) {
108
+ return !prop.startsWith('$');
109
+ },
110
+ displayName: "AgentInput__RegionItem",
111
+ componentId: "sc-fhl996-9"
112
+ })(templateObject_21 || (templateObject_21 = __makeTemplateObject(["\n display: grid;\n grid-template-rows: 1fr;\n animation: ", " ", " ", ";\n\n ", " {\n animation-name: ", ";\n }\n\n ", "\n"], ["\n display: grid;\n grid-template-rows: 1fr;\n animation: ", " ", " ", ";\n\n ", " {\n animation-name: ", ";\n }\n\n ", "\n"
113
+ // Clips while the row collapses. Its bottom padding is the space to the next part, so it closes too.
114
+ ])), regionEnter, semantic.motion.duration.moderate, semantic.motion.easing.decelerate, reducedMotion, regionFadeIn, function (_a) {
115
+ var $exiting = _a.$exiting,
116
+ $reducedExit = _a.$reducedExit;
117
+ return $exiting && css(templateObject_20 || (templateObject_20 = __makeTemplateObject(["\n pointer-events: none;\n animation: ", ";\n "], ["\n pointer-events: none;\n animation: ", ";\n "])), $reducedExit ? css(templateObject_18 || (templateObject_18 = __makeTemplateObject(["", " ", " ", " forwards"], ["", " ", " ", " forwards"])), regionFadeOut, semantic.motion.duration.fast, semantic.motion.easing.accelerate) : css(templateObject_19 || (templateObject_19 = __makeTemplateObject(["", " calc(2 * ", ") ", " forwards"], ["", " calc(2 * ", ") ", " forwards"])), regionFadeThenCollapse, semantic.motion.duration.fast, semantic.motion.easing.accelerate));
70
118
  });
119
+ // Clips while the row collapses. Its bottom padding is the space to the next part, so it closes too.
120
+ var RegionItemContent = /*#__PURE__*/styled.div.withConfig({
121
+ displayName: "AgentInput__RegionItemContent",
122
+ componentId: "sc-fhl996-10"
123
+ })(templateObject_22 || (templateObject_22 = __makeTemplateObject(["\n min-height: 0;\n overflow: hidden;\n padding-bottom: ", ";\n padding-left: ", ";\n"], ["\n min-height: 0;\n overflow: hidden;\n padding-bottom: ", ";\n padding-left: ", ";\n"
124
+ // The summary first, then the actions, left-aligned after it; they wrap below on narrow screens (0021)
125
+ ])), semantic.space[200], semantic.space[500]);
126
+ // The summary first, then the actions, left-aligned after it; they wrap below on narrow screens (0021)
127
+ var ReviewBar = /*#__PURE__*/styled.div.withConfig({
128
+ displayName: "AgentInput__ReviewBar",
129
+ componentId: "sc-fhl996-11"
130
+ })(templateObject_23 || (templateObject_23 = __makeTemplateObject(["\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n column-gap: ", ";\n row-gap: ", ";\n"], ["\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n column-gap: ", ";\n row-gap: ", ";\n"])), semantic.space[300], semantic.space[200]);
131
+ var ReviewActions = /*#__PURE__*/styled.div.withConfig({
132
+ displayName: "AgentInput__ReviewActions",
133
+ componentId: "sc-fhl996-12"
134
+ })(templateObject_24 || (templateObject_24 = __makeTemplateObject(["\n display: flex;\n flex-wrap: wrap;\n gap: ", ";\n"], ["\n display: flex;\n flex-wrap: wrap;\n gap: ", ";\n"])), semantic.space[200]);
135
+ var SuggestionList = /*#__PURE__*/styled.ul.withConfig({
136
+ displayName: "AgentInput__SuggestionList",
137
+ componentId: "sc-fhl996-13"
138
+ })(templateObject_25 || (templateObject_25 = __makeTemplateObject(["\n display: flex;\n flex-wrap: wrap;\n gap: ", ";\n margin: 0;\n padding: 0;\n list-style: none;\n\n & > li {\n max-width: 100%;\n }\n"], ["\n display: flex;\n flex-wrap: wrap;\n gap: ", ";\n margin: 0;\n padding: 0;\n list-style: none;\n\n & > li {\n max-width: 100%;\n }\n"])), semantic.space[200]);
71
139
  var StatusRow = /*#__PURE__*/styled.div.withConfig({
72
140
  displayName: "AgentInput__StatusRow",
73
- componentId: "sc-fhl996-9"
74
- })(templateObject_13 || (templateObject_13 = __makeTemplateObject(["\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: ", ";\n min-height: ", ";\n"], ["\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: ", ";\n min-height: ", ";\n"])), semantic.space[300], semantic.space[600]);
141
+ componentId: "sc-fhl996-14"
142
+ })(templateObject_26 || (templateObject_26 = __makeTemplateObject(["\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: ", ";\n min-height: ", ";\n"], ["\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: ", ";\n min-height: ", ";\n"])), semantic.space[300], semantic.space[600]);
75
143
  var StatusText = /*#__PURE__*/styled.div.withConfig({
76
144
  shouldForwardProp: function shouldForwardProp(prop) {
77
145
  return !prop.startsWith('$');
78
146
  },
79
147
  displayName: "AgentInput__StatusText",
80
- componentId: "sc-fhl996-10"
81
- })(templateObject_14 || (templateObject_14 = __makeTemplateObject(["\n padding-left: ", ";\n"], ["\n padding-left: ", ";\n"])), semantic.space[500]);
148
+ componentId: "sc-fhl996-15"
149
+ })(templateObject_27 || (templateObject_27 = __makeTemplateObject(["\n padding-left: ", ";\n"], ["\n padding-left: ", ";\n"])), semantic.space[500]);
82
150
  var StatusAnnouncement = /*#__PURE__*/styled.div.withConfig({
83
151
  displayName: "AgentInput__StatusAnnouncement",
84
- componentId: "sc-fhl996-11"
85
- })(templateObject_15 || (templateObject_15 = __makeTemplateObject([""], [""])));
152
+ componentId: "sc-fhl996-16"
153
+ })(templateObject_28 || (templateObject_28 = __makeTemplateObject([""], [""])));
154
+ var StatusEnd = /*#__PURE__*/styled.div.withConfig({
155
+ displayName: "AgentInput__StatusEnd",
156
+ componentId: "sc-fhl996-17"
157
+ })(templateObject_29 || (templateObject_29 = __makeTemplateObject(["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n"], ["\n display: inline-flex;\n align-items: center;\n gap: ", ";\n"])), semantic.space[300]);
158
+ var VisuallyHidden = /*#__PURE__*/styled.span.withConfig({
159
+ displayName: "AgentInput__VisuallyHidden",
160
+ componentId: "sc-fhl996-18"
161
+ })(templateObject_30 || (templateObject_30 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), visuallyHidden);
86
162
  var MeterContainer = /*#__PURE__*/styled.div.withConfig({
87
163
  displayName: "AgentInput__MeterContainer",
88
- componentId: "sc-fhl996-12"
89
- })(templateObject_16 || (templateObject_16 = __makeTemplateObject(["\n display: inline-flex;\n align-items: flex-end;\n gap: ", ";\n height: ", ";\n"], ["\n display: inline-flex;\n align-items: flex-end;\n gap: ", ";\n height: ", ";\n"])), semantic.space[100], semantic.space[400]);
164
+ componentId: "sc-fhl996-19"
165
+ })(templateObject_31 || (templateObject_31 = __makeTemplateObject(["\n display: inline-flex;\n align-items: flex-end;\n gap: ", ";\n height: ", ";\n"], ["\n display: inline-flex;\n align-items: flex-end;\n gap: ", ";\n height: ", ";\n"])), semantic.space[100], semantic.space[400]);
90
166
  var MeterBar = /*#__PURE__*/styled.div.withConfig({
91
167
  shouldForwardProp: function shouldForwardProp(prop) {
92
168
  return !prop.startsWith('$');
93
169
  },
94
170
  displayName: "AgentInput__MeterBar",
95
- componentId: "sc-fhl996-13"
96
- })(templateObject_17 || (templateObject_17 = __makeTemplateObject(["\n width: ", ";\n border-radius: ", ";\n background-color: ", ";\n height: ", ";\n transition: height ", " ", ";\n"], ["\n width: ", ";\n border-radius: ", ";\n background-color: ", ";\n height: ", ";\n transition: height ", " ", ";\n"])), semantic.space[100], semantic.radius.xs, semantic.color.fill.accent, function (_a) {
171
+ componentId: "sc-fhl996-20"
172
+ })(templateObject_32 || (templateObject_32 = __makeTemplateObject(["\n width: ", ";\n border-radius: ", ";\n background-color: ", ";\n height: ", ";\n transition: height ", " ", ";\n"], ["\n width: ", ";\n border-radius: ", ";\n background-color: ", ";\n height: ", ";\n transition: height ", " ", ";\n"])), semantic.space[100], semantic.radius.xs, semantic.color.fill.accent, function (_a) {
97
173
  var $height = _a.$height;
98
174
  return "".concat($height * 100, "%");
99
175
  }, semantic.motion.duration.fast, semantic.motion.easing.decelerate);
100
176
  var VisuallyHiddenLabel = /*#__PURE__*/styled.label.withConfig({
101
177
  displayName: "AgentInput__VisuallyHiddenLabel",
102
- componentId: "sc-fhl996-14"
103
- })(templateObject_18 || (templateObject_18 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), visuallyHidden);
178
+ componentId: "sc-fhl996-21"
179
+ })(templateObject_33 || (templateObject_33 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), visuallyHidden);
104
180
  function getInitialState(hasText) {
105
181
  return hasText ? 'typing' : 'idle';
106
182
  }
@@ -108,60 +184,104 @@ function createMeterBars(level) {
108
184
  var clampedLevel = Math.max(0, Math.min(level, 1));
109
185
  return [0.25 + clampedLevel * 0.75, 0.2 + clampedLevel * 0.6, 0.3 + clampedLevel * 0.7, 0.2 + clampedLevel * 0.5];
110
186
  }
111
- var AgentInput = function AgentInput(_a) {
187
+ // Declared separately so the react-hooks lint rules analyse it (they skip functions inline in forwardRef)
188
+ // Development builds warn about props that break the pattern. Bundlers replace process.env.NODE_ENV
189
+ // with a literal; without a bundler, a browser has no process, so reading it throws.
190
+ function readIsProduction() {
191
+ try {
192
+ return process.env.NODE_ENV === 'production';
193
+ } catch (_a) {
194
+ return false;
195
+ }
196
+ }
197
+ var isProduction = readIsProduction();
198
+ // A part of the region above the field. It stays mounted while it fades out and its space closes.
199
+ function RegionPart(_a) {
200
+ var show = _a.show,
201
+ reducedExit = _a.reducedExit,
202
+ children = _a.children;
203
+ var _b = usePresence(show, reducedExit ? EXIT_DURATION_MS : EXIT_DURATION_MS * 2),
204
+ isPresent = _b.isPresent,
205
+ isExiting = _b.isExiting;
206
+ var ref = useRef(null);
207
+ // Leaving controls drop out of the tab order and can't be activated
208
+ useInert(ref, isExiting);
209
+ if (!isPresent) return null;
210
+ return jsx(RegionItem, {
211
+ ref: ref,
212
+ "$exiting": isExiting,
213
+ "$reducedExit": reducedExit,
214
+ "aria-hidden": isExiting || undefined,
215
+ children: jsx(RegionItemContent, {
216
+ children: children
217
+ })
218
+ });
219
+ }
220
+ function AgentInputWithRef(_a, ref) {
221
+ var _this = this;
222
+ var _b;
112
223
  var value = _a.value,
113
- _b = _a.defaultValue,
114
- defaultValue = _b === void 0 ? '' : _b,
224
+ _c = _a.defaultValue,
225
+ defaultValue = _c === void 0 ? '' : _c,
115
226
  onChange = _a.onChange,
116
227
  onSubmit = _a.onSubmit,
117
- _c = _a.placeholder,
118
- placeholder = _c === void 0 ? 'Ask a question' : _c,
119
- _d = _a.disabled,
120
- disabled = _d === void 0 ? false : _d,
228
+ onStop = _a.onStop,
229
+ review = _a.review,
230
+ suggestions = _a.suggestions,
231
+ onSuggestionSelect = _a.onSuggestionSelect,
232
+ _d = _a.placeholder,
233
+ placeholder = _d === void 0 ? 'Ask a question' : _d,
234
+ maxLength = _a.maxLength,
235
+ _e = _a.disabled,
236
+ disabled = _e === void 0 ? false : _e,
121
237
  isSubmitting = _a.isSubmitting,
122
- _e = _a.enableVoice,
123
- enableVoice = _e === void 0 ? true : _e,
124
- _f = _a.autoSubmitOnVoiceFinal,
125
- autoSubmitOnVoiceFinal = _f === void 0 ? true : _f,
126
- _g = _a.voiceLanguage,
127
- voiceLanguage = _g === void 0 ? 'en-AU' : _g,
128
- _h = _a.noSpeechTimeoutMs,
129
- noSpeechTimeoutMs = _h === void 0 ? 8000 : _h,
238
+ _f = _a.enableVoice,
239
+ enableVoice = _f === void 0 ? true : _f,
240
+ _g = _a.autoSubmitOnVoiceFinal,
241
+ autoSubmitOnVoiceFinal = _g === void 0 ? true : _g,
242
+ _h = _a.voiceLanguage,
243
+ voiceLanguage = _h === void 0 ? 'en-AU' : _h,
244
+ _j = _a.noSpeechTimeoutMs,
245
+ noSpeechTimeoutMs = _j === void 0 ? 8000 : _j,
130
246
  onVoiceStart = _a.onVoiceStart,
131
247
  onVoiceStop = _a.onVoiceStop,
132
248
  onVoiceInterim = _a.onVoiceInterim,
133
249
  onVoiceFinal = _a.onVoiceFinal,
134
250
  onVoiceError = _a.onVoiceError,
251
+ _k = _a.helperText,
252
+ helperText = _k === void 0 ? '' : _k,
135
253
  statusMessage = _a.statusMessage,
136
254
  errorMessage = _a.errorMessage,
137
- _j = _a.label,
138
- label = _j === void 0 ? 'Agent input' : _j,
255
+ _l = _a.label,
256
+ label = _l === void 0 ? 'Agent input' : _l,
257
+ _m = _a.docked,
258
+ docked = _m === void 0 ? false : _m,
139
259
  dataTestId = _a["data-testid"];
140
260
  var isControlled = value !== undefined;
141
- var _k = useState(defaultValue),
142
- internalValue = _k[0],
143
- setInternalValue = _k[1];
144
- var _l = useState(getInitialState(normalizeTranscript(value !== null && value !== void 0 ? value : defaultValue).length > 0)),
145
- machineState = _l[0],
146
- setMachineState = _l[1];
147
- var _m = useState(''),
148
- interimTranscript = _m[0],
149
- setInterimTranscript = _m[1];
150
- var _o = useState(false),
151
- internalSubmitting = _o[0],
152
- setInternalSubmitting = _o[1];
153
- var _p = useState(undefined),
154
- internalErrorMessage = _p[0],
155
- setInternalErrorMessage = _p[1];
156
- var _q = useState(false),
157
- voiceSupported = _q[0],
158
- setVoiceSupported = _q[1];
261
+ var _o = useState(defaultValue),
262
+ internalValue = _o[0],
263
+ setInternalValue = _o[1];
264
+ var _p = useState(getInitialState(normalizeTranscript(value !== null && value !== void 0 ? value : defaultValue).length > 0)),
265
+ machineState = _p[0],
266
+ setMachineState = _p[1];
267
+ var _q = useState(''),
268
+ interimTranscript = _q[0],
269
+ setInterimTranscript = _q[1];
159
270
  var _r = useState(false),
160
- prefersReducedMotion = _r[0],
161
- setPrefersReducedMotion = _r[1];
162
- var _s = useState(0),
163
- meterLevel = _s[0],
164
- setMeterLevel = _s[1];
271
+ internalSubmitting = _r[0],
272
+ setInternalSubmitting = _r[1];
273
+ var _s = useState(undefined),
274
+ internalErrorMessage = _s[0],
275
+ setInternalErrorMessage = _s[1];
276
+ var _t = useState(false),
277
+ voiceSupported = _t[0],
278
+ setVoiceSupported = _t[1];
279
+ var _u = useState(false),
280
+ prefersReducedMotion = _u[0],
281
+ setPrefersReducedMotion = _u[1];
282
+ var _v = useState(0),
283
+ meterLevel = _v[0],
284
+ setMeterLevel = _v[1];
165
285
  var recognitionRef = useRef(null);
166
286
  var noSpeechTimeoutRef = useRef(null);
167
287
  var isStoppingRef = useRef(false);
@@ -169,10 +289,19 @@ var AgentInput = function AgentInput(_a) {
169
289
  var audioStreamRef = useRef(null);
170
290
  var audioContextRef = useRef(null);
171
291
  var meterRafRef = useRef(null);
292
+ var inputRef = useRef(null);
172
293
  var inputId = useId();
173
294
  var statusId = "".concat(inputId, "-status");
295
+ var counterId = "".concat(inputId, "-counter");
174
296
  var currentValue = isControlled ? value !== null && value !== void 0 ? value : '' : internalValue;
297
+ // Read after onSubmit settles, when the closure's value may be stale
298
+ var currentValueRef = useRef(currentValue);
299
+ useEffect(function () {
300
+ currentValueRef.current = currentValue;
301
+ }, [currentValue]);
175
302
  var hasText = normalizeTranscript(currentValue).length > 0;
303
+ var messageLength = normalizeMessage(currentValue).length;
304
+ var isOverLimit = maxLength !== undefined && messageLength > maxLength;
176
305
  var effectiveSubmitting = isSubmitting !== null && isSubmitting !== void 0 ? isSubmitting : internalSubmitting;
177
306
  var effectiveState = disabled ? 'disabled' : machineState;
178
307
  var isListening = effectiveState === 'listening';
@@ -195,7 +324,7 @@ var AgentInput = function AgentInput(_a) {
195
324
  }
196
325
  }, []);
197
326
  var stopMeter = useCallback(function () {
198
- return __awaiter(void 0, void 0, void 0, function () {
327
+ return __awaiter(_this, void 0, void 0, function () {
199
328
  return __generator(this, function (_a) {
200
329
  switch (_a.label) {
201
330
  case 0:
@@ -223,7 +352,7 @@ var AgentInput = function AgentInput(_a) {
223
352
  });
224
353
  }, []);
225
354
  var startMeter = useCallback(function () {
226
- return __awaiter(void 0, void 0, void 0, function () {
355
+ return __awaiter(_this, void 0, void 0, function () {
227
356
  var stream, audioContext, analyser_1, source, data_1, _loop_;
228
357
  var _b;
229
358
  return __generator(this, function (_c) {
@@ -284,15 +413,22 @@ var AgentInput = function AgentInput(_a) {
284
413
  });
285
414
  }, [onVoiceError, updateMachineState]);
286
415
  var submitPayload = useCallback(function (rawText, source) {
287
- return __awaiter(void 0, void 0, void 0, function () {
416
+ return __awaiter(_this, void 0, void 0, function () {
288
417
  var text;
289
418
  return __generator(this, function (_b) {
290
419
  switch (_b.label) {
291
420
  case 0:
292
- text = normalizeTranscript(rawText);
421
+ text = source === 'text' ? normalizeMessage(rawText) : normalizeTranscript(rawText);
293
422
  if (!text || disabled || effectiveSubmitting) {
294
423
  return [2 /*return*/];
295
424
  }
425
+ if (maxLength !== undefined && text.length > maxLength) {
426
+ // A long transcript goes into the field to be shortened, unless the user has typed since
427
+ if (source === 'voice-final' && !normalizeMessage(currentValueRef.current)) {
428
+ updateValue(text);
429
+ }
430
+ return [2 /*return*/];
431
+ }
296
432
  updateMachineState({
297
433
  type: 'SUBMIT'
298
434
  });
@@ -314,14 +450,18 @@ var AgentInput = function AgentInput(_a) {
314
450
  type: 'SUBMIT_SUCCESS'
315
451
  });
316
452
  setInterimTranscript('');
317
- updateValue('');
453
+ // Keep a follow-up typed while submitting
454
+ if (normalizeMessage(currentValueRef.current) === text) {
455
+ updateValue('');
456
+ }
318
457
  return [3 /*break*/, 5];
319
458
  case 3:
320
459
  _b.sent();
321
460
  updateMachineState({
322
461
  type: 'SUBMIT_ERROR'
323
462
  });
324
- if (source === 'voice-final') {
463
+ // Restore the transcript unless the user has typed something since
464
+ if (source === 'voice-final' && !normalizeMessage(currentValueRef.current)) {
325
465
  updateValue(text);
326
466
  }
327
467
  setInternalErrorMessage('Submission failed. Please try again.');
@@ -336,13 +476,13 @@ var AgentInput = function AgentInput(_a) {
336
476
  }
337
477
  });
338
478
  });
339
- }, [disabled, effectiveSubmitting, isSubmitting, onSubmit, updateMachineState, updateValue]);
479
+ }, [disabled, effectiveSubmitting, isSubmitting, maxLength, onSubmit, updateMachineState, updateValue]);
340
480
  var stopListening = useCallback(function (emitStopEvent_1) {
341
481
  var args_1 = [];
342
482
  for (var _i = 1; _i < arguments.length; _i++) {
343
483
  args_1[_i - 1] = arguments[_i];
344
484
  }
345
- return __awaiter(void 0, __spreadArray([emitStopEvent_1], args_1, true), void 0, function (emitStopEvent, preserveState) {
485
+ return __awaiter(_this, __spreadArray([emitStopEvent_1], args_1, true), void 0, function (emitStopEvent, preserveState) {
346
486
  if (preserveState === void 0) {
347
487
  preserveState = false;
348
488
  }
@@ -378,7 +518,7 @@ var AgentInput = function AgentInput(_a) {
378
518
  });
379
519
  }, [clearNoSpeechTimeout, hasText, onVoiceStop, stopMeter, updateMachineState]);
380
520
  var onNoSpeechTimeout = useCallback(function () {
381
- return __awaiter(void 0, void 0, void 0, function () {
521
+ return __awaiter(_this, void 0, void 0, function () {
382
522
  var message;
383
523
  return __generator(this, function (_a) {
384
524
  switch (_a.label) {
@@ -402,7 +542,7 @@ var AgentInput = function AgentInput(_a) {
402
542
  });
403
543
  }, [clearNoSpeechTimeout, onVoiceError, stopListening, updateMachineState]);
404
544
  var beginListening = useCallback(function () {
405
- return __awaiter(void 0, void 0, void 0, function () {
545
+ return __awaiter(_this, void 0, void 0, function () {
406
546
  return __generator(this, function (_b) {
407
547
  switch (_b.label) {
408
548
  case 0:
@@ -446,7 +586,7 @@ var AgentInput = function AgentInput(_a) {
446
586
  recognition.interimResults = true;
447
587
  recognition.continuous = false;
448
588
  recognition.onresult = function (event) {
449
- return __awaiter(void 0, void 0, void 0, function () {
589
+ return __awaiter(_this, void 0, void 0, function () {
450
590
  var interim, finalText, index, result, normalizedInterim, normalizedFinal;
451
591
  return __generator(this, function (_a) {
452
592
  switch (_a.label) {
@@ -504,7 +644,7 @@ var AgentInput = function AgentInput(_a) {
504
644
  });
505
645
  };
506
646
  recognition.onerror = function (event) {
507
- return __awaiter(void 0, void 0, void 0, function () {
647
+ return __awaiter(_this, void 0, void 0, function () {
508
648
  var mappedCode;
509
649
  return __generator(this, function (_a) {
510
650
  switch (_a.label) {
@@ -520,7 +660,7 @@ var AgentInput = function AgentInput(_a) {
520
660
  });
521
661
  };
522
662
  recognition.onend = function () {
523
- return __awaiter(void 0, void 0, void 0, function () {
663
+ return __awaiter(_this, void 0, void 0, function () {
524
664
  return __generator(this, function (_a) {
525
665
  switch (_a.label) {
526
666
  case 0:
@@ -549,6 +689,95 @@ var AgentInput = function AgentInput(_a) {
549
689
  stopMeter();
550
690
  };
551
691
  }, [autoSubmitOnVoiceFinal, clearNoSpeechTimeout, emitVoiceError, enableVoice, hasText, noSpeechTimeoutMs, onNoSpeechTimeout, onVoiceFinal, onVoiceInterim, stopListening, stopMeter, submitPayload, updateMachineState, updateValue, voiceLanguage]);
692
+ var focusField = useCallback(function (cursor) {
693
+ var input = inputRef.current;
694
+ if (!input) return;
695
+ input.focus();
696
+ if (!cursor) return;
697
+ var placeCursor = function placeCursor() {
698
+ if (cursor === 'all') {
699
+ input.select();
700
+ return;
701
+ }
702
+ var position = cursor === 'start' ? 0 : input.value.length;
703
+ input.setSelectionRange(position, position);
704
+ };
705
+ placeCursor();
706
+ // Again after the render a prefill in the same handler triggers, so 'end' lands after the new text
707
+ window.setTimeout(placeCursor, 0);
708
+ }, []);
709
+ useImperativeHandle(ref, function () {
710
+ return {
711
+ focus: function focus(options) {
712
+ return focusField(options === null || options === void 0 ? void 0 : options.cursor);
713
+ },
714
+ clear: function clear() {
715
+ setInterimTranscript('');
716
+ updateValue('');
717
+ }
718
+ };
719
+ }, [focusField, updateValue]);
720
+ // The region above the field (decision 0046). The last review and suggestions shown are kept, so
721
+ // they can fade out after the product removes them.
722
+ var _w = useState(review),
723
+ shownReview = _w[0],
724
+ setShownReview = _w[1];
725
+ var _x = useState(null),
726
+ lastReviewAction = _x[0],
727
+ setLastReviewAction = _x[1];
728
+ if (review && review !== shownReview) {
729
+ setShownReview(review);
730
+ // A new proposal or change replaces what the last Discard or Undo announced
731
+ if (lastReviewAction) setLastReviewAction(null);
732
+ }
733
+ // A proposal replacing an open one is announced as an update (decision 0047). Consecutive
734
+ // replacements alternate a trailing no-break space, so the live region's text changes each time
735
+ // even when the summary doesn't; screen readers don't speak it.
736
+ var openProposalId = (review === null || review === void 0 ? void 0 : review.state) === 'proposal' ? review.id : null;
737
+ var _y = useState(openProposalId),
738
+ announcedProposalId = _y[0],
739
+ setAnnouncedProposalId = _y[1];
740
+ var _z = useState(0),
741
+ replacementCount = _z[0],
742
+ setReplacementCount = _z[1];
743
+ if (openProposalId !== announcedProposalId) {
744
+ setAnnouncedProposalId(openProposalId);
745
+ setReplacementCount(openProposalId !== null && announcedProposalId !== null ? replacementCount + 1 : 0);
746
+ }
747
+ var cappedSuggestions = useMemo(function () {
748
+ return (suggestions !== null && suggestions !== void 0 ? suggestions : []).slice(0, MAX_SUGGESTIONS);
749
+ }, [suggestions]);
750
+ var showSuggestions = cappedSuggestions.length > 0 && !hasText && (review === null || review === void 0 ? void 0 : review.state) !== 'proposal';
751
+ var _0 = useState(cappedSuggestions),
752
+ shownSuggestions = _0[0],
753
+ setShownSuggestions = _0[1];
754
+ if (showSuggestions && cappedSuggestions !== shownSuggestions) {
755
+ setShownSuggestions(cappedSuggestions);
756
+ }
757
+ var suggestionCount = (_b = suggestions === null || suggestions === void 0 ? void 0 : suggestions.length) !== null && _b !== void 0 ? _b : 0;
758
+ useEffect(function () {
759
+ if (!isProduction && suggestionCount > MAX_SUGGESTIONS) {
760
+ console.warn("AgentInput shows at most ".concat(MAX_SUGGESTIONS, " suggestions (decision 0046); ").concat(suggestionCount, " were passed, so the rest are hidden."));
761
+ }
762
+ }, [suggestionCount]);
763
+ var reviewAnnouncement = review ? review.state === 'proposal' ? "".concat(replacementCount > 0 ? 'Proposal updated. ' : '').concat(review.summary, ". Accept or discard.").concat(replacementCount > 0 && replacementCount % 2 === 0 ? "\xA0" : '') : 'Changes applied. Undo available.' : lastReviewAction === 'discarded' ? 'Suggestion discarded.' : lastReviewAction === 'undone' ? 'Changes undone.' : '';
764
+ var onReviewDiscard = function onReviewDiscard(onDiscard) {
765
+ setLastReviewAction('discarded');
766
+ onDiscard();
767
+ // The bar is leaving, so focus goes to the field rather than being lost
768
+ focusField();
769
+ };
770
+ var onReviewUndo = function onReviewUndo(onUndo) {
771
+ setLastReviewAction('undone');
772
+ onUndo();
773
+ focusField();
774
+ };
775
+ var onPickSuggestion = function onPickSuggestion(text) {
776
+ updateValue(text);
777
+ onSuggestionSelect === null || onSuggestionSelect === void 0 ? void 0 : onSuggestionSelect(text);
778
+ // Fills, never sends: the user reviews the text in the field first
779
+ focusField('end');
780
+ };
552
781
  useEffect(function () {
553
782
  var mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
554
783
  var sync = function sync() {
@@ -580,8 +809,40 @@ var AgentInput = function AgentInput(_a) {
580
809
  if (!interimTranscript) {
581
810
  return currentValue;
582
811
  }
583
- return normalizeTranscript([currentValue, interimTranscript].filter(Boolean).join(' '));
812
+ return normalizeMessage([currentValue, interimTranscript].filter(Boolean).join(' '));
584
813
  }, [currentValue, interimTranscript]);
814
+ var lastFieldWidthRef = useRef(0);
815
+ var resizeField = useCallback(function () {
816
+ var field = inputRef.current;
817
+ // Hidden (for example in a closed sheet): measure when it's shown, via the observer below
818
+ if (!field || field.clientWidth === 0) return;
819
+ lastFieldWidthRef.current = field.clientWidth;
820
+ // Collapsing the field to measure it shrinks the page for a moment; some engines clamp a page
821
+ // scrolled to the bottom when that happens, so restore its scroll position
822
+ var scrollX = window.scrollX,
823
+ scrollY = window.scrollY;
824
+ field.style.height = 'auto';
825
+ field.style.height = "".concat(field.scrollHeight, "px");
826
+ if (window.scrollY !== scrollY) window.scrollTo(scrollX, scrollY);
827
+ // Scroll only once max-height (six rows) clips the content
828
+ field.style.overflowY = field.scrollHeight > field.clientHeight ? 'auto' : 'hidden';
829
+ }, []);
830
+ useIsomorphicLayoutEffect(function () {
831
+ resizeField();
832
+ }, [displayedValue, resizeField]);
833
+ // Text rewraps when the field's width changes (layout, or being shown), not only the window's
834
+ useEffect(function () {
835
+ var field = inputRef.current;
836
+ if (!field || typeof ResizeObserver === 'undefined') return;
837
+ var observer = new ResizeObserver(function () {
838
+ // Its own height changes trigger this too; only a width change can rewrap the text
839
+ if (field.clientWidth !== lastFieldWidthRef.current) resizeField();
840
+ });
841
+ observer.observe(field);
842
+ return function () {
843
+ return observer.disconnect();
844
+ };
845
+ }, [resizeField]);
585
846
  var onTextChange = function onTextChange(event) {
586
847
  updateValue(event.target.value);
587
848
  if (effectiveState === 'error') {
@@ -593,10 +854,18 @@ var AgentInput = function AgentInput(_a) {
593
854
  }
594
855
  };
595
856
  var onInputKeyDown = function onInputKeyDown(event) {
596
- return __awaiter(void 0, void 0, void 0, function () {
857
+ return __awaiter(_this, void 0, void 0, function () {
858
+ var isComposing;
597
859
  return __generator(this, function (_a) {
598
860
  switch (_a.label) {
599
861
  case 0:
862
+ isComposing = event.nativeEvent.isComposing || event.keyCode === 229;
863
+ // Escape during an IME composition cancels the composition, not the request
864
+ if (event.key === 'Escape' && onStop && effectiveSubmitting && !isComposing) {
865
+ event.preventDefault();
866
+ onStop();
867
+ return [2 /*return*/];
868
+ }
600
869
  if (!(event.key === 'Escape' && isListening)) return [3 /*break*/, 2];
601
870
  event.preventDefault();
602
871
  return [4 /*yield*/, stopListening(true)];
@@ -604,7 +873,7 @@ var AgentInput = function AgentInput(_a) {
604
873
  _a.sent();
605
874
  return [2 /*return*/];
606
875
  case 2:
607
- if (!(event.key === 'Enter')) return [3 /*break*/, 4];
876
+ if (!(event.key === 'Enter' && !event.shiftKey && !isComposing)) return [3 /*break*/, 4];
608
877
  event.preventDefault();
609
878
  return [4 /*yield*/, submitPayload(currentValue, 'text')];
610
879
  case 3:
@@ -617,7 +886,7 @@ var AgentInput = function AgentInput(_a) {
617
886
  });
618
887
  };
619
888
  var onMicToggle = function onMicToggle() {
620
- return __awaiter(void 0, void 0, void 0, function () {
889
+ return __awaiter(_this, void 0, void 0, function () {
621
890
  return __generator(this, function (_a) {
622
891
  switch (_a.label) {
623
892
  case 0:
@@ -637,40 +906,105 @@ var AgentInput = function AgentInput(_a) {
637
906
  });
638
907
  };
639
908
  var statusByState = {
640
- idle: statusMessage || DEFAULT_IDLE_HELPER,
641
- typing: statusMessage || DEFAULT_IDLE_HELPER,
909
+ idle: statusMessage || helperText,
910
+ typing: statusMessage || helperText,
642
911
  listening: statusMessage || DEFAULT_LISTENING_MESSAGE,
643
912
  processingFinalTranscript: statusMessage || DEFAULT_PROCESSING_MESSAGE,
644
913
  submitting: statusMessage || 'Submitting…',
645
914
  error: errorMessage || internalErrorMessage || 'Voice input failed. Try again.',
646
- disabled: statusMessage || DEFAULT_IDLE_HELPER
915
+ disabled: statusMessage || helperText
647
916
  };
648
- var unsupportedMessage = defaultVoiceErrorMessage('not-supported');
649
- var shouldShowUnsupported = enableVoice && !hasVoice;
650
- var resolvedStatus = shouldShowUnsupported ? unsupportedMessage : statusByState[effectiveState];
651
- var isStatusError = effectiveState === 'error' || shouldShowUnsupported;
917
+ var overLimitMessage = isOverLimit && maxLength !== undefined ? "Over the ".concat(maxLength, "-character limit. Shorten your message to send.") : undefined;
918
+ // A product-set error shows in any state. Unsupported voice hides the mic without a message.
919
+ var resolvedStatus = errorMessage || overLimitMessage || statusByState[effectiveState];
920
+ var isStatusError = Boolean(errorMessage) || isOverLimit || effectiveState === 'error';
652
921
  var meterBars = createMeterBars(meterLevel);
653
- var sendDisabled = disabled || effectiveSubmitting || normalizeTranscript(currentValue).length === 0;
922
+ var showStop = Boolean(onStop) && effectiveSubmitting;
923
+ var sendDisabled = disabled || effectiveSubmitting || isOverLimit || messageLength === 0;
924
+ var showCounter = maxLength !== undefined && messageLength >= maxLength * COUNTER_THRESHOLD;
925
+ // Announced once when the limit is reached; going over is announced by the status line
926
+ var limitAnnouncement = maxLength !== undefined && messageLength === maxLength ? "Character limit reached: ".concat(maxLength, " characters") : '';
654
927
  return jsxs(Container, {
655
928
  "data-testid": dataTestId,
929
+ "$docked": docked,
930
+ "data-docked": docked || undefined,
656
931
  children: [jsx(VisuallyHiddenLabel, {
657
932
  htmlFor: inputId,
658
933
  children: label
659
- }), jsx(ControlsRow, {
660
- children: jsx(InputWrapper, {
934
+ }), jsxs(ControlsRow, {
935
+ children: [jsx(RegionPart, {
936
+ show: Boolean(review),
937
+ reducedExit: prefersReducedMotion,
938
+ children: shownReview && jsxs(ReviewBar, {
939
+ "data-testid": "agent-input-review",
940
+ children: [jsx(Typography, {
941
+ variant: "small",
942
+ color: "default",
943
+ children: shownReview.state === 'proposal' ? shownReview.summary : 'Changes applied'
944
+ }), jsx(ReviewActions, {
945
+ children: shownReview.state === 'proposal' ? jsxs(Fragment, {
946
+ children: [jsx(Button, {
947
+ variant: "primary",
948
+ size: "small",
949
+ onClick: shownReview.onAccept,
950
+ children: "Accept"
951
+ }), jsx(Button, {
952
+ variant: "secondary",
953
+ size: "small",
954
+ onClick: function onClick() {
955
+ return onReviewDiscard(shownReview.onDiscard);
956
+ },
957
+ children: "Discard"
958
+ })]
959
+ }) : jsx(Button, {
960
+ variant: "naked",
961
+ size: "small",
962
+ onClick: function onClick() {
963
+ return onReviewUndo(shownReview.onUndo);
964
+ },
965
+ children: "Undo"
966
+ })
967
+ })]
968
+ })
969
+ }), jsx(RegionPart, {
970
+ show: showSuggestions,
971
+ reducedExit: prefersReducedMotion,
972
+ children: jsx(SuggestionList, {
973
+ "aria-label": "Suggestions",
974
+ "data-testid": "agent-input-suggestions",
975
+ children: shownSuggestions.map(function (suggestion, index) {
976
+ return jsx("li", {
977
+ children: jsx(SuggestionChip, {
978
+ onClick: function onClick() {
979
+ return onPickSuggestion(suggestion);
980
+ },
981
+ children: suggestion
982
+ })
983
+ }, "".concat(index, "-").concat(suggestion));
984
+ })
985
+ })
986
+ }), jsx(VisuallyHidden, {
987
+ "aria-live": "polite",
988
+ "aria-atomic": "true",
989
+ "data-testid": "agent-input-review-announcement",
990
+ children: reviewAnnouncement
991
+ }), jsx(InputWrapper, {
661
992
  children: jsxs(InputShell, {
662
- "data-disabled": disabled || effectiveSubmitting,
993
+ "data-disabled": disabled,
663
994
  children: [jsx(Input, {
995
+ ref: inputRef,
664
996
  id: inputId,
665
- type: "text",
997
+ rows: 1,
998
+ enterKeyHint: "send",
666
999
  "aria-label": label,
667
1000
  value: displayedValue,
668
1001
  onChange: onTextChange,
669
1002
  onKeyDown: onInputKeyDown,
670
1003
  placeholder: placeholder,
671
- disabled: disabled || effectiveSubmitting,
1004
+ disabled: disabled,
672
1005
  readOnly: isListening,
673
- "aria-describedby": statusId
1006
+ "aria-describedby": showCounter ? "".concat(statusId, " ").concat(counterId) : statusId,
1007
+ "aria-invalid": isOverLimit || undefined
674
1008
  }), jsxs(InputActions, {
675
1009
  children: [hasVoice && jsxs(MicButtonWrapper, {
676
1010
  "$isListening": isListening,
@@ -690,7 +1024,17 @@ var AgentInput = function AgentInput(_a) {
690
1024
  disabled: disabled || effectiveSubmitting,
691
1025
  type: "button"
692
1026
  })]
693
- }), jsx(CircularActionIconButton, {
1027
+ }), showStop ? jsx(CircularActionIconButton, {
1028
+ variant: "secondary",
1029
+ size: "medium",
1030
+ iconName: "stop",
1031
+ onClick: function onClick() {
1032
+ return onStop === null || onStop === void 0 ? void 0 : onStop();
1033
+ },
1034
+ disabled: disabled,
1035
+ "aria-label": "Stop",
1036
+ type: "button"
1037
+ }) : jsx(CircularActionIconButton, {
694
1038
  variant: "secondary",
695
1039
  size: "medium",
696
1040
  iconName: "directionUp",
@@ -703,7 +1047,7 @@ var AgentInput = function AgentInput(_a) {
703
1047
  })]
704
1048
  })]
705
1049
  })
706
- })
1050
+ })]
707
1051
  }), jsxs(StatusRow, {
708
1052
  children: [jsx(StatusText, {
709
1053
  "$isError": isStatusError,
@@ -712,24 +1056,44 @@ var AgentInput = function AgentInput(_a) {
712
1056
  role: "status",
713
1057
  "aria-live": "polite",
714
1058
  "aria-atomic": "true",
715
- children: jsx(Typography, {
1059
+ children: resolvedStatus && jsx(Typography, {
716
1060
  variant: "small",
717
1061
  color: isStatusError ? 'error' : 'subdued',
718
1062
  children: resolvedStatus
719
1063
  })
720
1064
  })
721
- }), isListening && !prefersReducedMotion && jsx(MeterContainer, {
722
- "aria-hidden": "true",
723
- children: meterBars.map(function (barHeight, index) {
724
- return jsx(MeterBar, {
725
- "$height": barHeight
726
- }, "meter-".concat(index));
727
- })
1065
+ }), jsxs(StatusEnd, {
1066
+ children: [isListening && !prefersReducedMotion && jsx(MeterContainer, {
1067
+ "aria-hidden": "true",
1068
+ children: meterBars.map(function (barHeight, index) {
1069
+ return jsx(MeterBar, {
1070
+ "$height": barHeight
1071
+ }, "meter-".concat(index));
1072
+ })
1073
+ }), showCounter && jsx(Typography, {
1074
+ variant: "small",
1075
+ color: isOverLimit ? 'error' : 'subdued',
1076
+ children: jsxs("span", {
1077
+ id: counterId,
1078
+ "data-testid": "agent-input-counter",
1079
+ children: [messageLength, "/", maxLength, jsx(VisuallyHidden, {
1080
+ children: " characters"
1081
+ })]
1082
+ })
1083
+ })]
1084
+ }), maxLength !== undefined && jsx(VisuallyHidden, {
1085
+ role: "status",
1086
+ "aria-live": "polite",
1087
+ "aria-atomic": "true",
1088
+ "data-testid": "agent-input-limit-announcement",
1089
+ children: limitAnnouncement
728
1090
  })]
729
1091
  })]
730
1092
  });
731
- };
732
- var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18;
1093
+ }
1094
+ var AgentInput = /*#__PURE__*/forwardRef(AgentInputWithRef);
1095
+ AgentInput.displayName = 'AgentInput';
1096
+ var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5, templateObject_6, templateObject_7, templateObject_8, templateObject_9, templateObject_10, templateObject_11, templateObject_12, templateObject_13, templateObject_14, templateObject_15, templateObject_16, templateObject_17, templateObject_18, templateObject_19, templateObject_20, templateObject_21, templateObject_22, templateObject_23, templateObject_24, templateObject_25, templateObject_26, templateObject_27, templateObject_28, templateObject_29, templateObject_30, templateObject_31, templateObject_32, templateObject_33;
733
1097
 
734
1098
  export { AgentInput };
735
1099
  //# sourceMappingURL=AgentInput.mjs.map