Files
openchamber/FEATURES_PLAN.md
T
shekohexandBohdan Triapitsyn 1d8ff97c95 feat(ui): add dynamic window title and sprite-based project/file icons (#529)
* feat(ui): add dynamic titles and sprite-based project/file icons

* feat(files): add viewer syntax fallback and tab file icons

* fix(files): restore file viewer highlighting and add diff file icons

* feat(git): add file icons and async file-viewer syntax fallback

* fix(files): force codemirror token colors in file viewer

* feat(files): add shiki view mode for file viewer

* fix(files): force codemirror parse after programmatic content updates

* feat(files): support markdown frontmatter preview

* feat(chat): use pierre diffs for tool previews

* feat(chat): add configurable beautiful-mermaid rendering

* feat(perf): virtualize chat rendering and add react-scan toggle

* feat(build): enable React Compiler in Vite React apps

* fix(chat): reduce rerenders from tooltips and streamed activity

* fix(ui): make MessageList React Compiler safe

* chore(ui): batch commit remaining pending ui updates

* fix: polish chat and diff preview rendering

- Keep Mermaid action buttons fixed while diagram content scrolls
- Align Diff All Files headers and match Git-style path truncation
- Default chat tool diffs to unified view with lightweight indicators disabled

* fix: preserve file tree expansion and delay git action label collapse

* fix: refine project icon controls in settings

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
2026-02-27 20:03:42 +02:00

54 lines
3.9 KiB
Markdown

**Plan**
**1) Dynamic Window Title (web + desktop)**
- Add `packages/ui/src/hooks/useWindowTitle.ts` and call it from `packages/ui/src/App.tsx`.
- Title parts: `[projectName] | [instanceName if non-local] | OpenChamber`.
- `projectName`: from `useProjectsStore` active project; prefer `label`, else basename of `path`.
- `instanceName` (desktop only): reuse `desktopHostsGet + locationMatchesHost` logic (same as `Header.tsx`), omit when local.
- Apply:
- always set `document.title`
- if Tauri: `@tauri-apps/api/window``getCurrentWindow().setTitle(title)` (best-effort)
**2) Custom Project Icons (PNG/JPEG/SVG) stored on server + persisted in `settings.json`**
- Extend project schema:
- `packages/ui/src/lib/api/types.ts` `ProjectEntry` add `iconImage?: { mime: string; updatedAt: number; source: 'custom' | 'auto' } | null`
- Update UI+server sanitizers to preserve it:
- `packages/ui/src/stores/useProjectsStore.ts` `sanitizeProjects`
- `packages/ui/src/lib/persistence.ts` `sanitizeProjects`
- `packages/web/server/index.js` `sanitizeProjects`
- Server storage + API (Express):
- Add JSON parsing for `req.path.startsWith('/api/projects')` in the existing body-parser gate (`packages/web/server/index.js` around the `express.json` middleware switch).
- Store icon files under `~/.config/openchamber/project-icons/` (i.e. `OPENCHAMBER_DATA_DIR/project-icons`), filename based on `sha1(projectId)` + extension (prevents path traversal / odd IDs).
- New routes in `packages/web/server/index.js`:
- `GET /api/projects/:projectId/icon` → serve file (Content-Type from stored mime, `Cache-Control: immutable` with `?v=updatedAt`)
- `PUT /api/projects/:projectId/icon` body `{ dataUrl: string }` → validate mime, decode, write file, update `projects[].iconImage` in `settings.json` via `persistSettings`
- `DELETE /api/projects/:projectId/icon` → delete file(s), clear `iconImage`, persist
- `POST /api/projects/:projectId/icon/discover` → best-effort “try this first”: search project dir for `favicon.(ico|png|svg|jpg|jpeg|webp)` using existing `searchFilesystemFiles()`, pick shortest match, store it as `source:'auto'` (skip if `source:'custom'` unless `force=true`)
- UI wiring:
- Add async actions in `packages/ui/src/stores/useProjectsStore.ts`: `uploadProjectIcon(id, file)`, `removeProjectIcon(id)`, `discoverProjectIcon(id)`
- Update renderers to prefer `iconImage` over `PROJECT_ICON_MAP`:
- `packages/ui/src/components/layout/NavRail.tsx`
- `packages/ui/src/components/sections/projects/ProjectsSidebar.tsx`
- `packages/ui/src/components/chat/MobileSessionStatusBar.tsx`
- Add upload/remove/(discover) UI controls:
- `packages/ui/src/components/sections/projects/ProjectsPage.tsx`
- `packages/ui/src/components/layout/ProjectEditDialog.tsx`
- `<img src={`/api/projects/${encodeURIComponent(id)}/icon?v=${updatedAt}`}>` (small size, fits existing tile layout)
**3) File Tree File-Type Icons (full icon pack)**
- Vendor the full directory from `/tmp/opencode/packages/ui/src/assets/icons/file-types` into `packages/ui/src/assets/icons/file-types/`.
- Add `packages/ui/src/lib/fileTypeIcons.ts`:
- build an icon URL map with `import.meta.glob('../assets/icons/file-types/*.svg', { eager: true, as: 'url' })`
- map filename/extension → icon key (use `getLanguageFromExtension()` + a small alias map, plus `_light` variant selection based on `useThemeSystem().currentTheme.metadata.variant`)
- fallback to `document.svg`
- Add `packages/ui/src/components/icons/FileTypeIcon.tsx` (renders the resolved icon URL).
- Replace current generic file icons with `FileTypeIcon` (keep folder icons as-is):
- `packages/ui/src/components/layout/SidebarFilesTree.tsx`
- `packages/ui/src/components/views/FilesView.tsx`
- `packages/ui/src/components/chat/ServerFilePicker.tsx`
**Verification (end of build mode)**
- `bun run type-check`
- `bun run lint`
- `bun run build`