Skip to content

Merge dev into main#245

Merged
IgorWarzocha merged 36 commits into
mainfrom
dev
May 15, 2026
Merged

Merge dev into main#245
IgorWarzocha merged 36 commits into
mainfrom
dev

Conversation

@IgorWarzocha
Copy link
Copy Markdown
Owner

Summary

  • Merge the current dev branch into main for the next stable update.
  • Includes recent fixes for release channels, updater behavior, composer controls, sidebar polish, landing changelog, and workflow updates.

Validation

  • Not run; merge-only PR from dev to main with no local code changes.

* chore: add project cleanup config

* Fix compact diff baseline icon

* Add chat message copy controls

* Fix chat markdown wrapping

* Fix macOS window chrome and quit path

* Add sidebar project folder browser

* Refine project folder browser controls

* Fix composer textarea alignment jump

* Align composer textarea scrollbar

* Measure composer textarea before paint

* Stop composer text resize from rerendering timeline

* Anchor thread composer to bottom while typing

* Stabilize composer dictation adornment layout

* Revert "Stabilize composer dictation adornment layout"

This reverts commit 00c4586.

* Reserve composer dictation adornment space

* Pad composer text around dictation adornment

* Hide composer adornment at tiny widths

* Keep composer text wrapping vertically

* Avoid reserving dictation space on multiline composer

* Keep dictation adornment and refresh composer resize state

* Preserve session composer model choices

* Polish compact composer controls

* Close terminal drawer when entering compact mode

* Align takeover sidebar toggle

* Fix dictation setup prompt routing

* Fix project folder browser edge cases

* Update changelog for 0.1.64

* Address PR review edge cases
* Improve Pi message status details

* Address Pi status review edge cases
* Support dashboard terminal and sidebar toggle

* Polish project dashboard narrow layout

* Refine composer and dashboard narrow chrome

* Add about link to landing page

* Add beta update branch setting

* Fix beta updater channel edge cases

* Fix release notes and landing settings state
* Fix launcher download timeout

* Bump launcher package version
* Add React Doctor and tidy low-risk issues

* Continue React Doctor cleanup

* Polish diff baseline popover

* Align GitOps composer sizing

* Expose GitOps origin settings

* Restore accessible composer placeholder
* Polish sidebar and artifact interactions

* Add stacked PR workflow guidance

* Address artifact and diff review findings
* Fix Codex review follow-ups

* Ignore semantic grep cache files

* Restore dev workflow validation triggers

* Document workflow release conventions

* Clarify docs-only validation guidance

* Address updater and inbox review feedback

* Clarify validation command guidance
* Fix composer controls and project terminals

* Polish sidebar favorite indicators

* Close status model picker on selection

* Refine composer mic and sidebar icon alignment

* Restore project favorite icon alignment

* Limit left mic placement to empty composer

* Align empty composer mic with placeholder

* Nudge empty composer mic left

* Move empty composer mic farther left

* Set empty composer mic midpoint offset

* Tighten empty composer mic spacing

* Move updater channels to prefixed release tags

* Bump npm launcher version

* Stop release artifact builds on PRs

* Address updater and terminal review findings

* Fix gitops terminal restore and archive cleanup
@IgorWarzocha
Copy link
Copy Markdown
Owner Author

