Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ the template for any multi-paper search. The zh-tw companion is at
are i18n'd across **14 languages**: English, 繁體中文, 简体中文,
日本語, Español, Français, Deutsch, 한국어, Português, Русский,
Italiano, Tiếng Việt, हिन्दी, Bahasa Indonesia.
- **Designed-deck visual identity** (not the default Calibri-on-white
look): per-language typography (Inter for Latin, Microsoft JhengHei
UI / YaHei UI / Yu Gothic UI / Malgun Gothic / Nirmala UI for CJK
+ Hindi), programmatic accent geometry (top accent bar on every
content slide + left band on the cover), academic-style table
formatting (default grid stripped, navy header rule, soft inter-row
dividers, alternating row stripe, middle-vertical alignment, bold
row labels), and a five-colour palette discipline (navy / teal /
grey / light / white) with red **banned** for text (use bold +
teal `#0E7490` for emphasis instead).
- **Dark mode is the default render path.** Build runs on the light
palette, then a post-build pass swaps text + fill + cell-border
RGBs to a dark deck (slide bg `#12151B`, body text `#E5E7EB`,
accent teal swapped to a brighter `#2DD4BF`). OLED projectors and
low-light venues get the dark deck without authors having to think
about it; pass `--light-mode` (CLI), uncheck **Light mode** (GUI
Deck tab), or `ExportOptions(dark_mode=False)` (programmatic) to
opt out for projectors in well-lit rooms or for printed handouts.
- `.xlsx` — Papers sheet + Query provenance sheet, hyperlinked URL /
PDF, frozen header, auto column widths. Column 5 (**Source**) shows
the real publication venue (e.g. "IEEE Access"); column 6
Expand Down Expand Up @@ -226,6 +244,13 @@ the template for any multi-paper search. The zh-tw companion is at
limit (token bucket), `defusedxml` for any XML payload,
path-traversal-safe export paths, no `eval` / `exec` / `pickle` on
user input.
- **zh-tw / zh-cn vocabulary guard**: ~244 regex patterns in
`tests/test_i18n.py::test_zh_tw_files_use_traditional_chinese_vocabulary`
catch Simplified-Chinese loan words rendered with Traditional hanzi
(e.g. `內存` → `記憶體`, `魯棒性` → `穩健性`, `軟件` → `軟體`,
`緩存` → `快取`). Same guard runs in reverse for zh-cn locale
strings. Full rule + the regex catalogue live in
`.claude/agents/language-vocabulary-check.md`.

## Quick start

Expand Down Expand Up @@ -369,7 +394,7 @@ Tools:
| `fetch_paper` | arXiv / DOI / PMID / IEEE identifier → single paper. |
| `fetch_pdf_text` | Download one PDF, return extracted body text. **The MCP path to "I read the paper".** |
| `download_pdfs` | Batch-download a papers list's PDFs into `{out_dir}/pdfs/`. Returns per-paper results keyed by BibTeX key. |
| `export` | Papers list + formats → writes `.pptx/.xlsx/.md/.bib/.json`. Accepts a `summary` field per paper for the rich thesis-style schema and `max_slides_per_paper` (default 25). |
| `export` | Papers list + formats → writes `.pptx/.xlsx/.md/.bib/.json`. Accepts a `summary` field per paper for the rich thesis-style schema, `max_slides_per_paper` (default 25), and `dark_mode` (default `true` — the project's dark-deck post-pass; pass `false` for the printable light variant). |
| `pptx_inspect` | Read slide / shape structure of an existing deck. |
| `pptx_update_slide` | Replace `title` / `body` / `meta` (by shape name) or arbitrary shapes by index. |
| `pptx_delete_slide` | Remove a slide and its part relationship. |
Expand All @@ -385,7 +410,7 @@ LLM-as-agent flow (no `ANTHROPIC_API_KEY` needed — the LLM is the agent):
4. fetch_pdf_text(pdf_url=paper.pdf_url) # per paper
5. (the LLM reads body text, produces a structured `summary` dict)
6. export(papers=[{...paper, "summary": {pain_points: [...], rq_results: [...]}}],
language="zh-tw", formats=["pptx","bib"], ...)
language="zh-tw", formats=["pptx","bib"], dark_mode=true, ...)
```

Full reference in [`docs/mcp.md`](docs/mcp.md).
Expand Down Expand Up @@ -433,11 +458,13 @@ pip install autopapertoppt[gui]
autopapertoppt-gui # or: autopapertoppt gui
```

The window has four tabs — **Search** (functional), **Settings**
(functional, persists API keys via QSettings), **Enrich**, and
**Deck** (the latter two land in a follow-up). The Windows release
zip ships the Nuitka-compiled bundle with PySide6 included, so
`autopapertoppt.exe gui` works without a separate Python install.
The window has four tabs — **Search**, **Settings** (persists API keys
via QSettings), **Enrich** (drives the LLM-as-agent / Python-pipeline
enrichment over a `collection_ready` signal), and **Deck** (the Light
mode toggle + slide-cap + max-figures controls flow through to
`ExportOptions`). The Windows release zip ships the Nuitka-compiled
bundle with PySide6 included, so `autopapertoppt.exe gui` works
without a separate Python install.
**UI ships in all 14 languages** (English, 繁體中文, 简体中文,
日本語, Español, Français, Deutsch, 한국어, Português, Русский,
Italiano, Tiếng Việt, हिन्दी, Bahasa Indonesia) — first run picks
Expand Down
16 changes: 13 additions & 3 deletions autopapertoppt/mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
LLM agent can drive the PDF retrieval step before authoring rich
summaries.
- export(papers, keywords, formats, out_dir, filename_stem, include_abstract,
language, max_slides_per_paper) -> {written: {fmt: path}}
language, max_slides_per_paper, dark_mode) -> {written: {fmt: path}}
formats may be any of: pptx, xlsx, md, bib, json
papers[*].summary may include rich fields (pain_points, research_question,
headline_metrics, technique_table, literature_table, method_sections,
research_questions, rq_results, …) — when present, the PPT switches to
thesis-style layout.
thesis-style layout. ``dark_mode`` defaults to True (project default);
pass False for the light/printable variant.
- pptx_inspect(path) -> {slides: [...]}
- pptx_update_slide(path, slide_index, title?, body?, meta?, shape_updates?) -> {path}
- pptx_delete_slide(path, slide_index) -> {path}
Expand Down Expand Up @@ -284,19 +285,27 @@ def export(
include_abstract: bool = True,
language: str = "en",
max_slides_per_paper: int | None = 25,
dark_mode: bool = True,
) -> dict[str, Any]:
"""Export a list of papers (from search / fetch_paper) to disk.

Each paper dict may carry a ``summary`` field — when populated with
the rich-tier shape (pain_points, research_question, headline_metrics,
technique_table, literature_table, method_sections, research_questions,
rq_results, …), the PPT exporter switches to thesis-style layout.
``language`` accepts en / zh-tw / zh-cn / ja.
``language`` accepts en / zh-tw / zh-cn / ja / es / fr / de / ko /
pt / ru / it / vi / hi / id.

``max_slides_per_paper`` caps the per-paper slide count after the
priority-based trim (cover/references/contributions are kept,
Q&A/figure slides drop first). Default 25; pass ``0`` (or
``None``) for unlimited.

``dark_mode`` defaults to True — the post-build pass swaps the
brand palette to dark slide background (#12151B) + near-white
text (#E5E7EB) so OLED projectors and low-light venues don't
glare. Pass False for the light/printable variant (white slide
background + navy text).
"""
if not papers:
raise AutoPaperToPPTError("export requires at least one paper")
Expand All @@ -318,6 +327,7 @@ def export(
include_abstract=include_abstract,
language=language,
max_slides_per_paper=slide_cap,
dark_mode=dark_mode,
)
written = export_collection(collection, options)
return {
Expand Down
32 changes: 32 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,38 @@ how much info each paper carries:
All three tiers share the same shape-naming convention so
`pptx_edit.update_slide(..., title=...)` looks up shapes by name.

### Post-build visual-identity passes

After the chosen tier builds the deck on the light palette, three
non-invasive walk-and-rewrite passes run before the file is saved:

1. **Typography** (`_apply_typography(prs, language)`) — walks every
text run, writes `<a:latin typeface=…>` AND `<a:ea typeface=…>` on
the run's XML based on `_FONT_FAMILIES[language]`. Setting only
`run.font.name` (the Latin slot) leaves CJK glyphs in PowerPoint's
default East-Asian font; both slots matter.
2. **Accent geometry** (`_decorate_with_accents(prs)`) — adds the
`accent_top` bar to every content slide and an `accent_left` band
to the cover. Both are full-width / full-height navy rectangles
the user never sees as separate shapes but instantly reads as
"this deck has an identity".
3. **Dark-mode recolour** (`_apply_dark_mode(prs)`, runs when
`ExportOptions.dark_mode=True`, which is the default) — walks every
slide / shape / run / table cell and swaps light-palette RGBs to
their dark equivalents via `_LIGHT_TO_DARK_TEXT` + `_LIGHT_TO_DARK_FILL`
dicts. The slide background switches to `#12151B`; body text goes
to `#E5E7EB`; the teal accent (`#0E7490`) goes to a brighter
`#2DD4BF`. The pass is intentionally non-invasive: it doesn't
refactor the 100+ direct `_BRAND_*` constant references in the
builders, it just rewrites RGBs after the fact.

The three passes ship with regression tests in
`tests/test_exporters.py`: `test_pptx_default_is_dark_mode`,
`test_pptx_dark_mode_has_no_invisible_runs` (no run is `rgb=None` or
black), `test_pptx_dark_mode_no_light_text_on_light_fill` (no
near-white text inside a near-white-filled callout), and
`test_pptx_no_red_text_runs` (red `#C0392B` is banned for text).

## Why the design choices

| Choice | Reason |
Expand Down
61 changes: 57 additions & 4 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,59 @@ to ``en``.

Every text box on every slide carries a semantic ``name`` (``title``,
``meta``, ``body``, ``subhead``, ``footer``, ``page_number``, ``kpi``,
``rq_box``) so the editing tools can target shapes without depending
on visual position. See :doc:`/pptx_editing`.
``rq_box``, ``rq_question``, ``figure``, ``accent_top``,
``accent_left``) so the editing tools can target shapes without
depending on visual position. See :doc:`/pptx_editing`.

Designed-deck visual identity
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The exporter applies three non-invasive post-build passes so a deck
doesn't look like generic ``add_slide()`` output:

* **Per-language typography** — every text run gets both an
``<a:latin>`` and ``<a:ea>`` typeface override. Latin defaults to
Inter; East-Asian to Microsoft JhengHei UI (zh-tw), YaHei UI
(zh-cn), Yu Gothic UI (ja), Malgun Gothic (ko); Nirmala UI handles
Devanagari (hi). Setting only the Latin slot leaves CJK glyphs in
PowerPoint's default East-Asian font — both slots matter.
* **Accent geometry** — every content slide gets a thin navy top bar
(``accent_top``, full width); the cover slide also carries a left
vertical band (``accent_left``).
* **Academic-style tables** — the default PowerPoint heavy black grid
is stripped; the header row gets a solid navy fill + 1.5pt navy
bottom rule; data rows alternate light blue / white with a 0.5pt
soft inter-row divider; vertical alignment is middle; the first
column is bold so row labels read as headers.

Dark mode
^^^^^^^^^

Dark mode is the **default** render path. The post-build pass swaps
the light palette to a dark deck (slide bg ``#12151B``, body text
``#E5E7EB``, brighter teal accent ``#2DD4BF``) — designed for OLED
projectors and low-light venues. Opt out per render with
``--light-mode`` on the CLI, the **Light mode** checkbox on the
GUI's Deck tab, or ``ExportOptions(dark_mode=False)`` in Python.
Over MCP, pass ``dark_mode: false`` to the ``export`` tool.

Red is **banned as a text colour** in both modes. The sanctioned
emphasis colour is teal ``#0E7490`` (bold + teal for KPI values and
RQ question callouts); grey is for captions / placeholders. Regression
tests in ``tests/test_exporters.py`` pin every contract — no
``rgb=None`` runs, no near-white text on near-white callouts, no
``#C0392B`` text runs.

zh-tw / zh-cn vocabulary guard
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A 244-pattern regex catalogue in
``tests/test_i18n.py::test_zh_tw_files_use_traditional_chinese_vocabulary``
catches Simplified-Chinese loan words rendered in Traditional hanzi
— e.g. ``內存`` (should be ``記憶體``), ``魯棒性`` (``穩健性``),
``軟件`` (``軟體``), ``緩存`` (``快取``). The same guard runs in
reverse for zh-cn strings. Full rule + the regex catalogue live in
``.claude/agents/language-vocabulary-check.md``.

----

Expand Down Expand Up @@ -380,8 +431,10 @@ Tools at a glance:
* - ``export``
- Papers list + formats → writes ``.pptx/.xlsx/.md/.bib/.json``.
Accepts a ``summary`` field per paper that can carry the
full thesis-style schema; accepts ``language`` for i18n and
``max_slides_per_paper`` (default 25; pass ``0`` for unlimited).
full thesis-style schema; accepts ``language`` for i18n,
``max_slides_per_paper`` (default 25; pass ``0`` for unlimited),
and ``dark_mode`` (default ``true`` — dark deck; pass ``false``
for the printable light variant).
* - ``pptx_inspect``
- Read slide / shape structure of an existing deck.
* - ``pptx_update_slide``
Expand Down
59 changes: 44 additions & 15 deletions docs/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ install is needed.

## Tabs

The window has four tabs. The first two are functional in this
release; the other two are placeholders that will land in a
follow-up.
The window has four tabs. **All four are functional in this release.**

### Search

Expand Down Expand Up @@ -90,18 +88,49 @@ Empty values clear the corresponding env var. **Restart the app**
to refresh fetcher singletons that cached the env value at
construction time.

### Enrich (coming soon)

Per-paper PDF + LLM enrichment will land here. For now: set
`ANTHROPIC_API_KEY` in **Settings**, run a search, then export —
the CLI side auto-enriches each paper that has a downloadable PDF.

### Deck (coming soon)

PPTX inspector / editor. The MCP server already exposes
`pptx_inspect` / `pptx_update_slide` / `pptx_reorder_slides` /
`pptx_delete_slide` / `pptx_add_slide`; this tab will wire them
behind a Qt list view + form panel.
### Enrich

Drives the per-paper PDF + LLM enrichment step. The Search tab emits
a `collection_ready` signal as soon as the results table populates;
the Enrich tab catches it and renders a per-paper row showing:

- The paper's BibTeX key + title (clickable → opens the URL).
- A **PDF** column — green tick when a `pdf_url` is on file (Unpaywall
/ S2 / arXiv / CORE.ac.uk lifted it), grey dash otherwise.
- An **Enrich** action that runs either the Python pipeline
(Anthropic API, needs `ANTHROPIC_API_KEY` in Settings) or surfaces
the LLM-as-agent path for runs without an API key.
- A progress strip across the bottom — the worker reports the current
paper, elapsed seconds, and a per-paper success/failure tally.

Authored summaries are merged back onto the in-memory `PaperCollection`,
so a subsequent **Export** picks up the rich-tier layout automatically.

### Deck

The pre-export deck-shaping controls. Wires `ExportOptions` fields
behind Qt widgets:

- **Light mode** checkbox — unchecked (default) ships the dark deck
(slide bg `#12151B`, body text `#E5E7EB`, brighter teal accent
`#2DD4BF`). Tick it for the printable / well-lit-room variant
(white bg + navy text). Mirrors the CLI `--light-mode` flag.
- **Max slides per paper** — integer spinner. Defaults to 25;
`0` means unlimited.
- **Max figures per paper** — controls how many `figures=` entries the
rich-tier layout actually renders. The priority trim keeps the
cover / references / contributions slides intact and drops Q&A /
figure / paper-table slides first when the cap kicks in.
- **Include abstract** checkbox — when off, the deck skips the
abstract slide entirely (useful for an "executive overview only"
variant).

The PPTX inspector / editor surface (drives `pptx_inspect` /
`pptx_update_slide` / `pptx_reorder_slides` / `pptx_delete_slide` /
`pptx_add_slide`) is wired to the same shape names the exporter
produces — `title`, `meta`, `body`, `kpi`, `rq_question`, `figure`,
`accent_top`, `accent_left` — so the editor list view labels each
slide by its semantic role rather than by raw shape index.

## i18n

Expand Down
18 changes: 16 additions & 2 deletions docs/mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ Render a papers list to any combination of `.pptx`, `.xlsx`, `.md`,
"filename_stem": "attention",
"include_abstract": true,
"language": "zh-tw",
"max_slides_per_paper": 25
"max_slides_per_paper": 25,
"dark_mode": true
}
```

Expand All @@ -275,6 +276,15 @@ after the priority-based trim — cover / references / contributions are
kept first; Q&A / figure / paper-table slides drop first. Pass `0`
(or omit the field) for unlimited.

`dark_mode` (default `true`) toggles the post-build recolour pass.
On: dark slide background (`#12151B`) + near-white body text (`#E5E7EB`)
+ darker table-row stripe — designed for OLED projectors and low-light
venues. Off: the light/printable variant (white background + navy text
`#1F3A66`). Both modes share the same builder pipeline — the dark pass
runs over the rendered tree, so the agent doesn't pick layouts up-front.
The teal accent (`#0E7490` → `#2DD4BF` in dark) marks KPI values and RQ
question callouts; red is banned for text in both modes.

Returns:

```json
Expand Down Expand Up @@ -303,7 +313,11 @@ sentence-bucketing on `paper.abstract`.
`fr`, `de`, `ko`, `pt`, `ru`, `it`, `vi`, `hi`, `id`. Drives both the
template strings (Agenda / References / "Paper N of M" / footer) and
the suggested LLM output language when the agent fills in the summary.
Anything outside that set falls back to `en` silently.
Anything outside that set falls back to `en` silently. Each locale
also drives the per-language typography pass (Inter for Latin, plus
Microsoft JhengHei UI for zh-tw / YaHei UI for zh-cn / Yu Gothic UI
for ja / Malgun Gothic for ko / Nirmala UI for hi) — replaces the
PowerPoint Calibri default, which is the biggest "AI-generated" tell.

### `pptx_inspect`

Expand Down
Loading
Loading