feat(sidebar): show a project's own icon from project-icon.svg - #67
Merged
Conversation
A project can now ship a project-icon.svg in its root directory; the sidebar shows it in place of the generic folder icon, at the same 16px size. Discovery lives in a GTK-free projecticons module (empty or oversized files are ignored) so it is unit-testable headless. The header row's arrow, icon, and label are now explicitly valign-centered so the text sits properly against whichever icon renders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC
The sidebar only rebuilds rows when the store reports an order change, so a manual refresh with an unchanged session list never re-read project-icon.svg. The refresh action now passes force_rebuild=True, a one-shot flag that makes the post-scan refreshed signal report an order change even when nothing moved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC
Dogfoods the new sidebar feature: Collins working on Collins shows the Tom Collins glass instead of the generic folder icon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC
New "Project icon size" spin row (16-32 px, default 16) under the Session list preferences. Applies to every group-header icon — custom project icons, the folder fallback, and the Favorites/Chats icons — so the sidebar stays visually consistent. Existing header rows resize in place when the setting changes; new rows read it at build time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC
A project row can exist before any agent has run in its directory (new session from within the app), so project-icon.svg may be the first repo-controlled content Collins touches. Two layers of hardening: - projecticons now vets the bytes, not just the path: gzip/svgz is refused (it would expand past the size cap inside librsvg) and the content must be XML-shaped text with an <svg> element near the top, so crafted binaries for other image codecs never leave the module. - The sidebar rasterizes through a PixbufLoader forced to the "svg" type — gdk-pixbuf content sniffing can no longer route repo bytes to another installed codec — and decodes at icon size, so the raster surface is bounded regardless of the document's canvas dimensions. Icon-size changes re-render the texture so custom icons stay sharp. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Each project can now ship its own sidebar icon: drop a
project-icon.svgin the project's root directory and Collins shows it in place of the generic folder icon next to the project name. Projects without one keep thefolder-symbolicfallback, and Favorites/Chats keep their existing icons. The header row's arrow, icon, and label are now explicitly vertically centered so the text sits properly against whichever icon renders.This repo now ships its own
project-icon.svg(the Tom Collins glass app icon), so Collins-on-Collins dogfoods the feature.How
collins/projecticons.py(modeled ongitinfo.py) resolves<cwd>/project-icon.svg, ignoring missing, empty, or implausibly large (>256 KB) files — headlessly unit-testable.GroupHeaderRowrasterizes the custom icon through aGdkPixbuf.PixbufLoaderforced to the SVG codec at exactly the icon size. Custom icons skip thedim-labelclass so they keep their own colors.Security
A project row can exist before any agent has run in its directory (new session from within the app), so
project-icon.svgmay be the first repo-controlled content Collins touches. Hardening for untrusted repos:projecticonsvets the bytes, not just the path: gzip/svgz is refused (it would decompress past the size cap inside librsvg), and content must be XML-shaped text with an<svg>element near the top — crafted binaries for other codecs never leave the module.store.refresh(force_rebuild=True)), so adding or changing an icon while Collins runs shows up on refresh — no file watching.Screenshots
alpha-widgetsships aproject-icon.svg(blue bolt);beta-serverhas none and falls back to the folder icon.Full window:
Icon size setting
A new Project icon size spin row (16–32 px, default 16) under the Session list preferences sizes every group-header icon — custom project icons, the folder fallback, and the Favorites/Chats icons — consistently. Existing rows resize in place when the setting changes. At 24 px:
Testing
python3 -m pytest tests/ -q— 269 passed (14 new intests/test_projecticons.py, 1 new intests/test_store.py)ruff check collins/ tests/— clean🤖 Generated with Claude Code
https://claude.ai/code/session_01NWcqNwkCLdwN4gExvhAAwC