@@ -29,15 +29,17 @@ import {
2929 newFolderIcon ,
3030 fileIcon ,
3131 notebookIcon ,
32- editIcon
32+ editIcon ,
33+ copyIcon ,
34+ pasteIcon
3335} from '@jupyterlab/ui-components' ;
3436import { PageConfig , PathExt } from '@jupyterlab/coreutils' ;
3537import { CommandRegistry } from '@lumino/commands' ;
3638import { Widget } from '@lumino/widgets' ;
3739
3840import { driveBrowserIcon , removeIcon } from '../icons' ;
3941import { Drive } from '../contents' ;
40- import { setListingLimit } from '../requests' ;
42+ import { getContents , setListingLimit } from '../requests' ;
4143import { CommandIDs } from '../token' ;
4244
4345/**
@@ -697,5 +699,85 @@ namespace Private {
697699 '#drive-file-browser.jp-SidePanel .jp-DirListing-content .jp-DirListing-item[data-isdir]' ,
698700 rank : 110
699701 } ) ;
702+
703+ app . commands . addCommand ( CommandIDs . copyToFilebrowser , {
704+ isVisible : ( ) => {
705+ // So long as this command only handles one file at time, don't show it
706+ // if multiple files are selected.
707+ return (
708+ ! ! tracker . currentWidget &&
709+ Array . from ( tracker . currentWidget . selectedItems ( ) ) . length === 1 &&
710+ browser . model . path !== 's3:'
711+ ) ;
712+ } ,
713+ isEnabled : ( ) => {
714+ return (
715+ ! ! tracker . currentWidget &&
716+ tracker . currentWidget ?. selectedItems ( ) . next ( ) ! . value &&
717+ tracker . currentWidget ?. selectedItems ( ) . next ( ) ! . value . type !==
718+ 'directory'
719+ ) ;
720+ } ,
721+ execute : async ( ) => {
722+ let currentPath : string = tracker . currentWidget ?. selectedItems ( ) . next ( )
723+ . value . path ;
724+ Clipboard . copyToSystem ( currentPath ) ;
725+ } ,
726+ label : 'Copy to Filebrowser' ,
727+ icon : copyIcon . bindprops ( { stylesheet : 'menuItem' } )
728+ } ) ;
729+
730+ app . contextMenu . addItem ( {
731+ command : CommandIDs . copyToFilebrowser ,
732+ selector : '#drive-file-browser.jp-SidePanel .jp-DirListing-content' ,
733+ rank : 10
734+ } ) ;
735+
736+ app . commands . addCommand ( CommandIDs . pasteToFilebrowser , {
737+ isVisible : ( ) => {
738+ return (
739+ ! ! factory . tracker . currentWidget &&
740+ factory . tracker . currentWidget ! . id === 'filebrowser'
741+ ) ;
742+ } ,
743+ execute : async ( args : any ) => {
744+ // Get path from clipboard.
745+ let currentPath = await navigator . clipboard . readText ( ) ;
746+ // Remove leading drive suffix.
747+ currentPath = currentPath . substring ( currentPath . indexOf ( ':' ) + 1 ) ;
748+ const driveName = currentPath . substring ( 0 , currentPath . indexOf ( '/' ) ) ;
749+ // Remove drive name.
750+ currentPath = currentPath . substring ( currentPath . indexOf ( '/' ) + 1 ) ;
751+ const fileName = PathExt . basename ( currentPath ) ;
752+
753+ const filebrowser = factory . tracker . find ( fb => fb . id === 'filebrowser' ) ;
754+
755+ // Save new file.
756+ const toDir = factory . tracker . currentWidget ! . model . path ;
757+ app . serviceManager . contents . save ( PathExt . join ( toDir , fileName ) , {
758+ type : 'file' ,
759+ format :
760+ PathExt . extname ( fileName ) === '.txt' ||
761+ PathExt . extname ( fileName ) === '.ipynb'
762+ ? 'text'
763+ : 'base64' ,
764+ content : (
765+ await getContents ( driveName , {
766+ path : currentPath ,
767+ registeredFileTypes : drive . registeredFileTypes
768+ } )
769+ ) . response . data . content
770+ } ) ;
771+ await filebrowser ?. model . refresh ( ) ;
772+ } ,
773+ label : 'Paste from Drive' ,
774+ icon : pasteIcon . bindprops ( { stylesheet : 'menuItem' } )
775+ } ) ;
776+
777+ app . contextMenu . addItem ( {
778+ command : CommandIDs . pasteToFilebrowser ,
779+ selector : '.jp-FileBrowser-listing' ,
780+ rank : 10
781+ } ) ;
700782 }
701783}
0 commit comments