fix(web): show Tux icon for WSL environments - #8511
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
There was a problem hiding this comment.
UI consistency review found icon-system inconsistencies introduced by this change. Details inline.
Posted via Macroscope — UI Consistency
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This change updates automatic WSL detection and changes the default environment icon and label across web and mobile surfaces, in addition to adding a small backward-compatible contract value. Because the default presentation changes for existing WSL environments without an explicit user override, the product-default policy calls for human review. Not approved because:
Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more. |
There was a problem hiding this comment.
One new finding on the latest commit (8f6ec6e). The two prior comments on Sidebar.tsx (row vs. SidebarThreadTooltip / SidebarSearchResultRow glyph divergence) and BranchToolbarEnvironmentSelector.tsx (icon mapping duplicated across three places) still apply and are not repeated here.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Follow-up on the icon-resolver refactor: the shared getEnvironmentIcon helper addresses the earlier duplication between the composer trigger and the select list. Two things from the open threads still stand, plus one small leftover noted inline.
apps/web/src/components/LegacySidebar.tsx(~2325-2343): the badge's accessible name (aria-label="Local sandbox project") and tooltip (Local sandbox: …) are unchanged and OS-neutral, while the glyph now asserts Linux;allRemoteMembersAreDesktopLocalderives from thelocal:connection-id prefix (connection/desktopLocal.ts), which carries no OS. Either the wording or the glyph should move so the label and the mark describe the same thing.- The same environment class is still drawn with three different marks:
LinuxIconhere and in the composer/sidebar row, lucideLaptopIcon+ "Local device" insettings/ProviderSettingsPanel.tsx(~157/165), andServerIconinSidebar.tsx(~318) andCommandPalette.tsx(~1048). Routing the settings resolver through the new helper (or narrowing the helper's name to the surfaces it owns) would keep one mapping.
No new duplicate comments were posted for the already-open LinuxIcon.tsx and Sidebar.tsx threads.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d9f2b09. Configure here.
There was a problem hiding this comment.
One finding: the sidebar thread row's environment glyph now disagrees with its own details tooltip, and the non-WSL fallback silently changed from ServerIcon to CloudIcon for every remote thread. See the inline comment on apps/web/src/components/Sidebar.tsx.
Context (not blocking, no action required in this PR): getEnvironmentIcon is now the shared owner of the primary / desktop-local / cloud mapping, but apps/web/src/components/settings/ProviderSettingsPanel.tsx (~157/165) still resolves the same isDesktopLocalConnectionTarget state to LaptopIcon + "Local device", so the same environment class remains Tux in the sidebar/toolbar and a laptop in settings. Previously flagged points about the OS assertion and the LegacySidebar ternary/"Local sandbox" wording still stand and are not repeated here.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
One new finding: the composer's mobile environment picker still hardcodes the old primary/cloud icon mapping, so the same WSL environment renders Tux on desktop and a cloud on mobile. Details inline.
Still open from the previous review: SidebarThreadTooltip (apps/web/src/components/Sidebar.tsx ~318) renders ServerIcon unconditionally, so a desktop-local thread row shows the Linux glyph while its own hover card — and SidebarSearchResultRow, which shares that tooltip — still shows a server glyph for that identical environment.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Two consistency findings on the shared environment-icon change, plus one earlier finding that is still open.
apps/web/src/components/BranchToolbar.logic.ts— the newisDesktopLocaloption is not read by the mobile composer picker, so the same environment gets two glyphs depending on breakpoint.apps/web/src/components/Icons.tsx—getEnvironmentIconis a second, disagreeing resolver alongsideproviderEnvironmentIconin Settings.
Still open from a previous review (not re-commented): SidebarThreadTooltip (Sidebar.tsx ~318) renders ServerIcon next to environmentLabel, so a desktop-local thread row now shows the Linux glyph while its own hover card and the search-result row describe that identical environment with a server glyph.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
Two places where this PR's narrowing from "desktop-local" to "WSL" splits one piece of state into two predicates, so the same environment can be described two different ways.
Posted via Macroscope — UI Consistency
There was a problem hiding this comment.
UI consistency review found two issues, both in scope for this check.
apps/web/src/components/Icons.tsx: the new sharedLinuxIconbakes a fixedp-0.5inset, so the WSL glyph renders smaller than the lucide icons it replaces in the same slots, at a ratio that varies per call site.apps/web/src/components/LegacySidebar.tsx: the reworded comment now describes header behavior that only holds for WSL projects after the badge switched toallRemoteMembersAreWsl, and the thread row still classifies on the broader desktop-local predicate.
Still open from an earlier round (not re-commented): MobileRunContextSelector in apps/web/src/components/BranchToolbar.tsx (lines ~107 and ~154) consumes the same availableEnvironments (now carrying isWsl) but keeps env.isPrimary ? MonitorIcon : CloudIcon, so the same WSL environment shows the Linux glyph on the desktop selector and a cloud glyph on the mobile one.
Posted via Macroscope — UI Consistency
Dismissing prior approval to re-evaluate 3d64750
Dismissing prior approval to re-evaluate 8c50438
Dismissing prior approval to re-evaluate bf5cc13
Fold pingdotgg#8511 onto the in-progress sync so fork/dev catches current upstream/main, not only 223ff44.
## What's Changed * fix(web): show Tux icon for WSL environments by @UtkarshUsername in pingdotgg/t3code#8511 * perf(web): speed up folder menu sorting by @AksharP5 in pingdotgg/t3code#10190 * style(web): fix inconsistencies in new settings layouts by @extoci in pingdotgg/t3code#10177 * feat(threads): persist manual active thread order by @juliusmarminge in pingdotgg/t3code#9729 * feat(mobile): arrange active threads from both thread lists by @juliusmarminge in pingdotgg/t3code#9730 * feat(web): drag threads across sections with consistent motion by @juliusmarminge in pingdotgg/t3code#9731 * feat(web): simplify sidebar drag destination cues by @juliusmarminge in pingdotgg/t3code#9750 * fix(mobile): keep pending tasks queued when a send fails in flight by @juliusmarminge in pingdotgg/t3code#10245 * feat(mobile): show new-task drafts alongside pending tasks in the thread list by @juliusmarminge in pingdotgg/t3code#10260 * feat(mobile): allow several new-task drafts per project by @juliusmarminge in pingdotgg/t3code#10327 * fix(mobile): slide settled threads out before collapsing by @StiensWout in pingdotgg/t3code#10345 **Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260906.1303...v0.0.39-nightly.20260906.1316 Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260906.1316
Merges 141 upstream commits (`b438447f6..8b2838e`) into the fork, following the `fork-upstream-merge` skill. Landed as a merge commit; conflicts resolved by the path-policy verdicts in `docs/fork/inventory.json`. ## Merge shape 563 files landed (`git diff --stat HEAD^1 HEAD`) against 563 in the upstream range, so no upstream work was dropped. Fork delta 720 files. ## Conflicts 14 files, resolved by concern. The merge commit message names each. Two are worth carrying forward: - Upstream extracted the project action rows into `ProjectActionsList.tsx`. The fork's Edit gate now rides an `editable` prop that defaults to upstream's always-editable behavior, so the gate survives the next extraction. - Upstream moved the `agent-browser-access` setting onto its new `/settings/projects` page. `settingsSearch.ts` points that item there and drops a fork delta. ## Two things a clean merge did not show **Upstream can delete an export the fork still imports.** pingdotgg#10225 removed `ClientTracingLive` as unused. `apps/web/src/lib/runtime.ts` still installs it, and neither file conflicted, so the typecheck failed four ways on a merge git called clean. Restored with a `// Fork:` comment naming the consumer. **A green test step can hide a suite that never finished.** `vp run -r test` kills the packages still running when one of them fails. A `@t3tools/desktop` failure truncated `apps/web` and `@t3tools/mobile` after each had reported hundreds of passing files, and `verify.mjs` counted any package with labeled output as tested. Four failing web tests went unreported. The check now keys on the closing `Test Files` line and runs every unfinished package alone. ## Unsupported methods `unsupported-methods.mjs` reports 0 ADD, 0 DROP, 2 KEEP, against 61 dispatched backend methods and 131 contract methods. Getting there took a fix. The backend moved its dispatch from `crates/t3code/src/lib.rs` to `crates/t3code/src/rpc/dispatch.rs`, where every arm is a one-line call into a handler below the match. The script read the old path and reported zero dispatched methods, then read the new one and called `vcs.switchRef` a DROP, because the `unsupported_exit` that refuses it had moved out of the arm. It now tries both paths and follows an arm two calls deep. Contract changes: `provider.consumeResetCredit` and `server.getHostResources` gained `UnsupportedMethodError`; `server.getUsageSummary` lost it, which closes the item the previous merge left open. ## Feature classification **Usable as-is** — client-only, nothing new on the wire. Sidebar drag across sections with destination cues and a named drop action (pingdotgg#9731, pingdotgg#9750, pingdotgg#10378, pingdotgg#10453, pingdotgg#10464). Composer behavior: a multiline draft survives timeline scrolling (pingdotgg#10444), the composer stops collapsing on blur (pingdotgg#10437) and regains focus when you tab back (pingdotgg#10463). Panel and preview chrome: resize the floating preview from any edge (pingdotgg#10467), toolbar controls stay anchored (pingdotgg#10478), the stuck resize cursor clears (pingdotgg#10461), the browser hides as the right panel closes (pingdotgg#10385), manual panel choices hold during a turn (pingdotgg#10113). Settings and accessibility polish (pingdotgg#10177, pingdotgg#10262, pingdotgg#10415, pingdotgg#10258, pingdotgg#10124, pingdotgg#10125, pingdotgg#10127, pingdotgg#10128, pingdotgg#10175). Performance (pingdotgg#10413, pingdotgg#10190, pingdotgg#10118). Plus the GitHub mark on `github.com` links (pingdotgg#10324), the Tux icon for WSL (pingdotgg#8511), a remembered usage page selection (pingdotgg#10189), project settings in the legacy sidebar menu (pingdotgg#10021), and text-only preview snapshots (pingdotgg#10232). **Unsupported in Moatless** — resolves to a refusal, or falls through to its own empty state. Each is recorded in `docs/fork/gaps.md`: - Reset credits through the hub and CLIProxyAPI (pingdotgg#10462, pingdotgg#10395, pingdotgg#10308). `provider.consumeResetCredit`, new union entry. `UsageLimits.tsx` catches the refusal and shows "Could not use the reset credit." - Balancing new threads across connected machines (pingdotgg#9895, pingdotgg#10433, pingdotgg#10407). `server.getHostResources`, new union entry. Nothing polls until a user picks automatic routing, and the composer then reads "Auto balance unavailable." - Onboarding: import grouped by repository (pingdotgg#10493), the shared multi-computer wizard (pingdotgg#10465), agent install without Node or npm (pingdotgg#10402). All ride `agentSessions.scan` and `.import`, an existing gap. - Shared project defaults and scoped overrides (pingdotgg#9754). The page reads, and every write goes through `server.updateSettings`, which the backend does not dispatch. - Two new `orchestration.dispatchCommand` types: `thread.active.reorder` (pingdotgg#9729) and `thread.user-input.dismiss` (pingdotgg#10431). Both are ordinary controls, a sidebar drag and a Dismiss button, and a dispatched command cannot be refused per type. That is the standing _A command cannot be refused_ gap, now 26 members wide. **Backend behavior to consider reproducing in Moatless** — upstream server fixes whose behavior the fork's client assumes: - Invalid script IDs no longer crash threads (pingdotgg#10019). The fork ships project scripts, so this one is worth reading first. - Settlement: settle inactive threads without a PR lookup (pingdotgg#10103), skip disabled settlement lookups (pingdotgg#10424), settle threads with unanswered async questions (pingdotgg#10400). - Interrupted threads stay resumable after a restart (pingdotgg#10421). - Completed requests stay closed across clients (pingdotgg#10123). - Placeholder branches are followed after a checkout updates (pingdotgg#10441). - A thread's PR links without an open client (pingdotgg#10101), and checkpoints are captured before a PR status refresh (pingdotgg#10347). - Adapters declare their own context compaction (pingdotgg#10112). - Transcripts with oversized tool records import (pingdotgg#10430), and git status scans are skipped while the index is locked (pingdotgg#9845). - Usage limits pool per provider across accounts and environments (pingdotgg#10300). The client renders what `server.getUsageSummary` returns, so this shows something only if the Moatless payload carries per-account limits. Mobile, marketing, desktop, provider adapters and release tooling are not this fork's surface and are not classified. ## Also fixed here, and not upstream's doing - Three `browser-*` search items still routed to `/settings/integrations`, which the fork owns for its Moatless administration page. A non-administrator who searched for them was redirected away from the result. - `moatless/listSearch.ts` carried no fork-only declaration. - `pnpm fmt:check` failed on 294 files, 293 of them orval output. The generator now formats what it writes through an `afterAllFilesWrite` hook. - `@t3tools/moatless-api` exported `./generated`, a barrel that is never checked in. ## Verification `inventory-check.mjs` clean. `verify.mjs` green on seven checks: duplicate-adds, tripwires, resolution-check, unsupported-methods, `fmt:check` (3876 files), `lint` and `typecheck`. `test` is red on one package, and it is the machine. `@t3tools/desktop`'s `bundled libsecret helper` shells out to `pkg-config` for `libsecret-1`, which this sandbox does not have; it fails the same way when retried alone. Everything else passes: `apps/web` 369 files, `t3` 291, `@t3tools/mobile` 149, `t3code-relay` 27, `@t3tools/client-runtime` 71, plus the smaller packages. `spec:check` cannot run in a sandbox: it needs a sibling `moatless` checkout or a deployment URL and has neither. Written by Claude Opus 5 in Claude Code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Moatless task: https://moatless.soaplabstest.com/tasks/8d348ecf-f9cb-4e65-b96b-6c1054a8ed33

What Changed
components/Icons.tsxbrand icon barrel.Why
WSL runs locally alongside the Windows environment, but was previously represented using generic remote or desktop-local icons. WSL now identifies itself as Linux automatically, while other Linux environments can select the same “Linux/WSL” icon manually.
UI Changes
Before:





After:







Checklist
Verification: web typecheck, targeted lint, and exact SVG path comparison passed.
Implemented by GPT-5.6 via Codex.
Note
[!NOTE]
Show Linux icon for WSL environments across web UI
isWslConnectionTargetpredicate in desktopLocal.ts that classifies a desktop-local backend identifier as WSL when it starts with the WSL prefixLinuxIconand updatesgetEnvironmentIconin Icons.tsx with precedence: primary, then WSL, then remote, then cloudwslprop toEnvironmentMachineIconso it renders the Linux glyph when true, overriding the machine-kind mappingMacroscope summarized ec30f95.
Note
Low Risk
Presentation-only icon and labeling changes with a small connection-target helper and unit tests; no auth, data, or runtime behavior changes.
Overview
WSL backends were shown with the same icons as generic remote or desktop-local environments. This PR detects WSL via
isWslConnectionTarget(wsl:*desktop backend IDs) and routes all environment icon rendering through sharedgetEnvironmentIcon(primary → monitor, WSL → newLinuxIcon, otherwise caller-specific remote icon).isWslis threaded through composer environment options (ChatView,EnvironmentOption), sidebar thread rows/tooltips/search, legacy project header badges (allRemoteMembersAreWslon grouped projects), branch toolbar pickers, and provider settings tabs. Legacy sidebar remote-only badges swapContainerIconfor the centralized helper (laptop vs cloud vs Tux) and vertically center the badge.Reviewed by Cursor Bugbot for commit 8cb96c3. Bugbot is set up for automated code reviews on this repo. Configure here.