@datalayer/core 0.0.26 → 0.0.27

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 (47) hide show
  1. package/lib/components/auth/Login.d.ts +4 -0
  2. package/lib/components/auth/Login.js +8 -2
  3. package/lib/components/auth/Login.stories.d.ts +1 -0
  4. package/lib/components/progress/ConsumptionBar.js +7 -7
  5. package/lib/components/progress/CreditsIndicator.d.ts +7 -1
  6. package/lib/components/progress/CreditsIndicator.js +4 -2
  7. package/lib/components/{screenshot/ScreenCapture.d.ts → screencapture/Screencapture.d.ts} +5 -5
  8. package/lib/components/{screenshot/ScreenCapture.js → screencapture/Screencapture.js} +2 -2
  9. package/lib/components/screencapture/ScreencaptureButton.d.ts +3 -0
  10. package/lib/components/{screenshot/ScreenCaptureButton.js → screencapture/ScreencaptureButton.js} +8 -8
  11. package/lib/components/screencapture/index.d.ts +2 -0
  12. package/lib/components/{screenshot → screencapture}/index.js +2 -2
  13. package/lib/hooks/index.d.ts +1 -1
  14. package/lib/hooks/index.js +1 -1
  15. package/lib/hooks/layouts/{LayoutScreenshot.css → LayoutScreencapture.css} +2 -2
  16. package/lib/hooks/layouts/LayoutScreencapture.d.ts +3 -0
  17. package/lib/hooks/layouts/{LayoutScreenshot.js → LayoutScreencapture.js} +14 -14
  18. package/lib/hooks/layouts/index.d.ts +1 -1
  19. package/lib/hooks/layouts/index.js +1 -1
  20. package/lib/hooks/useCache.d.ts +3 -1
  21. package/lib/hooks/useCache.js +113 -62
  22. package/lib/hooks/useCellOutputshot.js +2 -2
  23. package/lib/hooks/useScreencapture.d.ts +18 -0
  24. package/lib/hooks/useScreencapture.js +53 -0
  25. package/lib/models/IAMProviderUsers.d.ts +11 -0
  26. package/lib/models/IAMProvidersSpecs.d.ts +2 -1
  27. package/lib/models/IAMProvidersSpecs.js +14 -0
  28. package/lib/models/Page.js +1 -1
  29. package/lib/models/PageTag.d.ts +1 -1
  30. package/lib/models/PageTag.js +21 -0
  31. package/lib/state/substates/LayoutState.d.ts +5 -5
  32. package/lib/state/substates/LayoutState.js +3 -3
  33. package/lib/utils/Screencapture.d.ts +1 -0
  34. package/lib/utils/{Screenshot.js → Screencapture.js} +1 -1
  35. package/lib/utils/index.js +1 -1
  36. package/lib/views/datasources/DatasourceNew.d.ts +7 -1
  37. package/lib/views/datasources/DatasourceNew.js +3 -3
  38. package/lib/views/datasources/Datasources.d.ts +7 -1
  39. package/lib/views/datasources/Datasources.js +6 -4
  40. package/package.json +1 -1
  41. package/style/screencapture/index.css +58 -0
  42. package/lib/components/screenshot/ScreenCaptureButton.d.ts +0 -3
  43. package/lib/components/screenshot/index.d.ts +0 -2
  44. package/lib/hooks/layouts/LayoutScreenshot.d.ts +0 -3
  45. package/lib/hooks/useScreenshot.d.ts +0 -18
  46. package/lib/hooks/useScreenshot.js +0 -53
  47. package/lib/utils/Screenshot.d.ts +0 -1
@@ -31,6 +31,10 @@ export interface ILoginProps {
31
31
  * Show GitHub Login button
32
32
  */
33
33
  showGitHubLogin?: boolean;
34
+ /**
35
+ * Show Google Login button
36
+ */
37
+ showGoogleLogin?: boolean;
34
38
  /**
35
39
  * Show token Login buttons
36
40
  */
@@ -14,8 +14,10 @@ import { CenteredSpinner } from '../display';
14
14
  import { isInsideJupyterLab, validateLength } from '../../utils';
15
15
  import { useNavigate, useCache, useToast, useIAM } from '../../hooks';
16
16
  import { LoginToken } from './LoginToken';
17
+ /** Inline Google "G" icon for the login button. */
18
+ const GoogleIcon = () => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 48 48", children: [_jsx("path", { fill: "#EA4335", d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z" }), _jsx("path", { fill: "#4285F4", d: "M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z" }), _jsx("path", { fill: "#FBBC05", d: "M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.1 24.1 0 0 0 0 21.56l7.98-6.19z" }), _jsx("path", { fill: "#34A853", d: "M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z" })] }));
17
19
  export const Login = (props) => {
18
- const { heading, homeRoute, loginRoute, showEmailLogin = true, showGitHubLogin = true, showTokenLogin = true, } = props;
20
+ const { heading, homeRoute, loginRoute, showEmailLogin = true, showGitHubLogin = true, showGoogleLogin = true, showTokenLogin = true, } = props;
19
21
  const { useLogin, useOAuth2AuthorizationURL } = useCache({ loginRoute });
20
22
  const loginMutation = useLogin();
21
23
  const getOAuth2URLMutation = useOAuth2AuthorizationURL();
@@ -68,6 +70,9 @@ export const Login = (props) => {
68
70
  if (!iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name]) {
69
71
  initIAMProvider(IAMProvidersSpecs.GitHub);
70
72
  }
73
+ if (!iamProvidersAuthorizationURL[IAMProvidersSpecs.Google.name]) {
74
+ initIAMProvider(IAMProvidersSpecs.Google);
75
+ }
71
76
  if (!iamProvidersAuthorizationURL[IAMProvidersSpecs.LinkedIn.name]) {
72
77
  initIAMProvider(IAMProvidersSpecs.LinkedIn);
73
78
  }
@@ -163,6 +168,7 @@ export const Login = (props) => {
163
168
  : heading
164
169
  ? 'Login with Datalayer'
165
170
  : 'Login' }), _jsx(Box, { pt: 6 }), _jsx(Link, { href: "https://datalayer.app/password", target: "_blank", children: "Forgot password?" })] })] })), _jsx(Box, { children: _jsxs(Box, { display: "flex", flexDirection: "column", sx: { margin: 'auto' }, children: [showGitHubLogin &&
166
- iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name] && (_jsx(Button, { leadingVisual: MarkGithubIcon, href: iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name], as: "a", style: { margin: '10px 0' }, children: "Login with GitHub" })), showTokenLogin && (_jsx(LoginToken, { homeRoute: homeRoute, style: { margin: '10px 0' } }))] }) })] }) })) : loadingWithToken ? (_jsx(CenteredSpinner, { message: "Checking authentication\u2026" })) : (_jsx(_Fragment, {})) })] }));
171
+ iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name] && (_jsx(Button, { leadingVisual: MarkGithubIcon, href: iamProvidersAuthorizationURL[IAMProvidersSpecs.GitHub.name], as: "a", style: { margin: '10px 0' }, children: "Login with GitHub" })), showGoogleLogin &&
172
+ iamProvidersAuthorizationURL[IAMProvidersSpecs.Google.name] && (_jsx(Button, { leadingVisual: GoogleIcon, href: iamProvidersAuthorizationURL[IAMProvidersSpecs.Google.name], as: "a", style: { margin: '10px 0' }, children: "Login with Google" })), showTokenLogin && (_jsx(LoginToken, { homeRoute: homeRoute, style: { margin: '10px 0' } }))] }) })] }) })) : loadingWithToken ? (_jsx(CenteredSpinner, { message: "Checking authentication\u2026" })) : (_jsx(_Fragment, {})) })] }));
167
173
  };
168
174
  export default Login;
