mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
chore: remove unused code
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
||||
type TerminalShortcutSpecialKey
|
||||
} from '../terminal/terminal-accessory-keys'
|
||||
|
||||
export const CUSTOM_ACCESSORY_KEYS_STORAGE_KEY = 'orca:custom-accessory-keys'
|
||||
const CUSTOM_ACCESSORY_KEYS_STORAGE_KEY = 'orca:custom-accessory-keys'
|
||||
|
||||
export type CustomKey = {
|
||||
id: string
|
||||
|
||||
@@ -13,7 +13,7 @@ function stripTrailingSeparators(p: string): string {
|
||||
// Why: cross-platform path basename — handles both POSIX ("/") and Windows
|
||||
// ("\\") separators, mirroring src/renderer/src/lib/path.ts so the mobile
|
||||
// suggestion logic agrees with the desktop's collision check.
|
||||
export function pathBasename(p: string): string {
|
||||
function pathBasename(p: string): string {
|
||||
const normalized = stripTrailingSeparators(p)
|
||||
const idx = Math.max(normalized.lastIndexOf('/'), normalized.lastIndexOf('\\'))
|
||||
return idx === -1 ? normalized : normalized.slice(idx + 1)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type TaskProvider = 'github' | 'gitlab' | 'linear'
|
||||
|
||||
export const MOBILE_TASK_PROVIDERS: readonly TaskProvider[] = ['github', 'gitlab', 'linear']
|
||||
const MOBILE_TASK_PROVIDERS: readonly TaskProvider[] = ['github', 'gitlab', 'linear']
|
||||
|
||||
const TASK_PROVIDER_SET = new Set<TaskProvider>(MOBILE_TASK_PROVIDERS)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ export const MOBILE_TUI_AGENT_AUTO_PICK_ORDER = [
|
||||
'claude',
|
||||
'openclaude',
|
||||
'codex',
|
||||
'openclaude',
|
||||
'grok',
|
||||
'copilot',
|
||||
'opencode',
|
||||
@@ -135,7 +136,7 @@ export function isMobileTuiAgent(value: unknown): value is TuiAgent {
|
||||
return MOBILE_TUI_AGENT_AUTO_PICK_ORDER.includes(value as TuiAgent)
|
||||
}
|
||||
|
||||
export function normalizeDisabledMobileTuiAgents(value: unknown): TuiAgent[] {
|
||||
function normalizeDisabledMobileTuiAgents(value: unknown): TuiAgent[] {
|
||||
if (!Array.isArray(value)) {
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export const PER_REPO_FETCH_LIMIT = 36
|
||||
export const CROSS_REPO_DISPLAY_LIMIT = 100
|
||||
|
||||
export const GITHUB_WORK_ITEMS_SSH_REMOTE_REQUIRED_MESSAGE =
|
||||
const GITHUB_WORK_ITEMS_SSH_REMOTE_REQUIRED_MESSAGE =
|
||||
'GitHub work items require a GitHub remote for SSH repositories'
|
||||
|
||||
export function isGitHubWorkItemsSshRemoteRequiredError(error: unknown): boolean {
|
||||
|
||||
@@ -5,7 +5,7 @@ export function resolveMobileWorkspaceCreateName(args: {
|
||||
return args.draft?.trim() || args.fallback
|
||||
}
|
||||
|
||||
export function slugifyForWorkspaceName(input: string): string {
|
||||
function slugifyForWorkspaceName(input: string): string {
|
||||
return input
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
|
||||
@@ -7,7 +7,7 @@ export type WorkspaceSshGate = {
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export function isWorkspaceSshConnectInProgress(status: SshConnectionStatus | null): boolean {
|
||||
function isWorkspaceSshConnectInProgress(status: SshConnectionStatus | null): boolean {
|
||||
return status === 'connecting' || status === 'deploying-relay' || status === 'reconnecting'
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import type { ConnectionState } from './types'
|
||||
// session but haven't been for ≥ 1 minute despite the retry loop
|
||||
// spinning, treat the same as never-connected. Catches the case
|
||||
// where the desktop's IP changed mid-session.
|
||||
export const WARNING_ATTEMPTS = 3
|
||||
export const UNREACHABLE_ATTEMPTS = 12
|
||||
export const STALE_SINCE_LAST_CONNECT_MS = 60_000
|
||||
const WARNING_ATTEMPTS = 3
|
||||
const UNREACHABLE_ATTEMPTS = 12
|
||||
const STALE_SINCE_LAST_CONNECT_MS = 60_000
|
||||
|
||||
export type ConnectionVerdict =
|
||||
| { kind: 'normal'; label: string }
|
||||
@@ -76,13 +76,3 @@ export function classifyConnection(args: {
|
||||
|
||||
return { kind: 'normal', label: 'Reconnecting…' }
|
||||
}
|
||||
|
||||
// Why: the message under the banner explains what likely happened so the
|
||||
// user understands why we're suggesting Re-pair. Tuned to be specific
|
||||
// about IP/port without being technical (we don't want to leak
|
||||
// "ws://192.168.x.y:port" unless someone is debugging).
|
||||
export function unreachableHint(reason: 'never-connected' | 'stale'): string {
|
||||
return reason === 'never-connected'
|
||||
? "Can't reach this Orca desktop. Its network address may have changed since pairing — try re-pairing from the desktop's Settings → Mobile screen."
|
||||
: 'Lost contact with the Orca desktop. If your network changed (different Wi-Fi, IP renewed, or desktop restarted), try re-pairing.'
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export function decrypt(encrypted: string, sharedKey: Uint8Array): string | null
|
||||
return plaintext ? new TextDecoder().decode(plaintext) : null
|
||||
}
|
||||
|
||||
export function encryptBytes(plaintext: Uint8Array, sharedKey: Uint8Array): Uint8Array {
|
||||
function encryptBytes(plaintext: Uint8Array, sharedKey: Uint8Array): Uint8Array {
|
||||
const nonce = u8(nacl.randomBytes(nacl.box.nonceLength))
|
||||
const ciphertext = nacl.box.after(u8(plaintext), nonce, u8(sharedKey))
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
export { connect, type RpcClient } from './rpc-client'
|
||||
export {
|
||||
loadHosts,
|
||||
saveHost,
|
||||
removeHost,
|
||||
renameHost,
|
||||
getNextHostName,
|
||||
updateLastConnected
|
||||
} from './host-store'
|
||||
export type {
|
||||
RpcRequest,
|
||||
RpcResponse,
|
||||
RpcSuccess,
|
||||
RpcFailure,
|
||||
ConnectionState,
|
||||
HostProfile,
|
||||
PairingOffer
|
||||
} from './types'
|
||||
export { PairingOfferSchema, PAIRING_OFFER_VERSION } from './types'
|
||||
@@ -24,7 +24,7 @@ export type RpcFailure = {
|
||||
|
||||
export type RpcResponse = RpcSuccess | RpcFailure
|
||||
|
||||
export const PAIRING_OFFER_VERSION = 2
|
||||
const PAIRING_OFFER_VERSION = 2
|
||||
|
||||
export const PairingOfferSchema = z.object({
|
||||
v: z.literal(PAIRING_OFFER_VERSION),
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@linear/sdk": "^82.1.0",
|
||||
@@ -125,7 +124,6 @@
|
||||
"remark-parse": "^11.0.0",
|
||||
"shadcn": "^4.7.0",
|
||||
"sherpa-onnx": "1.12.37",
|
||||
"simple-git": "^3.36.0",
|
||||
"sonner": "^2.0.7",
|
||||
"ssh2": "^1.17.0",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
|
||||
Generated
-45
@@ -25,9 +25,6 @@ importers:
|
||||
'@dnd-kit/sortable':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)
|
||||
'@dnd-kit/utilities':
|
||||
specifier: ^3.2.2
|
||||
version: 3.2.2(react@19.2.5)
|
||||
'@electron-toolkit/preload':
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2(electron@41.5.0)
|
||||
@@ -220,9 +217,6 @@ importers:
|
||||
sherpa-onnx:
|
||||
specifier: 1.12.37
|
||||
version: 1.12.37
|
||||
simple-git:
|
||||
specifier: ^3.36.0
|
||||
version: 3.36.0
|
||||
sonner:
|
||||
specifier: ^2.0.7
|
||||
version: 2.0.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||
@@ -1006,12 +1000,6 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@kwsites/file-exists@1.1.1':
|
||||
resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==}
|
||||
|
||||
'@kwsites/promise-deferred@1.1.1':
|
||||
resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
|
||||
|
||||
'@linear/sdk@82.1.0':
|
||||
resolution: {integrity: sha512-Ok7o+LqXaenx6Um58NQqjQoQanDsCgAIe9yNgpVbqRSh5APz3Ds1kZUz2vWmSNTNATFZm1zDQtEktTMga2X7UQ==}
|
||||
engines: {node: '>=18.x'}
|
||||
@@ -2365,12 +2353,6 @@ packages:
|
||||
'@sec-ant/readable-stream@0.4.1':
|
||||
resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
|
||||
|
||||
'@simple-git/args-pathspec@1.0.3':
|
||||
resolution: {integrity: sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==}
|
||||
|
||||
'@simple-git/argv-parser@1.1.1':
|
||||
resolution: {integrity: sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==}
|
||||
|
||||
'@sindresorhus/is@4.6.0':
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -5793,9 +5775,6 @@ packages:
|
||||
simple-get@4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
|
||||
|
||||
simple-git@3.36.0:
|
||||
resolution: {integrity: sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==}
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -7054,14 +7033,6 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@kwsites/file-exists@1.1.1':
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@kwsites/promise-deferred@1.1.1': {}
|
||||
|
||||
'@linear/sdk@82.1.0(graphql@16.13.2)':
|
||||
dependencies:
|
||||
'@graphql-typed-document-node/core': 3.2.0(graphql@16.13.2)
|
||||
@@ -8244,12 +8215,6 @@ snapshots:
|
||||
|
||||
'@sec-ant/readable-stream@0.4.1': {}
|
||||
|
||||
'@simple-git/args-pathspec@1.0.3': {}
|
||||
|
||||
'@simple-git/argv-parser@1.1.1':
|
||||
dependencies:
|
||||
'@simple-git/args-pathspec': 1.0.3
|
||||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@sindresorhus/merge-streams@4.0.0': {}
|
||||
@@ -12349,16 +12314,6 @@ snapshots:
|
||||
once: 1.4.0
|
||||
simple-concat: 1.0.1
|
||||
|
||||
simple-git@3.36.0:
|
||||
dependencies:
|
||||
'@kwsites/file-exists': 1.1.1
|
||||
'@kwsites/promise-deferred': 1.1.1
|
||||
'@simple-git/args-pathspec': 1.0.3
|
||||
'@simple-git/argv-parser': 1.1.1
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
simple-update-notifier@2.0.0:
|
||||
dependencies:
|
||||
semver: 7.7.4
|
||||
|
||||
@@ -665,14 +665,6 @@ export async function scanClaudeUsageFiles(
|
||||
}
|
||||
}
|
||||
|
||||
export function getClaudeProjectsDirectory(): string {
|
||||
return CLAUDE_PROJECTS_DIR
|
||||
}
|
||||
|
||||
export function getClaudeTranscriptsDirectory(): string {
|
||||
return CLAUDE_TRANSCRIPTS_DIR
|
||||
}
|
||||
|
||||
export function createWorktreeRefs(
|
||||
repos: Repo[],
|
||||
worktreesByRepo: Map<string, { path: string; worktreeId: string; displayName: string }[]>
|
||||
|
||||
@@ -1,29 +1,10 @@
|
||||
import { Notification, shell, systemPreferences } from 'electron'
|
||||
import { Notification, shell } from 'electron'
|
||||
|
||||
const ACCESSIBILITY_SETTINGS_URL =
|
||||
'x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility'
|
||||
const DEFAULT_ACCESSIBILITY_INSTRUCTIONS =
|
||||
'System Settings -> Privacy & Security -> Accessibility -> enable Orca'
|
||||
|
||||
const activePermissionNotifications = new Set<Notification>()
|
||||
|
||||
/** Probe accessibility permissions; lazy -- invoked only on first failure path. */
|
||||
export async function checkAccessibilityPermission(): Promise<{
|
||||
ok: boolean
|
||||
instructions?: string
|
||||
}> {
|
||||
if (process.platform !== 'darwin') {
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
try {
|
||||
const ok = systemPreferences.isTrustedAccessibilityClient(false)
|
||||
return ok ? { ok: true } : { ok: false, instructions: DEFAULT_ACCESSIBILITY_INSTRUCTIONS }
|
||||
} catch {
|
||||
return { ok: false, instructions: DEFAULT_ACCESSIBILITY_INSTRUCTIONS }
|
||||
}
|
||||
}
|
||||
|
||||
/** Surface a notification through Orca's existing notification system (do not duplicate UI). */
|
||||
export function notifyPermissionRequired(instructions: string): void {
|
||||
if (!Notification.isSupported()) {
|
||||
|
||||
@@ -113,14 +113,6 @@ function ownerScopeKey(owner: string, ownerType: GitHubProjectOwnerType): string
|
||||
return `${owner}\u0000${ownerType}`
|
||||
}
|
||||
|
||||
/** @internal — test-only */
|
||||
export function _resetProjectViewModuleState(): void {
|
||||
ownerTypeCache.clear()
|
||||
parentFieldRetriedByOwner.clear()
|
||||
parentFieldWarningLoggedByOwner.clear()
|
||||
parentFieldProbeInFlight.clear()
|
||||
}
|
||||
|
||||
// ─── Normalizers ───────────────────────────────────────────────────────
|
||||
|
||||
type RawProjectV2Field = {
|
||||
|
||||
@@ -368,14 +368,6 @@ export async function getWorkItemByProjectRef(
|
||||
}
|
||||
}
|
||||
|
||||
// Why: combined MR + issue list for the Tasks-screen and picker
|
||||
// surfaces. Centralizes the merge logic that TaskPage previously did
|
||||
// inline so the IPC layer has a single function to call. Pagination is
|
||||
// approximate — the v1 contract is "page 1 of perPage MRs + perPage
|
||||
// issues, mixed by updatedAt desc" which is good enough for a typical
|
||||
// project's <100 active items.
|
||||
export type ListWorkItemsState = MRListState
|
||||
|
||||
function mrStateToIssueState(state: MRListState): IssueListState | null {
|
||||
// Why: GitLab issues don't have a 'merged' state. When the user is
|
||||
// filtering MRs to merged, return null so listWorkItems can skip the
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
// Why: extracted from worktrees.ts to keep the main IPC module under the
|
||||
// max-lines threshold. Hooks IPC handlers (check, readIssueCommand,
|
||||
// writeIssueCommand) are self-contained and don't interact with worktree
|
||||
// creation or removal state.
|
||||
|
||||
import { ipcMain } from 'electron'
|
||||
import { join } from 'path'
|
||||
import type { Store } from '../persistence'
|
||||
import { isFolderRepo } from '../../shared/repo-kind'
|
||||
import { getSshFilesystemProvider } from '../providers/ssh-filesystem-dispatch'
|
||||
import {
|
||||
hasHooksFile,
|
||||
hasUnrecognizedOrcaYamlKeys,
|
||||
loadHooks,
|
||||
readIssueCommand,
|
||||
writeIssueCommand
|
||||
} from '../hooks'
|
||||
|
||||
export function registerHooksHandlers(store: Store): void {
|
||||
ipcMain.removeHandler('hooks:check')
|
||||
ipcMain.removeHandler('hooks:readIssueCommand')
|
||||
ipcMain.removeHandler('hooks:writeIssueCommand')
|
||||
|
||||
ipcMain.handle('hooks:check', async (_event, args: { repoId: string }) => {
|
||||
const repo = store.getRepo(args.repoId)
|
||||
if (!repo || isFolderRepo(repo)) {
|
||||
return { hasHooks: false, hooks: null, mayNeedUpdate: false }
|
||||
}
|
||||
|
||||
// Why: remote repos read orca.yaml via the SSH filesystem provider.
|
||||
// Parsing happens in the main process since it's CPU-cheap and avoids
|
||||
// adding YAML parsing to the relay.
|
||||
if (repo.connectionId) {
|
||||
const fsProvider = getSshFilesystemProvider(repo.connectionId)
|
||||
if (!fsProvider) {
|
||||
return { hasHooks: false, hooks: null, mayNeedUpdate: false }
|
||||
}
|
||||
try {
|
||||
const result = await fsProvider.readFile(join(repo.path, '.orca.yaml'))
|
||||
if (result.isBinary) {
|
||||
return { hasHooks: false, hooks: null, mayNeedUpdate: false }
|
||||
}
|
||||
const { parse } = await import('yaml')
|
||||
const parsed = parse(result.content)
|
||||
return { hasHooks: true, hooks: parsed, mayNeedUpdate: false }
|
||||
} catch {
|
||||
return { hasHooks: false, hooks: null, mayNeedUpdate: false }
|
||||
}
|
||||
}
|
||||
|
||||
const has = hasHooksFile(repo.path)
|
||||
const hooks = has ? loadHooks(repo.path) : null
|
||||
// Why: when a newer Orca version adds a top-level key to `orca.yaml`, older
|
||||
// versions that don't recognise it return null and show "could not be parsed".
|
||||
// Detecting well-formed but unrecognised keys lets the UI suggest updating
|
||||
// instead of implying the file is broken.
|
||||
const mayNeedUpdate = has && !hooks && hasUnrecognizedOrcaYamlKeys(repo.path)
|
||||
return {
|
||||
hasHooks: has,
|
||||
hooks,
|
||||
mayNeedUpdate
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('hooks:readIssueCommand', async (_event, args: { repoId: string }) => {
|
||||
const repo = store.getRepo(args.repoId)
|
||||
if (!repo || isFolderRepo(repo)) {
|
||||
return {
|
||||
localContent: null,
|
||||
sharedContent: null,
|
||||
effectiveContent: null,
|
||||
localFilePath: '',
|
||||
source: 'none' as const
|
||||
}
|
||||
}
|
||||
|
||||
if (repo.connectionId) {
|
||||
const fsProvider = getSshFilesystemProvider(repo.connectionId)
|
||||
if (!fsProvider) {
|
||||
return {
|
||||
localContent: null,
|
||||
sharedContent: null,
|
||||
effectiveContent: null,
|
||||
localFilePath: '',
|
||||
source: 'none' as const
|
||||
}
|
||||
}
|
||||
try {
|
||||
const result = await fsProvider.readFile(join(repo.path, '.orca', 'issue-command'))
|
||||
return {
|
||||
localContent: result.isBinary ? null : result.content,
|
||||
sharedContent: null,
|
||||
effectiveContent: result.isBinary ? null : result.content,
|
||||
localFilePath: join(repo.path, '.orca', 'issue-command'),
|
||||
source: 'local' as const
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
localContent: null,
|
||||
sharedContent: null,
|
||||
effectiveContent: null,
|
||||
localFilePath: '',
|
||||
source: 'none' as const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return readIssueCommand(repo.path)
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
'hooks:writeIssueCommand',
|
||||
async (_event, args: { repoId: string; content: string }) => {
|
||||
const repo = store.getRepo(args.repoId)
|
||||
if (!repo || isFolderRepo(repo)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (repo.connectionId) {
|
||||
const fsProvider = getSshFilesystemProvider(repo.connectionId)
|
||||
if (!fsProvider) {
|
||||
return
|
||||
}
|
||||
await fsProvider.writeFile(join(repo.path, '.orca', 'issue-command'), args.content)
|
||||
return
|
||||
}
|
||||
|
||||
writeIssueCommand(repo.path, args.content)
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -114,12 +114,6 @@ export function setActiveSink(sink: TracerSink | null): void {
|
||||
activeSink = sink
|
||||
}
|
||||
|
||||
/** Read the currently-active sink. Used by the bundle path to flush before
|
||||
* collecting traces, and by tests for assertion. */
|
||||
export function getActiveSink(): TracerSink | null {
|
||||
return activeSink
|
||||
}
|
||||
|
||||
/** Get the current parent context, or `undefined` if we are at the top of
|
||||
* the trace tree. Renderer-IPC entry points capture this, embed it in
|
||||
* span-event attributes (so cross-process spans can be visually linked
|
||||
|
||||
@@ -7,14 +7,6 @@ export function makeResponse(body: unknown, status = 200): Response {
|
||||
} as Response
|
||||
}
|
||||
|
||||
export function makeDirent(name: string, isDir: boolean) {
|
||||
return {
|
||||
name,
|
||||
isDirectory: () => isDir,
|
||||
isFile: () => !isDir
|
||||
}
|
||||
}
|
||||
|
||||
export const authJsonGoogle = {
|
||||
google: {
|
||||
type: 'oauth',
|
||||
@@ -45,11 +37,6 @@ export const expiredCreds = {
|
||||
expiry_date: new Date('2026-04-24T11:00:00.000Z').getTime()
|
||||
}
|
||||
|
||||
export const oauth2JsContent = `
|
||||
const OAUTH_CLIENT_ID = 'client-id-123';
|
||||
const OAUTH_CLIENT_SECRET = 'client-secret-456';
|
||||
`
|
||||
|
||||
export const quotaResponse = [
|
||||
{ remainingFraction: 0.75, resetTime: '2026-04-24T13:00:00.000Z', modelId: 'gemini-2.5-pro' },
|
||||
{ remainingFraction: 0.9, resetTime: '2026-04-24T14:00:00.000Z', modelId: 'gemini-2.5-flash' }
|
||||
|
||||
@@ -22,10 +22,6 @@ export const MessageType = {
|
||||
export const KEEPALIVE_SEND_MS = 5_000
|
||||
export const TIMEOUT_MS = 20_000
|
||||
|
||||
/** PTY flow control watermarks (VS Code FlowControlConstants). */
|
||||
export const PTY_FLOW_HIGH_WATERMARK = 100_000
|
||||
export const PTY_FLOW_LOW_WATERMARK = 5_000
|
||||
|
||||
/** Reconnection grace period (default, overridable by relay --grace-time). */
|
||||
export const DEFAULT_GRACE_TIME_MS = 3 * 60 * 60 * 1000 // 3 hours
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type React from 'react'
|
||||
|
||||
export function SettingsToggleSwitchButton({
|
||||
checked,
|
||||
onToggle,
|
||||
ariaLabel
|
||||
}: {
|
||||
checked: boolean
|
||||
onToggle: () => void
|
||||
ariaLabel?: string
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked={checked}
|
||||
aria-label={ariaLabel}
|
||||
onClick={onToggle}
|
||||
className={`relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent transition-colors ${
|
||||
checked ? 'bg-foreground' : 'bg-muted-foreground/30'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`pointer-events-none block size-3.5 rounded-full bg-background shadow-sm transition-transform ${
|
||||
checked ? 'translate-x-4' : 'translate-x-0.5'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { Worktree } from '../../../../shared/types'
|
||||
|
||||
export function LinkedWorktreeItem({
|
||||
worktree,
|
||||
onOpen
|
||||
}: {
|
||||
worktree: Worktree
|
||||
onOpen: () => void
|
||||
}): React.JSX.Element {
|
||||
const branchLabel = worktree.branch.replace(/^refs\/heads\//, '')
|
||||
|
||||
return (
|
||||
<button
|
||||
className="group flex items-center justify-between gap-3 w-full rounded-md border border-border/60 bg-secondary/30 px-3 py-2 text-left transition-colors hover:bg-accent cursor-pointer"
|
||||
onClick={onOpen}
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">{worktree.displayName}</p>
|
||||
{branchLabel !== worktree.displayName && (
|
||||
<p className="text-xs text-muted-foreground truncate mt-0.5">{branchLabel}</p>
|
||||
)}
|
||||
</div>
|
||||
<span className="shrink-0 text-xs font-medium text-muted-foreground group-hover:text-foreground transition-colors">
|
||||
Open
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -123,108 +123,6 @@ export function barColor(leftPct: number): string {
|
||||
return 'bg-red-500'
|
||||
}
|
||||
|
||||
function TooltipWindowSection({
|
||||
w,
|
||||
label
|
||||
}: {
|
||||
w: RateLimitWindow | null
|
||||
label: string
|
||||
}): React.JSX.Element | null {
|
||||
if (!w) {
|
||||
return null
|
||||
}
|
||||
const leftPct = Math.max(0, Math.round(100 - w.usedPercent))
|
||||
const resetLabel = w.resetsAt ? formatResetCountdown(w.resetsAt - Date.now()) : null
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="font-medium text-background">{label}</div>
|
||||
<div className="w-full h-[6px] rounded-full bg-background/20 overflow-hidden">
|
||||
<div
|
||||
className={`h-full rounded-full ${barColor(leftPct)} transition-all duration-300`}
|
||||
style={{ width: `${Math.min(100, Math.max(0, leftPct))}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between text-background/60">
|
||||
<span>{leftPct}% left</span>
|
||||
{resetLabel && <span>{resetLabel}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tooltip content
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function ProviderTooltip({ p }: { p: ProviderRateLimits | null }): React.JSX.Element {
|
||||
if (!p) {
|
||||
return <span className="text-xs text-background/60">No data available</span>
|
||||
}
|
||||
|
||||
const name =
|
||||
p.provider === 'claude'
|
||||
? 'Claude'
|
||||
: p.provider === 'codex'
|
||||
? 'Codex'
|
||||
: p.provider === 'gemini'
|
||||
? 'Gemini'
|
||||
: p.provider === 'opencode-go'
|
||||
? 'OpenCode Go'
|
||||
: p.provider
|
||||
|
||||
if (p.status === 'unavailable') {
|
||||
return (
|
||||
<div className="text-xs w-[200px]">
|
||||
<div className="flex items-center gap-1.5 font-medium text-background">
|
||||
<ProviderIcon provider={p.provider} />
|
||||
{name}
|
||||
</div>
|
||||
<div className="text-background/60">{p.error ?? 'Unavailable'}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (p.status === 'error' && !p.session && !p.weekly && !p.monthly) {
|
||||
return (
|
||||
<div className="text-xs w-[200px]">
|
||||
<div className="flex items-center gap-1.5 font-medium text-background">
|
||||
<ProviderIcon provider={p.provider} />
|
||||
{name}
|
||||
</div>
|
||||
<div className="text-background/60">{p.error ?? 'Unable to fetch usage'}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const updatedAgo = p.updatedAt ? `Updated ${formatTimeAgo(p.updatedAt)}` : 'Not yet updated'
|
||||
|
||||
return (
|
||||
<div className="text-xs w-[200px] space-y-3">
|
||||
{/* Header */}
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5 font-medium text-background text-[13px]">
|
||||
<ProviderIcon provider={p.provider} />
|
||||
{name}
|
||||
</div>
|
||||
<div className="text-background/50">{updatedAgo}</div>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="border-t border-background/15" />
|
||||
|
||||
{getWindowSections(p).map((s) => (
|
||||
<TooltipWindowSection key={s.label} w={s.window} label={s.label} />
|
||||
))}
|
||||
|
||||
{/* Stale data warning — softer label when prior data is still shown */}
|
||||
{p.error ? (
|
||||
<ErrorMessage message={p.error} stale={!!(p.session || p.weekly || p.monthly)} inverted />
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProviderPanel({
|
||||
p,
|
||||
inverted = false,
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import {
|
||||
TerminalStreamOpcode,
|
||||
encodeTerminalStreamFrame,
|
||||
encodeTerminalStreamJson,
|
||||
encodeTerminalStreamText
|
||||
} from '../../../../shared/terminal-stream-protocol'
|
||||
|
||||
export type RemoteRuntimeBinarySender = (bytes: Uint8Array<ArrayBufferLike>) => void
|
||||
|
||||
export function sendRemoteRuntimeTerminalInputFrame(
|
||||
sendBinary: RemoteRuntimeBinarySender | null,
|
||||
streamId: number | null,
|
||||
text: string
|
||||
): boolean {
|
||||
if (!sendBinary || streamId === null) {
|
||||
return false
|
||||
}
|
||||
sendBinary(
|
||||
encodeTerminalStreamFrame({
|
||||
opcode: TerminalStreamOpcode.Input,
|
||||
streamId,
|
||||
seq: 0,
|
||||
payload: encodeTerminalStreamText(text)
|
||||
})
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
export function sendRemoteRuntimeTerminalResizeFrame(
|
||||
sendBinary: RemoteRuntimeBinarySender | null,
|
||||
streamId: number | null,
|
||||
cols: number,
|
||||
rows: number
|
||||
): boolean {
|
||||
if (!sendBinary || streamId === null) {
|
||||
return false
|
||||
}
|
||||
sendBinary(
|
||||
encodeTerminalStreamFrame({
|
||||
opcode: TerminalStreamOpcode.Resize,
|
||||
streamId,
|
||||
seq: 0,
|
||||
payload: encodeTerminalStreamJson({ cols, rows })
|
||||
})
|
||||
)
|
||||
return true
|
||||
}
|
||||
@@ -50,58 +50,6 @@ export function createNewTerminalTab(
|
||||
state.setTabBarOrder(activeWorktreeId, order)
|
||||
}
|
||||
|
||||
export function closeTerminalTab(tabId: string): void {
|
||||
const state = useAppStore.getState()
|
||||
const owningWorktreeEntry = Object.entries(state.tabsByWorktree).find(([, worktreeTabs]) =>
|
||||
worktreeTabs.some((tab) => tab.id === tabId)
|
||||
)
|
||||
const owningWorktreeId = owningWorktreeEntry?.[0] ?? null
|
||||
|
||||
if (!owningWorktreeId) {
|
||||
return
|
||||
}
|
||||
|
||||
const runtimeEnvironmentId = state.settings?.activeRuntimeEnvironmentId?.trim()
|
||||
if (isWebRuntimeSessionActive(runtimeEnvironmentId)) {
|
||||
// Why: paired web tabs are host-owned. Closing locally leaves the host to
|
||||
// re-publish the same stale surface on the next session-tabs snapshot.
|
||||
void closeWebRuntimeSessionTab({
|
||||
worktreeId: owningWorktreeId,
|
||||
tabId,
|
||||
environmentId: runtimeEnvironmentId
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const currentTabs = state.tabsByWorktree[owningWorktreeId] ?? []
|
||||
if (currentTabs.length <= 1) {
|
||||
state.closeTab(tabId)
|
||||
if (state.activeWorktreeId === owningWorktreeId) {
|
||||
// Why: only deactivate the worktree when no tabs of any kind remain.
|
||||
// Editor files are a separate tab type; closing the last terminal tab
|
||||
// should switch to the editor view instead of tearing down the workspace.
|
||||
const worktreeFile = state.openFiles.find((f) => f.worktreeId === owningWorktreeId)
|
||||
if (worktreeFile) {
|
||||
state.setActiveFile(worktreeFile.id)
|
||||
state.setActiveTabType('editor')
|
||||
} else {
|
||||
state.setActiveWorktree(null)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (state.activeWorktreeId === owningWorktreeId && tabId === state.activeTabId) {
|
||||
const currentIndex = currentTabs.findIndex((tab) => tab.id === tabId)
|
||||
const nextTab = currentTabs[currentIndex + 1] ?? currentTabs[currentIndex - 1]
|
||||
if (nextTab) {
|
||||
state.setActiveTab(nextTab.id)
|
||||
}
|
||||
}
|
||||
|
||||
state.closeTab(tabId)
|
||||
}
|
||||
|
||||
export function closeOtherTerminalTabs(tabId: string, activeWorktreeId: string | null): void {
|
||||
if (!activeWorktreeId) {
|
||||
return
|
||||
@@ -190,12 +138,6 @@ export function activateTerminalTab(tabId: string): void {
|
||||
s.setActiveTabType('terminal')
|
||||
}
|
||||
|
||||
export function activateEditorFile(fileId: string): void {
|
||||
const s = useAppStore.getState()
|
||||
s.setActiveFile(fileId)
|
||||
s.setActiveTabType('editor')
|
||||
}
|
||||
|
||||
export function toggleTerminalPaneExpand(tabId: string): void {
|
||||
useAppStore.getState().setActiveTab(tabId)
|
||||
requestAnimationFrame(() => {
|
||||
|
||||
@@ -7,7 +7,7 @@ export const BUILTIN_TERMINAL_THEME_NAMES = getThemeNames()
|
||||
export const DEFAULT_TERMINAL_THEME_DARK = 'Ghostty Default Style Dark'
|
||||
export const DEFAULT_TERMINAL_THEME_LIGHT = 'Builtin Tango Light'
|
||||
export const DEFAULT_TERMINAL_DIVIDER_DARK = '#3f3f46'
|
||||
export const DEFAULT_TERMINAL_DIVIDER_LIGHT = '#d4d4d8'
|
||||
const DEFAULT_TERMINAL_DIVIDER_LIGHT = '#d4d4d8'
|
||||
|
||||
export type EffectiveTerminalAppearance = {
|
||||
mode: 'dark' | 'light'
|
||||
@@ -77,25 +77,6 @@ export function normalizeColor(value: string | undefined, fallback: string): str
|
||||
return trimmed
|
||||
}
|
||||
|
||||
export function buildTerminalFontMatchers(fontFamily: string): string[] {
|
||||
const trimmed = fontFamily.trim()
|
||||
const normalized = trimmed.toLowerCase()
|
||||
const matchers = trimmed ? [trimmed, normalized] : []
|
||||
return Array.from(
|
||||
new Set([
|
||||
...matchers,
|
||||
'sf mono',
|
||||
'sfmono-regular',
|
||||
'menlo',
|
||||
'menlo regular',
|
||||
'dejavu sans mono',
|
||||
'liberation mono',
|
||||
'ubuntu mono',
|
||||
'monospace'
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
export function clampNumber(value: number, min: number, max: number): number {
|
||||
return Math.min(max, Math.max(min, value))
|
||||
}
|
||||
@@ -120,71 +101,4 @@ export function resolvePaneStyleOptions(
|
||||
}
|
||||
}
|
||||
|
||||
export function getCursorStyleSequence(
|
||||
style: 'bar' | 'block' | 'underline',
|
||||
blinking: boolean
|
||||
): string {
|
||||
const code =
|
||||
style === 'block'
|
||||
? blinking
|
||||
? 1
|
||||
: 2
|
||||
: style === 'underline'
|
||||
? blinking
|
||||
? 3
|
||||
: 4
|
||||
: blinking
|
||||
? 5
|
||||
: 6
|
||||
|
||||
return `\u001b[${code} q`
|
||||
}
|
||||
|
||||
export function colorToCss(
|
||||
color: { r: number; g: number; b: number; a?: number } | string | undefined,
|
||||
fallback: string
|
||||
): string {
|
||||
if (!color) {
|
||||
return fallback
|
||||
}
|
||||
if (typeof color === 'string') {
|
||||
return color
|
||||
}
|
||||
const alpha = typeof color.a === 'number' ? color.a / 255 : 1
|
||||
return `rgba(${color.r}, ${color.g}, ${color.b}, ${alpha})`
|
||||
}
|
||||
|
||||
export { isTerminalBackgroundLight } from './terminal-title-contrast'
|
||||
|
||||
const PALETTE_KEYS = [
|
||||
'black',
|
||||
'red',
|
||||
'green',
|
||||
'yellow',
|
||||
'blue',
|
||||
'magenta',
|
||||
'cyan',
|
||||
'white',
|
||||
'brightBlack',
|
||||
'brightRed',
|
||||
'brightGreen',
|
||||
'brightYellow',
|
||||
'brightBlue',
|
||||
'brightMagenta',
|
||||
'brightCyan',
|
||||
'brightWhite'
|
||||
] as const
|
||||
|
||||
export function terminalPalettePreview(theme: ITheme | null): string[] {
|
||||
if (!theme) {
|
||||
return []
|
||||
}
|
||||
const swatches: string[] = []
|
||||
for (const key of PALETTE_KEYS) {
|
||||
const color = theme[key]
|
||||
if (color) {
|
||||
swatches.push(color)
|
||||
}
|
||||
}
|
||||
return swatches
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { GlobalSettings } from '../../../shared/types'
|
||||
import { readRuntimeFileContent, type RuntimeReadableFileContent } from './runtime-file-client'
|
||||
|
||||
export type RemoteReadableFile = {
|
||||
worktreeId: string
|
||||
relativePath: string
|
||||
filePath?: string
|
||||
}
|
||||
|
||||
export type RemoteFileContent = RuntimeReadableFileContent
|
||||
|
||||
export async function readFileFromActiveRuntime(
|
||||
settings: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null | undefined,
|
||||
file: RemoteReadableFile
|
||||
): Promise<RemoteFileContent> {
|
||||
return readRuntimeFileContent({
|
||||
settings,
|
||||
filePath: file.filePath ?? file.relativePath,
|
||||
relativePath: file.relativePath,
|
||||
worktreeId: file.worktreeId
|
||||
})
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
import {
|
||||
TerminalStreamOpcode,
|
||||
decodeTerminalStreamFrame,
|
||||
decodeTerminalStreamText
|
||||
} from '../../../shared/terminal-stream-protocol'
|
||||
import type { GlobalSettings } from '../../../shared/types'
|
||||
import { RuntimeRpcCallError, getActiveRuntimeTarget } from './runtime-rpc-client'
|
||||
import { getRemoteRuntimeTerminalMultiplexer } from './remote-runtime-terminal-multiplexer'
|
||||
@@ -15,20 +10,6 @@ export type RemoteRuntimePtyIdParts = {
|
||||
handle: string
|
||||
}
|
||||
|
||||
export type RuntimeTerminalSubscribeEvent =
|
||||
| {
|
||||
type: 'scrollback' | 'subscribed'
|
||||
streamId?: number | null
|
||||
lines?: string[]
|
||||
truncated?: boolean
|
||||
serialized?: string
|
||||
cols?: number
|
||||
rows?: number
|
||||
}
|
||||
| { type: 'data'; chunk: string }
|
||||
| { type: 'end' }
|
||||
| { type: string; [key: string]: unknown }
|
||||
|
||||
export function toRemoteRuntimePtyId(handle: string, environmentId?: string | null): string {
|
||||
const owner = environmentId?.trim()
|
||||
if (!owner) {
|
||||
@@ -60,18 +41,6 @@ export function getRemoteRuntimePtyEnvironmentId(ptyId: string): string | null {
|
||||
return parseRemoteRuntimePtyId(ptyId)?.environmentId ?? null
|
||||
}
|
||||
|
||||
export function isRuntimeTerminalScrollbackEvent(
|
||||
event: RuntimeTerminalSubscribeEvent
|
||||
): event is Extract<RuntimeTerminalSubscribeEvent, { type: 'scrollback' | 'subscribed' }> {
|
||||
return event.type === 'scrollback' || event.type === 'subscribed'
|
||||
}
|
||||
|
||||
export function isRuntimeTerminalDataEvent(
|
||||
event: RuntimeTerminalSubscribeEvent
|
||||
): event is Extract<RuntimeTerminalSubscribeEvent, { type: 'data' }> {
|
||||
return event.type === 'data' && typeof (event as { chunk?: unknown }).chunk === 'string'
|
||||
}
|
||||
|
||||
export function runtimeTerminalErrorMessage(error: unknown): string {
|
||||
if (error instanceof RuntimeRpcCallError) {
|
||||
return error.message
|
||||
@@ -79,62 +48,6 @@ export function runtimeTerminalErrorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
|
||||
export function readRuntimeTerminalScrollback(event: {
|
||||
serialized?: string
|
||||
lines?: string[]
|
||||
}): string | null {
|
||||
if (event.serialized) {
|
||||
return event.serialized
|
||||
}
|
||||
if (event.lines && event.lines.length > 0) {
|
||||
return `${event.lines.join('\r\n')}\r\n`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function concatBytes(chunks: Uint8Array<ArrayBufferLike>[]): Uint8Array<ArrayBufferLike> {
|
||||
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0)
|
||||
const out = new Uint8Array(total)
|
||||
let offset = 0
|
||||
for (const chunk of chunks) {
|
||||
out.set(chunk, offset)
|
||||
offset += chunk.byteLength
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
export function createRuntimeTerminalBinaryReader(callbacks: {
|
||||
onData: (data: string) => void
|
||||
onSnapshot: (data: string) => void
|
||||
onEnd?: () => void
|
||||
}): (bytes: Uint8Array<ArrayBufferLike>) => void {
|
||||
let snapshotChunks: Uint8Array<ArrayBufferLike>[] = []
|
||||
|
||||
return (bytes) => {
|
||||
const frame = decodeTerminalStreamFrame(bytes)
|
||||
if (!frame) {
|
||||
return
|
||||
}
|
||||
if (frame.opcode === TerminalStreamOpcode.Output) {
|
||||
callbacks.onData(decodeTerminalStreamText(frame.payload))
|
||||
return
|
||||
}
|
||||
if (frame.opcode === TerminalStreamOpcode.SnapshotStart) {
|
||||
snapshotChunks = []
|
||||
return
|
||||
}
|
||||
if (frame.opcode === TerminalStreamOpcode.SnapshotChunk) {
|
||||
snapshotChunks.push(frame.payload)
|
||||
return
|
||||
}
|
||||
if (frame.opcode === TerminalStreamOpcode.SnapshotEnd) {
|
||||
callbacks.onSnapshot(decodeTerminalStreamText(concatBytes(snapshotChunks)))
|
||||
snapshotChunks = []
|
||||
callbacks.onEnd?.()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function subscribeToRuntimeTerminalData(
|
||||
settings: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null | undefined,
|
||||
ptyId: string,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// renderer (preview/tests) and main (actual generation) reach the exact same
|
||||
// string without duplicating the wording.
|
||||
|
||||
export const COMMIT_MESSAGE_BASE_PROMPT = `You are generating a single git commit message.
|
||||
const COMMIT_MESSAGE_BASE_PROMPT = `You are generating a single git commit message.
|
||||
Read the staged diff below and produce the message.
|
||||
|
||||
Rules:
|
||||
|
||||
@@ -82,8 +82,4 @@ export function getFeatureWallMediaTile(id: FeatureWallMediaTileId): FeatureWall
|
||||
return TILE_BY_ID.get(id) ?? null
|
||||
}
|
||||
|
||||
export function getFeatureWallWorkflow(id: FeatureWallWorkflowId): FeatureWallWorkflow | null {
|
||||
return FEATURE_WALL_WORKFLOWS.find((w) => w.id === id) ?? null
|
||||
}
|
||||
|
||||
export const DEFAULT_FEATURE_WALL_WORKFLOW_ID: FeatureWallWorkflowId = 'workspaces'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
export const APP_STAR_SOURCE_VALUES = ['star_nag', 'settings', 'landing'] as const
|
||||
const APP_STAR_SOURCE_VALUES = ['star_nag', 'settings', 'landing'] as const
|
||||
|
||||
// Why: renderer-originated IPC is untrusted, so main validates against this
|
||||
// closed enum before attaching source context to successful star telemetry.
|
||||
|
||||
@@ -991,12 +991,6 @@ function normalizeKeybindingArrayWithOptions(
|
||||
return normalized
|
||||
}
|
||||
|
||||
export function normalizeKeybindingArray(
|
||||
input: readonly string[]
|
||||
): KeybindingValidationResult | string[] {
|
||||
return normalizeKeybindingArrayWithOptions(input)
|
||||
}
|
||||
|
||||
function normalizeOptionsForAction(actionId: KeybindingActionId): NormalizeKeybindingOptions {
|
||||
return {
|
||||
allowBareKeybindings: DEFINITIONS_BY_ID.get(actionId)?.allowBareKeybindings === true
|
||||
@@ -1438,31 +1432,6 @@ export function formatKeybindingList(
|
||||
.join(', ')
|
||||
}
|
||||
|
||||
export function formatElectronAccelerator(binding: string): string | null {
|
||||
const parsed = parseKeybinding(binding)
|
||||
if (!parsed) {
|
||||
return null
|
||||
}
|
||||
const parts: string[] = []
|
||||
if (parsed.mod) {
|
||||
parts.push('CmdOrCtrl')
|
||||
}
|
||||
if (parsed.meta) {
|
||||
parts.push('Command')
|
||||
}
|
||||
if (parsed.control) {
|
||||
parts.push('Control')
|
||||
}
|
||||
if (parsed.alt) {
|
||||
parts.push('Alt')
|
||||
}
|
||||
if (parsed.shift) {
|
||||
parts.push('Shift')
|
||||
}
|
||||
parts.push(formatElectronKeyToken(parsed.key))
|
||||
return parts.join('+')
|
||||
}
|
||||
|
||||
function formatKeyToken(token: string): string {
|
||||
const labels: Record<string, string> = {
|
||||
BracketLeft: '[',
|
||||
@@ -1497,35 +1466,6 @@ function formatKeyToken(token: string): string {
|
||||
return labels[token] ?? token
|
||||
}
|
||||
|
||||
function formatElectronKeyToken(token: string): string {
|
||||
const labels: Record<string, string> = {
|
||||
BracketLeft: '[',
|
||||
BracketRight: ']',
|
||||
Minus: '-',
|
||||
Underscore: '_',
|
||||
Equal: '=',
|
||||
Plus: 'Plus',
|
||||
Comma: ',',
|
||||
Period: '.',
|
||||
Slash: '/',
|
||||
Backslash: '\\',
|
||||
Semicolon: ';',
|
||||
Quote: "'",
|
||||
Backquote: '`',
|
||||
ArrowLeft: 'Left',
|
||||
ArrowRight: 'Right',
|
||||
ArrowUp: 'Up',
|
||||
ArrowDown: 'Down',
|
||||
PageUp: 'PageUp',
|
||||
PageDown: 'PageDown',
|
||||
NumpadAdd: 'numadd',
|
||||
NumpadSubtract: 'numsub',
|
||||
Escape: 'Esc',
|
||||
Space: 'Space'
|
||||
}
|
||||
return labels[token] ?? token
|
||||
}
|
||||
|
||||
export function findKeybindingConflicts(
|
||||
platform: NodeJS.Platform,
|
||||
overrides?: KeybindingOverrides
|
||||
@@ -1563,7 +1503,3 @@ export function findKeybindingConflicts(
|
||||
actionIds
|
||||
}))
|
||||
}
|
||||
|
||||
export function getDefaultKeybindingOverrides(): KeybindingOverrides {
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,6 @@ export const NATIVE_FILE_DROP_TARGET = {
|
||||
projectSidebar: 'project-sidebar'
|
||||
} as const
|
||||
|
||||
export type NativeFileDropTarget =
|
||||
(typeof NATIVE_FILE_DROP_TARGET)[keyof typeof NATIVE_FILE_DROP_TARGET]
|
||||
|
||||
export type NativeDropResolution =
|
||||
| { target: typeof NATIVE_FILE_DROP_TARGET.editor }
|
||||
| { target: typeof NATIVE_FILE_DROP_TARGET.terminal; tabId?: string }
|
||||
@@ -36,7 +33,7 @@ export type NativeFileDropPathEntry = {
|
||||
terminalTabId?: string
|
||||
}
|
||||
|
||||
export function getDataTransferTypes(
|
||||
function getDataTransferTypes(
|
||||
types: Iterable<string> | ArrayLike<string> | null | undefined
|
||||
): string[] {
|
||||
return types ? Array.from(types) : []
|
||||
|
||||
@@ -2,7 +2,7 @@ export const DEFAULT_TERMINAL_FONT_WEIGHT = 500
|
||||
export const TERMINAL_FONT_WEIGHT_MIN = 100
|
||||
export const TERMINAL_FONT_WEIGHT_MAX = 900
|
||||
export const TERMINAL_FONT_WEIGHT_STEP = 100
|
||||
export const DEFAULT_TERMINAL_FONT_WEIGHT_BOLD = 700
|
||||
const DEFAULT_TERMINAL_FONT_WEIGHT_BOLD = 700
|
||||
|
||||
export function normalizeTerminalFontWeight(fontWeight: number | null | undefined): number {
|
||||
const numericFontWeight = typeof fontWeight === 'number' ? fontWeight : NaN
|
||||
|
||||
@@ -13,7 +13,7 @@ const MAX_QUICK_COMMAND_REPO_ID_LENGTH = 200
|
||||
const MAX_QUICK_COMMAND_TEXT_LENGTH = 4000
|
||||
const REMOVED_PRESET_IDS = new Set(['default-pwd', 'default-git-status'])
|
||||
|
||||
export const DEFAULT_TERMINAL_QUICK_COMMANDS: TerminalQuickCommand[] = []
|
||||
const DEFAULT_TERMINAL_QUICK_COMMANDS: TerminalQuickCommand[] = []
|
||||
|
||||
export function getDefaultTerminalQuickCommands(): TerminalQuickCommand[] {
|
||||
return DEFAULT_TERMINAL_QUICK_COMMANDS.map((command) => ({ ...command }))
|
||||
@@ -60,12 +60,6 @@ export function isTerminalAgentQuickCommand(
|
||||
return getTerminalQuickCommandAction(command) === 'agent-prompt'
|
||||
}
|
||||
|
||||
export function isTerminalCommandQuickCommand(
|
||||
command: TerminalQuickCommand
|
||||
): command is TerminalCommandQuickCommand {
|
||||
return getTerminalQuickCommandAction(command) === 'terminal-command'
|
||||
}
|
||||
|
||||
export function supportsTerminalAgentQuickCommand(
|
||||
agent: unknown
|
||||
): agent is TerminalAgentQuickCommand['agent'] {
|
||||
|
||||
@@ -7,6 +7,7 @@ export const TUI_AGENT_AUTO_PICK_ORDER = [
|
||||
'claude',
|
||||
'openclaude',
|
||||
'codex',
|
||||
'openclaude',
|
||||
'grok',
|
||||
'copilot',
|
||||
'opencode',
|
||||
|
||||
Reference in New Issue
Block a user