( root: PaneLayout, fn: (path: string) => string | null )
| 225 | * leaf (so the UI always has a pane to render). |
| 226 | */ |
| 227 | export function rewritePathsInTree( |
| 228 | root: PaneLayout, |
| 229 | fn: (path: string) => string | null |
| 230 | ): PaneLayout { |
| 231 | const result = mapLeaves(root, (leaf) => { |
| 232 | const nextTabs: string[] = [] |
| 233 | const seen = new Set<string>() |
| 234 | for (const tab of leaf.tabs) { |
| 235 | const mapped = fn(tab) |
| 236 | if (mapped == null) continue |
| 237 | if (seen.has(mapped)) continue |
| 238 | seen.add(mapped) |
| 239 | nextTabs.push(mapped) |
| 240 | } |
| 241 | if (nextTabs.length === 0) return null |
| 242 | const nextTabSet = new Set(nextTabs) |
| 243 | const nextPinned: string[] = [] |
| 244 | const pinnedSeen = new Set<string>() |
| 245 | for (const p of leaf.pinnedTabs) { |
| 246 | const mapped = fn(p) |
| 247 | if (mapped == null) continue |
| 248 | if (!nextTabSet.has(mapped)) continue |
| 249 | if (pinnedSeen.has(mapped)) continue |
| 250 | pinnedSeen.add(mapped) |
| 251 | nextPinned.push(mapped) |
| 252 | } |
| 253 | let nextActive: string | null = null |
| 254 | if (leaf.activeTab) { |
| 255 | const mappedActive = fn(leaf.activeTab) |
| 256 | if (mappedActive && nextTabs.includes(mappedActive)) { |
| 257 | nextActive = mappedActive |
| 258 | } |
| 259 | } |
| 260 | if (!nextActive) nextActive = nextTabs[0] |
| 261 | let nextPreview: string | null = null |
| 262 | if (leaf.previewTab) { |
| 263 | const mappedPreview = fn(leaf.previewTab) |
| 264 | if (mappedPreview && nextTabs.includes(mappedPreview)) { |
| 265 | nextPreview = mappedPreview |
| 266 | } |
| 267 | } |
| 268 | return enforcePinnedPrefix({ |
| 269 | ...leaf, |
| 270 | tabs: nextTabs, |
| 271 | pinnedTabs: nextPinned, |
| 272 | activeTab: nextActive, |
| 273 | previewTab: nextPreview |
| 274 | }) |
| 275 | }) |
| 276 | return result ?? makeLeaf() |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * Classify where within a pane's bounding rect the cursor falls when |
no test coverage detected