@opengeni/react 5.0.4 → 5.0.5-canary.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "5.0.4",
3
+ "version": "5.0.5-canary.0",
4
4
  "description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -133,8 +133,8 @@
133
133
  "@dnd-kit/utilities": "3.2.2",
134
134
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
135
135
  "@fontsource-variable/noto-sans-jp": "5.2.10",
136
- "@opengeni/connect": "^0.2.0",
137
- "@opengeni/sdk": "^5.0.3",
136
+ "@opengeni/connect": "^0.2.0-canary.2",
137
+ "@opengeni/sdk": "^5.0.5-canary.0",
138
138
  "clsx": "^2.1.1",
139
139
  "dequal": "2.0.3",
140
140
  "lucide-react": "^1.8.0",
@@ -1002,6 +1002,8 @@ export type SandboxWorkspaceProps = ClientOverride & {
1002
1002
  * omitted the workbench decides from the durable workspace capture. */
1003
1003
  initialTab?: string | undefined;
1004
1004
  onActiveTabChange?: ((activeTab: string) => void) | undefined;
1005
+ /** Open a built-in or host-injected tab. A new requestId reopens the same tab. */
1006
+ openTabRequest?: { tab: string; requestId: number } | null | undefined;
1005
1007
  initialFilePath?: string | null | undefined;
1006
1008
  onFilePathChange?: ((path: string | null) => void) | undefined;
1007
1009
  /** Presentation-only filter for Files tree nodes and selected-file viewing. */
@@ -1061,6 +1063,7 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
1061
1063
  trailingTabs,
1062
1064
  initialTab,
1063
1065
  onActiveTabChange,
1066
+ openTabRequest,
1064
1067
  initialFilePath,
1065
1068
  onFilePathChange,
1066
1069
  isFileNodeVisible,
@@ -1102,6 +1105,32 @@ export function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {
1102
1105
  const nextFileRequestId = useRef(0);
1103
1106
  const nextComputerRequestId = useRef(0);
1104
1107
  const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
1108
+ const handledTabRequest = useRef<{ sessionId: string; requestId: number } | null>(null);
1109
+ useEffect(() => {
1110
+ if (
1111
+ !openTabRequest ||
1112
+ (handledTabRequest.current?.sessionId === sessionId &&
1113
+ handledTabRequest.current.requestId === openTabRequest.requestId)
1114
+ )
1115
+ return;
1116
+ const available =
1117
+ [...(leadingTabs ?? []), ...(trailingTabs ?? [])].some(
1118
+ (tab) => tab.id === openTabRequest.tab,
1119
+ ) || (surfaces ?? WORKBENCH_SURFACES).some((tab) => tab === openTabRequest.tab);
1120
+ if (!available) return;
1121
+ handledTabRequest.current = { sessionId, requestId: openTabRequest.requestId };
1122
+ setStoredSelection({ sessionId, tab: openTabRequest.tab });
1123
+ onActiveTabChange?.(openTabRequest.tab);
1124
+ onCollapsedChange?.(false);
1125
+ }, [
1126
+ openTabRequest,
1127
+ sessionId,
1128
+ leadingTabs,
1129
+ trailingTabs,
1130
+ surfaces,
1131
+ onActiveTabChange,
1132
+ onCollapsedChange,
1133
+ ]);
1105
1134
  const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
1106
1135
  const openFile = useCallback(
1107
1136
  (path: string, line?: number | null) => {
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef, useState } from "react";
1
+ import { useEffect, useRef, useState, type ReactNode } from "react";
2
2
  import { copyTextToClipboard } from "./clipboard";
3
3
 
4
4
  export type DeviceAuthorizationProps = {
@@ -10,6 +10,13 @@ export type DeviceAuthorizationProps = {
10
10
  codeAttributes?: Record<`data-${string}`, string>;
11
11
  loadClipboard?: () => Promise<{ copyTextToClipboard(text: string): Promise<boolean> }>;
12
12
  onCopyResult?: (copied: boolean) => void;
13
+ /** Optional host presentation; clipboard state and URL validation remain shared. */
14
+ render?: (state: {
15
+ copied: boolean;
16
+ copyError: boolean;
17
+ copy: () => Promise<void>;
18
+ verificationHref: string | undefined;
19
+ }) => ReactNode;
13
20
  };
14
21
 
15
22
  /** Optional presentation for the existing model-account device APIs. It neither
@@ -61,6 +68,7 @@ export function DeviceAuthorization(props: DeviceAuthorizationProps) {
61
68
  } catch {
62
69
  /* A malformed provider URL must never become an executable link. */
63
70
  }
71
+ if (props.render) return props.render({ copied, copyError, copy, verificationHref: href });
64
72
  return (
65
73
  <section className={`og-connect ${props.className ?? ""}`} aria-label="Device authorization">
66
74
  <p>{props.description ?? `Enter this code at ${props.providerLabel ?? "the provider"}.`}</p>