index.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. ILayoutRestorer,
  5. JupyterLab,
  6. JupyterLabPlugin
  7. } from '@jupyterlab/application';
  8. import {
  9. ICommandPalette,
  10. InstanceTracker,
  11. MainAreaWidget
  12. } from '@jupyterlab/apputils';
  13. import { ILauncher } from '@jupyterlab/launcher';
  14. import { IMainMenu } from '@jupyterlab/mainmenu';
  15. import { ServiceManager } from '@jupyterlab/services';
  16. import { ITerminalTracker, Terminal } from '@jupyterlab/terminal';
  17. /**
  18. * The command IDs used by the terminal plugin.
  19. */
  20. namespace CommandIDs {
  21. export const createNew = 'terminal:create-new';
  22. export const open = 'terminal:open';
  23. export const refresh = 'terminal:refresh';
  24. export const increaseFont = 'terminal:increase-font';
  25. export const decreaseFont = 'terminal:decrease-font';
  26. export const toggleTheme = 'terminal:toggle-theme';
  27. }
  28. /**
  29. * The class name for the terminal icon in the default theme.
  30. */
  31. const TERMINAL_ICON_CLASS = 'jp-TerminalIcon';
  32. /**
  33. * The default terminal extension.
  34. */
  35. const plugin: JupyterLabPlugin<ITerminalTracker> = {
  36. activate,
  37. id: '@jupyterlab/terminal-extension:plugin',
  38. provides: ITerminalTracker,
  39. requires: [IMainMenu, ICommandPalette, ILayoutRestorer],
  40. optional: [ILauncher],
  41. autoStart: true
  42. };
  43. /**
  44. * Export the plugin as default.
  45. */
  46. export default plugin;
  47. /**
  48. * Activate the terminal plugin.
  49. */
  50. function activate(
  51. app: JupyterLab,
  52. mainMenu: IMainMenu,
  53. palette: ICommandPalette,
  54. restorer: ILayoutRestorer,
  55. launcher: ILauncher | null
  56. ): ITerminalTracker {
  57. const { serviceManager } = app;
  58. const category = 'Terminal';
  59. const namespace = 'terminal';
  60. const tracker = new InstanceTracker<MainAreaWidget<Terminal>>({ namespace });
  61. // Bail if there are no terminals available.
  62. if (!serviceManager.terminals.isAvailable()) {
  63. console.log(
  64. 'Disabling terminals plugin because they are not available on the server'
  65. );
  66. return tracker;
  67. }
  68. // Handle state restoration.
  69. restorer.restore(tracker, {
  70. command: CommandIDs.createNew,
  71. args: widget => ({ name: widget.content.session.name }),
  72. name: widget => widget.content.session && widget.content.session.name
  73. });
  74. addCommands(app, serviceManager, tracker);
  75. // Add some commands to the application view menu.
  76. const viewGroup = [
  77. CommandIDs.increaseFont,
  78. CommandIDs.decreaseFont,
  79. CommandIDs.toggleTheme
  80. ].map(command => {
  81. return { command };
  82. });
  83. mainMenu.viewMenu.addGroup(viewGroup, 30);
  84. // Add command palette items.
  85. [
  86. CommandIDs.createNew,
  87. CommandIDs.refresh,
  88. CommandIDs.increaseFont,
  89. CommandIDs.decreaseFont,
  90. CommandIDs.toggleTheme
  91. ].forEach(command => {
  92. palette.addItem({ command, category, args: { isPalette: true } });
  93. });
  94. // Add terminal creation to the file menu.
  95. mainMenu.fileMenu.newMenu.addGroup([{ command: CommandIDs.createNew }], 20);
  96. // Add a launcher item if the launcher is available.
  97. if (launcher) {
  98. launcher.add({
  99. command: CommandIDs.createNew,
  100. category: 'Other',
  101. rank: 0
  102. });
  103. }
  104. app.contextMenu.addItem({
  105. command: CommandIDs.refresh,
  106. selector: '.jp-Terminal',
  107. rank: 1
  108. });
  109. return tracker;
  110. }
  111. /**
  112. * Add the commands for the terminal.
  113. */
  114. export function addCommands(
  115. app: JupyterLab,
  116. services: ServiceManager,
  117. tracker: InstanceTracker<MainAreaWidget<Terminal>>
  118. ) {
  119. let { commands, shell } = app;
  120. /**
  121. * Whether there is an active terminal.
  122. */
  123. function isEnabled(): boolean {
  124. return (
  125. tracker.currentWidget !== null &&
  126. tracker.currentWidget === app.shell.currentWidget
  127. );
  128. }
  129. // Add terminal commands.
  130. commands.addCommand(CommandIDs.createNew, {
  131. label: args => (args['isPalette'] ? 'New Terminal' : 'Terminal'),
  132. caption: 'Start a new terminal session',
  133. iconClass: args => (args['isPalette'] ? '' : TERMINAL_ICON_CLASS),
  134. execute: args => {
  135. const name = args['name'] as string;
  136. const initialCommand = args['initialCommand'] as string;
  137. const term = new Terminal({ initialCommand });
  138. const promise = name
  139. ? services.terminals.connectTo(name)
  140. : services.terminals.startNew();
  141. term.title.icon = TERMINAL_ICON_CLASS;
  142. term.title.label = '...';
  143. let main = new MainAreaWidget({ content: term });
  144. shell.addToMainArea(main);
  145. return promise
  146. .then(session => {
  147. term.session = session;
  148. tracker.add(main);
  149. shell.activateById(main.id);
  150. return main;
  151. })
  152. .catch(() => {
  153. term.dispose();
  154. });
  155. }
  156. });
  157. commands.addCommand(CommandIDs.open, {
  158. execute: args => {
  159. const name = args['name'] as string;
  160. // Check for a running terminal with the given name.
  161. const widget = tracker.find(value => {
  162. let content = value.content;
  163. return (content.session && content.session.name === name) || false;
  164. });
  165. if (widget) {
  166. shell.activateById(widget.id);
  167. } else {
  168. // Otherwise, create a new terminal with a given name.
  169. return commands.execute(CommandIDs.createNew, { name });
  170. }
  171. }
  172. });
  173. commands.addCommand(CommandIDs.refresh, {
  174. label: 'Refresh Terminal',
  175. caption: 'Refresh the current terminal session',
  176. execute: () => {
  177. let current = tracker.currentWidget;
  178. if (!current) {
  179. return;
  180. }
  181. shell.activateById(current.id);
  182. return current.content.refresh().then(() => {
  183. if (current) {
  184. current.content.activate();
  185. }
  186. });
  187. },
  188. isEnabled: () => tracker.currentWidget !== null
  189. });
  190. commands.addCommand(CommandIDs.increaseFont, {
  191. label: 'Increase Terminal Font Size',
  192. execute: () => {
  193. let options = Terminal.defaultOptions;
  194. if (options.fontSize < 72) {
  195. options.fontSize++;
  196. tracker.forEach(widget => {
  197. widget.content.fontSize = options.fontSize;
  198. });
  199. }
  200. },
  201. isEnabled
  202. });
  203. commands.addCommand(CommandIDs.decreaseFont, {
  204. label: 'Decrease Terminal Font Size',
  205. execute: () => {
  206. let options = Terminal.defaultOptions;
  207. if (options.fontSize > 9) {
  208. options.fontSize--;
  209. tracker.forEach(widget => {
  210. widget.content.fontSize = options.fontSize;
  211. });
  212. }
  213. },
  214. isEnabled
  215. });
  216. let terminalTheme: Terminal.Theme = 'dark';
  217. commands.addCommand(CommandIDs.toggleTheme, {
  218. label: 'Use Dark Terminal Theme',
  219. caption: 'Whether to use the dark terminal theme',
  220. isToggled: () => terminalTheme === 'dark',
  221. execute: () => {
  222. terminalTheme = terminalTheme === 'dark' ? 'light' : 'dark';
  223. let options = Terminal.defaultOptions;
  224. options.theme = terminalTheme;
  225. tracker.forEach(widget => {
  226. if (widget.content.theme !== terminalTheme) {
  227. widget.content.theme = terminalTheme;
  228. }
  229. });
  230. commands.notifyCommandChanged(CommandIDs.toggleTheme);
  231. },
  232. isEnabled
  233. });
  234. }