# SLICE 5 — gtd-gantt rendering
> **State-saving rule:** update this file immediately after EVERY completed task (tick the checkbox, update Status and Resume notes) and whenever pausing for input — automatically, without being asked. State on disk must always match reality.
## Status
**COMPLETE** (2026-07-15). All rendering code landed; 140/140 unit tests pass;
`tsc` clean; `npm run dist` builds the `.jpl`. User manually verified in Joplin
desktop. README + SPEC gantt docs added (beyond the original task list, at user
request). No automated front-end tests (per the earlier webview-testing
decision; disambiguation mirrors the unit-tested collectGantt).
## Goal
Render the gantt in the webview: fence interception, message plumbing, DOM, CSS.
## Tasks
- [x] `src/gtd-calendar-renderer.ts`: intercepts `gtd-gantt` fences. A block with
a non-empty `project:` value (cheap regex `GANTT_PROJECT_LINE`, no YAML
lib) → inline `gantt: `;
otherwise emits the chart placeholder (`data-block-type="gantt"`). Project
value is HTML-escaped and unquoted. Empty-project alignment matches
collectGantt (empty ⇒ chart).
- [x] `src/index.ts`: `handleGetGantt` + `getGantt` switch case, mirroring
handleGetKanban (parse → selectedNote → resolveScanFolder → collectGantt →
payload with title/cardDetail/scopeAll/configError/warnings+softcap/chart/
stats). No-selectedNote path returns an empty chart.
- [x] `src/gtd-calendar-webview.js`: `renderGantt` + `buildGanttGrid` and helpers
(offsetDays, clampOffset, monthTicks, todayLocalISO, tooltips). Single CSS
grid: label column (sticky) + `repeat(totalDays, 26px)`; month tick header
row; project header rows; bars via `grid-column` from day offsets (bg/fg
colour or `colourFromId` hash); milestones as ◆ markers (done → dimmed +
struck label, ↻ for recurring); today line spanning body rows; click →
openNote; hover text via native `title` (suppressed when card-detail:none);
title/configError/warnings/stats blocks as in other views (stats footnote
carries the scope: all marker).
- [x] `src/event-calendar.css`: `.gtd-gantt-*` styles (badge, scroll container,
grid, sticky corner/labels, ticks, bars, milestones, today line), using
the same rgba/theme-var conventions as existing styles.
- [x] Manual verification in Joplin desktop — CONFIRMED 2026-07-15. Verified
live: item-block badge (`gantt: bluesky`) renders inline (not a chart);
chart renders with title, month tick header, alphabetical project header
(`bluesky (3)`), a note **bar** spanning begin→end (Jul 1–14), ◆
**milestones** on to-do due dates, and the red **today** line at Jul 15.
Footnote reports item/note/folder counts. Host-note exclusion confirmed
(an item block in the chart's own note is correctly omitted — put items in
separate notes). Unknown-option warning fires correctly for stray keys like
`todos` (gantt has no notes/todos inclusion option).
- [x] Docs (added at user request; not in the original task list): README gantt
section (item vs chart, bar/milestone rules, chart options table) + SPEC.md
§2.5 (dual-role disambiguation rule, item/chart schemas). Screenshots not
committed — verified interactively instead.
## Resume notes
Code complete. Files touched:
- `src/gtd-calendar-renderer.ts` (gantt fence + badge/chart disambiguation)
- `src/index.ts` (handleGetGantt + switch case + imports)
- `src/gtd-calendar-webview.js` (dispatch + renderGantt/buildGanttGrid + helpers)
- `src/event-calendar.css` (.gtd-gantt-* styles)
Design choices worth noting for a resumed session:
- Grid layout is a single `display:grid` with explicit gridRow/gridColumn per
element (label col = 1, day columns = 2..N+1). Bars: `grid-column: off+2 /
endOff+3` (inclusive end). Milestones: `off+2 / span 1`. Today line:
`grid-row: 1 / ` at the today column.
- Hover uses native `title` tooltips (not the calendar's hover-card DOM) — simpler
and reliable; revisit if richer hover cards are wanted. card-detail:none
suppresses tooltips; "always" currently behaves like "hover" (no inline text on
bars) — a possible future refinement, noted but out of scope.
- No automated renderer/webview tests by design (front-end testing deferred, per
the SLICE-boundary discussion). Disambiguation regex mirrors the unit-tested
collectGantt classification.
Next after manual sign-off: SLICE6.