|
| 1 | +# Mobile Files Content PC Alignment Implementation Plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| 4 | +
|
| 5 | +**Goal:** Restyle the mobile workspace files content area so its tabs, actions, search, file tree, and Git list align with the flat PC sidebar language while preserving mobile touch sizing and existing behavior. |
| 6 | + |
| 7 | +**Architecture:** Keep the current `MobileFilesSheet`, `FileTreePanel`, and `GitPanel` structure, then tighten the mobile-only CSS scopes so the content area reuses PC sidebar semantics instead of the current floating card language. Lock the redesign in stylesheet tests first, then update the scoped mobile selectors in `components.css`, and finally rerun the targeted mobile files tests to verify behavior did not regress. |
| 8 | + |
| 9 | +**Tech Stack:** TypeScript, React, Vitest, Testing Library, vanilla CSS custom properties |
| 10 | + |
| 11 | +**Spec reference:** `docs/superpowers/specs/2026-05-18-mobile-files-content-pc-alignment-design.md` |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## File Structure |
| 16 | + |
| 17 | +- Modify: `packages/web/src/styles/components.theme.test.ts` |
| 18 | + - Replace the current mobile files surface assertions with the new flat-panel contract. |
| 19 | +- Modify: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx` |
| 20 | + - Keep the current structural assertions aligned if header wrappers or class expectations shift. |
| 21 | +- Modify: `packages/web/src/styles/components.css` |
| 22 | + - Rewrite the scoped mobile files content selectors to align tabs, actions, search, file tree, and Git mobile surfaces with PC sidebar language. |
| 23 | +- Modify: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.tsx` |
| 24 | + - Only if needed for a lightweight header wrapper that improves tab/actions alignment without changing behavior. |
| 25 | + |
| 26 | +## Task 1: Lock The New Mobile Files Style Contract In Tests |
| 27 | + |
| 28 | +**Files:** |
| 29 | +- Modify: `packages/web/src/styles/components.theme.test.ts` |
| 30 | +- Test: `packages/web/src/styles/components.theme.test.ts` |
| 31 | + |
| 32 | +- [ ] **Step 1: Write the failing mobile files content style assertions** |
| 33 | + |
| 34 | +Update the mobile workspace home-screen style test in `packages/web/src/styles/components.theme.test.ts` so it captures the new files content language: |
| 35 | + |
| 36 | +```ts |
| 37 | + const mobileFilesSurface = getLastRuleBlock(".mobile-sheet--files .file-tree-shell--mobile"); |
| 38 | + const mobileFilesGitSurface = getLastRuleBlock(".mobile-sheet--files .git-panel--mobile"); |
| 39 | + const mobileFilesSegmented = getLastRuleBlock(".mobile-files-sheet__segmented"); |
| 40 | + const mobileFilesSegment = getLastRuleBlock(".mobile-files-sheet__segment"); |
| 41 | + const mobileFilesSegmentActive = getLastRuleBlock(".mobile-files-sheet__segment.active"); |
| 42 | + const mobileFilesSegmentIndicator = getLastRuleBlock(".mobile-files-sheet__segment.active::after"); |
| 43 | + const mobileFilesTabAction = getLastRuleBlock(".mobile-files-sheet__tab-action"); |
| 44 | + const mobileFileSearch = getLastRuleBlock(".file-tree-shell--mobile .file-tree-search"); |
| 45 | + const mobileFileRow = getLastRuleBlock(".file-tree-shell--mobile .tree-item"); |
| 46 | + const mobileFileRowSelected = getLastRuleBlock(".file-tree-shell--mobile .tree-item.selected"); |
| 47 | + |
| 48 | + expect(mobileFilesSegmented).toContain("border-bottom: 1px solid color-mix(in srgb, var(--border) 78%, transparent)"); |
| 49 | + expect(mobileFilesSegmented).toContain("border-radius: 0"); |
| 50 | + expect(mobileFilesSegmented).not.toContain("linear-gradient("); |
| 51 | + expect(mobileFilesSegmented).not.toContain("box-shadow:"); |
| 52 | + |
| 53 | + expect(mobileFilesSegment).toContain("padding: 0"); |
| 54 | + expect(mobileFilesSegment).toContain("font-weight: var(--type-label-weight)"); |
| 55 | + expect(mobileFilesSegmentActive).toContain("background: transparent"); |
| 56 | + expect(mobileFilesSegmentIndicator).toContain("height: 1.5px"); |
| 57 | + |
| 58 | + expect(mobileFilesTabAction).toContain("border: none"); |
| 59 | + expect(mobileFilesTabAction).toContain("border-radius: 6px"); |
| 60 | + expect(mobileFilesTabAction).toContain("background: transparent"); |
| 61 | + |
| 62 | + expect(mobileFilesSurface).toContain("border: 1px solid color-mix(in srgb, var(--border) 80%, transparent)"); |
| 63 | + expect(mobileFilesSurface).toContain("border-radius: var(--radius-md)"); |
| 64 | + expect(mobileFilesSurface).not.toContain("box-shadow:"); |
| 65 | + expect(mobileFilesSurface).not.toContain("linear-gradient("); |
| 66 | + |
| 67 | + expect(mobileFilesGitSurface).toContain("border: 1px solid color-mix(in srgb, var(--border) 80%, transparent)"); |
| 68 | + expect(mobileFilesGitSurface).toContain("border-radius: var(--radius-md)"); |
| 69 | + expect(mobileFilesGitSurface).not.toContain("box-shadow:"); |
| 70 | + |
| 71 | + expect(mobileFileSearch).toContain("margin: 0"); |
| 72 | + expect(mobileFileSearch).toContain("border-radius: 0"); |
| 73 | + expect(mobileFileSearch).toContain("border-right: none"); |
| 74 | + expect(mobileFileSearch).toContain("border-left: none"); |
| 75 | + expect(mobileFileSearch).toContain("background: transparent"); |
| 76 | + |
| 77 | + expect(mobileFileRow).toContain("min-height: 40px"); |
| 78 | + expect(mobileFileRow).toContain("border-radius: 0"); |
| 79 | + expect(mobileFileRowSelected).toContain("border-left: 2px solid color-mix(in srgb, var(--accent-blue) 88%, white 12%)"); |
| 80 | +``` |
| 81 | + |
| 82 | +- [ ] **Step 2: Run the style test to verify it fails** |
| 83 | + |
| 84 | +Run: |
| 85 | + |
| 86 | +```bash |
| 87 | +pnpm --filter @coder-studio/web exec vitest run src/styles/components.theme.test.ts |
| 88 | +``` |
| 89 | + |
| 90 | +Expected: |
| 91 | + |
| 92 | +- FAIL because the current mobile files selectors still use rounded segmented chrome, circular actions, and floating-card surfaces |
| 93 | + |
| 94 | +- [ ] **Step 3: Commit nothing yet** |
| 95 | + |
| 96 | +Do not stage or commit after the red run. The next task will make the code pass. |
| 97 | + |
| 98 | +## Task 2: Flatten The Mobile Files Header And Content Surfaces |
| 99 | + |
| 100 | +**Files:** |
| 101 | +- Modify: `packages/web/src/styles/components.css` |
| 102 | +- Modify: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.tsx` (only if layout wrapper is needed) |
| 103 | +- Test: `packages/web/src/styles/components.theme.test.ts` |
| 104 | + |
| 105 | +- [ ] **Step 1: Update the mobile files header styles to match the PC sidebar language** |
| 106 | + |
| 107 | +In `packages/web/src/styles/components.css`, replace the existing `.mobile-files-sheet__segmented`, `.mobile-files-sheet__segment`, `.mobile-files-sheet__tab-actions`, and `.mobile-files-sheet__tab-action` block bodies with this contract: |
| 108 | + |
| 109 | +```css |
| 110 | +.mobile-files-sheet__segmented { |
| 111 | + display: flex; |
| 112 | + align-items: center; |
| 113 | + flex-shrink: 0; |
| 114 | + gap: var(--sp-3); |
| 115 | + padding: 0 0 var(--sp-2); |
| 116 | + border-bottom: 1px solid color-mix(in srgb, var(--border) 78%, transparent); |
| 117 | + border-radius: 0; |
| 118 | + background: transparent; |
| 119 | + box-shadow: none; |
| 120 | +} |
| 121 | + |
| 122 | +.mobile-files-sheet__segment { |
| 123 | + position: relative; |
| 124 | + display: inline-flex; |
| 125 | + align-items: center; |
| 126 | + justify-content: flex-start; |
| 127 | + gap: 6px; |
| 128 | + min-height: 32px; |
| 129 | + padding: 0; |
| 130 | + border: none; |
| 131 | + border-radius: 0; |
| 132 | + background: transparent; |
| 133 | + color: var(--text-tertiary); |
| 134 | + font-size: var(--type-label-size); |
| 135 | + line-height: var(--type-label-line-height); |
| 136 | + font-weight: var(--type-label-weight); |
| 137 | + transition: color var(--duration-fast) var(--ease-out); |
| 138 | +} |
| 139 | + |
| 140 | +.mobile-files-sheet__segment.active { |
| 141 | + background: transparent; |
| 142 | + color: var(--text-primary); |
| 143 | +} |
| 144 | + |
| 145 | +.mobile-files-sheet__segment.active::after { |
| 146 | + content: ""; |
| 147 | + position: absolute; |
| 148 | + right: 0; |
| 149 | + bottom: -10px; |
| 150 | + left: 0; |
| 151 | + height: 1.5px; |
| 152 | + border-radius: 999px; |
| 153 | + background: color-mix(in srgb, var(--accent-blue) 90%, white 10%); |
| 154 | +} |
| 155 | + |
| 156 | +.mobile-files-sheet__tab-actions { |
| 157 | + display: inline-flex; |
| 158 | + align-items: center; |
| 159 | + gap: 4px; |
| 160 | + flex-shrink: 0; |
| 161 | + margin-left: auto; |
| 162 | +} |
| 163 | + |
| 164 | +.mobile-files-sheet__tab-action { |
| 165 | + display: inline-flex; |
| 166 | + align-items: center; |
| 167 | + justify-content: center; |
| 168 | + width: 32px; |
| 169 | + height: 32px; |
| 170 | + padding: 0; |
| 171 | + border: none; |
| 172 | + border-radius: 6px; |
| 173 | + background: transparent; |
| 174 | + color: var(--text-tertiary); |
| 175 | + transition: |
| 176 | + background-color var(--duration-fast) var(--ease-out), |
| 177 | + color var(--duration-fast) var(--ease-out); |
| 178 | +} |
| 179 | +``` |
| 180 | + |
| 181 | +- [ ] **Step 2: Update the mobile-only files surfaces to remove the floating-card shell** |
| 182 | + |
| 183 | +In the `.mobile-sheet--files` section of `packages/web/src/styles/components.css`, rewrite the shared mobile files surface selector: |
| 184 | + |
| 185 | +```css |
| 186 | + .mobile-sheet--files .file-tree-shell--mobile, |
| 187 | + .mobile-sheet--files .git-panel--mobile, |
| 188 | + .mobile-sheet--files .workspace-git-editor, |
| 189 | + .mobile-sheet--files .workspace-git-view { |
| 190 | + overflow: hidden; |
| 191 | + border: 1px solid color-mix(in srgb, var(--border) 80%, transparent); |
| 192 | + border-radius: var(--radius-md); |
| 193 | + background: var(--bg-panel); |
| 194 | + box-shadow: none; |
| 195 | + } |
| 196 | +``` |
| 197 | + |
| 198 | +Keep the existing exclusions for code-editor and diff wrappers unchanged. |
| 199 | + |
| 200 | +- [ ] **Step 3: Align the mobile file-tree search and row styling with the PC sidebar** |
| 201 | + |
| 202 | +In the `file-tree-shell--mobile` section of `packages/web/src/styles/components.css`, keep the touch-friendly height but flatten the presentation: |
| 203 | + |
| 204 | +```css |
| 205 | +.file-tree-shell--mobile .file-tree-search { |
| 206 | + margin: 0; |
| 207 | + border-right: none; |
| 208 | + border-left: none; |
| 209 | + border-radius: 0; |
| 210 | + padding-inline: 12px; |
| 211 | + background: transparent; |
| 212 | +} |
| 213 | + |
| 214 | +.file-tree-shell--mobile .file-tree { |
| 215 | + padding-bottom: 10px; |
| 216 | +} |
| 217 | + |
| 218 | +.file-tree-shell--mobile .tree-item { |
| 219 | + min-height: 40px; |
| 220 | + margin: 0; |
| 221 | + border-radius: 0; |
| 222 | + padding-top: 8px; |
| 223 | + padding-bottom: 8px; |
| 224 | +} |
| 225 | + |
| 226 | +.file-tree-shell--mobile .tree-item.selected { |
| 227 | + padding-left: 14px; |
| 228 | + border-left: 2px solid color-mix(in srgb, var(--accent-blue) 88%, white 12%); |
| 229 | + background: color-mix(in srgb, var(--accent-blue) 12%, transparent); |
| 230 | +} |
| 231 | +``` |
| 232 | + |
| 233 | +- [ ] **Step 4: Re-run the style test to verify it passes** |
| 234 | + |
| 235 | +Run: |
| 236 | + |
| 237 | +```bash |
| 238 | +pnpm --filter @coder-studio/web exec vitest run src/styles/components.theme.test.ts |
| 239 | +``` |
| 240 | + |
| 241 | +Expected: |
| 242 | + |
| 243 | +- PASS for the updated mobile files surface assertions |
| 244 | +- PASS for existing unrelated theme-sensitive surface assertions |
| 245 | + |
| 246 | +- [ ] **Step 5: Commit the style implementation** |
| 247 | + |
| 248 | +```bash |
| 249 | +git add packages/web/src/styles/components.css packages/web/src/styles/components.theme.test.ts |
| 250 | +git commit -m "feat(web): align mobile files content with pc sidebar" |
| 251 | +``` |
| 252 | + |
| 253 | +## Task 3: Verify Mobile Files Behavior Did Not Regress |
| 254 | + |
| 255 | +**Files:** |
| 256 | +- Modify: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx` (only if needed) |
| 257 | +- Test: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx` |
| 258 | + |
| 259 | +- [ ] **Step 1: Review the current mobile files sheet structure tests** |
| 260 | + |
| 261 | +Open `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx` and confirm whether any selectors or wrapper assumptions changed. Only edit the test if the header layout gained a new wrapper or if class assertions need to include a new structural class. |
| 262 | + |
| 263 | +- [ ] **Step 2: If needed, add a focused regression assertion for the unchanged behavior** |
| 264 | + |
| 265 | +If the component structure changes, keep the behavior lock tight with a focused test like this: |
| 266 | + |
| 267 | +```tsx |
| 268 | + it("keeps file actions in the same header row as the tabs after the visual refresh", async () => { |
| 269 | + const sendCommand = vi.fn().mockResolvedValue({ |
| 270 | + path: "/workspace", |
| 271 | + children: [], |
| 272 | + }); |
| 273 | + |
| 274 | + const store = createStore(); |
| 275 | + store.set(wsClientAtom, { sendCommand } as never); |
| 276 | + |
| 277 | + render( |
| 278 | + <Provider store={store}> |
| 279 | + <MobileFilesSheet workspaceId="ws-test" route={{ kind: "root" }} activeTab="files" /> |
| 280 | + </Provider> |
| 281 | + ); |
| 282 | + |
| 283 | + const tabList = screen.getByRole("tablist", { name: "Files tabs" }); |
| 284 | + const newFileButton = await screen.findByRole("button", { name: "New File" }); |
| 285 | + |
| 286 | + expect(tabList.closest(".mobile-files-sheet__segmented")).toBe( |
| 287 | + newFileButton.closest(".mobile-files-sheet__segmented") |
| 288 | + ); |
| 289 | + }); |
| 290 | +``` |
| 291 | + |
| 292 | +- [ ] **Step 3: Run the targeted mobile files tests** |
| 293 | + |
| 294 | +Run: |
| 295 | + |
| 296 | +```bash |
| 297 | +pnpm --filter @coder-studio/web exec vitest run src/features/workspace/views/mobile/mobile-files-sheet.test.tsx |
| 298 | +``` |
| 299 | + |
| 300 | +Expected: |
| 301 | + |
| 302 | +- PASS for the existing structural tests |
| 303 | +- PASS for any new regression assertion added in Step 2 |
| 304 | + |
| 305 | +- [ ] **Step 4: Commit any structural-test-only adjustments** |
| 306 | + |
| 307 | +If `mobile-files-sheet.test.tsx` changed: |
| 308 | + |
| 309 | +```bash |
| 310 | +git add packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx packages/web/src/features/workspace/views/mobile/mobile-files-sheet.tsx |
| 311 | +git commit -m "test(web): keep mobile files header behavior covered" |
| 312 | +``` |
| 313 | + |
| 314 | +If it did not change, skip this commit. |
| 315 | + |
| 316 | +## Task 4: Final Verification |
| 317 | + |
| 318 | +**Files:** |
| 319 | +- No new files |
| 320 | +- Test: `packages/web/src/styles/components.theme.test.ts` |
| 321 | +- Test: `packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx` |
| 322 | + |
| 323 | +- [ ] **Step 1: Run the combined verification suite** |
| 324 | + |
| 325 | +Run: |
| 326 | + |
| 327 | +```bash |
| 328 | +pnpm --filter @coder-studio/web exec vitest run src/styles/components.theme.test.ts src/features/workspace/views/mobile/mobile-files-sheet.test.tsx |
| 329 | +``` |
| 330 | + |
| 331 | +Expected: |
| 332 | + |
| 333 | +- PASS with `0` failures |
| 334 | +- No regressions in the mobile files content style contract or structure contract |
| 335 | + |
| 336 | +- [ ] **Step 2: Perform a quick requirement checklist against the spec** |
| 337 | + |
| 338 | +Confirm each item directly against the code and test coverage: |
| 339 | + |
| 340 | +- tabs are flat and aligned with PC sidebar language |
| 341 | +- header actions are flat icon actions, not circular floating buttons |
| 342 | +- file and Git surfaces no longer use floating-card shadows |
| 343 | +- file-tree search and rows keep mobile touch sizing |
| 344 | +- editor, diff, terminal, top header, and bottom status bar selectors were not changed as part of this task |
| 345 | + |
| 346 | +- [ ] **Step 3: Commit the verification checkpoint only if there are uncommitted plan-scope changes left** |
| 347 | + |
| 348 | +If there are still plan-scope code changes not yet committed: |
| 349 | + |
| 350 | +```bash |
| 351 | +git add packages/web/src/styles/components.css packages/web/src/styles/components.theme.test.ts packages/web/src/features/workspace/views/mobile/mobile-files-sheet.test.tsx packages/web/src/features/workspace/views/mobile/mobile-files-sheet.tsx |
| 352 | +git commit -m "chore(web): verify mobile files content refresh" |
| 353 | +``` |
| 354 | + |
| 355 | +If there are no plan-scope changes left, skip this step. |
0 commit comments