first commit
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
---
|
||||
name: publication-chart-skill
|
||||
description: This skill should be used when the user asks for a publication-quality scientific figure or table, wants help choosing the right chart for results, needs a paper-ready pubfig or pubtab workflow, wants a figure + companion table for a results section, wants an Excel sheet turned into publication-ready LaTeX, or wants an existing scientific figure/table reviewed and upgraded.
|
||||
version: 0.2.0
|
||||
---
|
||||
|
||||
# Publication Chart Skill
|
||||
|
||||
## Goal
|
||||
|
||||
Use this skill to turn research results into **publication-grade figures and tables** with an end-to-end workflow.
|
||||
|
||||
Primary production stack:
|
||||
|
||||
- **`pubfig`** for figures
|
||||
- **`pubtab`** for publication tables
|
||||
|
||||
This skill covers the full delivery chain:
|
||||
|
||||
1. understand the scientific communication goal,
|
||||
2. choose the right artifact type,
|
||||
3. map the task to `pubfig`, `pubtab`, or both,
|
||||
4. generate concrete runnable instructions,
|
||||
5. export paper-ready assets,
|
||||
6. run publication QA,
|
||||
7. propose targeted revisions.
|
||||
|
||||
## Use this skill when
|
||||
|
||||
Trigger this skill for requests like:
|
||||
|
||||
- “make a publication-quality figure”
|
||||
- “choose the right chart for these results”
|
||||
- “turn these results into a paper-ready figure”
|
||||
- “make a benchmark / ablation / calibration / forest / heatmap / scatter / line / bar figure”
|
||||
- “make a benchmark / appendix / ablation table from Excel”
|
||||
- “convert this Excel table into publication-ready LaTeX”
|
||||
- “prepare one summary figure plus one companion table for the results section”
|
||||
- “review and improve this scientific figure/table”
|
||||
- “I already have a weak chart / screenshot / draft plot — make it publication-ready”
|
||||
- “export panels for a paper figure”
|
||||
|
||||
## Do not use this skill for
|
||||
|
||||
Do **not** use this skill when the task is mainly:
|
||||
|
||||
- manuscript prose writing,
|
||||
- statistical testing without artifact design,
|
||||
- raw exploratory analysis with no publication deliverable,
|
||||
- Figma-first layout work before the figure/table content is solid.
|
||||
|
||||
For simple composite assembly after the figure content is already strong, use the optional secondary workflow in `references/composite-assembly.md`.
|
||||
|
||||
## Primary contract
|
||||
|
||||
### Inputs
|
||||
|
||||
Expect some combination of:
|
||||
|
||||
- the scientific communication goal,
|
||||
- available data shape,
|
||||
- venue or style constraints,
|
||||
- whether the artifact is a figure, table, or mixed deliverable,
|
||||
- optional existing assets such as code, spreadsheets, `.tex`, screenshots, or draft plots,
|
||||
- whether the user needs a first draft, a publication-ready artifact, or a review/revision pass.
|
||||
|
||||
### Outputs
|
||||
|
||||
The minimum useful output is:
|
||||
|
||||
- the recommended figure/table form,
|
||||
- the recommended `pubfig` / `pubtab` route,
|
||||
- a minimal runnable code snippet or CLI command,
|
||||
- explicit export filenames and formats,
|
||||
- a publication QA summary,
|
||||
- and, when needed, a revision plan.
|
||||
|
||||
## Default workflow
|
||||
|
||||
### 0. Probe the environment and artifact state
|
||||
|
||||
Before generating anything, identify:
|
||||
|
||||
- whether `pubfig` or `pubtab` is actually available,
|
||||
- whether the user already has code / spreadsheets / `.tex` / screenshots,
|
||||
- whether the deliverable is a fresh build or a revision,
|
||||
- whether the result needs exact values, fast visual perception, or both.
|
||||
|
||||
Prefer the smallest environment check that helps execution. When the bundled helper script is available, use it first:
|
||||
|
||||
- `python3 scripts/ensure_publication_tooling.py --require pubfig --json`
|
||||
- `python3 scripts/ensure_publication_tooling.py --require pubtab --json`
|
||||
|
||||
Equivalent manual checks are still acceptable when needed:
|
||||
|
||||
- `python -c "import pubfig; print(pubfig.__version__)"`
|
||||
- `python -c "import pubtab; print(pubtab.__version__)"`
|
||||
- `pubtab --help`
|
||||
|
||||
Report the result clearly as **available** or **missing**.
|
||||
|
||||
If a dependency is missing and the task requires runnable execution:
|
||||
|
||||
- **auto-install it by default**,
|
||||
- prefer the user’s active environment instead of guessing a random global interpreter,
|
||||
- use `python3 scripts/ensure_publication_tooling.py --require ...` as the default bundled route when the script is present,
|
||||
- let that helper choose `uv` vs `python -m pip` against the active interpreter,
|
||||
- re-run the availability probe after installation,
|
||||
- and only then continue with the artifact workflow.
|
||||
|
||||
Equivalent concrete commands include:
|
||||
|
||||
- `python3 scripts/ensure_publication_tooling.py --require pubfig`
|
||||
- `python3 scripts/ensure_publication_tooling.py --require pubtab`
|
||||
- `uv pip install pubfig`
|
||||
- `uv pip install pubtab`
|
||||
- `python -m pip install pubfig`
|
||||
- `python -m pip install pubtab`
|
||||
|
||||
If auto-install fails, report the exact failure and then degrade gracefully.
|
||||
|
||||
Do not block on a full environment audit.
|
||||
|
||||
### 1. Classify the task
|
||||
|
||||
Classify the request along these axes:
|
||||
|
||||
- **artifact type**: figure / table / mixed deliverable
|
||||
- **maturity**: exploratory draft / publication-ready generation / revision of an existing artifact
|
||||
- **structure**: single panel / multi-panel / figure-plus-table package
|
||||
- **evidence mode**: pattern perception / exact value lookup / both
|
||||
|
||||
Do not jump into plotting code before the communication target is clear.
|
||||
|
||||
Before plotting research results, lock the evidence contract:
|
||||
- primary scientific claim,
|
||||
- unit of analysis,
|
||||
- primary metric and metric direction,
|
||||
- whether repeated rows are independent,
|
||||
- missing cells or incomplete comparison blocks,
|
||||
- error-bar basis: subject, subject-task, fold, seed, run, or bootstrap sample,
|
||||
- whether exact values need a companion table,
|
||||
- whether the current evidence allows a winner/significance claim.
|
||||
|
||||
If these are unclear, ask or produce an audit recommendation instead of a polished figure. Do not create a paper-ready plot while the unit of analysis, missing-cell handling, or error-bar basis is unresolved.
|
||||
|
||||
### 2. Choose the representation
|
||||
|
||||
Choose the representation based on the scientific claim, not novelty or visual flair.
|
||||
|
||||
Common families:
|
||||
|
||||
- **comparison** — grouped scatter, bar, line comparison, benchmark summary, companion table
|
||||
- **ablation** — grouped comparison, dumbbell, paired comparison, compact table
|
||||
- **distribution** — box, violin, raincloud, histogram, density, ECDF, QQ
|
||||
- **relationship** — scatter, bubble, contour2d, hexbin
|
||||
- **trend** — line, area
|
||||
- **evaluation / diagnostic** — calibration, ROC, PR, Bland–Altman, forest plot, volcano
|
||||
- **composition / hierarchy** — UpSet, stacked ratio, donut, radial hierarchy, circular grouped or stacked bars
|
||||
- **table** — benchmark table, ablation table, dataset summary, appendix table, error breakdown
|
||||
|
||||
Avoid weak defaults:
|
||||
|
||||
- avoid pie/donut when exact comparison matters and a bar/table is clearer,
|
||||
- avoid radar unless the comparison is genuinely profile-like and low-cardinality,
|
||||
- avoid 3D, decorative gradients, and dense legends used only for style,
|
||||
- avoid forcing every result into a figure when a publication table communicates the evidence better.
|
||||
|
||||
If the request is ambiguous, explicitly state what scientific claim the artifact is supposed to support.
|
||||
|
||||
### 3. Map to the toolchain
|
||||
|
||||
Default mapping:
|
||||
|
||||
- **Figures** → `pubfig`
|
||||
- **Tables** → `pubtab`
|
||||
- **Mixed deliverables** → use both, with each artifact carrying a distinct role
|
||||
|
||||
Tool roles:
|
||||
|
||||
- `pubfig` is the default figure engine for scientific plots and paper-ready export.
|
||||
- `pubtab` is the default table engine for Excel ↔ LaTeX workflows, preview, and publication-ready table export.
|
||||
- Figma/composite assembly is an **optional secondary branch** for multi-panel finishing.
|
||||
|
||||
Route selection rules:
|
||||
|
||||
- prefer **Python** for `pubfig` figure generation,
|
||||
- prefer **CLI** for `pubtab` when the task is file-driven,
|
||||
- prefer **Python** for `pubtab` when the task is already inside a notebook or scripted pipeline,
|
||||
- keep the figure and table responsibilities separate in mixed requests.
|
||||
|
||||
### 4. Generate concrete artifact instructions
|
||||
|
||||
Prefer the smallest production-ready artifact first:
|
||||
|
||||
- minimal runnable Python for `pubfig`, or
|
||||
- minimal CLI/Python for `pubtab`
|
||||
|
||||
Then add publication parameters only when justified:
|
||||
|
||||
- labels, caption, width, export format, backend, preview, panel packaging, or composite layout.
|
||||
|
||||
Keep filenames and suffixes explicit.
|
||||
|
||||
Good defaults:
|
||||
|
||||
- figures: one `pubfig` call + one `save_figure(...)`
|
||||
- multiple figure outputs: `batch_export(...)`
|
||||
- tables: one `pubtab xlsx2tex ...` or `pubtab.preview ...`
|
||||
- mixed requests: one figure route + one table route, clearly separated
|
||||
|
||||
### 5. Define the delivery contract
|
||||
|
||||
For every response, make these explicit when possible:
|
||||
|
||||
- the claim the artifact supports,
|
||||
- which part is handled by `pubfig` and which by `pubtab`,
|
||||
- the output filenames,
|
||||
- the output formats,
|
||||
- whether the artifact is draft / final / revision,
|
||||
- what still needs user-provided data or manuscript context.
|
||||
|
||||
### 6. Run publication QA
|
||||
|
||||
After generation, check:
|
||||
|
||||
- title and legend density,
|
||||
- axis labels and units,
|
||||
- category ordering and baseline clarity,
|
||||
- color accessibility and grayscale robustness,
|
||||
- font / line-weight consistency,
|
||||
- caption readiness,
|
||||
- figure/table readability after downscaling,
|
||||
- panel consistency for multi-panel figures,
|
||||
- venue-fit issues such as width, crowding, or over-annotation.
|
||||
|
||||
The QA output must be concrete. Do not say “looks better” without naming why.
|
||||
|
||||
### 7. Revise
|
||||
|
||||
If the result is weak, revise with specific changes such as:
|
||||
|
||||
- switch chart family,
|
||||
- remove chartjunk,
|
||||
- reorder categories,
|
||||
- move exact values into a table,
|
||||
- split a crowded panel,
|
||||
- add or simplify the caption,
|
||||
- change export width,
|
||||
- or convert the deliverable from figure-first to table-first.
|
||||
|
||||
## Missing dependency behavior
|
||||
|
||||
If `pubfig` or `pubtab` is not available:
|
||||
|
||||
- do **not** fail immediately,
|
||||
- first attempt automatic installation into the active environment,
|
||||
- prefer `python3 scripts/ensure_publication_tooling.py --require ...` when the bundled script exists,
|
||||
- explicitly state which dependency is missing,
|
||||
- state which install command or helper route is being used,
|
||||
- re-check availability after installation,
|
||||
- if installation succeeds, continue with the runnable workflow,
|
||||
- if installation fails, degrade to a design/specification workflow,
|
||||
- provide pseudocode or draft commands,
|
||||
- preserve the recommended figure/table structure,
|
||||
- still provide QA and revision guidance.
|
||||
|
||||
## Composite assembly rule
|
||||
|
||||
Treat composite or Figma assembly as **secondary**:
|
||||
|
||||
- use it when the user explicitly wants a multi-panel paper figure,
|
||||
- or when panel-level export and layout polishing are genuinely needed.
|
||||
|
||||
Do not escalate simple figure tasks into composite/Figma workflows by default.
|
||||
|
||||
## Output style rules
|
||||
|
||||
- Prefer direct, implementation-usable outputs.
|
||||
- Explain the **why** of chart/table choice briefly, then give the runnable route.
|
||||
- When execution matters, include a short environment status block such as `pubfig: available/missing`, `pubtab: available/missing`.
|
||||
- If a dependency is missing, state the exact helper command or install command, perform the installation, and report the post-install status.
|
||||
- When a table is stronger than a figure, say so explicitly.
|
||||
- When a figure is stronger than a table, say so explicitly.
|
||||
- When both are needed, assign them different communication roles.
|
||||
- Keep revision guidance actionable and falsifiable.
|
||||
|
||||
## Recommended response shape
|
||||
|
||||
A strong response using this skill usually has 6 parts:
|
||||
|
||||
1. **Artifact decision** — figure / table / paired deliverable, and why
|
||||
2. **Tool route** — `pubfig`, `pubtab`, or both
|
||||
3. **Minimal implementation** — runnable code or CLI
|
||||
4. **Export plan** — filenames, formats, width/backend/preview choices
|
||||
5. **Publication QA** — what to verify before paper submission
|
||||
6. **Revision plan** — what to change if the current artifact is weak
|
||||
|
||||
## Resources
|
||||
|
||||
Load these as needed:
|
||||
|
||||
- `references/workflow.md` — full end-to-end decision order and delivery contract
|
||||
- `references/chart-selection.md` — task-to-chart mapping and anti-patterns
|
||||
- `references/execution-and-verification.md` — environment probing, forced install behavior, and runnable verification
|
||||
- `scripts/ensure_publication_tooling.py` — bundled probe + auto-install helper for `pubfig` / `pubtab`
|
||||
- `references/pubfig-recipes.md` — shortest useful figure patterns and export routes
|
||||
- `references/pubtab-recipes.md` — shortest useful table routes and backend guidance
|
||||
- `references/source-guides/pubfig-architecture.md` — package layout and figure-generation boundaries from source
|
||||
- `references/source-guides/pubfig-api-map.md` — stable public pubfig surface and chart-family map from `__init__.py`
|
||||
- `references/source-guides/pubfig-export-flow.md` — figure export, publication sizing, and panel-export flow from source
|
||||
- `references/source-guides/pubtab-architecture.md` — package layout and forward/reverse conversion architecture from source
|
||||
- `references/source-guides/pubtab-cli-api-flow.md` — CLI-to-API control flow and batch/sheet behavior from source
|
||||
- `references/source-guides/pubtab-backend-and-preview.md` — backend/theme split and real preview compile pipeline from source
|
||||
- `references/publication-qa-checklist.md` — figure/table QA checklist
|
||||
- `references/composite-assembly.md` — optional multi-panel and Figma branch
|
||||
|
||||
For prompt-shaped examples, see `examples/`.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 1 — Benchmark comparison figure
|
||||
|
||||
## User-style prompt
|
||||
|
||||
Help me make a publication-quality benchmark comparison figure for six methods across three datasets.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: figure
|
||||
- maturity: publication-ready generation
|
||||
- structure: single panel
|
||||
- goal: fast comparison across methods and datasets
|
||||
|
||||
## Preferred route
|
||||
|
||||
- primary tool: `pubfig`
|
||||
- likely family: `grouped_scatter` or `bar_scatter`
|
||||
- companion table: optional, only if exact values must be preserved nearby
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- explain why the chosen comparison form is better than a raw grouped bar if density is high
|
||||
- provide a minimal `pubfig` implementation
|
||||
- provide one explicit export command/path such as `benchmark.pdf`
|
||||
- provide a short QA note about readability, legend density, and category ordering
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 2 — Ablation figure
|
||||
|
||||
## User-style prompt
|
||||
|
||||
Turn these ablation results into a paper-ready figure that makes the contribution of each module easy to read.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: figure
|
||||
- maturity: publication-ready generation
|
||||
- structure: single panel or compact paired panel
|
||||
- goal: isolate incremental effect clearly
|
||||
|
||||
## Preferred route
|
||||
|
||||
- primary tool: `pubfig`
|
||||
- likely family: `dumbbell` or `paired` for low-cardinality paired ablations, otherwise a compact grouped comparison chart or companion table
|
||||
- do not default to a crowded decorative chart
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- recommend the ablation-focused visual form and why
|
||||
- provide runnable `pubfig` code
|
||||
- export to a paper-ready format
|
||||
- include a QA note on baseline clarity and ordering
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 3 — Calibration / evaluation figure
|
||||
|
||||
## User-style prompt
|
||||
|
||||
I need a publication-quality evaluation figure showing whether my predicted probabilities are calibrated.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: figure
|
||||
- maturity: publication-ready generation
|
||||
- structure: single panel
|
||||
- goal: diagnostic evaluation, not generic trend plotting
|
||||
|
||||
## Preferred route
|
||||
|
||||
- primary tool: `pubfig`
|
||||
- likely family: `calibration`
|
||||
- related alternatives: ROC / PR only if the scientific question changes
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- explain why calibration is the right diagnostic form
|
||||
- provide minimal runnable code
|
||||
- export to PDF or SVG
|
||||
- include QA on labels, reference lines, and downscaled readability
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 4 — Forest / diagnostic figure
|
||||
|
||||
## User-style prompt
|
||||
|
||||
Help me convert these effect estimates and confidence intervals into a paper-quality figure.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: figure
|
||||
- maturity: publication-ready generation
|
||||
- structure: single panel
|
||||
- goal: communicate effect size with uncertainty
|
||||
|
||||
## Preferred route
|
||||
|
||||
- primary tool: `pubfig`
|
||||
- likely family: `forest_plot`
|
||||
- table may accompany it in appendix if exact values are numerous
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- identify forest plot as the default route
|
||||
- provide the required arrays/arguments shape
|
||||
- export a paper-ready artifact
|
||||
- include QA on reference line, label clarity, and ordering
|
||||
@@ -0,0 +1,26 @@
|
||||
# Example 5 — Publication-ready LaTeX table from Excel
|
||||
|
||||
## User-style prompt
|
||||
|
||||
Convert this Excel benchmark sheet into a publication-ready LaTeX table and give me a preview path before I paste it into the paper.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: table
|
||||
- maturity: publication-ready generation
|
||||
- structure: single table
|
||||
- goal: exact benchmark values in LaTeX
|
||||
|
||||
## Preferred route
|
||||
|
||||
- primary tool: `pubtab`
|
||||
- likely route: `xlsx2tex` + `preview`
|
||||
- for a robust preview-first workflow, preview the table body first and keep final caption/label handling as a separate manuscript-facing step when needed
|
||||
- `tabularray` only if the manuscript/backend requires it
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- provide the exact `pubtab` command or Python route
|
||||
- include caption/label guidance when appropriate, but do not force them into the preview-first route
|
||||
- include preview generation
|
||||
- mention table width/span concerns if relevant
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 6 — Mixed figure + table deliverable
|
||||
|
||||
## User-style prompt
|
||||
|
||||
For this results section, I want one summary figure and one companion table: the figure should show the overall pattern, and the table should preserve the exact metrics.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: mixed deliverable
|
||||
- maturity: publication-ready generation
|
||||
- structure: one figure plus one table
|
||||
- goal: split visual summary from exact evidence
|
||||
|
||||
## Preferred route
|
||||
|
||||
- figure: `pubfig`
|
||||
- table: `pubtab`
|
||||
- each artifact should have a distinct communication role
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- explicitly separate figure and table responsibilities
|
||||
- provide one minimal `pubfig` route and one minimal `pubtab` route
|
||||
- define export filenames for both
|
||||
- include QA on redundancy and role separation
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 7 — Revision from a weak chart request
|
||||
|
||||
## User-style prompt
|
||||
|
||||
I was thinking of using a radar chart for these benchmark results. Can you improve this into something publication-quality?
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: revision task
|
||||
- maturity: review/revision
|
||||
- structure: likely single figure or figure+table alternative
|
||||
- goal: replace a weak chart choice with a stronger one
|
||||
|
||||
## Preferred route
|
||||
|
||||
- review the proposed radar choice critically
|
||||
- recommend a stronger alternative such as grouped scatter, line, or a table depending on the claim
|
||||
- only keep radar if the profile comparison is genuinely the right representation
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- explain why the original chart is weak or risky
|
||||
- recommend a better artifact type
|
||||
- provide the replacement implementation route
|
||||
- include targeted QA / revision notes, not vague style comments
|
||||
@@ -0,0 +1,25 @@
|
||||
# Example 8 — Review an existing figure or table draft
|
||||
|
||||
## User-style prompt
|
||||
|
||||
Here is my current results figure screenshot and the draft benchmark table. Please tell me what is weak, whether the figure should stay a figure or become a figure + table pair, and give me the recommended pubfig/pubtab revision path.
|
||||
|
||||
## Expected skill interpretation
|
||||
|
||||
- artifact: review / revision
|
||||
- maturity: existing deliverable upgrade
|
||||
- structure: figure-only or figure + companion table
|
||||
- goal: keep the strongest communication role for each artifact instead of polishing a weak form blindly
|
||||
|
||||
## Preferred route
|
||||
|
||||
- inspect the current figure/table role split first
|
||||
- recommend whether the figure should stay visual and the table should preserve exact values
|
||||
- use `pubfig` for the figure revision and `pubtab` for the table revision when both are needed
|
||||
|
||||
## Minimum acceptable output shape
|
||||
|
||||
- identify the concrete publication weaknesses in the current artifact(s)
|
||||
- say whether the final deliverable should be figure-only, table-only, or figure + table
|
||||
- provide one runnable `pubfig` and/or `pubtab` revision route
|
||||
- define explicit export filenames and a short QA checklist
|
||||
@@ -0,0 +1,47 @@
|
||||
# Chart Selection
|
||||
|
||||
## Task-to-chart mapping
|
||||
|
||||
| Communication task | Preferred forms | Notes |
|
||||
|---|---|---|
|
||||
| Benchmark comparison | grouped scatter, bar, companion table | use table when exact values matter most |
|
||||
| Ablation | grouped comparison, dumbbell, compact table | keep the dimension count small |
|
||||
| Calibration / evaluation | calibration, ROC, PR, Bland–Altman | choose what matches the evaluation claim |
|
||||
| Distribution | box, violin, raincloud, histogram, density, ECDF, QQ | choose by whether shape or exact quantiles matter |
|
||||
| Relationship | scatter, bubble, contour2d, hexbin | use hexbin/contour2d when overplotting is severe |
|
||||
| Trend | line, area | line is usually the safer default |
|
||||
| Diagnostic effect size | forest plot, volcano | match domain and inference style |
|
||||
| Set/composition | UpSet, stacked ratio, donut, radial hierarchy | avoid decorative complexity unless it helps interpretation |
|
||||
| Exact benchmark appendix | publication table | default to `pubtab` |
|
||||
|
||||
## Use X instead of Y
|
||||
|
||||
- Use **grouped scatter** or a **table** instead of a dense grouped bar when exact per-group values matter.
|
||||
- Use **line** instead of bar for ordered progression over time or scale.
|
||||
- Use **UpSet** instead of Venn-style thinking once the set count grows.
|
||||
- Use **forest plot** instead of overloaded textual effect summaries.
|
||||
- Use **table** instead of radar when precision and comparability matter more than shape.
|
||||
- Use **hexbin** or **contour2d** instead of raw scatter when point overlap hides structure.
|
||||
- Use **ECDF** when comparing cumulative distributions clearly is more important than showing a smoothed KDE.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
Avoid:
|
||||
|
||||
- pie/donut for exact quantitative comparison unless the composition story is primary and category count is small,
|
||||
- radar for many categories or when axes are not semantically comparable,
|
||||
- 3D effects,
|
||||
- decorative color ramps without semantic purpose,
|
||||
- overly dense legends that repeat axis information,
|
||||
- mixed chart types that make the evidence harder to read,
|
||||
- turning every result into a figure when a publication table would be cleaner.
|
||||
|
||||
## Selection heuristic
|
||||
|
||||
Ask in order:
|
||||
|
||||
1. What claim is the reader supposed to take away?
|
||||
2. Does the reader need pattern perception or exact value lookup?
|
||||
3. Are the groups ordered, categorical, repeated, hierarchical, or overlapping?
|
||||
4. Is the result single-panel or likely part of a multi-panel figure?
|
||||
5. Would a figure-only answer hide important exact values that should live in a table?
|
||||
@@ -0,0 +1,34 @@
|
||||
# Composite Assembly
|
||||
|
||||
## Principle
|
||||
|
||||
Composite assembly is a **secondary branch**, not the default workflow.
|
||||
|
||||
Use it when:
|
||||
|
||||
- the user explicitly wants a multi-panel paper figure,
|
||||
- panel-level maintenance matters,
|
||||
- or the final paper figure needs finishing beyond a single exported plot.
|
||||
|
||||
## Default stance
|
||||
|
||||
- Single-panel figure → stay in `pubfig` normal export mode.
|
||||
- Multi-panel figure with real assembly needs → use panel/composite export.
|
||||
- Figma is optional and should not be introduced unless it solves a real assembly problem.
|
||||
|
||||
## pubfig routes
|
||||
|
||||
Relevant `pubfig` capabilities:
|
||||
|
||||
- `export_panel(...)`
|
||||
- `export_panels(...)`
|
||||
- `batch_export(...)`
|
||||
- `save_figure(...)`
|
||||
|
||||
If the environment already uses a pubfig/Figma bridge workflow, keep `figure_id` stable across revisions.
|
||||
|
||||
## Practical rule
|
||||
|
||||
Escalate to composite assembly only after the panel content itself is strong.
|
||||
|
||||
Do not use Figma/composite assembly to hide weak chart choice, poor labels, or overloaded panels.
|
||||
@@ -0,0 +1,159 @@
|
||||
# Execution and Verification
|
||||
|
||||
## Goal
|
||||
|
||||
Turn a high-level publication figure/table request into a route that is actually runnable in the current environment.
|
||||
|
||||
## Minimum environment probe
|
||||
|
||||
Prefer the lightest useful checks.
|
||||
|
||||
### Preferred bundled probe
|
||||
|
||||
```bash
|
||||
python3 scripts/ensure_publication_tooling.py --require pubfig --json
|
||||
python3 scripts/ensure_publication_tooling.py --require pubtab --json
|
||||
```
|
||||
|
||||
The helper probes availability, force-installs missing dependencies into the active interpreter, and returns the post-install status.
|
||||
|
||||
### Equivalent manual checks
|
||||
|
||||
```bash
|
||||
python -c "import pubfig; print(pubfig.__version__)"
|
||||
python -c "import pubtab; print(pubtab.__version__)"
|
||||
pubtab --help
|
||||
```
|
||||
|
||||
Do not spend the whole turn on setup if the user primarily needs design guidance. Just identify whether the route is executable now or should degrade gracefully.
|
||||
|
||||
## Automatic installation policy
|
||||
|
||||
If a dependency is missing and the task requires real execution, install it automatically before continuing.
|
||||
|
||||
### Preferred bundled route
|
||||
|
||||
Use the bundled helper when it is present:
|
||||
|
||||
```bash
|
||||
python3 scripts/ensure_publication_tooling.py --require pubfig
|
||||
python3 scripts/ensure_publication_tooling.py --require pubtab
|
||||
```
|
||||
|
||||
The helper chooses `uv pip install --python <active-python>` when the project is clearly `uv`-managed, and otherwise falls back to `python -m pip install ...`.
|
||||
|
||||
### Equivalent manual install commands
|
||||
|
||||
```bash
|
||||
uv pip install --python "$VIRTUAL_ENV/bin/python" pubfig
|
||||
uv pip install --python "$VIRTUAL_ENV/bin/python" pubtab
|
||||
python -m pip install pubfig
|
||||
python -m pip install pubtab
|
||||
```
|
||||
|
||||
### Required follow-up
|
||||
|
||||
After installation:
|
||||
|
||||
1. re-run the availability probe,
|
||||
2. report the updated environment status,
|
||||
3. continue with the runnable figure/table workflow.
|
||||
|
||||
If installation fails, capture the exact error and then fall back to design/specification guidance.
|
||||
|
||||
## Route selection
|
||||
|
||||
### Use `pubfig` when
|
||||
|
||||
- the task is primarily a figure,
|
||||
- the user already has Python data structures,
|
||||
- the result is a plot family already covered by `pubfig`,
|
||||
- export quality matters immediately.
|
||||
|
||||
### Use `pubtab` when
|
||||
|
||||
- the task is primarily a publication table,
|
||||
- the input is an Excel workbook, a `.tex` table, or a file-driven workflow,
|
||||
- the reader needs exact values,
|
||||
- previewing the table before manuscript insertion matters.
|
||||
|
||||
### Use both when
|
||||
|
||||
- the figure carries the visual pattern,
|
||||
- the table preserves exact benchmark values,
|
||||
- the paper section benefits from one fast visual plus one exact-value artifact.
|
||||
|
||||
## First runnable verification
|
||||
|
||||
### `pubfig`
|
||||
|
||||
After generating a minimal figure route, the first useful verification is:
|
||||
|
||||
- can the code execute,
|
||||
- does `save_figure(...)` or `batch_export(...)` produce the expected files,
|
||||
- do output suffixes match the intended formats.
|
||||
|
||||
### `pubtab`
|
||||
|
||||
After generating a minimal table route, the first useful verification is:
|
||||
|
||||
- can `xlsx2tex` or `tex2xlsx` run,
|
||||
- can `preview` render PNG or PDF,
|
||||
- does the chosen backend (`tabular` or `tabularray`) match the manuscript need.
|
||||
|
||||
## Current practical notes
|
||||
|
||||
### `pubfig`
|
||||
|
||||
Useful export primitives include:
|
||||
|
||||
- `save_figure(...)`
|
||||
- `batch_export(...)`
|
||||
- `export_panel(...)`
|
||||
- `export_panels(...)`
|
||||
|
||||
Use panel export only when multi-panel assembly is truly needed.
|
||||
|
||||
### `pubtab`
|
||||
|
||||
Useful file-oriented routes include:
|
||||
|
||||
- `pubtab xlsx2tex ...`
|
||||
- `pubtab tex2xlsx ...`
|
||||
- `pubtab preview ...`
|
||||
|
||||
Remember:
|
||||
|
||||
- `xlsx2tex` exports all sheets by default when `--sheet` is not set,
|
||||
- `preview` can render PNG or PDF,
|
||||
- `--latex-backend tabularray` should be chosen only when the manuscript/backend requires `tblr`,
|
||||
- when preview reliability is the immediate priority, validate the table body first and add final `caption` / `label` in a separate manuscript-facing step if needed.
|
||||
|
||||
## Graceful degradation
|
||||
|
||||
If the tool is missing:
|
||||
|
||||
- first try the bundled auto-install helper,
|
||||
- if that route is unavailable, use the manual install commands above,
|
||||
- if installation still fails, provide:
|
||||
- the artifact recommendation,
|
||||
- the exact files the user should prepare,
|
||||
- a draft CLI or Python route,
|
||||
- the export targets,
|
||||
- and the publication QA checklist.
|
||||
|
||||
## Default output wording
|
||||
|
||||
When the route is runnable now, say:
|
||||
|
||||
- what to run,
|
||||
- what files should appear,
|
||||
- what to inspect next.
|
||||
|
||||
When the route is not runnable now, say:
|
||||
|
||||
- what is missing,
|
||||
- which helper command or install command was attempted,
|
||||
- whether the install succeeded or failed,
|
||||
- what the intended route will be after install,
|
||||
- and what design decision can already be locked in today.
|
||||
@@ -0,0 +1,104 @@
|
||||
# pubfig Recipes
|
||||
|
||||
`pubfig` is the default engine for scientific figures.
|
||||
|
||||
## Core route
|
||||
|
||||
Typical minimal workflow:
|
||||
|
||||
```python
|
||||
import pubfig as pf
|
||||
|
||||
fig = pf.line(data, x=x, series_names=["A", "B"])
|
||||
pf.save_figure(fig, "figure1.pdf")
|
||||
```
|
||||
|
||||
## Common figure families
|
||||
|
||||
| Need | Preferred `pubfig` functions |
|
||||
|---|---|
|
||||
| benchmark comparison | `bar_scatter`, `grouped_scatter`, `bar`, `line` |
|
||||
| ablation | `bar_scatter`, `dumbbell`, `paired`, `bar` |
|
||||
| distribution | `box`, `violin`, `raincloud`, `histogram`, `density`, `ecdf`, `qq` |
|
||||
| relationship | `scatter`, `bubble`, `contour2d`, `hexbin` |
|
||||
| trend | `line`, `area` |
|
||||
| diagnostic / evaluation | `calibration`, `forest_plot`, `bland_altman`, `volcano`, `roc`, `pr_curve` |
|
||||
| composition / hierarchy | `donut`, `upset`, `radial_hierarchy`, `circular_grouped_bar`, `circular_stacked_bar`, `stacked_ratio_barh` |
|
||||
| matrix / map | `heatmap`, `corr_matrix`, `clustermap` |
|
||||
|
||||
## Export defaults
|
||||
|
||||
For a normal first pass:
|
||||
|
||||
```python
|
||||
pf.save_figure(fig, "figure1.pdf")
|
||||
```
|
||||
|
||||
For multiple formats:
|
||||
|
||||
```python
|
||||
pf.batch_export(
|
||||
fig,
|
||||
"figure1",
|
||||
formats=("pdf", "svg", "png"),
|
||||
spec="nature",
|
||||
width="single",
|
||||
dpi=300,
|
||||
)
|
||||
```
|
||||
|
||||
## When to add export parameters
|
||||
|
||||
Only add more export controls when the task demands them:
|
||||
|
||||
- `spec` / `width` for venue-style export
|
||||
- explicit SVG for vector-first downstream editing
|
||||
- PNG for quick review or raster deliverables
|
||||
- panel export when the user truly needs composite assembly
|
||||
- `batch_export(...)` when the same figure needs several publication-style outputs
|
||||
|
||||
## Panel export branch
|
||||
|
||||
Use these only when multi-panel assembly is genuinely needed:
|
||||
|
||||
- `export_panel(...)`
|
||||
- `export_panels(...)`
|
||||
|
||||
Do not default to panel export for single figures.
|
||||
|
||||
## Minimal recipe patterns
|
||||
|
||||
### Benchmark comparison
|
||||
|
||||
```python
|
||||
fig = pf.grouped_scatter(values, category_names=category_names, group_names=model_names)
|
||||
pf.save_figure(fig, "benchmark.pdf")
|
||||
```
|
||||
|
||||
### Ablation
|
||||
|
||||
```python
|
||||
fig = pf.dumbbell(baseline, improved, category_names=labels)
|
||||
pf.save_figure(fig, "ablation.pdf")
|
||||
```
|
||||
|
||||
### Calibration
|
||||
|
||||
```python
|
||||
fig = pf.calibration(prob_true, prob_pred)
|
||||
pf.save_figure(fig, "calibration.pdf")
|
||||
```
|
||||
|
||||
### Forest plot
|
||||
|
||||
```python
|
||||
fig = pf.forest_plot(effect, lower, upper, labels=labels, reference=1.0)
|
||||
pf.save_figure(fig, "forest.pdf")
|
||||
```
|
||||
|
||||
### Heatmap
|
||||
|
||||
```python
|
||||
fig = pf.heatmap(matrix)
|
||||
pf.save_figure(fig, "heatmap.pdf")
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
# Publication QA Checklist
|
||||
|
||||
## Figure QA
|
||||
|
||||
Check:
|
||||
|
||||
- Is the chart family appropriate for the scientific claim?
|
||||
- Are axes labeled clearly, with units when needed?
|
||||
- Is category ordering meaningful and stable?
|
||||
- Are color choices readable in grayscale or for color-deficient readers?
|
||||
- Are fonts, line weights, and marker sizes internally consistent?
|
||||
- Is the legend necessary, and if so, is it compact?
|
||||
- Does the figure stay readable after downscaling to likely paper width?
|
||||
- If multi-panel, are labels, spacing, and styling consistent across panels?
|
||||
- Does the caption need exact numbers that should instead live in a table?
|
||||
|
||||
## Table QA
|
||||
|
||||
Check:
|
||||
|
||||
- Is a table actually the right artifact, or should a companion figure exist?
|
||||
- Are row/column labels short but unambiguous?
|
||||
- Are important values easy to locate?
|
||||
- Is the caption specific enough to stand alone?
|
||||
- Is the table too dense for the main paper and better suited to appendix?
|
||||
- If two-column format matters, was width/span considered explicitly?
|
||||
- Was a preview rendered before treating the table as final?
|
||||
|
||||
## Mixed deliverable QA
|
||||
|
||||
If both a figure and table are present:
|
||||
|
||||
- Does each have a distinct role?
|
||||
- Is the figure carrying pattern perception?
|
||||
- Is the table preserving exact values?
|
||||
- Is there duplication that should be reduced?
|
||||
|
||||
## Revision questions
|
||||
|
||||
If the artifact is weak, ask:
|
||||
|
||||
- Should the chart family change?
|
||||
- Should exact values move into a table?
|
||||
- Should the artifact split into two panels?
|
||||
- Should labels or legends be simplified?
|
||||
- Should export width/format change?
|
||||
- Should the artifact move from figure-first to table-first, or the reverse?
|
||||
@@ -0,0 +1,133 @@
|
||||
# pubtab Recipes
|
||||
|
||||
`pubtab` is the default engine for publication-ready tables.
|
||||
|
||||
## Core routes
|
||||
|
||||
### Excel to LaTeX
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex results.xlsx -o results.tex
|
||||
```
|
||||
|
||||
### LaTeX to Excel
|
||||
|
||||
```bash
|
||||
pubtab tex2xlsx tables.tex -o tables.xlsx
|
||||
```
|
||||
|
||||
### Preview
|
||||
|
||||
```bash
|
||||
pubtab preview results.tex -o results.png --dpi 300
|
||||
pubtab preview results.tex --format pdf -o results.pdf
|
||||
```
|
||||
|
||||
## Python route
|
||||
|
||||
```python
|
||||
import pubtab
|
||||
|
||||
pubtab.xlsx2tex("results.xlsx", output="results.tex", theme="three_line")
|
||||
pubtab.preview("results.tex", output="results.png", dpi=300)
|
||||
```
|
||||
|
||||
## Route selection rule
|
||||
|
||||
Prefer the **CLI** when:
|
||||
|
||||
- the user already speaks in files,
|
||||
- the source is Excel or `.tex`,
|
||||
- the main need is export and preview.
|
||||
|
||||
Prefer the **Python API** when:
|
||||
|
||||
- the task already lives inside a notebook or script,
|
||||
- the table generation is part of a larger reproducible pipeline.
|
||||
|
||||
## Current practical notes
|
||||
|
||||
- `xlsx2tex` exports **all sheets by default** when `--sheet` is not set.
|
||||
- `preview` can render **PNG or PDF**.
|
||||
- `--latex-backend tabularray` is useful only when the manuscript/backend really requires `tblr`.
|
||||
- `preview` can auto-detect `tblr`, but explicit backend override is still fine when needed.
|
||||
- for a robust preview-first workflow, preview the table body first and add the final `caption` / `label` in the manuscript or in a final non-preview export step when needed.
|
||||
|
||||
## When to use `tabularray`
|
||||
|
||||
Use `--latex-backend tabularray` when:
|
||||
|
||||
- the user explicitly wants `tblr`,
|
||||
- the manuscript already uses `tabularray`,
|
||||
- or the backend must match an existing paper template.
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex results.xlsx -o results_tblr.tex --theme three_line --latex-backend tabularray
|
||||
```
|
||||
|
||||
## Common publication controls
|
||||
|
||||
Use these when they are justified:
|
||||
|
||||
- `--caption`
|
||||
- `--label`
|
||||
- `--span-columns`
|
||||
- `--preview`
|
||||
- `--latex-backend`
|
||||
- `--sheet`
|
||||
- `--with-resizebox`
|
||||
- `--without-resizebox`
|
||||
- `--resizebox-width`
|
||||
|
||||
## Default guidance
|
||||
|
||||
- start with the smallest `xlsx2tex` route,
|
||||
- preview before treating the table as final,
|
||||
- use a publication table when exact values matter more than quick pattern perception,
|
||||
- keep figure and table roles distinct in mixed deliverables.
|
||||
|
||||
## Minimal recipe patterns
|
||||
|
||||
### Benchmark table from Excel
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex benchmark.xlsx -o benchmark.tex --caption "Main benchmark results." --label "tab:benchmark"
|
||||
```
|
||||
|
||||
### Two-column table
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex benchmark.xlsx -o benchmark.tex --span-columns
|
||||
```
|
||||
|
||||
### Preview before submission
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex benchmark.xlsx -o benchmark_preview.tex
|
||||
pubtab preview benchmark_preview.tex -o benchmark.png --dpi 300
|
||||
```
|
||||
|
||||
Use this route when the immediate goal is a reliable visual check of the table body.
|
||||
Keep `caption` / `label` as a separate manuscript-facing step if the preview is the main verification target.
|
||||
|
||||
### Final manuscript-facing export
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex benchmark.xlsx -o benchmark.tex --caption "Main benchmark results." --label "tab:benchmark"
|
||||
```
|
||||
|
||||
### All-sheets export
|
||||
|
||||
```bash
|
||||
pubtab xlsx2tex benchmark.xlsx -o out/benchmark.tex
|
||||
```
|
||||
|
||||
### Native file-pipeline batch roundtrip
|
||||
|
||||
```bash
|
||||
pubtab tex2xlsx ./tables_tex -o ./out/xlsx
|
||||
pubtab xlsx2tex ./out/xlsx -o ./out/tex
|
||||
pubtab preview ./out/tex -o ./out/png --format png --dpi 300
|
||||
```
|
||||
@@ -0,0 +1,199 @@
|
||||
# pubfig API map (source-driven)
|
||||
|
||||
This guide maps the public `pubfig` API from `pubfig/src/pubfig/__init__.py` to the underlying modules.
|
||||
|
||||
## 1. Stable entrypoint
|
||||
|
||||
The public contract is defined by re-exports in `pubfig.__init__`.
|
||||
|
||||
For agents, this means:
|
||||
|
||||
- if a symbol is re-exported there, it is a good default public entrypoint;
|
||||
- if a helper only exists in deep internal modules, treat it as implementation detail unless there is a strong reason not to.
|
||||
|
||||
## 2. Public API groups
|
||||
|
||||
### Themes
|
||||
|
||||
Public re-exports:
|
||||
|
||||
- `set_default_theme`
|
||||
- `get_default_theme`
|
||||
- `get_theme`
|
||||
- `register_theme`
|
||||
|
||||
Use these when the task is really about reusable visual policy.
|
||||
Do not hardcode theme assumptions if a registry call is more appropriate.
|
||||
|
||||
### Colors and palettes
|
||||
|
||||
Public re-exports include:
|
||||
|
||||
- `DEFAULT`, `NATURE`, `SCIENCE`, `LANCET`, `JAMA`
|
||||
- `get_palette`
|
||||
- `register_palette`
|
||||
- `color_to_rgba`
|
||||
- `darken_color`
|
||||
- `show_palette`
|
||||
|
||||
Source fact:
|
||||
|
||||
- palette registration and palette inspection are exposed from the package root.
|
||||
|
||||
Operational implication:
|
||||
|
||||
- treat palette selection and palette registration as public API usage, not as deep internal customization.
|
||||
|
||||
### Export
|
||||
|
||||
Public re-exports:
|
||||
|
||||
- `save_figure`
|
||||
- `batch_export`
|
||||
- `PanelExportRecord`
|
||||
- `export_panel`
|
||||
- `export_panels`
|
||||
- `package_figma_bundle`
|
||||
- `validate_figma_bundle`
|
||||
- `inspect_figma_bundle`
|
||||
|
||||
Skill implication:
|
||||
|
||||
- normal paper figures should usually stop at `save_figure` or `batch_export`
|
||||
- panel workflows should use `export_panel(s)`
|
||||
- bundle helpers are for bridge/Figma handoff, not the default answer
|
||||
|
||||
### Figure specs
|
||||
|
||||
Public re-exports:
|
||||
|
||||
- `FigureSpec`
|
||||
- `get_figure_spec`
|
||||
- `register_figure_spec`
|
||||
- `list_figure_specs`
|
||||
|
||||
Use this layer whenever the user asks for venue-aware width, journal defaults, or a custom export profile.
|
||||
|
||||
### Plot families
|
||||
|
||||
The public plot surface is broad, but the source still clusters naturally.
|
||||
|
||||
#### Comparison / summary figures
|
||||
|
||||
Representative public calls:
|
||||
|
||||
- `bar`
|
||||
- `bar_scatter`
|
||||
- `stacked_bar`
|
||||
- `stacked_ratio_barh`
|
||||
- `donut`
|
||||
- `dumbbell`
|
||||
- `forest_plot`
|
||||
- `grouped_scatter`
|
||||
- `upset`
|
||||
|
||||
Use these for benchmark, ablation, summary, composition, and set-overlap tasks.
|
||||
|
||||
#### Distribution figures
|
||||
|
||||
Representative public calls:
|
||||
|
||||
- `ecdf`
|
||||
- `qq`
|
||||
- `box`
|
||||
- `density`
|
||||
- `hexbin`
|
||||
- `histogram`
|
||||
- `raincloud`
|
||||
- `strip`
|
||||
- `ridgeline`
|
||||
- `violin`
|
||||
|
||||
Use these when the scientific claim is about spread, calibration of assumptions, or cohort structure.
|
||||
|
||||
#### Trend / profile figures
|
||||
|
||||
Representative public calls:
|
||||
|
||||
- `line`
|
||||
- `area`
|
||||
- `parallel_coordinates`
|
||||
- `radar`
|
||||
- `radial_hierarchy`
|
||||
- `circular_stacked_bar`
|
||||
- `circular_grouped_bar`
|
||||
|
||||
Not all of these are equally strong for publication use. The skill should still apply chart-selection discipline before calling them.
|
||||
|
||||
#### Relationship / embedding figures
|
||||
|
||||
Representative public calls:
|
||||
|
||||
- `scatter`
|
||||
- `volcano`
|
||||
- `bubble`
|
||||
- `contour2d`
|
||||
- `paired`
|
||||
- `heatmap`
|
||||
- `corr_matrix`
|
||||
- `clustermap`
|
||||
- `dimreduce`
|
||||
- `pca_biplot`
|
||||
|
||||
Use these for association, error structure, feature layout, and representation views.
|
||||
|
||||
#### Evaluation figures
|
||||
|
||||
Representative public calls:
|
||||
|
||||
- `roc`
|
||||
- `pr_curve`
|
||||
- `calibration`
|
||||
- `bland_altman`
|
||||
|
||||
This cluster matters because the source gives them dedicated implementation in `plots/evaluation.py`, which is a sign that evaluation charts are a first-class use case.
|
||||
|
||||
## 3. Return-value contract
|
||||
|
||||
The source in `export/io.py` makes a subtle but important contract explicit:
|
||||
|
||||
- export functions accept a Matplotlib `Figure`,
|
||||
- or an `Axes`,
|
||||
- or any object exposing a `.figure` attribute that resolves to a `Figure`.
|
||||
|
||||
Source fact:
|
||||
|
||||
- the export layer accepts standard Matplotlib figure objects or figure-bearing wrappers.
|
||||
|
||||
Operational implication:
|
||||
|
||||
- keep the `Figure` handle available and route export through the standard Matplotlib-facing export path.
|
||||
|
||||
For the skill, the safest phrasing is:
|
||||
|
||||
- create the figure,
|
||||
- keep a handle to the `Figure`,
|
||||
- then export explicitly.
|
||||
|
||||
## 4. What is not the main stable plotting interface
|
||||
|
||||
From the source tree, the CLI is not where normal chart creation happens.
|
||||
It is mostly an operational layer for Figma bundle and bridge actions.
|
||||
|
||||
So if a user says “generate a paper-ready figure,” the skill should not default to a CLI answer.
|
||||
|
||||
## 5. Safe public usage pattern
|
||||
|
||||
The most source-faithful pattern is:
|
||||
|
||||
1. choose a public plot function from `pubfig`
|
||||
2. generate a `Figure`
|
||||
3. export via `save_figure(...)` or `batch_export(...)`
|
||||
4. only use panel/bundle helpers when composition is actually required
|
||||
|
||||
## 6. Source-guided caution points
|
||||
|
||||
- Do not mix up plot-time design sizing with export-time publication sizing.
|
||||
- Do not use `save_figure(...)` as a multi-format exporter; the source now pushes that role to `batch_export(...)`.
|
||||
- Do not route ordinary figure-generation tasks through the Figma bridge CLI.
|
||||
- Do not assume all public plot families are equally appropriate; the skill must still filter by scientific communication quality.
|
||||
@@ -0,0 +1,190 @@
|
||||
# pubfig architecture (source-driven)
|
||||
|
||||
This guide reads `pubfig` from the source tree rather than from high-level overview material.
|
||||
|
||||
Source root:
|
||||
|
||||
- `pubfig/src/pubfig/__init__.py`
|
||||
- `pubfig/src/pubfig/plots/`
|
||||
- `pubfig/src/pubfig/export/`
|
||||
- `pubfig/src/pubfig/specs.py`
|
||||
- `pubfig/src/pubfig/themes/`
|
||||
- `pubfig/src/pubfig/colors/`
|
||||
- `pubfig/src/pubfig/cli.py`
|
||||
|
||||
## 1. Start at `pubfig.__init__`
|
||||
|
||||
The stable user-facing surface is re-exported from `__init__.py`.
|
||||
|
||||
That file tells you the package is organized into five main layers:
|
||||
|
||||
1. **plot constructors** from `plots/`
|
||||
2. **export helpers** from `export/`
|
||||
3. **publication sizing** from `specs.py`
|
||||
4. **theme and palette registries** from `themes/` and `colors/`
|
||||
5. **Figma/bridge helpers** and related CLI support
|
||||
|
||||
For skill design, this is the most important architectural fact:
|
||||
|
||||
- figure generation lives in `plots/`
|
||||
- figure export lives in `export/`
|
||||
- venue sizing lives in `specs.py`
|
||||
- multi-panel/Figma handoff is a separate downstream layer
|
||||
|
||||
The default mental model is **plot first, export second, compose third**.
|
||||
|
||||
## 2. Package boundaries
|
||||
|
||||
### `plots/`
|
||||
|
||||
This is the core figure-construction layer.
|
||||
|
||||
Representative files:
|
||||
|
||||
- `plots/line.py`
|
||||
- `plots/comparison.py`
|
||||
- `plots/evaluation.py`
|
||||
- `plots/_grouped_scatter.py`
|
||||
|
||||
From the source, plot functions usually do the same sequence:
|
||||
|
||||
1. normalize/coerce input data,
|
||||
2. enter `theme_context(theme)`,
|
||||
3. resolve design-time size via `resolve_design_dpi(...)`,
|
||||
4. allocate figure/axes via `get_fig_ax(...)`,
|
||||
5. style axes/legends through helpers in `_style.py`,
|
||||
6. return a Matplotlib `Figure`.
|
||||
|
||||
Interpretation from source:
|
||||
|
||||
- `pubfig` behaves as a **Matplotlib-first figure factory layer**, not as a separate scene-graph runtime.
|
||||
|
||||
### `export/`
|
||||
|
||||
This is intentionally separated from plotting.
|
||||
|
||||
Important files:
|
||||
|
||||
- `export/io.py`
|
||||
- `export/panels.py`
|
||||
|
||||
`export/io.py` handles normal figure export:
|
||||
|
||||
- coerce Figure/Axes into a real `Figure`
|
||||
- enforce explicit suffixes
|
||||
- apply publication width/height rules
|
||||
- write vector or raster output
|
||||
|
||||
Current source implication:
|
||||
|
||||
- `batch_export(...)` now belongs to the same publication-aware export layer, rather than to a simple multi-format `savefig` wrapper.
|
||||
|
||||
`export/panels.py` handles panel-level export for composite or Figma-oriented workflows:
|
||||
|
||||
- one panel at a time or many panels together
|
||||
- optional publication-aware sizing
|
||||
- optional title stripping
|
||||
- metadata index generation (`panel-index.json`)
|
||||
|
||||
### `specs.py`
|
||||
|
||||
This file is the publication-sizing contract.
|
||||
|
||||
`FigureSpec` defines:
|
||||
|
||||
- `font_family`
|
||||
- `design_dpi`
|
||||
- `single_column_mm`
|
||||
- `double_column_mm`
|
||||
- `default_raster_dpi`
|
||||
- `background_color`
|
||||
|
||||
Built-in registry entries include:
|
||||
|
||||
- `nature`
|
||||
- `science`
|
||||
- `cell`
|
||||
|
||||
The source shows a strong split between:
|
||||
|
||||
- **design size** used when constructing interactive figures,
|
||||
- **physical export size** used when saving publication figures.
|
||||
|
||||
That split is why the skill should not treat `width` in plot calls and `width` in export calls as the same semantic layer.
|
||||
|
||||
### `themes/` and `colors/`
|
||||
|
||||
These are registries, not plain constants.
|
||||
|
||||
From `__init__.py`, the public surface includes:
|
||||
|
||||
- `get_theme`, `register_theme`, `set_default_theme`
|
||||
- `get_palette`, `register_palette`, `show_palette`
|
||||
|
||||
Operational implication:
|
||||
|
||||
- treat theme and palette selection as first-class API configuration rather than as hardcoded styling trivia.
|
||||
|
||||
### `cli.py`
|
||||
|
||||
The current CLI is not the main figure-generation interface.
|
||||
|
||||
From the source, `cli.py` is mainly about:
|
||||
|
||||
- Figma bridge serving
|
||||
- bundle packaging/inspection/validation
|
||||
- sync job submission and waiting
|
||||
- local bridge auto-start logic
|
||||
|
||||
So for this skill:
|
||||
|
||||
- **Python API is the primary route for figure generation**
|
||||
- `pubfig.cli` is a secondary operational layer for bridge/Figma workflows
|
||||
|
||||
## 3. Plotting architecture pattern
|
||||
|
||||
From `line.py`, `comparison.py`, and `evaluation.py`, the recurring internal pattern is:
|
||||
|
||||
- input normalization is local to the chart family,
|
||||
- shared visual behavior is delegated to internal helpers,
|
||||
- returned artifact is still a standard Matplotlib figure.
|
||||
|
||||
This is why the skill should map requests to a chart family first, instead of jumping directly to export or panel assembly.
|
||||
|
||||
Examples from source:
|
||||
|
||||
- `line.py` groups time/trend style plots
|
||||
- `comparison.py` groups comparison-style statistical displays like `dumbbell` and `forest_plot`
|
||||
- `evaluation.py` groups metric/evaluation plots like `roc`, `pr_curve`, and `calibration`
|
||||
- `_grouped_scatter.py` contains the more specialized placement/jitter/annotation logic behind grouped scatter layouts
|
||||
|
||||
## 4. Export architecture pattern
|
||||
|
||||
From `export/io.py` and `export/panels.py`, `pubfig` uses three distinct output modes:
|
||||
|
||||
1. **single explicit artifact** via `save_figure(...)`
|
||||
2. **publication-aware multi-format artifact set** via `batch_export(...)`
|
||||
3. **panel bundle workflow** via `export_panel(...)` / `export_panels(...)`
|
||||
|
||||
Those are different contracts, and the skill should keep them separate in its recommendations.
|
||||
|
||||
## 5. Reading order for deep debugging
|
||||
|
||||
When a skill or agent needs source-level certainty, use this order:
|
||||
|
||||
1. `pubfig/src/pubfig/__init__.py`
|
||||
2. relevant chart-family module in `plots/`
|
||||
3. `pubfig/src/pubfig/specs.py`
|
||||
4. `pubfig/src/pubfig/export/io.py`
|
||||
5. `pubfig/src/pubfig/export/panels.py`
|
||||
6. `pubfig/src/pubfig/cli.py` only if the task involves bridge/Figma sync
|
||||
|
||||
## 6. Implications for this skill
|
||||
|
||||
This source layout implies the skill should:
|
||||
|
||||
- default to **Python plot API + explicit export call**,
|
||||
- treat publication sizing as an export concern,
|
||||
- treat panel/Figma work as optional downstream composition,
|
||||
- avoid presenting the CLI as the main path for ordinary figure generation,
|
||||
- keep chart selection logically ahead of export tuning.
|
||||
@@ -0,0 +1,210 @@
|
||||
# pubfig export flow (source-driven)
|
||||
|
||||
This guide explains how `pubfig` moves from a generated Matplotlib figure to paper-ready files.
|
||||
|
||||
Primary source files:
|
||||
|
||||
- `pubfig/src/pubfig/export/io.py`
|
||||
- `pubfig/src/pubfig/export/panels.py`
|
||||
- `pubfig/src/pubfig/specs.py`
|
||||
|
||||
## 1. Core export contract
|
||||
|
||||
`export/io.py` separates two concerns:
|
||||
|
||||
- **coercing a valid figure object**
|
||||
- **writing explicit output files**
|
||||
|
||||
The helper `_coerce_mpl_figure(...)` accepts:
|
||||
|
||||
- a `Figure`
|
||||
- an `Axes`
|
||||
- an object with a `.figure` attribute pointing to a `Figure`
|
||||
|
||||
The export layer is standardized around Matplotlib figures, even if upstream plotting code returns a richer wrapper.
|
||||
|
||||
## 2. `save_figure(...)` is now single-target and suffix-explicit
|
||||
|
||||
A key source-level rule lives in `_resolve_save_figure_target(...)`:
|
||||
|
||||
- `save_figure(...)` now requires an explicit filename suffix,
|
||||
- supported examples include `.pdf`, `.svg`, `.png`, `.jpg`, `.tif`, `.eps`, `.ps`,
|
||||
- if there is no suffix, the function raises an error,
|
||||
- if multiple outputs are wanted, the source tells you to use `batch_export(...)`.
|
||||
|
||||
Skill implication:
|
||||
|
||||
- always write `results/figure1.pdf` rather than `results/figure1`
|
||||
- when you want several formats, recommend `batch_export(...)`, not legacy vector/raster format lists
|
||||
|
||||
## 3. Publication sizing path
|
||||
|
||||
`save_figure(...)` is publication-aware.
|
||||
|
||||
Internally it does the following:
|
||||
|
||||
1. load the chosen `FigureSpec` via `get_figure_spec(...)`
|
||||
2. resolve width through `resolve_width_mm(...)`
|
||||
3. resolve height through `resolve_height_mm(...)`
|
||||
4. set the Matplotlib figure size in inches using mm-to-inch conversion
|
||||
5. choose raster DPI from the spec unless overridden
|
||||
6. save the explicit target file
|
||||
7. restore original caller state afterward
|
||||
|
||||
Interpretation from source:
|
||||
|
||||
- export sizing is more than a file-write step; it can temporarily resize the figure to venue-oriented physical dimensions before output.
|
||||
|
||||
## 4. Width and height semantics
|
||||
|
||||
From `specs.py`:
|
||||
|
||||
- width can be `single`, `double`, or a numeric mm value
|
||||
- the built-in registry contains `nature`, `science`, and `cell`
|
||||
- height can be explicit `height_mm`
|
||||
- otherwise height is derived from `aspect_ratio`
|
||||
|
||||
That yields a clean rule for the skill:
|
||||
|
||||
- if the user asks for publication width, use `save_figure(..., spec=..., width=...)`
|
||||
- if the user only wants quick draft export, keep the recommendation minimal
|
||||
|
||||
## 5. `batch_export(...)`
|
||||
|
||||
`batch_export(...)` is the publication-aware multi-format lane.
|
||||
|
||||
Source behavior:
|
||||
|
||||
- it takes a `base_path`
|
||||
- it accepts publication export controls such as `spec`, `width`, `height_mm`, `aspect_ratio`, and `dpi`
|
||||
- appends each explicit suffix from `formats`
|
||||
- relayouts the figure through `_export_with_publication_layout(...)` for each target format
|
||||
- restores the original in-memory figure size/state after export
|
||||
|
||||
This is the right recommendation when the user needs, for example:
|
||||
|
||||
- `PDF` for manuscript submission
|
||||
- `SVG` for downstream editing
|
||||
- `PNG` for slides or issue threads
|
||||
|
||||
Operational implication:
|
||||
|
||||
- use `batch_export(...)` when the task needs multiple publication-style outputs from the same figure,
|
||||
- do not describe it as a plain `savefig` loop.
|
||||
|
||||
## 6. What `_save_basic_figure(...)` still does
|
||||
|
||||
`_save_basic_figure(...)` is still relevant, but it is no longer the main multi-format path for `batch_export(...)`.
|
||||
|
||||
It remains the lower-level path used for:
|
||||
|
||||
- direct basic export helpers,
|
||||
- size-preserving panel export in `export/panels.py`,
|
||||
- and internal single-target save operations that do not need publication relayout.
|
||||
|
||||
From the source, it also handles:
|
||||
|
||||
- output directory creation
|
||||
- vector-text rcParams (important for editable SVG/PDF text handling)
|
||||
- post-layout legend alignment
|
||||
- post-layout callbacks attached by plot code
|
||||
- trim/tight bbox behavior
|
||||
|
||||
So export quality is partially centralized in the export layer, not only inside plot modules.
|
||||
|
||||
## 7. Panel export lane
|
||||
|
||||
`export/panels.py` defines the multi-panel handoff path.
|
||||
|
||||
Key components:
|
||||
|
||||
- `PanelExportRecord`
|
||||
- `export_panel(...)`
|
||||
- `export_panels(...)`
|
||||
- `_write_panel_index(...)`
|
||||
|
||||
A `PanelExportRecord` stores:
|
||||
|
||||
- `panel_id`
|
||||
- `path`
|
||||
- `format`
|
||||
- `exported_at`
|
||||
- `figma_node_name`
|
||||
- `pubfig_version`
|
||||
- optional `title`
|
||||
- optional `label`
|
||||
|
||||
This shows that panel export is not only file emission. It also preserves minimal sync metadata.
|
||||
|
||||
## 8. Title stripping is intentional
|
||||
|
||||
One subtle but important source behavior:
|
||||
|
||||
- `_temporarily_strip_titles(...)` removes figure/axes titles during panel export by default unless `include_title=True`
|
||||
|
||||
Operational implication:
|
||||
|
||||
- panel-first composite assembly usually wants clean panel artwork,
|
||||
- whole-figure titles and layout labels are often handled later,
|
||||
- prefer exporting clean panel art first and adding whole-figure titles or layout labels downstream when needed.
|
||||
|
||||
## 9. Publication-aware vs size-preserving panel export
|
||||
|
||||
`export_panel(...)` has two modes:
|
||||
|
||||
### Publication-aware mode
|
||||
|
||||
Triggered when any of these are supplied:
|
||||
|
||||
- `spec`
|
||||
- `width`
|
||||
- `height_mm`
|
||||
|
||||
Then it delegates to `save_figure(...)`.
|
||||
|
||||
### Size-preserving mode
|
||||
|
||||
If none of those are supplied, it falls back to `_save_basic_figure(...)` and preserves the figure’s current size.
|
||||
|
||||
Skill implication:
|
||||
|
||||
- for reproducible paper panels, specify publication export parameters
|
||||
- for design review or quick composition, preserving current size may be acceptable
|
||||
|
||||
## 10. Multiple panel export
|
||||
|
||||
`export_panels(...)` does three main things:
|
||||
|
||||
1. normalize and validate panel ids,
|
||||
2. resolve labels for each panel,
|
||||
3. export each panel and optionally write `panel-index.json`.
|
||||
|
||||
Default recommendation:
|
||||
|
||||
- prefer this route when the user wants a structured panel directory rather than a single whole-figure asset.
|
||||
|
||||
## 11. Overwrite and safety behavior
|
||||
|
||||
From `_ensure_writable_target(...)`:
|
||||
|
||||
- an existing panel file raises unless `overwrite=True`
|
||||
|
||||
That is useful for skill guidance because it means refresh-in-place is an explicit decision.
|
||||
|
||||
## 12. Recommended source-faithful export patterns
|
||||
|
||||
### Single paper figure
|
||||
|
||||
- plot with `pubfig.<chart_family>(...)`
|
||||
- save with `save_figure(fig, 'out/figure1.pdf', spec='nature', width='single')`
|
||||
|
||||
### Same figure in several formats
|
||||
|
||||
- plot once
|
||||
- export with `batch_export(fig, 'out/figure1', formats=('pdf', 'svg', 'png'), spec='nature', width='single', dpi=300)`
|
||||
|
||||
### Multi-panel downstream assembly
|
||||
|
||||
- generate each panel as a separate `Figure`
|
||||
- export with `export_panels(...)`
|
||||
- use the index file for composite/Figma-aware downstream handling
|
||||
@@ -0,0 +1,216 @@
|
||||
# pubtab architecture (source-driven)
|
||||
|
||||
This guide explains `pubtab` from the actual package layout under `pubtab/src/pubtab`.
|
||||
|
||||
Core files:
|
||||
|
||||
- `__init__.py`
|
||||
- `cli.py`
|
||||
- `models.py`
|
||||
- `reader.py`
|
||||
- `renderer.py`
|
||||
- `config.py`
|
||||
- `_preview.py`
|
||||
- `tex_reader.py`
|
||||
- `backends/`
|
||||
- `themes/`
|
||||
|
||||
## 1. Start at `pubtab.__init__`
|
||||
|
||||
The public contract is defined in `__init__.py`.
|
||||
|
||||
Public exports are intentionally small:
|
||||
|
||||
- `xlsx2tex`
|
||||
- `preview`
|
||||
- `compile_pdf`
|
||||
- `tex_to_excel`
|
||||
- `SpacingConfig`
|
||||
|
||||
This is the key architectural signal: `pubtab` exposes a compact API, while the real complexity is pushed into reader/renderer/preview internals.
|
||||
|
||||
## 2. Core data model layer
|
||||
|
||||
`models.py` defines the structured table representation.
|
||||
|
||||
Important dataclasses:
|
||||
|
||||
- `CellStyle`
|
||||
- `Cell`
|
||||
- `TableData`
|
||||
- `SpacingConfig`
|
||||
- `ThemeConfig`
|
||||
- `BackendConfig`
|
||||
|
||||
Source fact:
|
||||
|
||||
- the shared structured representation is `TableData`, not raw Excel cells or raw LaTeX text.
|
||||
|
||||
Forward and reverse conversions both pass through this structured table model.
|
||||
|
||||
## 3. Forward pipeline: Excel to LaTeX
|
||||
|
||||
The main forward path is:
|
||||
|
||||
1. `xlsx2tex(...)` in `__init__.py`
|
||||
2. `read_excel(...)` in `reader.py`
|
||||
3. `render(...)` in `renderer.py`
|
||||
4. optional preview through `_preview.py`
|
||||
|
||||
This gives a clean source-driven decomposition:
|
||||
|
||||
- `reader.py` = file ingestion and structure recovery
|
||||
- `renderer.py` = LaTeX generation
|
||||
- `_preview.py` = compile and raster preview
|
||||
|
||||
## 4. Reverse pipeline: LaTeX to Excel
|
||||
|
||||
The reverse path is:
|
||||
|
||||
1. `tex_to_excel(...)` in `__init__.py`
|
||||
2. `read_tex_multi(...)` from `tex_reader.py`
|
||||
3. writer functions to `.xlsx`
|
||||
|
||||
So roundtrip support is not an afterthought. It is a real architecture branch.
|
||||
|
||||
## 5. `reader.py` is richer than a plain spreadsheet loader
|
||||
|
||||
From the source, `reader.py` does much more than “read cells”:
|
||||
|
||||
- supports `.xlsx` and `.xls`
|
||||
- extracts rich text segments
|
||||
- reconstructs merged cells and spans
|
||||
- reads styling information
|
||||
- trims only trailing globally empty columns
|
||||
- reads pubtab metadata sheets
|
||||
- restores group separators, multicolumn alignment hints, and math-script hints
|
||||
|
||||
Interpretation from source:
|
||||
|
||||
- `pubtab` is optimized for **publication table semantics**, rather than for plain tabular text dumping.
|
||||
|
||||
## 6. `renderer.py` is the central logic hub
|
||||
|
||||
`renderer.py` turns `TableData` into backend-specific LaTeX.
|
||||
|
||||
From the source, it handles:
|
||||
|
||||
- style/theme loading
|
||||
- backend template loading
|
||||
- spacing resolution
|
||||
- column spec construction or projection
|
||||
- tabular vs tabularray differences
|
||||
- merged cells, row/column spans, header rules, vertical rules
|
||||
- background colors and grouped separators
|
||||
- final template rendering
|
||||
|
||||
This file is where most of the difficult publication logic lives.
|
||||
|
||||
For skill design, that means:
|
||||
|
||||
- backend choice is not a cosmetic toggle
|
||||
- column spec and rule behavior are structural concerns
|
||||
- preview/render bugs usually require reading `renderer.py`
|
||||
|
||||
## 7. Theme vs backend is a real split
|
||||
|
||||
The codebase separates:
|
||||
|
||||
- **themes** in `themes/`
|
||||
- **LaTeX backends** in `backends/`
|
||||
|
||||
That is reflected in two dataclasses:
|
||||
|
||||
- `ThemeConfig`
|
||||
- `BackendConfig`
|
||||
|
||||
And in two loaders:
|
||||
|
||||
- `load_theme(...)`
|
||||
- `load_backend(...)`
|
||||
|
||||
This is a major architectural point for the skill:
|
||||
|
||||
- theme decides stylistic defaults,
|
||||
- backend decides LaTeX environment/template behavior.
|
||||
|
||||
Do not explain them as if they were the same thing.
|
||||
|
||||
## 8. Config precedence is explicit
|
||||
|
||||
In `xlsx2tex(...)`, the source implements a clear precedence order:
|
||||
|
||||
1. hardcoded defaults
|
||||
2. YAML config loaded by `config.py`
|
||||
3. explicit function kwargs
|
||||
4. in some roundtrip cases, values recovered from `TableData`
|
||||
|
||||
This is why user-facing guidance should say “CLI flags or function kwargs override YAML config.”
|
||||
|
||||
## 9. Multi-file and multi-sheet support are first-class
|
||||
|
||||
From `__init__.py`:
|
||||
|
||||
- directory input is supported for both forward and reverse paths
|
||||
- sheet enumeration is supported when `sheet=None`
|
||||
- multi-sheet export produces `*_sheetNN.tex`
|
||||
|
||||
The default skill guidance can therefore recommend batch/file-driven workflows, not only one-table-at-a-time usage.
|
||||
|
||||
## 10. Preview is a real compilation layer
|
||||
|
||||
`preview(...)` and `compile_pdf(...)` in `__init__.py` delegate into `_preview.py`.
|
||||
|
||||
That layer:
|
||||
|
||||
- finds or installs `pdflatex`
|
||||
- builds a standalone document
|
||||
- compiles the output
|
||||
- retries missing packages through `tlmgr`
|
||||
- converts PDF to PNG when requested
|
||||
|
||||
Preview is not a fake HTML-like snapshot. It is a real LaTeX compile pipeline.
|
||||
|
||||
## 11. `tex_reader.py` closes the roundtrip loop
|
||||
|
||||
`tex_reader.py` is substantial, not decorative.
|
||||
|
||||
From the source it supports parsing of:
|
||||
|
||||
- `tabular`
|
||||
- `tblr`
|
||||
- `longtblr`
|
||||
- `talltblr`
|
||||
|
||||
It also handles:
|
||||
|
||||
- color parsing
|
||||
- rule parsing
|
||||
- multirow/multicolumn reconstruction
|
||||
- metadata extraction
|
||||
- grouped rows and placeholder cleanup
|
||||
|
||||
This makes `pubtab` suitable for source-aware roundtrip and migration tasks, rather than only one-way Excel export.
|
||||
|
||||
## 12. Reading order for source debugging
|
||||
|
||||
When you need source-level certainty, use this order:
|
||||
|
||||
1. `pubtab/src/pubtab/__init__.py`
|
||||
2. `pubtab/src/pubtab/models.py`
|
||||
3. `pubtab/src/pubtab/reader.py`
|
||||
4. `pubtab/src/pubtab/renderer.py`
|
||||
5. `pubtab/src/pubtab/_preview.py`
|
||||
6. `pubtab/src/pubtab/tex_reader.py`
|
||||
7. `pubtab/src/pubtab/backends/` and `themes/`
|
||||
8. `pubtab/src/pubtab/cli.py` for flag-to-API mapping only
|
||||
|
||||
## 13. Implications for this skill
|
||||
|
||||
The source says the most faithful default workflow is:
|
||||
|
||||
- use `xlsx2tex(...)` or CLI `xlsx2tex` for forward generation,
|
||||
- use `preview(...)` to verify actual compile output,
|
||||
- use `tex_to_excel(...)` for roundtrip or migration tasks,
|
||||
- explain theme/backend separately,
|
||||
- escalate into renderer/source debugging only when table structure or LaTeX behavior is the real problem.
|
||||
@@ -0,0 +1,201 @@
|
||||
# pubtab backend and preview internals (source-driven)
|
||||
|
||||
This guide focuses on the two most important deeper layers in `pubtab`:
|
||||
|
||||
- backend/theme separation
|
||||
- real preview/compile execution
|
||||
|
||||
Primary source files:
|
||||
|
||||
- `pubtab/src/pubtab/themes/__init__.py`
|
||||
- `pubtab/src/pubtab/backends/__init__.py`
|
||||
- `pubtab/src/pubtab/renderer.py`
|
||||
- `pubtab/src/pubtab/_preview.py`
|
||||
|
||||
## 1. Theme and backend are different contracts
|
||||
|
||||
The source separates them cleanly.
|
||||
|
||||
### Theme layer
|
||||
|
||||
In `themes/__init__.py`, a theme resolves to `ThemeConfig`.
|
||||
|
||||
It carries style defaults such as:
|
||||
|
||||
- `column_sep`
|
||||
- `font_size`
|
||||
- `caption_position`
|
||||
- `SpacingConfig`
|
||||
|
||||
### Backend layer
|
||||
|
||||
In `backends/__init__.py`, a backend resolves to:
|
||||
|
||||
- `BackendConfig`
|
||||
- a Jinja template string
|
||||
|
||||
`BackendConfig` carries things like:
|
||||
|
||||
- package requirements
|
||||
- preamble hints
|
||||
- backend identity
|
||||
|
||||
Operational implication:
|
||||
|
||||
- theme = stylistic defaults
|
||||
- backend = LaTeX environment/template machinery
|
||||
|
||||
## 2. Legacy normalization exists, but it is not the conceptual model
|
||||
|
||||
The source still supports legacy composite theme names like `_tabularray` suffixed themes.
|
||||
|
||||
Normalization happens in places like:
|
||||
|
||||
- `_normalize_theme_backend_choice(...)`
|
||||
- `resolve_theme(...)`
|
||||
|
||||
But that compatibility layer should not define the skill’s main explanation.
|
||||
The current conceptual model is still **separate theme + backend**.
|
||||
|
||||
## 3. What `render(...)` actually does
|
||||
|
||||
`renderer.py` is the core place where theme/backend decisions become concrete LaTeX.
|
||||
|
||||
Inside `render(...)`, the source does roughly this:
|
||||
|
||||
1. normalize theme/backend choice
|
||||
2. load theme config
|
||||
3. load backend config and template
|
||||
4. merge default/theme/user spacing
|
||||
5. compute or project column specs
|
||||
6. branch into backend-specific row/cell rendering
|
||||
7. render through Jinja
|
||||
|
||||
Backend choice changes the internal rendering algorithm, not only the final environment name.
|
||||
|
||||
## 4. `tabular` vs `tabularray` from the source perspective
|
||||
|
||||
The source suggests this practical distinction:
|
||||
|
||||
### `tabular`
|
||||
|
||||
- more classic LaTeX path
|
||||
- column spec and rules are handled in the traditional environment
|
||||
- when source `column_spec` exists, renderer tries to preserve classic rule structure
|
||||
|
||||
### `tabularray`
|
||||
|
||||
- dedicated `tblr`-style rendering path
|
||||
- special handling for promoted vertical lines and grouped header boundaries
|
||||
- extra sanitization in preview compile path
|
||||
|
||||
Interpretation from source:
|
||||
|
||||
- `tabularray` is a distinct renderer path with its own structural handling, not merely a cosmetic wrapper around `tabular`.
|
||||
|
||||
## 5. Why backend choice sometimes matters a lot
|
||||
|
||||
From `renderer.py`, backend differences affect:
|
||||
|
||||
- how colspec is interpreted
|
||||
- how vertical rules are preserved or promoted
|
||||
- how merged cells are encoded
|
||||
- how row coloring and header boundaries are emitted
|
||||
|
||||
So if a table is structurally complex, the skill should not present backend choice as arbitrary.
|
||||
|
||||
## 6. Preview is a real LaTeX toolchain
|
||||
|
||||
`_preview.py` proves preview is a full execution pipeline.
|
||||
|
||||
Major steps:
|
||||
|
||||
1. locate `pdflatex`
|
||||
2. install TinyTeX if missing
|
||||
3. build a standalone document around the table
|
||||
4. compile with `pdflatex`
|
||||
5. auto-install missing LaTeX packages when possible
|
||||
6. return PDF or convert to PNG
|
||||
|
||||
Preview is therefore a genuine verification step.
|
||||
|
||||
## 7. `pdflatex` discovery and TinyTeX fallback
|
||||
|
||||
The source checks:
|
||||
|
||||
- system `PATH`
|
||||
- pubtab-managed TinyTeX under `~/.pubtab/TinyTeX`
|
||||
|
||||
If neither exists, it installs TinyTeX automatically.
|
||||
|
||||
This is why the skill can confidently describe preview as relatively self-healing, while still warning that first-run setup may download TeX assets.
|
||||
|
||||
## 8. Missing package retry
|
||||
|
||||
A particularly important source behavior:
|
||||
|
||||
- compile logs are scanned for missing `.sty`
|
||||
- missing style names are mapped to `tlmgr` package names when needed
|
||||
- `tlmgr install <pkg>` is run automatically
|
||||
- compilation is retried
|
||||
|
||||
This is strong evidence that the recommended workflow should include preview, because preview can repair part of the environment on the way.
|
||||
|
||||
## 9. Standalone preview wrapping
|
||||
|
||||
`_build_standalone(...)` does more than wrap text in a document.
|
||||
|
||||
It also:
|
||||
|
||||
- imports backend-required packages
|
||||
- preserves setup commands outside the `resizebox` body
|
||||
- wraps the body in a preview-friendly standalone/minipage layout
|
||||
- converts `\caption{...}` into `\captionof{table}{...}` during float stripping
|
||||
|
||||
This explains why preview output can differ from naive manual compilation if the user simply pastes a table fragment into a document incorrectly.
|
||||
|
||||
## 10. `tabularray` preview sanitization
|
||||
|
||||
Before compilation, `_sanitize_tblr_for_compile(...)` removes some commands that break inside `tblr` preview contexts, including certain row-color and `\cmidrule` forms.
|
||||
|
||||
So when debugging preview-vs-final-document differences, this source behavior matters.
|
||||
|
||||
## 11. PDF to PNG conversion fallback stack
|
||||
|
||||
For PNG previews, the source tries:
|
||||
|
||||
1. `pdf2image`
|
||||
2. fallback to PyMuPDF (`fitz`)
|
||||
|
||||
Preview generation remains resilient even after PDF compilation succeeds.
|
||||
|
||||
## 12. Practical skill guidance from the source
|
||||
|
||||
### Recommend `tabular` when
|
||||
|
||||
- the table is simple/classic,
|
||||
- the user wants conservative LaTeX output,
|
||||
- compatibility matters more than modern layout features.
|
||||
|
||||
### Recommend `tabularray` when
|
||||
|
||||
- grouped headers and more complex structural layouts matter,
|
||||
- the user is already targeting a `tblr`-capable workflow,
|
||||
- the roundtrip/source table has rule structure that benefits from the dedicated renderer path.
|
||||
|
||||
### Recommend preview almost always when
|
||||
|
||||
- the table is intended for publication,
|
||||
- the user is changing backend/theme/colspec,
|
||||
- the task involves debugging table layout,
|
||||
- the task depends on compile-time package correctness.
|
||||
|
||||
## 13. Failure triage order
|
||||
|
||||
When the generated table is wrong, debug in this order:
|
||||
|
||||
1. check theme/backend choice,
|
||||
2. inspect `render(...)` inputs (`TableData`, colspec, span settings),
|
||||
3. run preview/compile,
|
||||
4. inspect missing package or backend-specific compile issues,
|
||||
5. only then move into manuscript-level integration debugging.
|
||||
@@ -0,0 +1,178 @@
|
||||
# pubtab CLI and API flow (source-driven)
|
||||
|
||||
This guide follows the actual control flow from `pubtab/src/pubtab/cli.py` into the public API and then into the internal pipeline.
|
||||
|
||||
## 1. Architectural headline
|
||||
|
||||
`cli.py` is a **thin Click wrapper** over the public API in `pubtab.__init__`.
|
||||
|
||||
Operational implication:
|
||||
|
||||
- command-line behavior should usually match Python API behavior,
|
||||
- when docs disagree, the source of truth is `pubtab.__init__`, not CLI help text alone.
|
||||
|
||||
## 2. CLI commands exposed in `cli.py`
|
||||
|
||||
The main commands are:
|
||||
|
||||
- `pubtab xlsx2tex`
|
||||
- `pubtab themes`
|
||||
- `pubtab tex2xlsx`
|
||||
- `pubtab preview`
|
||||
|
||||
There is also a hidden backward-compatible alias:
|
||||
|
||||
- `convert` -> `xlsx2tex`
|
||||
|
||||
## 3. `xlsx2tex` command flow
|
||||
|
||||
CLI entrypoint:
|
||||
|
||||
- `xlsx2tex_cmd(...)` in `cli.py`
|
||||
|
||||
Control flow:
|
||||
|
||||
1. validate input/output shape
|
||||
2. coerce `--sheet` into int when possible
|
||||
3. build kwargs only for explicitly provided options
|
||||
4. call `pubtab.xlsx2tex(input_file, output, **kwargs)`
|
||||
5. print output summary based on sheet count and preview mode
|
||||
|
||||
This thin-wrapper design matters because the CLI does **not** reimplement conversion logic.
|
||||
|
||||
## 4. `xlsx2tex(...)` API flow
|
||||
|
||||
The public API in `__init__.py` adds the real orchestration:
|
||||
|
||||
### Input modes
|
||||
|
||||
- single Excel file
|
||||
- directory of Excel files
|
||||
- single sheet
|
||||
- all sheets (`sheet=None`)
|
||||
|
||||
### Output path behavior
|
||||
|
||||
- single-sheet export can target a direct `.tex` path
|
||||
- directory input must target a directory
|
||||
- multi-sheet export uses `*_sheetNN.tex`
|
||||
|
||||
This behavior is implemented by `_build_sheet_output_paths(...)` and directory iteration helpers.
|
||||
|
||||
## 5. Config precedence in the real API
|
||||
|
||||
Inside `xlsx2tex(...)`, the source builds parameters in this order:
|
||||
|
||||
1. defaults
|
||||
2. YAML config via `load_config(...)`
|
||||
3. explicit kwargs passed from CLI or Python
|
||||
4. roundtrip-restored values where relevant
|
||||
|
||||
Operational rule:
|
||||
|
||||
- YAML config sets baseline behavior,
|
||||
- CLI flags / Python kwargs override it.
|
||||
|
||||
## 6. Sheet expansion behavior
|
||||
|
||||
When `sheet is None`, the source does not simply choose the first sheet.
|
||||
It calls `list_excel_sheets(...)` and expands all sheet names into separate outputs.
|
||||
|
||||
That is why a single workbook can generate:
|
||||
|
||||
- `table_sheet01.tex`
|
||||
- `table_sheet02.tex`
|
||||
- ...
|
||||
|
||||
The skill should explicitly mention this when users want appendix exports or workbook-wide conversion.
|
||||
|
||||
## 7. Read -> render -> write flow
|
||||
|
||||
For each selected sheet, `xlsx2tex(...)` does:
|
||||
|
||||
1. `read_excel(...)`
|
||||
2. optional header or group-separator reconstruction
|
||||
3. `render(...)`
|
||||
4. write `.tex`
|
||||
5. optional preview generation to `.png`
|
||||
|
||||
Preview is downstream of actual `.tex` generation, not an alternate renderer.
|
||||
|
||||
## 8. `preview` command flow
|
||||
|
||||
CLI entrypoint:
|
||||
|
||||
- `preview_cmd(...)`
|
||||
|
||||
The CLI again mostly validates paths and forwards to `pubtab.preview(...)`.
|
||||
|
||||
The public `preview(...)` API supports:
|
||||
|
||||
- raw LaTeX content
|
||||
- a single `.tex` file
|
||||
- a directory of `.tex` files
|
||||
- `png` or `pdf` output
|
||||
|
||||
A key source detail: when backend is omitted, `preview(...)` may infer it from the LaTeX content using `_resolve_preview_inputs(...)`.
|
||||
|
||||
## 9. Backend inference path
|
||||
|
||||
`_infer_latex_backend(...)` checks for environments like:
|
||||
|
||||
- `tblr`
|
||||
- `longtblr`
|
||||
- `talltblr`
|
||||
|
||||
If found, backend becomes `tabularray`; otherwise `tabular`.
|
||||
|
||||
Operational implication:
|
||||
|
||||
- a preview or compile call can often resolve the correct backend without requiring an explicit `--latex-backend` flag.
|
||||
|
||||
## 10. `compile_pdf(...)` API flow
|
||||
|
||||
Public `compile_pdf(...)` in `__init__.py` does:
|
||||
|
||||
1. detect whether input is raw LaTeX or a file path,
|
||||
2. infer theme/backend if needed,
|
||||
3. delegate to `_preview.compile_pdf(...)`.
|
||||
|
||||
The compile path is still part of the public API, even though the heavy lifting is in `_preview.py`.
|
||||
|
||||
## 11. `tex2xlsx` command flow
|
||||
|
||||
CLI entrypoint:
|
||||
|
||||
- `tex2xlsx(...)` in `cli.py`
|
||||
|
||||
It forwards to `pubtab.tex_to_excel(...)`.
|
||||
|
||||
The public API then handles:
|
||||
|
||||
- single `.tex` file -> one `.xlsx`
|
||||
- multi-table `.tex` -> one workbook with multiple sheets
|
||||
- directory of `.tex` files -> one `.xlsx` per file
|
||||
|
||||
This keeps the reverse path operationally symmetric with the forward path.
|
||||
|
||||
## 12. Why the CLI should stay thin in this skill
|
||||
|
||||
Because the real logic is centralized in `pubtab.__init__`, the skill should:
|
||||
|
||||
- use CLI examples for file-driven shell workflows,
|
||||
- use Python API examples for notebooks or scripted pipelines,
|
||||
- avoid duplicating pseudo-logic that already exists in the library.
|
||||
|
||||
## 13. Recommended source-faithful routing
|
||||
|
||||
### Use CLI when
|
||||
|
||||
- the user already has Excel or `.tex` files on disk,
|
||||
- the task is batch conversion,
|
||||
- the user wants a terminal-first workflow.
|
||||
|
||||
### Use Python API when
|
||||
|
||||
- the user is in a notebook or script,
|
||||
- the table needs custom preprocessing before render,
|
||||
- the agent is composing a larger Python pipeline.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Workflow
|
||||
|
||||
## Default decision order
|
||||
|
||||
1. Identify the scientific communication goal.
|
||||
2. Probe the environment and available assets lightly.
|
||||
3. Decide whether the evidence should be a figure, a table, or a paired figure+table deliverable.
|
||||
4. Choose the strongest representation family.
|
||||
5. Route to `pubfig`, `pubtab`, or both.
|
||||
6. Produce the smallest runnable implementation.
|
||||
7. Specify export outputs explicitly.
|
||||
8. Run publication QA.
|
||||
9. Propose revisions if the result is weak.
|
||||
|
||||
## Handoff checklist
|
||||
|
||||
For every task, try to make these explicit:
|
||||
|
||||
- claim the artifact is supposed to support
|
||||
- data shape and grouping structure
|
||||
- target audience or venue expectations
|
||||
- figure vs table role
|
||||
- exact output filenames and formats
|
||||
- whether the artifact is final, draft, or revision
|
||||
- whether the current environment can execute the proposed route immediately
|
||||
|
||||
## Delivery contract
|
||||
|
||||
A strong response should make clear:
|
||||
|
||||
- which artifact type was chosen,
|
||||
- why it was chosen,
|
||||
- which tool owns each artifact,
|
||||
- what the first runnable command/code path is,
|
||||
- what output files should be produced,
|
||||
- what still needs user input or upstream data.
|
||||
|
||||
## Default output priorities
|
||||
|
||||
Prioritize in this order:
|
||||
|
||||
1. clarity of claim
|
||||
2. correct artifact type
|
||||
3. minimal runnable implementation
|
||||
4. publication-ready export
|
||||
5. QA and revision guidance
|
||||
|
||||
## Graceful degradation when tools are missing
|
||||
|
||||
If `pubfig` or `pubtab` is not installed:
|
||||
|
||||
- keep the workflow going,
|
||||
- provide installation guidance,
|
||||
- provide pseudocode or draft commands,
|
||||
- specify the recommended artifact structure,
|
||||
- preserve the QA and revision guidance.
|
||||
|
||||
## Figure / table split rules
|
||||
|
||||
Use a **figure** when the reader needs to quickly perceive:
|
||||
|
||||
- trend
|
||||
- distribution shape
|
||||
- relationship
|
||||
- calibration or diagnostic behavior
|
||||
- composition or hierarchy
|
||||
- visual comparison across a moderate number of groups
|
||||
|
||||
Use a **table** when the reader needs:
|
||||
|
||||
- exact numbers
|
||||
- many metrics side by side
|
||||
- benchmark grids
|
||||
- ablation matrices
|
||||
- appendix-style detail
|
||||
- reproducible value lookup
|
||||
|
||||
Use **both** when:
|
||||
|
||||
- the figure carries the visual claim,
|
||||
- the table preserves exact values,
|
||||
- or the paper section benefits from a fast visual summary plus precise numeric evidence.
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
from typing import Sequence
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PackageStatus:
|
||||
package: str
|
||||
available: bool
|
||||
version: str | None = None
|
||||
error: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class InstallPlan:
|
||||
installer: str
|
||||
command: tuple[str, ...]
|
||||
|
||||
|
||||
DEFAULT_PACKAGES: tuple[str, ...] = ("pubfig", "pubtab")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Probe and force-install pubfig/pubtab into the active Python environment."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--require",
|
||||
dest="packages",
|
||||
action="append",
|
||||
choices=DEFAULT_PACKAGES,
|
||||
help="Package that must be available. Defaults to pubfig + pubtab.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--prefer",
|
||||
choices=("auto", "uv", "pip"),
|
||||
default="auto",
|
||||
help="Installer selection policy. Default: auto.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--cwd",
|
||||
type=Path,
|
||||
default=Path.cwd(),
|
||||
help="Project directory used for uv-managed detection. Default: current directory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--json",
|
||||
action="store_true",
|
||||
help="Print machine-readable JSON summary.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def probe_package(package: str) -> PackageStatus:
|
||||
try:
|
||||
module = importlib.import_module(package)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
return PackageStatus(package=package, available=False, error=f"{type(exc).__name__}: {exc}")
|
||||
|
||||
version = getattr(module, "__version__", None)
|
||||
return PackageStatus(package=package, available=True, version=str(version) if version else None)
|
||||
|
||||
|
||||
def is_uv_managed(cwd: Path) -> bool:
|
||||
for candidate in [cwd, *cwd.parents]:
|
||||
if (candidate / "uv.lock").exists():
|
||||
return True
|
||||
pyproject = candidate / "pyproject.toml"
|
||||
if pyproject.exists():
|
||||
try:
|
||||
text = pyproject.read_text(encoding="utf-8")
|
||||
except OSError:
|
||||
continue
|
||||
if "[tool.uv" in text or "uv_build" in text:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def choose_installer(prefer: str, cwd: Path) -> str:
|
||||
if prefer in {"uv", "pip"}:
|
||||
return prefer
|
||||
if shutil.which("uv") and is_uv_managed(cwd):
|
||||
return "uv"
|
||||
return "pip"
|
||||
|
||||
|
||||
def build_install_plan(installer: str, package: str) -> InstallPlan:
|
||||
if installer == "uv":
|
||||
command = ("uv", "pip", "install", "--python", sys.executable, package)
|
||||
else:
|
||||
command = (sys.executable, "-m", "pip", "install", package)
|
||||
return InstallPlan(installer=installer, command=command)
|
||||
|
||||
|
||||
def run_command(command: Sequence[str]) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(command, check=False, text=True, capture_output=True)
|
||||
|
||||
|
||||
def emit_text(
|
||||
before: list[PackageStatus],
|
||||
after: list[PackageStatus],
|
||||
attempts: list[dict[str, object]],
|
||||
) -> None:
|
||||
print("[publication-chart-skill] environment summary")
|
||||
print(f"python: {sys.executable}")
|
||||
print("before:")
|
||||
for status in before:
|
||||
suffix = f" ({status.version})" if status.version else ""
|
||||
detail = f" - {status.error}" if status.error else ""
|
||||
print(f" - {status.package}: {'available' if status.available else 'missing'}{suffix}{detail}")
|
||||
if attempts:
|
||||
print("install attempts:")
|
||||
for attempt in attempts:
|
||||
print(f" - {attempt['package']}: {attempt['command']} -> exit {attempt['returncode']}")
|
||||
print("after:")
|
||||
for status in after:
|
||||
suffix = f" ({status.version})" if status.version else ""
|
||||
detail = f" - {status.error}" if status.error else ""
|
||||
print(f" - {status.package}: {'available' if status.available else 'missing'}{suffix}{detail}")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
packages = tuple(dict.fromkeys(args.packages or DEFAULT_PACKAGES))
|
||||
before = [probe_package(package) for package in packages]
|
||||
missing = [status.package for status in before if not status.available]
|
||||
installer = choose_installer(args.prefer, args.cwd.resolve())
|
||||
|
||||
attempts: list[dict[str, object]] = []
|
||||
for package in missing:
|
||||
plan = build_install_plan(installer=installer, package=package)
|
||||
result = run_command(plan.command)
|
||||
attempts.append(
|
||||
{
|
||||
"package": package,
|
||||
"installer": plan.installer,
|
||||
"command": " ".join(plan.command),
|
||||
"returncode": result.returncode,
|
||||
"stdout": result.stdout.strip(),
|
||||
"stderr": result.stderr.strip(),
|
||||
}
|
||||
)
|
||||
|
||||
after = [probe_package(package) for package in packages]
|
||||
payload = {
|
||||
"python": sys.executable,
|
||||
"installer": installer,
|
||||
"before": [asdict(status) for status in before],
|
||||
"attempts": attempts,
|
||||
"after": [asdict(status) for status in after],
|
||||
"ok": all(status.available for status in after),
|
||||
}
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(payload, indent=2, ensure_ascii=False))
|
||||
else:
|
||||
emit_text(before=before, after=after, attempts=attempts)
|
||||
|
||||
return 0 if payload["ok"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user