Skip to content

chore: merge upstream anomalyco/opencode dev - #6

Closed
DevFlex-AI wants to merge 16 commits into
devfrom
merge-upstream-dev
Closed

chore: merge upstream anomalyco/opencode dev#6
DevFlex-AI wants to merge 16 commits into
devfrom
merge-upstream-dev

Conversation

@DevFlex-AI

@DevFlex-AI DevFlex-AI commented Jul 30, 2026

Copy link
Copy Markdown

Issue for this PR

Closes anomalyco#7 (supersedes #5)

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

Upstream sync (chore): merges anomalyco/opencode:dev into dev.

What does this PR do?

Resolves the merge conflicts blocking #5 by merging anomalyco/opencode:dev (v1.18.10, 8c38d26) into dev with conflicts resolved. That PR's head lives in the upstream repo, so the conflicts can't be fixed there; merging this supersedes it and pull[bot] should close it once dev contains the upstream commits.

Conflict resolutions follow the precedent from previous upstream merges (e.g. f6697a3):

  • packages/opencode/package.json and bun.lock: kept the fork identity (@bolt-builder/bolt-cli, version 0.0.1, private: false) over upstream's opencode@1.18.10. The lockfile is regenerated from upstream's resolutions (git show 8c38d26:bun.lock + bun install --lockfile-only) so external pins match upstream; this fixes the storybook build failure caused by the fork's lock drifting to storybook-solidjs-vite@10.6.0 (upstream pins 10.1.1).
  • sdks/vscode/package.json: kept Bolt branding, took upstream's version bump to 1.18.10.
  • nix/hashes.json: took upstream's hashes; the nix-hashes workflow recomputes them automatically on push to dev since bun.lock changed.

How did you verify your code works?

The conflict resolutions mirror the ones used in previous upstream merges (e.g. f6697a3), and CI on this PR validates the merged tree. No fork-local code is changed beyond resolving the conflicts.

Screenshots / recordings

Not a UI change in the fork; all UI changes come verbatim from upstream commits.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

Summary by CodeRabbit

  • New Features

    • Improved duplicate attachment detection, with localized notifications when a file is uploaded more than once.
    • Added smoother attachment and sidebar scroll-fade visuals.
    • Enhanced toast notifications with better accessibility, actions, animations, and responsive layouts.
    • Added safer restoration of saved tabs and improved session-review keyboard navigation.
  • Bug Fixes

    • Prevented duplicate clipboard and file attachments from being added repeatedly.
    • Kept the new-session button available while a session is being created.
    • Improved toast dismissal consistency.
  • Chores

    • Updated model catalog sources and released version 1.18.10.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bolt-cli-app Ready Ready Preview Jul 30, 2026 3:53pm

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d2f4787e-1dcf-4afc-8599-1677fd9941d1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This release updates package versions and model endpoints, migrates V2 toasts to Solid Sonner, adds attachment deduplication and localized feedback, centralizes tab migration, and updates session-review navigation and sidebar behavior.

Changes

Release and model configuration

