@agentiffai/design 0.1.10 → 0.1.12

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.
@@ -217,7 +217,7 @@ declare const FileAttachment: React__default.FC<FileAttachmentProps>;
217
217
  interface ActionButtonType$1 {
218
218
  label: string;
219
219
  onClick: () => void;
220
- icon?: React__default.ReactNode;
220
+ icon?: React.ReactNode;
221
221
  }
222
222
  interface AssistantMessageProps {
223
223
  /** Message content to display */
@@ -241,20 +241,7 @@ interface AssistantMessageProps {
241
241
  /** Action buttons to display */
242
242
  actions?: ActionButtonType$1[];
243
243
  }
244
- /**
245
- * AssistantMessage component displays AI assistant messages with avatar
246
- *
247
- * Features:
248
- * - Avatar with image or initials fallback
249
- * - Loading state with animated dots
250
- * - Streaming state indicator
251
- * - Markdown support (optional)
252
- * - Dark gray bubble (#2D2D3D) from Figma design
253
- * - Left-aligned layout
254
- * - Action buttons support
255
- * - Timestamp display
256
- */
257
- declare const AssistantMessage: React__default.FC<AssistantMessageProps>;
244
+ declare const AssistantMessage: React$1.NamedExoticComponent<AssistantMessageProps>;
258
245
 
259
246
  interface MessagesProps {
260
247
  /**
@@ -440,17 +427,7 @@ interface StreamingTextProps {
440
427
  /** Additional CSS class name */
441
428
  className?: string;
442
429
  }
443
- /**
444
- * StreamingText component with typewriter animation
445
- *
446
- * Features:
447
- * - Character-by-character streaming animation
448
- * - Configurable typing speed
449
- * - Optional blinking cursor
450
- * - Code and markdown variants
451
- * - Completion callback
452
- */
453
- declare const StreamingText: React.FC<StreamingTextProps>;
430
+ declare const StreamingText: React$1.NamedExoticComponent<StreamingTextProps>;
454
431
 
455
432
  /**
456
433
  * StreamStatusIndicator Component
@@ -217,7 +217,7 @@ declare const FileAttachment: React__default.FC<FileAttachmentProps>;
217
217
  interface ActionButtonType$1 {
218
218
  label: string;
219
219
  onClick: () => void;
220
- icon?: React__default.ReactNode;
220
+ icon?: React.ReactNode;
221
221
  }
222
222
  interface AssistantMessageProps {
223
223
  /** Message content to display */
@@ -241,20 +241,7 @@ interface AssistantMessageProps {
241
241
  /** Action buttons to display */
242
242
  actions?: ActionButtonType$1[];
243
243
  }
244
- /**
245
- * AssistantMessage component displays AI assistant messages with avatar
246
- *
247
- * Features:
248
- * - Avatar with image or initials fallback
249
- * - Loading state with animated dots
250
- * - Streaming state indicator
251
- * - Markdown support (optional)
252
- * - Dark gray bubble (#2D2D3D) from Figma design
253
- * - Left-aligned layout
254
- * - Action buttons support
255
- * - Timestamp display
256
- */
257
- declare const AssistantMessage: React__default.FC<AssistantMessageProps>;
244
+ declare const AssistantMessage: React$1.NamedExoticComponent<AssistantMessageProps>;
258
245
 
259
246
  interface MessagesProps {
260
247
  /**
@@ -440,17 +427,7 @@ interface StreamingTextProps {
440
427
  /** Additional CSS class name */
441
428
  className?: string;
442
429
  }
443
- /**
444
- * StreamingText component with typewriter animation
445
- *
446
- * Features:
447
- * - Character-by-character streaming animation
448
- * - Configurable typing speed
449
- * - Optional blinking cursor
450
- * - Code and markdown variants
451
- * - Completion callback
452
- */
453
- declare const StreamingText: React.FC<StreamingTextProps>;
430
+ declare const StreamingText: React$1.NamedExoticComponent<StreamingTextProps>;
454
431
 
455
432
  /**
456
433
  * StreamStatusIndicator Component
@@ -4,8 +4,8 @@ var button = require('@react-aria/button');
4
4
  var react = require('react');
5
5
  var styled4 = require('styled-components');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
- var textfield = require('@react-aria/textfield');
8
7
  var reactUi = require('@copilotkit/react-ui');
8
+ var textfield = require('@react-aria/textfield');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
@@ -133,6 +133,8 @@ var tokens = {
133
133
  lg: "0.75rem",
134
134
  // 12px
135
135
  xl: "1rem",
136
+ // 16px
137
+ "2xl": "1.5rem",
136
138
  // 24px
137
139
  full: "9999px"
138
140
  // Fully rounded
@@ -633,7 +635,16 @@ var ActionExecutionAdapter = ({ message, inProgress }) => {
633
635
  return null;
634
636
  }
635
637
  const toolName = message.name;
636
- const toolArgs = message.arguments || {};
638
+ let toolArgs = {};
639
+ if (typeof message.arguments === "string") {
640
+ try {
641
+ toolArgs = JSON.parse(message.arguments);
642
+ } catch {
643
+ toolArgs = {};
644
+ }
645
+ } else if (message.arguments && typeof message.arguments === "object") {
646
+ toolArgs = message.arguments;
647
+ }
637
648
  const state = inProgress ? "thinking" : "responding";
638
649
  const statusMessage = getToolMessage(toolName, toolArgs);
639
650
  return /* @__PURE__ */ jsxRuntime.jsxs(Container, { "data-testid": "action-execution-message", children: [
@@ -821,6 +832,9 @@ var Container3 = styled4__default.default.div`
821
832
  white-space: pre-wrap;
822
833
  word-break: break-word;
823
834
  line-height: ${tokens.typography.lineHeight.normal};
835
+ /* Performance optimizations for streaming text */
836
+ text-rendering: optimizeSpeed;
837
+ contain: content;
824
838
  `;
825
839
  var Cursor = styled4__default.default.span`
826
840
  display: inline-block;
@@ -842,7 +856,7 @@ var Cursor = styled4__default.default.span`
842
856
  }
843
857
  }
844
858
  `;
845
- var StreamingText2 = ({
859
+ var StreamingTextBase = ({
846
860
  content,
847
861
  isStreaming = false,
848
862
  typingSpeed: _typingSpeed = 50,
@@ -852,25 +866,27 @@ var StreamingText2 = ({
852
866
  onStreamComplete,
853
867
  className
854
868
  }) => {
855
- const [displayedText, setDisplayedText] = react.useState("");
869
+ const [displayedText, setDisplayedText] = react.useState(content);
870
+ const wasStreamingRef = react.useRef(isStreaming);
871
+ const completionCalledRef = react.useRef(false);
856
872
  react.useEffect(() => {
857
- if (isStreaming) {
858
- setDisplayedText(content);
859
- return;
873
+ setDisplayedText(content);
874
+ if (wasStreamingRef.current && !isStreaming && !completionCalledRef.current) {
875
+ completionCalledRef.current = true;
876
+ onStreamComplete?.();
860
877
  }
861
- if (!isStreaming && displayedText !== content) {
862
- setDisplayedText(content);
863
- if (onStreamComplete) {
864
- onStreamComplete();
865
- }
878
+ if (isStreaming) {
879
+ completionCalledRef.current = false;
866
880
  }
867
- }, [content, isStreaming, onStreamComplete, displayedText]);
881
+ wasStreamingRef.current = isStreaming;
882
+ }, [content, isStreaming, onStreamComplete]);
868
883
  const showCursor = isStreaming && cursor;
869
884
  return /* @__PURE__ */ jsxRuntime.jsxs(Container3, { variant, className, children: [
870
885
  displayedText,
871
886
  showCursor && /* @__PURE__ */ jsxRuntime.jsx(Cursor, {})
872
887
  ] });
873
888
  };
889
+ var StreamingText2 = react.memo(StreamingTextBase);
874
890
  StreamingText2.displayName = "StreamingText";
875
891
  var MessageContainer = styled4__default.default.div`
876
892
  display: flex;
@@ -878,20 +894,9 @@ var MessageContainer = styled4__default.default.div`
878
894
  align-items: flex-start;
879
895
  padding: ${tokens.spacing.sm} 0;
880
896
  max-width: 100%;
881
-
882
- /* Fade in and slide up animation */
883
- animation: fadeInSlideUp ${tokens.animation.duration.medium} ease-out;
884
-
885
- @keyframes fadeInSlideUp {
886
- from {
887
- opacity: 0;
888
- transform: translateY(${tokens.spacing.sm});
889
- }
890
- to {
891
- opacity: 1;
892
- transform: translateY(0);
893
- }
894
- }
897
+ /* GPU acceleration hint for smooth rendering during streaming */
898
+ will-change: contents;
899
+ contain: content;
895
900
  `;
896
901
  var AvatarContainer = styled4__default.default.div`
897
902
  flex-shrink: 0;
@@ -926,9 +931,8 @@ var ContentContainer = styled4__default.default.div`
926
931
  min-width: 0;
927
932
  `;
928
933
  var MessageContent = styled4__default.default.div`
929
- background-color: ${tokens.colors.surface.glass};
930
- backdrop-filter: blur(10px);
931
- -webkit-backdrop-filter: blur(10px);
934
+ /* Use solid color instead of backdrop-filter for performance during streaming */
935
+ background-color: ${tokens.colors.surface.elevated};
932
936
  padding: ${tokens.spacing.sm} ${tokens.spacing.md};
933
937
  border-radius: ${tokens.borderRadius.lg};
934
938
  border-top-left-radius: ${tokens.borderRadius.sm};
@@ -938,24 +942,25 @@ var MessageContent = styled4__default.default.div`
938
942
  font-family: ${tokens.typography.fontFamily.primary};
939
943
  word-wrap: break-word;
940
944
  white-space: pre-wrap;
945
+ /* Optimize text rendering */
946
+ text-rendering: optimizeSpeed;
941
947
  `;
942
948
  var LoadingDots3 = styled4__default.default.div`
943
949
  display: flex;
944
950
  gap: ${tokens.spacing.xs};
945
951
  padding: ${tokens.spacing.sm} ${tokens.spacing.md};
946
- background-color: ${tokens.colors.surface.glass};
947
- backdrop-filter: blur(10px);
948
- -webkit-backdrop-filter: blur(10px);
952
+ /* Use solid color instead of backdrop-filter for performance */
953
+ background-color: ${tokens.colors.surface.elevated};
949
954
  border-radius: ${tokens.borderRadius.lg};
950
955
  border-top-left-radius: ${tokens.borderRadius.sm};
951
956
  width: fit-content;
952
957
  `;
953
- var bounce = styled4.keyframes`
954
- 0%, 60%, 100% {
955
- transform: translateY(0);
958
+ var pulse2 = styled4.keyframes`
959
+ 0%, 100% {
960
+ opacity: 0.4;
956
961
  }
957
- 30% {
958
- transform: translateY(-${tokens.spacing.sm});
962
+ 50% {
963
+ opacity: 1;
959
964
  }
960
965
  `;
961
966
  var LoadingDot = styled4__default.default.div`
@@ -963,7 +968,8 @@ var LoadingDot = styled4__default.default.div`
963
968
  height: ${tokens.spacing.sm};
964
969
  border-radius: ${tokens.borderRadius.full};
965
970
  background-color: ${tokens.colors.text.tertiary};
966
- animation: ${bounce} 1.4s ease-in-out infinite;
971
+ /* Use simple opacity animation instead of transform bounce */
972
+ animation: ${pulse2} 1.4s ease-in-out infinite;
967
973
  animation-delay: ${(props) => props.delay}s;
968
974
  `;
969
975
  var FileAttachmentContainer = styled4__default.default.div`
@@ -1158,7 +1164,7 @@ var StreamingIndicator = styled4__default.default.span`
1158
1164
  }
1159
1165
  }
1160
1166
  `;
1161
- var AssistantMessage = ({
1167
+ var AssistantMessageBase = ({
1162
1168
  content = "",
1163
1169
  avatarUrl,
1164
1170
  avatarInitials: _avatarInitials = "AI",
@@ -1205,7 +1211,9 @@ var AssistantMessage = ({
1205
1211
  /* @__PURE__ */ jsxRuntime.jsx(ContentContainer, { children: renderContent() })
1206
1212
  ] });
1207
1213
  };
1208
- var AssistantMessageAdapter = ({
1214
+ var AssistantMessage = react.memo(AssistantMessageBase);
1215
+ AssistantMessage.displayName = "AssistantMessage";
1216
+ var AssistantMessageAdapterBase = ({
1209
1217
  message,
1210
1218
  isLoading,
1211
1219
  isGenerating
@@ -1235,6 +1243,7 @@ var AssistantMessageAdapter = ({
1235
1243
  }
1236
1244
  );
1237
1245
  };
1246
+ var AssistantMessageAdapter = react.memo(AssistantMessageAdapterBase);
1238
1247
  AssistantMessageAdapter.displayName = "AssistantMessageAdapter";
1239
1248
  var ChatInputContainer = styled4__default.default.div`
1240
1249
  display: flex;
@@ -1268,7 +1277,7 @@ var SuggestionButton = styled4__default.default.button`
1268
1277
  font-weight: ${tokens.typography.fontWeight.regular};
1269
1278
  text-align: center;
1270
1279
  border: 1px solid ${tokens.colors.border.default};
1271
- border-radius: 20px;
1280
+ border-radius: ${tokens.borderRadius.full};
1272
1281
  cursor: pointer;
1273
1282
  transition: all ${tokens.transitions.fast};
1274
1283
  white-space: nowrap;
@@ -1302,9 +1311,9 @@ var SuggestionButton = styled4__default.default.button`
1302
1311
  `;
1303
1312
  var InputWrapper = styled4__default.default.div`
1304
1313
  display: flex;
1305
- align-items: center;
1306
- gap: 10px;
1307
- padding: 10px 14px;
1314
+ align-items: flex-end; /* Align button to bottom when textarea expands */
1315
+ gap: ${tokens.spacing.sm};
1316
+ padding: ${tokens.spacing.sm} ${tokens.spacing.md};
1308
1317
  background-color: rgba(25, 25, 25, 0.4);
1309
1318
  border: 1px solid ${tokens.colors.border.subtle};
1310
1319
  border-radius: ${tokens.borderRadius.lg};
@@ -1318,7 +1327,7 @@ var InputWrapper = styled4__default.default.div`
1318
1327
  background-color: rgba(50, 50, 52, 0.6);
1319
1328
  }
1320
1329
  `;
1321
- var InputField = styled4__default.default.input`
1330
+ var InputField = styled4__default.default.textarea`
1322
1331
  flex: 1;
1323
1332
  border: none;
1324
1333
  outline: none;
@@ -1326,6 +1335,13 @@ var InputField = styled4__default.default.input`
1326
1335
  font-size: ${tokens.typography.fontSize.sm};
1327
1336
  color: ${tokens.colors.text.secondary};
1328
1337
  background: transparent;
1338
+ resize: none; /* Disable manual resize, we auto-resize */
1339
+ min-height: 24px; /* Single line height */
1340
+ max-height: 150px; /* Max ~6 lines before scrolling */
1341
+ overflow-y: auto;
1342
+ line-height: 1.5;
1343
+ padding: 0;
1344
+ margin: 0;
1329
1345
 
1330
1346
  &::placeholder {
1331
1347
  color: ${tokens.colors.text.tertiary};
@@ -1381,31 +1397,29 @@ var ChatInput = ({
1381
1397
  isReadOnly = false,
1382
1398
  autoFocus = false,
1383
1399
  className,
1384
- "aria-label": ariaLabel = "Chat message input",
1385
- ...ariaProps
1400
+ "aria-label": ariaLabel = "Chat message input"
1401
+ // Note: Additional AriaTextFieldProps are accepted but not spread to textarea
1402
+ // since useTextField is not compatible with textarea elements
1386
1403
  }) => {
1387
1404
  const [internalValue, setInternalValue] = react.useState(value);
1388
1405
  const inputRef = react.useRef(null);
1389
1406
  const currentValue = value !== void 0 ? value : internalValue;
1390
1407
  const setValue = onChange || setInternalValue;
1391
- const { inputProps } = textfield.useTextField(
1392
- {
1393
- ...ariaProps,
1394
- "aria-label": ariaLabel,
1395
- value: currentValue,
1396
- onChange: (newValue) => {
1397
- setValue(newValue);
1398
- },
1399
- isDisabled,
1400
- isReadOnly
1401
- },
1402
- inputRef
1403
- );
1408
+ const autoResize = react.useCallback(() => {
1409
+ const textarea = inputRef.current;
1410
+ if (textarea) {
1411
+ textarea.style.height = "auto";
1412
+ textarea.style.height = `${textarea.scrollHeight}px`;
1413
+ }
1414
+ }, []);
1415
+ react.useEffect(() => {
1416
+ autoResize();
1417
+ }, [currentValue, autoResize]);
1404
1418
  const handleDirectChange = (e) => {
1405
1419
  setValue(e.target.value);
1406
1420
  };
1407
1421
  const handleKeyDown = (e) => {
1408
- if (e.key === "Enter") {
1422
+ if (e.key === "Enter" && !e.shiftKey) {
1409
1423
  e.preventDefault();
1410
1424
  handleSubmit();
1411
1425
  }
@@ -1414,6 +1428,9 @@ var ChatInput = ({
1414
1428
  if (currentValue.trim() && onSubmit && !isDisabled && !isReadOnly) {
1415
1429
  onSubmit(currentValue.trim());
1416
1430
  setValue("");
1431
+ if (inputRef.current) {
1432
+ inputRef.current.style.height = "auto";
1433
+ }
1417
1434
  inputRef.current?.focus();
1418
1435
  }
1419
1436
  };
@@ -1437,16 +1454,16 @@ var ChatInput = ({
1437
1454
  /* @__PURE__ */ jsxRuntime.jsx(
1438
1455
  InputField,
1439
1456
  {
1440
- ...inputProps,
1441
1457
  ref: inputRef,
1442
- type: "text",
1458
+ "aria-label": ariaLabel,
1443
1459
  placeholder,
1444
1460
  onKeyDown: handleKeyDown,
1445
1461
  onChange: handleDirectChange,
1446
1462
  autoFocus,
1447
1463
  disabled: isDisabled,
1448
1464
  readOnly: isReadOnly,
1449
- value: currentValue
1465
+ value: currentValue,
1466
+ rows: 1
1450
1467
  }
1451
1468
  ),
1452
1469
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1594,7 +1611,7 @@ var StyledUserMessage = styled4__default.default.button`
1594
1611
  && {
1595
1612
  background: ${tokens.colors.message.user} !important;
1596
1613
  color: ${tokens.colors.text.primary} !important;
1597
- border-radius: ${tokens.borderRadius.full} !important; /* Pill shape - fully rounded ends */
1614
+ border-radius: ${tokens.borderRadius["2xl"]} !important; /* Rounded corners that work for multi-line */
1598
1615
  }
1599
1616
 
1600
1617
  /* Subtle shadow and glow effect */
@@ -1674,7 +1691,7 @@ var StyledUserMessage = styled4__default.default.button`
1674
1691
  }
1675
1692
  }
1676
1693
  `;
1677
- function UserMessage({
1694
+ function UserMessageBase({
1678
1695
  children,
1679
1696
  className,
1680
1697
  isPressed = false,
@@ -1705,28 +1722,17 @@ function UserMessage({
1705
1722
  }
1706
1723
  );
1707
1724
  }
1725
+ var UserMessage = react.memo(UserMessageBase);
1708
1726
  UserMessage.displayName = "UserMessage";
1709
1727
  var UserMessageWrapper = styled4__default.default.div`
1710
1728
  display: flex;
1711
1729
  justify-content: flex-end;
1712
1730
  width: 100%;
1713
1731
  padding: ${tokens.spacing.sm} 0;
1714
-
1715
- /* Fade in and slide up animation */
1716
- animation: fadeInSlideUp ${tokens.animation.duration.medium} ease-out;
1717
-
1718
- @keyframes fadeInSlideUp {
1719
- from {
1720
- opacity: 0;
1721
- transform: translateY(${tokens.spacing.sm});
1722
- }
1723
- to {
1724
- opacity: 1;
1725
- transform: translateY(0);
1726
- }
1727
- }
1732
+ /* Performance optimization - isolate layout/paint */
1733
+ contain: content;
1728
1734
  `;
1729
- var UserMessageAdapter = ({
1735
+ var UserMessageAdapterBase = ({
1730
1736
  message,
1731
1737
  ImageRenderer
1732
1738
  }) => {
@@ -1736,8 +1742,9 @@ var UserMessageAdapter = ({
1736
1742
  const content = message?.content || "";
1737
1743
  return /* @__PURE__ */ jsxRuntime.jsx(UserMessageWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(UserMessage, { children: content }) });
1738
1744
  };
1745
+ var UserMessageAdapter = react.memo(UserMessageAdapterBase);
1739
1746
  UserMessageAdapter.displayName = "UserMessageAdapter";
1740
- var pulse2 = styled4.keyframes`
1747
+ var pulse3 = styled4.keyframes`
1741
1748
  0%, 100% {
1742
1749
  opacity: 1;
1743
1750
  transform: scale(1);
@@ -1821,7 +1828,7 @@ var StatusDot = styled4__default.default.div`
1821
1828
  return tokens.colors.connection.reconnecting;
1822
1829
  }
1823
1830
  }};
1824
- animation: ${(props) => props.status === "streaming" ? pulse2 : "none"} 2s ease-in-out infinite;
1831
+ animation: ${(props) => props.status === "streaming" ? pulse3 : "none"} 2s ease-in-out infinite;
1825
1832
  flex-shrink: 0;
1826
1833
  `;
1827
1834
  var Label = styled4__default.default.span`
@@ -2337,8 +2344,8 @@ var StyledChatButton = styled4__default.default.button`
2337
2344
  height: 36px;
2338
2345
  border-radius: 18px;
2339
2346
  border: none;
2340
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2341
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
2347
+ background: linear-gradient(135deg, ${tokens.colors.primary} 0%, ${tokens.colors.accent} 100%);
2348
+ box-shadow: 0 4px 12px rgba(44, 176, 171, 0.4);
2342
2349
  display: flex;
2343
2350
  align-items: center;
2344
2351
  justify-content: center;
@@ -2348,7 +2355,7 @@ var StyledChatButton = styled4__default.default.button`
2348
2355
 
2349
2356
  &:hover {
2350
2357
  transform: scale(1.05);
2351
- box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
2358
+ box-shadow: 0 6px 16px rgba(44, 176, 171, 0.5);
2352
2359
  }
2353
2360
 
2354
2361
  &:active {
@@ -2460,16 +2467,24 @@ var CustomCopilotSidebar = ({
2460
2467
  };
2461
2468
  CustomCopilotSidebar.displayName = "CustomCopilotSidebar";
2462
2469
  var GlobalSidebarStyles2 = styled4.createGlobalStyle`
2470
+ /* Override CopilotKit's content wrapper to respect safe areas in landscape */
2471
+ @media (orientation: landscape) {
2472
+ .copilotKitSidebarContentWrapper {
2473
+ right: env(safe-area-inset-right, 0px) !important;
2474
+ left: env(safe-area-inset-left, 0px) !important;
2475
+ }
2476
+ }
2477
+
2463
2478
  /* Override CopilotKit's default positioning - keep sidebar container in place */
2464
2479
  /* z-index must be higher than NavHorizontal (sticky: 1020) for button to show above nav */
2465
2480
  .copilotKitSidebar {
2466
2481
  position: fixed !important;
2467
- top: 0 !important;
2468
- right: 0 !important;
2469
- bottom: 0 !important;
2470
- left: 0 !important;
2471
- width: 100vw !important;
2472
- height: 100vh !important;
2482
+ top: calc(var(--safe-area-top, 0px) + 8px) !important;
2483
+ right: calc(var(--safe-area-right, 0px) + 8px) !important;
2484
+ bottom: calc(var(--safe-area-bottom, 0px) + 8px) !important;
2485
+ left: calc(var(--safe-area-left, 0px) + 8px) !important;
2486
+ width: auto !important;
2487
+ height: auto !important;
2473
2488
  pointer-events: none !important;
2474
2489
  z-index: ${tokens.zIndex.fixed} !important;
2475
2490
  }
@@ -2484,16 +2499,19 @@ var GlobalSidebarStyles2 = styled4.createGlobalStyle`
2484
2499
  .copilotKitSidebar [role="dialog"] {
2485
2500
  /* Override CopilotKit defaults for mobile */
2486
2501
  position: fixed !important;
2487
- top: ${tokens.spacing.sm} !important;
2488
- right: ${tokens.spacing.sm} !important;
2489
- /* Reserve space for Android nav buttons - matches NavHorizontal approach */
2490
- bottom: max(90px, env(safe-area-inset-bottom, 90px)) !important;
2491
- left: ${tokens.spacing.sm} !important;
2492
- width: calc(100vw - ${tokens.spacing.lg}) !important;
2493
- /* Adjust height to account for bottom safe area */
2494
- height: calc(100vh - ${tokens.spacing.lg} - max(80px, env(safe-area-inset-bottom, 80px))) !important;
2495
- max-width: calc(100vw - ${tokens.spacing.lg}) !important;
2496
- max-height: calc(100vh - ${tokens.spacing.lg} - max(80px, env(safe-area-inset-bottom, 80px))) !important;
2502
+ /* TOP: Account for status bar safe area (Android notch/status bar) */
2503
+ top: calc(${tokens.spacing.sm} + env(safe-area-inset-top, 0px)) !important;
2504
+ /* RIGHT: Account for landscape notch on right side */
2505
+ right: calc(${tokens.spacing.sm} + env(safe-area-inset-right, 0px)) !important;
2506
+ /* BOTTOM: Account for horizontal navbar (52px) + safe area for Android nav buttons */
2507
+ bottom: calc(52px + ${tokens.spacing.sm} + env(safe-area-inset-bottom, 0px)) !important;
2508
+ /* LEFT: Account for landscape notch on left side */
2509
+ left: calc(${tokens.spacing.sm} + env(safe-area-inset-left, 0px)) !important;
2510
+ width: calc(100vw - ${tokens.spacing.lg} - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)) !important;
2511
+ /* Adjust height to account for top, bottom safe areas, and navbar */
2512
+ height: calc(100vh - ${tokens.spacing.lg} - env(safe-area-inset-top, 0px) - 52px - env(safe-area-inset-bottom, 0px)) !important;
2513
+ max-width: calc(100vw - ${tokens.spacing.lg} - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)) !important;
2514
+ max-height: calc(100vh - ${tokens.spacing.lg} - env(safe-area-inset-top, 0px) - 52px - env(safe-area-inset-bottom, 0px)) !important;
2497
2515
  margin: 0 !important;
2498
2516
  border-radius: ${tokens.borderRadius.xl} !important;
2499
2517
 
@@ -2529,16 +2547,18 @@ var GlobalSidebarStyles2 = styled4.createGlobalStyle`
2529
2547
  @media (min-width: ${tokens.breakpoints.mobile}px) {
2530
2548
  .copilotKitSidebar [role="dialog"] {
2531
2549
  inset: auto !important;
2532
- top: ${tokens.spacing.sm} !important;
2533
- right: ${tokens.spacing.sm} !important;
2534
- /* Reserve space for Android nav buttons on desktop too */
2535
- bottom: max(90px, env(safe-area-inset-bottom, 90px)) !important;
2550
+ /* TOP: Account for status bar safe area */
2551
+ top: calc(${tokens.spacing.sm} + env(safe-area-inset-top, 0px)) !important;
2552
+ /* RIGHT: Account for landscape notch on right side */
2553
+ right: calc(${tokens.spacing.sm} + env(safe-area-inset-right, 0px)) !important;
2554
+ /* BOTTOM: Account for horizontal navbar (52px) + safe area */
2555
+ bottom: calc(52px + ${tokens.spacing.sm} + env(safe-area-inset-bottom, 0px)) !important;
2536
2556
  left: auto !important;
2537
2557
  width: 28rem !important;
2538
- /* Adjust height to account for bottom safe area */
2539
- height: calc(100vh - ${tokens.spacing.lg} - max(80px, env(safe-area-inset-bottom, 80px))) !important;
2558
+ /* Adjust height to account for top, bottom safe areas, and navbar */
2559
+ height: calc(100vh - ${tokens.spacing.lg} - env(safe-area-inset-top, 0px) - 52px - env(safe-area-inset-bottom, 0px)) !important;
2540
2560
  max-width: 28rem !important;
2541
- max-height: calc(100vh - ${tokens.spacing.lg} - max(80px, env(safe-area-inset-bottom, 80px))) !important;
2561
+ max-height: calc(100vh - ${tokens.spacing.lg} - env(safe-area-inset-top, 0px) - 52px - env(safe-area-inset-bottom, 0px)) !important;
2542
2562
  }
2543
2563
  }
2544
2564
 
@@ -2558,25 +2578,27 @@ var GlobalSidebarStyles2 = styled4.createGlobalStyle`
2558
2578
  }
2559
2579
  }
2560
2580
 
2581
+ /* Position chat button with safe area on landscape (right side for Android controls) */
2582
+ @media (orientation: landscape) {
2583
+ .copilotKitSidebarContentWrapper {
2584
+ button[aria-label="Open chat"],
2585
+ button[aria-label="Close chat"] {
2586
+ bottom: calc(var(--safe-area-bottom, 0px) + 8px) !important;
2587
+ right: calc(var(--safe-area-right, 0px) + 8px) !important;
2588
+ z-index: 2000 !important;
2589
+ }
2590
+ }
2591
+ }
2592
+
2561
2593
  /* Fix messages container background */
2562
2594
  .copilotKitMessages {
2563
2595
  background-color: transparent !important;
2564
2596
  }
2565
2597
 
2566
- /* Fade-in animation for new messages */
2567
- @keyframes messagesFadeIn {
2568
- from {
2569
- opacity: 0;
2570
- transform: translateY(${tokens.spacing.sm});
2571
- }
2572
- to {
2573
- opacity: 1;
2574
- transform: translateY(0);
2575
- }
2576
- }
2577
-
2598
+ /* Performance optimization for messages - no animations to reduce jitter during streaming */
2578
2599
  .copilotKitMessage {
2579
- animation: messagesFadeIn 0.3s ease-out;
2600
+ /* contain: content isolates layout/paint to this element */
2601
+ contain: content;
2580
2602
  }
2581
2603
 
2582
2604
  /* Override CopilotKit suggestions to be single-row with horizontal scroll */
@@ -2628,8 +2650,8 @@ var GlobalSidebarStyles2 = styled4.createGlobalStyle`
2628
2650
  `;
2629
2651
  var StyledChatButton2 = styled4__default.default.button`
2630
2652
  position: fixed;
2631
- bottom: ${tokens.spacing.sm};
2632
- right: ${tokens.spacing.sm};
2653
+ bottom: calc(${tokens.spacing.sm} + env(safe-area-inset-bottom, 0px));
2654
+ right: calc(${tokens.spacing.sm} + env(safe-area-inset-right, 0px));
2633
2655
  width: 36px;
2634
2656
  height: 36px;
2635
2657
  border-radius: ${tokens.borderRadius.full};