@opengeni/react 7.1.0 → 7.1.2-canary.36199476632001

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 (78) hide show
  1. package/dist/artifacts-document.js +2 -2
  2. package/dist/artifacts-presentation.js +2 -2
  3. package/dist/artifacts-spreadsheet.js +2 -2
  4. package/dist/artifacts.js +4 -4
  5. package/dist/{browser-viewer-MN7MOYQT.js → browser-viewer-K2H3SLIW.js} +2 -2
  6. package/dist/{chunk-KCGXPP5V.js → chunk-6P7T4RPE.js} +7 -3
  7. package/dist/chunk-6P7T4RPE.js.map +1 -0
  8. package/dist/{chunk-SHRCATV6.js → chunk-7J4DUR3E.js} +12 -5
  9. package/dist/chunk-7J4DUR3E.js.map +1 -0
  10. package/dist/{chunk-UQS7BGNQ.js → chunk-ETA37UZ7.js} +82 -11
  11. package/dist/chunk-ETA37UZ7.js.map +1 -0
  12. package/dist/{chunk-VR4Z73EU.js → chunk-G2SCXNXW.js} +9 -2
  13. package/dist/chunk-G2SCXNXW.js.map +1 -0
  14. package/dist/{chunk-5UONOQ3Y.js → chunk-K3MQZO7R.js} +150 -67
  15. package/dist/chunk-K3MQZO7R.js.map +1 -0
  16. package/dist/{chunk-OWHPKAOV.js → chunk-L6MYP3QU.js} +4 -2
  17. package/dist/{chunk-OWHPKAOV.js.map → chunk-L6MYP3QU.js.map} +1 -1
  18. package/dist/{chunk-ZC2DK5SC.js → chunk-RSFCYKA7.js} +88 -7
  19. package/dist/chunk-RSFCYKA7.js.map +1 -0
  20. package/dist/{chunk-VI7HELBI.js → chunk-SJ5HE4ZA.js} +21 -2
  21. package/dist/chunk-SJ5HE4ZA.js.map +1 -0
  22. package/dist/{chunk-4ACTWOOW.js → chunk-VDSZ3AVO.js} +41 -18
  23. package/dist/chunk-VDSZ3AVO.js.map +1 -0
  24. package/dist/{chunk-XMXRI5JK.js → chunk-ZZ4744OZ.js} +128 -71
  25. package/dist/chunk-ZZ4744OZ.js.map +1 -0
  26. package/dist/components/artifacts/artifact-surface.d.ts +3 -1
  27. package/dist/components/artifacts/document-editor.d.ts +4 -2
  28. package/dist/components/artifacts/editable-document.d.ts +1 -1
  29. package/dist/components/artifacts/editable-presentation.d.ts +1 -1
  30. package/dist/components/artifacts/editable-spreadsheet.d.ts +2 -1
  31. package/dist/components/artifacts/presentation-editor.d.ts +4 -2
  32. package/dist/components/browser-viewer.d.ts +4 -1
  33. package/dist/components/markdown.d.ts +2 -0
  34. package/dist/components/session-capability-frame.d.ts +5 -1
  35. package/dist/composer.js +1 -1
  36. package/dist/connect.js +2 -2
  37. package/dist/hooks/use-browser-session.d.ts +3 -0
  38. package/dist/index.js +7 -6
  39. package/dist/index.js.map +1 -1
  40. package/dist/interaction.js +1 -1
  41. package/dist/session-ui.js +4 -4
  42. package/dist/session.js +1 -1
  43. package/dist/timeline/types.d.ts +2 -0
  44. package/package.json +5 -5
  45. package/src/components/artifacts/artifact-surface.tsx +39 -19
  46. package/src/components/artifacts/document-editor.tsx +77 -9
  47. package/src/components/artifacts/editable-document.tsx +3 -0
  48. package/src/components/artifacts/editable-presentation.tsx +3 -0
  49. package/src/components/artifacts/editable-spreadsheet.tsx +3 -0
  50. package/src/components/artifacts/presentation-editor.tsx +6 -0
  51. package/src/components/browser-viewer.tsx +11 -5
  52. package/src/components/composer-transcription-control.tsx +4 -2
  53. package/src/components/composer.tsx +45 -20
  54. package/src/components/markdown.tsx +9 -2
  55. package/src/components/sandbox-workspace.tsx +1 -0
  56. package/src/components/session-capability-frame.tsx +11 -3
  57. package/src/components/session-mcp-capability-card.tsx +174 -70
  58. package/src/hooks/use-browser-frame-stream.ts +3 -1
  59. package/src/hooks/use-browser-session.ts +107 -3
  60. package/src/hooks/use-voice-input.ts +12 -1
  61. package/src/timeline/compaction-copy.ts +2 -0
  62. package/src/timeline/parsers.ts +2 -2
  63. package/src/timeline/projection.ts +31 -5
  64. package/src/timeline/tool-renderers.tsx +105 -16
  65. package/src/timeline/turn-summary.tsx +10 -2
  66. package/src/timeline/types.ts +2 -0
  67. package/styles/compiled.css +6 -0
  68. package/styles/session-capability.css +2 -0
  69. package/dist/chunk-4ACTWOOW.js.map +0 -1
  70. package/dist/chunk-5UONOQ3Y.js.map +0 -1
  71. package/dist/chunk-KCGXPP5V.js.map +0 -1
  72. package/dist/chunk-SHRCATV6.js.map +0 -1
  73. package/dist/chunk-UQS7BGNQ.js.map +0 -1
  74. package/dist/chunk-VI7HELBI.js.map +0 -1
  75. package/dist/chunk-VR4Z73EU.js.map +0 -1
  76. package/dist/chunk-XMXRI5JK.js.map +0 -1
  77. package/dist/chunk-ZC2DK5SC.js.map +0 -1
  78. /package/dist/{browser-viewer-MN7MOYQT.js.map → browser-viewer-K2H3SLIW.js.map} +0 -0
