diff --git a/packages/app/src/components/message.tsx b/packages/app/src/components/message.tsx
index 27a38261d..5219b79ad 100644
--- a/packages/app/src/components/message.tsx
+++ b/packages/app/src/components/message.tsx
@@ -68,7 +68,7 @@ import type { AgentAttachment } from "@server/shared/messages";
import type { ToolCallDetail } from "@server/server/agent/agent-sdk-types";
import { buildToolCallPresentation } from "@/tool-calls/presentation";
import { resolveToolCallIcon } from "@/utils/tool-call-icon";
-import { parseInlinePathToken, type InlinePathTarget } from "@/utils/inline-path";
+import { type InlinePathTarget, parseInlinePathToken } from "@/utils/inline-path";
import { getMarkdownListMarker, getMarkdownNextSiblingType } from "@/utils/markdown-list";
import { type AssistantFileLinkSource } from "@/utils/assistant-file-link-resolver";
import { useAssistantFileLinkResolver } from "@/hooks/use-assistant-file-link-resolver";
@@ -730,21 +730,6 @@ export const assistantMessageStylesheet = StyleSheet.create((theme) => ({
containerCompactBottom: {
paddingBottom: 0,
},
- // Used in custom markdownRules for path chip styling
- pathChip: {
- backgroundColor: theme.colors.surface2,
- borderRadius: theme.borderRadius.full,
- paddingHorizontal: theme.spacing[2],
- paddingVertical: 2,
- marginRight: theme.spacing[1],
- marginVertical: 2,
- },
- pathChipText: {
- color: theme.colors.foreground,
- fontFamily: Fonts.mono,
- fontSize: 13,
- userSelect: isWeb ? "text" : "auto",
- },
imageFrame: {
width: "100%",
minHeight: 160,
@@ -1018,30 +1003,6 @@ function resolveAssistantImageErrorText(fileError: unknown, dataError: unknown):
return "Unable to load image preview.";
}
-interface InlinePathChipProps {
- content: string;
- parsed: InlinePathTarget;
- onPress: (target: InlinePathTarget) => void;
-}
-
-const INLINE_PATH_CHIP_STYLE = [
- assistantMessageStylesheet.pathChip,
- assistantMessageStylesheet.pathChipText,
-];
-
-function InlinePathChip({ content, parsed, onPress }: InlinePathChipProps) {
- const handlePress = useCallback(() => onPress(parsed), [onPress, parsed]);
- return (
-
- {content}
-
- );
-}
-
function MarkdownLink({
source,
style,
@@ -1156,7 +1117,7 @@ function getMarkdownLinkSource(node: AssistantMarkdownAstNode): AssistantFileLin
text: getMarkdownNodeText(node),
markup: node.markup,
sourceInfo: node.sourceInfo,
- sourceType: node.sourceType,
+ sourceType: node.sourceType === "inline-code" ? "inline-code" : undefined,
};
}
@@ -1627,6 +1588,46 @@ function MarkdownInheritedCodeLink({
);
}
+interface MarkdownInlinePathCodeLinkProps {
+ content: string;
+ inheritedStyles: TextStyle;
+ codeInlineStyle: TextStyle;
+ linkStyle: TextStyle;
+ onPress: (source: AssistantFileLinkSource) => void;
+ onPrefetch: (source: AssistantFileLinkSource) => void;
+}
+
+function MarkdownInlinePathCodeLink({
+ content,
+ inheritedStyles,
+ codeInlineStyle,
+ linkStyle,
+ onPress,
+ onPrefetch,
+}: MarkdownInlinePathCodeLinkProps) {
+ const source = useMemo(
+ () => ({
+ href: content,
+ text: content,
+ sourceType: "inline-code",
+ }),
+ [content],
+ );
+
+ return (
+
+ {content}
+
+ );
+}
+
interface MarkdownListItemContentProps {
contentStyle: ViewStyle;
children: ReactNode;
@@ -1811,16 +1812,19 @@ export const AssistantMessage = memo(function AssistantMessage({
) => {
const content = node.content ?? "";
const isLinkedInlineCode = nodeHasParentType(parent, "link");
- const parsed =
- onInlinePathPress && !isLinkedInlineCode ? parseInlinePathToken(content) : null;
+ const shouldResolveInlinePath =
+ onInlinePathPress && !isLinkedInlineCode && parseInlinePathToken(content);
- if (parsed && onInlinePathPress) {
+ if (shouldResolveInlinePath) {
return (
-
);
}
diff --git a/packages/app/src/utils/assistant-file-link-resolver.test.ts b/packages/app/src/utils/assistant-file-link-resolver.test.ts
index 2e279d84d..0ac22cbaf 100644
--- a/packages/app/src/utils/assistant-file-link-resolver.test.ts
+++ b/packages/app/src/utils/assistant-file-link-resolver.test.ts
@@ -238,6 +238,44 @@ describe("assistant file link resolver", () => {
expect(result.opened).toBe(true);
});
+ it("resolves inline-code basename line refs through directory suggestions", async () => {
+ const suggestions = vi.fn(async () =>
+ resolvedSuggestions([
+ { path: "packages/server/src/server/workspace-git-service.ts", kind: "file" },
+ ]),
+ );
+ const openWorkspaceFile = vi.fn();
+ const resolver = createAssistantFileLinkResolver({
+ getDirectorySuggestions: suggestions,
+ openWorkspaceFile,
+ openExternalUrl: vi.fn(),
+ });
+
+ const result = await resolver.open({
+ context: { ...CONTEXT, workspaceRoot: "/Users/test/project" },
+ source: {
+ href: "workspace-git-service.ts:1553",
+ text: "workspace-git-service.ts:1553",
+ sourceType: "inline-code",
+ },
+ });
+
+ expect(suggestions).toHaveBeenCalledWith({
+ query: "workspace-git-service.ts",
+ cwd: "/Users/test/project",
+ includeFiles: true,
+ includeDirectories: false,
+ limit: 1,
+ });
+ expect(openWorkspaceFile).toHaveBeenCalledWith({
+ raw: "workspace-git-service.ts:1553",
+ path: "/Users/test/project/packages/server/src/server/workspace-git-service.ts",
+ lineStart: 1553,
+ lineEnd: undefined,
+ });
+ expect(result.opened).toBe(true);
+ });
+
it("keeps explicit external URLs external", async () => {
const openExternalUrl = vi.fn();
const resolver = createAssistantFileLinkResolver({
@@ -391,5 +429,12 @@ describe("assistant file link resolver", () => {
sourceInfo: "",
}),
).toBe("http://dumm.md");
+ expect(
+ getAssistantFileLinkToken({
+ href: "workspace-git-service.ts:1553",
+ text: "workspace-git-service.ts:1553",
+ sourceType: "inline-code",
+ }),
+ ).toBe("workspace-git-service.ts:1553");
});
});
diff --git a/packages/app/src/utils/assistant-file-link-resolver.ts b/packages/app/src/utils/assistant-file-link-resolver.ts
index ccde952b7..bd3dbd83b 100644
--- a/packages/app/src/utils/assistant-file-link-resolver.ts
+++ b/packages/app/src/utils/assistant-file-link-resolver.ts
@@ -15,7 +15,7 @@ export interface AssistantFileLinkSource {
text?: string;
markup?: string;
sourceInfo?: string;
- sourceType?: string;
+ sourceType?: "inline-code";
}
export interface DirectorySuggestionEntry {
@@ -163,7 +163,7 @@ export function createAssistantFileLinkResolver(
}
export function getAssistantFileLinkToken(source: AssistantFileLinkSource): string {
- if (isLinkifiedSource(source)) {
+ if (isLinkifiedSource(source) || source.sourceType === "inline-code") {
const text = source.text?.trim();
if (text && isFileLookingAssistantToken(text)) {
return text;