Initial scaffold: pnpm workspace monorepo with backend + frontend
- Backend: Express + WebSocket proxy (tsx watch, strict TS, ESM) - Frontend: React 19 + Vite + Monaco + Tailwind v4 + Tauri v2 - pnpm workspace at root for single-install workflow - AGENTS.md with architectural constraints and commands - Removed Tauri greet demo, added minimal App shell - No external database, Git-as-Database philosophy
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Guidelines for AI coding assistants working on typst-leaf.
|
||||||
|
|
||||||
|
## Project Context
|
||||||
|
Self-hosted web IDE for the [Typst](https://typst.app/) typesetting language — a self-hosted "Overleaf" alternative. Built on "Git as the Database": projects are plain folders with `.git`, no external database. Architecture and philosophy are detailed in `VISION.md`.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
This is a **pnpm workspace** (see `pnpm-workspace.yaml`). Install once from the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install # installs both backend and frontend
|
||||||
|
pnpm -r typecheck # typecheck ALL workspaces (run before finishing a task)
|
||||||
|
pnpm --filter typst-leaf-backend dev # backend dev server on http://localhost:4000
|
||||||
|
pnpm --filter typst-leaf-frontend dev # frontend dev server (Vite)
|
||||||
|
pnpm --filter typst-leaf-frontend tauri dev # Tauri desktop wrapper (runs Vite first)
|
||||||
|
```
|
||||||
|
|
||||||
|
**No test framework is configured** — the `test` scripts are placeholders. Do not assume one exists.
|
||||||
|
|
||||||
|
## Architecture & Boundaries
|
||||||
|
|
||||||
|
```
|
||||||
|
typst-leaf-backend/ Node.js/TS — stateless orchestration: spawns tinymist, proxies WebSockets, REST for files + git
|
||||||
|
typst-leaf-frontend/ React (Vite) + Monaco — the IDE SPA; also contains src-tauri/ for the desktop build
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Backend** (`typst-leaf-backend/`): ESM (`"type": "module"`), strict TS with `noUncheckedIndexedAccess`. Entry: `src/index.ts`. Dev via `tsx watch`; build via `tsc` → `dist/`. Uses `express` + `ws` + `simple-git` + `execa` + `zod`.
|
||||||
|
* **Frontend** (`typst-leaf-frontend/`): React 19 + Vite. Monaco Editor wired to LSP via `monaco-languageclient` + `vscode-ws-jsonrpc`. Styling is **Tailwind v4** via the `@tailwindcss/vite` plugin (import with `@import "tailwindcss"`; there is no `tailwind.config.js`).
|
||||||
|
* **Tauri v2** lives at `typst-leaf-frontend/src-tauri/`. It shells out to `pnpm dev`/`pnpm build` and serves `../dist`.
|
||||||
|
* **Data plane:** `ws://localhost:4000/lsp` (LSP), `ws://localhost:4000/preview` (SVG stream).
|
||||||
|
|
||||||
|
## Critical Constraints (do NOT violate)
|
||||||
|
|
||||||
|
1. **Never re-implement Typst/LSP logic in TypeScript.** Spawn `tinymist` as a child process and pipe WebSocket messages straight to its stdio. The backend is a thin proxy.
|
||||||
|
2. **Never use PDF.js.** The preview renders **SVG** streamed from tinymist (selectable text, instant diffs, no binary PDFs).
|
||||||
|
3. **Path duality:** the frontend sees *virtual paths*; the backend sees *absolute OS paths* under the projects root. Keep the mapping explicit — this is a frequent source of bugs.
|
||||||
|
4. **No polling** for file or preview changes. Drive updates from LSP/WebSocket events only.
|
||||||
|
5. **Monaco LSP lifecycle is fragile.** Always dispose the languageclient + WebSocket on component unmount to avoid leaking duplicate socket connections.
|
||||||
|
6. **Browser-first.** The app must run in a generic browser. Guard any Tauri-only API behind a `window.__TAURI__` check; do not rely on it exclusively.
|
||||||
|
7. **No databases.** State = filesystem; history = `.git`. Collaboration is Git (commit/push/pull in the UI), not OT/CRDTs.
|
||||||
|
|
||||||
|
## Port Gotcha
|
||||||
|
The frontend dev server is **port 1420** (`strictPort: true`, mandated by Tauri) — **not** 3000 as `README.md` states. Backend is port 4000. `README.md` is stale on this point; trust `vite.config.ts`.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
* TypeScript strict mode in both packages. **No `any`.** Validate at API boundaries with `zod`.
|
||||||
|
* Prefer `async/await` over raw Promises.
|
||||||
|
* Comment complex logic around WebSocket message passing and process spawning.
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# typst-leaf 🍂
|
||||||
|
|
||||||
|
A completely self-hostable, high-performance editor for [Typst](https://typst.app/), built on the philosophy of "Git as the Database."
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
* **Instant Preview:** Sub-50ms incremental compilation using `tinymist`.
|
||||||
|
* **Vim Mode:** First-class support via Monaco.
|
||||||
|
* **Git Native:** No hidden databases. Your projects are just folders with `.git`.
|
||||||
|
* **Hybrid:** Run it in Docker on your server, or as a native desktop app via Tauri.
|
||||||
|
|
||||||
|
## 📂 Repository Structure
|
||||||
|
|
||||||
|
```text
|
||||||
|
typst-leaf/
|
||||||
|
├── typst-leaf-backend/ # Node.js/TS server (manages tinymist & git)
|
||||||
|
├── typst-leaf-frontend/ # React + Monaco frontend (includes Tauri)
|
||||||
|
├── VISION.md # Project philosophy and architecture
|
||||||
|
└── AGENTS.md # Guidelines for AI coding assistants
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Quick Start (Development)
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
|
||||||
|
* Node.js 20+
|
||||||
|
* pnpm
|
||||||
|
* Rust (for Tauri)
|
||||||
|
* `tinymist` binary installed in your PATH.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Install dependencies
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
# 2. Start the backend (Port 4000)
|
||||||
|
cd typst-leaf-backend
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 3. Start the web frontend (Port 3000)
|
||||||
|
# (In a new terminal)
|
||||||
|
cd typst-leaf-frontend
|
||||||
|
pnpm dev
|
||||||
|
|
||||||
|
# 4. Start Tauri Desktop App (Optional)
|
||||||
|
# (In a new terminal)
|
||||||
|
cd typst-leaf-frontend
|
||||||
|
pnpm tauri dev
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Architecture
|
||||||
|
|
||||||
|
* **Editor:** Monaco Editor
|
||||||
|
* **LSP:** `tinymist` (via WebSocket proxy)
|
||||||
|
* **Communication:**
|
||||||
|
* `ws://localhost:4000/lsp` -> Tinymist Language Server
|
||||||
|
* `ws://localhost:4000/preview` -> Tinymist Preview Service
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# VISION: typst-leaf
|
||||||
|
|
||||||
|
## 1. Product Philosophy
|
||||||
|
**typst-leaf** is a high-performance, self-hosted LaTeX alternative built for the modern era. It rejects the bloated, slow architecture of traditional LaTeX editors in favor of the **Typst** ecosystem.
|
||||||
|
|
||||||
|
It is designed for:
|
||||||
|
1. **Speed:** Sub-50ms compile times.
|
||||||
|
2. **Sovereignty:** Your data lives in a standard Git repository on your filesystem, not a proprietary database.
|
||||||
|
3. **Developer Experience:** First-class Vim support, fast LSP feedback, and CLI-based workflows.
|
||||||
|
|
||||||
|
## 2. Architecture Overview
|
||||||
|
The system follows a strict **Client-Server** model, even when running locally.
|
||||||
|
|
||||||
|
### The Stack
|
||||||
|
* **Monorepo Structure:** Flat directory structure (`typst-leaf-backend`, `typst-leaf-frontend`).
|
||||||
|
* **Frontend:** React (Vite) + Monaco Editor.
|
||||||
|
* **Desktop Wrapper:** Tauri v2 (integrated into `typst-leaf-frontend`).
|
||||||
|
* **Backend:** TypeScript (Node.js/Bun) + Express/Fastify.
|
||||||
|
* **Core Engine:** `tinymist` (Binary) for LSP and compilation.
|
||||||
|
|
||||||
|
### Component Breakdown
|
||||||
|
|
||||||
|
#### A. Backend (`typst-leaf-backend`)
|
||||||
|
The backend is a stateless orchestration layer that sits on top of the file system.
|
||||||
|
* **Role:**
|
||||||
|
* Spawns and manages the `tinymist` process.
|
||||||
|
* Proxies WebSocket connections for LSP (Language Server Protocol).
|
||||||
|
* Proxies WebSocket connections for the Preview (SVG stream).
|
||||||
|
* Exposes a REST API for file management (CRUD) and Git operations (`git commit`, `git push`).
|
||||||
|
* **Data Source:** Standard OS file system. Each "Project" is a directory.
|
||||||
|
|
||||||
|
#### B. Frontend (`typst-leaf-frontend`)
|
||||||
|
A Single Page Application (SPA) that acts as the IDE interface.
|
||||||
|
* **Editor:** Monaco Editor configured with `monaco-languageclient` to talk to the backend LSP.
|
||||||
|
* **Preview:** A high-performance SVG renderer that receives delta updates from Tinymist.
|
||||||
|
* **Vim Mode:** `monaco-vim` enabled by default or toggleable.
|
||||||
|
* **Tauri Integration:** The frontend folder contains `src-tauri`, allowing it to build as a native desktop application.
|
||||||
|
|
||||||
|
## 3. Critical Technical Decisions
|
||||||
|
|
||||||
|
### 3.1 The "No-Database" Database
|
||||||
|
We do not use MongoDB, Postgres, or SQL.
|
||||||
|
* **State:** The state is the file system.
|
||||||
|
* **History:** The history is the `.git` folder.
|
||||||
|
* **Collaboration:** We do not implement Operational Transforms (OT) or CRDTs for simultaneous editing. Collaboration is handled via Git (Commit/Push/Pull) logic exposed in the UI.
|
||||||
|
|
||||||
|
### 3.2 The Rendering Pipeline
|
||||||
|
We **do not** render PDFs in the browser for preview.
|
||||||
|
1. User types in Monaco.
|
||||||
|
2. Frontend sends `textDocument/didChange` (LSP) to Backend.
|
||||||
|
3. Backend pipes to `tinymist`.
|
||||||
|
4. `tinymist` computes layout and pushes an **SVG** diff to the preview socket.
|
||||||
|
5. Frontend renders the SVG.
|
||||||
|
*Rationale:* SVGs are selectable, zoomable without blur, and significantly lighter than streaming PDF binaries.
|
||||||
|
|
||||||
|
### 3.3 The LSP Connection
|
||||||
|
We use `monaco-languageclient` to connect Monaco to the backend via WebSocket. The backend simply pipes this socket to the stdin/stdout of the running `tinymist` binary. We do not reimplement LSP logic in TypeScript.
|
||||||
|
|
||||||
|
## 4. User Stories
|
||||||
|
* **As a User,** I want to type code and see the result instantly (<100ms lag).
|
||||||
|
* **As a User,** I want to double-click an element in the preview and have my cursor jump to the code (SyncTeX/Source Mapping).
|
||||||
|
* **As a User,** I want to hit `Cmd+S` to save to disk and `Cmd+Shift+S` to create a Git commit.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
packages:
|
||||||
|
- 'typst-leaf-backend'
|
||||||
|
- 'typst-leaf-frontend'
|
||||||
|
allowBuilds:
|
||||||
|
esbuild: true
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"name": "typst-leaf-backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsx watch src/index.ts",
|
||||||
|
"build": "tsc",
|
||||||
|
"start": "node dist/index.js",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "echo \"Error: no test specified\""
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"execa": "^9.6.1",
|
||||||
|
"express": "^4.22.2",
|
||||||
|
"simple-git": "^3.27.0",
|
||||||
|
"ws": "^8.18.2",
|
||||||
|
"zod": "^3.24.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/cors": "^2.8.17",
|
||||||
|
"@types/express": "^4.17.25",
|
||||||
|
"@types/node": "^22.15.17",
|
||||||
|
"@types/ws": "^8.18.1",
|
||||||
|
"tsx": "^4.19.4",
|
||||||
|
"typescript": "~5.8.3"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.23.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { createServer } from "http";
|
||||||
|
import { WebSocketServer } from "ws";
|
||||||
|
|
||||||
|
const PORT = Number(process.env.PORT) || 4000;
|
||||||
|
|
||||||
|
const server = createServer((_req, res) => {
|
||||||
|
res.writeHead(200, { "Content-Type": "application/json" });
|
||||||
|
res.end(JSON.stringify({ status: "ok", service: "typst-leaf-backend" }));
|
||||||
|
});
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ server });
|
||||||
|
|
||||||
|
wss.on("connection", (ws) => {
|
||||||
|
console.log("WebSocket client connected");
|
||||||
|
|
||||||
|
ws.on("message", (data) => {
|
||||||
|
console.log("Received:", data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on("close", () => {
|
||||||
|
console.log("WebSocket client disconnected");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(PORT, () => {
|
||||||
|
console.log(`Backend running on http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noEmit": false
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Tauri + React + Typescript
|
||||||
|
|
||||||
|
This template should help get you started developing with Tauri, React and Typescript in Vite.
|
||||||
|
|
||||||
|
## Recommended IDE Setup
|
||||||
|
|
||||||
|
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Tauri + React + Typescript</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "typst-leaf-frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"tauri": "tauri",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
|
"@tauri-apps/api": "^2",
|
||||||
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
|
"monaco-languageclient": "^10.7.0",
|
||||||
|
"monaco-vim": "^0.4.4",
|
||||||
|
"react": "^19.1.0",
|
||||||
|
"react-dom": "^19.1.0",
|
||||||
|
"vscode-ws-jsonrpc": "^3.5.0",
|
||||||
|
"zod": "^3.24.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.1.6",
|
||||||
|
"@types/react": "^19.1.8",
|
||||||
|
"@types/react-dom": "^19.1.6",
|
||||||
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
|
"tailwindcss": "^4.1.6",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"vite": "^7.0.4",
|
||||||
|
"@tauri-apps/cli": "^2"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.23.0"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# Generated by Tauri
|
||||||
|
# will have schema files for capabilities auto-completion
|
||||||
|
/gen/schemas
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "typst-leaf-frontend"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
# The `_lib` suffix may seem redundant but it is necessary
|
||||||
|
# to make the lib name unique and wouldn't conflict with the bin name.
|
||||||
|
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
||||||
|
name = "typst_leaf_frontend_lib"
|
||||||
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tauri = { version = "2", features = [] }
|
||||||
|
tauri-plugin-opener = "2"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"$schema": "../gen/schemas/desktop-schema.json",
|
||||||
|
"identifier": "default",
|
||||||
|
"description": "Capability for the main window",
|
||||||
|
"windows": ["main"],
|
||||||
|
"permissions": [
|
||||||
|
"core:default",
|
||||||
|
"opener:default"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 974 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 903 B |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
|
#[tauri::command]
|
||||||
|
fn greet(name: &str) -> String {
|
||||||
|
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
|
pub fn run() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_opener::init())
|
||||||
|
.invoke_handler(tauri::generate_handler![greet])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
typst_leaf_frontend_lib::run()
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
|
"productName": "typst-leaf-frontend",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"identifier": "com.nav.typst-leaf-frontend",
|
||||||
|
"build": {
|
||||||
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
"devUrl": "http://localhost:1420",
|
||||||
|
"beforeBuildCommand": "pnpm build",
|
||||||
|
"frontendDist": "../dist"
|
||||||
|
},
|
||||||
|
"app": {
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"title": "typst-leaf-frontend",
|
||||||
|
"width": 800,
|
||||||
|
"height": 600
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
function App() {
|
||||||
|
return <div className="h-screen w-screen bg-zinc-900 text-zinc-100" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { StrictMode } from "react";
|
||||||
|
import { createRoot } from "react-dom/client";
|
||||||
|
import App from "./App";
|
||||||
|
import "./index.css";
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
);
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
// @ts-expect-error process is a nodejs global
|
||||||
|
const host = process.env.TAURI_DEV_HOST;
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig(async () => ({
|
||||||
|
plugins: [tailwindcss(), react()],
|
||||||
|
|
||||||
|
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||||
|
//
|
||||||
|
// 1. prevent Vite from obscuring rust errors
|
||||||
|
clearScreen: false,
|
||||||
|
// 2. tauri expects a fixed port, fail if that port is not available
|
||||||
|
server: {
|
||||||
|
port: 1420,
|
||||||
|
strictPort: true,
|
||||||
|
host: host || false,
|
||||||
|
hmr: host
|
||||||
|
? {
|
||||||
|
protocol: "ws",
|
||||||
|
host,
|
||||||
|
port: 1421,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
watch: {
|
||||||
|
// 3. tell Vite to ignore watching `src-tauri`
|
||||||
|
ignored: ["**/src-tauri/**"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||