Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

📑 Add proof, solution, exercise defaults to typst export #1757

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/curvy-geese-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"myst-to-typst": patch
---

Add exercise and solution handlers
41 changes: 2 additions & 39 deletions packages/myst-to-typst/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import type { VFile } from 'vfile';
import type { GenericNode } from 'myst-common';
import { fileError, fileWarn, toText, getMetadataTags } from 'myst-common';
import { captionHandler, containerHandler, getDefaultCaptionSupplement } from './container.js';

Check warning on line 6 in packages/myst-to-typst/src/index.ts

View workflow job for this annotation

GitHub Actions / lint

'getDefaultCaptionSupplement' is defined but never used
import type {
Handler,
ITypstSerializer,
Expand All @@ -23,6 +23,7 @@
import { select, selectAll } from 'unist-util-select';
import type { Admonition, Code, CrossReference, FootnoteDefinition, TabItem } from 'myst-spec-ext';
import { tableCellHandler, tableHandler, tableRowHandler } from './table.js';
import { proofHandlers } from './proofs.js';

export type { TypstResult } from './types.js';

Expand Down Expand Up @@ -73,26 +74,6 @@
])
}`;

const proof = `
#let proof(body, heading: [], kind: "proof", supplement: "Proof", labelName: none, color: blue, float: true) = {
let stroke = 1pt + color.lighten(90%)
let fill = color.lighten(90%)
let title
set figure.caption(position: top)
set figure(placement: none)
show figure.caption.where(body: heading): (it) => {
block(width: 100%, stroke: stroke, fill: fill, inset: 8pt, it)
}
place(auto, float: float, block(width: 100%, [
#figure(kind: kind, supplement: supplement, gap: 0pt, [
#set align(left);
#set figure.caption(position: bottom)
#block(width: 100%, fill: luma(253), stroke: stroke, inset: 8pt)[#body]
], caption: heading)
#if(labelName != none){label(labelName)}
]))
}`;

const INDENT = ' ';

const linkHandler = (node: any, state: ITypstSerializer) => {
Expand Down Expand Up @@ -456,25 +437,6 @@
state.renderChildren(node);
state.write('\n]\n\n');
},
proof(node: GenericNode, state) {
state.useMacro(proof);
const title = select('admonitionTitle', node);
const kind = node.kind || 'proof';
const supplement = getDefaultCaptionSupplement(kind);
state.write(
`#proof(kind: "${kind}", supplement: "${supplement}", labelName: ${node.identifier ? `"${node.identifier}"` : 'none'}`,
);
if (title) {
state.write(', heading: [');
state.renderChildren(title);
state.write('])[');
} else {
state.write(')[');
}
state.renderChildren(node);
state.write(']');
state.ensureNewLine();
},
card(node, state) {
if (node.url) {
node.children?.push({ type: 'paragraph', children: [{ type: 'text', value: node.url }] });
Expand All @@ -496,6 +458,7 @@
footer() {
return;
},
...proofHandlers,
};

class TypstSerializer implements ITypstSerializer {
Expand Down
55 changes: 55 additions & 0 deletions packages/myst-to-typst/src/proofs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { type GenericNode } from 'myst-common';
import type { Handler, ITypstSerializer } from './types.js';
import { getDefaultCaptionSupplement } from './container.js';
import { select } from 'unist-util-select';

const proof = `
#let proof(body, heading: [], kind: "proof", supplement: "Proof", labelName: none, color: blue, float: true) = {
let stroke = 1pt + color.lighten(90%)
let fill = color.lighten(90%)
let title
set figure.caption(position: top)
set figure(placement: none)
show figure.caption.where(body: heading): (it) => {
block(width: 100%, stroke: stroke, fill: fill, inset: 8pt, it)
}
place(auto, float: float, block(width: 100%, [
#figure(kind: kind, supplement: supplement, gap: 0pt, [
#set align(left);
#set figure.caption(position: bottom)
#block(width: 100%, fill: luma(253), stroke: stroke, inset: 8pt)[#body]
], caption: heading)
#if(labelName != none){label(labelName)}
]))
}`;

function writeProof(node: GenericNode, state: ITypstSerializer, kind: string) {
state.useMacro(proof);
const title = select('admonitionTitle', node);
const supplement = getDefaultCaptionSupplement(kind);
state.write(
`#proof(kind: "${kind}", supplement: "${supplement}", labelName: ${node.identifier ? `"${node.identifier}"` : 'none'}`,
);
if (title) {
state.write(', heading: [');
state.renderChildren(title);
state.write('])[\n');
} else {
state.write(')[\n');
}
state.renderChildren(node);
state.write(']');
state.ensureNewLine();
}

export const proofHandlers: Record<string, Handler> = {
proof(node: GenericNode, state) {
writeProof(node, state, node.kind || 'proof');
},
exercise(node: GenericNode, state) {
writeProof(node, state, 'exercise');
},
solution(node: GenericNode, state) {
writeProof(node, state, 'solution');
},
};
16 changes: 16 additions & 0 deletions packages/myst-to-typst/tests/proofs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
title: myst-to-typst code
cases:
- title: proof
mdast:
type: root
children:
- type: exercise
identifier: my-exercise
children:
- type: paragraph
children:
- type: text
value: Recall...
typst: |-
#proof(kind: "exercise", supplement: "Exercise", labelName: "my-exercise")[
Recall...]
Loading