@@ -4,7 +4,7 @@ import { join } from "@tauri-apps/api/path";
44import { message } from "@tauri-apps/plugin-dialog" ;
55import { fetch as tauriFetch } from "@tauri-apps/plugin-http" ;
66import { openPath , openUrl } from "@tauri-apps/plugin-opener" ;
7- import { BookText , ChevronDown , ChevronUp , FileText , HelpCircle , Mail , Share2Icon } from "lucide-react" ;
7+ import { BookText , Check , ChevronDown , ChevronUp , Copy , FileText , HelpCircle , Mail , Share2Icon } from "lucide-react" ;
88import { useState } from "react" ;
99
1010import { useHypr } from "@/contexts" ;
@@ -38,6 +38,7 @@ function ShareButtonInNote() {
3838 const [ open , setOpen ] = useState ( false ) ;
3939 const [ expandedId , setExpandedId ] = useState < string | null > ( null ) ;
4040 const [ selectedObsidianFolder , setSelectedObsidianFolder ] = useState < string > ( "default" ) ;
41+ const [ copySuccess , setCopySuccess ] = useState ( false ) ;
4142 const hasEnhancedNote = ! ! session ?. enhanced_memo_html ;
4243
4344 const isObsidianConfigured = useQuery ( {
@@ -72,6 +73,15 @@ function ShareButtonInNote() {
7273 staleTime : 5 * 60 * 1000 ,
7374 } ) ;
7475
76+ const directActions : DirectAction [ ] = [
77+ {
78+ id : "copy" ,
79+ title : "Copy Note" ,
80+ icon : < Copy size = { 20 } /> ,
81+ description : "" ,
82+ } ,
83+ ] ;
84+
7585 const exportOptions : ExportCard [ ] = [
7686 {
7787 id : "pdf" ,
@@ -124,6 +134,10 @@ function ShareButtonInNote() {
124134 setOpen ( newOpen ) ;
125135 setExpandedId ( null ) ;
126136
137+ if ( ! newOpen ) {
138+ setCopySuccess ( false ) ;
139+ }
140+
127141 if ( newOpen ) {
128142 isObsidianConfigured . refetch ( ) . then ( ( configResult ) => {
129143 if ( configResult . data ) {
@@ -143,7 +157,9 @@ function ShareButtonInNote() {
143157 const start = performance . now ( ) ;
144158 let result : ExportResult | null = null ;
145159
146- if ( optionId === "pdf" ) {
160+ if ( optionId === "copy" ) {
161+ result = await exportHandlers . copy ( session ) ;
162+ } else if ( optionId === "pdf" ) {
147163 result = await exportHandlers . pdf ( session ) ;
148164 } else if ( optionId === "email" ) {
149165 result = await exportHandlers . email ( session ) ;
@@ -187,16 +203,22 @@ function ShareButtonInNote() {
187203 } ) ;
188204 } ,
189205 onSuccess : ( result ) => {
190- if ( result ?. type === "pdf" && result . path ) {
206+ if ( result ?. type === "copy" && result . success ) {
207+ setCopySuccess ( true ) ;
208+ // Reset after 2 seconds
209+ setTimeout ( ( ) => setCopySuccess ( false ) , 2000 ) ;
210+ } else if ( result ?. type === "pdf" && result . path ) {
191211 openPath ( result . path ) ;
192212 } else if ( result ?. type === "email" && result . url ) {
193213 openUrl ( result . url ) ;
194214 } else if ( result ?. type === "obsidian" && result . url ) {
195215 openUrl ( result . url ) ;
196216 }
197217 } ,
198- onSettled : ( ) => {
199- setOpen ( false ) ;
218+ onSettled : ( result ) => {
219+ if ( result ?. type !== "copy" ) {
220+ setOpen ( false ) ;
221+ }
200222 } ,
201223 onError : ( error ) => {
202224 console . error ( error ) ;
@@ -238,6 +260,35 @@ function ShareButtonInNote() {
238260 </ p >
239261 </ div >
240262 < div className = "space-y-2" >
263+ { /* Direct action buttons */ }
264+ { directActions . map ( ( action ) => {
265+ const isLoading = exportMutation . isPending && exportMutation . variables ?. optionId === action . id ;
266+ const isSuccess = action . id === "copy" && copySuccess ;
267+
268+ return (
269+ < div key = { action . id } className = "border rounded-lg overflow-hidden" >
270+ < button
271+ onClick = { ( ) => handleExport ( action . id ) }
272+ disabled = { exportMutation . isPending }
273+ className = "w-full flex items-center justify-between p-3 hover:bg-gray-50 transition-colors disabled:opacity-50"
274+ >
275+ < div className = "flex items-center space-x-3" >
276+ < div className = { `text-gray-700 transition-colors ${ isSuccess ? "text-green-600" : "" } ` } >
277+ { isSuccess ? < Check size = { 20 } /> : action . icon }
278+ </ div >
279+ < div className = "text-left" >
280+ < span className = "font-medium text-sm block" > { action . title } </ span >
281+ < span className = "text-xs text-gray-600" > { action . description } </ span >
282+ </ div >
283+ </ div >
284+ { isLoading && < span className = "text-xs text-gray-500" > Copying...</ span > }
285+ { isSuccess && < span className = "text-xs text-green-600" > Copied!</ span > }
286+ </ button >
287+ </ div >
288+ ) ;
289+ } ) }
290+
291+ { /* Expandable export options */ }
241292 { exportOptions . map ( ( option ) => {
242293 const expanded = expandedId === option . id ;
243294
@@ -315,6 +366,13 @@ function ShareButtonInNote() {
315366 ) ;
316367}
317368
369+ interface DirectAction {
370+ id : "copy" ;
371+ title : string ;
372+ icon : React . ReactNode ;
373+ description : string ;
374+ }
375+
318376interface ExportCard {
319377 id : "pdf" | "email" | "obsidian" ;
320378 title : string ;
@@ -324,9 +382,10 @@ interface ExportCard {
324382}
325383
326384interface ExportResult {
327- type : "pdf" | "email" | "obsidian" ;
385+ type : "copy" | " pdf" | "email" | "obsidian" ;
328386 path ?: string ;
329387 url ?: string ;
388+ success ?: boolean ;
330389}
331390
332391interface ObsidianFolder {
@@ -335,6 +394,26 @@ interface ObsidianFolder {
335394}
336395
337396const exportHandlers = {
397+ copy : async ( session : Session ) : Promise < ExportResult > => {
398+ try {
399+ let textToCopy = "" ;
400+
401+ if ( session . enhanced_memo_html ) {
402+ textToCopy = html2md ( session . enhanced_memo_html ) ;
403+ } else if ( session . raw_memo_html ) {
404+ textToCopy = html2md ( session . raw_memo_html ) ;
405+ } else {
406+ textToCopy = session . title || "No content available" ;
407+ }
408+
409+ await navigator . clipboard . writeText ( textToCopy ) ;
410+ return { type : "copy" , success : true } ;
411+ } catch ( error ) {
412+ console . error ( "Failed to copy to clipboard:" , error ) ;
413+ throw new Error ( "Failed to copy note to clipboard" ) ;
414+ }
415+ } ,
416+
338417 pdf : async ( session : Session ) : Promise < ExportResult > => {
339418 const path = await exportToPDF ( session ) ;
340419 return { type : "pdf" , path } ;
0 commit comments