Skip to content

Commit 6bf80cc

Browse files
Improved editor design (#455)
* fix: improved editor design * fix: simplify folder button visibility logic and add local settings for permissions * fix: replace Ruler Cross Pen icon with new Pen icon and improve event handling in file actions
1 parent 97bb372 commit 6bf80cc

3 files changed

Lines changed: 107 additions & 18 deletions

File tree

src/main/frontend/app/components/file-structure/editor-file-structure.tsx

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import '/styles/editor-files.css'
77
import AltArrowRightIcon from '../../../icons/solar/Alt Arrow Right.svg?react'
88
import AltArrowDownIcon from '../../../icons/solar/Alt Arrow Down.svg?react'
99
import CodeIcon from '../../../icons/solar/Code.svg?react'
10+
import CodeFileIcon from '../../../icons/solar/Code File.svg?react'
11+
import TrashBinIcon from '../../../icons/solar/Trash Bin.svg?react'
12+
import Pen from '../../../icons/solar/Pen.svg?react'
1013
import { useShortcut } from '~/hooks/use-shortcut'
1114
import type { ContextMenuState } from './use-file-tree-context-menu'
1215

@@ -114,6 +117,15 @@ export default function EditorFileStructure() {
114117
[selectedItemId, buildContextForItem],
115118
)
116119

120+
const triggerItemAction = useCallback(
121+
(itemId: TreeItemIndex, action: (menuState: ContextMenuState) => void) => {
122+
void buildContextForItem(itemId).then((menuState) => {
123+
if (menuState) action(menuState)
124+
})
125+
},
126+
[buildContextForItem],
127+
)
128+
117129
useShortcut({
118130
'explorer.new-file': () => triggerExplorerAction(editorContextMenu.handleNewFile, false),
119131
'explorer.new-folder': () => triggerExplorerAction(editorContextMenu.handleNewFolder, false),
@@ -285,6 +297,7 @@ export default function EditorFileStructure() {
285297
context: TreeItemRenderContext
286298
}) => {
287299
const Icon = item.isFolder ? (context.isExpanded ? FolderOpenIcon : FolderIcon) : CodeIcon
300+
const isRoot = (item.data as { projectRoot?: boolean }).projectRoot ?? false
288301

289302
const searchLower = searchTerm.toLowerCase()
290303
const titleLower = title.toLowerCase()
@@ -310,28 +323,103 @@ export default function EditorFileStructure() {
310323

311324
const isHighlighted = highlightedItemId === item.index
312325

326+
const actionBtnClass = 'cursor-pointer rounded p-0.5 hover:bg-hover flex-shrink-0'
327+
313328
return (
314329
<div
315-
className="flex h-full w-full cursor-pointer items-center"
330+
className="group/row flex h-full w-full items-center"
316331
onContextMenu={(e) => editorContextMenu.openContextMenu(e, item.index)}
317332
>
318333
{Icon && <Icon className="fill-foreground w-4 flex-shrink-0" />}
319334
<span
320-
className={`ml-1 overflow-hidden text-nowrap text-ellipsis ${
335+
className={`ml-1 min-w-0 flex-1 overflow-hidden text-nowrap text-ellipsis ${
321336
isHighlighted ? 'outline-foreground-active rounded-sm px-1 outline-2' : ''
322337
}`}
323338
>
324339
{highlightedTitle}
325340
</span>
341+
<div className="ml-1 hidden items-center gap-0.5 group-hover/row:flex">
342+
{item.isFolder && (
343+
<button
344+
className={actionBtnClass}
345+
title="New File"
346+
onClick={(mouseEvent) => {
347+
mouseEvent.stopPropagation()
348+
triggerItemAction(item.index, editorContextMenu.handleNewFile)
349+
}}
350+
>
351+
<CodeFileIcon className="fill-foreground h-3.5 w-3.5" />
352+
</button>
353+
)}
354+
{item.isFolder && (
355+
<button
356+
className={actionBtnClass}
357+
title="New Folder"
358+
onClick={(mouseEvent) => {
359+
mouseEvent.stopPropagation()
360+
triggerItemAction(item.index, editorContextMenu.handleNewFolder)
361+
}}
362+
>
363+
<FolderIcon className="fill-foreground h-3.5 w-3.5" />
364+
</button>
365+
)}
366+
{!isRoot && (
367+
<button
368+
className={actionBtnClass}
369+
title="Rename"
370+
onClick={(mouseEvent) => {
371+
mouseEvent.stopPropagation()
372+
triggerItemAction(item.index, editorContextMenu.handleRename)
373+
}}
374+
>
375+
<Pen className="stroke-foreground h-3.5 w-3.5" />
376+
</button>
377+
)}
378+
{!isRoot && (
379+
<button
380+
className={actionBtnClass}
381+
title="Delete"
382+
onClick={(mouseEvent) => {
383+
mouseEvent.stopPropagation()
384+
triggerItemAction(item.index, editorContextMenu.handleDelete)
385+
}}
386+
>
387+
<TrashBinIcon className="fill-foreground h-3.5 w-3.5" />
388+
</button>
389+
)}
390+
</div>
326391
</div>
327392
)
328393
}
329394

330395
if (!dataProvider) return <LoadingSpinner message="Loading files..." className="p-8" />
331396

397+
const toolbarBtnClass = 'cursor-pointer rounded p-1 hover:bg-hover text-foreground'
398+
332399
return (
333400
<>
334-
<Search onChange={(e) => setSearchTerm(e.target.value)} />
401+
<div className="border-border flex items-center justify-between border-b px-2 py-1">
402+
<span className="text-foreground/50 text-xs font-semibold tracking-wider uppercase">Explorer</span>
403+
<div className="flex items-center gap-0.5">
404+
<button
405+
className={toolbarBtnClass}
406+
title="New File"
407+
onClick={() => triggerExplorerAction(editorContextMenu.handleNewFile, false)}
408+
>
409+
<CodeFileIcon className="fill-foreground h-4 w-4" />
410+
</button>
411+
<button
412+
className={toolbarBtnClass}
413+
title="New Folder"
414+
onClick={() => triggerExplorerAction(editorContextMenu.handleNewFolder, false)}
415+
>
416+
<FolderIcon className="fill-foreground h-4 w-4" />
417+
</button>
418+
</div>
419+
</div>
420+
<div className="mt-2">
421+
<Search onChange={(changeEvent) => setSearchTerm(changeEvent.target.value)} />
422+
</div>
335423
<div
336424
className="h-full overflow-auto pr-2"
337425
onContextMenu={(e) => {

src/main/frontend/app/routes/editor/editor.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -671,19 +671,19 @@ export default function CodeEditor() {
671671
<>
672672
<div className="flex h-12 items-center justify-between pr-4">
673673
<SidebarHeader side={SidebarSide.LEFT} title="Files" />
674-
<div className="border-border ml-auto flex overflow-hidden rounded border text-sm">
675-
<button
676-
onClick={() => setLeftTab('files')}
677-
className={clsx(
678-
'cursor-pointer px-3 py-1 transition-colors',
679-
leftTab === 'files'
680-
? 'bg-selected text-foreground font-medium'
681-
: 'hover:bg-foreground-active text-muted-foreground',
682-
)}
683-
>
684-
Files
685-
</button>
686-
{isGitRepo && (
674+
{isGitRepo && (
675+
<div className="border-border ml-auto flex overflow-hidden rounded border text-sm">
676+
<button
677+
onClick={() => setLeftTab('files')}
678+
className={clsx(
679+
'cursor-pointer px-3 py-1 transition-colors',
680+
leftTab === 'files'
681+
? 'bg-selected text-foreground font-medium'
682+
: 'hover:bg-foreground-active text-muted-foreground',
683+
)}
684+
>
685+
Files
686+
</button>
687687
<button
688688
onClick={() => setLeftTab('git')}
689689
className={clsx(
@@ -695,8 +695,8 @@ export default function CodeEditor() {
695695
>
696696
Git
697697
</button>
698-
)}
699-
</div>
698+
</div>
699+
)}
700700
</div>
701701
{leftTab === 'files' && <EditorFileStructure />}
702702
{leftTab !== 'files' && isGitRepo && (
Lines changed: 1 addition & 0 deletions
Loading

0 commit comments

Comments
 (0)