feat(web): add Copy link to dashboard artifact cards - #19
renagent[bot] wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
What it does: moves copyText into apps/web/lib/copy-text.ts, wraps ArtifactTile in a relative div and adds an always-visible Copy link button as a sibling of the <Link> (top-left, badge-style chrome). Copies ${origin}${artifactPath}, shows Copied! / Copy failed for 1600ms, clears the timer on unmount, names the artifact in aria-label, announces via aria-live.
Checked: checked out 95c1188, read the full diff against the LAX-9 plan. bun run typecheck and bun run test (4 files, 12 tests) pass in apps/web; git diff --check clean. Button is outside the anchor so no nested interactive markup, and the existing .lip-hover-card / .artifact-card hover styles are unaffected since the button is not a descendant of either. Detail page still uses the shared helper with its original "Link copied" text. The added Copy failed state is a reasonable small extension of the plan's "never fail silently" intent.
CI: the red check is @agent-artifacts/cli#test (content integrity / Zod fixture errors). main has failed the same job on every push since 2026-07-07, so it is pre-existing and not caused by this PR. Worth a separate issue.
Not blocking, two inline notes. No manual browser/screen-reader pass was done here; QA should cover click-without-navigate, keyboard focus ring, and the announcement.
| <button | ||
| type="button" | ||
| aria-label={`Copy link to ${artifact.title}`} | ||
| className="absolute left-3 top-3 inline-flex items-center gap-1 rounded-[0.3rem] border border-border bg-[var(--wb-content)]/80 px-1.5 py-0.5 font-mono text-[9px] text-foreground/70 backdrop-blur-sm hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/70 focus-visible:ring-offset-2" |
There was a problem hiding this comment.
Non-blocking: Tailwind v4's ring-offset-2 uses --tw-ring-offset-color: #fff by default, so on the dark theme the focus ring will have a white halo between the button and the ring. Either drop the offset or set it to the page background:
| className="absolute left-3 top-3 inline-flex items-center gap-1 rounded-[0.3rem] border border-border bg-[var(--wb-content)]/80 px-1.5 py-0.5 font-mono text-[9px] text-foreground/70 backdrop-blur-sm hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/70 focus-visible:ring-offset-2" | |
| className="absolute left-3 top-3 inline-flex items-center gap-1 rounded-[0.3rem] border border-border bg-[var(--wb-content)]/80 px-1.5 py-0.5 font-mono text-[9px] text-foreground/70 backdrop-blur-sm hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/70 focus-visible:ring-offset-2 focus-visible:ring-offset-background" |
| expect(writeText).toHaveBeenCalledWith("https://example.com/artifact"); | ||
| }); | ||
|
|
||
| it("falls back when the Clipboard API rejects and returns the fallback result", async () => { |
There was a problem hiding this comment.
Non-blocking: this case asserts both the fallback-success and fallback-failure paths in one it via chained mockReturnValueOnce. If the second branch regresses, the failure message points at the whole test. Splitting into "fallback succeeds" and "fallback reports false" would be clearer and matches the two-case shape the plan asked for. Fine to leave.
Summary
Implements https://linear.app/laxman-personal-workspace/issue/LAX-9 after approval on the issue.
Validation
Review focus / remaining verification
Please check the sibling overlay structure, focus/live feedback and timeout lifecycle. Browser clipboard interaction, visual placement, and keyboard/screen-reader behavior have not been manually exercised. Copied links preserve existing access requirements; no share-link flow is introduced.
Keep this PR draft through Review and QA. A human merges.