@datalayer/core 1.1.66 → 1.2.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +43 -20
  2. package/lib/api/constants.d.ts +1 -1
  3. package/lib/api/constants.js +1 -1
  4. package/lib/api/contents/attachments.js +3 -7
  5. package/lib/api/contents/datasets.d.ts +48 -0
  6. package/lib/api/contents/datasets.js +44 -7
  7. package/lib/api/contents/runtimeMounts.js +1 -1
  8. package/lib/api/contents/sandboxUid.d.ts +13 -45
  9. package/lib/api/contents/sandboxUid.js +14 -65
  10. package/lib/api/evals/client.d.ts +402 -0
  11. package/lib/api/evals/client.js +359 -0
  12. package/lib/api/evals/index.d.ts +10 -0
  13. package/lib/api/evals/index.js +18 -0
  14. package/lib/api/evals/request.d.ts +43 -0
  15. package/lib/api/evals/request.js +67 -0
  16. package/lib/api/evals/status.d.ts +27 -0
  17. package/lib/api/evals/status.js +122 -0
  18. package/lib/api/evals/types.d.ts +1335 -0
  19. package/lib/api/evals/types.js +5 -0
  20. package/lib/api/iam/authentication.js +6 -4
  21. package/lib/api/iam/connectedAgents.d.ts +29 -0
  22. package/lib/api/iam/connectedAgents.js +35 -0
  23. package/lib/api/iam/healthz.js +2 -2
  24. package/lib/api/iam/identityProviders.d.ts +76 -0
  25. package/lib/api/iam/identityProviders.js +187 -0
  26. package/lib/api/iam/index.d.ts +1 -0
  27. package/lib/api/iam/index.js +1 -0
  28. package/lib/api/iam/mcpPolicy.d.ts +102 -1
  29. package/lib/api/iam/mcpPolicy.js +38 -2
  30. package/lib/api/iam/oauth2.js +18 -12
  31. package/lib/api/iam/profile.d.ts +17 -1
  32. package/lib/api/iam/profile.js +27 -0
  33. package/lib/api/iam/scimTokens.d.ts +79 -0
  34. package/lib/api/iam/scimTokens.js +141 -0
  35. package/lib/api/iam/secrets.js +10 -23
  36. package/lib/api/iam/trials.d.ts +38 -0
  37. package/lib/api/iam/trials.js +66 -0
  38. package/lib/api/index.d.ts +4 -0
  39. package/lib/api/index.js +4 -0
  40. package/lib/api/mcp/observability.d.ts +34 -11
  41. package/lib/api/mcp/observability.js +97 -59
  42. package/lib/api/orchestration/client.d.ts +53 -0
  43. package/lib/api/orchestration/client.js +105 -0
  44. package/lib/api/orchestration/events.d.ts +88 -0
  45. package/lib/api/orchestration/events.js +245 -0
  46. package/lib/api/orchestration/generated.d.ts +693 -0
  47. package/lib/api/orchestration/generated.js +687 -0
  48. package/lib/api/orchestration/index.d.ts +30 -0
  49. package/lib/api/orchestration/index.js +34 -0
  50. package/lib/api/orchestration/lifecycle.d.ts +39 -0
  51. package/lib/api/orchestration/lifecycle.js +57 -0
  52. package/lib/api/orchestration/measures.d.ts +55 -0
  53. package/lib/api/orchestration/measures.js +99 -0
  54. package/lib/api/otel/dashboards.d.ts +56 -0
  55. package/lib/api/otel/dashboards.js +50 -0
  56. package/lib/api/otel/index.d.ts +1 -0
  57. package/lib/api/otel/index.js +1 -0
  58. package/lib/api/otel/metrics.d.ts +16 -0
  59. package/lib/api/otel/metrics.js +15 -0
  60. package/lib/api/scheduler/client.d.ts +32 -0
  61. package/lib/api/scheduler/client.js +42 -0
  62. package/lib/api/scheduler/index.d.ts +8 -0
  63. package/lib/api/scheduler/index.js +16 -0
  64. package/lib/api/scheduler/request.d.ts +28 -0
  65. package/lib/api/scheduler/request.js +50 -0
  66. package/lib/api/scheduler/types.d.ts +88 -0
  67. package/lib/api/scheduler/types.js +5 -0
  68. package/lib/api/spacer/comments.d.ts +97 -0
  69. package/lib/api/spacer/comments.js +45 -0
  70. package/lib/api/spacer/index.d.ts +9 -0
  71. package/lib/api/spacer/index.js +17 -0
  72. package/lib/api/spacer/notebooks.d.ts +33 -0
  73. package/lib/api/spacer/notebooks.js +39 -0
  74. package/lib/api/spacer/request.d.ts +25 -0
  75. package/lib/api/spacer/request.js +46 -0
  76. package/lib/api/spacer/spaces.d.ts +68 -0
  77. package/lib/api/spacer/spaces.js +45 -0
  78. package/lib/api/utils/validation.d.ts +4 -4
  79. package/lib/api/utils/validation.js +10 -7
  80. package/lib/client/auth/strategies.js +1 -1
  81. package/lib/client/constants.d.ts +1 -0
  82. package/lib/client/constants.js +1 -0
  83. package/lib/client/mixins/IAMMixin.js +2 -2
  84. package/lib/components/animation/AnimatedText.d.ts +1 -44
  85. package/lib/components/animation/AnimatedText.js +6 -122
  86. package/lib/components/anonymous/AnonymousKeyExpired.d.ts +56 -0
  87. package/lib/components/anonymous/AnonymousKeyExpired.js +96 -0
  88. package/lib/components/anonymous/AnonymousKeyTimer.d.ts +63 -0
  89. package/lib/components/anonymous/AnonymousKeyTimer.js +140 -0
  90. package/lib/components/anonymous/index.d.ts +7 -0
  91. package/lib/components/anonymous/index.js +15 -0
  92. package/lib/components/avatars/UserAvatar.d.ts +7 -1
  93. package/lib/components/avatars/UserAvatar.js +15 -1
  94. package/lib/components/billing/BillingEntitySelect.d.ts +6 -0
  95. package/lib/components/billing/BillingEntitySelect.js +49 -10
  96. package/lib/components/billing/eligibility.d.ts +22 -0
  97. package/lib/components/billing/eligibility.js +27 -0
  98. package/lib/components/billing/index.d.ts +1 -0
  99. package/lib/components/billing/index.js +1 -0
  100. package/lib/components/checkout/StripeCheckout.d.ts +1 -1
  101. package/lib/components/checkout/StripeCheckout.js +1 -1
  102. package/lib/components/collaboration/LiveEditorCollaborators.d.ts +5 -0
  103. package/lib/components/collaboration/LiveEditorCollaborators.js +3 -1
  104. package/lib/components/display/DatalayerBox.d.ts +2 -3
  105. package/lib/components/display/DatalayerBox.js +1 -1
  106. package/lib/components/display/NavLink.js +3 -2
  107. package/lib/components/display/VisuallyHidden.d.ts +1 -2
  108. package/lib/components/labels/StatusLabels.d.ts +2 -10
  109. package/lib/components/labels/StatusLabels.js +2 -26
  110. package/lib/components/principal/PrincipalAppearance.d.ts +21 -42
  111. package/lib/components/principal/PrincipalAppearance.js +232 -174
  112. package/lib/components/principal/PrincipalAvatar.d.ts +8 -1
  113. package/lib/components/principal/PrincipalAvatar.js +2 -2
  114. package/lib/components/principal/PrincipalDetailsOverlay.d.ts +8 -1
  115. package/lib/components/principal/PrincipalDetailsOverlay.js +2 -2
  116. package/lib/components/screencapture/Screencapture.js +46 -3
  117. package/lib/components/sharing/ShareAccessComponent.d.ts +45 -4
  118. package/lib/components/sharing/ShareAccessComponent.js +110 -78
  119. package/lib/components/sharing/SharingEditor.d.ts +5 -1
  120. package/lib/components/sharing/SharingEditor.js +9 -4
  121. package/lib/components/sharing/index.d.ts +1 -0
  122. package/lib/components/sharing/index.js +1 -0
  123. package/lib/components/sharing/sandboxSharing.d.ts +26 -0
  124. package/lib/components/sharing/sandboxSharing.js +47 -0
  125. package/lib/components/spaces/SpaceDetailsOverlay.d.ts +39 -0
  126. package/lib/components/spaces/SpaceDetailsOverlay.js +80 -0
  127. package/lib/components/spaces/SpaceDisplay.d.ts +38 -0
  128. package/lib/components/spaces/SpaceDisplay.js +92 -0
  129. package/lib/components/spaces/index.d.ts +4 -0
  130. package/lib/components/spaces/index.js +7 -0
  131. package/lib/components/spaces/spaceDisplayModel.d.ts +93 -0
  132. package/lib/components/spaces/spaceDisplayModel.js +170 -0
  133. package/lib/config/Configuration.d.ts +4 -4
  134. package/lib/config/Configuration.js +7 -7
  135. package/lib/config/index.d.ts +1 -0
  136. package/lib/config/index.js +1 -0
  137. package/lib/config/planes.d.ts +45 -0
  138. package/lib/config/planes.js +53 -0
  139. package/lib/hooks/cacheConverters.d.ts +0 -7
  140. package/lib/hooks/cacheConverters.js +0 -25
  141. package/lib/hooks/index.d.ts +1 -0
  142. package/lib/hooks/index.js +1 -0
  143. package/lib/hooks/useBackdrop.d.ts +4 -5
  144. package/lib/hooks/useBillingEntityStore.js +3 -0
  145. package/lib/hooks/useCache.d.ts +197 -44
  146. package/lib/hooks/useCache.js +1586 -494
  147. package/lib/hooks/useContents.d.ts +22 -1
  148. package/lib/hooks/useContents.js +90 -21
  149. package/lib/hooks/useMcp.d.ts +87 -1
  150. package/lib/hooks/useMcp.js +248 -13
  151. package/lib/hooks/useNavigate.d.ts +41 -3
  152. package/lib/hooks/useNavigate.js +116 -52
  153. package/lib/hooks/usePrincipalCacheStore.js +3 -0
  154. package/lib/hooks/usePrincipalStore.js +3 -0
  155. package/lib/hooks/useSpaceCacheStore.d.ts +50 -0
  156. package/lib/hooks/useSpaceCacheStore.js +85 -0
  157. package/lib/hooks/useWindowSize.js +1 -1
  158. package/lib/models/CreditsDTO.d.ts +6 -5
  159. package/lib/models/CreditsDTO.js +7 -5
  160. package/lib/models/IAMProvidersSpecs.d.ts +10 -5
  161. package/lib/models/IAMProvidersSpecs.js +0 -23
  162. package/lib/models/McpBinding.d.ts +6 -0
  163. package/lib/models/Organization.d.ts +69 -0
  164. package/lib/models/Organization.js +96 -0
  165. package/lib/models/Secret.d.ts +1 -2
  166. package/lib/models/Secret.js +2 -18
  167. package/lib/models/StartedBy.d.ts +34 -0
  168. package/lib/models/StartedBy.js +36 -0
  169. package/lib/models/Team.d.ts +2 -0
  170. package/lib/models/Team.js +2 -0
  171. package/lib/models/User.d.ts +4 -0
  172. package/lib/models/User.js +3 -0
  173. package/lib/models/UserSettings.d.ts +13 -0
  174. package/lib/models/UserSettings.js +9 -0
  175. package/lib/models/contents/__fixtures__/v1-contracts.json +158 -30
  176. package/lib/navigation/adapters/react-router.d.ts +1 -1
  177. package/lib/navigation/adapters/react-router.js +3 -0
  178. package/lib/navigation/components.js +2 -3
  179. package/lib/routes/publicPaths.js +4 -0
  180. package/lib/state/index.d.ts +1 -0
  181. package/lib/state/index.js +1 -0
  182. package/lib/state/sessionEnd.d.ts +55 -0
  183. package/lib/state/sessionEnd.js +162 -0
  184. package/lib/state/substates/CoreState.js +3 -16
  185. package/lib/state/substates/IAMState.d.ts +3 -3
  186. package/lib/state/substates/IAMState.js +7 -0
  187. package/lib/state/substates/LayoutState.js +12 -0
  188. package/lib/state/substates/NavigationState.d.ts +82 -0
  189. package/lib/state/substates/NavigationState.js +255 -0
  190. package/lib/state/substates/ProfileState.d.ts +1 -0
  191. package/lib/state/substates/ProfileState.js +1 -0
  192. package/lib/state/substates/index.d.ts +1 -0
  193. package/lib/state/substates/index.js +1 -0
  194. package/lib/utils/Lazy.d.ts +20 -0
  195. package/lib/utils/Lazy.js +32 -6
  196. package/lib/utils/Screencapture.d.ts +15 -0
  197. package/lib/utils/Screencapture.js +26 -20
  198. package/lib/utils/WithSuspense.d.ts +11 -1
  199. package/lib/utils/WithSuspense.js +11 -1
  200. package/lib/views/mcp/AdmittedClients.d.ts +44 -0
  201. package/lib/views/mcp/AdmittedClients.js +59 -0
  202. package/lib/views/mcp/ApprovalQueue.d.ts +66 -0
  203. package/lib/views/mcp/ApprovalQueue.js +95 -0
  204. package/lib/views/mcp/ConnectedAgents.js +38 -3
  205. package/lib/views/mcp/EnterpriseConsole.d.ts +1 -1
  206. package/lib/views/mcp/EnterpriseConsole.js +23 -1
  207. package/lib/views/mcp/IdentityProviders.d.ts +33 -0
  208. package/lib/views/mcp/IdentityProviders.js +299 -0
  209. package/lib/views/mcp/McpDashboard.js +90 -7
  210. package/lib/views/mcp/McpHome.js +1 -1
  211. package/lib/views/mcp/McpObservability.d.ts +30 -1
  212. package/lib/views/mcp/McpObservability.js +105 -19
  213. package/lib/views/mcp/NotebookRuns.d.ts +39 -0
  214. package/lib/views/mcp/NotebookRuns.js +44 -0
  215. package/lib/views/mcp/OrganizationPolicy.js +2 -1
  216. package/lib/views/mcp/PersonalPolicy.js +2 -1
  217. package/lib/views/mcp/PolicyForm.d.ts +17 -1
  218. package/lib/views/mcp/PolicyForm.js +25 -3
  219. package/lib/views/mcp/PolicyHistory.d.ts +13 -0
  220. package/lib/views/mcp/PolicyHistory.js +14 -1
  221. package/lib/views/mcp/RunDetail.d.ts +87 -0
  222. package/lib/views/mcp/RunDetail.js +186 -0
  223. package/lib/views/mcp/ScimProvisioning.d.ts +54 -0
  224. package/lib/views/mcp/ScimProvisioning.js +173 -0
  225. package/lib/views/mcp/TeamPolicies.js +66 -5
  226. package/lib/views/mcp/ToolAccess.d.ts +60 -0
  227. package/lib/views/mcp/ToolAccess.js +101 -0
  228. package/lib/views/mcp/TraceTimeline.d.ts +81 -0
  229. package/lib/views/mcp/TraceTimeline.js +132 -0
  230. package/lib/views/mcp/index.d.ts +5 -0
  231. package/lib/views/mcp/index.js +5 -0
  232. package/lib/views/otel/simpleAuthStore.js +3 -0
  233. package/lib/views/secrets/SecretEdit.js +3 -2
  234. package/lib/views/secrets/SecretNew.js +5 -1
  235. package/package.json +271 -264
  236. package/scripts/generate-mcp-types.py +16 -10
  237. package/scripts/generate-orchestration-types.py +920 -0
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { createStore } from 'zustand/vanilla';
6
6
  import { useStore } from 'zustand';
