Changed frontend to not reference local storage

This commit is contained in:
Lucas Oliveira 2025-12-23 13:22:48 -03:00
parent 633afde224
commit 527bc7f67e
9 changed files with 157 additions and 202 deletions

View file

@ -12,6 +12,7 @@ import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants";
import { useLoadingStore } from "@/stores/loadingStore";
import { useGetConversationsQuery } from "../api/queries/useGetConversationsQuery";
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
import { useGetSettingsQuery } from "../api/queries/useGetSettingsQuery";
import { AssistantMessage } from "./_components/assistant-message";
import { ChatInput, type ChatInputHandle } from "./_components/chat-input";
import Nudges from "./_components/nudges";
@ -638,27 +639,14 @@ function ChatPage() {
};
}, [endpoint, setPreviousResponseIds, setLoading]);
// Check if onboarding is complete by looking at local storage
const [isOnboardingComplete, setIsOnboardingComplete] = useState(() => {
if (typeof window === "undefined") return false;
return localStorage.getItem("onboarding-step") === null;
});
// Listen for storage changes to detect when onboarding completes
useEffect(() => {
const checkOnboarding = () => {
if (typeof window !== "undefined") {
setIsOnboardingComplete(
localStorage.getItem("onboarding-step") === null,
);
}
};
// Check periodically since storage events don't fire in the same tab
const interval = setInterval(checkOnboarding, 500);
return () => clearInterval(interval);
}, []);
// Get settings to check onboarding completion
const { data: settings } = useGetSettingsQuery();
// Check if onboarding is complete (current_step >= 4 means complete)
const TOTAL_ONBOARDING_STEPS = 4;
const isOnboardingComplete =
settings?.onboarding?.current_step !== undefined &&
settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS;
// Prepare filters for nudges (same as chat)
const processedFiltersForNudges = parsedFilterData?.filters

View file

@ -10,7 +10,6 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants";
import { cn } from "@/lib/utils";
export function AnimatedProviderSteps({
@ -18,7 +17,6 @@ export function AnimatedProviderSteps({
isCompleted,
setCurrentStep,
steps,
storageKey = ONBOARDING_CARD_STEPS_KEY,
processingStartTime,
hasError = false,
}: {
@ -26,25 +24,19 @@ export function AnimatedProviderSteps({
isCompleted: boolean;
setCurrentStep: (step: number) => void;
steps: string[];
storageKey?: string;
processingStartTime?: number | null;
hasError?: boolean;
}) {
const [startTime, setStartTime] = useState<number | null>(null);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// Initialize start time from prop or local storage
// Initialize start time from prop
useEffect(() => {
const storedElapsedTime = localStorage.getItem(storageKey);
if (isCompleted && storedElapsedTime) {
// If completed, use stored elapsed time
setElapsedTime(parseFloat(storedElapsedTime));
} else if (processingStartTime) {
if (processingStartTime) {
// Use the start time passed from parent (when user clicked Complete)
setStartTime(processingStartTime);
}
}, [storageKey, isCompleted, processingStartTime]);
}, [processingStartTime]);
// Progress through steps
useEffect(() => {
@ -56,14 +48,13 @@ export function AnimatedProviderSteps({
}
}, [currentStep, setCurrentStep, steps, isCompleted]);
// Calculate and store elapsed time when completed
// Calculate elapsed time when completed
useEffect(() => {
if (isCompleted && startTime) {
const elapsed = Date.now() - startTime;
setElapsedTime(elapsed);
localStorage.setItem(storageKey, elapsed.toString());
}
}, [isCompleted, startTime, storageKey]);
}, [isCompleted, startTime]);
const isDone = currentStep >= steps.length && !isCompleted && !hasError;

View file

@ -10,6 +10,7 @@ import {
useOnboardingMutation,
} from "@/app/api/mutations/useOnboardingMutation";
import { useOnboardingRollbackMutation } from "@/app/api/mutations/useOnboardingRollbackMutation";
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
import type { ProviderHealthResponse } from "@/app/api/queries/useProviderHealthQuery";
@ -25,7 +26,6 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants";
import { cn } from "@/lib/utils";
import { AnimatedProviderSteps } from "./animated-provider-steps";
import { AnthropicOnboarding } from "./anthropic-onboarding";
@ -173,6 +173,8 @@ const OnboardingCard = ({
// Track which tasks we've already handled to prevent infinite loops
const handledFailedTasksRef = useRef<Set<string>>(new Set());
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
// Query tasks to track completion
const { data: tasks } = useGetTasksQuery({
@ -307,11 +309,12 @@ const OnboardingCard = ({
console.log("Onboarding completed successfully", data);
// Save OpenRAG docs filter ID if sample data was ingested
if (data.openrag_docs_filter_id && typeof window !== "undefined") {
localStorage.setItem(
"onboarding_openrag_docs_filter_id",
data.openrag_docs_filter_id
);
if (data.openrag_docs_filter_id) {
// Save to backend
updateOnboardingMutation.mutateAsync({
openrag_docs_filter_id: data.openrag_docs_filter_id,
});
console.log("Saved OpenRAG docs filter ID:", data.openrag_docs_filter_id);
}
@ -674,7 +677,6 @@ const OnboardingCard = ({
setCurrentStep={setCurrentStep}
steps={isEmbedding ? EMBEDDING_STEP_LIST : STEP_LIST}
processingStartTime={processingStartTime}
storageKey={ONBOARDING_CARD_STEPS_KEY}
hasError={!!error}
/>
</motion.div>

View file

@ -3,6 +3,8 @@
import { useEffect, useRef, useState } from "react";
import { StickToBottom } from "use-stick-to-bottom";
import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
import { AssistantMessage } from "@/app/chat/_components/assistant-message";
import Nudges from "@/app/chat/_components/nudges";
import { UserMessage } from "@/app/chat/_components/user-message";
@ -10,11 +12,6 @@ import type { Message, SelectedFilters } from "@/app/chat/_types/types";
import OnboardingCard from "@/app/onboarding/_components/onboarding-card";
import { useChat } from "@/contexts/chat-context";
import { useChatStreaming } from "@/hooks/useChatStreaming";
import {
ONBOARDING_ASSISTANT_MESSAGE_KEY,
ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY,
ONBOARDING_SELECTED_NUDGE_KEY,
} from "@/lib/constants";
import { OnboardingStep } from "./onboarding-step";
import OnboardingUpload from "./onboarding-upload";
@ -36,43 +33,46 @@ export function OnboardingContent({
currentStep: number;
}) {
const { setConversationFilter, setCurrentConversationId } = useChat();
const { data: settings } = useGetSettingsQuery();
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
const parseFailedRef = useRef(false);
const [responseId, setResponseId] = useState<string | null>(null);
// Initialize from backend settings
const [selectedNudge, setSelectedNudge] = useState<string>(() => {
// Retrieve selected nudge from localStorage on mount
if (typeof window === "undefined") return "";
return localStorage.getItem(ONBOARDING_SELECTED_NUDGE_KEY) || "";
return settings?.onboarding?.selected_nudge || "";
});
const [assistantMessage, setAssistantMessage] = useState<Message | null>(
() => {
// Retrieve assistant message from localStorage on mount
if (typeof window === "undefined") return null;
const savedMessage = localStorage.getItem(
ONBOARDING_ASSISTANT_MESSAGE_KEY,
);
if (savedMessage) {
try {
const parsed = JSON.parse(savedMessage);
// Convert timestamp string back to Date object
return {
...parsed,
timestamp: new Date(parsed.timestamp),
};
} catch (error) {
console.error("Failed to parse saved assistant message:", error);
parseFailedRef.current = true;
// Clear corrupted data - will go back a step in useEffect
if (typeof window !== "undefined") {
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY);
}
return null;
}
// Get from backend settings
if (settings?.onboarding?.assistant_message) {
const msg = settings.onboarding.assistant_message;
return {
role: msg.role as "user" | "assistant",
content: msg.content,
timestamp: new Date(msg.timestamp),
};
}
return null;
},
);
// Sync state when settings change
useEffect(() => {
if (settings?.onboarding?.selected_nudge) {
setSelectedNudge(settings.onboarding.selected_nudge);
}
if (settings?.onboarding?.assistant_message) {
const msg = settings.onboarding.assistant_message;
setAssistantMessage({
role: msg.role as "user" | "assistant",
content: msg.content,
timestamp: new Date(msg.timestamp),
});
}
}, [settings?.onboarding]);
// Handle parse errors by going back a step
useEffect(() => {
if (parseFailedRef.current && currentStep >= 2) {
@ -83,28 +83,23 @@ export function OnboardingContent({
const { streamingMessage, isLoading, sendMessage } = useChatStreaming({
onComplete: async (message, newResponseId) => {
setAssistantMessage(message);
// Save assistant message to localStorage when complete
if (typeof window !== "undefined") {
try {
localStorage.setItem(
ONBOARDING_ASSISTANT_MESSAGE_KEY,
JSON.stringify(message),
);
} catch (error) {
console.error(
"Failed to save assistant message to localStorage:",
error,
);
}
}
// Save assistant message to backend
await updateOnboardingMutation.mutateAsync({
assistant_message: {
role: message.role,
content: message.content,
timestamp: message.timestamp.toISOString(),
},
});
if (newResponseId) {
setResponseId(newResponseId);
// Set the current conversation ID
setCurrentConversationId(newResponseId);
// Save the filter association for this conversation
const openragDocsFilterId = localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY);
// Get filter ID from backend settings
const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id;
if (openragDocsFilterId) {
try {
// Load the filter and set it in the context with explicit responseId
@ -136,21 +131,17 @@ export function OnboardingContent({
const handleNudgeClick = async (nudge: string) => {
setSelectedNudge(nudge);
// Save selected nudge to localStorage
if (typeof window !== "undefined") {
localStorage.setItem(ONBOARDING_SELECTED_NUDGE_KEY, nudge);
}
setAssistantMessage(null);
// Clear saved assistant message when starting a new conversation
if (typeof window !== "undefined") {
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
}
// Save selected nudge to backend and clear assistant message
await updateOnboardingMutation.mutateAsync({
selected_nudge: nudge,
assistant_message: null,
});
setTimeout(async () => {
// Check if we have the OpenRAG docs filter ID (sample data was ingested)
const openragDocsFilterId =
typeof window !== "undefined"
? localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY)
: null;
const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id;
// Load and set the OpenRAG docs filter if available
let filterToUse = null;

View file

@ -3,14 +3,11 @@ import { AnimatePresence, motion } from "motion/react";
import { type ChangeEvent, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { useCreateFilter } from "@/app/api/mutations/useCreateFilter";
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
import { useGetNudgesQuery } from "@/app/api/queries/useGetNudgesQuery";
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
import { AnimatedProviderSteps } from "@/app/onboarding/_components/animated-provider-steps";
import { Button } from "@/components/ui/button";
import {
ONBOARDING_UPLOAD_STEPS_KEY,
ONBOARDING_USER_DOC_FILTER_ID_KEY,
} from "@/lib/constants";
import { uploadFile } from "@/lib/upload-utils";
interface OnboardingUploadProps {
@ -27,6 +24,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
const [isCreatingFilter, setIsCreatingFilter] = useState(false);
const createFilterMutation = useCreateFilter();
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
const STEP_LIST = [
"Uploading your document",
@ -103,12 +101,13 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
description: `Filter for ${filename}`,
queryData: queryData,
})
.then((result) => {
if (result.filter?.id && typeof window !== "undefined") {
localStorage.setItem(
ONBOARDING_USER_DOC_FILTER_ID_KEY,
result.filter.id,
);
.then(async (result) => {
if (result.filter?.id) {
// Save to backend
await updateOnboardingMutation.mutateAsync({
user_doc_filter_id: result.filter.id,
});
console.log(
"Created knowledge filter for uploaded document",
result.filter.id,
@ -267,7 +266,6 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
setCurrentStep={setCurrentStep}
isCompleted={false}
steps={STEP_LIST}
storageKey={ONBOARDING_UPLOAD_STEPS_KEY}
/>
</motion.div>
)}

View file

@ -9,6 +9,7 @@ import {
} from "@/app/api/queries/useGetConversationsQuery";
import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery";
import type { Settings } from "@/app/api/queries/useGetSettingsQuery";
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
import { OnboardingContent } from "@/app/onboarding/_components/onboarding-content";
import { ProgressBar } from "@/app/onboarding/_components/progress-bar";
import { AnimatedConditional } from "@/components/animated-conditional";
@ -19,13 +20,6 @@ import { useChat } from "@/contexts/chat-context";
import {
ANIMATION_DURATION,
HEADER_HEIGHT,
ONBOARDING_ASSISTANT_MESSAGE_KEY,
ONBOARDING_CARD_STEPS_KEY,
ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY,
ONBOARDING_SELECTED_NUDGE_KEY,
ONBOARDING_STEP_KEY,
ONBOARDING_UPLOAD_STEPS_KEY,
ONBOARDING_USER_DOC_FILTER_ID_KEY,
SIDEBAR_WIDTH,
TOTAL_ONBOARDING_STEPS,
} from "@/lib/constants";
@ -50,21 +44,27 @@ export function ChatRenderer({
setOnboardingComplete,
} = useChat();
// Initialize onboarding state based on local storage and settings
// Initialize onboarding state from backend settings
const [currentStep, setCurrentStep] = useState<number>(() => {
if (typeof window === "undefined") return 0;
const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY);
return savedStep !== null ? parseInt(savedStep, 10) : 0;
return settings?.onboarding?.current_step ?? 0;
});
const [showLayout, setShowLayout] = useState<boolean>(() => {
if (typeof window === "undefined") return false;
const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY);
// Show layout if settings.edited is true and if no onboarding step is saved
const isEdited = settings?.edited ?? true;
return isEdited ? savedStep === null : false;
// Show layout only if onboarding is complete (current_step >= TOTAL_ONBOARDING_STEPS)
// This means onboarding will show even if edited=true, as long as it's not complete
const onboardingStep = settings?.onboarding?.current_step ?? 0;
return onboardingStep >= TOTAL_ONBOARDING_STEPS;
});
// Update currentStep and showLayout when settings change
useEffect(() => {
if (settings?.onboarding?.current_step !== undefined) {
setCurrentStep(settings.onboarding.current_step);
// Update showLayout based on whether onboarding is complete
setShowLayout(settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS);
}
}, [settings?.onboarding?.current_step]);
// Only fetch conversations on chat page
const isOnChatPage = pathname === "/" || pathname === "/chat";
const { data: conversations = [], isLoading: isConversationsLoading } =
@ -108,18 +108,18 @@ export function ChatRenderer({
}
}
// Try to get the appropriate filter ID
// Try to get the appropriate filter ID from settings
let filterId: string | null = null;
if (preferUserDoc) {
// Completed full onboarding - prefer user document filter
filterId = localStorage.getItem(ONBOARDING_USER_DOC_FILTER_ID_KEY);
filterId = settings?.onboarding?.user_doc_filter_id || null;
console.log("[FILTER] User doc filter ID:", filterId);
}
// Fall back to OpenRAG docs filter
if (!filterId) {
filterId = localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY);
filterId = settings?.onboarding?.openrag_docs_filter_id || null;
console.log("[FILTER] OpenRAG docs filter ID:", filterId);
}
@ -149,25 +149,29 @@ export function ChatRenderer({
[setConversationFilter]
);
// Save current step to local storage whenever it changes
useEffect(() => {
if (typeof window !== "undefined" && !showLayout) {
localStorage.setItem(ONBOARDING_STEP_KEY, currentStep.toString());
}
}, [currentStep, showLayout]);
// Note: Current step is now saved to backend via handleStepComplete
// No need to save on every change, only on completion
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
const handleStepComplete = async () => {
if (currentStep < TOTAL_ONBOARDING_STEPS - 1) {
setCurrentStep(currentStep + 1);
const nextStep = currentStep + 1;
setCurrentStep(nextStep);
// Save step to backend
await updateOnboardingMutation.mutateAsync({ current_step: nextStep });
} else {
// Onboarding is complete - remove from local storage and show layout
if (typeof window !== "undefined") {
localStorage.removeItem(ONBOARDING_STEP_KEY);
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY);
localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY);
localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY);
}
// Onboarding is complete - set step to TOTAL_ONBOARDING_STEPS to indicate completion
// and clear intermediate state in backend
await updateOnboardingMutation.mutateAsync({
current_step: TOTAL_ONBOARDING_STEPS,
assistant_message: null,
selected_nudge: null,
card_steps: null,
upload_steps: null,
openrag_docs_filter_id: null,
user_doc_filter_id: null,
});
// Mark onboarding as complete in context
setOnboardingComplete(true);
@ -180,36 +184,35 @@ export function ChatRenderer({
// This will pick up the default filter we just set
await startNewConversation();
// Clean up onboarding filter IDs now that we've set the default
if (typeof window !== "undefined") {
localStorage.removeItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY);
localStorage.removeItem(ONBOARDING_USER_DOC_FILTER_ID_KEY);
console.log("[FILTER] Cleaned up onboarding filter IDs");
}
setShowLayout(true);
}
};
const handleStepBack = () => {
const handleStepBack = async () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1);
const prevStep = currentStep - 1;
setCurrentStep(prevStep);
// Save step to backend
await updateOnboardingMutation.mutateAsync({ current_step: prevStep });
}
};
const handleSkipOnboarding = () => {
// Skip onboarding by marking it as complete
if (typeof window !== "undefined") {
localStorage.removeItem(ONBOARDING_STEP_KEY);
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY);
localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY);
localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY);
}
const handleSkipOnboarding = async () => {
// Skip onboarding by marking it as complete in backend
await updateOnboardingMutation.mutateAsync({
current_step: TOTAL_ONBOARDING_STEPS,
assistant_message: null,
selected_nudge: null,
card_steps: null,
upload_steps: null,
openrag_docs_filter_id: null,
user_doc_filter_id: null,
});
// Mark onboarding as complete in context
setOnboardingComplete(true);
// Store the OpenRAG docs filter as default for new conversations
storeDefaultFilterForNewConversations(false);
await storeDefaultFilterForNewConversations(false);
setShowLayout(true);
};

View file

@ -10,7 +10,6 @@ import {
useRef,
useState,
} from "react";
import { ONBOARDING_STEP_KEY } from "@/lib/constants";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
export type EndpointType = "chat" | "langflow";
@ -129,28 +128,15 @@ export function ChatProvider({ children }: ChatProviderProps) {
return false;
});
// Sync onboarding completion state with settings.edited and localStorage
// Sync onboarding completion state with settings from backend
useEffect(() => {
const checkOnboarding = () => {
if (typeof window !== "undefined") {
// Onboarding is complete if settings.edited is true AND step key is null
const stepKeyExists = localStorage.getItem(ONBOARDING_STEP_KEY) !== null;
const isEdited = settings?.edited === true;
// Complete if edited is true and step key doesn't exist (onboarding flow finished)
setIsOnboardingComplete(isEdited && !stepKeyExists);
}
};
// Check on mount and when settings change
checkOnboarding();
// Listen for storage events (for cross-tab sync)
window.addEventListener("storage", checkOnboarding);
return () => {
window.removeEventListener("storage", checkOnboarding);
};
}, [settings?.edited]);
const TOTAL_ONBOARDING_STEPS = 4;
// Onboarding is complete if current_step >= 4
const isComplete =
settings?.onboarding?.current_step !== undefined &&
settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS;
setIsOnboardingComplete(isComplete);
}, [settings?.onboarding?.current_step]);
const setOnboardingComplete = useCallback((complete: boolean) => {
setIsOnboardingComplete(complete);

View file

@ -17,8 +17,8 @@ import {
type TaskFileEntry,
useGetTasksQuery,
} from "@/app/api/queries/useGetTasksQuery";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { useAuth } from "@/contexts/auth-context";
import { ONBOARDING_STEP_KEY } from "@/lib/constants";
// Task interface is now imported from useGetTasksQuery
export type { Task };
@ -90,11 +90,18 @@ export function TaskProvider({ children }: { children: React.ReactNode }) {
},
});
// Get settings to check if onboarding is active
const { data: settings } = useGetSettingsQuery();
// Helper function to check if onboarding is active
const isOnboardingActive = useCallback(() => {
if (typeof window === "undefined") return false;
return localStorage.getItem(ONBOARDING_STEP_KEY) !== null;
}, []);
const TOTAL_ONBOARDING_STEPS = 4;
// Onboarding is active if current_step < 4
return (
settings?.onboarding?.current_step !== undefined &&
settings.onboarding.current_step < TOTAL_ONBOARDING_STEPS
);
}, [settings?.onboarding?.current_step]);
const refetchSearch = useCallback(() => {
queryClient.invalidateQueries({

View file

@ -37,17 +37,6 @@ export const SIDEBAR_WIDTH = 280;
export const HEADER_HEIGHT = 54;
export const TOTAL_ONBOARDING_STEPS = 4;
/**
* Local Storage Keys
*/
export const ONBOARDING_STEP_KEY = "onboarding_current_step";
export const ONBOARDING_ASSISTANT_MESSAGE_KEY = "onboarding_assistant_message";
export const ONBOARDING_SELECTED_NUDGE_KEY = "onboarding_selected_nudge";
export const ONBOARDING_CARD_STEPS_KEY = "onboarding_card_steps";
export const ONBOARDING_UPLOAD_STEPS_KEY = "onboarding_upload_steps";
export const ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY = "onboarding_openrag_docs_filter_id";
export const ONBOARDING_USER_DOC_FILTER_ID_KEY = "onboarding_user_doc_filter_id";
export const FILES_REGEX =
/(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;