feat: add support for '--image-align'

This commit is contained in:
Johan Fagerberg 2026-02-18 09:15:51 +01:00 committed by Manuel Rüger
parent 875723da90
commit c32cd79dc8
8 changed files with 62 additions and 2 deletions

View File

@ -30,6 +30,7 @@ File in the extended format should follow the specification:
<!-- Attachment: <local path> -->
<!-- Label: <label 1> -->
<!-- Label: <label 2> -->
<!-- Image-Align: <left|center|right> -->
<page contents>
```
@ -73,6 +74,12 @@ Setting the sidebar creates a column on the right side. You're able to add any
You can set a page emoji icon by specifying the icon in the headers.
```markdown
<!-- Image-Align: center -->
```
You can set the alignment for all images in the page. Common values are `left`, `center`, and `right`. This adds the `ac:align` attribute to image tags. Can also be set globally via the `--image-align` CLI option (per-page header takes precedence).
Mark supports Go templates, which can be included into article by using path
to the template relative to current working dir, e.g.:
@ -845,6 +852,7 @@ GLOBAL OPTIONS:
--d2-scale float defines the scaling factor for d2 renderings. (default: 1) [$MARK_D2_SCALE]
--features string [ --features string ] Enables optional features. Current features: d2, mermaid, mention, mkdocsadmonitions (default: "mermaid", "mention") [$MARK_FEATURES]
--insecure-skip-tls-verify skip TLS certificate verification (useful for self-signed certificates) [$MARK_INSECURE_SKIP_TLS_VERIFY]
--image-align string set image alignment (left, center, right). Can be overridden per-file via the Image-Align header. [$MARK_IMAGE_ALIGN]
--help, -h show help
--version, -v print the version
```
@ -859,6 +867,7 @@ password = "password-or-api-key-for-confluence-cloud"
base-url = "http://confluence.local"
title-from-h1 = true
drop-h1 = true
image-align = "center"
```
**NOTE**: Labels aren't supported when using `minor-edit`!

View File

@ -53,7 +53,7 @@ func (c *ConfluenceExtension) Extend(m goldmark.Markdown) {
util.Prioritized(crenderer.NewConfluenceFencedCodeBlockRenderer(c.Stdlib, c, c.MarkConfig), 100),
util.Prioritized(crenderer.NewConfluenceHTMLBlockRenderer(c.Stdlib), 100),
util.Prioritized(crenderer.NewConfluenceHeadingRenderer(c.MarkConfig.DropFirstH1), 100),
util.Prioritized(crenderer.NewConfluenceImageRenderer(c.Stdlib, c, c.Path), 100),
util.Prioritized(crenderer.NewConfluenceImageRenderer(c.Stdlib, c, c.Path, c.MarkConfig.ImageAlign), 100),
util.Prioritized(crenderer.NewConfluenceParagraphRenderer(), 100),
util.Prioritized(crenderer.NewConfluenceLinkRenderer(), 100),
util.Prioritized(crenderer.NewConfluenceTaskListRenderer(), 100),

View File

@ -26,6 +26,7 @@ const (
HeaderInclude = `Include`
HeaderSidebar = `Sidebar`
ContentAppearance = `Content-Appearance`
HeaderImageAlign = `Image-Align`
)
type Meta struct {
@ -39,6 +40,7 @@ type Meta struct {
Attachments []string
Labels []string
ContentAppearance string
ImageAlign string
}
const (
@ -130,6 +132,17 @@ func ExtractMeta(data []byte, spaceFromCli string, titleFromH1 bool, titleFromFi
meta.ContentAppearance = FullWidthContentAppearance
}
case HeaderImageAlign:
align := strings.ToLower(strings.TrimSpace(value))
if align != "left" && align != "center" && align != "right" {
log.Warningf(
nil,
`unknown image alignment %q, expected one of: left, center, right; passing through to Confluence`,
value,
)
}
meta.ImageAlign = align
default:
log.Errorf(
nil,

View File

@ -20,15 +20,17 @@ type ConfluenceImageRenderer struct {
Stdlib *stdlib.Lib
Path string
Attachments attachment.Attacher
ImageAlign string
}
// NewConfluenceRenderer creates a new instance of the ConfluenceRenderer
func NewConfluenceImageRenderer(stdlib *stdlib.Lib, attachments attachment.Attacher, path string, opts ...html.Option) renderer.NodeRenderer {
func NewConfluenceImageRenderer(stdlib *stdlib.Lib, attachments attachment.Attacher, path string, imageAlign string, opts ...html.Option) renderer.NodeRenderer {
return &ConfluenceImageRenderer{
Config: html.NewConfig(),
Stdlib: stdlib,
Path: path,
Attachments: attachments,
ImageAlign: imageAlign,
}
}
@ -55,6 +57,7 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
writer,
"ac:image",
struct {
Align string
Width string
Height string
Title string
@ -62,6 +65,7 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
Attachment string
Url string
}{
r.ImageAlign,
"",
"",
string(n.Title),
@ -78,6 +82,7 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
writer,
"ac:image",
struct {
Align string
Width string
Height string
Title string
@ -85,6 +90,7 @@ func (r *ConfluenceImageRenderer) renderImage(writer util.BufWriter, source []by
Attachment string
Url string
}{
r.ImageAlign,
"",
"",
string(n.Title),

View File

@ -211,6 +211,7 @@ func templates(api *confluence.API) (*template.Template, error) {
`ac:image`: text(
`<ac:image`,
`{{ if .Align }} ac:align="{{ .Align }}"{{ end }}`,
`{{ if .Width }} ac:width="{{ .Width }}"{{ end }}`,
`{{ if .Height }} ac:height="{{ .Height }}"{{ end }}`,
`{{ if .Title }} ac:title="{{ .Title }}"{{ end }}`,

View File

@ -6,4 +6,5 @@ type MarkConfig struct {
DropFirstH1 bool
StripNewlines bool
Features []string
ImageAlign string
}

View File

@ -223,6 +223,7 @@ func processFile(
DropFirstH1: cmd.Bool("drop-h1"),
StripNewlines: cmd.Bool("strip-linebreaks"),
Features: cmd.StringSlice("features"),
ImageAlign: getImageAlign(cmd, meta),
}
html, _ := mark.CompileMarkdown(markdown, stdlib, file, cfg)
fmt.Println(html)
@ -302,6 +303,7 @@ func processFile(
DropFirstH1: cmd.Bool("drop-h1"),
StripNewlines: cmd.Bool("strip-linebreaks"),
Features: cmd.StringSlice("features"),
ImageAlign: getImageAlign(cmd, meta),
}
html, inlineAttachments := mark.CompileMarkdown(markdown, stdlib, file, cfg)
@ -474,6 +476,28 @@ func determineLabelsToAdd(meta *metadata.Meta, labelInfo *confluence.LabelInfo)
return labels
}
func getImageAlign(cmd *cli.Command, meta *metadata.Meta) string {
// Header comment takes precedence over CLI flag
if meta != nil && meta.ImageAlign != "" {
return meta.ImageAlign
}
cliAlign := cmd.String("image-align")
if cliAlign != "" {
align := strings.ToLower(strings.TrimSpace(cliAlign))
if align != "left" && align != "center" && align != "right" {
log.Warningf(
nil,
"unknown --image-align value %q, expected one of: left, center, right; passing through to Confluence",
cliAlign,
)
}
return align
}
return ""
}
func ConfigFilePath() string {
fp, err := os.UserConfigDir()
if err != nil {

View File

@ -213,6 +213,12 @@ var Flags = []cli.Flag{
Usage: "skip TLS certificate verification (useful for self-signed certificates)",
Sources: cli.NewValueSourceChain(cli.EnvVar("MARK_INSECURE_SKIP_TLS_VERIFY"), altsrctoml.TOML("insecure-skip-tls-verify", altsrc.NewStringPtrSourcer(&filename))),
},
&cli.StringFlag{
Name: "image-align",
Value: "",
Usage: "set image alignment (left, center, right). Can be overridden per-file via the Image-Align header.",
Sources: cli.NewValueSourceChain(cli.EnvVar("MARK_IMAGE_ALIGN"), altsrctoml.TOML("image-align", altsrc.NewStringPtrSourcer(&filename))),
},
}
// CheckFlags validates combinations and values of global flags.