@datalayer/agent-runtimes 0.0.7 → 0.0.8

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 (103) hide show
  1. package/README.md +9 -0
  2. package/lib/components/chat/components/AgentDetails.d.ts +14 -1
  3. package/lib/components/chat/components/AgentDetails.js +3 -2
  4. package/lib/components/chat/components/AgentIdentity.d.ts +92 -0
  5. package/lib/components/chat/components/AgentIdentity.js +318 -0
  6. package/lib/components/chat/components/Chat.d.ts +20 -1
  7. package/lib/components/chat/components/Chat.js +16 -3
  8. package/lib/components/chat/components/ChatFloating.d.ts +6 -1
  9. package/lib/components/chat/components/ChatFloating.js +12 -6
  10. package/lib/components/chat/components/base/ChatBase.d.ts +47 -1
  11. package/lib/components/chat/components/base/ChatBase.js +242 -63
  12. package/lib/components/chat/components/display/ToolCallDisplay.d.ts +16 -2
  13. package/lib/components/chat/components/display/ToolCallDisplay.js +148 -6
  14. package/lib/components/chat/components/display/index.d.ts +1 -1
  15. package/lib/components/chat/components/display/index.js +1 -1
  16. package/lib/components/chat/components/elements/ChatInputPrompt.d.ts +12 -1
  17. package/lib/components/chat/components/elements/ChatInputPrompt.js +8 -3
  18. package/lib/components/chat/components/index.d.ts +1 -0
  19. package/lib/components/chat/components/index.js +1 -0
  20. package/lib/components/chat/components/parts/ToolPart.d.ts +1 -1
  21. package/lib/components/chat/components/parts/ToolPart.js +142 -6
  22. package/lib/components/chat/index.d.ts +1 -1
  23. package/lib/components/chat/index.js +1 -1
  24. package/lib/components/chat/protocols/A2AAdapter.d.ts +9 -0
  25. package/lib/components/chat/protocols/A2AAdapter.js +13 -2
  26. package/lib/components/chat/protocols/ACPAdapter.d.ts +9 -0
  27. package/lib/components/chat/protocols/ACPAdapter.js +13 -2
  28. package/lib/components/chat/protocols/AGUIAdapter.d.ts +9 -0
  29. package/lib/components/chat/protocols/AGUIAdapter.js +19 -1
  30. package/lib/components/chat/protocols/VercelAIAdapter.d.ts +7 -0
  31. package/lib/components/chat/protocols/VercelAIAdapter.js +19 -0
  32. package/lib/components/chat/types/execution.d.ts +78 -0
  33. package/lib/components/chat/types/execution.js +64 -0
  34. package/lib/components/chat/types/index.d.ts +1 -0
  35. package/lib/components/chat/types/index.js +1 -0
  36. package/lib/components/chat/types/protocol.d.ts +9 -0
  37. package/lib/components/ui/pagination.d.ts +2 -2
  38. package/lib/components/ui/pagination.js +4 -4
  39. package/lib/components/ui/resizable.d.ts +4 -4
  40. package/lib/components/ui/resizable.js +4 -4
  41. package/lib/examples/A2UiRestaurantExample.js +2 -2
  42. package/lib/examples/AgUiAgenticExample.js +2 -2
  43. package/lib/examples/AgUiBackendToolRenderingExample.js +2 -2
  44. package/lib/examples/AgUiHaikuGenUIExample.js +2 -2
  45. package/lib/examples/AgUiHumanInTheLoopExample.js +2 -2
  46. package/lib/examples/AgUiSharedStateExample.js +2 -2
  47. package/lib/examples/AgUiToolsBasedGenUIExample.js +2 -2
  48. package/lib/examples/AgentRuntimeCustomExample.js +2 -2
  49. package/lib/examples/AgentRuntimeLexical2Example.js +2 -1
  50. package/lib/examples/AgentRuntimeLexicalExample.js +5 -2
  51. package/lib/examples/AgentRuntimeLexicalSidebarExample.js +4 -2
  52. package/lib/examples/AgentRuntimeNotebookExample.js +1 -1
  53. package/lib/examples/AgentRuntimeStandaloneExample.js +2 -2
  54. package/lib/examples/AgentSpaceFormExample.d.ts +70 -2
  55. package/lib/examples/AgentSpaceFormExample.js +177 -43
  56. package/lib/examples/CopilotKitLexicalExample.js +2 -1
  57. package/lib/examples/components/AgentConfiguration.d.ts +17 -2
  58. package/lib/examples/components/AgentConfiguration.js +220 -16
  59. package/lib/examples/components/LexicalEditor.js +2 -1
  60. package/lib/examples/components/MockFileBrowser.js +6 -2
  61. package/lib/examples/components/index.d.ts +0 -1
  62. package/lib/examples/components/index.js +0 -1
  63. package/lib/examples/example-selector.js +0 -1
  64. package/lib/examples/index.d.ts +0 -1
  65. package/lib/examples/index.js +0 -1
  66. package/lib/examples/lexical/editorConfig.d.ts +3 -2
  67. package/lib/examples/lexical/editorConfig.js +7 -1
  68. package/lib/examples/lexical/initial-content.json +2210 -0
  69. package/lib/examples/main.js +15 -1
  70. package/lib/identity/IdentityConnect.d.ts +90 -0
  71. package/lib/identity/IdentityConnect.js +316 -0
  72. package/lib/identity/OAuthCallback.d.ts +58 -0
  73. package/lib/identity/OAuthCallback.js +223 -0
  74. package/lib/identity/dcr.d.ts +257 -0
  75. package/lib/identity/dcr.js +282 -0
  76. package/lib/identity/identityStore.d.ts +72 -0
  77. package/lib/identity/identityStore.js +529 -0
  78. package/lib/identity/index.d.ts +46 -0
  79. package/lib/identity/index.js +17 -0
  80. package/lib/identity/pkce.d.ts +30 -0
  81. package/lib/identity/pkce.js +65 -0
  82. package/lib/identity/types.d.ts +293 -0
  83. package/lib/identity/types.js +73 -0
  84. package/lib/identity/useIdentity.d.ts +108 -0
  85. package/lib/identity/useIdentity.js +323 -0
  86. package/lib/index.d.ts +1 -0
  87. package/lib/index.js +1 -0
  88. package/lib/lib/utils.js +1 -1
  89. package/lib/renderers/a2ui/lib/utils.js +1 -1
  90. package/lib/test-setup.d.ts +1 -1
  91. package/lib/test-setup.js +1 -0
  92. package/lib/tools/adapters/agent-runtimes/AgentRuntimesToolAdapter.js +32 -1
  93. package/lib/tools/adapters/agent-runtimes/lexicalHooks.d.ts +6 -0
  94. package/lib/tools/adapters/agent-runtimes/lexicalHooks.js +16 -17
  95. package/package.json +20 -7
  96. package/patches/@datalayer+jupyter-lexical+1.0.8.patch +11628 -0
  97. package/patches/@datalayer+jupyter-react+2.0.2.patch +5338 -0
  98. package/lib/examples/AgentSpaceHomeExample.d.ts +0 -8
  99. package/lib/examples/AgentSpaceHomeExample.js +0 -171
  100. package/lib/examples/components/AgentsDataTable.d.ts +0 -13
  101. package/lib/examples/components/AgentsDataTable.js +0 -74
  102. package/lib/examples/components/Rating.d.ts +0 -14
  103. package/lib/examples/components/Rating.js +0 -12
