Compare commits

..

863 Commits

Author SHA1 Message Date
Aiden Cline 3b6a21f173 wip 2025-12-28 18:14:31 -06:00
Aiden Cline f7bdf0b74f wip 2025-12-28 18:14:31 -06:00
Aiden Cline 7539cbeea2 wip 2025-12-28 18:14:31 -06:00
Github Action b683567c2c Update Nix flake.lock and hashes 2025-12-28 22:30:51 +00:00
Aiden Cline 6965805eeb adjust deps 2025-12-28 16:29:35 -06:00
Aiden Cline 7a511a1a25 wip 2025-12-28 01:20:18 -06:00
Aiden Cline f3437d9a49 wip 2025-12-28 01:09:26 -06:00
Aiden Cline ccf1826400 wip 2025-12-26 23:09:58 -06:00
Aiden Cline f6b24d81d4 wip 2025-12-26 22:37:31 -06:00
Aiden Cline 3127f4f30e chore: kill some unused tools 2025-12-26 14:35:51 -06:00
Aiden Cline d7a3a13034 test: add more tests to make sure that cwd is locked for read tool 2025-12-26 14:35:50 -06:00
Aiden Cline a92af6b9ef tweak: bash tool description to avoid unnecessary 'cd &&' usage 2025-12-26 14:35:50 -06:00
Matt Silverlock 21e4fe3718 github: support issues and workflow_dispatch events (#6157) 2025-12-26 14:35:50 -06:00
Aiden Cline fa64085aba ci: re-enable sync zed 2025-12-26 14:35:50 -06:00
Aiden Cline 06307dc8c3 ci: fix token for gh 2025-12-26 14:35:50 -06:00
Aiden Cline 1bf950179a ci: fix var 2025-12-26 14:35:50 -06:00
Aiden Cline b112c8132c ci: update zed extension sync 2025-12-26 14:35:50 -06:00
Github Action b279da3684 Update Nix flake.lock and hashes 2025-12-26 14:35:50 -06:00
GitHub Action dcfeeb6919 chore: generate 2025-12-26 14:35:50 -06:00
Aiden Cline 0762a794ab Revert "feat(core): optional mdns service (#6192)"
This reverts commit 26e7043718.
2025-12-26 14:35:49 -06:00
Aiden Cline f28628b744 chore: rm comments 2025-12-26 14:35:49 -06:00
Aiden Cline 336d165fd2 fix: adjust upgrade command to use gh releases page if not npm/bun/pnpm install method 2025-12-26 14:35:49 -06:00
Rohan Godha af89e7f117 fix: opencode web baseURL error (#6181) 2025-12-26 14:35:49 -06:00
Ariane Emory 57ad8211d5 chore: kill the dead Polaris Alpha code (#6193) 2025-12-26 14:35:49 -06:00
ja 3fc23c3e8b feat: add shfmt formatter for shell scripts (#6204) 2025-12-26 14:35:49 -06:00
Aiden Cline d6899cf9b0 tweak: make install script handle 404s better 2025-12-26 14:35:49 -06:00
Github Action 58c643f205 Update Nix flake.lock and hashes 2025-12-26 14:35:48 -06:00
GitHub Action dcc5b7d6db chore: generate 2025-12-26 14:35:48 -06:00
Ayush Walekar 1c35e59e4f chore: createOpencodeServer expose logLevel (#6202) 2025-12-26 14:35:48 -06:00
Roberto Carvajal c1d21f9b11 fix(dep): Update package.json - fix perplexity provider version (#6199)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:35:48 -06:00
Daniel Polito 822c20b320 Desktop: MCP UI (#6162)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-26 14:35:47 -06:00
GitHub Action fbe676d1da chore: generate 2025-12-26 14:35:47 -06:00
Adam 3f3c0f370b feat(core): optional mdns service (#6192)
Co-authored-by: Github Action <action@github.com>
2025-12-26 14:35:47 -06:00
GitHub Action 3e4b8e2bf5 chore: generate 2025-12-26 14:35:47 -06:00
Didier Durand ccd182f522 doc: fix typos in various files (#6196) 2025-12-26 14:35:47 -06:00
GitHub Action bbf59fcb12 ignore: update download stats 2025-12-26 2025-12-26 14:35:46 -06:00
Aiden Cline b40852abe3 tweak: update transform for gemini models so that topP and topK match gemini-cli values 2025-12-26 14:35:46 -06:00
GitHub Action b6d8779c22 chore: generate 2025-12-26 14:35:46 -06:00
JackNorris b479893c9a fix: only show diagnostics block when errors exist (#6175) 2025-12-26 14:35:46 -06:00
opencode fbc9120dc3 release: v1.0.203 2025-12-26 14:35:45 -06:00
Dax Raad 95074ab541 prompt update to prevent searching via bash tool 2025-12-26 14:35:45 -06:00
Donghyun Shin e98a2a5e47 fix(lsp): make JDTLS use the correct config directory on Windows (#6121) 2025-12-26 14:35:45 -06:00
GitHub Action e60ef0286d chore: generate 2025-12-26 14:35:45 -06:00
Marco a64e24a991 feat: haskell lsp support (#6141) 2025-12-26 14:35:45 -06:00
opencode 01ca39791d release: v1.0.202 2025-12-26 14:35:44 -06:00
Adam 13b6a47e77 chore: brain icon 2025-12-26 14:35:44 -06:00
Adam 6fab9d2ac3 fix(desktop): user message display 2025-12-26 14:35:44 -06:00
Adam 2381a83fc7 fix(desktop): padding 2025-12-26 14:35:44 -06:00
Adam 4ed0dc1dcc fix(desktop): move session context to top-right 2025-12-26 14:35:44 -06:00
Adam 01dd70eab1 fix(desktop): missing keybinds in tooltips 2025-12-26 14:35:44 -06:00
Adam 3c5529bf19 fix(desktop): markdown rendering perf 2025-12-26 14:35:44 -06:00
GitHub Action 9a2f955910 chore: generate 2025-12-26 14:35:44 -06:00
Adam fefb9e5e0c fix(desktop): can't collapse project with active session 2025-12-26 14:35:44 -06:00
Adam 61bad1fd90 chore(ui): radio group primitive 2025-12-26 14:35:43 -06:00
Adam e42d586460 feat(desktop): better indicator that session is busy 2025-12-26 14:35:43 -06:00
opencode 7c4d1e9443 release: v1.0.201 2025-12-26 14:35:43 -06:00
Adam b5e5bf549e fix(desktop): so many prompt input fixes, merry christmas 2025-12-26 14:35:43 -06:00
GitHub Action f91839889f ignore: update download stats 2025-12-25 2025-12-26 14:35:43 -06:00
GitHub Action 512b3088f7 chore: generate 2025-12-26 14:35:42 -06:00
Dax Raad 0b5df9c4c4 remove list tool 2025-12-26 14:35:42 -06:00
opencode 371a1fd115 release: v1.0.200 2025-12-26 14:35:42 -06:00
Adam 87d904422b fix(desktop): scroll jank in session turn and review 2025-12-26 14:35:42 -06:00
GitHub Action 8d85f975d8 chore: generate 2025-12-26 14:35:41 -06:00
Adam 37f98de6e6 fix(desktop): override agent model 2025-12-26 14:35:41 -06:00
Adam 1285f67b39 fix(desktop): reconcile session diff updates 2025-12-26 14:35:41 -06:00
opencode 4c44a09991 release: v1.0.199 2025-12-26 14:35:41 -06:00
Adam 7466c08277 chore: toast on file load error 2025-12-26 14:35:40 -06:00
Adam d2da59f844 chore: cleanup dead code 2025-12-26 14:35:40 -06:00
Adam 52d81bdfd2 chore: show version on error page 2025-12-26 14:35:40 -06:00
Adam b3ab52fe79 fix(desktop): show server connection failure 2025-12-26 14:35:40 -06:00
Ahmed Mansour b39cf15833 fix: correct Content-Type headers for static assets on app.opencode.ai (#6113) 2025-12-26 14:35:40 -06:00
Connor Adams cc39dbedf2 docs: update skills to use canonical ~/.config/opencode location (#6132) 2025-12-26 14:35:40 -06:00
Robb Tolliver 6242baea0a docs: Corrected the number of built-in subagents in documentation (#6133) 2025-12-26 14:35:40 -06:00
GitHub Action b43a91db78 chore: generate 2025-12-26 14:35:40 -06:00
Dax Raad 19c1ed11b8 tui: disable tips display in home route 2025-12-26 14:35:40 -06:00
Dax Raad b4d25efbb9 CI 2025-12-26 14:35:39 -06:00
opencode fd56d8da8d release: v1.0.198 2025-12-26 14:35:39 -06:00
Dax Raad 8a1c3702a5 ci 2025-12-26 14:35:39 -06:00
GitHub Action a0621840d4 chore: generate 2025-12-26 14:35:38 -06:00
Dax Raad 073fba09a4 ci 2025-12-26 14:35:38 -06:00
opencode 49fa4fce1c release: v1.0.197 2025-12-26 14:35:38 -06:00
Dax Raad b59a021c94 Revert "feat: better styling for small screens (short and/or not wide) (#5968)"
This reverts commit ac371d2987.
2025-12-26 14:35:38 -06:00
GitHub Action 571a6ec6fc chore: generate 2025-12-26 14:35:37 -06:00
Patrick Schiel 7b89bb2a3b docs: add infos about server debugging (#6085) 2025-12-26 14:35:37 -06:00
opencode f6a622d2fd release: v1.0.196 2025-12-26 14:35:37 -06:00
Adam b47c9a4f9e fix(desktop): last text part streaming 2025-12-26 14:35:37 -06:00
Adam 807d61cb94 fix(desktop): render perf 2025-12-26 14:35:37 -06:00
Jay V 32cf23ffef docs: edits 2025-12-26 14:35:36 -06:00
Jay V f2ceb55514 docs: add comprehensive CLI command documentation for agent, mcp, session, stats, and web commands 2025-12-26 14:35:36 -06:00
Jay V cdc9c007bd docs: make MCP server documentation more scannable and add Sentry example 2025-12-26 14:35:36 -06:00
Adam 5de8dd131b chore: cleanup 2025-12-26 14:35:36 -06:00
Adam a663ab81e5 fix(desktop): summary flicker 2025-12-26 14:35:36 -06:00
Adam 4154067dfd feat(desktop): show read tool args 2025-12-26 14:35:35 -06:00
GitHub Action 4dcd196135 chore: generate 2025-12-26 14:35:35 -06:00
Adam 7fe4e76666 fix(desktop): better session navigation, hide child sessions 2025-12-26 14:35:35 -06:00
opencode c8b69d2ad0 release: v1.0.195 2025-12-26 14:35:35 -06:00
Adam 754d332692 chore: cleanup 2025-12-26 14:35:34 -06:00
GitHub Action a2cd45ada1 chore: generate 2025-12-26 14:35:34 -06:00
Adam a806023bdc refactor(ui): rewrite createAutoScroll with robust event tracking to fix sticky behavior 2025-12-26 14:35:33 -06:00
opencode 74a93f871f release: v1.0.194 2025-12-26 14:35:33 -06:00
Adam 59f1b41f73 chore: cleanup 2025-12-26 14:35:32 -06:00
Adam d06aae5544 fix(desktop): session sort when multiple active 2025-12-26 14:35:32 -06:00
Adam 4b236451e9 fix(share): page title should be session title 2025-12-26 14:35:32 -06:00
GitHub Action 0ff29879de ignore: update download stats 2025-12-24 2025-12-26 14:35:32 -06:00
Adam 2ade0d8f3e fix(desktop): exclude deprecated models 2025-12-26 14:35:31 -06:00
Adam 78046d06e1 fix(desktop): auto-scroll 2025-12-26 14:35:31 -06:00
Adam 270505b0be fix: don't disable text selection 2025-12-26 14:35:31 -06:00
Github Action c1cea004f4 Update Nix flake.lock and hashes 2025-12-26 14:35:31 -06:00
Adam 6cd9e5d3ac deps: update marked and marked-shiki 2025-12-26 14:35:30 -06:00
Adam df04dfc957 fix(desktop): hang on backtracing-prone regex 2025-12-26 14:35:30 -06:00
Adam fa53ab0cce fix(desktop): conditionally show review pane toggle 2025-12-26 14:35:30 -06:00
Ryan Vogel f22dd0646e fix: remove SVG favicon to improve SEO (#5755) 2025-12-26 14:35:30 -06:00
Aiden Cline 73e5315077 docs: tweak lsp.mdx 2025-12-26 14:35:30 -06:00
opencode-agent[bot] 7ea4a63257 docs: experimental LSP tool (#5943)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:35:30 -06:00
opencode-agent[bot] e94499d178 docs: skill tool/perm + parent keybind (#6001)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:35:29 -06:00
xiantang 05e8dfb8a2 docs: add Neovim to the list of editors (#6081) 2025-12-26 14:35:29 -06:00
Aiden Cline e8ca1d1f7d tweak: retry err 2025-12-26 14:35:29 -06:00
GitHub Action b8c2f9db59 chore: generate 2025-12-26 14:35:29 -06:00
Frank 245a63e3cf zen: sync 2025-12-26 14:35:29 -06:00
Abdelkader Boudih 0877ba302d feat(mcp): handle tools/list_changed notifications (#5913)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:35:29 -06:00
Aiden Cline 3f2aa25a94 test: add test for retry 2025-12-26 14:35:28 -06:00
Aiden Cline fe91899428 chore: regen sdk 2025-12-26 14:35:28 -06:00
GitHub Action e8bce0aaf2 chore: generate 2025-12-26 14:35:28 -06:00
Aiden Cline 8767cca3ea make 'The socket connection was closed unexpectedly' errors retryable 2025-12-26 14:35:28 -06:00
Rohan Mukherjee 6ef69dd905 chore: update AGENTS.md to ~150 lines (#5955) 2025-12-26 14:35:28 -06:00
David Hill 24f9ce30fc style: update current todo style (#6077) 2025-12-26 14:35:27 -06:00
rari404 523c47d285 feat(tui): console copy-to-clipboard via opentui (#5658)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:35:27 -06:00
OpeOginni 466ed745ab fix: resize textarea when pasting prompt less than 150 chars (#6070) 2025-12-26 14:35:27 -06:00
Matt Silverlock 1f2a4537bc providers: add Cloudflare AI Gateway (#5174) 2025-12-26 14:35:27 -06:00
Frank 0e50b4adea ci: adam is not a full stack engineer 2025-12-26 14:35:27 -06:00
Adam e7cf8b0813 fix: remove desktop dup 2025-12-26 14:35:27 -06:00
Viktor Nagy 459db5c655 Update gitlab.mdx to use the 2.x component version (#6062) 2025-12-26 14:35:27 -06:00
Github Action 0e6be83872 Update Nix flake.lock and hashes 2025-12-26 14:35:26 -06:00
Aiden Cline 3612e09e95 Revert "Add animated braille spinner to terminal title when agent is running (#5984)"
This reverts commit 59b87f60f7.
2025-12-26 14:35:26 -06:00
Aiden Cline 67e74aef10 tweak: update import & pr commands to use new share link ur 2025-12-26 14:35:26 -06:00
GitHub Action 3f9ee3330f chore: generate 2025-12-26 14:35:26 -06:00
Jon Redeker 180e1f35ef Add opencode-shell-strategy plugin to ecosystem (#5995) 2025-12-26 14:35:26 -06:00
ja e2e36adab7 feat(install): add standard CLI flags (--help, --version, --no-modify-path) (#5885) 2025-12-26 14:35:25 -06:00
David Hill 3b4519911e Add animated braille spinner to terminal title when agent is running (#5984)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:35:25 -06:00
GitHub Action 74cd5456b9 chore: generate 2025-12-26 14:35:25 -06:00
David Hill 6417a72f0b "Did you know?" start screen tips (#5982)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:35:25 -06:00
Daniel Gray dfdde091f5 fix: favorites and recents stay visible when filtering models (#6053) 2025-12-26 14:35:25 -06:00
Matt Silverlock 72cb02f4d5 docs: add MCP OAuth debugging section (#6047) 2025-12-26 14:35:24 -06:00
Aiden Cline 7692af47c5 ci: update zed sync script 2025-12-26 14:35:24 -06:00
ja 441f5ba911 fix(tui): prevent keybinds from executing when dialog is open (#6017) 2025-12-26 14:35:24 -06:00
Frank ad93850696 ci: fix 2025-12-26 14:35:24 -06:00
GitHub Action c19a0c5d09 chore: generate 2025-12-26 14:35:24 -06:00
Rhys Sullivan 5d0fd6e7b9 [feat]: prompt stashing (#6021) 2025-12-26 14:35:24 -06:00
GitHub Action 7dc18c35c2 chore: generate 2025-12-26 14:35:24 -06:00
Daniel Polito 9c2d6c7f6a Fix Github Pull Request Event (#6037) 2025-12-26 14:35:23 -06:00
opencode aafa717d4c release: v1.0.193 2025-12-26 14:35:23 -06:00
GitHub Action 327dd225a8 chore: generate 2025-12-26 14:35:23 -06:00
Sebastian Herrlinger eb5c544e45 indent wrapped todo items properly 2025-12-26 14:35:23 -06:00
opencode 6d1a9227b9 release: v1.0.192 2025-12-26 14:35:22 -06:00
Frank a73a36e533 zen: glm 4.7 2025-12-26 14:35:22 -06:00
Frank 2d6c3a886c ci: fix 2025-12-26 14:35:22 -06:00
Frank b926c4f75a ci: fix 2025-12-26 14:35:22 -06:00
GitHub Action e2b65f6ebd ignore: update download stats 2025-12-23 2025-12-26 14:35:22 -06:00
Sebastian Herrlinger 3341f3ae42 no intermediate autocomplete result to avoid flickering 2025-12-26 14:35:22 -06:00
GitHub Action 45a147858d chore: generate 2025-12-26 14:35:22 -06:00
Brendan Allan 38a67c2e5e desktop: kill_sidecar before update install on windows 2025-12-26 14:35:21 -06:00
Github Action d5fd078a42 Update Nix flake.lock and hashes 2025-12-26 14:35:21 -06:00
GitHub Action 5ce08ba95a chore: generate 2025-12-26 14:35:21 -06:00
Adam 80333e13e1 deps: diffs, shiki updates 2025-12-26 14:35:20 -06:00
opencode 4a6facee03 release: v1.0.191 2025-12-26 14:35:20 -06:00
GitHub Action 0cf94b991a chore: generate 2025-12-26 14:35:20 -06:00
Brendan Allan 4c4bbc89e1 console: add AppImage download link 2025-12-26 14:35:19 -06:00
GitHub Action dd4b348154 chore: generate 2025-12-26 14:35:19 -06:00
Matt Silverlock 9ffa3ab24e improve mcp CLI + ability to debug MCP oauth (#5980) 2025-12-26 14:35:19 -06:00
Aiden Cline d1581fbcf2 ci: docs sync 2025-12-26 14:35:19 -06:00
opencode-agent[bot] 0c4b36a433 docs: new /global/health API (#6006)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-12-26 14:35:19 -06:00
lif 139eb94bba fix: handle Windows CRLF line endings in grep tool (#5948)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-26 14:35:18 -06:00
Joel Hooks ce8ea03bd4 feat(plugin): allow compaction hook to replace prompt entirely (#5907) 2025-12-26 14:35:18 -06:00
Brendan Allan 0ccf6a8e2e ci: rename tauri -> desktop 2025-12-26 14:35:17 -06:00
Github Action cc35060491 Update Nix flake.lock and hashes 2025-12-26 14:35:17 -06:00
Adam 11878623bb chore: rename packages/tauri -> packages/desktop 2025-12-26 14:35:17 -06:00
Github Action ed59558070 Update Nix flake.lock and hashes 2025-12-26 14:35:16 -06:00
Adam 992327a05d chore: rename packages/desktop -> packages/app 2025-12-26 14:35:15 -06:00
GitHub Action 4ccd407259 chore: generate 2025-12-26 14:35:15 -06:00
Adam 3fef2d3b43 fix(desktop): better error messages on connection failure 2025-12-26 14:35:15 -06:00
Mohammad Alhashemi 30e6af1b2f feat(skill): add per-agent filtering to skill tool description (#6000) 2025-12-26 14:35:15 -06:00
Frank 1d71f33b71 zen: glm 4.7 2025-12-26 14:35:14 -06:00
Aiden Cline e96fdcf042 feat: better styling for small screens (short and/or not wide) (#5968) 2025-12-26 14:35:14 -06:00
GitHub Action 2d22216826 chore: generate 2025-12-26 14:35:14 -06:00
Dax Raad c81633aa3c tui: change task tool container to block layout for better subagent session display 2025-12-26 14:35:13 -06:00
opencode f8563e901b release: v1.0.190 2025-12-26 14:35:13 -06:00
GitHub Action 6f4ddd15c9 chore: generate 2025-12-26 14:35:13 -06:00
Mohammad Alhashemi d3eb85e330 feat: add native skill tool with permission system (#5930)
Co-authored-by: Dax Raad <d@ironbay.co>
2025-12-26 14:35:12 -06:00
opencode 86148821eb release: v1.0.189 2025-12-26 14:35:12 -06:00
GitHub Action 15c6052711 chore: generate 2025-12-26 14:35:11 -06:00
Jay V b0c88929ed docs: edit gitlab 2025-12-26 14:35:11 -06:00
opencode 58a9cb901e release: v1.0.188 2025-12-26 14:35:11 -06:00
Github Action 997f84a5bd Update Nix flake.lock and hashes 2025-12-26 14:35:10 -06:00
Frank 4a347586d0 remove sharp 2025-12-26 14:35:10 -06:00
Josh Thomas 10745d5bef fix(tui): resize textarea if text inserted via appendPrompt TUI API (#5983) 2025-12-26 14:35:09 -06:00
Frank 35e6fc42b0 zen: add glm 4.7 2025-12-26 14:35:09 -06:00
Aiden Cline 523ed96df8 ignore: agents.md 2025-12-26 14:35:09 -06:00
Adam 834f144c52 feat(desktop): review pane toggle 2025-12-26 14:35:09 -06:00
Viktor Nagy d5bf844e2c Add gitlab-opencode to GitLab docs
The current GitLab page describes OpenCode integration through GitLab Duo.

GitLab Duo is a paying functionality and is limited to workflows supported by GitLab.

GitLab-OpenCode is a community project that offers more flexiblity, better customization and easier setup to use OpenCode in GitLab. On the downside, it does not have the level of integration into GitLab as Duo does.
2025-12-26 14:35:09 -06:00
Frank 64a8852637 support glm 4.7 2025-12-26 14:35:09 -06:00
GitHub Action 73578d69cb chore: generate 2025-12-26 14:35:08 -06:00
Jay V f4bd11a43d ignore: update GitHub stars to 41K and project stats to reflect current growth 2025-12-26 14:35:08 -06:00
opencode 82826c0dde release: v1.0.187 2025-12-26 14:35:07 -06:00
Aiden Cline cc4a1e8849 ci: add failure case for changelog 2025-12-26 14:35:07 -06:00
Aiden Cline de20383402 test: rm claude skills test 2025-12-26 14:35:07 -06:00
Aiden Cline baee918ed0 fix: disable claude skill loading for now 2025-12-26 14:35:07 -06:00
Shpetim 67db1ef771 fix: stop auto execute on sendText vscode extension (#5994)
Co-authored-by: Shpetim <shpetim.alimi@ndbit.net>
2025-12-26 14:35:07 -06:00
Jon Redeker 1ac00cebea docs: add opencode-morph-fast-apply plugin to ecosystem (#5992) 2025-12-26 14:35:07 -06:00
Blake North 2fd23715f4 fix(providers.opencode): check config for api key in addition to auth (#5906) 2025-12-26 14:35:07 -06:00
Github Action a9ae1ef2c1 Update Nix flake.lock and hashes 2025-12-26 14:35:06 -06:00
Aiden Cline 141d24c28c fix: bundle more providers to fix breaking ai sdk issue 2025-12-26 14:35:06 -06:00
Rohan Godha f30992001f feat(tui): go to parent keybind for subagents (#5762) 2025-12-26 14:35:06 -06:00
GitHub Action e79b9ad111 chore: generate 2025-12-26 14:35:06 -06:00
wienans 72a9e23926 Add OpenChamber to ecosystem documentation (#5978) 2025-12-26 14:35:05 -06:00
ja 21d81dcf4e feat(lsp): add Tinymist LSP support for Typst (#5933) 2025-12-26 14:35:05 -06:00
Github Action 811c8aac5e Update Nix flake.lock and hashes 2025-12-26 14:35:05 -06:00
Sebastian Herrlinger 2cf69ec114 upgrade opentui to v0.1.63, enabling kitty alternate keys by default 2025-12-26 14:35:05 -06:00
Tim Kleinschmidt 439af1740a support clojure projects with built-in lsp (#5975) 2025-12-26 14:35:04 -06:00
Shpetim cf3d91e15b [FEATURE]: Show context usage in OpenCode Desktop Context usage (#5979) 2025-12-26 14:35:04 -06:00
Adam 344cdbb36b fix(prompt): better summary prompt 2025-12-26 14:35:04 -06:00
Daniel Polito 8ca4fdafd9 docs: Github Auto Pull Request Docs (#5974) 2025-12-26 14:35:04 -06:00
Adam 2fb212071b chore(desktop): auto scroll utility 2025-12-26 14:35:04 -06:00
GitHub Action 61be2c7f83 chore: generate 2025-12-26 14:35:04 -06:00
Will Marella e81c496bb6 Add keybindable commands to navigate between user messages (#5078)
Co-authored-by: Will@Cambridge <willcambridge@MacBook-Pro-59.local>
Co-authored-by: Will@Cambridge <willcambridge@macbookpro.mynetworksettings.com>
2025-12-26 14:35:03 -06:00
Aiden Cline c9a42f760f ci: tweak docs prompt 2025-12-26 14:35:03 -06:00
Daniel Polito b6089e4371 GitHub pull request event (#5335) 2025-12-26 14:35:03 -06:00
Adam f8b0ee9637 fix(share): expanded state and responsiveness 2025-12-26 14:35:03 -06:00
Aiden Cline 0b7b6d105e ci: limit to opencode repo 2025-12-26 14:35:03 -06:00
Lekë Dobruna 59cc15ecde fix: duplicate words in dialog options (#5944) 2025-12-26 14:35:03 -06:00
Adam 9236478182 fix(desktop): diff readability (colors) 2025-12-26 14:35:03 -06:00
Dax Raad fa9edbe9d2 fix url for web 2025-12-26 14:35:02 -06:00
GitHub Action 99e04ad2ed chore: generate 2025-12-26 14:35:02 -06:00
Buck Evan e70ae665d0 fix(read): narrow .env file blocking to not block .envrc (#5654)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-26 14:35:02 -06:00
opencode ac03ab1e10 release: v1.0.186 2025-12-26 14:35:02 -06:00
Adam cc572343be Revert "server: ensure frontend has correct port for PTY websocket connections (#5898)"
This reverts commit a05915ddc8.
2025-12-26 14:35:01 -06:00
Adam b0c860a17b Revert "fix: server"
This reverts commit dbaac79039.
2025-12-26 14:35:01 -06:00
GitHub Action bfcbdaceb7 ignore: update download stats 2025-12-22 2025-12-26 14:35:01 -06:00
Adam 73e8caa5e9 fix: server 2025-12-26 14:35:01 -06:00
Ashutosh Kumar 3c6c7c5278 server: ensure frontend has correct port for PTY websocket connections (#5898) 2025-12-26 14:35:00 -06:00
Adam 86d34aee8e Revert "fix: use current page port instead of hardcoded 4096 (#5949)"
This reverts commit d04a72a4ad.
2025-12-26 14:35:00 -06:00
Adam dcee57f572 fix(desktop): cleanup auto scroll 2025-12-26 14:35:00 -06:00
Adam 249431d84a feat(desktop): mobile responsiveness 2025-12-26 14:34:59 -06:00
Adam 806c21c535 fix(desktop): filter child sessions from header 2025-12-26 14:34:59 -06:00
Adam 309e594e41 feat(desktop): better task tool rendering 2025-12-26 14:34:58 -06:00
lif 355323f180 fix: use current page port instead of hardcoded 4096 (#5949)
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-26 14:34:58 -06:00
Aaron Iker 9f4c5d6736 feat: polish dialog & list styles for the desktop app, add fixed logos from models.dev (#5925) 2025-12-26 14:34:58 -06:00
Brendan Allan 24ed94bc1f ci: fix tauri build args 2025-12-26 14:34:58 -06:00
Brendan Allan 599da3ee8f ci: verbose build and re-enable appimage 2025-12-26 14:34:57 -06:00
GitHub Action b1cd7d617b chore: generate 2025-12-26 14:34:57 -06:00
Brendan Allan ddab63be61 ci: run prepare step for tauri build 2025-12-26 14:34:57 -06:00
Brendan Allan 18ff962f6e ci: try downloading artifact in desktop prepare 2025-12-26 14:34:57 -06:00
NN708 5a84872777 feat(desktop): arm64 build for linux (#5935) 2025-12-26 14:34:57 -06:00
Brendan Allan 1282f3457d ci: replace with just upload-artifact whole dir 2025-12-26 14:34:57 -06:00
Brendan Allan 2ea105735d ci: import bun shell 2025-12-26 14:34:56 -06:00
Brendan Allan 2e7086e321 try uploading artifacts in workflow 2025-12-26 14:34:56 -06:00
Brendan Allan 64a50bb030 remove actions artifact uploading 2025-12-26 14:34:56 -06:00
Github Action 320a434ae7 Update Nix flake.lock and hashes 2025-12-26 14:34:56 -06:00
GitHub Action 09875f5984 chore: generate 2025-12-26 14:34:56 -06:00
Brendan Allan 89ce5d9ea2 ci: try to upload cli artifacts 2025-12-26 14:34:55 -06:00
Aiden Cline 330e4a7c73 feat: add experimental lsp tool (#5886) 2025-12-26 14:34:55 -06:00
Luo Chen 79134dd16a feat: add nixd as lsp for nix language (#5929) 2025-12-26 14:34:55 -06:00
opencode-agent[bot] 10e454481c docs: Agent Skills (#5931)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:34:55 -06:00
Valerio Di Maggio d9b1b876e5 fix: ensure installation commands are using .quiet (#5758)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:34:55 -06:00
Ben Vargas e21c6d93e0 docs: add ai-sdk-provider-opencode-sdk to ecosystem (#5772) 2025-12-26 14:34:55 -06:00
Aiden Cline 7eba950c6d ci: update docs prompt 2025-12-26 14:34:55 -06:00
Aiden Cline 53149554c8 ci: add automatic doc update workflow 2025-12-26 14:34:55 -06:00
Neil Daquioag 2dc9528867 fix: support clipboard image paste (Ctrl+V) on Windows (#5919) 2025-12-26 14:34:54 -06:00
Aiden Cline b7a9cc5fd3 tweak: adjust minimax m2 topK and add minimax m2.1 topP 2025-12-26 14:34:54 -06:00
Noam Bressler a8f5e69ede fix: Perform snapshot in cases finish-step is not reached (#5912)
Co-authored-by: noamzbr <noamzbr@users.noreply.github.com>
2025-12-26 14:34:54 -06:00
Matt Silverlock 6fe028dd21 prompts: improve built-in /review prompt (#5918) 2025-12-26 14:34:54 -06:00
YeonGyu-Kim 6d6bc0140b feat(server): expose auto param in session.summarize for plugins (#5924) 2025-12-26 14:34:54 -06:00
GitHub Action a22a67b8bc chore: generate 2025-12-26 14:34:54 -06:00
Dax f2ddf5a7d4 feat: add Agent Skills support (#5921) 2025-12-26 14:34:54 -06:00
opencode d4cb80a5e1 release: v1.0.185 2025-12-26 14:34:53 -06:00
Dax Raad 221093a52b core: fix LSP server binary installation and shell command execution
- Ensure proper file permissions are set for installed LSP binaries on non-Windows platforms
- Add error handling for shell command execution in prompt system to prevent crashes
2025-12-26 14:34:53 -06:00
Aiden Cline 80271f939c tweak: update kimi-k2 and kimi-k2-thinking to use recommended temperature values 2025-12-26 14:34:53 -06:00
Dax Raad 0cc373de7b tui: fix SDK context usage and server port fallback
- Update SDK context to return client instead of event for proper usage
- Add server port fallback to 4096 when port 0 is specified but unavailable
- Fix SDK event listener usage in TUI app
2025-12-26 14:34:53 -06:00
Github Action d3f42d5302 Update Nix flake.lock and hashes 2025-12-26 14:34:52 -06:00
GitHub Action d0fb6e72fe chore: generate 2025-12-26 14:34:52 -06:00
Sherlock Holmes 9e9eaabe68 fix(deps): add missing @opencode-ai/plugin to dependencies (#5797) 2025-12-26 14:34:52 -06:00
Nalin Singh c04c92778b feat: add syntax highlighting for .ets files (#5889) 2025-12-26 14:34:52 -06:00
Adam Hosker 76b0ac2204 fix: prevent stats workflow from running on forks (#5897) 2025-12-26 14:34:52 -06:00
Abdelkader Boudih a81cd3c8c4 fix: use official MCP SDK for better tool schema handling (#5463) 2025-12-26 14:34:51 -06:00
GitHub Action 3bd665c5b6 chore: generate 2025-12-26 14:34:51 -06:00
Matt Silverlock 2fa7b01072 github: support schedule events (#5810) 2025-12-26 14:34:51 -06:00
GitHub Action 84a2180d0b ignore: update download stats 2025-12-21 2025-12-26 14:34:51 -06:00
opencode 6b21504dbc release: v1.0.184 2025-12-26 14:34:50 -06:00
Adam 244afdf4ef fix(desktop): layout regression 2025-12-26 14:34:50 -06:00
Adam 8bc49ccf2c fix(desktop): better keybind tooltips 2025-12-26 14:34:50 -06:00
Adam 0f5ce6fb04 fix(desktop): todo tool title 2025-12-26 14:34:50 -06:00
Adam 0d1f8aa40e fix(desktop): allow text selection 2025-12-26 14:34:50 -06:00
GitHub Action eb6ae39917 chore: generate 2025-12-26 14:34:50 -06:00
Adam c57e4dc411 fix(desktop): incorrect state dir on macos 2025-12-26 14:34:50 -06:00
opencode 61be42e08c release: v1.0.183 2025-12-26 14:34:49 -06:00
Adam 9f1ed1c186 fix(desktop): better error reporting 2025-12-26 14:34:49 -06:00
Adam 1cd4cafc35 fix(desktop): non-latin file paths failed 2025-12-26 14:34:49 -06:00
Adam 0ec1e14c63 fix(desktop): file loading errors 2025-12-26 14:34:49 -06:00
Github Action bfeef85d59 Update Nix flake.lock and hashes 2025-12-26 14:34:49 -06:00
Christopher Tso 31de4fc223 Make CLI build script Windows-friendly (#5835) 2025-12-26 14:34:48 -06:00
Aiden Cline 267447018e core: add verification that at least 1 primary agent is enabled, add regression tests (#5881) 2025-12-26 14:34:48 -06:00
GitHub Action 53d9c745cd chore: generate 2025-12-26 14:34:48 -06:00
opencode bfe2ddf81a release: v1.0.182 2025-12-26 14:34:47 -06:00
Aiden Cline 72bf8b4b6d fix: regression where config would error despite valid agents 2025-12-26 14:34:47 -06:00
opencode a0c279d75e release: v1.0.181 2025-12-26 14:34:47 -06:00
Adam 0e8303c016 fix(desktop): layout 2025-12-26 14:34:46 -06:00
Adam 7d88443ca8 feat(desktop): new layout 2025-12-26 14:34:46 -06:00
YuY801103 85ef34ca06 docs: add Traditional Chinese (Taiwan) README translation (#5861)
Co-authored-by: Yu <YuY801103@users.noreply.github.com>
2025-12-26 14:34:46 -06:00
Ryan Vogel 3fdeee126a docs: clarify model ID format for OpenCode provider (#5854) 2025-12-26 14:34:46 -06:00
Aiden Cline 7654839159 tweak: better error message if no primary agents are enabled 2025-12-26 14:34:46 -06:00
Ryan Vogel af66701f44 feat(docs): adding .md to docs pages shows raw markdown (#5823) 2025-12-26 14:34:46 -06:00
shamil2 25e25a2110 feat: add Catppuccin Frappé theme (#5821)
Co-authored-by: shamil2 <shamil2@users.noreply.github.com>
2025-12-26 14:34:45 -06:00
ja f57538ebba docs: add name property to model configuration example (#5853) 2025-12-26 14:34:45 -06:00
Shpetim cbe06c47b3 fix: system theme flicker (#5842)
Co-authored-by: Shpetim <shpetim.alimi@ndbit.net>
2025-12-26 14:34:45 -06:00
Frank cdfdd63bee zen: sync 2025-12-26 14:34:45 -06:00
Matt Silverlock 753b7362a3 feat: support configuring a default_agent across all API/user surfaces (#5843)
Co-authored-by: observerw <observerw@users.noreply.github.com>
2025-12-26 14:34:44 -06:00
Aiden Cline bd24e1a715 ci: adjust review agent prompt to discourage bad diffs 2025-12-26 14:34:44 -06:00
lif b1e7975bf8 fix: add transform case for gemini if mcp tool has missing array items (#5846) 2025-12-26 14:34:44 -06:00
ja 2d47d5cf83 fix: prioritize session list loading when resuming with -c (#5816)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:34:44 -06:00
Ryan Cassidy 1e867702a3 feat: add cursor theme (#5850) 2025-12-26 14:34:44 -06:00
GitHub Action 2a3b9bde24 chore: generate 2025-12-26 14:34:44 -06:00
opencode 2fec3c1859 release: v1.0.180 2025-12-26 14:34:43 -06:00
Dax Raad c60ef697f2 ci 2025-12-26 14:34:43 -06:00
opencode eb84c5820f release: v1.0.179 2025-12-26 14:34:43 -06:00
Dax Raad b7c3d0dd9d ci 2025-12-26 14:34:43 -06:00
GitHub Action e5954a97b4 chore: generate 2025-12-26 14:34:42 -06:00
Dax Raad 13e1091f0f ci 2025-12-26 14:34:42 -06:00
opencode 0d94abc69a release: v1.0.178 2025-12-26 14:34:42 -06:00
Dax Raad 5d5c2523b8 ci 2025-12-26 14:34:42 -06:00
Tommy D. Rossi 5bac846e98 feat: add endpoints to delete and update message parts (#5433) 2025-12-26 14:34:41 -06:00
GitHub Action 997cf6e207 chore: generate 2025-12-26 14:34:41 -06:00
opencode 8dfdd09dcb release: v1.0.177 2025-12-26 14:34:41 -06:00
Dax Raad 1024724cc6 ci 2025-12-26 14:34:40 -06:00
opencode 2774e4f6fd release: v1.0.176 2025-12-26 14:34:40 -06:00
Adam 29b22b46db fix(desktop): sidebar UX issues 2025-12-26 14:34:40 -06:00
Adam 9d75fbd1b2 fix(desktop): task rendering perf 2025-12-26 14:34:40 -06:00
GitHub Action 6557fa2e0b chore: generate 2025-12-26 14:34:40 -06:00
Adam 6e5d7fcae3 fix(desktop): show last text part when summarized 2025-12-26 14:34:39 -06:00
Adam 2912ba9526 fix(desktop): performance with lots of session changes 2025-12-26 14:34:39 -06:00
Adam 8ec421a3be fix(desktop): event reconnect gaps 2025-12-26 14:34:39 -06:00
GitHub Action b279b5022c ignore: update download stats 2025-12-20 2025-12-26 14:34:39 -06:00
opencode 78364f0137 release: v1.0.175 2025-12-26 14:34:38 -06:00
Adam 67a7483505 fix(desktop): perf tweaks 2025-12-26 14:34:38 -06:00
Adam a023273d9e fix(desktop): add retries to init promises 2025-12-26 14:34:38 -06:00
Adam 64dd604ea0 fix: types 2025-12-26 14:34:38 -06:00
Adam 34d9c657e8 fix: shouldEncode 2025-12-26 14:34:38 -06:00
Adam 1fcacfd889 fix(desktop): perf stuff 2025-12-26 14:34:38 -06:00
Adam 979a50ecfe fix(desktop): removed projects 2025-12-26 14:34:38 -06:00
Frank 901801d3d8 zen: add minimax m2.1 2025-12-26 14:34:37 -06:00
GitHub Action f5468a0399 chore: generate 2025-12-26 14:34:37 -06:00
Frank b9dd35bfcc zen: add minimax m2.1 2025-12-26 14:34:37 -06:00
opencode 192975e07e release: v1.0.174 2025-12-26 14:34:37 -06:00
GitHub Action 669a7e0897 chore: generate 2025-12-26 14:34:36 -06:00
Aiden Cline 1eaa71e236 fix: file permissions 2025-12-26 14:34:36 -06:00
opencode 583f0aa5ef release: v1.0.173 2025-12-26 14:34:36 -06:00
Ariane Emory 6c159d4f78 feat(tui): reinsert forked message text in prompt text input box when forking session (resolves #5495) (#5545)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-26 14:34:35 -06:00
GitHub Action 160b26f360 chore: generate 2025-12-26 14:34:35 -06:00
Aiden Cline f85a57d4af ci: fix archive 2025-12-26 14:34:35 -06:00
opencode 38eb36f4f6 release: v1.0.172 2025-12-26 14:34:34 -06:00
Aiden Cline 01ddf7deb6 ci: fix undefined 2025-12-26 14:34:34 -06:00
Aiden Cline be47071ce5 ci: fix release notes 2025-12-26 14:34:34 -06:00
Aiden Cline 7bc6ac85a3 Revert "tweak: better release notes (grouped changelog) (#5768)"
This reverts commit b99afdad91.
2025-12-26 14:34:34 -06:00
Aiden Cline 65ca3c463a Revert "tweak: DevEx to run changelog independently (#5774)"
This reverts commit 7f8e799392.
2025-12-26 14:34:33 -06:00
Aiden Cline d30536efa6 Revert "ci: gemini 3 flash doesnt exist in pinned cicd version (#5776)"
This reverts commit 382905602c.
2025-12-26 14:34:33 -06:00
Aiden Cline 1682fdf0d1 test: fixture cleanup 2025-12-26 14:34:33 -06:00
Aiden Cline d2919b1d74 test: fix test case 2025-12-26 14:34:33 -06:00
Kaspar 72c4cadb22 tweak: Make LSP message more accurate when LSPs disabled (#5814) 2025-12-26 14:34:33 -06:00
Aiden Cline 35787ffaed chore: rm dead code 2025-12-26 14:34:33 -06:00
Aiden Cline c0fe626748 ci: fix publish auth failure 2025-12-26 14:34:32 -06:00
1XD 3306d8f666 docs: replace deprecated mise ubi backend with github backend (#5811) 2025-12-26 14:34:32 -06:00
Github Action 5e8b1d8f4a Update Nix flake.lock and hashes 2025-12-26 14:34:31 -06:00
Frank 9ce16ac197 zen: sync 2025-12-26 14:34:31 -06:00
Frank 8597343df0 zen: sync 2025-12-26 14:34:31 -06:00
Frank 47444b398b zen: sync 2025-12-26 14:34:31 -06:00
Cameron b95be947c6 Desktop file encoding issue (#5490) 2025-12-26 14:34:30 -06:00
Adam f17e4fbaf0 chore: cleanup 2025-12-26 14:34:30 -06:00
Adam fd9e54978a fix(desktop): auto-scroll and session perf 2025-12-26 14:34:30 -06:00
Adam 59e6454823 fix(desktop): don't use tauri http for sse events 2025-12-26 14:34:30 -06:00
Adam 8199a3b5e5 fix(desktop): error height 2025-12-26 14:34:30 -06:00
Github Action 881465a7d0 Update Nix flake.lock and hashes 2025-12-26 14:34:29 -06:00
Aiden Cline feb9f9dbd6 tweak: use fetch instead of octokit for now 2025-12-26 14:34:29 -06:00
Steven T. Cramer 5c47adb782 docs: add Windows Terminal Shift+Enter configuration guide (#5788) 2025-12-26 14:34:29 -06:00
Brendan Allan bf653d44d1 ci: separate standalone publishing from dependent publishing (#5634)
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:34:28 -06:00
Dax Raad 71af1859fa core: prevent file system scanning when in root directory to avoid unnecessary operations 2025-12-26 14:34:28 -06:00
Aiden Cline f084a0cfee ci: change token for gh release to allow discord job to actually trigger see: https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow 2025-12-26 14:34:28 -06:00
GitHub Action 28d2ae5208 chore: generate 2025-12-26 14:34:27 -06:00
José Valim cdb7949280 Do not include hidden agents in ACP (#5791) 2025-12-26 14:34:27 -06:00
Github Action f02d1ef4a1 Update Nix flake.lock and hashes 2025-12-26 14:34:27 -06:00
Sebastian Herrlinger 59968ebd25 upgrade opentui to v0.1.62, enabling textarea mouse scroll and cursor set 2025-12-26 14:34:26 -06:00
Github Action a346d22c25 Update Nix flake.lock and hashes 2025-12-26 14:34:26 -06:00
opencode f15db51fe0 release: v1.0.170 2025-12-26 14:34:26 -06:00
Adam 92dfbdc08b fix(desktop): error handling 2025-12-26 14:34:25 -06:00
Adam 923bad1f70 chore: logging 2025-12-26 14:34:25 -06:00
Adam 551c5283bd fix(desktop): separate prompt history for shell 2025-12-26 14:34:25 -06:00
Adam a05851b0ea fix(desktop): don't navigate prompt history if dirty 2025-12-26 14:34:25 -06:00
Sebastian Herrlinger d888a318c5 user messages as markdown with toggle 2025-12-26 14:34:25 -06:00
GitHub Action cfa95461b2 ignore: update download stats 2025-12-19 2025-12-26 14:34:25 -06:00
Github Action 182cf2d878 Update Nix flake.lock and hashes 2025-12-26 14:34:24 -06:00
Brendan Allan 6473b3f91a Update SolidStart and bring back HttpHeader usage (#5355)
Co-authored-by: Github Action <action@github.com>
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-26 14:34:24 -06:00
Sherlock Holmes 6c8a7f05be feat(tui): implement smooth scrolling for autocomplete dropdown navigation (#5559)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:34:24 -06:00
Eric Shirley d8a450ce17 lsp: add oxlint server (#5570) 2025-12-26 14:34:24 -06:00
Aiden Cline d96fa874b0 docs: contributing 2025-12-26 14:34:24 -06:00
Aiden Cline 29541578b1 ci: only run generate for dev 2025-12-26 14:34:24 -06:00
Luke Parker 4c805fb327 ci: gemini 3 flash doesnt exist in pinned cicd version (#5776) 2025-12-26 14:34:24 -06:00
GitHub Action 906bb8821c chore: generate 2025-12-26 14:34:23 -06:00
Matt Silverlock df93dbe347 fix: use correct octokit API for PR review comment reactions (#5778) 2025-12-26 14:34:23 -06:00
GitHub Action dc7562189b chore: generate 2025-12-26 14:34:23 -06:00
Brendan Allan e88ef5b294 tauri: remove pinch-to-zoom on window 2025-12-26 14:34:23 -06:00
Basit Mustafa 16b7e85256 docs(ecosystem): add opencode-zellij-namer plugin (#5771) 2025-12-26 14:34:23 -06:00
Luke Parker 5e403d3a9e tweak: DevEx to run changelog independently (#5774) 2025-12-26 14:34:22 -06:00
opencode d058c0aa2d release: v1.0.169 2025-12-26 14:34:22 -06:00
Adam ba081d64ad fix(desktop): shell mode 2025-12-26 14:34:22 -06:00
Adam 24005e3fc8 fix(desktop): extra reqs 2025-12-26 14:34:21 -06:00
Aiden Cline 9f3942d7da ci: better err msg for generate workflow 2025-12-26 14:34:21 -06:00
Luke Parker 536b4e9055 tweak: better release notes (grouped changelog) (#5768) 2025-12-26 14:34:21 -06:00
Aiden Cline 4d1d499c93 fix: better api call error msgs in some cases 2025-12-26 14:34:21 -06:00
GitHub Action f5c5eff3ac chore: generate 2025-12-26 14:34:21 -06:00
Rohan Godha 2e0c6d5274 feat(tui): click on subagents to open them (#5761)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:34:21 -06:00
opencode 39e4b85273 release: v1.0.168 2025-12-26 14:34:20 -06:00
Github Action 0b4e84b9fa Update Nix flake.lock and hashes 2025-12-26 14:34:20 -06:00
GitHub Action 72105666cc chore: generate 2025-12-26 14:34:20 -06:00
Adam 2e3c122462 fix: id 2025-12-26 14:34:19 -06:00
Adam 80935a2dd1 fix(desktop): expanded states 2025-12-26 14:34:19 -06:00
Adam a858dae20b chore: cleanup 2025-12-26 14:34:19 -06:00
Adam 2e431a0269 fix(desktop): smaller max-width when review open 2025-12-26 14:34:19 -06:00
Aiden Cline 66aee1bffd ci: fix generate 2025-12-26 14:34:19 -06:00
Aiden Cline 0c2bf6870b ci: fix file perm 2025-12-26 14:34:19 -06:00
Aiden Cline e73b8cf171 ci: handle case where generate.yml fails better 2025-12-26 14:34:19 -06:00
Aiden Cline 0c4d68e9d3 tweak: more retry cases 2025-12-26 14:34:19 -06:00
Aiden Cline 92270f582a chore: rm dead code 2025-12-26 14:34:18 -06:00
GitHub Action 9aabebd644 chore: format code 2025-12-26 14:34:18 -06:00
OpeOginni 859ee9904d feat: add experimental support for Ty language server (#5575) 2025-12-26 14:34:18 -06:00
Adam d02b54085e fix(desktop): markdown styles 2025-12-26 14:34:18 -06:00
Adam 65be48a14d fix(desktop): don't show image button in shell mode 2025-12-26 14:34:18 -06:00
Frank 0b44adf551 zen: cleanup headers 2025-12-26 14:34:18 -06:00
Aiden Cline eed4a950c9 rm interleaved thinking filter for certain kimi k2 thinking model providers that were bugged 2025-12-26 14:34:18 -06:00
Adam cc932a5f4f fix(desktop): submit prompt 2025-12-26 14:34:18 -06:00
GitHub Action d4c89df601 chore: format code 2025-12-26 14:34:17 -06:00
Matt Silverlock f707f6254e github: add OIDC_BASE_URL for custom GitHub App installs (#5756) 2025-12-26 14:34:17 -06:00
Adam de933a68c9 fix(desktop): checkbox render in safari fml 2025-12-26 14:34:17 -06:00
Adam 24a9f95cc7 fix(desktop): rendering shell mode messages 2025-12-26 14:34:17 -06:00
Adam 6469ab5f95 fix(desktop): error styles 2025-12-26 14:34:16 -06:00
Adam c31545351a fix(desktop): prompt history nav, optimistic prompt dup 2025-12-26 14:34:16 -06:00
Adam 51e5bc3e2b fix(desktop): session ordered by most recent 2025-12-26 14:34:16 -06:00
Adam 6759a50bee feat(desktop): shell mode 2025-12-26 14:34:16 -06:00
Adam 71e92a7e76 chore: cleanup 2025-12-26 14:34:15 -06:00
GitHub Action ac25f3c738 chore: format code 2025-12-26 14:34:15 -06:00
Ariane Emory 52d0dbb2be feat(tui): fork slash command for keyboard-friendly session forking (resolves #5599) (#5610)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-26 14:34:15 -06:00
Jeon Suyeol 83b3cf6135 fix(command): validate model before executing slash command (#5740) 2025-12-26 14:34:15 -06:00
Frank b67b876b00 Revert "add client header"
This reverts commit 2fb89161c8.
2025-12-26 14:34:15 -06:00
barış e1b7bdb35f docs: fix typos (#5753) 2025-12-26 14:34:15 -06:00
Aiden Cline 198ccc5ef7 ci: fix discord 2025-12-26 14:34:15 -06:00
Aiden Cline 04665c8c53 bump bun version 2025-12-26 14:34:14 -06:00
Daniel Polito 89ef686546 Improve Github Action Hallucinations (#5751) 2025-12-26 14:34:14 -06:00
Frank dc1cc77ee1 add client header 2025-12-26 14:34:14 -06:00
GitHub Action f5a1cdad2f ignore: update download stats 2025-12-18 2025-12-26 14:34:14 -06:00
Brendan Allan 9c1dbcdaae tauri: disable pinch zoom on linux (#5735) 2025-12-26 14:34:14 -06:00
Brendan Allan 26e80c6ce3 tauri: configure display backends more correctly on linux (#5730) 2025-12-26 14:34:13 -06:00
GitHub Action de9aee8f40 chore: format code 2025-12-26 14:34:13 -06:00
Adam d34464593c fix(desktop): disable pinch to zoom 2025-12-26 14:34:13 -06:00
Adam 1ece7ebe50 feat(desktop): custom update toast 2025-12-26 14:34:12 -06:00
opencode 97c6bd5ab2 release: v1.0.167 2025-12-26 14:34:12 -06:00
shuv 81d367bd8e fix: handle empty directory query parameter in server middleware (#5732) 2025-12-26 14:34:12 -06:00
Brendan Allan ea99db7d72 tauri: say OpenCode Server instead of OpenCode CLI 2025-12-26 14:34:11 -06:00
Brendan Allan 8ecd7566c1 tauri: server spawn fail dialog w/ copy logs button (#5729) 2025-12-26 14:34:11 -06:00
Frank ede5871cf4 zen: error handling for stream requests 2025-12-26 14:34:11 -06:00
GitHub Action 9aebc4d976 chore: format code 2025-12-26 14:34:11 -06:00
Jeon Suyeol 0a1ef150c6 docs: add OPENCODE_DISABLE_TERMINAL_TITLE to environment variables (#5725) 2025-12-26 14:34:11 -06:00
Jake Nelson e1b2fd5044 feat(tui): add option to disable terminal title (#5713) 2025-12-26 14:34:10 -06:00
Frank f84e54517f zen: error handling for stream requests 2025-12-26 14:34:10 -06:00
Rohan Mukherjee 71a1aa5b16 MCP improvements (#5699) 2025-12-26 14:34:10 -06:00
Jay V f0b2dfce6e docs: add legal pages with privacy policy and terms of service links 2025-12-26 14:34:10 -06:00
GitHub Action 9e842e5fbf chore: format code 2025-12-26 14:34:10 -06:00
Ryan Vogel 7becca83ae docs: add opencode.cafe to ecosystem page (#5714) 2025-12-26 14:34:10 -06:00
opencode 7b333eb352 release: v1.0.166 2025-12-26 14:34:09 -06:00
Adam 7091299de5 chore: cleanup 2025-12-26 14:34:09 -06:00
Adam 09172e9a55 fix: better init error messages 2025-12-26 14:34:09 -06:00
Adam 371a47ca11 fix: auto-scroll 2025-12-26 14:34:09 -06:00
GitHub Action e5c6960b4c chore: format code 2025-12-26 14:34:09 -06:00
Sercan Sagman 193303a325 fix(tui): exclude reverted assistant reply when copying last message (#5705)
Signed-off-by: assagman <ahmetsercansagman@gmail.com>
2025-12-26 14:34:08 -06:00
opencode 836a7f5d83 release: v1.0.165 2025-12-26 14:34:08 -06:00
Adam b4b633eee3 feat(desktop): startup errors shown 2025-12-26 14:34:08 -06:00
Adam 2f6e4e7e77 feat(desktop): optimistic prompt submit 2025-12-26 14:34:07 -06:00
Aiden Cline a43981fa66 fix: better error messages 2025-12-26 14:34:07 -06:00
Aiden Cline 8b344dd8d1 fix: prevent 1 from showing when preparing write 2025-12-26 14:34:07 -06:00
Nalin Singh ddb1105a94 feat: add viewportOptions to scrollbox for padding adjustments to avoid scrollbar overlap (#5703) 2025-12-26 14:34:07 -06:00
Spoon 6b3e0923fe UI: show plugins in /status (#4515)
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:34:07 -06:00
Nalin Singh 8a19de4672 fix: prevent session list selection from jumping to active session when confirming delete (#5666) 2025-12-26 14:34:07 -06:00
Joel Hooks 247d1b34cf feat(plugin): add experimental.session.compacting hook for pre-compaction context injection (#5698)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:34:07 -06:00
Github Action 3e11f1d3ec Update Nix flake.lock and hashes 2025-12-26 14:34:07 -06:00
Adam b860ff8901 chore: localStorage -> tauri store 2025-12-26 14:34:06 -06:00
Brendan Allan 9c2ee8320e console: use download proxy to rename mac and windows installers (#5697)
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:34:06 -06:00
Ravi Kumar c83f2f3676 fix(tui): resolve session_status TypeError (#5520) 2025-12-26 14:34:06 -06:00
Aiden Cline b9eb55b772 fix: keep session dialog open if deleting session 2025-12-26 14:34:06 -06:00
Aiden Cline c7a7fced41 fix: remove needless tui event publish on session delete 2025-12-26 14:34:06 -06:00
GitHub Action 91d1b65920 chore: format code 2025-12-26 14:34:06 -06:00
Adam 59ddf2a823 fix: sticky visual issues 2025-12-26 14:34:06 -06:00
Aiden Cline befd8b59ee ci: add windows label to triage bot 2025-12-26 14:34:06 -06:00
GitHub Action aa51979592 chore: format code 2025-12-26 14:34:05 -06:00
Brendan Allan 5a9b52e92e console: add /download/[platform] endpoint 2025-12-26 14:34:05 -06:00
Qio 4222758324 add OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX to override 32k default (#5679)
Co-authored-by: qio <handsomehust@gmail.com>
2025-12-26 14:34:05 -06:00
Adam edd7032cb8 feat(desktop): inter and ibm plex mono 2025-12-26 14:34:05 -06:00
Adam eff31afd33 feat(desktop): inter and ibm plex mono 2025-12-26 14:34:05 -06:00
Paolo Ricciuti d1fc208e4a fix: send mcpName as state if authUrl doesn't have state (#5681) 2025-12-26 14:34:05 -06:00
Aiden Cline 628cab0f6b test: add regression test for setCacheKey option 2025-12-26 14:34:05 -06:00
Spoon bf1f0a52c0 batch: enable edit, todoread, clarify error message, minor tool description change (#5659) 2025-12-26 14:34:05 -06:00
Rhys Sullivan 61aecc92ea fix: change subagent navigation order to newest-to-oldest (#5680) 2025-12-26 14:34:05 -06:00
Shantur Rathore a652fe9076 fix: config option setCacheKey not being respected (#5686) 2025-12-26 14:34:04 -06:00
Aiden Cline 92f4fdc568 docs: update share link 2025-12-26 14:34:04 -06:00
Frank c64c29821a zen: add gemini 3 flash 2025-12-26 14:34:04 -06:00
Brendan Allan 5b65e14c2b tauri: nsis header and sidebar 2025-12-26 14:34:04 -06:00
Brendan Allan 3f8f6177d4 tauri: update nsis icon 2025-12-26 14:34:04 -06:00
Stoufiler 3a46b78994 docs: Sort LSP Server list (#5688) 2025-12-26 14:34:04 -06:00
Rohan Mukherjee d14f767381 feat: added lucent-orng theme (#5678)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2025-12-26 14:34:04 -06:00
Sachnun 355e027ea6 fix: remove unsupported parameter from bash tool description (#5676) 2025-12-26 14:34:04 -06:00
Brendan Allan f4e851d9a1 tauri: return after update failures 2025-12-26 14:34:04 -06:00
GitHub Action 5524c9c6dc chore: format code 2025-12-26 14:34:04 -06:00
Brendan Allan 566e0f75a4 tauri: only alert on update failure when triggered manually 2025-12-26 14:34:03 -06:00
Brendan Allan daf387861d tauri: dev icons + separate prod config (#5691)
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:34:03 -06:00
Adam c0b9f1c147 fix: command shortcuts 2025-12-26 14:34:03 -06:00
Github Action 978c6a30f6 Update Nix flake.lock and hashes 2025-12-26 14:34:03 -06:00
Amadeus Demarzi 96719bdf52 Diffs Performance Improvements (#5653)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-12-26 14:34:03 -06:00
GitHub Action 561e9f35ae ignore: update download stats 2025-12-17 2025-12-26 14:34:03 -06:00
David Hill 201b3f4014 wip: auto-detect OS and show desktop download button 2025-12-26 14:34:03 -06:00
David Hill b3e173fb8f fix: website cta button 2025-12-26 14:34:03 -06:00
Adam 9f6032f250 chore: cleanup 2025-12-26 14:34:02 -06:00
Adam 6362cff7cb feat(desktop): share sessions 2025-12-26 14:34:02 -06:00
Adam 0ae1c170c2 feat(desktop): lsp diagnostics displayed 2025-12-26 14:34:02 -06:00
Github Action 6afe7337a9 Update Nix flake.lock and hashes 2025-12-26 14:34:02 -06:00
GitHub Action 831ef65a41 chore: format code 2025-12-26 14:34:02 -06:00
Sebastian Herrlinger 797271e2e2 upgrade opentui to v0.1.61 2025-12-26 14:34:01 -06:00
Jeon Suyeol f268d81e16 Add availability to disable terminal title using OPENCODE_DISABLE_TERMINAL_TITLE env (#5661) 2025-12-26 14:34:01 -06:00
Dax Raad 1b28bd68f6 ci: update publish workflow configuration 2025-12-26 14:34:01 -06:00
GitHub Action 798dee27d0 chore: format code 2025-12-26 14:34:01 -06:00
Spoon 463bf7ab3a plugin(hook): add task tool execution hooks and command context tracking (#5642) 2025-12-26 14:34:01 -06:00
Aiden Cline e4d7bb4869 ci: tweak triage 2025-12-26 14:34:01 -06:00
Matt Silverlock 80f1173652 github: add configurable mentions input (#5655) 2025-12-26 14:34:00 -06:00
Adam be7b9c05a5 tui: increase session width to accommodate longer code blocks and improve readability 2025-12-26 14:34:00 -06:00
David Hill 31897bf974 fix: load more button 2025-12-26 14:34:00 -06:00
Aiden Cline 09a2d3b8b7 ci: fix missing pkg issue 2025-12-26 14:34:00 -06:00
Dax Raad f80e555a58 ci: fix release draft configuration to prevent automatic draft flag 2025-12-26 14:34:00 -06:00
GitHub Action 59a618ddcb chore: format code 2025-12-26 14:33:59 -06:00
Adam 1cbc9e252c fix(share): content wasn't centered 2025-12-26 14:33:59 -06:00
opencode 9e98bd3283 release: v1.0.164 2025-12-26 14:33:59 -06:00
Adam 15ca31370d fix(desktop): prompt history nav 2025-12-26 14:33:59 -06:00
Aiden Cline d9a6000a33 fix: user invoked subtasks causing tool_use or missing thinking signa… (#5650) 2025-12-26 14:33:59 -06:00
Adam daaf88b8e4 fix(desktop): auto-scroll 2025-12-26 14:33:59 -06:00
Adam c15b581131 fix(desktop): focus prompt input after dialog close 2025-12-26 14:33:59 -06:00
Adam 67973728ef fix(desktop): prompt history navigation 2025-12-26 14:33:58 -06:00
Adam 3d7bdfc52e fix: auto-scroll 2025-12-26 14:33:58 -06:00
Adam 02c8c2cb67 fix: working logic 2025-12-26 14:33:58 -06:00
Adam 53c386e2e5 fix: prompt input multi line input 2025-12-26 14:33:58 -06:00
Adam 196ce5af40 fix: defensive audio init 2025-12-26 14:33:58 -06:00
Eric Guo 299e88b9cf feat(desktop): Loading more session number per project by button (#5616)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:58 -06:00
matvey 0302b8317b feat: add experimental oxfmt formatter (#5620) 2025-12-26 14:33:57 -06:00
Aiden Cline cc7fa6af83 ci: include desktop & tauri in release notes 2025-12-26 14:33:57 -06:00
Aiden Cline 5dd09b13b1 ci: fix branch name 2025-12-26 14:33:57 -06:00
Aiden Cline 77c7b094a8 fix: git branch filewatcher, add flag to completely disable watcher 2025-12-26 14:33:57 -06:00
Aiden Cline 2fdf6e1029 ci: fix triage 2025-12-26 14:33:57 -06:00
Aiden Cline c3f1e631a0 ci: auto tag github action once a change is shipped for it 2025-12-26 14:33:57 -06:00
Dax Raad 9c8b670886 core: update plugin dependency and config loading for .opencode directory support 2025-12-26 14:33:57 -06:00
Dax Raad d0cb59c000 core: fix Tauri desktop app SSE connection timeout
- Add heartbeat events to /global/event and /event SSE endpoints
- Send server.heartbeat event every 30s to prevent WKWebView 60s timeout
- Fixes desktop app disconnecting from global events after 1 minute
2025-12-26 14:33:57 -06:00
Dax Raad 9dbbdcc555 fix 2025-12-26 14:33:57 -06:00
Dax Raad 8b3e0422ee ignore: update opencode plugin dependency 2025-12-26 14:33:57 -06:00
Dax Raad 13e26ff18a core: ensure desktop app loads user shell environment variables
Changes shell spawn flags from -l to -il so that ~/.zshrc and
~/.bashrc are sourced when starting the desktop app on macOS
and Linux. This fixes missing PATH and other environment
variables that users expect to be available.
2025-12-26 14:33:57 -06:00
Adam 183cbc1981 feat(desktop): show retries 2025-12-26 14:33:57 -06:00
Adam 25ca8cec4d fix: layout badness 2025-12-26 14:33:56 -06:00
Adam 01261e7f79 fix: defensive audio init 2025-12-26 14:33:56 -06:00
Adam e598524b40 feat(desktop): show write tool output 2025-12-26 14:33:56 -06:00
Adam 582d9b4187 chore: cleanup 2025-12-26 14:33:56 -06:00
Github Action babba4e6db Update Nix flake.lock and hashes 2025-12-26 14:33:56 -06:00
Dax Raad 419db64fc9 chore: update opencode plugin dependencies and fix tauri sidecar path 2025-12-26 14:33:56 -06:00
Dax Raad f386601cbd tui: fix autocomplete file loading and update dependencies 2025-12-26 14:33:55 -06:00
David Hill 12d5a83ba3 fix: remove the selected state from button when select deselected 2025-12-26 14:33:55 -06:00
David Hill cc17c3338e wip: add active state to open select 2025-12-26 14:33:55 -06:00
Fran Zekan 3460e29107 fix: enable shell alias expansion in ! command (#5621) 2025-12-26 14:33:55 -06:00
GitHub Action e11051a29a chore: format code 2025-12-26 14:33:55 -06:00
shekohex debba2ab37 docs: fix typo in Google Antigravity github link (#5625) 2025-12-26 14:33:54 -06:00
jinzhongjia 91b7c3bfa4 docs: Add new project entry for opencode.nvim frontend (#5626) 2025-12-26 14:33:54 -06:00
Connor Adams 8c15365133 docs: add homebrew command for opencode-desktop (#5631)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:54 -06:00
Aiden Cline b4db2b1bdd fix: github install cmd if repo has . in it 2025-12-26 14:33:54 -06:00
David Hill 5570b9f05f fix: breadcrumb dropdown position left aligned 2025-12-26 14:33:53 -06:00
David Hill 370aa7b515 Revert "wip: make the default container wider"
This reverts commit 1f18f389c0.
2025-12-26 14:33:53 -06:00
Tommy D. Rossi e8c2e494d0 fix: use system prompt field from prompt input (#5633)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-26 14:33:53 -06:00
GitHub Action 434e12666b chore: format code 2025-12-26 14:33:53 -06:00
Aiden Cline 1de89153d8 ci: tweak triage 2025-12-26 14:33:52 -06:00
Brendan Allan 22192a2e92 Keep release a draft until all builds are finished (#5632)
Co-authored-by: opencode <opencode@sst.dev>
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:33:52 -06:00
David Hill 73b6464998 wip: make the default container wider 2025-12-26 14:33:52 -06:00
Simon D'Morias 3f571c6117 fix: preserve argument boundaries in run command (#4979) 2025-12-26 14:33:51 -06:00
David Hill 4126084dfd Revert "fix: strip parentheses from file paths generated by llm"
This reverts commit 6c1a1a77b7.
2025-12-26 14:33:51 -06:00
GitHub Action 5529d1b741 ignore: update download stats 2025-12-16 2025-12-26 14:33:51 -06:00
GitHub Action 3555acbe78 chore: format code 2025-12-26 14:33:51 -06:00
Brendan Allan 331ece741f tauri: explicitly kill sidecar before updater relaunch 2025-12-26 14:33:51 -06:00
David Hill 039f853d80 fix: strip parentheses from file paths generated by llm 2025-12-26 14:33:51 -06:00
David Hill 38a0f49595 fix: font size updates 2025-12-26 14:33:50 -06:00
David Hill f82ac7a7b8 wip: font-size updates 2025-12-26 14:33:50 -06:00
GitHub Action a8ff5b5174 chore: format code 2025-12-26 14:33:50 -06:00
Brendan Allan 3b1b621d00 tauri: macos-only app menu 2025-12-26 14:33:50 -06:00
Aiden Cline 200ea62f56 fix: small bug w/ install script 2025-12-26 14:33:50 -06:00
Aiden Cline 158f68070f chore: centralize dep to catalog & fix typos 2025-12-26 14:33:49 -06:00
Aiden Cline aea35df988 ci: Update issue assignment and labeling guidelines
Clarified assignment responsibilities and labeling for issues related to OpenCode tools.
2025-12-26 14:33:49 -06:00
Dax Raad 73b44d198e tui: fix dialog select items taking up 2 lines when truncated
Prevents text wrapping in dialog select options by removing wrapMode,
ensuring truncated text stays on single line and maintains proper timestamp visibility
2025-12-26 14:33:49 -06:00
Luke Parker 3b75ffdc41 fix: debounce LSP diagnostics to get complete results (#5600) 2025-12-26 14:33:48 -06:00
DS bf8210d750 fix: rejoin system prompt if experimental plugin hook triggers to preserve caching (#5550)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-26 14:33:48 -06:00
Lucas Duailibe 033212c201 make install script use tmp dir (#5601) 2025-12-26 14:33:48 -06:00
Aiden Cline dc5df2cd10 ci: cheaper model 2025-12-26 14:33:48 -06:00
Aiden Cline ca2f281a30 ci: ignore 2025-12-26 14:33:48 -06:00
GitHub Action 85ba9b7831 chore: format code 2025-12-26 14:33:48 -06:00
Aiden Cline 1f3129fe96 ci: auto triage issues 2025-12-26 14:33:48 -06:00
opencode b0c241a59f release: v1.0.162 2025-12-26 14:33:47 -06:00
GitHub Action 780e32bc2b chore: format code 2025-12-26 14:33:47 -06:00
Dax Raad 133e0983d6 tui: update dialog context and server to use new single dialog system 2025-12-26 14:33:47 -06:00
Dax Raad 1f24cccd66 tui: refactor dialog system to use single active dialog instead of stack 2025-12-26 14:33:47 -06:00
opencode 3774fafb26 release: v1.0.161 2025-12-26 14:33:46 -06:00
Adam c0842ec113 fix: undefined events 2025-12-26 14:33:46 -06:00
opencode-agent[bot] 2560cba9ef docs: models --refresh flag in cli.mdx (#5596)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2025-12-26 14:33:46 -06:00
Luke Parker 9b470429de fix(win32): Normalise LSP paths on windows (fixes lua) (#5597) 2025-12-26 14:33:46 -06:00
Aiden Cline e8dba6264f tweak: add .catch for extractZip calls 2025-12-26 14:33:46 -06:00
GitHub Action bb47a1e0f8 chore: format code 2025-12-26 14:33:45 -06:00
Luke Parker 7e9f77117f fix(win32): Missing LSP can now unzip on windows (#5594) 2025-12-26 14:33:45 -06:00
opencode ca7ac43cff release: v1.0.160 2025-12-26 14:33:45 -06:00
Dax Raad 471ec0350c tui: fix dialog replacement to prevent nested dialogs from showing simultaneously 2025-12-26 14:33:44 -06:00
Dax Raad 90077b6dd0 tui: fix model selection dialog to properly replace current dialog instead of creating nested dialogs 2025-12-26 14:33:44 -06:00
Luke Parker 5fc0a55f54 fix(win32): correct ElixirLS extension typo (#5590) 2025-12-26 14:33:44 -06:00
GitHub Action 3ad2a40e89 chore: format code 2025-12-26 14:33:43 -06:00
Luke Parker 59da2e16b2 fix(win32): use path.delimiter for PATH separator in LSP server lookups (#5589) 2025-12-26 14:33:43 -06:00
opencode a9a5cf0a76 release: v1.0.159 2025-12-26 14:33:43 -06:00
Dax Raad e9f899cb9a fix dialog root complexity 2025-12-26 14:33:42 -06:00
Luke Parker 8b976833a3 fix(windows): opencode github install (#5587) 2025-12-26 14:33:42 -06:00
Ariane Emory fc5493e8bd fix: restore ability to bind keys for model_cycle_favorite model_cycle_favorite_reverse (resolves #5198) (#5202) 2025-12-26 14:33:42 -06:00
opencode-agent[bot] ecef4d2a92 docs: enabled_providers docs section (#5586)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:42 -06:00
Adam 2771367dc4 fix: multiline user input 2025-12-26 14:33:42 -06:00
Adam e0ca160360 fix: keybinds for agent and model selection 2025-12-26 14:33:42 -06:00
Adam 94d6c1bc92 fix: landing page CLS hero jump down 2025-12-26 14:33:41 -06:00
Adam f58c96fea5 fix: session nav on homepage 2025-12-26 14:33:41 -06:00
Adam f4336b504a fix: default steps expanded unless done 2025-12-26 14:33:41 -06:00
Adam acd2d77e47 fix: landing page CLS hero jump down 2025-12-26 14:33:41 -06:00
Aiden Cline c0aaba9f65 tweak: add model flag support for agent create command 2025-12-26 14:33:41 -06:00
Jay V 58a37e72e7 docs: make project names clickable links in ecosystem documentation 2025-12-26 14:33:41 -06:00
Adam 589cdc40e3 fix: missing event type (global) 2025-12-26 14:33:41 -06:00
Ariane Emory b77826f3c5 fix: record shell mode in history (resolves #5454) (#5551) 2025-12-26 14:33:40 -06:00
GitHub Action 46b04f1163 chore: format code 2025-12-26 14:33:40 -06:00
Aiden Cline 31ddd0798a add ability to set topK 2025-12-26 14:33:40 -06:00
opencode 813c330c47 release: v1.0.158 2025-12-26 14:33:40 -06:00
Adam 6205f0bde0 fix: modal search 2025-12-26 14:33:40 -06:00
justfortheloveof c28709827f tweak: prioritize fuzzysort results that start with user input (#5571) 2025-12-26 14:33:40 -06:00
Adam ce7f4c0e6e fix: allow for non-vcs projects in desktop 2025-12-26 14:33:40 -06:00
GitHub Action 362502cf6f chore: format code 2025-12-26 14:33:39 -06:00
Dax Raad 5d29af3b45 core: fix server response handling to prevent connection timeouts 2025-12-26 14:33:39 -06:00
opencode a13af69de5 release: v1.0.157 2025-12-26 14:33:39 -06:00
Dax Raad 653c181312 ci: fix tauri updater version mismatch by checking out the release tag 2025-12-26 14:33:39 -06:00
Dax Raad 8dca9051f1 ci: add app bundle target to fix macOS updater by generating .app.tar.gz files 2025-12-26 14:33:39 -06:00
GitHub Action 6f56b69264 chore: format code 2025-12-26 14:33:39 -06:00
Dax Raad 64b8f0a1a9 core: fix message caching for Anthropic models to improve response consistency 2025-12-26 14:33:38 -06:00
opencode 6fd6eecbf5 release: v1.0.156 2025-12-26 14:33:38 -06:00
Adam 74d4a6bcb1 fix: connect provider on homepage 2025-12-26 14:33:38 -06:00
Adam 47eaef468f fix: terminal in desktop 2025-12-26 14:33:38 -06:00
Adam 1d68c34be6 fix: image attachments in desktop 2025-12-26 14:33:37 -06:00
Github Action 9fb67310bb Update Nix flake.lock and hashes 2025-12-26 14:33:37 -06:00
Dax Raad f23fc83d3c docs: update header navigation to include desktop download 2025-12-26 14:33:37 -06:00
Adam 8138c36343 fix: share page 2025-12-26 14:33:36 -06:00
Dax Raad 9ffaaf9bbf docs: restore desktop beta banner to homepage 2025-12-26 14:33:36 -06:00
GitHub Action 52bced424f chore: format code 2025-12-26 14:33:36 -06:00
Nalin Singh ba1cd2777a feat: add F# language server support (#5549) 2025-12-26 14:33:35 -06:00
opencode f0da095552 release: v1.0.155 2025-12-26 14:33:35 -06:00
Dax Raad ccaf562c9e 20min 2025-12-26 14:33:35 -06:00
Adam 973162c444 chore: update stats 2025-12-26 14:33:34 -06:00
GitHub Action be261c6cdb chore: format code 2025-12-26 14:33:34 -06:00
Adam 27da9c1852 chore: cleanup 2025-12-26 14:33:34 -06:00
Adam 779311d064 wip(desktop): progress 2025-12-26 14:33:34 -06:00
Adam 25d6d17296 wip(desktop): progress 2025-12-26 14:33:33 -06:00
Adam ab48ff0471 wip(desktop): progress 2025-12-26 14:33:33 -06:00
Adam 1078ae03a0 wip(desktop): progress 2025-12-26 14:33:33 -06:00
Adam a4aa758043 chore: cleanup 2025-12-26 14:33:33 -06:00
Adam 9536746fb7 chore: cleanup 2025-12-26 14:33:33 -06:00
Adam b088180464 feat(desktop): custom commands 2025-12-26 14:33:33 -06:00
Adam 211524cb0b wip(desktop): progress 2025-12-26 14:33:33 -06:00
Adam c7b8b7c49e wip(desktop): progress 2025-12-26 14:33:33 -06:00
Adam 105cfa8d4e wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam eab1b44f66 wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 668c932631 wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 5fb20a52a7 wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 73b74581f8 wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 9ac9396aec wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 3df9f708b3 Revert "wip(desktop): session turn state consolidation"
This reverts commit 453f862616dc4d3ac90680581cde279e118b0da1.
2025-12-26 14:33:32 -06:00
Adam dc65a46fb8 wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 71cb4c3ddc wip(desktop): progress 2025-12-26 14:33:32 -06:00
Adam 0a556bb646 wip(desktop): progress 2025-12-26 14:33:31 -06:00
Adam c8e1950522 wip(desktop): session turn state consolidation 2025-12-26 14:33:31 -06:00
Adam 32bbc07e36 wip(desktop): progress 2025-12-26 14:33:31 -06:00
Adam c76a00fc3f wip(desktop): progress 2025-12-26 14:33:31 -06:00
Adam ce4d0d76ea wip(desktop): progress 2025-12-26 14:33:31 -06:00
Adam df844dc408 wip(desktop): progress 2025-12-26 14:33:31 -06:00
Adam 10276e8b3f wip(desktop): progress 2025-12-26 14:33:30 -06:00
opencode ea1c063d98 release: v1.0.154 2025-12-26 14:33:30 -06:00
Dax Raad 83249e69f3 ci: update publish workflow concurrency to include version inputs and upgrade ubuntu runner to 24.04 2025-12-26 14:33:30 -06:00
GitHub Action b612b939c9 chore: format code 2025-12-26 14:33:30 -06:00
Dax Raad 0ffc67792c core: reorganize agent configuration to separate primary agents (build, plan) from subagents 2025-12-26 14:33:30 -06:00
Dax Raad 37d0ef3362 ci: fix AppImage build hanging by using portable appimage format
- Add appimage target back to tauri.conf.json
- Force reinstall tauri-cli from feat/truly-portable-appimage branch
- Add 10 minute timeout to prevent indefinite hangs
- Add logging to verify correct tauri-cli version is installed

This fixes the issue where AppImage builds would hang at 'Running input plugin: gtk' by using the new portable appimage format that bypasses linuxdeploy entirely.
2025-12-26 14:33:30 -06:00
René 62e5e42707 docs: Fix Wakatime repository link in ecosystem.mdx (#5552)
Co-authored-by: Github Action <action@github.com>
2025-12-26 14:33:29 -06:00
David Hill f8af042811 fix: font size updates 2025-12-26 14:33:29 -06:00
David Hill 45d85a71c6 fix: add tooltip to close review tab 2025-12-26 14:33:29 -06:00
David Hill cd76512659 fix: prompt input using border shadow 2025-12-26 14:33:29 -06:00
David Hill 9a4e7319df fix: replace agents dropdown with shadow border 2025-12-26 14:33:29 -06:00
David Hill c6ed81fa8d fix: fix width and padding on agent select
- conditionally hide the role=presentation element because it was adding extra gap before the first agent
2025-12-26 14:33:28 -06:00
David Hill ba4cd97d16 fix: hide prompt input send tooltip when the send button is disabled 2025-12-26 14:33:28 -06:00
David Hill bc2d2297bb fix: avatar and icon size in sidebar 2025-12-26 14:33:28 -06:00
David Hill 731233c96d fix: remove blue border from prompt input 2025-12-26 14:33:28 -06:00
GitHub Action bcb5840537 ignore: update download stats 2025-12-15 2025-12-26 14:33:28 -06:00
René 0bf1c40eff Provider fix, anthropic Errorhandling if empty image file is read (#5521) 2025-12-26 14:33:28 -06:00
Nalin Singh 48eeb4e80d fix: input lip visibility for transparent themes (#5544) 2025-12-26 14:33:28 -06:00
Spoon 64552fe7e1 fix(edit): add per-file lock to prevent read-before-write race (#4388) 2025-12-26 14:33:28 -06:00
Aiden Cline c7b5831a0f ignore: fix debug var in last commit 2025-12-26 14:33:27 -06:00
opencode-agent[bot] 70a8efc843 Add dismiss button to Getting Started box (#5543)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:33:27 -06:00
DS b98bd90d50 feat: restore experimental.chat.messages.transform and add experimental.chat.system.transform hooks (#5542) 2025-12-26 14:33:27 -06:00
Aiden Cline c985ef172d ci: switch model 2025-12-26 14:33:27 -06:00
Brendan Allan 83a320a2e7 tauri: use correct sidecar name 2025-12-26 14:33:27 -06:00
Brendan Allan cc82a2711b tauri: rename sidecar to opencode-cli 2025-12-26 14:33:26 -06:00
Aiden Cline 7b83294590 ci: smart oc 2025-12-26 14:33:26 -06:00
Ravi Kumar 4e4a24b769 fix(session): fix unshare command not clearing share state (#5523) 2025-12-26 14:33:26 -06:00
GitHub Action 9fbf6d4bd6 chore: format code 2025-12-26 14:33:25 -06:00
Adam f27b737038 fix: test 2025-12-26 14:33:25 -06:00
Adam 5246ecfc88 fix: update sdk 2025-12-26 14:33:25 -06:00
Adam d1c3bb2616 wip(desktop): progress 2025-12-26 14:33:25 -06:00
Adam e9f8b7d91f wip(desktop): progress 2025-12-26 14:33:24 -06:00
Adam bdd5e63529 wip(desktop): progress 2025-12-26 14:33:24 -06:00
Adam 7584bd0d71 wip(desktop): progress 2025-12-26 14:33:24 -06:00
Adam 60fadb253a wip(desktop): progress 2025-12-26 14:33:23 -06:00
Adam de635e3c51 wip(desktop): progress 2025-12-26 14:33:23 -06:00
Adam 6c0638c73b fix(desktop): auto scroll 2025-12-26 14:33:22 -06:00
Adam 238ca62ba7 wip(desktop): progress 2025-12-26 14:33:22 -06:00
Adam d8aba3a35f fix(desktop): layout fixes 2025-12-26 14:33:22 -06:00
Mark Jaquith 8a518c8b2a feat(cli): auto-submit prompt when using --prompt flag (#4510) 2025-12-26 14:33:21 -06:00
Dax Raad bc7f81c1a3 fix share link 2025-12-26 14:33:21 -06:00
Dax Raad c8d735de6c ci: fix test failures in CI by pre-populating models cache
Tests were failing in CI because the models.json cache file doesn't exist
and the data() macro fallback only works at build time, not runtime.
The preload now pre-fetches models.json and disables the background
refresh to prevent race conditions during test execution.
2025-12-26 14:33:21 -06:00
Dax Raad f4e1fa28b8 fix desktop updater 2025-12-26 14:33:21 -06:00
Dax 262d836dd7 LLM cleanup (#5462)
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:21 -06:00
Ravi Kumar 01c2ba2e4a fix(tui): --continue selects wrong session (#5513) 2025-12-26 14:33:21 -06:00
GitHub Action 3b59308282 chore: format code 2025-12-26 14:33:21 -06:00
Martijn Baay b6714c645e feat: add experimental.continue_loop_on_deny config option (#4729)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2025-12-26 14:33:20 -06:00
Dax Raad 0b60e82266 ignore: simplify download page to use GitHub latest redirect URLs 2025-12-26 14:33:20 -06:00
GitHub Action 321354529b chore: format code 2025-12-26 14:33:20 -06:00
Dax Raad 3efb17e91e disable app image 2025-12-26 14:33:20 -06:00
opencode df1bc0e9bb release: v1.0.153 2025-12-26 14:33:19 -06:00
Aiden Cline ac843d39ea tweak: fallback to provider default for temperature 2025-12-26 14:33:19 -06:00
GitHub Action dbfdb54315 chore: format code 2025-12-26 14:33:19 -06:00
Nalin Singh 2816e84f0a fix: ensure input borders are drawn in transparent themes (#5524) 2025-12-26 14:33:19 -06:00
GitHub Action 5b7581ebbe chore: format code 2025-12-26 14:33:19 -06:00
Sellers Crisp 62bcdf3144 feat: add server_error, rate_limit, and no_kv_space retry logic to accommodate Foundry API issues (#5527)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:19 -06:00
shekohex 9746d89cc1 docs: add opencode-pty and opencode-google-antigravity-auth plugins to the echosystem (#5530) 2025-12-26 14:33:18 -06:00
Lawrence Sarpong 74fb054040 Add Gleam LSP and formatter (#5514) 2025-12-26 14:33:18 -06:00
Aymvn 527836a90b docs: Fix Wakatime link in ecosystem documentation (#5528) 2025-12-26 14:33:18 -06:00
GitHub Action 481147c946 ignore: update download stats 2025-12-14 2025-12-26 14:33:18 -06:00
Aiden Cline 3babb33f8d rm unnecessary code 2025-12-26 14:33:18 -06:00
Aiden Cline 1192e16278 add topK function to transform, add temp defaults for glm and minimax 2025-12-26 14:33:18 -06:00
YeonGyu-Kim e59d479947 fix(ui): guard Node reference for SSR compatibility in isTriggerTitle (#5509) 2025-12-26 14:33:18 -06:00
Brendan Allan 2980eff805 tauri: change mainBinaryName to just OpenCode 2025-12-26 14:33:17 -06:00
Brendan Allan 72c0af5b6c tauri: bring back appimage 2025-12-26 14:33:17 -06:00
Sachnun deaf35707d fix(tui): open parent session instead of subagent on continue flag (#5503) 2025-12-26 14:33:17 -06:00
Zhou Rui 73b9705410 docs: Add opencode-websearch-cited to plugin list (#5501) 2025-12-26 14:33:17 -06:00
Adam 782ed12244 fix: sort models 2025-12-26 14:33:17 -06:00
Adam 696d14167c fix: use opencode icon 2025-12-26 14:33:17 -06:00
Adam e9eb799464 chore: cleanup 2025-12-26 14:33:17 -06:00
Adam 05846ed29d chore: cleanup 2025-12-26 14:33:17 -06:00
Tommy D. Rossi d80634642c fix: limit LSP diagnostics to prevent context window waste (#5480)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:17 -06:00
Aiden Cline d7f32a57ff docs: networking 2025-12-26 14:33:17 -06:00
Aiden Cline 51267f256e chore: reduce duplication of field in transform 2025-12-26 14:33:17 -06:00
Adam 860b62a6e8 chore: cleanup 2025-12-26 14:33:17 -06:00
Adam c952505526 fix(desktop): archive button 2025-12-26 14:33:17 -06:00
Adam 1f8ac107e9 fix(desktop): auto scroll 2025-12-26 14:33:17 -06:00
Adam eceac1e727 feat(desktop): show richer status when thinking 2025-12-26 14:33:16 -06:00
Adam 4202298422 fix: css scroll jitter 2025-12-26 14:33:16 -06:00
Adam 43e50b713e fix: don't rotate placeholders in session 2025-12-26 14:33:16 -06:00
Adam f7544b729c fix: don't open shell by default 2025-12-26 14:33:16 -06:00
Github Action b84530821e Update Nix flake.lock and hashes 2025-12-26 14:33:16 -06:00
Adam c808815ec8 chore: cleanup 2025-12-26 14:33:16 -06:00
Adam 280d309a52 fix(desktop): terminal light mode 2025-12-26 14:33:15 -06:00
Adam a8d7b8e946 feat(desktop): message history 2025-12-26 14:33:15 -06:00
Adam 7a494abea9 fix: session turn scroll 2025-12-26 14:33:15 -06:00
Felipe Oduardo Sierra 48ff68512a add ARM64 Docker image support (#5483) 2025-12-26 14:33:15 -06:00
Github Action deb6e37cc1 Update Nix flake.lock and hashes 2025-12-26 14:33:15 -06:00
Aiden Cline 5257b22135 bump bun version & set flags this time 2025-12-26 14:33:15 -06:00
GitHub Action b376d5b622 chore: format code 2025-12-26 14:33:14 -06:00
Jan-Niklas W. 484416b503 docs: fix title for JetBrains ACP config file (#5479) 2025-12-26 14:33:14 -06:00
YeonGyu-Kim 8ed42c8bbb docs: add oh-my-opencode to plugins list (#5481) 2025-12-26 14:33:14 -06:00
GitHub Action 91a6f77ff0 chore: format code 2025-12-26 14:33:14 -06:00
rari404 84975a5d82 feat: add dockerfile language server (#5252)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:14 -06:00
GitHub Action 74319df2e3 ignore: update download stats 2025-12-13 2025-12-26 14:33:14 -06:00
rari404 a9c179db7a feat: add texlab language server and latexindent formatter (#5251) 2025-12-26 14:33:14 -06:00
GitHub Action e85d53f2a9 chore: format code 2025-12-26 14:33:14 -06:00
Jan-Niklas W. 06e93668ee docs: JetBrains IDEs to ACP config docs page (#5465) 2025-12-26 14:33:14 -06:00
Matt Silverlock a837cef3cb github: support GITHUB_TOKEN + skip OIDC (#5459) 2025-12-26 14:33:14 -06:00
Charles Cooper 5ee52d2f84 fix: add --session flag to attach command (#5460)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2025-12-26 14:33:14 -06:00
David Hill f1ceee1f2e fix: mute the project path in the sidebar that proceeds the final directory 2025-12-26 14:33:14 -06:00
Dax Raad 8e7269d985 fix 2025-12-26 14:33:14 -06:00
GitHub Action 829d1bc084 chore: format code 2025-12-26 14:33:14 -06:00
Aiden Cline 10e0cd619d docs: add env vars sections 2025-12-26 14:33:13 -06:00
opencode c8e64f208a release: v1.0.152 2025-12-26 14:33:13 -06:00
Adam 5ddc0fd901 Revert "fix: archive button"
This reverts commit bc3286de46.
2025-12-26 14:33:13 -06:00
Aiden Cline 9bd8c6d3d3 shell tweaks, better handling for windows (#5455)
Co-authored-by: GitHub Action <action@github.com>
2025-12-26 14:33:13 -06:00
Adam 11100c4ba2 fix: max height on bash tool 2025-12-26 14:33:13 -06:00
Adam 7bc072c9ad fix: archive button 2025-12-26 14:33:12 -06:00
Dax Raad e2530833e3 desktop: fix build on Linux and Windows by making macOS title bar styling conditional 2025-12-26 14:33:12 -06:00
Sebastian Herrlinger cd3f2412e8 only exit app when prompt is empty, otherwise fallthrough, fix #5457 2025-12-26 14:33:12 -06:00
GitHub Action e8a20cfe9e chore: format code 2025-12-26 14:33:12 -06:00
Dan Brown 071a87177a Shell: No -l in fallback, for max compatibility (#5452) 2025-12-26 14:33:12 -06:00
opencode c1fda41fad release: v1.0.151 2025-12-26 14:33:11 -06:00
Github Action fa275b0bf9 Update Nix flake.lock and hashes 2025-12-26 14:33:11 -06:00
Adam 1e3cd35cb8 fix: desktop layout 2025-12-26 14:33:11 -06:00
Adam 4425f13430 fix: desktop layout 2025-12-26 14:33:11 -06:00
Adam 5c2f616c73 fix: desktop layout 2025-12-26 14:33:11 -06:00
Adam 04d71a1820 fix: desktop layout 2025-12-26 14:33:10 -06:00
Adam 11062f83ae fix: desktop layout 2025-12-26 14:33:10 -06:00
Adam 0925ea6c7e fix: desktop layout 2025-12-26 14:33:10 -06:00
Adam fe1e0b52c7 fix: desktop layout 2025-12-26 14:33:09 -06:00
Adam ac8f4ab678 fix: desktop layout 2025-12-26 14:33:09 -06:00
Adam 72d1aadea1 fix: desktop layout 2025-12-26 14:33:09 -06:00
Adam 4ca8dcf79d fix: desktop layout 2025-12-26 14:33:09 -06:00
Adam db8ee3471b chore: cleanup 2025-12-26 14:33:08 -06:00
Adam c06b78859a chore: cleanup 2025-12-26 14:33:08 -06:00
Adam b319187151 wip: desktop timeline changes 2025-12-26 14:33:08 -06:00
Dax Raad 4746338e8e only gen summary if diffs 2025-12-26 14:33:08 -06:00
Aiden Cline 5221251b5c tweak: 5.1 -> 5. in transform 2025-12-26 14:33:08 -06:00
GitHub Action e6520dd6a9 chore: format code 2025-12-26 14:33:08 -06:00
David Hill 845a21da35 fix: mute the whole prompt area when leader key is active 2025-12-26 14:33:08 -06:00
Dax Raad b41333d30f reuse existing server query 2025-12-26 14:33:07 -06:00
Dax Raad f9c24ba266 sync 2025-12-26 14:33:07 -06:00
Dax Raad 0d6eba5392 sync 2025-12-26 14:33:07 -06:00
Aiden Cline 9abbd74a51 tweak: 5.1 -> 5. reasoning effort match 2025-12-26 14:33:07 -06:00
Frank 376639f6b8 Zen: add gpt5.2 2025-12-26 14:33:06 -06:00
Luke Parker f31151deaf fix: osascript for clipboard typo (#5430) 2025-12-26 14:33:06 -06:00
Adam 22487904ab fix: desktop layout 2025-12-26 14:33:06 -06:00
Jeremy Osih 8b1b411989 Change tooltip text from 'Open file' to 'New Terminal' (#5435) 2025-12-26 14:33:06 -06:00
Adam 70b6c4dfc9 fix: tweak missing colors 2025-12-26 14:33:06 -06:00
Adam 03be72bb2b chore: cleanup 2025-12-26 14:33:06 -06:00
xu0o0 c4c36ea54b acp: fix internal error on /compact (#5424) 2025-12-26 14:33:06 -06:00
Brendan Allan 3d751c0da1 tauri: add basic custom titlebar (#5438) 2025-12-26 14:33:05 -06:00
Adam 385ac04c1d feat(desktop): archive sessions 2025-12-26 14:33:05 -06:00
Adam 7cdd33199e fix(desktop): audio stuff 2025-12-26 14:33:05 -06:00
Adam 1a83ffdf01 fix(desktop): homedir aware path on home 2025-12-26 14:33:05 -06:00
GitHub Action 44084dac5f ignore: update download stats 2025-12-12 2025-12-26 14:33:05 -06:00
Github Action 28066ecfbd Update Nix flake.lock and hashes 2025-12-26 14:33:04 -06:00
Adam b2d028693d feat(desktop): basic alerting 2025-12-26 14:33:04 -06:00
GitHub Action dfe6c58b04 chore: format code 2025-12-26 14:33:04 -06:00
David Hill 644e360a63 fix: increase font-size-small to 13px 2025-12-26 14:33:04 -06:00
David Hill 8cee140196 fix: make syntax colors have more contrast 2025-12-26 14:33:04 -06:00
Brendan Allan c301703f89 tauri: create window with full screen size 2025-12-26 14:33:04 -06:00
Github Action 57b7f112d0 Update Nix flake.lock and hashes 2025-12-26 14:33:04 -06:00
GitHub Action e45d014b7b chore: format code 2025-12-26 14:33:03 -06:00
Brendan Allan 9dfacb8dd1 tauri: initialise store and window-state plugins 2025-12-26 14:33:03 -06:00
Viktor Forsman ddf44233e7 fix: debug lsp diagnostics cmd for certain lsps (#5420) 2025-12-26 14:33:03 -06:00
Frank 010d1298c0 Zen: sync 2025-12-26 14:33:03 -06:00
Rhys Sullivan 729eca8095 [feat]: show indicator for in progress chats in the sessions list (#5417) 2025-12-26 14:33:03 -06:00
Sachnun aed32dd840 fix(tui): restore input on timeline revert and show newest first (#5366) 2025-12-26 14:33:02 -06:00
GitHub Action 24c9d2b891 chore: format code 2025-12-26 14:33:02 -06:00
Sachnun 2062dd1086 fix(server): make time field optional in session update validator (#5372) 2025-12-26 14:33:02 -06:00
xu0o0 3f580ef53e acp: replay conversation history in session/load (#5385) 2025-12-26 14:33:02 -06:00
opencode-agent[bot] fb9ea51018 Removed cache mention from webfetch prompt. (#5412)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2025-12-26 14:33:02 -06:00
Zeno Jiricek 807c421248 docs: Add opencode-md-table-formatter and plugin template (#5405) 2025-12-26 14:33:02 -06:00
Koichi Nakayamada 8992072b95 fix(tui): ensure fatal error UI is readable in light mode (#5387) 2025-12-26 14:33:02 -06:00
David Hill 41319a2885 wip: desktop sidebar icon updates 2025-12-26 14:33:02 -06:00
David Hill c316ef35d9 fix: make the logo on the home screen non-selectable 2025-12-26 14:33:01 -06:00
GitHub Action 86f9c3acce chore: format code 2025-12-11 23:08:04 +00:00
Aiden Cline bacf705ee5 wip 2025-12-11 17:07:22 -06:00
441 changed files with 8275 additions and 3256 deletions
+69
View File
@@ -0,0 +1,69 @@
name: Docs Update
on:
schedule:
- cron: "0 */12 * * *"
workflow_dispatch:
jobs:
update-docs:
if: github.repository == 'sst/opencode'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
id-token: write
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch full history to access commits
- name: Setup Bun
uses: ./.github/actions/setup-bun
- name: Get recent commits
id: commits
run: |
COMMITS=$(git log --since="4 hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "")
if [ -z "$COMMITS" ]; then
echo "No commits in the last 4 hours"
echo "has_commits=false" >> $GITHUB_OUTPUT
else
echo "has_commits=true" >> $GITHUB_OUTPUT
{
echo "list<<EOF"
echo "$COMMITS"
echo "EOF"
} >> $GITHUB_OUTPUT
fi
- name: Run opencode
if: steps.commits.outputs.has_commits == 'true'
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/gpt-5.2
agent: docs
prompt: |
Review the following commits from the last 4 hours and identify any new features that may need documentation.
<recent_commits>
${{ steps.commits.outputs.list }}
</recent_commits>
Steps:
1. For each commit that looks like a new feature or significant change:
- Read the changed files to understand what was added
- Check if the feature is already documented in packages/web/src/content/docs/*
2. If you find undocumented features:
- Update the relevant documentation files in packages/web/src/content/docs/*
- Follow the existing documentation style and structure
- Make sure to document the feature clearly with examples where appropriate
3. If all new features are already documented, report that no updates are needed
4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too.
Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior.
Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all.
Try to keep documentation only for large features or changes that already have a good spot to be documented.
+13 -5
View File
@@ -79,6 +79,12 @@ jobs:
AUR_KEY: ${{ secrets.AUR_KEY }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
NPM_CONFIG_PROVENANCE: false
- uses: actions/upload-artifact@v4
with:
name: opencode-cli
path: packages/opencode/dist
outputs:
release: ${{ steps.publish.outputs.release }}
tag: ${{ steps.publish.outputs.tag }}
@@ -99,6 +105,8 @@ jobs:
target: x86_64-pc-windows-msvc
- host: blacksmith-4vcpu-ubuntu-2404
target: x86_64-unknown-linux-gnu
- host: blacksmith-4vcpu-ubuntu-2404-arm
target: aarch64-unknown-linux-gnu
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v3
@@ -143,13 +151,12 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
workspaces: packages/tauri/src-tauri
workspaces: packages/desktop/src-tauri
shared-key: ${{ matrix.settings.target }}
- name: Prepare
if: inputs.bump || inputs.version
run: |
cd packages/tauri
cd packages/desktop
bun ./scripts/prepare.ts
env:
OPENCODE_VERSION: ${{ needs.publish.outputs.version }}
@@ -159,6 +166,7 @@ jobs:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
RUST_TARGET: ${{ matrix.settings.target }}
GH_TOKEN: ${{ github.token }}
GITHUB_RUN_ID: ${{ github.run_id }}
# Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released
- name: Install tauri-cli from portable appimage branch
@@ -183,10 +191,10 @@ jobs:
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8
with:
projectPath: packages/tauri
projectPath: packages/desktop
uploadWorkflowArtifacts: true
tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }}
args: --target ${{ matrix.settings.target }} --config src-tauri/tauri.prod.conf.json
args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose
updaterJsonPreferNsis: true
releaseId: ${{ needs.publish.outputs.release }}
tagName: ${{ needs.publish.outputs.tag }}
+3
View File
@@ -5,8 +5,11 @@ on:
- cron: "0 12 * * *" # Run daily at 12:00 UTC
workflow_dispatch: # Allow manual trigger
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
stats:
if: github.repository == 'sst/opencode'
runs-on: blacksmith-4vcpu-ubuntu-2404
permissions:
contents: write
+3 -3
View File
@@ -2,8 +2,8 @@ name: "sync-zed-extension"
on:
workflow_dispatch:
# release:
# types: [published]
release:
types: [published]
jobs:
zed:
@@ -31,4 +31,4 @@ jobs:
run: |
./script/sync-zed.ts ${{ steps.get_tag.outputs.tag }}
env:
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
ZED_EXTENSIONS_PAT: ${{ secrets.ZED_EXTENSIONS_PAT }}
+2
View File
@@ -6,6 +6,8 @@ You are an expert technical documentation writer
You are not verbose
Use a relaxed and friendly tone
The title of the page should be a word or a 2-3 word phrase
The description should be one short line, should not start with "The", should
+6
View File
@@ -0,0 +1,6 @@
---
name: test-skill
description: use this when asked to test skill
---
woah this is a test skill
+1 -28
View File
@@ -4,31 +4,4 @@
## Tool Calling
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. Here is an example illustrating how to execute 3 parallel file reads in this chat environment:
json
{
"recipient_name": "multi_tool_use.parallel",
"parameters": {
"tool_uses": [
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.tsx"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.ts"
}
},
{
"recipient_name": "functions.read",
"parameters": {
"filePath": "path/to/file.md"
}
}
]
}
}
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE.
+4 -4
View File
@@ -53,12 +53,12 @@ your debugger via that URL. Other methods can result in breakpoints being mapped
Caveats:
- `*.tsx` files won't have their breakpoints correctly mapped. This seems due to Bun currently not supporting source maps on code transformed
via `BunPlugin`s (currently necessary due to our dependency on `@opentui/solid`). Currently, the best you can do in terms of debugging `*.tsx`
files is writing a `debugger;` statement. Debugging facilities like stepping won't work, but at least you will be informed if a specific code
is triggered.
- If you want to run the OpenCode TUI and have breakpoints triggered in the server code, you might need to run `bun dev spawn` instead of
the usual `bun dev`. This is because `bun dev` runs the server in a worker thread and breakpoints might not work there.
- If `spawn` does not work for you, you can debug the server separately:
- Debug server: `bun run --inspect=ws://localhost:6499/ ./src/index.ts serve --port 4096`,
then attach TUI with `opencode attach http://localhost:4096`
- Debug TUI: `bun run --inspect=ws://localhost:6499/ --conditions=browser ./src/index.ts`
Other tips and tricks:
+2 -2
View File
@@ -79,7 +79,7 @@ you can switch between these using the `Tab` key.
- Asks permission before running bash commands
- Ideal for exploring unfamiliar codebases or planning changes
Also, included is a **general** subagent for complex searches and multi-step tasks.
Also, included is a **general** subagent for complex searches and multistep tasks.
This is used internally and can be invoked using `@general` in messages.
Learn more about [agents](https://opencode.ai/docs/agents).
@@ -98,7 +98,7 @@ If you are working on a project that's related to OpenCode and is using "opencod
### FAQ
#### How is this different than Claude Code?
#### How is this different from Claude Code?
It's very similar to Claude Code in terms of capability. Here are the key differences:
+6
View File
@@ -176,3 +176,9 @@
| 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) |
| 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) |
| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) |
| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) |
| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) |
| 2025-12-23 | 1,286,548 (+24,026) | 1,186,439 (+17,318) | 2,472,987 (+41,344) |
| 2025-12-24 | 1,309,323 (+22,775) | 1,203,767 (+17,328) | 2,513,090 (+40,103) |
| 2025-12-25 | 1,333,032 (+23,709) | 1,217,283 (+13,516) | 2,550,315 (+37,225) |
| 2025-12-26 | 1,352,411 (+19,379) | 1,227,615 (+10,332) | 2,580,026 (+29,711) |
+369 -330
View File
File diff suppressed because it is too large Load Diff
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1766125104,
"narHash": "sha256-l/YGrEpLromL4viUo5GmFH3K5M1j0Mb9O+LiaeCPWEM=",
"lastModified": 1766870016,
"narHash": "sha256-fHmxAesa6XNqnIkcS6+nIHuEmgd/iZSP/VXxweiEuQw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7d853e518814cca2a657b72eeba67ae20ebf7059",
"rev": "5c2bc52fb9f8c264ed6c93bd20afa2ff5e763dce",
"type": "github"
},
"original": {
+4 -1
View File
@@ -574,10 +574,13 @@ async function subscribeSessionEvents() {
}
async function summarize(response: string) {
const payload = useContext().payload as IssueCommentEvent
try {
return await chat(`Summarize the following in less than 40 characters:\n\n${response}`)
} catch (e) {
if (isScheduleEvent()) {
return "Scheduled task changes"
}
const payload = useContext().payload as IssueCommentEvent
return `Fix issue: ${payload.issue.title}`
}
}
+9
View File
@@ -44,3 +44,12 @@ new sst.cloudflare.x.Astro("Web", {
VITE_API_URL: api.url.apply((url) => url!),
},
})
new sst.cloudflare.StaticSite("WebApp", {
domain: "app." + domain,
path: "packages/app",
build: {
command: "bun turbo build",
output: "./dist",
},
})
+3
View File
@@ -103,6 +103,7 @@ const ZEN_MODELS = [
new sst.Secret("ZEN_MODELS3"),
new sst.Secret("ZEN_MODELS4"),
new sst.Secret("ZEN_MODELS5"),
new sst.Secret("ZEN_MODELS6"),
]
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
@@ -118,6 +119,7 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
////////////////
const bucket = new sst.cloudflare.Bucket("ZenData")
const bucketNew = new sst.cloudflare.Bucket("ZenDataNew")
const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
@@ -136,6 +138,7 @@ new sst.cloudflare.x.SolidStart("Console", {
path: "packages/console/app",
link: [
bucket,
bucketNew,
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
-10
View File
@@ -1,10 +0,0 @@
import { domain } from "./stage"
new sst.cloudflare.StaticSite("Desktop", {
domain: "desktop." + domain,
path: "packages/desktop",
build: {
command: "bun turbo build",
output: "./dist",
},
})
+89 -35
View File
@@ -7,7 +7,51 @@ RED='\033[0;31m'
ORANGE='\033[38;5;214m'
NC='\033[0m' # No Color
usage() {
cat <<EOF
OpenCode Installer
Usage: install.sh [options]
Options:
-h, --help Display this help message
-v, --version <version> Install a specific version (e.g., 1.0.180)
--no-modify-path Don't modify shell config files (.zshrc, .bashrc, etc.)
Examples:
curl -fsSL https://opencode.ai/install | bash
curl -fsSL https://opencode.ai/install | bash -s -- --version 1.0.180
EOF
}
requested_version=${VERSION:-}
no_modify_path=false
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--version)
if [[ -n "${2:-}" ]]; then
requested_version="$2"
shift 2
else
echo -e "${RED}Error: --version requires a version argument${NC}"
exit 1
fi
;;
--no-modify-path)
no_modify_path=true
shift
;;
*)
echo -e "${ORANGE}Warning: Unknown option '$1'${NC}" >&2
shift
;;
esac
done
raw_os=$(uname -s)
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
@@ -111,8 +155,18 @@ if [ -z "$requested_version" ]; then
exit 1
fi
else
# Strip leading 'v' if present
requested_version="${requested_version#v}"
url="https://github.com/sst/opencode/releases/download/v${requested_version}/$filename"
specific_version=$requested_version
# Verify the release exists before downloading
http_status=$(curl -sI -o /dev/null -w "%{http_code}" "https://github.com/sst/opencode/releases/tag/v${requested_version}")
if [ "$http_status" = "404" ]; then
echo -e "${RED}Error: Release v${requested_version} not found${NC}"
echo -e "${MUTED}Available releases: https://github.com/sst/opencode/releases${NC}"
exit 1
fi
fi
print_message() {
@@ -304,42 +358,42 @@ case $current_shell in
;;
esac
config_file=""
for file in $config_files; do
if [[ -f $file ]]; then
config_file=$file
break
if [[ "$no_modify_path" != "true" ]]; then
config_file=""
for file in $config_files; do
if [[ -f $file ]]; then
config_file=$file
break
fi
done
if [[ -z $config_file ]]; then
print_message warning "No config file found for $current_shell. You may need to manually add to PATH:"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
elif [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
case $current_shell in
fish)
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
;;
zsh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
bash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
ash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
sh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
*)
export PATH=$INSTALL_DIR:$PATH
print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
;;
esac
fi
done
if [[ -z $config_file ]]; then
print_message error "No config file found for $current_shell. Checked files: ${config_files[@]}"
exit 1
fi
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
case $current_shell in
fish)
add_to_path "$config_file" "fish_add_path $INSTALL_DIR"
;;
zsh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
bash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
ash)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
sh)
add_to_path "$config_file" "export PATH=$INSTALL_DIR:\$PATH"
;;
*)
export PATH=$INSTALL_DIR:$PATH
print_message warning "Manually add the directory to $config_file (or similar):"
print_message info " export PATH=$INSTALL_DIR:\$PATH"
;;
esac
fi
if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
+1 -1
View File
@@ -1,3 +1,3 @@
{
"nodeModules": "sha256-cpXmqJQJeFj3eED/aOb4YLUdkZFV//7u4f0STBxzUhk="
"nodeModules": "sha256-d10pu1tpwBbZ4YZqG0WcaK6W1+K2+Q0wdPjuI1rVIpY="
}
+6 -2
View File
@@ -31,7 +31,7 @@
"@tsconfig/bun": "1.0.9",
"@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.0.0-beta.3",
"@pierre/diffs": "1.0.2",
"@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11",
"diff": "8.0.2",
@@ -40,10 +40,13 @@
"hono-openapi": "1.1.2",
"fuzzysort": "3.1.0",
"luxon": "3.6.1",
"marked": "17.0.1",
"marked-shiki": "1.2.1",
"typescript": "5.8.2",
"@typescript/native-preview": "7.0.0-dev.20251207.1",
"zod": "4.1.8",
"remeda": "2.26.0",
"shiki": "3.20.0",
"solid-list": "0.3.0",
"tailwindcss": "4.1.11",
"virtua": "0.42.3",
@@ -56,6 +59,7 @@
}
},
"devDependencies": {
"@actions/artifact": "5.0.1",
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
@@ -64,6 +68,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "3.933.0",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*",
"typescript": "catalog:"
@@ -80,7 +85,6 @@
"trustedDependencies": [
"esbuild",
"protobufjs",
"sharp",
"tree-sitter",
"tree-sitter-bash",
"web-tree-sitter"
+1
View File
@@ -0,0 +1 @@
src/assets/theme.css
+34
View File
@@ -0,0 +1,34 @@
## Usage
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
```bash
$ npm install # or pnpm install or yarn install
```
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
## Available Scripts
In the project directory, you can run:
### `npm run dev` or `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
### `npm run build`
Builds the app for production to the `dist` folder.<br>
It correctly bundles Solid in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
## Deployment
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
@@ -23,6 +23,6 @@
</script>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root" class="flex flex-col h-screen"></div>
<script src="/src/index.tsx" type="module"></script>
<script src="/src/entry.tsx" type="module"></script>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@opencode-ai/app",
"version": "1.0.203",
"description": "",
"type": "module",
"exports": {
".": "./src/index.ts",
"./vite": "./vite.js"
},
"scripts": {
"typecheck": "tsgo -b",
"start": "vite",
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"license": "MIT",
"devDependencies": {
"@happy-dom/global-registrator": "20.0.11",
"@tailwindcss/vite": "catalog:",
"@tsconfig/bun": "1.0.9",
"@types/bun": "catalog:",
"@types/luxon": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
"typescript": "catalog:",
"vite": "catalog:",
"vite-plugin-icons-spritesheet": "3.0.1",
"vite-plugin-solid": "catalog:"
},
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
"@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2",
"@solid-primitives/event-bus": "1.1.2",
"@solid-primitives/media": "2.3.3",
"@solid-primitives/resize-observer": "2.1.3",
"@solid-primitives/scroll": "2.1.3",
"@solid-primitives/storage": "catalog:",
"@solid-primitives/websocket": "1.3.1",
"@solidjs/meta": "catalog:",
"@solidjs/router": "catalog:",
"@thisbeyond/solid-dnd": "0.7.5",
"diff": "catalog:",
"fuzzysort": "catalog:",
"ghostty-web": "0.3.0",
"luxon": "catalog:",
"marked": "catalog:",
"marked-shiki": "catalog:",
"remeda": "catalog:",
"shiki": "catalog:",
"solid-js": "catalog:",
"solid-list": "catalog:",
"tailwindcss": "catalog:",
"virtua": "catalog:",
"zod": "catalog:"
}
}
+17
View File
@@ -0,0 +1,17 @@
/assets/*.js
Content-Type: application/javascript
/assets/*.mjs
Content-Type: application/javascript
/assets/*.css
Content-Type: text/css
/*.js
Content-Type: application/javascript
/*.mjs
Content-Type: application/javascript
/*.css
Content-Type: text/css
@@ -21,6 +21,7 @@ import Home from "@/pages/home"
import DirectoryLayout from "@/pages/directory-layout"
import Session from "@/pages/session"
import { ErrorPage } from "./pages/error"
import { iife } from "@opencode-ai/util/iife"
declare global {
interface Window {
@@ -28,14 +29,17 @@ declare global {
}
}
const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1"
const port = window.__OPENCODE__?.port ?? import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"
const url = iife(() => {
const param = new URLSearchParams(document.location.search).get("url")
if (param) return param
const url =
new URLSearchParams(document.location.search).get("url") ||
(location.hostname.includes("opencode.ai") || location.hostname.includes("localhost")
? `http://${host}:${port}`
: "/")
if (location.hostname.includes("opencode.ai")) return "http://localhost:4096"
if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}`
if (import.meta.env.DEV)
return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}`
return window.location.origin
})
export function App() {
return (
@@ -1,24 +1,24 @@
import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import type { IconName } from "@opencode-ai/ui/icons/provider"
import { List, type ListRef } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Spinner } from "@opencode-ai/ui/spinner"
import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
import { iife } from "@opencode-ai/util/iife"
import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useGlobalSync } from "@/context/global-sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { usePlatform } from "@/context/platform"
import { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List, ListRef } from "@opencode-ai/ui/list"
import { Button } from "@opencode-ai/ui/button"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { TextField } from "@opencode-ai/ui/text-field"
import { Spinner } from "@opencode-ai/ui/spinner"
import { Icon } from "@opencode-ai/ui/icon"
import { showToast } from "@opencode-ai/ui/toast"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { IconName } from "@opencode-ai/ui/icons/provider"
import { iife } from "@opencode-ai/util/iife"
import { Link } from "@/components/link"
import { DialogSelectProvider } from "./dialog-select-provider"
import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync"
import { usePlatform } from "@/context/platform"
import { DialogSelectModel } from "./dialog-select-model"
import { DialogSelectProvider } from "./dialog-select-provider"
export function DialogConnectProvider(props: { provider: string }) {
const dialog = useDialog()
@@ -154,7 +154,9 @@ export function DialogConnectProvider(props: { provider: string }) {
<div class="text-14-regular text-text-base">Select login method for {provider().name}.</div>
<div class="">
<List
ref={(ref) => (listRef = ref)}
ref={(ref) => {
listRef = ref
}}
items={methods}
key={(m) => m?.label}
onSelect={async (method, index) => {
@@ -163,7 +165,7 @@ export function DialogConnectProvider(props: { provider: string }) {
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-4">
<div class="w-full flex items-center gap-x-2">
<div class="w-4 h-2 rounded-[1px] bg-input-base shadow-xs-border-base flex items-center justify-center">
<div class="w-2.5 h-0.5 bg-icon-strong-base hidden" data-slot="list-item-extra-icon" />
</div>
@@ -175,7 +177,7 @@ export function DialogConnectProvider(props: { provider: string }) {
</Match>
<Match when={store.state === "pending"}>
<div class="text-14-regular text-text-base">
<div class="flex items-center gap-x-4">
<div class="flex items-center gap-x-2">
<Spinner />
<span>Authorization in progress...</span>
</div>
@@ -183,7 +185,7 @@ export function DialogConnectProvider(props: { provider: string }) {
</Match>
<Match when={store.state === "error"}>
<div class="text-14-regular text-text-base">
<div class="flex items-center gap-x-4">
<div class="flex items-center gap-x-2">
<Icon name="circle-ban-sign" class="text-icon-critical-base" />
<span>Authorization failed: {store.error}</span>
</div>
@@ -1,16 +1,15 @@
import { Component } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders } from "@/hooks/use-providers"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch"
import type { Component } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders } from "@/hooks/use-providers"
export const DialogManageModels: Component = () => {
const local = useLocal()
return (
<Dialog title="Manage models" description="Customize which models appear in the model selector.">
<List
class="px-2.5"
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x?.provider?.id}:${x?.id}`}
@@ -27,16 +26,24 @@ export const DialogManageModels: Component = () => {
}}
onSelect={(x) => {
if (!x) return
const visible = local.model.visible({ modelID: x.id, providerID: x.provider.id })
const visible = local.model.visible({
modelID: x.id,
providerID: x.provider.id,
})
local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, !visible)
}}
>
{(i) => (
<div class="w-full flex items-center justify-between gap-x-2.5">
<div class="w-full flex items-center justify-between gap-x-3">
<span>{i.name}</span>
<div onClick={(e) => e.stopPropagation()}>
<Switch
checked={!!local.model.visible({ modelID: i.id, providerID: i.provider.id })}
checked={
!!local.model.visible({
modelID: i.id,
providerID: i.provider.id,
})
}
onChange={(checked) => {
local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked)
}}
@@ -1,12 +1,12 @@
import { useLocal } from "@/context/local"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { useLayout } from "@/context/layout"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/util/path"
import { useParams } from "@solidjs/router"
import { createMemo } from "solid-js"
import { useLayout } from "@/context/layout"
import { useLocal } from "@/context/local"
export function DialogSelectFile() {
const layout = useLayout()
@@ -18,7 +18,6 @@ export function DialogSelectFile() {
return (
<Dialog title="Select file">
<List
class="px-2.5"
search={{ placeholder: "Search files", autofocus: true }}
emptyMessage="No files found"
items={local.file.searchFiles}
@@ -32,7 +31,7 @@ export function DialogSelectFile() {
>
{(i) => (
<div class="w-full flex items-center justify-between rounded-md">
<div class="flex items-center gap-x-2 grow min-w-0">
<div class="flex items-center gap-x-3 grow min-w-0">
<FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" />
<div class="flex items-center text-14-regular">
<span class="text-text-weak whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0">
@@ -0,0 +1,91 @@
import { Component, createMemo, createSignal, Show } from "solid-js"
import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch"
export const DialogSelectMcp: Component = () => {
const sync = useSync()
const sdk = useSDK()
const [loading, setLoading] = createSignal<string | null>(null)
const items = createMemo(() =>
Object.entries(sync.data.mcp ?? {})
.map(([name, status]) => ({ name, status: status.status }))
.sort((a, b) => a.name.localeCompare(b.name)),
)
const toggle = async (name: string) => {
if (loading()) return
setLoading(name)
const status = sync.data.mcp[name]
if (status?.status === "connected") {
await sdk.client.mcp.disconnect({ name })
} else {
await sdk.client.mcp.connect({ name })
}
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
setLoading(null)
}
const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)
const totalCount = createMemo(() => items().length)
return (
<Dialog title="MCPs" description={`${enabledCount()} of ${totalCount()} enabled`}>
<List
search={{ placeholder: "Search", autofocus: true }}
emptyMessage="No MCPs configured"
key={(x) => x?.name ?? ""}
items={items}
filterKeys={["name", "status"]}
sortBy={(a, b) => a.name.localeCompare(b.name)}
onSelect={(x) => {
if (x) toggle(x.name)
}}
>
{(i) => {
const mcpStatus = () => sync.data.mcp[i.name]
const status = () => mcpStatus()?.status
const error = () => {
const s = mcpStatus()
return s?.status === "failed" ? s.error : undefined
}
const enabled = () => status() === "connected"
return (
<div class="w-full flex items-center justify-between gap-x-3">
<div class="flex flex-col gap-0.5 min-w-0">
<div class="flex items-center gap-2">
<span class="truncate">{i.name}</span>
<Show when={status() === "connected"}>
<span class="text-11-regular text-text-weaker">connected</span>
</Show>
<Show when={status() === "failed"}>
<span class="text-11-regular text-text-weaker">failed</span>
</Show>
<Show when={status() === "needs_auth"}>
<span class="text-11-regular text-text-weaker">needs auth</span>
</Show>
<Show when={status() === "disabled"}>
<span class="text-11-regular text-text-weaker">disabled</span>
</Show>
<Show when={loading() === i.name}>
<span class="text-11-regular text-text-weak">...</span>
</Show>
</div>
<Show when={error()}>
<span class="text-11-regular text-text-weaker truncate">{error()}</span>
</Show>
</div>
<div onClick={(e) => e.stopPropagation()}>
<Switch checked={enabled()} disabled={loading() === i.name} onChange={() => toggle(i.name)} />
</div>
</div>
)
}}
</List>
</Dialog>
)
}
@@ -1,15 +1,15 @@
import { Component, onCleanup, onMount, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { Button } from "@opencode-ai/ui/button"
import { Tag } from "@opencode-ai/ui/tag"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List, ListRef } from "@opencode-ai/ui/list"
import type { IconName } from "@opencode-ai/ui/icons/provider"
import { List, type ListRef } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { IconName } from "@opencode-ai/ui/icons/provider"
import { DialogSelectProvider } from "./dialog-select-provider"
import { Tag } from "@opencode-ai/ui/tag"
import { type Component, onCleanup, onMount, Show } from "solid-js"
import { useLocal } from "@/context/local"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { DialogConnectProvider } from "./dialog-connect-provider"
import { DialogSelectProvider } from "./dialog-select-provider"
export const DialogSelectModelUnpaid: Component = () => {
const local = useLocal()
@@ -64,7 +64,7 @@ export const DialogSelectModelUnpaid: Component = () => {
<div class="px-2 text-14-medium text-text-base">Add more models from popular providers</div>
<div class="w-full">
<List
class="w-full"
class="w-full px-0"
key={(x) => x?.id}
items={providers.popular}
activeIcon="plus-small"
@@ -79,17 +79,8 @@ export const DialogSelectModelUnpaid: Component = () => {
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-4">
<ProviderIcon
data-slot="list-item-extra-icon"
id={i.id as IconName}
// TODO: clean this up after we update icon in models.dev
classList={{
"text-icon-weak-base": true,
"size-4 mx-0.5": i.id === "opencode",
"size-5": i.id !== "opencode",
}}
/>
<div class="w-full flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
<span>{i.name}</span>
<Show when={i.id === "opencode"}>
<Tag>Recommended</Tag>
@@ -35,7 +35,6 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
}
>
<List
class="px-2.5"
search={{ placeholder: "Search models", autofocus: true }}
emptyMessage="No model results"
key={(x) => `${x.provider.id}:${x.id}`}
@@ -61,7 +60,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => {
}}
>
{(i) => (
<div class="w-full flex items-center gap-x-2.5">
<div class="w-full flex items-center gap-x-3">
<span>{i.name}</span>
<Show when={i.provider.id === "opencode" && (!i.cost || i.cost?.input === 0)}>
<Tag>Free</Tag>
@@ -15,7 +15,6 @@ export const DialogSelectProvider: Component = () => {
return (
<Dialog title="Connect provider">
<List
class="px-2.5"
search={{ placeholder: "Search providers", autofocus: true }}
activeIcon="plus-small"
key={(x) => x?.id}
@@ -38,17 +37,8 @@ export const DialogSelectProvider: Component = () => {
}}
>
{(i) => (
<div class="px-1.25 w-full flex items-center gap-x-4">
<ProviderIcon
data-slot="list-item-extra-icon"
id={i.id as IconName}
// TODO: clean this up after we update icon in models.dev
classList={{
"text-icon-weak-base": true,
"size-4 mx-0.5": i.id === "opencode",
"size-5": i.id !== "opencode",
}}
/>
<div class="px-1.25 w-full flex items-center gap-x-3">
<ProviderIcon data-slot="list-item-extra-icon" id={i.id as IconName} />
<span>{i.name}</span>
<Show when={i.id === "opencode"}>
<Tag>Recommended</Tag>
@@ -2,7 +2,7 @@ import { useLocal, type LocalFile } from "@/context/local"
import { Collapsible } from "@opencode-ai/ui/collapsible"
import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { For, Match, Switch, Show, type ComponentProps, type ParentProps } from "solid-js"
import { For, Match, Switch, type ComponentProps, type ParentProps } from "solid-js"
import { Dynamic } from "solid-js/web"
export default function FileTree(props: {
@@ -57,14 +57,14 @@ export default function FileTree(props: {
"text-text-muted/40": p.node.ignored,
"text-text-muted/80": !p.node.ignored,
// "!text-text": local.file.active()?.path === p.node.path,
"!text-primary": local.file.changed(p.node.path),
// "!text-primary": local.file.changed(p.node.path),
}}
>
{p.node.name}
</span>
<Show when={local.file.changed(p.node.path)}>
<span class="ml-auto mr-1 w-1.5 h-1.5 rounded-full bg-primary/50 shrink-0" />
</Show>
{/* <Show when={local.file.changed(p.node.path)}> */}
{/* <span class="ml-auto mr-1 w-1.5 h-1.5 rounded-full bg-primary/50 shrink-0" /> */}
{/* </Show> */}
</Dynamic>
)
@@ -20,6 +20,7 @@ import { iife } from "@opencode-ai/util/iife"
export function Header(props: {
navigateToProject: (directory: string) => void
navigateToSession: (session: Session | undefined) => void
onMobileMenuToggle?: () => void
}) {
const globalSync = useGlobalSync()
const globalSDK = useGlobalSDK()
@@ -29,11 +30,19 @@ export function Header(props: {
return (
<header class="h-12 shrink-0 bg-background-base border-b border-border-weak-base flex" data-tauri-drag-region>
<button
type="button"
class="xl:hidden w-12 shrink-0 flex items-center justify-center border-r border-border-weak-base hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active transition-colors"
onClick={props.onMobileMenuToggle}
>
<Icon name="menu" size="small" />
</button>
<A
href="/"
classList={{
"hidden xl:flex": true,
"w-12 shrink-0 px-4 py-3.5": true,
"flex items-center justify-start self-stretch": true,
"items-center justify-start self-stretch": true,
"border-r border-border-weak-base": true,
}}
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
@@ -46,30 +55,32 @@ export function Header(props: {
{(directory) => {
const currentDirectory = createMemo(() => base64Decode(directory()))
const store = createMemo(() => globalSync.child(currentDirectory())[0])
const sessions = createMemo(() => store().session ?? [])
const sessions = createMemo(() => (store().session ?? []).filter((s) => !s.parentID))
const currentSession = createMemo(() => sessions().find((s) => s.id === params.id))
const shareEnabled = createMemo(() => store().config.share !== "disabled")
return (
<>
<div class="flex items-center gap-3">
<div class="flex items-center gap-2">
<Select
options={layout.projects.list().map((project) => project.worktree)}
current={currentDirectory()}
label={(x) => getFilename(x)}
onSelect={(x) => (x ? props.navigateToProject(x) : undefined)}
class="text-14-regular text-text-base"
variant="ghost"
>
{/* @ts-ignore */}
{(i) => (
<div class="flex items-center gap-2">
<Icon name="folder" size="small" />
<div class="text-text-strong">{getFilename(i)}</div>
</div>
)}
</Select>
<div class="text-text-weaker">/</div>
<div class="flex items-center gap-3 min-w-0">
<div class="flex items-center gap-2 min-w-0">
<div class="hidden xl:flex items-center gap-2">
<Select
options={layout.projects.list().map((project) => project.worktree)}
current={currentDirectory()}
label={(x) => getFilename(x)}
onSelect={(x) => (x ? props.navigateToProject(x) : undefined)}
class="text-14-regular text-text-base"
variant="ghost"
>
{/* @ts-ignore */}
{(i) => (
<div class="flex items-center gap-2">
<Icon name="folder" size="small" />
<div class="text-text-strong">{getFilename(i)}</div>
</div>
)}
</Select>
<div class="text-text-weaker">/</div>
</div>
<Select
options={sessions()}
current={currentSession()}
@@ -77,12 +88,13 @@ export function Header(props: {
label={(x) => x.title}
value={(x) => x.id}
onSelect={props.navigateToSession}
class="text-14-regular text-text-base max-w-md"
class="text-14-regular text-text-base max-w-[calc(100vw-180px)] md:max-w-md"
variant="ghost"
/>
</div>
<Show when={currentSession()}>
<Tooltip
class="hidden xl:block"
value={
<div class="flex items-center gap-2">
<span>New session</span>
@@ -97,8 +109,39 @@ export function Header(props: {
</Show>
</div>
<div class="flex items-center gap-4">
<Show when={currentSession()?.summary?.files}>
<Tooltip
class="hidden md:block shrink-0"
value={
<div class="flex items-center gap-2">
<span>Toggle review</span>
<span class="text-icon-base text-12-medium">{command.keybind("review.toggle")}</span>
</div>
}
>
<Button variant="ghost" class="group/review-toggle size-6 p-0" onClick={layout.review.toggle}>
<div class="relative flex items-center justify-center size-4 [&>*]:absolute [&>*]:inset-0">
<Icon
name={layout.review.opened() ? "layout-right" : "layout-left"}
size="small"
class="group-hover/review-toggle:hidden"
/>
<Icon
name={layout.review.opened() ? "layout-right-partial" : "layout-left-partial"}
size="small"
class="hidden group-hover/review-toggle:inline-block"
/>
<Icon
name={layout.review.opened() ? "layout-right-full" : "layout-left-full"}
size="small"
class="hidden group-active/review-toggle:inline-block"
/>
</div>
</Button>
</Tooltip>
</Show>
<Tooltip
class="shrink-0"
class="hidden md:block shrink-0"
value={
<div class="flex items-center gap-2">
<span>Toggle terminal</span>
@@ -22,6 +22,7 @@ import { useProviders } from "@/hooks/use-providers"
import { useCommand } from "@/context/command"
import { persisted } from "@/utils/persist"
import { Identifier } from "@/utils/id"
import { SessionContextUsage } from "@/components/session-context-usage"
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"]
const ACCEPTED_FILE_TYPES = [...ACCEPTED_IMAGE_TYPES, "application/pdf"]
@@ -102,7 +103,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
imageAttachments: ImageAttachmentPart[]
mode: "normal" | "shell"
applyingHistory: boolean
userHasEdited: boolean
}>({
popover: null,
historyIndex: -1,
@@ -112,7 +112,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
imageAttachments: [],
mode: "normal",
applyingHistory: false,
userHasEdited: false,
})
const MAX_HISTORY = 100
@@ -149,7 +148,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const applyHistoryPrompt = (p: Prompt, position: "start" | "end") => {
const length = position === "start" ? 0 : promptLength(p)
setStore("applyingHistory", true)
setStore("userHasEdited", false)
prompt.set(p, length)
requestAnimationFrame(() => {
editorRef.focus()
@@ -278,11 +276,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
})
createEffect(() => {
if (isFocused()) {
handleInput()
} else {
setStore("popover", null)
}
if (!isFocused()) setStore("popover", null)
})
const handleFileSelect = (path: string | undefined) => {
@@ -362,7 +356,12 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
() => prompt.current(),
(currentParts) => {
const domParts = parseFromDOM()
if (isPromptEqual(currentParts, domParts)) return
const normalized = Array.from(editorRef.childNodes).every((node) => {
if (node.nodeType === Node.TEXT_NODE) return true
if (node.nodeType !== Node.ELEMENT_NODE) return false
return (node as HTMLElement).dataset.type === "file"
})
if (normalized && isPromptEqual(currentParts, domParts)) return
const selection = window.getSelection()
let cursorPosition: number | null = null
@@ -394,34 +393,21 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
)
const parseFromDOM = (): Prompt => {
const newParts: Prompt = []
const parts: Prompt = []
let position = 0
let buffer = ""
const pushText = (content: string) => {
const flushText = () => {
const content = buffer.replace(/\r\n?/g, "\n")
buffer = ""
if (!content) return
newParts.push({ type: "text", content, start: position, end: position + content.length })
parts.push({ type: "text", content, start: position, end: position + content.length })
position += content.length
}
const rangeText = (range: Range) => {
const fragment = range.cloneContents()
const container = document.createElement("div")
container.append(fragment)
return container.innerText
}
const files = Array.from(editorRef.querySelectorAll<HTMLElement>("[data-type=file]"))
let last: HTMLElement | undefined
files.forEach((file) => {
const before = document.createRange()
before.selectNodeContents(editorRef)
if (last) before.setStartAfter(last)
before.setEndBefore(file)
pushText(rangeText(before))
const pushFile = (file: HTMLElement) => {
const content = file.textContent ?? ""
newParts.push({
parts.push({
type: "file",
path: file.dataset.path!,
content,
@@ -429,16 +415,44 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
end: position + content.length,
})
position += content.length
last = file
}
const visit = (node: Node) => {
if (node.nodeType === Node.TEXT_NODE) {
buffer += node.textContent ?? ""
return
}
if (node.nodeType !== Node.ELEMENT_NODE) return
const el = node as HTMLElement
if (el.dataset.type === "file") {
flushText()
pushFile(el)
return
}
if (el.tagName === "BR") {
buffer += "\n"
return
}
for (const child of Array.from(el.childNodes)) {
visit(child)
}
}
const children = Array.from(editorRef.childNodes)
children.forEach((child, index) => {
const isBlock = child.nodeType === Node.ELEMENT_NODE && ["DIV", "P"].includes((child as HTMLElement).tagName)
visit(child)
if (isBlock && index < children.length - 1) {
buffer += "\n"
}
})
const after = document.createRange()
after.selectNodeContents(editorRef)
if (last) after.setStartAfter(last)
pushText(rangeText(after))
flushText()
if (newParts.length === 0) newParts.push(...DEFAULT_PROMPT)
return newParts
if (parts.length === 0) parts.push(...DEFAULT_PROMPT)
return parts
}
const handleInput = () => {
@@ -451,7 +465,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
if (shouldReset) {
setStore("popover", null)
setStore("userHasEdited", false)
if (store.historyIndex >= 0 && !store.applyingHistory) {
setStore("historyIndex", -1)
setStore("savedPrompt", null)
@@ -486,10 +499,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
setStore("savedPrompt", null)
}
if (!store.applyingHistory) {
setStore("userHasEdited", true)
}
prompt.set(rawParts, cursorPosition)
}
@@ -515,27 +524,39 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const gap = document.createTextNode(" ")
const range = selection.getRangeAt(0)
if (atMatch) {
let runningLength = 0
const setEdge = (edge: "start" | "end", offset: number) => {
let remaining = offset
const nodes = Array.from(editorRef.childNodes)
const walker = document.createTreeWalker(editorRef, NodeFilter.SHOW_TEXT, null)
let currentNode = walker.nextNode()
while (currentNode) {
const textContent = currentNode.textContent || ""
if (runningLength + textContent.length >= atMatch.index!) {
const localStart = atMatch.index! - runningLength
const localEnd = cursorPosition - runningLength
if (currentNode === range.startContainer || runningLength + textContent.length >= cursorPosition) {
range.setStart(currentNode, localStart)
range.setEnd(currentNode, Math.min(localEnd, textContent.length))
break
}
for (const node of nodes) {
const length = node.textContent?.length ?? 0
const isText = node.nodeType === Node.TEXT_NODE
const isFile = node.nodeType === Node.ELEMENT_NODE && (node as HTMLElement).dataset.type === "file"
if (isText && remaining <= length) {
if (edge === "start") range.setStart(node, remaining)
if (edge === "end") range.setEnd(node, remaining)
return
}
runningLength += textContent.length
currentNode = walker.nextNode()
if (isFile && remaining <= length) {
if (edge === "start" && remaining === 0) range.setStartBefore(node)
if (edge === "start" && remaining > 0) range.setStartAfter(node)
if (edge === "end" && remaining === 0) range.setEndBefore(node)
if (edge === "end" && remaining > 0) range.setEndAfter(node)
return
}
remaining -= length
}
}
if (atMatch) {
const start = atMatch.index ?? cursorPosition - atMatch[0].length
setEdge("start", start)
setEdge("end", cursorPosition)
}
range.deleteContents()
range.insertNode(gap)
range.insertNode(pill)
@@ -583,8 +604,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}
const navigateHistory = (direction: "up" | "down") => {
if (store.userHasEdited) return false
const entries = store.mode === "shell" ? shellHistory.entries : history.entries
const current = store.historyIndex
@@ -691,6 +710,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
return
}
if (event.key === "Enter" && event.shiftKey) {
addPart({ type: "text", content: "\n", start: 0, end: 0 })
event.preventDefault()
return
}
if (event.key === "Enter" && !event.shiftKey) {
handleSubmit(event)
}
@@ -716,7 +740,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
addToHistory(currentPrompt, store.mode)
setStore("historyIndex", -1)
setStore("savedPrompt", null)
setStore("userHasEdited", false)
let existing = info()
if (!existing) {
@@ -966,18 +989,21 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
onInput={handleInput}
onKeyDown={handleKeyDown}
classList={{
"w-full px-5 py-3 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
"[&>[data-type=file]]:text-icon-info-active": true,
"w-full px-5 py-3 pr-12 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
"[&_[data-type=file]]:text-icon-info-active": true,
"font-mono!": store.mode === "shell",
}}
/>
<Show when={!prompt.dirty() && store.imageAttachments.length === 0}>
<div class="absolute top-0 left-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none">
<div class="absolute top-0 inset-x-0 px-5 py-3 pr-12 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate">
{store.mode === "shell"
? "Enter shell command..."
: `Ask anything... "${PLACEHOLDERS[store.placeholder]}"`}
</div>
</Show>
<div class="absolute top-4.5 right-4">
<SessionContextUsage />
</div>
</div>
<div class="relative p-3 flex items-center justify-between">
<div class="flex items-center justify-start gap-1">
@@ -1026,7 +1052,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}
>
{local.model.current()?.name ?? "Select model"}
<span class="ml-0.5 text-text-weak text-12-regular">{local.model.current()?.provider.name}</span>
<span class="hidden md:block ml-0.5 text-text-weak text-12-regular">
{local.model.current()?.provider.name}
</span>
<Icon name="chevron-down" size="small" />
</Button>
</Tooltip>
@@ -0,0 +1,63 @@
import { createMemo, Show } from "solid-js"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { ProgressCircle } from "@opencode-ai/ui/progress-circle"
import { useSync } from "@/context/sync"
import { useParams } from "@solidjs/router"
import { AssistantMessage } from "@opencode-ai/sdk/v2"
export function SessionContextUsage() {
const sync = useSync()
const params = useParams()
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
const cost = createMemo(() => {
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(total)
})
const context = createMemo(() => {
const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
if (!last) return
const total =
last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID]
return {
tokens: total.toLocaleString(),
percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null,
}
})
return (
<Show when={context?.()}>
{(ctx) => (
<Tooltip
value={
<div class="flex flex-col gap-1">
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Tokens</span>
<span class="text-left flex-1">{ctx().tokens}</span>
</div>
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Usage</span>
<span class="text-left flex-1">{ctx().percentage ?? 0}%</span>
</div>
<div class="flex gap-3">
<span class="opacity-70 text-right flex-1">Cost</span>
<span class="text-left flex-1">{cost()}</span>
</div>
</div>
}
placement="top"
>
<div class="flex items-center gap-1.5">
<ProgressCircle size={16} strokeWidth={2} percentage={ctx().percentage ?? 0} />
{/* <span class="text-12-medium text-text-weak">{`${ctx().percentage ?? 0}%`}</span> */}
</div>
</Tooltip>
)}
</Show>
)
}
@@ -0,0 +1,40 @@
import { createMemo, Show } from "solid-js"
import { Icon } from "@opencode-ai/ui/icon"
import { useSync } from "@/context/sync"
import { Tooltip } from "@opencode-ai/ui/tooltip"
export function SessionLspIndicator() {
const sync = useSync()
const lspStats = createMemo(() => {
const lsp = sync.data.lsp ?? []
const connected = lsp.filter((s) => s.status === "connected").length
const hasError = lsp.some((s) => s.status === "error")
const total = lsp.length
return { connected, hasError, total }
})
const tooltipContent = createMemo(() => {
const lsp = sync.data.lsp ?? []
if (lsp.length === 0) return "No LSP servers"
return lsp.map((s) => s.name).join(", ")
})
return (
<Show when={lspStats().total > 0}>
<Tooltip placement="top" value={tooltipContent()}>
<div class="flex items-center gap-1 px-2 cursor-default select-none">
<Icon
name="code"
size="small"
classList={{
"text-icon-critical-base": lspStats().hasError,
"text-icon-success-base": !lspStats().hasError && lspStats().connected > 0,
}}
/>
<span class="text-12-regular text-text-weak">{lspStats().connected} LSP</span>
</div>
</Tooltip>
</Show>
)
}
@@ -0,0 +1,36 @@
import { createMemo, Show } from "solid-js"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { useSync } from "@/context/sync"
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
export function SessionMcpIndicator() {
const sync = useSync()
const dialog = useDialog()
const mcpStats = createMemo(() => {
const mcp = sync.data.mcp ?? {}
const entries = Object.entries(mcp)
const enabled = entries.filter(([, status]) => status.status === "connected").length
const failed = entries.some(([, status]) => status.status === "failed")
const total = entries.length
return { enabled, failed, total }
})
return (
<Show when={mcpStats().total > 0}>
<Button variant="ghost" onClick={() => dialog.show(() => <DialogSelectMcp />)}>
<Icon
name="mcp"
size="small"
classList={{
"text-icon-critical-base": mcpStats().failed,
"text-icon-success-base": !mcpStats().failed && mcpStats().enabled > 0,
}}
/>
<span class="text-12-regular text-text-weak">{mcpStats().enabled} MCP</span>
</Button>
</Show>
)
}
@@ -0,0 +1,14 @@
import { Show, type ParentProps } from "solid-js"
import { usePlatform } from "@/context/platform"
export function StatusBar(props: ParentProps) {
const platform = usePlatform()
return (
<div class="h-8 w-full shrink-0 flex items-center justify-between px-2 border-t border-border-weak-base bg-background-base">
<Show when={platform.version}>
<span class="text-12-regular text-text-weak">v{platform.version}</span>
</Show>
<div class="flex items-center">{props.children}</div>
</div>
)
}
@@ -119,7 +119,6 @@ function DialogCommand(props: { options: CommandOption[] }) {
return (
<Dialog title="Commands">
<List
class="px-2.5"
search={{ placeholder: "Search commands", autofocus: true }}
emptyMessage="No commands found"
items={() => props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)}
@@ -5,8 +5,6 @@ import {
type Part,
type Config,
type Path,
type File,
type FileNode,
type Project,
type FileDiff,
type Todo,
@@ -14,6 +12,8 @@ import {
type ProviderListResponse,
type ProviderAuthResponse,
type Command,
type McpStatus,
type LspStatus,
createOpencodeClient,
} from "@opencode-ai/sdk/v2/client"
import { createStore, produce, reconcile } from "solid-js/store"
@@ -43,6 +43,10 @@ type State = {
todo: {
[sessionID: string]: Todo[]
}
mcp: {
[name: string]: McpStatus
}
lsp: LspStatus[]
limit: number
message: {
[sessionID: string]: Message[]
@@ -50,8 +54,6 @@ type State = {
part: {
[messageID: string]: Part[]
}
node: FileNode[]
changes: File[]
}
function createGlobalSync() {
@@ -89,11 +91,11 @@ function createGlobalSync() {
session_status: {},
session_diff: {},
todo: {},
mcp: {},
lsp: [],
limit: 5,
message: {},
part: {},
node: [],
changes: [],
})
children[directory] = createStore(globalStore.children[directory])
bootstrapInstance(directory)
@@ -136,15 +138,27 @@ function createGlobalSync() {
})
const load = {
project: () => sdk.project.current().then((x) => setStore("project", x.data!.id)),
provider: () => sdk.provider.list().then((x) => setStore("provider", x.data!)),
provider: () =>
sdk.provider.list().then((x) => {
const data = x.data!
setStore("provider", {
...data,
all: data.all.map((provider) => ({
...provider,
models: Object.fromEntries(
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
),
})),
})
}),
path: () => sdk.path.get().then((x) => setStore("path", x.data!)),
agent: () => sdk.app.agents().then((x) => setStore("agent", x.data ?? [])),
command: () => sdk.command.list().then((x) => setStore("command", x.data ?? [])),
session: () => loadSessions(directory),
status: () => sdk.session.status().then((x) => setStore("session_status", x.data!)),
config: () => sdk.config.get().then((x) => setStore("config", x.data!)),
changes: () => sdk.file.status().then((x) => setStore("changes", x.data!)),
node: () => sdk.file.list({ path: "/" }).then((x) => setStore("node", x.data!)),
mcp: () => sdk.mcp.status().then((x) => setStore("mcp", x.data ?? {})),
lsp: () => sdk.lsp.status().then((x) => setStore("lsp", x.data ?? [])),
}
await Promise.all(Object.values(load).map((p) => retry(p).catch((e) => setGlobalStore("error", e))))
.then(() => setStore("ready", true))
@@ -211,13 +225,13 @@ function createGlobalSync() {
break
}
case "session.diff":
setStore("session_diff", event.properties.sessionID, event.properties.diff)
setStore("session_diff", event.properties.sessionID, reconcile(event.properties.diff, { key: "file" }))
break
case "todo.updated":
setStore("todo", event.properties.sessionID, event.properties.todos)
setStore("todo", event.properties.sessionID, reconcile(event.properties.todos))
break
case "session.status": {
setStore("session_status", event.properties.sessionID, event.properties.status)
setStore("session_status", event.properties.sessionID, reconcile(event.properties.status))
break
}
case "message.updated": {
@@ -295,6 +309,18 @@ function createGlobalSync() {
})
async function bootstrap() {
const health = await globalSDK.client.global
.health()
.then((x) => x.data)
.catch(() => undefined)
if (!health?.healthy) {
setGlobalStore(
"error",
new Error(`Could not connect to server. Is there a server running at \`${globalSDK.url}\`?`),
)
return
}
return Promise.all([
retry(() =>
globalSDK.client.path.get().then((x) => {
@@ -311,7 +337,16 @@ function createGlobalSync() {
),
retry(() =>
globalSDK.client.provider.list().then((x) => {
setGlobalStore("provider", x.data ?? {})
const data = x.data!
setGlobalStore("provider", {
...data,
all: data.all.map((provider) => ({
...provider,
models: Object.fromEntries(
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
),
})),
})
}),
),
retry(() =>
@@ -46,6 +46,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
opened: false,
height: 280,
},
review: {
opened: true,
},
session: {
width: 600,
},
@@ -108,10 +111,12 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setStore("projects", (x) => x.filter((x) => x.worktree !== directory))
},
expand(directory: string) {
setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: true } : x)))
const index = store.projects.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", index, "expanded", true)
},
collapse(directory: string) {
setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: false } : x)))
const index = store.projects.findIndex((x) => x.worktree === directory)
if (index !== -1) setStore("projects", index, "expanded", false)
},
move(directory: string, toIndex: number) {
setStore("projects", (projects) => {
@@ -156,6 +161,18 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
setStore("terminal", "height", height)
},
},
review: {
opened: createMemo(() => store.review?.opened ?? true),
open() {
setStore("review", "opened", true)
},
close() {
setStore("review", "opened", false)
},
toggle() {
setStore("review", "opened", (x) => !x)
},
},
session: {
width: createMemo(() => store.session?.width ?? 600),
resize(width: number) {
@@ -1,5 +1,5 @@
import { createStore, produce, reconcile } from "solid-js/store"
import { batch, createEffect, createMemo } from "solid-js"
import { batch, createMemo } from "solid-js"
import { filter, firstBy, flat, groupBy, mapValues, pipe, uniqueBy, values } from "remeda"
import type { FileContent, FileNode, Model, Provider, File as FileStatus } from "@opencode-ai/sdk/v2"
import { createSimpleContext } from "@opencode-ai/ui/context"
@@ -9,6 +9,7 @@ import { base64Encode } from "@opencode-ai/util/encode"
import { useProviders } from "@/hooks/use-providers"
import { DateTime } from "luxon"
import { persisted } from "@/utils/persist"
import { showToast } from "@opencode-ai/ui/toast"
export type LocalFile = FileNode &
Partial<{
@@ -61,24 +62,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
}
}
// Automatically update model when agent changes
createEffect(() => {
const value = agent.current()
if (value.model) {
if (isModelValid(value.model))
model.set({
providerID: value.model.providerID,
modelID: value.model.modelID,
})
// else
// toast.show({
// type: "warning",
// message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`,
// duration: 3000,
// })
}
})
const agent = (() => {
const list = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden))
const [store, setStore] = createStore<{
@@ -276,11 +259,11 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const [store, setStore] = createStore<{
node: Record<string, LocalFile>
}>({
node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
node: {}, // Object.fromEntries(sync.data.node.map((x) => [x.path, x])),
})
const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b)))
// const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path)))
// const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b)))
// createEffect((prev: FileStatus[]) => {
// const removed = prev.filter((p) => !sync.data.changes.find((c) => c.path === p.path))
@@ -308,16 +291,16 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
// return sync.data.changes
// }, sync.data.changes)
const changed = (path: string) => {
const node = store.node[path]
if (node?.status) return true
const set = changeset()
if (set.has(path)) return true
for (const p of set) {
if (p.startsWith(path ? path + "/" : "")) return true
}
return false
}
// const changed = (path: string) => {
// const node = store.node[path]
// if (node?.status) return true
// const set = changeset()
// if (set.has(path)) return true
// for (const p of set) {
// if (p.startsWith(path ? path + "/" : "")) return true
// }
// return false
// }
// const resetNode = (path: string) => {
// setStore("node", path, {
@@ -336,17 +319,26 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
const load = async (path: string) => {
const relativePath = relative(path)
await sdk.client.file.read({ path: relativePath }).then((x) => {
if (!store.node[relativePath]) return
setStore(
"node",
relativePath,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
await sdk.client.file
.read({ path: relativePath })
.then((x) => {
if (!store.node[relativePath]) return
setStore(
"node",
relativePath,
produce((draft) => {
draft.loaded = true
draft.content = x.data
}),
)
})
.catch((e) => {
showToast({
variant: "error",
title: "Failed to load file",
description: e.message,
})
})
}
const fetch = async (path: string) => {
@@ -466,8 +458,8 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
setChangeIndex(path: string, index: number | undefined) {
setStore("node", path, "selectedChange", index)
},
changes,
changed,
// changes,
// changed,
children(path: string) {
return Object.values(store.node).filter(
(x) =>
@@ -5,6 +5,9 @@ export type Platform = {
/** Platform discriminator */
platform: "web" | "tauri"
/** App version */
version?: string
/** Open a URL in the default browser */
openLink(url: string): void
@@ -2,6 +2,7 @@
import { render } from "solid-js/web"
import { App } from "@/app"
import { Platform, PlatformProvider } from "@/context/platform"
import pkg from "../package.json"
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
@@ -12,6 +13,7 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
const platform: Platform = {
platform: "web",
version: pkg.version,
openLink(url: string) {
window.open(url, "_blank")
},
@@ -1,7 +1,7 @@
import { TextField } from "@opencode-ai/ui/text-field"
import { Logo } from "@opencode-ai/ui/logo"
import { Button } from "@opencode-ai/ui/button"
import { Component } from "solid-js"
import { Component, Show } from "solid-js"
import { usePlatform } from "@/context/platform"
import { Icon } from "@opencode-ai/ui/icon"
@@ -62,27 +62,49 @@ function formatInitError(error: InitError): string {
}
}
function formatErrorChain(error: unknown, depth = 0): string {
function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): string {
if (!error) return "Unknown error"
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
if (isInitError(error)) {
return indent + formatInitError(error)
const message = formatInitError(error)
if (depth > 0 && parentMessage === message) return ""
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
return indent + message
}
if (error instanceof Error) {
const parts = [indent + `${error.name}: ${error.message}`]
if (error.stack) {
parts.push(error.stack)
const isDuplicate = depth > 0 && parentMessage === error.message
const parts: string[] = []
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
if (!isDuplicate) {
// Stack already includes error name and message, so prefer it
parts.push(indent + (error.stack ?? `${error.name}: ${error.message}`))
} else if (error.stack) {
// Duplicate message - only show the stack trace lines (skip message)
const trace = error.stack.split("\n").slice(1).join("\n").trim()
if (trace) {
parts.push(trace)
}
}
if (error.cause) {
parts.push(formatErrorChain(error.cause, depth + 1))
const causeResult = formatErrorChain(error.cause, depth + 1, error.message)
if (causeResult) {
parts.push(causeResult)
}
}
return parts.join("\n\n")
}
if (typeof error === "string") return indent + error
if (typeof error === "string") {
if (depth > 0 && parentMessage === error) return ""
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
return indent + error
}
const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : ""
return indent + JSON.stringify(error, null, 2)
}
@@ -116,16 +138,21 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
<Button size="large" onClick={platform.restart}>
Restart
</Button>
<div class="flex items-center justify-center gap-1">
Please report this error to the OpenCode team
<button
type="button"
class="flex items-center text-text-interactive-base gap-1"
onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")}
>
<div>on Discord</div>
<Icon name="discord" class="text-text-interactive-base" />
</button>
<div class="flex flex-col items-center gap-2">
<div class="flex items-center justify-center gap-1">
Please report this error to the OpenCode team
<button
type="button"
class="flex items-center text-text-interactive-base gap-1"
onClick={() => platform.openLink("https://opencode.ai/desktop-feedback")}
>
<div>on Discord</div>
<Icon name="discord" class="text-text-interactive-base" />
</button>
</div>
<Show when={platform.version}>
<p class="text-xs text-text-weak">Version: {platform.version}</p>
</Show>
</div>
</div>
</div>
@@ -1,4 +1,17 @@
import { createEffect, createMemo, For, Match, onMount, ParentProps, Show, Switch, type JSX } from "solid-js"
import {
createEffect,
createMemo,
createSignal,
For,
Match,
onCleanup,
onMount,
ParentProps,
Show,
Switch,
untrack,
type JSX,
} from "solid-js"
import { DateTime } from "luxon"
import { A, useNavigate, useParams } from "@solidjs/router"
import { useLayout, getAvatarColors, LocalProject } from "@/context/layout"
@@ -42,9 +55,29 @@ export default function Layout(props: ParentProps) {
const [store, setStore] = createStore({
lastSession: {} as { [directory: string]: string },
activeDraggable: undefined as string | undefined,
mobileSidebarOpen: false,
mobileProjectsExpanded: {} as Record<string, boolean>,
})
const mobileSidebar = {
open: () => store.mobileSidebarOpen,
show: () => setStore("mobileSidebarOpen", true),
hide: () => setStore("mobileSidebarOpen", false),
toggle: () => setStore("mobileSidebarOpen", (x) => !x),
}
const mobileProjects = {
expanded: (directory: string) => store.mobileProjectsExpanded[directory] ?? true,
expand: (directory: string) => setStore("mobileProjectsExpanded", directory, true),
collapse: (directory: string) => setStore("mobileProjectsExpanded", directory, false),
}
let scrollContainerRef: HTMLDivElement | undefined
const xlQuery = window.matchMedia("(min-width: 1280px)")
const [isLargeViewport, setIsLargeViewport] = createSignal(xlQuery.matches)
const handleViewportChange = (e: MediaQueryListEvent) => setIsLargeViewport(e.matches)
xlQuery.addEventListener("change", handleViewportChange)
onCleanup(() => xlQuery.removeEventListener("change", handleViewportChange))
const params = useParams()
const globalSDK = useGlobalSDK()
@@ -84,42 +117,31 @@ export default function Layout(props: ParentProps) {
}
})
function flattenSessions(sessions: Session[]): Session[] {
const childrenMap = new Map<string, Session[]>()
for (const session of sessions) {
if (session.parentID) {
const children = childrenMap.get(session.parentID) ?? []
children.push(session)
childrenMap.set(session.parentID, children)
}
}
const result: Session[] = []
function visit(session: Session) {
result.push(session)
for (const child of childrenMap.get(session.id) ?? []) {
visit(child)
}
}
for (const session of sessions) {
if (!session.parentID) visit(session)
}
return result
function sortSessions(a: Session, b: Session) {
const now = Date.now()
const oneMinuteAgo = now - 60 * 1000
const aUpdated = a.time.updated ?? a.time.created
const bUpdated = b.time.updated ?? b.time.created
const aRecent = aUpdated > oneMinuteAgo
const bRecent = bUpdated > oneMinuteAgo
if (aRecent && bRecent) return a.id.localeCompare(b.id)
if (aRecent && !bRecent) return -1
if (!aRecent && bRecent) return 1
return bUpdated - aUpdated
}
function scrollToSession(sessionId: string) {
if (!scrollContainerRef) return
const element = scrollContainerRef.querySelector(`[data-session-id="${sessionId}"]`)
if (element) {
element.scrollIntoView({ block: "center", behavior: "smooth" })
element.scrollIntoView({ block: "nearest", behavior: "smooth" })
}
}
function projectSessions(directory: string) {
if (!directory) return []
const sessions = globalSync
.child(directory)[0]
.session.toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
return flattenSessions(sessions ?? [])
const sessions = globalSync.child(directory)[0].session.toSorted(sortSessions)
return (sessions ?? []).filter((s) => !s.parentID)
}
const currentSessions = createMemo(() => {
@@ -259,11 +281,13 @@ export default function Layout(props: ParentProps) {
if (!directory) return
const lastSession = store.lastSession[directory]
navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`)
mobileSidebar.hide()
}
function navigateToSession(session: Session | undefined) {
if (!session) return
navigate(`/${params.dir}/session/${session?.id}`)
mobileSidebar.hide()
}
function openProject(directory: string, navigate = true) {
@@ -297,13 +321,20 @@ export default function Layout(props: ParentProps) {
createEffect(() => {
if (!params.dir || !params.id) return
const directory = base64Decode(params.dir)
setStore("lastSession", directory, params.id)
notification.session.markViewed(params.id)
const id = params.id
setStore("lastSession", directory, id)
notification.session.markViewed(id)
untrack(() => layout.projects.expand(directory))
requestAnimationFrame(() => scrollToSession(id))
})
createEffect(() => {
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
if (isLargeViewport()) {
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
} else {
document.documentElement.style.setProperty("--dialog-left-margin", "0px")
}
})
function getDraggableId(event: unknown): string | undefined {
@@ -417,12 +448,9 @@ export default function Layout(props: ParentProps) {
session: Session
slug: string
project: LocalProject
depth?: number
childrenMap: Map<string, Session[]>
mobile?: boolean
}): JSX.Element => {
const notification = useNotification()
const depth = props.depth ?? 0
const children = createMemo(() => props.childrenMap.get(props.session.id) ?? [])
const updated = createMemo(() => DateTime.fromMillis(props.session.time.updated))
const notifications = createMemo(() => notification.session.unseen(props.session.id))
const hasError = createMemo(() => notifications().some((n) => n.type === "error"))
@@ -437,15 +465,20 @@ export default function Layout(props: ParentProps) {
data-session-id={props.session.id}
class="group/session relative w-full pr-2 py-1 rounded-md cursor-default transition-colors
hover:bg-surface-raised-base-hover focus-within:bg-surface-raised-base-hover has-[.active]:bg-surface-raised-base-hover"
style={{ "padding-left": `${16 + depth * 12}px` }}
style={{ "padding-left": "16px" }}
>
<Tooltip placement="right" value={props.session.title} gutter={10}>
<Tooltip placement={props.mobile ? "bottom" : "right"} value={props.session.title} gutter={10}>
<A
href={`${props.slug}/session/${props.session.id}`}
class="flex flex-col min-w-0 text-left w-full focus:outline-none"
>
<div class="flex items-center self-stretch gap-6 justify-between transition-[padding] group-hover/session:pr-7 group-focus-within/session:pr-7 group-active/session:pr-7">
<span class="text-14-regular text-text-strong overflow-hidden text-ellipsis truncate">
<span
classList={{
"text-14-regular text-text-strong overflow-hidden text-ellipsis truncate": true,
"animate-pulse": isWorking(),
}}
>
{props.session.title}
</span>
<div class="shrink-0 group-hover/session:hidden group-active/session:hidden group-focus-within/session:hidden">
@@ -486,66 +519,54 @@ export default function Layout(props: ParentProps) {
</A>
</Tooltip>
<div class="hidden group-hover/session:flex group-active/session:flex group-focus-within/session:flex text-text-base gap-1 items-center absolute top-1 right-1">
<Tooltip placement="right" value="Archive session">
<Tooltip
placement={props.mobile ? "bottom" : "right"}
value={
<div class="flex items-center gap-2">
<span>Archive session</span>
<span class="text-icon-base text-12-medium">{command.keybind("session.archive")}</span>
</div>
}
>
<IconButton icon="archive" variant="ghost" onClick={() => archiveSession(props.session)} />
</Tooltip>
</div>
</div>
<For each={children()}>
{(child) => (
<SessionItem
session={child}
slug={props.slug}
project={props.project}
depth={depth + 1}
childrenMap={props.childrenMap}
/>
)}
</For>
</>
)
}
const SortableProject = (props: { project: LocalProject }): JSX.Element => {
const SortableProject = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => {
const sortable = createSortable(props.project.worktree)
const showExpanded = createMemo(() => props.mobile || layout.sidebar.opened())
const slug = createMemo(() => base64Encode(props.project.worktree))
const name = createMemo(() => getFilename(props.project.worktree))
const [store, setProjectStore] = globalSync.child(props.project.worktree)
const sessions = createMemo(() =>
store.session.toSorted((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created)),
)
const sessions = createMemo(() => store.session.toSorted(sortSessions))
const rootSessions = createMemo(() => sessions().filter((s) => !s.parentID))
const childSessionsByParent = createMemo(() => {
const map = new Map<string, Session[]>()
for (const session of sessions()) {
if (session.parentID) {
const children = map.get(session.parentID) ?? []
children.push(session)
map.set(session.parentID, children)
}
}
return map
})
const hasMoreSessions = createMemo(() => store.session.length >= store.limit)
const loadMoreSessions = async () => {
setProjectStore("limit", (limit) => limit + 5)
await globalSync.project.loadSessions(props.project.worktree)
}
const isExpanded = createMemo(() =>
props.mobile ? mobileProjects.expanded(props.project.worktree) : props.project.expanded,
)
const handleOpenChange = (open: boolean) => {
if (open) layout.projects.expand(props.project.worktree)
else layout.projects.collapse(props.project.worktree)
if (props.mobile) {
if (open) mobileProjects.expand(props.project.worktree)
else mobileProjects.collapse(props.project.worktree)
} else {
if (open) layout.projects.expand(props.project.worktree)
else layout.projects.collapse(props.project.worktree)
}
}
return (
// @ts-ignore
<div use:sortable classList={{ "opacity-30": sortable.isActiveDraggable }}>
<Switch>
<Match when={layout.sidebar.opened()}>
<Collapsible
variant="ghost"
open={props.project.expanded}
class="gap-2 shrink-0"
onOpenChange={handleOpenChange}
>
<Match when={showExpanded()}>
<Collapsible variant="ghost" open={isExpanded()} class="gap-2 shrink-0" onOpenChange={handleOpenChange}>
<Button
as={"div"}
variant="ghost"
@@ -556,7 +577,7 @@ export default function Layout(props: ParentProps) {
project={props.project}
class="group-hover/session:hidden"
expandable
notify={!props.project.expanded}
notify={!isExpanded()}
/>
<span class="truncate text-14-medium text-text-strong">{name()}</span>
</Collapsible.Trigger>
@@ -571,7 +592,15 @@ export default function Layout(props: ParentProps) {
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu>
<Tooltip placement="top" value="New session">
<Tooltip
placement="top"
value={
<div class="flex items-center gap-2">
<span>New session</span>
<span class="text-icon-base text-12-medium">{command.keybind("session.new")}</span>
</div>
}
>
<IconButton as={A} href={`${slug()}/session`} icon="plus-small" variant="ghost" />
</Tooltip>
</div>
@@ -580,12 +609,7 @@ export default function Layout(props: ParentProps) {
<nav class="hidden @[4rem]:flex w-full flex-col gap-1.5">
<For each={rootSessions()}>
{(session) => (
<SessionItem
session={session}
slug={slug()}
project={props.project}
childrenMap={childSessionsByParent()}
/>
<SessionItem session={session} slug={slug()} project={props.project} mobile={props.mobile} />
)}
</For>
<Show when={rootSessions().length === 0}>
@@ -595,7 +619,7 @@ export default function Layout(props: ParentProps) {
>
<div class="flex items-center self-stretch w-full">
<div class="flex-1 min-w-0">
<Tooltip placement="right" value="New session">
<Tooltip placement={props.mobile ? "bottom" : "right"} value="New session">
<A
href={`${slug()}/session`}
class="flex flex-col gap-1 min-w-0 text-left w-full focus:outline-none"
@@ -650,30 +674,12 @@ export default function Layout(props: ParentProps) {
)
}
return (
<div class="relative flex-1 min-h-0 flex flex-col">
<Header navigateToProject={navigateToProject} navigateToSession={navigateToSession} />
<div class="flex-1 min-h-0 flex">
<div
classList={{
"relative @container w-12 pb-5 shrink-0 bg-background-base": true,
"flex flex-col gap-5.5 items-start self-stretch justify-between": true,
"border-r border-border-weak-base contain-strict": true,
}}
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
>
<Show when={layout.sidebar.opened()}>
<ResizeHandle
direction="horizontal"
size={layout.sidebar.width()}
min={150}
max={window.innerWidth * 0.3}
collapseThreshold={80}
onResize={layout.sidebar.resize}
onCollapse={layout.sidebar.close}
/>
</Show>
<div class="flex flex-col items-start self-stretch gap-4 p-2 min-h-0 overflow-hidden">
const SidebarContent = (sidebarProps: { mobile?: boolean }) => {
const expanded = () => sidebarProps.mobile || layout.sidebar.opened()
return (
<>
<div class="flex flex-col items-start self-stretch gap-4 p-2 min-h-0 overflow-hidden">
<Show when={!sidebarProps.mobile}>
<Tooltip
class="shrink-0"
placement="right"
@@ -683,7 +689,7 @@ export default function Layout(props: ParentProps) {
<span class="text-icon-base text-12-medium">{command.keybind("sidebar.toggle")}</span>
</div>
}
inactive={layout.sidebar.opened()}
inactive={expanded()}
>
<Button
variant="ghost"
@@ -715,110 +721,162 @@ export default function Layout(props: ParentProps) {
</Show>
</Button>
</Tooltip>
<DragDropProvider
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
collisionDetector={closestCenter}
</Show>
<DragDropProvider
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onDragOver={handleDragOver}
collisionDetector={closestCenter}
>
<DragDropSensors />
<ConstrainDragXAxis />
<div
ref={(el) => {
if (!sidebarProps.mobile) scrollContainerRef = el
}}
class="w-full min-w-8 flex flex-col gap-2 min-h-0 overflow-y-auto no-scrollbar"
>
<DragDropSensors />
<ConstrainDragXAxis />
<div
ref={scrollContainerRef}
class="w-full min-w-8 flex flex-col gap-2 min-h-0 overflow-y-auto no-scrollbar"
>
<SortableProvider ids={layout.projects.list().map((p) => p.worktree)}>
<For each={layout.projects.list()}>{(project) => <SortableProject project={project} />}</For>
</SortableProvider>
</div>
<DragOverlay>
<ProjectDragOverlay />
</DragOverlay>
</DragDropProvider>
</div>
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
<Switch>
<Match when={!providers.paid().length && layout.sidebar.opened()}>
<div class="rounded-md bg-background-stronger shadow-xs-border-base">
<div class="p-3 flex flex-col gap-2">
<div class="text-12-medium text-text-strong">Getting started</div>
<div class="text-text-base">OpenCode includes free models so you can start immediately.</div>
<div class="text-text-base">Connect any provider to use models, inc. Claude, GPT, Gemini etc.</div>
</div>
<Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}>
<Button
class="flex w-full text-left justify-start text-12-medium text-text-strong stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-2.25 pb-px"
size="large"
icon="plus"
onClick={connectProvider}
>
<Show when={layout.sidebar.opened()}>Connect provider</Show>
</Button>
</Tooltip>
<SortableProvider ids={layout.projects.list().map((p) => p.worktree)}>
<For each={layout.projects.list()}>
{(project) => <SortableProject project={project} mobile={sidebarProps.mobile} />}
</For>
</SortableProvider>
</div>
<DragOverlay>
<ProjectDragOverlay />
</DragOverlay>
</DragDropProvider>
</div>
<div class="flex flex-col gap-1.5 self-stretch items-start shrink-0 px-2 py-3">
<Switch>
<Match when={!providers.paid().length && expanded()}>
<div class="rounded-md bg-background-stronger shadow-xs-border-base">
<div class="p-3 flex flex-col gap-2">
<div class="text-12-medium text-text-strong">Getting started</div>
<div class="text-text-base">OpenCode includes free models so you can start immediately.</div>
<div class="text-text-base">Connect any provider to use models, inc. Claude, GPT, Gemini etc.</div>
</div>
</Match>
<Match when={true}>
<Tooltip placement="right" value="Connect provider" inactive={layout.sidebar.opened()}>
<Tooltip placement="right" value="Connect provider" inactive={expanded()}>
<Button
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
variant="ghost"
class="flex w-full text-left justify-start text-12-medium text-text-strong stroke-[1.5px] rounded-lg rounded-t-none shadow-none border-t border-border-weak-base pl-2.25 pb-px"
size="large"
icon="plus"
onClick={connectProvider}
>
<Show when={layout.sidebar.opened()}>Connect provider</Show>
Connect provider
</Button>
</Tooltip>
</Match>
</Switch>
<Show when={platform.openDirectoryPickerDialog}>
<Tooltip
placement="right"
value={
<div class="flex items-center gap-2">
<span>Open project</span>
<span class="text-icon-base text-12-medium">{command.keybind("project.open")}</span>
</div>
}
inactive={layout.sidebar.opened()}
>
</div>
</Match>
<Match when={true}>
<Tooltip placement="right" value="Connect provider" inactive={expanded()}>
<Button
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
variant="ghost"
size="large"
icon="folder-add-left"
onClick={chooseProject}
icon="plus"
onClick={connectProvider}
>
<Show when={layout.sidebar.opened()}>Open project</Show>
<Show when={expanded()}>Connect provider</Show>
</Button>
</Tooltip>
</Show>
{/* <Tooltip placement="right" value="Settings" inactive={layout.sidebar.opened()}> */}
{/* <Button */}
{/* disabled */}
{/* class="flex w-full text-left justify-start text-12-medium text-text-base stroke-[1.5px] rounded-lg px-2" */}
{/* variant="ghost" */}
{/* size="large" */}
{/* icon="settings-gear" */}
{/* > */}
{/* <Show when={layout.sidebar.opened()}>Settings</Show> */}
{/* </Button> */}
{/* </Tooltip> */}
<Tooltip placement="right" value="Share feedback" inactive={layout.sidebar.opened()}>
</Match>
</Switch>
<Show when={platform.openDirectoryPickerDialog}>
<Tooltip
placement="right"
value={
<div class="flex items-center gap-2">
<span>Open project</span>
<Show when={!sidebarProps.mobile}>
<span class="text-icon-base text-12-medium">{command.keybind("project.open")}</span>
</Show>
</div>
}
inactive={expanded()}
>
<Button
as={"a"}
href="https://opencode.ai/desktop-feedback"
target="_blank"
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
variant="ghost"
size="large"
icon="bubble-5"
icon="folder-add-left"
onClick={chooseProject}
>
<Show when={layout.sidebar.opened()}>Share feedback</Show>
<Show when={expanded()}>Open project</Show>
</Button>
</Tooltip>
</Show>
<Tooltip placement="right" value="Share feedback" inactive={expanded()}>
<Button
as={"a"}
href="https://opencode.ai/desktop-feedback"
target="_blank"
class="flex w-full text-left justify-start text-text-base stroke-[1.5px] rounded-lg px-2"
variant="ghost"
size="large"
icon="bubble-5"
>
<Show when={expanded()}>Share feedback</Show>
</Button>
</Tooltip>
</div>
</>
)
}
return (
<div class="relative flex-1 min-h-0 flex flex-col">
<Header
navigateToProject={navigateToProject}
navigateToSession={navigateToSession}
onMobileMenuToggle={mobileSidebar.toggle}
/>
<div class="flex-1 min-h-0 flex">
<div
classList={{
"hidden xl:flex": true,
"relative @container w-12 pb-5 shrink-0 bg-background-base": true,
"flex-col gap-5.5 items-start self-stretch justify-between": true,
"border-r border-border-weak-base contain-strict": true,
}}
style={{ width: layout.sidebar.opened() ? `${layout.sidebar.width()}px` : undefined }}
>
<Show when={layout.sidebar.opened()}>
<ResizeHandle
direction="horizontal"
size={layout.sidebar.width()}
min={150}
max={window.innerWidth * 0.3}
collapseThreshold={80}
onResize={layout.sidebar.resize}
onCollapse={layout.sidebar.close}
/>
</Show>
<SidebarContent />
</div>
<div class="xl:hidden">
<div
classList={{
"fixed inset-0 bg-black/50 z-40 transition-opacity duration-200": true,
"opacity-100 pointer-events-auto": mobileSidebar.open(),
"opacity-0 pointer-events-none": !mobileSidebar.open(),
}}
onClick={(e) => {
if (e.target === e.currentTarget) mobileSidebar.hide()
}}
/>
<div
classList={{
"@container fixed inset-y-0 left-0 z-50 w-72 bg-background-base border-r border-border-weak-base flex flex-col gap-5.5 items-start self-stretch justify-between pt-12 pb-5 transition-transform duration-200 ease-out": true,
"translate-x-0": mobileSidebar.open(),
"-translate-x-full": !mobileSidebar.open(),
}}
onClick={(e) => e.stopPropagation()}
>
<SidebarContent mobile />
</div>
</div>
<main class="size-full overflow-x-hidden flex flex-col items-start contain-strict">{props.children}</main>
</div>
<Toast.Region />
@@ -12,6 +12,7 @@ import {
createRenderEffect,
batch,
} from "solid-js"
import { Dynamic } from "solid-js/web"
import { useLocal, type LocalFile } from "@/context/local"
import { createStore } from "solid-js/store"
@@ -26,6 +27,7 @@ import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Tabs } from "@opencode-ai/ui/tabs"
import { useCodeComponent } from "@opencode-ai/ui/context/code"
import { SessionTurn } from "@opencode-ai/ui/session-turn"
import { createAutoScroll } from "@opencode-ai/ui/hooks"
import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail"
import { SessionReview } from "@opencode-ai/ui/session-review"
import {
@@ -47,6 +49,7 @@ import { checksum } from "@opencode-ai/util/encode"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { DialogSelectFile } from "@/components/dialog-select-file"
import { DialogSelectModel } from "@/components/dialog-select-model"
import { DialogSelectMcp } from "@/components/dialog-select-mcp"
import { useCommand } from "@/context/command"
import { useNavigate, useParams } from "@solidjs/router"
import { UserMessage } from "@opencode-ai/sdk/v2"
@@ -54,6 +57,9 @@ import { useSDK } from "@/context/sdk"
import { usePrompt } from "@/context/prompt"
import { extractPromptFromParts } from "@/utils/prompt"
import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd"
import { StatusBar } from "@/components/status-bar"
import { SessionMcpIndicator } from "@/components/session-mcp-indicator"
import { SessionLspIndicator } from "@/components/session-lsp-indicator"
export default function Page() {
const layout = useLayout()
@@ -70,7 +76,6 @@ export default function Page() {
const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`)
const tabs = createMemo(() => layout.tabs(sessionKey()))
const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined))
const revertMessageID = createMemo(() => info()?.revert?.messageID)
const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : []))
@@ -79,7 +84,6 @@ export default function Page() {
.filter((m) => m.role === "user")
.sort((a, b) => a.id.localeCompare(b.id)),
)
// Visible user messages excludes reverted messages (those >= revertMessageID)
const visibleUserMessages = createMemo(() => {
const revert = revertMessageID()
if (!revert) return userMessages()
@@ -87,15 +91,24 @@ export default function Page() {
})
const lastUserMessage = createMemo(() => visibleUserMessages()?.at(-1))
const [messageStore, setMessageStore] = createStore<{ messageId?: string }>({})
const [store, setStore] = createStore({
clickTimer: undefined as number | undefined,
activeDraggable: undefined as string | undefined,
activeTerminalDraggable: undefined as string | undefined,
userInteracted: false,
stepsExpanded: true,
mobileStepsExpanded: {} as Record<string, boolean>,
messageId: undefined as string | undefined,
})
const activeMessage = createMemo(() => {
if (!messageStore.messageId) return lastUserMessage()
if (!store.messageId) return lastUserMessage()
// If the stored message is no longer visible (e.g., was reverted), fall back to last visible
const found = visibleUserMessages()?.find((m) => m.id === messageStore.messageId)
const found = visibleUserMessages()?.find((m) => m.id === store.messageId)
return found ?? lastUserMessage()
})
const setActiveMessage = (message: UserMessage | undefined) => {
setMessageStore("messageId", message?.id)
setStore("messageId", message?.id)
}
function navigateMessageByOffset(offset: number) {
@@ -119,13 +132,6 @@ export default function Page() {
const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : []))
const [store, setStore] = createStore({
clickTimer: undefined as number | undefined,
activeDraggable: undefined as string | undefined,
activeTerminalDraggable: undefined as string | undefined,
userInteracted: false,
stepsExpanded: true,
})
let inputRef!: HTMLDivElement
createEffect(() => {
@@ -146,7 +152,7 @@ export default function Page() {
() => visibleUserMessages().at(-1)?.id,
(lastId, prevLastId) => {
if (lastId && prevLastId && lastId > prevLastId) {
setMessageStore("messageId", undefined)
setStore("messageId", undefined)
}
},
{ defer: true },
@@ -219,6 +225,14 @@ export default function Page() {
slash: "terminal",
onSelect: () => layout.terminal.toggle(),
},
{
id: "review.toggle",
title: "Toggle review",
description: "Show or hide the review panel",
category: "View",
keybind: "mod+shift+r",
onSelect: () => layout.review.toggle(),
},
{
id: "terminal.new",
title: "New terminal",
@@ -264,6 +278,15 @@ export default function Page() {
slash: "model",
onSelect: () => dialog.show(() => <DialogSelectModel />),
},
{
id: "mcp.toggle",
title: "Toggle MCPs",
description: "Toggle MCPs",
category: "MCP",
keybind: "mod+;",
slash: "mcp",
onSelect: () => dialog.show(() => <DialogSelectMcp />),
},
{
id: "agent.cycle",
title: "Cycle agent",
@@ -531,74 +554,163 @@ export default function Page() {
)
}
const showTabs = createMemo(() => diffs().length > 0 || tabs().all().length > 0)
const showTabs = createMemo(() => layout.review.opened() && (diffs().length > 0 || tabs().all().length > 0))
const mobileWorking = createMemo(() => status().type !== "idle")
const mobileAutoScroll = createAutoScroll({
working: mobileWorking,
onUserInteracted: () => setStore("userInteracted", true),
})
const MobileTurns = () => (
<div
ref={mobileAutoScroll.scrollRef}
onScroll={mobileAutoScroll.handleScroll}
onClick={mobileAutoScroll.handleInteraction}
class="relative mt-2 min-w-0 w-full h-full overflow-y-auto no-scrollbar pb-12"
>
<div ref={mobileAutoScroll.contentRef} class="flex flex-col gap-45 items-start justify-start mt-4">
<For each={visibleUserMessages()}>
{(message) => (
<SessionTurn
sessionID={params.id!}
messageID={message.id}
stepsExpanded={store.mobileStepsExpanded[message.id] ?? false}
onStepsExpandedToggle={() => setStore("mobileStepsExpanded", message.id, (x) => !x)}
onUserInteracted={() => setStore("userInteracted", true)}
classes={{
root: "min-w-0 w-full relative",
content:
"flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]",
container: "px-4",
}}
/>
)}
</For>
</div>
</div>
)
const NewSessionView = () => (
<div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6">
<div class="text-20-medium text-text-weaker">New session</div>
<div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" />
<div class="text-12-medium text-text-weak">
{getDirectory(sync.data.path.directory)}
<span class="text-text-strong">{getFilename(sync.data.path.directory)}</span>
</div>
</div>
<Show when={sync.project}>
{(project) => (
<div class="flex justify-center items-center gap-3">
<Icon name="pencil-line" size="small" />
<div class="text-12-medium text-text-weak">
Last modified&nbsp;
<span class="text-text-strong">
{DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()}
</span>
</div>
</div>
)}
</Show>
</div>
)
const DesktopSessionContent = () => (
<Switch>
<Match when={params.id}>
<div class="flex items-start justify-start h-full min-h-0">
<SessionMessageRail
messages={visibleUserMessages()}
current={activeMessage()}
onMessageSelect={setActiveMessage}
wide={!showTabs()}
/>
<Show when={activeMessage()}>
<SessionTurn
sessionID={params.id!}
messageID={activeMessage()!.id}
stepsExpanded={store.stepsExpanded}
onStepsExpandedToggle={() => setStore("stepsExpanded", (x) => !x)}
onUserInteracted={() => setStore("userInteracted", true)}
classes={{
root: "pb-20 flex-1 min-w-0",
content: "pb-20",
container:
"w-full " +
(!showTabs() ? "max-w-200 mx-auto px-6" : visibleUserMessages().length > 1 ? "pr-6 pl-18" : "px-6"),
}}
/>
</Show>
</div>
</Match>
<Match when={true}>
<NewSessionView />
</Match>
</Switch>
)
return (
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
<div class="min-h-0 grow w-full flex">
{/* Session pane - always visible */}
<div class="md:hidden flex-1 min-h-0 flex flex-col bg-background-stronger">
<Switch>
<Match when={!params.id}>
<div class="flex-1 min-h-0 overflow-hidden">
<NewSessionView />
</div>
</Match>
<Match when={diffs().length > 0}>
<Tabs class="flex-1 min-h-0 flex flex-col pb-28">
<Tabs.List>
<Tabs.Trigger value="session" class="w-1/2" classes={{ button: "w-full" }}>
Session
</Tabs.Trigger>
<Tabs.Trigger value="review" class="w-1/2 !border-r-0" classes={{ button: "w-full" }}>
{diffs().length} Files Changed
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="session" class="flex-1 !overflow-hidden">
<MobileTurns />
</Tabs.Content>
<Tabs.Content forceMount value="review" class="flex-1 !overflow-hidden hidden data-[selected]:block">
<div class="relative h-full mt-6 overflow-y-auto no-scrollbar">
<SessionReview
diffs={diffs()}
classes={{
root: "pb-32",
header: "px-4",
container: "px-4",
}}
/>
</div>
</Tabs.Content>
</Tabs>
</Match>
<Match when={true}>
<div class="flex-1 min-h-0 overflow-hidden">
<MobileTurns />
</div>
</Match>
</Switch>
<div class="absolute inset-x-0 bottom-4 flex flex-col justify-center items-center z-50 px-4">
<div class="w-full">
<PromptInput
ref={(el) => {
inputRef = el
}}
/>
</div>
</div>
</div>
<div class="hidden md:flex min-h-0 grow w-full">
<div
class="@container relative shrink-0 py-3 flex flex-col gap-6 min-h-0 h-full bg-background-stronger"
style={{ width: showTabs() ? `${layout.session.width()}px` : "100%" }}
>
<div class="flex-1 min-h-0 overflow-hidden">
<Switch>
<Match when={params.id}>
<div class="flex items-start justify-start h-full min-h-0">
<SessionMessageRail
messages={visibleUserMessages()}
current={activeMessage()}
onMessageSelect={setActiveMessage}
wide={!showTabs()}
/>
<Show when={activeMessage()}>
<SessionTurn
sessionID={params.id!}
messageID={activeMessage()!.id}
stepsExpanded={store.stepsExpanded}
onStepsExpandedToggle={() => setStore("stepsExpanded", (x) => !x)}
onUserInteracted={() => setStore("userInteracted", true)}
classes={{
root: "pb-20 flex-1 min-w-0",
content: "pb-20",
container:
"w-full " +
(!showTabs()
? "max-w-200 mx-auto px-6"
: visibleUserMessages().length > 1
? "pr-6 pl-18"
: "px-6"),
}}
/>
</Show>
</div>
</Match>
<Match when={true}>
<div class="size-full flex flex-col pb-45 justify-end items-start gap-4 flex-[1_0_0] self-stretch max-w-200 mx-auto px-6">
<div class="text-20-medium text-text-weaker">New session</div>
<div class="flex justify-center items-center gap-3">
<Icon name="folder" size="small" />
<div class="text-12-medium text-text-weak">
{getDirectory(sync.data.path.directory)}
<span class="text-text-strong">{getFilename(sync.data.path.directory)}</span>
</div>
</div>
<Show when={sync.project}>
{(project) => (
<div class="flex justify-center items-center gap-3">
<Icon name="pencil-line" size="small" />
<div class="text-12-medium text-text-weak">
Last modified&nbsp;
<span class="text-text-strong">
{DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()}
</span>
</div>
</div>
)}
</Show>
</div>
</Match>
</Switch>
<DesktopSessionContent />
</div>
<div class="absolute inset-x-0 bottom-8 flex flex-col justify-center items-center z-50">
<div
@@ -625,7 +737,6 @@ export default function Page() {
</Show>
</div>
{/* Tabs pane - visible when there are diffs or file tabs */}
<Show when={showTabs()}>
<div class="relative flex-1 min-w-0 h-full border-l border-border-weak-base">
<DragDropProvider
@@ -683,7 +794,7 @@ export default function Page() {
</div>
<Show when={diffs().length}>
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden contain-strict">
<div class="relative pt-3 flex-1 min-h-0 overflow-hidden">
<div class="relative pt-2 flex-1 min-h-0 overflow-hidden">
<SessionReview
classes={{
root: "pb-40",
@@ -754,9 +865,10 @@ export default function Page() {
</div>
</Show>
</div>
<Show when={layout.terminal.opened()}>
<div
class="relative w-full flex flex-col shrink-0 border-t border-border-weak-base"
class="hidden md:flex relative w-full flex-col shrink-0 border-t border-border-weak-base"
style={{ height: `${layout.terminal.height()}px` }}
>
<ResizeHandle
@@ -822,6 +934,10 @@ export default function Page() {
</DragDropProvider>
</div>
</Show>
<StatusBar>
<SessionLspIndicator />
<SessionMcpIndicator />
</StatusBar>
</div>
)
}
@@ -1,5 +1,7 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"composite": true,
"target": "ESNext",
"module": "ESNext",
"skipLibCheck": true,
@@ -9,12 +11,16 @@
"jsx": "preserve",
"jsxImportSource": "solid-js",
"allowJs": true,
"resolveJsonModule": true,
"strict": true,
"noEmit": false,
"emitDeclarationOnly": true,
"outDir": "node_modules/.ts-dist",
"isolatedModules": true,
"noEmit": true,
"emitDeclarationOnly": false,
"outDir": "node_modules/.ts-dist"
"paths": {
"@/*": ["./src/*"]
}
},
"references": [{ "path": "../desktop" }],
"include": ["src"]
"include": ["src", "package.json"],
"exclude": ["dist", "ts-dist"]
}
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from "vite"
import desktopPlugin from "./vite"
export default defineConfig({
plugins: [desktopPlugin] as any,
server: {
host: "0.0.0.0",
allowedHosts: true,
port: 3000,
},
build: {
target: "esnext",
sourcemap: true,
},
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.0.184",
"version": "1.0.203",
"type": "module",
"scripts": {
"typecheck": "tsgo --noEmit",
+4 -4
View File
@@ -9,8 +9,8 @@ export const config = {
github: {
repoUrl: "https://github.com/sst/opencode",
starsFormatted: {
compact: "38K",
full: "38,000",
compact: "41K",
full: "41,000",
},
},
@@ -22,8 +22,8 @@ export const config = {
// Static stats (used on landing page)
stats: {
contributors: "400",
commits: "5,000",
contributors: "450",
commits: "6,000",
monthlyUsers: "400,000",
},
} as const
@@ -6,6 +6,7 @@ const assetNames: Record<string, string> = {
"darwin-x64-dmg": "opencode-desktop-darwin-x64.dmg",
"windows-x64-nsis": "opencode-desktop-windows-x64.exe",
"linux-x64-deb": "opencode-desktop-linux-amd64.deb",
"linux-x64-appimage": "opencode-desktop-linux-amd64.AppImage",
"linux-x64-rpm": "opencode-desktop-linux-x86_64.rpm",
} satisfies Record<DownloadPlatform, string>
@@ -244,6 +244,22 @@ export default function Download() {
Download
</a>
</div>
<div data-component="download-row">
<div data-component="download-info">
<span data-slot="icon">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M4.34591 22.7088C5.61167 22.86 7.03384 23.6799 8.22401 23.8247C9.42058 23.9758 9.79086 23.0098 9.79086 23.0098C9.79086 23.0098 11.1374 22.7088 12.553 22.6741C13.97 22.6344 15.3113 22.9688 15.3113 22.9688C15.3113 22.9688 15.5714 23.5646 16.057 23.8247C16.5426 24.0898 17.588 24.1257 18.258 23.4198C18.9293 22.7088 20.7204 21.8132 21.7261 21.2533C22.7382 20.6922 22.5525 19.8364 21.917 19.5763C21.2816 19.3163 20.7614 18.9063 20.8011 18.1196C20.8357 17.3394 20.24 16.8193 20.24 16.8193C20.24 16.8193 20.7614 15.1025 20.2759 13.6805C19.7903 12.2648 18.1889 9.98819 16.9577 8.27657C15.7266 6.55985 16.7719 4.5779 15.651 2.04503C14.5299 -0.491656 11.623 -0.341713 10.0562 0.739505C8.4893 1.8208 8.96968 4.50225 9.04526 5.77447C9.12084 7.04022 9.07985 7.94598 8.93509 8.27146C8.79033 8.60198 7.77951 9.80243 7.1082 10.8081C6.43818 11.819 5.95254 13.906 5.46187 14.7669C4.98142 15.6228 5.31711 16.403 5.31711 16.403C5.31711 16.403 4.98149 16.5182 4.71628 17.0795C4.45616 17.6342 3.93601 17.8993 2.99948 18.0801C2.06934 18.2709 2.06934 18.8705 2.29357 19.5419C2.51902 20.2119 2.29357 20.5873 2.03346 21.4431C1.77342 22.2988 3.07506 22.5588 4.34591 22.7088ZM17.5034 18.805C18.1683 19.0958 19.124 18.691 19.4149 18.4001C19.7045 18.1106 19.9094 17.6801 19.9094 17.6801C19.9094 17.6801 20.2002 17.8249 20.1707 18.2848C20.14 18.7512 20.3706 19.4161 20.8062 19.6467C21.2418 19.876 21.9067 20.1963 21.5621 20.5166C21.211 20.8369 19.2688 21.6183 18.6885 22.2282C18.1132 22.8341 17.3573 23.33 16.8974 23.1839C16.4324 23.0391 16.0262 22.4037 16.2261 21.4736C16.4324 20.5473 16.6066 19.5313 16.5771 18.951C16.5464 18.3707 16.4324 17.5892 16.5771 17.4738C16.7219 17.3598 16.9525 17.4148 16.9525 17.4148C16.9525 17.4148 16.8371 18.5156 17.5034 18.805ZM13.1885 3.12632C13.829 3.12632 14.3454 3.76175 14.3454 4.54324C14.3454 5.09798 14.0853 5.57844 13.7048 5.80906C13.6087 5.76937 13.5087 5.72449 13.3986 5.67832C13.6292 5.56434 13.7893 5.27352 13.7893 4.93783C13.7893 4.49844 13.519 4.13714 13.1794 4.13714C12.8489 4.13714 12.5734 4.49836 12.5734 4.93783C12.5734 5.09806 12.6132 5.25813 12.6785 5.38369C12.4786 5.30293 12.298 5.23383 12.1532 5.17874C12.0776 4.98781 12.0328 4.77257 12.0328 4.54331C12.0328 3.76183 12.5478 3.12632 13.1885 3.12632ZM11.6024 5.56823C11.9176 5.62331 12.7835 5.9987 13.1039 6.11398C13.4242 6.22415 13.7791 6.4291 13.7445 6.63413C13.7048 6.84548 13.5395 6.84548 13.1039 7.1107C12.6735 7.37082 11.7331 7.95116 11.432 7.99085C11.1322 8.03055 10.9618 7.86141 10.6415 7.65516C10.3211 7.44503 9.72039 6.95436 9.87147 6.69432C9.87147 6.69432 10.3416 6.33432 10.5467 6.14986C10.7517 5.95893 11.2821 5.50925 11.6024 5.56823ZM10.2213 3.35185C10.726 3.35185 11.1373 3.95268 11.1373 4.69318C11.1373 4.82773 11.1219 4.95322 11.0976 5.07878C10.972 5.11847 10.8466 5.18385 10.726 5.28891C10.6671 5.33889 10.612 5.38369 10.5621 5.43367C10.6415 5.28381 10.6722 5.06857 10.6363 4.84305C10.5672 4.44335 10.2968 4.14743 10.0316 4.18712C9.76511 4.232 9.60625 4.5984 9.67033 5.00327C9.74081 5.41325 10.0059 5.7091 10.2763 5.6643C10.2917 5.6592 10.3058 5.65409 10.3211 5.64891C10.1918 5.77447 10.0713 5.88464 9.94576 5.97432C9.58065 5.80388 9.31033 5.29402 9.31033 4.69318C9.31041 3.94758 9.71521 3.35185 10.2213 3.35185ZM7.40915 13.045C7.9293 12.2251 8.26492 10.4328 8.78507 9.83702C9.31041 9.24259 9.71521 7.97554 9.53075 7.41569C9.53075 7.41569 10.6517 8.75702 11.432 8.53668C12.2135 8.31116 13.97 7.00571 14.23 7.22994C14.4901 7.45539 16.727 12.375 16.9525 13.9419C17.178 15.5074 16.8026 16.7041 16.8026 16.7041C16.8026 16.7041 15.9468 16.4785 15.8366 16.9987C15.7264 17.524 15.7264 19.4265 15.7264 19.4265C15.7264 19.4265 14.5695 21.0279 12.7784 21.2931C10.9874 21.5532 10.0905 21.3636 10.0905 21.3636L9.08481 20.2118C9.08481 20.2118 9.86637 20.0965 9.75612 19.3112C9.64595 18.531 7.36801 17.4496 6.95803 16.4785C6.5482 15.5073 6.8826 13.8662 7.40915 13.045ZM2.9802 18.9204C3.06988 18.5361 4.23056 18.5361 4.67643 18.2657C5.12229 17.9954 5.21189 17.219 5.57197 17.0141C5.92679 16.804 6.58279 17.5496 6.85311 17.9697C7.11833 18.3797 8.13433 20.1721 8.54942 20.6179C8.96961 21.0676 9.35528 21.6633 9.23483 22.1988C9.12084 22.7343 8.48923 23.1251 8.48923 23.1251C7.92427 23.2993 6.34843 22.619 5.63231 22.3192C4.9162 22.0182 3.09433 21.9284 2.8599 21.6633C2.61906 21.393 2.97517 20.7972 3.06995 20.2322C3.15445 19.6609 2.8893 19.306 2.9802 18.9204Z"
fill="currentColor"
/>
</svg>
</span>
<span>Linux (.AppImage)</span>
</div>
<a href={getDownloadHref("linux-x64-appimage")} data-component="action-button">
Download
</a>
</div>
</div>
</section>
@@ -1 +1,4 @@
export type DownloadPlatform = `darwin-${"x64" | "aarch64"}-dmg` | "windows-x64-nsis" | `linux-x64-${"deb" | "rpm"}`
export type DownloadPlatform =
| `darwin-${"x64" | "aarch64"}-dmg`
| "windows-x64-nsis"
| `linux-x64-${"deb" | "rpm" | "appimage"}`
@@ -19,17 +19,23 @@ export function createDataDumper(sessionId: string, requestId: string, projectId
if (!data.modelName) return
const timestamp = new Date().toISOString().replace(/[^0-9]/g, "")
const year = timestamp.substring(0, 4)
const month = timestamp.substring(4, 6)
const day = timestamp.substring(6, 8)
const hour = timestamp.substring(8, 10)
const minute = timestamp.substring(10, 12)
const second = timestamp.substring(12, 14)
waitUntil(
Resource.ZenData.put(
`data/${data.modelName}/${sessionId}/${requestId}.json`,
Resource.ZenDataNew.put(
`data/${data.modelName}/${year}/${month}/${day}/${hour}/${minute}/${second}/${requestId}.json`,
JSON.stringify({ timestamp, ...data }),
),
)
waitUntil(
Resource.ZenData.put(
`meta/${data.modelName}/${timestamp}/${requestId}.json`,
Resource.ZenDataNew.put(
`meta/${data.modelName}/${sessionId}/${requestId}.json`,
JSON.stringify({ timestamp, ...metadata }),
),
)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.0.184",
"version": "1.0.203",
"private": true,
"type": "module",
"dependencies": {
@@ -17,14 +17,16 @@ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
const value6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1]
if (!value1) throw new Error("ZEN_MODELS1 not found")
if (!value2) throw new Error("ZEN_MODELS2 not found")
if (!value3) throw new Error("ZEN_MODELS3 not found")
if (!value4) throw new Error("ZEN_MODELS4 not found")
if (!value5) throw new Error("ZEN_MODELS5 not found")
if (!value6) throw new Error("ZEN_MODELS6 not found")
// validate value
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5))
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5 + value6))
// update the secret
await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}`
@@ -32,3 +34,4 @@ await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS3 ${value3} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS4 ${value4} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS5 ${value5} --stage ${stage}`
await $`bun sst secret set ZEN_MODELS6 ${value6} --stage ${stage}`
+4 -2
View File
@@ -17,14 +17,15 @@ const value2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=")[
const value3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const value4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
const value5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
const value6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1]
if (!value1) throw new Error("ZEN_MODELS1 not found")
if (!value2) throw new Error("ZEN_MODELS2 not found")
if (!value3) throw new Error("ZEN_MODELS3 not found")
if (!value4) throw new Error("ZEN_MODELS4 not found")
if (!value5) throw new Error("ZEN_MODELS5 not found")
if (!value6) throw new Error("ZEN_MODELS6 not found")
// validate value
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5))
ZenData.validate(JSON.parse(value1 + value2 + value3 + value4 + value5 + value6))
// update the secret
await $`bun sst secret set ZEN_MODELS1 ${value1}`
@@ -32,3 +33,4 @@ await $`bun sst secret set ZEN_MODELS2 ${value2}`
await $`bun sst secret set ZEN_MODELS3 ${value3}`
await $`bun sst secret set ZEN_MODELS4 ${value4}`
await $`bun sst secret set ZEN_MODELS5 ${value5}`
await $`bun sst secret set ZEN_MODELS6 ${value6}`
@@ -15,16 +15,20 @@ const oldValue2 = lines.find((line) => line.startsWith("ZEN_MODELS2"))?.split("=
const oldValue3 = lines.find((line) => line.startsWith("ZEN_MODELS3"))?.split("=")[1]
const oldValue4 = lines.find((line) => line.startsWith("ZEN_MODELS4"))?.split("=")[1]
const oldValue5 = lines.find((line) => line.startsWith("ZEN_MODELS5"))?.split("=")[1]
const oldValue6 = lines.find((line) => line.startsWith("ZEN_MODELS6"))?.split("=")[1]
if (!oldValue1) throw new Error("ZEN_MODELS1 not found")
if (!oldValue2) throw new Error("ZEN_MODELS2 not found")
if (!oldValue3) throw new Error("ZEN_MODELS3 not found")
if (!oldValue4) throw new Error("ZEN_MODELS4 not found")
if (!oldValue5) throw new Error("ZEN_MODELS5 not found")
if (!oldValue6) throw new Error("ZEN_MODELS6 not found")
// store the prettified json to a temp file
const filename = `models-${Date.now()}.json`
const tempFile = Bun.file(path.join(os.tmpdir(), filename))
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5), null, 2))
await tempFile.write(
JSON.stringify(JSON.parse(oldValue1 + oldValue2 + oldValue3 + oldValue4 + oldValue5 + oldValue6), null, 2),
)
console.log("tempFile", tempFile.name)
// open temp file in vim and read the file on close
@@ -33,15 +37,17 @@ const newValue = JSON.stringify(JSON.parse(await tempFile.text()))
ZenData.validate(JSON.parse(newValue))
// update the secret
const chunk = Math.ceil(newValue.length / 5)
const chunk = Math.ceil(newValue.length / 6)
const newValue1 = newValue.slice(0, chunk)
const newValue2 = newValue.slice(chunk, chunk * 2)
const newValue3 = newValue.slice(chunk * 2, chunk * 3)
const newValue4 = newValue.slice(chunk * 3, chunk * 4)
const newValue5 = newValue.slice(chunk * 4)
const newValue5 = newValue.slice(chunk * 4, chunk * 5)
const newValue6 = newValue.slice(chunk * 5)
await $`bun sst secret set ZEN_MODELS1 ${newValue1}`
await $`bun sst secret set ZEN_MODELS2 ${newValue2}`
await $`bun sst secret set ZEN_MODELS3 ${newValue3}`
await $`bun sst secret set ZEN_MODELS4 ${newValue4}`
await $`bun sst secret set ZEN_MODELS5 ${newValue5}`
await $`bun sst secret set ZEN_MODELS6 ${newValue6}`
+2 -1
View File
@@ -72,7 +72,8 @@ export namespace ZenData {
Resource.ZEN_MODELS2.value +
Resource.ZEN_MODELS3.value +
Resource.ZEN_MODELS4.value +
Resource.ZEN_MODELS5.value,
Resource.ZEN_MODELS5.value +
Resource.ZEN_MODELS6.value,
)
return ModelsSchema.parse(json)
})

Some files were not shown because too many files have changed in this diff Show More