How to add an icon to link bodies?

This works to put a little link icon after links:

#show link: it => {
	it + box(image("assets/link icon.jpg", height: 0.8em))
}

However, the icon itself is not linked. I really would like it to be.

I tried this:

#show link: it => {
	let b = it.body + box(image("assets/link icon.jpg", height: 0.8em))
	it.body = b
}

This does not work, because Typst does not allow functions to mutate their own inputs.

And I tried recursion, but of course that doesn’t work either:

#show link: it => {
	let dest = it.dest
	it + link(dest, box(image("assets/link icon.jpg", height: 0.8em)))
}

What I can do is replace all links in the document with a custom function, but it would be neater to do this with a show rule if possible.

Well, I think defining a function is more suitable in your case. Just #let link(dest, body) = .., and there’s almost no difference in usage.

#let link(dest, body) = std.link(dest, {
  body
  box(image("assets/link icon.jpg", height: 0.8em))
})
// If you omit the body sometimes, `#let link(dest, ..body)` and check if `body.pos()` is empty.

Bonus: You could get rid of the icon easily when appropriate, or use dedicated icons for special links such as mailto: and github in a specifc chapter.

2 Likes

I had forgotten that it’s possible to overwrite built-in functions. That is indeed a neat solution. Thank you.

1 Like

You have to be a bit careful, but can make it work. See for example How to overwrite built-in functions? - #2 by janekfleper

This is also a recursive show rule, but it works. The trick is checking whether the show rule is triggered by itself and just return it in that case. In your example, it would probably need to read if it.body == box(image("assets/link icon.jpg", height: 0.8em)) or something like that.

I think it’s cleaner here to have a single link, so ideally combine this with Y.D.X’s solution. Then the body consists of the original body and the new body; you’ll need something like if it.body.func() == [].func() and it.body.children.last() == ... (body is a sequence ending in the image). When writing this code, it’s super useful to temporarily add repr(it.body) to your show rule to see exactly what you’re trying to match.