goji-search 2.0.8 → 3.0.1
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/README.md +81 -5
- package/dist/goji-search/assets/chat_logo.jpeg +0 -0
- package/dist/goji-search/components/elements/action-buttons.d.ts +2 -1
- package/dist/goji-search/components/elements/calendar-integration.d.ts +1 -0
- package/dist/goji-search/components/elements/choice-bubbles.d.ts +15 -0
- package/dist/goji-search/components/elements/deep-analysis-card.d.ts +16 -0
- package/dist/goji-search/components/elements/email-input-box.d.ts +23 -0
- package/dist/goji-search/components/elements/expandable-section.d.ts +7 -0
- package/dist/goji-search/components/elements/inspiration-menu.d.ts +6 -3
- package/dist/goji-search/components/elements/message-list.d.ts +9 -2
- package/dist/goji-search/components/elements/search-input.d.ts +9 -5
- package/dist/goji-search/components/elements/slide-panel.d.ts +15 -0
- package/dist/goji-search/components/error-boundary.d.ts +16 -0
- package/dist/goji-search/components/goji-search-component.d.ts +7 -5
- package/dist/goji-search/hooks/useChatStream.d.ts +48 -0
- package/dist/goji-search/hooks/useCompanyTheme.d.ts +27 -0
- package/dist/goji-search/hooks/useIdleReengagement.d.ts +90 -0
- package/dist/goji-search/hooks/useReturningVisitor.d.ts +61 -0
- package/dist/goji-search/lib/debug.d.ts +9 -0
- package/dist/goji-search/lib/goji-client.d.ts +204 -5
- package/dist/goji-search/lib/utm-capture.d.ts +40 -0
- package/dist/goji-search/lib/visitor-id.d.ts +62 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8991 -6117
- package/dist/setupTests.d.ts +27 -0
- package/dist/web-component.d.ts +59 -0
- package/dist/widget.js +572 -0
- package/package.json +20 -5
- package/dist/goji-search/components/elements/language-selector.d.ts +0 -10
- package/dist/goji-search/components/elements/suggested-questions.d.ts +0 -6
package/README.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Goji Search
|
|
2
2
|
|
|
3
|
+
AI-powered chat widget for websites. Embeddable as a React component, Web Component, or vanilla JS widget.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Real-time streaming chat via WebSocket
|
|
8
|
+
- Multi-tenant support with company-specific branding
|
|
9
|
+
- Returning visitor detection and personalized welcome messages
|
|
10
|
+
- Idle re-engagement prompts
|
|
11
|
+
- Deep analysis cards (ROI, comparisons, cost breakdowns)
|
|
12
|
+
- CTA cards with calendar booking and email capture
|
|
13
|
+
- Multi-language support (EN, FR, IT, ES, DE)
|
|
14
|
+
- Voice transcription
|
|
15
|
+
- Shadow DOM style isolation for embedding
|
|
16
|
+
- Gated debug logging
|
|
17
|
+
|
|
3
18
|
## Installation
|
|
4
19
|
|
|
5
20
|
```bash
|
|
@@ -8,16 +23,77 @@ npm install goji-search
|
|
|
8
23
|
yarn add goji-search
|
|
9
24
|
```
|
|
10
25
|
|
|
11
|
-
##
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### React
|
|
12
29
|
|
|
13
30
|
```tsx
|
|
14
31
|
import { GojiSearchComponent } from 'goji-search';
|
|
15
32
|
|
|
16
33
|
function App() {
|
|
17
34
|
return (
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
35
|
+
<GojiSearchComponent
|
|
36
|
+
companyId="your-company-id"
|
|
37
|
+
apiKey="goji_yourcompany_prod_xxx"
|
|
38
|
+
apiUrl="https://api.getgoji.ai"
|
|
39
|
+
/>
|
|
21
40
|
);
|
|
22
41
|
}
|
|
23
|
-
```
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### HTML / Vanilla JS
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<script src="https://cdn.getgoji.ai/widget.js"></script>
|
|
48
|
+
<script>
|
|
49
|
+
GojiWidget.init({
|
|
50
|
+
companyId: 'your-company-id',
|
|
51
|
+
apiKey: 'goji_yourcompany_prod_xxx',
|
|
52
|
+
apiUrl: 'https://api.getgoji.ai'
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Required | Description |
|
|
60
|
+
|------|------|----------|-------------|
|
|
61
|
+
| `companyId` | `string` | Yes | Your company identifier |
|
|
62
|
+
| `apiKey` | `string` | Yes | Your Goji API key |
|
|
63
|
+
| `apiUrl` | `string` | No | Backend API URL (default: `http://localhost:8000`) |
|
|
64
|
+
| `aiColor` | `string` | No | AI avatar color in HEX (falls back to brand color from company theme) |
|
|
65
|
+
| `userMessageBackgroundColor` | `string` | No | User message bubble background color |
|
|
66
|
+
|
|
67
|
+
## Exports
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
// React component and error boundary
|
|
71
|
+
import { GojiSearchComponent, GojiSearchErrorBoundary } from 'goji-search';
|
|
72
|
+
|
|
73
|
+
// API client
|
|
74
|
+
import { GojiSearchClient, createGojiClient } from 'goji-search';
|
|
75
|
+
|
|
76
|
+
// Hooks
|
|
77
|
+
import { useReturningVisitor, useVisitorId } from 'goji-search';
|
|
78
|
+
|
|
79
|
+
// UTM and visitor utilities
|
|
80
|
+
import { captureVisitorContext, getVisitorContext, getOrCreateVisitorId } from 'goji-search';
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Development
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install # Install dependencies
|
|
87
|
+
npm run dev # Development server
|
|
88
|
+
npm run build # Build React library + widget bundle
|
|
89
|
+
npm run lint # Lint
|
|
90
|
+
npm run test # Run tests (watch mode)
|
|
91
|
+
npm run test:run # Run tests once
|
|
92
|
+
npm run test:coverage # Run tests with coverage
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
See [USAGE.md](./USAGE.md) for detailed documentation, advanced usage, and API reference.
|
|
98
|
+
|
|
99
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
Binary file
|
|
@@ -4,10 +4,11 @@ interface ActionButtonsProps {
|
|
|
4
4
|
isStreaming: boolean;
|
|
5
5
|
searchQuery: string;
|
|
6
6
|
isRecording?: boolean;
|
|
7
|
+
hideCalendar?: boolean;
|
|
7
8
|
onSubmit: () => void;
|
|
8
9
|
onVoiceSearch: () => void;
|
|
9
10
|
onClose: () => void;
|
|
10
11
|
onCalendarClick: () => void;
|
|
11
12
|
}
|
|
12
|
-
export declare function ActionButtons({ size, isHovered, isStreaming, searchQuery, isRecording, onSubmit, onVoiceSearch, onClose, onCalendarClick, }: ActionButtonsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
|
+
export declare function ActionButtons({ size, isHovered, isStreaming, searchQuery, isRecording, hideCalendar, onSubmit, onVoiceSearch, onClose, onCalendarClick, }: ActionButtonsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
13
14
|
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChoiceBubbles Component
|
|
3
|
+
*
|
|
4
|
+
* Renders interactive choice buttons for guided conversations.
|
|
5
|
+
* When clicked, the selected choice is sent as the user's next message.
|
|
6
|
+
*/
|
|
7
|
+
interface ChoiceBubblesProps {
|
|
8
|
+
choices: string[];
|
|
9
|
+
onChoiceClick: (choice: string) => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
variant?: "default" | "faded" | "dark";
|
|
12
|
+
aiColor?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function ChoiceBubbles({ choices, onChoiceClick, disabled, variant, aiColor, }: ChoiceBubblesProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type AnalysisType = "roi" | "comparison" | "cost_breakdown" | "key_metrics";
|
|
2
|
+
export type ConfidenceLevel = "high" | "medium" | "low";
|
|
3
|
+
export interface DeepAnalysisData {
|
|
4
|
+
analysis_type: AnalysisType;
|
|
5
|
+
data: Record<string, any>;
|
|
6
|
+
confidence: ConfidenceLevel;
|
|
7
|
+
data_source_note: string;
|
|
8
|
+
generated_at?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface DeepAnalysisCardProps {
|
|
11
|
+
analysisType: AnalysisType;
|
|
12
|
+
data?: DeepAnalysisData;
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
onCollapse?: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare function DeepAnalysisCard({ analysisType, data, isLoading, onCollapse, }: DeepAnalysisCardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email Input Box Component
|
|
3
|
+
*
|
|
4
|
+
* Displays a dedicated email input box when the backend requests email collection.
|
|
5
|
+
* - Appears below follow-up questions (after ClosingCard if present)
|
|
6
|
+
* - Includes email validation
|
|
7
|
+
* - Animated entrance/exit
|
|
8
|
+
* - Respects localized prompt text from backend
|
|
9
|
+
*/
|
|
10
|
+
interface EmailInputBoxProps {
|
|
11
|
+
/** Localized prompt text from backend */
|
|
12
|
+
promptText: string;
|
|
13
|
+
/** Session ID for submission */
|
|
14
|
+
sessionId?: string;
|
|
15
|
+
/** Callback when email is submitted */
|
|
16
|
+
onSubmit: (email: string) => Promise<void>;
|
|
17
|
+
/** Callback when user dismisses the box */
|
|
18
|
+
onDismiss?: () => void;
|
|
19
|
+
/** Additional CSS class */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function EmailInputBox({ promptText, sessionId, onSubmit, onDismiss, className, }: EmailInputBoxProps): import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface ExpandableSectionProps {
|
|
2
|
+
title: string;
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
defaultExpanded?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function ExpandableSection({ title, children, defaultExpanded }: ExpandableSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
1
|
interface InspirationMenuProps {
|
|
3
2
|
questions: string[];
|
|
4
3
|
onQuestionClick: (question: string) => void;
|
|
5
4
|
onClose: () => void;
|
|
6
5
|
suggestedLabel?: string;
|
|
7
|
-
|
|
6
|
+
showLanguageSelector?: boolean;
|
|
7
|
+
supportedLanguages?: string[];
|
|
8
|
+
currentLanguage?: string;
|
|
9
|
+
onLanguageChange?: (lang: string) => void;
|
|
10
|
+
languageLabels?: Record<string, string>;
|
|
8
11
|
color?: string;
|
|
9
12
|
}
|
|
10
|
-
export declare function InspirationMenu({ questions, onQuestionClick, onClose, suggestedLabel,
|
|
13
|
+
export declare function InspirationMenu({ questions, onQuestionClick, onClose, suggestedLabel, showLanguageSelector, supportedLanguages, currentLanguage, onLanguageChange, languageLabels, color }: InspirationMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
11
14
|
export {};
|
|
@@ -11,6 +11,11 @@ interface Message {
|
|
|
11
11
|
content: string;
|
|
12
12
|
timestamp: number;
|
|
13
13
|
sources?: ChatSource[];
|
|
14
|
+
isReengagement?: boolean;
|
|
15
|
+
choices?: string[];
|
|
16
|
+
choicesSelected?: boolean;
|
|
17
|
+
choicesVariant?: "default" | "faded" | "dark";
|
|
18
|
+
choiceMode?: "send" | "action";
|
|
14
19
|
}
|
|
15
20
|
interface MessageListProps {
|
|
16
21
|
messages: Message[];
|
|
@@ -18,7 +23,9 @@ interface MessageListProps {
|
|
|
18
23
|
aiAvatarSrc?: string;
|
|
19
24
|
size?: "m" | "l" | "xl" | "s" | "xs";
|
|
20
25
|
brandName?: string;
|
|
21
|
-
|
|
26
|
+
onChoiceClick?: (messageIndex: number, choice: string) => void;
|
|
27
|
+
userMessageBackgroundColor?: string;
|
|
28
|
+
aiColor?: string;
|
|
22
29
|
}
|
|
23
|
-
export declare function MessageList({ messages, isStreaming, aiAvatarSrc, size, brandName,
|
|
30
|
+
export declare function MessageList({ messages, isStreaming, aiAvatarSrc, size, brandName, onChoiceClick, userMessageBackgroundColor, aiColor }: MessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
24
31
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
|
-
import type { ReactNode } from "react";
|
|
3
2
|
interface SearchInputProps {
|
|
4
3
|
value: string;
|
|
5
4
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
@@ -8,13 +7,18 @@ interface SearchInputProps {
|
|
|
8
7
|
placeholder: string;
|
|
9
8
|
size: "xs" | "s" | "m" | "l" | "xl";
|
|
10
9
|
setSize: (size: "xs" | "s" | "m" | "l" | "xl") => void;
|
|
11
|
-
inputRef: React.RefObject<HTMLTextAreaElement>;
|
|
10
|
+
inputRef: React.RefObject<HTMLTextAreaElement | null>;
|
|
12
11
|
inspirationQuestions: string[];
|
|
13
12
|
onInspirationClick: (question: string) => void;
|
|
14
|
-
sparkleRef: React.RefObject<HTMLDivElement>;
|
|
13
|
+
sparkleRef: React.RefObject<HTMLDivElement | null>;
|
|
15
14
|
suggestedLabel?: string;
|
|
16
|
-
|
|
15
|
+
showLanguageSelector?: boolean;
|
|
16
|
+
supportedLanguages?: string[];
|
|
17
|
+
currentLanguage?: string;
|
|
18
|
+
onLanguageChange?: (lang: string) => void;
|
|
19
|
+
languageLabels?: Record<string, string>;
|
|
20
|
+
forceMenuOpenToken?: number;
|
|
17
21
|
color?: string;
|
|
18
22
|
}
|
|
19
|
-
export declare function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, setSize, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, suggestedLabel,
|
|
23
|
+
export declare function SearchInput({ value, onChange, onFocus, onBlur, placeholder, size, setSize, inputRef, inspirationQuestions, onInspirationClick, sparkleRef, suggestedLabel, showLanguageSelector, supportedLanguages, currentLanguage, onLanguageChange, languageLabels, forceMenuOpenToken, color }: SearchInputProps): import("react/jsx-runtime").JSX.Element;
|
|
20
24
|
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { VisualSlide } from "../../lib/goji-client";
|
|
2
|
+
interface SlidePanelProps {
|
|
3
|
+
/** Visual slides with id and description */
|
|
4
|
+
visualSlides: VisualSlide[];
|
|
5
|
+
isOpen: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
/** Base URL for slide images (defaults to /slides/) */
|
|
8
|
+
slidesBasePath?: string;
|
|
9
|
+
/** Total number of slides (for display like "3/9") - optional */
|
|
10
|
+
totalSlides?: number;
|
|
11
|
+
/** Show description below media (default: false). For videos, this could be used for subtitles. */
|
|
12
|
+
showDescription?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function SlidePanel({ visualSlides, isOpen, onClose, slidesBasePath, totalSlides, showDescription, }: SlidePanelProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface ErrorBoundaryProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
5
|
+
}
|
|
6
|
+
interface ErrorBoundaryState {
|
|
7
|
+
hasError: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class GojiSearchErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
10
|
+
constructor(props: ErrorBoundaryProps);
|
|
11
|
+
static getDerivedStateFromError(_error: Error): ErrorBoundaryState;
|
|
12
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
13
|
+
private handleRetry;
|
|
14
|
+
render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
@@ -16,14 +16,16 @@ export interface GojiSearchComponentProps {
|
|
|
16
16
|
*/
|
|
17
17
|
apiKey: string;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Custom color for AI avatar animation (CSS color value)
|
|
20
|
+
* Used by web-component for theming
|
|
21
|
+
* @example "#4654F7"
|
|
21
22
|
*/
|
|
22
23
|
aiColor?: string;
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
|
|
25
|
+
* Custom background color for user message bubbles (CSS color value)
|
|
26
|
+
* Used by web-component for theming
|
|
27
|
+
* @example "#E8E8FF"
|
|
28
|
+
*/
|
|
27
29
|
userMessageBackgroundColor?: string;
|
|
28
30
|
/**
|
|
29
31
|
* Widget position on the horizontal axis.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { GojiSearchClient, ChatSource, CTACard, EmailRequest, DeepAnalysis, PendingAnalysis, VisualSlide } from "../lib/goji-client";
|
|
2
|
+
import type { VisitorContext } from "../lib/utm-capture";
|
|
3
|
+
export interface Message {
|
|
4
|
+
role: "user" | "assistant";
|
|
5
|
+
content: string;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
sources?: ChatSource[];
|
|
8
|
+
slideIds?: number[];
|
|
9
|
+
isReengagement?: boolean;
|
|
10
|
+
choices?: string[];
|
|
11
|
+
choicesSelected?: boolean;
|
|
12
|
+
choicesVariant?: "default" | "faded" | "dark";
|
|
13
|
+
choiceMode?: "send" | "action";
|
|
14
|
+
emailToken?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UseChatStreamOptions {
|
|
17
|
+
client: GojiSearchClient;
|
|
18
|
+
visitorId: string | undefined;
|
|
19
|
+
companyId: string;
|
|
20
|
+
supportedLanguages: string[];
|
|
21
|
+
onLanguageDetected?: (lang: string) => void;
|
|
22
|
+
onResponseComplete?: () => void;
|
|
23
|
+
resetIdleTimerRef: React.MutableRefObject<() => void>;
|
|
24
|
+
}
|
|
25
|
+
export interface UseChatStreamReturn {
|
|
26
|
+
messages: Message[];
|
|
27
|
+
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
28
|
+
isStreaming: boolean;
|
|
29
|
+
sessionId: string | undefined;
|
|
30
|
+
setSessionId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
|
31
|
+
currentCTACard: CTACard | null;
|
|
32
|
+
setCurrentCTACard: React.Dispatch<React.SetStateAction<CTACard | null>>;
|
|
33
|
+
currentEmailRequest: EmailRequest | null;
|
|
34
|
+
setCurrentEmailRequest: React.Dispatch<React.SetStateAction<EmailRequest | null>>;
|
|
35
|
+
pendingAnalysis: PendingAnalysis | null;
|
|
36
|
+
currentAnalysis: DeepAnalysis | null;
|
|
37
|
+
setCurrentAnalysis: React.Dispatch<React.SetStateAction<DeepAnalysis | null>>;
|
|
38
|
+
currentVisualSlides: VisualSlide[];
|
|
39
|
+
isSlidePanelOpen: boolean;
|
|
40
|
+
setIsSlidePanelOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
41
|
+
backendSuggestedQuestions: string[] | null;
|
|
42
|
+
setBackendSuggestedQuestions: React.Dispatch<React.SetStateAction<string[] | null>>;
|
|
43
|
+
streamingCancelRef: React.MutableRefObject<(() => void) | null>;
|
|
44
|
+
sendMessage: (message: string, language: string, visitorContext: VisitorContext | undefined, options?: {
|
|
45
|
+
preAction?: () => void;
|
|
46
|
+
}) => Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export declare function useChatStream({ client, visitorId, companyId, supportedLanguages, onLanguageDetected, onResponseComplete, resetIdleTimerRef, }: UseChatStreamOptions): UseChatStreamReturn;
|
|
@@ -12,10 +12,37 @@ interface CompanyTheme {
|
|
|
12
12
|
welcome_message: string;
|
|
13
13
|
placeholder_text: string;
|
|
14
14
|
initial_suggestions: string[];
|
|
15
|
+
languages?: {
|
|
16
|
+
supported: string[];
|
|
17
|
+
default: string;
|
|
18
|
+
};
|
|
19
|
+
calendar_link?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ThemeCSSVars {
|
|
22
|
+
'--brand-color': string;
|
|
23
|
+
'--brand-color-dark': string;
|
|
24
|
+
'--brand-color-light': string;
|
|
25
|
+
'--brand-color-lighter': string;
|
|
26
|
+
'--brand-color-rgb': string;
|
|
27
|
+
'--brand-color-dark-rgb': string;
|
|
15
28
|
}
|
|
16
29
|
export declare function useCompanyTheme(apiUrl: string, companyId: string, apiKey: string): {
|
|
17
30
|
theme: CompanyTheme | null;
|
|
31
|
+
cssVars: ThemeCSSVars | null;
|
|
18
32
|
loading: boolean;
|
|
19
33
|
error: string | null;
|
|
34
|
+
applyToElement: (element: HTMLElement | null) => void;
|
|
35
|
+
};
|
|
36
|
+
export declare function parseColor(color: string): {
|
|
37
|
+
r: number;
|
|
38
|
+
g: number;
|
|
39
|
+
b: number;
|
|
40
|
+
} | null;
|
|
41
|
+
export declare function hexToRgb(hex: string): {
|
|
42
|
+
r: number;
|
|
43
|
+
g: number;
|
|
44
|
+
b: number;
|
|
20
45
|
};
|
|
46
|
+
export declare function rgbToHex(r: number, g: number, b: number): string;
|
|
47
|
+
export declare function darken(color: string, amount: number): string;
|
|
21
48
|
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useIdleReengagement Hook
|
|
3
|
+
*
|
|
4
|
+
* Manages idle detection and re-engagement prompts for chat visitors.
|
|
5
|
+
* Detects when a visitor has been idle for a specified time and triggers
|
|
6
|
+
* a re-engagement request to the backend.
|
|
7
|
+
*/
|
|
8
|
+
export interface IdleReengagementConfig {
|
|
9
|
+
/** Time in ms before first re-engagement (default: 60000 = 60s) */
|
|
10
|
+
softTimeoutMs?: number;
|
|
11
|
+
/** Time in ms before second re-engagement (default: 120000 = 120s) */
|
|
12
|
+
hardTimeoutMs?: number;
|
|
13
|
+
/** Minimum time in ms between re-engagement attempts (default: 60000 = 60s) */
|
|
14
|
+
minBetweenMs?: number;
|
|
15
|
+
/** Maximum number of re-engagement attempts (default: 2) */
|
|
16
|
+
maxAttempts?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface IdleReengagementState {
|
|
19
|
+
/** Number of re-engagement attempts made this session */
|
|
20
|
+
attemptCount: number;
|
|
21
|
+
/** Whether re-engagement is currently blocked */
|
|
22
|
+
isBlocked: boolean;
|
|
23
|
+
/** Reason for blocking (if any) */
|
|
24
|
+
blockedReason: string | null;
|
|
25
|
+
/** Time until next re-engagement check (ms) */
|
|
26
|
+
timeUntilNextCheck: number | null;
|
|
27
|
+
/** Whether the idle timer is active */
|
|
28
|
+
isTimerActive: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface UseIdleReengagementOptions {
|
|
31
|
+
/** Configuration for timeouts and limits */
|
|
32
|
+
config?: IdleReengagementConfig;
|
|
33
|
+
/** Whether the widget is minimized (blocks re-engagement) */
|
|
34
|
+
isMinimized?: boolean;
|
|
35
|
+
/** Whether the assistant is currently streaming a response */
|
|
36
|
+
isStreaming?: boolean;
|
|
37
|
+
/** Whether a CTA card is currently displayed */
|
|
38
|
+
hasCTACard?: boolean;
|
|
39
|
+
/** Whether an email request is currently displayed */
|
|
40
|
+
hasEmailRequest?: boolean;
|
|
41
|
+
/** Whether the calendar is open */
|
|
42
|
+
isCalendarOpen?: boolean;
|
|
43
|
+
/** Whether there are any messages in the conversation */
|
|
44
|
+
hasMessages?: boolean;
|
|
45
|
+
/** Current session ID (required for re-engagement) */
|
|
46
|
+
sessionId?: string;
|
|
47
|
+
/** Callback when re-engagement should be triggered */
|
|
48
|
+
onReengagementTrigger?: (attempt: number) => void;
|
|
49
|
+
/** Whether idle detection should be enabled */
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface UseIdleReengagementResult {
|
|
53
|
+
/** Current state of the re-engagement system */
|
|
54
|
+
state: IdleReengagementState;
|
|
55
|
+
/** Reset the idle timer (call on user activity) */
|
|
56
|
+
resetTimer: () => void;
|
|
57
|
+
/** Stop the idle timer completely */
|
|
58
|
+
stopTimer: () => void;
|
|
59
|
+
/** Start or restart the idle timer */
|
|
60
|
+
startTimer: () => void;
|
|
61
|
+
/** Increment attempt count (call after re-engagement sent) */
|
|
62
|
+
incrementAttempt: () => void;
|
|
63
|
+
/** Reset the entire re-engagement state for a new session */
|
|
64
|
+
resetState: () => void;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Hook for managing idle detection and re-engagement prompts.
|
|
68
|
+
*
|
|
69
|
+
* Tracks visitor idle time and triggers re-engagement when thresholds are met.
|
|
70
|
+
* Respects blocking conditions (CTA displayed, streaming, minimized, etc.)
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```tsx
|
|
74
|
+
* const { state, resetTimer, incrementAttempt } = useIdleReengagement({
|
|
75
|
+
* isMinimized: size === "xs",
|
|
76
|
+
* isStreaming,
|
|
77
|
+
* hasCTACard: !!currentCTACard,
|
|
78
|
+
* hasMessages: messages.length > 0,
|
|
79
|
+
* sessionId,
|
|
80
|
+
* onReengagementTrigger: (attempt) => {
|
|
81
|
+
* // Request re-engagement from backend
|
|
82
|
+
* client.requestReengagement(sessionId, attempt)
|
|
83
|
+
* },
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* // Reset timer on user activity
|
|
87
|
+
* const handleUserActivity = () => resetTimer()
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function useIdleReengagement(options: UseIdleReengagementOptions): UseIdleReengagementResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useReturningVisitor Hook
|
|
3
|
+
*
|
|
4
|
+
* Manages returning visitor detection and personalized welcome messages.
|
|
5
|
+
* Checks if the visitor has been here before and loads their context.
|
|
6
|
+
*/
|
|
7
|
+
import type { GojiSearchClient, ReturningVisitorContext } from "../lib/goji-client";
|
|
8
|
+
export interface UseReturningVisitorResult {
|
|
9
|
+
/** Whether the visitor has been here before */
|
|
10
|
+
isReturning: boolean;
|
|
11
|
+
/** Whether we're still loading the visitor context */
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
/** The visitor's unique ID */
|
|
14
|
+
visitorId: string;
|
|
15
|
+
/** Full visitor context (if returning) */
|
|
16
|
+
visitorContext: ReturningVisitorContext | null;
|
|
17
|
+
/** Personalized or default welcome message */
|
|
18
|
+
welcomeMessage: string | null;
|
|
19
|
+
/** Visitor's preferred language (if returning) */
|
|
20
|
+
preferredLanguage: string | null;
|
|
21
|
+
/** Error if context loading failed */
|
|
22
|
+
error: Error | null;
|
|
23
|
+
/** Manually refresh the visitor context */
|
|
24
|
+
refresh: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export interface UseReturningVisitorOptions {
|
|
27
|
+
/** The GojiSearch client instance */
|
|
28
|
+
client: GojiSearchClient;
|
|
29
|
+
/** Default language for welcome message */
|
|
30
|
+
language?: string;
|
|
31
|
+
/** Whether to automatically fetch context on mount */
|
|
32
|
+
autoFetch?: boolean;
|
|
33
|
+
/** Callback when returning visitor is detected */
|
|
34
|
+
onReturningVisitor?: (context: ReturningVisitorContext) => void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Hook for managing returning visitor detection and personalization.
|
|
38
|
+
*
|
|
39
|
+
* On mount:
|
|
40
|
+
* 1. Gets or creates a visitor ID (stored in localStorage)
|
|
41
|
+
* 2. Checks if visitor is returning via backend API
|
|
42
|
+
* 3. Returns their context and personalized welcome message
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* const { isReturning, welcomeMessage, visitorId } = useReturningVisitor({
|
|
47
|
+
* client,
|
|
48
|
+
* language: "en",
|
|
49
|
+
* })
|
|
50
|
+
*
|
|
51
|
+
* // Use welcomeMessage instead of default welcome for returning visitors
|
|
52
|
+
* const displayMessage = isReturning ? welcomeMessage : defaultWelcome
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function useReturningVisitor(options: UseReturningVisitorOptions): UseReturningVisitorResult;
|
|
56
|
+
/**
|
|
57
|
+
* Lightweight hook that just provides the visitor ID without fetching context.
|
|
58
|
+
*
|
|
59
|
+
* Use this when you only need the visitor ID for tracking purposes.
|
|
60
|
+
*/
|
|
61
|
+
export declare function useVisitorId(): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Window {
|
|
3
|
+
__GOJI_DEBUG__?: boolean;
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
export declare function isGojiDebugEnabled(): boolean;
|
|
7
|
+
export declare function gojiDebugLog(...args: unknown[]): void;
|
|
8
|
+
export declare function gojiDebugWarn(...args: unknown[]): void;
|
|
9
|
+
export declare function gojiDebugError(...args: unknown[]): void;
|