From 65983ec6be0722191df59fe9ad78ebe0efd95992 Mon Sep 17 00:00:00 2001 From: luxick Date: Fri, 24 Jul 2026 16:10:44 +0200 Subject: [PATCH] Missing changes --- assets/companion.js | 4 +++- assets/style.css | 36 ++++++++++++++++++++++++++++++++++++ render.go | 2 +- thumb_image.go | 10 ++++++++++ wikilinks.go | 31 +++++++++++++++++++++++++++---- 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/assets/companion.js b/assets/companion.js index f8df2d8..18b96cc 100644 --- a/assets/companion.js +++ b/assets/companion.js @@ -123,7 +123,9 @@ // folder anchors there end in "/" and fall through to navigation. // Search file-result anchors join in too: page results end in "/" // and fall through to navigation, file results don't and open locally. - var anchor = e.target.closest('.list-item a, a.thumb-tile, .photo-grid a, aside.tree-sidebar a.tree-file, .search-card a'); + // Embedded images (a.embed-link inside rendered content) wrap the + // raw file href, so they open the full-size original locally too. + var anchor = e.target.closest('.list-item a, a.thumb-tile, .photo-grid a, aside.tree-sidebar a.tree-file, .search-card a, .content a.embed-link'); if (!anchor) return; var item = anchor.closest('.list-item'); // Only intercept the primary file link, and only for files (not folders). diff --git a/assets/style.css b/assets/style.css index 9af2849..28f60c1 100644 --- a/assets/style.css +++ b/assets/style.css @@ -308,6 +308,42 @@ main > h2 { text-decoration: line-through; } +/* === Embedded images === + Wiki-style figure box: a bounded, framed thumbnail with an optional caption. + The fixed width matches the 300px thumbnail request so the box never scales + the image up. left/right float so body text wraps; center is a plain block. */ +.embed { + margin: var(--space-2) 0; + width: 300px; + max-width: 100%; + padding: var(--space-2); + background: var(--bg-panel); + border: var(--border); +} +.embed-left { + float: left; + margin-right: var(--space-4); +} +.embed-right { + float: right; + margin-left: var(--space-4); +} +.embed-center { + margin-inline: auto; +} +.embed .embed-link { display: block; } +.embed img { + display: block; + width: 100%; + height: auto; +} +.embed figcaption { + margin-top: var(--space-2); + color: var(--text-muted); + font-size: var(--font-sm); + line-height: 1.4; +} + a.heading-anchor { color: var(--text-muted); margin-right: 0.4em; diff --git a/render.go b/render.go index 19eb11f..6c423ba 100644 --- a/render.go +++ b/render.go @@ -24,7 +24,7 @@ var md goldmark.Markdown // targets against the filesystem. func initMarkdown(root string) { md = goldmark.New( - goldmark.WithExtensions(extension.GFM, extension.Table, newWikiLinkExt(root), newExtLinksExt()), + goldmark.WithExtensions(extension.GFM, extension.Table, newWikiLinkExt(root), newWikiEmbedExt(root), newExtLinksExt()), goldmark.WithParserOptions(parser.WithAutoHeadingID()), goldmark.WithRendererOptions(html.WithUnsafe(), html.WithHardWraps()), ) diff --git a/thumb_image.go b/thumb_image.go index aae2221..30e0643 100644 --- a/thumb_image.go +++ b/thumb_image.go @@ -7,12 +7,22 @@ import ( "image/jpeg" _ "image/png" "io" + "path/filepath" + "strings" ) func init() { thumbnailers = append(thumbnailers, &imageThumbnailer{}) } +// isImageFile reports whether name is a raster image we can thumbnail and thus +// embed on a page. Video files also carry thumbnails but are not embeddable, so +// this keys on the image thumbnailer's own extension set rather than +// hasThumbnail. +func isImageFile(name string) bool { + return (&imageThumbnailer{}).CanHandle(strings.ToLower(filepath.Ext(name))) +} + type imageThumbnailer struct{} func (it *imageThumbnailer) CanHandle(ext string) bool { diff --git a/wikilinks.go b/wikilinks.go index 790d073..1598f9e 100644 --- a/wikilinks.go +++ b/wikilinks.go @@ -115,6 +115,23 @@ func wikiTargetHref(target string) string { return b.String() } +// wikiFileHref converts a wiki target to a URL href for the raw file: each +// segment is percent-encoded and, unlike wikiTargetHref, no trailing slash is +// appended. Used for image-embed click-through so the href points straight at +// the file (companion click-interception skips anything ending in "/"). +func wikiFileHref(target string) string { + target = normalizeWikiTarget(target) + if target == "/" { + return "/" + } + var b strings.Builder + for _, seg := range strings.Split(strings.TrimPrefix(target, "/"), "/") { + b.WriteByte('/') + b.WriteString(url.PathEscape(seg)) + } + return b.String() +} + // wikiTargetExists reports whether the on-disk path backing the target exists // under root. Any existing path — file or folder — counts as resolved; only a // missing path is treated as broken. @@ -148,13 +165,20 @@ func (r *wikiLinkRenderer) render(w util.BufWriter, source []byte, node ast.Node return ast.WalkContinue, nil } n := node.(*wikiLinkNode) - target := string(n.Target) + writeWikiLink(w, r.root, string(n.Target), string(n.Display)) + return ast.WalkContinue, nil +} + +// writeWikiLink renders a wiki-link anchor for target with an optional display +// override. An empty display falls back to the target's last segment; a target +// that does not resolve on disk gets the `broken` class. Shared by the wiki-link +// renderer and the embed renderer's link fallback. +func writeWikiLink(w util.BufWriter, root, target, display string) { href := wikiTargetHref(target) - display := string(n.Display) if display == "" { display = wikiDefaultDisplay(target) } - broken := !wikiTargetExists(r.root, target) + broken := !wikiTargetExists(root, target) w.WriteString(`