Layer / File(s) Summary
Deployment and model source updates
infra/console.ts, nix/hashes.json, packages/cli/script/generate.ts, packages/core/src/models-dev.ts, packages/llm/script/recording-cost-report.ts, packages/stats/app/src/routes/model-catalog.ts, packages/ui/vite.config.ts
Deployment worker flags, dependency hashes, and model catalog, pricing, cache, and icon endpoints are updated to use the new model host.
Package release metadata
packages/*/package.json, sdks/vscode/package.json
Package and extension versions are bumped from 1.18.9 to 1.18.10.

Toast system migration

Layer / File(s) Summary
Solid Sonner toast runtime
package.json, packages/ui/package.json, packages/ui/src/v2/components/toast-v2.tsx
V2 toasts use Solid Sonner with active-toast tracking, custom rendering, persistence, actions, dismissal, and accessibility synchronization.
Toast rendering and accessibility presentation
packages/ui/src/v2/components/toast-v2.css, packages/ui/src/v2/components/toast-v2.stories.tsx
Toast layout, selectors, transitions, responsive behavior, reduced-motion handling, and accessibility documentation are updated.
Application dismissal integration and validation
packages/app/src/utils/toast.tsx, packages/app/src/pages/layout.tsx, packages/app/test-browser/toast-owner.test.ts
Toast dismissal routes through the appropriate V1 or V2 implementation, with tests for coalescing and recreation.

Attachment deduplication

Layer / File(s) Summary
Attachment duplicate detection
packages/session-ui/src/v2/components/prompt-input/attachments.ts
Attachment insertion compares image content and source identity, reports duplicates through a callback, and applies clipboard-specific matching.
Attachment feedback and presentation
packages/app/src/components/prompt-input-v2.tsx, packages/app/src/i18n/*, packages/session-ui/src/v2/components/prompt-input/*
Duplicate feedback is localized, and prompt attachment clipping, slots, overlays, and scroll-linked fades are added.
Attachment duplicate tests
packages/app/test-browser/prompt-attachments.test.ts
Browser tests cover repeated clipboard images, repeated files, edited filenames, and duplicate callback behavior.

Tab state migration

Layer / File(s) Summary
Tab migration contract and integration
packages/app/src/context/tab-migration.ts, packages/app/src/context/tabs.tsx, packages/app/src/context/tabs.test.ts
Persisted session and draft tabs are validated, normalized, assigned fallback servers, and tested through a shared migration helper.

Session review navigation

Layer / File(s) Summary
File navigation behavior
packages/session-ui/src/v2/components/session-review-v2.tsx
File cycling safely wraps around, arrow keys replace angle-bracket shortcuts, and toolbar hints are updated.
Sidebar presentation state
packages/session-ui/src/v2/components/session-review-v2.css, packages/session-ui/src/v2/components/session-review-v2.tsx
Sidebar spacing, scroll fades, toggle styling, and expanded-state markup are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: brendonovich, kommander

Poem

A rabbit hops through toast and tabs,
With glowing files and model maps.
Duplicates thump, then fade away,
Arrow keys guide the review today.
“Ship the release!” the bunny sings,
While Sonner spreads its silky wings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly reflects the main change: merging upstream dev into the fork.
Description check ✅ Passed The PR description follows the template and covers the issue, change type, summary, verification, screenshots, and checklist.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch merge-upstream-dev

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

1 similar comment
@github-actions

Copy link
Copy Markdown

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown

The following comment was made by an LLM, it may be inaccurate:

@github-actions

Copy link
Copy Markdown

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@blacksmith-sh

blacksmith-sh Bot commented Jul 30, 2026

Copy link
Copy Markdown

Found 4 test failures on Blacksmith runners:

Failures

Test View Logs
POST /session/{sessionID}/command session.command/
command session.command
View Logs
POST /session/{sessionID}/init session.init/
init session.init
View Logs
POST /session/{sessionID}/message session.prompt/
message session.prompt
View Logs
POST /session/{sessionID}/prompt_async session.prompt_async/
prompt_async session.prompt_async
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Co-authored-by: Codesmith <codesmith-bot@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/context/tab-migration.ts`:
- Around line 8-9: Update the server selection logic in the tab migration to use
fallback when tab.server is absent or not a string, rather than returning an
empty tab list for invalid persisted values. Preserve valid string server
values, and add a regression case covering a legacy tab with server: null to
verify it is retained with fallback.

In `@packages/app/src/i18n/uk.ts`:
- Line 302: Update the duplicate-attachment title translations to use attachment
terminology instead of upload terminology: in packages/app/src/i18n/uk.ts lines
302-302, use “Це вкладення вже додано” or equivalent; in
packages/app/src/i18n/zh.ts lines 319-319, use “此附件已添加” or equivalent.

In `@packages/app/src/utils/toast.tsx`:
- Around line 36-41: Update the toast ID tracking around showToastV2 and
dismissToast so each issued ID records whether it belongs to the v2 registry,
using the existing v2Ids mechanism. Make dismissToast consult and remove that
per-ID origin record before routing dismissal, falling back to the legacy
registry for IDs not recorded as v2, rather than reading the mutable v2 flag.

In `@packages/core/src/models-dev.ts`:
- Around line 154-157: Update the cache path construction in the model-loading
flow around Flag.OPENCODE_MODELS_URL and loadFromDisk so the default opencode.ai
source no longer reuses the legacy models.json cache. Use a
source/version-specific filename or explicitly migrate and invalidate the legacy
cache, preserving existing cache behavior for current sources; add a test
covering the upgrade from the models.dev cache.

In `@packages/session-ui/src/v2/components/session-review-v2.tsx`:
- Around line 167-177: Update canCycle in the session review navigation logic to
return true only when props.files contains more than one file. Preserve the
existing prev, next, and cycle behavior for multi-file lists while disabling
navigation for empty and single-file lists.

In `@packages/ui/src/v2/components/toast-v2.tsx`:
- Line 11: Update the ToastV2 component’s swipeDirections handling: either
forward the extracted swipeDirections value to the underlying toaster/region or
stop extracting it so it remains in rest. Remove the hardcoded ["bottom"]
behavior at the affected render path so consumer-provided
ToastV2RegionProps.swipeDirections take effect.
- Around line 20-42: Update the MutationObserver setup around connect so
document.body remains observed while watching current .toast-v2-region nodes; do
not disconnect the body observer when connecting to regions. Detect when all
observed regions disappear or are replaced, reset connected to false, and let
the next body mutation re-run connect and sync so recreated regions receive
inert/tabIndex updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e84b2ef9-71f6-4daf-a79c-562799cd865c

📥 Commits

Reviewing files that changed from the base of the PR and between 6f45fd5 and b9eac0c.

📒 Files selected for processing (71)
  • infra/console.ts
  • nix/hashes.json
  • package.json
  • packages/app/package.json
  • packages/app/src/components/prompt-input-v2.tsx
  • packages/app/src/components/titlebar.tsx
  • packages/app/src/context/tab-migration.ts
  • packages/app/src/context/tabs.test.ts
  • packages/app/src/context/tabs.tsx
  • packages/app/src/i18n/ar.ts
  • packages/app/src/i18n/br.ts
  • packages/app/src/i18n/bs.ts
  • packages/app/src/i18n/da.ts
  • packages/app/src/i18n/de.ts
  • packages/app/src/i18n/en.ts
  • packages/app/src/i18n/es.ts
  • packages/app/src/i18n/fr.ts
  • packages/app/src/i18n/ja.ts
  • packages/app/src/i18n/ko.ts
  • packages/app/src/i18n/no.ts
  • packages/app/src/i18n/pl.ts
  • packages/app/src/i18n/ru.ts
  • packages/app/src/i18n/th.ts
  • packages/app/src/i18n/tr.ts
  • packages/app/src/i18n/uk.ts
  • packages/app/src/i18n/zh.ts
  • packages/app/src/i18n/zht.ts
  • packages/app/src/pages/layout.tsx
  • packages/app/src/utils/toast.tsx
  • packages/app/test-browser/prompt-attachments.test.ts
  • packages/app/test-browser/toast-owner.test.ts
  • packages/cli/package.json
  • packages/cli/script/generate.ts
  • packages/codemode/package.json
  • packages/console/app/package.json
  • packages/console/core/package.json
  • packages/console/function/package.json
  • packages/console/mail/package.json
  • packages/console/support/package.json
  • packages/core/package.json
  • packages/core/src/models-dev.ts
  • packages/desktop/package.json
  • packages/effect-drizzle-sqlite/package.json
  • packages/effect-sqlite-node/package.json
  • packages/enterprise/package.json
  • packages/function/package.json
  • packages/http-recorder/package.json
  • packages/llm/package.json
  • packages/llm/script/recording-cost-report.ts
  • packages/plugin/package.json
  • packages/sdk/js/package.json
  • packages/server/package.json
  • packages/session-ui/package.json
  • packages/session-ui/src/v2/components/prompt-input/attachments.css
  • packages/session-ui/src/v2/components/prompt-input/attachments.ts
  • packages/session-ui/src/v2/components/prompt-input/index.tsx
  • packages/session-ui/src/v2/components/session-review-v2.css
  • packages/session-ui/src/v2/components/session-review-v2.tsx
  • packages/slack/package.json
  • packages/stats/app/package.json
  • packages/stats/app/src/routes/model-catalog.ts
  • packages/stats/core/package.json
  • packages/stats/server/package.json
  • packages/tui/package.json
  • packages/ui/package.json
  • packages/ui/src/v2/components/toast-v2.css
  • packages/ui/src/v2/components/toast-v2.stories.tsx
  • packages/ui/src/v2/components/toast-v2.tsx
  • packages/ui/vite.config.ts
  • packages/web/package.json
  • sdks/vscode/package.json

Comment on lines +8 to +9
if ("server" in tab && typeof tab.server !== "string") return []
const server = ("server" in tab ? tab.server : fallback) as ServerConnection.Key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Restore fallback handling for invalid persisted server values.

The prior migration backfilled missing and invalid server values, but this now drops otherwise valid legacy tabs (for example, { type: "session", sessionId: "a", server: null }). That silently loses persisted tabs during upgrade. Use fallback whenever the stored server is absent or non-string, and add a regression case.

Proposed fix
-    if ("server" in tab && typeof tab.server !== "string") return []
-    const server = ("server" in tab ? tab.server : fallback) as ServerConnection.Key
+    const savedServer = "server" in tab ? tab.server : undefined
+    const server = (typeof savedServer === "string" ? savedServer : fallback) as ServerConnection.Key
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ("server" in tab && typeof tab.server !== "string") return []
const server = ("server" in tab ? tab.server : fallback) as ServerConnection.Key
const savedServer = "server" in tab ? tab.server : undefined
const server = (typeof savedServer === "string" ? savedServer : fallback) as ServerConnection.Key
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/context/tab-migration.ts` around lines 8 - 9, Update the
server selection logic in the tab migration to use fallback when tab.server is
absent or not a string, rather than returning an empty tab list for invalid
persisted values. Preserve valid string server values, and add a regression case
covering a legacy tab with server: null to verify it is retained with fallback.

"prompt.action.stop": "Зупинити",

"prompt.toast.pasteUnsupported.title": "Непідтримуване вкладення",
"prompt.toast.attachmentDuplicate.title": "Цей файл уже завантажено",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use attachment terminology for duplicate feedback. The localized messages describe an upload, although the feature detects an attachment already present locally.

  • packages/app/src/i18n/uk.ts#L302-L302: change the text to Це вкладення вже додано or equivalent.
  • packages/app/src/i18n/zh.ts#L319-L319: change the text to 此附件已添加 or equivalent.
📍 Affects 2 files
  • packages/app/src/i18n/uk.ts#L302-L302 (this comment)
  • packages/app/src/i18n/zh.ts#L319-L319
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/i18n/uk.ts` at line 302, Update the duplicate-attachment
title translations to use attachment terminology instead of upload terminology:
in packages/app/src/i18n/uk.ts lines 302-302, use “Це вкладення вже додано” or
equivalent; in packages/app/src/i18n/zh.ts lines 319-319, use “此附件已添加” or
equivalent.

Comment on lines +36 to +41
// v1 and v2 ids come from separate registries, so dismissal has to use the same
// implementation that issued the id.
export function dismissToast(toastId: number) {
if (!v2) return legacyToaster.dismiss(toastId)
return toasterV2.dismiss(toastId)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Dismissal routes on the current flag, not the flag that issued the id.

v2 is mutable module state (setV2Toast), while persistent toasts (session alerts, update-available, workspace reset progress) can outlive a flag flip. After a flip, dismissToast(id) hits the other registry and the toast is never removed — the very failure the comment above it warns about. Track the origin with the id instead of re-reading the flag.

🛡️ Sketch: remember which registry issued each id
+const v2Ids = new Set<number>()
+
 export function dismissToast(toastId: number) {
-  if (!v2) return legacyToaster.dismiss(toastId)
-  return toasterV2.dismiss(toastId)
+  if (!v2Ids.delete(toastId)) return legacyToaster.dismiss(toastId)
+  return toasterV2.dismiss(toastId)
 }

Record the id in v2Ids at the showToastV2(...) call site (Lines 24-33).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/utils/toast.tsx` around lines 36 - 41, Update the toast ID
tracking around showToastV2 and dismissToast so each issued ID records whether
it belongs to the v2 registry, using the existing v2Ids mechanism. Make
dismissToast consult and remove that per-ID origin record before routing
dismissal, falling back to the legacy registry for IDs not recorded as v2,
rather than reading the mutable v2 flag.

Comment on lines +154 to +157
const source = Flag.OPENCODE_MODELS_URL || "https://models.opencode.ai"
const filepath = path.join(
Global.Path.cache,
source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
source === "https://models.opencode.ai" ? "models.json" : `models-${Hash.fast(source)}.json`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Use a versioned cache key or migrate the legacy models.json cache.

The previous default source and the new source both resolve to Global.Path.cache/models.json, while loadFromDisk returns that file before fetching. Existing users can therefore load a cache populated from models.dev as if it came from models.opencode.ai, leaving the new catalog stale until a later refresh.

Use a source/version-specific filename or explicitly invalidate the legacy cache during migration, and add an upgrade-path test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/models-dev.ts` around lines 154 - 157, Update the cache
path construction in the model-loading flow around Flag.OPENCODE_MODELS_URL and
loadFromDisk so the default opencode.ai source no longer reuses the legacy
models.json cache. Use a source/version-specific filename or explicitly migrate
and invalidate the legacy cache, preserving existing cache behavior for current
sources; add a test covering the upgrade from the models.dev cache.

Comment on lines 167 to 177
const prev = () => {
return props.files[fileIndex() - 1]
if (!canCycle()) return
return props.files[(fileIndex() - 1 + props.files.length) % props.files.length]
}

const next = () => {
return props.files[fileIndex() + 1]
if (!canCycle()) return
return props.files[(fileIndex() + 1) % props.files.length]
}

const canCycle = () => props.files.length > 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

canCycle() allows cycling with a single file.

canCycle only checks props.files.length > 0. With exactly one file, prev()/next() still resolve via modulo to that same file, so the nav buttons stay enabled and the arrow-key shortcut fires cycle() on a no-op destination (single-file diffs are a common case for session review). Consider length > 1.

🔧 Proposed fix
-  const canCycle = () => props.files.length > 0
+  const canCycle = () => props.files.length > 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prev = () => {
return props.files[fileIndex() - 1]
if (!canCycle()) return
return props.files[(fileIndex() - 1 + props.files.length) % props.files.length]
}
const next = () => {
return props.files[fileIndex() + 1]
if (!canCycle()) return
return props.files[(fileIndex() + 1) % props.files.length]
}
const canCycle = () => props.files.length > 0
const prev = () => {
if (!canCycle()) return
return props.files[(fileIndex() - 1 + props.files.length) % props.files.length]
}
const next = () => {
if (!canCycle()) return
return props.files[(fileIndex() + 1) % props.files.length]
}
const canCycle = () => props.files.length > 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/session-ui/src/v2/components/session-review-v2.tsx` around lines 167
- 177, Update canCycle in the session review navigation logic to return true
only when props.files contains more than one file. Preserve the existing prev,
next, and cycle behavior for multi-file lists while disabling navigation for
empty and single-file lists.

export interface ToastV2RegionProps extends ToasterProps {}

function ToastV2Region(props: ToastV2RegionProps) {
const [local, rest] = splitProps(props, ["class", "className", "style", "toastOptions", "swipeDirections"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

swipeDirections is split out but never forwarded, so the prop is silently ignored.

ToastV2RegionProps extends ToasterProps, yet swipeDirections is removed from rest at Line 11 and Line 52 hardcodes ["bottom"]. Consumers passing swipeDirections get no effect. Either merge it or stop extracting it.

🐛 Proposed fix
-        swipeDirections={["bottom"]}
+        swipeDirections={local.swipeDirections ?? ["bottom"]}

Also applies to: 52-52

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/v2/components/toast-v2.tsx` at line 11, Update the ToastV2
component’s swipeDirections handling: either forward the extracted
swipeDirections value to the underlying toaster/region or stop extracting it so
it remains in rest. Remove the hardcoded ["bottom"] behavior at the affected
render path so consumer-provided ToastV2RegionProps.swipeDirections take effect.

Comment on lines +20 to +42
let connected = false
const connect = () => {
const regions = document.querySelectorAll(".toast-v2-region")
if (!regions.length) return
observer.disconnect()
regions.forEach((region) => {
observer.observe(region, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ["data-visible"],
})
})
connected = true
sync()
}
const observer = new MutationObserver(() => {
if (!connected) connect()
else sync()
})
observer.observe(document.body, { subtree: true, childList: true })
queueMicrotask(connect)
onCleanup(() => observer.disconnect())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Region sync stops permanently if the toast region is replaced.

connect() disconnects the body observer and only observes the current .toast-v2-region nodes, and connected is never reset. If Sonner tears down and recreates the region (or the portal remounts), no observer watches document.body anymore, so inert/tabIndex are never resynced and hidden stacked toasts stay keyboard-focusable.

🐛 Proposed fix: keep watching the body and re-connect on region churn
     let connected = false
     const connect = () => {
       const regions = document.querySelectorAll(".toast-v2-region")
-      if (!regions.length) return
-      observer.disconnect()
+      if (!regions.length) {
+        connected = false
+        return
+      }
       regions.forEach((region) => {
         observer.observe(region, {
           subtree: true,
           childList: true,
           attributes: true,
           attributeFilter: ["data-visible"],
         })
       })
       connected = true
       sync()
     }
     const observer = new MutationObserver(() => {
-      if (!connected) connect()
-      else sync()
+      if (!connected) connect()
+      else sync()
     })
     observer.observe(document.body, { subtree: true, childList: true })

With the body observation retained, connected flips back to false whenever the region disappears and the next body mutation re-attaches the attribute observers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui/src/v2/components/toast-v2.tsx` around lines 20 - 42, Update the
MutationObserver setup around connect so document.body remains observed while
watching current .toast-v2-region nodes; do not disconnect the body observer
when connecting to regions. Detect when all observed regions disappear or are
replaced, reset connected to false, and let the next body mutation re-run
connect and sync so recreated regions receive inert/tabIndex updates.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feature: Interactive Configuration

4 participants