feat(code): enhance CodeTab with clone and search slots for improved navigation
ZhenYi committed
Modified web/src/app/styles/workspace.css web/src/app/styles/workspace.css +3 −2 View web/src/app/styles/workspace.css
@@ -139,8 +139,9 @@
139139.board-canvas { min-height: 0; padding: 20px 24px; background: var(--shell-field); }
140140.board-track { gap: 18px; }
141141.repo-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; min-height: 60px; padding: 12px 24px; border-bottom: 1px solid var(--shell-hairline); }
142.repo-toolbar-refs { display: flex; min-width: 0; flex-wrap: wrap; gap: 8px; }
143.repo-toolbar-clone { display: flex; min-width: 0; margin-left: auto; align-items: center; gap: 8px; }
142.repo-toolbar-refs { display: flex; min-width: 0; flex-shrink: 0; flex-wrap: wrap; gap: 8px; }
143.repo-toolbar-clone { display: flex; flex-shrink: 0; align-items: center; gap: 8px; }
144.repo-toolbar-clone.empty .repo-overview-toggle { margin-left: auto; }
144145.repo-toolbar-tabs { display: flex; min-width: 0; align-items: center; padding-left: 12px; border-left: 1px solid var(--shell-hairline); }
145146.repo-content-layout { display: flex; flex: 1; min-height: 0; gap: 24px; padding: 16px 24px 24px; background: var(--shell-field); }
146147.repo-content-layout > aside { width: 260px; }
Modified web/src/features/repo-browse/ui/code-tab.tsx web/src/features/repo-browse/ui/code-tab.tsx +127 −43 View web/src/features/repo-browse/ui/code-tab.tsx
@@ -1,4 +1,4 @@
1import { useEffect, useMemo, useRef, useState } from 'react'
1import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
22import { ArrowLeft, ArrowUp, ChevronRight, File as FileIcon, Folder, FolderGit2, History, Users } from 'lucide-react'
33import { formatBytes, repoKey, useAsync } from '@/shared/lib'
44import { ErrorPanel, PanelNote } from '@/shared/ui'
@@ -15,8 +15,6 @@
1515import { prepareReadmeHtml, resolveRepoPath, statRepoPath } from '../model/readme'
1616import { useFragmentNavigation, type FragmentRoutes } from '../model/use-fragment-navigation'
1717import { BlameView } from './blame-view'
18import { RepoCodeSearch } from './repo-code-search'
19import type { SearchHit } from '@/entities/search'
2018
2119export interface CodeTabProps {
2220 space: string
@@ -37,7 +35,10 @@
3735 onOpenCommit: (sha: string) => void
3836 /** Show the commit history for a path (file header links to it). */
3937 onShowHistory: (path: string) => void
40 onOpenSearchResult: (hit: SearchHit) => void
38 /** Clone address box, rendered in the header row above the listing. */
39 cloneSlot?: ReactNode
40 /** In-repo search box, centered in that same header row. */
41 searchSlot?: ReactNode
4142}
4243
4344export type FileRef = { path: string; oid?: string }
@@ -58,7 +59,8 @@
5859 closeFile,
5960 onOpenCommit,
6061 onShowHistory,
61 onOpenSearchResult,
62 cloneSlot,
63 searchSlot,
6264}: CodeTabProps) {
6365 // Path and file arrive from the route: the URL is the only place this view
6466 // state exists, so a refresh, a back click or a pasted link rebuilds it.
@@ -80,11 +82,31 @@
8082 return <EmptyRepo repoPath={`${space}/${repo}`} cloneUrl={primaryCloneUrl(repoDto, null)} />
8183 if (file) {
8284 return (
83 <FileView space={space} repo={repo} rev={rev} mode={mode} file={file} line={line} routes={routes} onBack={closeFile} />
85 <FileView
86 space={space}
87 repo={repo}
88 rev={rev}
89 mode={mode}
90 file={file}
91 line={line}
92 routes={routes}
93 onBack={closeFile}
94 cloneSlot={cloneSlot}
95 searchSlot={searchSlot}
96 />
8497 )
8598 }
8699 return (
87 <DirectoryView space={space} repo={repo} rev={rev} defaultBranch={repoDto.default_branch} path={path} mode={mode} routes={routes} onOpenSearchResult={onOpenSearchResult} />
100 <DirectoryView
101 space={space}
102 repo={repo}
103 rev={rev}
104 path={path}
105 mode={mode}
106 routes={routes}
107 cloneSlot={cloneSlot}
108 searchSlot={searchSlot}
109 />
88110 )
89111}
90112
@@ -94,48 +116,57 @@
94116 space,
95117 repo,
96118 rev,
97 defaultBranch,
98119 path,
99120 mode,
100121 routes,
101 onOpenSearchResult,
122 cloneSlot,
123 searchSlot,
102124}: {
103125 space: string
104126 repo: string
105127 rev: string
106 defaultBranch: string
107128 path: string
108129 mode: 'html' | 'json'
109130 routes: FragmentRoutes
110 onOpenSearchResult: (hit: SearchHit) => void
131 cloneSlot?: ReactNode
132 searchSlot?: ReactNode
111133}) {
134 const headerless = !cloneSlot && !searchSlot
112135 return (
113136 <div className="repo-code-view">
114 <div className="repo-directory-toolbar flex flex-wrap items-center gap-2">
115 <div className="min-w-0 flex-1">
116 <Breadcrumbs path={path} onNavigate={routes.onNavigate} />
137 <CodeHeader
138 path={path}
139 onNavigate={routes.onNavigate}
140 cloneSlot={cloneSlot}
141 searchSlot={searchSlot}
142 breadcrumbOnly={headerless}
143 />
144 <div className="repo-tree-card">
145 <div className="repo-directory-header flex flex-wrap items-center gap-2">
146 <div className="min-w-0 flex-1">
147 {headerless ? <Breadcrumbs path={path} onNavigate={routes.onNavigate} /> : null}
148 </div>
149 <button
150 type="button"
151 onClick={() => routes.onShowHistory(path)}
152 className="flex shrink-0 items-center gap-1.5 rounded-md bg-shell-field px-2.5 py-1 text-[12.5px] transition-colors hover:brightness-95"
153 >
154 <History className="h-3.5 w-3.5 text-shell-ink-muted" aria-hidden /> 提交历史
155 </button>
117156 </div>
118 <button
119 type="button"
120 onClick={() => routes.onShowHistory(path)}
121 className="mb-2 flex shrink-0 items-center gap-1.5 rounded-md bg-shell-field px-2.5 py-1 text-[12.5px] transition-colors hover:brightness-95"
122 >
123 <History className="h-3.5 w-3.5 text-shell-ink-muted" aria-hidden /> 提交历史
124 </button>
157 {mode === 'html' ? (
158 <HtmlTree space={space} repo={repo} rev={rev} path={path} routes={routes} />
159 ) : (
160 <JsonTree
161 space={space}
162 repo={repo}
163 rev={rev}
164 path={path}
165 onNavigate={routes.onNavigate}
166 onOpenFile={routes.onOpenFile}
167 />
168 )}
125169 </div>
126 <RepoCodeSearch space={space} repo={repo} currentRevision={rev} defaultBranch={defaultBranch} onOpen={onOpenSearchResult} />
127 {mode === 'html' ? (
128 <HtmlTree space={space} repo={repo} rev={rev} path={path} routes={routes} />
129 ) : (
130 <JsonTree
131 space={space}
132 repo={repo}
133 rev={rev}
134 path={path}
135 onNavigate={routes.onNavigate}
136 onOpenFile={routes.onOpenFile}
137 />
138 )}
139170 {mode === 'html' ? (
140171 <HtmlReadme space={space} repo={repo} rev={rev} path={path} routes={routes} />
141172 ) : (
@@ -145,10 +176,46 @@
145176 )
146177}
147178
148function Breadcrumbs({ path, onNavigate }: { path: string; onNavigate: (p: string) => void }) {
179function CodeHeader({
180 path,
181 leaf,
182 onNavigate,
183 cloneSlot,
184 searchSlot,
185 breadcrumbOnly = false,
186}: {
187 path: string
188 leaf?: string
189 onNavigate: (p: string) => void
190 cloneSlot?: ReactNode
191 searchSlot?: ReactNode
192 /** Keep the row alive when it carries nothing but the path. */
193 breadcrumbOnly?: boolean
194}) {
195 if (!cloneSlot && !searchSlot && !breadcrumbOnly) return null
196 return (
197 <div className="repo-code-header flex flex-wrap items-center gap-2.5">
198 <Breadcrumbs path={path} leaf={leaf} onNavigate={onNavigate} className="repo-code-header-path" />
199 {searchSlot ? <div className="repo-code-header-search min-w-[180px]">{searchSlot}</div> : null}
200 {cloneSlot ? <div className="repo-code-header-clone flex items-center">{cloneSlot}</div> : null}
201 </div>
202 )
203}
204
205function Breadcrumbs({
206 path,
207 leaf,
208 onNavigate,
209 className = 'min-w-0 flex-1',
210}: {
211 path: string
212 leaf?: string
213 onNavigate: (p: string) => void
214 className?: string
215}) {
149216 const segments = path ? path.split('/') : []
150217 return (
151 <nav aria-label="仓库路径" className="repo-breadcrumbs mb-2 flex flex-wrap items-center gap-1 text-[13px]">
218 <nav aria-label="仓库路径" className={`repo-breadcrumbs flex flex-wrap items-center gap-1 text-[13px] ${className}`}>
152219 <button
153220 type="button"
154221 className="font-semibold hover:underline"
@@ -169,6 +236,12 @@
169236 </button>
170237 </span>
171238 ))}
239 {leaf ? (
240 <span className="flex items-center gap-1">
241 <ChevronRight className="h-3.5 w-3.5 text-shell-ink-muted" aria-hidden />
242 <span aria-current="location" className="font-semibold">{leaf}</span>
243 </span>
244 ) : null}
172245 </nav>
173246 )
174247}
@@ -198,9 +271,7 @@
198271 <ErrorPanel status={frag.status} what="目录" onRetry={() => setRetry(r => r + 1)} />
199272 )
200273 }
201 return (
202 <div className="repo-tree-card repo-embed overflow-x-auto" onClick={onClick} dangerouslySetInnerHTML={{ __html: frag.html }} />
203 )
274 return <div className="repo-embed overflow-x-auto p-3" onClick={onClick} dangerouslySetInnerHTML={{ __html: frag.html }} />
204275}
205276
206277function JsonTree({
@@ -233,8 +304,8 @@
233304 })
234305 if (rows.length === 0) return <PanelNote text="此目录为空。" />
235306 return (
236 <div className="repo-tree-card overflow-x-auto">
237 <table aria-label="仓库文件" className="repo-file-table w-full border-collapse text-[13px]">
307 <div className="overflow-x-auto">
308 <table aria-label="仓库文件" className="repo-file-table w-full border-collapse text-[13px]">
238309 <thead>
239310 <tr className="text-left font-mono text-[10px] uppercase tracking-[1.2px] text-shell-ink-muted">
240311 <th className="px-2 py-1.5">名称</th>
@@ -281,7 +352,7 @@
281352 )
282353 })}
283354 </tbody>
284 </table>
355 </table>
285356 </div>
286357 )
287358}
@@ -550,6 +621,8 @@
550621 line,
551622 routes,
552623 onBack,
624 cloneSlot,
625 searchSlot,
553626}: {
554627 space: string
555628 repo: string
@@ -559,11 +632,22 @@
559632 line?: number
560633 routes: FragmentRoutes
561634 onBack: () => void
635 cloneSlot?: ReactNode
636 searchSlot?: ReactNode
562637}) {
563638 const [blame, setBlame] = useState(false)
639 const dir = file.path.includes('/') ? file.path.slice(0, file.path.lastIndexOf('/')) : ''
640 const leaf = file.path.includes('/') ? file.path.slice(file.path.lastIndexOf('/') + 1) : file.path
564641 return (
565642 <div className="repo-code-view">
566 <div className="repo-file-toolbar mb-4 flex flex-wrap items-center gap-2">
643 <CodeHeader
644 path={dir}
645 leaf={leaf}
646 onNavigate={routes.onNavigate}
647 cloneSlot={cloneSlot}
648 searchSlot={searchSlot}
649 />
650 <div className="repo-file-toolbar flex flex-wrap items-center gap-2">
567651 <button
568652 type="button"
569653 onClick={onBack}
Modified web/src/features/repo-browse/ui/repo-code-search.tsx web/src/features/repo-browse/ui/repo-code-search.tsx +15 −7 View web/src/features/repo-browse/ui/repo-code-search.tsx
@@ -35,12 +35,15 @@
3535 currentRevision,
3636 defaultBranch,
3737 onOpen,
38 variant = 'block',
3839}: {
3940 space: string
4041 repo: string
4142 currentRevision: string
4243 defaultBranch: string
4344 onOpen: (hit: SearchHit) => void
45 /** block: full-width bar under the toolbar · toolbar: compact box inside the repo toolbar */
46 variant?: 'block' | 'toolbar'
4447}) {
4548 const [query, setQuery] = useState('')
4649 const [hits, setHits] = useState<SearchHit[]>([])
@@ -131,10 +134,13 @@
131134
132135 const indexedBranch = currentRevision === defaultBranch || !currentRevision
133136 const panelOpen = open && Boolean(query.trim())
137 const inline = variant === 'toolbar'
134138
135139 return (
136 <div ref={root} className="relative mb-3">
137 <div className="flex items-center gap-2 rounded-lg border border-shell-hairline bg-shell-surface px-2.5 shadow-sm transition-colors focus-within:border-shell-focus">
140 <div ref={root} className={inline ? 'repo-search-inline relative w-full' : 'relative mb-3'}>
141 <div className={inline
142 ? 'flex h-9 w-full min-w-0 items-center gap-1.5 rounded-[9px] border border-shell-hairline bg-shell-field px-2 transition-colors focus-within:border-shell-tile focus-within:bg-shell-surface'
143 : 'flex items-center gap-2 rounded-lg border border-shell-hairline bg-shell-surface px-2.5 shadow-sm transition-colors focus-within:border-shell-focus'}>
138144 {loading ? (
139145 <Loader2 className="h-4 w-4 shrink-0 animate-spin text-shell-ink-muted" aria-hidden />
140146 ) : (
@@ -168,22 +174,24 @@
168174 role="combobox"
169175 aria-expanded={panelOpen}
170176 aria-controls="repo-search-results"
171 className="h-10 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-shell-ink-muted"
177 className={inline ? 'h-full min-w-0 flex-1 bg-transparent text-[12.5px] outline-none placeholder:text-shell-ink-muted' : 'h-10 min-w-0 flex-1 bg-transparent text-[13px] outline-none placeholder:text-shell-ink-muted'}
172178 />
173179 {query ? (
174 <button type="button" onClick={() => { setQuery(''); setOpen(false) }} className="rounded p-1 text-shell-ink-muted hover:bg-shell-field hover:text-shell-ink" aria-label="清除仓库搜索">
180 <button type="button" onClick={() => { setQuery(''); setOpen(false) }} className="rounded p-1 text-shell-ink-muted hover:bg-shell-active hover:text-shell-ink" aria-label="清除仓库搜索">
175181 <X className="h-3.5 w-3.5" aria-hidden />
176182 </button>
177183 ) : (
178 <kbd className="hidden shrink-0 rounded border border-shell-hairline bg-shell-field px-1.5 py-0.5 font-mono text-[10px] text-shell-ink-muted sm:block">/</kbd>
184 <kbd className="hidden shrink-0 rounded border border-shell-hairline bg-shell-surface px-1.5 py-0.5 font-mono text-[10px] text-shell-ink-muted sm:block">/</kbd>
179185 )}
180186 </div>
181 {!indexedBranch ? <p className="mt-1 px-1 text-[11px] text-shell-ink-muted">结果来自默认分支 {defaultBranch}</p> : null}
187 {!indexedBranch && !inline ? <p className="mt-1 px-1 text-[11px] text-shell-ink-muted">结果来自默认分支 {defaultBranch}</p> : null}
182188 {panelOpen ? (
183189 <div
184190 id="repo-search-results"
185191 role="listbox"
186 className="absolute inset-x-0 z-30 mt-1 max-h-[min(60vh,480px)] overflow-y-auto overscroll-contain rounded-lg border border-shell-hairline bg-shell-surface py-1 shadow-xl"
192 className={inline
193 ? 'absolute inset-x-0 z-40 mt-1 max-h-[min(60vh,480px)] overflow-y-auto overscroll-contain rounded-lg border border-shell-hairline bg-shell-surface py-1 shadow-xl'
194 : 'absolute inset-x-0 z-30 mt-1 max-h-[min(60vh,480px)] overflow-y-auto overscroll-contain rounded-lg border border-shell-hairline bg-shell-surface py-1 shadow-xl'}
187195 >
188196 {loading ? <p className="px-3 py-4 text-[12px] text-shell-ink-muted">正在搜索…</p> : null}
189197 {error ? <p role="alert" className="px-3 py-4 text-[12px] text-[#b42318]">{error}</p> : null}
Modified web/src/features/repo-browse/index.ts web/src/features/repo-browse/index.ts +1 −0 View web/src/features/repo-browse/index.ts
@@ -2,3 +2,4 @@
22// Consumers import from '@/features/repo-browse', never from deeper files.
33export { CodeTab, type CodeTabProps } from './ui/code-tab'
44export { prefetchRepoView } from './model/prefetch'
5export { RepoCodeSearch } from './ui/repo-code-search'
Modified web/src/features/search/ui/global-search-command.tsx web/src/features/search/ui/global-search-command.tsx +4 −2 View web/src/features/search/ui/global-search-command.tsx
@@ -91,10 +91,12 @@
9191 <button
9292 type="button"
9393 onClick={() => setOpen(true)}
94 className="fixed right-4 top-3 z-40 hidden h-9 items-center gap-2 rounded-lg border border-shell-hairline bg-shell-surface px-3 text-[12px] text-shell-ink-muted shadow-sm transition-colors hover:bg-shell-field md:flex"
94 className="fixed right-4 top-3 z-40 hidden h-9 w-[clamp(200px,22vw,300px)] items-center gap-2 rounded-lg border border-shell-hairline bg-shell-surface px-3 text-[12px] text-shell-ink-muted shadow-sm transition-colors hover:bg-shell-field md:flex"
9595 aria-label="打开全局搜索"
9696 >
97 <Search className="h-4 w-4" aria-hidden /> 搜索 <kbd className="rounded border border-shell-hairline bg-shell-field px-1.5 py-0.5 font-mono text-[10px]">⌘K</kbd>
97 <Search className="h-4 w-4 shrink-0" aria-hidden />
98 <span className="flex-1 truncate text-left">搜索代码、符号、Issue 与 PR…</span>
99 <kbd className="shrink-0 rounded border border-shell-hairline bg-shell-field px-1.5 py-0.5 font-mono text-[10px]">⌘K</kbd>
98100 </button>
99101 {open ? (
100102 <div className="fixed inset-0 z-50 flex items-start justify-center bg-black/35 p-4 pt-[10vh]" onMouseDown={() => setOpen(false)}>
Modified web/src/pages/workspace/ui/layout/repo-layout.tsx web/src/pages/workspace/ui/layout/repo-layout.tsx +3 −12 View web/src/pages/workspace/ui/layout/repo-layout.tsx
@@ -1,5 +1,5 @@
11import { useState, type ReactNode } from 'react'
2import { ChevronDown, Copy, File, Folder, FolderGit2, GitBranch, History, Info, Tag } from 'lucide-react'
2import { ChevronDown, File, Folder, FolderGit2, GitBranch, Info, Tag } from 'lucide-react'
33import { AppShell, ShellTopBar, type AppShellProps } from '@/shared/ui'
44import type { RepoTabId } from '../../model/route'
55
@@ -26,8 +26,6 @@
2626 refsSlot?: ReactNode
2727 /** Right segment of the repo toolbar (clone box). */
2828 clone?: ReactNode
29 /** Clone URL shown in the default clone box. */
30 cloneUrl?: string
3129 /** Default branch shown on the branch chip. */
3230 branch?: ReactNode
3331 /** Latest tag shown on the ref chip. */
@@ -47,7 +45,6 @@
4745 onTabChange,
4846 refsSlot,
4947 clone,
50 cloneUrl,
5148 branch,
5249 ref,
5350 children,
@@ -80,15 +77,9 @@
8077 )}
8178 </div>
8279 <div className="repo-toolbar-tabs">{tabs ?? <DefaultTabs active={activeTab} onChange={onTabChange} />}</div>
83 <div className="repo-toolbar-clone">
80 <div className={clone ? 'repo-toolbar-clone' : 'repo-toolbar-clone empty'}>
8481 <button type="button" className="repo-overview-toggle workspace-icon-button" title="仓库概览" aria-label="仓库概览" aria-expanded={overviewOpen} onClick={() => setOverviewOpen(value => !value)}><Info size={18} /></button>
85 {clone ?? (
86 <span className="flex h-[30px] w-[310px] items-center gap-2 rounded-md bg-shell-field px-3 font-mono text-[12px] text-shell-ink-muted">
87 <History className="h-3.5 w-3.5 shrink-0" />
88 <span className="truncate">{cloneUrl ?? '—'}</span>
89 <Copy className="ml-auto h-3.5 w-3.5 shrink-0" />
90 </span>
91 )}
82 {clone}
9283 </div>
9384 </div>
9485 <div className={`repo-content-layout ${overviewOpen ? 'overview-open' : ''}`}>
Modified web/src/pages/workspace/ui/repo/repo-polish.css web/src/pages/workspace/ui/repo/repo-polish.css +19 −3 View web/src/pages/workspace/ui/repo/repo-polish.css
@@ -9,10 +9,21 @@
99.repo-content-layout [data-repo-scroll] { padding-right: 2px; }
1010.repo-section-toolbar { min-height: 42px; }
1111.repo-section-toolbar h3 { letter-spacing: -.01em; }
12.repo-directory-toolbar, .repo-file-toolbar { min-height: 44px; padding: 10px 12px; border: 1px solid var(--shell-hairline); border-radius: 12px; background: var(--shell-surface); }
12.repo-file-toolbar { min-height: 44px; padding: 10px 12px; border: 1px solid var(--shell-hairline); border-radius: 12px; background: var(--shell-surface); }
13.repo-code-view { display: flex; flex-direction: column; gap: 12px; }
14.repo-code-header { padding: 6px 8px; border: 1px solid var(--shell-hairline); border-radius: 12px; background: var(--shell-surface); box-shadow: 0 1px 2px rgb(24 28 31 / 2%); }
15.repo-code-header-path { min-width: 0; flex: 0 1 auto; overflow: hidden; white-space: nowrap; }
16.repo-code-header-path.repo-breadcrumbs { flex-wrap: nowrap; }
17.repo-code-header-path button { white-space: nowrap; }
18.repo-code-header-search { display: flex; flex: 1 1 240px; min-width: 160px; align-items: center; }
19.repo-code-header-search > * { width: 100%; }
20.repo-code-header-clone { flex: 0 1 auto; min-width: 0; }
21.repo-code-header-clone > .repo-clone { width: auto; min-width: 0; max-width: 100%; }
22.repo-directory-header { padding: 9px 12px; border-bottom: 1px solid var(--shell-hairline); background: color-mix(in srgb, var(--shell-field) 45%, var(--shell-surface)); }
23.repo-tree-card .repo-embed { border-radius: 0; }
1324.repo-breadcrumbs button { border-radius: 6px; padding: 3px 5px; transition: color 160ms, background 160ms; }
1425.repo-breadcrumbs button:hover { color: var(--shell-ink); background: var(--shell-field); }
15.repo-tree-card { overflow: hidden; border: 1px solid var(--shell-hairline); border-radius: 14px; background: var(--shell-surface); box-shadow: 0 1px 2px rgb(24 28 31 / 2%); }
26.repo-tree-card { display: flex; flex-direction: column; gap: 2px; padding-bottom: 4px; overflow: hidden; border: 1px solid var(--shell-hairline); border-radius: 14px; background: var(--shell-surface); box-shadow: 0 1px 2px rgb(24 28 31 / 2%); }
1627.repo-file-table th { padding: 12px 16px; background: var(--shell-field); font-size: 11px; }
1728.repo-file-table td { padding: 11px 16px; }
1829.repo-file-table tbody tr { transition: background 150ms; }
@@ -50,7 +61,12 @@
5061 .repo-view-content { padding-top: 0; }
5162 .repo-toolbar-tabs button, .repo-toolbar-tabs span { padding-inline: 10px; }
5263 .repo-clone { width: 100%; }
53 .repo-directory-toolbar, .repo-file-toolbar { padding: 8px 10px; }
64 .repo-file-toolbar { padding: 8px 10px; }
65 .repo-directory-header { padding: 8px 10px; }
66 .repo-code-header { padding: 8px; }
67 .repo-code-header-search { flex: 1 1 100%; order: 3; margin-inline: 0; }
68 .repo-code-header-clone { flex: 1 1 100%; order: 4; }
69 .repo-code-header-clone > .repo-clone { width: 100%; }
5470 .repo-file-table th, .repo-file-table td { padding-left: 12px; padding-right: 12px; }
5571 .repo-commit-row { padding: 14px; }
5672 .repo-commit-actions { width: 100%; margin-left: 28px; }
Modified web/src/pages/workspace/ui/repo/repo-view.tsx web/src/pages/workspace/ui/repo/repo-view.tsx +26 −15 View web/src/pages/workspace/ui/repo/repo-view.tsx
@@ -11,7 +11,7 @@
1111import { useAsync, repoKey } from '@/shared/lib'
1212import { useRepoViewLocation } from '../../model/repo-location'
1313import { BranchesTab } from '@/features/repo-branches'
14import { CodeTab } from '@/features/repo-browse'
14import { CodeTab, RepoCodeSearch } from '@/features/repo-browse'
1515import { CommitsTab, CommitView } from '@/features/repo-history'
1616import { PullsTab, PullDetail } from '@/features/pull-request'
1717import { SettingsTab } from '@/features/repo-settings'
@@ -156,7 +156,6 @@
156156 }
157157 activeTab={tab}
158158 onTabChange={setTab}
159 clone={<CloneBox targets={clones} />}
160159 aside={
161160 <RepoAside
162161 space={spaceName}
@@ -191,17 +190,27 @@
191190 closeFile={closeFile}
192191 onOpenCommit={sha => patch({ tab: 'commits', commit: sha })}
193192 onShowHistory={showHistory}
194 onOpenSearchResult={hit => {
195 if (!hit.path) return
196 patch({
197 tab: 'code',
198 rev: hit.revision || repo.default_branch,
199 path: '',
200 file: hit.path,
201 oid: '',
202 line: hit.line_start ?? 0,
203 })
204 }}
193 cloneSlot={<CloneBox targets={clones} fill />}
194 searchSlot={
195 <RepoCodeSearch
196 space={spaceName}
197 repo={repoName}
198 currentRevision={rev}
199 defaultBranch={repo.default_branch}
200 variant="toolbar"
201 onOpen={hit => {
202 if (!hit.path) return
203 patch({
204 tab: 'code',
205 rev: hit.revision || repo.default_branch,
206 path: '',
207 file: hit.path,
208 oid: '',
209 line: hit.line_start ?? 0,
210 })
211 }}
212 />
213 }
205214 />
206215 ) : null}
207216 {tab === 'commits' && rev ? (
@@ -299,7 +308,7 @@
299308 )
300309}
301310
302function CloneBox({ targets }: { targets: CloneTarget[] }) {
311function CloneBox({ targets, fill = false }: { targets: CloneTarget[]; fill?: boolean }) {
303312 const [protocol, setProtocol] = useState<CloneTarget['protocol'] | null>(null)
304313 const [copied, setCopied] = useState(false)
305314 const [copyError, setCopyError] = useState(false)
@@ -325,7 +334,9 @@
325334 }
326335
327336 return (
328 <span className="repo-clone flex w-[300px] min-w-[170px] items-center gap-1.5 rounded-md bg-shell-field px-1.5 xl:w-[360px]">
337 <span
338 className={`repo-clone flex items-center gap-1.5 rounded-md bg-shell-field px-1.5 ${fill ? 'w-auto min-w-0 max-w-full' : 'w-[300px] min-w-[170px] xl:w-[360px]'}`}
339 >
329340 {targets.length > 1 ? (
330341 <span className="flex shrink-0 items-center gap-0.5">
331342 {targets.map(target => (