223 lines
14 KiB
Markdown
223 lines
14 KiB
Markdown
---
|
||
name: workspace-chat-polish
|
||
classification: feature
|
||
success_metric: Workspace Chat presents one coherent, accessible navigation and message experience with a contextual sidebar, recognizable authors, composer-based editing, modal deletion, and Favorites/Channels/DMs organization.
|
||
skip: []
|
||
status: in-progress
|
||
created: 2026-11-02
|
||
model_policy:
|
||
roles:
|
||
role-conductor: claude-planning
|
||
role-ia-architect: claude-planning
|
||
role-ui-designer: claude-planning
|
||
role-ux-reviewer: claude-planning
|
||
role-architect: claude-planning
|
||
role-implementer: qwen-coder
|
||
role-reviewer: qwen-coder
|
||
role-a11y-auditor: claude-planning
|
||
escalate_to: claude-planning
|
||
---
|
||
|
||
# Workspace Chat polish
|
||
|
||
## Why
|
||
|
||
The production Chat foundation works, but the current shell exposes too many competing side panels and several interactions do not match the rest of Echodo. This convoy polishes the human Chat experience before AI participation is added. It does not add agents, agent membership, agent execution, or AI delivery.
|
||
|
||
## Scope
|
||
|
||
- Chat routes replace the Spaces sidebar with one reusable Chat-owned sidebar between the global rail and the conversation.
|
||
- Non-Chat workspace views retain the existing Spaces/sidebar layout.
|
||
- The Chat sidebar has exactly three collapsible sections: Favorites, Channels, and DMs.
|
||
- Channels are not grouped by space by default; metadata supports optional filtering, viewing, and grouping.
|
||
- Hover actions also appear on keyboard focus.
|
||
- Messages show avatar/name identity instead of raw user IDs.
|
||
- Editing uses the normal channel composer with Update/Save and Cancel.
|
||
- Deletion uses an accessible confirmation dialog.
|
||
- Favorites use one user-scoped persistence API where feasible.
|
||
- DMs use existing channel membership and support one or many participants.
|
||
|
||
## Roles invoked
|
||
|
||
- role-conductor — scope and dependency framing
|
||
- role-ia-architect — information architecture
|
||
- role-ui-designer — visual and interaction direction
|
||
- role-ux-reviewer — heuristic and flow review
|
||
- role-architect — implementation and data architecture
|
||
- role-implementer — implementation after planning
|
||
- role-reviewer — standards/spec review
|
||
- role-a11y-auditor — WCAG verification
|
||
|
||
## Todos
|
||
|
||
- [ ] Implement the contextual Chat shell without changing non-Chat navigation.
|
||
- [ ] Build Favorites, Channels, and DMs sections with shared row/header primitives.
|
||
- [ ] Resolve channel authors to safe profile identity.
|
||
- [ ] Move edit mode into the primary composer and delete confirmation into a dialog.
|
||
- [ ] Decide and implement the least disruptive favorites/channel-target persistence extension.
|
||
- [ ] Add reusable channel metadata and optional nullable space linkage only if justified by existing data.
|
||
- [ ] Verify one-to-many DM creation and participant projection.
|
||
- [ ] Run UX, accessibility, regression, and responsive verification.
|
||
|
||
## IA — role-ia-architect
|
||
|
||
### Primary hierarchy
|
||
|
||
```text
|
||
Global app shell
|
||
└── Global rail
|
||
└── Chat contextual surface (Chat routes only)
|
||
├── Chat header and tools
|
||
│ ├── Search
|
||
│ ├── Filter
|
||
│ └── Collapse
|
||
├── Favorites
|
||
├── Channels
|
||
└── DMs
|
||
└── Conversation workspace
|
||
├── Channel header
|
||
├── Message history
|
||
├── Thread/reply context when active
|
||
└── Composer
|
||
```
|
||
|
||
The Chat surface is a contextual replacement for the Spaces sidebar, not a third persistent panel. It must be a reusable component that can later be mounted by other workspace views without requiring those views to adopt the Chat route.
|
||
|
||
### Navigation model
|
||
|
||
- The global rail remains the stable top-level navigation.
|
||
- Chat is the selected rail destination for `/[workspaceSlug]/chat` and `/[workspaceSlug]/chat/[channelId]`.
|
||
- The Chat sidebar owns channel discovery and direct-message discovery.
|
||
- The conversation owns message history and composition; it must not render a second channel-navigation sidebar.
|
||
- Favorites are shortcuts, not a separate object hierarchy.
|
||
- Channels are a flat default list. Space metadata may be shown as secondary context or used by an explicit user-selected filter/grouping mode later.
|
||
- DMs are a flat participant-oriented list. A DM row should identify the other participant(s), with a group-DM indicator when there is more than one.
|
||
|
||
### Section structure
|
||
|
||
Each section has a shared header with:
|
||
|
||
- A left-aligned disclosure control and section label.
|
||
- A right-aligned Add action when creation is supported.
|
||
- A right-aligned More menu for section-level actions.
|
||
- Actions hidden at rest but exposed on hover and `:focus-within`.
|
||
- `aria-expanded` on the disclosure control and a labelled region for the section body.
|
||
|
||
There are deliberately no Defaults or New Message sections. New DMs are created through Add in the DMs header; new channels are created through Add in Channels.
|
||
|
||
### Cross-surface reuse
|
||
|
||
Extract the Chat sidebar and its data contract from `ChatWorkspace`. Future surfaces should be able to render it with a workspace handle, optional selected channel, and navigation callbacks without importing the full conversation implementation.
|
||
|
||
## UI direction — role-ui-designer
|
||
|
||
### Layout
|
||
|
||
- Preserve the global rail width and behavior.
|
||
- Use a single secondary Chat surface around 260–300px wide with a clear border separating it from the conversation.
|
||
- Keep the conversation flexible and independently scrollable; the sidebar must not create a second page-level scroll container.
|
||
- On narrow viewports, the Chat surface becomes an accessible sheet/drawer rather than forcing three columns.
|
||
|
||
### Sidebar visual language
|
||
|
||
- Header: `Chat` title, compact Search and Collapse controls.
|
||
- Section headers: compact, left-disclosure/right-actions layout with a minimum 40px target height.
|
||
- Rows: avatar/icon, primary label, optional preview or count, selected state, and unread treatment.
|
||
- Use the existing sidebar colors, button primitives, tooltip behavior, and dropdown menus rather than introducing a parallel visual system.
|
||
- Keep hover-only controls visually quiet, but use `focus-within` and `focus-visible` to expose the same controls to keyboard and assistive-technology users.
|
||
- Channel rows may show a small secondary metadata label only when available; do not make space grouping visually dominant.
|
||
|
||
### Message identity
|
||
|
||
Use the existing avatar primitive. Prefer display name, then email local-part, then a short fallback such as `Unknown member`; never use the full UUID as the visible identity. Preserve a tooltip or accessible description for any abbreviated identity.
|
||
|
||
### Edit flow
|
||
|
||
Selecting Edit should:
|
||
|
||
1. Focus the primary composer.
|
||
2. Load the original body into the composer.
|
||
3. Show a clear `Update` or `Save changes` action.
|
||
4. Show `Cancel`.
|
||
5. Keep the original message visible in history with an editing indicator.
|
||
|
||
The edit state belongs to the conversation scope, not to an inline message row.
|
||
|
||
### Delete flow
|
||
|
||
Use the existing dialog primitive if available, or add the smallest shared dialog primitive required by the codebase. The dialog must state that deletion is destructive, provide Cancel and Delete actions, prevent accidental submission, and restore focus to the originating message action.
|
||
|
||
## UX review — role-ux-reviewer
|
||
|
||
### Strengths
|
||
|
||
- The contextual shell matches the user's mental model: global destinations stay in the rail, Chat-specific discovery stays beside the conversation.
|
||
- Three sections are easier to scan than the former stacked Spaces plus Chat navigation.
|
||
- Composer-based editing preserves spatial consistency and reduces duplicate interaction patterns.
|
||
- A dialog makes destructive intent explicit and provides a reliable cancel path.
|
||
|
||
### Risks and recommendations
|
||
|
||
1. **Recognition over recall — major:** A flat Channels list can become long. Add search/filter affordances now, but keep grouping opt-in so the default remains simple.
|
||
2. **Visibility of system status — major:** Saving edits, failed sends, reconnecting, and delete completion must have visible status and non-visual announcements.
|
||
3. **User control and freedom — major:** Edit requires an obvious Cancel path; delete requires Cancel as the safe/default action and focus restoration.
|
||
4. **Consistency and standards — moderate:** Use the same section header, disclosure, More, Add, avatar, tooltip, dropdown, and dialog primitives used by Spaces/Home.
|
||
5. **Flexibility and efficiency — moderate:** Keyboard focus must reveal the same actions as hover. Do not make users rely on pointer hover to discover Add or More.
|
||
6. **Error prevention — major:** Disable Update/Delete while mutations are pending, preserve the expected revision, and show a recoverable conflict message rather than losing edited text.
|
||
7. **Aesthetic/minimalist design — moderate:** Do not add Defaults, New Message, or duplicate channel lists. Keep summaries short and avoid showing full IDs.
|
||
8. **Help users recover from errors — moderate:** A failed profile lookup needs a stable initials fallback; a failed favorite or DM creation needs an inline alert/toast with retry where safe.
|
||
|
||
### Flow acceptance
|
||
|
||
- Chat route: rail → Chat sidebar → conversation.
|
||
- Non-Chat route: rail → existing Spaces sidebar → view.
|
||
- Edit: message action → composer populated → Update/Cancel → status announcement → history refresh.
|
||
- Delete: message action → confirmation dialog → Cancel/Delete → focus restoration → tombstone/recovery state.
|
||
- Add DM: DMs Add → participant picker supporting one or many members → create/open direct channel.
|
||
|
||
## Architecture — role-architect
|
||
|
||
### Component boundaries
|
||
|
||
- `AppShell` determines whether the current pathname is a Chat route and selects either the existing `Sidebar` or a reusable `ChatSidebar`.
|
||
- `ChatSidebar` owns section state, search/filter state, channel/DM navigation, and sidebar mutations. It must not own message history.
|
||
- `ChatSectionHeader` owns disclosure, hover/focus action visibility, Add, and More semantics.
|
||
- `ChatListRow` is shared by Favorites, Channels, and DMs with variants for metadata, unread state, and participant avatars.
|
||
- `ChatWorkspace` owns channel history, thread state, composer state, and message mutations. Remove its channel-navigation aside once `ChatSidebar` is mounted by the shell.
|
||
- `ChatComposer` accepts an explicit mode (`send` or `edit`) and controlled initial/current body with submit and cancel callbacks.
|
||
- Delete uses a shared accessible dialog primitive, not a message-specific inline panel.
|
||
|
||
### Route detection
|
||
|
||
Use a segment-aware helper based on `usePathname`, not a broad substring match. It should recognize only `/{workspaceSlug}/chat` and descendants, avoiding false positives from object titles or unrelated URLs. Keep the Chat sidebar reusable through props rather than coupling it to route detection internally.
|
||
|
||
### Existing data and APIs
|
||
|
||
- `chatChannelMembers` already supports channel membership and the existing `createDirect` input accepts `participantUserIds`; reuse this for one-to-many DMs.
|
||
- `users` already contains `name` and `avatarUrl`; add a workspace-authorized participant/profile projection to visible channel results rather than exposing raw IDs to the UI.
|
||
- `userFavorites` currently has an object-only foreign key. The preferred evolution is a single favorites router contract with a discriminated target (`object` or `chatChannel`) and storage that preserves object referential integrity while adding a nullable channel target plus a mutual-exclusion check and appropriate unique indexes. Do not weaken cross-workspace authorization.
|
||
- `chatChannels` currently has no space metadata. Add only the minimum reusable fields justified by product use: nullable `spaceId` referencing an object, plus bounded metadata if filtering needs it. Enforce that a referenced object is an eligible workspace space in service authorization; a database FK alone cannot enforce object type.
|
||
- Channel listing should return channel identity, kind, name, unread summary, favorite state, optional space metadata, and authorized participant summaries in one workspace-scoped response where practical.
|
||
|
||
### Security and tenancy
|
||
|
||
Every channel, favorite, space link, and participant query must resolve through the workspace context and verify membership. DMs must never reveal participant names or previews for channels the caller cannot see. Favorite toggles must verify the target belongs to the active workspace and the caller has access to the channel.
|
||
|
||
### Implementation order
|
||
|
||
1. Add route-aware shell seam and extract ChatSidebar contracts without changing data behavior.
|
||
2. Add profile projection and controlled composer edit state; replace inline delete with dialog.
|
||
3. Add/reuse favorites target contract, channel metadata, and participant-aware sidebar query only where required by the UI.
|
||
4. Implement sidebar sections, interactions, filters, favorites, DM creation UI, and responsive presentation.
|
||
5. Run UX review, accessibility audit, component tests, type-check, and production build.
|
||
|
||
### Testing requirements
|
||
|
||
- Route-aware shell renders ChatSidebar only for Chat routes and preserves Sidebar elsewhere.
|
||
- Section disclosure and hover/focus actions expose correct accessible names and `aria-expanded` state.
|
||
- Profile fallback never renders a raw UUID as the primary author label.
|
||
- Edit populates the main composer, submits the edit mutation, supports Cancel, and preserves conflict recovery.
|
||
- Delete dialog opens, traps/restores focus, disables duplicate submission, and leaves the message unchanged on Cancel.
|
||
- Favorite authorization rejects cross-workspace targets.
|
||
- Channel metadata and optional space links remain tenant-scoped.
|
||
- DM creation validates one or more additional participants and renders participant identity safely.
|