Add a Typst compiler alongside Quarto and LaTeX
Build and Deploy Verso / deploy (push) Successful in 7m37s

A project whose root file is a .typ file now compiles straight to PDF with
Typst, as a third engine beside Quarto (.qmd) and latexmk (.tex). Dispatch
stays purely extension-based.

CLSI:
- New TypstRunner.js: runs `quarto typst compile <main>.typ output.pdf` (reuses
  the Typst bundled in Quarto, so no extra binary / Docker change). stderr is
  merged into output.log.
- CompileManager: _isTypstFile + a TypstRunner branch in _getRunner, and
  TypstRunner added to the isRunning check and stopCompile kill list.
- RequestParser: 'typst' added to VALID_COMPILERS.

web:
- settings.defaults: 'typ' added to validRootDocExtensions and the text
  extensions (so .typ opens in the editor); 'typst' added to safeCompilers.
- output-files: the Quarto/Typst log parser (which already understands Typst
  `error:`/`warning:` + `┌─ file:line:col` diagnostics) now also handles .typ
  compiles, so their errors/warnings populate the log tabs.

Polish:
- New-project menu: "Blank Typst project" + "Example Typst project" in both the
  main and welcome dropdowns, backed by createBasicProject/createExampleProject
  flavour 'typst', a new mainbasic.typ template and an example-project-typst
  presentation (math, an image, a table, lists).
- Compiler dropdown gains a "Typst" option (cosmetic; dispatch is by extension).

