mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(mobile): phone terminal and chat tab names match the desktop (#31384)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { RuntimeMobileSessionTerminalTab } from '../../shared/runtime-types'
|
||||
import type { TerminalTab } from '../../shared/terminal-tab-types'
|
||||
import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types'
|
||||
import { resolveTerminalTabTitle } from '../../shared/tab-title-resolution'
|
||||
import {
|
||||
readCanonicalPtyOwners,
|
||||
resolveCanonicalPtyClaim
|
||||
@@ -44,7 +45,8 @@ function withoutAbsorbedLegacyRows(
|
||||
export function buildHeadlessMobileSessionTerminalTabs(
|
||||
worktreeId: string,
|
||||
persistedTabs: readonly TerminalTab[],
|
||||
session: WorkspaceSessionState
|
||||
session: WorkspaceSessionState,
|
||||
generatedTitlesEnabled = false
|
||||
): RuntimeMobileSessionTerminalTab[] {
|
||||
const { rows, selection } = withoutAbsorbedLegacyRows(worktreeId, persistedTabs, session)
|
||||
return rows
|
||||
@@ -55,14 +57,19 @@ export function buildHeadlessMobileSessionTerminalTabs(
|
||||
if (leafIds.length === 0) {
|
||||
leafIds.push(deriveHeadlessLegacyTerminalLeafId(tab.id))
|
||||
}
|
||||
// As the window publishes them: tab-wide names describe a sole pane.
|
||||
const tabWide = leafIds.length === 1
|
||||
return leafIds.flatMap((leafId) => {
|
||||
const ptyId = layout?.ptyIdsByLeafId?.[leafId] ?? (leafIds.length === 1 ? tab.ptyId : null)
|
||||
const title =
|
||||
tab.customTitle?.trim() ||
|
||||
tab.generatedTitle?.trim() ||
|
||||
tab.title?.trim() ||
|
||||
tab.defaultTitle?.trim() ||
|
||||
`Terminal ${index + 1}`
|
||||
const fallbackTitle = tab.defaultTitle?.trim() || `Terminal ${index + 1}`
|
||||
const liveTitle = layout?.titlesByLeafId?.[leafId]?.trim() || tab.title?.trim()
|
||||
const title = tabWide
|
||||
? resolveTerminalTabTitle(
|
||||
{ ...tab, title: liveTitle },
|
||||
generatedTitlesEnabled,
|
||||
fallbackTitle
|
||||
)
|
||||
: tab.customTitle?.trim() || liveTitle || fallbackTitle
|
||||
return [
|
||||
{
|
||||
type: 'terminal' as const,
|
||||
@@ -71,6 +78,15 @@ export function buildHeadlessMobileSessionTerminalTabs(
|
||||
leafId,
|
||||
title,
|
||||
customTitle: tab.customTitle?.trim() || null,
|
||||
...(tabWide && tab.quickCommandLabel?.trim()
|
||||
? { quickCommandLabel: tab.quickCommandLabel.trim() }
|
||||
: {}),
|
||||
...(tabWide && tab.aiVaultTitle?.title.trim()
|
||||
? { aiVaultTitle: tab.aiVaultTitle }
|
||||
: {}),
|
||||
...(tabWide && generatedTitlesEnabled && tab.generatedTitle?.trim()
|
||||
? { generatedTitle: tab.generatedTitle.trim() }
|
||||
: {}),
|
||||
...(ptyId ? { ptyId } : {}),
|
||||
...(tab.startupCwd ? { startupCwd: tab.startupCwd } : {}),
|
||||
...(tab.launchAgent ? { launchAgent: tab.launchAgent } : {}),
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// A phone shows the name the desktop shows for a tab (#8705, #30538).
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { getDefaultWorkspaceSession } from '../../shared/constants'
|
||||
import type { ExecutionHostId } from '../../shared/execution-host'
|
||||
import type { Tab } from '../../shared/tab-types'
|
||||
import type { TerminalTab } from '../../shared/terminal-tab-types'
|
||||
import type { WorkspaceSessionState } from '../../shared/workspace-session-state-types'
|
||||
import { setStructuredAgentSessionHost } from '../native-chat/agent-session-wire/structured-agent-session-registry'
|
||||
import { buildHeadlessMobileSessionTerminalTabs } from './mobile-session-terminal-projection'
|
||||
|
||||
// Fragments stay side-effect ordered: mocks, then lifecycle, then fixtures.
|
||||
const { OrcaRuntimeService } = await import('./orca-runtime-test-mocks.spec')
|
||||
await import('./orca-runtime-test-lifecycle.spec')
|
||||
const { store, TEST_WORKTREE_ID, HEADLESS_LEAF_ID, makeRuntimeStoreWithWorkspaceSession } =
|
||||
await import('./orca-runtime-test-fixtures.spec')
|
||||
|
||||
afterEach(() => setStructuredAgentSessionHost(null))
|
||||
|
||||
// #8705: a chat tab renamed on the desktop.
|
||||
it("publishes the desktop's chat tab name over the saved name, and the saved name once cleared", () => {
|
||||
let session: WorkspaceSessionState = getDefaultWorkspaceSession()
|
||||
const listeners = new Set<(hostId: ExecutionHostId | null) => void>()
|
||||
const runtime = new OrcaRuntimeService({
|
||||
...store,
|
||||
getWorkspaceSession: () => session,
|
||||
onWorkspaceSessionWritten: (listener: (hostId: ExecutionHostId | null) => void) => {
|
||||
listeners.add(listener)
|
||||
return () => {
|
||||
listeners.delete(listener)
|
||||
}
|
||||
}
|
||||
})
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: title projection reads only the record from this host stub.
|
||||
setStructuredAgentSessionHost({
|
||||
deps: {
|
||||
store: {
|
||||
getRecord: () => ({
|
||||
location: { workspaceId: TEST_WORKTREE_ID },
|
||||
conversationName: 'auth/login'
|
||||
})
|
||||
}
|
||||
}
|
||||
} as never)
|
||||
runtime.projectStructuredAgentSessionTab({
|
||||
activate: true,
|
||||
sessionId: 'session-1',
|
||||
workspaceId: TEST_WORKTREE_ID,
|
||||
agent: 'claude'
|
||||
})
|
||||
const titles: string[] = []
|
||||
runtime.onMobileSessionTabsChanged((snapshot) => {
|
||||
const tab = snapshot.tabs.find((candidate) => candidate.type === 'agent-session')
|
||||
if (tab) {
|
||||
titles.push(tab.title)
|
||||
}
|
||||
})
|
||||
const saveChatLabel = (customLabel: string | null): void => {
|
||||
const chat: Tab = {
|
||||
id: 'chat-tab',
|
||||
entityId: 'session-1',
|
||||
groupId: 'group-1',
|
||||
worktreeId: TEST_WORKTREE_ID,
|
||||
contentType: 'agent-session',
|
||||
label: 'Claude Chat',
|
||||
customLabel,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 0
|
||||
}
|
||||
session = { ...session, unifiedTabs: { [TEST_WORKTREE_ID]: [chat] } }
|
||||
listeners.forEach((listener) => listener(null))
|
||||
}
|
||||
|
||||
saveChatLabel('Release notes')
|
||||
saveChatLabel(null)
|
||||
|
||||
expect(titles).toEqual(['Release notes', 'auth/login'])
|
||||
})
|
||||
|
||||
// #30538: a headless host ranks a saved tab's names as the window does.
|
||||
async function listRestartedHeadlessCursorTab(
|
||||
generatedTitlesEnabled: boolean,
|
||||
liveFrame: string | null
|
||||
): Promise<unknown> {
|
||||
const { runtimeStore, getSession, setSession } = makeRuntimeStoreWithWorkspaceSession(
|
||||
getDefaultWorkspaceSession()
|
||||
)
|
||||
const runtime = new OrcaRuntimeService({
|
||||
...runtimeStore,
|
||||
getSettings: () => ({
|
||||
...runtimeStore.getSettings(),
|
||||
tabAutoGenerateTitle: generatedTitlesEnabled
|
||||
})
|
||||
})
|
||||
runtime.setPtyController({
|
||||
spawn: vi.fn().mockResolvedValue({ id: 'cursor-pty' }),
|
||||
write: () => true,
|
||||
kill: () => true,
|
||||
getForegroundProcess: async () => null
|
||||
})
|
||||
await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, {
|
||||
tabId: 'cursor-tab',
|
||||
leafId: HEADLESS_LEAF_ID,
|
||||
launchAgent: 'cursor'
|
||||
})
|
||||
const session = getSession()
|
||||
setSession({
|
||||
...session,
|
||||
tabsByWorktree: {
|
||||
...session.tabsByWorktree,
|
||||
[TEST_WORKTREE_ID]: (session.tabsByWorktree[TEST_WORKTREE_ID] ?? []).map((tab) =>
|
||||
tab.id === 'cursor-tab' ? { ...tab, title: 'bash', generatedTitle: 'Fix tab titles' } : tab
|
||||
)
|
||||
}
|
||||
})
|
||||
// Rebuilt from the saved session, as a restarted headless host does.
|
||||
runtime.hydrateHeadlessMobileSessionTabsFromWorkspaceSession(TEST_WORKTREE_ID, { force: true })
|
||||
if (liveFrame) {
|
||||
runtime.onPtyData('cursor-pty', `\x1b]0;${liveFrame}\x07`, Date.now())
|
||||
}
|
||||
return (await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)).tabs[0]
|
||||
}
|
||||
|
||||
it("publishes a headless tab's generated name over the agent's live frames", async () => {
|
||||
expect(await listRestartedHeadlessCursorTab(true, '⠋ Cursor Agent')).toEqual(
|
||||
expect.objectContaining({ title: 'Fix tab titles' })
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps a headless tab on its saved title while generated names are off', async () => {
|
||||
expect(await listRestartedHeadlessCursorTab(false, null)).toEqual(
|
||||
expect.objectContaining({ title: 'bash' })
|
||||
)
|
||||
})
|
||||
|
||||
const LEAF_A = '11111111-1111-4111-8111-111111111111'
|
||||
const LEAF_B = '22222222-2222-4222-8222-222222222222'
|
||||
|
||||
it("keeps a headless split's tab-wide names off its panes, as the window does", () => {
|
||||
const tab: TerminalTab = {
|
||||
id: 'split-tab',
|
||||
ptyId: null,
|
||||
worktreeId: TEST_WORKTREE_ID,
|
||||
title: 'bash',
|
||||
quickCommandLabel: 'Run tests',
|
||||
customTitle: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 0
|
||||
}
|
||||
const session: WorkspaceSessionState = {
|
||||
...getDefaultWorkspaceSession(),
|
||||
terminalLayoutsByTabId: {
|
||||
'split-tab': {
|
||||
root: {
|
||||
type: 'split',
|
||||
direction: 'vertical',
|
||||
first: { type: 'leaf', leafId: LEAF_A },
|
||||
second: { type: 'leaf', leafId: LEAF_B }
|
||||
},
|
||||
activeLeafId: LEAF_A,
|
||||
expandedLeafId: null,
|
||||
titlesByLeafId: { [LEAF_A]: 'api-server', [LEAF_B]: 'bash' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rows = buildHeadlessMobileSessionTerminalTabs(TEST_WORKTREE_ID, [tab], session, true)
|
||||
|
||||
expect(rows.map((row) => [row.title, row.quickCommandLabel])).toEqual([
|
||||
['api-server', undefined],
|
||||
['bash', undefined]
|
||||
])
|
||||
})
|
||||
@@ -89,7 +89,7 @@ describe('OrcaRuntimeService', () => {
|
||||
expect(suppressed?.type === 'terminal' && suppressed.agentStatus?.terminalTitle).toBeUndefined()
|
||||
})
|
||||
|
||||
it('publishes a renamed tab over the live agent title, and the live title once cleared', async () => {
|
||||
it("publishes the desktop's tab name over the live agent title, and the live title without one", async () => {
|
||||
const runtime = new OrcaRuntimeService(store)
|
||||
runtime.setPtyController({
|
||||
spawn: vi.fn().mockResolvedValue({ id: 'renamed-pty' }),
|
||||
@@ -100,7 +100,10 @@ describe('OrcaRuntimeService', () => {
|
||||
const leafId = HEADLESS_LEAF_ID
|
||||
await runtime.createTerminal(`id:${TEST_WORKTREE_ID}`, { tabId: 'renamed-tab', leafId })
|
||||
runtime.attachWindow(1)
|
||||
const publish = (snapshotVersion: number, customTitle: string | null) =>
|
||||
const publish = (
|
||||
snapshotVersion: number,
|
||||
names: { customTitle: string | null; generatedTitle?: string }
|
||||
) =>
|
||||
runtime.syncWindowGraph(1, {
|
||||
tabs: [
|
||||
{
|
||||
@@ -127,20 +130,25 @@ describe('OrcaRuntimeService', () => {
|
||||
parentTabId: 'renamed-tab',
|
||||
leafId,
|
||||
title: '✳ Claude Code',
|
||||
customTitle,
|
||||
...names,
|
||||
isActive: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
publish(1, 'Build fixer')
|
||||
publish(1, { customTitle: 'Build fixer' })
|
||||
runtime.onPtyData('renamed-pty', '\x1b]0;⠂ Claude Code\x07', 124)
|
||||
|
||||
const renamed = await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)
|
||||
expect(renamed.tabs[0]).toEqual(expect.objectContaining({ title: 'Build fixer' }))
|
||||
|
||||
publish(2, null)
|
||||
// #30538: a generated name the desktop shows outranks every OSC frame, as it does there.
|
||||
publish(2, { customTitle: null, generatedTitle: 'Fix tab titles' })
|
||||
const generated = await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)
|
||||
expect(generated.tabs[0]).toEqual(expect.objectContaining({ title: 'Fix tab titles' }))
|
||||
|
||||
publish(3, { customTitle: null })
|
||||
const cleared = await runtime.listMobileSessionTabs(`id:${TEST_WORKTREE_ID}`)
|
||||
expect(cleared.tabs[0]).toEqual(expect.objectContaining({ title: '⠂ Claude Code' }))
|
||||
})
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
RuntimeMobileSessionTabsSnapshot
|
||||
} from '../../shared/runtime-types'
|
||||
import { isTerminalLeafId, makePaneKey } from '../../shared/stable-pane-id'
|
||||
import { resolveTerminalTabTitle } from '../../shared/tab-title-resolution'
|
||||
import { indexAgentStatusRowsByPaneKey } from '../agent-hooks/agent-status-pane-index'
|
||||
import {
|
||||
renewRuntimeMobileAgentStatusFromPtyTitle,
|
||||
@@ -180,10 +181,10 @@ export function projectRuntimeMobileSessionTabs(
|
||||
ownerRecord?.agent ?? liveLeafPty?.foregroundAgent ?? pty?.foregroundAgent ?? null
|
||||
const ownerOptions = { ownerIsLaunch: ownerRecord?.ownerIsLaunch === true }
|
||||
const liveTitle = trackerOnlyTitle ?? leafTitle ?? ptyTitle ?? syncedTab?.title ?? tab.title
|
||||
// Why: agents repaint their title constantly, so a user's rename must outrank it.
|
||||
const title =
|
||||
tab.customTitle?.trim() ||
|
||||
normalizeCompatibleAgentTitleForOwner(liveTitle, ownerAgent, ownerOptions)
|
||||
const agentTitle = normalizeCompatibleAgentTitleForOwner(liveTitle, ownerAgent, ownerOptions)
|
||||
// Why: the desktop's ranking over main's fresher live title; the window sends a generated
|
||||
// name only while the desktop shows it, so it is passed as enabled.
|
||||
const title = resolveTerminalTabTitle({ ...tab, title: agentTitle }, true, agentTitle)
|
||||
const liveTitleEvidence = leafTitle ?? ptyTitle
|
||||
// Why: renderer status can precede hook session identity, leaving native chat with no transcript address.
|
||||
const rendererStatusAgent =
|
||||
|
||||
+36
-8
@@ -28,8 +28,25 @@ import {
|
||||
retitleStructuredConversationTab,
|
||||
titleStructuredConversationTabs
|
||||
} from '../../structured-conversation-tab-title'
|
||||
import { structuredChatDisplayName } from '../../../../shared/structured-chat-row-name'
|
||||
|
||||
export abstract class OrcaRuntimeWithPruneMobileSessionTabGroupLayout extends OrcaRuntimeWithScheduleMobileSessionTabsChanged {
|
||||
constructor(
|
||||
...args: ConstructorParameters<typeof OrcaRuntimeWithScheduleMobileSessionTabsChanged>
|
||||
) {
|
||||
super(...args)
|
||||
// Why: a desktop rename of a chat tab reaches main only through the window's saved session.
|
||||
this.store?.onWorkspaceSessionWritten?.(() => {
|
||||
for (const [workspaceId, snapshot] of this.mobileSessionTabsByWorktree) {
|
||||
for (const tab of snapshot.tabs) {
|
||||
if (tab.type === 'agent-session') {
|
||||
this.refreshStructuredConversationTabTitle(workspaceId, tab.sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected pruneMobileSessionTabGroupLayout(
|
||||
layout: TabGroupLayoutNode | null | undefined,
|
||||
validGroupIds: ReadonlySet<string>
|
||||
@@ -79,11 +96,9 @@ export abstract class OrcaRuntimeWithPruneMobileSessionTabGroupLayout extends Or
|
||||
protected toMobileSessionTabsResult(
|
||||
snapshot: RuntimeMobileSessionTabsSnapshot
|
||||
): RuntimeMobileSessionTabsResult {
|
||||
const host = getStructuredAgentSessionHost()
|
||||
snapshot = titleStructuredConversationTabs(snapshot, (sessionId) => {
|
||||
const record = host?.deps?.store?.getRecord(sessionId)
|
||||
return record?.location.workspaceId === snapshot.worktree ? record.conversationName : null
|
||||
})
|
||||
snapshot = titleStructuredConversationTabs(snapshot, (sessionId) =>
|
||||
this.structuredConversationTabName(snapshot.worktree, sessionId)
|
||||
)
|
||||
return projectRuntimeMobileSessionTabs(snapshot, this.getMobileSessionProjectionHost())
|
||||
}
|
||||
|
||||
@@ -92,14 +107,27 @@ export abstract class OrcaRuntimeWithPruneMobileSessionTabGroupLayout extends Or
|
||||
if (!snapshot) {
|
||||
return
|
||||
}
|
||||
const record = getStructuredAgentSessionHost()?.deps?.store?.getRecord(sessionId)
|
||||
const name = record?.location.workspaceId === workspaceId ? record.conversationName : null
|
||||
const next = retitleStructuredConversationTab(snapshot, sessionId, name)
|
||||
const next = retitleStructuredConversationTab(
|
||||
snapshot,
|
||||
sessionId,
|
||||
this.structuredConversationTabName(workspaceId, sessionId)
|
||||
)
|
||||
if (next) {
|
||||
this.emitMobileSessionTabsSnapshot(this.storeMobileSessionSnapshot(workspaceId, next))
|
||||
}
|
||||
}
|
||||
|
||||
/** The desktop tab's rename, else the chat's saved name; null leaves the agent's default label. */
|
||||
private structuredConversationTabName(workspaceId: string, sessionId: string): string | null {
|
||||
const record = getStructuredAgentSessionHost()?.deps?.store?.getRecord(sessionId)
|
||||
const customLabel = this.getWorkspaceSessionForWorktree(workspaceId)?.unifiedTabs?.[
|
||||
workspaceId
|
||||
]?.find((tab) => tab.contentType === 'agent-session' && tab.entityId === sessionId)?.customLabel
|
||||
const conversationName =
|
||||
record?.location.workspaceId === workspaceId ? record.conversationName : null
|
||||
return structuredChatDisplayName(customLabel, conversationName, '') || null
|
||||
}
|
||||
|
||||
protected getMobileSessionProjectionHost(): RuntimeMobileSessionProjectionHost {
|
||||
return {
|
||||
tabs: this.tabs,
|
||||
|
||||
@@ -212,7 +212,8 @@ export abstract class OrcaRuntimeWithHydrateHeadlessMobileSessionTabsFromWorkspa
|
||||
const terminalTabs = buildHeadlessMobileSessionTerminalTabs(
|
||||
entryWorktreeId,
|
||||
persistedTabs,
|
||||
session
|
||||
session,
|
||||
this.store?.getSettings?.()?.tabAutoGenerateTitle === true
|
||||
).filter(
|
||||
(tab) =>
|
||||
!runtimeOwnedOnly ||
|
||||
|
||||
@@ -110,6 +110,12 @@ export function buildMobileTerminalSurfaceTabs(
|
||||
...(tabWideFallbackSafe && terminal.quickCommandLabel?.trim()
|
||||
? { quickCommandLabel: terminal.quickCommandLabel.trim() }
|
||||
: {}),
|
||||
...(tabWideFallbackSafe && terminal.aiVaultTitle?.title.trim()
|
||||
? { aiVaultTitle: terminal.aiVaultTitle }
|
||||
: {}),
|
||||
...(tabWideFallbackSafe && inputs.generatedTitlesEnabled && terminal.generatedTitle?.trim()
|
||||
? { generatedTitle: terminal.generatedTitle.trim() }
|
||||
: {}),
|
||||
parentTabId: terminal.id,
|
||||
leafId,
|
||||
ptyId,
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { AgentStatusEntry } from './agent-status-types'
|
||||
import type { BrowserCertificateFailure, BrowserLoadError } from './browser-workspace-types'
|
||||
import type { RuntimeBrowserPlacement } from './runtime-browser-placement'
|
||||
import type { TerminalColorOverrides } from './terminal-color-overrides'
|
||||
import type { TerminalLayoutSnapshot } from './terminal-tab-types'
|
||||
import type { TerminalLayoutSnapshot, TerminalTab } from './terminal-tab-types'
|
||||
import type { TuiAgent } from './tui-agent'
|
||||
import type { StructuredAgentId } from './agent-session-provider-handle'
|
||||
|
||||
@@ -14,6 +14,10 @@ export type RuntimeMobileSessionTerminalTab = {
|
||||
* client keeps its own record then rather than reading absence as a cleared name. */
|
||||
customTitle?: string | null
|
||||
quickCommandLabel?: string | null
|
||||
/** Window-to-main only: the names the desktop ranks above the live title. `generatedTitle` is
|
||||
* sent only while the desktop shows generated names. */
|
||||
aiVaultTitle?: TerminalTab['aiVaultTitle']
|
||||
generatedTitle?: string | null
|
||||
parentTabId: string
|
||||
leafId: string
|
||||
ptyId?: string | null
|
||||
|
||||
@@ -3,10 +3,9 @@ import type { TerminalTab } from './terminal-tab-types'
|
||||
import { isMeaningfulOpenCodeTerminalTitle } from './opencode-terminal-title'
|
||||
|
||||
export function resolveTerminalTabTitle(
|
||||
tab: Pick<
|
||||
TerminalTab,
|
||||
'customTitle' | 'quickCommandLabel' | 'aiVaultTitle' | 'generatedTitle' | 'title'
|
||||
>,
|
||||
tab: Pick<TerminalTab, 'quickCommandLabel' | 'aiVaultTitle' | 'generatedTitle' | 'title'> & {
|
||||
customTitle?: string | null
|
||||
},
|
||||
generatedTitlesEnabled: boolean,
|
||||
fallback = ''
|
||||
): string {
|
||||
|
||||
@@ -215,6 +215,10 @@ function terminalRows(tab: LayoutTerminalTab, scope: Scope): RuntimeMobileSessio
|
||||
...(tabWide && tab.quickCommandLabel?.trim()
|
||||
? { quickCommandLabel: tab.quickCommandLabel.trim() }
|
||||
: {}),
|
||||
...(tabWide && tab.aiVaultTitle?.title.trim() ? { aiVaultTitle: tab.aiVaultTitle } : {}),
|
||||
...(tabWide && scope.generatedTitlesEnabled && tab.generatedTitle?.trim()
|
||||
? { generatedTitle: tab.generatedTitle.trim() }
|
||||
: {}),
|
||||
parentTabId: tab.entityId,
|
||||
leafId,
|
||||
...(leaf?.ptyId ? { ptyId: leaf.ptyId } : {}),
|
||||
|
||||
Reference in New Issue
Block a user