palette.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /* -----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import { find } from '@lumino/algorithm';
  6. import { CommandRegistry } from '@lumino/commands';
  7. import { DisposableDelegate, IDisposable } from '@lumino/disposable';
  8. import { CommandPalette } from '@lumino/widgets';
  9. import { ILayoutRestorer, JupyterFrontEnd } from '@jupyterlab/application';
  10. import {
  11. ICommandPalette,
  12. IPaletteItem,
  13. ModalCommandPalette
  14. } from '@jupyterlab/apputils';
  15. import { ITranslator, nullTranslator } from '@jupyterlab/translation';
  16. import { ISettingRegistry } from '@jupyterlab/settingregistry';
  17. import { CommandPaletteSvg, paletteIcon } from '@jupyterlab/ui-components';
  18. /**
  19. * The command IDs used by the apputils extension.
  20. */
  21. namespace CommandIDs {
  22. export const activate = 'apputils:activate-command-palette';
  23. }
  24. const PALETTE_PLUGIN_ID = '@jupyterlab/apputils-extension:palette';
  25. /**
  26. * A thin wrapper around the `CommandPalette` class to conform with the
  27. * JupyterLab interface for the application-wide command palette.
  28. */
  29. export class Palette implements ICommandPalette {
  30. /**
  31. * Create a palette instance.
  32. */
  33. constructor(palette: CommandPalette, translator?: ITranslator) {
  34. this.translator = translator || nullTranslator;
  35. const trans = this.translator.load('jupyterlab');
  36. this._palette = palette;
  37. this._palette.title.label = '';
  38. this._palette.title.caption = trans.__('Command Palette');
  39. }
  40. /**
  41. * The placeholder text of the command palette's search input.
  42. */
  43. set placeholder(placeholder: string) {
  44. this._palette.inputNode.placeholder = placeholder;
  45. }
  46. get placeholder(): string {
  47. return this._palette.inputNode.placeholder;
  48. }
  49. /**
  50. * Activate the command palette for user input.
  51. */
  52. activate(): void {
  53. this._palette.activate();
  54. }
  55. /**
  56. * Add a command item to the command palette.
  57. *
  58. * @param options - The options for creating the command item.
  59. *
  60. * @returns A disposable that will remove the item from the palette.
  61. */
  62. addItem(options: IPaletteItem): IDisposable {
  63. const item = this._palette.addItem(options as CommandPalette.IItemOptions);
  64. return new DisposableDelegate(() => {
  65. this._palette.removeItem(item);
  66. });
  67. }
  68. protected translator: ITranslator;
  69. private _palette: CommandPalette;
  70. }
  71. /**
  72. * A namespace for `Palette` statics.
  73. */
  74. export namespace Palette {
  75. /**
  76. * Activate the command palette.
  77. */
  78. export function activate(
  79. app: JupyterFrontEnd,
  80. translator: ITranslator,
  81. settingRegistry: ISettingRegistry | null
  82. ): ICommandPalette {
  83. const { commands, shell } = app;
  84. const trans = translator.load('jupyterlab');
  85. const palette = Private.createPalette(app, translator);
  86. const modalPalette = new ModalCommandPalette({ commandPalette: palette });
  87. let modal = false;
  88. shell.add(palette, 'left', { rank: 300 });
  89. if (settingRegistry) {
  90. const loadSettings = settingRegistry.load(PALETTE_PLUGIN_ID);
  91. const updateSettings = (settings: ISettingRegistry.ISettings): void => {
  92. const newModal = settings.get('modal').composite as boolean;
  93. if (modal && !newModal) {
  94. palette.parent = null;
  95. modalPalette.detach();
  96. shell.add(palette, 'left', { rank: 300 });
  97. } else if (!modal && newModal) {
  98. palette.parent = null;
  99. modalPalette.palette = palette;
  100. palette.show();
  101. modalPalette.attach();
  102. }
  103. modal = newModal;
  104. };
  105. Promise.all([loadSettings, app.restored])
  106. .then(([settings]) => {
  107. updateSettings(settings);
  108. settings.changed.connect(settings => {
  109. updateSettings(settings);
  110. });
  111. })
  112. .catch((reason: Error) => {
  113. console.error(reason.message);
  114. });
  115. }
  116. // Show the current palette shortcut in its title.
  117. const updatePaletteTitle = () => {
  118. const binding = find(
  119. app.commands.keyBindings,
  120. b => b.command === CommandIDs.activate
  121. );
  122. if (binding) {
  123. const ks = CommandRegistry.formatKeystroke(binding.keys.join(' '));
  124. palette.title.caption = trans.__('Commands (%1)', ks);
  125. } else {
  126. palette.title.caption = trans.__('Commands');
  127. }
  128. };
  129. updatePaletteTitle();
  130. app.commands.keyBindingChanged.connect(() => {
  131. updatePaletteTitle();
  132. });
  133. commands.addCommand(CommandIDs.activate, {
  134. execute: () => {
  135. if (modal) {
  136. modalPalette.activate();
  137. } else {
  138. shell.activateById(palette.id);
  139. }
  140. },
  141. label: trans.__('Activate Command Palette')
  142. });
  143. palette.inputNode.placeholder = trans.__('SEARCH');
  144. return new Palette(palette, translator);
  145. }
  146. /**
  147. * Restore the command palette.
  148. */
  149. export function restore(
  150. app: JupyterFrontEnd,
  151. restorer: ILayoutRestorer,
  152. translator: ITranslator
  153. ): void {
  154. const palette = Private.createPalette(app, translator);
  155. // Let the application restorer track the command palette for restoration of
  156. // application state (e.g. setting the command palette as the current side bar
  157. // widget).
  158. restorer.add(palette, 'command-palette');
  159. }
  160. }
  161. /**
  162. * The namespace for module private data.
  163. */
  164. namespace Private {
  165. /**
  166. * The private command palette instance.
  167. */
  168. let palette: CommandPalette;
  169. /**
  170. * Create the application-wide command palette.
  171. */
  172. export function createPalette(
  173. app: JupyterFrontEnd,
  174. translator: ITranslator
  175. ): CommandPalette {
  176. if (!palette) {
  177. // use a renderer tweaked to use inline svg icons
  178. palette = new CommandPalette({
  179. commands: app.commands,
  180. renderer: CommandPaletteSvg.defaultRenderer
  181. });
  182. palette.id = 'command-palette';
  183. palette.title.icon = paletteIcon;
  184. const trans = translator.load('jupyterlab');
  185. palette.title.label = trans.__('Commands');
  186. }
  187. return palette;
  188. }
  189. }