@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.
- package/dist/{CommandPalette-2AL327XQ.js → CommandPalette-U7MMA2WH.js} +27 -4
- package/dist/CommandPalette-U7MMA2WH.js.map +1 -0
- package/dist/app/AdminShell.d.ts.map +1 -1
- package/dist/app/admin-app.d.ts.map +1 -1
- package/dist/app/config.d.ts +7 -0
- package/dist/app/config.d.ts.map +1 -1
- package/dist/components/chat/AssistantInvite.d.ts +10 -0
- package/dist/components/chat/AssistantInvite.d.ts.map +1 -1
- package/dist/components/chat/ChatFab.d.ts.map +1 -1
- package/dist/components/chat/ConfirmActionCard.d.ts.map +1 -1
- package/dist/components/chat/fab-anchor.d.ts +15 -0
- package/dist/components/chat/fab-anchor.d.ts.map +1 -0
- package/dist/components/connect/connector-map.generated.d.ts.map +1 -1
- package/dist/components/layout/CommandPalette.d.ts +5 -2
- package/dist/components/layout/CommandPalette.d.ts.map +1 -1
- package/dist/components/shared/PersonLink.d.ts +8 -1
- package/dist/components/shared/PersonLink.d.ts.map +1 -1
- package/dist/components/shared/RecordPickerModal.d.ts +7 -1
- package/dist/components/shared/RecordPickerModal.d.ts.map +1 -1
- package/dist/crud/CrudListView.d.ts +8 -3
- package/dist/crud/CrudListView.d.ts.map +1 -1
- package/dist/crud/CrudPage.d.ts.map +1 -1
- package/dist/crud/archetype-lookup.d.ts +4 -0
- package/dist/crud/archetype-lookup.d.ts.map +1 -1
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useGlobalSearch.d.ts +3 -1
- package/dist/hooks/useGlobalSearch.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +837 -558
- package/dist/index.js.map +1 -1
- package/dist/lib/assistant-invite.d.ts +18 -0
- package/dist/lib/assistant-invite.d.ts.map +1 -1
- package/dist/lib/cache.d.ts +2 -17
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/stores/createCrudStore.d.ts +8 -0
- package/dist/stores/createCrudStore.d.ts.map +1 -1
- package/dist/theme/utils.d.ts.map +1 -1
- package/package.json +6 -6
- 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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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/
|
|
1017
|
-
var
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
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
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
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
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
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
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
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({
|
|
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(
|
|
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
|
-
|
|
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({
|
|
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-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|