mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
Merge pull request #1299 from AnishSarkar22/feat/swappable-filesystem
Some checks failed
Build and Push Docker Images / tag_release (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Has been cancelled
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Has been cancelled
Some checks failed
Build and Push Docker Images / tag_release (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend) (push) Has been cancelled
Build and Push Docker Images / create_manifest (web, surfsense-web) (push) Has been cancelled
feat: introduce swappable filesystem on desktop & monaco editor to edit local files
This commit is contained in:
commit
a0f2851784
82 changed files with 4818 additions and 418 deletions
|
|
@ -7,16 +7,20 @@ import {
|
|||
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
||||
useIsMarkdownCodeBlock,
|
||||
} from "@assistant-ui/react-markdown";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { ExternalLinkIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useTheme } from "next-themes";
|
||||
import { memo, type ReactNode } from "react";
|
||||
import rehypeKatex from "rehype-katex";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import { openEditorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
||||
import { ImagePreview, ImageRoot, ImageZoom } from "@/components/assistant-ui/image";
|
||||
import "katex/dist/katex.min.css";
|
||||
import { InlineCitation, UrlCitation } from "@/components/assistant-ui/inline-citation";
|
||||
import { useElectronAPI } from "@/hooks/use-platform";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
Table,
|
||||
|
|
@ -222,6 +226,18 @@ function extractDomain(url: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
// Canonical local-file virtual paths are mount-prefixed: /<mount>/<relative/path>
|
||||
const LOCAL_FILE_PATH_REGEX = /^\/[a-z0-9_-]+\/[^\s`]+(?:\/[^\s`]+)*$/;
|
||||
|
||||
function isVirtualFilePathToken(value: string): boolean {
|
||||
if (!LOCAL_FILE_PATH_REGEX.test(value) || value.startsWith("//")) {
|
||||
return false;
|
||||
}
|
||||
const normalized = value.replace(/\/+$/, "");
|
||||
const segments = normalized.split("/").filter(Boolean);
|
||||
return segments.length >= 2;
|
||||
}
|
||||
|
||||
function MarkdownImage({ src, alt }: { src?: string; alt?: string }) {
|
||||
if (!src) return null;
|
||||
|
||||
|
|
@ -392,7 +408,51 @@ const defaultComponents = memoizeMarkdownComponents({
|
|||
code: function Code({ className, children, ...props }) {
|
||||
const isCodeBlock = useIsMarkdownCodeBlock();
|
||||
const { resolvedTheme } = useTheme();
|
||||
const openEditorPanel = useSetAtom(openEditorPanelAtom);
|
||||
const params = useParams();
|
||||
const electronAPI = useElectronAPI();
|
||||
const language = /language-(\w+)/.exec(className || "")?.[1] ?? "text";
|
||||
const codeString = String(children).replace(/\n$/, "");
|
||||
const isWebLocalFileCodeBlock =
|
||||
isCodeBlock &&
|
||||
!electronAPI &&
|
||||
isVirtualFilePathToken(codeString.trim()) &&
|
||||
!codeString.trim().startsWith("//") &&
|
||||
!codeString.includes("\n");
|
||||
if (!isCodeBlock) {
|
||||
const inlineValue = String(children ?? "").trim();
|
||||
const isLocalPath =
|
||||
!!electronAPI && isVirtualFilePathToken(inlineValue) && !inlineValue.startsWith("//");
|
||||
const displayLocalPath = inlineValue.replace(/^\/+/, "");
|
||||
const searchSpaceIdParam = params?.search_space_id;
|
||||
const parsedSearchSpaceId = Array.isArray(searchSpaceIdParam)
|
||||
? Number(searchSpaceIdParam[0])
|
||||
: Number(searchSpaceIdParam);
|
||||
if (isLocalPath) {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"cursor-pointer font-mono text-[0.9em] font-medium text-primary underline underline-offset-4 transition-colors hover:text-primary/80"
|
||||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
openEditorPanel({
|
||||
kind: "local_file",
|
||||
localFilePath: inlineValue,
|
||||
title: inlineValue.split("/").pop() || inlineValue,
|
||||
searchSpaceId: Number.isFinite(parsedSearchSpaceId)
|
||||
? parsedSearchSpaceId
|
||||
: undefined,
|
||||
});
|
||||
}}
|
||||
title="Open in editor panel"
|
||||
>
|
||||
{displayLocalPath}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
|
|
@ -405,8 +465,19 @@ const defaultComponents = memoizeMarkdownComponents({
|
|||
</code>
|
||||
);
|
||||
}
|
||||
const language = /language-(\w+)/.exec(className || "")?.[1] ?? "text";
|
||||
const codeString = String(children).replace(/\n$/, "");
|
||||
if (isWebLocalFileCodeBlock) {
|
||||
return (
|
||||
<code
|
||||
className={cn(
|
||||
"aui-md-inline-code rounded-md border bg-muted px-1.5 py-0.5 font-mono text-[0.9em] font-normal",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{codeString.trim()}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<LazyMarkdownCodeBlock
|
||||
className={className}
|
||||
|
|
|
|||
|
|
@ -1104,7 +1104,13 @@ const ComposerAction: FC<ComposerActionProps> = ({ isBlockedByOtherUser = false
|
|||
group.tools.flatMap((t, i) =>
|
||||
i === 0
|
||||
? [t.description]
|
||||
: [<Dot key={i} className="inline h-4 w-4" />, t.description]
|
||||
: [
|
||||
<Dot
|
||||
key={`dot-${group.label}-${t.description}`}
|
||||
className="inline h-4 w-4"
|
||||
/>,
|
||||
t.description,
|
||||
]
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ActionBarPrimitive, AuiIf, MessagePrimitive, useAuiState } from "@assistant-ui/react";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { CheckIcon, CopyIcon, FileText, Pen } from "lucide-react";
|
||||
import { CheckIcon, CopyIcon, FileText, Pencil } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { type FC, useState } from "react";
|
||||
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
|
||||
|
|
@ -136,7 +136,7 @@ const UserActionBar: FC = () => {
|
|||
{canEdit && (
|
||||
<ActionBarPrimitive.Edit asChild>
|
||||
<TooltipIconButton tooltip="Edit" className="aui-user-action-edit">
|
||||
<Pen />
|
||||
<Pencil />
|
||||
</TooltipIconButton>
|
||||
</ActionBarPrimitive.Edit>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue