@fayz-ai/admin 0.11.0 → 0.12.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 (40) hide show
  1. package/dist/{CommandPalette-2AL327XQ.js → CommandPalette-U7MMA2WH.js} +27 -4
  2. package/dist/CommandPalette-U7MMA2WH.js.map +1 -0
  3. package/dist/app/AdminShell.d.ts.map +1 -1
  4. package/dist/app/admin-app.d.ts.map +1 -1
  5. package/dist/app/config.d.ts +7 -0
  6. package/dist/app/config.d.ts.map +1 -1
  7. package/dist/components/chat/AssistantInvite.d.ts +10 -0
  8. package/dist/components/chat/AssistantInvite.d.ts.map +1 -1
  9. package/dist/components/chat/ChatFab.d.ts.map +1 -1
  10. package/dist/components/chat/ConfirmActionCard.d.ts.map +1 -1
  11. package/dist/components/chat/fab-anchor.d.ts +15 -0
  12. package/dist/components/chat/fab-anchor.d.ts.map +1 -0
  13. package/dist/components/connect/connector-map.generated.d.ts.map +1 -1
  14. package/dist/components/layout/CommandPalette.d.ts +5 -2
  15. package/dist/components/layout/CommandPalette.d.ts.map +1 -1
  16. package/dist/components/shared/PersonLink.d.ts +8 -1
  17. package/dist/components/shared/PersonLink.d.ts.map +1 -1
  18. package/dist/components/shared/RecordPickerModal.d.ts +7 -1
  19. package/dist/components/shared/RecordPickerModal.d.ts.map +1 -1
  20. package/dist/crud/CrudListView.d.ts +8 -3
  21. package/dist/crud/CrudListView.d.ts.map +1 -1
  22. package/dist/crud/CrudPage.d.ts.map +1 -1
  23. package/dist/crud/archetype-lookup.d.ts +4 -0
  24. package/dist/crud/archetype-lookup.d.ts.map +1 -1
  25. package/dist/hooks/useAuth.d.ts.map +1 -1
  26. package/dist/hooks/useGlobalSearch.d.ts +3 -1
  27. package/dist/hooks/useGlobalSearch.d.ts.map +1 -1
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +837 -558
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/assistant-invite.d.ts +18 -0
  33. package/dist/lib/assistant-invite.d.ts.map +1 -1
  34. package/dist/lib/cache.d.ts +2 -17
  35. package/dist/lib/cache.d.ts.map +1 -1
  36. package/dist/stores/createCrudStore.d.ts +8 -0
  37. package/dist/stores/createCrudStore.d.ts.map +1 -1
  38. package/dist/theme/utils.d.ts.map +1 -1
  39. package/package.json +6 -6
  40. package/dist/CommandPalette-2AL327XQ.js.map +0 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ export { getScheduleBlockConfig, setScheduleBlockConfig, subscribeScheduleBlockC
2
2
  import { getFayzSupabaseClientOptional, createFayzSupabaseClient, createSupabaseOrgAdapter, createMockOrgAdapter, getFayzSupabaseClient } from './chunk-OI7RYSTM.js';
3
3
  export { createFayzSupabaseClient, createMockOrgAdapter, createSupabaseOrgAdapter, getFayzSupabaseClient, getFayzSupabaseClientOptional } from './chunk-OI7RYSTM.js';
4
4
  export { ImportWizard } from './chunk-UMRN6427.js';
5
- import { useLimitGuard, invalidateLimit, useLimit, useUpgradeModalStore, checkAccess, guardLimit, cn as cn$1, featureDisplayName, PlanFeatureItem, useAccessOptional, AccessProvider, CORE_LIMIT_DECLARATIONS, entityDerivedLimitDeclarations, getPlanEntitlements, resolvePlanCurrency } from './chunk-5O2UJAQ7.js';
5
+ import { useLimitGuard, invalidateLimit, useLimit, useUpgradeModalStore, cn as cn$1, checkAccess, guardLimit, featureDisplayName, PlanFeatureItem, useAccessOptional, AccessProvider, CORE_LIMIT_DECLARATIONS, entityDerivedLimitDeclarations, getPlanEntitlements, resolvePlanCurrency } from './chunk-5O2UJAQ7.js';
6
6
  export { AccessProvider, UPGRADE_URL, agentDenial, checkAccess, getAccessSnapshot, guardLimit, invalidateLimit, isEntitledByPlan, resolveAccess, useAccess, useAccessOptional, useLimit, useLimitGuard, useModuleNavAccess, useUpgradeModalStore } from './chunk-5O2UJAQ7.js';
7
7
  import { useTranslation as useTranslation$1 } from './chunk-YUHCC7JY.js';
8
8
  import { useBillingStore, resolvePlanBadge } from './chunk-VN5NTK26.js';
@@ -13,21 +13,21 @@ import { usePermissionOptional, usePermissionsStore, PermissionsProvider } from
13
13
  export { PermissionsProvider, useHasPermission, usePermission, usePermissionOptional, usePermissions, usePermissionsStore } from './chunk-VTEP2AQX.js';
14
14
  import * as React63 from 'react';
15
15
  import React63__default, { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
16
- import { hashRouterAdapter, getSupabaseClientOptional, deriveEntityKey, registerEntity, getActiveTenantId, resolveDataProvider, bulkRemove, emitDomainEvent, entityEventKey, useTranslation, useDataChanged, exportCSV, formatDateTime as formatDateTime$1, formatDate as formatDate$1, formatCurrency as formatCurrency$1, emitDataChanged, bulkCreate, matchesRange, isConnectorCategory, reportAppError, usePluginRuntimeOptional, getWidgetsForZone, resolvePluginComponent, setCurrentLocale, resolvePluginRuntime, PluginRuntimeProvider, I18nProvider, getAllEntities, defineApp, registerScaffold, getCountry, DEFAULT_COUNTRY, useActiveTenantId, maskPhone, useConnectorsForPlugin, renderApp as renderApp$1, unmaskPhone, parseRangeFilter, isFilterObject, connectionKey, useManifest, usePluginRuntime, installGlobalErrorHandlers, mergeTranslations, coreTranslations, createMockProvider, listConnections, connectorPlanes, connectorPermissions, connectorEntities, preferredPlane, connectorDirection, searchEverything, registerEventSink, getComponent, getPluginFactory, connectorSettings, groupedPermissions, invocationPlane, missingRequiredSettings, isSettingSet, connectorSubscribesTo, eventKeyMatches } from '@fayz-ai/core';
16
+ import { hashRouterAdapter, getSupabaseClientOptional, deriveEntityKey, registerEntity, getActiveTenantId, resolveDataProvider, bulkRemove, emitDomainEvent, entityEventKey, useTranslation, useDataChanged, emitDataChanged, exportCSV, formatDateTime as formatDateTime$1, formatDate as formatDate$1, formatCurrency as formatCurrency$1, bulkCreate, matchesRange, isConnectorCategory, reportAppError, usePluginRuntimeOptional, getWidgetsForZone, resolvePluginComponent, setCurrentLocale, resolvePluginRuntime, PluginRuntimeProvider, I18nProvider, getAllEntities, defineApp, registerScaffold, getCountry, DEFAULT_COUNTRY, useActiveTenantId, maskPhone, useConnectorsForPlugin, renderApp as renderApp$1, unmaskPhone, parseRangeFilter, isFilterObject, connectionKey, useManifest, usePluginRuntime, installGlobalErrorHandlers, mergeTranslations, coreTranslations, attachPersistentCache, getPersistentCache, createMockProvider, listConnections, connectorPlanes, connectorPermissions, connectorEntities, preferredPlane, connectorDirection, searchEverything, registerEventSink, getComponent, getPluginFactory, connectorSettings, groupedPermissions, invocationPlane, missingRequiredSettings, isSettingSet, connectorSubscribesTo, eventKeyMatches } from '@fayz-ai/core';
17
17
  export { ConnectorRuntimeTokenUnavailableError, createSafeDataProvider, formatDate, formatDateTime, getActiveLocale, getActiveTenantId, getConnectorRuntimeTokenSource, getCurrentLocale, getSupabaseClientOptional, preloadLocale, resolveBackendEnv, setConnectorRuntimeTokenSource, setCurrentLocale, tl, useActiveTenantId } from '@fayz-ai/core';
18
18
  import { usePasswordSetupGate, SetPasswordPage, AuthGate, AuthProvider, createAuthRuntime } from '@fayz-ai/plugin-auth';
19
19
  export { LoginPage } from '@fayz-ai/plugin-auth';
20
- import { toast, usePageTrailParent, navigateToCrumb, Button, PageHeader, ConfirmDialog, FilterChip, Dropdown, DropdownTrigger, Hint, cn, DropdownContent, DropdownLabel, DropdownItem, DropdownSeparator, HeaderTabs, StatTiles, BulkActionBar, Card, DataTable, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalFooter, Badge, getLucideIcon, ICON_MAP, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, useLayoutStore, Skeleton, Input, useSaveBar, useBackHandler, SubpageHeader, Separator, Tabs, TabsList, TabsTrigger, TabsContent, CardContent, ToastProvider, NavTransitionProvider, ListView, Thumbnail, SearchSelect, CurrencyInput, SearchField, ChevronPager, useModuleLayout, Popover, PopoverTrigger, PopoverContent, PhoneLink, normalizePhone, ModalPortal, ModalOverlay, ModalBody, SearchCombobox, usePageTrail, AppShellSkeleton, ModuleLayoutProvider, AppShell, PageTransition, createFayzTheme, Checkbox, MarkdownEditor, Switch, Markdown, PageTrail, Avatar, AvatarImage, AvatarFallback, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, CardTitle, CardDescription, DropdownSub, DropdownSubTrigger, DropdownSubContent, DropdownRadioGroup, DropdownRadioItem, CardHeader, CardFooter } from '@fayz-ai/ui';
20
+ import { toast, usePageTrailParent, navigateToCrumb, Button, PageHeader, ConfirmDialog, FilterChip, Dropdown, DropdownTrigger, Hint, cn, DropdownContent, DropdownLabel, DropdownItem, DropdownSeparator, RefreshButton, HeaderTabs, StatTiles, BulkActionBar, Card, DataTable, Modal, ModalContent, ModalHeader, ModalTitle, ModalDescription, ModalFooter, Badge, getLucideIcon, ICON_MAP, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, useLayoutStore, Skeleton, Input, useSaveBar, useBackHandler, SubpageHeader, Separator, Tabs, TabsList, TabsTrigger, TabsContent, CardContent, ToastProvider, NavTransitionProvider, ListView, Thumbnail, SearchSelect, CurrencyInput, SearchField, ChevronPager, useModuleLayout, Popover, PopoverTrigger, PopoverContent, PhoneLink, normalizePhone, ModalPortal, ModalOverlay, ModalBody, SearchCombobox, usePageTrail, AppShellSkeleton, ModuleLayoutProvider, AppShell, PageTransition, createFayzTheme, Checkbox, MarkdownEditor, Switch, Markdown, PageTrail, Avatar, AvatarImage, AvatarFallback, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, CardTitle, CardDescription, DropdownSub, DropdownSubTrigger, DropdownSubContent, DropdownRadioGroup, DropdownRadioItem, CardHeader, CardFooter } from '@fayz-ai/ui';
21
21
  export { createFayzTheme, fayzThemePresets } from '@fayz-ai/ui';
22
22
  import * as LucideIcons3 from 'lucide-react';
23
- import { Globe, Store, LayoutTemplate, Trash2, Upload, Search, ArrowDown, ArrowUp, ArrowUpDown, Check, RefreshCw, Download, ChevronLeft, ChevronRight, AlertTriangle, Pencil, Inbox, Package, X, Sparkles, Plug, Loader2, ArrowDownToLine, ArrowUpFromLine, CheckCircle2, Crown, ArrowRight, ArrowLeft, SquarePen, Settings2, MessageSquare, RotateCcw, Copy, ChevronsUpDown, ExternalLink, PenSquare, Plus, XCircle, Clock, Star, Edit, Eye, ShoppingBag, ChevronDown, Settings, Mail, MessageCircle, Cake, Calendar, TableProperties, Mic, BookOpen, Paperclip, ShieldCheck, Infinity, Play, Square, StickyNote, ArrowUpRight, Pause, CalendarDays, CircleCheckBig, UserX, Ban, CircleAlert, CircleEllipsis, FileText, CircleDashed, Power, File, FileImage, FileSpreadsheet, AlertCircle, CreditCard, Users, MapPin, SlidersHorizontal, Puzzle, Building2, User, Shield, Palette, ShieldAlert, UserCog, LogOut, Bell, PinOff, Pin, ImagePlus, Database, Radio, Zap, Save, UserPlus, MoreHorizontal, Sun, Moon, Monitor, Languages, CheckCheck, Info, CheckSquare2, Camera, KeyRound, Bug, Wrench, Heart, Briefcase, BarChart3, Tag, Truck } from 'lucide-react';
23
+ import { Globe, Store, LayoutTemplate, Trash2, Upload, Search, ArrowDown, ArrowUp, ArrowUpDown, Check, Download, ChevronLeft, ChevronRight, AlertTriangle, Pencil, Inbox, Package, X, Sparkles, Plug, Loader2, ArrowDownToLine, ArrowUpFromLine, CheckCircle2, Crown, ArrowRight, ArrowLeft, SquarePen, Settings2, MessageSquare, RotateCcw, Copy, ChevronsUpDown, ExternalLink, PenSquare, RefreshCw, Plus, XCircle, Clock, Star, Edit, Eye, ShoppingBag, ChevronDown, Settings, Mail, MessageCircle, Cake, Calendar, TableProperties, Mic, BookOpen, Paperclip, ShieldCheck, Infinity, Play, Square, StickyNote, ArrowUpRight, Pause, CalendarDays, CircleCheckBig, UserX, Ban, CircleAlert, CircleEllipsis, FileText, CircleDashed, Power, File, FileImage, FileSpreadsheet, AlertCircle, CreditCard, Users, MapPin, SlidersHorizontal, Puzzle, Building2, User, Shield, Palette, ShieldAlert, UserCog, LogOut, Bell, PinOff, Pin, ImagePlus, Database, Radio, Zap, Save, UserPlus, MoreHorizontal, Sun, Moon, Monitor, Languages, CheckCheck, Info, CheckSquare2, Camera, KeyRound, Bug, Wrench, Heart, Briefcase, BarChart3, Tag, Truck } from 'lucide-react';
24
24
  import { create, useStore } from 'zustand';
25
25
  import { useAuthStore, useAuth } from '@fayz-ai/auth';
26
26
  export { useAuth } from '@fayz-ai/auth';
27
+ import { createPortal } from 'react-dom';
28
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
27
29
  import { profileHasSystemPermission, mergeLimitDeclarations } from '@fayz-ai/core/access';
28
30
  import { createFayzAgentClient } from '@fayz-ai/sdk/agent';
29
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
30
- import { createPortal } from 'react-dom';
31
31
  import { getConnectorCredentialSink, ConnectorOAuthStarterMissingError } from '@fayz-ai/sdk';
32
32
  export { ConnectorCredentialSinkError, ConnectorOAuthError, ConnectorOAuthStarterMissingError, createFayzConnectorCredentialSink, createFayzConnectorOAuthStarter, fayz, fayzConnectorOAuth, getConnectorCredentialSink, getConnectorOAuthStarter, setConnectorCredentialSink, setConnectorOAuthStarter, startConnectorOAuth } from '@fayz-ai/sdk';
33
33
  import { createFayzEventsClient } from '@fayz-ai/sdk/events';
@@ -997,15 +997,29 @@ function rememberDismissal(id) {
997
997
  var useAssistantInviteStore = create((set, get) => ({
998
998
  invite: null,
999
999
  stage: "dock",
1000
+ dockBox: null,
1001
+ leaving: null,
1000
1002
  setInvite: (invite, id) => {
1001
1003
  if (!invite) {
1002
1004
  if (id && get().invite?.id !== id) return;
1003
- set({ invite: null, stage: "dock" });
1005
+ const { invite: going, dockBox } = get();
1006
+ set({
1007
+ invite: null,
1008
+ stage: "dock",
1009
+ dockBox: null,
1010
+ leaving: going && dockBox ? { invite: going, box: dockBox } : null
1011
+ });
1004
1012
  return;
1005
1013
  }
1006
1014
  const hero = invite.auto !== false && !dismissedIds().includes(invite.id);
1007
- set({ invite, stage: hero ? "hero" : "dock" });
1015
+ set({ invite, stage: hero ? "hero" : "dock", leaving: null });
1016
+ },
1017
+ setDockBox: (box) => {
1018
+ const current = get().dockBox;
1019
+ if (current?.left === box?.left && current?.width === box?.width) return;
1020
+ set({ dockBox: box });
1008
1021
  },
1022
+ endLeave: () => set({ leaving: null }),
1009
1023
  dismiss: () => {
1010
1024
  const { invite } = get();
1011
1025
  if (invite) rememberDismissal(invite.id);
@@ -1013,73 +1027,461 @@ var useAssistantInviteStore = create((set, get) => ({
1013
1027
  }
1014
1028
  }));
1015
1029
 
1016
- // src/app/core-entities.ts
1017
- var addressEntity = {
1018
- name: "Address",
1019
- namePlural: "Addresses",
1020
- icon: "MapPin",
1021
- data: {
1022
- table: "addresses",
1023
- tenantScoped: true,
1024
- tenantIdColumn: "tenant_id",
1025
- searchColumns: ["label", "street", "district", "city", "postal_code", "recipient"]
1026
- },
1027
- fields: [
1028
- // owner_id/owner_type are how an address attaches to its person. They are
1029
- // required and described rather than hidden: the agent reaches this table
1030
- // from a record it already has in hand ("adiciona o endereço X pra ele"),
1031
- // so the id it must pass is the one it just searched for or created.
1032
- {
1033
- key: "owner_id",
1034
- label: "Owner record id",
1035
- type: "text",
1036
- required: true,
1037
- hint: "Id of the person (client, contact, supplier, staff\u2026) this address belongs to."
1030
+ // src/components/chat/fab-anchor.ts
1031
+ var FAB_MARGIN_RIGHT = 21;
1032
+ var FAB_SIZE = 48;
1033
+ var DOCK_MAX_WIDTH = 640;
1034
+ var MORPH_MS = 380;
1035
+ var MORPH_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
1036
+ function fabRightEdge(railOpen, railWidth) {
1037
+ if (typeof window === "undefined") return 0;
1038
+ return window.innerWidth - (railOpen ? railWidth + FAB_MARGIN_RIGHT : FAB_MARGIN_RIGHT);
1039
+ }
1040
+ function AssistantInvite({ className, ...def }) {
1041
+ const setInvite = useAssistantInviteStore((s) => s.setInvite);
1042
+ const stage = useAssistantInviteStore((s) => s.stage);
1043
+ const dismiss = useAssistantInviteStore((s) => s.dismiss);
1044
+ const railOpen = useRightRailStore((s) => s.open);
1045
+ const hasAssistant = useRightRailStore((s) => s.panels.some((p) => p.id === "chat"));
1046
+ const { id, persona, headline, pitch, description, placeholder, auto } = def;
1047
+ const suggestions = def.suggestions;
1048
+ const suggestionKey = JSON.stringify(suggestions ?? []);
1049
+ React63.useEffect(() => {
1050
+ if (!hasAssistant) return;
1051
+ setInvite({
1052
+ id,
1053
+ persona,
1054
+ headline,
1055
+ pitch,
1056
+ description,
1057
+ placeholder,
1058
+ auto,
1059
+ suggestions: JSON.parse(suggestionKey)
1060
+ });
1061
+ return () => setInvite(null, id);
1062
+ }, [hasAssistant, id, persona, headline, pitch, description, placeholder, auto, suggestionKey, setInvite]);
1063
+ const anchorRef = React63.useRef(null);
1064
+ const [box, setBox] = React63.useState(null);
1065
+ const docked = stage === "dock" && !railOpen && hasAssistant;
1066
+ const showing = hasAssistant && (stage === "hero" || docked);
1067
+ React63.useEffect(() => {
1068
+ if (!showing) return;
1069
+ const el = anchorRef.current;
1070
+ if (!el) return;
1071
+ const measure = () => {
1072
+ const rect = el.getBoundingClientRect();
1073
+ setBox({ left: rect.left, width: rect.width, top: rect.top });
1074
+ };
1075
+ measure();
1076
+ const observer = new ResizeObserver(measure);
1077
+ observer.observe(el);
1078
+ window.addEventListener("resize", measure);
1079
+ window.addEventListener("scroll", measure, true);
1080
+ return () => {
1081
+ observer.disconnect();
1082
+ window.removeEventListener("resize", measure);
1083
+ window.removeEventListener("scroll", measure, true);
1084
+ };
1085
+ }, [showing]);
1086
+ const setDockBox = useAssistantInviteStore((s) => s.setDockBox);
1087
+ const wide = useWideViewport();
1088
+ React63.useEffect(() => {
1089
+ if (!docked || !wide || !box) {
1090
+ setDockBox(null);
1091
+ return;
1092
+ }
1093
+ setDockBox({ left: box.left + box.width / 2, width: Math.min(box.width, DOCK_MAX_WIDTH) });
1094
+ }, [docked, wide, box?.left, box?.width, setDockBox]);
1095
+ if (!hasAssistant) return null;
1096
+ const floating = box && stage === "hero" ? /* @__PURE__ */ jsx(InviteHero, { def, box, onDismiss: dismiss }) : box && docked ? /* @__PURE__ */ jsx(InviteDock, { def, left: box.left + box.width / 2, maxWidth: box.width }) : null;
1097
+ return /* @__PURE__ */ jsx("div", { ref: anchorRef, className: cn$1("h-0 w-full", className), "aria-hidden": !showing, children: floating && typeof document !== "undefined" ? createPortal(floating, document.body) : null });
1098
+ }
1099
+ function useAsk() {
1100
+ const queuePrompt = useChatStore((s) => s.queuePrompt);
1101
+ const dismiss = useAssistantInviteStore((s) => s.dismiss);
1102
+ return React63.useCallback(
1103
+ (text) => {
1104
+ const trimmed = text.trim();
1105
+ if (!trimmed) return;
1106
+ dismiss();
1107
+ queuePrompt(trimmed);
1038
1108
  },
1109
+ [queuePrompt, dismiss]
1110
+ );
1111
+ }
1112
+ function InviteHero({
1113
+ def,
1114
+ box,
1115
+ onDismiss
1116
+ }) {
1117
+ const { t } = useTranslation$1();
1118
+ const user = useAuthStore((s) => s.user);
1119
+ const ask = useAsk();
1120
+ const [draft, setDraft] = React63.useState("");
1121
+ const name = (user?.fullName || user?.email || "").split(/[\s@]/)[0];
1122
+ React63.useEffect(() => {
1123
+ const onKey = (event) => {
1124
+ if (event.key === "Escape") onDismiss();
1125
+ };
1126
+ window.addEventListener("keydown", onKey);
1127
+ return () => window.removeEventListener("keydown", onKey);
1128
+ }, [onDismiss]);
1129
+ return /* @__PURE__ */ jsxs(
1130
+ "section",
1039
1131
  {
1040
- key: "owner_type",
1041
- label: "Owner kind",
1042
- type: "select",
1043
- options: ["person", "location", "tenant"],
1044
- hint: "Defaults to 'person'. Only set it for an address that belongs to a business location or to the tenant itself."
1132
+ className: cn$1(
1133
+ "fixed z-30 flex flex-col items-center justify-center overflow-y-auto px-4 pb-14 text-center",
1134
+ "bg-background/95 backdrop-blur-sm motion-safe:animate-in motion-safe:fade-in-0"
1135
+ ),
1136
+ style: { left: box.left, width: box.width, top: box.top, bottom: 0 },
1137
+ "aria-label": def.persona ?? t("chat.defaultTitle"),
1138
+ children: [
1139
+ /* @__PURE__ */ jsx(
1140
+ "button",
1141
+ {
1142
+ type: "button",
1143
+ onClick: onDismiss,
1144
+ "aria-label": t("assistant.invite.dismiss"),
1145
+ title: t("assistant.invite.dismiss"),
1146
+ className: "absolute right-1 top-2 inline-flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground/60 transition-colors hover:bg-muted hover:text-foreground",
1147
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1148
+ }
1149
+ ),
1150
+ /* @__PURE__ */ jsx("h2", { className: "max-w-2xl text-balance text-[26px] font-semibold leading-tight tracking-tight text-foreground sm:text-[32px]", children: def.headline.replace("{{name}}", name) }),
1151
+ /* @__PURE__ */ jsx(
1152
+ InviteComposer,
1153
+ {
1154
+ className: "mt-7 w-full max-w-3xl",
1155
+ placeholder: def.placeholder ?? t("assistant.invite.placeholder"),
1156
+ value: draft,
1157
+ onChange: setDraft,
1158
+ onSubmit: () => {
1159
+ ask(draft);
1160
+ setDraft("");
1161
+ },
1162
+ size: "lg"
1163
+ }
1164
+ ),
1165
+ def.suggestions && def.suggestions.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: def.suggestions.slice(0, 4).map((suggestion) => /* @__PURE__ */ jsx(
1166
+ "button",
1167
+ {
1168
+ type: "button",
1169
+ onClick: () => ask(suggestion.prompt ?? suggestion.label),
1170
+ className: cn$1(
1171
+ "rounded-full border border-border/70 bg-card px-4 py-2 text-[13px] text-foreground/80 transition-colors",
1172
+ "hover:border-border hover:bg-muted hover:text-foreground",
1173
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1174
+ ),
1175
+ children: suggestion.label
1176
+ },
1177
+ suggestion.label
1178
+ )) })
1179
+ ]
1180
+ }
1181
+ );
1182
+ }
1183
+ function useMorphIn(skip = false) {
1184
+ const [entered, setEntered] = React63.useState(() => skip || prefersReducedMotion());
1185
+ React63.useEffect(() => {
1186
+ if (entered) return;
1187
+ let second = 0;
1188
+ const first = requestAnimationFrame(() => {
1189
+ second = requestAnimationFrame(() => setEntered(true));
1190
+ });
1191
+ return () => {
1192
+ cancelAnimationFrame(first);
1193
+ cancelAnimationFrame(second);
1194
+ };
1195
+ }, [entered]);
1196
+ return entered;
1197
+ }
1198
+ function prefersReducedMotion() {
1199
+ return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
1200
+ }
1201
+ var lastDockExit = null;
1202
+ var RESUME_WINDOW_MS = 700;
1203
+ function useWideViewport() {
1204
+ const [wide, setWide] = React63.useState(
1205
+ () => typeof window !== "undefined" && !!window.matchMedia?.("(min-width: 768px)").matches
1206
+ );
1207
+ React63.useEffect(() => {
1208
+ const query = window.matchMedia?.("(min-width: 768px)");
1209
+ if (!query) return;
1210
+ const sync = () => setWide(query.matches);
1211
+ sync();
1212
+ query.addEventListener("change", sync);
1213
+ return () => query.removeEventListener("change", sync);
1214
+ }, []);
1215
+ return wide;
1216
+ }
1217
+ function morphStyles(box, railOpen, railWidth) {
1218
+ const rightEdge = box.left + box.width / 2;
1219
+ const dx = fabRightEdge(railOpen, railWidth) - rightEdge;
1220
+ return {
1221
+ open: {
1222
+ transform: "translateX(-50%)",
1223
+ clipPath: "inset(0 round 9999px)"
1045
1224
  },
1046
- { key: "label", label: "Label", type: "text", searchable: true, hint: "Casa, Trabalho, Dep\xF3sito\u2026" },
1047
- {
1048
- key: "kind",
1049
- label: "Use",
1050
- type: "select",
1051
- options: ["both", "shipping", "billing"],
1052
- hint: "Defaults to 'both'."
1225
+ collapsed: {
1226
+ transform: `translateX(calc(-50% + ${dx}px))`,
1227
+ clipPath: `inset(0 0 0 ${Math.max(0, box.width - FAB_SIZE)}px round 9999px)`
1053
1228
  },
1054
- { key: "postal_code", label: "Postal code", type: "text", required: true, searchable: true, hint: "CEP." },
1055
- { key: "street", label: "Street", type: "text", required: true, searchable: true },
1056
- { key: "number", label: "Number", type: "text" },
1057
- { key: "complement", label: "Complement", type: "text" },
1058
- { key: "district", label: "District", type: "text", searchable: true, hint: "Bairro." },
1059
- { key: "city", label: "City", type: "text", required: true, searchable: true },
1060
- { key: "state", label: "State", type: "text", required: true, hint: "Two-letter UF." },
1061
- { key: "country", label: "Country", type: "text", hint: "Defaults to 'BR'." },
1062
- { key: "recipient", label: "Recipient", type: "text", searchable: true, hint: "Who receives, if not the owner." },
1063
- { key: "phone", label: "Phone", type: "text" },
1229
+ transition: `transform ${MORPH_MS}ms ${MORPH_EASE}, clip-path ${MORPH_MS}ms ${MORPH_EASE}`
1230
+ };
1231
+ }
1232
+ function InviteDock({ def, left, maxWidth }) {
1233
+ const { t } = useTranslation$1();
1234
+ const ask = useAsk();
1235
+ const setOpen = useChatStore((s) => s.setOpen);
1236
+ const railOpen = useRightRailStore((s) => s.open);
1237
+ const railWidth = useRightRailStore((s) => s.width);
1238
+ const [draft, setDraft] = React63.useState("");
1239
+ const resumed = React63.useRef(
1240
+ !!lastDockExit && lastDockExit.id === def.id && Date.now() - lastDockExit.at < RESUME_WINDOW_MS
1241
+ ).current;
1242
+ React63.useEffect(() => () => {
1243
+ lastDockExit = { id: def.id, at: Date.now() };
1244
+ }, [def.id]);
1245
+ const entered = useMorphIn(resumed);
1246
+ const width = Math.min(maxWidth, DOCK_MAX_WIDTH);
1247
+ const morph = morphStyles({ left, width }, railOpen, railWidth);
1248
+ return /* @__PURE__ */ jsx(
1249
+ "div",
1064
1250
  {
1065
- key: "is_default",
1066
- label: "Default address",
1067
- type: "boolean",
1068
- hint: "Only one default per owner and kind \u2014 clear the previous one before setting this."
1251
+ className: "fixed bottom-4 z-40 hidden md:block",
1252
+ style: {
1253
+ left,
1254
+ width,
1255
+ // Arrives as the FAB and unrolls leftward into a composer. The circle it
1256
+ // grows out of is the one that just disappeared from that exact spot.
1257
+ ...entered ? morph.open : morph.collapsed,
1258
+ transition: prefersReducedMotion() ? void 0 : morph.transition
1259
+ },
1260
+ children: /* @__PURE__ */ jsx(
1261
+ InviteComposer,
1262
+ {
1263
+ tone: "glass",
1264
+ className: "fayz-assistant-glow",
1265
+ placeholder: def.placeholder ?? t("assistant.invite.placeholder"),
1266
+ value: draft,
1267
+ onChange: setDraft,
1268
+ onSubmit: () => {
1269
+ if (draft.trim()) {
1270
+ ask(draft);
1271
+ setDraft("");
1272
+ } else {
1273
+ setOpen(true);
1274
+ }
1275
+ },
1276
+ onFocusAction: () => setOpen(true),
1277
+ label: def.persona
1278
+ }
1279
+ )
1069
1280
  }
1070
- ]
1071
- };
1072
- var CORE_QUERY_ENTITIES = [
1073
- {
1074
- key: "address",
1075
- entity: addressEntity,
1076
- writable: true,
1077
- agentHint: "`address` holds the street address of a PERSON (client, contact, supplier, staff). A person record has NO street/city/postal_code fields of its own. To read where someone lives, search `address` with filters {\"owner_id\": <the person's id>} \u2014 a person with no address field is not a person without an address. To record one, create an `address` with owner_id set to the person's id. Never write an address into a notes field."
1078
- }
1079
- ];
1080
-
1081
- // src/lib/core-ai-tools.ts
1082
- var coreAITools = [
1281
+ );
1282
+ }
1283
+ function InviteComposer({
1284
+ className,
1285
+ placeholder,
1286
+ value,
1287
+ onChange,
1288
+ onSubmit,
1289
+ onFocusAction,
1290
+ label,
1291
+ size = "md",
1292
+ tone = "card"
1293
+ }) {
1294
+ const { t } = useTranslation$1();
1295
+ const glass = tone === "glass";
1296
+ return /* @__PURE__ */ jsxs(
1297
+ "form",
1298
+ {
1299
+ onSubmit: (event) => {
1300
+ event.preventDefault();
1301
+ onSubmit();
1302
+ },
1303
+ className: cn$1(
1304
+ "relative flex items-center gap-2.5 pl-4 pr-2",
1305
+ glass ? "fayz-assistant-surface rounded-full bg-primary text-primary-foreground focus-within:-translate-y-0.5" : "rounded-2xl border border-border/70 bg-card transition-shadow focus-within:border-border focus-within:shadow-md",
1306
+ size === "lg" ? "py-2.5" : "py-2",
1307
+ className
1308
+ ),
1309
+ children: [
1310
+ /* @__PURE__ */ jsx(
1311
+ Sparkles,
1312
+ {
1313
+ className: cn$1(
1314
+ "relative shrink-0",
1315
+ glass ? "text-primary-foreground drop-shadow-sm" : "text-primary",
1316
+ size === "lg" ? "h-5 w-5" : "h-[18px] w-[18px]"
1317
+ )
1318
+ }
1319
+ ),
1320
+ /* @__PURE__ */ jsx(
1321
+ "input",
1322
+ {
1323
+ value,
1324
+ onChange: (event) => onChange(event.target.value),
1325
+ placeholder,
1326
+ "aria-label": label ?? placeholder,
1327
+ className: cn$1(
1328
+ "relative min-w-0 flex-1 bg-transparent focus:outline-none",
1329
+ // Glass text and placeholder come from .fayz-assistant-surface: on a
1330
+ // tinted bar the label is not decoration, so it cannot depend on a
1331
+ // utility class the app's Tailwind build may never have generated.
1332
+ !glass && "text-foreground placeholder:text-muted-foreground/70",
1333
+ size === "lg" ? "py-1.5 text-[15px]" : "text-sm"
1334
+ )
1335
+ }
1336
+ ),
1337
+ /* @__PURE__ */ jsx(
1338
+ "button",
1339
+ {
1340
+ type: "submit",
1341
+ onClick: () => {
1342
+ if (!value.trim()) onFocusAction?.();
1343
+ },
1344
+ "aria-label": t("chat.send"),
1345
+ "data-ready": value.trim() ? "true" : "false",
1346
+ className: cn$1(
1347
+ "relative inline-flex shrink-0 items-center justify-center rounded-full",
1348
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1349
+ // Opaque in both states, and coloured from CSS: a translucent circle
1350
+ // let the page's own lines print through it, and a primary-on-primary
1351
+ // arrow — what an ungenerated utility leaves behind — is no button.
1352
+ glass ? "fayz-assistant-send" : cn$1(
1353
+ "transition-colors",
1354
+ value.trim() ? "bg-primary text-primary-foreground hover:opacity-90" : "bg-muted text-muted-foreground hover:bg-muted/80",
1355
+ size === "lg" ? "h-9 w-9" : "h-8 w-8"
1356
+ )
1357
+ ),
1358
+ children: /* @__PURE__ */ jsx(ArrowUp, { className: "h-4 w-4" })
1359
+ }
1360
+ )
1361
+ ]
1362
+ }
1363
+ );
1364
+ }
1365
+ function InviteMorphBack() {
1366
+ const leaving = useAssistantInviteStore((s) => s.leaving);
1367
+ return leaving ? /* @__PURE__ */ jsx(MorphBackPill, {}, leaving.invite.id) : null;
1368
+ }
1369
+ function MorphBackPill() {
1370
+ const leaving = useAssistantInviteStore((s) => s.leaving);
1371
+ const endLeave = useAssistantInviteStore((s) => s.endLeave);
1372
+ const railOpen = useRightRailStore((s) => s.open);
1373
+ const railWidth = useRightRailStore((s) => s.width);
1374
+ const { t } = useTranslation$1();
1375
+ const collapsed = useMorphIn();
1376
+ React63.useEffect(() => {
1377
+ if (prefersReducedMotion()) {
1378
+ endLeave();
1379
+ return;
1380
+ }
1381
+ const timer = setTimeout(endLeave, MORPH_MS + 40);
1382
+ return () => clearTimeout(timer);
1383
+ }, [endLeave]);
1384
+ if (!leaving || typeof document === "undefined") return null;
1385
+ const morph = morphStyles(leaving.box, railOpen, railWidth);
1386
+ return createPortal(
1387
+ /* @__PURE__ */ jsx(
1388
+ "div",
1389
+ {
1390
+ "aria-hidden": true,
1391
+ className: "pointer-events-none fixed bottom-4 z-40 hidden md:block",
1392
+ style: {
1393
+ left: leaving.box.left,
1394
+ width: leaving.box.width,
1395
+ ...collapsed ? morph.collapsed : morph.open,
1396
+ transition: morph.transition
1397
+ },
1398
+ children: /* @__PURE__ */ jsx(
1399
+ InviteComposer,
1400
+ {
1401
+ tone: "glass",
1402
+ className: collapsed ? "fayz-assistant-leaving" : void 0,
1403
+ placeholder: leaving.invite.placeholder ?? t("assistant.invite.placeholder"),
1404
+ value: "",
1405
+ onChange: () => {
1406
+ },
1407
+ onSubmit: () => {
1408
+ },
1409
+ label: leaving.invite.persona
1410
+ }
1411
+ )
1412
+ }
1413
+ ),
1414
+ document.body
1415
+ );
1416
+ }
1417
+
1418
+ // src/app/core-entities.ts
1419
+ var addressEntity = {
1420
+ name: "Address",
1421
+ namePlural: "Addresses",
1422
+ icon: "MapPin",
1423
+ data: {
1424
+ table: "addresses",
1425
+ tenantScoped: true,
1426
+ tenantIdColumn: "tenant_id",
1427
+ searchColumns: ["label", "street", "district", "city", "postal_code", "recipient"]
1428
+ },
1429
+ fields: [
1430
+ // owner_id/owner_type are how an address attaches to its person. They are
1431
+ // required and described rather than hidden: the agent reaches this table
1432
+ // from a record it already has in hand ("adiciona o endereço X pra ele"),
1433
+ // so the id it must pass is the one it just searched for or created.
1434
+ {
1435
+ key: "owner_id",
1436
+ label: "Owner record id",
1437
+ type: "text",
1438
+ required: true,
1439
+ hint: "Id of the person (client, contact, supplier, staff\u2026) this address belongs to."
1440
+ },
1441
+ {
1442
+ key: "owner_type",
1443
+ label: "Owner kind",
1444
+ type: "select",
1445
+ options: ["person", "location", "tenant"],
1446
+ hint: "Defaults to 'person'. Only set it for an address that belongs to a business location or to the tenant itself."
1447
+ },
1448
+ { key: "label", label: "Label", type: "text", searchable: true, hint: "Casa, Trabalho, Dep\xF3sito\u2026" },
1449
+ {
1450
+ key: "kind",
1451
+ label: "Use",
1452
+ type: "select",
1453
+ options: ["both", "shipping", "billing"],
1454
+ hint: "Defaults to 'both'."
1455
+ },
1456
+ { key: "postal_code", label: "Postal code", type: "text", required: true, searchable: true, hint: "CEP." },
1457
+ { key: "street", label: "Street", type: "text", required: true, searchable: true },
1458
+ { key: "number", label: "Number", type: "text" },
1459
+ { key: "complement", label: "Complement", type: "text" },
1460
+ { key: "district", label: "District", type: "text", searchable: true, hint: "Bairro." },
1461
+ { key: "city", label: "City", type: "text", required: true, searchable: true },
1462
+ { key: "state", label: "State", type: "text", required: true, hint: "Two-letter UF." },
1463
+ { key: "country", label: "Country", type: "text", hint: "Defaults to 'BR'." },
1464
+ { key: "recipient", label: "Recipient", type: "text", searchable: true, hint: "Who receives, if not the owner." },
1465
+ { key: "phone", label: "Phone", type: "text" },
1466
+ {
1467
+ key: "is_default",
1468
+ label: "Default address",
1469
+ type: "boolean",
1470
+ hint: "Only one default per owner and kind \u2014 clear the previous one before setting this."
1471
+ }
1472
+ ]
1473
+ };
1474
+ var CORE_QUERY_ENTITIES = [
1475
+ {
1476
+ key: "address",
1477
+ entity: addressEntity,
1478
+ writable: true,
1479
+ agentHint: "`address` holds the street address of a PERSON (client, contact, supplier, staff). A person record has NO street/city/postal_code fields of its own. To read where someone lives, search `address` with filters {\"owner_id\": <the person's id>} \u2014 a person with no address field is not a person without an address. To record one, create an `address` with owner_id set to the person's id. Never write an address into a notes field."
1480
+ }
1481
+ ];
1482
+
1483
+ // src/lib/core-ai-tools.ts
1484
+ var coreAITools = [
1083
1485
  {
1084
1486
  id: "core.business-summary",
1085
1487
  name: "getBusinessSummary",
@@ -3163,6 +3565,20 @@ var SDK_INTEGRATIONS = [
3163
3565
  status: "soon",
3164
3566
  declaredBy: { pluginId: "reputation", package: "@fayz-ai/plugin-reputation" }
3165
3567
  },
3568
+ {
3569
+ slug: "vindi",
3570
+ name: "Vindi",
3571
+ description: "Pix e boleto pela Vindi. A loja recebe na conta dela, com o cliente pagando sem sair da p\xE1gina.",
3572
+ icon: "CreditCard",
3573
+ logoUrl: "https://assets.fayz.ai/connectors/vindi.png",
3574
+ category: "payments",
3575
+ direction: "outbound",
3576
+ planes: ["request", "sync"],
3577
+ moduleId: "shop",
3578
+ status: "available",
3579
+ connectorId: "vindi",
3580
+ declaredBy: { pluginId: "shop", package: "@fayz-ai/plugin-shop" }
3581
+ },
3166
3582
  {
3167
3583
  slug: "whatsapp",
3168
3584
  name: "WhatsApp",
@@ -4294,7 +4710,6 @@ ${JSON.stringify(snapshot.length === 1 ? snapshot[0] : snapshot)}` : void 0
4294
4710
  startNewConversation
4295
4711
  };
4296
4712
  }
4297
- var FAB_MARGIN_RIGHT = 21;
4298
4713
  var TEASER_MAX_PER_SESSION = 2;
4299
4714
  var KEY_DISMISSED2 = "fayz.agent.teaser.dismissed";
4300
4715
  var KEY_SHOWN = "fayz.agent.teaser.shown";
@@ -4325,14 +4740,14 @@ function recordTeaser(label) {
4325
4740
  store.setItem(KEY_SHOWN, String(teaserShownCount() + 1));
4326
4741
  store.setItem(KEY_SEEN, JSON.stringify([...teaserSeen(), label].slice(-12)));
4327
4742
  }
4328
- function prefersReducedMotion() {
4743
+ function prefersReducedMotion2() {
4329
4744
  return typeof window !== "undefined" && !!window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
4330
4745
  }
4331
4746
  function TypewriterText({ text, speed = 26, onDone }) {
4332
4747
  const [displayed, setDisplayed] = React63.useState("");
4333
4748
  const indexRef = React63.useRef(0);
4334
4749
  React63.useEffect(() => {
4335
- if (prefersReducedMotion()) {
4750
+ if (prefersReducedMotion2()) {
4336
4751
  setDisplayed(text);
4337
4752
  onDone?.();
4338
4753
  return;
@@ -4430,6 +4845,7 @@ function ChatFab({ className, apiEndpoint, systemPrompt, agent, voice, mobile })
4430
4845
  const topSuggestion = contextualSuggestions[0] ?? null;
4431
4846
  const topLabel = topSuggestion?.label ?? null;
4432
4847
  const invited = useAssistantInviteStore((s) => !!s.invite && s.stage === "dock");
4848
+ const morphingBack = useAssistantInviteStore((s) => !!s.leaving);
4433
4849
  const [wide, setWide] = React63.useState(
4434
4850
  () => typeof window !== "undefined" && !!window.matchMedia?.("(min-width: 768px)").matches
4435
4851
  );
@@ -4467,477 +4883,267 @@ function ChatFab({ className, apiEndpoint, systemPrompt, agent, voice, mobile })
4467
4883
  } else {
4468
4884
  addTimer(startExpand, 800);
4469
4885
  }
4470
- addTimer(() => {
4471
- setPhase("collapsing");
4472
- addTimer(() => setPhase("idle"), 450);
4473
- }, 9e3);
4474
- return clearTimers;
4475
- }, [topLabel, expanded, teaserAllowed]);
4476
- React63.useEffect(() => {
4477
- if (expanded && phase !== "idle") {
4478
- clearTimers();
4479
- setPhase("idle");
4480
- }
4481
- }, [expanded]);
4482
- const [entrance, setEntrance] = React63.useState("pending");
4483
- React63.useEffect(() => {
4484
- if (!currentOrg || entrance !== "pending") return;
4485
- if (prefersReducedMotion()) {
4486
- setEntrance("done");
4487
- return;
4488
- }
4489
- setEntrance("playing");
4490
- const timer = setTimeout(() => setEntrance("done"), 1600);
4491
- return () => clearTimeout(timer);
4492
- }, [currentOrg, entrance]);
4493
- const entering = entrance === "playing" && !expanded;
4494
- if (!currentOrg) return null;
4495
- if (invited && wide && !expanded && !activity) return null;
4496
- const resolveLabel = (s, index) => {
4497
- const key = `chat.suggestion.${s.toolId}.${index}`;
4498
- const translated = t(key);
4499
- return translated === key ? s.label : translated;
4500
- };
4501
- const activeLabel = activeSuggestion ? resolveLabel(activeSuggestion, 0) : "";
4502
- const handleSuggestionClick = () => {
4503
- if (!activeSuggestion) return;
4504
- clearTimers();
4505
- setPhase("idle");
4506
- setOpen(true);
4507
- setTimeout(() => {
4508
- sendMessage(activeSuggestion.prompt ?? activeLabel);
4509
- }, 150);
4510
- };
4511
- const dismissTeaser = (event) => {
4512
- event.stopPropagation();
4513
- session2()?.setItem(KEY_DISMISSED2, "1");
4514
- clearTimers();
4515
- setPhase("collapsing");
4516
- addTimer(() => setPhase("idle"), 400);
4517
- };
4518
- const showPill = phase === "expanding" || phase === "typing" || phase === "visible";
4519
- return /* @__PURE__ */ jsxs(
4520
- "div",
4521
- {
4522
- className: cn$1(
4523
- "fixed z-50 md:block md:bottom-4",
4524
- // Anchored to the CONTENT, not the screen: with the rail open the FAB
4525
- // slides left and keeps its bottom-right spot relative to the app —
4526
- // outside the column — gliding in step with the aside's width tween.
4527
- "motion-safe:transition-[right] motion-safe:duration-200 motion-safe:ease-out",
4528
- // On a phone the open panel is full-screen and carries its own close
4529
- // button — a floating FAB there only lands on top of the composer.
4530
- mobile && !expanded ? "block bottom-[calc(1rem+env(safe-area-inset-bottom))]" : "hidden bottom-4",
4531
- className
4532
- ),
4533
- style: { right: railOpen ? railWidth + FAB_MARGIN_RIGHT : FAB_MARGIN_RIGHT },
4534
- children: [
4535
- activity && !expanded && /* @__PURE__ */ jsxs(
4536
- "div",
4537
- {
4538
- className: cn$1(
4539
- "absolute bottom-0 right-0 flex items-center overflow-hidden rounded-full shadow-lg",
4540
- activity.state === "done" ? "bg-primary text-primary-foreground" : "bg-foreground text-background"
4541
- ),
4542
- style: {
4543
- paddingRight: "3rem",
4544
- height: "3rem",
4545
- animation: "fabPillGrow 500ms cubic-bezier(0.22, 1, 0.36, 1) forwards"
4546
- },
4547
- children: [
4548
- /* @__PURE__ */ jsxs(
4549
- "button",
4550
- {
4551
- type: "button",
4552
- onClick: () => setOpen(true),
4553
- title: activity.label,
4554
- className: "flex min-w-0 items-center gap-2 py-1 pl-4 pr-1 text-left transition-opacity hover:opacity-90",
4555
- children: [
4556
- /* @__PURE__ */ jsxs("span", { className: "relative inline-flex h-2 w-2 shrink-0", children: [
4557
- activity.state === "running" && /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-70" }),
4558
- /* @__PURE__ */ jsx(
4559
- "span",
4560
- {
4561
- className: cn$1(
4562
- "relative inline-flex h-2 w-2 rounded-full",
4563
- activity.state === "done" ? "bg-background" : "bg-red-500"
4564
- )
4565
- }
4566
- )
4567
- ] }),
4568
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
4569
- /* @__PURE__ */ jsx("span", { className: "max-w-[11rem] truncate text-[10px] uppercase tracking-wide opacity-60", children: activity.label }),
4570
- activity.elapsedMs !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold tabular-nums leading-tight", children: formatActivityElapsed(activity.elapsedMs) })
4571
- ] })
4572
- ]
4573
- }
4574
- ),
4575
- (activity.actions ?? []).filter((action) => action.primary).map((action) => {
4576
- const Icon = FAB_INTENT_ICON[action.intent];
4577
- return /* @__PURE__ */ jsx(
4578
- "button",
4579
- {
4580
- type: "button",
4581
- onClick: (event) => {
4582
- event.stopPropagation();
4583
- void action.run();
4584
- },
4585
- "aria-label": action.label,
4586
- title: action.label,
4587
- className: "mr-1.5 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full opacity-70 transition-opacity hover:bg-white/15 hover:opacity-100",
4588
- children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" })
4589
- },
4590
- action.intent
4591
- );
4592
- })
4593
- ]
4594
- }
4595
- ),
4596
- (showPill || phase === "collapsing") && !expanded && /* @__PURE__ */ jsxs(
4597
- "div",
4598
- {
4599
- className: cn$1(
4600
- "absolute bottom-0 right-0 flex items-center overflow-hidden rounded-full shadow-lg",
4601
- // Same fill as the FAB: the pill is the FAB stretching out, not a
4602
- // second black object parked next to a purple one. Reserving
4603
- // exactly the circle's width also closes the dead gap the dismiss
4604
- // button used to float in.
4605
- "bg-primary text-primary-foreground"
4606
- ),
4607
- style: {
4608
- paddingRight: "3rem",
4609
- height: "3rem",
4610
- animation: showPill ? "fabPillGrow 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards" : "fabPillShrink 400ms cubic-bezier(0.55, 0, 1, 0.45) forwards"
4611
- },
4612
- children: [
4613
- /* @__PURE__ */ jsxs(
4614
- "button",
4615
- {
4616
- onClick: handleSuggestionClick,
4617
- className: "flex min-w-0 flex-1 flex-col items-start justify-center py-1 pl-5 pr-1 text-left transition-opacity hover:opacity-90",
4618
- children: [
4619
- /* @__PURE__ */ jsx("span", { className: "text-[7px] font-semibold uppercase tracking-widest opacity-50", children: t("chat.fab.tryIt") }),
4620
- /* @__PURE__ */ jsx(
4621
- "span",
4622
- {
4623
- className: cn$1(
4624
- "min-h-[1.15em] whitespace-nowrap text-[13px] font-medium transition-opacity",
4625
- phase === "expanding" ? "opacity-0 duration-200" : "opacity-100 duration-300"
4626
- ),
4627
- children: phase === "typing" && activeSuggestion ? /* @__PURE__ */ jsx(TypewriterText, { text: `"${activeLabel}"`, onDone: () => setPhase("visible") }) : phase === "visible" && activeSuggestion ? `"${activeLabel}"` : "\xA0"
4628
- }
4629
- )
4630
- ]
4631
- }
4632
- ),
4633
- /* @__PURE__ */ jsx(
4634
- "button",
4635
- {
4636
- onClick: dismissTeaser,
4637
- "aria-label": t("chat.fab.dismiss"),
4638
- title: t("chat.fab.dismiss"),
4639
- className: "mr-1.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-50 transition-opacity hover:bg-black/10 hover:opacity-100",
4640
- children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
4641
- }
4642
- )
4643
- ]
4644
- }
4645
- ),
4646
- /* @__PURE__ */ jsxs(
4647
- "button",
4648
- {
4649
- onClick: () => {
4650
- if (expanded) {
4651
- closeRail();
4652
- setOpen(false);
4653
- } else {
4654
- setOpen(true);
4655
- }
4656
- },
4657
- className: cn$1(
4658
- "relative z-10 flex h-12 w-12 items-center justify-center transition-all duration-200",
4659
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
4660
- "rounded-full bg-primary text-primary-foreground shadow-lg hover:scale-105 hover:shadow-xl"
4661
- ),
4662
- "aria-label": expanded ? t("chat.fab.close") : t("chat.fab.open"),
4663
- "aria-expanded": expanded,
4664
- style: entering ? { animation: "fabEnter 600ms cubic-bezier(0.34, 1.56, 0.64, 1) both" } : void 0,
4665
- children: [
4666
- entering && /* @__PURE__ */ jsxs(Fragment, { children: [
4667
- /* @__PURE__ */ jsx(
4668
- "span",
4669
- {
4670
- "aria-hidden": true,
4671
- className: "absolute inset-0 rounded-full bg-primary/40",
4672
- style: { animation: "fabEnterRing 900ms ease-out 250ms both" }
4673
- }
4674
- ),
4675
- /* @__PURE__ */ jsx(
4676
- "span",
4677
- {
4678
- "aria-hidden": true,
4679
- className: "absolute inset-0 rounded-full bg-primary/25",
4680
- style: { animation: "fabEnterRing 1100ms ease-out 450ms both" }
4681
- }
4682
- )
4683
- ] }),
4684
- isStreaming && !expanded && /* @__PURE__ */ jsx(
4685
- "span",
4686
- {
4687
- "aria-hidden": true,
4688
- className: "absolute inset-0 rounded-full bg-primary motion-safe:[animation:fabHalo_1.8s_ease-out_infinite]"
4689
- }
4690
- ),
4691
- activity && activity.state !== "done" && expanded && /* @__PURE__ */ jsxs(
4692
- "span",
4693
- {
4694
- "aria-hidden": true,
4695
- className: "absolute -right-0.5 -top-0.5 flex h-3 w-3 items-center justify-center",
4696
- children: [
4697
- activity.state === "running" && /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-70" }),
4698
- /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full border-2 border-primary bg-red-500" })
4699
- ]
4700
- }
4701
- ),
4702
- expanded ? /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(
4703
- Sparkles,
4704
- {
4705
- className: "relative h-5 w-5",
4706
- style: entering ? { animation: "fabEnterIcon 700ms cubic-bezier(0.34, 1.56, 0.64, 1) 120ms both" } : void 0
4707
- }
4708
- )
4709
- ]
4710
- }
4711
- )
4712
- ]
4713
- }
4714
- );
4715
- }
4716
- function AssistantInvite({ className, ...def }) {
4717
- const setInvite = useAssistantInviteStore((s) => s.setInvite);
4718
- const stage = useAssistantInviteStore((s) => s.stage);
4719
- const dismiss = useAssistantInviteStore((s) => s.dismiss);
4720
- const railOpen = useRightRailStore((s) => s.open);
4721
- const hasAssistant = useRightRailStore((s) => s.panels.some((p) => p.id === "chat"));
4722
- const { id, persona, headline, pitch, description, placeholder, auto } = def;
4723
- const suggestions = def.suggestions;
4724
- const suggestionKey = JSON.stringify(suggestions ?? []);
4725
- React63.useEffect(() => {
4726
- if (!hasAssistant) return;
4727
- setInvite({
4728
- id,
4729
- persona,
4730
- headline,
4731
- pitch,
4732
- description,
4733
- placeholder,
4734
- auto,
4735
- suggestions: JSON.parse(suggestionKey)
4736
- });
4737
- return () => setInvite(null, id);
4738
- }, [hasAssistant, id, persona, headline, pitch, description, placeholder, auto, suggestionKey, setInvite]);
4739
- const anchorRef = React63.useRef(null);
4740
- const [box, setBox] = React63.useState(null);
4741
- const docked = stage === "dock" && !railOpen && hasAssistant;
4742
- const showing = hasAssistant && (stage === "hero" || docked);
4743
- React63.useEffect(() => {
4744
- if (!showing) return;
4745
- const el = anchorRef.current;
4746
- if (!el) return;
4747
- const measure = () => {
4748
- const rect = el.getBoundingClientRect();
4749
- setBox({ left: rect.left, width: rect.width, top: rect.top });
4750
- };
4751
- measure();
4752
- const observer = new ResizeObserver(measure);
4753
- observer.observe(el);
4754
- window.addEventListener("resize", measure);
4755
- window.addEventListener("scroll", measure, true);
4756
- return () => {
4757
- observer.disconnect();
4758
- window.removeEventListener("resize", measure);
4759
- window.removeEventListener("scroll", measure, true);
4760
- };
4761
- }, [showing]);
4762
- if (!hasAssistant) return null;
4763
- const floating = box && stage === "hero" ? /* @__PURE__ */ jsx(InviteHero, { def, box, onDismiss: dismiss }) : box && docked ? /* @__PURE__ */ jsx(InviteDock, { def, left: box.left + box.width / 2, maxWidth: box.width }) : null;
4764
- return /* @__PURE__ */ jsx("div", { ref: anchorRef, className: cn$1("h-0 w-full", className), "aria-hidden": !showing, children: floating && typeof document !== "undefined" ? createPortal(floating, document.body) : null });
4765
- }
4766
- function useAsk() {
4767
- const queuePrompt = useChatStore((s) => s.queuePrompt);
4768
- const dismiss = useAssistantInviteStore((s) => s.dismiss);
4769
- return React63.useCallback(
4770
- (text) => {
4771
- const trimmed = text.trim();
4772
- if (!trimmed) return;
4773
- dismiss();
4774
- queuePrompt(trimmed);
4775
- },
4776
- [queuePrompt, dismiss]
4777
- );
4778
- }
4779
- function InviteHero({
4780
- def,
4781
- box,
4782
- onDismiss
4783
- }) {
4784
- const { t } = useTranslation$1();
4785
- const user = useAuthStore((s) => s.user);
4786
- const ask = useAsk();
4787
- const [draft, setDraft] = React63.useState("");
4788
- const name = (user?.fullName || user?.email || "").split(/[\s@]/)[0];
4789
- React63.useEffect(() => {
4790
- const onKey = (event) => {
4791
- if (event.key === "Escape") onDismiss();
4792
- };
4793
- window.addEventListener("keydown", onKey);
4794
- return () => window.removeEventListener("keydown", onKey);
4795
- }, [onDismiss]);
4796
- return /* @__PURE__ */ jsxs(
4797
- "section",
4798
- {
4799
- className: cn$1(
4800
- "fixed z-30 flex flex-col items-center justify-center overflow-y-auto px-4 pb-14 text-center",
4801
- "bg-background/95 backdrop-blur-sm motion-safe:animate-in motion-safe:fade-in-0"
4802
- ),
4803
- style: { left: box.left, width: box.width, top: box.top, bottom: 0 },
4804
- "aria-label": def.persona ?? t("chat.defaultTitle"),
4805
- children: [
4806
- /* @__PURE__ */ jsx(
4807
- "button",
4808
- {
4809
- type: "button",
4810
- onClick: onDismiss,
4811
- "aria-label": t("assistant.invite.dismiss"),
4812
- title: t("assistant.invite.dismiss"),
4813
- className: "absolute right-1 top-2 inline-flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground/60 transition-colors hover:bg-muted hover:text-foreground",
4814
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
4815
- }
4816
- ),
4817
- /* @__PURE__ */ jsx("h2", { className: "max-w-2xl text-balance text-[26px] font-semibold leading-tight tracking-tight text-foreground sm:text-[32px]", children: def.headline.replace("{{name}}", name) }),
4818
- /* @__PURE__ */ jsx(
4819
- InviteComposer,
4820
- {
4821
- className: "mt-7 w-full max-w-3xl",
4822
- placeholder: def.placeholder ?? t("assistant.invite.placeholder"),
4823
- value: draft,
4824
- onChange: setDraft,
4825
- onSubmit: () => {
4826
- ask(draft);
4827
- setDraft("");
4828
- },
4829
- size: "lg"
4830
- }
4831
- ),
4832
- def.suggestions && def.suggestions.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: def.suggestions.slice(0, 4).map((suggestion) => /* @__PURE__ */ jsx(
4833
- "button",
4834
- {
4835
- type: "button",
4836
- onClick: () => ask(suggestion.prompt ?? suggestion.label),
4837
- className: cn$1(
4838
- "rounded-full border border-border/70 bg-card px-4 py-2 text-[13px] text-foreground/80 transition-colors",
4839
- "hover:border-border hover:bg-muted hover:text-foreground",
4840
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
4841
- ),
4842
- children: suggestion.label
4843
- },
4844
- suggestion.label
4845
- )) })
4846
- ]
4847
- }
4848
- );
4849
- }
4850
- function InviteDock({ def, left, maxWidth }) {
4851
- const { t } = useTranslation$1();
4852
- const ask = useAsk();
4853
- const setOpen = useChatStore((s) => s.setOpen);
4854
- const [draft, setDraft] = React63.useState("");
4855
- return /* @__PURE__ */ jsx(
4856
- "div",
4857
- {
4858
- className: "fixed bottom-4 z-40 hidden md:block",
4859
- style: { left, width: Math.min(maxWidth, 640), transform: "translateX(-50%)" },
4860
- children: /* @__PURE__ */ jsx(
4861
- InviteComposer,
4862
- {
4863
- className: "shadow-lg motion-safe:animate-slide-in-from-bottom",
4864
- placeholder: def.placeholder ?? t("assistant.invite.placeholder"),
4865
- value: draft,
4866
- onChange: setDraft,
4867
- onSubmit: () => {
4868
- if (draft.trim()) {
4869
- ask(draft);
4870
- setDraft("");
4871
- } else {
4872
- setOpen(true);
4873
- }
4874
- },
4875
- onFocusAction: () => setOpen(true),
4876
- label: def.persona
4877
- }
4878
- )
4886
+ addTimer(() => {
4887
+ setPhase("collapsing");
4888
+ addTimer(() => setPhase("idle"), 450);
4889
+ }, 9e3);
4890
+ return clearTimers;
4891
+ }, [topLabel, expanded, teaserAllowed]);
4892
+ React63.useEffect(() => {
4893
+ if (expanded && phase !== "idle") {
4894
+ clearTimers();
4895
+ setPhase("idle");
4879
4896
  }
4880
- );
4881
- }
4882
- function InviteComposer({
4883
- className,
4884
- placeholder,
4885
- value,
4886
- onChange,
4887
- onSubmit,
4888
- onFocusAction,
4889
- label,
4890
- size = "md"
4891
- }) {
4892
- const { t } = useTranslation$1();
4893
- return /* @__PURE__ */ jsxs(
4894
- "form",
4895
- {
4896
- onSubmit: (event) => {
4897
- event.preventDefault();
4898
- onSubmit();
4899
- },
4900
- className: cn$1(
4901
- "flex items-center gap-2 rounded-2xl border border-border/70 bg-card pl-4 pr-2 transition-shadow",
4902
- "focus-within:border-border focus-within:shadow-md",
4903
- size === "lg" ? "py-2.5" : "py-1.5",
4904
- className
4905
- ),
4906
- children: [
4907
- /* @__PURE__ */ jsx(Sparkles, { className: cn$1("shrink-0 text-primary", size === "lg" ? "h-5 w-5" : "h-4 w-4") }),
4908
- /* @__PURE__ */ jsx(
4909
- "input",
4910
- {
4911
- value,
4912
- onChange: (event) => onChange(event.target.value),
4913
- placeholder,
4914
- "aria-label": label ?? placeholder,
4915
- className: cn$1(
4916
- "min-w-0 flex-1 bg-transparent text-foreground placeholder:text-muted-foreground/70 focus:outline-none",
4917
- size === "lg" ? "py-1.5 text-[15px]" : "text-[13.5px]"
4918
- )
4919
- }
4920
- ),
4921
- /* @__PURE__ */ jsx(
4922
- "button",
4923
- {
4924
- type: "submit",
4925
- onClick: () => {
4926
- if (!value.trim()) onFocusAction?.();
4927
- },
4928
- "aria-label": t("chat.send"),
4929
- className: cn$1(
4930
- "inline-flex shrink-0 items-center justify-center rounded-full transition-colors",
4931
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4932
- value.trim() ? "bg-primary text-primary-foreground hover:opacity-90" : "bg-muted text-muted-foreground hover:bg-muted/80",
4933
- size === "lg" ? "h-9 w-9" : "h-7 w-7"
4934
- ),
4935
- children: /* @__PURE__ */ jsx(ArrowUp, { className: size === "lg" ? "h-4 w-4" : "h-3.5 w-3.5" })
4936
- }
4937
- )
4938
- ]
4897
+ }, [expanded]);
4898
+ const [entrance, setEntrance] = React63.useState("pending");
4899
+ React63.useEffect(() => {
4900
+ if (!currentOrg || entrance !== "pending") return;
4901
+ if (prefersReducedMotion2()) {
4902
+ setEntrance("done");
4903
+ return;
4939
4904
  }
4940
- );
4905
+ setEntrance("playing");
4906
+ const timer = setTimeout(() => setEntrance("done"), 1600);
4907
+ return () => clearTimeout(timer);
4908
+ }, [currentOrg, entrance]);
4909
+ const [poppingBack, setPoppingBack] = React63.useState(false);
4910
+ const wasMorphing = React63.useRef(false);
4911
+ React63.useEffect(() => {
4912
+ const was = wasMorphing.current;
4913
+ wasMorphing.current = morphingBack;
4914
+ if (!was || morphingBack || prefersReducedMotion2()) return;
4915
+ setPoppingBack(true);
4916
+ const timer = setTimeout(() => setPoppingBack(false), 620);
4917
+ return () => clearTimeout(timer);
4918
+ }, [morphingBack]);
4919
+ const entering = (entrance === "playing" || poppingBack) && !expanded;
4920
+ if (!currentOrg) return null;
4921
+ if ((invited || morphingBack) && wide && !expanded && !activity) return /* @__PURE__ */ jsx(InviteMorphBack, {});
4922
+ const resolveLabel = (s, index) => {
4923
+ const key = `chat.suggestion.${s.toolId}.${index}`;
4924
+ const translated = t(key);
4925
+ return translated === key ? s.label : translated;
4926
+ };
4927
+ const activeLabel = activeSuggestion ? resolveLabel(activeSuggestion, 0) : "";
4928
+ const handleSuggestionClick = () => {
4929
+ if (!activeSuggestion) return;
4930
+ clearTimers();
4931
+ setPhase("idle");
4932
+ setOpen(true);
4933
+ setTimeout(() => {
4934
+ sendMessage(activeSuggestion.prompt ?? activeLabel);
4935
+ }, 150);
4936
+ };
4937
+ const dismissTeaser = (event) => {
4938
+ event.stopPropagation();
4939
+ session2()?.setItem(KEY_DISMISSED2, "1");
4940
+ clearTimers();
4941
+ setPhase("collapsing");
4942
+ addTimer(() => setPhase("idle"), 400);
4943
+ };
4944
+ const showPill = phase === "expanding" || phase === "typing" || phase === "visible";
4945
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4946
+ /* @__PURE__ */ jsx(InviteMorphBack, {}),
4947
+ /* @__PURE__ */ jsxs(
4948
+ "div",
4949
+ {
4950
+ className: cn$1(
4951
+ "fixed z-50 md:block md:bottom-4",
4952
+ // Anchored to the CONTENT, not the screen: with the rail open the FAB
4953
+ // slides left and keeps its bottom-right spot relative to the app —
4954
+ // outside the column — gliding in step with the aside's width tween.
4955
+ "motion-safe:transition-[right] motion-safe:duration-200 motion-safe:ease-out",
4956
+ // On a phone the open panel is full-screen and carries its own close
4957
+ // button — a floating FAB there only lands on top of the composer.
4958
+ mobile && !expanded ? "block bottom-[calc(1rem+env(safe-area-inset-bottom))]" : "hidden bottom-4",
4959
+ className
4960
+ ),
4961
+ style: { right: railOpen ? railWidth + FAB_MARGIN_RIGHT : FAB_MARGIN_RIGHT },
4962
+ children: [
4963
+ activity && !expanded && /* @__PURE__ */ jsxs(
4964
+ "div",
4965
+ {
4966
+ className: cn$1(
4967
+ "absolute bottom-0 right-0 flex items-center overflow-hidden rounded-full shadow-lg",
4968
+ activity.state === "done" ? "bg-primary text-primary-foreground" : "bg-foreground text-background"
4969
+ ),
4970
+ style: {
4971
+ paddingRight: "3rem",
4972
+ height: "3rem",
4973
+ animation: "fabPillGrow 500ms cubic-bezier(0.22, 1, 0.36, 1) forwards"
4974
+ },
4975
+ children: [
4976
+ /* @__PURE__ */ jsxs(
4977
+ "button",
4978
+ {
4979
+ type: "button",
4980
+ onClick: () => setOpen(true),
4981
+ title: activity.label,
4982
+ className: "flex min-w-0 items-center gap-2 py-1 pl-4 pr-1 text-left transition-opacity hover:opacity-90",
4983
+ children: [
4984
+ /* @__PURE__ */ jsxs("span", { className: "relative inline-flex h-2 w-2 shrink-0", children: [
4985
+ activity.state === "running" && /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-70" }),
4986
+ /* @__PURE__ */ jsx(
4987
+ "span",
4988
+ {
4989
+ className: cn$1(
4990
+ "relative inline-flex h-2 w-2 rounded-full",
4991
+ activity.state === "done" ? "bg-background" : "bg-red-500"
4992
+ )
4993
+ }
4994
+ )
4995
+ ] }),
4996
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
4997
+ /* @__PURE__ */ jsx("span", { className: "max-w-[11rem] truncate text-[10px] uppercase tracking-wide opacity-60", children: activity.label }),
4998
+ activity.elapsedMs !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold tabular-nums leading-tight", children: formatActivityElapsed(activity.elapsedMs) })
4999
+ ] })
5000
+ ]
5001
+ }
5002
+ ),
5003
+ (activity.actions ?? []).filter((action) => action.primary).map((action) => {
5004
+ const Icon = FAB_INTENT_ICON[action.intent];
5005
+ return /* @__PURE__ */ jsx(
5006
+ "button",
5007
+ {
5008
+ type: "button",
5009
+ onClick: (event) => {
5010
+ event.stopPropagation();
5011
+ void action.run();
5012
+ },
5013
+ "aria-label": action.label,
5014
+ title: action.label,
5015
+ className: "mr-1.5 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full opacity-70 transition-opacity hover:bg-white/15 hover:opacity-100",
5016
+ children: /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" })
5017
+ },
5018
+ action.intent
5019
+ );
5020
+ })
5021
+ ]
5022
+ }
5023
+ ),
5024
+ (showPill || phase === "collapsing") && !expanded && /* @__PURE__ */ jsxs(
5025
+ "div",
5026
+ {
5027
+ className: cn$1(
5028
+ "absolute bottom-0 right-0 flex items-center overflow-hidden rounded-full shadow-lg",
5029
+ // Same fill as the FAB: the pill is the FAB stretching out, not a
5030
+ // second black object parked next to a purple one. Reserving
5031
+ // exactly the circle's width also closes the dead gap the dismiss
5032
+ // button used to float in.
5033
+ "bg-primary text-primary-foreground"
5034
+ ),
5035
+ style: {
5036
+ paddingRight: "3rem",
5037
+ height: "3rem",
5038
+ animation: showPill ? "fabPillGrow 650ms cubic-bezier(0.22, 1, 0.36, 1) forwards" : "fabPillShrink 400ms cubic-bezier(0.55, 0, 1, 0.45) forwards"
5039
+ },
5040
+ children: [
5041
+ /* @__PURE__ */ jsxs(
5042
+ "button",
5043
+ {
5044
+ onClick: handleSuggestionClick,
5045
+ className: "flex min-w-0 flex-1 flex-col items-start justify-center py-1 pl-5 pr-1 text-left transition-opacity hover:opacity-90",
5046
+ children: [
5047
+ /* @__PURE__ */ jsx("span", { className: "text-[7px] font-semibold uppercase tracking-widest opacity-50", children: t("chat.fab.tryIt") }),
5048
+ /* @__PURE__ */ jsx(
5049
+ "span",
5050
+ {
5051
+ className: cn$1(
5052
+ "min-h-[1.15em] whitespace-nowrap text-[13px] font-medium transition-opacity",
5053
+ phase === "expanding" ? "opacity-0 duration-200" : "opacity-100 duration-300"
5054
+ ),
5055
+ children: phase === "typing" && activeSuggestion ? /* @__PURE__ */ jsx(TypewriterText, { text: `"${activeLabel}"`, onDone: () => setPhase("visible") }) : phase === "visible" && activeSuggestion ? `"${activeLabel}"` : "\xA0"
5056
+ }
5057
+ )
5058
+ ]
5059
+ }
5060
+ ),
5061
+ /* @__PURE__ */ jsx(
5062
+ "button",
5063
+ {
5064
+ onClick: dismissTeaser,
5065
+ "aria-label": t("chat.fab.dismiss"),
5066
+ title: t("chat.fab.dismiss"),
5067
+ className: "mr-1.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-50 transition-opacity hover:bg-black/10 hover:opacity-100",
5068
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
5069
+ }
5070
+ )
5071
+ ]
5072
+ }
5073
+ ),
5074
+ /* @__PURE__ */ jsxs(
5075
+ "button",
5076
+ {
5077
+ onClick: () => {
5078
+ if (expanded) {
5079
+ closeRail();
5080
+ setOpen(false);
5081
+ } else {
5082
+ setOpen(true);
5083
+ }
5084
+ },
5085
+ className: cn$1(
5086
+ "relative z-10 flex h-12 w-12 items-center justify-center",
5087
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
5088
+ // Same material as the docked composer — the two are one object in
5089
+ // two shapes, and the morph between them only reads if the surface
5090
+ // does not change on the way.
5091
+ "fayz-assistant-surface rounded-full bg-primary text-primary-foreground hover:scale-105"
5092
+ ),
5093
+ "aria-label": expanded ? t("chat.fab.close") : t("chat.fab.open"),
5094
+ "aria-expanded": expanded,
5095
+ style: entering ? { animation: "fabEnter 600ms cubic-bezier(0.34, 1.56, 0.64, 1) both" } : void 0,
5096
+ children: [
5097
+ entering && /* @__PURE__ */ jsxs(Fragment, { children: [
5098
+ /* @__PURE__ */ jsx(
5099
+ "span",
5100
+ {
5101
+ "aria-hidden": true,
5102
+ className: "absolute inset-0 rounded-full bg-primary/40",
5103
+ style: { animation: "fabEnterRing 900ms ease-out 250ms both" }
5104
+ }
5105
+ ),
5106
+ /* @__PURE__ */ jsx(
5107
+ "span",
5108
+ {
5109
+ "aria-hidden": true,
5110
+ className: "absolute inset-0 rounded-full bg-primary/25",
5111
+ style: { animation: "fabEnterRing 1100ms ease-out 450ms both" }
5112
+ }
5113
+ )
5114
+ ] }),
5115
+ isStreaming && !expanded && /* @__PURE__ */ jsx(
5116
+ "span",
5117
+ {
5118
+ "aria-hidden": true,
5119
+ className: "absolute inset-0 rounded-full bg-primary motion-safe:[animation:fabHalo_1.8s_ease-out_infinite]"
5120
+ }
5121
+ ),
5122
+ activity && activity.state !== "done" && expanded && /* @__PURE__ */ jsxs(
5123
+ "span",
5124
+ {
5125
+ "aria-hidden": true,
5126
+ className: "absolute -right-0.5 -top-0.5 flex h-3 w-3 items-center justify-center",
5127
+ children: [
5128
+ activity.state === "running" && /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-red-500 opacity-70" }),
5129
+ /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full border-2 border-primary bg-red-500" })
5130
+ ]
5131
+ }
5132
+ ),
5133
+ expanded ? /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(
5134
+ Sparkles,
5135
+ {
5136
+ className: "relative h-5 w-5",
5137
+ style: entering ? { animation: "fabEnterIcon 700ms cubic-bezier(0.34, 1.56, 0.64, 1) 120ms both" } : void 0
5138
+ }
5139
+ )
5140
+ ]
5141
+ }
5142
+ )
5143
+ ]
5144
+ }
5145
+ )
5146
+ ] });
4941
5147
  }
4942
5148
  var MAX_SUGGESTIONS = 5;
4943
5149
  function ChatSuggestions({ suggestions, onSelect, onDismiss, compact }) {
@@ -7702,7 +7908,7 @@ function ConfirmActionCard({
7702
7908
  onResolve
7703
7909
  }) {
7704
7910
  const { t } = useTranslation$1();
7705
- const schema = action.fields ?? [];
7911
+ const schema = React63.useMemo(() => action.fields ?? [], [action.fields]);
7706
7912
  const partial = action.partial === true;
7707
7913
  const seedValues = React63.useCallback(
7708
7914
  (params) => {
@@ -9487,7 +9693,13 @@ var BOOT_THEME_STORAGE_KEY = "fayz:boot-theme";
9487
9693
  function persistBootPalette(colors) {
9488
9694
  if (typeof localStorage === "undefined") return;
9489
9695
  try {
9490
- localStorage.setItem(BOOT_THEME_STORAGE_KEY, JSON.stringify({ sidebar: colors.sidebar }));
9696
+ localStorage.setItem(BOOT_THEME_STORAGE_KEY, JSON.stringify({
9697
+ sidebar: colors.sidebar,
9698
+ // The boot script seeds --sidebar-foreground too: the React skeleton's
9699
+ // spinner draws in it, and the default (dark-on-light) token is
9700
+ // invisible on a seeded dark rail.
9701
+ sidebarForeground: colors.sidebarForeground
9702
+ }));
9491
9703
  } catch {
9492
9704
  }
9493
9705
  }
@@ -9698,12 +9910,13 @@ function useGlobalSearch() {
9698
9910
  );
9699
9911
  const generation = useRef(0);
9700
9912
  const search = useCallback(
9701
- async (query, onPartial) => {
9913
+ async (query, onPartial, opts) => {
9702
9914
  const token = ++generation.current;
9703
9915
  return searchEverything(query, {
9704
9916
  targets,
9705
9917
  limit: 30,
9706
9918
  perTarget: 5,
9919
+ fresh: opts?.fresh,
9707
9920
  onPartial: onPartial ? (partial) => {
9708
9921
  if (token === generation.current) onPartial(partial);
9709
9922
  } : void 0
@@ -14548,6 +14761,7 @@ function CrudListView({
14548
14761
  headerActions,
14549
14762
  stats,
14550
14763
  onRefresh,
14764
+ refreshFromCache,
14551
14765
  onSort,
14552
14766
  onSortField,
14553
14767
  onSortDir,
@@ -14711,7 +14925,16 @@ function CrudListView({
14711
14925
  ] })
14712
14926
  ] })
14713
14927
  ] }),
14714
- onRefresh && /* @__PURE__ */ jsx(Hint, { label: t("crud.list.refresh"), children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": t("crud.list.refresh"), onClick: onRefresh, className: iconBtn, children: /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }) }) }),
14928
+ onRefresh && /* @__PURE__ */ jsx(
14929
+ RefreshButton,
14930
+ {
14931
+ onRefresh,
14932
+ fromCache: refreshFromCache,
14933
+ label: t("crud.list.refresh"),
14934
+ labelCached: t("crud.list.refreshCached"),
14935
+ className: iconBtn
14936
+ }
14937
+ ),
14715
14938
  onExport && /* @__PURE__ */ jsx(Hint, { label: t("crud.export.action"), children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": t("crud.export.action"), onClick: onExport, className: iconBtn, children: /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }) }) })
14716
14939
  ] }) : null;
14717
14940
  const rows = items?.length ?? 0;
@@ -16454,8 +16677,10 @@ function CrudPage({ entityDef: rawEntityDef, useStore, basePath, display, featur
16454
16677
  stats: stats?.(store.items, store.total),
16455
16678
  error: store.error,
16456
16679
  onRefresh: () => {
16457
- void store.fetch();
16680
+ emitDataChanged(entityDef.data?.table ? { table: entityDef.data.table, op: "unknown" } : { op: "unknown" });
16681
+ return store.refresh();
16458
16682
  },
16683
+ refreshFromCache: store.fromCache,
16459
16684
  onSort: handleSort,
16460
16685
  onSortField: handleSortField,
16461
16686
  onSortDir: handleSortDir,
@@ -16615,6 +16840,7 @@ function createCrudStore(dataProvider, entity) {
16615
16840
  query: {},
16616
16841
  error: null,
16617
16842
  errorAt: 0,
16843
+ fromCache: false,
16618
16844
  async fetch() {
16619
16845
  const currentKey = queryKey(get().query);
16620
16846
  if (inflightFetch && currentKey === lastQueryKey) {
@@ -16628,7 +16854,13 @@ function createCrudStore(dataProvider, entity) {
16628
16854
  const promise = (async () => {
16629
16855
  try {
16630
16856
  const result = await dataProvider.list(get().query);
16631
- set({ items: result.data, total: result.total, loading: false, error: null });
16857
+ set({
16858
+ items: result.data,
16859
+ total: result.total,
16860
+ loading: false,
16861
+ error: null,
16862
+ fromCache: Boolean(result.cached || result.stale)
16863
+ });
16632
16864
  } catch (err) {
16633
16865
  set({
16634
16866
  items: get().items ?? [],
@@ -16644,6 +16876,10 @@ function createCrudStore(dataProvider, entity) {
16644
16876
  inflightFetch = promise;
16645
16877
  return promise;
16646
16878
  },
16879
+ async refresh() {
16880
+ invalidate();
16881
+ return get().fetch();
16882
+ },
16647
16883
  async create(data) {
16648
16884
  const item = await dataProvider.create(data);
16649
16885
  emit3("created", { record_id: item?.id, record_name: displayName(item) });
@@ -17323,7 +17559,7 @@ function SoftLimitBanner({ keys }) {
17323
17559
  ] });
17324
17560
  }
17325
17561
  var CommandPalette = React63.lazy(
17326
- () => import('./CommandPalette-2AL327XQ.js').then((m) => ({ default: m.CommandPalette }))
17562
+ () => import('./CommandPalette-U7MMA2WH.js').then((m) => ({ default: m.CommandPalette }))
17327
17563
  );
17328
17564
  var SubscriptionPage = React63.lazy(
17329
17565
  () => import('./SubscriptionPage-NLZAZO4P.js').then((m) => ({ default: m.SubscriptionPage }))
@@ -17591,6 +17827,7 @@ function navEntryToItem(entry) {
17591
17827
  icon: entry.icon ?? "LayoutTemplate",
17592
17828
  route: entry.route,
17593
17829
  section: entry.section,
17830
+ group: entry.group,
17594
17831
  badge: entry.badge,
17595
17832
  href: entry.href,
17596
17833
  permission: entry.permission
@@ -17638,6 +17875,7 @@ function pageToNavigationItem(page, fallbackPosition) {
17638
17875
  icon: page.icon ?? "FileText",
17639
17876
  route: page.path,
17640
17877
  section: page.section ?? "main",
17878
+ group: page.group,
17641
17879
  badge: page.badge,
17642
17880
  href: page.href,
17643
17881
  permission: page.permission,
@@ -17741,7 +17979,7 @@ function AdminShellInner({ appName, layout = "sidebar", logo, pages = [], showSe
17741
17979
  const railAutoCollapsedRef = React63.useRef(false);
17742
17980
  React63.useEffect(() => {
17743
17981
  if (layout !== "sidebar") return;
17744
- const SIDEBAR_EXPANDED = 256;
17982
+ const SIDEBAR_EXPANDED = 224;
17745
17983
  const MIN_CONTENT = 620;
17746
17984
  const apply = () => {
17747
17985
  if (typeof window === "undefined") return;
@@ -17923,7 +18161,7 @@ function AdminShellInner({ appName, layout = "sidebar", logo, pages = [], showSe
17923
18161
  if (settingsTabDef) return settingsTabDef.label;
17924
18162
  if (path === "/settings" || path.startsWith("/settings/")) return tr("common.settings", "Settings");
17925
18163
  if (match?.route.title) return match.route.title;
17926
- const candidates = [...navigation, ...navigation.flatMap((n) => n.children ?? [])].filter((n) => n.route).sort((a, b) => b.route.length - a.route.length);
18164
+ const candidates = [...navigation, ...navigation.flatMap((n) => n.children ?? [])].filter((n) => n.route).sort((a, b) => b.route.length - a.route.length || (a.children?.length ? 1 : 0) - (b.children?.length ? 1 : 0));
17927
18165
  const hit = candidates.find(
17928
18166
  (n) => n.route === "/" ? path === "/" || path === "" : path === n.route || path.startsWith(n.route + "/") || path.startsWith(n.route)
17929
18167
  );
@@ -18288,6 +18526,13 @@ function ThemeInitializer({ config }) {
18288
18526
  }, [config.defaultThemeMode, config.theme, initialize, setMode, setOverrides]);
18289
18527
  return null;
18290
18528
  }
18529
+ function PersistentCacheInitializer() {
18530
+ React63.useEffect(() => {
18531
+ attachPersistentCache();
18532
+ void getPersistentCache()?.hydrate();
18533
+ }, []);
18534
+ return null;
18535
+ }
18291
18536
  function FayzEventBridgeInitializer({ config }) {
18292
18537
  React63.useEffect(() => startFayzEventBridge(config.chat?.agent), [config.chat?.agent]);
18293
18538
  return null;
@@ -18408,6 +18653,7 @@ function AdminProviders({ config, children }) {
18408
18653
  cacheTtlMs: config.org?.cacheTtlMs,
18409
18654
  children: /* @__PURE__ */ jsx(PluginRuntimeProvider, { value: runtime, children: /* @__PURE__ */ jsx(PermissionsProvider, { config: config.permissions, children: /* @__PURE__ */ jsx(AccessProvider, { limitDeclarations: config.billing?.limitDeclarations, children: /* @__PURE__ */ jsxs(I18nProvider, { value: resolved.i18n, children: [
18410
18655
  /* @__PURE__ */ jsx(ThemeInitializer, { config }),
18656
+ /* @__PURE__ */ jsx(PersistentCacheInitializer, {}),
18411
18657
  /* @__PURE__ */ jsx(TeamInitializer, { config }),
18412
18658
  /* @__PURE__ */ jsx(BillingInitializer, { config }),
18413
18659
  /* @__PURE__ */ jsx(FayzEventBridgeInitializer, { config }),
@@ -19645,6 +19891,13 @@ function createClientOrdersProvider() {
19645
19891
  }
19646
19892
  };
19647
19893
  }
19894
+ var PERSON_LOOKUPS = {};
19895
+ function registerPersonLookup(kind, lookup2) {
19896
+ PERSON_LOOKUPS[kind] = lookup2;
19897
+ }
19898
+ function getPersonLookup(kind) {
19899
+ return PERSON_LOOKUPS[kind] ?? null;
19900
+ }
19648
19901
  var ARCHETYPE_DISPLAY = {
19649
19902
  person: { table: "people", labelField: "name", subtitleFields: ["phone"], groupField: "kind" },
19650
19903
  product: { table: "products", labelField: "name", subtitleFields: ["sku", "description"], priceField: "price" },
@@ -20265,7 +20518,7 @@ function formatAge(dob) {
20265
20518
  return null;
20266
20519
  }
20267
20520
  }
20268
- function PersonLink2({ personId, name, lookup: lookup2, profileHref, tab, size = "default", className }) {
20521
+ function PersonLink2({ personId, name, lookup: lookup2, kind, profileHref, tab, size = "default", className }) {
20269
20522
  const [person, setPerson] = useState(null);
20270
20523
  const [loading, setLoading] = useState(false);
20271
20524
  const [fetched, setFetched] = useState(false);
@@ -20274,7 +20527,7 @@ function PersonLink2({ personId, name, lookup: lookup2, profileHref, tab, size =
20274
20527
  const fetchPerson = useCallback(() => {
20275
20528
  if (!fetched && personId) {
20276
20529
  setLoading(true);
20277
- (lookup2 ?? personLookup).getById(personId).then((result) => {
20530
+ (lookup2 ?? (kind ? getPersonLookup(kind) : null) ?? personLookup).getById(personId).then((result) => {
20278
20531
  setPerson(result);
20279
20532
  setFetched(true);
20280
20533
  setLoading(false);
@@ -20283,7 +20536,7 @@ function PersonLink2({ personId, name, lookup: lookup2, profileHref, tab, size =
20283
20536
  setLoading(false);
20284
20537
  });
20285
20538
  }
20286
- }, [personId, fetched, lookup2]);
20539
+ }, [personId, fetched, lookup2, kind]);
20287
20540
  const handleOpenChange = useCallback((v) => {
20288
20541
  setOpen(v);
20289
20542
  if (v) fetchPerson();
@@ -20306,13 +20559,14 @@ function PersonLink2({ personId, name, lookup: lookup2, profileHref, tab, size =
20306
20559
  const data = person?.data;
20307
20560
  const email = data?.email;
20308
20561
  const phone = data?.phone;
20309
- const kind = data?.kind ?? person?.group;
20562
+ const resolvedKind = kind ?? data?.kind ?? person?.group;
20310
20563
  const isActive = data?.is_active;
20311
20564
  const dob = data?.date_of_birth;
20312
20565
  const createdAt = data?.created_at;
20313
20566
  const age = dob ? formatAge(dob) : null;
20314
20567
  const initial = name.charAt(0).toUpperCase();
20315
- const resolvedHref = profileHref ?? (personId ? resolveEntityAnchor(personId, "person", kind ?? void 0, tab) : void 0);
20568
+ const registeredPath = kind ? getPersonPath(kind) : null;
20569
+ const resolvedHref = profileHref ?? (registeredPath ? `#${registeredPath}/${personId}${tab ? `/${tab}` : ""}` : void 0) ?? resolveEntityAnchor(personId, "person", resolvedKind ?? void 0, tab);
20316
20570
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
20317
20571
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
20318
20572
  "button",
@@ -20347,7 +20601,7 @@ function PersonLink2({ personId, name, lookup: lookup2, profileHref, tab, size =
20347
20601
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
20348
20602
  /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold truncate", children: name }),
20349
20603
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-0.5", children: [
20350
- kind && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground capitalize", children: kind }),
20604
+ resolvedKind && /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground capitalize", children: resolvedKind }),
20351
20605
  isActive !== void 0 && /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1 text-[10px] ${isActive ? "text-success" : "text-muted-foreground/50"}`, children: [
20352
20606
  /* @__PURE__ */ jsx("span", { className: `h-1.5 w-1.5 rounded-full ${isActive ? "bg-success" : "bg-muted-foreground/30"}` }),
20353
20607
  isActive ? "Active" : "Inactive"
@@ -20491,13 +20745,27 @@ function RecordPickerModal({
20491
20745
  placeholder = "Buscar\u2026",
20492
20746
  confirmLabel = "Adicionar",
20493
20747
  emptyLabel = "Nada encontrado.",
20494
- columns
20748
+ columns,
20749
+ onRefresh,
20750
+ fromCache
20495
20751
  }) {
20752
+ const t = useTranslation();
20496
20753
  const [term, setTerm] = useState("");
20497
20754
  const [items, setItems] = useState([]);
20498
20755
  const [loading, setLoading] = useState(false);
20499
20756
  const [picked, setPicked] = useState([]);
20500
20757
  const input = useRef(null);
20758
+ async function refreshNow() {
20759
+ onRefresh?.();
20760
+ setLoading(true);
20761
+ try {
20762
+ setItems(await search(term.trim()));
20763
+ } catch {
20764
+ setItems([]);
20765
+ } finally {
20766
+ setLoading(false);
20767
+ }
20768
+ }
20501
20769
  useEffect(() => {
20502
20770
  if (!open) {
20503
20771
  setTerm("");
@@ -20558,6 +20826,17 @@ function RecordPickerModal({
20558
20826
  "data-testid": "record-picker-search",
20559
20827
  className: "w-full bg-transparent py-2 text-sm outline-none"
20560
20828
  }
20829
+ ),
20830
+ onRefresh && /* @__PURE__ */ jsx(
20831
+ RefreshButton,
20832
+ {
20833
+ onRefresh: refreshNow,
20834
+ fromCache,
20835
+ label: t("crud.list.refresh"),
20836
+ labelCached: t("crud.list.refreshCached"),
20837
+ "data-testid": "record-picker-refresh",
20838
+ className: "shrink-0 rounded p-1 text-muted-foreground hover:text-foreground"
20839
+ }
20561
20840
  )
20562
20841
  ] }) }),
20563
20842
  /* @__PURE__ */ jsx(ModalBody, { className: "px-5 py-3", children: /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-md border", children: [
@@ -21306,6 +21585,6 @@ var renderApp = (...args) => {
21306
21585
  return renderApp$1(...args);
21307
21586
  };
21308
21587
 
21309
- export { AddConnectorButton, AddConnectorPanel, AdminProviders, AdminScaffold, AdminShell, AssistantInvite, ChatConversation, ClientOrdersTab, ConnectConsentPage, ConnectorDeliveries, ConnectorDetail, ConnectorMark, ConnectorRow, ConnectorSyncActions, ConnectorsHub, ConnectorsTab, ContactPicker, CredentialSinkMissingError, CrudCardGrid, CrudDetailPage, CrudDetailSkeleton, CrudFormPage, CrudFormSkeleton, CrudListView, CrudPage, CrudRecordModal, DeleteConfirmDialog, EntitlementGate, EntityLink, LimitGate, ModuleActionBar, MultiSelectRow, NOTE_LABEL_PREFIX, OrderDetailBlock, OrderDetailRow, OrderDetailSkeleton, OrderDetailView, OrderFormAddItemStep, OrderDetailBlock as OrderFormBlock, OrderFormItemRow, OrderFormItemsCard, OrderFormSkeleton, OrderFormView, OrderListView, PermissionGate, PersonLink2 as PersonLink, PersonTimeline, PluginRegistryManager, PluginSettingsPanel, ProjectsSettings, QuickActionsButton, RecordPickerModal, RegistryCrudView2 as RegistryCrudView, RouteErrorBoundary, RouteErrorScreen, SelectRow, SettingsGroup, SeuSiteWidget, StateChip, TextRow, ToggleRow, UpgradePrompt, WidgetSlot, navigateTo as adminNavigateTo, categoryLabel, connectorRoute, createArchetypeLookup, createClientOrdersProvider, createCrudPage, createFayzApp, createFayzProject, createCrudPage as createNativeCrudPage, createPerson, createPlaceholder, createPluginContext, createViewRouter, dedup, defaultProjectOf, defineSaas, deleteFayzProject, deriveAgentContract, exportToCSV, filterConnectors, formatActivityElapsed, formatCurrency4 as formatCurrency, getDefaultFayzProject, getFayzAgentClient, getPersonPath, getTimelineSources, listFayzProjects, needsCredentialSink, openAssistant, openConnectorsPanel, openEntity, parseViewId, publishAssistantActivity, registerAdminScaffold, registerAssistantLauncher, registerEntityPath, registerNotesTasksAdapter, registerTimelineSource, renderApp, resolveEntityAnchor, resolveEntityHref, resolveFayzAgentConnection, splitConnectorValues, submitConnection, unregisterAssistantLauncher, unregisterTimelineSource, updateFayzProject, useAdminPath, useAgentSurface, useAssistantActivity, useAssistantInviteStore, useAssistantLaunchers, useChatStore, useConnectors, useConnectorsPanelAvailable, useContextualNav, useContextualNavStore, useDelegateToAssistant, useFayzProjectsStore, useFieldRules, useHasRightRail, useModuleNavigation, useNavContribution, useNavContributionsStore, useNotesTasksBridge, usePersonTimeline, usePluginPrefs, usePublishAssistantActivity, useRegisterAssistantLauncher, useRightRailPanel, useRightRailStore, useShellRailAvailable, useTenantPluginSettings, withdrawAssistantActivity };
21588
+ export { AddConnectorButton, AddConnectorPanel, AdminProviders, AdminScaffold, AdminShell, AssistantInvite, ChatConversation, ClientOrdersTab, ConnectConsentPage, ConnectorDeliveries, ConnectorDetail, ConnectorMark, ConnectorRow, ConnectorSyncActions, ConnectorsHub, ConnectorsTab, ContactPicker, CredentialSinkMissingError, CrudCardGrid, CrudDetailPage, CrudDetailSkeleton, CrudFormPage, CrudFormSkeleton, CrudListView, CrudPage, CrudRecordModal, DeleteConfirmDialog, EntitlementGate, EntityLink, LimitGate, ModuleActionBar, MultiSelectRow, NOTE_LABEL_PREFIX, OrderDetailBlock, OrderDetailRow, OrderDetailSkeleton, OrderDetailView, OrderFormAddItemStep, OrderDetailBlock as OrderFormBlock, OrderFormItemRow, OrderFormItemsCard, OrderFormSkeleton, OrderFormView, OrderListView, PermissionGate, PersonLink2 as PersonLink, PersonTimeline, PluginRegistryManager, PluginSettingsPanel, ProjectsSettings, QuickActionsButton, RecordPickerModal, RegistryCrudView2 as RegistryCrudView, RouteErrorBoundary, RouteErrorScreen, SelectRow, SettingsGroup, SeuSiteWidget, StateChip, TextRow, ToggleRow, UpgradePrompt, WidgetSlot, navigateTo as adminNavigateTo, categoryLabel, connectorRoute, createArchetypeLookup, createClientOrdersProvider, createCrudPage, createFayzApp, createFayzProject, createCrudPage as createNativeCrudPage, createPerson, createPlaceholder, createPluginContext, createViewRouter, dedup, defaultProjectOf, defineSaas, deleteFayzProject, deriveAgentContract, exportToCSV, filterConnectors, formatActivityElapsed, formatCurrency4 as formatCurrency, getDefaultFayzProject, getFayzAgentClient, getPersonLookup, getPersonPath, getTimelineSources, listFayzProjects, needsCredentialSink, openAssistant, openConnectorsPanel, openEntity, parseViewId, publishAssistantActivity, registerAdminScaffold, registerAssistantLauncher, registerEntityPath, registerNotesTasksAdapter, registerPersonLookup, registerTimelineSource, renderApp, resolveEntityAnchor, resolveEntityHref, resolveFayzAgentConnection, splitConnectorValues, submitConnection, unregisterAssistantLauncher, unregisterTimelineSource, updateFayzProject, useAdminPath, useAgentSurface, useAssistantActivity, useAssistantInviteStore, useAssistantLaunchers, useChatStore, useConnectors, useConnectorsPanelAvailable, useContextualNav, useContextualNavStore, useDelegateToAssistant, useFayzProjectsStore, useFieldRules, useHasRightRail, useModuleNavigation, useNavContribution, useNavContributionsStore, useNotesTasksBridge, usePersonTimeline, usePluginPrefs, usePublishAssistantActivity, useRegisterAssistantLauncher, useRightRailPanel, useRightRailStore, useShellRailAvailable, useTenantPluginSettings, withdrawAssistantActivity };
21310
21589
  //# sourceMappingURL=index.js.map
21311
21590
  //# sourceMappingURL=index.js.map