Skip to content

Commit 7ef16ef

Browse files
fGolkefgolke
andauthored
lemming:0.3.1 (#5210)
Co-authored-by: fgolke <fgolke@noreply.codeberg.org>
1 parent 1b2a508 commit 7ef16ef

11 files changed

Lines changed: 1314 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# 0\.3\.1
2+
- fix: Change default show rule of `environment` to make use of `end-marker`
3+
4+
# 0\.3\.0
5+
- feat: Emit `math-environment` and `math-proof` tags when exporting to html
6+
- feat: Add `end-marker` parameter to `environment`. This allows putting a symbol at the end of math environments, similar to the QED symbol.
7+
- fix: Make proofs and environments full width, such that equation-only statements display correctly
8+
- fix: Do not insert QED symbols to figure captions, place them afterwards instead.
9+
10+
11+
# 0\.2\.0
12+
- feat: Provide a wrapper for `elembic.fields` to facilitate custom `show`-rules.
13+
- fix: References now link to their targets.
14+
15+
# 0\.1\.0
16+
- First release with support for `environment` and `proof`. Based on elembic.

packages/preview/lemming/0.3.1/LICENSE

Lines changed: 841 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Lemming
2+
Provides customizable theorem-like and proof environments that aim to behave closely to the way native elements behave.
3+
This package will upgrade to [native custom elements](https://github.com/typst/typst/issues/147), once they arrive.
4+
For now it employs the great [elembic](https://typst.app/universe/package/elembic).
5+
6+
![Usage example with a theorem and a proof.](gallery/readme-example.png)
7+
8+
See the [CHANGELOG](CHANGELOG.md) for differences between versions.
9+
10+
11+
## Basic usage
12+
13+
First, create the environments you want to use.
14+
By default, the `supplement` of the environment will be a capitalized version of the `kind` specified.
15+
```typ
16+
#import "@preview/lemming:0.3.1" as lem
17+
18+
#let theorem = lem.environment.with(kind: "theorem")
19+
#let lemma = lem.environment.with(kind: "lemma")
20+
21+
#let proof = lem.proof
22+
23+
// Unfortunately necessary for now to make references work
24+
#show: lem.prepare()
25+
```
26+
If you want your environments to use a shared counter, set their `supplement` instead of `kind`, i.e.: [^1]
27+
```typ
28+
#let theorem = lem.environment.with(supplement: [Theorem])
29+
#let lemma = lem.environment.with(supplement: [Lemma])
30+
```
31+
[^1]:
32+
Note that you should pass a value of type `content` and not `str` to `supplement`.
33+
Otherwise `show`-rules might not work as expected.
34+
35+
36+
Now, just use your environments in your document.
37+
38+
```typ
39+
#theorem(name: "Noether")[
40+
#lorem(20)
41+
]
42+
43+
#theorem(numbering: none, supplement: [Satz])[
44+
#lorem(20)
45+
]
46+
```
47+
48+
## Labels and references
49+
Due to implementation constraints, you have to specify labels as below instead of attaching it at the back of your environments.
50+
Hopefully, this will become unnecessary in the future.
51+
```typ
52+
#theorem(name: "Noether", label: <theorem:noether>)[
53+
#lorem(20)
54+
]
55+
```
56+
If references do not work, make sure you have applied the following `show` rule.
57+
```typ
58+
#show: lem.prepare()
59+
```
60+
61+
## Set rules
62+
You can change the defaults of all parameters using `set` rules.
63+
```typ
64+
// Unnecessary as "1.1" is the default numbering
65+
// Think: #set lem.environment(numbering: "1")
66+
#show: lem.set_(lem.environment, numbering: "1")
67+
```
68+
69+
## Show-Set rules
70+
You can use `show set` rules too.
71+
For example, you can make the body text `upright` instead of the default `italic`
72+
```typ
73+
// Think: #show lem.environment.body: set text(style: "normal")
74+
#show lem.selector(lem.environment-body): set text(style: "normal")
75+
```
76+
Self-referential `show set` rules work a bit different than native ones.
77+
```typ
78+
// Think: #show lem.environment.where(kind: "theorem"): set lem.environment(
79+
// numbering: heading-subnumbering(
80+
// lem.environment.where(kind: "theorem"), 1, "H.1.1"
81+
// )
82+
// )
83+
#show: lem.cond-set(
84+
theorem,
85+
numbering: heading-subnumbering(lem.counter-name("theorem"), 1, "H.1.1"),
86+
)
87+
```
88+
This rules employs the `heading-subnumbering` function from the [discount](https://typst.app/universe/package/discount) package.
89+
90+
**Advice**: Instead of setting block properties
91+
```typ
92+
#show lem.selector(lem.proof): set block(inset: (left: 1cm)) // DON'T DO THIS
93+
```
94+
wrapping elements in blocks leads to less unwanted side-effects
95+
```typ
96+
#show: lem.show_(lem.proof, it => block(inset: (left: 1cm), it))
97+
```
98+
99+
## Show rules
100+
To customize the display of your environment even further, you can write a custom `show` rule.
101+
```typ
102+
// Think: #show lem.environment: box(stroke: blue)
103+
#show: lem.show_(lem.environment, it => box(stroke: blue, it))
104+
```
105+
These can be conditional too.
106+
```typ
107+
// Think: #show lem.environment.where(kind: "lemma"): it => box(stroke: red, inset: 0.5em, it)
108+
#show: lem.show_(lemma, it => box(stroke: red, inset: 0.5em, it))
109+
```
110+
111+
To change the visuals further, you can write a full custom `show`-rule.
112+
For example, if you want the number at front and the supplement in smallcaps, you can write the following.
113+
```typ
114+
#show: lem.show_(lem.environment, it => {
115+
it = lem.fields(it) // Hopefully not necessary in the future
116+
block({
117+
strong(
118+
if it.numbering != none {
119+
numbering(it.numbering, ..it.counter.get())
120+
sym.space.nobreak
121+
} + smallcaps(it.supplement),
122+
)
123+
if it.name != none {
124+
[ (#emph(it.name))]
125+
}
126+
strong(it.separator)
127+
128+
// For convenience, by default it just makes text italic
129+
lem.environment-body(it.body)
130+
})
131+
})
132+
```
133+
134+
![Usage example with a theorem and a proof.](gallery/show-example.png)
135+
136+
## HTML
137+
In HTML export, `environment` elements emit a `math-environment` tag and `proof` elements emit a `math-proof` tag.
138+
139+
## Enviroment Fields
140+
While writing your `show` rule, you can acess the following fields on `it`:
141+
| name | type | description |
142+
| -- | -- | -- |
143+
| body | `content` | The body of the environment, required and positional. |
144+
| name | `content \| none` | Additional note for the environment, for example the name of a theorem. |
145+
| kind | `str` | The kind of math environment. Default: `"theorem"`|
146+
| supplement | `content \| auto` | The word used for the environment. Defaults to the capizalized kind. |
147+
| separator | `content` | The symbol between between the header and body. Default: `[. ]` |
148+
| numbering | `str \| function \| none` | How to number the environment. Accepts a numbering pattern or function. Default: `"1.1"` |
149+
| end-marker | `content \| none` | The symbol to place at the end of the environment. Default: `none` |
150+
| counter | `counter` | The counter corresponding to the environment, synthesized. |
151+
152+
## Proofs
153+
The `proof` environment works similar and should do exactly what you expect.
154+
Its fields are:
155+
156+
| name | type | description |
157+
| -- | -- | -- |
158+
| body | `content` | The body of the proof, required and positional. |
159+
| supplement | `content` | The term before the proof body, Default `"Proof"` |
160+
| separator | `content` | The symbol between proof supplement and body, Default: `[. ]` |
161+
| qed | `content \| none` | The Symbol to insert after the body, Default: `$square$` |
36.1 KB
Loading
30.4 KB
Loading
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#let sequence-func = [. ].func()
2+
#let space-func = [. ].children.last().func()
3+
#let styled-func = [#set text(fill: red)].func()
4+
5+
#let get-last-nonempty(content_) = {
6+
for (i, part) in content_.children.enumerate().rev() {
7+
let func = part.func()
8+
if func in (space-func, parbreak, linebreak, colbreak, pagebreak) {
9+
continue
10+
}
11+
return (func, i)
12+
}
13+
return (it => it, content_.children.len() - 1)
14+
}
15+
16+
/// Similar to
17+
/// ```typ
18+
/// #h(1fr)
19+
/// #marker
20+
/// ```
21+
/// but keeps the spacing even after soft line-breaks
22+
///
23+
/// - marker (content): The content to place at the end
24+
/// ->
25+
#let space-marker(marker, minimal-spacing) = {
26+
if minimal-spacing != none {
27+
h(minimal-spacing)
28+
}
29+
box(
30+
// Don't apply user set-rules
31+
baseline: 0% + 0pt, fill: none, height: auto, inset: (:), outset: (:), stroke: (:),
32+
width: 1fr, align(right, marker)
33+
)
34+
}
35+
36+
37+
#let clone-without(dict, keys) = for (k, value) in dict {
38+
if not keys.contains(k) {
39+
({ k }: value)
40+
}
41+
}
42+
43+
#let append(body, qed, minimal-spacing: 2em) = if type(body) == content {
44+
if qed == none {
45+
return body
46+
}
47+
48+
let func = body.func()
49+
if func == sequence-func and body.children.len() > 0 {
50+
let (child-func, index) = get-last-nonempty(body)
51+
for part in body.children.slice(0, index) {
52+
part
53+
}
54+
append(body.children.at(index), qed)
55+
for part in body.children.slice(index + 1) {
56+
part
57+
}
58+
} else if func == math.equation and body.block {
59+
body
60+
place(bottom + right, qed)
61+
context if math.equation.numbering != none {
62+
let number-align = math.equation.number-align
63+
let equation-height = measure(body).height
64+
let numbering-height = measure(numbering(math.equation.numbering, 1)).height
65+
let qed-height = measure(qed).height
66+
67+
if number-align.x == end or number-align.x == none {
68+
if number-align.y == top {
69+
let remaining-height = equation-height - numbering-height - qed-height
70+
if remaining-height < par.leading.to-absolute() {
71+
h(par.leading - remaining-height)
72+
}
73+
} else if number-align.y == horizon {
74+
let remaining-height = (equation-height - numbering-height) / 2 - qed-height
75+
if remaining-height < par.leading.to-absolute() {
76+
h(par.leading - remaining-height)
77+
}
78+
} else if number-align.y == bottom {
79+
h(par.leading)
80+
}
81+
}
82+
}
83+
} else if func in (list.item, enum.item, block) {
84+
func(append(body.body, qed), ..clone-without(body.fields(), ("body",)))
85+
} else if func == terms.item {
86+
func(body.term, append(body.description, qed))
87+
} else if func == columns {
88+
func(append(body.body, qed), body.count)
89+
} else if func == styled-func {
90+
func(append(body.child, qed), body.styles)
91+
} else {
92+
// if func != text {panic("Unknown: " + repr(func))}
93+
body
94+
space-marker(qed, minimal-spacing)
95+
}
96+
} else {
97+
panic("Can't append qed to non-content.")
98+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Thin wrappers around elembic to enable deprecation warnings once native custom types land
2+
#import "@preview/elembic:1.1.1" as e
3+
4+
#let set_(elem, ..fields) = {
5+
e.set_(elem, ..fields)
6+
}
7+
8+
#let show_(filter, replacement, mode: auto) = {
9+
e.show_(filter, replacement, mode: mode)
10+
}
11+
12+
#let cond-set(filter, ..fields) = {
13+
e.cond-set(filter, ..fields)
14+
}
15+
16+
#let selector(elem, outline: false, outer: false, meta: false) = {
17+
e.selector(elem, outline: outline, outer: outer, meta: meta)
18+
}
19+
20+
#let prepare(..args) = {
21+
e.prepare(..args)
22+
}
23+
24+
#let fields(elem) = {
25+
e.fields(elem)
26+
}

0 commit comments

Comments
 (0)