@@ -457,9 +457,9 @@ export function useChatComposerController({
457
457
  (event: DragEvent<HTMLDivElement>) => {
458
458
  if (!attachments || !dragCarriesFiles(event)) return;
459
459
  event.preventDefault();
460
- setDragging(true);
460
+ if (!disabled) setDragging(true);
461
461
  },
462
- [attachments],
462
+ [attachments, disabled],
463
463
  );
464
464
  const handleDragLeave = useCallback(
465
465
  (event: DragEvent<HTMLDivElement>) => {
@@ -474,11 +474,16 @@ export function useChatComposerController({
474
474
  if (!attachments || !dragCarriesFiles(event)) return;
475
475
  event.preventDefault();
476
476
  setDragging(false);
477
- if (event.dataTransfer.files.length > 0) attachments.addFiles(event.dataTransfer.files);
477
+ if (!disabled && event.dataTransfer.files.length > 0)
478
+ attachments.addFiles(event.dataTransfer.files);
478
479
  },
479
- [attachments],
480
+ [attachments, disabled],
480
481
  );
481
482
 
483
+ useEffect(() => {
484
+ if (disabled) setDragging(false);
485
+ }, [disabled]);
486
+
482
487
  const [notice, setNotice] = useState<Notice | null>(null);
483
488
  const [helpOpen, setHelpOpen] = useState(false);
484
489
  const [confirmState, setConfirmState] = useState<ConfirmState>(null);
@@ -624,17 +629,18 @@ export function useChatComposerController({
624
629
  );
625
630
  const handlePaste = useCallback(
626
631
  (event: ClipboardEvent<HTMLTextAreaElement>) => {
632
+ if (disabled) return;
627
633
  onPaste?.(event);
628
634
  attachments?.addFromPaste(event);
629
635
  },
630
- [attachments, onPaste],
636
+ [attachments, disabled, onPaste],
631
637
  );
632
638
  const handleFileChange = useCallback(
633
639
  (event: ChangeEvent<HTMLInputElement>) => {
634
- if (event.target.files) attachments?.addFiles(event.target.files);
640
+ if (!disabled && event.target.files) attachments?.addFiles(event.target.files);
635
641
  event.target.value = "";
636
642
  },
637
- [attachments],
643
+ [attachments, disabled],
638
644
  );
639
645
 
640
646
  const helpCommands = useMemo(
@@ -975,6 +981,7 @@ export function Attachments() {
975
981
  <AttachmentChips
976
982
  attachments={controller.attachments.attachments}
977
983
  messages={controller.messages}
984
+ disabled={controller.disabled}
978
985
  onRemove={controller.attachments.remove}
979
986
  onRetry={controller.attachments.retry}
980
987
  onRetainPreview={controller.attachments.retainPreview}
@@ -996,6 +1003,26 @@ type OwnedInputProps =
996
1003
  | "aria-keyshortcuts";
997
1004
  export type ComposerInputProps = Omit<ComponentPropsWithoutRef<"textarea">, OwnedInputProps>;
998
1005
 
1006
+ const EDITING_FOCUS_SELECTOR =
1007
+ 'input, textarea, select, [contenteditable="true"], [role="textbox"]';
1008
+ const POPUP_FOCUS_SELECTOR =
1009
+ '[role="menu"], [role="menubar"], [role="listbox"], [role="dialog"], [role="alertdialog"]';
1010
+
1011
+ /**
1012
+ * Initial focus is a convenience, not authority to move focus the person has
1013
+ * placed elsewhere. It yields to a field they started editing and to an open
1014
+ * menu, listbox, or dialog that does not contain the composer: moving focus
1015
+ * behind a non-modal popup dismisses it, and a modal surface owns focus until
1016
+ * it closes.
1017
+ */
1018
+ function focusBelongsElsewhere(textarea: HTMLTextAreaElement): boolean {
1019
+ const active = textarea.ownerDocument.activeElement;
1020
+ if (!active || active === textarea) return false;
1021
+ if (active.closest(EDITING_FOCUS_SELECTOR)) return true;
1022
+ const popup = active.closest(POPUP_FOCUS_SELECTOR);
1023
+ return popup !== null && !popup.contains(textarea);
1024
+ }
1025
+
999
1026
  export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(function ComposerInput(
1000
1027
  { rows = 1, placeholder, className, "aria-label": ariaLabel, autoFocus = false, ...props },
1001
1028
  forwardedRef,
@@ -1005,22 +1032,17 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
1005
1032
  const paletteOpen =
1006
1033
  controller.paletteEnabled && controller.paletteMounted && controller.palette.open;
1007
1034
 
1008
- // Native autoFocus loses when the textarea mounts disabled (create-session draft
1009
- // hydrate). Retry once the controller becomes interactive.
1035
+ // Focus once the controller is interactive. The textarea can mount disabled
1036
+ // (create-session draft hydrate), and the route can finish loading after the
1037
+ // person has already moved on, so this deliberately does not use the native
1038
+ // autoFocus attribute: that would take focus at mount without the guard.
1010
1039
  useEffect(() => {
1011
1040
  if (!autoFocus || controller.disabled || autoFocusedRef.current) return;
1012
1041
  const frame = window.requestAnimationFrame(() => {
1013
1042
  const textarea = controller.textareaRef.current;
1014
1043
  if (!textarea || textarea.disabled) return;
1015
1044
  autoFocusedRef.current = true;
1016
- // Draft hydration may finish after the user has started editing another
1017
- // field. Initial focus is a convenience, not authority to take their caret.
1018
- const active = textarea.ownerDocument.activeElement;
1019
- if (
1020
- active !== textarea &&
1021
- active?.closest('input, textarea, select, [contenteditable="true"], [role="textbox"]')
1022
- )
1023
- return;
1045
+ if (focusBelongsElsewhere(textarea)) return;
1024
1046
  textarea.focus();
1025
1047
  });
1026
1048
  return () => window.cancelAnimationFrame(frame);
@@ -1041,7 +1063,6 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
1041
1063
  : (placeholder ?? controller.messages.messagePlaceholder)
1042
1064
  }
1043
1065
  disabled={controller.disabled}
1044
- autoFocus={autoFocus && !controller.disabled}
1045
1066
  aria-label={ariaLabel ?? controller.messages.inputLabel}
1046
1067
  aria-keyshortcuts="Enter Meta+Enter Control+Enter Shift+Enter"
1047
1068
  aria-autocomplete={
@@ -1679,6 +1700,7 @@ function ConfirmBar({
1679
1700
  function AttachmentChips({
1680
1701
  attachments,
1681
1702
  messages,
1703
+ disabled,
1682
1704
  onRemove,
1683
1705
  onRetry,
1684
1706
  onRetainPreview,
@@ -1686,6 +1708,7 @@ function AttachmentChips({
1686
1708
  }: {
1687
1709
  attachments: UseFileAttachmentsResult["attachments"];
1688
1710
  messages: ChatComposerMessages;
1711
+ disabled: boolean;
1689
1712
  onRemove: (id: string) => void;
1690
1713
  onRetry?: ((id: string) => void) | undefined;
1691
1714
  onRetainPreview: UseFileAttachmentsResult["retainPreview"];
@@ -1837,7 +1860,8 @@ function AttachmentChips({
1837
1860
  <button
1838
1861
  type="button"
1839
1862
  onClick={() => onRetry(attachment.id)}
1840
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1863
+ disabled={disabled}
1864
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1841
1865
  aria-label={messages.retryAttachment(attachment.name)}
1842
1866
  >
1843
1867
  <RotateCwIcon className="size-3.5" />
@@ -1847,7 +1871,8 @@ function AttachmentChips({
1847
1871
  <button
1848
1872
  type="button"
1849
1873
  onClick={() => onRemove(attachment.id)}
1850
- className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg pointer-coarse:size-10"
1874
+ disabled={disabled}
1875
+ className="shrink-0 rounded-og-xs p-1 text-og-fg-muted hover:bg-og-surface-1 hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-10"
1851
1876
  aria-label={messages.removeAttachment(attachment.name)}
1852
1877
  >
1853
1878
  <XIcon className="size-3.5" />
@@ -53,6 +53,7 @@ const InteractiveContext = createContext<{
53
53
  streaming?: boolean | undefined;
54
54
  render?: (block: MarkdownInteractiveBlock) => ReactNode;
55
55
  renderImage?: (image: { src: string; alt: string }) => ReactNode;
56
+ suppressImages?: boolean;
56
57
  }>({ source: "" });
57
58
 
58
59
  export type MarkdownProps = {
@@ -61,6 +62,8 @@ export type MarkdownProps = {
61
62
  /** Reveals a bounded source excerpt; closing Find retains it until explicit restore. */
62
63
  searchTarget?: TimelineSearchTarget | null | undefined;
63
64
  renderImage?: ((image: { src: string; alt: string }) => ReactNode) | undefined;
65
+ /** Show alt text without loading image URLs (e.g. in search previews). */
66
+ suppressImages?: boolean | undefined;
64
67
  /** Host opt-in for assistant-authored interactive fences. */
65
68
  renderInteractiveBlock?: ((block: MarkdownInteractiveBlock) => ReactNode) | undefined;
66
69
  children: string;
@@ -399,7 +402,9 @@ export function retainedImageId(src: string): string | null {
399
402
  );
400
403
  }
401
404
  function MarkdownImage({ src, alt }: ComponentPropsWithoutRef<"img">) {
402
- const { renderImage } = useContext(InteractiveContext);
405
+ const { renderImage, suppressImages } = useContext(InteractiveContext);
406
+ if (suppressImages)
407
+ return <span data-og-image-placeholder="">{alt || "Image"} (preview unavailable)</span>;
403
408
  if (!src) return <span>{alt ?? "Image unavailable"}</span>;
404
409
  if (retainedImageId(src))
405
410
  return renderImage ? (
@@ -592,6 +597,7 @@ function MarkdownImpl({
592
597
  onSandboxFile,
593
598
  renderInteractiveBlock,
594
599
  renderImage,
600
+ suppressImages,
595
601
  }: MarkdownProps) {
596
602
  // Tip-ink engine for THIS body: created on the first streaming render, kept
597
603
  // through a short linger after the stream ends (so the last age window can
@@ -692,8 +698,9 @@ function MarkdownImpl({
692
698
  streaming,
693
699
  ...(renderInteractiveBlock ? { render: renderInteractiveBlock } : {}),
694
700
  ...(renderImage ? { renderImage } : {}),
701
+ ...(suppressImages ? { suppressImages } : {}),
695
702
  }),
696
- [children, streaming, renderInteractiveBlock, renderImage],
703
+ [children, streaming, renderInteractiveBlock, renderImage, suppressImages],
697
704
  );
698
705
 
699
706
  // `min-w-0` lets the prose shrink inside flex parents (message bubbles) so
@@ -843,6 +843,7 @@ export function useSandboxWorkspaceTabs(
843
843
  // start BrowserViewer—the lazy panel is still unmounted until the
844
844
  // user/agent first visits it.
845
845
  enabled={workspaceVisible}
846
+ active={resolvedActiveTab === WORKBENCH_TAB_BROWSER}
846
847
  onNotify={onNotify}
847
848
  initialBrowserSessionId={initialBrowserSessionId}
848
849
  onBrowserSessionIdChange={onBrowserSessionIdChange}
@@ -12,7 +12,11 @@ export type SessionCapabilityFrameProps = {
12
12
  skill: boolean;
13
13
  expanded: boolean;
14
14
  complete: boolean;
15
+ /** Completion copy must reflect the provider's actual scope of access. */
16
+ completeLabel?: string;
15
17
  actionLabel: string;
18
+ /** Direct navigation actions should not announce a dialog on the card. */
19
+ opensDialog?: boolean;
16
20
  note: string;
17
21
  onOpen(): void;
18
22
  onClose(): void;
@@ -48,7 +52,9 @@ export function SessionCapabilityFrame({
48
52
  skill,
49
53
  expanded,
50
54
  complete,
55
+ completeLabel,
51
56
  actionLabel,
57
+ opensDialog = true,
52
58
  note,
53
59
  onOpen,
54
60
  onClose,
@@ -90,7 +96,8 @@ export function SessionCapabilityFrame({
90
96
  </div>
91
97
  {complete ? (
92
98
  <p role="status" className="og-session-capability-copy">
93
- {skill ? "Installed · Workspace" : "Connected · Available in this conversation"}
99
+ {completeLabel ??
100
+ (skill ? "Installed · Workspace" : "Connected · Available in this conversation")}
94
101
  </p>
95
102
  ) : (
96
103
  <>
@@ -104,8 +111,9 @@ export function SessionCapabilityFrame({
104
111
  ref={buttonRef}
105
112
  type="button"
106
113
  onClick={onOpen}
107
- aria-haspopup="dialog"
108
- aria-expanded={expanded}
114
+ disabled={busy}
115
+ aria-haspopup={opensDialog ? "dialog" : undefined}
116
+ aria-expanded={opensDialog ? expanded : undefined}
109
117
  >
110
118
  {actionLabel}
111
119
  </button>