11import "./AttachmentMenu.css" ;
2- import { Tooltip } from "@components/ui/Tooltip" ;
32import { File , GithubLogo , Paperclip } from "@phosphor-icons/react" ;
43import { IconButton , Popover } from "@radix-ui/themes" ;
5- import { useTRPC } from "@renderer/trpc/client" ;
4+ import { trpcClient , useTRPC } from "@renderer/trpc/client" ;
5+ import { toast } from "@renderer/utils/toast" ;
66import { useQuery } from "@tanstack/react-query" ;
7+ import { getFileName } from "@utils/path" ;
78import { useRef , useState } from "react" ;
89import type { FileAttachment , MentionChip } from "../utils/content" ;
10+ import { persistBrowserFile } from "../utils/persistFile" ;
911import { IssuePicker } from "./IssuePicker" ;
1012
1113type View = "menu" | "issues" ;
@@ -54,20 +56,42 @@ export function AttachmentMenu({
5456
5557 const issueDisabledReason = getIssueDisabledReason ( ghStatus , repoPath ) ;
5658
57- const handleFileSelect = ( e : React . ChangeEvent < HTMLInputElement > ) => {
58- const files = e . target . files ;
59- if ( files && files . length > 0 ) {
60- const fileArray = Array . from ( files ) ;
61- for ( const file of fileArray ) {
62- const filePath =
63- ( file as globalThis . File & { path ?: string } ) . path || file . name ;
64- onAddAttachment ( { id : filePath , label : file . name } ) ;
65- }
66- onAttachFiles ?.( fileArray ) ;
67- }
59+ const handleFileSelect = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
60+ const files = e . target . files ? Array . from ( e . target . files ) : [ ] ;
6861 if ( fileInputRef . current ) {
6962 fileInputRef . current . value = "" ;
7063 }
64+
65+ if ( files . length === 0 ) {
66+ return ;
67+ }
68+
69+ try {
70+ const attachments = await Promise . all (
71+ files . map ( async ( file ) => {
72+ const filePath = ( file as globalThis . File & { path ?: string } ) . path ;
73+ if ( filePath ) {
74+ return { id : filePath , label : file . name } satisfies FileAttachment ;
75+ }
76+
77+ return await persistBrowserFile ( file ) ;
78+ } ) ,
79+ ) ;
80+
81+ for ( const attachment of attachments ) {
82+ if ( attachment ) {
83+ onAddAttachment ( attachment ) ;
84+ }
85+ }
86+
87+ onAttachFiles ?.( files ) ;
88+ } catch ( error ) {
89+ toast . error (
90+ error instanceof Error
91+ ? error . message
92+ : "Unable to attach selected files from this picker" ,
93+ ) ;
94+ }
7195 } ;
7296
7397 const handleOpenChange = ( isOpen : boolean ) => {
@@ -77,8 +101,21 @@ export function AttachmentMenu({
77101 }
78102 } ;
79103
80- const handleAddFile = ( ) => {
104+ const handleAddFile = async ( ) => {
81105 setOpen ( false ) ;
106+
107+ try {
108+ const filePaths = await trpcClient . os . selectFiles . query ( ) ;
109+ if ( filePaths . length > 0 ) {
110+ for ( const filePath of filePaths ) {
111+ onAddAttachment ( { id : filePath , label : getFileName ( filePath ) } ) ;
112+ }
113+ }
114+ return ;
115+ } catch {
116+ // Fall back to the input element for non-Electron environments.
117+ }
118+
82119 fileInputRef . current ?. click ( ) ;
83120 } ;
84121
@@ -112,18 +149,17 @@ export function AttachmentMenu({
112149 style = { { display : "none" } }
113150 />
114151 < Popover . Root open = { open } onOpenChange = { handleOpenChange } >
115- < Tooltip content = { attachTooltip } >
116- < Popover . Trigger >
117- < IconButton
118- size = "1"
119- variant = "ghost"
120- color = "gray"
121- disabled = { disabled }
122- >
123- < Paperclip size = { iconSize } weight = "bold" />
124- </ IconButton >
125- </ Popover . Trigger >
126- </ Tooltip >
152+ < Popover . Trigger >
153+ < IconButton
154+ size = "1"
155+ variant = "ghost"
156+ color = "gray"
157+ disabled = { disabled }
158+ title = { attachTooltip }
159+ >
160+ < Paperclip size = { iconSize } weight = "bold" />
161+ </ IconButton >
162+ </ Popover . Trigger >
127163 < Popover . Content side = "top" align = "start" style = { { padding : 0 } } >
128164 { view === "menu" ? (
129165 < div className = "attachment-menu" >
@@ -138,9 +174,7 @@ export function AttachmentMenu({
138174 < span > Add file</ span >
139175 </ button >
140176 { issueDisabledReason ? (
141- < Tooltip content = { issueDisabledReason } side = "right" >
142- < span > { issueButton } </ span >
143- </ Tooltip >
177+ < span title = { issueDisabledReason } > { issueButton } </ span >
144178 ) : (
145179 issueButton
146180 ) }
0 commit comments