Terminal fixes, sort fixes, flashbang fix

This commit is contained in:
Neil
2026-03-21 14:44:45 -07:00
parent 665be09ba8
commit 9de1eb4318
10 changed files with 157 additions and 13 deletions
+14
View File
@@ -6,7 +6,21 @@ on:
- 'v*'
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Create release with auto-generated notes
run: gh release create ${{ github.ref_name }} --generate-notes --prerelease=${{ contains(github.ref_name, 'rc') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release:
needs: create-release
strategy:
fail-fast: false
matrix:
+1
View File
@@ -53,3 +53,4 @@ publish:
provider: github
owner: stablyai
repo: orca
releaseType: release
+2 -1
View File
@@ -21,6 +21,7 @@
"build:icons": "bash icon/generate.sh",
"build:mac": "electron-vite build && electron-builder --mac",
"build:linux": "electron-vite build && electron-builder --linux",
"release:rc": "npm version prerelease --preid=rc && git push --follow-tags",
"release:patch": "npm version patch && git push --follow-tags",
"release:minor": "npm version minor && git push --follow-tags",
"release:major": "npm version major && git push --follow-tags"
@@ -66,7 +67,7 @@
"oxlint": "^1.56.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-grab": "link:/Users/nwparker/projects/react-grab/packages/react-grab",
"react-grab": "^0.1.28",
"tailwindcss": "^4.2.2",
"typescript": "^5.9.3",
"vite": "^7.3.1"
+111 -2
View File
@@ -124,8 +124,8 @@ importers:
specifier: ^19.2.4
version: 19.2.4(react@19.2.4)
react-grab:
specifier: link:/Users/nwparker/projects/react-grab/packages/react-grab
version: link:../react-grab/packages/react-grab
specifier: ^0.1.28
version: 0.1.28(@types/react@19.2.14)(react@19.2.4)
tailwindcss:
specifier: ^4.2.2
version: 4.2.2
@@ -141,6 +141,10 @@ packages:
7zip-bin@5.2.0:
resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==}
'@antfu/ni@0.23.2':
resolution: {integrity: sha512-FSEVWXvwroExDXUu8qV6Wqp2X3D1nJ0Li4LFymCyvCVrm7I3lNfG0zZWSWvGU1RE7891eTnFTyh31L3igOwNKQ==}
hasBin: true
'@babel/code-frame@7.29.0':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
@@ -785,6 +789,9 @@ packages:
resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
engines: {node: '>= 10.0.0'}
'@medv/finder@4.0.2':
resolution: {integrity: sha512-RraNY9SCcx4KZV0Dh6BEW6XEW2swkqYca74pkFFRw6hHItSHiy+O/xMnpbofjYbzXj0tSpBGthUF1hHTsr3vIQ==}
'@modelcontextprotocol/sdk@1.27.1':
resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==}
engines: {node: '>=18'}
@@ -1762,6 +1769,10 @@ packages:
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
'@react-grab/cli@0.1.28':
resolution: {integrity: sha512-IE4bTeH0mCq0FBRaYRUtdiIfvO7NCv14lHS4TiTY8YNG5tPYwHnLZZtniud0ElmLIWGP95Kk6E7A6J2uDmOY+Q==}
hasBin: true
'@rolldown/pluginutils@1.0.0-rc.3':
resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==}
@@ -2054,6 +2065,11 @@ packages:
peerDependencies:
'@types/react': ^19.2.0
'@types/react-reconciler@0.28.9':
resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==}
peerDependencies:
'@types/react': '*'
'@types/react@19.2.14':
resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==}
@@ -2209,6 +2225,11 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
bippy@0.5.32:
resolution: {integrity: sha512-yt1mC8eReTxjfg41YBZdN4PvsDwHFWxltoiQX0Q+Htlbf41aSniopb7ECZits01HwNAvXEh69RGk/ImlswDTEw==}
peerDependencies:
react: '>=17.0.1'
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@@ -3004,6 +3025,10 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
ignore@7.0.5:
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
engines: {node: '>= 4'}
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -3179,6 +3204,9 @@ packages:
engines: {node: '>=6'}
hasBin: true
jsonc-parser@3.3.1:
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==}
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
@@ -3754,6 +3782,15 @@ packages:
peerDependencies:
react: ^19.2.4
react-grab@0.1.28:
resolution: {integrity: sha512-u3fvu7a7ejHhuWKzf/N6sFavV04vcqwtbcqyxwNPtvd3ts9KSVerpHp1ZH0/XVKTsh3MZz1u1jyIt0KYC9L/Rg==}
hasBin: true
peerDependencies:
react: '>=17.0.0'
peerDependenciesMeta:
react:
optional: true
react-refresh@0.18.0:
resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==}
engines: {node: '>=0.10.0'}
@@ -3913,6 +3950,16 @@ packages:
resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==}
engines: {node: '>=10'}
seroval-plugins@1.5.1:
resolution: {integrity: sha512-4FbuZ/TMl02sqv0RTFexu0SP6V+ywaIe5bAWCCEik0fk17BhALgwvUDVF7e3Uvf9pxmwCEJsRPmlkUE6HdzLAw==}
engines: {node: '>=10'}
peerDependencies:
seroval: ^1.0
seroval@1.5.1:
resolution: {integrity: sha512-OwrZRZAfhHww0WEnKHDY8OM0U/Qs8OTfIDWhUD4BLpNJUfXK4cGmjiagGze086m+mhI+V2nD0gfbHEnJjb9STA==}
engines: {node: '>=10'}
serve-static@2.2.1:
resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==}
engines: {node: '>= 18'}
@@ -3981,6 +4028,10 @@ packages:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
smol-toml@1.6.0:
resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==}
engines: {node: '>= 18'}
socks-proxy-agent@8.0.5:
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
engines: {node: '>= 14'}
@@ -3989,6 +4040,9 @@ packages:
resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
solid-js@1.9.11:
resolution: {integrity: sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==}
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -4428,6 +4482,8 @@ snapshots:
7zip-bin@5.2.0: {}
'@antfu/ni@0.23.2': {}
'@babel/code-frame@7.29.0':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
@@ -5045,6 +5101,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@medv/finder@4.0.2': {}
'@modelcontextprotocol/sdk@1.27.1(zod@3.25.76)':
dependencies:
'@hono/node-server': 1.19.11(hono@4.12.8)
@@ -5983,6 +6041,17 @@ snapshots:
'@radix-ui/rect@1.1.1': {}
'@react-grab/cli@0.1.28':
dependencies:
'@antfu/ni': 0.23.2
commander: 14.0.3
ignore: 7.0.5
jsonc-parser: 3.3.1
ora: 8.2.0
picocolors: 1.1.1
prompts: 2.4.2
smol-toml: 1.6.0
'@rolldown/pluginutils@1.0.0-rc.3': {}
'@rollup/rollup-android-arm-eabi@4.59.1':
@@ -6216,6 +6285,10 @@ snapshots:
dependencies:
'@types/react': 19.2.14
'@types/react-reconciler@0.28.9(@types/react@19.2.14)':
dependencies:
'@types/react': 19.2.14
'@types/react@19.2.14':
dependencies:
csstype: 3.2.3
@@ -6384,6 +6457,13 @@ snapshots:
baseline-browser-mapping@2.10.10: {}
bippy@0.5.32(@types/react@19.2.14)(react@19.2.4):
dependencies:
'@types/react-reconciler': 0.28.9(@types/react@19.2.14)
react: 19.2.4
transitivePeerDependencies:
- '@types/react'
bl@4.1.0:
dependencies:
buffer: 5.7.1
@@ -7349,6 +7429,8 @@ snapshots:
ignore@5.3.2: {}
ignore@7.0.5: {}
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -7466,6 +7548,8 @@ snapshots:
json5@2.2.3: {}
jsonc-parser@3.3.1: {}
jsonfile@4.0.0:
optionalDependencies:
graceful-fs: 4.2.11
@@ -8108,6 +8192,17 @@ snapshots:
react: 19.2.4
scheduler: 0.27.0
react-grab@0.1.28(@types/react@19.2.14)(react@19.2.4):
dependencies:
'@medv/finder': 4.0.2
'@react-grab/cli': 0.1.28
bippy: 0.5.32(@types/react@19.2.14)(react@19.2.4)
solid-js: 1.9.11
optionalDependencies:
react: 19.2.4
transitivePeerDependencies:
- '@types/react'
react-refresh@0.18.0: {}
react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.4):
@@ -8296,6 +8391,12 @@ snapshots:
type-fest: 0.13.1
optional: true
seroval-plugins@1.5.1(seroval@1.5.1):
dependencies:
seroval: 1.5.1
seroval@1.5.1: {}
serve-static@2.2.1:
dependencies:
encodeurl: 2.0.0
@@ -8421,6 +8522,8 @@ snapshots:
smart-buffer@4.2.0: {}
smol-toml@1.6.0: {}
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.4
@@ -8434,6 +8537,12 @@ snapshots:
ip-address: 10.1.0
smart-buffer: 4.2.0
solid-js@1.9.11:
dependencies:
csstype: 3.2.3
seroval: 1.5.1
seroval-plugins: 1.5.1(seroval@1.5.1)
source-map-js@1.2.1: {}
source-map-support@0.5.21:
+9 -1
View File
@@ -1,4 +1,4 @@
import { app, shell, BrowserWindow, Menu, nativeImage, ipcMain } from 'electron'
import { app, shell, BrowserWindow, Menu, nativeImage, ipcMain, nativeTheme } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
@@ -40,6 +40,7 @@ function createWindow(): BrowserWindow {
minHeight: 400,
show: false,
autoHideMenuBar: true,
backgroundColor: nativeTheme.shouldUseDarkColors ? '#0a0a0a' : '#ffffff',
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : undefined,
...(process.platform === 'darwin' ? { trafficLightPosition: { x: 16, y: 12 } } : {}),
icon: is.dev ? devIcon : icon,
@@ -203,6 +204,13 @@ app.whenReady().then(() => {
// Initialize persistence
store = new Store()
// Apply persisted theme before creating the window so that:
// 1. BrowserWindow gets the correct backgroundColor (no white flash)
// 2. nativeTheme.themeSource makes prefers-color-scheme report correctly
// so the renderer's applySystemTheme() picks the right mode immediately
const theme = store.getSettings().theme ?? 'system'
nativeTheme.themeSource = theme
// Create window
mainWindow = createWindow()
+4 -1
View File
@@ -1,4 +1,4 @@
import { ipcMain } from 'electron'
import { ipcMain, nativeTheme } from 'electron'
import type { Store } from '../persistence'
import type { GlobalSettings, PersistedState } from '../../shared/types'
import { listSystemFontFamilies } from '../system-fonts'
@@ -9,6 +9,9 @@ export function registerSettingsHandlers(store: Store): void {
})
ipcMain.handle('settings:set', (_event, args: Partial<GlobalSettings>) => {
if (args.theme) {
nativeTheme.themeSource = args.theme
}
return store.updateSettings(args)
})
+3 -4
View File
@@ -77,10 +77,9 @@ export function registerWorktreeHandlers(mainWindow: BrowserWindow, store: Store
if (!created) throw new Error('Worktree created but not found in listing')
const worktreeId = `${repo.id}::${worktreePath}`
const meta =
branchName === requestedName
? undefined
: store.setWorktreeMeta(worktreeId, { displayName: requestedName })
const metaUpdates: Partial<WorktreeMeta> =
branchName === requestedName ? {} : { displayName: requestedName }
const meta = store.setWorktreeMeta(worktreeId, metaUpdates)
const worktree = mergeWorktree(repo.id, created, meta)
// Run setup hook asynchronously (don't block the UI)
+1 -1
View File
@@ -217,6 +217,6 @@ function getDefaultWorktreeMeta(): WorktreeMeta {
linkedPR: null,
isArchived: false,
isUnread: false,
sortOrder: 0
sortOrder: Date.now()
}
}
+3 -1
View File
@@ -619,9 +619,11 @@ export default function TerminalPane({
// Connect a pane's terminal to a PTY via IPC transport
const connectPanePty = (pane: ManagedPane, manager: PaneManager): void => {
const onExit = (ptyId: string): void => {
// Always clear the dead PTY ID from the store to avoid stale state
clearTabPtyId(tabId, ptyId)
const panes = manager.getPanes()
if (panes.length <= 1) {
clearTabPtyId(tabId, ptyId)
onPtyExitRef.current(ptyId)
return
}
+9 -2
View File
@@ -404,10 +404,13 @@ export class PaneManager {
webglAddon: null
}
// Focus handler: clicking a pane makes it active
// Focus handler: clicking a pane makes it active and explicitly focuses
// the terminal. We must call focus: true here because after DOM reparenting
// (e.g. splitPane moves the original pane into a flex container), xterm.js's
// native click-to-focus on its internal textarea may not fire reliably.
container.addEventListener('pointerdown', () => {
if (!this.destroyed && this.activePaneId !== id) {
this.setActivePane(id, { focus: false })
this.setActivePane(id, { focus: true })
}
})
@@ -503,6 +506,10 @@ export class PaneManager {
el.style.minHeight = '0'
el.style.position = 'relative'
el.style.overflow = 'hidden'
// Clear any fixed width/height from createInitialPane so flex sizing
// controls the layout instead of the leftover 100% values.
el.style.width = ''
el.style.height = ''
}
private createDivider(isVertical: boolean): HTMLElement {