@pallasoft/ps-core-components 1.4.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/components/ps-accordion.js +1 -1
  2. package/dist/components/ps-button.js +2 -1
  3. package/dist/components/ps-card.js +1 -1
  4. package/dist/components/ps-draggable-grid.js +2 -1
  5. package/dist/components/ps-empty-state.d.ts +4 -2
  6. package/dist/components/ps-empty-state.js +20 -1
  7. package/dist/components/ps-fab.d.ts +1 -1
  8. package/dist/components/ps-infinite-scroll.js +1 -1
  9. package/dist/components/ps-list-tile.js +1 -1
  10. package/dist/components/ps-metric-card.d.ts +1 -1
  11. package/dist/components/ps-metric-card.js +1 -1
  12. package/dist/components/ps-navigation-progress.d.ts +8 -0
  13. package/dist/components/ps-navigation-progress.js +10 -0
  14. package/dist/components/ps-portable-text.d.ts +4 -4
  15. package/dist/components/ps-portable-text.js +1 -1
  16. package/dist/components/ps-reflective-app-shell.d.ts +4 -2
  17. package/dist/components/ps-reflective-app-shell.js +11 -1
  18. package/dist/components/ps-section-header.d.ts +2 -1
  19. package/dist/components/ps-section-header.js +1 -1
  20. package/dist/components/ps-site-header.d.ts +5 -1
  21. package/dist/components/ps-site-header.js +4 -3
  22. package/dist/components/ps-theme-toggle.d.ts +1 -1
  23. package/dist/components/ps-voice-orb.d.ts +1 -6
  24. package/dist/components/ps-voice-orb.js +94 -30
  25. package/dist/constants/ps-voice-orb.d.ts +18 -0
  26. package/dist/constants/ps-voice-orb.js +88 -0
  27. package/dist/hooks/use-ps-invisible-recaptcha.d.ts +3 -1
  28. package/dist/hooks/use-ps-invisible-recaptcha.js +8 -7
  29. package/dist/hooks/use-ps-route-loader-data.js +2 -2
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +2 -0
  32. package/dist/models/ps-voice-orb.d.ts +23 -0
  33. package/dist/models/ps-voice-orb.js +2 -0
  34. package/package.json +5 -3
@@ -35,8 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.PsAccordion = void 0;
37
37
  const jsx_runtime_1 = require("react/jsx-runtime");
38
- const AccordionPrimitive = __importStar(require("@radix-ui/react-accordion"));
39
38
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
40
39
  const ps_icons_1 = require("@pallasoft/ps-icons");
40
+ const AccordionPrimitive = __importStar(require("@radix-ui/react-accordion"));
41
41
  const PsAccordion = ({ children, className, isOpen, label, onToggle, trailing, }) => ((0, jsx_runtime_1.jsx)(AccordionPrimitive.Root, { type: 'single', collapsible: true, value: isOpen ? 'open' : '', onValueChange: onToggle, className: ps_design_system_1.PallasoftClassNameService.merge('overflow-hidden rounded-ps-card border border-line/70 bg-surface/60 transition-colors duration-200', isOpen ? 'border-primary/25 bg-surface' : 'hover:border-line hover:bg-surface/90', className), children: (0, jsx_runtime_1.jsxs)(AccordionPrimitive.Item, { value: 'open', children: [(0, jsx_runtime_1.jsx)(AccordionPrimitive.Header, { children: (0, jsx_runtime_1.jsxs)(AccordionPrimitive.Trigger, { className: 'ps-focus-ring flex w-full items-center justify-between gap-3 px-4 py-3.5 text-left', children: [(0, jsx_runtime_1.jsx)("span", { className: 'text-body-2 font-semibold text-ink', children: label }), (0, jsx_runtime_1.jsxs)("span", { className: 'flex shrink-0 items-center gap-2', children: [trailing, (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { iconKey: ps_icons_1.PsIconKey.ChevronRight, className: ps_design_system_1.PallasoftClassNameService.merge('h-4 w-4 text-ink/40 transition-transform duration-200', isOpen ? 'rotate-90 text-primary' : '') })] })] }) }), (0, jsx_runtime_1.jsx)(AccordionPrimitive.Content, { className: 'ps-accordion-content', children: (0, jsx_runtime_1.jsx)("div", { className: 'border-t border-line/70 p-4', children: children }) })] }) }));
42
42
  exports.PsAccordion = PsAccordion;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsButton = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const ps_spinner_1 = require("./ps-spinner");
6
7
  const sizeClassNames = {
7
8
  compact: 'min-h-9 px-3 py-2 text-sm',
8
9
  default: 'min-h-10 px-4 py-2.5 text-sm',
@@ -15,5 +16,5 @@ const variantClassNames = {
15
16
  secondary: 'ps-liquid ps-liquid-control bg-white/72 text-ink hover:border-primary/30 hover:text-primary dark:border-white/16 dark:bg-white/12 dark:hover:text-white',
16
17
  surface: 'ps-liquid ps-liquid-control bg-white/72 text-ink hover:border-primary/30 dark:border-white/16 dark:bg-white/12',
17
18
  };
18
- const PsButton = ({ children, className, disabled, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring inline-flex max-w-full items-center justify-center gap-2 rounded-full border text-center font-semibold disabled:cursor-not-allowed disabled:opacity-50', sizeClassNames[size], variantClassNames[variant], className), "aria-busy": isLoading || undefined, disabled: disabled || isLoading, type: type, ...props, children: [isLoading ? ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'h-4 w-4 shrink-0 animate-spin rounded-full border-2 border-current border-t-transparent' })) : leadingIcon ? ((0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: leadingIcon })) : null, (0, jsx_runtime_1.jsx)("span", { className: 'truncate', children: isLoading && loadingLabel ? loadingLabel : children }), !isLoading && trailingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: trailingIcon }) : null] }));
19
+ const PsButton = ({ children, className, disabled, isLoading = false, leadingIcon, loadingLabel, size = 'default', trailingIcon, type = 'button', variant = 'secondary', ...props }) => ((0, jsx_runtime_1.jsxs)("button", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring relative inline-flex max-w-full items-center justify-center gap-2 rounded-full border text-center font-semibold disabled:cursor-not-allowed disabled:opacity-50', sizeClassNames[size], variantClassNames[variant], className), "aria-busy": isLoading || undefined, disabled: disabled || isLoading, type: type, ...props, children: [isLoading ? ((0, jsx_runtime_1.jsx)(ps_spinner_1.PsSpinner, { className: 'absolute inset-0 m-auto text-current', label: loadingLabel !== null && loadingLabel !== undefined ? loadingLabel : 'Loading', size: 'small' })) : null, (0, jsx_runtime_1.jsxs)("span", { "aria-hidden": isLoading || undefined, className: ps_design_system_1.PallasoftClassNameService.merge('inline-flex min-w-0 items-center gap-2', isLoading ? 'invisible' : ''), children: [leadingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: leadingIcon }) : null, (0, jsx_runtime_1.jsx)("span", { className: 'truncate', children: children }), trailingIcon ? (0, jsx_runtime_1.jsx)("span", { className: 'shrink-0', children: trailingIcon }) : null] })] }));
19
20
  exports.PsButton = PsButton;
@@ -14,5 +14,5 @@ const variantClassNames = {
14
14
  glass: 'ps-glass',
15
15
  muted: 'ps-card-muted',
16
16
  };
17
- const PsCard = ({ children, className, density = 'normal', eyebrow, title, trailing, variant = 'default', }) => ((0, jsx_runtime_1.jsxs)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden rounded-ps-card', variantClassNames[variant], densityClassNames[density], className), children: [title ? ((0, jsx_runtime_1.jsxs)("div", { className: 'mb-3 flex min-w-0 items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("h2", { className: 'mt-1 break-words text-base font-bold text-ink', children: title })] }), trailing ? (0, jsx_runtime_1.jsx)("div", { className: 'shrink-0', children: trailing }) : null] })) : null, children] }));
17
+ const PsCard = ({ children, className, density = 'normal', eyebrow, title, trailing, variant = 'default', }) => ((0, jsx_runtime_1.jsxs)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden rounded-ps-card', variantClassNames[variant], densityClassNames[density], className), children: [title ? ((0, jsx_runtime_1.jsxs)("div", { className: 'mb-3 flex min-w-0 items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [eyebrow ? (0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow }) : null, (0, jsx_runtime_1.jsx)("h2", { className: 'mt-1 break-words text-base font-bold text-ink', children: title })] }), trailing ? (0, jsx_runtime_1.jsx)("div", { className: 'shrink-0', children: trailing }) : null] })) : null, children] }));
18
18
  exports.PsCard = PsCard;
@@ -39,9 +39,10 @@ const PsDraggableGrid = ({ getItemClassName, items, onReorder, onReorderEnd, ren
39
39
  draggedItemId = item.id;
40
40
  }, title: `Move ${item.label}`, type: 'button', children: (0, jsx_runtime_1.jsx)(DragHandleIcon, {}) }));
41
41
  const renderedItem = renderItem({ handle, index, isDragging, item });
42
+ const itemClassName = getItemClassName?.(item);
42
43
  if (!renderedItem)
43
44
  return null;
44
- return ((0, jsx_runtime_1.jsx)("div", { className: `ps-page-enter min-w-0 rounded-[1.7rem] transition ${getItemClassName?.(item) ?? ''} ${isDragging ? 'scale-[0.985] opacity-70' : ''}`, onDragOver: (event) => {
45
+ return ((0, jsx_runtime_1.jsx)("div", { className: `ps-page-enter min-w-0 rounded-[1.7rem] transition ${itemClassName !== null && itemClassName !== undefined ? itemClassName : ''} ${isDragging ? 'scale-[0.985] opacity-70' : ''}`, onDragOver: (event) => {
45
46
  if (!draggedItemId)
46
47
  return;
47
48
  event.preventDefault();
@@ -8,12 +8,14 @@ export interface PsEmptyStateAction {
8
8
  }
9
9
  export interface PsEmptyStateProps {
10
10
  actions?: PsEmptyStateAction[];
11
+ children?: ReactNode;
11
12
  badge?: string;
12
13
  className?: string;
13
14
  detail?: string;
14
- description: string;
15
+ description?: string;
15
16
  eyebrow?: string;
16
17
  icon?: ReactNode;
17
18
  title: string;
19
+ variant?: 'inline' | 'page';
18
20
  }
19
- export declare const PsEmptyState: ({ actions, badge, className, detail, description, eyebrow, icon, title, }: PsEmptyStateProps) => import("react").JSX.Element;
21
+ export declare const PsEmptyState: ({ actions, badge, children, className, detail, description, eyebrow, icon, title, variant, }: PsEmptyStateProps) => import("react").JSX.Element;
@@ -9,5 +9,24 @@ const getActionClassName = (variant = 'secondary') => {
9
9
  }
10
10
  return 'border-line bg-surface text-ink hover:border-primary/40 hover:text-primary';
11
11
  };
12
- const PsEmptyState = ({ actions = [], badge, className, detail, description, eyebrow, icon, title, }) => ((0, jsx_runtime_1.jsx)("main", { className: ps_design_system_1.PallasoftClassNameService.merge('relative flex min-h-screen items-center justify-center bg-mist px-4 py-10 text-ink', className), children: (0, jsx_runtime_1.jsx)("section", { className: 'ps-card relative w-full max-w-2xl p-6 sm:p-8', children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col gap-5 sm:flex-row sm:items-start', children: [icon ? (0, jsx_runtime_1.jsx)("div", { className: 'ps-icon-surface h-14 w-14', children: icon }) : null, (0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0 flex-1', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-wrap items-center gap-2', children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-primary', children: eyebrow })) : null, eyebrow && badge ? (0, jsx_runtime_1.jsx)("span", { className: 'h-1 w-1 rounded-full bg-line' }) : null, badge ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-ink/40', children: badge })) : null] }), (0, jsx_runtime_1.jsx)("h1", { className: 'mt-3 text-3xl font-semibold tracking-tight sm:text-4xl', children: title }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-3 text-sm leading-6 text-ink/60', children: description }), detail ? ((0, jsx_runtime_1.jsx)("p", { className: 'mt-4 rounded-2xl border border-line bg-mist px-4 py-3 text-left text-xs leading-5 text-ink/60', children: detail })) : null, actions.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: 'mt-6 flex flex-col gap-2 sm:flex-row', children: actions.map((action) => ((0, jsx_runtime_1.jsx)("a", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-pressable inline-flex items-center justify-center rounded-ps-control border px-4 py-3 text-sm font-semibold', getActionClassName(action.variant)), href: action.href, rel: action.rel, target: action.target, children: action.label }, `${action.href}-${action.label}`))) })) : null] })] }) }) }));
12
+ const PsEmptyState = ({ actions = [], badge, children, className, detail, description, eyebrow, icon, title, variant = 'page', }) => {
13
+ const isInline = variant === 'inline';
14
+ const Wrapper = isInline ? 'div' : 'main';
15
+ const Heading = isInline ? 'h3' : 'h1';
16
+ return ((0, jsx_runtime_1.jsx)(Wrapper, { className: ps_design_system_1.PallasoftClassNameService.merge(isInline
17
+ ? 'flex items-center justify-center rounded-ps-panel border border-dashed border-line bg-mist/40 px-6 py-10 text-ink'
18
+ : 'relative flex min-h-screen items-center justify-center bg-mist px-4 py-10 text-ink', className), children: (0, jsx_runtime_1.jsx)("section", { className: isInline
19
+ ? 'relative w-full max-w-2xl text-center'
20
+ : 'ps-card relative w-full max-w-2xl p-6 sm:p-8', children: (0, jsx_runtime_1.jsxs)("div", { className: isInline
21
+ ? 'flex flex-col items-center gap-3'
22
+ : 'flex flex-col gap-5 sm:flex-row sm:items-start', children: [icon ? (0, jsx_runtime_1.jsx)("div", { className: 'ps-icon-surface h-14 w-14', children: icon }) : null, (0, jsx_runtime_1.jsxs)("div", { className: isInline ? 'min-w-0' : 'min-w-0 flex-1', children: [(0, jsx_runtime_1.jsxs)("div", { className: isInline
23
+ ? 'flex flex-wrap items-center justify-center gap-2'
24
+ : 'flex flex-wrap items-center gap-2', children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-primary', children: eyebrow })) : null, eyebrow && badge ? (0, jsx_runtime_1.jsx)("span", { className: 'h-1 w-1 rounded-full bg-line' }) : null, badge ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-xs font-semibold uppercase tracking-[0.22em] text-ink/40', children: badge })) : null] }), (0, jsx_runtime_1.jsx)(Heading, { className: isInline
25
+ ? 'text-base font-semibold text-ink'
26
+ : 'mt-3 text-3xl font-semibold tracking-tight sm:text-4xl', children: title }), description ? ((0, jsx_runtime_1.jsx)("p", { className: isInline
27
+ ? 'mt-1.5 text-sm leading-6 text-ink/60'
28
+ : 'mt-3 text-sm leading-6 text-ink/60', children: description })) : null, detail ? ((0, jsx_runtime_1.jsx)("p", { className: 'mt-4 rounded-2xl border border-line bg-mist px-4 py-3 text-left text-xs leading-5 text-ink/60', children: detail })) : null, children ? (0, jsx_runtime_1.jsx)("div", { className: 'mt-5 flex justify-center', children: children }) : null, actions.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { className: isInline
29
+ ? 'mt-5 flex flex-col justify-center gap-2 sm:flex-row'
30
+ : 'mt-6 flex flex-col gap-2 sm:flex-row', children: actions.map((action) => ((0, jsx_runtime_1.jsx)("a", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-focus-ring ps-pressable inline-flex items-center justify-center rounded-ps-control border px-4 py-3 text-sm font-semibold', getActionClassName(action.variant)), href: action.href, rel: action.rel, target: action.target, children: action.label }, `${action.href}-${action.label}`))) })) : null] })] }) }) }));
31
+ };
13
32
  exports.PsEmptyState = PsEmptyState;
@@ -1,4 +1,4 @@
1
- import type { PsButtonProps } from './ps-button';
1
+ import { type PsButtonProps } from './ps-button';
2
2
  export interface PsFabProps extends PsButtonProps {
3
3
  position?: 'bottom-center' | 'bottom-left' | 'bottom-right';
4
4
  }
@@ -8,5 +8,5 @@ const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const react_infinite_scroll_component_1 = __importDefault(require("react-infinite-scroll-component"));
9
9
  const ps_spinner_1 = require("./ps-spinner");
10
10
  const defaultLoader = ((0, jsx_runtime_1.jsx)("div", { className: 'flex w-full justify-center py-8', children: (0, jsx_runtime_1.jsx)(ps_spinner_1.PsSpinner, {}) }));
11
- const PsInfiniteScroll = ({ children, className, dataLength, hasMore, loader, next, }) => ((0, jsx_runtime_1.jsx)(react_infinite_scroll_component_1.default, { className: className, dataLength: dataLength, hasMore: hasMore, loader: loader ?? defaultLoader, next: next, children: children }));
11
+ const PsInfiniteScroll = ({ children, className, dataLength, hasMore, loader, next, }) => ((0, jsx_runtime_1.jsx)(react_infinite_scroll_component_1.default, { className: className, dataLength: dataLength, hasMore: hasMore, loader: loader !== null && loader !== undefined ? loader : defaultLoader, next: next, children: children }));
12
12
  exports.PsInfiniteScroll = PsInfiniteScroll;
@@ -11,5 +11,5 @@ const variantClassNames = {
11
11
  card: 'ps-surface px-3',
12
12
  row: 'border-b border-line/70 px-0 last:border-b-0',
13
13
  };
14
- const PsListTile = ({ children, className, density = 'normal', eyebrow, isPrivateTitle = false, subtitle, title, variant = 'row', }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden', variantClassNames[variant], densityClassNames[density], className), children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("p", { className: ps_design_system_1.PallasoftClassNameService.merge('break-words text-sm font-semibold text-ink', eyebrow && 'mt-1', isPrivateTitle && 'private-number'), children: title }), subtitle ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 break-words text-sm leading-5 text-slate-500', children: subtitle }) : null, children] }));
14
+ const PsListTile = ({ children, className, density = 'normal', eyebrow, isPrivateTitle = false, subtitle, title, variant = 'row', }) => ((0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('min-w-0 overflow-hidden', variantClassNames[variant], densityClassNames[density], className), children: [eyebrow ? (0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow break-words', children: eyebrow }) : null, (0, jsx_runtime_1.jsx)("p", { className: ps_design_system_1.PallasoftClassNameService.merge('break-words text-sm font-semibold text-ink', eyebrow && 'mt-1', isPrivateTitle && 'private-number'), children: title }), subtitle ? ((0, jsx_runtime_1.jsx)("p", { className: 'mt-1 break-words text-sm leading-5 text-slate-500', children: subtitle })) : null, children] }));
15
15
  exports.PsListTile = PsListTile;
@@ -6,4 +6,4 @@ export interface PsMetricCardProps {
6
6
  label: string;
7
7
  value: ReactNode;
8
8
  }
9
- export declare const PsMetricCard: ({ changeLabel, className, icon, label, value, }: PsMetricCardProps) => import("react").JSX.Element;
9
+ export declare const PsMetricCard: ({ changeLabel, className, icon, label, value }: PsMetricCardProps) => import("react").JSX.Element;
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsMetricCard = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
- const PsMetricCard = ({ changeLabel, className, icon, label, value, }) => ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-metric-card min-w-0 p-3.5 sm:p-4', className), children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow truncate', children: label }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-2 truncate text-2xl font-semibold text-ink', children: value }), changeLabel ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 text-sm text-slate-500', children: changeLabel }) : null] }), icon ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary-soft text-primary', children: icon })) : null] }) }));
6
+ const PsMetricCard = ({ changeLabel, className, icon, label, value }) => ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-metric-card min-w-0 p-3.5 sm:p-4', className), children: (0, jsx_runtime_1.jsxs)("div", { className: 'flex items-start justify-between gap-3', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'min-w-0', children: [(0, jsx_runtime_1.jsx)("p", { className: 'ps-eyebrow truncate', children: label }), (0, jsx_runtime_1.jsx)("p", { className: 'mt-2 truncate text-2xl font-semibold text-ink', children: value }), changeLabel ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-1 text-sm text-slate-500', children: changeLabel }) : null] }), icon ? ((0, jsx_runtime_1.jsx)("div", { className: 'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary-soft text-primary', children: icon })) : null] }) }));
7
7
  exports.PsMetricCard = PsMetricCard;
@@ -0,0 +1,8 @@
1
+ export interface PsNavigationProgressProps {
2
+ color: string;
3
+ height?: number;
4
+ loaderSpeed?: number;
5
+ progress: number;
6
+ shadow?: boolean;
7
+ }
8
+ export declare const PsNavigationProgress: ({ color, height, loaderSpeed, progress, shadow, }: PsNavigationProgressProps) => import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PsNavigationProgress = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_top_loading_bar_1 = __importDefault(require("react-top-loading-bar"));
9
+ const PsNavigationProgress = ({ color, height = 3, loaderSpeed = 1000, progress, shadow = true, }) => ((0, jsx_runtime_1.jsx)(react_top_loading_bar_1.default, { color: color, height: height, loaderSpeed: loaderSpeed, progress: progress, shadow: shadow }));
10
+ exports.PsNavigationProgress = PsNavigationProgress;
@@ -9,10 +9,10 @@ export interface PsPortableTextProps {
9
9
  linkClassName?: string;
10
10
  LinkComponent?: ElementType<{
11
11
  'aria-label'?: string;
12
- children?: ReactNode;
13
- className?: string;
14
- href: string;
15
- rel?: string;
12
+ 'children'?: ReactNode;
13
+ 'className'?: string;
14
+ 'href': string;
15
+ 'rel'?: string;
16
16
  }>;
17
17
  listClasses?: string;
18
18
  text: TypedObject[];
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsPortableText = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const react_1 = require("@portabletext/react");
6
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
+ const react_1 = require("@portabletext/react");
7
7
  const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = '', containerStyles = {}, headerClasses = 'text-ink', isLargeBodyFont = false, linkClassName = 'text-primary underline', LinkComponent = 'a', listClasses = 'text-ink', text, }) => {
8
8
  const hasContainerStyles = Object.keys(containerStyles).length > 0;
9
9
  const getBodyClass = hasContainerStyles ? '' : bodyClasses;
@@ -1,10 +1,12 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  export interface PsReflectiveAppShellProps {
3
3
  children: ReactNode;
4
4
  className?: string;
5
5
  contentClassName?: string;
6
6
  frameClassName?: string;
7
+ scrollResetKey?: string;
7
8
  sidebar: ReactNode;
9
+ sidebarWidth?: string;
8
10
  topbar: ReactNode;
9
11
  }
10
- export declare const PsReflectiveAppShell: ({ children, className, contentClassName, frameClassName, sidebar, topbar, }: PsReflectiveAppShellProps) => import("react").JSX.Element;
12
+ export declare const PsReflectiveAppShell: ({ children, className, contentClassName, frameClassName, scrollResetKey, sidebar, sidebarWidth, topbar, }: PsReflectiveAppShellProps) => import("react").JSX.Element;
@@ -2,6 +2,16 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsReflectiveAppShell = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
5
6
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
- const PsReflectiveAppShell = ({ children, className, contentClassName, frameClassName, sidebar, topbar, }) => ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-reflective-frame min-h-screen text-ink lg:px-3 lg:py-3', className), children: (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-liquid-strong mx-auto grid h-screen min-h-0 overflow-hidden border-line/70 bg-white/70 backdrop-blur-2xl dark:border-white/10 dark:bg-surface/70 lg:h-[calc(100vh-1.5rem)] lg:max-w-[1720px] lg:grid-cols-[17rem_minmax(0,1fr)] lg:rounded-[1.5rem] lg:border', frameClassName), children: [sidebar, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-h-0 min-w-0 flex-col bg-white/32 dark:bg-white/[0.02]', children: [topbar, (0, jsx_runtime_1.jsx)("main", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar min-h-0 flex-1 overflow-y-auto px-4 py-5 ps-page-enter sm:px-5 lg:px-6 xl:px-7', contentClassName), children: children })] })] }) }));
7
+ const PsReflectiveAppShell = ({ children, className, contentClassName, frameClassName, scrollResetKey, sidebar, sidebarWidth = '17rem', topbar, }) => {
8
+ const contentRef = (0, react_1.useRef)(null);
9
+ (0, react_1.useLayoutEffect)(() => {
10
+ if (scrollResetKey === undefined)
11
+ return;
12
+ contentRef.current?.scrollTo({ top: 0, left: 0 });
13
+ window.scrollTo({ top: 0, left: 0 });
14
+ }, [scrollResetKey]);
15
+ return ((0, jsx_runtime_1.jsx)("section", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-reflective-frame min-h-screen text-ink lg:px-3 lg:py-3', className), children: (0, jsx_runtime_1.jsxs)("div", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-liquid-strong mx-auto grid h-screen min-h-0 overflow-hidden border-line/70 bg-white/70 backdrop-blur-2xl transition-[grid-template-columns] duration-200 dark:border-white/10 dark:bg-surface/70 md:grid-cols-[var(--ps-sidebar-width)_minmax(0,1fr)] lg:h-[calc(100vh-1.5rem)] lg:max-w-[1720px] lg:rounded-[1.5rem] lg:border', frameClassName), style: { ['--ps-sidebar-width']: sidebarWidth }, children: [sidebar, (0, jsx_runtime_1.jsxs)("div", { className: 'flex min-h-0 min-w-0 flex-col bg-white/32 dark:bg-white/[0.02]', children: [topbar, (0, jsx_runtime_1.jsx)("main", { ref: contentRef, className: ps_design_system_1.PallasoftClassNameService.merge('ps-scrollbar min-h-0 flex-1 overflow-y-auto px-4 py-5 ps-page-enter sm:px-5 lg:px-6 xl:px-7', contentClassName), children: children })] })] }) }));
16
+ };
7
17
  exports.PsReflectiveAppShell = PsReflectiveAppShell;
