Skip to content

Add clickable links support using Bluesky facets#122

Merged
simonw merged 1 commit intomainfrom
bluesky-thread-clickable-links
Nov 27, 2025
Merged

Add clickable links support using Bluesky facets#122
simonw merged 1 commit intomainfrom
bluesky-thread-clickable-links

Conversation

@simonw
Copy link
Owner

@simonw simonw commented Nov 27, 2025

Summary

  • Parse the facets array from post records to render links as clickable <a> tags
  • Bluesky stores truncated link text in the post but includes the full URL in facets with byte-indexed positions
  • Uses TextEncoder/TextDecoder to properly handle byte offsets for accurate link placement

Test plan

  • Test with post containing a link: ?url=https://bsky.app/profile/simonwillison.net/post/3m6kjxcpz3c2q&view=thread
  • Verify the truncated "simonwillison.net/2025/Nov/6/a..." text is now a clickable link
  • Verify clicking opens the full URL in a new tab

🤖 Generated with Claude Code


A continuation of:

Claude Code: https://gistpreview.github.io/?0271478bceedf64e0896f0816ba116ff

One more change: visit http://localhost:8010/bluesky-thread.html?url=https%3A%2F%2Fbsky.app%2Fprofile%2Fsimonwillison.net%2Fpos
t%2F3m6kjxcpz3c2q&view=thread and note that there is a link on that page but it is displayed truncated as
simonwillison.net/2025/Nov/6/a... and cannot be clicked. fix that.

Parse the facets array from post records to render links as clickable <a> tags.
Bluesky stores truncated link text in the post but includes the full URL in
facets with byte-indexed positions. This change properly decodes and renders
these links.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Nov 27, 2025

Claude finished @simonw's task —— View job


Code Review for Clickable Links Feature

Reviewing the pull request that adds clickable links support using Bluesky facets.

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Analyze the modified bluesky-thread.html file
  • Review code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Assess security concerns
  • Review test coverage
  • Provide comprehensive feedback

Code Review Results:

Code Quality & Best Practices

The implementation is well-structured and follows good practices:

  • Proper Unicode handling: Using TextEncoder/TextDecoder correctly handles byte offsets for Unicode characters (bluesky-thread.html:334-337)
  • Clean separation of concerns: The renderTextWithFacets function is focused and reusable (bluesky-thread.html:328-380)
  • Proper sorting: Facets are sorted by start index before processing (bluesky-thread.html:340)
  • Good integration: Links open in new tabs with appropriate target="_blank" (bluesky-thread.html:362)

Security Assessment

The security implementation is solid:

  • No XSS vulnerabilities: Uses textContent for link text instead of innerHTML (bluesky-thread.html:363)
  • Safe DOM manipulation: Uses proper DOM methods throughout
  • URL handling: Direct assignment to href is safe as the browser handles URL validation

⚠️ Potential Issues

  1. Overlapping facets not handled: If facets overlap in byte ranges, the current implementation could create malformed HTML. Consider adding validation:

    // Add before processing facets
    for (let i = 1; i < sorted.length; i++) {
      if (sorted[i].index.byteStart < sorted[i-1].index.byteEnd) {
        console.warn('Overlapping facets detected, skipping');
        continue;
      }
    }
  2. Error handling: The function doesn't handle malformed facet data. Consider adding checks:

    if (!facet.index || typeof facet.index.byteStart !== 'number') continue;
  3. Non-link facets: Currently renders mentions/tags as plain text. The comment mentions "for now" (bluesky-thread.html:366) - this is fine but should be tracked for future enhancement.

Performance Considerations

Performance is well-optimized:

  • Efficient byte operations: Single encode/decode cycle per text block
  • Fragment usage: Uses DocumentFragment for efficient DOM building (bluesky-thread.html:342)
  • No unnecessary operations: Direct byte slicing instead of string manipulation

Test Coverage

The PR includes a manual test plan but no automated tests. The test plan is good but consider adding:

  • Unit tests for renderTextWithFacets with various facet combinations
  • Edge case tests (empty facets, malformed data, Unicode edge cases)
  • Integration tests for the complete flow

Minor Suggestions

  1. Add input validation at the start of renderTextWithFacets:

    if (!text || typeof text !== 'string') return document.createTextNode('');
  2. Consider extracting magic strings: The facet type 'app.bsky.richtext.facet#link' could be a constant.

Summary

This is a solid implementation that correctly handles the complex task of parsing Bluesky facets with proper Unicode support. The code is clean, secure, and performant. The main areas for improvement are error handling for edge cases and automated test coverage.

Recommendation: ✅ Approve with minor suggestions

The implementation successfully addresses the core requirement and follows good practices. The suggestions above are enhancements rather than blocking issues.


@cloudflare-workers-and-pages
Copy link

Deploying tools with  Cloudflare Pages  Cloudflare Pages

Latest commit: e615ebc
Status: ✅  Deploy successful!
Preview URL: https://c75bf7fb.tools-b1q.pages.dev
Branch Preview URL: https://bluesky-thread-clickable-lin.tools-b1q.pages.dev

View logs

@simonw
Copy link
Owner Author

simonw commented Nov 27, 2025

@simonw simonw merged commit 0f3eb43 into main Nov 27, 2025
3 of 4 checks passed
@simonw simonw deleted the bluesky-thread-clickable-links branch November 27, 2025 01:40
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.

1 participant