feat(gallery): lucide/flag icon redesign + community marketplace (omnivoice-gallery) - #207
Conversation
…emoji) - backend archetypes emit lucide-react icon *names* (cross-platform; emoji render inconsistently across OSes) for use-cases and the 24 featured voices. - new frontend/src/utils/archetypeIcons.jsx: name→lucide map, accent→country flag (country-flag-icons, tree-shaken to ~11), per-category color scale, color-coded avatar tile, and a CSS-animated now-playing equalizer (prefers-reduced-motion aware). - card redesign: real elevated surfaces (cards were invisible on the dark bg), avatar + name + facet sub-line, accent/flag chips, and a footer with Preview / category-colored "Use voice" / Open-in-Designer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…odule Offloads curated + community gallery content to the standalone debpalash/omnivoice-gallery repo — added here as a submodule for authoring, loaded at runtime via the jsDelivr CDN so the binary stays small. Content repo (seeded + pushed separately): manifest.json (24-voice starter pack generated from the featured archetypes), a JSON schema, CONTRIBUTING, and GitHub submission templates carrying consent / no-impersonation guardrails. Backend (api/routers/community.py): configurable sources (env var > file > default), CDN fetch with offline disk cache, strict validation (invalid presets and non-allow-listed audio URLs are dropped, so a bad community entry can neither crash synthesis nor fetch from an arbitrary host), filtering, the prefilled submit URL, and "use" (preset → archetype render path; voice → sha256-verified download). 11 tests. Frontend: a third gallery zone, "Community", reusing the redesigned card, plus "Submit a preset / voice" buttons opening the prefilled GitHub forms. Local-first preserved: network only on open/refresh; everything cached; the built-in generated archetypes need no network, so the gallery is never empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughAdds a community gallery: a git submodule data source, backend manifest loader with strict validation/caching and endpoints to list/filter/use items, tests, frontend icon utilities and hooks, and a redesigned VoiceGallery UI with a new Community zone for browsing, previewing, and adopting community items. ChangesCommunity Marketplace Feature
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser
participant UI as CommunityZone UI
participant client as communityApi
participant Backend as /community API
participant Cache as Gallery Cache (DATA_DIR/gallery_cache)
participant Engine as Archetype Engine
Browser->>UI: open Community tab
UI->>client: listCommunityItems(limit=100)
client->>Backend: GET /community/items
Backend->>Cache: read per-source cached manifest
Cache-->>Backend: cached manifests
Backend-->>client: CommunityPage (items, packs, count, offline)
client-->>UI: CommunityPage
UI->>UI: render grid (ArchetypeCard)
UI->>client: addCommunityItem(item_id, name?)
client->>Backend: POST /community/items/{item_id}/use
alt preset item
Backend->>Engine: render preset to WAV
Engine-->>Backend: WAV bytes
else voice item
Backend->>Backend: download voice audio (host allow-list, optional SHA-256)
end
Backend->>Backend: insert voice_profiles row
Backend-->>client: { profile_id, name }
client-->>UI: profile created (flash)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
| Filename | Overview |
|---|---|
| backend/api/routers/community.py | New community marketplace backend: CDN manifest fetch, offline cache, strict validation, and use/submit endpoints. Sync HTTP in sync FastAPI routes is handled correctly; previous blocking-in-async and redirect-bypass issues remain open from prior review threads. |
| backend/core/archetypes.py | Replaces emoji icon strings with lucide component names across USE_CASES and _FEATURED_SPEC; purely mechanical substitution with no logic changes. |
| backend/tests/test_community.py | 11 tests covering validation drop rules (invalid instruct, unsafe URL, unknown use_case), manifest merge/dedup, endpoint responses from cache, and submit-URL generation — all non-network, fast. |
| frontend/src/utils/archetypeIcons.jsx | New icon utilities: lucide name→component map, country-flag-icons tree-shaken to 10 flags, per-category color scale, ArchetypeAvatar tile, and CSS-animated NowPlaying equalizer. |
| frontend/src/pages/VoiceGallery.jsx | Adds Community zone tab and CommunityZone component; redesigns ArchetypeCard with avatar tile, NowPlaying equalizer, and footer actions. List-view CSS was removed from VoiceGallery.css but the grid/list toggle in ArchetypesZone remains, leaving list mode visually broken. |
| frontend/src/pages/VoiceGallery.css | Card surface redesign: elevated gradient background, NowPlaying animation with prefers-reduced-motion guard, avatar flag badge, and new footer button styles. .archetype-card.list and its dependent rules were removed while the list-mode toggle still exists. |
| frontend/src/api/community.ts | Clean TypeScript API layer mirroring the five new backend endpoints with typed interfaces; no logic concerns. |
| frontend/src/api/hooks.ts | Adds useCommunityItems and useCommunityManifest React Query hooks with 5-minute stale time, consistent with existing archetype hooks. |
| frontend/src/store/gallerySlice.ts | Adds 'community' to GalleryZone union type; one-line change, no logic concerns. |
Sequence Diagram
sequenceDiagram
participant UI as CommunityZone (Frontend)
participant API as community.ts (API layer)
participant BE as community.py (Backend)
participant CDN as jsDelivr CDN
participant Cache as Disk Cache
participant DB as SQLite DB
participant EB as event_bus
UI->>API: listCommunityItems(filters)
API->>BE: GET /community/items
BE->>Cache: "read manifest (if cached & !refresh)"
alt cache hit
Cache-->>BE: manifest JSON
else cache miss or refresh
BE->>CDN: GET manifest.json
CDN-->>BE: manifest JSON
BE->>Cache: write manifest JSON
end
BE-->>API: "{ items, total, ... }"
API-->>UI: CommunityPage
UI->>API: addCommunityItem(id, name)
API->>BE: "POST /community/items/{id}/use"
BE->>Cache: _load() via asyncio.to_thread
alt "type == preset"
BE->>BE: _render_archetype_wav() [await]
else "type == voice"
BE->>CDN: httpx download (asyncio.to_thread + SHA-256 verify)
CDN-->>BE: audio bytes
end
BE->>DB: INSERT INTO voice_profiles
BE->>EB: emit(profiles, created)
BE-->>API: "{ profile_id, name }"
API-->>UI: flash Added to your voices
UI->>API: communitySubmitUrl(type)
API->>BE: "GET /community/submit-url?type=preset"
BE-->>API: "{ url: github.com/.../issues/new?template=... }"
API-->>UI: openExternal(url)
Reviews (2): Last reviewed commit: "refactor(marketplace): rename useCommuni..." | Re-trigger Greptile
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
backend/api/routers/community.py (2)
125-149: ⚡ Quick winConsider logging cache read failures for debugging.
Lines 131-132 and 147-148 silently ignore cache read errors. While the fallback logic is sound, logging these failures would help diagnose cache corruption or permission issues.
📝 Suggested improvement
if not refresh and cache.exists(): try: return json.loads(cache.read_text(encoding="utf-8")) except Exception: - pass + logger.debug("Cache read failed for %s, fetching from network", source) try: import httpx with httpx.Client(timeout=15.0, follow_redirects=True) as client: ... if cache.exists(): try: return json.loads(cache.read_text(encoding="utf-8")) except Exception: - pass + logger.debug("Cache read failed for %s after network error", source) return None🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/api/routers/community.py` around lines 125 - 149, The cache read failures in _fetch_manifest are currently swallowed in both the initial cache load (when not refresh) and the fallback after a failed fetch; update those except blocks to log the exception (e.g., using logger.warning or logger.exception) including the source and the exception details so cache read errors (cache.read_text/json.loads) are recorded for debugging while preserving the existing fallback behavior.
212-269: ⚡ Quick winUse
raise ... from eto preserve exception context.Line 253 raises an HTTPException after catching a generic Exception but doesn't chain the exceptions. This makes debugging harder for API consumers.
🔗 Proposed fix
except HTTPException: raise except Exception as e: logger.error("Community 'use' failed", exc_info=True) - raise HTTPException(status_code=503, detail=f"Couldn't add this voice right now. Error: {e}") + raise HTTPException(status_code=503, detail=f"Couldn't add this voice right now. Error: {e}") from e🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/api/routers/community.py` around lines 212 - 269, In community_use, the generic except block that catches Exception (the one setting logger.error and then raising HTTPException) should chain the original exception to preserve context; change the raise to use exception chaining (raise HTTPException(status_code=503, detail=...) from e) so the original traceback is retained for debugging; locate the except Exception as e in the community_use function (the rendering/downloading try/except) and modify the raise accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/api/routers/community.py`:
- Around line 204-209: The parameter name `type` in function
community_submit_url shadows the Python builtin; rename it (for example to
submission_type) in the function signature and in references inside
community_submit_url, and update its Query declaration accordingly so the
endpoint behavior and template selection ("preset" vs other) remain the same;
ensure any external callers/tests using the query parameter name continue to use
"type" if that query name must be preserved by keeping Query("preset") but
changing only the Python variable name (e.g., submission_type: str =
Query("preset")).
- Around line 173-201: The community_items endpoint uses the parameter name type
which shadows the Python builtin; rename the parameter to a non-built-in
identifier (e.g., item_type) in the community_items signature and update all
internal references (the keep function and filtering where it checks
it.get("type") != ... ) to use that new name, and preserve the external query
name by using FastAPI's Query alias (e.g., item_type: Optional[str] =
Query(None, alias="type")). Ensure no other occurrences still reference the old
name.
In `@frontend/src/api/community.ts`:
- Around line 68-71: The exported function useCommunityItem is incorrectly named
with a "use" prefix which triggers react-hooks lint rules; rename the function
(e.g., to adoptCommunityItem) in frontend/src/api/community.ts (change the
export name from useCommunityItem to adoptCommunityItem) and update all call
sites and imports—specifically replace import and await useCommunityItem(...) in
VoiceGallery.jsx with the new name (adoptCommunityItem) so the module still
performs the same POST behavior without triggering rules-of-hooks.
In `@frontend/src/pages/VoiceGallery.css`:
- Line 423: The CSS keyword in the .now-playing i rule uses "currentColor" with
a capital C which violates stylelint's value-keyword-case; update the background
declaration in the .now-playing i selector to use the lowercase form
"currentcolor" so the rule (background: currentcolor;) conforms to linting and
CI checks.
In `@frontend/src/pages/VoiceGallery.jsx`:
- Around line 395-402: Replace the window.open call inside the submit function
with the project's cross-platform opener: import openExternal from
../api/external and call openExternal(url) after receiving the url from
communitySubmitUrl(type); keep the existing try/catch and flash(t(...)) error
handling but remove window.open usage so desktop (Tauri) builds use
`@tauri-apps/plugin-opener` while web/dev fallback remains intact.
---
Nitpick comments:
In `@backend/api/routers/community.py`:
- Around line 125-149: The cache read failures in _fetch_manifest are currently
swallowed in both the initial cache load (when not refresh) and the fallback
after a failed fetch; update those except blocks to log the exception (e.g.,
using logger.warning or logger.exception) including the source and the exception
details so cache read errors (cache.read_text/json.loads) are recorded for
debugging while preserving the existing fallback behavior.
- Around line 212-269: In community_use, the generic except block that catches
Exception (the one setting logger.error and then raising HTTPException) should
chain the original exception to preserve context; change the raise to use
exception chaining (raise HTTPException(status_code=503, detail=...) from e) so
the original traceback is retained for debugging; locate the except Exception as
e in the community_use function (the rendering/downloading try/except) and
modify the raise accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a9ebac2b-9d3e-479e-b06e-fa29f18043f0
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock,!**/*.lock,!**/bun.lock
📒 Files selected for processing (13)
.gitmodulesbackend/api/routers/community.pybackend/core/archetypes.pybackend/main.pybackend/tests/test_community.pyfrontend/package.jsonfrontend/src/api/community.tsfrontend/src/api/hooks.tsfrontend/src/pages/VoiceGallery.cssfrontend/src/pages/VoiceGallery.jsxfrontend/src/store/gallerySlice.tsfrontend/src/utils/archetypeIcons.jsxomnivoice-gallery
- community_use: run the blocking manifest read + voice download in a thread (asyncio.to_thread) so they don't stall the event loop (greptile P2). - community_submit_url: validate the `source` override against an owner/repo pattern, falling back to the configured default (greptile P1 hardening). - rename the `type` query param to `item_type` (alias="type") so it no longer shadows the Python builtin (coderabbit). - frontend submit buttons use the canonical openExternal() (Tauri-aware) instead of window.open, which doesn't open the system browser in the desktop app. - lowercase the `currentcolor` CSS keyword (stylelint). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ot a hook) Avoids the use-prefix on a plain API function (rules-of-hooks smell). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on the merged #203 gallery. Two commits.
1. Icon redesign — replace emoji with lucide + country flags
Emoji rendered inconsistently across OSes and the cards were invisible on the dark bg.
archetypeIcons.jsx: name→lucide map, accent→country flag (country-flag-icons, tree-shaken to ~11), per-category color scale, color-coded avatar tile, and a CSS now-playing equalizer (respectsprefers-reduced-motion).2. Community marketplace —
debpalash/omnivoice-galleryOffloads curated + community content to a standalone repo (added here as a submodule for authoring; loaded at runtime via jsDelivr CDN so the binary stays small).
manifest.json(24-voice starter pack), JSON schema, CONTRIBUTING, and GitHub submission templates with consent / no-impersonation guardrails.community.py: configurable sources, CDN fetch + offline disk cache, strict validation (invalid presets and non-allow-listed audio URLs are dropped — can't crash synthesis or SSRF), filtering, prefilled submit URL,use(preset→render path; voice→sha256-verified download). 11 tests.Local-first preserved: network only on open/refresh; everything cached; the built-in generated archetypes need no network so the gallery is never empty.
Verified locally: backend 84 tests pass, frontend builds clean.
🤖 Generated with Claude Code
Summary by CodeRabbit