@@ -2,7 +2,8 @@ import type { ReactNode } from 'react';
2
2
  export interface PsSectionHeaderProps {
3
3
  description?: string;
4
4
  eyebrow?: string;
5
+ leading?: ReactNode;
5
6
  title: string;
6
7
  trailing?: ReactNode;
7
8
  }
8
- export declare const PsSectionHeader: ({ description, eyebrow, title, trailing }: PsSectionHeaderProps) => import("react").JSX.Element;
9
+ export declare const PsSectionHeader: ({ description, eyebrow, leading, title, trailing, }: PsSectionHeaderProps) => import("react").JSX.Element;
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsSectionHeader = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
- const PsSectionHeader = ({ description, eyebrow, title, trailing }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col justify-between gap-3 lg:flex-row lg:items-end', children: [(0, jsx_runtime_1.jsxs)("div", { children: [eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.22em] text-slate-400', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("h1", { className: 'text-2xl font-extrabold text-ink', children: title }), description ? (0, jsx_runtime_1.jsx)("p", { className: 'mt-2 max-w-3xl text-sm leading-6 text-slate-500', children: description }) : null] }), trailing] }));
5
+ const PsSectionHeader = ({ description, eyebrow, leading, title, trailing, }) => ((0, jsx_runtime_1.jsxs)("div", { className: 'flex flex-col justify-between gap-3 lg:flex-row lg:items-end', children: [(0, jsx_runtime_1.jsxs)("div", { className: 'flex min-w-0 flex-col gap-2', children: [leading, eyebrow ? ((0, jsx_runtime_1.jsx)("p", { className: 'text-[0.68rem] font-semibold uppercase tracking-[0.22em] text-ink/50', children: eyebrow })) : null, (0, jsx_runtime_1.jsx)("h1", { className: 'text-2xl font-extrabold text-ink', children: title }), description ? ((0, jsx_runtime_1.jsx)("p", { className: 'max-w-3xl text-sm leading-6 text-ink/60', children: description })) : null] }), trailing] }));
6
6
  exports.PsSectionHeader = PsSectionHeader;
