mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
chore: linting
This commit is contained in:
parent
4dda02c06c
commit
94e834134f
80 changed files with 443 additions and 404 deletions
|
|
@ -221,10 +221,7 @@ export default async function FreeHubPage() {
|
|||
<Separator className="my-12 max-w-4xl mx-auto" />
|
||||
|
||||
{/* In-content ad: above the model table */}
|
||||
<aside
|
||||
aria-label="Advertisement"
|
||||
className="max-w-4xl mx-auto mb-8 min-h-[100px]"
|
||||
>
|
||||
<aside aria-label="Advertisement" className="max-w-4xl mx-auto mb-8 min-h-[100px]">
|
||||
<AdUnit slot={ADSENSE_SLOTS.freeHubInContent} />
|
||||
</aside>
|
||||
|
||||
|
|
@ -353,10 +350,7 @@ export default async function FreeHubPage() {
|
|||
<Separator className="my-12 max-w-4xl mx-auto" />
|
||||
|
||||
{/* In-content ad: after CTA, before FAQ */}
|
||||
<aside
|
||||
aria-label="Advertisement"
|
||||
className="max-w-3xl mx-auto my-8 min-h-[100px]"
|
||||
>
|
||||
<aside aria-label="Advertisement" className="max-w-3xl mx-auto my-8 min-h-[100px]">
|
||||
<AdUnit slot={ADSENSE_SLOTS.freeHubBeforeFaq} />
|
||||
</aside>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ export default function PrivacyPolicy() {
|
|||
</p>
|
||||
<p className="mt-4">
|
||||
By accessing or using the Service, you acknowledge that you have read and understood
|
||||
this Privacy Policy. If you do not agree with our policies and practices, do not use
|
||||
the Service. We may modify this policy from time to time; material changes will be
|
||||
reflected by updating the "Last updated" date above.
|
||||
this Privacy Policy. If you do not agree with our policies and practices, do not use the
|
||||
Service. We may modify this policy from time to time; material changes will be reflected
|
||||
by updating the "Last updated" date above.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -71,9 +71,9 @@ export default function PrivacyPolicy() {
|
|||
Notion, Confluence, GitHub, and others) under the scopes you authorize.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Billing Data</strong> includes information necessary to process payments
|
||||
(such as transaction identifiers and credit balances). Card details are handled by
|
||||
our payment processor and are not stored on our servers.
|
||||
<strong>Billing Data</strong> includes information necessary to process payments (such
|
||||
as transaction identifiers and credit balances). Card details are handled by our
|
||||
payment processor and are not stored on our servers.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Technical Data</strong> includes internet protocol (IP) address, browser type
|
||||
|
|
@ -126,8 +126,8 @@ export default function PrivacyPolicy() {
|
|||
incidents.
|
||||
</li>
|
||||
<li>
|
||||
To communicate with you about product updates, security notices, support requests,
|
||||
and (with your consent where required) marketing.
|
||||
To communicate with you about product updates, security notices, support requests, and
|
||||
(with your consent where required) marketing.
|
||||
</li>
|
||||
<li>
|
||||
To serve and measure advertising on pages where ads are shown (currently, our free
|
||||
|
|
@ -141,8 +141,8 @@ export default function PrivacyPolicy() {
|
|||
<h2 className="text-2xl font-semibold mb-4">4. Cookies and Tracking Technologies</h2>
|
||||
<p>
|
||||
We and our partners use cookies, local storage, and similar technologies to operate the
|
||||
Service, remember your preferences, measure usage, and serve advertising. The
|
||||
categories include:
|
||||
Service, remember your preferences, measure usage, and serve advertising. The categories
|
||||
include:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 my-4 space-y-2">
|
||||
<li>
|
||||
|
|
@ -179,9 +179,9 @@ export default function PrivacyPolicy() {
|
|||
</p>
|
||||
<ul className="list-disc pl-6 my-4 space-y-2">
|
||||
<li>
|
||||
Google, as a third-party vendor, uses cookies (including the DoubleClick DART
|
||||
cookie) to serve ads to you based on your visits to our Service and other websites
|
||||
on the Internet.
|
||||
Google, as a third-party vendor, uses cookies (including the DoubleClick DART cookie)
|
||||
to serve ads to you based on your visits to our Service and other websites on the
|
||||
Internet.
|
||||
</li>
|
||||
<li>
|
||||
Google's use of advertising cookies enables it and its partners to serve ads to you
|
||||
|
|
@ -195,14 +195,12 @@ export default function PrivacyPolicy() {
|
|||
<a href="https://www.youronlinechoices.com/">youronlinechoices.com</a> (EU).
|
||||
</li>
|
||||
<li>
|
||||
For users in the European Economic Area, the United Kingdom, and Switzerland, we
|
||||
use a Google-certified Consent Management Platform to obtain your consent for
|
||||
personalized advertising before such cookies are set. You may change or withdraw
|
||||
your consent at any time through the consent banner.
|
||||
</li>
|
||||
<li>
|
||||
We do not knowingly serve personalized advertising to children. See Section 11.
|
||||
For users in the European Economic Area, the United Kingdom, and Switzerland, we use a
|
||||
Google-certified Consent Management Platform to obtain your consent for personalized
|
||||
advertising before such cookies are set. You may change or withdraw your consent at
|
||||
any time through the consent banner.
|
||||
</li>
|
||||
<li>We do not knowingly serve personalized advertising to children. See Section 11.</li>
|
||||
</ul>
|
||||
<p className="mt-4">
|
||||
For more information about how Google uses data when you use our Service, see{" "}
|
||||
|
|
@ -217,8 +215,8 @@ export default function PrivacyPolicy() {
|
|||
<h2 className="text-2xl font-semibold mb-4">6. Data Security</h2>
|
||||
<p>
|
||||
We implement technical and organizational measures designed to protect your personal
|
||||
data against accidental loss, unauthorized access, alteration, and disclosure. Access
|
||||
to personal data is limited to personnel who need it to operate the Service.
|
||||
data against accidental loss, unauthorized access, alteration, and disclosure. Access to
|
||||
personal data is limited to personnel who need it to operate the Service.
|
||||
</p>
|
||||
<p className="mt-4">
|
||||
No system can be guaranteed to be fully secure. We cannot guarantee that personal data
|
||||
|
|
@ -232,10 +230,10 @@ export default function PrivacyPolicy() {
|
|||
<p>
|
||||
We retain personal data only for as long as necessary to provide the Service and to
|
||||
comply with our legal, accounting, and reporting obligations. Account data is retained
|
||||
for the life of your account; you can request deletion at any time. Aggregated data
|
||||
that no longer identifies you may be retained indefinitely for analytics and product
|
||||
improvement purposes. Anonymous chat sessions on our free pages are not retained in
|
||||
any user-linked database.
|
||||
for the life of your account; you can request deletion at any time. Aggregated data that
|
||||
no longer identifies you may be retained indefinitely for analytics and product
|
||||
improvement purposes. Anonymous chat sessions on our free pages are not retained in any
|
||||
user-linked database.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -243,8 +241,7 @@ export default function PrivacyPolicy() {
|
|||
<h2 className="text-2xl font-semibold mb-4">8. Third-Party Services</h2>
|
||||
<p>
|
||||
We rely on the following categories of third-party processors and providers to operate
|
||||
the Service. Each is bound by its own privacy policy, which we encourage you to
|
||||
review:
|
||||
the Service. Each is bound by its own privacy policy, which we encourage you to review:
|
||||
</p>
|
||||
<ul className="list-disc pl-6 my-4 space-y-2">
|
||||
<li>
|
||||
|
|
@ -261,9 +258,9 @@ export default function PrivacyPolicy() {
|
|||
<strong>Advertising</strong>: Google AdSense (see Section 5).
|
||||
</li>
|
||||
<li>
|
||||
<strong>Large language model providers</strong>: OpenAI, Anthropic, Google, and
|
||||
other LLM providers process the prompts and content you submit to the Service in
|
||||
order to generate responses.
|
||||
<strong>Large language model providers</strong>: OpenAI, Anthropic, Google, and other
|
||||
LLM providers process the prompts and content you submit to the Service in order to
|
||||
generate responses.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Integration providers</strong>: When you explicitly connect a third-party
|
||||
|
|
@ -278,9 +275,7 @@ export default function PrivacyPolicy() {
|
|||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="text-2xl font-semibold mb-4">
|
||||
9. Your Legal Rights (Including GDPR)
|
||||
</h2>
|
||||
<h2 className="text-2xl font-semibold mb-4">9. Your Legal Rights (Including GDPR)</h2>
|
||||
<p>
|
||||
Subject to applicable law, you have the following rights in relation to your personal
|
||||
data:
|
||||
|
|
@ -314,17 +309,17 @@ export default function PrivacyPolicy() {
|
|||
</p>
|
||||
<ul className="list-disc pl-6 my-4 space-y-2">
|
||||
<li>
|
||||
The right to know what categories of personal information we have collected about
|
||||
you and how it is used and shared.
|
||||
The right to know what categories of personal information we have collected about you
|
||||
and how it is used and shared.
|
||||
</li>
|
||||
<li>The right to delete personal information we have collected from you.</li>
|
||||
<li>The right to correct inaccurate personal information.</li>
|
||||
<li>
|
||||
The right to opt out of the "sale" or "sharing" of personal information for
|
||||
cross-context behavioral advertising. We do not sell personal data; however,
|
||||
advertising cookies set by Google AdSense may be considered "sharing" under
|
||||
California law. To opt out, you can use the consent controls described in Section 5
|
||||
or enable a Global Privacy Control (GPC) signal in your browser, which we honor.
|
||||
advertising cookies set by Google AdSense may be considered "sharing" under California
|
||||
law. To opt out, you can use the consent controls described in Section 5 or enable a
|
||||
Global Privacy Control (GPC) signal in your browser, which we honor.
|
||||
</li>
|
||||
<li>The right not to be discriminated against for exercising your privacy rights.</li>
|
||||
</ul>
|
||||
|
|
@ -337,33 +332,32 @@ export default function PrivacyPolicy() {
|
|||
<h2 className="text-2xl font-semibold mb-4">11. Children's Privacy</h2>
|
||||
<p>
|
||||
The Service is not directed to children under 13 (or under 16 in the EEA, UK, and
|
||||
Switzerland). We do not knowingly collect personal data from children. If you believe
|
||||
a child has provided us with personal data, please contact us and we will take steps
|
||||
to delete it. We do not knowingly serve personalized advertising to children.
|
||||
Switzerland). We do not knowingly collect personal data from children. If you believe a
|
||||
child has provided us with personal data, please contact us and we will take steps to
|
||||
delete it. We do not knowingly serve personalized advertising to children.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="text-2xl font-semibold mb-4">12. Changes to This Policy</h2>
|
||||
<p>
|
||||
We may update this Privacy Policy from time to time to reflect changes in our
|
||||
practices, technology, legal requirements, or for other operational reasons. When we
|
||||
make material changes, we will update the "Last updated" date at the top of this page
|
||||
and, where appropriate, provide additional notice (such as an in-product notification
|
||||
or email). Your continued use of the Service after the updated policy becomes
|
||||
effective constitutes your acceptance of the revised policy.
|
||||
We may update this Privacy Policy from time to time to reflect changes in our practices,
|
||||
technology, legal requirements, or for other operational reasons. When we make material
|
||||
changes, we will update the "Last updated" date at the top of this page and, where
|
||||
appropriate, provide additional notice (such as an in-product notification or email).
|
||||
Your continued use of the Service after the updated policy becomes effective constitutes
|
||||
your acceptance of the revised policy.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mb-8">
|
||||
<h2 className="text-2xl font-semibold mb-4">13. Contact Us</h2>
|
||||
<p>
|
||||
If you have questions about this Privacy Policy or our privacy practices, or if you
|
||||
want to exercise any of your rights, please contact us at:
|
||||
If you have questions about this Privacy Policy or our privacy practices, or if you want
|
||||
to exercise any of your rights, please contact us at:
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
<strong>Email:</strong>{" "}
|
||||
<a href="mailto:rohan@surfsense.com">rohan@surfsense.com</a>
|
||||
<strong>Email:</strong> <a href="mailto:rohan@surfsense.com">rohan@surfsense.com</a>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { mustGetQuery } from "@rocicorp/zero";
|
||||
import { handleQueryRequest } from "@rocicorp/zero/server";
|
||||
import { NextResponse } from "next/server";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import type { Context } from "@/types/zero";
|
||||
import { queries } from "@/zero/queries";
|
||||
import { schema } from "@/zero/schema";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const backendURL = BACKEND_URL;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@ interface AutomationEditContentProps {
|
|||
* structure but gates on ``canUpdate`` instead of ``canRead``: a user who
|
||||
* can read but not update is bounced to the access-denied panel.
|
||||
*/
|
||||
export function AutomationEditContent({
|
||||
searchSpaceId,
|
||||
automationId,
|
||||
}: AutomationEditContentProps) {
|
||||
export function AutomationEditContent({ searchSpaceId, automationId }: AutomationEditContentProps) {
|
||||
const perms = useAutomationPermissions();
|
||||
const validId = Number.isInteger(automationId) && automationId > 0;
|
||||
const { data: automation, isLoading, error } = useAutomation(validId ? automationId : undefined);
|
||||
|
|
|
|||
|
|
@ -9,10 +9,7 @@ import { JsonView } from "@/components/json-view";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import {
|
||||
type Automation,
|
||||
automationUpdateRequest,
|
||||
} from "@/contracts/types/automation.types";
|
||||
import { type Automation, automationUpdateRequest } from "@/contracts/types/automation.types";
|
||||
|
||||
interface AutomationEditFormProps {
|
||||
automation: Automation;
|
||||
|
|
|
|||
|
|
@ -69,11 +69,11 @@ import { documentsApiService } from "@/lib/apis/documents-api.service";
|
|||
import { getBearerToken } from "@/lib/auth-utils";
|
||||
import { type ChatFlow, classifyChatError } from "@/lib/chat/chat-error-classifier";
|
||||
import { tagPreAcceptSendFailure, toHttpResponseError } from "@/lib/chat/chat-request-errors";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import {
|
||||
convertToThreadMessage,
|
||||
reconcileInterruptedAssistantMessages,
|
||||
} from "@/lib/chat/message-utils";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import {
|
||||
isPodcastGenerating,
|
||||
looksLikePodcastRequest,
|
||||
|
|
@ -110,6 +110,7 @@ import {
|
|||
extractUserTurnForNewChatApi,
|
||||
type NewChatUserImagePayload,
|
||||
} from "@/lib/chat/user-turn-api-parts";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { NotFoundError } from "@/lib/error";
|
||||
import {
|
||||
trackChatBlocked,
|
||||
|
|
@ -119,7 +120,7 @@ import {
|
|||
trackChatResponseReceived,
|
||||
} from "@/lib/posthog/events";
|
||||
import Loading from "../loading";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const MobileEditorPanel = dynamic(
|
||||
() =>
|
||||
import("@/components/editor-panel/editor-panel").then((m) => ({
|
||||
|
|
@ -1977,14 +1978,12 @@ export default function NewChatPage() {
|
|||
mentioned_folder_ids: regenerateFolderIds.length > 0 ? regenerateFolderIds : undefined,
|
||||
mentioned_connector_ids:
|
||||
regenerateConnectors.length > 0 ? regenerateConnectors.map((d) => d.id) : undefined,
|
||||
mentioned_connectors:
|
||||
regenerateConnectors.length > 0 ? regenerateConnectors : undefined,
|
||||
mentioned_connectors: regenerateConnectors.length > 0 ? regenerateConnectors : undefined,
|
||||
// Full mention metadata for the regenerate-specific
|
||||
// source list. Only meaningful for edit (the BE only
|
||||
// re-persists a user row when ``user_query`` is set);
|
||||
// reload reuses the original turn's mentioned_documents.
|
||||
mentioned_documents:
|
||||
sourceMentionedDocs.length > 0 ? sourceMentionedDocs : undefined,
|
||||
mentioned_documents: sourceMentionedDocs.length > 0 ? sourceMentionedDocs : undefined,
|
||||
};
|
||||
if (isEdit) {
|
||||
requestBody.user_images = editExtras?.userImages ?? [];
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
deleteMemberMutationAtom,
|
||||
updateMemberMutationAtom,
|
||||
} from "@/atoms/members/members-mutation.atoms";
|
||||
import { membersAtom, myAccessAtom, canPerform } from "@/atoms/members/members-query.atoms";
|
||||
import { canPerform, membersAtom, myAccessAtom } from "@/atoms/members/members-query.atoms";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ export const myAccessAtom = atomWithQuery((get) => {
|
|||
|
||||
/**
|
||||
* Helper function to check if the current user has a specific permission.
|
||||
*
|
||||
*
|
||||
* @param access - The access object from useAtomValue(myAccessAtom)
|
||||
* @param permission - The permission string to check
|
||||
* @returns boolean indicating if the user has the permission
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const access = useAtomValue(myAccessAtom);
|
||||
* if (canPerform(access, 'manage_members')) { ... }
|
||||
|
|
@ -63,10 +63,10 @@ export function canPerform(
|
|||
/**
|
||||
* Hook wrapper for canPerform that reads from myAccessAtom internally.
|
||||
* Use this if you want to avoid calling useAtomValue(myAccessAtom) separately.
|
||||
*
|
||||
*
|
||||
* @param permission - The permission string to check
|
||||
* @returns boolean indicating if the user has the permission
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const canManageMembers = usePermissionGate('manage_members');
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import {
|
|||
CheckIcon,
|
||||
ClipboardPaste,
|
||||
CopyIcon,
|
||||
DownloadIcon,
|
||||
Dot,
|
||||
DownloadIcon,
|
||||
ExternalLink,
|
||||
Globe,
|
||||
MessageCircleReply,
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { EnumConnectorName } from "@/contracts/enums/connector";
|
||||
import { useApiKey } from "@/hooks/use-api-key";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { getConnectorBenefits } from "../connector-benefits";
|
||||
import type { ConnectFormProps } from "../index";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const PLUGIN_RELEASES_URL =
|
||||
"https://github.com/MODSetter/SurfSense/releases?q=obsidian&expanded=true";
|
||||
|
||||
|
||||
/**
|
||||
* Obsidian connect form for the plugin-only architecture.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import type { ConnectorConfigProps } from "../index";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import type { ConnectorConfigProps } from "../index";
|
||||
export interface CirclebackConfigProps extends ConnectorConfigProps {
|
||||
onNameChange?: (name: string) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { LIVE_CONNECTOR_TYPES } from "../../constants/connector-constants";
|
|||
import { getConnectorDisplayName } from "../../tabs/all-connectors-tab";
|
||||
import { MCPServiceConfig } from "../components/mcp-service-config";
|
||||
import { getConnectorConfigComponent } from "../index";
|
||||
|
||||
const VISION_LLM_CONNECTOR_TYPES = new Set<SearchSourceConnector["connector_type"]>([
|
||||
EnumConnectorName.GOOGLE_DRIVE_CONNECTOR,
|
||||
EnumConnectorName.COMPOSIO_GOOGLE_DRIVE_CONNECTOR,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { cn } from "@/lib/utils";
|
|||
import { LIVE_CONNECTOR_TYPES } from "../constants/connector-constants";
|
||||
import { useConnectorStatus } from "../hooks/use-connector-status";
|
||||
import { getConnectorDisplayName } from "../tabs/all-connectors-tab";
|
||||
|
||||
interface ConnectorAccountsListViewProps {
|
||||
connectorType: string;
|
||||
connectorTitle: string;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,12 @@ interface InlineMentionEditorProps {
|
|||
onActionClose?: () => void;
|
||||
onSubmit?: () => void;
|
||||
onChange?: (text: string, docs: MentionedDocument[]) => void;
|
||||
onDocumentRemove?: (docId: number, docType?: string, kind?: MentionKind, connectorType?: string) => void;
|
||||
onDocumentRemove?: (
|
||||
docId: number,
|
||||
docType?: string,
|
||||
kind?: MentionKind,
|
||||
connectorType?: string
|
||||
) => void;
|
||||
onKeyDown?: (e: React.KeyboardEvent) => void;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
|
|
@ -171,9 +176,10 @@ const MentionElement: FC<PlateElementProps<MentionElementNode>> = ({
|
|||
{isFolder ? (
|
||||
<FolderIcon className="h-3 w-3" />
|
||||
) : isConnector ? (
|
||||
getConnectorIcon(element.connector_type ?? element.document_type ?? "UNKNOWN", "h-3 w-3") ?? (
|
||||
<PlugIcon className="h-3 w-3" />
|
||||
)
|
||||
(getConnectorIcon(
|
||||
element.connector_type ?? element.document_type ?? "UNKNOWN",
|
||||
"h-3 w-3"
|
||||
) ?? <PlugIcon className="h-3 w-3" />)
|
||||
) : (
|
||||
getConnectorIcon(element.document_type ?? "UNKNOWN", "h-3 w-3")
|
||||
)}
|
||||
|
|
@ -357,7 +363,11 @@ function getSelectionAnchorRect(root: HTMLElement | null): SuggestionAnchorRect
|
|||
const rect = range.getClientRects()[0] ?? range.getBoundingClientRect();
|
||||
if (rect.width > 0 || rect.height > 0) return rectToAnchor(rect);
|
||||
|
||||
if (range.collapsed && range.startContainer.nodeType === Node.TEXT_NODE && range.startOffset > 0) {
|
||||
if (
|
||||
range.collapsed &&
|
||||
range.startContainer.nodeType === Node.TEXT_NODE &&
|
||||
range.startOffset > 0
|
||||
) {
|
||||
const fallbackRange = range.cloneRange();
|
||||
fallbackRange.setStart(range.startContainer, range.startOffset - 1);
|
||||
fallbackRange.setEnd(range.startContainer, range.startOffset);
|
||||
|
|
|
|||
|
|
@ -68,11 +68,6 @@ import {
|
|||
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
||||
import { UserMessage } from "@/components/assistant-ui/user-message";
|
||||
import { ComposerSuggestionPopoverContent } from "@/components/new-chat/composer-suggestion-popup";
|
||||
import {
|
||||
DocumentMentionPicker,
|
||||
promoteRecentMention,
|
||||
type DocumentMentionPickerRef,
|
||||
} from "../new-chat/document-mention-picker";
|
||||
import { PromptPicker, type PromptPickerRef } from "@/components/new-chat/prompt-picker";
|
||||
import { Avatar, AvatarFallback, AvatarGroup } from "@/components/ui/avatar";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -112,6 +107,11 @@ import { captureDisplayToPngDataUrl } from "@/lib/chat/display-media-capture";
|
|||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import { slideoutOpenedTickAtom } from "@/lib/layout-events";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
DocumentMentionPicker,
|
||||
type DocumentMentionPickerRef,
|
||||
promoteRecentMention,
|
||||
} from "../new-chat/document-mention-picker";
|
||||
|
||||
const COMPOSER_PLACEHOLDER = "Ask anything, type / for prompts, type @ to mention docs";
|
||||
|
||||
|
|
@ -601,21 +601,24 @@ const Composer: FC = () => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const handleActionTrigger = useCallback((trigger: SuggestionTriggerInfo) => {
|
||||
const anchorPoint = getComposerSuggestionAnchorPoint(
|
||||
trigger.anchorRect,
|
||||
clipboardInitialText ? "bottom" : "top"
|
||||
);
|
||||
if (!anchorPoint) {
|
||||
setShowPromptPicker(false);
|
||||
setActionQuery("");
|
||||
setSuggestionAnchorPoint(null);
|
||||
return;
|
||||
}
|
||||
setSuggestionAnchorPoint((current) => current ?? anchorPoint);
|
||||
setShowPromptPicker(true);
|
||||
setActionQuery(trigger.query);
|
||||
}, [clipboardInitialText]);
|
||||
const handleActionTrigger = useCallback(
|
||||
(trigger: SuggestionTriggerInfo) => {
|
||||
const anchorPoint = getComposerSuggestionAnchorPoint(
|
||||
trigger.anchorRect,
|
||||
clipboardInitialText ? "bottom" : "top"
|
||||
);
|
||||
if (!anchorPoint) {
|
||||
setShowPromptPicker(false);
|
||||
setActionQuery("");
|
||||
setSuggestionAnchorPoint(null);
|
||||
return;
|
||||
}
|
||||
setSuggestionAnchorPoint((current) => current ?? anchorPoint);
|
||||
setShowPromptPicker(true);
|
||||
setActionQuery(trigger.query);
|
||||
},
|
||||
[clipboardInitialText]
|
||||
);
|
||||
|
||||
const handleActionClose = useCallback(() => {
|
||||
if (showPromptPicker) {
|
||||
|
|
@ -754,7 +757,12 @@ const Composer: FC = () => {
|
|||
]);
|
||||
|
||||
const handleDocumentRemove = useCallback(
|
||||
(docId: number, docType?: string, kind?: "doc" | "folder" | "connector", connectorType?: string) => {
|
||||
(
|
||||
docId: number,
|
||||
docType?: string,
|
||||
kind?: "doc" | "folder" | "connector",
|
||||
connectorType?: string
|
||||
) => {
|
||||
setMentionedDocuments((prev) => {
|
||||
const removedKey = getMentionDocKey({
|
||||
id: docId,
|
||||
|
|
@ -768,27 +776,30 @@ const Composer: FC = () => {
|
|||
[setMentionedDocuments]
|
||||
);
|
||||
|
||||
const handleDocumentsMention = useCallback((mentions: MentionedDocumentInfo[]) => {
|
||||
const parsedSearchSpaceId = Number(search_space_id);
|
||||
const editorMentionedDocs = editorRef.current?.getMentionedDocuments() ?? [];
|
||||
const editorDocKeys = new Set(editorMentionedDocs.map((doc) => getMentionDocKey(doc)));
|
||||
const handleDocumentsMention = useCallback(
|
||||
(mentions: MentionedDocumentInfo[]) => {
|
||||
const parsedSearchSpaceId = Number(search_space_id);
|
||||
const editorMentionedDocs = editorRef.current?.getMentionedDocuments() ?? [];
|
||||
const editorDocKeys = new Set(editorMentionedDocs.map((doc) => getMentionDocKey(doc)));
|
||||
|
||||
for (const mention of mentions) {
|
||||
const key = getMentionDocKey(mention);
|
||||
if (editorDocKeys.has(key)) continue;
|
||||
editorRef.current?.insertMentionChip(mention);
|
||||
if (Number.isFinite(parsedSearchSpaceId)) {
|
||||
promoteRecentMention(parsedSearchSpaceId, mention);
|
||||
for (const mention of mentions) {
|
||||
const key = getMentionDocKey(mention);
|
||||
if (editorDocKeys.has(key)) continue;
|
||||
editorRef.current?.insertMentionChip(mention);
|
||||
if (Number.isFinite(parsedSearchSpaceId)) {
|
||||
promoteRecentMention(parsedSearchSpaceId, mention);
|
||||
}
|
||||
// Track within the loop so a duplicate-in-batch can't double-insert.
|
||||
editorDocKeys.add(key);
|
||||
}
|
||||
// Track within the loop so a duplicate-in-batch can't double-insert.
|
||||
editorDocKeys.add(key);
|
||||
}
|
||||
|
||||
// Atom is reconciled by ``handleEditorChange`` via the editor's
|
||||
// onChange — no second write path here.
|
||||
setMentionQuery("");
|
||||
setSuggestionAnchorPoint(null);
|
||||
}, [search_space_id]);
|
||||
// Atom is reconciled by ``handleEditorChange`` via the editor's
|
||||
// onChange — no second write path here.
|
||||
setMentionQuery("");
|
||||
setSuggestionAnchorPoint(null);
|
||||
},
|
||||
[search_space_id]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const editor = editorRef.current;
|
||||
|
|
|
|||
|
|
@ -104,9 +104,9 @@ const UserTextPart: FC = () => {
|
|||
const icon = isFolder ? (
|
||||
<FolderIcon className="size-3.5" />
|
||||
) : isConnector ? (
|
||||
getConnectorIcon(segment.doc.connector_type, "size-3.5") ?? (
|
||||
(getConnectorIcon(segment.doc.connector_type, "size-3.5") ?? (
|
||||
<Plug className="size-3.5" />
|
||||
)
|
||||
))
|
||||
) : (
|
||||
getConnectorIcon(segment.doc.document_type ?? "UNKNOWN", "size-3.5")
|
||||
);
|
||||
|
|
@ -123,7 +123,9 @@ const UserTextPart: FC = () => {
|
|||
: segment.doc.title
|
||||
}
|
||||
onClick={
|
||||
isFolder || isConnector ? undefined : () => handleOpenDoc(segment.doc.id, segment.doc.title)
|
||||
isFolder || isConnector
|
||||
? undefined
|
||||
: () => handleOpenDoc(segment.doc.id, segment.doc.title)
|
||||
}
|
||||
className="mx-0.5"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import { useElectronAPI } from "@/hooks/use-platform";
|
|||
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
|
||||
import { inferMonacoLanguageFromPath } from "@/lib/editor-language";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const PlateEditor = dynamic(
|
||||
() => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
|
||||
{ ssr: false, loading: () => <EditorPanelSkeleton /> }
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ import { Button } from "@/components/ui/button";
|
|||
import type { AnonModel, AnonQuotaResponse } from "@/contracts/types/anonymous-chat.types";
|
||||
import { anonymousChatApiService } from "@/lib/apis/anonymous-chat-api.service";
|
||||
import { readSSEStream } from "@/lib/chat/streaming-state";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { trackAnonymousChatMessageSent } from "@/lib/posthog/events";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { QuotaBar } from "./quota-bar";
|
||||
import { QuotaWarningBanner } from "./quota-warning-banner";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
role: "user" | "assistant";
|
||||
|
|
@ -80,19 +81,16 @@ export function AnonymousChat({ model }: AnonymousChatProps) {
|
|||
content: m.content,
|
||||
}));
|
||||
|
||||
const response = await fetch(
|
||||
`${BACKEND_URL}/api/v1/public/anon-chat/stream`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
model_slug: modelSlug,
|
||||
messages: chatHistory,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
}
|
||||
);
|
||||
const response = await fetch(`${BACKEND_URL}/api/v1/public/anon-chat/stream`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
model_slug: modelSlug,
|
||||
messages: chatHistory,
|
||||
}),
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
if (response.status === 429) {
|
||||
|
|
|
|||
|
|
@ -193,11 +193,7 @@ export function SearchSpaceAvatar({
|
|||
|
||||
// If delete or settings handlers are provided, expose them through a dropdown menu.
|
||||
if (onDelete || onSettings) {
|
||||
const trigger = (
|
||||
<DropdownMenuTrigger asChild>
|
||||
{avatarButton(true)}
|
||||
</DropdownMenuTrigger>
|
||||
);
|
||||
const trigger = <DropdownMenuTrigger asChild>{avatarButton(true)}</DropdownMenuTrigger>;
|
||||
|
||||
return (
|
||||
<DropdownMenu open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
|
|
|
|||
|
|
@ -78,11 +78,11 @@ import { foldersApiService } from "@/lib/apis/folders-api.service";
|
|||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { getMentionDocKey } from "@/lib/chat/mention-doc-key";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { uploadFolderScan } from "@/lib/folder-sync-upload";
|
||||
import { getSupportedExtensionsSet } from "@/lib/supported-extensions";
|
||||
import { queries } from "@/zero/queries/index";
|
||||
import { SidebarSlideOutPanel } from "./SidebarSlideOutPanel";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const DesktopLocalTabContent = dynamic(
|
||||
() => import("./DesktopLocalTabContent").then((mod) => mod.DesktopLocalTabContent),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const LARGE_DOCUMENT_THRESHOLD = 2 * 1024 * 1024; // 2MB
|
||||
|
||||
interface DocumentContent {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,10 @@ const ComposerSuggestionItem = React.forwardRef<
|
|||
));
|
||||
ComposerSuggestionItem.displayName = "ComposerSuggestionItem";
|
||||
|
||||
function ComposerSuggestionSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
|
||||
function ComposerSuggestionSeparator({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Separator>) {
|
||||
return (
|
||||
<div className={cn("my-0.5 px-2.5", className)}>
|
||||
<Separator className="bg-popover-border" {...props} />
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import { useQuery as useZeroQuery } from "@rocicorp/zero/react";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { useAtomValue } from "jotai";
|
||||
import {
|
||||
BookOpen,
|
||||
ChevronLeft,
|
||||
|
|
@ -22,7 +23,6 @@ import {
|
|||
useState,
|
||||
} from "react";
|
||||
import type { MentionedDocumentInfo } from "@/atoms/chat/mentioned-documents.atom";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms";
|
||||
import { getConnectorTitle } from "@/components/assistant-ui/connector-popup/constants/connector-constants";
|
||||
import { getConnectorDisplayName } from "@/components/assistant-ui/connector-popup/tabs/all-connectors-tab";
|
||||
|
|
@ -178,7 +178,9 @@ function useDebounced<T>(value: T, delay = DEBOUNCE_MS) {
|
|||
return debounced;
|
||||
}
|
||||
|
||||
function makeDocMention(doc: Pick<Document, "id" | "title" | "document_type">): MentionedDocumentInfo {
|
||||
function makeDocMention(
|
||||
doc: Pick<Document, "id" | "title" | "document_type">
|
||||
): MentionedDocumentInfo {
|
||||
return {
|
||||
id: doc.id,
|
||||
title: doc.title,
|
||||
|
|
@ -187,9 +189,10 @@ function makeDocMention(doc: Pick<Document, "id" | "title" | "document_type">):
|
|||
};
|
||||
}
|
||||
|
||||
function makeFolderMention(
|
||||
folder: { id: number; title: string }
|
||||
): Extract<MentionedDocumentInfo, { kind: "folder" }> {
|
||||
function makeFolderMention(folder: {
|
||||
id: number;
|
||||
title: string;
|
||||
}): Extract<MentionedDocumentInfo, { kind: "folder" }> {
|
||||
return {
|
||||
id: folder.id,
|
||||
title: folder.title,
|
||||
|
|
@ -319,24 +322,24 @@ export const DocumentMentionPicker = forwardRef<
|
|||
|
||||
useEffect(() => {
|
||||
if (currentPage !== 0) return;
|
||||
const combinedDocs: Pick<Document, "id" | "title" | "document_type">[] = [];
|
||||
const combinedDocs: Pick<Document, "id" | "title" | "document_type">[] = [];
|
||||
|
||||
if (surfsenseDocs?.items) {
|
||||
for (const doc of surfsenseDocs.items) {
|
||||
combinedDocs.push({
|
||||
id: doc.id,
|
||||
title: doc.title,
|
||||
document_type: "SURFSENSE_DOCS",
|
||||
});
|
||||
}
|
||||
if (surfsenseDocs?.items) {
|
||||
for (const doc of surfsenseDocs.items) {
|
||||
combinedDocs.push({
|
||||
id: doc.id,
|
||||
title: doc.title,
|
||||
document_type: "SURFSENSE_DOCS",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (titleSearchResults?.items) {
|
||||
combinedDocs.push(...titleSearchResults.items);
|
||||
setHasMore(titleSearchResults.has_more);
|
||||
}
|
||||
if (titleSearchResults?.items) {
|
||||
combinedDocs.push(...titleSearchResults.items);
|
||||
setHasMore(titleSearchResults.has_more);
|
||||
}
|
||||
|
||||
setAccumulatedDocuments(filterBySearchTerm(combinedDocs));
|
||||
setAccumulatedDocuments(filterBySearchTerm(combinedDocs));
|
||||
}, [titleSearchResults, surfsenseDocs, currentPage, filterBySearchTerm]);
|
||||
|
||||
const loadNextPage = useCallback(async () => {
|
||||
|
|
@ -352,9 +355,11 @@ export const DocumentMentionPicker = forwardRef<
|
|||
page_size: PAGE_SIZE,
|
||||
...(isSearchValid ? { title: debouncedSearch.trim() } : {}),
|
||||
};
|
||||
const response: SearchDocumentTitlesResponse = await documentsApiService.searchDocumentTitles({
|
||||
queryParams,
|
||||
});
|
||||
const response: SearchDocumentTitlesResponse = await documentsApiService.searchDocumentTitles(
|
||||
{
|
||||
queryParams,
|
||||
}
|
||||
);
|
||||
|
||||
setAccumulatedDocuments((prev) => [...prev, ...response.items]);
|
||||
setHasMore(response.has_more);
|
||||
|
|
@ -431,7 +436,13 @@ export const DocumentMentionPicker = forwardRef<
|
|||
)
|
||||
.filter((mention): mention is MentionedDocumentInfo => mention !== null)
|
||||
.slice(0, RECENTS_LIMIT),
|
||||
[activeConnectors, hasHydratedRecentDocs, recentMentions, recentValidationDocuments, zeroFolders]
|
||||
[
|
||||
activeConnectors,
|
||||
hasHydratedRecentDocs,
|
||||
recentMentions,
|
||||
recentValidationDocuments,
|
||||
zeroFolders,
|
||||
]
|
||||
);
|
||||
|
||||
const selectedKeys = useMemo(
|
||||
|
|
@ -460,47 +471,46 @@ export const DocumentMentionPicker = forwardRef<
|
|||
[visibleRecentMentions, selectedKeys]
|
||||
);
|
||||
|
||||
const rootNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(
|
||||
() => {
|
||||
const nodes: ComposerSuggestionNode<ResourceNodeValue>[] = [...recentRootNodes];
|
||||
if (showSurfsenseDocsRoot) {
|
||||
nodes.push({
|
||||
id: "surfsense-docs",
|
||||
label: "SurfSense Docs",
|
||||
subtitle: "Browse product documentation",
|
||||
icon: <BookOpen className="size-4" />,
|
||||
type: "branch",
|
||||
value: { kind: "view", view: { kind: "surfsense-docs" } },
|
||||
});
|
||||
}
|
||||
nodes.push(
|
||||
{
|
||||
id: "files-folders",
|
||||
label: "Files & Folders",
|
||||
subtitle: "Browse your knowledge base",
|
||||
icon: <Files className="size-4" />,
|
||||
type: "branch",
|
||||
value: { kind: "view", view: { kind: "files-folders" } },
|
||||
},
|
||||
{
|
||||
id: "connectors",
|
||||
label: "Connectors",
|
||||
subtitle: activeConnectors.length
|
||||
? "Choose the exact account for tool use"
|
||||
: "No connected accounts yet",
|
||||
const rootNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
|
||||
const nodes: ComposerSuggestionNode<ResourceNodeValue>[] = [...recentRootNodes];
|
||||
if (showSurfsenseDocsRoot) {
|
||||
nodes.push({
|
||||
id: "surfsense-docs",
|
||||
label: "SurfSense Docs",
|
||||
subtitle: "Browse product documentation",
|
||||
icon: <BookOpen className="size-4" />,
|
||||
type: "branch",
|
||||
value: { kind: "view", view: { kind: "surfsense-docs" } },
|
||||
});
|
||||
}
|
||||
nodes.push(
|
||||
{
|
||||
id: "files-folders",
|
||||
label: "Files & Folders",
|
||||
subtitle: "Browse your knowledge base",
|
||||
icon: <Files className="size-4" />,
|
||||
type: "branch",
|
||||
value: { kind: "view", view: { kind: "files-folders" } },
|
||||
},
|
||||
{
|
||||
id: "connectors",
|
||||
label: "Connectors",
|
||||
subtitle: activeConnectors.length
|
||||
? "Choose the exact account for tool use"
|
||||
: "No connected accounts yet",
|
||||
icon: <Unplug className="size-4" />,
|
||||
type: "branch",
|
||||
disabled: activeConnectors.length === 0,
|
||||
value: { kind: "view", view: { kind: "connectors" } },
|
||||
}
|
||||
);
|
||||
return nodes;
|
||||
},
|
||||
[activeConnectors.length, recentRootNodes, showSurfsenseDocsRoot]
|
||||
);
|
||||
type: "branch",
|
||||
disabled: activeConnectors.length === 0,
|
||||
value: { kind: "view", view: { kind: "connectors" } },
|
||||
}
|
||||
);
|
||||
return nodes;
|
||||
}, [activeConnectors.length, recentRootNodes, showSurfsenseDocsRoot]);
|
||||
|
||||
const searchNodes = useMemo<ComposerSuggestionNode<ResourceNodeValue>[]>(() => {
|
||||
const searchLower = (isSingleCharSearch ? deferredSearch : debouncedSearch).trim().toLowerCase();
|
||||
const searchLower = (isSingleCharSearch ? deferredSearch : debouncedSearch)
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const docNodes = actualDocuments.map((doc) => {
|
||||
const mention = makeDocMention(doc);
|
||||
return {
|
||||
|
|
@ -619,7 +629,9 @@ export const DocumentMentionPicker = forwardRef<
|
|||
id: getMentionDocKey(mention),
|
||||
label: getConnectorDisplayName(connector.name),
|
||||
subtitle: `${view.title} account`,
|
||||
icon: getConnectorIcon(connector.connector_type, "size-4") ?? <Unplug className="size-4" />,
|
||||
icon: getConnectorIcon(connector.connector_type, "size-4") ?? (
|
||||
<Unplug className="size-4" />
|
||||
),
|
||||
type: "item" as const,
|
||||
disabled: selectedKeys.has(getMentionDocKey(mention)),
|
||||
value: { kind: "mention" as const, mention },
|
||||
|
|
@ -733,7 +745,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
icon={
|
||||
<span className="-ml-0.5 flex size-4.5 items-center justify-center">
|
||||
<ChevronLeft className="size-3.5" />
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<span className="flex-1 truncate">{title}</span>
|
||||
|
|
@ -759,7 +771,7 @@ export const DocumentMentionPicker = forwardRef<
|
|||
return (
|
||||
<Fragment key={node.id}>
|
||||
{showRecentsSeparator ? <ComposerSuggestionSeparator /> : null}
|
||||
<ComposerSuggestionItem
|
||||
<ComposerSuggestionItem
|
||||
ref={navigator.getItemRef(index)}
|
||||
icon={node.icon}
|
||||
selected={index === navigator.highlightedIndex}
|
||||
|
|
@ -776,11 +788,11 @@ export const DocumentMentionPicker = forwardRef<
|
|||
{node.subtitle}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
{node.type === "branch" ? (
|
||||
<ChevronRight className="size-3.5 shrink-0 text-muted-foreground" />
|
||||
) : null}
|
||||
</ComposerSuggestionItem>
|
||||
</ComposerSuggestionItem>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,9 @@ export const PromptPicker = forwardRef<PromptPickerRef, PromptPickerProps>(funct
|
|||
{isLoading ? (
|
||||
<ComposerSuggestionSkeleton rows={8} mobileRows={8} />
|
||||
) : isError ? (
|
||||
<ComposerSuggestionMessage variant="destructive">Failed to load prompts</ComposerSuggestionMessage>
|
||||
<ComposerSuggestionMessage variant="destructive">
|
||||
Failed to load prompts
|
||||
</ComposerSuggestionMessage>
|
||||
) : filtered.length === 0 ? (
|
||||
<ComposerSuggestionMessage>No matching prompts</ComposerSuggestionMessage>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import { Label } from "@/components/ui/label";
|
|||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||
import { Spinner } from "../ui/spinner";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
interface GeneralSettingsManagerProps {
|
||||
searchSpaceId: number;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ interface PromptConfigManagerProps {
|
|||
}
|
||||
|
||||
export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) {
|
||||
const {
|
||||
data: searchSpace,
|
||||
isLoading: loading,
|
||||
} = useQuery({
|
||||
const { data: searchSpace, isLoading: loading } = useQuery({
|
||||
queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()),
|
||||
queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }),
|
||||
enabled: !!searchSpaceId,
|
||||
|
|
@ -56,8 +53,7 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps)
|
|||
});
|
||||
toast.success("System instructions saved successfully");
|
||||
} catch (error: unknown) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Failed to save system instructions";
|
||||
const message = error instanceof Error ? error.message : "Failed to save system instructions";
|
||||
console.error("Error saving system instructions:", error);
|
||||
toast.error(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { baseApiService } from "@/lib/apis/base-api.service";
|
|||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
/**
|
||||
* Zod schemas for runtime validation
|
||||
*/
|
||||
|
|
@ -193,10 +194,10 @@ function PodcastPlayer({
|
|||
} else {
|
||||
// Authenticated view - fetch audio and details in parallel
|
||||
const [audioResponse, details] = await Promise.all([
|
||||
authenticatedFetch(
|
||||
`${BACKEND_URL}/api/v1/podcasts/${podcastId}/audio`,
|
||||
{ method: "GET", signal: controller.signal }
|
||||
),
|
||||
authenticatedFetch(`${BACKEND_URL}/api/v1/podcasts/${podcastId}/audio`, {
|
||||
method: "GET",
|
||||
signal: controller.signal,
|
||||
}),
|
||||
baseApiService.get<unknown>(`/api/v1/podcasts/${podcastId}`),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { TextShimmerLoader } from "@/components/prompt-kit/loader";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { baseApiService } from "@/lib/apis/base-api.service";
|
||||
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
import { compileCheck, compileToComponent } from "@/lib/remotion/compile-check";
|
||||
import { FPS } from "@/lib/remotion/constants";
|
||||
import {
|
||||
|
|
@ -19,7 +20,6 @@ import {
|
|||
type CompiledSlide,
|
||||
} from "./combined-player";
|
||||
import { getPptxExportErrorToast, getVideoDownloadErrorToast } from "./errors";
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const GenerateVideoPresentationArgsSchema = z.object({
|
||||
source_content: z.string(),
|
||||
|
|
|
|||
|
|
@ -107,9 +107,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
|||
setError(null);
|
||||
|
||||
// Build URL with optional search_space_id query parameter
|
||||
const url = new URL(
|
||||
`${BACKEND_URL}/api/v1/search-source-connectors`
|
||||
);
|
||||
const url = new URL(`${BACKEND_URL}/api/v1/search-source-connectors`);
|
||||
if (spaceId !== undefined) {
|
||||
url.searchParams.append("search_space_id", spaceId.toString());
|
||||
}
|
||||
|
|
@ -169,9 +167,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
|||
) => {
|
||||
try {
|
||||
// Add search_space_id as a query parameter
|
||||
const url = new URL(
|
||||
`${BACKEND_URL}/api/v1/search-source-connectors`
|
||||
);
|
||||
const url = new URL(`${BACKEND_URL}/api/v1/search-source-connectors`);
|
||||
url.searchParams.append("search_space_id", spaceId.toString());
|
||||
|
||||
const response = await authenticatedFetch(url.toString(), {
|
||||
|
|
@ -283,9 +279,7 @@ export const useSearchSourceConnectors = (lazy: boolean = false, searchSpaceId?:
|
|||
}
|
||||
|
||||
const response = await authenticatedFetch(
|
||||
`${
|
||||
BACKEND_URL
|
||||
}/api/v1/search-source-connectors/${connectorId}/index?${params.toString()}`,
|
||||
`${BACKEND_URL}/api/v1/search-source-connectors/${connectorId}/index?${params.toString()}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* Authentication utilities for handling token expiration and redirects
|
||||
*/
|
||||
import { BACKEND_URL } from "@/lib/env-config";
|
||||
|
||||
const REDIRECT_PATH_KEY = "surfsense_redirect_path";
|
||||
const BEARER_TOKEN_KEY = "surfsense_bearer_token";
|
||||
const REFRESH_TOKEN_KEY = "surfsense_refresh_token";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { ChatErrorKind, ChatErrorSeverity, ChatFlow } from "@/lib/chat/chat-error-classifier";
|
||||
import type { ConnectorTelemetryMeta } from "@/lib/connector-telemetry";
|
||||
import { getConnectorTelemetryMeta } from "@/lib/connector-telemetry";
|
||||
import type { ChatErrorKind, ChatErrorSeverity, ChatFlow } from "@/lib/chat/chat-error-classifier";
|
||||
|
||||
/**
|
||||
* PostHog Analytics Event Definitions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue