Skip to content

[FEATURE]: Restore the legacy UI with the persistent left sidebar as an option #48882

Open

Description

@HRronaldo

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

The recent sidebar redesign (#20242) replaced the classic two-panel layout
(persistent left sidebar + session panel) with a Codex-style unified view.
For me this is not a visual refresh — it removed the single most important
part of my daily workflow, and there is currently no way to go back.

What I want changed / added:

  1. Keep the legacy UI with the persistent left sidebar available as an
    option (a legacy / classic UI toggle or config flag is enough — it
    doesn't have to be the default).
  2. If the legacy layout can't be maintained, at minimum restore an
    always-visible left sidebar — not hover-only menus or a collapsed rail
    that requires extra clicks.

Why this matters (the benefit):

I live inside opencode for hours every day. The left sidebar gives me an
at-a-glance, persistent view of my projects, workspaces, and sessions:

  • I can see and switch between projects/contexts without breaking flow.
  • Sessions are one click away, not buried behind hover or search.
  • Context switching stays fast and predictable.

The new interface hides all of this behind a single unified list with
hover/focus interactions. Every session feels slower and more disoriented.
This is not an aesthetic preference — removing the sidebar with no escape
hatch is a functional regression for anyone who relies on it.

Related:

Happy to provide screenshots, my opencode version, and platform details if
that helps. Please don't leave those of us who depend on the left sidebar
behind.

Activity

  1. github-actions commented on Sep 14, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Please review these issues and consider adding your feedback there instead, or linking them as related.

  2. HRronaldo commented on Sep 14, 2026

    @HRronaldo
    Author
    Image

    win11 opencode desktop
    version:v1.18.30

  3. zhengxingjian commented on Sep 14, 2026

    @zhengxingjian

    准备换deepseek-harness,新布局生产力不行

  4. aa2013 commented on Sep 14, 2026

    @aa2013

    新UI太难用了,完全没用旧UI好用,新UI操作太反人类

  5. 0-SamboNZ-0 commented on Sep 14, 2026

    @0-SamboNZ-0

    I also agree that the original layout was MUCH easier to use; this is a significant loss for usability!

  6. aa2013 commented on Sep 14, 2026

    @aa2013

    写了个powersell脚本,替换opencode打包后的 asar 里面的新布局判断逻辑强制使用旧布局,执行powershell,输入asar的文件夹,通常是:
    C:\Users\%Username%\AppData\Local\Programs\@opencode-aidesktop\resources
    注意替换路径里面的用户名,脚本会自动备份当前的asar,执行完成后重启opencode即可,支持 1.18.30 版本
    注意:每次更新opencode后需要重新执行

    I wrote a PowerShell script that replaces the new layout detection logic inside the packaged asar of opencode, forcing it to use the old layout. Run PowerShell and enter the asar folder, which is usually:
    C:\Users\%Username%\AppData\Local\Programs\@opencode-aidesktop\resources
    Note: replace the username in the path. The script will automatically back up the current asar. After it finishes, just restart opencode. It supports version 1.18.30.
    Note: You need to run it again after each opencode update.

    Image

    script

    generated by ai

    Copy and save it as a .ps1 file, then run it.

    $ErrorActionPreference = "Stop"
    
    # Wait for a key press so the console window does not close before the result is visible.
    function Wait-Key {
        Write-Host ""
        Write-Host "Press any key to exit ..."
        [void][Console]::ReadKey($true)
    }
    
    # ---------- Step 0: locate app.asar ----------
    Write-Host "[1/8] Resolve app.asar path from the folder you provide ..."
    $inputPath = Read-Host "Enter the opencode install folder"
    $inputPath = $inputPath.Trim().Trim('"', "'")
    
    if ([string]::IsNullOrWhiteSpace($inputPath)) {
        Write-Host "Error: path cannot be empty."
        Wait-Key
        exit 1
    }
    
    if (-not (Test-Path -LiteralPath $inputPath -PathType Container)) {
        Write-Host "Error: folder not found: $inputPath"
        Wait-Key
        exit 1
    }
    
    # 只接受文件夹:优先找其下的 resources\app.asar,其次文件夹内直接存在的 app.asar
    $item = Get-Item -LiteralPath $inputPath
    $candidates = @(
        (Join-Path $item.FullName "resources\app.asar"),
        (Join-Path $item.FullName "app.asar")
    )
    $AsarPath = $null
    foreach ($candidate in $candidates) {
        if (Test-Path -LiteralPath $candidate -PathType Leaf) {
            $AsarPath = $candidate
            break
        }
    }
    if (-not $AsarPath) {
        Write-Host "Error: no app.asar found under: $($item.FullName)"
        Write-Host "Expected at 'resources\app.asar' or 'app.asar'."
        Wait-Key
        exit 1
    }
    Write-Host "       app.asar: $AsarPath"
    
    # ---------- Step 1: prepare equal-length replacement ----------
    Write-Host "[2/8] Building equal-length replacement string ..."
    $target = "if (layoutUpgrade()) return true;"
    $replacement = "return false;"
    $pad = " " * ($target.Length - $replacement.Length)
    $newString = $replacement + $pad
    
    # Equal-length invariant must hold, otherwise the ASAR header offsets break.
    if ($newString.Length -ne $target.Length) {
        Write-Host "Error: equal-length invariant broken."
        Wait-Key
        exit 1
    }
    Write-Host "       target len: $($target.Length) / replacement len: $($newString.Length)"
    
    # ---------- Step 2: read bytes ----------
    Write-Host "[3/8] Reading bytes from $AsarPath ..."
    $targetBytes = [System.Text.Encoding]::ASCII.GetBytes($target)
    $newBytes = [System.Text.Encoding]::ASCII.GetBytes($newString)
    $bytes = [System.IO.File]::ReadAllBytes($AsarPath)
    Write-Host "       read $($bytes.Length) bytes"
    
    # Naive scan for a byte sub-sequence; good enough for a short needle.
    function Find-Bytes {
        param(
            [byte[]]$Data,
            [byte[]]$Pattern,
            [int]$StartIndex = 0
        )
        $last = $Data.Length - $Pattern.Length
        for ($i = $StartIndex; $i -le $last; $i++) {
            if ($Data[$i] -ne $Pattern[0]) { continue }
            $ok = $true
            for ($j = 1; $j -lt $Pattern.Length; $j++) {
                if ($Data[$i + $j] -ne $Pattern[$j]) {
                    $ok = $false
                    break
                }
            }
            if ($ok) { return $i }
        }
        return -1
    }
    
    # ---------- Step 3: search for target ----------
    Write-Host "[4/8] Searching for target string ..."
    $index = Find-Bytes -Data $bytes -Pattern $targetBytes
    
    if ($index -lt 0) {
        Write-Host "       Target string not found; maybe already locked."
        Wait-Key
        exit 0
    }
    Write-Host "       found at offset $index"
    
    # Ensure the match is unique so we never patch the wrong spot.
    $second = Find-Bytes -Data $bytes -Pattern $targetBytes -StartIndex ($index + 1)
    if ($second -ne -1) {
        Write-Host "Error: multiple matches found (offset $second); aborting."
        Wait-Key
        exit 1
    }
    
    # ---------- Step 4: backup ----------
    Write-Host "[5/8] Creating backup ..."
    $bakPath = [System.IO.Path]::ChangeExtension($AsarPath, "asar.bak")
    if (-not (Test-Path -LiteralPath $bakPath)) {
        Copy-Item -LiteralPath $AsarPath -Destination $bakPath
        Write-Host "       Backup created: $bakPath"
    }
    else {
        Write-Host "       Backup exists, skip: $bakPath"
    }
    
    # ---------- Step 5: patch in memory ----------
    Write-Host "[6/8] Applying equal-length replacement ..."
    $originalLength = $bytes.Length
    
    # Overwrite the matched region with the equal-length replacement.
    [System.Array]::Copy($newBytes, 0, $bytes, $index, $newBytes.Length)
    
    if ($bytes.Length -ne $originalLength) {
        Write-Host "Error: array length changed; aborting before write."
        Wait-Key
        exit 1
    }
    
    # ---------- Step 6: write back ----------
    Write-Host "[7/8] Writing patched bytes back ..."
    [System.IO.File]::WriteAllBytes($AsarPath, $bytes)
    
    # ---------- Step 7: verify ----------
    Write-Host "[8/8] Verifying patched region ..."
    $verifyBytes = [System.IO.File]::ReadAllBytes($AsarPath)
    $verifySlice = $verifyBytes[$index..($index + $newBytes.Length - 1)]
    $verifyText = [System.Text.Encoding]::ASCII.GetString($verifySlice)
    if (-not $verifyText.StartsWith($replacement)) {
        Write-Host "Error: verification failed; unexpected content after write."
        Wait-Key
        exit 1
    }
    
    Write-Host "Success: old layout locked."
    Wait-Key
  7. JamesLi2013 commented on Sep 14, 2026

    @JamesLi2013

    Keep legacy layout option
    If the new layout is used, I never want to use it again.

  8. diuwu commented on Sep 14, 2026

    @diuwu

    We strongly demand that the old layout be restored. Ever since it was changed to the new layout, my colleagues and I have all given up on opencode.

  9. 0-SamboNZ-0 commented on Sep 14, 2026

    @0-SamboNZ-0

    I wrote a PowerShell script that replaces the new layout detection logic inside the packaged asar of opencode, forcing it to use the old layout.

    Beautiful! Works perfectly! Thank you for sharing!

  10. ellamas-grupoarpada commented on Sep 14, 2026

    @ellamas-grupoarpada

    写了个powersell脚本,替换opencode打包后的 asar 里面的新布局判断逻辑强制使用旧布局,执行powershell,输入asar的文件夹,通常是: C:\Users\%Username%\AppData\Local\Programs\@opencode-aidesktop\resources 注意替换路径里面的用户名,脚本会自动备份当前的asar,执行完成后重启opencode即可,支持 1.18.30 版本 注意:每次更新opencode后需要重新执行

    I wrote a PowerShell script that replaces the new layout detection logic inside the packaged asar of opencode, forcing it to use the old layout. Run PowerShell and enter the asar folder, which is usually: C:\Users\%Username%\AppData\Local\Programs\@opencode-aidesktop\resources Note: replace the username in the path. The script will automatically back up the current asar. After it finishes, just restart opencode. It supports version 1.18.30. Note: You need to run it again after each opencode update.

    Image

    script

    generated by ai

    Copy and save it as a .ps1 file, then run it.

    $ErrorActionPreference = "Stop"

    Wait for a key press so the console window does not close before the result is visible.

    function Wait-Key {
    Write-Host ""
    Write-Host "Press any key to exit ..."
    [void][Console]::ReadKey($true)
    }

    ---------- Step 0: locate app.asar ----------

    Write-Host "[1/8] Resolve app.asar path from the folder you provide ..."
    $inputPath = Read-Host "Enter the opencode install folder"
    $inputPath = $inputPath.Trim().Trim('"', "'")

    if ([string]::IsNullOrWhiteSpace($inputPath)) {
    Write-Host "Error: path cannot be empty."
    Wait-Key
    exit 1
    }

    if (-not (Test-Path -LiteralPath $inputPath -PathType Container)) {
    Write-Host "Error: folder not found: $inputPath"
    Wait-Key
    exit 1
    }

    只接受文件夹:优先找其下的 resources\app.asar,其次文件夹内直接存在的 app.asar

    $item = Get-Item -LiteralPath $inputPath
    $candidates = @(
    (Join-Path $item.FullName "resources\app.asar"),
    (Join-Path $item.FullName "app.asar")
    )
    $AsarPath = $null
    foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate -PathType Leaf) {
    $AsarPath = $candidate
    break
    }
    }
    if (-not $AsarPath) {
    Write-Host "Error: no app.asar found under: $($item.FullName)"
    Write-Host "Expected at 'resources\app.asar' or 'app.asar'."
    Wait-Key
    exit 1
    }
    Write-Host " app.asar: $AsarPath"

    ---------- Step 1: prepare equal-length replacement ----------

    Write-Host "[2/8] Building equal-length replacement string ..."
    $target = "if (layoutUpgrade()) return true;"
    $replacement = "return false;"
    $pad = " " * ($target.Length - $replacement.Length)
    $newString = $replacement + $pad

    Equal-length invariant must hold, otherwise the ASAR header offsets break.

    if ($newString.Length -ne $target.Length) {
    Write-Host "Error: equal-length invariant broken."
    Wait-Key
    exit 1
    }
    Write-Host " target len: $($target.Length) / replacement len: $($newString.Length)"

    ---------- Step 2: read bytes ----------

    Write-Host "[3/8] Reading bytes from $AsarPath ..."
    $targetBytes = [System.Text.Encoding]::ASCII.GetBytes($target)
    $newBytes = [System.Text.Encoding]::ASCII.GetBytes($newString)
    $bytes = [System.IO.File]::ReadAllBytes($AsarPath)
    Write-Host " read $($bytes.Length) bytes"

    Naive scan for a byte sub-sequence; good enough for a short needle.

    function Find-Bytes {
    param(
    [byte[]]$Data,
    [byte[]]$Pattern,
    [int]$StartIndex = 0
    )
    $last = $Data.Length - $Pattern.Length
    for ($i = $StartIndex; $i -le $last; $i++) {
    if ($Data[$i] -ne $Pattern[0]) { continue }
    $ok = $true
    for ($j = 1; $j -lt $Pattern.Length; $j++) {
    if ($Data[$i + $j] -ne $Pattern[$j]) {
    $ok = $false
    break
    }
    }
    if ($ok) { return $i }
    }
    return -1
    }

    ---------- Step 3: search for target ----------

    Write-Host "[4/8] Searching for target string ..."
    $index = Find-Bytes -Data $bytes -Pattern $targetBytes

    if ($index -lt 0) {
    Write-Host " Target string not found; maybe already locked."
    Wait-Key
    exit 0
    }
    Write-Host " found at offset $index"

    Ensure the match is unique so we never patch the wrong spot.

    $second = Find-Bytes -Data $bytes -Pattern $targetBytes -StartIndex ($index + 1)
    if ($second -ne -1) {
    Write-Host "Error: multiple matches found (offset $second); aborting."
    Wait-Key
    exit 1
    }

    ---------- Step 4: backup ----------

    Write-Host "[5/8] Creating backup ..."
    $bakPath = [System.IO.Path]::ChangeExtension($AsarPath, "asar.bak")
    if (-not (Test-Path -LiteralPath $bakPath)) {
    Copy-Item -LiteralPath $AsarPath -Destination $bakPath
    Write-Host " Backup created: $bakPath"
    }
    else {
    Write-Host " Backup exists, skip: $bakPath"
    }

    ---------- Step 5: patch in memory ----------

    Write-Host "[6/8] Applying equal-length replacement ..."
    $originalLength = $bytes.Length

    Overwrite the matched region with the equal-length replacement.

    [System.Array]::Copy($newBytes, 0, $bytes, $index, $newBytes.Length)

    if ($bytes.Length -ne $originalLength) {
    Write-Host "Error: array length changed; aborting before write."
    Wait-Key
    exit 1
    }

    ---------- Step 6: write back ----------

    Write-Host "[7/8] Writing patched bytes back ..."
    [System.IO.File]::WriteAllBytes($AsarPath, $bytes)

    ---------- Step 7: verify ----------

    Write-Host "[8/8] Verifying patched region ..."
    $verifyBytes = [System.IO.File]::ReadAllBytes($AsarPath)
    $verifySlice = $verifyBytes[$index..($index + $newBytes.Length - 1)]
    $verifyText = [System.Text.Encoding]::ASCII.GetString($verifySlice)
    if (-not $verifyText.StartsWith($replacement)) {
    Write-Host "Error: verification failed; unexpected content after write."
    Wait-Key
    exit 1
    }

    Write-Host "Success: old layout locked."
    Wait-Key

    Works! Thanks!

    Made opencode check for security, and it added timestamped .bak (just in case of repetition):


    $ErrorActionPreference = "Stop"

    Wait for a key press so the console window does not close before the result is visible.

    function Wait-Key {
    Write-Host ""
    Write-Host "Press any key to exit ..."
    [void][Console]::ReadKey($true)
    }

    function Find-Bytes {
    param(
    [byte[]]$Data,
    [byte[]]$Pattern,
    [int]$StartIndex = 0
    )

    $last = $Data.Length - $Pattern.Length
    for ($i = $StartIndex; $i -le $last; $i++) {
        if ($Data[$i] -ne $Pattern[0]) { continue }
    
        $match = $true
        for ($j = 1; $j -lt $Pattern.Length; $j++) {
            if ($Data[$i + $j] -ne $Pattern[$j]) {
                $match = $false
                break
            }
        }
    
        if ($match) { return $i }
    }
    
    return -1
    

    }

    Write-Host "[1/8] Resolve app.asar path from the folder you provide ..."
    $inputPath = Read-Host "Enter the opencode install folder"
    $inputPath = $inputPath.Trim().Trim([char[]]@([char]34, [char]39))

    if ([string]::IsNullOrWhiteSpace($inputPath)) {
    Write-Host "Error: path cannot be empty."
    Wait-Key
    exit 1
    }

    if (-not (Test-Path -LiteralPath $inputPath -PathType Container)) {
    Write-Host "Error: folder not found: $inputPath"
    Wait-Key
    exit 1
    }

    $item = Get-Item -LiteralPath $inputPath
    $candidates = @(
    (Join-Path $item.FullName "resources\app.asar"),
    (Join-Path $item.FullName "app.asar")
    )

    $asarPath = $null
    foreach ($candidate in $candidates) {
    if (Test-Path -LiteralPath $candidate -PathType Leaf) {
    $asarPath = $candidate
    break
    }
    }

    if (-not $asarPath) {
    Write-Host "Error: no app.asar found under: $($item.FullName)"
    Write-Host "Expected at 'resources\app.asar' or 'app.asar'."
    Wait-Key
    exit 1
    }
    Write-Host " app.asar: $asarPath"

    Write-Host "[2/8] Building equal-length replacement string ..."
    $target = "if (layoutUpgrade()) return true;"
    $replacement = "return false;"
    $paddingLength = $target.Length - $replacement.Length

    if ($paddingLength -lt 0) {
    Write-Host "Error: replacement is longer than target."
    Wait-Key
    exit 1
    }

    $newString = $replacement + (" " * $paddingLength)
    if ($newString.Length -ne $target.Length) {
    Write-Host "Error: equal-length invariant broken."
    Wait-Key
    exit 1
    }
    Write-Host " target len: $($target.Length) / replacement len: $($newString.Length)"

    Write-Host "[3/8] Reading bytes from $asarPath ..."
    $targetBytes = [System.Text.Encoding]::ASCII.GetBytes($target)
    $newBytes = [System.Text.Encoding]::ASCII.GetBytes($newString)
    $bytes = [System.IO.File]::ReadAllBytes($asarPath)
    Write-Host " read $($bytes.Length) bytes"

    Write-Host "[4/8] Searching for target string ..."
    $index = Find-Bytes -Data $bytes -Pattern $targetBytes
    if ($index -lt 0) {
    Write-Host " Target string not found; no changes were made."
    Wait-Key
    exit 0
    }
    Write-Host " found at offset $index"

    $second = Find-Bytes -Data $bytes -Pattern $targetBytes -StartIndex ($index + 1)
    if ($second -ne -1) {
    Write-Host "Error: multiple matches found (second offset: $second); aborting."
    Wait-Key
    exit 1
    }

    Write-Host "[5/8] Creating timestamped backup ..."
    $timestamp = Get-Date -Format "yyyyMMddHHmmss"
    $backupPath = "$asarPath.$timestamp.bak"

    if (Test-Path -LiteralPath $backupPath) {
    Write-Host "Error: backup already exists: $backupPath"
    Wait-Key
    exit 1
    }

    Copy-Item -LiteralPath $asarPath -Destination $backupPath
    Write-Host " Backup created: $backupPath"

    Write-Host "[6/8] Applying equal-length replacement ..."
    $originalLength = $bytes.Length
    [System.Array]::Copy($newBytes, 0, $bytes, $index, $newBytes.Length)

    if ($bytes.Length -ne $originalLength) {
    Write-Host "Error: array length changed; aborting before write."
    Wait-Key
    exit 1
    }

    try {
    Write-Host "[7/8] Writing patched bytes back ..."
    [System.IO.File]::WriteAllBytes($asarPath, $bytes)

    Write-Host "[8/8] Verifying patched file ..."
    $verifyBytes = [System.IO.File]::ReadAllBytes($asarPath)
    if ($verifyBytes.Length -ne $originalLength) {
        throw "File length changed after write."
    }
    
    $verifySlice = $verifyBytes[$index..($index + $newBytes.Length - 1)]
    $verifyText = [System.Text.Encoding]::ASCII.GetString($verifySlice)
    if ($verifyText -ne $newString) {
        throw "Unexpected content in the patched region."
    }
    

    }
    catch {
    Write-Host "Error: patch failed: $($.Exception.Message)"
    Write-Host " Restoring backup ..."
    try {
    Copy-Item -LiteralPath $backupPath -Destination $asarPath -Force
    Write-Host " Original file restored."
    }
    catch {
    Write-Host "CRITICAL: automatic restore failed: $($
    .Exception.Message)"
    Write-Host " Restore manually from: $backupPath"
    }

    Wait-Key
    exit 1
    

    }

    Write-Host "Success: old layout locked."
    Write-Host "Backup: $backupPath"
    Wait-Key

  11. pav18 commented on Sep 14, 2026

    @pav18

    Thank you so much for your script!!!!!

  12. enngins commented on Sep 14, 2026

    @enngins

    Guys WTH!
    We really need to patch it for old (NORMAL) layout? I can't image who can work with this new dumb's interface.

  13. drobin04 commented on Sep 14, 2026

    @drobin04

    I HATE the new layout. I stayed on an old version for quite a while for the old layout, and then forgot one day and updated. HATE IT. PLEASE add a toggle to restore the old layout. The new one is AWFUL.

  14. kmanderson12 commented on Sep 14, 2026

    @kmanderson12

    The recent change to force the new layout completely breaks the mental model that the previous layout encouraged. I no longer have easy visibility over which one of my projects has active agent activity. I'm left to try to infer the activity from the thread's title, which might not always be relevant or accurate. This adds friction and confusion that is completely unnecessary.

    The old layout needs to stay as a configurable option in order to support the workflows we've grown to love as we've used this tool.

  15. 30 remaining items

  16. hq20211124 commented on Oct 28, 2026

    @hq20211124

    看了下两个左侧的模式,都不对,不便于使用,不直观,反而搞太复杂了:1.应该是按照项目空间,在左侧用二级菜单可以展开收起;2.正在运行中的项目也不用可以搞个模式,只需要在对应对话标题标一个绿色气泡就好了;3.需要大空间可以直接一键折叠左侧;

  17. nippur72 commented on Oct 28, 2026

    @nippur72

    I had to switch to Cline desktop or DSH because of this :-(

  18. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed write-up! You're asking for the classic layout, with an always-visible left sidebar showing projects, workspaces and sessions, to come back as an option (it doesn't have to be the default). If that isn't possible, you'd like at least a left sidebar that stays visible instead of hover menus or a collapsed rail. Maintainers will consider it, and this issue is where the request is being tracked. Others asking for the same thing are pointed here, so 👍 reactions and use cases on this issue help.

    Until then, Settings → Appearance → Tabs → Vertical puts session tabs in a sidebar on the left instead of across the top. It isn't the old project → workspace → session tree, but it may get you closer.

    Related issues:

  19. albgen commented on Oct 28, 2026

    @albgen

    29. tabs in a sidebar on

    we are talking about the Desktop version here you dumb bot

  20. Evil-Spirit commented on Nov 3, 2026

    @Evil-Spirit

    Why to change things? Go and create another application instead. Oh my god whata fucking imitation of improvements. I mean you made UI once, don't touch it anymore. My bank application changed 100 times and still piece of shit. Don't touch UI, why I should revert your changes every time!

  21. albgen commented on Nov 3, 2026

    @albgen

    i think they have changed it but seems nobody is reading the issues here.
    This is the current settings of opencode and i see this with a brand new installation. On my existing one, which is a continuous upgrade, i can't.
    Image

  22. ngutech21 commented on Nov 3, 2026

    @ngutech21

    @albgen The tabs are not grouped by project. It‘s just a flat list

  23. VL4DST3R commented on Nov 3, 2026

    @VL4DST3R

    i think they have changed it but seems nobody is reading the issues here.

    They virtually never do. Wish someone with enough claude tokens would just vibecode the v1 ui back at this point.

  24. albgen commented on Nov 3, 2026

    @albgen

    28. @albgen The tabs are not grouped by project. It‘s just a flat list

    True. Way better though than having only horisontal tabs.

  25. boesbo commented on Nov 3, 2026

    @boesbo

    I will contribute to this: #60867

  26. joe-ieta commented on Nov 4, 2026

    @joe-ieta
    Contributor

    Side tab mode is just a simple list. far behind from competitors.

  27. joe-ieta commented on Nov 4, 2026

    @joe-ieta
    Contributor

    5. 新UI太难用了,完全没用旧UI好用,新UI操作太反人类

    实在忍不了了,最近修改了一个Side bar模式,看看 #60867

  28. pav18 commented on Nov 10, 2026

    @pav18

    YEEESSSSS Projects in the sidebar (Experimental Settings)

  29. balabommablock-cpu commented on Nov 13, 2026

    @balabommablock-cpu

    For anyone landing here: a persistent project sidebar is in v2.2.7, behind two settings.

    1. Settings → General → Interface layout → Side tabs
    2. Settings → Experimental → Organize vertical sidebar → Projects

    From the code (sidebar-organized.tsx), that gives you a sidebar that stays open next to the session, grouped by project. It lists the project's sessions, not just open tabs: 5 per project and 7 projects, each with Show more. Each session shows a spinner while it's running and a dot when it's unread, and you can drag projects to reorder them.

    That covers most of what's asked above: seeing which project has a running session, and switching in one click without going Home. What it doesn't cover yet: workspaces as their own level, and phones (side tabs are desktop-only).

    A small suggestion for maintainers: once it leaves Experimental, a one-click "Classic sidebar" choice in Interface layout would help the people in this thread, and in the closed duplicates (#48837, #37012, #39835, #37546), find it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions