feat: Replace cookiecutter with built-in MiniJinja templating

- Add MiniJinja dependency for Jinja2-compatible templating
- Create new project_template.rs module for template rendering
- Bundle snowflake template in templates/snowflake/ directory
- Remove dependency on external cookiecutter Python tool
- Handle Gitea publishing natively in Rust instead of Python hooks
- Update default template path to use bundled template with fallback
- Update README to reflect new built-in templating
- All tests pass

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-09-13 08:37:18 -05:00
parent 981cd853b9
commit caa0001922
41 changed files with 789 additions and 53 deletions
+28 -17
View File
@@ -486,12 +486,16 @@ title page, if you asked for one) and one linked section per chapter; LibreOffic
treat it as a starting point to relink rather than as the assembled book. For a
single finished document, use **Export ODT**.
## New projects from a cookiecutter template
## New projects from templates
**File ▸ ✨ New project…** scaffolds a whole manuscript project from a
[cookiecutter](https://cookiecutter.readthedocs.io/) template and then opens its
drafting folder as the workspace, so you go from nothing to editing chapters in
one step.
template and then opens its drafting folder as the workspace, so you go from
nothing to editing chapters in one step.
The app now uses **built-in MiniJinja templating** (no external Python
cookiecutter required) to render templates. A default "snowflake" template is
bundled in the `templates/snowflake/` folder of the project, which you can modify
by hand to customise the project structure or file contents.
The dialog asks for a **name**, **author**, **description** and the **folder to
create it in** (prefilled with wherever you put the last one). Those are passed
@@ -510,27 +514,34 @@ A folder only appears in the file panel once it holds a `.md` file — the
template's empty acts are held open with `.gitkeep`, which is not markdown, so
they show up as you add scenes to them.
You can use your own templates by pointing the **Template** setting in
**Settings ▸ New project…** at any folder containing a `cookiecutter.json` file.
The template should contain a subdirectory named with a template expression
(e.g., `{{ cookiecutter.project_name }}`) which becomes the project root after
rendering.
### Settings ▸ New project…
| Setting | Meaning |
|---|---|
| **Template** | the folder holding the template's `cookiecutter.json`. Defaults to `~/Documents/Cookiecutters/snowflake`; the dialog confirms whether a `cookiecutter.json` is actually there |
| **Template** | the folder holding the template's `cookiecutter.json`. Defaults to the bundled `templates/snowflake`; falls back to `~/Documents/Cookiecutters/snowflake` |
| **Open subfolder** | which folder of the generated project becomes the workspace (default `06-First Draft`; blank opens the project root) |
| **cookiecutter path** | leave blank to search `PATH` and then the usual per-user Python prefixes (`~/.anaconda3/bin`, `~/miniconda3/bin`, `~/.local/bin`, …), which is what a desktop launcher needs since it does not inherit a conda `PATH`. The dialog shows which executable was found |
| **Run the template's hooks** | whether to run the template's pre/post-generation scripts (on by default) |
| **Gitea** | server URL, user and token, handed to the hooks as `GITEA_URL` / `GITEA_USER` / `GITEA_TOKEN` |
| **Publish to Gitea after creation** | when enabled and Gitea credentials are configured below, a new repository will be created and the project pushed to it |
| **Gitea** | server URL, user and token, used to create repositories when publishing is enabled |
Cookiecutter is a Python program and is **not bundled** — install it with
`pip install cookiecutter` or `conda install cookiecutter` if the settings window
reports it missing.
No external tools are required — templating is built into the application.
### Hooks and publishing
### Publishing to Gitea
The snowflake template's `hooks/post_gen_project.py` creates a repository on a
Gitea server and pushes the new project to it. It only does that when all three
Gitea settings are filled in; with any of them blank it prints a note and skips,
and **the project is still generated either way**. The token is passed to the
push command alone and is not written into the project's `.git/config`.
When **Publish to Gitea after creation** is enabled and all three Gitea settings
(**Server URL**, **User**, **Token**) are filled in, the app will:
1. Initialize a git repository in the new project
2. Create a repository on the Gitea server
3. Set the `origin` remote to point at the new repository
The token is passed to the push command temporarily and is not written into the
project's `.git/config`. With any setting blank, publishing is skipped but
the project is still generated locally.
The token is stored in `config.json` in plain text, the same as the Mistral API
key.