index.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import {
  6. ILayoutRestorer, JupyterLab, JupyterLabPlugin
  7. } from '@jupyterlab/application';
  8. import {
  9. ICommandPalette, IThemeManager, ThemeManager, ISplashScreen
  10. } from '@jupyterlab/apputils';
  11. import {
  12. DataConnector, ISettingRegistry, IStateDB, SettingRegistry, StateDB
  13. } from '@jupyterlab/coreutils';
  14. import {
  15. IMainMenu
  16. } from '@jupyterlab/mainmenu';
  17. import {
  18. ServiceManager
  19. } from '@jupyterlab/services';
  20. import {
  21. each
  22. } from '@phosphor/algorithm';
  23. import {
  24. JSONObject
  25. } from '@phosphor/coreutils';
  26. import {
  27. DisposableDelegate, IDisposable
  28. } from '@phosphor/disposable';
  29. import {
  30. Menu
  31. } from '@phosphor/widgets';
  32. import {
  33. activatePalette
  34. } from './palette';
  35. import '../style/index.css';
  36. /**
  37. * The command IDs used by the apputils plugin.
  38. */
  39. namespace CommandIDs {
  40. export
  41. const changeTheme = 'apputils:change-theme';
  42. export
  43. const clearStateDB = 'apputils:clear-statedb';
  44. export
  45. const loadState = 'apputils:load-statedb';
  46. }
  47. /**
  48. * A data connector to access plugin settings.
  49. */
  50. class SettingsConnector extends DataConnector<ISettingRegistry.IPlugin, string> {
  51. /**
  52. * Create a new settings connector.
  53. */
  54. constructor(manager: ServiceManager) {
  55. super();
  56. this._manager = manager;
  57. }
  58. /**
  59. * Retrieve a saved bundle from the data connector.
  60. */
  61. fetch(id: string): Promise<ISettingRegistry.IPlugin> {
  62. return this._manager.settings.fetch(id).then(data => {
  63. // Replace the server ID with the original unmodified version.
  64. data.id = id;
  65. return data;
  66. });
  67. }
  68. /**
  69. * Save the user setting data in the data connector.
  70. */
  71. save(id: string, raw: string): Promise<void> {
  72. return this._manager.settings.save(id, raw);
  73. }
  74. private _manager: ServiceManager;
  75. }
  76. /**
  77. * The default commmand palette extension.
  78. */
  79. const palette: JupyterLabPlugin<ICommandPalette> = {
  80. activate: activatePalette,
  81. id: '@jupyterlab/apputils-extension:palette',
  82. provides: ICommandPalette,
  83. requires: [ILayoutRestorer],
  84. autoStart: true
  85. };
  86. /**
  87. * The default setting registry provider.
  88. */
  89. const settings: JupyterLabPlugin<ISettingRegistry> = {
  90. id: '@jupyterlab/apputils-extension:settings',
  91. activate: (app: JupyterLab): ISettingRegistry => {
  92. const connector = new SettingsConnector(app.serviceManager);
  93. return new SettingRegistry({ connector });
  94. },
  95. autoStart: true,
  96. provides: ISettingRegistry
  97. };
  98. /**
  99. * The default theme manager provider.
  100. */
  101. const themes: JupyterLabPlugin<IThemeManager> = {
  102. id: '@jupyterlab/apputils-extension:themes',
  103. requires: [ISettingRegistry, ISplashScreen],
  104. optional: [ICommandPalette, IMainMenu],
  105. activate: (app: JupyterLab, settingRegistry: ISettingRegistry, splash: ISplashScreen, palette: ICommandPalette | null, mainMenu: IMainMenu | null): IThemeManager => {
  106. const host = app.shell;
  107. const when = app.started;
  108. const commands = app.commands;
  109. const manager = new ThemeManager({
  110. key: themes.id,
  111. host, settingRegistry,
  112. url: app.info.urls.themes,
  113. splash,
  114. when
  115. });
  116. commands.addCommand(CommandIDs.changeTheme, {
  117. label: args => {
  118. const theme = args['theme'] as string;
  119. return args['isPalette'] ? `Use ${theme} Theme` : theme;
  120. },
  121. isToggled: args => args['theme'] === manager.theme,
  122. execute: args => {
  123. if (args['theme'] === manager.theme) {
  124. return;
  125. }
  126. manager.setTheme(args['theme'] as string);
  127. }
  128. });
  129. // If we have a main menu, add the theme manager
  130. // to the settings menu.
  131. if (mainMenu) {
  132. const themeMenu = new Menu({ commands });
  133. themeMenu.title.label = 'JupyterLab Theme';
  134. manager.ready.then(() => {
  135. each(manager.themes, theme => {
  136. themeMenu.addItem({
  137. command: CommandIDs.changeTheme,
  138. args: { isPalette: false, theme: theme }
  139. });
  140. });
  141. });
  142. mainMenu.settingsMenu.addGroup([{
  143. type: 'submenu' as Menu.ItemType, submenu: themeMenu
  144. }], 0);
  145. }
  146. // If we have a command palette, add theme
  147. // switching options to it.
  148. if (palette) {
  149. const category = 'Settings';
  150. manager.ready.then(() => {
  151. each(manager.themes, theme => {
  152. palette.addItem({
  153. command: CommandIDs.changeTheme,
  154. args: { isPalette: true, theme: theme },
  155. category
  156. });
  157. });
  158. });
  159. }
  160. return manager;
  161. },
  162. autoStart: true,
  163. provides: IThemeManager
  164. };
  165. /**
  166. * The default splash screen provider.
  167. */
  168. const splash: JupyterLabPlugin<ISplashScreen> = {
  169. id: '@jupyterlab/apputils-extension:splash',
  170. autoStart: true,
  171. provides: ISplashScreen,
  172. activate: () => ({ show: () => Private.showSplash() })
  173. };
  174. /**
  175. * The default state database for storing application state.
  176. */
  177. const state: JupyterLabPlugin<IStateDB> = {
  178. id: '@jupyterlab/apputils-extension:state',
  179. autoStart: true,
  180. provides: IStateDB,
  181. requires: [IRouter],
  182. activate: (app: JupyterLab, router: IRouter) => {
  183. const { commands, info, restored } = app;
  184. const state = new StateDB({
  185. namespace: info.namespace,
  186. when: restored.then(() => { /* no-op */ })
  187. });
  188. const version = info.version;
  189. const key = 'statedb:version';
  190. const fetch = state.fetch(key);
  191. const save = () => state.save(key, { version });
  192. const reset = () => state.clear().then(save);
  193. const check = (value: JSONObject) => {
  194. let old = value && value['version'];
  195. if (!old || old !== version) {
  196. const previous = old || 'unknown';
  197. console.log(`Upgraded: ${previous} to ${version}; Resetting DB.`);
  198. return reset();
  199. }
  200. };
  201. app.commands.addCommand(CommandIDs.clearStateDB, {
  202. label: 'Clear Application Restore State',
  203. execute: () => state.clear()
  204. });
  205. return fetch.then(check, reset).then(() => state);
  206. }
  207. };
  208. /**
  209. * Export the plugins as default.
  210. */
  211. const plugins: JupyterLabPlugin<any>[] = [
  212. palette, settings, state, splash, themes
  213. ];
  214. export default plugins;
  215. /**
  216. * The namespace for module private data.
  217. */
  218. namespace Private {
  219. /**
  220. * The splash element.
  221. */
  222. let splash: HTMLElement | null;
  223. /**
  224. * The splash screen counter.
  225. */
  226. let splashCount = 0;
  227. /**
  228. * Show the splash element.
  229. */
  230. export
  231. function showSplash(): IDisposable {
  232. if (!splash) {
  233. splash = document.createElement('div');
  234. splash.id = 'jupyterlab-splash';
  235. let galaxy = document.createElement('div');
  236. galaxy.id = 'galaxy';
  237. splash.appendChild(galaxy);
  238. let mainLogo = document.createElement('div');
  239. mainLogo.id = 'main-logo';
  240. let planet = document.createElement('div');
  241. let planet2 = document.createElement('div');
  242. let planet3 = document.createElement('div');
  243. planet.className = 'planet';
  244. planet2.className = 'planet';
  245. planet3.className = 'planet';
  246. let moon1 = document.createElement('div');
  247. moon1.id = 'moon1';
  248. moon1.className = 'moon orbit';
  249. moon1.appendChild(planet);
  250. let moon2 = document.createElement('div');
  251. moon2.id = 'moon2';
  252. moon2.className = 'moon orbit';
  253. moon2.appendChild(planet2);
  254. let moon3 = document.createElement('div');
  255. moon3.id = 'moon3';
  256. moon3.className = 'moon orbit';
  257. moon3.appendChild(planet3);
  258. galaxy.appendChild(mainLogo);
  259. galaxy.appendChild(moon1);
  260. galaxy.appendChild(moon2);
  261. galaxy.appendChild(moon3);
  262. }
  263. splash.classList.remove('splash-fade');
  264. document.body.appendChild(splash);
  265. splashCount++;
  266. return new DisposableDelegate(() => {
  267. splashCount = Math.max(splashCount - 1, 0);
  268. if (splashCount === 0 && splash) {
  269. splash.classList.add('splash-fade');
  270. setTimeout(() => {
  271. document.body.removeChild(splash);
  272. }, 500);
  273. }
  274. });
  275. }
  276. }