@@ -1,15 +1,19 @@
1
1
  import type { ElementType, ReactNode } from 'react';
2
2
  export interface PsSiteHeaderProps {
3
3
  centerContent?: ReactNode;
4
+ className?: string;
4
5
  homeHref: string;
5
6
  label: string;
6
7
  LinkComponent?: ElementType;
7
8
  logoAlt?: string;
8
9
  logoClassName?: string;
9
10
  logoSrc: string;
11
+ logoSrcDark?: string;
10
12
  logoSrcMobile?: string;
13
+ logoSrcMobileDark?: string;
14
+ navClassName?: string;
11
15
  onBack?: () => void;
12
16
  showLabel?: boolean;
13
17
  trailing?: ReactNode;
14
18
  }
15
- export declare const PsSiteHeader: ({ centerContent, homeHref, label, LinkComponent, logoAlt, logoClassName, logoSrc, logoSrcMobile, onBack, showLabel, trailing, }: PsSiteHeaderProps) => import("react").JSX.Element;
19
+ export declare const PsSiteHeader: ({ centerContent, className, homeHref, label, LinkComponent, logoAlt, logoClassName, logoSrc, logoSrcDark, logoSrcMobile, logoSrcMobileDark, navClassName, onBack, showLabel, trailing, }: PsSiteHeaderProps) => import("react").JSX.Element;
@@ -5,10 +5,11 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
6
6
  const ps_icons_1 = require("@pallasoft/ps-icons");
7
7
  const logoLinkClassName = 'flex shrink-0 items-center gap-2.5';
