@sybilion/uilib 1.2.0 → 1.2.3

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 (45) hide show
  1. package/dist/esm/components/ui/AppHeader/AppHeader.js +3 -3
  2. package/dist/esm/components/ui/Image/Image.styl.js +1 -1
  3. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +28 -0
  4. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.styl.js +7 -0
  5. package/dist/esm/components/ui/Page/AppShell/AppShell.styl.js +1 -1
  6. package/dist/esm/components/ui/Page/PageScroll/PageScroll.js +4 -4
  7. package/dist/esm/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.js +9 -9
  8. package/dist/esm/index.js +2 -1
  9. package/dist/esm/sybilion-auth/SybilionAuthProvider.js +30 -7
  10. package/dist/esm/sybilion-auth/exchangeSybilionToken.js +6 -2
  11. package/dist/esm/types/src/components/ui/AppHeader/AppHeader.d.ts +2 -1
  12. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +2 -0
  13. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +25 -0
  14. package/dist/esm/types/src/components/ui/NavUserHeader/index.d.ts +2 -0
  15. package/dist/esm/types/src/components/ui/Page/PageScroll/PageScroll.d.ts +2 -1
  16. package/dist/esm/types/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.d.ts +3 -1
  17. package/dist/esm/types/src/docs/pages/NavUserHeaderPage.d.ts +1 -0
  18. package/dist/esm/types/src/docs/pages/StandaloneAppLayoutPage.d.ts +1 -0
  19. package/dist/esm/types/src/index.d.ts +1 -0
  20. package/dist/esm/types/src/sybilion-auth/SybilionAuthProvider.d.ts +5 -2
  21. package/dist/esm/types/src/sybilion-auth/exchangeSybilionToken.d.ts +3 -1
  22. package/dist/esm/types/src/sybilion-auth/index.d.ts +1 -1
  23. package/docs/standalone-apps.md +266 -27
  24. package/package.json +6 -1
  25. package/src/components/ui/AppHeader/AppHeader.tsx +7 -3
  26. package/src/components/ui/Image/Image.styl +1 -0
  27. package/src/components/ui/NavUserHeader/NavUserHeader.styl +125 -0
  28. package/src/components/ui/NavUserHeader/NavUserHeader.styl.d.ts +28 -0
  29. package/src/components/ui/NavUserHeader/NavUserHeader.tsx +148 -0
  30. package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +27 -0
  31. package/src/components/ui/NavUserHeader/avatar.svg +4 -0
  32. package/src/components/ui/NavUserHeader/index.ts +5 -0
  33. package/src/components/ui/Page/AppShell/AppShell.styl +1 -0
  34. package/src/components/ui/Page/PageScroll/PageScroll.tsx +9 -2
  35. package/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.tsx +9 -0
  36. package/src/docs/pages/NavUserHeaderPage.tsx +89 -0
  37. package/src/docs/pages/StandaloneAppLayoutPage.styl +46 -0
  38. package/src/docs/pages/StandaloneAppLayoutPage.styl.d.ts +8 -0
  39. package/src/docs/pages/StandaloneAppLayoutPage.tsx +242 -0
  40. package/src/docs/pages/SybilionAuthProviderPage.tsx +5 -2
  41. package/src/docs/registry.ts +12 -0
  42. package/src/index.ts +1 -0
  43. package/src/sybilion-auth/SybilionAuthProvider.tsx +33 -11
  44. package/src/sybilion-auth/exchangeSybilionToken.ts +5 -1
  45. package/src/sybilion-auth/index.ts +1 -0
@@ -8,11 +8,11 @@ import { PAGE_HEADER_ID } from './appChromeAnchors.js';
8
8
  function AppHeaderHost({ className, anchorId = PAGE_HEADER_ID, }) {
9
9
  return jsx("header", { className: cn(S.root, className), id: anchorId });
10
10
  }
11
- function AppHeaderPortal({ children }) {
11
+ function AppHeaderPortal({ children, pageHeaderId = PAGE_HEADER_ID, }) {
12
12
  const [container, setContainer] = useState(null);
13
13
  useLayoutEffect(() => {
14
- setContainer(document.getElementById(PAGE_HEADER_ID));
15
- }, []);
14
+ setContainer(document.getElementById(pageHeaderId));
15
+ }, [pageHeaderId]);
16
16
  if (!container) {
17
17
  return null;
18
18
  }
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = ".Image_container__beG4N{display:inline-block;height:100%;position:relative;width:100%}.Image_image__Kkmwo{border-radius:inherit;display:block;height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.Image_image__Kkmwo.Image_loading__Hi33D{opacity:0}.Image_fallback__N1ndD{align-items:center;border-radius:inherit;display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%}";
3
+ var css_248z = ".Image_container__beG4N{background-color:var(--muted-50);display:inline-block;height:100%;position:relative;width:100%}.Image_image__Kkmwo{border-radius:inherit;display:block;height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.Image_image__Kkmwo.Image_loading__Hi33D{opacity:0}.Image_fallback__N1ndD{align-items:center;border-radius:inherit;display:flex;height:100%;justify-content:center;left:0;position:absolute;top:0;width:100%}";
4
4
  var S = {"container":"Image_container__beG4N","image":"Image_image__Kkmwo","loading":"Image_loading__Hi33D","fallback":"Image_fallback__N1ndD"};
