Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
57a409b
chore: migrate project and CI to UniverLab org
JheisonMB Apr 7, 2026
72625dc
feat(config,version,placeholders): implement Phase 1 core systems
JheisonMB Apr 7, 2026
701cf0d
refactor: fix clippy warnings and doc comments
JheisonMB Apr 7, 2026
86d19cd
chore: remove unnecessary files
JheisonMB Apr 7, 2026
9d1402b
chore: bump version to 0.6.0
JheisonMB Apr 7, 2026
1749bc9
fix: remove absurd comparison in version_checker test
JheisonMB Apr 7, 2026
ff0fc38
fix: make version test compatible with 0.x.x versions
JheisonMB Apr 7, 2026
9d375b3
feat: add manual trigger to release workflow
JheisonMB Apr 8, 2026
33eae72
fix: remove invalid --allow-dirty flag from cargo clippy
JheisonMB Apr 8, 2026
48f2d22
fix: correct tar and powershell commands in release workflow
JheisonMB Apr 8, 2026
b5f2fb5
feat: add interactive wizard to config command
JheisonMB Apr 8, 2026
cfcd632
feat: add banner to config wizard
JheisonMB Apr 8, 2026
2d4a22e
feat: refactor config to simple key-based interface with wizard
JheisonMB Apr 8, 2026
4fd374d
feat: add language selector as autocomplete dropdown
JheisonMB Apr 8, 2026
d31729f
fix: set email default to email@domain.com in config wizard
JheisonMB Apr 8, 2026
11212c2
fix: load system fonts for SVG to PNG rendering
JheisonMB Apr 8, 2026
f51a8f7
fix: load system and platform fonts for diagram text rendering
JheisonMB Apr 8, 2026
2e0f292
docs(readme): improve README
JheisonMB Apr 8, 2026
6bcbb3f
ci: fix tar command in release workflow
JheisonMB Apr 8, 2026
f2e4081
Merge pull request #13 from UniverLab/migrate/univerlab
JheisonMB Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 56 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- closed
paths:
- Cargo.toml
workflow_dispatch:

permissions:
contents: write
Expand All @@ -18,7 +19,7 @@ env:
jobs:
create-tag:
name: Create Release Tag
if: github.event.pull_request.merged == true
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
Expand All @@ -32,7 +33,7 @@ jobs:
- name: Get version from Cargo.toml
id: version
run: |
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/' | tr -d '\r')
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/' | tr -d '\r' | xargs)
TAG="v${VERSION}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -107,15 +108,15 @@ jobs:
if: matrix.archive == 'tar.gz'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../texforge-${{ needs.create-tag.outputs.tag }}-${{ matrix.target }}.tar.gz texforge
tar czf "../../../texforge-${{ needs.create-tag.outputs.tag }}-${{ matrix.target }}.tar.gz" texforge
cd ../../..

- name: Package (windows)
if: matrix.archive == 'zip'
shell: pwsh
run: |
cd target/${{ matrix.target }}/release
Compress-Archive -Path texforge.exe -DestinationPath ../../../texforge-${{ needs.create-tag.outputs.tag }}-${{ matrix.target }}.zip
Compress-Archive -Path texforge.exe -DestinationPath "../../../texforge-${{ needs.create-tag.outputs.tag }}-${{ matrix.target }}.zip"
cd ../../..

- name: Upload artifact
Expand Down Expand Up @@ -154,9 +155,39 @@ jobs:
- name: Approval granted
run: echo "Release approved for publishing to crates.io"

ensure-owners:
name: Ensure crates.io owners
needs: create-tag
if: needs.create-tag.outputs.created == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.create-tag.outputs.tag }}

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Add crates.io owners (best-effort)
env:
TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
OWNERS: github:univerlab:owners
run: |
set -eu
if [ -z "$TOKEN" ]; then
echo "No cargo token found in CARGO_REGISTRY_TOKEN; skipping owners update"
exit 0
fi
CRATE_NAME=$(grep '^name = ' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/' | tr -d '\r')
echo "crate=$CRATE_NAME"
for owner in $(echo "$OWNERS" | tr ',' ' '); do
echo "Adding owner: $owner"
cargo owner --token "$TOKEN" --add "$owner" || echo "cargo owner failed for $owner (continuing)"
done

publish:
name: Publish to crates.io
needs: [create-tag, approve]
needs: [create-tag, approve, ensure-owners]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -166,5 +197,24 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Check if stable release
id: version_check
run: |
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/' | xargs)
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_stable=true" >> $GITHUB_OUTPUT
echo "Version $VERSION is stable — will publish"
else
echo "is_stable=false" >> $GITHUB_OUTPUT
echo "Version $VERSION is prerelease/dev — skipping publish"
fi

- name: Cargo publish
run: cargo publish --token ${{ secrets.CARGO_TOKEN }}
if: steps.version_check.outputs.is_stable == 'true'
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --allow-dirty

- name: Skipped prerelease
if: steps.version_check.outputs.is_stable == 'false'
run: echo "⏭️ Skipped crates.io publish for prerelease version"
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ skills-lock.json
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# texforge is a binary — Cargo.lock is committed intentionally
# Cargo.lock is committed for binaries (not for libraries)
# Remove this line if creating a library

# These are backup files generated by rustfmt
**/*.rs.bk
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "texforge"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
rust-version = "1.75"
description = "Self-contained LaTeX to PDF compiler CLI"
license = "MIT"
repository = "https://github.com/jheisonmb/texforge"
repository = "https://github.com/UniverLab/texforge"
keywords = ["latex", "pdf", "compiler", "cli"]
categories = ["command-line-utilities", "development-tools"]

Expand Down
96 changes: 84 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,31 @@
░░░░░░
```

[![CI](https://github.com/JheisonMB/texforge/actions/workflows/ci.yml/badge.svg)](https://github.com/JheisonMB/texforge/actions/workflows/ci.yml)
[![Release](https://github.com/JheisonMB/texforge/actions/workflows/release.yml/badge.svg)](https://github.com/JheisonMB/texforge/actions/workflows/release.yml)
[![CI](https://github.com/UniverLab/texforge/actions/workflows/ci.yml/badge.svg)](https://github.com/UniverLab/texforge/actions/workflows/ci.yml)
[![Release](https://github.com/UniverLab/texforge/actions/workflows/release.yml/badge.svg)](https://github.com/UniverLab/texforge/actions/workflows/release.yml)
[![Crates.io](https://img.shields.io/crates/v/texforge)](https://crates.io/crates/texforge)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Self-contained LaTeX to PDF compiler — one curl, zero friction. No TeX Live, no MiKTeX, no Perl, no Node. A single install sets up everything you need.
Texforge is a unified LaTeX workspace — one tool for writing, rendering diagrams (Mermaid, Graphviz), and building PDFs. Set it up once and stay focused on your document.

---

### Demo CLI

![Demo CLI](assets/texforge.gif)

---

## Features

- **🚀 One-command setup** — Install once, everything is included (LaTeX engine, templates, diagram renderers).
- **📊 Diagrams as first-class** — Write Mermaid or Graphviz blocks in your `.tex` files; they render and embed during build.
- **🪄 Guided workflows** — Start a new project or migrate an existing one with a guided init.
- **🔎 Template registry** — Install, manage, and validate templates — with built-in fallback for offline work.
- **🔨 Build and live edit** — Compile once or use watch mode; rebuild automatically as you edit.
- **🧭 Smart linting** — Catch missing files, broken references, bibliography keys, and unclosed environments before build.
- **✨ Format on demand** — Normalize `.tex` files with an opinionated formatter (including `--check` mode).
- **🔄 Placeholders and config** — Reuse project details from configuration without retyping.

---

Expand All @@ -28,13 +47,13 @@ Self-contained LaTeX to PDF compiler — one curl, zero friction. No TeX Live, n
**Linux / macOS:**

```bash
curl -fsSL https://raw.githubusercontent.com/JheisonMB/texforge/main/install.sh | sh
curl -fsSL https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.sh | sh
```

**Windows (PowerShell):**

```powershell
irm https://raw.githubusercontent.com/JheisonMB/texforge/main/install.ps1 | iex
irm https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.ps1 | iex
```

This downloads and installs `texforge`. No Rust toolchain required. Tectonic (the LaTeX engine) is installed automatically on first build.
Expand All @@ -43,15 +62,15 @@ You can customize the install:

```bash
# Pin a specific version
VERSION=0.1.0 curl -fsSL https://raw.githubusercontent.com/JheisonMB/texforge/main/install.sh | sh
VERSION=0.1.0 curl -fsSL https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.sh | sh

# Install to a custom directory
INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/JheisonMB/texforge/main/install.sh | sh
INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.sh | sh
```

```powershell
# Pin a specific version (PowerShell)
$env:VERSION="0.1.0"; irm https://raw.githubusercontent.com/JheisonMB/texforge/main/install.ps1 | iex
$env:VERSION="0.1.0"; irm https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.ps1 | iex
```

### Via cargo
Expand All @@ -67,15 +86,15 @@ Available on [crates.io](https://crates.io/crates/texforge).
### From source

```bash
git clone https://github.com/JheisonMB/texforge.git
git clone https://github.com/UniverLab/texforge.git
cd texforge
cargo build --release
# Binary at target/release/texforge
```

### GitHub Releases

Check the [Releases](https://github.com/JheisonMB/texforge/releases) page for precompiled binaries (Linux x86_64, macOS x86_64/ARM64, Windows x86_64).
Check the [Releases](https://github.com/UniverLab/texforge/releases) page for precompiled binaries (Linux x86_64, macOS x86_64/ARM64, Windows x86_64).

### Uninstall

Expand All @@ -86,12 +105,16 @@ rm -rf ~/.texforge/ # tectonic engine + cached templates

## Skill

An [texforge Skill](https://skills.sh/jheisonmb/skills/texforge) is available for AI-assisted LaTeX workflows with texforge:
If you want Copilot to understand texforge and help with common LaTeX tasks, install the [texforge Skill](https://skills.sh/jheisonmb/skills/texforge):

```bash
npx skills add https://github.com/jheisonmb/skills --skill texforge
```

### Demo with OpenCode agents

![Demo OpenCode](assets/opencode.gif)

## Quick Start

```bash
Expand Down Expand Up @@ -164,6 +187,10 @@ texforge init
| `texforge fmt` | Format .tex files |
| `texforge fmt --check` | Check formatting without modifying |
| `texforge check` | Lint without compiling |
| `texforge config` | Interactive wizard to set user details (name, email, institution, language) |
| `texforge config list` | Show all configured values |
| `texforge config <key>` | Show value for key (name, email, institution, language) |
| `texforge config <key> <value>` | Set value for key |
| `texforge template list` | List installed templates |
| `texforge template list --all` | List installed + available in registry |
| `texforge template add <name>` | Download template from registry |
Expand All @@ -172,9 +199,43 @@ texforge init

---

## Configuration

Global user details stored in `~/.texforge/config.toml`. These are used as replaceable placeholders in templates.

**Interactive setup:**

```bash
texforge config
```

This launches a wizard asking for:
- **Name**: Your full name
- **Email**: Your email address
- **Institution**: Your institution/organization
- **Language**: Document language (default: `english`)

**Command-line interface:**

```bash
# View all settings
texforge config list

# Get a specific value
texforge config name

# Set a value
texforge config name "Jheison Martinez"
texforge config email "jheison@example.com"
texforge config institution "University of Tech"
texforge config language "spanish"
```

---

## Templates

Templates are managed through the [texforge-templates](https://github.com/JheisonMB/texforge-templates) registry. The `general` template is embedded in the binary and works offline. Run `texforge template list --all` to see all available templates.
Templates are managed through the [texforge-templates](https://github.com/UniverLab/texforge-templates) registry. The `general` template is embedded in the binary and works offline. Run `texforge template list --all` to see all available templates.

---

Expand Down Expand Up @@ -319,3 +380,14 @@ texforge fmt --check # check without modifying (CI-friendly)
## License

MIT

---
## Support

- 📖 [GitHub Issues](https://github.com/UniverLab/texforge/issues) — Report bugs or request features
- 💬 [Discussions](https://github.com/UniverLab/texforge/discussions) — Ask questions
- 🐦 Twitter: [@JheisonMB](https://twitter.com/JheisonMB)

---

Made with ❤️ by [JheisonMB](https://github.com/JheisonMB) and [UniverLab](https://github.com/UniverLab)
Binary file added assets/opencode.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/texforge.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 20 additions & 14 deletions install.ps1 → scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# install.ps1 — download and install texforge on Windows
# tectonic (LaTeX engine) is installed automatically on first build
# Usage: irm https://raw.githubusercontent.com/JheisonMB/texforge/main/install.ps1 | iex
# Usage: irm https://raw.githubusercontent.com/UniverLab/texforge/main/scripts/install.ps1 | iex
#
# Options (set as env vars before running):
# $env:VERSION = "0.1.0" # pin a specific version
# $env:INSTALL_DIR = "C:\my\bin" # custom install directory
# $env:INSTALL_DIR = "C:\\my\\bin" # custom install directory

$ErrorActionPreference = "Stop"

$Repo = "JheisonMB/texforge"
$Repo = "UniverLab/texforge"
$Binary = "texforge.exe"
$Target = "x86_64-pc-windows-msvc"
$InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { "$env:USERPROFILE\.local\bin" }
Expand All @@ -29,10 +28,18 @@ if ($env:VERSION) {
$Tag = "v$($env:VERSION)"
Info "version" "$Tag (pinned)"
} else {
$latest = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
$Tag = $latest.tag_name
if (-not $Tag) { Fail "Could not resolve latest release tag" }
Info "version" "$Tag (latest)"
# Get latest stable release (exclude prerelease)
$releases = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases"
$stable = $releases | Where-Object { -not $_.prerelease } | Select-Object -First 1
if ($stable) {
$Tag = $stable.tag_name
} else {
# Fallback to latest if no stable found
$latest = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
$Tag = $latest.tag_name
}
if (-not $Tag) { Fail "Could not resolve latest stable release" }
Info "version" "$Tag (latest stable)"
}

# --- download ---
Expand All @@ -50,13 +57,13 @@ try {

# --- extract ---
Expand-Archive -Path "$Tmp\$Archive" -DestinationPath $Tmp -Force
$extracted = Join-Path $Tmp $Binary
$extracted = Join-Path $Tmp "texforge.exe"
if (-not (Test-Path $extracted)) { Fail "Binary not found in archive" }

# --- install ---
New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null
Copy-Item $extracted "$InstallDir\$Binary" -Force
Info "installed" "$InstallDir\$Binary"
Copy-Item $extracted "$InstallDir\texforge.exe" -Force
Info "installed" "$InstallDir\texforge.exe"

# --- ensure PATH ---
$userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
Expand All @@ -70,8 +77,7 @@ if ($userPath -notlike "*$InstallDir*") {
Remove-Item $Tmp -Recurse -Force

# --- verify ---
$ver = & "$InstallDir\$Binary" --version 2>$null
$ver = & "$InstallDir\texforge.exe" --version 2>$null
Info "done" $ver
Write-Host ""
Info "ready" "Run 'texforge new my-project' to get started!"
Info "note" "tectonic (LaTeX engine) will be installed automatically on first build"
Info "ready" "Run 'texforge --help' to get started!"
Loading
Loading