fix(linear): label Start workspace and add Open on Linear (#14492)

* fix(linear): label Start workspace and add Open on Linear

The issue page header used three unlabeled icons. Match the GitHub
issue header: copy stays quiet, Open on Linear is an external-link
control, and Start workspace is a labeled primary button.

* Address PR review feedback (#14492)

- Make the header source contract ignore Start workspace formatting
This commit is contained in:
Jinjing
2026-08-14 02:29:45 -07:00
committed by GitHub
parent 87bafb5e6a
commit cb42b60849
7 changed files with 58 additions and 25 deletions
@@ -10,10 +10,11 @@ import {
ChevronLeft,
ChevronRight,
Clipboard,
Copy,
ExternalLink,
FolderKanban,
FolderOpen,
GitBranch,
Link,
LoaderCircle,
Plus,
RefreshCw,
@@ -782,6 +783,7 @@ export default function LinearIssueWorkspace({
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-sm"
onClick={() => void copyTextToClipboard(displayed.url, 'URL')}
@@ -790,7 +792,7 @@ export default function LinearIssueWorkspace({
'Copy Linear URL'
)}
>
<Link className="size-4" />
<Copy className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
@@ -800,19 +802,20 @@ export default function LinearIssueWorkspace({
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-sm"
onClick={() => void copyTextToClipboard(displayed.identifier, 'Identifier')}
onClick={() => void window.api.shell.openUrl(displayed.url)}
aria-label={translate(
'auto.components.LinearIssueWorkspace.9e3c49beb8',
'Copy issue identifier'
'auto.components.LinearIssueWorkspace.openOnLinear',
'Open on Linear'
)}
>
<Clipboard className="size-4" />
<ExternalLink className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{translate('auto.components.LinearIssueWorkspace.30c1242f3a', 'Copy identifier')}
{translate('auto.components.LinearIssueWorkspace.openOnLinear', 'Open on Linear')}
</TooltipContent>
</Tooltip>
{attachedWorkspace ? (
@@ -858,24 +861,19 @@ export default function LinearIssueWorkspace({
</DropdownMenuContent>
</DropdownMenu>
) : (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon-sm"
onClick={handleOpenOrUseIssue}
aria-label={translate(
'auto.components.LinearIssueWorkspace.30a7f56c0a',
'Start workspace from issue'
)}
>
<ArrowRight className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{translate('auto.components.LinearIssueWorkspace.e1e0a9bca9', 'Start workspace')}
</TooltipContent>
</Tooltip>
<Button
type="button"
size="sm"
onClick={handleOpenOrUseIssue}
className="gap-1.5 whitespace-nowrap"
aria-label={translate(
'auto.components.LinearIssueWorkspace.30a7f56c0a',
'Start workspace from issue'
)}
>
{translate('auto.components.LinearIssueWorkspace.e1e0a9bca9', 'Start workspace')}
<ArrowRight className="size-3.5" />
</Button>
)}
{variant === 'sheet' ? (
<Tooltip>
@@ -0,0 +1,30 @@
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
const source = readFileSync(join(__dirname, 'LinearIssueWorkspace.tsx'), 'utf8')
function headerSource(): string {
const start = source.indexOf('<header className="flex h-[61px]')
const end = source.indexOf('</header>', start)
expect(start).toBeGreaterThanOrEqual(0)
expect(end).toBeGreaterThan(start)
return source.slice(start, end)
}
describe('LinearIssueWorkspace header actions', () => {
it('exposes copy, open-on-Linear, and a labeled start-workspace control', () => {
const header = headerSource()
expect(header).toContain("copyTextToClipboard(displayed.url, 'URL')")
expect(header).toContain('window.api.shell.openUrl(displayed.url)')
expect(header).toContain("'Open on Linear'")
expect(header).toContain('<Copy className="size-4" />')
expect(header).toContain('<ExternalLink className="size-4" />')
expect(header).toContain("'Start workspace'")
expect(header).toContain("'Start workspace from issue'")
expect(header).not.toContain("'Copy issue identifier'")
expect(header).toMatch(/size="sm"[\s\S]*onClick=\{handleOpenOrUseIssue\}/)
expect(header).not.toMatch(/size="icon-sm"\s+onClick=\{handleOpenOrUseIssue\}/)
})
})
+1
View File
@@ -1361,6 +1361,7 @@
"519c3587f3": "Add to project",
"openAttachedWorkspace": "Open workspace attached to issue",
"openWorkspace": "Open workspace",
"openOnLinear": "Open on Linear",
"moreWorkspaceActions": "More issue workspace actions",
"startNewWorkspace": "Start new workspace",
"workspaceSection": "Workspace",
+1
View File
@@ -1242,6 +1242,7 @@
"519c3587f3": "Agregar al proyecto",
"openAttachedWorkspace": "Abrir el espacio de trabajo vinculado al issue",
"openWorkspace": "Abrir espacio de trabajo",
"openOnLinear": "Abrir en Linear",
"moreWorkspaceActions": "Más acciones del espacio de trabajo del issue",
"startNewWorkspace": "Iniciar un espacio de trabajo nuevo",
"workspaceSection": "Espacio de trabajo",
+1
View File
@@ -1242,6 +1242,7 @@
"519c3587f3": "プロジェクトに追加",
"openAttachedWorkspace": "Issue にリンクされたワークスペースを開く",
"openWorkspace": "ワークスペースを開く",
"openOnLinear": "Linear で開く",
"moreWorkspaceActions": "Issue ワークスペースのその他の操作",
"startNewWorkspace": "新規ワークスペースを開始",
"workspaceSection": "ワークスペース",
+1
View File
@@ -1242,6 +1242,7 @@
"519c3587f3": "프로젝트에 추가",
"openAttachedWorkspace": "이슈에 연결된 워크스페이스 열기",
"openWorkspace": "워크스페이스 열기",
"openOnLinear": "Linear에서 열기",
"moreWorkspaceActions": "이슈 워크스페이스 작업 더 보기",
"startNewWorkspace": "새 워크스페이스 시작",
"workspaceSection": "워크스페이스",
+1
View File
@@ -1242,6 +1242,7 @@
"519c3587f3": "添加到项目",
"openAttachedWorkspace": "打开关联到议题的工作区",
"openWorkspace": "打开工作区",
"openOnLinear": "在 Linear 中打开",
"moreWorkspaceActions": "更多议题工作区操作",
"startNewWorkspace": "启动新工作区",
"workspaceSection": "工作区",