hachem.wtf
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-09-18 13:59:45 +0200
committerhachem <im@hachem.wtf>2026-09-18 13:59:45 +0200
commit2720a126aa5a8412f8d355f8937cf6c37975be78 (patch)
tree1070b2bbc875b6bb56ca1c19e652f882dfe10279
parentab2774b23c66521e13541ab3e361bba95c394a8a (diff)
feat: add letter template
-rw-r--r--.gitignore4
-rw-r--r--Makefile68
-rw-r--r--lib.typ3
-rw-r--r--samples/letter.typ21
-rw-r--r--samples/paper.typ39
-rw-r--r--templates/letter.typ90
-rw-r--r--templates/paper.typ94
-rw-r--r--typst.toml4
8 files changed, 278 insertions, 45 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6f2fc6d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+mktypst
+*.dSYM
+.DS_Store
+*.pdf
diff --git a/Makefile b/Makefile
index 4512b74..a4c3868 100644
--- a/Makefile
+++ b/Makefile
@@ -1,20 +1,72 @@
-NAME := mktypst
+BIN := mktypst
+PKG := hachem
VERSION := 0.1.0
+CC := clang
+STD := c17
+
+DEBUG ?= 1
+SANITIZE ?= 1
+
+CFLAGS := -std=$(STD) \
+ -Wall \
+ -Wextra \
+ -Wshadow \
+ -Wpedantic \
+ -Wconversion \
+ -Wstrict-prototypes \
+ -Wsign-conversion \
+ -Wmissing-prototypes
+
+ifeq ($(DEBUG),1)
+CFLAGS += -g -fno-omit-frame-pointer
+else
+CFLAGS += -O2
+endif
+
+ifeq ($(SANITIZE),1)
+CFLAGS += -fsanitize=address,undefined
+LDFLAGS += -fsanitize=address,undefined
+endif
+
+PREFIX ?= /usr/local
+bindir := $(DESTDIR)$(PREFIX)/bin
+
ifeq ($(shell uname),Darwin)
DATADIR := $(HOME)/Library/Application Support
else
DATADIR := $(if $(XDG_DATA_HOME),$(XDG_DATA_HOME),$(HOME)/.local/share)
endif
+pkgdir := $(DATADIR)/typst/packages/local/$(PKG)/$(VERSION)
+
+all: $(BIN)
+
+$(BIN): mktypst.c
+ $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
-PKGDIR := $(DATADIR)/typst/packages/local/$(NAME)/$(VERSION)
+install: install-bin install-pkg
-install:
- mkdir -p "$(PKGDIR)"
- cp typst.toml lib.typ README.md LICENSE "$(PKGDIR)"/
- cp -r templates "$(PKGDIR)"/
+install-bin:
+ $(MAKE) DEBUG=0 SANITIZE=0 clean $(BIN)
+ install -d $(bindir)
+ install -m 755 $(BIN) $(bindir)/$(BIN)
+
+install-pkg:
+ mkdir -p "$(pkgdir)"
+ cp typst.toml lib.typ README.md LICENSE "$(pkgdir)"/
+ cp -r templates "$(pkgdir)"/
uninstall:
- rm -rf "$(DATADIR)/typst/packages/local/$(NAME)/$(VERSION)"
+ rm -f $(bindir)/$(BIN)
+ rm -rf "$(DATADIR)/typst/packages/local/$(PKG)/$(VERSION)"
+
+samples:
+ @for f in samples/*.typ; do \
+ printf ' %s\n' "$${f%.typ}.pdf"; \
+ typst compile --root . "$$f" "$${f%.typ}.pdf" || exit 1; \
+ done
+
+clean:
+ rm -rf $(BIN) $(BIN).dSYM samples/*.pdf
-.PHONY: install uninstall
+.PHONY: all install install-bin install-pkg uninstall samples clean
diff --git a/lib.typ b/lib.typ
index 7b3212c..9e50244 100644
--- a/lib.typ
+++ b/lib.typ
@@ -1 +1,2 @@
-#import "templates/paper.typ": paper
+#import "templates/paper.typ": paper, booktabs
+#import "templates/letter.typ": letter
diff --git a/samples/letter.typ b/samples/letter.typ
new file mode 100644
index 0000000..d3ccad9
--- /dev/null
+++ b/samples/letter.typ
@@ -0,0 +1,21 @@
+#import "../lib.typ": letter
+
+#show: letter.with(
+ sender: (
+ name: [Hachem H.],
+ address: [12 rue de la Paix \ 75002 Paris],
+ ),
+ recipient: (
+ name: [Département de Musicologie],
+ address: [Université de Paris \ 75005 Paris],
+ ),
+ place: "Paris",
+ date: "18 septembre 2026",
+ subject: [candidature spontanée],
+)
+
+Je me permets de vous adresser ma candidature pour un poste de recherche au sein
+de votre département. #lorem(35)
+
+Passionné par la théorie spectrale et la composition, je serais honoré de
+contribuer à vos travaux. #lorem(25)
diff --git a/samples/paper.typ b/samples/paper.typ
new file mode 100644
index 0000000..95ad6a5
--- /dev/null
+++ b/samples/paper.typ
@@ -0,0 +1,39 @@
+#import "../lib.typ": paper, booktabs
+
+#show: paper.with(
+ title: "Les Origines de YHWH",
+ date: "mars 2026",
+ authors: (
+ (name: "Hachem H.", location: [Paris, France]),
+ ),
+ abstract: [
+ Cet article soutient que l'origine de Yahweh se comprend au mieux à travers
+ trois cadres : la « convergence des indices », la compétition de niches
+ fonctionnelles, et la portabilité cultuelle.
+ ],
+)
+
+= Introduction
+Yahweh serait apparu comme une divinité du sud, importée dans les hautes terres
+israélites où elle a fusionné avec El. #lorem(35)
+
+Un second paragraphe, avec alinéa, pour montrer le rythme du texte : la
+ponctuation haute est correctement espacée ; les guillemets « aussi ». #lorem(25)
+
+== La convergence des indices
+Trois lignes indépendantes convergent vers une même conclusion.
+
+#figure(
+ booktabs(
+ columns: (auto, auto),
+ align: (center, left),
+ header: ([Ligne], [Nature de l'indice]),
+ [1], [voyelle anormale (morphologie)],
+ [2], [poésie archaïque (littérature)],
+ [3], [absence à Ougarit (archéologie)],
+ ),
+ caption: [Les trois lignes d'évidence.],
+)
+
+= Conclusion
+Aucune ligne n'est décisive seule ; leur convergence l'est. #lorem(25)
diff --git a/templates/letter.typ b/templates/letter.typ
new file mode 100644
index 0000000..361f47b
--- /dev/null
+++ b/templates/letter.typ
@@ -0,0 +1,90 @@
+#let sender-block(sender) = if type(sender) == dictionary {
+ text(weight: "medium", tracking: 0.03em)[#smallcaps(sender.name)]
+ if sender.at("address", default: none) != none {
+ v(0.35em)
+ text(size: 0.85em, fill: luma(30%))[#sender.address]
+ }
+} else {
+ sender
+}
+
+#let recipient-block(recipient) = if type(recipient) == dictionary {
+ recipient.name
+ if recipient.at("address", default: none) != none {
+ linebreak()
+ text(size: 0.9em, fill: luma(30%))[#recipient.address]
+ }
+} else {
+ recipient
+}
+
+#let letter(
+ sender: none,
+ recipient: none,
+ place: none,
+ date: none,
+ subject: none,
+ opening: [Madame, Monsieur,],
+ closing: [Veuillez agréer, Madame, Monsieur, l'expression de mes salutations distinguées.],
+ signature: none,
+ lang: "fr",
+ region: "FR",
+ font: "EB Garamond",
+ size: 11pt,
+ doc,
+) = {
+ set page(paper: "a4", margin: (x: 26mm, top: 30mm, bottom: 26mm), numbering: none)
+ set par(justify: true, leading: 0.72em, spacing: 1.3em)
+ set text(font: font, size: size, lang: lang, region: region, number-type: "old-style")
+
+ let dateline = if place != none and date != none {
+ [#place, le #date]
+ } else if date != none {
+ [le #date]
+ } else {
+ place
+ }
+
+ let sign = if signature != none {
+ signature
+ } else if type(sender) == dictionary {
+ sender.name
+ } else {
+ none
+ }
+
+ grid(
+ columns: (1fr, 1fr),
+ align: (left + top, right + top),
+ sender-block(sender),
+ {
+ recipient-block(recipient)
+ if dateline != none {
+ if recipient != none { v(1.4em) }
+ dateline
+ }
+ },
+ )
+
+ v(1.9em)
+ line(length: 100%, stroke: 0.4pt)
+ v(1.9em)
+
+ if subject != none {
+ [#text(weight: "bold")[Objet :] #subject]
+ v(1.4em)
+ }
+
+ opening
+
+ v(0.6em)
+ doc
+
+ v(1.6em)
+ closing
+
+ if sign != none {
+ v(3.2em)
+ align(right, smallcaps(sign))
+ }
+}
diff --git a/templates/paper.typ b/templates/paper.typ
index c7658ec..caf33b6 100644
--- a/templates/paper.typ
+++ b/templates/paper.typ
@@ -1,38 +1,59 @@
-#let paper-title(title, date) = [
- #place(top + center, float: true)[
- #block(width: 100%)[
- #align(center)[
- #text(size: 18pt)[#smallcaps[#title]] \
- #v(0em)
- #text(size: 12pt)[--- #smallcaps(date) ---]
- ]
- ]
- ]
+#let title-block(title, date) = align(center)[
+ #text(size: 18pt)[#smallcaps[#title]] \
+ #v(0.15em)
+ #text(size: 12pt)[--- #smallcaps(date) ---]
]
-#let author-block(authors) = [
- #align(center)[
- #for author in authors [
- #author.name \
- #text(style: "italic")[#author.location]
- ]
+#let author-block(authors) = align(center)[
+ #for author in authors [
+ #author.name \
+ #text(style: "italic")[#author.location]
]
- #v(1.2em)
]
-#let abstract-block(body) = [
- #block(width: 100%)[
- #text(weight: "bold")[Abstract---]
- #body
- ]
- #v(0.8em)
+#let abstract-block(prefix, body) = block(width: 100%, inset: (x: 10%))[
+ #set par(justify: true, first-line-indent: 0pt)
+ #text(size: 9pt)[#text(weight: "bold")[#prefix]#body]
+]
+
+#let frontmatter(title, date, authors, abstract, abstract-prefix) = place(
+ top + center,
+ scope: "parent",
+ float: true,
+)[
+ #title-block(title, date)
+ #v(0.7em)
+ #author-block(authors)
+ #v(0.9em)
+ #if abstract != none {
+ abstract-block(abstract-prefix, abstract)
+ v(0.7em)
+ }
+ #line(length: 100%, stroke: 0.4pt)
+ #v(0.5em)
]
+// a booktabs table: a top rule, a rule under the header, a bottom rule, and
+// nothing else. header is the row of heading cells; the rest are body cells.
+#let booktabs(columns: auto, align: auto, header: (), ..rows) = table(
+ columns: columns,
+ align: align,
+ stroke: none,
+ inset: (x: 6pt, y: 4pt),
+ table.hline(stroke: 0.8pt),
+ table.header(..header.map(cell => strong(cell))),
+ table.hline(stroke: 0.4pt),
+ ..rows.pos(),
+ table.hline(stroke: 0.8pt),
+)
+
#let paper(
title: none,
date: none,
authors: (),
abstract: none,
+ lang: "fr",
+ region: "FR",
font: "EB Garamond",
size: 10pt,
doc,
@@ -46,18 +67,25 @@
numbering: "- 1/1 -",
)
- set par(justify: true, leading: 0.55em)
- set text(font: font, size: size)
+ set par(
+ justify: true,
+ leading: 0.62em,
+ spacing: 0.62em,
+ first-line-indent: (amount: 1.2em, all: false),
+ linebreaks: "optimized",
+ )
+ set text(font: font, size: size, lang: lang, region: region, number-type: "old-style")
set figure(gap: 0.65em)
+ set table(inset: (x: 6pt, y: 4pt))
show heading.where(level: 1): it => [
- #v(0.8em)
- #text(size: 12pt, weight: "medium")[#align(center)[#smallcaps[#it.body]]]
- #v(0.35em)
+ #v(0.9em)
+ #align(center)[#text(size: 11pt, weight: "medium")[#smallcaps[#it.body]]]
+ #v(0.4em)
]
show heading.where(level: 2): it => [
- #v(0.6em)
+ #v(0.7em)
#align(right)[#text(size: 10pt, style: "italic")[#it.body]]
#v(0.25em)
]
@@ -69,14 +97,12 @@
]
show figure.where(kind: table): set text(size: 8.5pt)
+ show figure.caption: set text(size: 8.5pt)
show bibliography: set text(size: 8.5pt)
- paper-title(title, date)
- author-block(authors)
+ let abstract-prefix = if lang == "fr" { [Résumé --- ] } else { [Abstract---] }
- if abstract != none {
- abstract-block(abstract)
- }
+ frontmatter(title, date, authors, abstract, abstract-prefix)
doc
}
diff --git a/typst.toml b/typst.toml
index 50bf16c..7a367aa 100644
--- a/typst.toml
+++ b/typst.toml
@@ -1,7 +1,7 @@
[package]
-name = "mktypst"
+name = "hachem"
version = "0.1.0"
entrypoint = "lib.typ"
-authors = ["hachem <im@hachem.wtf>"]
+authors = ["hachem <contact@hachem.cc>"]
license = "MIT"
description = "a couple of dumb typst templates"