From eb36ec6170bc7c943b57033dfe5a7169db5fb55d Mon Sep 17 00:00:00 2001 From: Rob Koch Date: Sat, 7 Dec 2024 18:11:38 -0800 Subject: [PATCH 1/3] basic context menu for folders --- app/components/workbench/FileTree.tsx | 75 +++++++++++++++++++++------ package.json | 1 + 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/app/components/workbench/FileTree.tsx b/app/components/workbench/FileTree.tsx index 0882e1f43..5b6e7a959 100644 --- a/app/components/workbench/FileTree.tsx +++ b/app/components/workbench/FileTree.tsx @@ -2,6 +2,7 @@ import { memo, useEffect, useMemo, useState, type ReactNode } from 'react'; import type { FileMap } from '~/lib/stores/files'; import { classNames } from '~/utils/classNames'; import { createScopedLogger, renderLogger } from '~/utils/logger'; +import * as ContextMenu from '@radix-ui/react-context-menu'; const logger = createScopedLogger('FileTree'); @@ -159,23 +160,65 @@ interface FolderProps { onClick: () => void; } -function Folder({ folder: { depth, name }, collapsed, selected = false, onClick }: FolderProps) { +interface FolderContextMenuProps { + children: ReactNode; +} + +function ContextMenuItem({ children }: { children: ReactNode }) { return ( - - {name} - + + + {children} + + ); +} + +function FolderContextMenu({ children }: FolderContextMenuProps) { + return ( + + {children} + + + + New file... + New folder... + + + Copy path + Copy relative path + + + Rename... + Delete + + + + + ); +} + +function Folder({ folder, collapsed, selected = false, onClick }: FolderProps) { + return ( + + + {folder.name} + + ); } diff --git a/package.json b/package.json index 15f4d991a..6463c96c6 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@octokit/rest": "^21.0.2", "@octokit/types": "^13.6.2", "@openrouter/ai-sdk-provider": "^0.0.5", + "@radix-ui/react-context-menu": "^2.2.2", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-separator": "^1.1.0", From 13a15e9a3d29597b02d8597898cf9087a9c78d05 Mon Sep 17 00:00:00 2001 From: Rob Koch Date: Sun, 8 Dec 2024 15:22:46 -0800 Subject: [PATCH 2/3] copyPath and copyRelativePath for files and folders --- app/components/workbench/FileTree.tsx | 107 ++++++++++++++++++-------- 1 file changed, 73 insertions(+), 34 deletions(-) diff --git a/app/components/workbench/FileTree.tsx b/app/components/workbench/FileTree.tsx index 5b6e7a959..eb185cbba 100644 --- a/app/components/workbench/FileTree.tsx +++ b/app/components/workbench/FileTree.tsx @@ -111,6 +111,22 @@ export const FileTree = memo( }); }; + const onCopyPath = (fileOrFolder: FileNode | FolderNode) => { + try { + navigator.clipboard.writeText(fileOrFolder.fullPath); + } catch (error) { + logger.error(error); + } + }; + + const onCopyRelativePath = (fileOrFolder: FileNode | FolderNode) => { + try { + navigator.clipboard.writeText(fileOrFolder.fullPath.substring((rootFolder || '').length)); + } catch (error) { + logger.error(error); + } + }; + return (
{filteredFileList.map((fileOrFolder) => { @@ -122,6 +138,12 @@ export const FileTree = memo( selected={selectedFile === fileOrFolder.fullPath} file={fileOrFolder} unsavedChanges={unsavedFiles?.has(fileOrFolder.fullPath)} + onCopyPath={() => { + onCopyPath(fileOrFolder); + }} + onCopyRelativePath={() => { + onCopyRelativePath(fileOrFolder); + }} onClick={() => { onFileSelect?.(fileOrFolder.fullPath); }} @@ -135,6 +157,12 @@ export const FileTree = memo( folder={fileOrFolder} selected={allowFolderSelection && selectedFile === fileOrFolder.fullPath} collapsed={collapsedFolders.has(fileOrFolder.fullPath)} + onCopyPath={() => { + onCopyPath(fileOrFolder); + }} + onCopyRelativePath={() => { + onCopyRelativePath(fileOrFolder); + }} onClick={() => { toggleCollapseState(fileOrFolder.fullPath); }} @@ -157,23 +185,30 @@ interface FolderProps { folder: FolderNode; collapsed: boolean; selected?: boolean; + onCopyPath: () => void; + onCopyRelativePath: () => void; onClick: () => void; } interface FolderContextMenuProps { + onCopyPath?: () => void; + onCopyRelativePath?: () => void; children: ReactNode; } -function ContextMenuItem({ children }: { children: ReactNode }) { +function ContextMenuItem({ onSelect, children }: { onSelect?: () => void; children: ReactNode }) { return ( - + {children} ); } -function FolderContextMenu({ children }: FolderContextMenuProps) { +function FileContextMenu({ onCopyPath, onCopyRelativePath, children }: FolderContextMenuProps) { return ( {children} @@ -183,16 +218,8 @@ function FolderContextMenu({ children }: FolderContextMenuProps) { className="border border-bolt-elements-borderColor rounded-md z-context-menu bg-bolt-elements-background-depth-1 dark:bg-bolt-elements-background-depth-2 data-[state=open]:animate-in animate-duration-100 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-98 w-56" > - New file... - New folder... - - - Copy path - Copy relative path - - - Rename... - Delete + Copy path + Copy relative path @@ -200,9 +227,9 @@ function FolderContextMenu({ children }: FolderContextMenuProps) { ); } -function Folder({ folder, collapsed, selected = false, onClick }: FolderProps) { +function Folder({ folder, collapsed, selected = false, onCopyPath, onCopyRelativePath, onClick }: FolderProps) { return ( - + {folder.name} - + ); } @@ -226,31 +253,43 @@ interface FileProps { file: FileNode; selected: boolean; unsavedChanges?: boolean; + onCopyPath: () => void; + onCopyRelativePath: () => void; onClick: () => void; } -function File({ file: { depth, name }, onClick, selected, unsavedChanges = false }: FileProps) { +function File({ + file: { depth, name }, + onClick, + onCopyPath, + onCopyRelativePath, + selected, + unsavedChanges = false, +}: FileProps) { return ( - -
+ -
{name}
- {unsavedChanges && } -
-
+
+
{name}
+ {unsavedChanges && } +
+ + ); } From 6eb2d84dfffd9ebada1167a6fef4aec20998b87c Mon Sep 17 00:00:00 2001 From: Rob Koch Date: Sun, 8 Dec 2024 16:31:53 -0800 Subject: [PATCH 3/3] pnpm lock file --- pnpm-lock.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e355d04ea..17bdc8999 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: '@openrouter/ai-sdk-provider': specifier: ^0.0.5 version: 0.0.5(zod@3.23.8) + '@radix-ui/react-context-menu': + specifier: ^2.2.2 + version: 2.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1557,6 +1560,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.2': + resolution: {integrity: sha512-99EatSTpW+hRYHt7m8wdDlLtkmTovEe8Z/hnxUPV+SKuuNL5HWNhQI4QSdjZqNSgXHay2z4M3Dym73j9p2Gx5Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-context@1.1.0': resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} peerDependencies: @@ -7032,6 +7048,20 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + '@radix-ui/react-context-menu@2.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-menu': 2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + '@radix-ui/react-context@1.1.0(@types/react@18.3.12)(react@18.3.1)': dependencies: react: 18.3.1