@codex please review this PR and give me 10-20 issues if any. Categorize findings as required, recommended, or optional.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2c26b0e01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +72 to +74
} else {
setErrorMessage(result.errorMessage)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge [required] Clear selected path when folder load fails

When openDirectory fails, the component keeps the previous currentPath in parent state, so the create action can still submit using an old folder even though navigation just failed. This can add/create a project in the wrong directory after an error (for example, permission denied or missing path). Resetting the current-path selection on error avoids stale-path submissions.

Useful? React with 👍 / 👎.

Comment on lines +91 to +95
.catch((error) => {
if (!cancelled) {
setArtifactLoadError(
error instanceof Error ? error.message : 'Could not load artifacts.',
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge [required] Reset artifact load error after successful updates

A transient failure in listArtifactsQuery sets artifactLoadError, but this error is only cleared on conversation change. If artifacts later arrive via desktop artifact-update events, the panel still renders the error screen and hides real data. Clearing artifactLoadError when artifacts are successfully populated prevents the panel from getting stuck in an error state.

Useful? React with 👍 / 👎.

Comment on lines 265 to 267
onClick={() => setPinned((current) => !current)}
onPointerDown={loadUsageTotals}
aria-label={label}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Load usage totals on keyboard open too

Usage totals are loaded on onPointerDown only. If a user opens the context popover via keyboard (Enter/Space), usageTotals is never populated, so the detailed usage section is missing even when message usage exists. Triggering the same data load from keyboard activation path keeps behavior consistent and accessible.

Useful? React with 👍 / 👎.

Comment on lines +212 to +220
const loadUsageTotals = useCallback(() => {
setUsageTotals(getMessageUsageTotals(messages))
}, [messages])

const openHoverPreview = useCallback(() => {
clearHoverTriangle()
loadUsageTotals()
setHovered(true)
}, [clearHoverTriangle])
}, [clearHoverTriangle, loadUsageTotals])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Refresh usage totals while popover stays open

Totals are computed only when opening/hovering, so a pinned-open popover shows stale token/cost numbers as new assistant messages stream in. Since this panel is used for live usage visibility, it should recompute while open (or on message changes) to avoid outdated figures.

Useful? React with 👍 / 👎.

Comment thread src/app/views/landing-view.tsx Outdated
Comment on lines +450 to +453
if (!(selectedProject && projectUsageQuery.data?.archivedUsageRefreshing)) return
const timeout = window.setTimeout(() => {
void projectUsageQuery.refetch()
}, 750)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Avoid tight polling during archived usage refresh

This effect refetches every 750ms while archivedUsageRefreshing is true. Each refetch re-runs full usage summary loading, including active-session scans, which can cause repeated disk-heavy work on large projects. This should be event-driven or throttled more conservatively to avoid unnecessary churn.

Useful? React with 👍 / 👎.

Comment thread src/app/views/landing-view.tsx Outdated
label="Spend"
loading={usageLoading}
value={formatCost(usageSummary?.costTotal)}
detail={`${formatAverageCost(usageSummary?.costTotal, sessionCount)}/session`}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge [optional] Compute spend/session from sessions with usage

The spend-per-session detail divides by total sessions, including sessions with zero assistant turns. For projects with many empty/draft sessions, this underreports average spend and can be misleading. Using sessionsWithUsageCount would better match what users expect from a spend average.

Useful? React with 👍 / 👎.

@IgorWarzocha
Copy link
Copy Markdown
Owner Author

@codex please review this PR and give me 10-20 issues if any. Categorize findings as required, recommended, or optional.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0ef9ab31e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (versionDiff < 0) return false

const runningRelease = getRunningReleaseFingerprint()
if (release.channel === 'main') return false
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge [required] Allow same-version updates on main channel

Returning false for every main release when versionDiff === 0 blocks hash-only channel updates from ever being offered. This is a regression with moving channel-main assets: if main is republished with the same semver but a new archive hash, clients remain stuck on the old build because available is never reached.

Useful? React with 👍 / 👎.

Comment on lines +248 to +251
if (command.source === 'app' && command.name === 'new') {
setDraft('')
onStartNewSession?.()
return
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge [required] Guard /new execution when no new-session handler exists

The /new branch clears the draft and returns even when onStartNewSession is undefined. In composers that don't pass this callback (for example inbox composer), submitting /new silently discards user input and performs no action, which is a user-visible behavior break.

Useful? React with 👍 / 👎.

Comment on lines +36 to +39
const canSubmit =
draft.trim().length > 0 &&
!busy &&
(Boolean(defaultLocation) || (browseOpen && Boolean(currentFolderPath)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Require a valid browsed folder before enabling submit

When browse mode is open, canSubmit still becomes true solely from defaultLocation. If the folder browser has not resolved a path (or failed), clicking add sends parentPath: null and falls back to the default location, which contradicts the visible “browse folder” flow and can create projects in the wrong place.

Useful? React with 👍 / 👎.

<button
type="button"
className="sidebar-project-create-submit sidebar-project-browse-toggle"
onClick={() => setBrowseOpen((current) => !current)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Clear stale browse path when toggling folder browser

Toggling the browser only flips browseOpen; it does not reset currentFolderPath. Reopening the browser can leave an old path active until the async directory load completes, so an immediate submit can target the previous folder rather than the currently shown one.

Useful? React with 👍 / 👎.

Comment thread desktop/runtime/slash-commands.ts Outdated
@@ -17,7 +18,11 @@ import type { PiRuntime } from './types.ts'
const builtinCommandNames = new Set([compactSlashCommand.name])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge [recommended] Reserve app slash commands from extension collisions

Builtin filtering still reserves only compact, even though app-level settings and new commands are now injected. Extensions/prompt templates can therefore register the same names and appear as conflicting duplicates, while exact /new handling is hardcoded to app behavior, making extension variants ambiguous or unreachable.

Useful? React with 👍 / 👎.

Comment thread scripts/dev-web-bridge-node.ts Outdated

async function listProjectDirectoryEntries(request: { path?: string | null | undefined }) {
const homePath = os.homedir()
const requestedPath = request.path?.trim() ? request.path : homePath
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge [optional] Normalize requested folder path in dev bridge

The dev bridge checks request.path?.trim() but still uses the untrimmed request.path value. A path with accidental leading/trailing whitespace resolves as a different filesystem path and can fail directory listing in dev mode, diverging from the Electron handler behavior.

Useful? React with 👍 / 👎.

@IgorWarzocha
Copy link
Copy Markdown
Owner Author

@codex please review this PR and give me 10-20 issues if any. Categorize findings as required, recommended, or optional.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

if (input.draft === '/settings') input.submit()

P2 Badge Submit /new while slash commands are loading

When Enter is pressed with /new before slash commands finish loading, this branch prevents default submission but only calls submit() for /settings. In that state /new is swallowed and does nothing, so users can hit Enter and get no action until they retry after loading completes.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/app/views/inbox-view.tsx Outdated
}
onOpenSettingsView={onOpenSettingsView}
onStartNewSession={() =>
void onAction('thread.new', { projectId: thread.projectId, composerMode: 'code' })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve chat mode when starting inbox session

The new-session action from the inbox always sends composerMode: 'code', so running /new from an inbox thread that came from chat will open a code draft instead of a chat draft. This regresses mode consistency for chat inbox items and can route the user into the wrong workflow unless they manually switch views first.

Useful? React with 👍 / 👎.

Comment on lines +248 to 250
} else {
onProjectSelect(project.id)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep create-session button creating a thread in code view

The project row “create session” handler no longer calls thread.new in the code view path and now only selects the project. This changes the button from an action that creates a fresh session into a no-op for already-selected projects, so users can no longer reliably start a new code thread from that control.

Useful? React with 👍 / 👎.

sessionPath: thread.sessionPath,
lastModifiedMs: thread.lastModifiedMs,
})
threadUsageCache.set(thread.sessionPath, { summary, signature: cacheKey })
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Bound project-usage caches to avoid unbounded growth

Per-session and per-project usage summaries are inserted into process-wide Maps without any eviction or cleanup path, so long-running desktop sessions that touch many projects/threads will retain stale entries indefinitely. This can steadily increase memory usage over time even after projects/threads are deleted.

Useful? React with 👍 / 👎.

@IgorWarzocha
Copy link
Copy Markdown
Owner Author

@codex please review this PR and give me 10-20 issues if any. Categorize findings as required, recommended, or optional.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f403dd0e1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/electron/main/runtime/shutdown.ts Outdated
.finally(() => {
cleanupFinished = true
app.quit()
app.exit(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.

P1 Badge Preserve graceful quit path after async shutdown completes

After before-quit is prevented, calling app.exit(0) terminates immediately and skips Electron’s normal app.quit() flow, including renderer beforeunload/unload handling. That can drop in-flight UI state or bypass window-level cleanup whenever runtime shutdown takes the async path (which is now every quit). Electron’s app docs note app.exit bypasses before-quit/will-quit semantics, so this changes shutdown behavior in a user-visible way.

Useful? React with 👍 / 👎.

const baseUrl = RELEASE_BASE_URL.replace(trailingSlashesPattern, '')
if (baseUrl.includes('{releaseTag}'))
return baseUrl.replace(releaseTagPlaceholderPattern, releaseTag)
if (baseUrl.includes('{channel}')) return baseUrl.replace(channelPlaceholderPattern, releaseTag)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep {channel} placeholder mapped to actual channel name

Replacing {channel} with channel-main/channel-dev instead of main/dev breaks existing custom mirror URLs that use {channel} literally for branch/channel folders. For deployments configured as .../{channel}/..., update checks will start requesting .../channel-main/... and fail unless mirrors are restructured, which is a regression for externally hosted update endpoints.

Useful? React with 👍 / 👎.

Comment on lines +338 to +346
const channelPrefixedMatch = channelReleaseKeyPattern.exec(runningReleaseKey)
if (channelPrefixedMatch?.[2] && channelPrefixedMatch[3]) {
return {
version: channelPrefixedMatch[2],
hash: channelPrefixedMatch[3].toLowerCase(),
}
}

return null
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recognize legacy cached release keys when comparing dev updates

The new running-release fingerprint parser only accepts keys prefixed with main-/dev-. Older installed caches use <version>-<hash>, so getRunningReleaseFingerprint() returns null for those users. In dev channel mode with equal semver, isUpdateCandidate() will then keep returning true and repeatedly offer/reinstall the same build because hash equality is never detected.

Useful? React with 👍 / 👎.

Comment thread desktop/terminal/manager.ts Outdated
Comment on lines +151 to +153
moveTranscript(input.record.transcriptPath, nextTranscriptPath)
input.record.transcriptPath = nextTranscriptPath
input.record.snapshot = {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid dropping transcript history when bind rename fails

If transcript rename fails (e.g. destination already exists), the code still rewires record.transcriptPath to the new file. That silently detaches the active terminal record from its existing transcript and can lose already collected shell history for this terminal session. This is especially likely when rebinding to a session ID with an existing transcript file.

Useful? React with 👍 / 👎.

Comment thread shared/thread-history.ts
Comment on lines 45 to +49
if (entry && isDisplayableEntry(entry)) {
count += 1
if (modelChangeConsumesNextEntry(entry, entries[index + 1])) {
index += 1
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Exclude non-emitted model-change entries from displayable count

countDisplayableEntriesInRange now counts all model_change entries as visible, but appendModelChangeMessage emits nothing when provider/model is missing. For legacy or partial session logs, the “previous message count” can be inflated relative to actual rendered rows, causing incorrect history-divider counts and compaction UX mismatch.

Useful? React with 👍 / 👎.

Comment thread shared/thread-history.ts
Comment on lines +131 to +134
case 'model_change':
return appendModelChangeMessage(messages, entry, nextEntry)
case 'thinking_level_change':
appendThinkingLevelChangeMessage(messages, entry)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Respect display:false for new model/reasoning history entries

The new model_change and thinking_level_change branches bypass the display guard that is used for hidden custom entries. If persisted history marks these events with display: false, they will still be surfaced as system rows, leaking entries that callers explicitly marked as non-displayable.

Useful? React with 👍 / 👎.

@IgorWarzocha IgorWarzocha merged commit b962edf into main May 15, 2026
6 checks passed
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.

1 participant