Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion artifacts/requirements.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7623,7 +7623,7 @@ artifacts:
- id: REQ-243
type: requirement
title: rivet serve artifact view opens the source file at the artifact location
status: proposed
status: verified
description: "In the `rivet serve` artifact view the source file is shown but clicking it does not open the file at the artifact's location, even though sources are configured — this deep-link only exists in the VSIX extension today. Wire the same source open-at-location behaviour into the dashboard. #623."
provenance:
created-by: ai-assisted
Expand Down
37 changes: 29 additions & 8 deletions rivet-cli/src/render/artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,24 +533,45 @@ pub(crate) fn render_artifact_detail(ctx: &RenderContext, id: &str) -> RenderRes
})
});

// Source file link (shown at top for quick access)
// Uses data-source-file + data-source-line attributes — the VS Code
// nav shim in shell.ts picks these up and opens the file at the
// exact line of the artifact definition.
// Source file link (shown at top for quick access).
//
// REQ-243 (#623): the link now navigates the dashboard to the built-in
// `/source` viewer at the artifact's definition line — previously it was a
// dead `href="#"` that did nothing in a plain browser and only worked via
// the VSIX editor shim. Two runtimes, one anchor:
// * Browser dashboard (htmx loaded): `hx-get` swaps in the `/source`
// view and the `onclick` scrolls to the artifact's line.
// * VSIX webview (no htmx): the `shell.ts` shim intercepts the
// `data-source-*` attributes and opens the file in the editor.
// Off-by-one: `/source` row anchors are 1-based, so the browser fragment is
// `source_line + 1`; `data-source-line` stays 0-based for the VSIX host.
let source_link = if let Some(ref sf) = source_file {
let filename = std::path::Path::new(sf)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(sf);
let encoded_path = urlencoding::encode(sf);
let line_attr = source_line
.map(|l| format!(" data-source-line=\"{l}\""))
.unwrap_or_default();
let onclick = source_line
.map(|l| {
format!(
" onclick=\"setTimeout(function(){{var e=document.getElementById('L{}');if(e)e.scrollIntoView({{behavior:'smooth',block:'center'}})}},200)\"",
l + 1
)
})
.unwrap_or_default();
// Distinct class (`source-file-link`, not `source-ref-link`): the
// header file link and the inline cited-source refs must not share a
// selector — a Playwright test picks the first `a.source-ref-link` on
// the detail page expecting an inline `.aadl` ref (aadl.spec.ts).
format!(
" <span class=\"meta\" style=\"float:right;font-size:.85rem\">\
<a href=\"#\" data-source-file=\"{}\"{} title=\"Open source file\">&#128196; {}</a></span>",
html_escape(sf),
line_attr,
html_escape(filename),
<a class=\"source-file-link\" hx-get=\"/source/{enc}\" hx-target=\"#content\" hx-push-url=\"true\" href=\"/source/{enc}\" data-source-file=\"{sf_esc}\"{line_attr}{onclick} title=\"Open source file\">&#128196; {fn_esc}</a></span>",
enc = encoded_path,
sf_esc = html_escape(sf),
fn_esc = html_escape(filename),
)
} else {
String::new()
Expand Down
43 changes: 43 additions & 0 deletions rivet-cli/tests/serve_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,49 @@ fn artifact_detail_has_oembed_discovery_link() {
child.wait().ok();
}

/// REQ-243 (#623): the artifact detail view's source link must navigate the
/// dashboard to the built-in `/source` viewer at the artifact's definition
/// line (previously a dead `href="#"` that only worked in the VSIX editor
/// shim). It must also keep the `data-source-*` attributes so the VSIX shim
/// still opens the file in the editor.
///
/// rivet: verifies REQ-243
#[test]
fn artifact_detail_source_link_opens_source_view() {
let (mut child, port) = start_server();

let (status, body, _headers) = fetch(port, "/artifacts/REQ-001", false);
assert_eq!(status, 200);

// Browser path: deep-links into the /source viewer via htmx (no dead #).
assert!(
body.contains("hx-get=\"/source/") && body.contains("href=\"/source/"),
"artifact detail source link must navigate to the /source viewer, \
not a dead href=\"#\""
);
// The header file link uses a DISTINCT class from inline cited-source refs
// (`source-ref-link`), so selectors targeting inline refs don't catch it —
// see aadl.spec.ts, which picks the first `a.source-ref-link`.
assert!(
body.contains("class=\"source-file-link\""),
"artifact detail source link must use the distinct source-file-link class"
);
// VSIX path: the editor shim still gets the file + line attributes.
assert!(
body.contains("data-source-file="),
"artifact detail source link must keep data-source-file for the VSIX shim"
);
// REQ-001 lives in a source file, so its definition line resolves and the
// browser scroll target / VSIX line attribute must be present.
assert!(
body.contains("data-source-line=") && body.contains("scrollIntoView"),
"artifact detail source link must target the artifact's definition line"
);

child.kill().ok();
child.wait().ok();
}

// ── Embed resolution in documents ──────────────────────────────────────

/// The documents page should not contain any embed-error spans for valid
Expand Down
Loading