@@ -22,6 +22,7 @@ declare const meta: {
22
22
  passwordRoute?: string | undefined;
23
23
  showEmailLogin?: boolean | undefined;
24
24
  showGitHubLogin?: boolean | undefined;
25
+ showGoogleLogin?: boolean | undefined;
25
26
  showTokenLogin?: boolean | undefined;
26
27
  }>) => import("react/jsx-runtime").JSX.Element)[];
27
28
  tags: string[];
@@ -57,11 +57,11 @@ export function ConsumptionBar(props) {
57
57
  const title = duration
58
58
  ? `${formatTimeRemaining(secondsRemaining)} left - ${((progress / 100) * burntCredits).toFixed(2)} / ${burntCredits.toFixed(2)} credits`
59
59
  : `Started at ${new Date(startedAt * 1000).toISOString()} - ${burntCredits.toFixed(2)} credits consumed`;
60
- return (_jsx(_Fragment, { children: _jsx(Tooltip, { text: title, direction: "w", children: _jsx(Button, { variant: "invisible", children: _jsx(Box, { sx: { width: '70px' }, children: _jsx(ProgressBar, { tabIndex: onClick ? 0 : -1, style: style, animated: expiredAt ? false : true, bg: bg, progress: progress, onClick: onClick, onKeyDown: onClick
61
- ? event => {
62
- if (event.key === 'Enter' || event.key === 'Space') {
63
- onClick();
64
- }
65
- }
66
- : undefined }) }) }) }) }));
60
+ return (_jsx(_Fragment, { children: _jsx(Tooltip, { text: title, direction: "w", children: _jsx(Button, { variant: "invisible", onClick: onClick, onKeyDown: onClick
61
+ ? event => {
62
+ if (event.key === 'Enter' || event.key === 'Space') {
63
+ onClick();
64
+ }
65
+ }
66
+ : undefined, tabIndex: onClick ? 0 : -1, sx: { cursor: onClick ? 'pointer' : 'default' }, children: _jsx(Box, { sx: { width: '70px' }, children: _jsx(ProgressBar, { style: style, animated: expiredAt ? false : true, bg: bg, progress: progress }) }) }) }) }));
67
67
  }
