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/**"],
|
||||
},
|
||||
},
|
||||
}));
|
||||