8
- const PsSiteHeader = ({ centerContent, homeHref, label, LinkComponent, logoAlt, logoClassName, logoSrc, logoSrcMobile, onBack, showLabel = false, trailing, }) => {
8
+ const PsSiteHeader = ({ centerContent, className, homeHref, label, LinkComponent, logoAlt, logoClassName, logoSrc, logoSrcDark, logoSrcMobile, logoSrcMobileDark, navClassName, onBack, showLabel = false, trailing, }) => {
9
9
  const logoLabel = `${label} home`;
10
- const logoContent = logoSrcMobile ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("img", { alt: logoAlt ?? label, className: ps_design_system_1.PallasoftClassNameService.merge('hidden h-8 w-auto sm:block', logoClassName), src: logoSrc }), (0, jsx_runtime_1.jsx)("img", { alt: logoAlt ?? label, className: ps_design_system_1.PallasoftClassNameService.merge('h-8 w-auto sm:hidden', logoClassName), src: logoSrcMobile }), showLabel ? (0, jsx_runtime_1.jsx)("span", { className: 'text-base font-bold text-ink', children: label }) : null] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("img", { alt: logoAlt ?? label, className: ps_design_system_1.PallasoftClassNameService.merge('h-8 w-auto', logoClassName), src: logoSrc }), showLabel ? (0, jsx_runtime_1.jsx)("span", { className: 'text-base font-bold text-ink', children: label }) : null] }));
10
+ const renderLogo = (src, darkSrc, baseClassName) => darkSrc ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("img", { alt: logoAlt !== null && logoAlt !== undefined ? logoAlt : label, className: ps_design_system_1.PallasoftClassNameService.merge(baseClassName, 'dark:hidden', logoClassName), src: src }), (0, jsx_runtime_1.jsx)("img", { alt: logoAlt !== null && logoAlt !== undefined ? logoAlt : label, className: ps_design_system_1.PallasoftClassNameService.merge(baseClassName, 'hidden dark:block', logoClassName), src: darkSrc })] })) : ((0, jsx_runtime_1.jsx)("img", { alt: logoAlt !== null && logoAlt !== undefined ? logoAlt : label, className: ps_design_system_1.PallasoftClassNameService.merge(baseClassName, logoClassName), src: src }));
11
+ const logoContent = logoSrcMobile ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("span", { className: 'hidden sm:contents', children: renderLogo(logoSrc, logoSrcDark, 'h-8 w-auto') }), (0, jsx_runtime_1.jsx)("span", { className: 'contents sm:hidden', children: renderLogo(logoSrcMobile, logoSrcMobileDark, 'h-8 w-auto') }), showLabel ? (0, jsx_runtime_1.jsx)("span", { className: 'text-base font-bold text-ink', children: label }) : null] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [renderLogo(logoSrc, logoSrcDark, 'h-8 w-auto'), showLabel ? (0, jsx_runtime_1.jsx)("span", { className: 'text-base font-bold text-ink', children: label }) : null] }));
11
12
  const logoLink = LinkComponent ? ((0, jsx_runtime_1.jsx)(LinkComponent, { "aria-label": logoLabel, className: logoLinkClassName, to: homeHref, children: logoContent })) : ((0, jsx_runtime_1.jsx)("a", { "aria-label": logoLabel, className: logoLinkClassName, href: homeHref, children: logoContent }));