@@ -10,6 +10,7 @@ import { loadJupyterConfig, JupyterReactTheme, createServerSettings, setJupyterS
10
10
  import { ServiceManager } from '@jupyterlab/services';
11
11
  import { coreStore, iamStore, createDatalayerServiceManager, } from '@datalayer/core';
12
12
  import { useChatStore } from '../components/chat/store';
13
+ import { OAuthCallback } from '../identity';
13
14
  import { EXAMPLES } from './example-selector';
14
15
  import nbformatExample from './stores/notebooks/NotebookExample1.ipynb.json';
15
16
  // Load configurations from DOM
@@ -94,6 +95,14 @@ const isNotebookOnlyRoute = () => {
94
95
  console.log('Is notebook-only route:', isNotebookRoute);
95
96
  return isNotebookRoute;
96
97
  };
98
+ // Check if we're handling an OAuth callback (code and state in URL params)
99
+ const isOAuthCallback = () => {
100
+ const params = new URLSearchParams(window.location.search);
101
+ const hasCode = params.has('code');
102
+ const hasState = params.has('state');
103
+ const hasError = params.has('error');
104
+ return (hasCode && hasState) || hasError;
105
+ };
97
106
  // Get the default example name from localStorage
98
107
  const getDefaultExampleName = () => {
99
108
  const stored = localStorage.getItem('selectedExample');
@@ -320,7 +329,12 @@ export const ExampleApp = () => {
320
329
  // Mount the app - check route to determine which app to render
321
330
  const root = document.getElementById('root');
322
331
  if (root) {
323
- if (isNotebookOnlyRoute()) {
332
+ if (isOAuthCallback()) {
333
+ // Handle OAuth callback - render OAuthCallback component
334
+ console.log('Rendering OAuthCallback (popup flow)');
335
+ createRoot(root).render(_jsx(JupyterReactTheme, { children: _jsx(OAuthCallback, { autoClose: true, autoCloseDelay: 1000 }) }));
336
+ }
337
+ else if (isNotebookOnlyRoute()) {
324
338
  console.log('Rendering NotebookOnlyApp');
325
339
  createRoot(root).render(_jsx(NotebookOnlyApp, {}));
326
340
  }
@@ -0,0 +1,90 @@
1
+ import React from 'react';
2
+ import type { OAuthProvider, OAuthProviderConfig, Identity } from './types';
3
+ /**
4
+ * Props for IdentityButton component
5
+ */
6
+ export interface IdentityButtonProps {
7
+ /** Provider type */
8
+ provider: OAuthProvider;
9
+ /** OAuth client ID (required for connecting) */
10
+ clientId?: string;
11
+ /** Custom provider configuration (for custom providers) */
12
+ providerConfig?: Partial<OAuthProviderConfig>;
13
+ /** Scopes to request (defaults to provider defaults) */
14
+ scopes?: string[];
15
+ /** Whether the button is disabled */
16
+ disabled?: boolean;
17
+ /** Size variant */
18
+ size?: 'small' | 'medium' | 'large';
19
+ /** Show full provider info or just icon */
20
+ variant?: 'full' | 'compact' | 'icon';
21
+ /** Callback when connection completes */
22
+ onConnect?: (identity: Identity) => void;
23
+ /** Callback when disconnection completes */
24
+ onDisconnect?: (provider: OAuthProvider) => void;
25
+ /** Callback when error occurs */
26
+ onError?: (error: Error) => void;
27
+ }
28
+ /**
29
+ * Single provider connect/disconnect button
30
+ */
31
+ export declare const IdentityButton: React.FC<IdentityButtonProps>;
32
+ /**
33
+ * Props for IdentityConnect component
34
+ */
35
+ export interface IdentityConnectProps {
36
+ /** Provider configurations with client IDs */
37
+ providers: {
38
+ [K in OAuthProvider]?: {
39
+ clientId: string;
40
+ scopes?: string[];
41
+ config?: Partial<OAuthProviderConfig>;
42
+ };
43
+ };
44
+ /** Layout variant */
45
+ layout?: 'list' | 'grid' | 'inline';
46
+ /** Show title/header */
47
+ showHeader?: boolean;
48
+ /** Custom title */
49
+ title?: string;
50
+ /** Size variant */
51
+ size?: 'small' | 'medium' | 'large';
52
+ /** Whether to show descriptions */
53
+ showDescriptions?: boolean;
54
+ /** Callback when any identity connects */
55
+ onConnect?: (identity: Identity) => void;
56
+ /** Callback when any identity disconnects */
57
+ onDisconnect?: (provider: OAuthProvider) => void;
58
+ /** Callback when error occurs */
59
+ onError?: (provider: OAuthProvider, error: Error) => void;
60
+ /** Whether all buttons are disabled */
61
+ disabled?: boolean;
62
+ }
63
+ /**
64
+ * Multi-provider identity connection panel
65
+ */
66
+ export declare const IdentityConnect: React.FC<IdentityConnectProps>;
67
+ /**
68
+ * Props for IdentityMenu component
69
+ */
70
+ export interface IdentityMenuProps {
71
+ /** Provider configurations with client IDs */
72
+ providers: {
73
+ [K in OAuthProvider]?: {
74
+ clientId: string;
75
+ scopes?: string[];
76
+ config?: Partial<OAuthProviderConfig>;
77
+ };
78
+ };
79
+ /** Callback when any identity connects */
80
+ onConnect?: (identity: Identity) => void;
81
+ /** Callback when any identity disconnects */
82
+ onDisconnect?: (provider: OAuthProvider) => void;
83
+ /** Whether the menu is disabled */
84
+ disabled?: boolean;
85
+ }
86
+ /**
87
+ * Dropdown menu for identity management
88
+ */
89
+ export declare const IdentityMenu: React.FC<IdentityMenuProps>;
90
+ export default IdentityConnect;
@@ -0,0 +1,316 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2025-2026 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ import React, { useCallback } from 'react';
7
+ import { Box, Text, Button, Label, Flash, ActionList, ActionMenu, Avatar, } from '@primer/react';
8
+ import { MarkGithubIcon, LinkIcon, UnlinkIcon, CheckCircleFillIcon, AlertIcon, KeyIcon, } from '@primer/octicons-react';
9
+ import { useIdentity } from './useIdentity';
10
+ import { GITHUB_PROVIDER, GOOGLE_PROVIDER, KAGGLE_PROVIDER } from './types';
11
+ /**
12
+ * Provider-specific display configurations.
13
+ * - GitHub: Uses official GitHub icon and dark theme color
14
+ * - Google: Uses key icon placeholder and Google blue
15
+ * - Kaggle: Uses key icon placeholder and Kaggle cyan
16
+ */
17
+ const PROVIDER_DISPLAY = {
18
+ github: {
19
+ name: 'GitHub',
20
+ icon: MarkGithubIcon, // Official GitHub Octicon
21
+ color: '#24292f', // GitHub dark theme color
22
+ description: 'Access GitHub repositories and APIs',
23
+ },
24
+ google: {
25
+ name: 'Google',
26
+ icon: KeyIcon, // Would use Google icon if available
27
+ color: '#4285f4',
28
+ description: 'Access Google services and APIs',
29
+ },
30
+ kaggle: {
31
+ name: 'Kaggle',
32
+ icon: KeyIcon, // Would use Kaggle icon if available
33
+ color: '#20beff',
34
+ description: 'Access Kaggle datasets and notebooks',
35
+ },
36
+ linkedin: {
37
+ name: 'LinkedIn',
38
+ icon: KeyIcon,
39
+ color: '#0077b5',
40
+ description: 'Access LinkedIn profile and connections',
41
+ },
42
+ slack: {
43
+ name: 'Slack',
44
+ icon: KeyIcon,
45
+ color: '#4a154b',
46
+ description: 'Access Slack workspaces and channels',
47
+ },
48
+ notion: {
49
+ name: 'Notion',
50
+ icon: KeyIcon,
51
+ color: '#000000',
52
+ description: 'Access Notion pages and databases',
53
+ },
54
+ custom: {
55
+ name: 'Custom',
56
+ icon: LinkIcon,
57
+ color: '#6f42c1',
58
+ description: 'Custom OAuth provider',
59
+ },
60
+ };
61
+ /**
62
+ * Default OAuth scopes for common providers.
63
+ * - GitHub: read:user and user:email for basic profile access
64
+ * - Google: OpenID Connect scopes for profile and email
65
+ * - Kaggle: read access for datasets
66
+ */
67
+ const DEFAULT_SCOPES = {
68
+ github: ['read:user', 'user:email'], // GitHub scopes for user profile
69
+ google: ['openid', 'profile', 'email'], // OpenID Connect scopes
70
+ kaggle: ['read'],
71
+ linkedin: ['r_liteprofile', 'r_emailaddress'],
72
+ slack: ['users:read', 'channels:read'],
73
+ notion: ['read_content'],
74
+ custom: [],
75
+ };
76
+ /**
77
+ * Single provider connect/disconnect button
78
+ */
79
+ export const IdentityButton = ({ provider, clientId, providerConfig, scopes, disabled = false, size = 'medium', variant = 'full', onConnect, onDisconnect, onError, }) => {
80
+ const { identities, isAuthorizing, error, connectWithPopup, disconnect, configureProvider, } = useIdentity();
81
+ const display = PROVIDER_DISPLAY[provider] || PROVIDER_DISPLAY.custom;
82
+ const identity = identities.find(id => id.provider === provider);
83
+ const isConnected = !!identity;
84
+ const isPending = isAuthorizing;
85
+ // Configure provider on mount if clientId is provided
86
+ React.useEffect(() => {
87
+ if (clientId) {
88
+ const baseConfig = provider === 'github'
89
+ ? GITHUB_PROVIDER
90
+ : provider === 'google'
91
+ ? GOOGLE_PROVIDER
92
+ : provider === 'kaggle'
93
+ ? KAGGLE_PROVIDER
94
+ : undefined;
95
+ if (baseConfig) {
96
+ // Build redirect URI from current page URL (without query params) if not provided
97
+ // This allows OAuth callback to return to the same page that initiated the flow
98
+ const redirectUri = providerConfig?.redirectUri ||
99
+ (typeof window !== 'undefined'
100
+ ? `${window.location.origin}${window.location.pathname}`
101
+ : '');
102
+ configureProvider({
103
+ ...baseConfig,
104
+ ...providerConfig,
105
+ provider,
106
+ clientId,
107
+ redirectUri,
108
+ });
109
+ }
110
+ }
111
+ }, [clientId, provider, providerConfig, configureProvider]);
112
+ const handleClick = useCallback(async () => {
113
+ try {
114
+ if (isConnected) {
115
+ await disconnect(provider);
116
+ onDisconnect?.(provider);
117
+ }
118
+ else {
119
+ // Use popup flow to avoid losing application state
120
+ const identity = await connectWithPopup(provider, scopes || DEFAULT_SCOPES[provider]);
121
+ onConnect?.(identity);
122
+ }
123
+ }
124
+ catch (err) {
125
+ // Don't treat 'popup closed by user' as an error to display
126
+ const errorMessage = err instanceof Error ? err.message : String(err);
127
+ if (errorMessage !== 'Popup closed by user') {
128
+ onError?.(err instanceof Error ? err : new Error(errorMessage));
129
+ }
130
+ }
131
+ }, [
132
+ isConnected,
133
+ provider,
134
+ scopes,
135
+ connectWithPopup,
136
+ disconnect,
137
+ onConnect,
138
+ onDisconnect,
139
+ onError,
140
+ ]);
141
+ // Handle error display
142
+ React.useEffect(() => {
143
+ if (error) {
144
+ onError?.(error instanceof Error ? error : new Error(String(error)));
145
+ }
146
+ }, [error, onError]);
147
+ const buttonProps = {
148
+ onClick: handleClick,
149
+ disabled: disabled || isPending || (!clientId && !isConnected),
150
+ size: size === 'large' ? 'large' : size === 'small' ? 'small' : 'medium',
151
+ };
152
+ if (variant === 'icon') {
153
+ return (_jsx(Button, { ...buttonProps, variant: isConnected ? 'primary' : 'default', "aria-label": isConnected ? `Disconnect ${display.name}` : `Connect ${display.name}`, sx: {
154
+ width: size === 'small' ? 28 : size === 'large' ? 44 : 36,
155
+ height: size === 'small' ? 28 : size === 'large' ? 44 : 36,
156
+ p: 0,
157
+ }, children: isPending ? (_jsx(Box, { as: "span", className: "anim-rotate", sx: { display: 'inline-block' }, children: "\u23F3" })) : (_jsx(display.icon, { size: size === 'small' ? 14 : 18 })) }));
158
+ }
159
+ if (variant === 'compact') {
160
+ return (_jsx(Button, { ...buttonProps, variant: isConnected ? 'primary' : 'default', leadingVisual: display.icon, trailingVisual: isConnected ? CheckCircleFillIcon : undefined, children: isPending ? 'Connecting...' : display.name }));
161
+ }
162
+ // Full variant
163
+ return (_jsxs(Box, { sx: {
164
+ display: 'flex',
165
+ flexDirection: 'column',
166
+ gap: 2,
167
+ p: 2,
168
+ border: '1px solid',
169
+ borderColor: isConnected ? 'success.muted' : 'border.default',
170
+ borderRadius: 2,
171
+ backgroundColor: isConnected ? 'success.subtle' : 'canvas.subtle',
172
+ }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Box, { sx: {
173
+ width: 40,
174
+ height: 40,
175
+ borderRadius: 2,
176
+ display: 'flex',
177
+ alignItems: 'center',
178
+ justifyContent: 'center',
179
+ backgroundColor: display.color,
180
+ color: 'white',
181
+ }, children: _jsx(display.icon, { size: 20 }) }), _jsxs(Box, { sx: { flex: 1 }, children: [_jsx(Text, { sx: { fontWeight: 'bold', display: 'block' }, children: display.name }), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: isConnected && identity?.userInfo
182
+ ? `Connected as ${identity.userInfo.name || identity.userInfo.email}`
183
+ : display.description })] }), !isConnected && (_jsx(Button, { ...buttonProps, variant: "primary", leadingVisual: LinkIcon, children: isPending ? 'Working...' : 'Connect' }))] }), isConnected && identity && (_jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2, pl: 1 }, children: [identity.userInfo?.avatarUrl && (_jsx("a", { href: identity.userInfo.profileUrl ||
184
+ `https://github.com/${identity.userInfo.username}`, target: "_blank", rel: "noopener noreferrer", style: { display: 'block', lineHeight: 0 }, children: _jsx("img", { src: identity.userInfo.avatarUrl, alt: identity.userInfo.name || 'User avatar', style: {
185
+ width: 32,
186
+ height: 32,
187
+ borderRadius: '50%',
188
+ cursor: 'pointer',
189
+ } }) })), _jsxs(Box, { sx: { flex: 1 }, children: [identity.userInfo?.username && (_jsx("a", { href: identity.userInfo.profileUrl ||
190
+ `https://github.com/${identity.userInfo.username}`, target: "_blank", rel: "noopener noreferrer", style: { textDecoration: 'none' }, children: _jsxs(Text, { sx: {
191
+ fontSize: 1,
192
+ color: 'fg.muted',
193
+ display: 'block',
194
+ ':hover': { textDecoration: 'underline' },
195
+ }, children: ["@", identity.userInfo.username] }) })), _jsx(Text, { sx: { fontSize: 0, color: 'fg.muted', display: 'block' }, children: identity.token?.expiresAt
196
+ ? `Expires ${new Date(identity.token.expiresAt).toLocaleDateString()}`
197
+ : 'Token does not expire' })] })] }), identity.scopes && identity.scopes.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 1, pl: 1 }, children: identity.scopes.map(scope => (_jsx(Text, { sx: {
198
+ fontSize: 0,
199
+ px: 2,
200
+ py: 1,
201
+ bg: 'neutral.muted',
202
+ borderRadius: 2,
203
+ color: 'fg.muted',
204
+ }, children: scope }, scope))) })), _jsxs(Button, { ...buttonProps, variant: "danger", leadingVisual: UnlinkIcon, children: ["Disconnect from ", display.name] })] }))] }));
205
+ };
206
+ /**
207
+ * Multi-provider identity connection panel
208
+ */
209
+ export const IdentityConnect = ({ providers, layout = 'list', showHeader = true, title = 'Connected Accounts', size = 'medium', showDescriptions = true, onConnect, onDisconnect, onError, disabled = false, }) => {
210
+ const { identities, error } = useIdentity();
211
+ const connectedCount = identities.length;
212
+ const providerKeys = Object.keys(providers);
213
+ const handleConnect = useCallback((identity) => {
214
+ onConnect?.(identity);
215
+ }, [onConnect]);
216
+ const handleDisconnect = useCallback((provider) => {
217
+ onDisconnect?.(provider);
218
+ }, [onDisconnect]);
219
+ const handleError = useCallback((provider) => (err) => {
220
+ onError?.(provider, err);
221
+ }, [onError]);
222
+ if (layout === 'inline') {
223
+ return (_jsxs(Box, { sx: { display: 'flex', gap: 2, alignItems: 'center' }, children: [showHeader && (_jsxs(Text, { sx: { fontSize: 1, color: 'fg.muted', mr: 2 }, children: [title, ":"] })), providerKeys.map(provider => {
224
+ const config = providers[provider];
225
+ return (_jsx(IdentityButton, { provider: provider, clientId: config.clientId, scopes: config.scopes, providerConfig: config.config, size: size, variant: "icon", disabled: disabled, onConnect: handleConnect, onDisconnect: handleDisconnect, onError: handleError(provider) }, provider));
226
+ }), connectedCount > 0 && (_jsxs(Label, { variant: "success", children: [_jsx(Box, { as: "span", sx: {
227
+ mr: 1,
228
+ display: 'inline-flex',
229
+ verticalAlign: 'text-bottom',
230
+ }, children: _jsx(CheckCircleFillIcon, { size: 12 }) }), connectedCount, " connected"] }))] }));
231
+ }
232
+ return (_jsxs(Box, { sx: {
233
+ border: '1px solid',
234
+ borderColor: 'border.default',
235
+ borderRadius: 2,
236
+ backgroundColor: 'canvas.default',
237
+ overflow: 'hidden',
238
+ }, children: [showHeader && (_jsxs(Box, { sx: {
239
+ px: 3,
240
+ py: 2,
241
+ borderBottom: '1px solid',
242
+ borderColor: 'border.default',
243
+ backgroundColor: 'canvas.subtle',
244
+ display: 'flex',
245
+ alignItems: 'center',
246
+ justifyContent: 'space-between',
247
+ }, children: [_jsx(Text, { sx: { fontWeight: 'bold' }, children: title }), connectedCount > 0 && (_jsxs(Label, { variant: "success", children: [_jsx(Box, { as: "span", sx: {
248
+ mr: 1,
249
+ display: 'inline-flex',
250
+ verticalAlign: 'text-bottom',
251
+ }, children: _jsx(CheckCircleFillIcon, { size: 12 }) }), connectedCount, " connected"] }))] })), error && (_jsxs(Flash, { variant: "danger", sx: { m: 2, borderRadius: 1 }, children: [_jsx(Box, { as: "span", sx: { mr: 2, display: 'inline-flex', verticalAlign: 'text-bottom' }, children: _jsx(AlertIcon, { size: 16 }) }), error instanceof Error ? error.message : String(error)] })), _jsx(Box, { sx: {
252
+ p: 2,
253
+ display: layout === 'grid' ? 'grid' : 'flex',
254
+ flexDirection: layout === 'list' ? 'column' : undefined,
255
+ gridTemplateColumns: layout === 'grid'
256
+ ? 'repeat(auto-fill, minmax(250px, 1fr))'
257
+ : undefined,
258
+ gap: 2,
259
+ }, children: providerKeys.map(provider => {
260
+ const config = providers[provider];
261
+ return (_jsx(IdentityButton, { provider: provider, clientId: config.clientId, scopes: config.scopes, providerConfig: config.config, size: size, variant: showDescriptions ? 'full' : 'compact', disabled: disabled, onConnect: handleConnect, onDisconnect: handleDisconnect, onError: handleError(provider) }, provider));
262
+ }) })] }));
263
+ };
264
+ /**
265
+ * Dropdown menu for identity management
266
+ */
267
+ export const IdentityMenu = ({ providers, onConnect, onDisconnect, disabled = false, }) => {
268
+ const { identities, connect, disconnect, configureProvider, isAuthorizing } = useIdentity();
269
+ const providerKeys = Object.keys(providers);
270
+ const connectedCount = identities.length;
271
+ // Configure all providers on mount
272
+ React.useEffect(() => {
273
+ providerKeys.forEach(provider => {
274
+ const config = providers[provider];
275
+ const baseConfig = provider === 'github'
276
+ ? GITHUB_PROVIDER
277
+ : provider === 'google'
278
+ ? GOOGLE_PROVIDER
279
+ : provider === 'kaggle'
280
+ ? KAGGLE_PROVIDER
281
+ : undefined;
282
+ if (baseConfig) {
283
+ // Build redirect URI from current page URL
284
+ const redirectUri = typeof window !== 'undefined'
285
+ ? `${window.location.origin}${window.location.pathname}`
286
+ : '';
287
+ configureProvider({
288
+ ...baseConfig,
289
+ ...config.config,
290
+ provider,
291
+ clientId: config.clientId,
292
+ redirectUri,
293
+ });
294
+ }
295
+ });
296
+ }, [providerKeys, providers, configureProvider]);
297
+ const handleProviderClick = useCallback(async (provider) => {
298
+ const identity = identities.find(id => id.provider === provider);
299
+ const config = providers[provider];
300
+ if (identity) {
301
+ await disconnect(provider);
302
+ onDisconnect?.(provider);
303
+ }
304
+ else if (config) {
305
+ await connect(provider, config.scopes || DEFAULT_SCOPES[provider]);
306
+ // Connection initiated, identity will be updated in store after callback
307
+ }
308
+ }, [identities, providers, connect, disconnect, onConnect, onDisconnect]);
309
+ return (_jsxs(ActionMenu, { children: [_jsxs(ActionMenu.Button, { disabled: disabled, variant: connectedCount > 0 ? 'primary' : 'default', leadingVisual: KeyIcon, children: ["Identities", connectedCount > 0 && (_jsx(Label, { variant: "accent", sx: { ml: 2 }, children: connectedCount }))] }), _jsx(ActionMenu.Overlay, { children: _jsx(ActionList, { children: _jsx(ActionList.Group, { title: "OAuth Providers", children: providerKeys.map(provider => {
310
+ const display = PROVIDER_DISPLAY[provider] || PROVIDER_DISPLAY.custom;
311
+ const identity = identities.find(id => id.provider === provider);
312
+ const isConnected = !!identity;
313
+ return (_jsxs(ActionList.Item, { onSelect: () => handleProviderClick(provider), disabled: isAuthorizing, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(display.icon, { size: 16 }) }), _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Text, { children: display.name }), isConnected && (_jsxs(_Fragment, { children: [identity?.userInfo?.avatarUrl && (_jsx(Avatar, { src: identity.userInfo.avatarUrl, size: 16 })), _jsx(Label, { variant: "success", sx: { ml: 1 }, children: "Connected" })] }))] }), _jsx(ActionList.TrailingVisual, { children: isConnected ? (_jsx(UnlinkIcon, { size: 16 })) : (_jsx(LinkIcon, { size: 16 })) })] }, provider));
314
+ }) }) }) })] }));
315
+ };
316
+ export default IdentityConnect;
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ /**
3
+ * Props for OAuthCallback component
4
+ */
5
+ export interface OAuthCallbackProps {
6
+ /**
7
+ * Custom success message
8
+ */
9
+ successMessage?: string;
10
+ /**
11
+ * Custom error message prefix
12
+ */
13
+ errorMessagePrefix?: string;
14
+ /**
15
+ * Whether to automatically close the window/tab on success
16
+ * @default true for popup flow, false for redirect flow
17
+ */
18
+ autoClose?: boolean;
19
+ /**
20
+ * Delay before auto-closing (in ms)
21
+ * @default 1500
22
+ */
23
+ autoCloseDelay?: number;
24
+ /**
25
+ * Callback when processing completes successfully
26
+ */
27
+ onSuccess?: (provider: string) => void;
28
+ /**
29
+ * Callback when processing fails
30
+ */
31
+ onError?: (error: Error) => void;
32
+ /**
33
+ * URL to redirect to after success (for redirect flow)
34
+ * If not provided, will try to use window.opener for popup flow
35
+ */
36
+ redirectUrl?: string;
37
+ /**
38
+ * Whether to show the close button
39
+ * @default true
40
+ */
41
+ showCloseButton?: boolean;
42
+ }
43
+ /**
44
+ * OAuth callback handler component.
45
+ *
46
+ * This component should be rendered on the OAuth callback URL (e.g., /oauth/callback).
47
+ * It handles the authorization code exchange and token storage.
48
+ *
49
+ * @example
50
+ * // For redirect flow - mount at /oauth/callback
51
+ * <Route path="/oauth/callback" element={<OAuthCallback redirectUrl="/" />} />
52
+ *
53
+ * @example
54
+ * // For popup flow - mount at /oauth/callback
55
+ * <Route path="/oauth/callback" element={<OAuthCallback autoClose />} />
56
+ */
57
+ export declare const OAuthCallback: React.FC<OAuthCallbackProps>;
58
+ export default OAuthCallback;