There are a lot of explicit things that are default anyway, so they basically just don’t give anything other than more code. I also noticed that basically the width of everything is the max width of caption/annotations/body. If this is what you want, then all three do have to be inside the same block, i.e., inside the figure. I think a basic wrapper with additional argument is the way.
#set figure.caption(position: top)
#show figure: set block(sticky: true, breakable: true)
#show figure.caption: it => context {
let numbering = if it.kind == image {
[~] + counter(figure.where(kind: image)).display()
} else if it.kind == table {
[~] + counter(figure.where(kind: table)).display()
} // kind: raw is excluded
set text(font: "Amaranth", weight: "bold")
set align(left)
grid(
columns: (1in, auto),
[#it.supplement#numbering#it.separator], it.body,
)
}
#let figure(annotations: none, body, ..args) = std.figure(..args, {
body
set align(left)
set text(10pt)
annotations
})
#figure(
rect(width: 15cm, height: 9cm),
caption: lorem(15),
annotations: [
Program Path: artifacts/create-data.jl \
Legend: #lorem(60) \
Abbreviations:
CL = clearance, \
],
)
#v(5em)
#figure(
table(columns: 2)[][][],
caption: lorem(15),
annotations: [
Program Path: artifacts/create-data.jl \
Legend: #lorem(10) \
Abbreviations:
CL = clearance, \
],
)