Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
5fee541
feat(ui): make file references clickable in session output
anduimagui Feb 26, 2026
d7b2a15
fix(ui): restore singular project path helper name
anduimagui Feb 27, 2026
8405c70
refactor(ui): extract markdown file reference parser
anduimagui Mar 1, 2026
1ac4a1a
fix(ui): make tool file links keyboard-accessible
anduimagui Mar 1, 2026
5e9dd5d
feat(ui): open clicked file refs in desktop default apps
anduimagui Mar 1, 2026
da34dfa
fix(app): apply file-link line targets in in-app fallback
anduimagui Mar 1, 2026
90345c5
tweak(ui): shimmering titles and animated counts
kitlangton Feb 28, 2026
33b6ba6
refactor(ui): simplify text shimmer API and story controls
kitlangton Feb 28, 2026
0ad0d84
chore(storybook): simplify animated count story locales
kitlangton Feb 28, 2026
9d53b3a
wip: checkpoint todo panel + odometer motion work
kitlangton Mar 2, 2026
14f88ae
feat(ui): spring animations for composer mode toggle and tray transit…
kitlangton Mar 2, 2026
55ab1f0
feat(ui): spring width animation for shell submessage, replace TextOd…
kitlangton Mar 2, 2026
bc56419
fix(ui): keep TextShimmer mounted for smooth transitions, move shell …
kitlangton Mar 2, 2026
b939299
wip: checkpoint tool-call motion and timeline animation fixes
kitlangton Mar 2, 2026
eadd42b
feat(ui): add gradient wipe reveal for tool metadata
kitlangton Mar 2, 2026
474e2e5
fix(ui): smooth timeline motion and auto-scroll during tool updates
kitlangton Mar 3, 2026
21b6a5f
fix(ui): reduce height animation jitter in GrowBox and thinking box
kitlangton Mar 3, 2026
c96a3b1
fix(ui): remove legacy shell submessage width style
kitlangton Mar 3, 2026
1b9ca3d
fix(ui): eliminate auto-scroll jitter and add compositor hints
kitlangton Mar 3, 2026
28538bc
fix(ui): use column-reverse for jitter-free bottom-anchored scrolling
kitlangton Mar 3, 2026
49f55ae
fix(ui): fix user message animation and smooth BasicTool toggle
kitlangton Mar 3, 2026
969f434
fix(ui): restore patch collapse behavior and add user message entry s…
kitlangton Mar 3, 2026
6f8f0d1
fix(ui): fix backfill scroll preservation for column-reverse
kitlangton Mar 3, 2026
0ce963b
fix(ui): keep apply_patch tool collapsed by default
kitlangton Mar 3, 2026
55762b2
fix(ui): restore in-flow user copy row spacing
kitlangton Mar 3, 2026
418ecc7
fix(ui): align defer behavior for animated tools
kitlangton Mar 3, 2026
8851c61
fix(ui): speed up nested collapsible panels
kitlangton Mar 3, 2026
45eb4be
refactor(ui): standardize tool call variants
kitlangton Mar 3, 2026
9cba8f9
fix(ui): inline turn diff summary under assistant text
kitlangton Mar 3, 2026
4e05d34
fix(app/ui): resolve rebase regressions
kitlangton Mar 3, 2026
83de487
fix(app): restore staging render path and nested scroll boundary checks
kitlangton Mar 3, 2026
a16d77b
fix(app/ui/opencode): align tool detail reveal timing and turn animat…
kitlangton Mar 3, 2026
7add2e6
chore(ui): add skill label translations
kitlangton Mar 3, 2026
a029609
fix(ui): preserve SolidJS reactivity and clean up minor issues
kitlangton Mar 3, 2026
7e5477c
fix(ui): close tool-output css block for app build
kitlangton Mar 3, 2026
24bcc96
fix(ui): unify turn row heights and remove inter-part gaps
kitlangton Mar 3, 2026
7ebf1c3
fix(ui): keep interrupted copy row left-aligned
kitlangton Mar 3, 2026
82a29ba
fix(ui): add top padding to text parts after tool calls
kitlangton Mar 3, 2026
abe4a25
fix(ui): simplify metaTail to return stamp directly
kitlangton Mar 3, 2026
4ef8ae9
chore(ui): clean up tool-call icon experiment
kitlangton Mar 3, 2026
bb8b850
fix(ui): animate immediate tool subtitles on first reveal
kitlangton Mar 3, 2026
bffda37
fix(session): stop gracefully after deleted session writes
kitlangton Mar 3, 2026
0461efa
fix(session): stabilize e2e teardown races
kitlangton Mar 3, 2026
9939e73
fix(ui): restore queued message feature and clean up dead code
kitlangton Mar 4, 2026
2c39a7f
refactor(ui): remove self-detection from SessionTurn
kitlangton Mar 4, 2026
986a6b1
chore(ui): remove trivial indirection and dead code
kitlangton Mar 4, 2026
c0fd605
refactor(ui): rename animated → springContent in ToolCallPanel
kitlangton Mar 4, 2026
2dcb54c
refactor(ui): replace manual thinking animation with GrowBox
kitlangton Mar 4, 2026
8efadf9
chore(ui): slow down tool-count animation durations
kitlangton Mar 4, 2026
c2796fa
perf(ui): use overflow: clip instead of hidden, drop stale will-change
kitlangton Mar 4, 2026
4c0f6cb
refactor(ui): deduplicate animation utils and shared helpers
kitlangton Mar 4, 2026
868fc18
refactor(app): deduplicate spring fade styles and remove redundant code
kitlangton Mar 4, 2026
6455ccb
refactor(app): extract useElementHeight hook and clean up todo dock
kitlangton Mar 4, 2026
7fe6152
fix(ui): cache reduced-motion query and clean up deferRender timers
kitlangton Mar 4, 2026
1abb7ef
fix(app): prevent session header flash on first message
kitlangton Mar 4, 2026
3032fe4
fix(ui): keep automatic context collapse on grow timing
kitlangton Mar 4, 2026
302f4bd
fix(ui): fix inconsistent dot separator spacing in user message meta
kitlangton Mar 4, 2026
92534bb
refactor(ui): remove assistant text part copy/meta UI and use NBSPs i…
kitlangton Mar 4, 2026
b7be878
fix(ui): smooth session turn handoff to hover meta
kitlangton Mar 4, 2026
6082393
fix(ui): keep Agent title fixed, animate type label in separately
kitlangton Mar 4, 2026
a2fb4ca
fix(ui): speed up text shimmer and remove idle glow pulse
kitlangton Mar 4, 2026
6784f91
refactor(ui): remove deprecated message wrapper and unused tool title…
kitlangton Mar 4, 2026
3a654cc
fix(session): preserve manual expand anchor near bottom
kitlangton Mar 4, 2026
1fca726
fix(ui): smooth streaming text edge fade lifecycle
kitlangton Mar 4, 2026
1152276
refactor(util): consolidate `same` array comparator into @opencode-ai…
kitlangton Mar 4, 2026
2b5a95b
refactor(app): deduplicate errorMessage into shared import from layou…
kitlangton Mar 4, 2026
6e123c5
refactor(app): consolidate isEditableTarget into utils/dom
kitlangton Mar 4, 2026
7de99b6
feat(ui): context tool group with rolling results, expandable list, a…
kitlangton Mar 5, 2026
37d3ad0
fix(ui): stabilize rolling results identity and improve animations
kitlangton Mar 5, 2026
e67bedb
fix(ui): adjust tool collapsible trigger height and sticky offset
kitlangton Mar 5, 2026
78cd0c9
fix(ui): fix AnimatedNumber clipping direction and shrinking gap
kitlangton Mar 5, 2026
f03910c
feat(ui): add ComposerIsland dynamic island composer prototype
kitlangton Mar 5, 2026
51b2ca6
Apply PR #12022: feat: update tui model dialog to utilize model famil…
opencode-agent[bot] Mar 5, 2026
94d7db9
Apply PR #12633: feat(tui): add auto-accept mode for permission requests
opencode-agent[bot] Mar 5, 2026
196d3ac
Apply PR #14307: fix: use parentID matching instead of ID ordering fo…
opencode-agent[bot] Mar 5, 2026
0d20e93
Apply PR #14471: [DO NOT MERGE]: beta badge for desktop app
opencode-agent[bot] Mar 5, 2026
f96f636
Apply PR #14677: feat: add experimental hashline edit mode with dual-…
opencode-agent[bot] Mar 5, 2026
93e4565
Apply PR #15012: refactor(opencode): replace Bun.which with npm which
opencode-agent[bot] Mar 5, 2026
aabb71b
Apply PR #15013: refactor: replace Bun.sleep with node timers
opencode-agent[bot] Mar 5, 2026
37e1906
Apply PR #15250: feat(app): view archived sessions & unarchive
opencode-agent[bot] Mar 5, 2026
38b0638
Apply PR #15266: feat(app): changelog with PR links
opencode-agent[bot] Mar 5, 2026
2da0342
Apply PR #15269: feat(ui): make edited file references clickable in d…
opencode-agent[bot] Mar 5, 2026
fc03e07
Apply PR #15487: core: make account login upgrades safe while adding …
opencode-agent[bot] Mar 5, 2026
6231cf5
Apply PR #15697: tweak(ui): make questions popup collapsible
opencode-agent[bot] Mar 5, 2026
b65d093
Apply PR #15698: tweak(ui): add sidebar fade mask under new buttons
opencode-agent[bot] Mar 5, 2026
c812347
Apply PR #15863: ANIMATION RETRIBUTION II: PROLAPSED OUBLIETTE
opencode-agent[bot] Mar 5, 2026
4fbdc9e
Apply PR #15951: fix(tui): use WinRT clipboard API on Windows
opencode-agent[bot] Mar 5, 2026
3301ed9
chore: update nix node_modules hashes
opencode-agent[bot] Mar 5, 2026
6267b97
Fix overlay problem in first user message.
Eric-Guo Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,7 @@ const table = sqliteTable("session", {
- Avoid mocks as much as possible
- Test actual implementation, do not duplicate logic into tests
- Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs like `packages/opencode`.

## Type Checking

- Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly.
956 changes: 529 additions & 427 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Context as GitHubContext } from "@actions/github/lib/context"
import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types"
import { createOpencodeClient } from "@opencode-ai/sdk"
import { spawn } from "node:child_process"
import { setTimeout as sleep } from "node:timers/promises"

type GitHubAuthor = {
login: string
Expand Down Expand Up @@ -281,7 +282,7 @@ async function assertOpencodeConnected() {
connected = true
break
} catch (e) {}
await Bun.sleep(300)
await sleep(300)
} while (retry++ < 30)

if (!connected) {
Expand Down
8 changes: 4 additions & 4 deletions nix/hashes.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-ZmxeRNy2chc9py4m1iW6B+c/NSccMnVZ0lfni/EMdHw=",
"aarch64-linux": "sha256-R+1mxsmAQicerN8ixVy0ff6V8bZ4GH18MHpihvWnaTg=",
"aarch64-darwin": "sha256-m+QT20ohlqo9e86qXu67eKthZm6VDRLwlqJ9CNlEV+0=",
"x86_64-darwin": "sha256-4GeNPyTT2Hq4rxHGSON23ul5Ud3yFGE0QUVsB03Gidc="
"x86_64-linux": "sha256-ZVmMw/nArbQtdB1tRrLHJFsciDAf88/SnUd90VVADh0=",
"aarch64-linux": "sha256-9mO8Oddy2knW1grGiBYvRiltVz9IN03rigo2tyyPwaU=",
"aarch64-darwin": "sha256-c7qucWy4aBytwt5M9nnGOcn9XolGCdHpSNjtcPXx/bk=",
"x86_64-darwin": "sha256-IeSD87LihSQWeC34YPpD927WbYzS+ZHe1e67mDcfRzM="
}
}
50 changes: 50 additions & 0 deletions packages/app/src/api/releases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { Platform } from "@/context/platform"

const REPO = "anomalyco/opencode"
const GITHUB_API_URL = `https://api.github.com/repos/${REPO}/releases`
const PER_PAGE = 30
const CACHE_TTL = 1000 * 60 * 30
const CACHE_KEY = "opencode.releases"

type Release = {
tag: string
body: string
date: string
}

function loadCache() {
const raw = localStorage.getItem(CACHE_KEY)
return raw ? JSON.parse(raw) : null
}

function saveCache(data: { releases: Release[]; timestamp: number }) {
localStorage.setItem(CACHE_KEY, JSON.stringify(data))
}

export async function fetchReleases(platform: Platform): Promise<{ releases: Release[] }> {
const now = Date.now()
const cached = loadCache()

if (cached && now - cached.timestamp < CACHE_TTL) {
return { releases: cached.releases }
}

const fetcher = platform.fetch ?? fetch
const res = await fetcher(`${GITHUB_API_URL}?per_page=${PER_PAGE}`, {
headers: { Accept: "application/vnd.github.v3+json" },
}).then((r) => (r.ok ? r.json() : Promise.reject(new Error("Failed to load"))))

const releases = (Array.isArray(res) ? res : []).map((r) => ({
tag: r.tag_name ?? "Unknown",
body: (r.body ?? "")
.replace(/#(\d+)/g, (_: string, id: string) => `[#${id}](https://github.com/anomalyco/opencode/pull/${id})`)
.replace(/@([a-zA-Z0-9_-]+)/g, (_: string, u: string) => `[@${u}](https://github.com/${u})`),
date: r.published_at ?? "",
}))

saveCache({ releases, timestamp: now })

return { releases }
}

export type { Release }
150 changes: 150 additions & 0 deletions packages/app/src/components/dialog-changelog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
.dialog-changelog {
min-height: 500px;
display: flex;
flex-direction: column;
}

.dialog-changelog [data-slot="dialog-body"] {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}

.dialog-changelog-list {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 0;
}

.dialog-changelog-list [data-slot="list-scroll"] {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--border-weak-base) transparent;
}

.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar {
width: 10px;
height: 10px;
}

.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-track {
background: transparent;
border-radius: 5px;
}

.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-thumb {
background: var(--border-weak-base);
border-radius: 5px;
border: 3px solid transparent;
background-clip: padding-box;
}

.dialog-changelog-list [data-slot="list-scroll"]::-webkit-scrollbar-thumb:hover {
background: var(--border-weak-base);
}

.dialog-changelog-header {
padding: 8px 12px 8px 8px;
display: flex;
align-items: baseline;
gap: 8px;
position: sticky;
top: 0;
z-index: 10;
background: var(--surface-raised-stronger-non-alpha);
}

.dialog-changelog-header::after {
content: "";
position: absolute;
top: 100%;
left: 0;
right: 0;
height: 16px;
background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent);
pointer-events: none;
opacity: 0;
transition: opacity 0.15s ease;
}

.dialog-changelog-header[data-stuck="true"]::after {
opacity: 1;
}



.dialog-changelog-version {
font-size: 20px;
font-weight: 600;
}

.dialog-changelog-date {
font-size: 12px;
font-weight: 400;
color: var(--text-weak);
}

.dialog-changelog-list [data-slot="list-item"] {
margin-bottom: 32px;
padding: 0;
border: none;
background: transparent;
cursor: default;
display: block;
text-align: left;
}

.dialog-changelog-list [data-slot="list-item"]:hover {
background: transparent;
}

.dialog-changelog-list [data-slot="list-item"]:focus {
outline: none;
}

.dialog-changelog-list [data-slot="list-item"]:focus-visible {
outline: 2px solid var(--focus-base);
outline-offset: 2px;
}

.dialog-changelog-content {
padding: 0 8px 24px;
}

.dialog-changelog-markdown h2 {
border-bottom: 1px solid var(--border-weak-base);
padding-bottom: 4px;
margin: 32px 0 12px 0;
font-size: 14px;
font-weight: 500;
text-transform: capitalize;
}

.dialog-changelog-markdown h2:first-child {
margin-top: 16px;
}

.dialog-changelog-markdown a.external-link {
color: var(--text-interactive-base);
font-weight: 500;
}

.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/pull/"],
.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/issues/"],
.dialog-changelog-markdown a.external-link[href^="https://github.com/"]
{
border-radius: 3px;
padding: 0 2px;
}

.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/pull/"]:hover,
.dialog-changelog-markdown a.external-link[href^="https://github.com/anomalyco/opencode/issues/"]:hover,
.dialog-changelog-markdown a.external-link[href^="https://github.com/"]:hover
{
background: var(--surface-weak-base);
}
43 changes: 43 additions & 0 deletions packages/app/src/components/dialog-changelog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { createResource, Suspense, ErrorBoundary, Show } from "solid-js"
import { Dialog } from "@opencode-ai/ui/dialog"
import { DataProvider } from "@opencode-ai/ui/context"
import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { fetchReleases } from "@/api/releases"
import { ReleaseList } from "@/components/release-list"

export function DialogChangelog() {
const language = useLanguage()
const platform = usePlatform()
const [data] = createResource(() => fetchReleases(platform))

return (
<Dialog size="x-large" transition title="Changelog">
<DataProvider data={{ session: [], session_status: {}, session_diff: {}, message: {}, part: {} }} directory="">
<div class="flex-1 min-h-0 flex flex-col">
<ErrorBoundary
fallback={(e) => (
<p class="text-text-weak p-6">
{e instanceof Error ? e.message : "Failed to load changelog"}
</p>
)}
>
<Suspense fallback={<p class="text-text-weak p-6">{language.t("common.loading")}...</p>}>
<Show
when={(data()?.releases.length ?? 0) > 0}
fallback={<p class="text-text-weak p-6">{language.t("common.noReleasesFound")}</p>}
>
<ReleaseList
releases={data()!.releases}
hasMore={false}
loadingMore={false}
onLoadMore={() => {}}
/>
</Show>
</Suspense>
</ErrorBoundary>
</div>
</DataProvider>
</Dialog>
)
}
2 changes: 1 addition & 1 deletion packages/app/src/components/dialog-select-file.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,4 +459,4 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil
</List>
</Dialog>
)
}
}
28 changes: 28 additions & 0 deletions packages/app/src/components/dialog-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@ import { Component } from "solid-js"
import { Dialog } from "@opencode-ai/ui/dialog"
import { Tabs } from "@opencode-ai/ui/tabs"
import { Icon } from "@opencode-ai/ui/icon"
import { Button } from "@opencode-ai/ui/button"
import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { SettingsGeneral } from "./settings-general"
import { SettingsKeybinds } from "./settings-keybinds"
import { SettingsProviders } from "./settings-providers"
import { SettingsModels } from "./settings-models"
import { SettingsArchive } from "./settings-archive"
import { DialogChangelog } from "@/components/dialog-changelog"

export const DialogSettings: Component = () => {
const language = useLanguage()
const platform = usePlatform()
const dialog = useDialog()

function handleShowChangelog() {
dialog.show(() => <DialogChangelog />)
}

return (
<Dialog size="x-large" transition>
Expand Down Expand Up @@ -47,11 +56,27 @@ export const DialogSettings: Component = () => {
</Tabs.Trigger>
</div>
</div>

<div class="flex flex-col gap-1.5">
<Tabs.SectionTitle>{language.t("settings.section.data")}</Tabs.SectionTitle>
<div class="flex flex-col gap-1.5 w-full">
<Tabs.Trigger value="archive">
<Icon name="archive" />
{language.t("settings.archive.title")}
</Tabs.Trigger>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-1 pl-1 py-1 text-12-medium text-text-weak">
<span>{language.t("app.name.desktop")}</span>
<span class="text-11-regular">v{platform.version}</span>
<button
class="text-11-regular text-text-weak hover:text-text-base self-start"
onClick={handleShowChangelog}
>
Changelog
</button>
</div>
</div>
</Tabs.List>
Expand All @@ -67,6 +92,9 @@ export const DialogSettings: Component = () => {
<Tabs.Content value="models" class="no-scrollbar">
<SettingsModels />
</Tabs.Content>
<Tabs.Content value="archive" class="no-scrollbar">
<SettingsArchive />
</Tabs.Content>
</Tabs>
</Dialog>
)
Expand Down
Loading
Loading