Fix file link and add lookLikeFileLink detector and click to preview
This commit is contained in:
parent
fcbec6c4d6
commit
f960ed3030
@ -35,7 +35,24 @@ export async function POST(req: Request) {
|
||||
? rawPath.replace(/^~/, homedir())
|
||||
: rawPath;
|
||||
|
||||
const resolved = resolve(normalize(expanded));
|
||||
let resolved = resolve(normalize(expanded));
|
||||
|
||||
// If the file doesn't exist and looks like a bare filename, try to locate it
|
||||
// using macOS Spotlight (mdfind).
|
||||
if (!existsSync(resolved) && !rawPath.includes("/")) {
|
||||
const found = await new Promise<string | null>((res) => {
|
||||
exec(
|
||||
`mdfind -name ${JSON.stringify(rawPath)} | head -1`,
|
||||
(err, stdout) => {
|
||||
if (err || !stdout.trim()) {res(null);}
|
||||
else {res(stdout.trim().split("\n")[0]);}
|
||||
},
|
||||
);
|
||||
});
|
||||
if (found && existsSync(found)) {
|
||||
resolved = found;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existsSync(resolved)) {
|
||||
return Response.json(
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { exec } from "node:child_process";
|
||||
import { existsSync, statSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { basename, normalize, resolve } from "node:path";
|
||||
@ -39,7 +40,24 @@ export async function GET(req: Request) {
|
||||
const expandedPath = candidatePath.startsWith("~/")
|
||||
? candidatePath.replace(/^~/, homedir())
|
||||
: candidatePath;
|
||||
const resolvedPath = resolve(normalize(expandedPath));
|
||||
let resolvedPath = resolve(normalize(expandedPath));
|
||||
|
||||
// If the path doesn't exist and looks like a bare filename, try to locate it
|
||||
// using macOS Spotlight (mdfind).
|
||||
if (!existsSync(resolvedPath) && !rawPath.includes("/")) {
|
||||
const found = await new Promise<string | null>((res) => {
|
||||
exec(
|
||||
`mdfind -name ${JSON.stringify(rawPath)} | head -1`,
|
||||
(err, stdout) => {
|
||||
if (err || !stdout.trim()) {res(null);}
|
||||
else {res(stdout.trim().split("\n")[0]);}
|
||||
},
|
||||
);
|
||||
});
|
||||
if (found && existsSync(found)) {
|
||||
resolvedPath = found;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existsSync(resolvedPath)) {
|
||||
return Response.json(
|
||||
|
||||
@ -452,17 +452,28 @@ function AttachedFilesCard({ paths }: { paths: string[] }) {
|
||||
function looksLikeFilePath(text: string): boolean {
|
||||
const t = text.trim();
|
||||
if (!t || t.length < 3 || t.length > 500) {return false;}
|
||||
// Must start with a path prefix
|
||||
if (!(t.startsWith("~/") || t.startsWith("/") || t.startsWith("./") || t.startsWith("../"))) {
|
||||
return false;
|
||||
// Full path prefix
|
||||
if (t.startsWith("~/") || t.startsWith("/") || t.startsWith("./") || t.startsWith("../")) {
|
||||
const afterPrefix = t.startsWith("~/") ? t.slice(2) :
|
||||
t.startsWith("../") ? t.slice(3) :
|
||||
t.startsWith("./") ? t.slice(2) :
|
||||
t.slice(1);
|
||||
return afterPrefix.includes("/") || afterPrefix.includes(".");
|
||||
}
|
||||
// Must have at least one path separator beyond the prefix
|
||||
// (avoids matching bare `/` or standalone commands like `/bin`)
|
||||
const afterPrefix = t.startsWith("~/") ? t.slice(2) :
|
||||
t.startsWith("../") ? t.slice(3) :
|
||||
t.startsWith("./") ? t.slice(2) :
|
||||
t.slice(1);
|
||||
return afterPrefix.includes("/") || afterPrefix.includes(".");
|
||||
// Bare filename with a known extension (e.g. "Rachapoom-Passport.pdf")
|
||||
const fileExtPattern = /\.(pdf|docx?|xlsx?|pptx?|csv|txt|rtf|pages|numbers|key|md|json|yaml|yml|toml|xml|html?|css|jsx?|tsx?|py|rb|go|rs|java|cpp|c|h|sh|sql|swift|kt|png|jpe?g|gif|webp|svg|bmp|ico|heic|tiff|mp[34]|webm|mov|avi|mkv|flv|wav|ogg|aac|flac|m4a|zip|tar|gz|dmg)$/i;
|
||||
if (fileExtPattern.test(t) && !t.includes(" ")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if text looks like a filename (allows spaces, used for bold text). */
|
||||
function looksLikeFileName(text: string): boolean {
|
||||
const t = text.trim();
|
||||
if (!t || t.length < 3 || t.length > 300) {return false;}
|
||||
const fileExtPattern = /\.(pdf|docx?|xlsx?|pptx?|csv|txt|rtf|pages|numbers|key|md|json|yaml|yml|toml|xml|html?|css|jsx?|tsx?|py|rb|go|rs|java|cpp|c|h|sh|sql|swift|kt|png|jpe?g|gif|webp|svg|bmp|ico|heic|tiff|mp[34]|webm|mov|avi|mkv|flv|wav|ogg|aac|flac|m4a|zip|tar|gz|dmg)$/i;
|
||||
return fileExtPattern.test(t);
|
||||
}
|
||||
|
||||
/** Open a file path using the system default application. */
|
||||
@ -559,7 +570,8 @@ function FilePathCode({
|
||||
|
||||
return (
|
||||
<code
|
||||
className={`inline-flex items-center gap-[0.2em] px-[0.3em] py-0 whitespace-nowrap max-w-full overflow-hidden text-ellipsis no-underline transition-colors duration-150 rounded-md text-[color:var(--color-accent)] border border-[color:var(--color-border)] bg-white/20 hover:bg-white/40 active:bg-white ${status === "opening" ? "cursor-wait opacity-70" : "cursor-pointer"}`}
|
||||
className={`px-[0.3em] no-underline transition-colors duration-150 rounded-[4px] border border-[color:var(--color-border)] bg-white/20 hover:bg-white/40 active:bg-white ${status === "opening" ? "cursor-wait opacity-70" : "cursor-pointer"}`}
|
||||
style={{ color: "var(--color-accent)" }}
|
||||
onClick={handleClick}
|
||||
onContextMenu={handleContextMenu}
|
||||
title={
|
||||
@ -570,30 +582,6 @@ function FilePathCode({
|
||||
: "Click to open · Right-click to reveal in Finder"
|
||||
}
|
||||
>
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="shrink-0 opacity-60"
|
||||
>
|
||||
{status === "error" ? (
|
||||
<>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="15" x2="9" y1="9" y2="15" />
|
||||
<line x1="9" x2="15" y1="9" y2="15" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
|
||||
<path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
@ -711,6 +699,22 @@ function createMarkdownComponents(
|
||||
// Regular inline code
|
||||
return <code {...props}>{children}</code>;
|
||||
},
|
||||
// Bold text — detect filenames and make them clickable
|
||||
strong: ({ children, ...props }) => {
|
||||
const text = typeof children === "string" ? children
|
||||
: Array.isArray(children) ? children.filter((c) => typeof c === "string").join("")
|
||||
: "";
|
||||
if (text && looksLikeFileName(text)) {
|
||||
return (
|
||||
<strong {...props}>
|
||||
<FilePathCode path={text} onFilePathClick={onFilePathClick}>
|
||||
{children}
|
||||
</FilePathCode>
|
||||
</strong>
|
||||
);
|
||||
}
|
||||
return <strong {...props}>{children}</strong>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -674,6 +674,7 @@ export const ChatPanel = forwardRef<ChatPanelHandle, ChatPanelProps>(
|
||||
|
||||
// ── Message queue (messages to send after current run completes) ──
|
||||
const [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);
|
||||
const [rawView, setRawView] = useState(false);
|
||||
|
||||
const filePath = fileContext?.path ?? null;
|
||||
|
||||
@ -1548,6 +1549,12 @@ export const ChatPanel = forwardRef<ChatPanelHandle, ChatPanelProps>(
|
||||
</svg>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" side="bottom">
|
||||
<DropdownMenuItem
|
||||
onSelect={() => setRawView((v) => !v)}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" /></svg>
|
||||
{rawView ? "Rendered view" : "Raw view"}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
variant="destructive"
|
||||
onSelect={() => onDeleteSession(currentSessionId)}
|
||||
@ -1692,7 +1699,14 @@ export const ChatPanel = forwardRef<ChatPanelHandle, ChatPanelProps>(
|
||||
<div
|
||||
className={`${compact ? "" : "max-w-2xl mx-auto"} py-3`}
|
||||
>
|
||||
{messages.map((message, i) => (
|
||||
{rawView ? (
|
||||
<pre
|
||||
className="text-xs whitespace-pre-wrap break-all font-mono p-4 rounded-xl"
|
||||
style={{ color: "var(--color-text)", background: "var(--color-surface-hover)" }}
|
||||
>
|
||||
{JSON.stringify(messages, null, 2)}
|
||||
</pre>
|
||||
) : messages.map((message, i) => (
|
||||
<ChatMessage
|
||||
key={message.id}
|
||||
message={message}
|
||||
|
||||
@ -211,7 +211,7 @@ export function ChatSessionsSidebar({
|
||||
width: typeof width === "number" ? `${width}px` : width,
|
||||
minWidth: typeof width === "number" ? `${width}px` : width,
|
||||
borderColor: "var(--color-border)",
|
||||
background: "var(--color-surface)",
|
||||
background: "var(--color-sidebar-bg)",
|
||||
}}
|
||||
>
|
||||
{/* Scrollable list fills the sidebar; header overlays the top with blur */}
|
||||
@ -414,7 +414,7 @@ export function ChatSessionsSidebar({
|
||||
style={{
|
||||
height: headerHeight,
|
||||
borderColor: "var(--color-border)",
|
||||
background: "color-mix(in srgb, var(--color-surface) 80%, transparent)",
|
||||
background: "color-mix(in srgb, var(--color-sidebar-bg) 80%, transparent)",
|
||||
}}
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
|
||||
@ -81,14 +81,15 @@ function isSystemFile(path: string): boolean {
|
||||
// --- Icons (inline SVG, zero-dep) ---
|
||||
|
||||
function FolderIcon({ open }: { open?: boolean }) {
|
||||
return open ? (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
||||
</svg>
|
||||
return (
|
||||
<img
|
||||
src={open ? "/icons/folder-open.png" : "/icons/folder.png"}
|
||||
alt=""
|
||||
width={16}
|
||||
height={16}
|
||||
draggable={false}
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -110,17 +111,13 @@ function KanbanIcon() {
|
||||
|
||||
function DocumentIcon() {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /><path d="M10 9H8" /><path d="M16 13H8" /><path d="M16 17H8" />
|
||||
</svg>
|
||||
<img src="/icons/document.png" alt="" width={16} height={16} draggable={false} style={{ flexShrink: 0, filter: "drop-shadow(0 0.5px 1.5px rgba(0,0,0,0.2))" }} />
|
||||
);
|
||||
}
|
||||
|
||||
function FileIcon() {
|
||||
return (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
||||
</svg>
|
||||
<img src="/icons/document.png" alt="" width={16} height={16} draggable={false} style={{ flexShrink: 0, filter: "drop-shadow(0 0.5px 1.5px rgba(0,0,0,0.2))" }} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -548,7 +545,7 @@ function DraggableNode({
|
||||
onCancel={onCancelRename}
|
||||
/>
|
||||
) : (
|
||||
<span className="truncate flex-1">{node.name.replace(/\.md$/, "")}</span>
|
||||
<span className="truncate flex-1">{node.name}</span>
|
||||
)}
|
||||
|
||||
{/* Workspace badge for the workspace root entry point */}
|
||||
|
||||
@ -68,18 +68,14 @@ function HomeIcon() {
|
||||
|
||||
function FolderOpenIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
<img
|
||||
src="/icons/folder-open.png"
|
||||
alt=""
|
||||
width={20}
|
||||
height={20}
|
||||
draggable={false}
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -173,25 +169,26 @@ function SearchIcon() {
|
||||
|
||||
function SmallFolderIcon() {
|
||||
return (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" />
|
||||
</svg>
|
||||
<img
|
||||
src="/icons/folder.png"
|
||||
alt=""
|
||||
width={14}
|
||||
height={14}
|
||||
draggable={false}
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SmallFileIcon() {
|
||||
return (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" />
|
||||
</svg>
|
||||
<img src="/icons/document.png" alt="" width={14} height={14} draggable={false} style={{ flexShrink: 0, filter: "drop-shadow(0 0.5px 1.5px rgba(0,0,0,0.2))" }} />
|
||||
);
|
||||
}
|
||||
|
||||
function SmallDocIcon() {
|
||||
return (
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /><path d="M10 9H8" /><path d="M16 13H8" /><path d="M16 17H8" />
|
||||
</svg>
|
||||
<img src="/icons/document.png" alt="" width={14} height={14} draggable={false} style={{ flexShrink: 0, filter: "drop-shadow(0 0.5px 1.5px rgba(0,0,0,0.2))" }} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -417,13 +414,13 @@ export function WorkspaceSidebar({
|
||||
style={{
|
||||
width: typeof width === "number" ? `${width}px` : width,
|
||||
minWidth: typeof width === "number" ? `${width}px` : width,
|
||||
background: "var(--color-surface)",
|
||||
background: "var(--color-sidebar-bg)",
|
||||
borderColor: "var(--color-border)",
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className="flex items-center gap-2.5 px-4 py-3 border-b"
|
||||
className="flex items-center gap-2 px-3 py-2.5 border-b"
|
||||
style={{ borderColor: "var(--color-border)" }}
|
||||
>
|
||||
{isBrowsing ? (
|
||||
@ -473,16 +470,14 @@ export function WorkspaceSidebar({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void onGoToChat?.()}
|
||||
className="w-8 h-8 rounded-lg flex items-center justify-center shrink-0 cursor-pointer transition-opacity"
|
||||
className="w-7 h-7 rounded-lg flex items-center justify-center shrink-0 cursor-pointer transition-colors hover:bg-stone-200 dark:hover:bg-stone-700"
|
||||
style={{
|
||||
background: "var(--color-accent-light)",
|
||||
color: "var(--color-accent)",
|
||||
background: "transparent",
|
||||
color: "var(--color-text-muted)",
|
||||
}}
|
||||
title="All Chats"
|
||||
onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.opacity = "0.7"; }}
|
||||
onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.opacity = "1"; }}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" />
|
||||
</svg>
|
||||
@ -495,29 +490,22 @@ export function WorkspaceSidebar({
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={switching}
|
||||
className="flex-1 min-w-0 w-full flex items-center justify-between gap-2 text-left rounded-lg py-1.5 px-2 transition-colors hover:bg-(--color-surface-hover) disabled:opacity-50"
|
||||
style={{ color: "var(--color-text)" }}
|
||||
className="flex-1 min-w-0 w-full flex items-center justify-between gap-1.5 text-left rounded-lg py-1 px-1.5 transition-colors hover:bg-stone-100 dark:hover:bg-stone-800 disabled:opacity-50"
|
||||
title="Switch workspace profile"
|
||||
>
|
||||
<div className="min-w-0 truncate">
|
||||
<div
|
||||
className="text-sm font-medium truncate"
|
||||
style={{ color: "var(--color-text)" }}
|
||||
className="text-[13px] font-semibold truncate text-stone-700 dark:text-stone-200"
|
||||
>
|
||||
{orgName || "Workspace"}
|
||||
</div>
|
||||
<div
|
||||
className="text-[11px] flex items-center gap-1 truncate"
|
||||
style={{ color: "var(--color-text-muted)" }}
|
||||
className="text-[11px] flex items-center gap-1 truncate text-stone-400 dark:text-stone-500"
|
||||
>
|
||||
<span>Ironclaw</span>
|
||||
{profileName && profileName !== "default" && (
|
||||
<span
|
||||
className="px-1 py-0.5 rounded text-[10px] shrink-0"
|
||||
style={{
|
||||
background: "var(--color-accent-light)",
|
||||
color: "var(--color-accent)",
|
||||
}}
|
||||
className="px-1 py-0.5 rounded text-[10px] shrink-0 bg-stone-200 text-stone-500 dark:bg-stone-700 dark:text-stone-400"
|
||||
>
|
||||
{profileName}
|
||||
</span>
|
||||
@ -525,11 +513,10 @@ export function WorkspaceSidebar({
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 shrink-0 transition-transform ${isOpen ? "rotate-180" : ""}`}
|
||||
className={`w-3 h-3 shrink-0 transition-transform text-stone-400 ${isOpen ? "rotate-180" : ""}`}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
style={{ color: "var(--color-text-muted)" }}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
/* Background / Surface */
|
||||
--color-bg: #f5f5f4;
|
||||
--color-surface: #ffffff;
|
||||
--color-sidebar-bg: #fafaf9;
|
||||
--color-sidebar-bg: #ffffff;
|
||||
--color-main-bg: rgba(250, 250, 249, 0.5);
|
||||
--color-surface-hover: #f5f4f1;
|
||||
--color-surface-raised: #ffffff;
|
||||
|
||||
BIN
apps/web/public/icons/document.png
Normal file
BIN
apps/web/public/icons/document.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
apps/web/public/icons/folder-open.png
Normal file
BIN
apps/web/public/icons/folder-open.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
BIN
apps/web/public/icons/folder.png
Normal file
BIN
apps/web/public/icons/folder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
Loading…
x
Reference in New Issue
Block a user