Skip to content

Exclude files and directories from the city via the UI#104

Merged
thalida merged 25 commits into
mainfrom
feat/issue-98-ui-ignores
Jul 17, 2026
Merged

Exclude files and directories from the city via the UI#104
thalida merged 25 commits into
mainfrom
feat/issue-98-ui-ignores

Conversation

@thalida

@thalida thalida commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Closes #98

Hide files and directories from the city through the UI, instead of only via a .codecityignore on disk. Select a road or building, click "Exclude from city" in the right pane, and it is gone. Works on remote URLs too, since nothing is written to the repo.

This branch grew past the core feature into the settings pane it touches, plus a building-height fix that came out of QA. Details below.

Excludes (the #98 core)

Excludes go through the existing scan pipeline, not a separate client-side filter. The frontend keeps your per-repo exclude list in localStorage and sends it as ?exclude= params; the backend feeds those paths into the same _should_skip logic that already handles .codecityignore and the built-in skip list. So the manifest stays backend-authored: every rollup, stat, and signature is computed once, over the already-filtered tree, and the city, tree, search, and overview all drop the excluded path.

  • Backend: extra_exclude_paths threaded through scan_tree / signature_tree, plus a repeated ?exclude= param on /api/manifest and /api/manifest/signature. Additive only, layered after ALWAYS_SKIP + .codecityignore (can't resurrect a default or reach .git).
  • State: a per-repo EXCLUDES store (keyed by src, branch-invariant) + a new { whole: true } mode on persistedSignal so a dynamic-key map actually persists.
  • Fetch: excludes on load + poll; an in-place re-fetch when the loaded repo's set changes (each set caches its own manifest via the signature, so repeats are instant).
  • UI: an "Exclude from city" button in the road/building panes (never on the repo root) + an "Excluded from city" list in a renamed "Scan" settings tab.

Settings pane

The excludes list needed a home, which turned into consistency work across the Settings pane:

  • Tabs reordered to Scan, Appearance, World (opens on Scan). Scan + Appearance sections are accordions, expanded by default; Appearance was converted from flat blocks to real accordions so every tab has the same header-reset + per-row-reset chrome. Only the save logic differs per tab.
  • Customization indicators: a dirty dot (with a white pulse) on the Settings activity-bar icon when anything differs from default, and a per-tab count badge showing how many changed items live under each tab. Excludes fold into the Scan count.

Refactors (folded in per review/feedback)

  • .lucide-icon class renamed to a library-neutral .icon across the app (call sites no longer name the icon library). Full <Icon> wrapper for the remaining lucide-preact imports is filed as Icon abstraction: decouple call sites from lucide-preact #105.
  • ThemeRow component renamed to SettingRow, and the whole theme-* settings-control class vocabulary to setting-* (the real theme system is untouched).
  • Removed the dead inline section variant.

Building heights + widths (from QA)

Heights/widths were min-max normalized per repo, so the largest file always hit MAX regardless of its real size: a repo of tiny files became a cluster of similar tall buildings. Added a per-repo ceiling anchored to the absolute size of the biggest file, tunable via two new Buildings sliders (Full height at (lines), Full width at (KB)). Small-file repos read as a low-rise/narrow city; repos whose biggest file is at/above the reference are unchanged. Layout + decoration golden baselines recaptured (counts identical, only dimensions shifted).

Testing

just test (api pytest + full app suite) and just lint green. New coverage across the exclude flow (backend precedence/signature, persistence round-trip, the switch-guard, URL builders, store, pane/settings UI), the settings indicators, and the building-dimension ceiling. Still wants a manual pass in the running app: exclude a dir and a file, confirm live re-render + persistence across reload on a remote URL and a local path; and eyeball the height scaling on a small-file vs large-file repo.

Out of scope

Editing the on-disk .codecityignore from the UI, glob patterns (exact path only), and un-ignoring backend defaults.

🤖 Generated with Claude Code

thalida added 14 commits July 16, 2026 10:58
The section header restore-all uses Section's reset affordance (via a new
optional onReset for non-draft sections) and per-row restores reuse the shared
theme-row-reset class, so both read as the accent reset glyph and line up at the
content edge, instead of a bespoke gray 'Clear all' button.
The icon sizing hook was named after the icon library, hardcoding lucide into
every icon call site's markup. Renamed to .icon so swapping icon libraries
doesn't mean touching every component. Mechanical class rename, no behavior
change.
Scan and Appearance sections now match World: expanded by default (defaultOpen),
a section-header reset, and per-row resets in the standard head slot (right of
the label) via ThemeRow's resetSlot. Converts the two Appearance sections from
flat inline blocks to Section accordions. Only the save logic differs per tab;
the settings UI is now consistent everywhere.
No SectionNode set inline: true after the Scan tab moved to real accordions;
.controls-inline-section had no CSS rule. Drop the unused branch + field.
It's the generic settings-panel row used by every tab, not theme-specific.
Renames the component + file + imports; leaves the theme-* CSS class vocabulary
alone (a broader rename, and some theme-* classes are genuinely about themes).
)

The theme-* class prefix was the settings panel's generic control vocabulary
(row, slider, toggle, select, range-pair, subgroup, ...), not visual-theme
styling. Renamed to setting-* to match the SettingRow component. The real theme
system (accent/surface/syntax pickers, data-cc-*, themes.css) is untouched.
A count badge on the Settings activity-bar icon shows how many settings +
excludes differ from default (pulses when it changes), and each subtab (Scan,
Appearance, World) shows a dot when anything under it is changed. Reactive
signals in settingsIndicators.ts; excludes are folded into the Scan count/dot
and the total explicitly since they aren't settings-draft stores.
Solid accent with white text was muddy at 16px; invert to a surface pill with
an accent border and accent text for legibility.
)

Move the total off the cramped activity-bar icon to a single dirty dot with a
pulsing ring; each subtab shows its own count of changed items in a small
rounded-square chip (light surface fill, surface border, light text).
settingsIndicators exposes per-tab counts instead of booleans.
A keyed remount doesn't restart a CSS animation (Preact reuses the node), so the
ring only played on first appearance. Restart it explicitly with an animation
reset + reflow in a layout effect keyed on the count.
#98)

The faint expanding ring was easy to miss; use a clear scale bounce, and under
prefers-reduced-motion flash opacity instead of disabling the cue entirely.
Heights and widths were min-max normalized per repo, so the largest file always
hit MAX_FLOORS/MAX_WIDTH regardless of its real size: a repo of tiny files
became a cluster of similar tall buildings. Add a per-repo ceiling anchored to
the absolute size of the biggest file (new FULL_HEIGHT_LINES / FULL_WIDTH_KB
Buildings sliders): small-file repos read as a low-rise/narrow city while the
per-repo relative spread is retained. Large repos (biggest file >= reference)
are unchanged. Recaptured the layout + decoration golden baselines (counts
identical, only dimensions shifted).
@thalida
thalida merged commit b7704d7 into main Jul 17, 2026
1 check passed
@thalida
thalida deleted the feat/issue-98-ui-ignores branch July 17, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Customize codecity ignores from the UI (exclude files/directories from rendering)

1 participant