zed/crates/agent_ui/Cargo.toml
MartinYe1234 f78f6da255
Make file paths in backticks clickable in agent panel (#57303)
When the agent mentions a file path inside `backticks` (e.g. ``
`src/main.rs` `` or `` `src/main.rs:42` ``), the rendered code span now
becomes a clickable link in the agent panel. Clicking opens the
referenced file in the workspace, jumping to the right line and column
when present.

## How it works

- **Shared path resolution.** Extracted `OpenTarget` and the
workspace/worktree resolution logic out of
`terminal_view::terminal_path_like_target` into a new
`workspace::path_link` module so both the terminal and the agent panel
can use the same code. Includes a `sanitize_path_text` helper ported
from the terminal's URL/punctuation handling. Pure refactor — terminal
behavior is unchanged.
- **`markdown` crate hook.** Added
`MarkdownElement::on_code_span_link(callback)`. When the callback
returns `Some(url)` for a given code span's contents, the existing
`push_link` machinery wires up cmd-hover, hit testing, and the existing
`on_url_click` callback. When it returns `None`, the code span renders
as before. The hook is opt-in, so `markdown` stays workspace-agnostic.
- **Agent panel wiring.** `render_agent_markdown` constructs an
`AgentCodeSpanResolver` that snapshots the project's visible worktree
entries plus their file extensions. `try_resolve` does a cheap
synchronous heuristic check (path must contain `/`/`\` or end in an
extension present in the workspace, can't be a URL, can't be all digits,
etc.) and then looks the candidate up in the per-worktree
`HashSet<Arc<RelPath>>`. On a hit it returns a `MentionUri::File` or
`MentionUri::Selection` URI, which the existing `thread_view::open_link`
already knows how to open at the right line.

## Edge cases handled

- Code spans inside fenced code blocks stay plain (gated on
`builder.code_block_stack.is_empty()`, matching how regular markdown
links behave).
- Trailing prose punctuation (`` `src/main.rs.` ``) is stripped before
lookup.
- Identifiers like `` `String` ``, `` `await` ``, `` `npm run dev` ``
stay plain — they don't pass the path-like heuristic.
- Cross-platform path separators handled via the per-worktree
`PathStyle`.

## Tests

- `crates/markdown` — unit test asserting code spans become links when
the callback returns `Some`, and stay plain when it doesn't.
- `crates/agent_ui` — unit test for `AgentCodeSpanResolver::try_resolve`
covering hits with and without a `:line` suffix, misses, identifiers,
and trailing punctuation.
- Existing `terminal_view` tests cover the moved resolution code
(unchanged behavior).

## Notes

- There's currently a temporary `log::info!` in
`AgentCodeSpanResolver::try_resolve` that reports per-call worktree-walk
timing and a cumulative total. Kept in for now to verify the feature
isn't being called excessively during streaming renders. Can be removed
before merge.
- Resolution is sync-only against worktree entries; absolute paths
outside the workspace are not resolved (would require an async re-render
path).

Closes AI-277

Release Notes:

- Made file paths in `backticks` clickable in the agent panel; clicking
opens the referenced file at the given line when present.
2026-05-21 22:04:32 +00:00

150 lines
4.4 KiB
TOML

[package]
name = "agent_ui"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/agent_ui.rs"
doctest = false
[features]
test-support = [
"acp_thread/test-support",
"eval_utils",
"gpui/test-support",
"language/test-support",
"reqwest_client",
"workspace/test-support",
"agent/test-support",
]
audio = ["dep:audio"]
unit-eval = []
[dependencies]
acp_thread.workspace = true
action_log.workspace = true
agent-client-protocol.workspace = true
agent.workspace = true
async-channel.workspace = true
agent_servers.workspace = true
agent_settings.workspace = true
agent_skills.workspace = true
ai_onboarding.workspace = true
anyhow.workspace = true
heapless.workspace = true
audio = { workspace = true, optional = true }
base64.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
client.workspace = true
cloud_api_types.workspace = true
collections.workspace = true
command_palette_hooks.workspace = true
component.workspace = true
context_server.workspace = true
db.workspace = true
editor.workspace = true
eval_utils = { workspace = true, optional = true }
extension.workspace = true
extension_host.workspace = true
feature_flags.workspace = true
file_icons.workspace = true
fs.workspace = true
futures.workspace = true
git.workspace = true
fuzzy.workspace = true
gpui.workspace = true
gpui_tokio.workspace = true
html_to_markdown.workspace = true
http_client.workspace = true
indoc.workspace = true
itertools.workspace = true
jsonschema.workspace = true
language.workspace = true
language_model.workspace = true
language_models.workspace = true
log.workspace = true
lru.workspace = true
lsp.workspace = true
markdown.workspace = true
menu.workspace = true
multi_buffer.workspace = true
notifications.workspace = true
ordered-float.workspace = true
parking_lot.workspace = true
paths.workspace = true
picker.workspace = true
platform_title_bar.workspace = true
postage.workspace = true
project.workspace = true
prompt_store.workspace = true
proto.workspace = true
rand.workspace = true
release_channel.workspace = true
remote.workspace = true
remote_connection.workspace = true
rope.workspace = true
rules_library.workspace = true
skill_creator.workspace = true
schemars.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
settings.workspace = true
streaming_diff.workspace = true
task.workspace = true
telemetry.workspace = true
terminal.workspace = true
terminal_view.workspace = true
text.workspace = true
theme.workspace = true
theme_settings.workspace = true
time.workspace = true
ui.workspace = true
ui_input.workspace = true
url.workspace = true
util.workspace = true
uuid.workspace = true
watch.workspace = true
workspace.workspace = true
zed_actions.workspace = true
image.workspace = true
reqwest_client = { workspace = true, optional = true }
[dev-dependencies]
acp_thread = { workspace = true, features = ["test-support"] }
agent = { workspace = true, features = ["test-support"] }
agent_servers = { workspace = true, features = ["test-support"] }
buffer_diff = { workspace = true, features = ["test-support"] }
client = { workspace = true, features = ["test-support"] }
clock = { workspace = true, features = ["test-support"] }
db = { workspace = true, features = ["test-support"] }
editor = { workspace = true, features = ["test-support"] }
eval_utils.workspace = true
git_ui.workspace = true
gpui = { workspace = true, "features" = ["test-support"] }
http_client = { workspace = true, features = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
language_model = { workspace = true, "features" = ["test-support"] }
node_runtime = { workspace = true, features = ["test-support"] }
pretty_assertions.workspace = true
project = { workspace = true, features = ["test-support"] }
remote = { workspace = true, features = ["test-support"] }
remote_connection = { workspace = true, features = ["test-support"] }
remote_server = { workspace = true, features = ["test-support"] }
search = { workspace = true, features = ["test-support"] }
semver.workspace = true
reqwest_client.workspace = true
tempfile.workspace = true
vim.workspace = true
tree-sitter-md.workspace = true
unindent.workspace = true
terminal = { workspace = true, features = ["test-support"] }