README updated: three compilers side by side, with a Writing-a-Typst-document
section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
claude
2026-06-01 12:56:30 +00:00
parent 2a9c4cfe81
commit d67bc77b0e
17 changed files with 390 additions and 30 deletions
+12 -4
View File
@@ -2,14 +2,14 @@
**A collaborative real-time editor for Quarto and LaTeX presentations and documents.**
Verso is a fork of [Overleaf](https://github.com/overleaf/overleaf) that adds first-class [Quarto](https://quarto.org) support alongside Overleaf's existing LaTeX toolchain. It keeps Overleaf's real-time collaboration infrastructure and runs **two compilers side by side**: `.qmd` files are built with Quarto (PDF via Typst, or HTML via RevealJS), while `.tex` files still compile with `latexmk`/TeX Live. The engine is selected automatically from the root file's extension, so Quarto and LaTeX projects coexist.
Verso is a fork of [Overleaf](https://github.com/overleaf/overleaf) that adds first-class [Quarto](https://quarto.org) support alongside Overleaf's existing LaTeX toolchain. It keeps Overleaf's real-time collaboration infrastructure and runs **three compilers side by side**, chosen automatically from the root file's extension: `.qmd` builds with Quarto (PDF via Typst, or HTML via RevealJS), `.tex` with `latexmk`/TeX Live, and `.typ` straight through [Typst](https://typst.app). All three coexist on one server.
---
## Features
- **Real-time collaboration** — multiple users editing the same `.qmd` file simultaneously, powered by Overleaf's operational-transformation engine
- **Dual compiler** — `.qmd` files build with Quarto; `.tex` files build with `latexmk`/TeX Live. The runner is chosen by the root file's extension, so LaTeX and Quarto projects live side by side
- **Three compilers** — `.qmd` builds with Quarto, `.tex` with `latexmk`/TeX Live, and `.typ` with Typst. The runner is chosen by the root file's extension, so Quarto, LaTeX and Typst projects live side by side
- **Two output formats** — set `format: typst` in the YAML frontmatter for a PDF preview, or `format: revealjs` for an interactive HTML presentation rendered in an iframe
- **Document outline** — headings (`#`, `##`, `###`) are extracted and shown in the sidebar outline panel
- **Math, code, tables** — standard Quarto/Pandoc Markdown features all work
@@ -70,7 +70,7 @@ browser ──→ nginx:80
└── /project/*/output/* → clsi-nginx:8080 (compiled output files)
web → document-updater → Redis pub/sub → real-time → browser
web → CLSI (quarto render / latexmk) → output files → nginx → browser
web → CLSI (quarto render / latexmk / typst) → output files → nginx → browser
```
Key services:
@@ -80,7 +80,7 @@ Key services:
| `web` | HTTP API, React frontend, auth, project management |
| `real-time` | WebSocket layer, live cursor and edit sync |
| `document-updater` | Operational transformation, Redis pub/sub |
| `clsi` | Compiler — runs `quarto render` (`.qmd`) or `latexmk` (`.tex`) and serves output |
| `clsi` | Compiler — runs `quarto render` (`.qmd`), `latexmk` (`.tex`) or `typst` (`.typ`) and serves output |
| `docstore` | Document text storage (MongoDB) |
| `filestore` | Binary file storage (S3 or local) |
| `project-history` | Change history and version tracking |
@@ -123,6 +123,14 @@ point; **Blank LaTeX project** gives you an empty `main.tex`.
> extra packages may not build out of the box yet — see `server-ce/Dockerfile-base`
> for how to switch to a fuller TeX Live scheme.
## Writing a Typst document
A project whose root file is a `.typ` file compiles directly to PDF with
[Typst](https://typst.app) — fast, modern markup with a real scripting
language. No extra install is needed: Verso drives the Typst that ships inside
Quarto (`quarto typst compile`). Use the **Blank Typst project** or **Example
Typst project** entries in the *New project* menu to get started.
## Environment variables
Verso inherits all of Overleaf's environment variables (prefixed `OVERLEAF_`).
+18 -1
View File
@@ -7,6 +7,7 @@ import OError from '@overleaf/o-error'
import ResourceWriter from './ResourceWriter.js'
import QuartoRunner from './QuartoRunner.js'
import LatexRunner from './LatexRunner.js'
import TypstRunner from './TypstRunner.js'
import OutputFileFinder from './OutputFileFinder.js'
import OutputCacheManager from './OutputCacheManager.js'
import ClsiMetrics from './Metrics.js'
@@ -47,9 +48,22 @@ function _isQuartoFile(rootResourcePath) {
return /\.(qmd|md|rmd)$/i.test(rootResourcePath || '')
}
// A bare Typst source (.typ) compiles straight to PDF with the Typst that
// ships inside Quarto (see TypstRunner), separate from the Quarto pipeline.
function _isTypstFile(rootResourcePath) {
return /\.typ$/i.test(rootResourcePath || '')
}
// Return a runner with a uniform { run, isRunning, kill } interface so the
// rest of CompileManager doesn't need to know which engine is in use.
function _getRunner(rootResourcePath) {
if (_isTypstFile(rootResourcePath)) {
return {
run: (name, opts) => TypstRunner.promises.runTypst(name, opts),
isRunning: name => TypstRunner.isRunning(name),
kill: name => TypstRunner.promises.killTypst(name),
}
}
if (_isQuartoFile(rootResourcePath)) {
return {
run: (name, opts) => QuartoRunner.promises.runQuarto(name, opts),
@@ -351,7 +365,9 @@ async function stopCompile(projectId, userId) {
// stopCompile has no root path, so check both runners — only one can be
// active for a given compileName at a time.
const isRunning =
QuartoRunner.isRunning(compileName) || LatexRunner.isRunning(compileName)
QuartoRunner.isRunning(compileName) ||
LatexRunner.isRunning(compileName) ||
TypstRunner.isRunning(compileName)
const lock = LockManager.getExistingLock(getCompileDir(projectId, userId))
let lockReleased
if (lock) {
@@ -363,6 +379,7 @@ async function stopCompile(projectId, userId) {
}
await QuartoRunner.promises.killQuarto(compileName)
await LatexRunner.promises.killLatex(compileName)
await TypstRunner.promises.killTypst(compileName)
await lockReleased
}
+8 -1
View File
@@ -2,7 +2,14 @@ import { promisify } from 'node:util'
import settings from '@overleaf/settings'
import OutputCacheManager from './OutputCacheManager.js'
const VALID_COMPILERS = ['quarto', 'pdflatex', 'latex', 'xelatex', 'lualatex']
const VALID_COMPILERS = [
'quarto',
'typst',
'pdflatex',
'latex',
'xelatex',
'lualatex',
]
const MAX_TIMEOUT = 600
const EDITOR_ID_REGEX = /^[a-f0-9-]{36}$/ // UUID
const HISTORY_ID_REGEX = /^([0-9a-f]{24}|[1-9][0-9]{0,9})$/ // mongo id or postgres id
+104
View File
@@ -0,0 +1,104 @@
import Path from 'node:path'
import { promisify } from 'node:util'
import logger from '@overleaf/logger'
import CommandRunner from './CommandRunner.js'
import fs from 'node:fs'
// Maps currently-running Typst jobs: compileName → PID (or docker container id)
const ProcessTable = {}
// Compiles a standalone Typst document (.typ) straight to output.pdf. We reuse
// the Typst that ships inside Quarto via `quarto typst compile`, so there is no
// extra binary to install. This is deliberately the simplest of the three
// runners: Typst only ever produces a PDF, so there is no format detection,
// no HTML asset directory and no extension merging (cf. QuartoRunner).
function runTypst(compileName, options, callback) {
const { directory, mainFile, image, environment, compileGroup } = options
const timeout = options.timeout || 60000
logger.debug(
{ directory, timeout, mainFile, compileGroup },
'starting typst compile'
)
const command = _buildTypstCommand(mainFile)
ProcessTable[compileName] = CommandRunner.run(
compileName,
command,
directory,
image,
timeout,
environment || {},
compileGroup,
null,
function (error, output) {
delete ProcessTable[compileName]
// Propagate real process-level errors (killed, timed out) but NOT
// ordinary non-zero exit codes from Typst itself. A compile failure
// (exit code 1) is not a server error — the absence of output.pdf is
// enough for CompileController to return 'failure'.
if (error && (error.terminated || error.timedout)) {
return callback(error)
}
// On exit-code-1 errors LocalCommandRunner attaches stdout to the error
// object; merge it so _writeLogOutput can persist it.
const combined = output || (error ? { stdout: error.stdout || '' } : null)
_writeLogOutput(compileName, directory, combined, () =>
callback(null, combined)
)
}
)
}
function _buildTypstCommand(mainFile) {
// Run through a POSIX shell so stderr (where Typst writes its diagnostics)
// is merged into stdout (2>&1). LocalCommandRunner replaces $COMPILE_DIR
// before the shell sees it; the output path is relative because the shell
// CWD is already the compile directory.
const inputPath = `$COMPILE_DIR/${mainFile}`
const cmd = `quarto typst compile ${inputPath} output.pdf 2>&1`
return ['/bin/sh', '-c', cmd]
}
function _writeLogOutput(compileName, directory, output, callback) {
const content = (output && output.stdout) || ''
if (!content) return callback()
// Write to output.log so the PDF-preview log panel picks it up. Typst's
// `error:`/`warning:` + `┌─ file:line:col` diagnostics are understood by the
// Quarto/Typst log parser on the web side.
const logFile = Path.join(directory, 'output.log')
fs.unlink(logFile, () => {
fs.writeFile(logFile, content, { flag: 'wx' }, err => {
if (err) {
logger.error({ err, compileName, logFile }, 'error writing typst log')
}
callback()
})
})
}
function isRunning(compileName) {
return ProcessTable[compileName] != null
}
function killTypst(compileName, callback) {
logger.debug({ compileName }, 'killing running typst compile')
if (!isRunning(compileName)) {
logger.warn({ compileName }, 'no such compile to kill')
return callback(null)
}
CommandRunner.kill(ProcessTable[compileName], callback)
}
export default {
isRunning,
runTypst,
killTypst,
promises: {
runTypst: promisify(runTypst),
killTypst: promisify(killTypst),
},
}
@@ -344,6 +344,14 @@ const _ProjectController = {
'quarto'
)
break
case 'example_typst':
project = await ProjectCreationHandler.promises.createExampleProject(
userId,
projectName,
{},
'typst'
)
break
case 'blank_latex':
project = await ProjectCreationHandler.promises.createBasicProject(
userId,
@@ -351,6 +359,13 @@ const _ProjectController = {
'latex'
)
break
case 'blank_typst':
project = await ProjectCreationHandler.promises.createBasicProject(
userId,
projectName,
'typst'
)
break
case 'blank_quarto':
case 'none':
default:
@@ -88,13 +88,23 @@ async function createProjectFromSnippet(ownerId, projectName, docLines) {
async function createBasicProject(ownerId, projectName, flavour = 'quarto') {
const project = await _createBlankProject(ownerId, projectName)
// Verso compiles .qmd with Quarto and .tex with latexmk; the root file's
// extension selects the runner (see CompileManager._isQuartoFile in CLSI),
// Verso compiles .qmd with Quarto, .tex with latexmk and .typ with Typst;
// the root file's extension selects the runner (see CompileManager in CLSI),
// so a blank project's flavour is just a choice of template + root name.
const { templateName, rootDocName } =
flavour === 'latex'
? { templateName: 'mainbasic.tex', rootDocName: 'main.tex' }
: { templateName: 'mainbasic.qmd', rootDocName: 'main.qmd' }
let templateName, rootDocName
switch (flavour) {
case 'latex':
templateName = 'mainbasic.tex'
rootDocName = 'main.tex'
break
case 'typst':
templateName = 'mainbasic.typ'
rootDocName = 'main.typ'
break
default:
templateName = 'mainbasic.qmd'
rootDocName = 'main.qmd'
}
const docLines = await _buildTemplate(templateName, ownerId, projectName)
await _createRootDoc(project, ownerId, docLines, rootDocName)
@@ -179,12 +189,20 @@ async function createExampleProject(
) {
const project = await _createBlankProject(ownerId, projectName, attributes)
const { fileEntries, docEntries } =
flavour === 'quarto'
? await _addQuartoExampleProjectFiles(ownerId, projectName, project)
: await _addExampleProjectFiles(ownerId, projectName, project)
let result
switch (flavour) {
case 'quarto':
result = await _addQuartoExampleProjectFiles(ownerId, projectName, project)
break
case 'typst':
result = await _addTypstExampleProjectFiles(ownerId, projectName, project)
break
default:
result = await _addExampleProjectFiles(ownerId, projectName, project)
}
const { fileEntries, docEntries } = result
if (flavour !== 'quarto') {
if (flavour === 'latex') {
// clsi-cache warming keys on a single static example; only do it for the
// long-standing LaTeX example to avoid the "content is not static" guard.
await populateClsiCacheForExampleProject(
@@ -285,6 +303,40 @@ async function _addQuartoExampleProjectFiles(ownerId, projectName, project) {
}
}
async function _addTypstExampleProjectFiles(ownerId, projectName, project) {
const mainDocLines = await _buildTemplate(
'example-project-typst/main.typ',
ownerId,
projectName
)
const rootDoc = await _createRootDoc(
project,
ownerId,
mainDocLines,
'main.typ'
)
const imagePath = path.join(
import.meta.dirname,
'/../../../templates/project_files/example-project-typst/frog.jpg'
)
const { fileRef } = await ProjectEntityUpdateHandler.promises.addFile(
project._id,
project.rootFolder[0]._id,
'frog.jpg',
imagePath,
null,
ownerId,
null
)
return {
fileEntries: [{ path: fileRef.name, file: fileRef }],
docEntries: [
{ path: 'main.typ', doc: rootDoc, docLines: mainDocLines.join('\n') },
],
}
}
async function _createBlankProject(
ownerId,
projectName,
Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

@@ -0,0 +1,57 @@
#set document(
title: "<%= project_name %>",
author: "<%= user.first_name %> <%= user.last_name %>",
)
#set page(numbering: "1")
#set heading(numbering: "1.1")
#set par(justify: true)
#align(center)[
#text(size: 22pt, weight: "bold")[<%= project_name %>] \
#v(0.4em)
<%= user.first_name %> <%= user.last_name %> · <%= month %> <%= year %>
]
= Introduction
Welcome to *Typst* in Verso. Typst is a modern typesetting system that
compiles in milliseconds — edit `main.typ` and hit *Recompile* to see the PDF
update. This example shows off math, figures, tables and lists.
= Mathematics
Inline math such as $e^(i pi) + 1 = 0$ sits naturally in the text, while block
equations are centred and can be numbered:
$ integral_(-oo)^(oo) e^(-x^2) dif x = sqrt(pi) $
= Figures
Images stored in the project are included with the `image` function:
#figure(
image("frog.jpg", width: 50%),
caption: [A friendly frog, loaded from a project file.],
)
= Tables
#figure(
table(
columns: 3,
align: (left, center, left),
[*Engine*], [*Output*], [*Best for*],
[Typst], [PDF], [Fast, modern layout],
[Quarto], [PDF / HTML], [Reproducible documents],
[LaTeX], [PDF], [Classic STEM articles],
),
caption: [Verso compiles three source formats side by side.],
)
= Lists
- Write documents in concise, readable markup
- Get near-instant PDF output
- Script your layout with a real programming language
Now make this document your own!
@@ -0,0 +1,14 @@
#set document(
title: "<%= project_name %>",
author: "<%= user.first_name %> <%= user.last_name %>",
)
#set page(numbering: "1")
#set heading(numbering: "1.1")
#align(center)[
#text(size: 20pt, weight: "bold")[<%= project_name %>] \
#v(0.4em)
<%= user.first_name %> <%= user.last_name %> · <%= month %> <%= year %>
]
= Introduction
+10 -2
View File
@@ -55,6 +55,7 @@ const defaultTextExtensions = [
'ldf',
'rmd',
'qmd',
'typ',
'lua',
'py',
'gv',
@@ -115,7 +116,14 @@ const httpPermissionsPolicy = {
},
}
const safeCompilers = ['quarto', 'xelatex', 'pdflatex', 'latex', 'lualatex']
const safeCompilers = [
'quarto',
'typst',
'xelatex',
'pdflatex',
'latex',
'lualatex',
]
module.exports = {
env: 'server-ce',
@@ -889,7 +897,7 @@ module.exports = {
process.env.FILE_IGNORE_PATTERN ||
'**/{{__MACOSX,.git,.texpadtmp,.R}{,/**},.!(latexmkrc),*.{dvi,aux,log,toc,out,pdfsync,synctex,synctex(busy),fdb_latexmk,fls,nlo,ind,glo,gls,glg,bbl,blg,doc,docx,gz,swp}}',
validRootDocExtensions: ['qmd', 'tex', 'Rtex', 'ltx', 'Rnw'],
validRootDocExtensions: ['qmd', 'typ', 'tex', 'Rtex', 'ltx', 'Rnw'],
emailConfirmationDisabled:
process.env.EMAIL_CONFIRMATION_DISABLED === 'true' || false,
@@ -130,11 +130,11 @@ export async function handleLogFiles(
MAX_LOG_SIZE
)
try {
// Quarto compiles (.qmd/.md/.Rmd, dispatched to QuartoRunner in CLSI by
// root-file extension) produce Typst/Pandoc/Quarto diagnostics that the
// LaTeX log parser does not understand. Route those to a dedicated parser
// so their errors and warnings populate the log tabs like LaTeX ones.
if (isQuartoCompile(data)) {
// Quarto (.qmd/.md/.Rmd) and bare Typst (.typ) compiles produce
// Typst/Pandoc/Quarto diagnostics that the LaTeX log parser does not
// understand. Route those to a dedicated parser so their errors and
// warnings populate the log tabs like LaTeX ones.
if (usesQuartoLogParser(data)) {
const { errors, warnings, typesetting } = parseQuartoLog(result.log)
accumulateResults({ errors, warnings, typesetting })
} else {
@@ -301,12 +301,13 @@ function isTransientWarning(warning: LatexLogEntry): boolean {
return TRANSIENT_WARNING_REGEX.test(warning.message || '')
}
// Mirror of CompileManager._isQuartoFile in CLSI: the runner is chosen by the
// root file's extension, so we detect Quarto compiles the same way client-side.
const QUARTO_ROOT_REGEX = /\.(qmd|md|rmd)$/i
// Mirrors CompileManager's runner dispatch in CLSI: both the Quarto runner
// (.qmd/.md/.Rmd) and the Typst runner (.typ) emit Typst-style diagnostics, so
// we pick the Quarto/Typst log parser for either, keyed on the root extension.
const QUARTO_TYPST_ROOT_REGEX = /\.(qmd|md|rmd|typ)$/i
function isQuartoCompile(data: CompileResponseData): boolean {
return QUARTO_ROOT_REGEX.test(data.options?.rootResourcePath || '')
function usesQuartoLogParser(data: CompileResponseData): boolean {
return QUARTO_TYPST_ROOT_REGEX.test(data.options?.rootResourcePath || '')
}
async function fetchFileWithSizeLimit(
@@ -212,6 +212,18 @@ function NewProjectButton({
{t('blank_latex_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
onClick={e =>
handleModalMenuClick(e, {
modalVariant: 'blank_typst',
dropdownMenuEvent: 'blank-project-typst',
})
}
>
{t('blank_typst_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
onClick={e =>
@@ -236,6 +248,18 @@ function NewProjectButton({
{t('example_latex_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
onClick={e =>
handleModalMenuClick(e, {
modalVariant: 'example_typst',
dropdownMenuEvent: 'example-project-typst',
})
}
>
{t('example_typst_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
onClick={e =>
@@ -13,8 +13,10 @@ const ImportDocumentModal = lazy(() => import('./import-document-modal'))
export type NewProjectButtonModalVariant =
| 'blank_quarto'
| 'blank_latex'
| 'blank_typst'
| 'example_quarto'
| 'example_latex'
| 'example_typst'
| 'upload_project'
| 'import_from_github'
| 'import_docx'
@@ -68,6 +70,14 @@ function NewProjectButtonModal({
template="blank_latex"
/>
)
case 'blank_typst':
return (
<BlankProjectModal
onHide={onHide}
initialTags={initialTags}
template="blank_typst"
/>
)
case 'example_quarto':
return (
<ExampleProjectModal
@@ -84,6 +94,14 @@ function NewProjectButtonModal({
template="example_latex"
/>
)
case 'example_typst':
return (
<ExampleProjectModal
onHide={onHide}
initialTags={initialTags}
template="example_typst"
/>
)
case 'upload_project':
return (
<Suspense fallback={<FullSizeLoadingSpinner delay={500} />}>
@@ -135,6 +135,17 @@ function WelcomeMessageCreateNewProjectDropdown({
{t('blank_latex_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
as="button"
onClick={e =>
handleDropdownItemClick(e, 'blank_typst', 'blank-project-typst')
}
tabIndex={-1}
>
{t('blank_typst_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
as="button"
@@ -165,6 +176,21 @@ function WelcomeMessageCreateNewProjectDropdown({
{t('example_latex_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
as="button"
onClick={e =>
handleDropdownItemClick(
e,
'example_typst',
'example-project-typst'
)
}
tabIndex={-1}
>
{t('example_typst_project')}
</DropdownItem>
</li>
<li role="none">
<DropdownItem
as="button"
@@ -12,6 +12,7 @@ function getCompilerOptions(): Option<ProjectCompiler>[] {
// LaTeX users pick which TeX engine latexmk should use.
return [
{ value: 'quarto', label: 'Quarto' },
{ value: 'typst', label: 'Typst' },
{ value: 'pdflatex', label: 'pdfLaTeX' },
{ value: 'latex', label: 'LaTeX' },
{ value: 'xelatex', label: 'XeLaTeX' },
+2
View File
@@ -295,6 +295,7 @@
"blank_latex_project": "Blank LaTeX project",
"blank_project": "Blank project",
"blank_quarto_project": "Blank Quarto project",
"blank_typst_project": "Blank Typst project",
"blocked_filename": "This file name is blocked.",
"blog": "Blog",
"bold": "Bold",
@@ -842,6 +843,7 @@
"example_latex_project": "Example LaTeX project",
"example_project": "Example project",
"example_quarto_project": "Example Quarto project",
"example_typst_project": "Example Typst project",
"examples": "Examples",
"examples_lowercase": "examples",
"examples_to_help_you_learn": "Examples to help you learn how to use powerful LaTeX packages and techniques.",
+7 -1
View File
@@ -17,7 +17,13 @@ export type MainDocument = {
path: string
}
export type ProjectCompiler = 'quarto' | 'pdflatex' | 'latex' | 'xelatex' | 'lualatex'
export type ProjectCompiler =
| 'quarto'
| 'typst'
| 'pdflatex'
| 'latex'
| 'xelatex'
| 'lualatex'
export type OverallThemeMeta = {
name: string