7
+ import { registerSessionState } from '../sessionEnd.js';
7
8
  /**
8
9
  * Name of the cookie holding the space last selected by the user.
9
10
  *
@@ -136,6 +137,17 @@ export const layoutStore = createStore((set, get) => ({
136
137
  setScreencapture: (screenCapture) => set((state) => ({ screenCapture })),
137
138
  reset: () => set((state) => ({ bootstrapped: false })),
138
139
  }));
140
+ // The current space, organization, team and item are the session's own:
141
+ // forgotten when it ends, cookie included.
142
+ registerSessionState({
143
+ forget: () => {
144
+ const state = layoutStore.getState();
145
+ state.updateLayoutSpace(undefined);
146
+ state.updateLayoutOrganization(undefined);
147
+ state.updateLayoutTeam(undefined);
148
+ state.setItem(undefined);
149
+ },
150
+ });
139
151
  export function useLayoutStore(selector) {
140
152
  return useStore(layoutStore, selector);
141
153
  }
@@ -0,0 +1,82 @@
1
+ /** The signed-in shell: the platform, the business cases, or the admin. */
2
+ export type NavigationView = 'home' | 'agentify' | 'admin';
3
+ /** The shells, and where each one opens when it has not been visited. */
4
+ export declare const NAVIGATION_VIEW_HOMES: Record<NavigationView, string>;
5
+ /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
6
+ export declare const LIBRARY_VIEW = "library";
7
+ export declare const AGENTS_VIEW = "agents";
8
+ export declare const SETTINGS_VIEW = "settings";
9
+ export declare const DATALAYER_VIEW = "datalayer";
10
+ export declare const CONTENTS_VIEW = "contents";
11
+ export declare const TUTOR_VIEW = "tutor";
12
+ export type NavigationState = {
13
+ /** The shell the reader last chose. */
14
+ view: NavigationView;
15
+ /** State which shell is being shown. */
16
+ setView: (view: NavigationView) => void;
17
+ /** Swap the platform and the business cases. */
18
+ toggleView: () => void;
19
+ /** The page each shell was last on. */
20
+ lastRouteByView: Partial<Record<NavigationView, string>>;
21
+ /**
22
+ * Remember a page as the current shell's, or as another's when told.
23
+ *
24
+ * Called by {@link useNavigate} for every navigation, which is what makes
25
+ * this the one place the answer comes from.
26
+ */
27
+ rememberRoute: (route: string, view?: NavigationView) => void;
28
+ /** Where a shell was left, or where it opens when it never was. */
29
+ routeForView: (view: NavigationView) => string;
30
+ /** Last selected tab, by view identifier. */
31
+ tabsByView: Record<string, string>;
32
+ /** Keep the tab a view was left on. */
33
+ setViewTab: (view: string, tab: string) => void;
34
+ /** The tab a view was left on, if any. */
35
+ getViewTab: (view: string) => string | undefined;
36
+ /**
37
+ * The route a signed-out reader asked for, to resume after signing in.
38
+ *
39
+ * Never persisted: a reload must land on the default route, and the OAuth
40
+ * round trip carries its target in `post_auth_redirect` instead.
41
+ */
42
+ requestedRoute?: string;
43
+ setRequestedRoute: (route: string) => void;
44
+ clearRequestedRoute: () => void;
45
+ /** The address the reader was on before the documentation. */
46
+ docsOrigin?: string;
47
+ setDocsOrigin: (origin: string) => void;
48
+ };
49
+ /**
50
+ * Whether a page is worth remembering as a shell's.
51
+ *
52
+ * Not the sign-in pages, the OAuth callbacks or the documentation: coming
53
+ * back to a shell should land on the work, not on the door the reader came
54
+ * through or on a page that belongs to no shell.
55
+ */
56
+ export declare function isRememberableRoute(route: string): boolean;
57
+ /**
58
+ * The shell a route belongs to, read off its first segment.
59
+ *
60
+ * The store used to trust whatever `view` said when a route was recorded,
61
+ * and `view` was only ever written by the header toggle. A reader who
62
+ * reached Home through a sidebar link while the toggle still said Agentify
63
+ * had their Home pages filed under Agentify — and the toggle, seeing
64
+ * "Agentify" already chosen, did nothing when they clicked it.
65
+ */
66
+ export declare function viewForRoute(route: string): NavigationView;
67
+ export declare const navigationStore: import("zustand").StoreApi<NavigationState>;
68
+ /**
69
+ * The hook, shaped like a `zustand` store created with `create`: callable
70
+ * with or without a selector, and carrying `getState`, `setState` and
71
+ * `subscribe` for the callers — a router guard, a test — that read it outside
72
+ * a render.
73
+ */
74
+ export interface UseNavigationStore {
75
+ (): NavigationState;
76
+ <T>(selector: (state: NavigationState) => T): T;
77
+ getState: () => NavigationState;
78
+ setState: typeof navigationStore.setState;
79
+ subscribe: typeof navigationStore.subscribe;
80
+ }
81
+ export declare const useNavigationStore: UseNavigationStore;
82
+ export default useNavigationStore;
@@ -0,0 +1,255 @@
1
+ /*
2
+ * Copyright (c) 2023-2025 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * Where the reader is, where they were, and where they left each shell.
7
+ *
8
+ * One store for navigation, in core, so every surface reads and writes the
9
+ * same thing. It answers five questions that used to be four stores in the
10
+ * landing and a handful of ad-hoc effects:
11
+ *
12
+ * - **the shell** — the platform Home, Agentify, or Admin;
13
+ * - **the page each shell was left on**, so switching shells returns the
14
+ * reader to their work rather than to a landing page;
15
+ * {@link useNavigate} records it on every navigation;
16
+ * - **the tab** last chosen in each tabbed view;
17
+ * - **the route asked for** before signing in, to resume afterwards;
18
+ * - **the address before the documentation**, for its way back.
19
+ *
20
+ * **Where each is kept.** The shell rides in a cookie, so it is sent with
21
+ * every request and a server render can read it. The shells' last pages and
22
+ * the tabs are in `localStorage`, which a cookie should not carry. The
23
+ * requested route and the documentation's origin are memory only, on
24
+ * purpose: a reload must forget the route asked for before signing in.
25
+ *
26
+ * Every read and write goes through the storage in a `try`: a browser that
27
+ * refuses its storage forgets, and never throws into a render.
28
+ *
29
+ * @module state/substates/NavigationState
30
+ */
31
+ import { useStore } from 'zustand';
32
+ import { createStore } from 'zustand/vanilla';
33
+ import { registerSessionState } from '../sessionEnd.js';
34
+ /** The shells, and where each one opens when it has not been visited. */
35
+ export const NAVIGATION_VIEW_HOMES = {
36
+ home: '/',
37
+ agentify: '/agentify',
38
+ admin: '/admin',
39
+ };
40
+ /** Well-known view identifiers for {@link NavigationState.tabsByView}. */
41
+ export const LIBRARY_VIEW = 'library';
42
+ export const AGENTS_VIEW = 'agents';
43
+ export const SETTINGS_VIEW = 'settings';
44
+ export const DATALAYER_VIEW = 'datalayer';
45
+ export const CONTENTS_VIEW = 'contents';
46
+ export const TUTOR_VIEW = 'tutor';
47
+ /** Where this store is kept, and what the landing kept it under before. */
48
+ const STORAGE_KEY = 'datalayer-navigation';
49
+ const SHELL_COOKIE = 'datalayer-signed-in-view';
50
+ const FORMER_TABS_KEY = 'datalayer_view_tabs';
51
+ /** Days a remembered shell lives in the cookie. */
52
+ const COOKIE_MAX_AGE_DAYS = 365;
53
+ const isView = (value) => value === 'home' || value === 'agentify' || value === 'admin';
54
+ function readCookie(name) {
55
+ if (typeof document === 'undefined') {
56
+ return undefined;
57
+ }
58
+ const match = document.cookie
59
+ .split('; ')
60
+ .find(row => row.startsWith(`${name}=`));
61
+ return match ? decodeURIComponent(match.slice(name.length + 1)) : undefined;
62
+ }
63
+ function writeCookie(name, value) {
64
+ if (typeof document === 'undefined') {
65
+ return;
66
+ }
67
+ const maxAge = COOKIE_MAX_AGE_DAYS * 24 * 60 * 60;
68
+ document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAge}; SameSite=Lax`;
69
+ }
70
+ function readLocal(name) {
71
+ try {
72
+ return globalThis.localStorage?.getItem(name) ?? null;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ }
78
+ /**
79
+ * What the last visit left, from the two places it belongs in.
80
+ *
81
+ * The shell is taken from the cookie it has always used, so a reader who
82
+ * chose Agentify before this store existed still finds it. The tabs kept
83
+ * under the landing's former key are picked up once.
84
+ */
85
+ function restore() {
86
+ let persisted = {};
87
+ const stored = readLocal(STORAGE_KEY);
88
+ if (stored) {
89
+ try {
90
+ const parsed = JSON.parse(stored);
91
+ // The landing kept this under the same key through zustand's
92
+ // `persist`, which wraps what it writes in `{ state, version }`.
93
+ persisted = parsed?.state ?? parsed ?? {};
94
+ }
95
+ catch {
96
+ persisted = {};
97
+ }
98
+ }
99
+ else {
100
+ const former = readLocal(FORMER_TABS_KEY);
101
+ if (former) {
102
+ try {
103
+ const { state } = JSON.parse(former);
104
+ persisted = { tabsByView: state?.tabsByView ?? {} };
105
+ }
106
+ catch {
107
+ persisted = {};
108
+ }
109
+ }
110
+ }
111
+ const shell = readCookie(SHELL_COOKIE);
112
+ if (isView(shell)) {
113
+ persisted.view = shell;
114
+ }
115
+ return persisted;
116
+ }
117
+ function persist(state) {
118
+ const written = {
119
+ view: state.view,
120
+ lastRouteByView: state.lastRouteByView,
121
+ tabsByView: state.tabsByView,
122
+ };
123
+ try {
124
+ globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(written));
125
+ }
126
+ catch {
127
+ /* the shells' pages are not remembered */
128
+ }
129
+ writeCookie(SHELL_COOKIE, state.view);
130
+ }
131
+ /**
132
+ * Whether a page is worth remembering as a shell's.
133
+ *
134
+ * Not the sign-in pages, the OAuth callbacks or the documentation: coming
135
+ * back to a shell should land on the work, not on the door the reader came
136
+ * through or on a page that belongs to no shell.
137
+ */
138
+ export function isRememberableRoute(route) {
139
+ if (!route.startsWith('/')) {
140
+ return false;
141
+ }
142
+ const path = route.split('?')[0];
143
+ return ![
144
+ '/signin',
145
+ '/signup',
146
+ '/login',
147
+ '/logout',
148
+ '/oauth2',
149
+ '/oauth',
150
+ '/docs',
151
+ '/join',
152
+ ].some(prefix => path === prefix || path.startsWith(`${prefix}/`));
153
+ }
154
+ /**
155
+ * The shell a route belongs to, read off its first segment.
156
+ *
157
+ * The store used to trust whatever `view` said when a route was recorded,
158
+ * and `view` was only ever written by the header toggle. A reader who
159
+ * reached Home through a sidebar link while the toggle still said Agentify
160
+ * had their Home pages filed under Agentify — and the toggle, seeing
161
+ * "Agentify" already chosen, did nothing when they clicked it.
162
+ */
163
+ export function viewForRoute(route) {
164
+ const path = route.split('?')[0];
165
+ if (path === '/agentify' || path.startsWith('/agentify/')) {
166
+ return 'agentify';
167
+ }
168
+ if (path === '/admin' || path.startsWith('/admin/')) {
169
+ return 'admin';
170
+ }
171
+ return 'home';
172
+ }
173
+ const initial = restore();
174
+ export const navigationStore = createStore((set, get) => ({
175
+ view: initial.view ?? 'home',
176
+ lastRouteByView: initial.lastRouteByView ?? {},
177
+ tabsByView: initial.tabsByView ?? {},
178
+ requestedRoute: undefined,
179
+ docsOrigin: undefined,
180
+ setView: view => {
181
+ set({ view });
182
+ persist(get());
183
+ },
184
+ toggleView: () => {
185
+ set(state => ({ view: state.view === 'home' ? 'agentify' : 'home' }));
186
+ persist(get());
187
+ },
188
+ rememberRoute: (route, view) => {
189
+ if (!isRememberableRoute(route)) {
190
+ return;
191
+ }
192
+ // The route says which shell it belongs to; the caller may only insist
193
+ // when it knows better. And being on a shell's page *is* being in that
194
+ // shell, so the view follows.
195
+ const shell = view ?? viewForRoute(route);
196
+ // Told which shell, the caller is filing a page away, not going there.
197
+ const follow = view === undefined;
198
+ const current = get();
199
+ if (current.lastRouteByView[shell] === route &&
200
+ (!follow || current.view === shell)) {
201
+ return;
202
+ }
203
+ set(state => ({
204
+ view: follow ? shell : state.view,
205
+ lastRouteByView: { ...state.lastRouteByView, [shell]: route },
206
+ }));
207
+ persist(get());
208
+ },
209
+ routeForView: view => {
210
+ // Only a page of that shell: an earlier version filed pages under the
211
+ // shell the toggle named rather than the one the route belonged to, so a
212
+ // reader can still have `/items` written down as Agentify's page. Going
213
+ // "to Agentify" must never land on a Home page.
214
+ const remembered = get().lastRouteByView[view];
215
+ return remembered && viewForRoute(remembered) === view
216
+ ? remembered
217
+ : NAVIGATION_VIEW_HOMES[view];
218
+ },
219
+ setViewTab: (view, tab) => {
220
+ if (get().tabsByView[view] === tab) {
221
+ return;
222
+ }
223
+ set(state => ({ tabsByView: { ...state.tabsByView, [view]: tab } }));
224
+ persist(get());
225
+ },
226
+ getViewTab: view => get().tabsByView[view],
227
+ setRequestedRoute: route => set({ requestedRoute: route }),
228
+ clearRequestedRoute: () => set({ requestedRoute: undefined }),
229
+ setDocsOrigin: origin => set({ docsOrigin: origin }),
230
+ }));
231
+ // The pages each shell was left on, the tabs, the route asked for and the
232
+ // way back from the documentation belong to the person who was signed in:
233
+ // forgotten when the session ends. The shell itself stays — it is the
234
+ // browser's choice of surface, read by the anonymous landing too.
235
+ registerSessionState({
236
+ forget: () => {
237
+ navigationStore.setState({
238
+ lastRouteByView: {},
239
+ tabsByView: {},
240
+ requestedRoute: undefined,
241
+ docsOrigin: undefined,
242
+ });
243
+ persist(navigationStore.getState());
244
+ },
245
+ localStorageKeys: [FORMER_TABS_KEY],
246
+ });
247
+ function useNavigationState(selector) {
248
+ return useStore(navigationStore, selector ?? ((state) => state));
249
+ }
250
+ export const useNavigationStore = Object.assign(useNavigationState, {
251
+ getState: navigationStore.getState,
252
+ setState: navigationStore.setState,
253
+ subscribe: navigationStore.subscribe,
254
+ });
255
+ export default useNavigationStore;
@@ -12,6 +12,7 @@ export type IProfile = {
12
12
  avatarUrl?: string;
13
13
  avatarIcon?: string;
14
14
  banner?: string;
15
+ showOrbits?: boolean;
15
16
  origin?: string;
16
17
  roles?: string[];
17
18
  };
@@ -34,6 +34,7 @@ const userToProfile = (user) => user
34
34
  avatarUrl: user.avatarUrl,
35
35
  avatarIcon: user.avatarIcon,
36
36
  banner: user.banner,
37
+ showOrbits: user.showOrbits,
37
38
  origin: user.origin,
38
39
  roles: user.roles,
39
40
  }
@@ -1,6 +1,7 @@
1
1
  export * from './CoreState';
2
2
  export * from './IAMState';
3
3
  export * from './LayoutState';
4
+ export * from './NavigationState';
4
5
  export * from './ProfileState';
5
6
  export * from './OrganizationState';
6
7
  export * from './SurveysState';
@@ -5,6 +5,7 @@
5
5
  export * from './CoreState.js';
6
6
  export * from './IAMState.js';
7
7
  export * from './LayoutState.js';
8
+ export * from './NavigationState.js';
8
9
  export * from './ProfileState.js';
9
10
  export * from './OrganizationState.js';
10
11
  export * from './SurveysState.js';
@@ -2,6 +2,26 @@ import { ComponentType } from 'react';
2
2
  export type PreloadableComponent<T extends ComponentType<any>> = T & {
3
3
  preload: () => Promise<void>;
4
4
  };
5
+ /**
6
+ * `React.lazy`, with a `preload()` that fetches the module ahead of the
7
+ * first render.
8
+ *
9
+ * Two rules keep it honest:
10
+ *
11
+ * - **One fetch.** `preload()` and React's own first render share a single
12
+ * call to `factory`. They used to call it separately, so a component that
13
+ * was preloaded and then rendered imported its module twice.
14
+ * - **One element type per mounted instance.** The component renders the
15
+ * loaded module directly when it was already loaded at mount — no fallback
16
+ * flash — and the lazy wrapper otherwise, and it keeps that choice for its
17
+ * lifetime. It used to switch from the wrapper to the loaded module on any
18
+ * render after `preload()` resolved, and React remounts on a change of
19
+ * element type: a component preloaded after it had mounted lost its state
20
+ * and ran its effects again on its next render.
21
+ *
22
+ * A failed fetch is forgotten, so the next `preload()` tries again rather
23
+ * than replaying the failure.
24
+ */
5
25
  export declare function lazyWithPreload<T extends ComponentType<any>>(factory: () => Promise<{
6
26
  default: T;
7
27
  }>): PreloadableComponent<T>;
package/lib/utils/Lazy.js CHANGED
@@ -2,22 +2,48 @@
2
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import { createElement, forwardRef, lazy as reactLazy, } from 'react';
5
+ import { createElement, forwardRef, lazy as reactLazy, useState, } from 'react';
6
+ /**
7
+ * `React.lazy`, with a `preload()` that fetches the module ahead of the
8
+ * first render.
9
+ *
10
+ * Two rules keep it honest:
11
+ *
12
+ * - **One fetch.** `preload()` and React's own first render share a single
13
+ * call to `factory`. They used to call it separately, so a component that
14
+ * was preloaded and then rendered imported its module twice.
15
+ * - **One element type per mounted instance.** The component renders the
16
+ * loaded module directly when it was already loaded at mount — no fallback
17
+ * flash — and the lazy wrapper otherwise, and it keeps that choice for its
18
+ * lifetime. It used to switch from the wrapper to the loaded module on any
19
+ * render after `preload()` resolved, and React remounts on a change of
20
+ * element type: a component preloaded after it had mounted lost its state
21
+ * and ran its effects again on its next render.
22
+ *
23
+ * A failed fetch is forgotten, so the next `preload()` tries again rather
24
+ * than replaying the failure.
25
+ */
6
26
  export function lazyWithPreload(factory) {
7
- const LazyComponent = reactLazy(factory);
8
27
  let factoryPromise;
9
28
  let LoadedComponent;
10
- const Component = forwardRef(function LazyWithPreload(props, ref) {
11
- return createElement(LoadedComponent ?? LazyComponent, Object.assign(ref ? { ref } : {}, props));
12
- });
13
- Component.preload = () => {
29
+ const load = () => {
14
30
  if (!factoryPromise) {
15
31
  factoryPromise = factory().then(module => {
16
32
  LoadedComponent = module.default;
33
+ return module;
34
+ }, error => {
35
+ factoryPromise = undefined;
36
+ throw error;
17
37
  });
18
38
  }
19
39
  return factoryPromise;
20
40
  };
41
+ const LazyComponent = reactLazy(load);
42
+ const Component = forwardRef(function LazyWithPreload(props, ref) {
43
+ const [Resolved] = useState(() => LoadedComponent ?? LazyComponent);
44
+ return createElement(Resolved, Object.assign(ref ? { ref } : {}, props));
45
+ });
46
+ Component.preload = () => load().then(() => undefined);
21
47
  return Component;
22
48
  }
23
49
  export default lazyWithPreload;
@@ -1 +1,16 @@
1
+ /**
2
+ * A picture of one rendered node, as a PNG data URL.
3
+ *
4
+ * This used `html2canvas`, which does not capture a node: it clones the
5
+ * **whole document** into a hidden iframe and paints all of it, then crops.
6
+ * On a rendered notebook that is every cell, every editor and every output,
7
+ * and the clone is synchronous — the page froze for ten seconds or more with
8
+ * the share dialog saying "preparing…", which reads as a hang, and on a
9
+ * longer notebook it was one. `html-to-image` serialises the node's own
10
+ * subtree into an SVG `foreignObject` and rasterises that: the work is
11
+ * proportional to the output being shared, and it yields to the page.
12
+ *
13
+ * Cross-origin pictures are fetched for the clone, as before, so a cell
14
+ * carrying an `<img>` from the web does not capture as a blank rectangle.
15
+ */
1
16
  export declare const takeHTMLNodeScreencapture: (node: HTMLDivElement) => Promise<string>;
@@ -2,26 +2,32 @@
2
2
  * Copyright (c) 2023-2025 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- import html2canvas from 'html2canvas';
5
+ import { toPng } from 'html-to-image';
6
+ /**
7
+ * A picture of one rendered node, as a PNG data URL.
8
+ *
9
+ * This used `html2canvas`, which does not capture a node: it clones the
10
+ * **whole document** into a hidden iframe and paints all of it, then crops.
11
+ * On a rendered notebook that is every cell, every editor and every output,
12
+ * and the clone is synchronous — the page froze for ten seconds or more with
13
+ * the share dialog saying "preparing…", which reads as a hang, and on a
14
+ * longer notebook it was one. `html-to-image` serialises the node's own
15
+ * subtree into an SVG `foreignObject` and rasterises that: the work is
16
+ * proportional to the output being shared, and it yields to the page.
17
+ *
18
+ * Cross-origin pictures are fetched for the clone, as before, so a cell
19
+ * carrying an `<img>` from the web does not capture as a blank rectangle.
20
+ */
6
21
  export const takeHTMLNodeScreencapture = async (node) => {
7
- const sc = html2canvas(node, {
8
- width: node.getBoundingClientRect().width,
9
- height: node.getBoundingClientRect().height,
10
- });
11
- return sc.then(canvas => {
12
- const croppedCanvas = document.createElement('canvas');
13
- const croppedCanvasContext = croppedCanvas.getContext('2d');
14
- const top = 0;
15
- const left = 0;
16
- const width = canvas.width;
17
- const height = canvas.height;
18
- croppedCanvas.width = width;
19
- croppedCanvas.height = height;
20
- croppedCanvasContext?.drawImage(canvas, left, top, width, height, left, top, width, height);
21
- // const type = 'image/jpeg';
22
- const type = 'image/png';
23
- const quality = 1;
24
- const outputshotData = croppedCanvas.toDataURL(type, quality);
25
- return outputshotData;
22
+ const { backgroundColor } = getComputedStyle(node);
23
+ return toPng(node, {
24
+ // Crisp on a dense display, still a modest file for a text output.
25
+ pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
26
+ // Outputs are usually transparent over the page's canvas: give the
27
+ // picture that canvas rather than a transparent one.
28
+ backgroundColor: backgroundColor && backgroundColor !== 'rgba(0, 0, 0, 0)'
29
+ ? backgroundColor
30
+ : getComputedStyle(document.body).backgroundColor || '#ffffff',
31
+ cacheBust: true,
26
32
  });
27
33
  };
@@ -1,2 +1,12 @@
1
- export declare const WithSuspense: (Component: any, preload?: boolean, skeleton?: boolean) => (props: any) => import("react").JSX.Element;
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * A lazily-loaded route, with something to look at while it arrives.
4
+ *
5
+ * `skeleton` was a boolean between one grey box and **nothing at all**, and
6
+ * nothing was the default — so every route in the application showed a blank
7
+ * page for as long as its chunk took, which for an editor is seconds. It now
8
+ * also takes a node, so a route can hand over a drawing of the thing that is
9
+ * coming rather than a hole where it will be.
10
+ */
11
+ export declare const WithSuspense: (Component: any, preload?: boolean, skeleton?: boolean | ReactNode) => (props: any) => import("react").JSX.Element;
2
12
  export default WithSuspense;
@@ -9,10 +9,20 @@ const Loading = (props) => {
9
9
  const { skeleton } = props;
10
10
  return skeleton ? (_jsx(_Fragment, { children: _jsx(SkeletonBox, { height: "100px" }) })) : (_jsx(_Fragment, {}));
11
11
  };
12
+ /**
13
+ * A lazily-loaded route, with something to look at while it arrives.
14
+ *
15
+ * `skeleton` was a boolean between one grey box and **nothing at all**, and
16
+ * nothing was the default — so every route in the application showed a blank
17
+ * page for as long as its chunk took, which for an editor is seconds. It now
18
+ * also takes a node, so a route can hand over a drawing of the thing that is
19
+ * coming rather than a hole where it will be.
20
+ */
12
21
  export const WithSuspense = (Component, preload = true, skeleton = false) => (props) => {
13
22
  if (preload) {
14
23
  Component.preload();
15
24
  }
16
- return (_jsx(Suspense, { fallback: _jsx(Loading, { skeleton: skeleton }), children: _jsx(Component, { ...props }) }));
25
+ const fallback = typeof skeleton === 'boolean' ? (_jsx(Loading, { skeleton: skeleton })) : (skeleton);
26
+ return (_jsx(Suspense, { fallback: fallback, children: _jsx(Component, { ...props }) }));
17
27
  };
18
28
  export default WithSuspense;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * What the admitted-clients list actually admits.
3
+ *
4
+ * The field above it is a textarea of client ids, because that is what they
5
+ * are: a client id is a URL somebody pastes, and there is nothing to pick
6
+ * from. What a textarea cannot do is tell whoever typed it whether an entry
7
+ * resolves — and this is the one policy rule whose typo fails silently in
8
+ * the direction that matters. A misspelled entry does not refuse anything
9
+ * loudly; it never matches, and every agent from that vendor is turned away
10
+ * with "your organization does not admit this client", which reads to its
11
+ * owner as a decision somebody made on purpose.
12
+ *
13
+ * So each stored entry is shown with what it names. Three states, and the
14
+ * reason this is a component rather than a line of text is that collapsing
15
+ * any two of them misleads:
16
+ *
17
+ * * a **hostname** admits every client that host publishes. There is no
18
+ * document and none is expected — shown as unresolved it would send
19
+ * somebody chasing a fetch that was never going to happen.
20
+ * * a **document that resolves**, with the name and publishing host.
21
+ * * a **document that does not**, with the reason. The state the whole
22
+ * thing is for.
23
+ *
24
+ * **It describes what is stored, not the draft.** Resolving on every
25
+ * keystroke would make a textarea a source of outbound requests, so this
26
+ * goes stale while somebody edits and says so rather than pretending to
27
+ * describe a line that has not been saved.
28
+ *
29
+ * @module views/mcp/AdmittedClients
30
+ */
31
+ import type { JSX } from 'react';
32
+ import type { McpPolicyScope } from '../../api/iam/mcpPolicy';
33
+ export interface AdmittedClientsProps {
34
+ scope: McpPolicyScope;
35
+ subjectUid: string;
36
+ /**
37
+ * Whether the textarea above differs from what is stored. The entries
38
+ * below describe the *stored* list, and saying so is the difference
39
+ * between a stale panel and a wrong one.
40
+ */
41
+ edited?: boolean;
42
+ }
43
+ export declare const AdmittedClients: ({ scope, subjectUid, edited, }: AdmittedClientsProps) => JSX.Element | null;
44
+ export default AdmittedClients;