Skip to content

Commit c609ba1

Browse files
authored
remove auto-generated tikz-figures.json and instead import YAML metadata for diagrams directly with @rollup/plugin-yaml (#39)
- update project description and readme to reflect newly added Cetz diagrams - update package dependencies to latest versions - use @sveltejs/enhanced-img for better performance and automatic layout-shift prevention, removes need for image-size to get manually set width and height
1 parent 52ba101 commit c609ba1

13 files changed

+145
-122
lines changed

site/.gitignore

-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@ node_modules
44
# production build
55
.svelte-kit
66
build
7-
src/lib/tikz-figures.json

site/package.json

+15-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"author": "Janosh Riebesell <[email protected]>",
3-
"description": "SvelteKit gallery site of standalone TikZ figures along with their LaTeX source code",
3+
"description": "Gallery of scientific diagrams along with their Typst and/or LaTeX source code",
44
"homepage": "https://janosh.github.io/tikz",
55
"license": "MIT",
66
"name": "tikz",
@@ -16,33 +16,34 @@
1616
"check": "svelte-check"
1717
},
1818
"devDependencies": {
19-
"@iconify/svelte": "^4.1.0",
20-
"@stylistic/eslint-plugin": "^2.12.1",
19+
"@iconify/svelte": "^4.2.0",
20+
"@rollup/plugin-yaml": "^4.1.2",
21+
"@stylistic/eslint-plugin": "^2.13.0",
2122
"@sveltejs/adapter-static": "^3.0.8",
22-
"@sveltejs/kit": "^2.15.1",
23+
"@sveltejs/enhanced-img": "^0.4.4",
24+
"@sveltejs/kit": "^2.15.2",
2325
"@sveltejs/vite-plugin-svelte": "^5.0.3",
2426
"@types/js-yaml": "^4.0.9",
25-
"eslint": "^9.17.0",
27+
"eslint": "^9.18.0",
2628
"eslint-plugin-svelte": "^2.46.1",
2729
"highlight.js": "^11.11.1",
28-
"image-size": "^1.2.0",
2930
"js-yaml": "^4.1.0",
3031
"prettier": "^3.4.2",
31-
"prettier-plugin-svelte": "^3.3.2",
32+
"prettier-plugin-svelte": "^3.3.3",
3233
"rehype-katex": "^7.0.1",
3334
"rehype-stringify": "^10.0.1",
3435
"remark-math": "6.0.0",
3536
"remark-parse": "^11.0.0",
3637
"remark-rehype": "^11.1.1",
37-
"svelte": "5.16.0",
38-
"svelte-check": "^4.1.1",
38+
"svelte": "5.17.4",
39+
"svelte-check": "^4.1.4",
3940
"svelte-multiselect": "11.0.0-rc.1",
4041
"svelte-preprocess": "^6.0.3",
41-
"svelte-zoo": "^0.4.15",
42-
"svelte2tsx": "^0.7.31",
43-
"typescript": "5.7.2",
44-
"typescript-eslint": "^8.19.0",
42+
"svelte-zoo": "^0.4.16",
43+
"svelte2tsx": "^0.7.34",
44+
"typescript": "5.7.3",
45+
"typescript-eslint": "^8.20.0",
4546
"unified": "^11.0.5",
46-
"vite": "^6.0.6"
47+
"vite": "^6.0.7"
4748
}
4849
}

site/readme.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<img src="../assets/favicon.svg" alt="TikZ" height=150>
33
</p>
44

5-
# Random TikZ Collection
5+
# Scientific Diagram Collection
66

