Skip to content

Improve Artifact tool look&feel: label-linked result, lean tool use (#262)#263

Merged
cboos merged 3 commits into
mainfrom
dev/artifact-lookfeel
Jul 4, 2026
Merged

Improve Artifact tool look&feel: label-linked result, lean tool use (#262)#263
cboos merged 3 commits into
mainfrom
dev/artifact-lookfeel

Conversation

@cboos

@cboos cboos commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

(Claude) Fixes #262.

What changed

Tool use is now description-primary — the favicon and version label belong to the published page, so they move to the result line. The redeploy addendum stays as a meta row: a redeploys existing artifact link (URL in href/hover only) plus the force flag spelled out as force: overwrite without conflict check — a normal redeploy sends the artifact's base version so a concurrent write from another session fails instead of being silently clobbered; force skips that check (hover tooltip explains this in the HTML).

Tool result becomes Published artifact (favicon) (label):

  • The label — falling back to page title, then source basename, then URL — is the clickable link; the full URL stays in href and the hover title instead of the visible text.
  • The wire result is {url, path, title} and carries no favicon/label, so both are enriched from the paired tool_use input.
  • The favicon renders as a real <img> only when the value is an is_safe_web_url http(s) URL (escaped src, referrerpolicy="no-referrer"); the normal emoji value stays an inert text span; over-long non-URL values are dropped (ARTIFACT_FAVICON_TEXT_MAX, shared between renderers).

Hardening found while testing

The Markdown link-target encoder only percent-encoded parentheses, so an accepted https URL carrying quotes could smuggle attributes through a downstream Markdown→HTML conversion. The new safe_markdown_link_target() also encodes quotes, angle brackets and spaces, applies to every Markdown link/image destination, and is surfaced as a plugin helper (plugins.__all__ lazy re-export + dev-docs/plugins.md §4.1/§4.2), following the is_safe_web_url pattern.

Verification

  • New/updated tests: label-link fallback chain, favicon img/emoji/hostile-scheme/quote-injection/over-long variants (HTML + Markdown), hostile fixture entry exercising the quote-injection favicon end-to-end through both pipelines, plugin-API presence + behaviour tests.
  • Full unit suite green; snapshot churn is the appended .artifact-favicon CSS rule only.
  • Before/after renders eyeballed via Playwright against the checked-in fixture.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Published artifact lines now use clearer label-based link text and can include a small favicon/icon when available.
  • Bug Fixes
    • Hardened HTML/Markdown rendering to prevent unsafe URLs/schemes from becoming clickable, and improved escaping against metadata injection.
    • Tightened favicon handling (only safe image URLs render as icons; hostile/overlong values are neutralized).
  • Documentation
    • Added safe_markdown_link_target to the documented plugin helper surface and clarified safe Markdown link target guidance.
  • Tests
    • Updated artifact rendering, XSS, and plugin helper expectations (including favicon styling).

cboos and others added 2 commits July 4, 2026 15:02
…262)

Tool use now shows the description as the primary content — the favicon
and version label belong to the published page, so the tool result line
carries them instead. The redeploy addendum stays as a meta row: a
'redeploys existing artifact' link (URL in href/hover only) plus the
force flag spelled out as 'force: overwrite without conflict check'
(a normal redeploy sends the artifact's base version so a concurrent
write from another session fails instead of being silently clobbered;
force skips that check).

Tool result becomes 'Published artifact (favicon) (label)': the label —
falling back to page title, then source basename, then URL — is the
clickable link, keeping the full URL out of the visible text (href +
hover title carry it). The favicon renders as a real <img> only when
the value is an is_safe_web_url http(s) URL (escaped src,
referrerpolicy=no-referrer — the wire result is {url, path, title} and
carries no favicon, so it is enriched from the paired tool_use input);
the normal emoji value stays an inert text span, over-long non-URL
values are dropped. A hostile-scheme result URL still degrades to the
escaped URL itself rather than hiding behind a label.

Hardening found while testing: the Markdown link-target encoder only
percent-encoded parentheses, so an accepted https URL carrying quotes
could smuggle attributes through a downstream Markdown→HTML conversion.
The new safe_markdown_link_target() also encodes quotes, angle brackets
and spaces, applies to every Markdown link/image destination, and is
surfaced as a plugin helper (plugins._PUBLIC_HELPERS lazy re-export +
dev-docs/plugins.md §4.1/§4.2) alongside is_safe_web_url.

Snapshot churn is the appended .artifact-favicon CSS rule only.

Closes #262

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review follow-up (#262): _artifact_favicon_md hardcoded 16 while the
HTML path used a named module constant — same policy, two literals.
Hoist ARTIFACT_FAVICON_TEXT_MAX to utils.py (alongside the shared
is_safe_web_url policy) so the cap can't drift between renderers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 987125af-a1c9-48c3-b7ae-4256dccf50a0

📥 Commits

Reviewing files that changed from the base of the PR and between 210dfd8 and 7c6d9bc.

📒 Files selected for processing (2)
  • claude_code_log/markdown/renderer.py
  • test/test_plugin_xss_api.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/test_plugin_xss_api.py
  • claude_code_log/markdown/renderer.py

📝 Walkthrough

Walkthrough

This PR forwards Artifact tool favicon and label metadata from tool use inputs into result rendering, changes HTML and Markdown artifact output to show a labeled “Published artifact” line, and adds URL-safety handling for Markdown targets and favicon rendering. It also updates plugin exports, docs, styles, snapshots, and tests.

Changes

Artifact rendering and link-safety updates

Layer / File(s) Summary
ArtifactOutput model and metadata forwarding
claude_code_log/models.py, claude_code_log/factories/tool_factory.py, claude_code_log/utils.py
ArtifactOutput gains favicon/label fields, create_tool_result_message() copies them from the paired tool use input, and ARTIFACT_FAVICON_TEXT_MAX is added.
HTML artifact rendering and favicon/link helpers
claude_code_log/html/tool_formatters.py, claude_code_log/html/templates/components/message_styles.css, test/__snapshots__/test_snapshot_html.ambr
Adds labeled-link and favicon helpers, reworks artifact input/output HTML formatting, and adds favicon image styling plus snapshot updates.
Markdown artifact rendering and target encoding
claude_code_log/markdown/renderer.py
Adds safe_markdown_link_target, extends Markdown URL safety, and rewrites artifact input/output rendering with favicon handling.
Plugin export and documentation
claude_code_log/plugins.py, dev-docs/plugins.md
Exposes safe_markdown_link_target through plugins.py and documents the helper plus URL-target encoding rules.
Artifact rendering and plugin API tests
test/test_artifact_rendering.py, test/test_data/artifact_tool.jsonl, test/test_plugin_xss_api.py
Updates artifact rendering assertions for the new favicon/label contract, revises the XSS fixture, and adds plugin API tests for safe_markdown_link_target.

Estimated code review effort: 3 (Moderate) | ~35 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Artifact tool_use
  participant tool_factory
  participant HTML/Markdown renderers

  Artifact tool_use->>tool_factory: paired input metadata
  tool_factory->>tool_factory: create_tool_result_message(tool_result)
  tool_factory->>tool_factory: create_tool_output() -> ArtifactOutput
  tool_factory->>tool_factory: copy favicon, label from paired tool_use
  tool_factory->>HTML/Markdown renderers: ArtifactOutput with favicon, label
  HTML/Markdown renderers->>HTML/Markdown renderers: render labeled Published artifact line
Loading

Possibly related PRs

  • daaain/claude-code-log#260: Adds the initial Artifact tool parsing and baseline HTML/Markdown rendering that this PR extends with favicon/label metadata plumbing.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main Artifact renderer and tool-use changes in the PR.
Linked Issues check ✅ Passed The changes match #262 by keeping tool use description-only and rendering the result as a published artifact with inline favicon and clickable label.
Out of Scope Changes check ✅ Passed The additional Markdown safety helper, docs, and tests support the new artifact link rendering and do not appear unrelated.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/artifact-lookfeel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dev-docs/plugins.md (1)

286-300: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale helper count: "Five helpers" is now six.

Line 286 still says "Five helpers are re-exported..." but the import example just below (and the table further down) now lists six: render_markdown, render_markdown_collapsible, escape_html, safe_markdown_inline, is_safe_web_url, safe_markdown_link_target.

📝 Proposed fix
-Five helpers are re-exported from `claude_code_log.plugins` for use
+Six helpers are re-exported from `claude_code_log.plugins` for use
 in `format_html` / `format_markdown` / `title` methods. The re-export is
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dev-docs/plugins.md` around lines 286 - 300, Update the plugins documentation
to reflect the current stable API helper count: the opening sentence still says
“Five helpers are re-exported” even though the `claude_code_log.plugins` import
example and the helper list now include six symbols. Edit the descriptive text
in the `plugins.md` section so it matches the actual exported helpers named in
the example (`render_markdown`, `render_markdown_collapsible`, `escape_html`,
`safe_markdown_inline`, `is_safe_web_url`, `safe_markdown_link_target`).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@claude_code_log/markdown/renderer.py`:
- Around line 315-341: safe_markdown_link_target currently percent-encodes only
a few breakout characters, but it also needs to encode ASCII control characters
so transcript-derived URLs cannot terminate a Markdown link destination early.
Update the normalization in safe_markdown_link_target to cover the full control
range (U+0000-U+001F and U+007F) in addition to the existing characters, while
keeping the is_safe_web_url scheme check separate; use the existing
_MARKDOWN_TARGET_ENCODINGS and safe_markdown_link_target symbols to implement
the fix.

---

Outside diff comments:
In `@dev-docs/plugins.md`:
- Around line 286-300: Update the plugins documentation to reflect the current
stable API helper count: the opening sentence still says “Five helpers are
re-exported” even though the `claude_code_log.plugins` import example and the
helper list now include six symbols. Edit the descriptive text in the
`plugins.md` section so it matches the actual exported helpers named in the
example (`render_markdown`, `render_markdown_collapsible`, `escape_html`,
`safe_markdown_inline`, `is_safe_web_url`, `safe_markdown_link_target`).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 63e781e8-b074-437c-9cac-2aca24abc9b9

📥 Commits

Reviewing files that changed from the base of the PR and between f13ee4c and 210dfd8.

📒 Files selected for processing (12)
  • claude_code_log/factories/tool_factory.py
  • claude_code_log/html/templates/components/message_styles.css
  • claude_code_log/html/tool_formatters.py
  • claude_code_log/markdown/renderer.py
  • claude_code_log/models.py
  • claude_code_log/plugins.py
  • claude_code_log/utils.py
  • dev-docs/plugins.md
  • test/__snapshots__/test_snapshot_html.ambr
  • test/test_artifact_rendering.py
  • test/test_data/artifact_tool.jsonl
  • test/test_plugin_xss_api.py

Comment thread claude_code_log/markdown/renderer.py Outdated
CodeRabbit review on #263: CommonMark terminates an un-bracketed link
destination at ASCII space OR any ASCII control character, and
is_safe_web_url only gates the scheme — so an accepted http(s) URL
carrying \n/\r/\t (or any of U+0000-U+001F, U+007F) could still end the
(target) slot early and let trailing text escape the link. Percent-
encode the full control range after the named-character pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cboos cboos merged commit fe2b746 into main Jul 4, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve look&feel of Artifact tool rendering

1 participant