12
- return ((0, jsx_runtime_1.jsx)("header", { className: 'fixed inset-x-0 top-0 z-header px-6 pt-4', children: (0, jsx_runtime_1.jsxs)("nav", { className: 'ps-glass mx-auto flex max-w-6xl items-center gap-3 rounded-ps-card px-5 py-3', children: [onBack ? ((0, jsx_runtime_1.jsx)("button", { "aria-label": 'Back', className: 'ps-focus-ring flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-ink md:hidden', onClick: onBack, type: 'button', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { iconKey: ps_icons_1.PsIconKey.ChevronRight, width: 20, height: 20, className: 'rotate-180' }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: onBack ? 'hidden md:flex' : 'contents', children: logoLink }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-w-0 flex-1 items-center justify-center gap-3', children: centerContent }), trailing ? (0, jsx_runtime_1.jsx)("div", { className: 'flex shrink-0 items-center gap-2.5', children: trailing }) : null] }) }));
13
+ return ((0, jsx_runtime_1.jsx)("header", { className: ps_design_system_1.PallasoftClassNameService.merge('fixed inset-x-0 top-0 z-header px-6 pt-4', className), children: (0, jsx_runtime_1.jsxs)("nav", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-glass mx-auto flex max-w-6xl items-center gap-3 rounded-ps-card px-5 py-3', navClassName), children: [onBack ? ((0, jsx_runtime_1.jsx)("button", { "aria-label": 'Back', className: 'ps-focus-ring flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-ink md:hidden', onClick: onBack, type: 'button', children: (0, jsx_runtime_1.jsx)(ps_icons_1.PsIcon, { iconKey: ps_icons_1.PsIconKey.ChevronRight, width: 20, height: 20, className: 'rotate-180' }) })) : null, (0, jsx_runtime_1.jsx)("div", { className: onBack ? 'hidden md:flex' : 'contents', children: logoLink }), (0, jsx_runtime_1.jsx)("div", { className: 'flex min-w-0 flex-1 items-center justify-center gap-3', children: centerContent }), trailing ? (0, jsx_runtime_1.jsx)("div", { className: 'flex shrink-0 items-center gap-2.5', children: trailing }) : null] }) }));
13
14
  };
14
15
  exports.PsSiteHeader = PsSiteHeader;
@@ -1,5 +1,5 @@
1
- import { PallasoftThemeMode } from '@pallasoft/ps-design-system';
2
1
  import type { ButtonHTMLAttributes } from 'react';
2
+ import { PallasoftThemeMode } from '@pallasoft/ps-design-system';
3
3
  export interface PsThemeToggleProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
4
4
  mode: PallasoftThemeMode;
5
5
  }
@@ -1,7 +1,2 @@
1
- export interface PsVoiceOrbProps {
2
- className?: string;
3
- level?: number;
4
- size?: number;
5
- variant?: 'default' | 'error';
6
- }
1
+ import { PsVoiceOrbProps } from '../models/ps-voice-orb';
7
2
  export declare const PsVoiceOrb: (props: PsVoiceOrbProps) => import("react").JSX.Element;
@@ -2,39 +2,103 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PsVoiceOrb = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
5
6
  const ps_design_system_1 = require("@pallasoft/ps-design-system");
7
+ const ps_voice_orb_1 = require("../constants/ps-voice-orb");
6
8
  const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
9
+ const approach = (current, target, factor) => current + (target - current) * factor;
10
+ const buildRimMask = (innerStopPercent) => `radial-gradient(circle, transparent ${innerStopPercent}%, black ${innerStopPercent + 6}%, black 92%, transparent 97%)`;
11
+ const initialRimMask = buildRimMask(ps_voice_orb_1.PsVoiceOrbConfig.rimInnerStopPercent);
12
+ const buildBlobRadius = (phase, depth) => {
13
+ const harmonic = ps_voice_orb_1.PsVoiceOrbConfig.blobSecondHarmonic;
14
+ const corner = (offset) => (50 +
15
+ Math.sin(phase + offset) * depth * 0.65 +
16
+ Math.sin(phase * harmonic + offset * 2) * depth * 0.35).toFixed(2);
17
+ return `${corner(0)}% ${corner(1.7)}% ${corner(3.4)}% ${corner(5.1)}% / ${corner(2.6)}% ${corner(4.3)}% ${corner(0.9)}% ${corner(6)}%`;
18
+ };
7
19
  const PsVoiceOrb = (props) => {
8
- const size = typeof props.size === 'number' ? props.size : 64;
20
+ const size = typeof props.size === 'number' ? props.size : ps_voice_orb_1.PsVoiceOrbConfig.defaultSize;
9
21
  const level = typeof props.level === 'number' ? clamp(props.level, 0, 1) : 0;
10
- const isError = props.variant === 'error';
11
- const glowGradient = isError
12
- ? 'radial-gradient(circle, rgb(var(--ps-color-danger-rgb) / 0.55), rgb(var(--ps-color-danger-rgb) / 0.2) 70%, transparent)'
13
- : 'radial-gradient(circle, rgb(var(--ps-color-primary-rgb) / 0.55), rgb(var(--ps-color-secondary-rgb) / 0.35) 70%, transparent)';
14
- const coreGradient = isError
15
- ? 'linear-gradient(135deg, rgb(var(--ps-color-danger-rgb)), rgb(var(--ps-color-danger-rgb) / 0.75))'
16
- : 'linear-gradient(135deg, rgb(var(--ps-color-primary-rgb)), rgb(var(--ps-color-primary-dark-rgb)))';
17
- return ((0, jsx_runtime_1.jsxs)("span", { className: ps_design_system_1.PallasoftClassNameService.merge('ps-voice-orb relative inline-flex shrink-0 items-center justify-center', props.className), style: { width: size, height: size }, children: [(0, jsx_runtime_1.jsx)("style", { children: `
18
- @keyframes ps-voice-orb-breathe {
19
- 0%, 100% { transform: scale(1); }
20
- 50% { transform: scale(1.06); }
21
- }
22
- @keyframes ps-voice-orb-drift {
23
- 0%, 100% { border-radius: 46% 54% 58% 42% / 44% 46% 54% 56%; }
24
- 50% { border-radius: 54% 46% 42% 58% / 56% 44% 56% 44%; }
25
- }
26
- .ps-voice-orb-glow {
27
- animation: ps-voice-orb-drift 9s ease-in-out infinite;
28
- }
29
- .ps-voice-orb-core {
30
- animation: ps-voice-orb-breathe 3.2s ease-in-out infinite;
31
- }
32
- ` }), (0, jsx_runtime_1.jsx)("span", { className: 'ps-voice-orb-glow absolute inset-[10%] blur-md transition-transform duration-150 ease-out', style: {
33
- background: glowGradient,
34
- transform: `scale(${1 + level * 0.55})`,
35
- } }), (0, jsx_runtime_1.jsx)("span", { className: 'ps-voice-orb-core absolute inset-[26%] flex items-center justify-center rounded-full shadow-lg transition-transform duration-150 ease-out', style: {
36
- background: coreGradient,
37
- transform: `scale(${1 + level * 0.28})`,
38
- }, children: (0, jsx_runtime_1.jsx)("span", { className: 'h-[28%] w-[28%] rounded-full bg-white/70' }) })] }));
22
+ const state = props.state ? props.state : props.variant === 'error' ? 'error' : 'idle';
23
+ const isError = state === 'error' || props.variant === 'error';
24
+ const cadence = ps_voice_orb_1.PsVoiceOrbCadenceByState[state];
25
+ const theme = ps_voice_orb_1.PsVoiceOrbThemes[isError ? 'error' : 'default'];
26
+ const containerRef = (0, react_1.useRef)(null);
27
+ const glowRef = (0, react_1.useRef)(null);
28
+ const glassRef = (0, react_1.useRef)(null);
29
+ const rimRef = (0, react_1.useRef)(null);
30
+ const coreRef = (0, react_1.useRef)(null);
31
+ const targetLevelRef = (0, react_1.useRef)(level);
32
+ const cadenceRef = (0, react_1.useRef)(cadence);
33
+ targetLevelRef.current = level;
34
+ cadenceRef.current = cadence;
35
+ (0, react_1.useEffect)(() => {
36
+ let frameId;
37
+ let lastFrameMs = performance.now();
38
+ let smoothedLevel = 0;
39
+ let spinDegrees = 0;
40
+ let wobblePhase = 0;
41
+ let pulsePhase = 0;
42
+ let orbitPhase = 0;
43
+ let smoothedPulse = 0;
44
+ let smoothedBlob = 0;
45
+ let smoothedOrbit = 0;
46
+ const tick = (now) => {
47
+ const deltaMs = Math.min(now - lastFrameMs, ps_voice_orb_1.PsVoiceOrbConfig.maxFrameMs);
48
+ const deltaS = deltaMs / 1000;
49
+ lastFrameMs = now;
50
+ const activeCadence = cadenceRef.current;
51
+ spinDegrees = (spinDegrees + (deltaS / activeCadence.spinS) * 360) % 360;
52
+ wobblePhase += deltaS * activeCadence.wobbleHz * Math.PI * 2;
53
+ pulsePhase += deltaS * activeCadence.pulseHz * Math.PI * 2;
54
+ orbitPhase += deltaS * ps_voice_orb_1.PsVoiceOrbConfig.coreOrbitHz * Math.PI * 2;
55
+ smoothedLevel = approach(smoothedLevel, targetLevelRef.current, ps_voice_orb_1.PsVoiceOrbConfig.levelSmoothing);
56
+ smoothedPulse = approach(smoothedPulse, activeCadence.pulseDepth, ps_voice_orb_1.PsVoiceOrbConfig.cadenceSmoothing);
57
+ smoothedBlob = approach(smoothedBlob, activeCadence.blobDepth, ps_voice_orb_1.PsVoiceOrbConfig.blobSmoothing);
58
+ smoothedOrbit = approach(smoothedOrbit, activeCadence.orbitDepth, ps_voice_orb_1.PsVoiceOrbConfig.blobSmoothing);
59
+ const voice = Math.pow(smoothedLevel, ps_voice_orb_1.PsVoiceOrbConfig.levelCurve);
60
+ const pulse = ((Math.sin(pulsePhase) + 1) / 2) * smoothedPulse;
61
+ const amplitude = clamp(Math.max(voice, pulse), 0, 1);
62
+ const blobRadius = buildBlobRadius(wobblePhase, smoothedBlob);
63
+ if (containerRef.current) {
64
+ containerRef.current.style.transform = `scale(${1 + amplitude * 0.3})`;
65
+ }
66
+ if (glowRef.current) {
67
+ glowRef.current.style.opacity = `${0.4 + amplitude * 0.55}`;
68
+ glowRef.current.style.transform = `scale(${1.05 + amplitude * 0.7})`;
69
+ glowRef.current.style.borderRadius = blobRadius;
70
+ }
71
+ if (glassRef.current) {
72
+ glassRef.current.style.borderRadius = blobRadius;
73
+ }
74
+ if (rimRef.current) {
75
+ const mask = buildRimMask(ps_voice_orb_1.PsVoiceOrbConfig.rimInnerStopPercent - amplitude * ps_voice_orb_1.PsVoiceOrbConfig.rimAmplitudeSpread);
76
+ rimRef.current.style.transform = `rotate(${spinDegrees}deg)`;
77
+ rimRef.current.style.maskImage = mask;
78
+ rimRef.current.style.webkitMaskImage = mask;
79
+ }
80
+ if (coreRef.current) {
81
+ const radius = ps_voice_orb_1.PsVoiceOrbConfig.coreOrbitRadiusPercent * smoothedOrbit;
82
+ const x = Math.cos(orbitPhase) * radius;
83
+ const y = Math.sin(orbitPhase * 1.3) * radius;
84
+ coreRef.current.style.opacity = `${smoothedOrbit * (0.55 + pulse * 0.45)}`;
85
+ coreRef.current.style.transform = `translate(calc(-50% + ${x.toFixed(2)}%), calc(-50% + ${y.toFixed(2)}%)) scale(${0.8 + pulse * 0.5})`;
86
+ }
87
+ frameId = requestAnimationFrame(tick);
88
+ };
89
+ frameId = requestAnimationFrame(tick);
90
+ return () => cancelAnimationFrame(frameId);
91
+ }, []);
92
+ return ((0, jsx_runtime_1.jsxs)("span", { ref: containerRef, className: ps_design_system_1.PallasoftClassNameService.merge('ps-voice-orb relative inline-flex shrink-0 items-center justify-center', props.className), style: { width: size, height: size }, children: [(0, jsx_runtime_1.jsx)("span", { ref: glowRef, "aria-hidden": 'true', className: 'absolute inset-0 rounded-full opacity-40 blur-xl', style: { background: theme.rim } }), (0, jsx_runtime_1.jsxs)("span", { ref: glassRef, "aria-hidden": 'true', className: 'absolute inset-0 overflow-hidden rounded-full', children: [(0, jsx_runtime_1.jsx)("span", { className: 'absolute inset-0', style: { background: theme.glass } }), (0, jsx_runtime_1.jsx)("span", { ref: rimRef, className: 'absolute inset-0', style: {
93
+ background: theme.rim,
94
+ maskImage: initialRimMask,
95
+ WebkitMaskImage: initialRimMask,
96
+ } }), (0, jsx_runtime_1.jsx)("span", { ref: coreRef, className: 'absolute left-1/2 top-1/2 rounded-full opacity-0 blur-sm', style: {
97
+ background: theme.core,
98
+ width: `${ps_voice_orb_1.PsVoiceOrbConfig.coreSizePercent}%`,
99
+ height: `${ps_voice_orb_1.PsVoiceOrbConfig.coreSizePercent}%`,
100
+ } })] }), cadence.isRippling
101
+ ? ps_voice_orb_1.PsVoiceOrbConfig.rippleDelaysMs.map((delayMs) => ((0, jsx_runtime_1.jsx)("span", { "aria-hidden": 'true', className: 'animate-ps-orb-ripple absolute inset-0 rounded-full border opacity-40', style: { borderColor: theme.ripple, animationDelay: `${delayMs}ms` } }, delayMs)))
102
+ : null] }));
39
103
  };
40
104
  exports.PsVoiceOrb = PsVoiceOrb;
@@ -0,0 +1,18 @@
1
+ import { PsVoiceOrbCadence, PsVoiceOrbState, PsVoiceOrbTheme } from '../models/ps-voice-orb';
2
+ export declare const PsVoiceOrbConfig: {
3
+ defaultSize: number;
4
+ levelSmoothing: number;
5
+ levelCurve: number;
6
+ cadenceSmoothing: number;
7
+ rimInnerStopPercent: number;
8
+ rimAmplitudeSpread: number;
9
+ maxFrameMs: number;
10
+ rippleDelaysMs: number[];
11
+ blobSmoothing: number;
12
+ blobSecondHarmonic: number;
13
+ coreSizePercent: number;
14
+ coreOrbitRadiusPercent: number;
15
+ coreOrbitHz: number;
16
+ };
17
+ export declare const PsVoiceOrbCadenceByState: Record<PsVoiceOrbState, PsVoiceOrbCadence>;
18
+ export declare const PsVoiceOrbThemes: Record<'default' | 'error', PsVoiceOrbTheme>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsVoiceOrbThemes = exports.PsVoiceOrbCadenceByState = exports.PsVoiceOrbConfig = void 0;
4
+ exports.PsVoiceOrbConfig = {
5
+ defaultSize: 64,
6
+ levelSmoothing: 0.18,
7
+ levelCurve: 0.55,
8
+ cadenceSmoothing: 0.06,
9
+ rimInnerStopPercent: 66,
10
+ rimAmplitudeSpread: 18,
11
+ maxFrameMs: 64,
12
+ rippleDelaysMs: [0, 500],
13
+ blobSmoothing: 0.05,
14
+ blobSecondHarmonic: 1.7,
15
+ coreSizePercent: 46,
16
+ coreOrbitRadiusPercent: 22,
17
+ coreOrbitHz: 0.8,
18
+ };
19
+ exports.PsVoiceOrbCadenceByState = {
20
+ idle: {
21
+ spinS: 16,
22
+ wobbleHz: 0.12,
23
+ pulseHz: 0.25,
24
+ pulseDepth: 0.08,
25
+ blobDepth: 3,
26
+ orbitDepth: 0,
27
+ isRippling: false,
28
+ },
29
+ connecting: {
30
+ spinS: 2.5,
31
+ wobbleHz: 0.5,
32
+ pulseHz: 1.4,
33
+ pulseDepth: 0.3,
34
+ blobDepth: 12,
35
+ orbitDepth: 1,
36
+ isRippling: false,
37
+ },
38
+ listening: {
39
+ spinS: 5,
40
+ wobbleHz: 0.4,
41
+ pulseHz: 0.7,
42
+ pulseDepth: 0.16,
43
+ blobDepth: 6,
44
+ orbitDepth: 0,
45
+ isRippling: true,
46
+ },
47
+ thinking: {
48
+ spinS: 1.6,
49
+ wobbleHz: 0.55,
50
+ pulseHz: 0.9,
51
+ pulseDepth: 0.4,
52
+ blobDepth: 16,
53
+ orbitDepth: 1,
54
+ isRippling: false,
55
+ },
56
+ speaking: {
57
+ spinS: 3,
58
+ wobbleHz: 0.7,
59
+ pulseHz: 1.3,
60
+ pulseDepth: 0.22,
61
+ blobDepth: 8,
62
+ orbitDepth: 0,
63
+ isRippling: true,
64
+ },
65
+ error: {
66
+ spinS: 14,
67
+ wobbleHz: 0.15,
68
+ pulseHz: 0.6,
69
+ pulseDepth: 0.14,
70
+ blobDepth: 3,
71
+ orbitDepth: 0,
72
+ isRippling: false,
73
+ },
74
+ };
75
+ exports.PsVoiceOrbThemes = {
76
+ default: {
77
+ glass: 'radial-gradient(circle at 32% 28%, rgb(255 255 255 / 0.16), transparent 42%), radial-gradient(circle, rgb(var(--ps-color-primary-rgb) / 0.2), rgb(var(--ps-color-ink-rgb) / 0.94) 75%)',
78
+ rim: 'conic-gradient(rgb(var(--ps-color-primary-rgb)), rgb(var(--ps-color-accent-rgb)), rgb(var(--ps-color-secondary-rgb)), rgb(var(--ps-color-primary-dark-rgb)), rgb(var(--ps-color-primary-rgb)))',
79
+ ripple: 'rgb(var(--ps-color-primary-rgb) / 0.5)',
80
+ core: 'radial-gradient(circle, rgb(255 255 255 / 0.9), rgb(var(--ps-color-accent-rgb) / 0.7) 35%, transparent 70%)',
81
+ },
82
+ error: {
83
+ glass: 'radial-gradient(circle at 32% 28%, rgb(255 255 255 / 0.16), transparent 42%), radial-gradient(circle, rgb(var(--ps-color-danger-rgb) / 0.22), rgb(var(--ps-color-ink-rgb) / 0.94) 75%)',
84
+ rim: 'conic-gradient(rgb(var(--ps-color-danger-rgb)), rgb(255 255 255 / 0.85), rgb(var(--ps-color-danger-rgb) / 0.7), rgb(var(--ps-color-danger-rgb)))',
85
+ ripple: 'rgb(var(--ps-color-danger-rgb) / 0.5)',
86
+ core: 'radial-gradient(circle, rgb(255 255 255 / 0.9), rgb(var(--ps-color-danger-rgb) / 0.7) 35%, transparent 70%)',
87
+ },
88
+ };
@@ -18,10 +18,12 @@ declare global {
18
18
  export type PsRecaptchaBadgePosition = 'bottomright' | 'bottomleft' | 'inline';
19
19
  export interface UsePsInvisibleRecaptchaOptions {
20
20
  siteKey: string;
21
+ /** reCAPTCHA api.js URL (without query), read from the consuming app's env. */
22
+ scriptUrl: string;
21
23
  badgePosition?: PsRecaptchaBadgePosition;
22
24
  }
23
25
  export interface UsePsInvisibleRecaptcha {
24
26
  execute: () => Promise<string>;
25
27
  isReady: boolean;
26
28
  }
27
- export declare const usePsInvisibleRecaptcha: ({ siteKey, badgePosition, }: UsePsInvisibleRecaptchaOptions) => UsePsInvisibleRecaptcha;
29
+ export declare const usePsInvisibleRecaptcha: ({ siteKey, scriptUrl, badgePosition, }: UsePsInvisibleRecaptchaOptions) => UsePsInvisibleRecaptcha;
@@ -2,13 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.usePsInvisibleRecaptcha = void 0;
4
4
  const react_1 = require("react");
5
- const SCRIPT_SRC = 'https://www.google.com/recaptcha/api.js?render=explicit';
6
5
  let scriptLoadPromise = null;
7
- const loadRecaptchaScript = () => {
6
+ const explicitRenderQuery = 'render=explicit';
7
+ const loadRecaptchaScript = (scriptUrl) => {
8
8
  if (scriptLoadPromise)
9
9
  return scriptLoadPromise;
10
+ const scriptSrc = `${scriptUrl}?${explicitRenderQuery}`;
10
11
  scriptLoadPromise = new Promise((resolve, reject) => {
11
- const existing = document.querySelector(`script[src="${SCRIPT_SRC}"]`);
12
+ const existing = document.querySelector(`script[src="${scriptSrc}"]`);
12
13
  if (existing) {
13
14
  existing.addEventListener('load', () => resolve());
14
15
  existing.addEventListener('error', () => reject(new Error('Failed to load reCAPTCHA')));
@@ -17,7 +18,7 @@ const loadRecaptchaScript = () => {
17
18
  return;
18
19
  }
19
20
  const script = document.createElement('script');
20
- script.src = SCRIPT_SRC;
21
+ script.src = scriptSrc;
21
22
  script.async = true;
22
23
  script.defer = true;
23
24
  script.onload = () => resolve();
@@ -26,7 +27,7 @@ const loadRecaptchaScript = () => {
26
27
  });
27
28
  return scriptLoadPromise;
28
29
  };
29
- const usePsInvisibleRecaptcha = ({ siteKey, badgePosition = 'bottomright', }) => {
30
+ const usePsInvisibleRecaptcha = ({ siteKey, scriptUrl, badgePosition = 'bottomright', }) => {
30
31
  const [isReady, setIsReady] = (0, react_1.useState)(false);
31
32
  const widgetIdRef = (0, react_1.useRef)(null);
32
33
  const containerRef = (0, react_1.useRef)(null);
@@ -37,7 +38,7 @@ const usePsInvisibleRecaptcha = ({ siteKey, badgePosition = 'bottomright', }) =>
37
38
  container.style.display = 'none';
38
39
  document.body.appendChild(container);
39
40
  containerRef.current = container;
40
- loadRecaptchaScript()
41
+ loadRecaptchaScript(scriptUrl)
41
42
  .then(() => {
42
43
  if (!isMounted || !window.grecaptcha)
43
44
  return;
@@ -73,7 +74,7 @@ const usePsInvisibleRecaptcha = ({ siteKey, badgePosition = 'bottomright', }) =>
73
74
  container.remove();
74
75
  containerRef.current = null;
75
76
  };
76
- }, [siteKey, badgePosition]);
77
+ }, [siteKey, scriptUrl, badgePosition]);
77
78
  const execute = (0, react_1.useCallback)(() => {
78
79
  return new Promise((resolve, reject) => {
79
80
  if (!window.grecaptcha || widgetIdRef.current === null) {
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createPsRouteLoaderDataHook = void 0;
4
- const react_1 = require("@remix-run/react");
5
4
  const createPsRouteLoaderDataHook = (routeId) => {
6
5
  let cache;
7
6
  return () => {
8
- const data = (0, react_1.useRouteLoaderData)(routeId);
7
+ const { useRouteLoaderData } = require('@remix-run/react');
8
+ const data = useRouteLoaderData(routeId);
9
9
  if (data !== undefined)
10
10
  cache = data;
11
11
  if (cache === undefined)
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from './components/ps-metric-card';
13
13
  export * from './components/ps-pill';
14
14
  export * from './components/ps-portable-text';
15
15
  export * from './components/ps-reflective-app-shell';
16
+ export * from './components/ps-navigation-progress';
16
17
  export * from './components/ps-section-header';
17
18
  export * from './components/ps-site-header';
18
19
  export * from './components/ps-skeleton';
@@ -26,3 +27,4 @@ export * from './hooks/use-ps-invisible-recaptcha';
26
27
  export * from './hooks/use-ps-route-loader-data';
27
28
  export * from './hooks/use-ps-viewport';
28
29
  export * from './interfaces/ps-component-props';
30
+ export * from './models/ps-voice-orb';
package/dist/index.js CHANGED
@@ -29,6 +29,7 @@ __exportStar(require("./components/ps-metric-card"), exports);
29
29
  __exportStar(require("./components/ps-pill"), exports);
30
30
  __exportStar(require("./components/ps-portable-text"), exports);
31
31
  __exportStar(require("./components/ps-reflective-app-shell"), exports);
32
+ __exportStar(require("./components/ps-navigation-progress"), exports);
32
33
  __exportStar(require("./components/ps-section-header"), exports);
33
34
  __exportStar(require("./components/ps-site-header"), exports);
34
35
  __exportStar(require("./components/ps-skeleton"), exports);
@@ -42,3 +43,4 @@ __exportStar(require("./hooks/use-ps-invisible-recaptcha"), exports);
42
43
  __exportStar(require("./hooks/use-ps-route-loader-data"), exports);
43
44
  __exportStar(require("./hooks/use-ps-viewport"), exports);
44
45
  __exportStar(require("./interfaces/ps-component-props"), exports);
46
+ __exportStar(require("./models/ps-voice-orb"), exports);
@@ -0,0 +1,23 @@
1
+ export type PsVoiceOrbState = 'idle' | 'connecting' | 'listening' | 'thinking' | 'speaking' | 'error';
2
+ export interface PsVoiceOrbProps {
3
+ className?: string;
4
+ level?: number;
5
+ size?: number;
6
+ state?: PsVoiceOrbState;
7
+ variant?: 'default' | 'error';
8
+ }
9
+ export interface PsVoiceOrbCadence {
10
+ spinS: number;
11
+ wobbleHz: number;
12
+ pulseHz: number;
13
+ pulseDepth: number;
14
+ blobDepth: number;
15
+ orbitDepth: number;
16
+ isRippling: boolean;
17
+ }
18
+ export interface PsVoiceOrbTheme {
19
+ glass: string;
20
+ rim: string;
21
+ ripple: string;
22
+ core: string;
23
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-core-components",
3
- "version": "1.4.0",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -33,10 +33,10 @@
33
33
  "@portabletext/react": "^3.2.4",
34
34
  "@portabletext/types": "^2.0.13",
35
35
  "@radix-ui/react-accordion": "^1.2.12",
36
- "react-infinite-scroll-component": "^6.1.1"
36
+ "react-infinite-scroll-component": "^6.1.1",
37
+ "react-top-loading-bar": "^3.0.2"
37
38
  },
38
39
  "peerDependencies": {
39
- "@remix-run/react": "^2.0.0",
40
40
  "react": "^18 || ^19"
41
41
  },
42
42
  "devDependencies": {
@@ -62,6 +62,8 @@
62
62
  "husky": "^9.1.7",
63
63
  "lint-staged": "^16.2.7",
64
64
  "prettier": "^3.6.2",
65
+ "react": "^19.3.0",
66
+ "react-dom": "^19.3.0",
65
67
  "semantic-release": "^24.2.0",
66
68
  "ts-node": "^10.9.2",
67
69
  "typescript": "^5.9.3"