diff --git a/.agents/skills/mytoolkit/references/latex.md b/.agents/skills/mytoolkit/references/latex.md
index b7ed9e4..3037d45 100644
--- a/.agents/skills/mytoolkit/references/latex.md
+++ b/.agents/skills/mytoolkit/references/latex.md
@@ -8,15 +8,16 @@ mytoolkit latex compile paper.tex -e xelatex
mytoolkit latex count paper.tex
```
-## 论文脚手架 / 投稿模板
+## 论文/手册脚手架 · 投稿模板
```bash
-mytoolkit init paper
# 新建论文项目(elsarticle 脚手架,xelatex,可移植;精确配置见生成的 main.tex 头部注释,设计约束见仓库 AGENTS.md §3)
+mytoolkit init paper # 新建论文项目(elsarticle 脚手架,xelatex,可移植;精确配置见生成的 main.tex 头部注释,设计约束见仓库 AGENTS.md §4)
+mytoolkit init manual # 新建手册项目(技术与使用文档;与 md-to-pdf/manual Typst 模板同一视觉风格,ctexart + xelatex)
mytoolkit init journal # 列出投稿套件
mytoolkit init journal elsevier # Elsevier / ieee / springer 套件 → ./submit-/
```
-投稿套件自带文档类与 bst,`make`(pdflatex+bibtex)直接编译;自动复用当前目录 `references.bib`。Springer 切样式只改 `\documentclass[...,sn-xxx]{sn-jnl}` 选项,勿写 `\bibliographystyle`。详见 AGENTS.md §3。
+投稿套件自带文档类与 bst,`make`(pdflatex+bibtex)直接编译;自动复用当前目录 `references.bib`。Springer 切样式只改 `\documentclass[...,sn-xxx]{sn-jnl}` 选项,勿写 `\bibliographystyle`。详见 AGENTS.md §4。
### PDF 命名约定
diff --git a/.agents/skills/mytoolkit/references/others.md b/.agents/skills/mytoolkit/references/others.md
index 355237c..94de5d2 100644
--- a/.agents/skills/mytoolkit/references/others.md
+++ b/.agents/skills/mytoolkit/references/others.md
@@ -4,6 +4,7 @@
```bash
mytoolkit init paper # 初始化 LaTeX 论文项目
+mytoolkit init manual # 初始化 LaTeX 手册项目(技术与使用文档)
mytoolkit env # 环境变量管理
mytoolkit git # Git 辅助工具
mytoolkit ssh # SSH 快捷连接
diff --git a/AGENTS.md b/AGENTS.md
index d8b3f4f..64650cc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -79,7 +79,9 @@ mytoolkit voice tts "文本" -v zh_male_wennuanahu_moon_bigtts --format mp3 --sp
---
-## 4. LaTeX 论文脚手架与投稿模板
+## 4. LaTeX 脚手架与投稿模板
+
+模板三分法(按用途,勿混放):`templates/`(convert 转换模板,md 为源)/ `scaffolds/`(init 项目脚手架,LaTeX 源工程)/ `journals/`(init journal 投稿套件,自包含)。
### 新建论文项目
@@ -94,6 +96,14 @@ mytoolkit init paper # 默认 Elsevier elsarticle 模板(全平
- 统一 **xelatex** 一个引擎(latexmk 驱动),便于后续加中文/系统字体;
- `make` 编译、`make highlighted OLDTEX=...` 出 latexdiff 修订稿;`diffpreamble.dtx` 标记增删。
+### 新建手册项目(技术与使用文档)
+
+```bash
+mytoolkit init manual # LaTeX 手册模板(ctexart,xelatex)
+```
+
+与 Typst `md-to-pdf/manual` 模板同一视觉风格(封面 + 目录 + 彩色标题 + 四色提示框 + 代码高亮 + 三线表),**配色与版式改动须两个模板同步**。生成 `main.tex`/`Makefile`/`README.md`;字体走 ctex 自动字体集,不写死系统字体。
+
### 切换投稿期刊模板
各出版社“投稿套件”预置在包内 `mytoolkit/journals/`,一条命令落地到子目录,自包含、`make` 直接编译(pdflatex + bibtex):
diff --git a/README.md b/README.md
index 99075ce..9551b98 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ mytoolkit --help
| Command | What it does |
|---------|--------------|
| `convert` | Universal format conversion (md/img/pdf/eps/avi/bib → pdf/docx/tiff/mp4/md). Auto-detects from extensions. |
-| `init` | Initialize projects/kits from bundled templates (`mytoolkit init paper `, `mytoolkit init journal `) |
+| `init` | Initialize projects/kits from bundled templates (`mytoolkit init paper|manual `, `mytoolkit init journal `) |
| `info` | Show bundled resource paths (`info paths`, `info docs `) |
| `pdf` | `compress` / `crop` / `merge` / `bookmark` / `to-tiff` |
| `image` | `compress` / `eps-fix` / `eps-to-pdf` / `jpg-to-pdf` / `tiff-to-pdf` / `tiff-compress` / `generate` (Doubao/Seedream) |
@@ -92,9 +92,11 @@ Registry is stored at `~/.mytoolkit/templates.json` (or `$MYTOOLKIT_HOME/templat
```bash
mytoolkit init paper # create a LaTeX paper project (Elsevier elsarticle, xelatex, fully portable)
+mytoolkit init manual # create a LaTeX manual project (technical/user docs, same style as md-to-pdf/manual)
```
-This generates `main.tex`, `Makefile`, `references.bib`, `diffpreamble.dtx`, `figs/`.
+`init paper` generates `main.tex`, `Makefile`, `references.bib`, `diffpreamble.dtx`, `figs/`.
+`init manual` generates `main.tex` (cover + TOC + callout boxes + code listing + three-line tables), `Makefile`, `README.md`.
### Journal submission kits
diff --git a/mytoolkit/commands/init.py b/mytoolkit/commands/init.py
index adfd8b3..a5acc96 100644
--- a/mytoolkit/commands/init.py
+++ b/mytoolkit/commands/init.py
@@ -39,14 +39,11 @@ def init_cmd():
pass
-@init_cmd.command("paper")
-@click.argument("target", type=click.Path(file_okay=False, dir_okay=True, path_type=Path))
-@handle_errors
-def init_paper(target: Path):
- """Initialize a LaTeX paper project in TARGET directory."""
- with _scaffold_path("paper") as scaffold:
+def _init_scaffold(name: str, target: Path) -> None:
+ """Copy the bundled scaffold NAME into TARGET (must be empty or absent)."""
+ with _scaffold_path(name) as scaffold:
if not scaffold.is_dir():
- raise RuntimeError(f"Paper scaffold not found: {scaffold}")
+ raise RuntimeError(f"Scaffold not found: {scaffold}")
if target.exists():
if any(target.iterdir()):
@@ -58,7 +55,7 @@ def init_paper(target: Path):
created = sorted(p.relative_to(target) for p in target.rglob("*") if p.is_file())
- click.secho(f"Initialized paper project at {target}", fg="green")
+ click.secho(f"Initialized {name} project at {target}", fg="green")
for path in created:
click.echo(f" {path}")
@@ -67,6 +64,22 @@ def init_paper(target: Path):
click.echo(" make")
+@init_cmd.command("paper")
+@click.argument("target", type=click.Path(file_okay=False, dir_okay=True, path_type=Path))
+@handle_errors
+def init_paper(target: Path):
+ """Initialize a LaTeX paper project in TARGET directory."""
+ _init_scaffold("paper", target)
+
+
+@init_cmd.command("manual")
+@click.argument("target", type=click.Path(file_okay=False, dir_okay=True, path_type=Path))
+@handle_errors
+def init_manual(target: Path):
+ """Initialize a LaTeX manual (technical/user doc) project in TARGET."""
+ _init_scaffold("manual", target)
+
+
@init_cmd.command("journal")
@click.argument("name", type=click.Choice(sorted(JOURNALS)), required=False)
@click.argument("target", type=click.Path(file_okay=False, dir_okay=True, path_type=Path), required=False)
diff --git a/mytoolkit/scaffolds/manual/Makefile b/mytoolkit/scaffolds/manual/Makefile
new file mode 100644
index 0000000..8c50bad
--- /dev/null
+++ b/mytoolkit/scaffolds/manual/Makefile
@@ -0,0 +1,54 @@
+.PHONY: all clean show-errors show-log watch help
+
+TEXFILE ?= $(if $(wildcard main.tex),main.tex,$(firstword $(filter-out highlighted.tex old.tex,$(wildcard *.tex))))
+PROJECT = $(basename $(TEXFILE))
+LATEXMK = latexmk
+OUTDIR = build
+MAKELOG = $(OUTDIR)/make.log
+TEXLOG = $(OUTDIR)/$(PROJECT).log
+ARTIFACTS = $(PROJECT).aux $(PROJECT).bbl $(PROJECT).blg $(PROJECT).log $(PROJECT).out \
+ $(PROJECT).toc $(PROJECT).synctex.gz $(PROJECT).fls $(PROJECT).fdb_latexmk $(PROJECT).xdv $(PROJECT).spl
+
+all:
+ @mkdir -p $(OUTDIR)
+ @echo "Compiling $(PROJECT).tex (xelatex, full log: $(MAKELOG))..."
+ @$(LATEXMK) -xelatex -outdir=$(OUTDIR) -interaction=nonstopmode $(PROJECT).tex < /dev/null > $(MAKELOG) 2>&1; \
+ status=$$?; \
+ if [ $$status -ne 0 ]; then \
+ echo "Build failed."; \
+ $(MAKE) -s show-errors; \
+ echo "Full log: $(MAKELOG)"; \
+ exit $$status; \
+ fi
+ @cp $(OUTDIR)/$(PROJECT).pdf ./$(PROJECT).pdf
+ @rm -f $(ARTIFACTS)
+ @echo "Done: $(PROJECT).pdf"
+
+show-errors:
+ @if [ -f $(TEXLOG) ]; then \
+ grep -E -A2 '^! |^l\.[0-9]+ |LaTeX Error|Undefined control sequence|Missing \$$ inserted|Package .* Error|Emergency stop|Fatal error' \
+ $(TEXLOG) 2>/dev/null \
+ || tail -30 $(MAKELOG); \
+ else \
+ tail -30 $(MAKELOG); \
+ fi
+
+show-log:
+ @less $(MAKELOG)
+
+clean:
+ $(LATEXMK) -C -outdir=$(OUTDIR) $(PROJECT).tex
+ rm -rf $(OUTDIR)
+ rm -f $(ARTIFACTS)
+
+watch:
+ $(LATEXMK) -pvc -xelatex -outdir=$(OUTDIR) -interaction=nonstopmode $(PROJECT).tex
+
+help:
+ @echo
+ @echo '1. "make" compile (latexmk, output in build/, PDF copied to root; on error prints the failing lines and stops)'
+ @echo '2. "make show-errors" re-print the error lines from the last build'
+ @echo '3. "make show-log" page through the full build log'
+ @echo '4. "make watch" continuous preview-compile (latexmk -pvc)'
+ @echo '5. "make clean" remove build/ and stray artifacts'
+ @echo
diff --git a/mytoolkit/scaffolds/manual/README.md b/mytoolkit/scaffolds/manual/README.md
new file mode 100644
index 0000000..bc942c3
--- /dev/null
+++ b/mytoolkit/scaffolds/manual/README.md
@@ -0,0 +1,38 @@
+# Manual Project
+
+This project was initialized with `mytoolkit init manual`. It is a LaTeX
+manual template (technical & user documentation) with the same visual style as
+the Typst `md-to-pdf/manual` template: cover page, TOC, colored headings,
+callout boxes, code listings, and three-line tables. Compiles with `xelatex`
+via `latexmk`; CJK fonts are resolved by `ctex` automatically.
+
+## Directory Structure
+
+- `main.tex`: main document (cover, styles, demo content — replace with yours)
+- `Makefile`: build script
+- `build/`: build output (auto-generated)
+
+## Build
+
+```bash
+make
+```
+
+A successful build produces `main.pdf`.
+
+## Callout Boxes
+
+Four colored boxes are predefined (optional argument overrides the title):
+
+```latex
+\begin{infobox}[自定义标题] ... \end{infobox}
+\begin{tipbox} ... \end{tipbox}
+\begin{warnbox} ... \end{warnbox}
+\begin{dangerbox} ... \end{dangerbox}
+```
+
+## Other Commands
+
+- `make clean`: remove build artifacts
+- `make watch`: continuous preview compilation
+- `make show-errors`: show the most recent build errors
diff --git a/mytoolkit/scaffolds/manual/main.tex b/mytoolkit/scaffolds/manual/main.tex
new file mode 100644
index 0000000..989934e
--- /dev/null
+++ b/mytoolkit/scaffolds/manual/main.tex
@@ -0,0 +1,227 @@
+%% Manual template (technical & user documentation).
+%% Same visual style as the Typst md-to-pdf/manual template:
+%% cover page + TOC + colored headings + callout boxes + code listing + three-line tables.
+%% Engine: xelatex (via latexmk); CJK fonts resolved by ctex automatically (portable).
+\documentclass[UTF8,10pt]{ctexart}
+
+\usepackage[a4paper,margin=2cm]{geometry}
+\usepackage{xcolor}
+\usepackage{graphicx}
+\usepackage{booktabs}
+\usepackage{colortbl}
+\usepackage{listings}
+\usepackage[most]{tcolorbox}
+\usepackage{titlesec}
+\usepackage{fancyhdr}
+\usepackage{enumitem}
+\usepackage{hyperref}
+
+% ---- palette (keep in sync with templates/md-to-pdf/manual/template.typ) ----
+\definecolor{primary}{HTML}{1F4E79}
+\definecolor{secondary}{HTML}{2C3E50}
+\definecolor{accent}{HTML}{3467A8}
+\definecolor{light}{HTML}{96A0AA}
+\colorlet{ruleblue}{accent!46!white}
+
+\definecolor{infocolor}{HTML}{0EA5E9}
+\definecolor{tipcolor}{HTML}{10B981}
+\definecolor{warncolor}{HTML}{F59E0B}
+\definecolor{dangercolor}{HTML}{EF4444}
+
+\definecolor{codebg}{HTML}{F8F9FA}
+\definecolor{codenum}{HTML}{6E7681}
+
+\hypersetup{colorlinks=true, linkcolor=accent, urlcolor=accent, citecolor=accent,
+ bookmarksopen=true, bookmarksopenlevel=2}
+
+% ---- headings (numbered; add \setcounter{secnumdepth}{-2} to go unnumbered) ----
+\setcounter{tocdepth}{2}
+\titleformat{\section}{\fontsize{18}{22}\selectfont\bfseries\color{primary}}
+ {\thesection}{0.6em}{}
+\titlespacing*{\section}{0pt}{1.45em plus 4pt}{0.55em}
+\titleformat{\subsection}{\fontsize{13}{16}\selectfont\bfseries\color{accent}}
+ {\thesubsection}{0.6em}{}
+\titlespacing*{\subsection}{0pt}{1.1em plus 3pt}{0.5em}
+\titleformat{\subsubsection}{\fontsize{11}{14}\selectfont\bfseries\color{secondary}}
+ {\thesubsubsection}{0.6em}{}
+\titlespacing*{\subsubsection}{0pt}{0.8em plus 2pt}{0.4em}
+
+% ---- header: current section (right); footer: page number (center) ----
+\pagestyle{fancy}
+\fancyhf{}
+\setlength{\headheight}{14pt}
+\renewcommand{\headrulewidth}{0pt}
+\renewcommand{\sectionmark}[1]{\markright{#1}}
+\fancyhead[R]{\fontsize{8}{10}\selectfont\color{light}\rightmark}
+\fancyfoot[C]{\fontsize{8}{10}\selectfont\color{light}--- \thepage\ ---}
+
+% ---- compact lists ----
+\setlist{nosep, leftmargin=1.8em}
+
+% ---- three-line tables: \toprule / \midrule / \bottomrule ----
+\arrayrulecolor{ruleblue}
+
+% ---- code listing (rounded corners need tcolorbox; plain frame keeps it light) ----
+\lstset{
+ basicstyle=\ttfamily\small,
+ backgroundcolor=\color{codebg},
+ frame=single, framerule=0.5pt, rulecolor=\color{ruleblue},
+ framesep=3pt, xleftmargin=2em,
+ numbers=left, numberstyle=\tiny\color{codenum}, numbersep=8pt,
+ breaklines=true, columns=fullflexible, keepspaces=true,
+ showstringspaces=false,
+ keywordstyle=\color{accent}\bfseries,
+ commentstyle=\color{light}\itshape,
+ stringstyle=\color{tipcolor},
+}
+
+% ---- callout boxes: title bar colored, body white (mirrors Typst showybox) ----
+\newtcolorbox{infobox}[1][信息]{enhanced, breakable,
+ colback=white, colframe=infocolor, colbacktitle=infocolor, coltitle=white,
+ fonttitle=\bfseries, title={#1}, boxrule=1pt, arc=2mm,
+ left=2mm, right=2mm, top=1.5mm, bottom=1.5mm}
+\newtcolorbox{tipbox}[1][提示]{enhanced, breakable,
+ colback=white, colframe=tipcolor, colbacktitle=tipcolor, coltitle=white,
+ fonttitle=\bfseries, title={#1}, boxrule=1pt, arc=2mm,
+ left=2mm, right=2mm, top=1.5mm, bottom=1.5mm}
+\newtcolorbox{warnbox}[1][注意]{enhanced, breakable,
+ colback=white, colframe=warncolor, colbacktitle=warncolor, coltitle=white,
+ fonttitle=\bfseries, title={#1}, boxrule=1pt, arc=2mm,
+ left=2mm, right=2mm, top=1.5mm, bottom=1.5mm}
+\newtcolorbox{dangerbox}[1][危险]{enhanced, breakable,
+ colback=white, colframe=dangercolor, colbacktitle=dangercolor, coltitle=white,
+ fonttitle=\bfseries, title={#1}, boxrule=1pt, arc=2mm,
+ left=2mm, right=2mm, top=1.5mm, bottom=1.5mm}
+
+\begin{document}
+
+% ---- cover page ----
+\begin{titlepage}
+ \centering
+ \vspace*{2cm}
+ {\fontsize{28}{34}\selectfont\bfseries\color{primary} Phynexis Python API 参考手册\par}
+ \vspace{0.8cm}
+ {\fontsize{16}{20}\selectfont\color{accent} 技术与使用文档\par}
+ \vspace{2cm}
+ {\fontsize{12}{16}\selectfont\color{light} 2026 年 7 月\par}
+ \vfill
+\end{titlepage}
+
+\tableofcontents
+\newpage
+
+\section{快速开始}
+
+\subsection{安装}
+
+通过 pip 安装 Phynexis:
+
+\begin{lstlisting}[language=bash]
+pip install phynexis
+\end{lstlisting}
+
+\begin{tipbox}[提示]
+建议使用虚拟环境安装,避免与系统 Python 包冲突:
+
+\begin{lstlisting}[language=bash]
+python -m venv venv
+source venv/bin/activate # Linux/macOS
+\end{lstlisting}
+\end{tipbox}
+
+\subsection{第一个示例}
+
+\begin{lstlisting}[language=Python]
+from phynexis import Model, Mesh
+
+# 创建简单网格
+mesh = Mesh.rectangle(width=1.0, height=1.0, nx=20, ny=20)
+
+# 定义材料模型并求解
+model = Model(mesh)
+model.set_material(young=210e9, nu=0.3, rho=7850)
+result = model.solve()
+print(f"最大位移: {result.max_displacement:.6f} m")
+\end{lstlisting}
+
+\section{核心 API}
+
+\subsection{Mesh 类}
+
+网格是有限元分析的基础。\lstinline|Mesh| 类提供多种工厂方法创建常见几何。
+
+\begin{infobox}[\lstinline|Mesh.rectangle|]
+\textbf{签名}: \lstinline|Mesh.rectangle(width, height, nx, ny)|
+
+\textbf{参数}:
+
+\begin{center}
+\begin{tabular}{lll}
+\toprule
+参数 & 类型 & 说明 \\
+\midrule
+\lstinline|width| & float & 矩形宽度 \\
+\lstinline|height| & float & 矩形高度 \\
+\lstinline|nx| & int & x 方向单元数 \\
+\lstinline|ny| & int & y 方向单元数 \\
+\bottomrule
+\end{tabular}
+\end{center}
+
+\textbf{返回}: \lstinline|Mesh| 实例
+\end{infobox}
+
+\subsection{Model 类}
+
+\lstinline|Model| 封装了物理场求解的完整流程:
+
+\begin{lstlisting}[language=Python]
+model = Model(mesh)
+model.set_material(young=210e9, nu=0.3)
+model.fix_boundary(tag="left") # 固定左侧边界
+model.apply_force(tag="right", fx=1e6) # 右侧施加力
+result = model.solve(solver="direct")
+\end{lstlisting}
+
+\begin{warnbox}[注意]
+\lstinline|solve()| 默认使用直接求解器。大规模问题(> 100,000 DOF)建议改用迭代求解器:
+
+\begin{lstlisting}[language=Python]
+result = model.solve(solver="cg", preconditioner="ilu")
+\end{lstlisting}
+\end{warnbox}
+
+\section{高级用法}
+
+\subsection{并行计算}
+
+\begin{dangerbox}[危险]
+在并行环境内分配数组时,务必确保每个进程只分配自己的局部部分。全局数组会导致内存溢出:
+
+\begin{lstlisting}[language=Python]
+local_K = np.zeros((n_local, n_local)) # 正确:只分配局部矩阵
+\end{lstlisting}
+\end{dangerbox}
+
+\section{故障排除}
+
+\textbf{Q: 求解器收敛失败}
+
+检查以下事项:
+
+\begin{enumerate}
+ \item 边界条件是否充分约束了刚体位移
+ \item 材料参数是否合理(特别是泊松比接近 0.5 时)
+ \item 网格质量是否太差
+\end{enumerate}
+
+\section{版本历史}
+
+\subsection{v2.1.0 (2026-05-01)}
+
+\begin{itemize}
+ \item 新增 GPU 加速求解器(CUDA 后端)
+ \item 支持非线性材料模型
+\end{itemize}
+
+\end{document}