themeplugins.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /* -----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import {
  6. JupyterFrontEnd,
  7. JupyterFrontEndPlugin
  8. } from '@jupyterlab/application';
  9. import {
  10. ICommandPalette,
  11. ISplashScreen,
  12. IThemeManager,
  13. ThemeManager
  14. } from '@jupyterlab/apputils';
  15. import { URLExt } from '@jupyterlab/coreutils';
  16. import { IMainMenu } from '@jupyterlab/mainmenu';
  17. import { ISettingRegistry } from '@jupyterlab/settingregistry';
  18. import { Menu } from '@lumino/widgets';
  19. namespace CommandIDs {
  20. export const changeTheme = 'apputils:change-theme';
  21. export const themeScrollbars = 'apputils:theme-scrollbars';
  22. export const incrFontSize = 'apputils:incr-font-size';
  23. export const decrFontSize = 'apputils:decr-font-size';
  24. }
  25. /**
  26. * The default theme manager provider.
  27. */
  28. export const themesPlugin: JupyterFrontEndPlugin<IThemeManager> = {
  29. id: '@jupyterlab/apputils-extension:themes',
  30. requires: [ISettingRegistry, JupyterFrontEnd.IPaths],
  31. optional: [ISplashScreen],
  32. activate: (
  33. app: JupyterFrontEnd,
  34. settings: ISettingRegistry,
  35. paths: JupyterFrontEnd.IPaths,
  36. splash: ISplashScreen | null
  37. ): IThemeManager => {
  38. const host = app.shell;
  39. const commands = app.commands;
  40. const url = URLExt.join(paths.urls.base, paths.urls.themes);
  41. const key = themesPlugin.id;
  42. const manager = new ThemeManager({
  43. key,
  44. host,
  45. settings,
  46. splash: splash ?? undefined,
  47. url
  48. });
  49. // Keep a synchronously set reference to the current theme,
  50. // since the asynchronous setting of the theme in `changeTheme`
  51. // can lead to an incorrect toggle on the currently used theme.
  52. let currentTheme: string;
  53. manager.themeChanged.connect((sender, args) => {
  54. // Set data attributes on the application shell for the current theme.
  55. currentTheme = args.newValue;
  56. document.body.dataset.jpThemeLight = String(
  57. manager.isLight(currentTheme)
  58. );
  59. document.body.dataset.jpThemeName = currentTheme;
  60. if (
  61. document.body.dataset.jpThemeScrollbars !==
  62. String(manager.themeScrollbars(currentTheme))
  63. ) {
  64. document.body.dataset.jpThemeScrollbars = String(
  65. manager.themeScrollbars(currentTheme)
  66. );
  67. }
  68. // Set any CSS overrides
  69. manager.loadCSSOverrides();
  70. commands.notifyCommandChanged(CommandIDs.changeTheme);
  71. });
  72. commands.addCommand(CommandIDs.changeTheme, {
  73. label: args => {
  74. const theme = args['theme'] as string;
  75. return args['isPalette'] ? `Use ${theme} Theme` : theme;
  76. },
  77. isToggled: args => args['theme'] === currentTheme,
  78. execute: args => {
  79. const theme = args['theme'] as string;
  80. if (theme === manager.theme) {
  81. return;
  82. }
  83. return manager.setTheme(theme);
  84. }
  85. });
  86. commands.addCommand(CommandIDs.themeScrollbars, {
  87. label: 'Theme Scrollbars',
  88. isToggled: () => manager.isToggledThemeScrollbars(),
  89. execute: () => manager.toggleThemeScrollbars()
  90. });
  91. commands.addCommand(CommandIDs.incrFontSize, {
  92. label: args => `Increase ${args['label']} Font Size`,
  93. execute: args => manager.incrFontSize(args['key'] as string)
  94. });
  95. commands.addCommand(CommandIDs.decrFontSize, {
  96. label: args => `Decrease ${args['label']} Font Size`,
  97. execute: args => manager.decrFontSize(args['key'] as string)
  98. });
  99. return manager;
  100. },
  101. autoStart: true,
  102. provides: IThemeManager
  103. };
  104. /**
  105. * The default theme manager's UI command palette and main menu functionality.
  106. *
  107. * #### Notes
  108. * This plugin loads separately from the theme manager plugin in order to
  109. * prevent blocking of the theme manager while it waits for the command palette
  110. * and main menu to become available.
  111. */
  112. export const themesPaletteMenuPlugin: JupyterFrontEndPlugin<void> = {
  113. id: '@jupyterlab/apputils-extension:themes-palette-menu',
  114. requires: [IThemeManager],
  115. optional: [ICommandPalette, IMainMenu],
  116. activate: (
  117. app: JupyterFrontEnd,
  118. manager: IThemeManager,
  119. palette: ICommandPalette | null,
  120. mainMenu: IMainMenu | null
  121. ): void => {
  122. const commands = app.commands;
  123. // If we have a main menu, add the theme manager to the settings menu.
  124. if (mainMenu) {
  125. const themeMenu = new Menu({ commands });
  126. themeMenu.title.label = 'JupyterLab Theme';
  127. void app.restored.then(() => {
  128. const isPalette = false;
  129. // choose a theme
  130. manager.themes.forEach(theme => {
  131. themeMenu.addItem({
  132. command: CommandIDs.changeTheme,
  133. args: { isPalette, theme }
  134. });
  135. });
  136. themeMenu.addItem({ type: 'separator' });
  137. // toggle scrollbar theming
  138. themeMenu.addItem({ command: CommandIDs.themeScrollbars });
  139. themeMenu.addItem({ type: 'separator' });
  140. // increase/decrease code font size
  141. themeMenu.addItem({
  142. command: CommandIDs.incrFontSize,
  143. args: { label: 'Code', key: 'code-font-size' }
  144. });
  145. themeMenu.addItem({
  146. command: CommandIDs.decrFontSize,
  147. args: { label: 'Code', key: 'code-font-size' }
  148. });
  149. themeMenu.addItem({ type: 'separator' });
  150. // increase/decrease content font size
  151. themeMenu.addItem({
  152. command: CommandIDs.incrFontSize,
  153. args: { label: 'Content', key: 'content-font-size1' }
  154. });
  155. themeMenu.addItem({
  156. command: CommandIDs.decrFontSize,
  157. args: { label: 'Content', key: 'content-font-size1' }
  158. });
  159. themeMenu.addItem({ type: 'separator' });
  160. // increase/decrease ui font size
  161. themeMenu.addItem({
  162. command: CommandIDs.incrFontSize,
  163. args: { label: 'UI', key: 'ui-font-size1' }
  164. });
  165. themeMenu.addItem({
  166. command: CommandIDs.decrFontSize,
  167. args: { label: 'UI', key: 'ui-font-size1' }
  168. });
  169. });
  170. mainMenu.settingsMenu.addGroup(
  171. [
  172. {
  173. type: 'submenu' as Menu.ItemType,
  174. submenu: themeMenu
  175. }
  176. ],
  177. 0
  178. );
  179. }
  180. // If we have a command palette, add theme switching options to it.
  181. if (palette) {
  182. void app.restored.then(() => {
  183. const category = 'Theme';
  184. const command = CommandIDs.changeTheme;
  185. const isPalette = true;
  186. // choose a theme
  187. manager.themes.forEach(theme => {
  188. palette.addItem({ command, args: { isPalette, theme }, category });
  189. });
  190. // toggle scrollbar theming
  191. palette.addItem({ command: CommandIDs.themeScrollbars, category });
  192. // increase/decrease code font size
  193. palette.addItem({
  194. command: CommandIDs.incrFontSize,
  195. args: { label: 'Code', key: 'code-font-size' },
  196. category
  197. });
  198. palette.addItem({
  199. command: CommandIDs.decrFontSize,
  200. args: { label: 'Code', key: 'code-font-size' },
  201. category
  202. });
  203. // increase/decrease content font size
  204. palette.addItem({
  205. command: CommandIDs.incrFontSize,
  206. args: { label: 'Content', key: 'content-font-size1' },
  207. category
  208. });
  209. palette.addItem({
  210. command: CommandIDs.decrFontSize,
  211. args: { label: 'Content', key: 'content-font-size1' },
  212. category
  213. });
  214. // increase/decrease ui font size
  215. palette.addItem({
  216. command: CommandIDs.incrFontSize,
  217. args: { label: 'UI', key: 'ui-font-size1' },
  218. category
  219. });
  220. palette.addItem({
  221. command: CommandIDs.decrFontSize,
  222. args: { label: 'UI', key: 'ui-font-size1' },
  223. category
  224. });
  225. });
  226. }
  227. },
  228. autoStart: true
  229. };