plugin.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. h, VNode
  5. } from 'phosphor/lib/ui/vdom';
  6. import {
  7. JupyterLab, JupyterLabPlugin
  8. } from '../application';
  9. import {
  10. ICommandPalette
  11. } from '../commandpalette';
  12. import {
  13. VDomModel, VDomWidget
  14. } from '../common/vdom';
  15. /**
  16. * The faq page extension.
  17. */
  18. export
  19. const faqExtension: JupyterLabPlugin<void> = {
  20. id: 'jupyter.extensions.faq',
  21. requires: [ICommandPalette],
  22. activate: activateFAQ,
  23. autoStart: true
  24. };
  25. /**
  26. * The class name added to the FAQ plugin.
  27. */
  28. const FAQ_CLASS = 'jp-FAQ';
  29. /**
  30. * The id name added to the header section element.
  31. */
  32. const HEADER_ID = 'faq-header';
  33. /**
  34. * The class name added to the title.
  35. */
  36. const TITLE_CLASS = 'jp-FAQ-title';
  37. /**
  38. * The class name added to h1 elements.
  39. */
  40. const HEADER_CLASS = 'jp-FAQ-h1';
  41. /**
  42. * The class name added to h2 elements.
  43. */
  44. const SUBHEADER_CLASS = 'jp-FAQ-h2';
  45. /**
  46. * The class name added for the question mark icon from default-theme.
  47. */
  48. const QUESTIONMARK_ICON_CLASS = 'jp-QuestionMark';
  49. /**
  50. * The class named added the question mark icon.
  51. */
  52. const QUESTIONMARK_CLASS = 'jp-FAQ-QuestionMark';
  53. /**
  54. * The class name added to faq content.
  55. */
  56. const CONTENT_CLASS = 'jp-FAQ-content';
  57. /**
  58. * The class name added to unordered list elements.
  59. */
  60. const FAQ_LIST_CLASS = 'jp-FAQ-ul';
  61. /**
  62. * The class name added to table of contents elements.
  63. */
  64. const TOC_CLASS = 'jp-FAQ-toc';
  65. /**
  66. * The class name added to questions.
  67. */
  68. const QUESTION_CLASS = 'jp-FAQ-question';
  69. /**
  70. * The class name added to answers.
  71. */
  72. const ANSWER_CLASS = 'jp-FAQ-answer';
  73. /**
  74. * The class name added to anchor elements.
  75. */
  76. const ANCHOR_CLASS = 'jp-FAQ-a';
  77. /**
  78. * FaqModel holds data which the FaqWidget will render.
  79. */
  80. class FaqModel extends VDomModel {
  81. // Title of the FAQ plugin.
  82. readonly title: string;
  83. // Contain subheadings for each section.
  84. readonly subHeadings: string[];
  85. // Contain questions for `the basics` section.
  86. readonly basicsQuestions: string[];
  87. // Contain questions for the `features` section.
  88. readonly featuresQuestions: string[];
  89. // Contain questions for the `developer` section.
  90. readonly developerQuestions: string[];
  91. /**
  92. * Construct a new faq model.
  93. */
  94. constructor() {
  95. super();
  96. this.title = 'Frequently Asked Questions';
  97. this.subHeadings = [
  98. 'THE BASICS',
  99. 'FEATURES',
  100. 'DEVELOPER'
  101. ];
  102. this.basicsQuestions = [
  103. 'What is JupyterLab?',
  104. 'What is a Jupyter Notebook?',
  105. 'How stable is JupyterLab?',
  106. 'I\'m confused with the interface. How do I navigate around JupyterLab?'
  107. ];
  108. this.featuresQuestions = [
  109. 'How do I add more kernels/languages to JupyterLab?',
  110. 'How can I share my notebooks?'
  111. ];
  112. this.developerQuestions = [
  113. 'How do I report a bug?',
  114. 'I have security concerns about JupyterLab.',
  115. 'How can I contribute?'
  116. ];
  117. }
  118. }
  119. /**
  120. * A virtual-DOM-based widget for the FAQ plugin.
  121. */
  122. class FaqWidget extends VDomWidget<FaqModel> {
  123. /**
  124. * Construct a new faq widget.
  125. */
  126. constructor(app: JupyterLab) {
  127. super();
  128. this._app = app;
  129. this.addClass(FAQ_CLASS);
  130. }
  131. /**
  132. * Render the faq plugin to virtual DOM nodes.
  133. */
  134. protected render(): VNode[] {
  135. let subHeadings = this.model.subHeadings;
  136. let basicsQuestions = this.model.basicsQuestions;
  137. let featuresQuestions = this.model.featuresQuestions;
  138. let developerQuestions = this.model.developerQuestions;
  139. // Create Frequently Asked Questions Header Section.
  140. let faqHeader =
  141. h.section({id: HEADER_ID},
  142. h.span({className: QUESTIONMARK_ICON_CLASS + ' ' + QUESTIONMARK_CLASS}),
  143. h.h1({className: HEADER_CLASS},
  144. h.span({className: TITLE_CLASS},
  145. this.model.title
  146. )
  147. )
  148. );
  149. // Create a section element that holds Table of Contents.
  150. let questionList =
  151. h.section({className: CONTENT_CLASS},
  152. h.h2({className: SUBHEADER_CLASS}, subHeadings[0]),
  153. h.ul({className: FAQ_LIST_CLASS},
  154. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  155. h.a({href: '#basicsQ1'}, basicsQuestions[0])
  156. ),
  157. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  158. h.a({href: '#basicsQ2'}, basicsQuestions[1])
  159. ),
  160. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  161. h.a({href: '#basicsQ3'}, basicsQuestions[2])
  162. ),
  163. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  164. h.a({href: '#basicsQ4'}, basicsQuestions[3])
  165. )
  166. ),
  167. h.h2({className: SUBHEADER_CLASS}, subHeadings[1]),
  168. h.ul({className: FAQ_LIST_CLASS},
  169. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  170. h.a({href: '#featuresQ1'}, featuresQuestions[0])
  171. ),
  172. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  173. h.a({href: '#featuresQ2'}, featuresQuestions[1])
  174. )
  175. ),
  176. h.h2({className: SUBHEADER_CLASS}, subHeadings[2]),
  177. h.ul({className: FAQ_LIST_CLASS},
  178. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  179. h.a({href: '#developerQ1'}, developerQuestions[0])
  180. ),
  181. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  182. h.a({href: '#developerQ2'}, developerQuestions[1])
  183. ),
  184. h.li({className: QUESTION_CLASS + ' ' + TOC_CLASS},
  185. h.a({href: '#developerQ3'}, developerQuestions[2])
  186. )
  187. )
  188. );
  189. // Create a section element that all other FAQ Content will go under.
  190. let questionAnswerList =
  191. h.section({className: CONTENT_CLASS},
  192. h.h2({className: SUBHEADER_CLASS}, subHeadings[0]),
  193. // Create list of questions/answers under the Basics section.
  194. h.ul({className: FAQ_LIST_CLASS},
  195. h.li({className: QUESTION_CLASS, id: 'basicsQ1'}, basicsQuestions[0]),
  196. h.li({className: ANSWER_CLASS},
  197. 'JupyterLab allows users to arrange multiple Jupyter notebooks, '
  198. + 'text editors, terminals, output areas, etc. on a single page with multiple '
  199. + 'panels and tabs into one application. The codebase and UI of JupyterLab '
  200. + 'is based on a flexible plugin system that makes it easy to extend '
  201. + 'with new components.'
  202. ),
  203. h.li({className: QUESTION_CLASS, id: 'basicsQ2'}, basicsQuestions[1]),
  204. h.li({className: ANSWER_CLASS},
  205. 'Central to the project is the Jupyter Notebook, a web-based '
  206. + 'platform that allows users to combine live code, equations, narrative '
  207. + 'text, visualizations, interactive dashboards and other media. Together '
  208. + 'these building blocks make science and data reproducible across over '
  209. + '40 programming languages and combine to form what we call a computational '
  210. + 'narrative.'
  211. ),
  212. h.li({className: QUESTION_CLASS, id: 'basicsQ3'}, basicsQuestions[2]),
  213. h.li({className: ANSWER_CLASS},
  214. 'JupyterLab is currently in a alpha release and not ready for public use '
  215. + 'as new features and bug fixes are being added very frequently. We strongly '
  216. + 'recommend to backup your work before using JupyterLab. However, testing, '
  217. + 'development, and user feedback are greatly appreciated.'
  218. ),
  219. h.li({className: QUESTION_CLASS, id: 'basicsQ4'}, basicsQuestions[3]),
  220. h.li({className: ANSWER_CLASS},
  221. 'Check out the JupyterLab tour ',
  222. h.a({className: ANCHOR_CLASS,
  223. onclick: () => {
  224. this._app.commands.execute('about-jupyterlab:show', void 0);
  225. }},
  226. 'here'
  227. )
  228. )
  229. ),
  230. h.h2({className: SUBHEADER_CLASS}, subHeadings[1]),
  231. // Create list of questions/answers under the Features section.
  232. h.ul({className: FAQ_LIST_CLASS},
  233. h.li({className: QUESTION_CLASS, id: 'featuresQ1'}, featuresQuestions[0]),
  234. h.li({className: ANSWER_CLASS},
  235. 'To add more languages to the JupyterLab you must install '
  236. + 'a new kernel. Installing a kernel is usually fairly simple and can be '
  237. + 'done with a couple terminal commands. However the instructions for installing '
  238. + 'kernels is different for each language. For further instructions, click ',
  239. h.a({className: ANCHOR_CLASS,
  240. href: 'https://jupyter.readthedocs.io/en/latest/install-kernel.html',
  241. target: '_blank'},
  242. 'this'
  243. ),
  244. ' link.'
  245. ),
  246. h.li({className: QUESTION_CLASS, id: 'featuresQ2'}, featuresQuestions[1]),
  247. h.li({className: ANSWER_CLASS},
  248. 'You can either publish your notebooks on GitHub or use a free service such as ',
  249. h.a({className: ANCHOR_CLASS, href: 'https://nbviewer.jupyter.org/', target: '_blank'},
  250. 'nbviewer.org'
  251. ),
  252. ' to render your notebooks online.'
  253. )
  254. ),
  255. h.h2({className: SUBHEADER_CLASS}, subHeadings[2]),
  256. // Create list of questions/answers under the Developer section.
  257. h.ul({className: FAQ_LIST_CLASS},
  258. h.li({className: QUESTION_CLASS, id: 'developerQ1'}, developerQuestions[0]),
  259. h.li({className: ANSWER_CLASS},
  260. 'You can open an issue on our ',
  261. h.a({className: ANCHOR_CLASS,
  262. href: 'https://github.com/jupyterlab/jupyterlab/issues',
  263. target: '_blank'},
  264. 'github repository'
  265. ),
  266. '. Please check already opened issues before posting.'
  267. ),
  268. h.li({className: QUESTION_CLASS, id: 'developerQ2'}, developerQuestions[1]),
  269. h.li({className: ANSWER_CLASS},
  270. 'If you have any inquiries, concerns, or thought you found a security '
  271. + 'vulnerability, please write to use at ',
  272. h.a({className: ANCHOR_CLASS, href: 'mailto:security@jupyter.org'},
  273. 'security@jupyter.org'
  274. ),
  275. '. We will do our best to repond to you promptly.'
  276. ),
  277. h.li({className: QUESTION_CLASS, id: 'developerQ3'}, developerQuestions[2]),
  278. h.li({className: ANSWER_CLASS},
  279. 'There are many ways to contribute to JupyterLab. '
  280. + 'Whether you are an experienced python programmer or a newcomer, any '
  281. + 'interested developers are welcome. You can learn about the JupyterLab '
  282. + 'codebase by going through our ',
  283. h.a({className: ANCHOR_CLASS,
  284. href: 'https://jupyterlab-tutorial.readthedocs.io/en/latest/index.html',
  285. target: '_blank'},
  286. 'tutorial walkthrough'
  287. ),
  288. ' and ',
  289. h.a({className: ANCHOR_CLASS,
  290. href: 'http://jupyter.org/jupyterlab/',
  291. target: '_blank'},
  292. 'documentation'
  293. ),
  294. '. Also, feel free to ask questions on our ',
  295. h.a({className: ANCHOR_CLASS,
  296. href: 'https://github.com/jupyterlab/jupyterlab',
  297. target: '_blank'},
  298. 'github'
  299. ),
  300. ' or through any of our ',
  301. h.a({className: ANCHOR_CLASS,
  302. href: 'http://jupyter.org/community.html',
  303. target: '_blank'},
  304. 'community resources'
  305. ),
  306. '.'
  307. )
  308. )
  309. );
  310. let domTree = [faqHeader, questionList, questionAnswerList];
  311. return domTree;
  312. }
  313. private _app: JupyterLab;
  314. }
  315. /**
  316. * Activate the faq plugin.
  317. */
  318. function activateFAQ(app: JupyterLab, palette: ICommandPalette): void {
  319. let faqModel = new FaqModel();
  320. let widget = new FaqWidget(app);
  321. let commandId = 'faq-jupyterlab:show';
  322. widget.model = faqModel;
  323. widget.id = 'faq-jupyterlab';
  324. widget.title.label = 'FAQ';
  325. widget.title.closable = true;
  326. widget.node.style.overflowY = 'auto';
  327. app.commands.addCommand(commandId, {
  328. label: 'Frequently Asked Questions',
  329. execute: () => {
  330. if (!widget.isAttached) {
  331. app.shell.addToMainArea(widget);
  332. }
  333. app.shell.activateMain(widget.id);
  334. }
  335. });
  336. palette.addItem({ command: commandId, category: 'Help' });
  337. }