Fix file opening and better detect file path

This commit is contained in:
Mark 2026-02-14 14:39:07 -08:00
parent 724be0bb81
commit 68ced90d13
3 changed files with 25 additions and 59 deletions

View File

@ -782,18 +782,10 @@ function ReasoningBlock({
return (
<div className="mb-2">
<div
className="text-[13px] whitespace-pre-wrap leading-relaxed"
style={{ color: "var(--color-text-secondary)" }}
className="text-[13px] whitespace-pre-wrap leading-relaxed"
style={{ color: "var(--color-text-muted)" }}
>
{text}
{isStreaming && (
<span
className="inline-block w-1 h-3.5 ml-0.5 animate-pulse align-text-bottom rounded-sm"
style={{
background: "var(--color-accent)",
}}
/>
)}
</div>
</div>
);

View File

@ -424,20 +424,27 @@ function AttachedFilesCard({ paths }: { paths: string[] }) {
/**
* Detect whether an inline code string looks like a local file/directory path.
* Matches patterns like:
* ~/Downloads/file.pdf
* /Users/name/Documents/file.txt
* /home/user/file.py
* ./relative/path
* ../parent/path
* /etc/config
* Matches anything starting with:
* ~/ (home-relative)
* / (absolute)
* ./ (current-dir-relative)
* ../ (parent-dir-relative)
* Must contain at least one `/` separator to distinguish from plain commands.
*/
const FILE_PATH_RE =
/^(?:~\/|\.\.?\/|\/(?:Users|home|tmp|var|etc|opt|usr|Library|Applications|Downloads|Documents|Desktop)\b)[^\s]*$/;
function looksLikeFilePath(text: string): boolean {
if (!text || text.length < 2 || text.length > 500) {return false;}
return FILE_PATH_RE.test(text.trim());
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;
}
// 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(".");
}
/** Open a file path using the system default application. */
@ -496,14 +503,10 @@ function FilePathCode({
return (
<code
className="file-path-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"}`}
onClick={handleClick}
onContextMenu={handleContextMenu}
title={status === "error" ? "File not found" : `Click to open · Right-click to reveal in Finder`}
style={{
cursor: status === "opening" ? "wait" : "pointer",
opacity: status === "opening" ? 0.7 : 1,
}}
title={status === "error" ? "File not found" : "Click to open · Right-click to reveal in Finder"}
>
<svg
width="12"
@ -514,7 +517,7 @@ function FilePathCode({
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="file-path-icon"
className="shrink-0 opacity-60"
>
{status === "error" ? (
<>
@ -650,7 +653,7 @@ export const ChatMessage = memo(function ChatMessage({ message, isStreaming }: {
return (
<div className="flex justify-end py-2">
<div
className="font-bookerly max-w-[80%] rounded-2xl rounded-br-sm px-4 py-2.5 text-sm leading-6"
className="font-bookerly max-w-[80%] rounded-2xl rounded-br-sm px-3 py-2 text-sm leading-6"
style={{
background: "var(--color-user-bubble)",
color: "var(--color-user-bubble-text)",

View File

@ -980,35 +980,6 @@ a,
padding: 0.15em 0.35em;
}
/* Clickable file path inline code */
.chat-prose code.file-path-code {
display: inline-flex;
align-items: center;
gap: 0.2em;
padding: 0em 0.3em;
cursor: pointer;
transition:
background 0.15s ease,
border-color 0.15s ease,
color 0.15s ease;
color: var(--color-accent);
border: 1px solid var(--color-border);
background: rgba(255, 255, 255, 0.2);
text-decoration: none;
}
.chat-prose code.file-path-code:hover {
background: rgba(255, 255, 255, 0.4);
}
.chat-prose code.file-path-code:active {
background: rgba(255, 255, 255, 1);
}
.chat-prose code.file-path-code .file-path-icon {
flex-shrink: 0;
opacity: 0.6;
}
.chat-prose pre {
background: var(--color-surface);