feat(init): add LaTeX manual scaffold and 'init manual' command

- scaffolds/manual/: ctexart+xelatex manual template (cover, TOC, colored
  headings, four callout boxes, listings code, booktabs three-line tables),
  visually aligned with Typst md-to-pdf/manual template
- commands/init.py: extract _init_scaffold() shared by paper/manual
- sync README/AGENTS/skill references; document templates-vs-scaffolds-vs-journals taxonomy
This commit is contained in:
Zhengshou Lai
2026-07-18 09:44:17 +08:00
parent 55c08cf937
commit 9174708d10
8 changed files with 360 additions and 14 deletions
+4 -3
View File
@@ -8,15 +8,16 @@ mytoolkit latex compile paper.tex -e xelatex
mytoolkit latex count paper.tex
```
## 论文脚手架 / 投稿模板
## 论文/手册脚手架 · 投稿模板
```bash
mytoolkit init paper <dir> # 新建论文项目(elsarticle 脚手架,xelatex,可移植;精确配置见生成的 main.tex 头部注释,设计约束见仓库 AGENTS.md §3
mytoolkit init paper <dir> # 新建论文项目(elsarticle 脚手架,xelatex,可移植;精确配置见生成的 main.tex 头部注释,设计约束见仓库 AGENTS.md §4
mytoolkit init manual <dir> # 新建手册项目(技术与使用文档;与 md-to-pdf/manual Typst 模板同一视觉风格,ctexart + xelatex
mytoolkit init journal # 列出投稿套件
mytoolkit init journal elsevier # Elsevier / ieee / springer 套件 → ./submit-<name>/
```
投稿套件自带文档类与 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 命名约定
@@ -4,6 +4,7 @@
```bash
mytoolkit init paper <dir> # 初始化 LaTeX 论文项目
mytoolkit init manual <dir> # 初始化 LaTeX 手册项目(技术与使用文档)
mytoolkit env # 环境变量管理
mytoolkit git # Git 辅助工具
mytoolkit ssh # SSH 快捷连接
+11 -1
View File
@@ -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 <dir> # 默认 Elsevier elsarticle 模板(全平
- 统一 **xelatex** 一个引擎(latexmk 驱动),便于后续加中文/系统字体;
- `make` 编译、`make highlighted OLDTEX=...` 出 latexdiff 修订稿;`diffpreamble.dtx` 标记增删。
### 新建手册项目(技术与使用文档)
```bash
mytoolkit init manual <dir> # LaTeX 手册模板(ctexartxelatex
```
与 Typst `md-to-pdf/manual` 模板同一视觉风格(封面 + 目录 + 彩色标题 + 四色提示框 + 代码高亮 + 三线表),**配色与版式改动须两个模板同步**。生成 `main.tex`/`Makefile`/`README.md`;字体走 ctex 自动字体集,不写死系统字体。
### 切换投稿期刊模板
各出版社“投稿套件”预置在包内 `mytoolkit/journals/`,一条命令落地到子目录,自包含、`make` 直接编译(pdflatex + bibtex):
+4 -2
View File
@@ -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 <dir>`, `mytoolkit init journal <name>`) |
| `init` | Initialize projects/kits from bundled templates (`mytoolkit init paper|manual <dir>`, `mytoolkit init journal <name>`) |
| `info` | Show bundled resource paths (`info paths`, `info docs <name>`) |
| `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 <dir> # create a LaTeX paper project (Elsevier elsarticle, xelatex, fully portable)
mytoolkit init manual <dir> # 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
+21 -8
View File
@@ -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)
+54
View File
@@ -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
+38
View File
@@ -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
+227
View File
@@ -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}