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

🔧 More typst export improvements #1665

Merged
merged 9 commits into from
Nov 28, 2024
5 changes: 5 additions & 0 deletions .changeset/beige-ways-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Allow overriding default breakable value for typst figures
5 changes: 5 additions & 0 deletions .changeset/clean-toys-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Add hook for customizing table columns in typst
5 changes: 5 additions & 0 deletions .changeset/heavy-melons-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Render table legends below tables in typst
5 changes: 5 additions & 0 deletions .changeset/soft-shrimps-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Support lists that do not start at 1 in typst
5 changes: 5 additions & 0 deletions .changeset/tidy-crabs-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Render cross-references with correct text in typst
5 changes: 5 additions & 0 deletions .changeset/unlucky-adults-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'myst-to-typst': patch
---

Support table cell background color in typst
33 changes: 24 additions & 9 deletions packages/myst-to-typst/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export function determineCaptionKind(node: GenericNode): CaptionKind | null {

function renderFigureChild(node: GenericNode, state: ITypstSerializer) {
const useBrackets = node.type !== 'image' && node.type !== 'table';
if (node.type === 'legend') {
state.useMacro('#let legendStyle = (fill: black.lighten(20%), size: 8pt, style: "italic")');
state.write('text(..legendStyle)');
node.type = 'paragraph';
}
if (useBrackets) state.write('[\n');
else state.write('\n ');
state.renderChildren({ children: [node] });
Expand All @@ -60,12 +65,17 @@ export const containerHandler: Handler = (node, state) => {
state.data.isInFigure = true;
const { identifier, kind } = node;
let label: string | undefined = identifier;
const captions = node.children?.filter(
(child: GenericNode) => child.type === 'caption' || child.type === 'legend',
);
const nonCaptions = node.children?.filter(
(child: GenericNode) => child.type !== 'caption' && child.type !== 'legend',
);
const captionTypes = node.kind === 'table' ? ['caption'] : ['caption', 'legend'];
const captions: GenericNode[] = node.children?.filter((child: GenericNode) => {
return captionTypes.includes(child.type);
});
let nonCaptions: GenericNode[] = node.children?.filter((child: GenericNode) => {
return !captionTypes.includes(child.type);
});
nonCaptions = [
...nonCaptions.filter((child) => child.type !== 'legend'),
...nonCaptions.filter((child) => child.type === 'legend'),
];
if (!nonCaptions || nonCaptions.length === 0) {
fileError(state.file, `Figure with no non-caption content: ${label}`, {
node,
Expand Down Expand Up @@ -99,7 +109,10 @@ export const containerHandler: Handler = (node, state) => {
nonCaptions.filter((item: GenericNode) => item.type === 'container').length ===
nonCaptions.length;
state.useMacro('#import "@preview/subpar:0.1.1"');
state.write(`#show figure: set block(breakable: ${allSubFigs ? 'false' : 'true'})\n`);
state.useMacro('#let breakableDefault = true');
state.write(
`#show figure: set block(breakable: ${allSubFigs ? 'false' : 'breakableDefault'})\n`,
);
state.write('#subpar.grid(');
let columns = nonCaptions.length <= 3 ? nonCaptions.length : 2; // TODO: allow this to be customized
nonCaptions.forEach((item: GenericNode) => {
Expand All @@ -123,12 +136,14 @@ export const containerHandler: Handler = (node, state) => {
label = undefined;
}
} else if (nonCaptions && nonCaptions.length === 1) {
state.write('#show figure: set block(breakable: true)\n');
state.useMacro('#let breakableDefault = true');
state.write('#show figure: set block(breakable: breakableDefault)\n');
state.write('#figure(');
renderFigureChild(nonCaptions[0], state);
state.write(',');
} else {
state.write('#show figure: set block(breakable: true)\n');
state.useMacro('#let breakableDefault = true');
state.write('#show figure: set block(breakable: breakableDefault)\n');
state.write('#figure([\n ');
state.renderChildren(node, 1);
state.write('],');
Expand Down
23 changes: 16 additions & 7 deletions packages/myst-to-typst/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,17 @@ const handlers: Record<string, Handler> = {
state.addNewLine();
},
list(node, state) {
const setStart = node.ordered && node.start && node.start !== 1;
if (setStart) {
state.write(`#set enum(start: ${node.start})`);
}
state.data.list ??= { env: [] };
state.data.list.env.push(node.ordered ? '+' : '-');
state.renderChildren(node, 2);
state.renderChildren(node, setStart ? 1 : 2);
state.data.list.env.pop();
if (setStart) {
state.write('#set enum(start: 1)\n\n');
}
},
listItem(node, state) {
const listEnv = state.data.list?.env ?? [];
Expand Down Expand Up @@ -355,13 +362,15 @@ const handlers: Record<string, Handler> = {
linkHandler({ ...node, url: url }, state);
return;
}
// Look up reference and add the text
// const usedTemplate = node.template?.includes('%s') ? node.template : undefined;
// const text = (usedTemplate ?? toText(node))?.replace(/\s/g, '~') || '%s';
const id = node.identifier;
// state.write(text.replace(/%s/g, `@${id}`));
const next = nextCharacterIsText(parent, node);
state.write(next ? `#[@${id}]` : `@${id}`);
if (node.children && node.children.length > 0) {
state.write(`#link(<${id}>)[`);
state.renderChildren(node);
state.write(']');
} else {
const next = nextCharacterIsText(parent, node);
state.write(next ? `#[@${id}]` : `@${id}`);
}
},
citeGroup(node, state) {
state.renderChildren(node, 0, { delim: ' ' });
Expand Down
10 changes: 8 additions & 2 deletions packages/myst-to-typst/src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,12 @@ export const tableHandler: Handler = (node, state) => {
return;
}
state.useMacro('#import "@preview/tablex:0.0.9": tablex, cellx, hlinex, vlinex');
// These two separate style hooks are somewhat redundant, but they allow defining
// article-wide styles and single-table styles separately
state.useMacro('#let tableStyle = (:)');
state.useMacro('#let columnStyle = (:)');
state.write(
`${command}(columns: ${columns}, header-rows: ${countHeaderRows(node)}, repeat-header: true, ..tableStyle,\n`,
`${command}(columns: ${columns}, header-rows: ${countHeaderRows(node)}, repeat-header: true, ..tableStyle, ..columnStyle,\n`,
);
state.renderChildren(node, 1);
state.write(')\n');
Expand All @@ -48,7 +51,7 @@ export const tableRowHandler: Handler = (node, state) => {
};

export const tableCellHandler: Handler = (node, state) => {
if (node.rowspan || node.colspan || node.align) {
if (node.rowspan || node.colspan || node.align || node.style?.backgroundColor) {
state.write('cellx(');
if (node.rowspan) {
state.write(`rowspan: ${node.rowspan}, `);
Expand All @@ -59,6 +62,9 @@ export const tableCellHandler: Handler = (node, state) => {
if (node.align) {
state.write(`align: ${node.align}, `);
}
if (node.style?.backgroundColor) {
state.write(`fill: rgb("${node.style.backgroundColor}"), `);
}
state.write(')');
}
state.write('[\n');
Expand Down
17 changes: 17 additions & 0 deletions packages/myst-to-typst/tests/cross-references.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,20 @@ cases:
value: 'a for the first part of the figure!'
typst: |-
See #[@fig1]a for the first part of the figure!
- title: references followed by text with children
mdast:
type: root
children:
- type: paragraph
children:
- type: text
value: 'See '
- type: crossReference
identifier: fig1
children:
- type: text
value: 'Figure 56'
- type: text
value: 'a for the first part of the figure!'
typst: |-
See #link(<fig1>)[Figure 56]a for the first part of the figure!
4 changes: 2 additions & 2 deletions packages/myst-to-typst/tests/figures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ cases:
url: glacier.jpg
width: 500px
typst: |-
#show figure: set block(breakable: true)
#show figure: set block(breakable: breakableDefault)
#figure(
image("glacier.jpg", width: 62.5%),
kind: "figure",
Expand All @@ -45,7 +45,7 @@ cases:
- type: text
value: A curious figure.
typst: |-
#show figure: set block(breakable: true)
#show figure: set block(breakable: breakableDefault)
#figure(
image("glacier.jpg", width: 62.5%),
caption: [
Expand Down
8 changes: 4 additions & 4 deletions packages/myst-to-typst/tests/tables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ cases:
value: Row 1, Column 2

typst: |-
#tablex(columns: 2, header-rows: 1, repeat-header: true, ..tableStyle,
#tablex(columns: 2, header-rows: 1, repeat-header: true, ..tableStyle, ..columnStyle,
[
Head 1, Column 1
],
Expand Down Expand Up @@ -78,7 +78,7 @@ cases:
value: Row 1, Column 2

typst: |-
#tablex(columns: 2, header-rows: 1, repeat-header: true, ..tableStyle,
#tablex(columns: 2, header-rows: 1, repeat-header: true, ..tableStyle, ..columnStyle,
[
Head 1, Column 1
],
Expand Down Expand Up @@ -119,7 +119,7 @@ cases:
value: Row 1, Column 2

typst: |-
#tablex(columns: 2, header-rows: 0, repeat-header: true, ..tableStyle,
#tablex(columns: 2, header-rows: 0, repeat-header: true, ..tableStyle, ..columnStyle,
cellx(rowspan: 2, )[
Head 1, Column 1
],
Expand Down Expand Up @@ -156,7 +156,7 @@ cases:
value: Row 1, Column 2

typst: |-
#tablex(columns: 2, header-rows: 0, repeat-header: true, ..tableStyle,
#tablex(columns: 2, header-rows: 0, repeat-header: true, ..tableStyle, ..columnStyle,
cellx(colspan: 2, )[
Head 1, Column 1
],
Expand Down
Loading