Fix mailto: and tel: links in markdown content - #1153
Merged
Conversation
makeUrl only passed URLs through untouched when they contained "://" or started with "data:". Every other scheme was treated as a relative path, so a markdown link like [Write us](mailto:a@b.c) was joined with the base path and rendered as href="/mailto:a@b.c". Links configured in hyperbook.json were unaffected, because those hrefs are used verbatim. Add an isExternalUrl helper to @hyperbook/types that matches any RFC 3986 scheme, and use it in the makeUrl implementations of the builder and the VS Code preview. This covers mailto: and tel: as well as sms:, geo: and friends, while https:// and data: keep working as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
Emojis were resolved to plain Unicode characters and drawn by whatever emoji font the reader's operating system provides, so the same book looked different on Windows, macOS, Android and Linux. Add a rehypeEmoji plugin that swaps emojis for Twemoji images when elements.emoji.style is set to "twemoji". It runs after the shell, so icons configured in hyperbook.json are covered along with the content, and it skips pre, code, script, style, textarea and title. Characters that are text by default, like (c) or (tm), are only replaced when the author asked for the emoji presentation, and an emoji without a Twemoji asset is left as text. The full Twemoji set ships in the package assets, but a build copies only the images a book actually uses into its output, both for a full build and for an incremental rebuild in the dev server. The default stays "native", so existing books render exactly as before. Twemoji graphics are CC-BY 4.0 by Twitter, Inc and other contributors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
A bookmark label was interpolated into the onclick attribute of the bookmark button and rendered with innerHTML. That had three problems: a heading containing a quote or a backslash produced a broken JavaScript string and the button threw, the label was persisted markup that came back through innerHTML, and the label was the markdown text rather than what the page renders, so an emoji drawn as an image was a plain character again in the bookmark list. Labels are now read from the rendered heading when a bookmark is saved and stored as parts, each with its text and, for an emoji, its id. The bookmark list builds its entries from those parts with the DOM, so nothing that was stored is parsed as HTML, emojis keep the look they have on the page, and the id rather than a URL means a bookmark survives a change of the basePath. The button lost its inline onclick and is handled by one delegated listener, which also covers headings that are added later. It carries data-key, a data-label fallback and aria-pressed. The store moves to version 6, which drops the pointless index on the label and migrates existing labels to the new shape. A plain string label is still rendered, so bookmarks from an older version and from an older export keep working. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
The indicator was the 🔖 character in two places: as the content of the button on a heading, and as the content of a ::before in the bookmark list. Both were drawn with the emoji font of the reader, so the control looked different on every platform. The one in the button also changed into a Twemoji image once a hyperbook set the emoji style, which made a piece of interface follow a content setting. Both are now a mask over the feather bookmark icon, so the control looks the same everywhere and stays a control. It takes the color of its surroundings, which keeps it readable in light and dark mode, and a bookmarked heading shows a filled icon instead of only a less transparent one. The button carries an aria-label now, since it no longer has content to name it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
The icon inherited its color, which picked up the color of the h1 rather than the color the heading appears in: the heading text is a link, and the button is its sibling, so inheriting missed the brand color the reader actually sees. Take the same color the heading link takes, which also keeps the icon in step with the brand color of the hyperbook and with the dark mode variant of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dogfoods the emoji style on the documentation, so the emojis in the docs, the language flags of the library and the icons of the custom links look the same for every reader. Also fixes a crash the website build turned up: an element that carries no children at all, which a directive can produce for a void element like an img, made the emoji walk throw. It is skipped now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW
Merged
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
makeUrl only passed URLs through untouched when they contained "://" or
started with "data:". Every other scheme was treated as a relative path,
so a markdown link like Write us was joined with the base
path and rendered as href="/mailto:a@b.c". Links configured in
hyperbook.json were unaffected, because those hrefs are used verbatim.
Add an isExternalUrl helper to @hyperbook/types that matches any RFC 3986
scheme, and use it in the makeUrl implementations of the builder and the
VS Code preview. This covers mailto: and tel: as well as sms:, geo: and
friends, while https:// and data: keep working as before.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_012kkjvqifo7fQ5W6woRWiSW