feat: allow hiding workspace card metadata (#2701)
This commit is contained in:
parent
21b30bc513
commit
ace241c43f
|
|
@ -1670,18 +1670,11 @@ describe('Store', () => {
|
|||
expect(ui.dismissedUpdateVersion).toBeNull()
|
||||
})
|
||||
|
||||
it('updateUI restores retired card properties from direct UI writes', async () => {
|
||||
it('updateUI restores fixed card properties from direct UI writes', async () => {
|
||||
const store = await createStore()
|
||||
store.updateUI({ worktreeCardProperties: ['inline-agents'] })
|
||||
|
||||
expect(store.getUI().worktreeCardProperties).toEqual([
|
||||
'status',
|
||||
'unread',
|
||||
'issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'inline-agents'
|
||||
])
|
||||
expect(store.getUI().worktreeCardProperties).toEqual(['status', 'unread', 'inline-agents'])
|
||||
})
|
||||
|
||||
it('persists updater reminder metadata in UI state', async () => {
|
||||
|
|
@ -2083,8 +2076,11 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
expect(store.getUI()._inlineAgentsDefaultedForExperiment).toBe(true)
|
||||
expect(store.getUI()._inlineAgentsDefaultedForAllUsers).toBe(true)
|
||||
expect(store.getUI()._expandedWorktreeCardPropertiesDefaulted).toBe(true)
|
||||
})
|
||||
|
||||
it('adds inline-agents for users who launched a prior RC with the experiment off', async () => {
|
||||
|
|
@ -2108,7 +2104,10 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
expect(store.getUI()._inlineAgentsDefaultedForAllUsers).toBe(true)
|
||||
expect(store.getUI()._expandedWorktreeCardPropertiesDefaulted).toBe(true)
|
||||
})
|
||||
|
||||
it('respects a deliberate post-migration uncheck', async () => {
|
||||
|
|
@ -2129,9 +2128,11 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).not.toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
})
|
||||
|
||||
it('leaves cardProps alone when inline-agents is already present', async () => {
|
||||
it('adds split-out default card properties without duplicating inline-agents', async () => {
|
||||
writeDataFile({
|
||||
schemaVersion: 1,
|
||||
repos: [],
|
||||
|
|
@ -2154,10 +2155,12 @@ describe('Store', () => {
|
|||
const store = await createStore()
|
||||
const props = store.getUI().worktreeCardProperties
|
||||
expect(props.filter((p) => p === 'inline-agents')).toHaveLength(1)
|
||||
expect(props.filter((p) => p === 'linear-issue')).toHaveLength(1)
|
||||
expect(props.filter((p) => p === 'ports')).toHaveLength(1)
|
||||
expect(store.getUI()._inlineAgentsDefaultedForAllUsers).toBe(true)
|
||||
})
|
||||
|
||||
it('restores retired card properties when loading old user choices', async () => {
|
||||
it('adds split-out default card properties when loading old user choices', async () => {
|
||||
writeDataFile({
|
||||
schemaVersion: 1,
|
||||
repos: [],
|
||||
|
|
@ -2174,14 +2177,12 @@ describe('Store', () => {
|
|||
expect(store.getUI().worktreeCardProperties).toEqual([
|
||||
'status',
|
||||
'unread',
|
||||
'issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'ports',
|
||||
'inline-agents'
|
||||
])
|
||||
})
|
||||
|
||||
it('keeps Agent activity opt-out while restoring retired card properties', async () => {
|
||||
it('keeps Agent activity opt-out while adding split-out default card properties', async () => {
|
||||
writeDataFile({
|
||||
schemaVersion: 1,
|
||||
repos: [],
|
||||
|
|
@ -2195,13 +2196,26 @@ describe('Store', () => {
|
|||
workspaceSession: {}
|
||||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).toEqual([
|
||||
'status',
|
||||
'unread',
|
||||
'issue',
|
||||
'pr',
|
||||
'comment'
|
||||
])
|
||||
expect(store.getUI().worktreeCardProperties).toEqual(['status', 'unread', 'ports'])
|
||||
})
|
||||
|
||||
it('preserves deliberate Linear and Ports opt-outs after split-out migration', async () => {
|
||||
writeDataFile({
|
||||
schemaVersion: 1,
|
||||
repos: [],
|
||||
worktreeMeta: {},
|
||||
settings: {},
|
||||
ui: {
|
||||
worktreeCardProperties: ['status', 'unread', 'issue', 'pr', 'comment'],
|
||||
_inlineAgentsDefaultedForAllUsers: true,
|
||||
_expandedWorktreeCardPropertiesDefaulted: true
|
||||
},
|
||||
githubCache: { pr: {}, issue: {} },
|
||||
workspaceSession: {}
|
||||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).not.toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).not.toContain('ports')
|
||||
})
|
||||
|
||||
it('preserves a deliberate uncheck from the experimental-toggle era (Case B)', async () => {
|
||||
|
|
@ -2226,6 +2240,8 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).not.toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
expect(store.getUI()._inlineAgentsDefaultedForAllUsers).toBe(true)
|
||||
})
|
||||
|
||||
|
|
@ -2249,6 +2265,8 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).not.toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
})
|
||||
|
||||
it('lapsed Case B (experiment off at upgrade time) re-adds inline-agents', async () => {
|
||||
|
|
@ -2272,6 +2290,8 @@ describe('Store', () => {
|
|||
})
|
||||
const store = await createStore()
|
||||
expect(store.getUI().worktreeCardProperties).toContain('inline-agents')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('linear-issue')
|
||||
expect(store.getUI().worktreeCardProperties).toContain('ports')
|
||||
expect(store.getUI()._inlineAgentsDefaultedForAllUsers).toBe(true)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1585,6 +1585,8 @@ export class Store {
|
|||
// depend on the deprecated value continuing to round-trip.
|
||||
const rawCardProps = parsed.ui?.worktreeCardProperties
|
||||
const inlineAgentsMigrated = parsed.ui?._inlineAgentsDefaultedForAllUsers === true
|
||||
const expandedCardPropsMigrated =
|
||||
parsed.ui?._expandedWorktreeCardPropertiesDefaulted === true
|
||||
const hadExperimentOn = readDeprecatedExperimentFlag(parsed)
|
||||
const deliberateUncheck =
|
||||
hadExperimentOn &&
|
||||
|
|
@ -1602,16 +1604,33 @@ export class Store {
|
|||
const candidate = needsInlineAgentsMigration
|
||||
? [...rawCardProps, 'inline-agents' as const]
|
||||
: rawCardProps
|
||||
// Why: only Agent activity remains configurable; older hidden
|
||||
// card fields must be restored because users can no longer
|
||||
// toggle them back on from the sidebar menu.
|
||||
const normalized = normalizeWorktreeCardProperties(candidate)
|
||||
const expandedCandidate = (() => {
|
||||
if (expandedCardPropsMigrated) {
|
||||
return candidate
|
||||
}
|
||||
const next = [...candidate]
|
||||
// Why: Linear used to be controlled by the generic issue
|
||||
// property and Ports were always visible. Add the split-out
|
||||
// properties once so existing cards keep their prior surface.
|
||||
if (candidate.includes('issue') && !candidate.includes('linear-issue')) {
|
||||
next.push('linear-issue' as const)
|
||||
}
|
||||
if (!candidate.includes('ports')) {
|
||||
next.push('ports' as const)
|
||||
}
|
||||
return next
|
||||
})()
|
||||
const normalized = normalizeWorktreeCardProperties(expandedCandidate)
|
||||
const changed =
|
||||
normalized.length !== rawCardProps.length ||
|
||||
normalized.some((property, index) => property !== rawCardProps[index])
|
||||
return changed ? normalized : undefined
|
||||
})()
|
||||
if (migratedCardProps !== undefined || !inlineAgentsMigrated) {
|
||||
if (
|
||||
migratedCardProps !== undefined ||
|
||||
!inlineAgentsMigrated ||
|
||||
!expandedCardPropsMigrated
|
||||
) {
|
||||
this.loadNeedsSave = true
|
||||
}
|
||||
return {
|
||||
|
|
@ -1630,7 +1649,8 @@ export class Store {
|
|||
// a rollback to a pre-default-on build that still reads it.
|
||||
// The new flag is the one that actually gates the migration.
|
||||
_inlineAgentsDefaultedForExperiment: true,
|
||||
_inlineAgentsDefaultedForAllUsers: true
|
||||
_inlineAgentsDefaultedForAllUsers: true,
|
||||
_expandedWorktreeCardPropertiesDefaulted: true
|
||||
}
|
||||
})(),
|
||||
// Why: the workspace session is the most volatile persisted surface
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ const WorktreeCardProperty = z.enum([
|
|||
'unread',
|
||||
'ci',
|
||||
'issue',
|
||||
'linear-issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'ports',
|
||||
'inline-agents'
|
||||
])
|
||||
const StatusBarItem = z.enum(['claude', 'codex', 'gemini', 'opencode-go', 'ssh', 'resource-usage'])
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ const GROUP_BY_OPTIONS = [
|
|||
] as const
|
||||
|
||||
const PROPERTY_OPTIONS: { id: WorktreeCardProperty; label: string }[] = [
|
||||
{ id: 'issue', label: 'GitHub ticket' },
|
||||
{ id: 'linear-issue', label: 'Linear issue' },
|
||||
{ id: 'pr', label: 'PR/MR link' },
|
||||
{ id: 'comment', label: 'Notes' },
|
||||
{ id: 'ports', label: 'Ports' },
|
||||
// Why: toggles the inline "Agent activity" list rendered below each
|
||||
// workspace card body (see WorktreeCard -> WorktreeCardAgents). Off hides
|
||||
// the list; there is no alternate surface.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { ReactNode } from 'react'
|
|||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostedReviewInfo } from '../../../../shared/hosted-review'
|
||||
import type { Repo, Worktree, WorktreeCardProperty } from '../../../../shared/types'
|
||||
import type { WorkspacePortScanResult } from '../../../../shared/workspace-ports'
|
||||
|
||||
const fetchHostedReviewForBranch = vi.fn()
|
||||
const fetchIssue = vi.fn()
|
||||
|
|
@ -12,6 +13,7 @@ const updateWorktreeMeta = vi.fn()
|
|||
|
||||
let worktreeCardProperties: WorktreeCardProperty[] = ['pr']
|
||||
let hostedReviewCache: Record<string, unknown> = {}
|
||||
let workspacePortScan: WorkspacePortScanResult | null = null
|
||||
|
||||
vi.mock('@/store', () => ({
|
||||
useAppStore: (selector: (state: unknown) => unknown) =>
|
||||
|
|
@ -30,6 +32,7 @@ vi.mock('@/store', () => ({
|
|||
sshConnectionStates: new Map(),
|
||||
sshTargetLabels: new Map(),
|
||||
updateWorktreeMeta,
|
||||
workspacePortScan,
|
||||
worktreeCardProperties
|
||||
})
|
||||
}))
|
||||
|
|
@ -123,6 +126,7 @@ describe('WorktreeCard linked PR display', () => {
|
|||
vi.clearAllMocks()
|
||||
worktreeCardProperties = ['pr']
|
||||
hostedReviewCache = {}
|
||||
workspacePortScan = null
|
||||
})
|
||||
|
||||
it('keeps an icon-only linked GH PR badge visible before hosted review details are cached', async () => {
|
||||
|
|
@ -137,7 +141,7 @@ describe('WorktreeCard linked PR display', () => {
|
|||
})
|
||||
|
||||
it('renders issue, Linear issue, PR, and notes as icon-only metadata in the closed card', async () => {
|
||||
worktreeCardProperties = ['issue', 'pr', 'comment']
|
||||
worktreeCardProperties = ['issue', 'linear-issue', 'pr', 'comment']
|
||||
const { default: WorktreeCard } = await import('./WorktreeCard')
|
||||
|
||||
const markup = renderWorktreeCardMarkup(
|
||||
|
|
@ -164,6 +168,67 @@ describe('WorktreeCard linked PR display', () => {
|
|||
expect(markup.indexOf('Workspace notes')).toBeLessThan(markup.indexOf('Linked issue #123'))
|
||||
})
|
||||
|
||||
it('hides individual metadata surfaces when their card properties are disabled', async () => {
|
||||
worktreeCardProperties = []
|
||||
const { default: WorktreeCard } = await import('./WorktreeCard')
|
||||
|
||||
const markup = renderWorktreeCardMarkup(
|
||||
<WorktreeCard
|
||||
worktree={makeWorktree({
|
||||
linkedIssue: 123,
|
||||
linkedLinearIssue: 'ENG-123',
|
||||
linkedPR: 456,
|
||||
comment: 'Reviewer handoff note'
|
||||
})}
|
||||
repo={makeRepo()}
|
||||
isActive={false}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).not.toContain('Linked issue #123')
|
||||
expect(markup).not.toContain('Linked Linear ENG-123')
|
||||
expect(markup).not.toContain('Linked PR #456')
|
||||
expect(markup).not.toContain('Workspace notes')
|
||||
expect(markup).not.toContain('Reviewer handoff note')
|
||||
})
|
||||
|
||||
it('hides live port metadata when the Ports card property is disabled', async () => {
|
||||
const worktree = makeWorktree()
|
||||
workspacePortScan = {
|
||||
platform: 'darwin',
|
||||
scannedAt: 1,
|
||||
ports: [
|
||||
{
|
||||
id: '127.0.0.1:58941:1234',
|
||||
bindHost: '127.0.0.1',
|
||||
connectHost: '127.0.0.1',
|
||||
port: 58941,
|
||||
pid: 1234,
|
||||
processName: 'node',
|
||||
protocol: 'http',
|
||||
kind: 'workspace',
|
||||
owner: {
|
||||
worktreeId: worktree.id,
|
||||
repoId: worktree.repoId,
|
||||
displayName: worktree.displayName,
|
||||
path: worktree.path,
|
||||
confidence: 'cwd'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
worktreeCardProperties = []
|
||||
const { default: WorktreeCard } = await import('./WorktreeCard')
|
||||
|
||||
const markup = renderWorktreeCardMarkup(
|
||||
<WorktreeCard worktree={worktree} repo={makeRepo()} isActive={false} />
|
||||
)
|
||||
|
||||
expect(markup).not.toContain('live port')
|
||||
expect(markup).not.toContain('Live Ports')
|
||||
expect(markup).not.toContain('58941')
|
||||
})
|
||||
|
||||
it('does not render the standalone CI badge and colors a failing linked PR icon red', async () => {
|
||||
worktreeCardProperties = ['pr', 'ci']
|
||||
hostedReviewCache = {
|
||||
|
|
|
|||
|
|
@ -251,7 +251,9 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
|
||||
const showPR = cardProps.includes('pr')
|
||||
const showIssue = cardProps.includes('issue')
|
||||
const showLinearIssue = cardProps.includes('linear-issue')
|
||||
const showComment = cardProps.includes('comment')
|
||||
const showPorts = cardProps.includes('ports')
|
||||
|
||||
// Skip hosted-review fetches when the corresponding card sections are hidden.
|
||||
// This preference is purely presentational, so background refreshes would
|
||||
|
|
@ -328,7 +330,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
}, [repo, isFolder, worktree.linkedIssue, fetchIssue, issueCacheKey, showIssue])
|
||||
|
||||
useEffect(() => {
|
||||
if (!worktree.linkedLinearIssue || !showIssue) {
|
||||
if (!worktree.linkedLinearIssue || !showLinearIssue) {
|
||||
return
|
||||
}
|
||||
const linearIssueId = worktree.linkedLinearIssue
|
||||
|
|
@ -345,7 +347,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
window.removeEventListener('focus', refreshLinearIssueIfVisible)
|
||||
document.removeEventListener('visibilitychange', refreshLinearIssueIfVisible)
|
||||
}
|
||||
}, [worktree.linkedLinearIssue, fetchLinearIssue, showIssue])
|
||||
}, [worktree.linkedLinearIssue, fetchLinearIssue, showLinearIssue])
|
||||
|
||||
// Stable click handler – ignore clicks that are really text selections.
|
||||
const handleClick = useCallback(
|
||||
|
|
@ -465,7 +467,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
// `worktree.isUnread` flag is unchanged; only the rendering changes.
|
||||
const showUnreadEmphasis = cardProps.includes('unread') && worktree.isUnread
|
||||
const metaIssue = showIssue ? issueDisplay : null
|
||||
const metaLinearIssue = showIssue ? linearIssueDisplay : null
|
||||
const metaLinearIssue = showLinearIssue ? linearIssueDisplay : null
|
||||
const metaReview = showPR ? prDisplay : null
|
||||
const metaComment = showComment ? worktree.comment : null
|
||||
const handleOpenGitHubIssueInOrca = useCallback(
|
||||
|
|
@ -530,7 +532,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
review: metaReview,
|
||||
comment: metaComment
|
||||
})
|
||||
const hasPorts = workspacePorts.length > 0
|
||||
const hasPorts = showPorts && workspacePorts.length > 0
|
||||
|
||||
const cardBody = (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@
|
|||
import { createStore, type StoreApi } from 'zustand/vanilla'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { getDefaultUIState } from '../../../../shared/constants'
|
||||
import type { GitHubWorkItem, PersistedUIState, Worktree } from '../../../../shared/types'
|
||||
import type {
|
||||
GitHubWorkItem,
|
||||
PersistedUIState,
|
||||
Worktree,
|
||||
WorktreeCardProperty
|
||||
} from '../../../../shared/types'
|
||||
import { createUISlice } from './ui'
|
||||
import { createWorktreeNavHistorySlice } from './worktree-nav-history'
|
||||
import type { AppState } from '../types'
|
||||
|
|
@ -180,7 +185,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
|||
expect(store.getState().hideDefaultBranchWorkspace).toBe(true)
|
||||
})
|
||||
|
||||
it('restores retired card properties during hydration', () => {
|
||||
it('restores fixed card properties during hydration', () => {
|
||||
const store = createUIStore()
|
||||
|
||||
store.getState().hydratePersistedUI(
|
||||
|
|
@ -189,14 +194,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
|||
})
|
||||
)
|
||||
|
||||
expect(store.getState().worktreeCardProperties).toEqual([
|
||||
'status',
|
||||
'unread',
|
||||
'issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'inline-agents'
|
||||
])
|
||||
expect(store.getState().worktreeCardProperties).toEqual(['status', 'unread', 'inline-agents'])
|
||||
})
|
||||
|
||||
it('adds the default-on Ports status item once for older persisted UI', () => {
|
||||
|
|
@ -537,7 +535,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
|||
expect(setUI).toHaveBeenCalledWith({ taskResumeState: expected })
|
||||
})
|
||||
|
||||
it('keeps retired card properties enabled when toggling Agent activity', () => {
|
||||
it('keeps fixed card properties when toggling Agent activity', () => {
|
||||
const setUI = vi.fn().mockResolvedValue(undefined)
|
||||
vi.stubGlobal('window', { api: { ui: { set: setUI } } })
|
||||
const store = createUIStore()
|
||||
|
|
@ -545,7 +543,7 @@ describe('createUISlice hydratePersistedUI', () => {
|
|||
store.setState({ worktreeCardProperties: ['inline-agents'] })
|
||||
store.getState().toggleWorktreeCardProperty('inline-agents')
|
||||
|
||||
const expected = ['status', 'unread', 'issue', 'pr', 'comment']
|
||||
const expected: WorktreeCardProperty[] = ['status', 'unread']
|
||||
expect(store.getState().worktreeCardProperties).toEqual(expected)
|
||||
expect(setUI).toHaveBeenCalledWith({ worktreeCardProperties: expected })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import { DEFAULT_WORKTREE_CARD_PROPERTIES } from './worktree-card-properties'
|
|||
|
||||
export { DEFAULT_STATUS_BAR_ITEMS } from './status-bar-defaults'
|
||||
export {
|
||||
ALWAYS_VISIBLE_WORKTREE_CARD_PROPERTIES,
|
||||
DEFAULT_WORKTREE_CARD_PROPERTIES,
|
||||
normalizeWorktreeCardProperties
|
||||
} from './worktree-card-properties'
|
||||
|
|
|
|||
|
|
@ -2025,9 +2025,13 @@ export type WorktreeCardProperty =
|
|||
| 'unread'
|
||||
// Legacy persisted preference. CI status is now represented by linked PR metadata.
|
||||
| 'ci'
|
||||
// GitHub issue metadata shown on workspace cards.
|
||||
| 'issue'
|
||||
// Linear issue metadata shown on workspace cards.
|
||||
| 'linear-issue'
|
||||
| 'pr'
|
||||
| 'comment'
|
||||
| 'ports'
|
||||
// Why: inline list of agent activity rendered directly inside each
|
||||
// workspace card when the experimental agent-activity feature is on. On by
|
||||
// default (see DEFAULT_WORKTREE_CARD_PROPERTIES in shared/constants.ts) —
|
||||
|
|
@ -2152,6 +2156,10 @@ export type PersistedUIState = {
|
|||
* stamped on every prior load and so is permanently dirty for the
|
||||
* prior-RC opt-out cohort the widened migration is meant to reach. */
|
||||
_inlineAgentsDefaultedForAllUsers?: boolean
|
||||
/** One-shot migration flag for card properties that were split out after
|
||||
* the original metadata toggles shipped. Set once so later deliberate
|
||||
* unchecks of Linear issue and Ports stick across restarts. */
|
||||
_expandedWorktreeCardPropertiesDefaulted?: boolean
|
||||
/** Snapshot of totalAgentsSpawned captured the first time we see the current
|
||||
* app version. Why: the nag threshold counts agents spawned *since the
|
||||
* user's last update* so a fresh install or new release does not trigger
|
||||
|
|
|
|||
|
|
@ -1,28 +1,39 @@
|
|||
import type { WorktreeCardProperty } from './types'
|
||||
|
||||
export const ALWAYS_VISIBLE_WORKTREE_CARD_PROPERTIES: WorktreeCardProperty[] = [
|
||||
'status',
|
||||
'unread',
|
||||
'issue',
|
||||
'pr',
|
||||
'comment'
|
||||
]
|
||||
const FIXED_WORKTREE_CARD_PROPERTIES: WorktreeCardProperty[] = ['status', 'unread']
|
||||
|
||||
export const DEFAULT_WORKTREE_CARD_PROPERTIES: WorktreeCardProperty[] = [
|
||||
...ALWAYS_VISIBLE_WORKTREE_CARD_PROPERTIES,
|
||||
...FIXED_WORKTREE_CARD_PROPERTIES,
|
||||
'issue',
|
||||
'linear-issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'ports',
|
||||
// Why: agent activity is the primary reason users opt into the feature, so
|
||||
// show it inline on each card by default. Unchecking this from the
|
||||
// Workspaces view options hides the inline list entirely.
|
||||
'inline-agents'
|
||||
]
|
||||
|
||||
const WORKTREE_CARD_PROPERTY_ORDER: WorktreeCardProperty[] = [
|
||||
'status',
|
||||
'unread',
|
||||
'ci',
|
||||
'issue',
|
||||
'linear-issue',
|
||||
'pr',
|
||||
'comment',
|
||||
'ports',
|
||||
'inline-agents'
|
||||
]
|
||||
|
||||
export function normalizeWorktreeCardProperties(
|
||||
properties: readonly WorktreeCardProperty[] | null | undefined
|
||||
): WorktreeCardProperty[] {
|
||||
const normalized = [...ALWAYS_VISIBLE_WORKTREE_CARD_PROPERTIES]
|
||||
const normalized: WorktreeCardProperty[] = [...FIXED_WORKTREE_CARD_PROPERTIES]
|
||||
const source = properties ?? DEFAULT_WORKTREE_CARD_PROPERTIES
|
||||
for (const property of source) {
|
||||
if (!normalized.includes(property)) {
|
||||
for (const property of WORKTREE_CARD_PROPERTY_ORDER) {
|
||||
if (source.includes(property) && !normalized.includes(property)) {
|
||||
normalized.push(property)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue