Vibecoded CI/CD
6
frontend/src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Rust's build output — hundreds of MB, never belongs in the wiki.
|
||||
target/
|
||||
|
||||
# Written by tauri-build on every build: the JSON schemas for the permissions
|
||||
# listed under capabilities/. Generated, so regenerable.
|
||||
gen/schemas
|
||||
4912
frontend/src-tauri/Cargo.lock
generated
Normal file
27
frontend/src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "mathew"
|
||||
version = "0.1.0"
|
||||
description = "Mathew — a wiki for mathematics, in a window of its own"
|
||||
authors = ["Aran Roig"]
|
||||
edition = "2021"
|
||||
|
||||
# The app is a library with a thin main: Tauri's own entry points want it that
|
||||
# way, so the same `run()` also serves a mobile build should one ever be asked
|
||||
# for (the window itself is desktop-shaped — see src/lib.rs).
|
||||
[lib]
|
||||
name = "mathew_desktop_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
# A link inside the window hands its document to the system's browser rather
|
||||
# than replacing the wiki in the only view there is. The wiki asks for this
|
||||
# itself (app/utils/desktop.ts); this is the door it goes out by.
|
||||
tauri-plugin-opener = "2"
|
||||
|
||||
# Only a desktop app can be launched twice onto one window.
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-single-instance = "2"
|
||||
78
frontend/src-tauri/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# src-tauri/ — the app window
|
||||
|
||||
Mathew as a **desktop app** (Tauri 2, Linux / Windows / macOS): the wiki in a
|
||||
window of its own, no browser around it. The shell is deliberately thin — the
|
||||
app *is* the same Nuxt bundle the website serves, written out as plain files
|
||||
and embedded in the binary. Nothing in `frontend/app/` is desktop code; the
|
||||
bundle asks at run time whether it happens to be sitting in a window
|
||||
(`app/utils/desktop.ts`) and only then takes the window's habits: a link leads
|
||||
out to the system's browser instead of replacing the wiki in the only view
|
||||
there is, and the session token and interface language are mirrored into
|
||||
`localStorage` because a webview is not everywhere obliged to keep cookies for
|
||||
its own `tauri://localhost` address.
|
||||
|
||||
The wiki needs a wiki to talk to: the window holds no server and no database.
|
||||
It calls the **public Mathew** over HTTPS by default — the API base is baked in
|
||||
at build, see the table below.
|
||||
|
||||
## Build & run
|
||||
|
||||
From `frontend/` — these need [Rust](https://rustup.rs) (stable) and the
|
||||
[Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for your OS;
|
||||
on Linux that is the WebKitGTK stack:
|
||||
`libwebkit2gtk-4.1-dev libgtk-3-dev libssl-dev libxdo-dev librsvg2-dev build-essential`.
|
||||
|
||||
| Script | What it does |
|
||||
| --- | --- |
|
||||
| `npm run desktop:dev` | `tauri dev` — opens the **Nuxt dev server** (`localhost:3000`, started for you) in the window, hot-reload included; `isDesktop()` is already true inside it |
|
||||
| `npm run desktop:build` | `tauri build` — runs `desktop:generate` (below), compiles the shell, embeds the files, and leaves installers under `src-tauri/target/release/bundle/` (deb/rpm/AppImage on Linux, NSIS/MSI on Windows, .app/.dmg on macOS) |
|
||||
| `npm run desktop:generate` | the bundle the window loads: `MATHEW_DESKTOP=1 nuxt generate` → `dist-desktop/public`, the `frontendDist` this config names (a desktop build never overwrites the web `.output/`) |
|
||||
| `npm run desktop:icons` | re-cuts `src-tauri/icons/` from `desktop/icon-source.png` (redraw that one with `python3 desktop/make-icon.py`) |
|
||||
|
||||
The desktop shape lives in `nuxt.config.ts` under one flag
|
||||
(`MATHEW_DESKTOP=1`, also implied by the `TAURI_ENV_*` the Tauri CLI passes to
|
||||
its build hooks): `ssr: false`, no `/api` proxy route, and the pre-paint theme
|
||||
script served from `public/preboot.js` instead of inline. The web build takes
|
||||
none of it. Deep routes (`/wiki/en/bayes-theorem`, …) are not files in the
|
||||
bundle — Tauri's own asset serving answers unknown paths with `index.html`, so
|
||||
the client router takes over exactly as it does on a static host.
|
||||
|
||||
**Dev-server port**: `devUrl` is fixed to `3000`, and Nuxt falls back to `3001`
|
||||
when `3000` is taken (see the main gotchas) — free the port before
|
||||
`desktop:dev`, or the window loads whatever else answers there.
|
||||
|
||||
## Where the app reaches
|
||||
|
||||
| Build variable | Default (desktop) | Meaning |
|
||||
| --- | --- | --- |
|
||||
| `NUXT_PUBLIC_API_BASE` | `https://mathew.aranroig.com` | the wiki the app reads and writes; point it at `http://localhost:5000` to test against this machine |
|
||||
| `NUXT_PUBLIC_LEAN_URL` | empty | the Lean server `/lean` talks to; empty leaves that page saying there is no server here, which is the honest answer on a random laptop. The API's default CORS (`*`) carries the window's `tauri://localhost` origin fine — it sends a bearer header, not cookies |
|
||||
|
||||
Set them in the environment before `npm run desktop:build`.
|
||||
|
||||
## What the shell owns
|
||||
|
||||
- **one window per launch** — `tauri-plugin-single-instance`: pressing the
|
||||
icon again brings the open window forward instead of stacking a second wiki
|
||||
behind it (`src/lib.rs`).
|
||||
- **the way out** — `tauri-plugin-opener` is the door `app/utils/desktop.ts`
|
||||
hands outward links to the system's browser; `capabilities/default.json` is
|
||||
what grants it. That and `core:default` are the whole permission list:
|
||||
reading and writing the wiki is the webview's own business.
|
||||
- **no CSP** (`csp: null` in `tauri.conf.json`): the window loads no foreign
|
||||
script and talks to one API over HTTPS, and `/lean`'s Monaco wants
|
||||
`eval`-like tricks a strict `script-src` would have to excuse. The one
|
||||
script the page writes itself (the pre-paint theme, `public/preboot.js`)
|
||||
already lives in a **file**, so turning a policy on later is a config line —
|
||||
keep it that way if one is ever set.
|
||||
|
||||
Nothing here is signed: bundles come out unsigned, and updating is
|
||||
reinstalling. An updater, signing, and a per-machine "which wiki?" setting are
|
||||
the obvious next steps if this ever ships for real.
|
||||
|
||||
## Versioning
|
||||
|
||||
The app's version lives in `tauri.conf.json` (`version`); it names the
|
||||
installers and the platform bundle metadata. Commit `Cargo.lock` once a build
|
||||
has produced one — for an application binary, a locked dependency set is what
|
||||
makes a rebuild reproducible.
|
||||
3
frontend/src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
7
frontend/src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "What the Mathew window may ask of the machine. Reading and writing the wiki is the webview's own business — plain HTTP out to the API and, if one is configured, a websocket to the Lean server — and needs no permission here. The one thing it cannot do by itself is hand a link to the system's browser, which is what `opener:default` lets app/utils/desktop.ts ask for.",
|
||||
"windows": ["main"],
|
||||
"permissions": ["core:default", "opener:default"]
|
||||
}
|
||||
BIN
frontend/src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
frontend/src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
frontend/src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
frontend/src-tauri/icons/64x64.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
frontend/src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
frontend/src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
frontend/src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
frontend/src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
frontend/src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
frontend/src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
frontend/src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
frontend/src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
frontend/src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
frontend/src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
</adaptive-icon>
|
||||
BIN
frontend/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
BIN
frontend/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
BIN
frontend/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
BIN
frontend/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
BIN
frontend/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
BIN
frontend/src-tauri/icons/icon.icns
Normal file
BIN
frontend/src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
frontend/src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-20x20@1x.png
Normal file
|
After Width: | Height: | Size: 951 B |
BIN
frontend/src-tauri/icons/ios/AppIcon-20x20@2x-1.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-20x20@2x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-20x20@3x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-29x29@1x.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-29x29@2x-1.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-29x29@2x.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-29x29@3x.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-40x40@1x.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-40x40@2x-1.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-40x40@2x.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-40x40@3x.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-512@2x.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-60x60@2x.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-60x60@3x.png
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-76x76@1x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-76x76@2x.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
frontend/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
38
frontend/src-tauri/src/lib.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
/* Mathew as a window.
|
||||
|
||||
The shell is thin on purpose. Everything the app *is* — the open book, the
|
||||
editor, the five interface languages — is the Nuxt bundle it loads, written
|
||||
out by `npm run desktop:generate` and read from disk by the system webview
|
||||
(see src-tauri/README.md). The wiki reaches its own API by itself: no Rust
|
||||
command stands between the two, so an article is fetched the same way in the
|
||||
window as in a browser.
|
||||
|
||||
Two things are the shell's own:
|
||||
|
||||
- `opener`, the door a link goes out by. The wiki asks for it by name
|
||||
(app/utils/desktop.ts); `capabilities/default.json` is what answers.
|
||||
- one window per launch. Pressing the icon again brings the window that is
|
||||
already open to the front instead of stacking a second copy of the wiki
|
||||
behind it. */
|
||||
|
||||
#[cfg(desktop)]
|
||||
use tauri::Manager;
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let mut builder = tauri::Builder::default().plugin(tauri_plugin_opener::init());
|
||||
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _argv, _cwd| {
|
||||
if let Some(window) = app.get_webview_window("main") {
|
||||
let _ = window.unminimize();
|
||||
let _ = window.set_focus();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
builder
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running Mathew");
|
||||
}
|
||||
7
frontend/src-tauri/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
// An app window has no terminal to answer from: keep one from opening behind
|
||||
// it on Windows, where a release build would otherwise start a console.
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
fn main() {
|
||||
mathew_desktop_lib::run()
|
||||
}
|
||||
47
frontend/src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Mathew",
|
||||
"version": "0.1.0",
|
||||
"identifier": "com.aranroig.mathew",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"devUrl": "http://localhost:3000",
|
||||
"beforeBuildCommand": "npm run desktop:generate",
|
||||
"frontendDist": "../dist-desktop/public"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"label": "main",
|
||||
"title": "Mathew",
|
||||
"width": 1440,
|
||||
"height": 900,
|
||||
"minWidth": 940,
|
||||
"minHeight": 600,
|
||||
"center": true,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"publisher": "Aran Roig",
|
||||
"homepage": "https://mathew.aranroig.com",
|
||||
"category": "Education",
|
||||
"shortDescription": "A collaborative wiki of mathematics",
|
||||
"longDescription": "Mathew is a wiki of mathematical concepts: Markdown with LaTeX, an open shelf of articles in several languages, a Markdown editor, a calculator, a graph plotter and a Lean 4 editor. This is the same wiki in an app window of its own; it reads and writes a Mathew wiki over HTTP, so it needs one to reach.",
|
||||
"copyright": "© Aran Roig",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||