@jetcrab/pi-desk 1.1.16 → 1.1.20-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/plugins.md +3 -1
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/src/client/l2_biz/workbench/file-workspace/l2-file-workspace-runtime.ts +1 -1
- package/src/client/l2_biz/workbench/hooks/l2-workbench-chat-reload.ts +42 -0
- package/src/client/l2_biz/workbench/l2-work-session-sidebar.tsx +19 -1
- package/src/client/l2_biz/workbench/l2-workbench-chat.ts +45 -0
- package/src/client/l2_biz/workbench/l2-workbench.tsx +4 -0
- package/src/client/l3_modules/conversation/l3-conversation-markdown.module.css +1 -0
- package/src/client/l3_modules/conversation/l3-conversation-markdown.tsx +10 -1
- package/src/client/l3_modules/project-files/l3-markdown-preview.tsx +10 -1
- package/src/client/l3_modules/project-files/l3-project-document-view.tsx +2 -0
- package/src/client/l3_modules/project-files/l3-project-file-browser.tsx +171 -229
- package/src/client/l3_modules/project-files/l3-project-file-runtime.ts +133 -70
- package/src/client/l3_modules/project-files/l3-project-reader.ts +4 -3
- package/src/client/l3_modules/project-files/views/l3-project-search-controls.tsx +201 -0
- package/src/client/l3_modules/project-files/views/l3-project-search-results.tsx +333 -0
- package/src/client/l4_foundation/ui/code/l4-code-preview.tsx +47 -3
- package/src/client/l4_foundation/ui/markdown/l4-markdown-remark.ts +89 -0
- package/src/client/l4_foundation/ui/markdown/l4-markdown.module.css +58 -0
- package/src/client/l4_foundation/ui/markdown/l4-markdown.tsx +108 -0
- package/src/common/l2_biz/work-session/l2-work-session-file-contract.ts +4 -3
- package/src/common/l2_biz/workbench/l2-workbench-locale-messages.ts +6 -0
- package/src/common/l3_modules/project-files/l3-project-files-contract.ts +2 -3
- package/src/common/l3_modules/project-files/l3-project-files-locale-messages.ts +56 -10
- package/src/common/l4_foundation/file/l4-project-file-contract.ts +51 -1
- package/src/common/l4_foundation/locale/l4-host-error-messages.ts +11 -0
- package/src/common/l4_foundation/locale/l4-locale-messages.ts +16 -0
- package/src/instrumentation.ts +0 -2
- package/src/server/l1_entry/api/l1-project-read-routes.ts +6 -4
- package/src/server/l1_entry/api/l1-project-route.ts +9 -8
- package/src/server/l1_entry/api/l1-work-session-file-error.ts +23 -0
- package/src/server/l2_biz/plugin-management/l2-plugin-management.ts +0 -3
- package/src/server/l2_biz/work-session/l2-work-session-chat-runtime.ts +30 -58
- package/src/server/l2_biz/work-session/l2-work-session-manage.ts +0 -4
- package/src/server/l4_foundation/file/l4-project-file-index.ts +276 -0
- package/src/server/l4_foundation/file/l4-project-search.ts +399 -0
- package/src/server/l4_foundation/file/l4-ripgrep-search.ts +192 -0
- package/src/server/l4_foundation/file/l4-work-session-project-file.ts +8 -332
- package/src/server/l4_foundation/pi/l4-pi-package-maintenance.ts +0 -6
- package/src/server/l4_foundation/pi/l4-pi-plugin-capability-probe.ts +1 -3
- package/src/server/l4_foundation/pi/l4-pi-plugin-probe.ts +1 -3
- package/src/server/l4_foundation/pi/l4-pi-runner-process.ts +3 -15
- package/src/server/l4_foundation/process/l4-process-tree.js +31 -5
- package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
- package/temp/build/pi-desk/release/.next/build-manifest.json +3 -3
- package/temp/build/pi-desk/release/.next/prerender-manifest.json +3 -3
- package/temp/build/pi-desk/release/.next/react-loadable-manifest.json +1 -1
- package/temp/build/pi-desk/release/.next/required-server-files.js +4 -3
- package/temp/build/pi-desk/release/.next/required-server-files.json +4 -3
- package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/reveal/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/search/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/page.js +77 -77
- package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/{33658.js → 10881.js} +8 -8
- package/temp/build/pi-desk/release/.next/server/chunks/23054.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/25406.js +3 -3
- package/temp/build/pi-desk/release/.next/server/chunks/35985.js +5 -5
- package/temp/build/pi-desk/release/.next/server/chunks/50520.js +2 -4
- package/temp/build/pi-desk/release/.next/server/chunks/6917.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/70780.js +1 -0
- package/temp/build/pi-desk/release/.next/server/chunks/74528.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/92386.js +1 -1
- package/temp/build/pi-desk/release/.next/server/instrumentation.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
- package/temp/build/pi-desk/release/.next/server/server-reference-manifest.json +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/27611-0890482fe76bd9d6.js +238 -0
- package/temp/build/pi-desk/release/.next/static/chunks/{31087-ba426d09d6f68bfd.js → 31087-d8e5765d541d5c26.js} +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/{49516-cc4dcd84d64ab4fe.js → 51474-8dc714cb76b2e60a.js} +2 -2
- package/temp/build/pi-desk/release/.next/static/chunks/77592-5bef6acb8e7c4b96.js +13 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-a50c456648537afb.js +3 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/{page-0069319a4fb97211.js → page-0789c43551b29dd2.js} +4 -4
- package/temp/build/pi-desk/release/.next/static/chunks/{webpack-40a782fc8a3535b9.js → webpack-d7248206b7fc7ba8.js} +1 -1
- package/temp/build/pi-desk/release/.next/static/css/250a6b93cc2222d1.css +1 -0
- package/temp/build/pi-desk/release/.next/static/css/6dbae2922f88ca31.css +1 -0
- package/temp/build/pi-desk/release/.next/static/css/a4cf28fd76c70165.css +1 -0
- package/temp/build/pi-desk/release/.next/trace-build +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/8584.js +0 -2
- package/temp/build/pi-desk/release/.next/static/chunks/39654-b5fedd762ea0312e.js +0 -238
- package/temp/build/pi-desk/release/.next/static/chunks/75130-d2ca74894aff8085.js +0 -13
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-c0e1c5cef8620cf8.js +0 -3
- package/temp/build/pi-desk/release/.next/static/css/6427d02b6edf406f.css +0 -1
- package/temp/build/pi-desk/release/.next/static/css/949d0d1a99d5de92.css +0 -1
- /package/temp/build/pi-desk/release/.next/static/{DqamF28k0ShQLbNVzXDIj → RLKcK-RQF5FvEeDmS9uih}/_buildManifest.js +0 -0
- /package/temp/build/pi-desk/release/.next/static/{DqamF28k0ShQLbNVzXDIj → RLKcK-RQF5FvEeDmS9uih}/_ssgManifest.js +0 -0
|
@@ -12,10 +12,9 @@ import {
|
|
|
12
12
|
ImageIcon,
|
|
13
13
|
ListFilterIcon,
|
|
14
14
|
LoaderCircleIcon,
|
|
15
|
-
SearchIcon
|
|
16
|
-
XIcon
|
|
15
|
+
SearchIcon
|
|
17
16
|
} from 'lucide-react'
|
|
18
|
-
import { useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
|
17
|
+
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react'
|
|
19
18
|
import { useTranslation } from 'react-i18next'
|
|
20
19
|
import type { L4GitRepository } from '@common/l4_foundation/git/l4-git-contract'
|
|
21
20
|
import { selectL4LocalizedText } from '@common/l4_foundation/locale/l4-localized-text'
|
|
@@ -30,12 +29,8 @@ import {
|
|
|
30
29
|
} from '@client/l4_foundation/ui/ai-elements/file-tree'
|
|
31
30
|
import { L4ScrollArea } from '@client/l4_foundation/ui/l4-scroll-area'
|
|
32
31
|
import { buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
InputGroupAddon,
|
|
36
|
-
InputGroupButton,
|
|
37
|
-
InputGroupInput
|
|
38
|
-
} from '@client/l4_foundation/ui/shadcn/input-group'
|
|
32
|
+
import { L3ProjectSearchControls } from './views/l3-project-search-controls'
|
|
33
|
+
import { L3ProjectSearchResults } from './views/l3-project-search-results'
|
|
39
34
|
import type { L3ProjectGitBrowser } from './l3-project-git-runtime'
|
|
40
35
|
import {
|
|
41
36
|
getL3GitChangeBadge,
|
|
@@ -69,6 +64,7 @@ interface L3ProjectFileBrowserProps {
|
|
|
69
64
|
}
|
|
70
65
|
|
|
71
66
|
const EMPTY_GIT_REPOSITORIES: readonly L4GitRepository[] = []
|
|
67
|
+
const composingSearches = new WeakMap<L3ProjectFileRuntime, object>()
|
|
72
68
|
|
|
73
69
|
function compactPath(path: string): string {
|
|
74
70
|
const parts = path.replaceAll('\\', '/').split('/').filter(Boolean)
|
|
@@ -169,8 +165,26 @@ export function L3ProjectFileBrowser({
|
|
|
169
165
|
const gitWorkspace = gitRuntime.getWorkspace(cwd)
|
|
170
166
|
const scrollRef = useRef<HTMLDivElement>(null)
|
|
171
167
|
const searchInputRef = useRef<HTMLInputElement>(null)
|
|
168
|
+
const searchResultsRef = useRef<HTMLDivElement>(null)
|
|
169
|
+
const searchButtonRef = useRef<HTMLButtonElement>(null)
|
|
170
|
+
const [composing, setComposing] = useState(false)
|
|
171
|
+
const compositionOwner = useRef<object>({})
|
|
172
|
+
const onComposingChange = useCallback(
|
|
173
|
+
(active: boolean): void => {
|
|
174
|
+
if (active) composingSearches.set(runtime, compositionOwner.current)
|
|
175
|
+
else if (composingSearches.get(runtime) === compositionOwner.current)
|
|
176
|
+
composingSearches.delete(runtime)
|
|
177
|
+
setComposing(active)
|
|
178
|
+
},
|
|
179
|
+
[runtime]
|
|
180
|
+
)
|
|
172
181
|
const searchOpen = workspace.searchOpen
|
|
173
182
|
const setSearchOpen = (open: boolean): void => runtime.setSearchOpen(open)
|
|
183
|
+
const closeSearch = (): void => {
|
|
184
|
+
setComposing(false)
|
|
185
|
+
setSearchOpen(false)
|
|
186
|
+
requestAnimationFrame(() => searchButtonRef.current?.focus())
|
|
187
|
+
}
|
|
174
188
|
const [visibleEntries, setVisibleEntries] = useState<Record<string, number>>({})
|
|
175
189
|
const moreEntries = (key: string, total: number): React.ReactNode => {
|
|
176
190
|
const count = visibleEntries[key] ?? 300
|
|
@@ -200,15 +214,17 @@ export function L3ProjectFileBrowser({
|
|
|
200
214
|
|
|
201
215
|
useEffect(() => {
|
|
202
216
|
const scrollElement = scrollRef.current
|
|
203
|
-
if (!scrollElement ||
|
|
217
|
+
if (!scrollElement || searchOpen) return
|
|
204
218
|
scrollElement.scrollTop = workspace.treeScrollTop
|
|
205
|
-
}, [workspace])
|
|
219
|
+
}, [searchOpen, workspace])
|
|
206
220
|
|
|
207
221
|
useEffect(() => {
|
|
208
|
-
if (!searchQuery.trim() ||
|
|
209
|
-
const timer = window.setTimeout(() =>
|
|
222
|
+
if (!searchOpen || !searchQuery.trim() || composing) return
|
|
223
|
+
const timer = window.setTimeout(() => {
|
|
224
|
+
if (!composingSearches.has(runtime)) void runtime.search(searchQuery)
|
|
225
|
+
}, 220)
|
|
210
226
|
return () => window.clearTimeout(timer)
|
|
211
|
-
}, [
|
|
227
|
+
}, [composing, runtime, searchOpen, searchQuery])
|
|
212
228
|
|
|
213
229
|
const expandedRepositoryRootsKey = [...(gitWorkspace?.expandedRepositories ?? [])].join('\u0000')
|
|
214
230
|
useEffect(() => {
|
|
@@ -242,11 +258,20 @@ export function L3ProjectFileBrowser({
|
|
|
242
258
|
toggleDirectory(directory, keys.has(keyL3ProjectTree('directory', path)))
|
|
243
259
|
}
|
|
244
260
|
|
|
245
|
-
const openPreview = (path: string): void => {
|
|
246
|
-
runtime.openFile(path)
|
|
261
|
+
const openPreview = (path: string, line?: number): void => {
|
|
262
|
+
runtime.openFile(path, line)
|
|
247
263
|
onFileOpened?.()
|
|
248
264
|
}
|
|
249
265
|
|
|
266
|
+
const openFirstSearchResult = (): void => {
|
|
267
|
+
const search = runtime.state.search
|
|
268
|
+
if (search.status !== 'ready') return
|
|
269
|
+
const firstFile = search.fileMatches[0]
|
|
270
|
+
const firstLine = search.contentMatches[0]
|
|
271
|
+
if (firstFile) openPreview(firstFile.path)
|
|
272
|
+
else if (firstLine) openPreview(firstLine.path, firstLine.line)
|
|
273
|
+
}
|
|
274
|
+
|
|
250
275
|
const openDiff = (change: L3GitProjectChange): void => {
|
|
251
276
|
runtime.openDiff(change.repositoryRoot, change.path)
|
|
252
277
|
onFileOpened?.()
|
|
@@ -271,16 +296,17 @@ export function L3ProjectFileBrowser({
|
|
|
271
296
|
path: string,
|
|
272
297
|
trigger: React.ReactElement,
|
|
273
298
|
change: L3GitProjectChange | null = null,
|
|
274
|
-
diffOnSelect = false
|
|
299
|
+
diffOnSelect = false,
|
|
300
|
+
line?: number
|
|
275
301
|
): React.JSX.Element => (
|
|
276
302
|
<L3ProjectPathMenu
|
|
277
|
-
key={keyL3ProjectTree('file', path)}
|
|
303
|
+
key={line === undefined ? keyL3ProjectTree('file', path) : `${path}:${line}`}
|
|
278
304
|
cwd={cwd}
|
|
279
305
|
path={path}
|
|
280
306
|
type="file"
|
|
281
307
|
trigger={trigger}
|
|
282
308
|
openLabel={t(diffOnSelect ? 'viewDiff' : 'openPreview')}
|
|
283
|
-
onOpen={() => (diffOnSelect && change ? openDiff(change) : openPreview(path))}
|
|
309
|
+
onOpen={() => (diffOnSelect && change ? openDiff(change) : openPreview(path, line))}
|
|
284
310
|
oldPath={
|
|
285
311
|
change?.change.oldPath ? joinPath(change.repositoryRoot, change.change.oldPath) : undefined
|
|
286
312
|
}
|
|
@@ -648,10 +674,6 @@ export function L3ProjectFileBrowser({
|
|
|
648
674
|
)
|
|
649
675
|
}
|
|
650
676
|
|
|
651
|
-
const changeSearchResults = projectChanges.filter((change) =>
|
|
652
|
-
change.projectPath.toLocaleLowerCase().includes(searchQuery.trim().toLocaleLowerCase())
|
|
653
|
-
)
|
|
654
|
-
const searching = searchOpen && Boolean(searchQuery.trim())
|
|
655
677
|
const singleProjectRootRepository =
|
|
656
678
|
repositories.length === 1 && repositories[0]?.root === '' ? repositories[0] : null
|
|
657
679
|
const showLayoutToggle = repositories.length > 0 && !singleProjectRootRepository
|
|
@@ -661,92 +683,82 @@ export function L3ProjectFileBrowser({
|
|
|
661
683
|
<section
|
|
662
684
|
className="flex h-full min-h-0 flex-col bg-sidebar"
|
|
663
685
|
aria-label={t('projectFiles', { name: projectName })}
|
|
686
|
+
onKeyDown={(event) => {
|
|
687
|
+
if (!searchOpen || event.key !== 'Escape') return
|
|
688
|
+
event.stopPropagation()
|
|
689
|
+
if (event.nativeEvent.isComposing || composing) return
|
|
690
|
+
event.preventDefault()
|
|
691
|
+
closeSearch()
|
|
692
|
+
}}
|
|
664
693
|
>
|
|
665
|
-
|
|
666
|
-
<
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
694
|
+
{searchOpen ? (
|
|
695
|
+
<L3ProjectSearchControls
|
|
696
|
+
input={workspace.search}
|
|
697
|
+
inputRef={searchInputRef}
|
|
698
|
+
invalid={workspace.search.status === 'error'}
|
|
699
|
+
onQueryChange={(query) => runtime.setSearchQuery(query)}
|
|
700
|
+
onOptionsChange={(options) => runtime.setSearchOptions(options)}
|
|
701
|
+
onComposingChange={onComposingChange}
|
|
702
|
+
onClose={closeSearch}
|
|
703
|
+
onOpenFirst={openFirstSearchResult}
|
|
704
|
+
onFocusResults={() =>
|
|
705
|
+
searchResultsRef.current?.querySelector<HTMLButtonElement>('button')?.focus()
|
|
706
|
+
}
|
|
707
|
+
/>
|
|
708
|
+
) : (
|
|
709
|
+
<div className="space-y-1.5 border-b px-2.5 py-2">
|
|
710
|
+
<div className="flex min-h-7 min-w-0 items-center gap-2">
|
|
711
|
+
<L3ProjectPathMenu
|
|
712
|
+
cwd={cwd}
|
|
713
|
+
path=""
|
|
714
|
+
type="directory"
|
|
715
|
+
onReveal={
|
|
716
|
+
runtime.canReveal ? (path, type) => runtime.revealFile(path, type) : undefined
|
|
717
|
+
}
|
|
718
|
+
trigger={
|
|
719
|
+
rootTitle ? (
|
|
720
|
+
<div className="flex min-w-0 flex-1">{rootTitle}</div>
|
|
721
|
+
) : (
|
|
722
|
+
<div tabIndex={0} className="flex min-w-0 flex-1 items-baseline gap-1">
|
|
723
|
+
<p className="shrink-0 truncate text-xs font-semibold" title={projectName}>
|
|
724
|
+
{projectName}
|
|
725
|
+
</p>
|
|
726
|
+
<span className="text-xs text-muted-foreground">·</span>
|
|
727
|
+
<p
|
|
728
|
+
className="min-w-0 truncate font-mono text-xs text-muted-foreground"
|
|
729
|
+
title={cwd}
|
|
730
|
+
>
|
|
731
|
+
{compactPath(cwd)}
|
|
732
|
+
</p>
|
|
733
|
+
</div>
|
|
734
|
+
)
|
|
705
735
|
}
|
|
706
|
-
onClick={() => onOpenGitBranches('')}
|
|
707
|
-
>
|
|
708
|
-
<GitBranchIcon className="size-3 shrink-0" />
|
|
709
|
-
<span className="truncate">
|
|
710
|
-
{singleProjectRootRepository.state === 'ready'
|
|
711
|
-
? getL3GitHeadLabel(singleProjectRootRepository.head)
|
|
712
|
-
: t('unavailable')}
|
|
713
|
-
</span>
|
|
714
|
-
</button>
|
|
715
|
-
) : null}
|
|
716
|
-
</div>
|
|
717
|
-
|
|
718
|
-
{searchOpen ? (
|
|
719
|
-
<InputGroup>
|
|
720
|
-
<InputGroupAddon>
|
|
721
|
-
<SearchIcon className="size-3.5" />
|
|
722
|
-
</InputGroupAddon>
|
|
723
|
-
<InputGroupInput
|
|
724
|
-
ref={searchInputRef}
|
|
725
|
-
data-testid="project-file-search"
|
|
726
|
-
value={searchQuery}
|
|
727
|
-
onChange={(event) => runtime.setSearchQuery(event.target.value)}
|
|
728
|
-
onKeyDown={(event) => {
|
|
729
|
-
if (event.key !== 'Escape') return
|
|
730
|
-
runtime.setSearchQuery('')
|
|
731
|
-
setSearchOpen(false)
|
|
732
|
-
}}
|
|
733
|
-
placeholder={t('searchFiles')}
|
|
734
|
-
aria-label={t('searchFiles')}
|
|
735
736
|
/>
|
|
736
|
-
|
|
737
|
-
<
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
737
|
+
{singleProjectRootRepository ? (
|
|
738
|
+
<button
|
|
739
|
+
type="button"
|
|
740
|
+
disabled={gitReadOnly || singleProjectRootRepository.state !== 'ready'}
|
|
741
|
+
className={cn(
|
|
742
|
+
buttonVariants({ variant: 'outline', size: 'sm' }),
|
|
743
|
+
'max-w-32 shrink gap-1 font-mono text-xs text-muted-foreground'
|
|
744
|
+
)}
|
|
745
|
+
title={
|
|
746
|
+
singleProjectRootRepository.state === 'ready'
|
|
747
|
+
? t(gitReadOnly ? 'gitBranchReadOnly' : 'gitBranches')
|
|
748
|
+
: selectL4LocalizedText(singleProjectRootRepository.message, locale)
|
|
749
|
+
}
|
|
750
|
+
onClick={() => onOpenGitBranches('')}
|
|
744
751
|
>
|
|
745
|
-
<
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
752
|
+
<GitBranchIcon className="size-3 shrink-0" />
|
|
753
|
+
<span className="truncate">
|
|
754
|
+
{singleProjectRootRepository.state === 'ready'
|
|
755
|
+
? getL3GitHeadLabel(singleProjectRootRepository.head)
|
|
756
|
+
: t('unavailable')}
|
|
757
|
+
</span>
|
|
758
|
+
</button>
|
|
759
|
+
) : null}
|
|
760
|
+
</div>
|
|
761
|
+
|
|
750
762
|
<div className="flex min-h-8 min-w-0 items-center gap-1.5">
|
|
751
763
|
{showLayoutToggle ? (
|
|
752
764
|
<div
|
|
@@ -797,6 +809,7 @@ export function L3ProjectFileBrowser({
|
|
|
797
809
|
</button>
|
|
798
810
|
) : null}
|
|
799
811
|
<button
|
|
812
|
+
ref={searchButtonRef}
|
|
800
813
|
type="button"
|
|
801
814
|
className={cn(buttonVariants({ variant: 'outline', size: 'icon-sm' }), 'ml-auto')}
|
|
802
815
|
aria-label={t('searchProject')}
|
|
@@ -809,133 +822,62 @@ export function L3ProjectFileBrowser({
|
|
|
809
822
|
<SearchIcon className="size-3.5" />
|
|
810
823
|
</button>
|
|
811
824
|
</div>
|
|
812
|
-
|
|
813
|
-
|
|
825
|
+
</div>
|
|
826
|
+
)}
|
|
814
827
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
className={cn(
|
|
858
|
-
'block truncate text-xs font-medium',
|
|
859
|
-
changedFileNameClass(change)
|
|
860
|
-
)}
|
|
861
|
-
>
|
|
862
|
-
{change.projectPath.split('/').at(-1)}
|
|
863
|
-
</span>
|
|
864
|
-
<span className="mt-0.5 block truncate font-mono text-xs text-muted-foreground">
|
|
865
|
-
{change.projectPath}
|
|
866
|
-
</span>
|
|
867
|
-
</span>
|
|
868
|
-
</button>,
|
|
869
|
-
change,
|
|
870
|
-
true
|
|
871
|
-
)
|
|
872
|
-
})
|
|
873
|
-
) : (
|
|
874
|
-
<p className="px-2 py-5 text-center text-xs text-muted-foreground">
|
|
875
|
-
{t('noMatches')}
|
|
828
|
+
{searchOpen ? (
|
|
829
|
+
<L3ProjectSearchResults
|
|
830
|
+
key={JSON.stringify([
|
|
831
|
+
workspace.search.query,
|
|
832
|
+
workspace.search.scope,
|
|
833
|
+
workspace.search.caseSensitive,
|
|
834
|
+
workspace.search.wholeWord,
|
|
835
|
+
workspace.search.regex
|
|
836
|
+
])}
|
|
837
|
+
search={workspace.search}
|
|
838
|
+
resultsRef={searchResultsRef}
|
|
839
|
+
activePath={workspace.activeDiff ? null : workspace.activePath}
|
|
840
|
+
onOpen={openPreview}
|
|
841
|
+
wrapFile={(path, line, trigger) =>
|
|
842
|
+
fileContextMenu(path, trigger, changeByProjectPath.get(path) ?? null, false, line)
|
|
843
|
+
}
|
|
844
|
+
renderFileIcon={fileIcon}
|
|
845
|
+
fileDecoration={(path) => {
|
|
846
|
+
const change = changeByProjectPath.get(path) ?? null
|
|
847
|
+
return {
|
|
848
|
+
className: changedFileNameClass(change),
|
|
849
|
+
badge: change ? getL3GitChangeBadge(change.change) : undefined
|
|
850
|
+
}
|
|
851
|
+
}}
|
|
852
|
+
/>
|
|
853
|
+
) : (
|
|
854
|
+
<L4ScrollArea
|
|
855
|
+
className="min-h-0 flex-1"
|
|
856
|
+
viewportClassName="py-1.5"
|
|
857
|
+
viewportRef={scrollRef}
|
|
858
|
+
onViewportScroll={(event) => runtime.setTreeScrollTop(event.currentTarget.scrollTop)}
|
|
859
|
+
>
|
|
860
|
+
{repositories
|
|
861
|
+
.filter((item) => item.state === 'unavailable')
|
|
862
|
+
.map((item) =>
|
|
863
|
+
item.state === 'unavailable' ? (
|
|
864
|
+
<p
|
|
865
|
+
key={item.root}
|
|
866
|
+
role="alert"
|
|
867
|
+
className="mx-2 mb-1 break-words rounded-md bg-muted px-2 py-2 text-sm text-destructive"
|
|
868
|
+
>
|
|
869
|
+
{item.root || projectName}:{selectL4LocalizedText(item.message, locale)}
|
|
876
870
|
</p>
|
|
877
|
-
)
|
|
878
|
-
) : (
|
|
879
|
-
<>
|
|
880
|
-
{workspace?.search.status === 'loading' ? (
|
|
881
|
-
<div className="flex min-h-10 items-center gap-2 px-2 text-xs text-muted-foreground">
|
|
882
|
-
<LoaderCircleIcon className="size-3.5 animate-spin" /> {t('searching')}
|
|
883
|
-
</div>
|
|
884
|
-
) : null}
|
|
885
|
-
{workspace?.search.status === 'error' ? (
|
|
886
|
-
<p className="px-2 py-2 text-xs text-destructive">{workspace.search.error}</p>
|
|
887
|
-
) : null}
|
|
888
|
-
{workspace?.search.status === 'ready'
|
|
889
|
-
? workspace.search.matches.map((path) => {
|
|
890
|
-
const change = changeByProjectPath.get(path) ?? null
|
|
891
|
-
return fileContextMenu(
|
|
892
|
-
path,
|
|
893
|
-
<button
|
|
894
|
-
data-testid={`project-search-result-${path}`}
|
|
895
|
-
type="button"
|
|
896
|
-
onClick={() => {
|
|
897
|
-
openPreview(path)
|
|
898
|
-
}}
|
|
899
|
-
className="flex min-h-10 w-full items-center gap-2 rounded-lg px-2 text-left hover:bg-muted/70"
|
|
900
|
-
title={path}
|
|
901
|
-
>
|
|
902
|
-
<span className="shrink-0">{fileIcon(path)}</span>
|
|
903
|
-
<span className="min-w-0 flex-1">
|
|
904
|
-
<span
|
|
905
|
-
className={cn(
|
|
906
|
-
'block truncate text-xs font-medium',
|
|
907
|
-
changedFileNameClass(change)
|
|
908
|
-
)}
|
|
909
|
-
>
|
|
910
|
-
{path.split('/').at(-1)}
|
|
911
|
-
</span>
|
|
912
|
-
<span className="mt-0.5 block truncate font-mono text-xs text-muted-foreground">
|
|
913
|
-
{path}
|
|
914
|
-
</span>
|
|
915
|
-
</span>
|
|
916
|
-
</button>
|
|
917
|
-
)
|
|
918
|
-
})
|
|
919
|
-
: null}
|
|
920
|
-
{workspace?.search.status === 'ready' && workspace.search.matches.length === 0 ? (
|
|
921
|
-
<p className="px-2 py-5 text-center text-xs text-muted-foreground">
|
|
922
|
-
{t('noMatches')}
|
|
923
|
-
</p>
|
|
924
|
-
) : null}
|
|
925
|
-
{workspace?.search.status === 'ready' && workspace.search.truncated ? (
|
|
926
|
-
<p className="px-2 py-2 text-xs text-amber-600 dark:text-amber-400">
|
|
927
|
-
{t('resultLimit')}
|
|
928
|
-
</p>
|
|
929
|
-
) : null}
|
|
930
|
-
</>
|
|
871
|
+
) : null
|
|
931
872
|
)}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
873
|
+
{gitWorkspace?.snapshot.status === 'error' ? (
|
|
874
|
+
<p className="mx-2 mb-1 rounded-md bg-muted px-2 py-2 text-sm text-destructive">
|
|
875
|
+
{gitWorkspace.snapshot.error}
|
|
876
|
+
</p>
|
|
877
|
+
) : null}
|
|
878
|
+
{effectiveLayout === 'repository' ? renderRepositoryView() : renderDirectoryView()}
|
|
879
|
+
</L4ScrollArea>
|
|
880
|
+
)}
|
|
939
881
|
</section>
|
|
940
882
|
)
|
|
941
883
|
}
|