5
5
  styleInject(css_248z);
6
6
 
@@ -0,0 +1,28 @@
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import cn from 'classnames';
3
+ import { UserCircleIcon, SunIcon, MoonIcon, SignOutIcon } from '@phosphor-icons/react';
4
+ import { ChevronDownIcon } from 'lucide-react';
5
+ import { Avatar } from '../Avatar/Avatar.js';
6
+ import { Button } from '../Button/Button.js';
7
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuGroup, DropdownMenuItem } from '../DropdownMenu/DropdownMenu.js';
8
+ import { Image } from '../Image/Image.js';
9
+ import S from './NavUserHeader.styl.js';
10
+
11
+ function NavUserHeader({ variant = 'default', isLoading = false, isAuthenticated, user = null, menuItems, theme, onThemeToggle, onLogout, signInSlot, onSignInClick, }) {
12
+ const authenticated = isAuthenticated ?? true;
13
+ const avatarUrl = user?.avatar ?? '';
14
+ const userName = user?.name ?? '';
15
+ const userEmail = user?.email ?? '';
16
+ if (isLoading) {
17
+ return (jsxs(Button, { variant: "ghost", size: "sm", disabled: true, className: S.loadingButton, children: [jsx("div", { className: S.avatarSkeleton }), jsx("div", { className: S.textSkeleton })] }));
18
+ }
19
+ if (!authenticated) {
20
+ if (signInSlot) {
21
+ return signInSlot;
22
+ }
23
+ return (jsxs(Button, { variant: "ghost", size: "sm", className: S.loginButton, type: "button", onClick: onSignInClick, children: [jsx(UserCircleIcon, { className: S.iconLg }), jsx("span", { children: "Log in" })] }));
24
+ }
25
+ return (jsxs(DropdownMenu, { children: [jsx(DropdownMenuTrigger, { asChild: true, children: jsxs(Button, { variant: "ghost", size: "sm", className: cn(S.userButton, variant === 'compact' && S.compact), children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), variant === 'default' && (jsxs(Fragment, { children: [jsxs("div", { className: S.userInfo, children: [jsx("span", { className: `${S.userName} ph-no-capture`, children: userName }), jsx("span", { className: S.userEmail, children: userEmail })] }), jsx(ChevronDownIcon, { className: S.iconSm })] }))] }) }), jsxs(DropdownMenuContent, { className: S.dropdownContent, align: "end", elevation: "md", children: [jsx(DropdownMenuLabel, { className: S.userLabel, children: jsxs("div", { className: S.userLabelContent, children: [jsx(Avatar, { className: S.avatar, children: jsx(Image, { url: avatarUrl, alt: userName, fallback: jsx("div", { className: S.avatarFallback }) }) }), jsxs("div", { className: S.userDetails, children: [jsx("span", { className: `${S.userDetailName} ph-no-capture`, children: userName }), jsx("span", { className: S.userDetailEmail, children: userEmail })] })] }) }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuGroup, { children: [menuItems, onThemeToggle ? (jsx(DropdownMenuItem, { onSelect: () => onThemeToggle(), children: theme === 'dark' ? (jsxs(Fragment, { children: [jsx(SunIcon, {}), "Light theme"] })) : (jsxs(Fragment, { children: [jsx(MoonIcon, {}), "Dark theme"] })) })) : null] }), jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, { variant: "destructive", onSelect: () => onLogout(), children: [jsx(SignOutIcon, {}), "Log out"] })] })] }));
26
+ }
27
+
28
+ export { NavUserHeader };
@@ -0,0 +1,7 @@
1
+ import styleInject from 'style-inject';
2
+
3
+ var css_248z = ".NavUserHeader_loadingButton__UM1cm{gap:.5rem}.NavUserHeader_avatarSkeleton__bdWE3{border-radius:9999px;height:2rem;width:2rem}.NavUserHeader_avatarSkeleton__bdWE3,.NavUserHeader_textSkeleton__AOp9E{animation:NavUserHeader_pulse__GWYG6 2s cubic-bezier(.4,0,.6,1) infinite;background-color:var(--color-muted)}.NavUserHeader_textSkeleton__AOp9E{border-radius:.25rem;height:1rem;width:5rem}.NavUserHeader_loginButton__QA4q6{gap:.5rem}.NavUserHeader_iconLg__246wU{height:1.25rem;width:1.25rem}.NavUserHeader_iconSm__CWx2D{height:.75rem;width:.75rem}.NavUserHeader_menuIcon__u0VeA{height:1rem;margin-right:.5rem;width:1rem}.NavUserHeader_dropdownContent__djONy{width:14rem}.NavUserHeader_userButton__sBFb-{gap:.5rem;height:52px;padding:var(--p-2)}.NavUserHeader_userButton__sBFb-.NavUserHeader_compact__UfbAp{background-color:transparent!important;padding:0;transition:transform .2s ease-in-out}.NavUserHeader_userButton__sBFb-.NavUserHeader_compact__UfbAp:hover{transform:scale(1.1)}.NavUserHeader_avatar__Yksm-{height:2rem;width:2rem}.NavUserHeader_avatarImage__d6rzY{border-radius:inherit;height:100%;-o-object-fit:cover;object-fit:cover;width:100%}.NavUserHeader_avatarFallback__TKi2T{background-color:var(--color-primary);background:url(avatar.svg) no-repeat 50%;border-radius:inherit;color:var(--color-primary-foreground);height:100%;width:100%}.NavUserHeader_userInfo__kReZ2{align-items:flex-start;display:flex;flex-direction:column;gap:.25rem;text-align:left}.NavUserHeader_userName__8xek-{font-size:var(--text-sm);font-weight:400;line-height:1;max-width:7.5rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.NavUserHeader_userEmail__HKdtn{color:var(--sb-slate-500);font-size:var(--text-xs);line-height:1}.NavUserHeader_userLabel__DAxki{font-weight:400;padding:0}.NavUserHeader_userLabelContent__n5ngd{align-items:center;display:flex;font-size:.875rem;gap:.5rem;padding:.5rem;text-align:left}.NavUserHeader_userDetails__5r-k8{display:grid;flex:1;font-size:.875rem;line-height:1.25;text-align:left}.NavUserHeader_userDetailName__EajCc{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.NavUserHeader_userDetailEmail__LsK5z{color:var(--color-muted-foreground);font-size:.75rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@keyframes NavUserHeader_pulse__GWYG6{0%,to{opacity:1}50%{opacity:.5}}";
4
+ var S = {"loadingButton":"NavUserHeader_loadingButton__UM1cm","avatarSkeleton":"NavUserHeader_avatarSkeleton__bdWE3","pulse":"NavUserHeader_pulse__GWYG6","textSkeleton":"NavUserHeader_textSkeleton__AOp9E","loginButton":"NavUserHeader_loginButton__QA4q6","iconLg":"NavUserHeader_iconLg__246wU","iconSm":"NavUserHeader_iconSm__CWx2D","menuIcon":"NavUserHeader_menuIcon__u0VeA","dropdownContent":"NavUserHeader_dropdownContent__djONy","userButton":"NavUserHeader_userButton__sBFb-","compact":"NavUserHeader_compact__UfbAp","avatar":"NavUserHeader_avatar__Yksm-","avatarImage":"NavUserHeader_avatarImage__d6rzY","avatarFallback":"NavUserHeader_avatarFallback__TKi2T","userInfo":"NavUserHeader_userInfo__kReZ2","userName":"NavUserHeader_userName__8xek-","userEmail":"NavUserHeader_userEmail__HKdtn","userLabel":"NavUserHeader_userLabel__DAxki","userLabelContent":"NavUserHeader_userLabelContent__n5ngd","userDetails":"NavUserHeader_userDetails__5r-k8","userDetailName":"NavUserHeader_userDetailName__EajCc","userDetailEmail":"NavUserHeader_userDetailEmail__LsK5z"};
5
+ styleInject(css_248z);
6
+
7
+ export { S as default };
@@ -1,6 +1,6 @@
1
1
  import styleInject from 'style-inject';
2
2
 
3
- var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.AppShell_root__ONlNK{display:grid;flex:1;grid-template-areas:\"main\";grid-template-columns:1fr;min-height:0;width:100%}@media (min-width:768px){.AppShell_root__ONlNK{grid-template-areas:\"sidebar main\";grid-template-columns:var(--sidebar-width) 1fr}[data-slot=sidebar-wrapper][data-state=collapsed] .AppShell_root__ONlNK{grid-template-columns:0 1fr}}.AppShell_mainColumn__Emn1p{display:flex;flex:1;flex-direction:column;grid-area:main;min-height:0;min-width:0}[data-slot=sidebar-wrapper][data-state=collapsed] .AppShell_mainColumn__Emn1p{margin-left:var(--p-3)}.AppShell_mainBody__IoVuy{background-color:var(--page-color);border-radius:var(--p-4);flex:1;min-width:0;padding-bottom:var(--page-y-padding)}";
3
+ var css_248z = "@media (max-width:768px){:root{--page-x-padding:var(--p-6);--page-y-padding:var(--p-6)}}.AppShell_root__ONlNK{display:grid;flex:1;grid-template-areas:\"main\";grid-template-columns:1fr;min-height:0;min-height:100%;width:100%}@media (min-width:768px){.AppShell_root__ONlNK{grid-template-areas:\"sidebar main\";grid-template-columns:var(--sidebar-width) 1fr}[data-slot=sidebar-wrapper][data-state=collapsed] .AppShell_root__ONlNK{grid-template-columns:0 1fr}}.AppShell_mainColumn__Emn1p{display:flex;flex:1;flex-direction:column;grid-area:main;min-height:0;min-width:0}[data-slot=sidebar-wrapper][data-state=collapsed] .AppShell_mainColumn__Emn1p{margin-left:var(--p-3)}.AppShell_mainBody__IoVuy{background-color:var(--page-color);border-radius:var(--p-4);flex:1;min-width:0;padding-bottom:var(--page-y-padding)}";
4
4
  var S = {"root":"AppShell_root__ONlNK","mainColumn":"AppShell_mainColumn__Emn1p","mainBody":"AppShell_mainBody__IoVuy"};
5
5
  styleInject(css_248z);
6
6
 
@@ -6,7 +6,7 @@ import { PageContext } from '../pageContext.js';
6
6
  import { Scroll } from '@homecode/ui';
7
7
  import S from './PageScroll.styl.js';
8
8
 
9
- function PageScrollInner({ className, children, }) {
9
+ function PageScrollInner({ className, rootClassName, children, }) {
10
10
  // const { setIsScrolled } = useContext(PageContext);
11
11
  // const prevScrollTop = useRef(0);
12
12
  const location = useLocation();
@@ -25,13 +25,13 @@ function PageScrollInner({ className, children, }) {
25
25
  // };
26
26
  return (jsx(Scroll, { y: true,
27
27
  // fadeSize="l"
28
- offset: { y: { before: 120, after: 130 } }, className: S.root, autoHide: true, innerClassName: cn(S.inner, className), yScrollbarClassName: S.scrollbar, onInnerRef: el => {
28
+ offset: { y: { before: 120, after: 130 } }, className: cn(S.root, rootClassName), autoHide: true, innerClassName: cn(S.inner, className), yScrollbarClassName: S.scrollbar, onInnerRef: el => {
29
29
  scrollInnerRef.current = el;
30
30
  }, children: children }));
31
31
  }
32
- const PageScroll = ({ children, className, }) => {
32
+ const PageScroll = ({ children, className, rootClassName, }) => {
33
33
  const [isScrolled, setIsScrolled] = useState(false);
34
- return (jsx(PageContext.Provider, { value: { isScrolled, setIsScrolled }, children: jsx(PageScrollInner, { className: className, children: children }) }));
34
+ return (jsx(PageContext.Provider, { value: { isScrolled, setIsScrolled }, children: jsx(PageScrollInner, { className: className, rootClassName: rootClassName, children: children }) }));
35
35
  };
36
36
 
37
37
  export { PageScroll };
@@ -6,7 +6,7 @@ import { PackageOpen, ChevronDown, ChevronRight } from 'lucide-react';
6
6
  import S from './SidebarDatasetsItemsGrouped.styl.js';
7
7
  import { groupSidebarDatasets } from './groupSidebarDatasets.js';
8
8
 
9
- function SidebarDatasetsItemsGrouped({ groupBy, datasets, selectedDatasetId, onDatasetClick, defaultExpandedGroupNames, className, }) {
9
+ function SidebarDatasetsItemsGrouped({ groupBy, datasets, preItems, postItems, selectedDatasetId, onDatasetClick, defaultExpandedGroupNames, className, }) {
10
10
  const grouped = useMemo(() => groupSidebarDatasets(datasets, groupBy), [datasets, groupBy]);
11
11
  const [expanded, setExpanded] = useState(new Set());
12
12
  useEffect(() => {
@@ -35,14 +35,14 @@ function SidebarDatasetsItemsGrouped({ groupBy, datasets, selectedDatasetId, onD
35
35
  return next;
36
36
  });
37
37
  };
38
- return (jsx(SidebarGroup, { className: className, children: jsx(SidebarMenu, { children: grouped.map(([groupName, groupDatasets]) => {
39
- const isExpanded = expanded.has(groupName);
40
- const parentActive = groupDatasets.some(d => d.id === selectedDatasetId);
41
- return (jsxs(SidebarMenuItem, { children: [jsxs(SidebarMenuButton, { type: "button", isActive: parentActive, onClick: () => toggleGroup(groupName), children: [jsx(PackageOpen, { strokeWidth: 1.5, size: 16 }), jsx(SmartTextTruncate, { children: groupName }), jsx("div", { className: S.chevronContainer, children: isExpanded ? (jsx(ChevronDown, { size: 12 })) : (jsx(ChevronRight, { size: 12 })) })] }), isExpanded && (jsxs(SidebarMenuSub, { className: S.subMenuContainer, children: [jsx("div", { className: S.subMenuBorder }), groupDatasets.map(dataset => (jsx(SidebarMenuSubItem, { className: S.subMenuItem, children: jsx(SidebarMenuSubButton, { href: `#dataset-${dataset.id}`, isActive: dataset.id === selectedDatasetId, onClick: e => {
42
- e.preventDefault();
43
- onDatasetClick?.(dataset.id);
44
- }, children: jsx(SmartTextTruncate, { children: dataset.name }) }) }, dataset.id)))] }))] }, groupName));
45
- }) }) }));
38
+ return (jsx(SidebarGroup, { className: className, children: jsxs(SidebarMenu, { children: [preItems, grouped.map(([groupName, groupDatasets]) => {
39
+ const isExpanded = expanded.has(groupName);
40
+ const parentActive = groupDatasets.some(d => d.id === selectedDatasetId);
41
+ return (jsxs(SidebarMenuItem, { children: [jsxs(SidebarMenuButton, { type: "button", isActive: parentActive, onClick: () => toggleGroup(groupName), children: [jsx(PackageOpen, { strokeWidth: 1.5, size: 16 }), jsx(SmartTextTruncate, { children: groupName }), jsx("div", { className: S.chevronContainer, children: isExpanded ? (jsx(ChevronDown, { size: 12 })) : (jsx(ChevronRight, { size: 12 })) })] }), isExpanded && (jsxs(SidebarMenuSub, { className: S.subMenuContainer, children: [jsx("div", { className: S.subMenuBorder }), groupDatasets.map(dataset => (jsx(SidebarMenuSubItem, { className: S.subMenuItem, children: jsx(SidebarMenuSubButton, { href: `#dataset-${dataset.id}`, isActive: dataset.id === selectedDatasetId, onClick: e => {
42
+ e.preventDefault();
43
+ onDatasetClick?.(dataset.id);
44
+ }, children: jsx(SmartTextTruncate, { children: dataset.name }) }) }, dataset.id)))] }))] }, groupName));
45
+ }), postItems] }) }));
46
46
  }
47
47
 
48
48
  export { SidebarDatasetsItemsGrouped };
package/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { SybilionAuthProvider, createSybilionApiFetch, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth } from './sybilion-auth/SybilionAuthProvider.js';
1
+ export { SybilionAuthProvider, createSybilionApiFetch, getSybilionApiOriginFromSdk, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth } from './sybilion-auth/SybilionAuthProvider.js';
2
2
  export { SYBILION_AUTH_LOGIN_PATH, normalizeApiBaseUrl } from './sybilion-auth/authPaths.js';
3
3
  export { exchangeAuth0AccessTokenForSybilionJwt } from './sybilion-auth/exchangeSybilionToken.js';
4
4
  export { ChatContext, ChatProvider, useChat, useChats, useChatsForDataset, useChatsForScopeId, useCurrentChat } from './contexts/chat-context.js';
@@ -42,6 +42,7 @@ export { LabelWithId } from './components/ui/LabelWithId/LabelWithId.js';
42
42
  export { LegacyPlatformLink } from './components/ui/LegacyPlatformLink/LegacyPlatformLink.js';
43
43
  export { Logo } from './components/ui/Logo/Logo.js';
44
44
  export { MobileAdaptiveSelector } from './components/ui/MobileAdaptiveSelector/MobileAdaptiveSelector.js';
45
+ export { NavUserHeader } from './components/ui/NavUserHeader/NavUserHeader.js';
45
46
  export { NumberControl } from './components/ui/NumberControl/NumberControl.js';
46
47
  export { AppShell, AppShellMainContent } from './components/ui/Page/AppShell/AppShell.js';
47
48
  export { PageHeader } from './components/ui/Page/PageHeader/PageHeader.js';
@@ -2,9 +2,30 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { Auth0Provider, useAuth0 } from '@auth0/auth0-react';
3
3
  import { createContext, useContext, useMemo, useRef, useState, useCallback, useEffect } from 'react';
4
4
  import { normalizeApiBaseUrl } from './authPaths.js';
5
- import { exchangeAuth0AccessTokenForSybilionJwt } from './exchangeSybilionToken.js';
6
5
 
7
6
  const DEFAULT_TOKEN_KEY = 'sybilion.standalone.jwt';
7
+ function sybilionJwtFromLoginResponse(body) {
8
+ const t = body.data?.token ?? body.token;
9
+ if (!t)
10
+ throw new Error('Sybilion auth: missing token in login response');
11
+ return t;
12
+ }
13
+ /** Origin (`scheme://host:port`) for paths like `/api/v1/...`; derived from SDK URL layout. */
14
+ function getSybilionApiOriginFromSdk(sdk) {
15
+ const loginUrl = sdk.http.buildUrl('/v1/auth/login');
16
+ try {
17
+ const baseHref = typeof window !== 'undefined' && window.location?.href
18
+ ? window.location.href
19
+ : 'http://localhost/';
20
+ const u = new URL(loginUrl, baseHref);
21
+ if (!u.pathname.endsWith('/v1/auth/login'))
22
+ return '';
23
+ return u.origin;
24
+ }
25
+ catch {
26
+ return '';
27
+ }
28
+ }
8
29
  const SybilionAuthContext = createContext(null);
9
30
  function useSybilionAuth() {
10
31
  const v = useContext(SybilionAuthContext);
@@ -51,7 +72,7 @@ function writeLs(key, value) {
51
72
  /* quota / blocked */
52
73
  }
53
74
  }
54
- function InnerSybilionSession({ children, apiBaseUrl, storageKey, logoutReturnTo, }) {
75
+ function InnerSybilionSession({ children, sdk, storageKey, logoutReturnTo, }) {
55
76
  const auth0 = useAuth0();
56
77
  const auth0Ref = useRef(auth0);
57
78
  auth0Ref.current = auth0;
@@ -71,6 +92,7 @@ function InnerSybilionSession({ children, apiBaseUrl, storageKey, logoutReturnTo
71
92
  logoutParams: returnTo ? { returnTo } : undefined,
72
93
  });
73
94
  }, [persistToken, logoutReturnTo]);
95
+ const apiBaseUrl = useMemo(() => getSybilionApiOriginFromSdk(sdk), [sdk]);
74
96
  useEffect(() => {
75
97
  if (!auth0.isAuthenticated || !auth0.user?.sub) {
76
98
  persistToken(null);
@@ -88,7 +110,8 @@ function InnerSybilionSession({ children, apiBaseUrl, storageKey, logoutReturnTo
88
110
  setExchangeError(null);
89
111
  try {
90
112
  const access = await auth0Ref.current.getAccessTokenSilently();
91
- const jwt = await exchangeAuth0AccessTokenForSybilionJwt(apiBaseUrl, access);
113
+ const loginBody = await sdk.auth.loginWithAuth0Identity(access);
114
+ const jwt = sybilionJwtFromLoginResponse(loginBody);
92
115
  if (cancelled)
93
116
  return;
94
117
  persistToken(jwt);
@@ -111,7 +134,7 @@ function InnerSybilionSession({ children, apiBaseUrl, storageKey, logoutReturnTo
111
134
  cancelled = true;
112
135
  };
113
136
  }, [
114
- apiBaseUrl,
137
+ sdk,
115
138
  auth0.isAuthenticated,
116
139
  auth0.user?.sub,
117
140
  persistToken,
@@ -163,7 +186,7 @@ function InnerSybilionSession({ children, apiBaseUrl, storageKey, logoutReturnTo
163
186
  ]);
164
187
  return (jsx(SybilionAuthContext.Provider, { value: value, children: children }));
165
188
  }
166
- function SybilionAuthProvider({ children, apiBaseUrl, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey = DEFAULT_TOKEN_KEY, logoutReturnTo, }) {
189
+ function SybilionAuthProvider({ children, sdk, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey = DEFAULT_TOKEN_KEY, logoutReturnTo, }) {
167
190
  const mergedAuthParams = useMemo(() => ({
168
191
  redirect_uri: authorizationParams?.redirect_uri ?? redirectUri,
169
192
  audience: authorizationParams?.audience ?? `https://${auth0Domain}/api/v2/`,
@@ -179,7 +202,7 @@ function SybilionAuthProvider({ children, apiBaseUrl, auth0Domain, auth0ClientId
179
202
  const cookieOpts = typeof window !== 'undefined'
180
203
  ? { cookieDomain: window.location.hostname }
181
204
  : {};
182
- return (jsx(Auth0Provider, { domain: auth0Domain, clientId: auth0ClientId, authorizationParams: mergedAuthParams, cacheLocation: "localstorage", useRefreshTokens: true, ...cookieOpts, children: jsx(InnerSybilionSession, { apiBaseUrl: apiBaseUrl, storageKey: sybilionTokenStorageKey, logoutReturnTo: logoutReturnTo, children: children }) }));
205
+ return (jsx(Auth0Provider, { domain: auth0Domain, clientId: auth0ClientId, authorizationParams: mergedAuthParams, cacheLocation: "localstorage", useRefreshTokens: true, ...cookieOpts, children: jsx(InnerSybilionSession, { sdk: sdk, storageKey: sybilionTokenStorageKey, logoutReturnTo: logoutReturnTo, children: children }) }));
183
206
  }
184
207
 
185
- export { SybilionAuthProvider, createSybilionApiFetch, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth };
208
+ export { SybilionAuthProvider, createSybilionApiFetch, getSybilionApiOriginFromSdk, sybilionApiFetch, useSybilionApiFetch, useSybilionAuth };
@@ -1,9 +1,13 @@
1
1
  import { normalizeApiBaseUrl, SYBILION_AUTH_LOGIN_PATH } from './authPaths.js';
2
2
 
3
+ /** Default API segment before `/v1/...`; matches {@link createSybilionSDK} default `apiPrefix`. */
4
+ const SYBILION_AUTH_API_PREFIX = '/api';
3
5
  /** POST `{ identity: auth0AccessToken, type: 'auth0' }` → Sybilion API JWT string. */
4
- async function exchangeAuth0AccessTokenForSybilionJwt(apiBaseUrl, auth0AccessToken) {
6
+ async function exchangeAuth0AccessTokenForSybilionJwt(
7
+ /** API origin only (no trailing slash), same as SDK `baseUrl`. */
8
+ apiBaseUrl, auth0AccessToken) {
5
9
  const base = normalizeApiBaseUrl(apiBaseUrl);
6
- const res = await fetch(`${base}${SYBILION_AUTH_LOGIN_PATH}`, {
10
+ const res = await fetch(`${base}${SYBILION_AUTH_API_PREFIX}${SYBILION_AUTH_LOGIN_PATH}`, {
7
11
  method: 'POST',
8
12
  headers: {
9
13
  Accept: 'application/json',
@@ -7,5 +7,6 @@ export type AppHeaderProps = {
7
7
  export declare function AppHeaderHost({ className, anchorId, }: AppHeaderProps): import("react/jsx-runtime").JSX.Element;
8
8
  export type AppHeaderPortalProps = {
9
9
  children: ReactNode;
10
+ pageHeaderId?: string;
10
11
  };
11
- export declare function AppHeaderPortal({ children }: AppHeaderPortalProps): import("react").ReactPortal;
12
+ export declare function AppHeaderPortal({ children, pageHeaderId, }: AppHeaderPortalProps): import("react").ReactPortal;
@@ -0,0 +1,2 @@
1
+ import type { NavUserHeaderProps } from './NavUserHeader.types';
2
+ export declare function NavUserHeader({ variant, isLoading, isAuthenticated, user, menuItems, theme, onThemeToggle, onLogout, signInSlot, onSignInClick, }: NavUserHeaderProps): string | number | bigint | true | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode>> | import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { ReactNode } from 'react';
2
+ export type NavUserHeaderUser = {
3
+ name: string;
4
+ email: string;
5
+ /** Passed to avatar `Image` as `src`. */
6
+ avatar?: string;
7
+ };
8
+ export type NavUserHeaderProps = {
9
+ variant?: 'default' | 'compact';
10
+ isLoading?: boolean;
11
+ /** When false, signed-out branch is shown. Defaults to true when omitted. */
12
+ isAuthenticated?: boolean;
13
+ /** Present when authenticated: shown in trigger and dropdown label. */
14
+ user?: NavUserHeaderUser | null;
15
+ /** Rows inside the menu above theme toggle and logout. Use `DropdownMenuItem` nodes. */
16
+ menuItems?: ReactNode;
17
+ /** Current theme drives the toggle row label/icons. */
18
+ theme: 'light' | 'dark';
19
+ /** When set, renders the light/dark theme menu row. */
20
+ onThemeToggle?: () => void;
21
+ onLogout: () => void;
22
+ /** Replaces default “Log in” control when signed out. */
23
+ signInSlot?: ReactNode;
24
+ onSignInClick?: () => void;
25
+ };
@@ -0,0 +1,2 @@
1
+ export { NavUserHeader } from './NavUserHeader';
2
+ export type { NavUserHeaderProps, NavUserHeaderUser, } from './NavUserHeader.types';
@@ -1,4 +1,5 @@
1
- export declare const PageScroll: ({ children, className, }: {
1
+ export declare const PageScroll: ({ children, className, rootClassName, }: {
2
2
  children: React.ReactNode;
3
3
  className?: string;
4
+ rootClassName?: string;
4
5
  }) => import("react/jsx-runtime").JSX.Element;
@@ -2,10 +2,12 @@ import { type SidebarDatasetsItemsGroupBy, type SidebarDatasetsItemsGroupedDatas
2
2
  export type SidebarDatasetsItemsGroupedProps = {
3
3
  groupBy: SidebarDatasetsItemsGroupBy;
4
4
  datasets: SidebarDatasetsItemsGroupedDataset[];
5
+ preItems?: React.ReactNode;
6
+ postItems?: React.ReactNode;
5
7
  selectedDatasetId?: number;
6
8
  onDatasetClick?: (datasetId: number) => void;
7
9
  /** When omitted, all groups start expanded. */
8
10
  defaultExpandedGroupNames?: string[];
9
11
  className?: string;
10
12
  };
11
- export declare function SidebarDatasetsItemsGrouped({ groupBy, datasets, selectedDatasetId, onDatasetClick, defaultExpandedGroupNames, className, }: SidebarDatasetsItemsGroupedProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function SidebarDatasetsItemsGrouped({ groupBy, datasets, preItems, postItems, selectedDatasetId, onDatasetClick, defaultExpandedGroupNames, className, }: SidebarDatasetsItemsGroupedProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export default function NavUserHeaderPage(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export default function StandaloneAppLayoutPage(): import("react/jsx-runtime").JSX.Element;
@@ -31,6 +31,7 @@ export * from './components/ui/LabelWithId';
31
31
  export * from './components/ui/LegacyPlatformLink';
32
32
  export * from './components/ui/Logo';
33
33
  export * from './components/ui/MobileAdaptiveSelector';
34
+ export * from './components/ui/NavUserHeader';
34
35
  export * from './components/ui/NumberControl';
35
36
  export * from './components/ui/Page';
36
37
  export * from './components/ui/Progress';
@@ -1,8 +1,11 @@
1
1
  import { type RedirectLoginOptions } from '@auth0/auth0-react';
2
+ import type { SybilionSDK } from '@sybilion/sdk';
2
3
  import type { JSX, ReactNode } from 'react';
4
+ /** Origin (`scheme://host:port`) for paths like `/api/v1/...`; derived from SDK URL layout. */
5
+ export declare function getSybilionApiOriginFromSdk(sdk: SybilionSDK): string;
3
6
  export type SybilionAuthProviderProps = {
4
7
  children: ReactNode;
5
- apiBaseUrl: string;
8
+ sdk: SybilionSDK;
6
9
  auth0Domain: string;
7
10
  auth0ClientId: string;
8
11
  redirectUri: string;
@@ -36,4 +39,4 @@ export declare function sybilionApiFetch(apiBaseUrl: string, bearerToken: string
36
39
  export declare function createSybilionApiFetch(apiBaseUrl: string, getSybilionAccessToken: () => Promise<string | null>): (path: string, init?: RequestInit) => Promise<Response>;
37
40
  /** Authenticated fetch using {@link useSybilionAuth} context. */
38
41
  export declare function useSybilionApiFetch(): (path: string, init?: RequestInit) => Promise<Response>;
39
- export declare function SybilionAuthProvider({ children, apiBaseUrl, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey, logoutReturnTo, }: SybilionAuthProviderProps): JSX.Element;
42
+ export declare function SybilionAuthProvider({ children, sdk, auth0Domain, auth0ClientId, redirectUri, authorizationParams, sybilionTokenStorageKey, logoutReturnTo, }: SybilionAuthProviderProps): JSX.Element;
@@ -1,2 +1,4 @@
1
1
  /** POST `{ identity: auth0AccessToken, type: 'auth0' }` → Sybilion API JWT string. */
2
- export declare function exchangeAuth0AccessTokenForSybilionJwt(apiBaseUrl: string, auth0AccessToken: string): Promise<string>;
2
+ export declare function exchangeAuth0AccessTokenForSybilionJwt(
3
+ /** API origin only (no trailing slash), same as SDK `baseUrl`. */
4
+ apiBaseUrl: string, auth0AccessToken: string): Promise<string>;
@@ -1,4 +1,4 @@
1
1
  export type { SybilionAuthProviderProps, SybilionAuthContextValue, } from '#uilib/sybilion-auth/SybilionAuthProvider';
2
- export { SybilionAuthProvider, useSybilionAuth, sybilionApiFetch, createSybilionApiFetch, useSybilionApiFetch, } from '#uilib/sybilion-auth/SybilionAuthProvider';
2
+ export { SybilionAuthProvider, getSybilionApiOriginFromSdk, useSybilionAuth, sybilionApiFetch, createSybilionApiFetch, useSybilionApiFetch, } from '#uilib/sybilion-auth/SybilionAuthProvider';
3
3
  export { SYBILION_AUTH_LOGIN_PATH, normalizeApiBaseUrl, } from '#uilib/sybilion-auth/authPaths';
4
4
  export { exchangeAuth0AccessTokenForSybilionJwt } from '#uilib/sybilion-auth/exchangeSybilionToken';