plugin.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. 'use strict';
  4. import {
  5. IContentsModel
  6. } from 'jupyter-js-services';
  7. import {
  8. DocumentManager
  9. } from 'jupyter-js-ui/lib/docmanager';
  10. import {
  11. FileBrowserWidget, FileBrowserModel
  12. } from 'jupyter-js-ui/lib/filebrowser';
  13. import {
  14. Application
  15. } from 'phosphide/lib/core/application';
  16. import {
  17. IChangedArgs
  18. } from 'phosphor-properties';
  19. import {
  20. Menu, MenuBar, MenuItem
  21. } from 'phosphor-menus';
  22. import {
  23. TabPanel
  24. } from 'phosphor-tabs';
  25. import {
  26. JupyterServices
  27. } from '../services/plugin';
  28. /**
  29. * The default file browser extension.
  30. */
  31. export
  32. const fileBrowserExtension = {
  33. id: 'jupyter.extensions.fileBrowser',
  34. requires: [DocumentManager, JupyterServices],
  35. activate: activateFileBrowser
  36. };
  37. /**
  38. * Activate the file browser.
  39. */
  40. function activateFileBrowser(app: Application, manager: DocumentManager, provider: JupyterServices): Promise<void> {
  41. let contents = provider.contentsManager;
  42. let sessions = provider.notebookSessionManager;
  43. let model = new FileBrowserModel(contents, sessions);
  44. let widget = new FileBrowserWidget(model);
  45. let menu = createMenu(widget);
  46. // Add a context menu to the dir listing.
  47. let node = widget.node.getElementsByClassName('jp-DirListing-content')[0];
  48. node.addEventListener('contextmenu', (event: MouseEvent) => {
  49. event.preventDefault();
  50. let x = event.clientX;
  51. let y = event.clientY;
  52. menu.popup(x, y);
  53. });
  54. let id = 0;
  55. let onOpenRequested = (model: IContentsModel) => {
  56. let widget = manager.open(model);
  57. if (!widget.id) widget.id = `document-manager-${++id}`;
  58. if (!widget.isAttached) app.shell.addToMainArea(widget);
  59. let stack = widget.parent;
  60. if (!stack) {
  61. return;
  62. }
  63. let tabs = stack.parent;
  64. if (tabs instanceof TabPanel) {
  65. tabs.currentWidget = widget;
  66. }
  67. }
  68. let onFileChanged = (args: IChangedArgs<string>) => {
  69. manager.rename(args.oldValue, args.newValue);
  70. }
  71. model.openRequested.connect((bModel, model) => onOpenRequested(model));
  72. model.fileChanged.connect((mModel, args) => onFileChanged(args));
  73. // Create a command to add a new empty text file.
  74. // This requires an id and an instance of a command object.
  75. let newTextFileId = 'file-operations:new-text-file';
  76. // Add the command to the command registry and command palette plugins.
  77. app.commands.add([
  78. {
  79. id: newTextFileId,
  80. handler: () => {
  81. widget.newUntitled('file', '.txt')
  82. .then(contents => onOpenRequested(contents));
  83. }
  84. }
  85. ]);
  86. app.palette.add([
  87. {
  88. command: newTextFileId,
  89. category: 'File Operations',
  90. text: 'New Text File',
  91. caption: 'Create a new text file'
  92. }
  93. ]);
  94. // Add the command for a new notebook.
  95. let newNotebookId = 'file-operations:new-notebook';
  96. app.commands.add([
  97. {
  98. id: newNotebookId,
  99. handler: () => {
  100. widget.newUntitled('notebook')
  101. .then(contents => onOpenRequested(contents));
  102. }
  103. }
  104. ]);
  105. app.palette.add([
  106. {
  107. command: newNotebookId,
  108. category: 'File Operations',
  109. text: 'New Notebook',
  110. caption: 'Create a new Jupyter Notebook'
  111. }
  112. ]);
  113. // Add the command for saving a document.
  114. let saveDocumentId = 'file-operations:save';
  115. app.commands.add([
  116. {
  117. id: saveDocumentId,
  118. handler: () => {
  119. manager.save();
  120. }
  121. }
  122. ]);
  123. app.palette.add([
  124. {
  125. command: saveDocumentId,
  126. category: 'File Operations',
  127. text: 'Save Document',
  128. caption: 'Save the current document'
  129. }
  130. ]);
  131. // Add the command for reverting a document.
  132. let revertDocumentId = 'file-operations:revert';
  133. app.commands.add([
  134. {
  135. id: revertDocumentId,
  136. handler: () => {
  137. manager.revert();
  138. }
  139. }
  140. ]);
  141. app.palette.add([
  142. {
  143. command: revertDocumentId,
  144. category: 'File Operations',
  145. text: 'Revert Document',
  146. caption: 'Revert the current document'
  147. }
  148. ]);
  149. // Add the command for closing a document.
  150. let closeDocumentId = 'file-operations:close';
  151. app.commands.add([
  152. {
  153. id: closeDocumentId,
  154. handler: () => {
  155. manager.close();
  156. }
  157. }
  158. ]);
  159. app.palette.add([
  160. {
  161. command: closeDocumentId,
  162. category: 'File Operations',
  163. text: 'Close Document',
  164. caption: 'Close the current document'
  165. }
  166. ]);
  167. // Add the command for closing all documents.
  168. let closeAllId = 'file-operations:close-all';
  169. app.commands.add([
  170. {
  171. id: closeAllId,
  172. handler: () => {
  173. manager.closeAll();
  174. }
  175. }
  176. ]);
  177. app.palette.add([
  178. {
  179. command: closeAllId,
  180. category: 'File Operations',
  181. text: 'Close All',
  182. caption: 'Close all open documents'
  183. }
  184. ]);
  185. app.commands.add([
  186. {
  187. id: 'file-browser:activate',
  188. handler: showBrowser
  189. },
  190. {
  191. id: 'file-browser:hide',
  192. handler: hideBrowser
  193. },
  194. {
  195. id: 'file-browser:toggle',
  196. handler: toggleBrowser
  197. }
  198. ]);
  199. widget.widgetFactory = model => {
  200. return manager.open(model);
  201. }
  202. widget.title.text = 'Files';
  203. widget.id = 'file-browser';
  204. app.shell.addToLeftArea(widget, { rank: 40 });
  205. return Promise.resolve(void 0);
  206. function showBrowser(): void {
  207. app.shell.activateLeft(widget.id);
  208. }
  209. function hideBrowser(): void {
  210. if (!widget.isHidden) app.shell.collapseLeft();
  211. }
  212. function toggleBrowser(): void {
  213. if (widget.isHidden) {
  214. showBrowser();
  215. } else {
  216. hideBrowser();
  217. }
  218. }
  219. }
  220. /**
  221. * Create a context menu for the file browser listing.
  222. */
  223. function createMenu(fbWidget: FileBrowserWidget): Menu {
  224. return new Menu([
  225. new MenuItem({
  226. text: '&Open',
  227. icon: 'fa fa-folder-open-o',
  228. shortcut: 'Ctrl+O',
  229. handler: () => { fbWidget.open(); }
  230. }),
  231. new MenuItem({
  232. text: '&Rename',
  233. icon: 'fa fa-edit',
  234. shortcut: 'Ctrl+R',
  235. handler: () => { fbWidget.rename(); }
  236. }),
  237. new MenuItem({
  238. text: '&Delete',
  239. icon: 'fa fa-remove',
  240. shortcut: 'Ctrl+D',
  241. handler: () => { fbWidget.delete(); }
  242. }),
  243. new MenuItem({
  244. text: 'Duplicate',
  245. icon: 'fa fa-copy',
  246. handler: () => { fbWidget.duplicate(); }
  247. }),
  248. new MenuItem({
  249. text: 'Cut',
  250. icon: 'fa fa-cut',
  251. shortcut: 'Ctrl+X',
  252. handler: () => { fbWidget.cut(); }
  253. }),
  254. new MenuItem({
  255. text: '&Copy',
  256. icon: 'fa fa-copy',
  257. shortcut: 'Ctrl+C',
  258. handler: () => { fbWidget.copy(); }
  259. }),
  260. new MenuItem({
  261. text: '&Paste',
  262. icon: 'fa fa-paste',
  263. shortcut: 'Ctrl+V',
  264. handler: () => { fbWidget.paste(); }
  265. }),
  266. new MenuItem({
  267. text: 'Download',
  268. icon: 'fa fa-download',
  269. handler: () => { fbWidget.download(); }
  270. }),
  271. new MenuItem({
  272. text: 'Shutdown Kernel',
  273. icon: 'fa fa-stop-circle-o',
  274. handler: () => { fbWidget.shutdownKernels(); }
  275. }),
  276. ]);
  277. }