@@ -9,7 +9,13 @@ type ICreditsIndicatorProps = {
9
9
  */
10
10
  kernelId: string;
11
11
  /**
12
- * Callback on progress bar click event
12
+ * Route to navigate to when the indicator is clicked.
13
+ * Ignored if `onClick` is provided.
14
+ */
15
+ navigateTo?: string;
16
+ /**
17
+ * Callback on progress bar click event.
18
+ * Takes precedence over `navigateTo`.
13
19
  */
14
20
  onClick?: () => void;
15
21
  /**
@@ -6,16 +6,18 @@ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
6
6
  import { useState, useEffect } from 'react';
7
7
  import { Box } from '@datalayer/primer-addons';
8
8
  import { ConsumptionBar } from '../../components/progress';
9
+ import useNavigate from '../../hooks/useNavigate';
9
10
  /**
10
11
  * Credits indicator component.
11
12
  */
12
13
  export function CreditsIndicator(props) {
13
- const { serviceManager, kernelId, onClick, onUpdate } = props;
14
+ const { serviceManager, kernelId, navigateTo, onClick, onUpdate } = props;
15
+ const navigate = useNavigate();
14
16
  const [model, setModel] = useState();
15
17
  useEffect(() => {
16
18
  serviceManager.runtimesManager.findById(kernelId).then(model => {
17
19
  setModel(model);
18
20
  });
19
21
  }, [kernelId, serviceManager]);
20
- return model ? (_jsx(Box, { display: "flex", style: { alignItems: 'center' }, children: _jsx(ConsumptionBar, { startedAt: parseFloat(model.started_at), expiredAt: model.expired_at ? parseFloat(model.expired_at) : undefined, burningRate: model.burning_rate, onClick: onClick, onUpdate: onUpdate, style: { cursor: 'pointer' } }) })) : (_jsx(_Fragment, {}));
22
+ return model ? (_jsx(Box, { display: "flex", style: { alignItems: 'center' }, children: _jsx(ConsumptionBar, { startedAt: parseFloat(model.started_at), expiredAt: model.expired_at ? parseFloat(model.expired_at) : undefined, burningRate: model.burning_rate, onClick: onClick ?? (navigateTo ? () => navigate(navigateTo) : undefined), onUpdate: onUpdate, style: { cursor: 'pointer' } }) })) : (_jsx(_Fragment, {}));
21
23
  }
@@ -1,10 +1,10 @@
1
1
  import { Component, CSSProperties } from 'react';
2
- type ScreenCaptureProps = {
2
+ type ScreencaptureProps = {
3
3
  children: any;
4
4
  onStartCapture?: () => void;
5
5
  onEndCapture: (url: string) => void;
6
6
  };
7
- type ScreenCaptureState = {
7
+ type ScreencaptureState = {
8
8
  on: boolean;
9
9
  startX: number;
10
10
  startY: number;
@@ -22,8 +22,8 @@ type ScreenCaptureState = {
22
22
  cropHeigth: number;
23
23
  imageURL: string;
24
24
  };
25
- export declare class ScreenCapture extends Component<ScreenCaptureProps, ScreenCaptureState> {
26
- state: ScreenCaptureState;
25
+ export declare class Screencapture extends Component<ScreencaptureProps, ScreencaptureState> {
26
+ state: ScreencaptureState;
27
27
  handleWindowResize: () => void;
28
28
  componentDidMount: () => void;
29
29
  componentWillUnmount: () => void;
@@ -35,4 +35,4 @@ export declare class ScreenCapture extends Component<ScreenCaptureProps, ScreenC
35
35
  renderChild: () => any;
36
36
  render(): any;
37
37
  }
38
- export default ScreenCapture;
38
+ export default Screencapture;
@@ -5,7 +5,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  */
6
6
  import { Component } from 'react';
7
7
  import html2canvas from 'html2canvas';
8
- export class ScreenCapture extends Component {
8
+ export class Screencapture extends Component {
9
9
  state = {
10
10
  on: false,
11
11
  startX: 0,
@@ -167,4 +167,4 @@ export class ScreenCapture extends Component {
167
167
  return (_jsxs("div", { onMouseMove: this.handleMouseMove, onMouseDown: this.handleMouseDown, onMouseUp: this.handleMouseUp, children: [this.renderChild(), _jsx("div", { className: `overlay ${isMouseDown && 'highlighting'}`, style: { borderWidth: `${borderWidth}` } }), _jsx("div", { className: "crosshairs", style: { left: crossHairsLeft + 'px', top: crossHairsTop + 'px' } })] }));
168
168
  }
169
169
  }
170
- export default ScreenCapture;
170
+ export default Screencapture;
@@ -0,0 +1,3 @@
1
+ import { PropsWithChildren } from 'react';
2
+ export declare const ScreencaptureButton: (props: PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
3
+ export default ScreencaptureButton;
@@ -4,16 +4,16 @@ import { ScreenFullIcon } from '@primer/octicons-react';
4
4
  import { lazyWithPreload, WithSuspense } from '../../utils';
5
5
  import { useToast } from '../../hooks';
6
6
  import { useLayoutStore } from '../../state';
7
- const ScreenCapture = WithSuspense(lazyWithPreload(() => import('../screenshot/ScreenCapture')));
8
- export const ScreenCaptureButton = (props) => {
7
+ const Screencapture = WithSuspense(lazyWithPreload(() => import('./Screencapture')));
8
+ export const ScreencaptureButton = (props) => {
9
9
  const { enqueueToast } = useToast();
10
- const { setScreenCapture, hideScreenshot } = useLayoutStore();
11
- const handleScreenCapture = (screenCapture) => {
12
- setScreenCapture(screenCapture);
13
- hideScreenshot();
10
+ const { setScreencapture, hideScreencapture } = useLayoutStore();
11
+ const handleScreencapture = (screenCapture) => {
12
+ setScreencapture(screenCapture);
13
+ hideScreencapture();
14
14
  enqueueToast('Screen is captured.', { variant: 'success' });
15
15
  };
16
- return (_jsx(ScreenCapture, { onEndCapture: handleScreenCapture, children: ({ onStartCapture }) => (_jsx(Tooltip, { text: "Take a screen capture", direction: "s", children: _jsx(Button, { variant: "invisible", children: _jsx(Link, { href: "javascript: return false;", sx: {
16
+ return (_jsx(Screencapture, { onEndCapture: handleScreencapture, children: ({ onStartCapture }) => (_jsx(Tooltip, { text: "Take a screen capture", direction: "s", children: _jsx(Button, { variant: "invisible", children: _jsx(Link, { href: "javascript: return false;", sx: {
17
17
  color: 'fg.muted',
18
18
  ':hover, :focus, &[aria-expanded=true]': {
19
19
  background: 'none !important',
@@ -24,4 +24,4 @@ export const ScreenCaptureButton = (props) => {
24
24
  onStartCapture();
25
25
  }, children: _jsx(ScreenFullIcon, {}) }) }) })) }));
26
26
  };
27
- export default ScreenCaptureButton;
27
+ export default ScreencaptureButton;
@@ -0,0 +1,2 @@
1
+ export * from './Screencapture';
2
+ export * from './ScreencaptureButton';
@@ -2,5 +2,5 @@
2
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- export * from './ScreenCapture';
6
- export * from './ScreenCaptureButton';
5
+ export * from './Screencapture';
6
+ export * from './ScreencaptureButton';
@@ -19,7 +19,7 @@ export * from './useParams';
19
19
  export * from './useOnClickOutside';
20
20
  export * from './useRef';
21
21
  export * from './useRuntimes';
22
- export * from './useScreenshot';
22
+ export * from './useScreencapture';
23
23
  export * from './useToast';
24
24
  export * from './useUpload';
25
25
  export * from './useUser';
@@ -24,7 +24,7 @@ export * from './useParams';
24
24
  export * from './useOnClickOutside';
25
25
  export * from './useRef';
26
26
  export * from './useRuntimes';
27
- export * from './useScreenshot';
27
+ export * from './useScreencapture';
28
28
  export * from './useToast';
29
29
  export * from './useUpload';
30
30
  export * from './useUser';
@@ -22,14 +22,14 @@
22
22
  .crosshairs::before {
23
23
  height: 24px;
24
24
  width: 2px;
25
- background: #000;
25
+ background: #fff;
26
26
  top: -11px;
27
27
  }
28
28
 
29
29
  .crosshairs::after {
30
30
  width: 24px;
31
31
  height: 2px;
32
- background: #000;
32
+ background: #fff;
33
33
  left: -11px;
34
34
  }
35
35
 
@@ -0,0 +1,3 @@
1
+ import '../../../style/screencapture/index.css';
2
+ export declare const LayoutScreencapture: () => import("react/jsx-runtime").JSX.Element;
3
+ export default LayoutScreencapture;
@@ -5,11 +5,11 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
5
5
  */
6
6
  import { useEffect } from 'react';
7
7
  import { Box } from '@primer/react';
8
- import { useScreenshot, useToast } from '..';
8
+ import { useScreencapture, useToast } from '..';
9
9
  import { lazyWithPreload, WithSuspense } from '../../utils';
10
10
  import { useLayoutStore } from '../../state';
11
- import './LayoutScreenshot.css';
12
- const ScreenCapture = WithSuspense(lazyWithPreload(() => import('../../components/screenshot/ScreenCapture')));
11
+ import '../../../style/screencapture/index.css';
12
+ const Screencapture = WithSuspense(lazyWithPreload(() => import('../../components/screencapture/Screencapture')));
13
13
  const Capture = (props) => {
14
14
  const { onStartCapture } = props;
15
15
  useEffect(() => {
@@ -17,28 +17,28 @@ const Capture = (props) => {
17
17
  }, []);
18
18
  return _jsx(_Fragment, {});
19
19
  };
20
- const ScreenshotContent = (props) => {
20
+ const ScreencaptureContent = (props) => {
21
21
  // const { screenshotDisplay } = props;
22
22
  const { enqueueToast } = useToast();
23
- const { setScreenCapture, hideScreenshot } = useLayoutStore();
24
- const handleScreenCapture = (screenCapture) => {
25
- setScreenCapture(screenCapture);
26
- hideScreenshot();
23
+ const { setScreencapture, hideScreencapture } = useLayoutStore();
24
+ const handleScreencapture = (screenCapture) => {
25
+ setScreencapture(screenCapture);
26
+ hideScreencapture();
27
27
  enqueueToast('Screen is captured.', { variant: 'success' });
28
28
  };
29
- return (_jsx(Box, { children: _jsx(ScreenCapture, { onEndCapture: handleScreenCapture, children: ({ onStartCapture }) => _jsx(Capture, { onStartCapture: onStartCapture }) }) }));
29
+ return (_jsx(Box, { children: _jsx(Screencapture, { onEndCapture: handleScreencapture, children: ({ onStartCapture }) => _jsx(Capture, { onStartCapture: onStartCapture }) }) }));
30
30
  };
31
- export const LayoutScreenshot = () => {
31
+ export const LayoutScreencapture = () => {
32
32
  const { screenshot } = useLayoutStore();
33
- const { displayScreenshot, closeScreenshot } = useScreenshot();
33
+ const { displayScreencapture, closeScreencapture } = useScreencapture();
34
34
  useEffect(() => {
35
35
  if (screenshot && screenshot.open) {
36
- displayScreenshot(() => (_jsx(ScreenshotContent, { screenshotDisplay: screenshot })));
36
+ displayScreencapture(() => (_jsx(ScreencaptureContent, { screenshotDisplay: screenshot })));
37
37
  }
38
38
  else {
39
- closeScreenshot();
39
+ closeScreencapture();
40
40
  }
41
41
  }, [screenshot]);
42
42
  return _jsx(_Fragment, {});
43
43
  };
44
- export default LayoutScreenshot;
44
+ export default LayoutScreencapture;
@@ -1,2 +1,2 @@
1
1
  export * from './LayoutBackdrop';
2
- export * from './LayoutScreenshot';
2
+ export * from './LayoutScreencapture';
@@ -3,4 +3,4 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  export * from './LayoutBackdrop';
6
- export * from './LayoutScreenshot';
6
+ export * from './LayoutScreencapture';
@@ -316,6 +316,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
316
316
  autorization_url: string;
317
317
  }, Error, Record<string, string>, unknown>;
318
318
  useGetGitHubProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
319
+ useGetGoogleProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
319
320
  useGetLinkedinProfile: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
320
321
  usePostLinkedinShare: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
321
322
  linkedinUserUrn: string;
@@ -325,7 +326,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
325
326
  usePostLinkedinShareWithUpload: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
326
327
  linkedinUserUrn: string;
327
328
  postText: string;
328
- uploadObject: string;
329
+ uploadObject?: string;
329
330
  accessToken: string;
330
331
  }, unknown>;
331
332
  useRegisterToWaitingList: () => import("@tanstack/react-query").UseMutationResult<any, Error, {
@@ -544,6 +545,7 @@ export declare const useCache: ({ loginRoute }?: CacheProps) => {
544
545
  creditsLimit?: number;
545
546
  type?: string;
546
547
  editorVariant?: string;
548
+ enableCodemode?: boolean;
547
549
  }, unknown>;
548
550
  useDeleteAgentRuntime: () => import("@tanstack/react-query").UseMutationResult<any, Error, string, unknown>;
549
551
  useRefreshAgentRuntimes: () => () => void;
@@ -1673,6 +1673,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1673
1673
  credits_limit: data.creditsLimit || 10,
1674
1674
  type: data.type || 'notebook',
1675
1675
  editor_variant: data.editorVariant || 'none',
1676
+ enable_codemode: data.enableCodemode ?? false,
1676
1677
  },
1677
1678
  });
1678
1679
  },
@@ -1710,10 +1711,21 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
1710
1711
  method: 'DELETE',
1711
1712
  });
1712
1713
  },
1713
- onSuccess: () => {
1714
- // Invalidate all agent runtime queries
1714
+ onSuccess: (_data, podName) => {
1715
+ // Cancel any in-flight queries for the deleted runtime so they
1716
+ // don't re-fetch a resource that no longer exists.
1717
+ queryClient.cancelQueries({
1718
+ queryKey: queryKeys.agentRuntimes.detail(podName),
1719
+ });
1720
+ // Remove the detail cache entry immediately — prevents React
1721
+ // Query from triggering a stale re-fetch while the component
1722
+ // unmounts.
1723
+ queryClient.removeQueries({
1724
+ queryKey: queryKeys.agentRuntimes.detail(podName),
1725
+ });
1726
+ // Invalidate the list so the sidebar refreshes.
1715
1727
  queryClient.invalidateQueries({
1716
- queryKey: queryKeys.agentRuntimes.all(),
1728
+ queryKey: queryKeys.agentRuntimes.lists(),
1717
1729
  });
1718
1730
  },
1719
1731
  });
@@ -5194,7 +5206,9 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
5194
5206
  if (!resp.success) {
5195
5207
  throw new Error(resp.message || 'Failed to search public items');
5196
5208
  }
5197
- return resp.items || [];
5209
+ return (resp.items || [])
5210
+ .map((item) => toItem(item))
5211
+ .filter(Boolean);
5198
5212
  },
5199
5213
  });
5200
5214
  };
@@ -5219,6 +5233,23 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
5219
5233
  },
5220
5234
  });
5221
5235
  };
5236
+ /**
5237
+ * Get Google profile from access token
5238
+ */
5239
+ const useGetGoogleProfile = () => {
5240
+ return useMutation({
5241
+ mutationFn: async (accessToken) => {
5242
+ const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', {
5243
+ method: 'GET',
5244
+ headers: {
5245
+ Accept: 'application/json',
5246
+ Authorization: `Bearer ${accessToken}`,
5247
+ },
5248
+ });
5249
+ return response.json();
5250
+ },
5251
+ });
5252
+ };
5222
5253
  /**
5223
5254
  * Get LinkedIn profile via proxy
5224
5255
  */
@@ -5275,68 +5306,87 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
5275
5306
  const usePostLinkedinShareWithUpload = () => {
5276
5307
  return useMutation({
5277
5308
  mutationFn: async ({ linkedinUserUrn, postText, uploadObject, accessToken, }) => {
5278
- // Step 1: Register upload
5279
- const registerUploadRequest = {
5280
- registerUploadRequest: {
5281
- recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
5282
- owner: linkedinUserUrn,
5283
- serviceRelationships: [
5284
- {
5285
- relationshipType: 'OWNER',
5286
- identifier: 'urn:li:userGeneratedContent',
5287
- },
5288
- ],
5289
- },
5290
- };
5291
- const registerResp = await requestDatalayer({
5292
- url: `${configuration.iamRunUrl}/api/iam/v1/proxy/request`,
5293
- method: 'POST',
5294
- body: {
5295
- request_method: 'POST',
5296
- request_url: 'https://api.linkedin.com/v2/assets?action=registerUpload',
5297
- request_token: accessToken,
5298
- request_body: registerUploadRequest,
5299
- },
5300
- });
5301
- const asset = registerResp.response.value.asset;
5302
- const uploadUrl = registerResp.response.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl;
5303
- // Step 2: Upload image
5304
- await requestDatalayer({
5305
- url: `${configuration.iamRunUrl}/api/iam/v1/proxy/request`,
5306
- method: 'POST',
5307
- body: {
5308
- request_method: 'PUT',
5309
- request_url: uploadUrl,
5310
- request_token: accessToken,
5311
- request_body: {
5312
- uploadURL: uploadUrl,
5313
- content: uploadObject,
5314
- userURN: linkedinUserUrn,
5315
- },
5316
- },
5317
- });
5318
- // Step 3: Create share with media
5319
- const shareRequest = {
5320
- author: linkedinUserUrn,
5321
- lifecycleState: 'PUBLISHED',
5322
- specificContent: {
5323
- 'com.linkedin.ugc.ShareContent': {
5324
- shareCommentary: { text: postText },
5325
- shareMediaCategory: 'IMAGE',
5326
- media: [
5309
+ let shareRequest;
5310
+ if (uploadObject) {
5311
+ // Step 1: Register upload
5312
+ const registerUploadRequest = {
5313
+ registerUploadRequest: {
5314
+ recipes: ['urn:li:digitalmediaRecipe:feedshare-image'],
5315
+ owner: linkedinUserUrn,
5316
+ serviceRelationships: [
5327
5317
  {
5328
- status: 'READY',
5329
- description: { text: 'Datalayer Notebook' },
5330
- media: asset,
5331
- title: { text: 'Datalayer Notebook' },
5318
+ relationshipType: 'OWNER',
5319
+ identifier: 'urn:li:userGeneratedContent',
5332
5320
  },
5333
5321
  ],
5334
5322
  },
5335
- },
5336
- visibility: {
5337
- 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
5338
- },
5339
- };
5323
+ };
5324
+ const registerResp = await requestDatalayer({
5325
+ url: `${configuration.iamRunUrl}/api/iam/v1/proxy/request`,
5326
+ method: 'POST',
5327
+ body: {
5328
+ request_method: 'POST',
5329
+ request_url: 'https://api.linkedin.com/v2/assets?action=registerUpload',
5330
+ request_token: accessToken,
5331
+ request_body: registerUploadRequest,
5332
+ },
5333
+ });
5334
+ const asset = registerResp.response.value.asset;
5335
+ const uploadUrl = registerResp.response.value.uploadMechanism['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest'].uploadUrl;
5336
+ // Step 2: Upload image
5337
+ await requestDatalayer({
5338
+ url: `${configuration.iamRunUrl}/api/iam/v1/proxy/request`,
5339
+ method: 'POST',
5340
+ body: {
5341
+ request_method: 'PUT',
5342
+ request_url: uploadUrl,
5343
+ request_token: accessToken,
5344
+ request_body: {
5345
+ uploadURL: uploadUrl,
5346
+ content: uploadObject,
5347
+ userURN: linkedinUserUrn,
5348
+ },
5349
+ },
5350
+ });
5351
+ // Step 3: Create share with media
5352
+ shareRequest = {
5353
+ author: linkedinUserUrn,
5354
+ lifecycleState: 'PUBLISHED',
5355
+ specificContent: {
5356
+ 'com.linkedin.ugc.ShareContent': {
5357
+ shareCommentary: { text: postText },
5358
+ shareMediaCategory: 'IMAGE',
5359
+ media: [
5360
+ {
5361
+ status: 'READY',
5362
+ description: { text: 'Datalayer Notebook' },
5363
+ media: asset,
5364
+ title: { text: 'Datalayer Notebook' },
5365
+ },
5366
+ ],
5367
+ },
5368
+ },
5369
+ visibility: {
5370
+ 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
5371
+ },
5372
+ };
5373
+ }
5374
+ else {
5375
+ // Text-only share (no image upload)
5376
+ shareRequest = {
5377
+ author: linkedinUserUrn,
5378
+ lifecycleState: 'PUBLISHED',
5379
+ specificContent: {
5380
+ 'com.linkedin.ugc.ShareContent': {
5381
+ shareCommentary: { text: postText },
5382
+ shareMediaCategory: 'NONE',
5383
+ },
5384
+ },
5385
+ visibility: {
5386
+ 'com.linkedin.ugc.MemberNetworkVisibility': 'PUBLIC',
5387
+ },
5388
+ };
5389
+ }
5340
5390
  return requestDatalayer({
5341
5391
  url: `${configuration.iamRunUrl}/api/iam/v1/proxy/request`,
5342
5392
  method: 'POST',
@@ -6353,6 +6403,7 @@ export const useCache = ({ loginRoute = '/login' } = {}) => {
6353
6403
  useOAuth2AuthorizationURL,
6354
6404
  useOAuth2AuthorizationLinkURL,
6355
6405
  useGetGitHubProfile,
6406
+ useGetGoogleProfile,
6356
6407
  useGetLinkedinProfile,
6357
6408
  usePostLinkedinShare,
6358
6409
  usePostLinkedinShareWithUpload,
@@ -4,13 +4,13 @@
4
4
  */
5
5
  import { useState } from 'react';
6
6
  import { useCellStore } from '../state';
7
- import { takeHTMLNodeScreenshot } from '../utils/Screenshot';
7
+ import { takeHTMLNodeScreencapture } from '../utils/Screencapture';
8
8
  const useCellOutputshot = () => {
9
9
  const [outputshot, setOutputshot] = useState('');
10
10
  const [error, setError] = useState();
11
11
  const { update } = useCellStore();
12
12
  const takeOutputshot = (node) => {
13
- takeHTMLNodeScreenshot(node)
13
+ takeHTMLNodeScreencapture(node)
14
14
  .then(outputshotData => {
15
15
  setOutputshot(outputshotData);
16
16
  update({
@@ -0,0 +1,18 @@
1
+ import { ReactNode } from 'react';
2
+ export type ScreencaptureContextType = {
3
+ closeScreencapture: () => void;
4
+ displayScreencapture: (nextScreencapture: any) => void;
5
+ };
6
+ export declare const ScreencaptureContext: import("react").Context<ScreencaptureContextType>;
7
+ export declare function useScreencapture(): ScreencaptureContextType;
8
+ /**
9
+ * The type for the Screencapture context provider.
10
+ */
11
+ export declare const ScreencaptureContextProvider: import("react").Provider<ScreencaptureContextType>;
12
+ export type IScreencaptureProviderProps = {
13
+ children?: ReactNode;
14
+ zIndex?: number;
15
+ disableDarken?: boolean;
16
+ screenshotSurface?: (qfds: any) => JSX.Element;
17
+ };
18
+ export declare function ScreencaptureProvider({ children, zIndex, disableDarken, screenshotSurface, }: IScreencaptureProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,53 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * Copyright (c) 2023-2025 Datalayer, Inc.
4
+ * Distributed under the terms of the Modified BSD License.
5
+ */
6
+ import { createContext, useState, useContext } from 'react';
7
+ import { LayoutScreencapture } from './layouts';
8
+ export const ScreencaptureContext = createContext({
9
+ closeScreencapture: () => { },
10
+ displayScreencapture: (nextScreencapture) => { },
11
+ });
12
+ export function useScreencapture() {
13
+ const context = useContext(ScreencaptureContext);
14
+ if (!context)
15
+ throw new Error('useContext must be inside a provider with a value.');
16
+ return context;
17
+ }
18
+ /**
19
+ * The type for the Screencapture context provider.
20
+ */
21
+ export const ScreencaptureContextProvider = ScreencaptureContext.Provider;
22
+ export function ScreencaptureProvider({ children = null, zIndex = 9999, disableDarken = false, screenshotSurface = undefined, }) {
23
+ const defaultScreencaptureSurface = {
24
+ position: 'fixed',
25
+ top: 0,
26
+ left: 0,
27
+ width: '100%',
28
+ height: '100%',
29
+ display: 'flex',
30
+ alignItems: 'center',
31
+ justifyContent: 'center',
32
+ backgroundColor: disableDarken ? 'initial' : 'rgba(0, 0, 0, 0.5)',
33
+ zIndex,
34
+ };
35
+ const [screenshot, setScreencapture] = useState({
36
+ open: false,
37
+ render: (closeScreencapture) => _jsx(_Fragment, {}),
38
+ });
39
+ const displayScreencapture = (nextScreencapture) => {
40
+ setScreencapture({
41
+ open: true,
42
+ render: nextScreencapture,
43
+ });
44
+ };
45
+ const closeScreencapture = () => {
46
+ setScreencapture({
47
+ open: false,
48
+ render: (closeScreencapture) => _jsx(_Fragment, {}),
49
+ });
50
+ };
51
+ return (_jsxs(ScreencaptureContextProvider, { value: { closeScreencapture, displayScreencapture }, children: [_jsx(LayoutScreencapture, {}), children, screenshot.open &&
52
+ (screenshotSurface ? (screenshotSurface(screenshot.render(closeScreencapture))) : (_jsx("div", { style: defaultScreencaptureSurface, children: screenshot.render(closeScreencapture) })))] }));
53
+ }
@@ -24,6 +24,17 @@ export type ILinkedInUser = {
24
24
  picture: string;
25
25
  getUrn(): string;
26
26
  };
27
+ export type IGoogleUser = {
28
+ iamProvider: IIAMProviderName;
29
+ sub: string;
30
+ email_verified: boolean;
31
+ name: string;
32
+ given_name: string;
33
+ family_name: string;
34
+ email: string;
35
+ picture: string;
36
+ locale: string;
37
+ };
27
38
  export type IXUser = {
28
39
  iamProvider: IIAMProviderName;
29
40
  sub: string;
@@ -1,5 +1,5 @@
1
1
  import { IUser } from './User';
2
- export type IIAMProviderName = 'bluesky' | 'discord' | 'github' | 'linkedin' | 'okta' | 'x';
2
+ export type IIAMProviderName = 'bluesky' | 'discord' | 'github' | 'google' | 'linkedin' | 'okta' | 'x';
3
3
  export type IIAMProviderSpec = {
4
4
  name: IIAMProviderName;
5
5
  oauth2CallbackServerRoute: string;
@@ -16,6 +16,7 @@ export declare class IAMProvidersSpecs {
16
16
  static getProvider(providerIAMProvidersType: any): IIAMProviderSpec;
17
17
  static readonly Bluesky: IIAMProviderSpec;
18
18
  static readonly GitHub: IIAMProviderSpec;
19
+ static readonly Google: IIAMProviderSpec;
19
20
  static readonly LinkedIn: IIAMProviderSpec;
20
21
  static readonly Okta: IIAMProviderSpec;
21
22
  }
@@ -12,6 +12,9 @@ export class IAMProvidersSpecs {
12
12
  case 'github': {
13
13
  return this.GitHub;
14
14
  }
15
+ case 'google': {
16
+ return this.Google;
17
+ }
15
18
  case 'linkedin': {
16
19
  return this.LinkedIn;
17
20
  }
@@ -46,6 +49,17 @@ export class IAMProvidersSpecs {
46
49
  postShareURL: '',
47
50
  registerUploadURL: '',
48
51
  };
52
+ static Google = {
53
+ name: 'google',
54
+ oauth2CallbackServerRoute: 'jupyter_iam/oauth2/callback',
55
+ oauth2CallbackUIRoute: '/iam/oauth2/google/callback',
56
+ accessTokenCookieName: (user) => `__datalayer__google_access_token_${user.id}`,
57
+ refreshTokenCookieName: '__datalayer__google_refresh_token',
58
+ userInfoURL: 'https://openidconnect.googleapis.com/v1/userinfo',
59
+ tokenRefreshURL: 'https://oauth2.googleapis.com/token',
60
+ postShareURL: '',
61
+ registerUploadURL: '',
62
+ };
49
63
  static LinkedIn = {
50
64
  name: 'linkedin',
51
65
  oauth2CallbackServerRoute: 'jupyter_iam/oauth2/callback',
@@ -13,7 +13,7 @@ export const asPage = (s) => {
13
13
  description: s.description_t,
14
14
  theme: s.theme_s,
15
15
  nbformat: s.nbformat_s ? JSON.parse(s.nbformat_s) : undefined,
16
- screenCapture: s.screen_capture_s,
16
+ screenCapture: s.screen_capture_cdn_url_s || s.screen_capture_s,
17
17
  tags: s.tags_ss ?? [],
18
18
  creator: s.creator ? asUser(s.creator) : undefined,
19
19
  creatorId: s.creator_uid,
@@ -4,7 +4,7 @@ type CardIconColorsType = (typeof CardIconColors)[number];
4
4
  export type PageTagMetadata = {
5
5
  fillColor: React.CSSProperties['backgroundColor'];
6
6
  };
7
- export type PageTagName = 'ai' | 'deep-learning' | 'example' | 'llm' | 'python' | 'pytorch' | 'spatial' | 'tutorial';
7
+ export type PageTagName = 'agent' | 'ai' | 'ai-agent' | 'deep-learning' | 'example' | 'llm' | 'notebook' | 'python' | 'pytorch' | 'spatial' | 'tutorial';
8
8
  export type PageTag = {
9
9
  id: string;
10
10
  text?: PageTagName;
@@ -9,12 +9,24 @@ export const PAGE_TAGE_NONE = {
9
9
  Icon: CopilotIcon,
10
10
  color: 'pink',
11
11
  };
12
+ const PAGE_TAGE_AGENT = {
13
+ id: '9',
14
+ text: 'agent',
15
+ Icon: CopilotIcon,
16
+ color: 'teal',
17
+ };
12
18
  const PAGE_TAGE_AI = {
13
19
  id: '1',
14
20
  text: 'ai',
15
21
  Icon: CopilotIcon,
16
22
  color: 'pink',
17
23
  };
24
+ const PAGE_TAGE_AI_AGENT = {
25
+ id: '10',
26
+ text: 'ai-agent',
27
+ Icon: CopilotIcon,
28
+ color: 'purple',
29
+ };
18
30
  const PAGE_TAGE_DEEP_LEARNING = {
19
31
  id: '2',
20
32
  text: 'deep-learning',
@@ -33,6 +45,12 @@ const PAGE_TAGE_LLM = {
33
45
  Icon: CopilotIcon,
34
46
  color: 'coral',
35
47
  };
48
+ const PAGE_TAGE_NOTEBOOK = {
49
+ id: '11',
50
+ text: 'notebook',
51
+ Icon: CopilotIcon,
52
+ color: 'blue',
53
+ };
36
54
  const PAGE_TAGE_PYTHON = {
37
55
  id: '5',
38
56
  text: 'python',
@@ -60,10 +78,13 @@ const PAGE_TAGE_TUTORIAL = {
60
78
  metadata: { fillColor: 'yellow' },
61
79
  };
62
80
  export const PAGE_TAGS = [
81
+ PAGE_TAGE_AGENT,
63
82
  PAGE_TAGE_AI,
83
+ PAGE_TAGE_AI_AGENT,
64
84
  PAGE_TAGE_DEEP_LEARNING,
65
85
  PAGE_TAGE_EXAMPLE,
66
86
  PAGE_TAGE_LLM,
87
+ PAGE_TAGE_NOTEBOOK,
67
88
  PAGE_TAGE_PYTHON,
68
89
  PAGE_TAGE_PYTORCH,
69
90
  PAGE_TAGE_SPATIAL,
@@ -15,7 +15,7 @@ export type PortalDisplay = {
15
15
  portal: ReactPortal;
16
16
  pinned: boolean;
17
17
  };
18
- export type ScreenshotDisplay = {
18
+ export type ScreencaptureDisplay = {
19
19
  open: boolean;
20
20
  message?: string | void;
21
21
  };
@@ -30,13 +30,13 @@ export type ILayoutState = {
30
30
  organization?: IAnyOrganization;
31
31
  rightPortal?: PortalDisplay;
32
32
  screenCapture?: string;
33
- screenshot?: ScreenshotDisplay;
33
+ screenshot?: ScreencaptureDisplay;
34
34
  space?: IAnySpace;
35
35
  team?: IAnyTeam;
36
36
  };
37
37
  export type LayoutState = ILayoutState & {
38
38
  hideBackdrop: () => void;
39
- hideScreenshot: () => void;
39
+ hideScreencapture: () => void;
40
40
  reset: () => void;
41
41
  resetForcedLeftPortal: () => void;
42
42
  resetForcedRightPortal: () => void;
@@ -48,9 +48,9 @@ export type LayoutState = ILayoutState & {
48
48
  setLeftPortal: (leftPortal: PortalDisplay) => void;
49
49
  setLeftSidebarVariant: (leftSidebarVariant: LeftSidebarVariant) => void;
50
50
  setRightPortal: (rightPortal: PortalDisplay) => void;
51
- setScreenCapture: (screenCapture?: string) => void;
51
+ setScreencapture: (screenCapture?: string) => void;
52
52
  showBackdrop: (message?: string) => void;
53
- showScreenshot: (message?: string) => void;
53
+ showScreencapture: (message?: string) => void;
54
54
  triggerItemsRefresh: () => void;
55
55
  updateLayoutOrganization: (organization?: Partial<IAnyOrganization>) => void;
56
56
  updateLayoutSpace: (space?: Partial<IAnySpace>) => void;
@@ -20,12 +20,12 @@ export const layoutStore = createStore((set, get) => ({
20
20
  hideBackdrop: () => set((state) => ({
21
21
  backdrop: { open: false, message: undefined },
22
22
  })),
23
- hideScreenshot: () => set((state) => ({
23
+ hideScreencapture: () => set((state) => ({
24
24
  screenshot: { open: false, message: undefined },
25
25
  })),
26
26
  setBootstrapped: (bootstrapped) => set((state) => ({ bootstrapped })),
27
27
  showBackdrop: (message) => set((state) => ({ backdrop: { open: true, message } })),
28
- showScreenshot: (message) => set((state) => ({ screenshot: { open: true, message } })),
28
+ showScreencapture: (message) => set((state) => ({ screenshot: { open: true, message } })),
29
29
  setBanner: (bannerDisplay) => set((state) => ({
30
30
  banner: {
31
31
  timestamp: new Date(),
@@ -78,7 +78,7 @@ export const layoutStore = createStore((set, get) => ({
78
78
  triggerItemsRefresh: () => set((state) => ({
79
79
  itemsRefreshCount: state.itemsRefreshCount + 1,
80
80
  })),
81
- setScreenCapture: (screenCapture) => set((state) => ({ screenCapture })),
81
+ setScreencapture: (screenCapture) => set((state) => ({ screenCapture })),
82
82
  reset: () => set((state) => ({ bootstrapped: false })),
83
83
  }));
84
84
  export function useLayoutStore(selector) {
@@ -0,0 +1 @@
1
+ export declare const takeHTMLNodeScreencapture: (node: HTMLDivElement) => Promise<string>;
@@ -3,7 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  import html2canvas from 'html2canvas';
6
- export const takeHTMLNodeScreenshot = async (node) => {
6
+ export const takeHTMLNodeScreencapture = async (node) => {
7
7
  const sc = html2canvas(node, {
8
8
  width: node.getBoundingClientRect().width,
9
9
  height: node.getBoundingClientRect().height,
@@ -23,7 +23,7 @@ export * from './Notebook';
23
23
  export * from './Number';
24
24
  export * from './Plots';
25
25
  export * from './Random';
26
- // export * from './Screenshot'; Do not export html2canvas for now.
26
+ // export * from './Screencapture'; Do not export html2canvas for now.
27
27
  export * from './Sleep';
28
28
  export * from './Snapshot';
29
29
  export * from './String';
@@ -1,2 +1,8 @@
1
- export declare const DatasourceNew: () => import("react/jsx-runtime").JSX.Element;
1
+ export type DatasourceNewProps = {
2
+ /** Route to navigate after creating a datasource. Defaults to '/settings/integrations/datasources'. */
3
+ datasourcesListRoute?: string;
4
+ /** Route to navigate to the secrets page. Defaults to '/settings/iam/secrets'. */
5
+ secretsRoute?: string;
6
+ };
7
+ export declare const DatasourceNew: ({ datasourcesListRoute, secretsRoute, }?: DatasourceNewProps) => import("react/jsx-runtime").JSX.Element;
2
8
  export default DatasourceNew;
@@ -8,7 +8,7 @@ import { PageHeader, FormControl, Button, TextInput, Text, Textarea, Select, Fla
8
8
  import { Box } from '@datalayer/primer-addons';
9
9
  import { useCache, useNavigate, useToast } from '../../hooks';
10
10
  import { useRunStore } from '../../state';
11
- export const DatasourceNew = () => {
11
+ export const DatasourceNew = ({ datasourcesListRoute = '/settings/integrations/datasources', secretsRoute = '/settings/iam/secrets', } = {}) => {
12
12
  const runStore = useRunStore();
13
13
  const { useCreateDatasource } = useCache();
14
14
  const createDatasourceMutation = useCreateDatasource();
@@ -99,7 +99,7 @@ export const DatasourceNew = () => {
99
99
  onSuccess: (resp) => {
100
100
  if (resp.success) {
101
101
  enqueueToast(resp.message, { variant: 'success' });
102
- navigate(`/settings/integrations/datasources`);
102
+ navigate(datasourcesListRoute);
103
103
  }
104
104
  },
105
105
  onSettled: () => {
@@ -107,7 +107,7 @@ export const DatasourceNew = () => {
107
107
  },
108
108
  });
109
109
  };
110
- return (_jsxs(Box, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New Datasource" }) }) }), _jsxs(Flash, { variant: "warning", children: [formValues.variant === 'athena' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://aws.amazon.com/athena", children: "Amazon Athena" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate('/settings/iam/secrets', e), children: "Secrets" }), ' ', "are available:", ' ', _jsx(Text, { as: "code", children: "AWS_SECRET_ACCESS_KEY" }), ' ', _jsx(Text, { as: "code", children: "AWS_ACCESS_KEY_ID" }), ' ', _jsx(Text, { as: "code", children: "AWS_DEFAULT_REGION" })] })), formValues.variant === 'bigquery' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://cloud.google.com/bigquery", children: "Google Big Query" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate('/settings/iam/secrets', e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "GOOGLE_APPLICATION_CREDENTIALS" })] })), formValues.variant === 'mssentinel' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://learn.microsoft.com/en-us/azure/sentinel/overview?tabs=defender-portaly", children: "Microsoft Sentinel" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate('/settings/iam/secrets', e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "AZURE_TENANT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_SECRET" }), ` `, _jsx(Text, { as: "code", children: "AZURE_SUBSCRIPTION_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_RESOURCE_GROUP" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_ID" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_NAME" })] })), formValues.variant === 'splunk' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://www.splunk.com/", children: "Splunk" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate('/settings/iam/secrets', e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "SPLUNK_HOST" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PORT" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_USERNAME" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PASSWORD" })] }))] }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Datasource type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "athena", children: "Amazon Athena" }), _jsx(Select.Option, { value: "bigquery", children: "Google BigQuery" }), _jsx(Select.Option, { value: "mssentinel", children: "Microsoft Sentinel" }), _jsx(Select.Option, { value: "splunk", children: "Splunk" })] }), _jsx(FormControl.Caption, { children: "Pick the most appropriate datasource type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The datasource name is a short name that identifies in a unique way your datasource." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), formValues.variant === 'athena' && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { block: true, value: formValues.database, onChange: valueDatabaseChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Database must have more than 1." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Output Bucket" }), _jsx(TextInput, { block: true, value: formValues.outputBucket, onChange: valueOutputBucketChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Output bucket must have more than 1." }))] })] })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.database ||
110
+ return (_jsxs(Box, { children: [_jsx(PageHeader, { children: _jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "New Datasource" }) }) }), _jsxs(Flash, { variant: "warning", children: [formValues.variant === 'athena' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://aws.amazon.com/athena", children: "Amazon Athena" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secrets" }), ' ', "are available:", ' ', _jsx(Text, { as: "code", children: "AWS_SECRET_ACCESS_KEY" }), ' ', _jsx(Text, { as: "code", children: "AWS_ACCESS_KEY_ID" }), ' ', _jsx(Text, { as: "code", children: "AWS_DEFAULT_REGION" })] })), formValues.variant === 'bigquery' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://cloud.google.com/bigquery", children: "Google Big Query" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "GOOGLE_APPLICATION_CREDENTIALS" })] })), formValues.variant === 'mssentinel' && (_jsxs(Text, { children: ["For", ' ', _jsx(Link, { href: "https://learn.microsoft.com/en-us/azure/sentinel/overview?tabs=defender-portaly", children: "Microsoft Sentinel" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "AZURE_TENANT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_CLIENT_SECRET" }), ` `, _jsx(Text, { as: "code", children: "AZURE_SUBSCRIPTION_ID" }), ` `, _jsx(Text, { as: "code", children: "AZURE_RESOURCE_GROUP" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_ID" }), ` `, _jsx(Text, { as: "code", children: "MSSENTINEL_WORKSPACE_NAME" })] })), formValues.variant === 'splunk' && (_jsxs(Text, { children: ["For ", _jsx(Link, { href: "https://www.splunk.com/", children: "Splunk" }), ", ensure the following", ' ', _jsx(Link, { href: "javascript: return false;", onClick: e => navigate(secretsRoute, e), children: "Secret" }), ' ', "is available:", ' ', _jsx(Text, { as: "code", children: "SPLUNK_HOST" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PORT" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_USERNAME" }), ` `, _jsx(Text, { as: "code", children: "SPLUNK_PASSWORD" })] }))] }), _jsx(Box, { display: "grid", gridTemplateColumns: "1fr 1fr", sx: { gap: 3 }, children: _jsx(Box, { children: _jsxs(Box, { sx: { label: { marginTop: 2 } }, children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Datasource type" }), _jsxs(Select, { name: "type", value: formValues.variant, onChange: valueVariantChange, children: [_jsx(Select.Option, { value: "athena", children: "Amazon Athena" }), _jsx(Select.Option, { value: "bigquery", children: "Google BigQuery" }), _jsx(Select.Option, { value: "mssentinel", children: "Microsoft Sentinel" }), _jsx(Select.Option, { value: "splunk", children: "Splunk" })] }), _jsx(FormControl.Caption, { children: "Pick the most appropriate datasource type." })] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Name" }), _jsx(TextInput, { block: true, value: formValues.name, onChange: valueNameChange, autoFocus: true }), _jsx(FormControl.Caption, { children: "Hint: The datasource name is a short name that identifies in a unique way your datasource." }), validationResult.name === false && (_jsx(FormControl.Validation, { variant: "error", children: "Name length must be between 2 and 32 characters." }))] }), formValues.variant === 'athena' && (_jsxs(_Fragment, { children: [_jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Database" }), _jsx(TextInput, { block: true, value: formValues.database, onChange: valueDatabaseChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Database must have more than 1." }))] }), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Output Bucket" }), _jsx(TextInput, { block: true, value: formValues.outputBucket, onChange: valueOutputBucketChange }), validationResult.database === false && (_jsx(FormControl.Validation, { variant: "error", children: "Output bucket must have more than 1." }))] })] })), _jsxs(FormControl, { required: true, children: [_jsx(FormControl.Label, { children: "Description" }), _jsx(Textarea, { block: true, value: formValues.description, onChange: valueDescriptionChange }), validationResult.description === false && (_jsx(FormControl.Validation, { variant: "error", children: "Description must have more than 2 characters." }))] }), _jsx(Button, { variant: "primary", disabled: !validationResult.database ||
111
111
  !validationResult.outputBucket ||
112
112
  !validationResult.name ||
113
113
  !validationResult.description, sx: { marginTop: 2 }, onClick: e => {
@@ -1,2 +1,8 @@
1
- export declare const Datasources: () => import("react/jsx-runtime").JSX.Element;
1
+ export type DatasourcesProps = {
2
+ /** Route to navigate when clicking "New datasource" button. Defaults to '/new/datasource'. */
3
+ newDatasourceRoute?: string;
4
+ /** Base route for the datasources list (used for edit navigation). Defaults to current relative path. */
5
+ datasourcesListRoute?: string;
6
+ };
7
+ export declare const Datasources: ({ newDatasourceRoute, datasourcesListRoute, }?: DatasourcesProps) => import("react/jsx-runtime").JSX.Element;
2
8
  export default Datasources;
@@ -9,7 +9,7 @@ import { Blankslate, PageHeader, Table, DataTable, } from '@primer/react/experim
9
9
  import { Box } from '@datalayer/primer-addons';
10
10
  import { EditIcon } from '@datalayer/icons-react';
11
11
  import { useCache, useNavigate } from '../../hooks';
12
- const DatasourcesTable = () => {
12
+ const DatasourcesTable = ({ datasourcesListRoute, }) => {
13
13
  const { useDatasources } = useCache();
14
14
  const datasourcesQuery = useDatasources();
15
15
  const navigate = useNavigate();
@@ -38,12 +38,14 @@ const DatasourcesTable = () => {
38
38
  {
39
39
  header: '',
40
40
  field: 'id',
41
- renderCell: datasource => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(`${datasource.id}`, e) })),
41
+ renderCell: datasource => (_jsx(IconButton, { icon: EditIcon, "aria-label": "Edit", size: "small", variant: "invisible", onClick: e => navigate(datasourcesListRoute
42
+ ? `${datasourcesListRoute}/${datasource.id}`
43
+ : `${datasource.id}`, e) })),
42
44
  },
43
45
  ] })] }));
44
46
  };
45
- export const Datasources = () => {
47
+ export const Datasources = ({ newDatasourceRoute = '/new/datasource', datasourcesListRoute, } = {}) => {
46
48
  const navigate = useNavigate();
47
- return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Datasources" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate('/new/datasource', e), children: "New datasource" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(DatasourcesTable, {}) }) })] }));
49
+ return (_jsxs(PageLayout, { containerWidth: "full", padding: "normal", style: { overflow: 'visible', minHeight: 'calc(100vh - 45px)' }, children: [_jsx(PageLayout.Header, { children: _jsxs(PageHeader, { children: [_jsx(PageHeader.TitleArea, { variant: "large", children: _jsx(PageHeader.Title, { children: "Datasources" }) }), _jsx(PageHeader.Actions, { children: _jsx(Button, { size: "small", variant: "primary", onClick: e => navigate(newDatasourceRoute, e), children: "New datasource" }) })] }) }), _jsx(PageLayout.Content, { children: _jsx(Box, { children: _jsx(DatasourcesTable, { datasourcesListRoute: datasourcesListRoute }) }) })] }));
48
50
  };
49
51
  export default Datasources;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/core",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",
@@ -0,0 +1,58 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+
6
+ .crosshairs {
7
+ position: fixed;
8
+ width: 100%;
9
+ z-index: 2147483645;
10
+ }
11
+
12
+ .crosshairs.hidden {
13
+ display: none;
14
+ }
15
+
16
+ .crosshairs::before,
17
+ .crosshairs::after {
18
+ content: '';
19
+ position: absolute;
20
+ }
21
+
22
+ .crosshairs::before {
23
+ height: 24px;
24
+ width: 2px;
25
+ background: #fff;
26
+ top: -11px;
27
+ }
28
+
29
+ .crosshairs::after {
30
+ width: 24px;
31
+ height: 2px;
32
+ background: #fff;
33
+ left: -11px;
34
+ }
35
+
36
+ .overlay {
37
+ position: fixed;
38
+ top: 0;
39
+ left: 0;
40
+ width: 100%;
41
+ height: 100%;
42
+ background-color: rgba(0, 0, 0, 0.5);
43
+ }
44
+
45
+ .overlay.highlighting {
46
+ background: none;
47
+ border-color: rgba(0, 0, 0, 0.5);
48
+ border-style: solid;
49
+ }
50
+
51
+ .crosshairs,
52
+ .crosshairs:before,
53
+ .crosshairs:after,
54
+ .overlay,
55
+ .overlay:before,
56
+ .overlay:after {
57
+ box-sizing: border-box;
58
+ }
@@ -1,3 +0,0 @@
1
- import { PropsWithChildren } from 'react';
2
- export declare const ScreenCaptureButton: (props: PropsWithChildren) => import("react/jsx-runtime").JSX.Element;
3
- export default ScreenCaptureButton;
@@ -1,2 +0,0 @@
1
- export * from './ScreenCapture';
2
- export * from './ScreenCaptureButton';
@@ -1,3 +0,0 @@
1
- import './LayoutScreenshot.css';
2
- export declare const LayoutScreenshot: () => import("react/jsx-runtime").JSX.Element;
3
- export default LayoutScreenshot;
@@ -1,18 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export type ScreenshotContextType = {
3
- closeScreenshot: () => void;
4
- displayScreenshot: (nextScreenshot: any) => void;
5
- };
6
- export declare const ScreenshotContext: import("react").Context<ScreenshotContextType>;
7
- export declare function useScreenshot(): ScreenshotContextType;
8
- /**
9
- * The type for the Screenshot context provider.
10
- */
11
- export declare const ScreenshotContextProvider: import("react").Provider<ScreenshotContextType>;
12
- export type IScreenshotProviderProps = {
13
- children?: ReactNode;
14
- zIndex?: number;
15
- disableDarken?: boolean;
16
- screenshotSurface?: (qfds: any) => JSX.Element;
17
- };
18
- export declare function ScreenshotProvider({ children, zIndex, disableDarken, screenshotSurface, }: IScreenshotProviderProps): import("react/jsx-runtime").JSX.Element;
@@ -1,53 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /*
3
- * Copyright (c) 2023-2025 Datalayer, Inc.
4
- * Distributed under the terms of the Modified BSD License.
5
- */
6
- import { createContext, useState, useContext } from 'react';
7
- import { LayoutScreenshot } from './layouts';
8
- export const ScreenshotContext = createContext({
9
- closeScreenshot: () => { },
10
- displayScreenshot: (nextScreenshot) => { },
11
- });
12
- export function useScreenshot() {
13
- const context = useContext(ScreenshotContext);
14
- if (!context)
15
- throw new Error('useContext must be inside a provider with a value.');
16
- return context;
17
- }
18
- /**
19
- * The type for the Screenshot context provider.
20
- */
21
- export const ScreenshotContextProvider = ScreenshotContext.Provider;
22
- export function ScreenshotProvider({ children = null, zIndex = 9999, disableDarken = false, screenshotSurface = undefined, }) {
23
- const defaultScreenshotSurface = {
24
- position: 'fixed',
25
- top: 0,
26
- left: 0,
27
- width: '100%',
28
- height: '100%',
29
- display: 'flex',
30
- alignItems: 'center',
31
- justifyContent: 'center',
32
- backgroundColor: disableDarken ? 'initial' : 'rgba(0, 0, 0, 0.5)',
33
- zIndex,
34
- };
35
- const [screenshot, setScreenshot] = useState({
36
- open: false,
37
- render: (closeScreenshot) => _jsx(_Fragment, {}),
38
- });
39
- const displayScreenshot = (nextScreenshot) => {
40
- setScreenshot({
41
- open: true,
42
- render: nextScreenshot,
43
- });
44
- };
45
- const closeScreenshot = () => {
46
- setScreenshot({
47
- open: false,
48
- render: (closeScreenshot) => _jsx(_Fragment, {}),
49
- });
50
- };
51
- return (_jsxs(ScreenshotContextProvider, { value: { closeScreenshot, displayScreenshot }, children: [_jsx(LayoutScreenshot, {}), children, screenshot.open &&
52
- (screenshotSurface ? (screenshotSurface(screenshot.render(closeScreenshot))) : (_jsx("div", { style: defaultScreenshotSurface, children: screenshot.render(closeScreenshot) })))] }));
53
- }
@@ -1 +0,0 @@
1
- export declare const takeHTMLNodeScreenshot: (node: HTMLDivElement) => Promise<string>;