7-
Source code for gallery site of `standalone` TikZ figures. Built with [SvelteKit](https://kit.svelte.dev).
7+
Source code for gallery of Cetz (Typst) and TikZ (LaTeX) diagrams. Built with [SvelteKit](https://kit.svelte.dev).
88

99
## Running this site locally
1010

site/src/app.d.ts

-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
/// <reference types="@sveltejs/kit" />
22

3-
declare module '*tikz-figures.json' {
4-
const tex_files: import('./lib').TikzFigure[]
5-
export default tex_files
6-
}
7-
83
declare module '*package.json'

site/src/lib/Card.svelte

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
<script lang="ts">
2-
import { dev } from '$app/environment'
32
import { fade } from 'svelte/transition'
4-
import type { TikzFigure } from '.'
3+
import type { Diagram } from '.'
54
import { Tags } from '.'
65
7-
export let item: TikzFigure
6+
export let item: Diagram
87
export let style = ``
8+
export let format: `short` | `full` = `full`
99
10-
// development server fetches files from local folder (specified by svelte.config.js kit.files.assets)
11-
// production server fetches files from GitHub (so we don't need to re-upload with every build)
12-
const asset_uri = dev ? `` : `https://raw.githubusercontent.com/janosh/tikz/main/assets`
13-
$: base_uri = `${asset_uri}/${slug}/${slug}`
14-
$: ({ slug, title, description, width, height, tags } = item)
10+
$: ({ slug, title, description, tags } = item)
1511
</script>
1612

1713
<a href={slug} transition:fade={{ duration: 200 }} {style}>
1814
<h2 id={slug}>{title}</h2>
1915
<Tags {tags} />
20-
<img src="{base_uri}.png" alt={title} {width} {height} />
21-
{#if description}
16+
<enhanced:img src={item.images.sd} alt={title} class="diagram" />
17+
{#if description && format === `full`}
2218
<p class="description">{@html description}</p>
2319
{/if}
2420
</a>
@@ -40,7 +36,7 @@
4036
h2 {
4137
margin: 4pt 1ex 1ex;
4238
}
43-
img {
39+
.diagram {
4440
box-sizing: border-box;
4541
width: calc(100% - 2ex);
4642
background: #ffffff85;
@@ -57,7 +53,9 @@
5753
margin: 0;
5854
visibility: hidden;
5955
opacity: 0;
60-
transition: opacity 0.4s, visibility 0.4s;
56+
transition:
57+
opacity 0.4s,
58+
visibility 0.4s;
6159
overflow-wrap: break-word;
6260
width: 100%;
6361
box-sizing: border-box;

site/src/lib/index.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@ export { default as Card } from './Card.svelte'
22
export { default as CodeBlock } from './CodeBlock.svelte'
33
export { default as Tags } from './Tags.svelte'
44

5-
export type TikzFigure = {
6-
title: string
5+
export type Diagram = {
76
slug: string
8-
width: number
9-
height: number
107
downloads: string[]
118
code: string
9+
images: {
10+
hd: string // TODO fix type, actual is {sources: png: string, avif, string, ...}
11+
sd: string
12+
}
1213
} & YamlMetadata
1314

1415
export type YamlMetadata = {
16+
title: string
1517
tags: string[]
1618
description: string | null
1719
creator?: string

site/src/lib/stores.ts

+72-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import rehype_katex from 'rehype-katex'
2+
import rehype_stringify from 'rehype-stringify'
3+
import remark_math from 'remark-math'
4+
import remark_parse from 'remark-parse'
5+
import remark_rehype from 'remark-rehype'
16
import { session_store, url_param_store } from 'svelte-zoo/stores'
27
import { writable } from 'svelte/store'
3-
import type { TikzFigure } from '.'
8+
import { unified } from 'unified'
9+
import type { YamlMetadata } from '.'
410

511
export const search = url_param_store(`search`, ``)
612

@@ -14,4 +20,68 @@ export const filter_tags = session_store<{ label: string; count: number }[]>(
1420
[],
1521
)
1622

17-
export const filtered_figs = writable<TikzFigure[]>([])
23+
// Load all YAML files from assets directory
24+
const yaml_data = import.meta.glob(`$assets/**/*.yml`, {
25+
eager: true,
26+
import: `default`,
27+
}) as Record<string, YamlMetadata>
28+
const code_files = import.meta.glob([`$assets/**/*.tex`, `$assets/**/*.typ`], {
29+
eager: true,
30+
query: `?raw`,
31+
import: `default`,
32+
})
33+
const asset_files = import.meta.glob(
34+
[`$assets/**/*.png`, `$assets/**/*.pdf`, `$assets/**/*.svg`],
35+
{ eager: true, query: `?url`, import: `default` },
36+
)
37+
const image_files = import.meta.glob(`$assets/**/*.png`, {
38+
eager: true,
39+
query: { enhanced: true },
40+
import: `default`,
41+
})
42+
43+
// Process YAML files to create figure data
44+
export const diagrams = Object.entries(yaml_data).map(([path, metadata]) => {
45+
const slug = path.split(`/`)[2] // get directory name
46+
const figure_basename = `../assets/${slug}/${slug}`
47+
48+
// Check if .tex or .typ file exists and get its content
49+
const tex_path = `${figure_basename}.tex`
50+
const typ_path = `${figure_basename}.typ`
51+
const code = code_files[tex_path] ?? code_files[typ_path] ?? ``
52+
53+
// Add typst and cetz tags if applicable
54+
if (!metadata.tags) metadata.tags = []
55+
56+
if (code_files[typ_path]) {
57+
if (!metadata.tags.includes(`typst`)) metadata.tags.push(`typst`)
58+
}
59+
60+
// Process description with remark/rehype if needed
61+
if (metadata.description) {
62+
metadata.description = unified()
63+
.use(remark_parse)
64+
.use(remark_math)
65+
.use(remark_rehype)
66+
.use(rehype_katex)
67+
.use(rehype_stringify)
68+
.processSync(metadata.description)
69+
.toString()
70+
}
71+
72+
const downloads = [
73+
asset_files[`${figure_basename}-hd.png`],
74+
asset_files[`${figure_basename}.png`],
75+
asset_files[`${figure_basename}.pdf`],
76+
asset_files[`${figure_basename}.svg`],
77+
].filter(Boolean)
78+
79+
const images = {
80+
hd: image_files[`${figure_basename}-hd.png`],
81+
sd: image_files[`${figure_basename}.png`],
82+
}
83+
84+
return { ...metadata, slug, code, downloads, images }
85+
})
86+
87+
export const filtered_diagrams = writable(diagrams)

site/src/routes/+layout.svelte

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<script lang="ts">
22
import { goto } from '$app/navigation'
3-
import figs from '$lib/tikz-figures.json'
3+
import { diagrams } from '$lib/stores'
44
import { repository } from '$root/package.json'
55
import Icon from '@iconify/svelte'
66
import { CmdPalette } from 'svelte-multiselect'
77
import { GitHubCorner } from 'svelte-zoo'
88
import '../app.css'
99
10-
const actions = figs.map(({ title, slug }) => {
10+
$: actions = diagrams.map(({ title, slug }) => {
1111
return { label: title, action: () => goto(slug) }
1212
})
1313
</script>

site/src/routes/+page.svelte

+20-16
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
import { building } from '$app/environment'
33
import { goto } from '$app/navigation'
44
import { Card } from '$lib'
5-
import { filter_tags, filtered_figs, search, tag_filter_mode } from '$lib/stores'
6-
import figs from '$lib/tikz-figures.json'
5+
import {
6+
diagrams,
7+
filter_tags,
8+
filtered_diagrams,
9+
search,
10+
tag_filter_mode,
11+
} from '$lib/stores'
712
import { homepage, repository } from '$root/package.json'
813
import Icon from '@iconify/svelte'
914
import MultiSelect from 'svelte-multiselect'
@@ -13,8 +18,8 @@
1318
$: cols = clamp(Math.floor(innerWidth / 300), 1, 6)
1419
1520
const tags = Object.entries(
16-
figs
17-
.flatMap((file) => file.tags)
21+
diagrams
22+
?.flatMap((file) => file.tags)
1823
.reduce(
1924
(acc, el) => {
2025
acc[el] = (acc[el] ?? 0) + 1
@@ -23,12 +28,12 @@
2328
{} as Record<string, number>,
2429
),
2530
).filter(([, count]) => count > 2)
26-
tags.sort(([a], [b]) => a.localeCompare(b))
31+
tags.sort(([t1], [t2]) => t1.localeCompare(t2))
2732
2833
const clamp = (num: number, min: number, max: number) =>
2934
Math.min(Math.max(num, min), max)
3035
31-
$: $filtered_figs = figs
36+
$: $filtered_diagrams = diagrams
3237
.filter((file) => {
3338
const searchTerms = $search?.toLowerCase().split(` `)
3439
const matches_search = searchTerms?.every((term) =>
@@ -45,23 +50,23 @@
4550
}
4651
return matches_search && matches_tags
4752
})
48-
.sort((a, b) => {
49-
return a.title.localeCompare(b.title)
53+
.sort((d1, d2) => {
54+
return d1.title.localeCompare(d2.title)
5055
})
5156
52-
const meta_description = `Random collection of ${figs.length} TikZ figures`
57+
const meta_description = `Collection of ${diagrams.length} math/physics/chemistry/ML diagrams`
5358
5459
let active_idx = -1
5560
5661
function handle_keyup(event: KeyboardEvent) {
5762
if (event.key === `Enter` && active_idx >= 0) {
58-
const site = figs[active_idx]
63+
const site = diagrams[active_idx]
5964
goto(site.slug)
6065
}
6166
const to = {
6267
// wrap around
63-
ArrowLeft: (active_idx - 1 + figs.length) % figs.length,
64-
ArrowRight: (active_idx + 1) % figs.length,
68+
ArrowLeft: (active_idx - 1 + diagrams.length) % diagrams.length,
69+
ArrowRight: (active_idx + 1) % diagrams.length,
6570
Escape: -1,
6671
}[event.key]
6772
// if not arrow or escape key, return early for browser default behavior
@@ -86,12 +91,11 @@
8691
<svelte:window bind:innerWidth on:keyup={handle_keyup} />
8792

8893
<h1>
89-
Random
9094
<img src="favicon.svg" alt="Logo" style="height: 2em; vertical-align: middle;" />
9195
Collection
9296
</h1>
9397
<p>
94-
{figs.length} standalone TikZ figures, mostly about
98+
{diagrams.length} Cetz (Typst) and TikZ (LaTeX) diagrams, mostly about
9599
{#each [`physics`, `chemistry`, `machine learning`] as tag, idx}
96100
{#if idx > 0},{/if}
97101
<button class="link" on:click={() => ($filter_tags = [{ label: tag, count: 0 }])}>
@@ -127,7 +131,7 @@
127131
</div>
128132

129133
{#if $search?.length || $filter_tags?.length}
130-
<p>{$filtered_figs.length} match{$filtered_figs.length != 1 ? `es` : ``}</p>
134+
<p>{$filtered_diagrams.length} match{$filtered_diagrams.length != 1 ? `es` : ``}</p>
131135
{/if}
132136

133137
{#if cols || building}
@@ -136,7 +140,7 @@
136140
style="column-gap: 1em;"
137141
use:highlight_matches={{ query: $search, css_class: `highlight-match` }}
138142
>
139-
{#each $filtered_figs as item, idx (item.slug)}
143+
{#each $filtered_diagrams as item, idx (item.slug)}
140144
<li class:active={active_idx == idx}>
141145
<Card {item} style="break-inside: avoid;" />
142146
</li>
+6-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { filtered_figs } from '$lib/stores'
1+
import { filtered_diagrams } from '$lib/stores'
22
import { error } from '@sveltejs/kit'
33
import { get } from 'svelte/store'
44
import type { PageServerLoad } from './$types'
55

66
export const load: PageServerLoad = ({ params }) => {
77
const { slug } = params
8-
const figs = get(filtered_figs)
8+
const diagrams = get(filtered_diagrams)
99

10-
const idx = figs.findIndex((itm) => itm.slug === slug)
10+
const idx = diagrams?.findIndex((itm) => itm.slug === slug)
1111
if (idx === -1) throw error(404, `Page '${slug}' not found`)
12-
const fig = figs[idx]
12+
const diagram = diagrams[idx] // figure to display on the rendered page
1313

14-
return { fig, figs, slug }
14+
// diagrams passed as well for rendering links to next/previous diagrams
15+
return { diagram, diagrams, slug }
1516
}

0 commit comments

Comments
 (0)