瀏覽代碼

Merge pull request #2785 from blink1073/faq-page

Update FAQ page to use rendered markdown
Steven Silvester 7 年之前
父節點
當前提交
961763ef64

+ 1 - 0
jupyterlab/webpack.config.js

@@ -42,6 +42,7 @@ module.exports = {
       { test: /\.css$/, use: ['style-loader', 'css-loader'] },
       { test: /\.json$/, use: 'json-loader' },
       { test: /\.html$/, use: 'file-loader' },
+      { test: /\.md$/, use: 'raw-loader' },
       { test: /\.(jpg|png|gif)$/, use: 'file-loader' },
       { test: /\.js.map$/, use: 'file-loader' },
       { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, use: 'url-loader?limit=10000&mimetype=application/font-woff' },

+ 109 - 0
packages/faq-extension/faq.md

@@ -0,0 +1,109 @@
+## General
+* [What is JupyterLab?](#What-is-JupyterLab?)
+* [What new features does JupyterLab offer?](#What-new-features-does-JupyterLab-offer?)
+* [How stable is JupyterLab?](#How-stable-is-JupyterLab?)
+* [What will happen to the classic Jupyter Notebook?](#What-will-happen-to-the-classic-Jupyter-Notebook?)
+* [Where is the documentation for JupyterLab?](#Where-is-the-documentation-for-JupyterLab?)
+
+## Development
+
+* [How do I report a bug or provide feedback?](#How-do-I-report-a-bug-or-provide-feedback?)
+* [How can I contribute?](#How-can-I-contribute?)
+* [How can I extend or customize JupyterLab?](#How-can-I-extend-or-customize-JupyterLab?)
+
+## General
+
+### What is JupyterLab?
+
+JupyterLab is a new user interface for the Jupyter Notebook, allowing
+users to arrange multiple Jupyter notebooks, text editors, terminals,
+output areas, etc. on a single page with multiple panels and tabs in
+one integrated application. The codebase and user-interface of JupyterLab
+is based on a flexible extension system that makes it easy to extend with new
+functionality.
+
+### What new features does JupyterLab offer?
+
+JupyterLab offers a number of features beyond the classic Jupyter
+Notebook. Here are a few of them you may want to try out:
+
+* Arrange multiple notebooks, terminals, text files, etc. in the
+  application using drag and drop.
+* Run code interactively outside of a notebook in the Code Console,
+  and connect one  to a text file.
+* Right click on a markdown file and "Open with..." a live markdown
+  viewer.
+* Double click on CSV files to view them as a nicely formatted table.
+* Drag and drop notebook cells within a notebook or between notebooks.
+
+### How stable is JupyterLab?
+
+We are currently making a series of alpha releases of JupyterLab.
+These releases are mostly usable, but you may
+experience bugs and other rough edges as the code base is still
+changing rapidly.
+
+In June 2017, we will be releasing a beta version. This beta
+version will be characterized by:
+
+* Stable and featureful enough for daily usage by most Jupyter users.
+* Most of the commonly used features in the classic notebook are
+  implemented.
+* Developer APIs that are approaching stability but stil undergoing
+  significant changes.
+
+Later in 2017, we will release the 1.0 version of JupyterLab that will
+provide additional UI/UX improvements, features, and API stability. At
+that point, JupyterLab should be a full featured replacement for the
+classic notebook - and go far beyond its capabilities.
+
+### What will happen to the classic Jupyter Notebook?
+
+JupyterLab is intended to be a full replacement for the classic Jupyter
+Notebook. Because of this, our plan is to gradually retire the classic
+Jupyter Notebook. However, we will support the classic notebook for a
+signifciant period of time to help users and extension authors through
+this transition. It is important to note that the notebook server
+and the notebook document format is unchanged during this transition.
+
+### Where is the documentation for JupyterLab?
+
+The documentation for JupyterLab can be found on ReadTheDocs [here](http://jupyterlab.readthedocs.io/en/latest/).
+
+## Development
+
+### How do I report a bug or provide feedback?
+
+If you find a bug or want to provide feedback, please open an issue
+on our [GitHub Issues page](https://github.com/jupyterlab/jupyterlab/issues).
+
+### How can I contribute?
+
+We welcome other developers and designers to contribute to JupyterLab.
+Development of JupyterLab takes place on our [GitHub Repository](https://github.com/jupyterlab/jupyterlab).
+To get started with development, please have a look at our
+[Contributing Guide](https://github.com/jupyterlab/jupyterlab/blob/master/CONTRIBUTING.md)
+or chat with us on our [Gitter Channel](https://gitter.im/jupyterlab/jupyterlab).
+
+JupyterLab is a
+part of Project Jupyter and follows the [Jupyter Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md).
+
+### How can I extend or customize JupyterLab?
+
+JupyterLab consists entirely of *JupyterLab Extensions*, which are simply
+[NPM](https://www.npmjs.com/) packages that utilize the public JupyterLab
+APIs. You can develop your own custom extensions that use these APIs to
+extend the functionality of JupyterLab. Examples of possible extensions include:
+
+* Custom renderers/viewers/editors for specific file types.
+* Renderers for custom output types in the notebook.
+* Entirely new user interfaces for working with data that utilize
+  JupyterLab's layout, command palette and integrate in various ways
+  with our core extensions (notebooks, code consoles, etc.).
+
+To start developing your own JupyterLab extension, please have a look
+at:
+
+* [JupyterLab Documentation](http://jupyterlab.readthedocs.io/en/latest/)
+* JupyterLab Extension Template for [TypeScript](https://github.com/jupyterlab/extension-cookiecutter-ts)
+* JupyterLab Extension Template for [JavaScript](https://github.com/jupyterlab/extension-cookiecutter-js)

+ 4 - 2
packages/faq-extension/package.json

@@ -7,7 +7,8 @@
   "files": [
     "lib/*.d.ts",
     "lib/*.js",
-    "style/*.css"
+    "style/*.css",
+    "faq.md"
   ],
   "directories": {
     "lib": "lib/"
@@ -17,9 +18,10 @@
     "@jupyterlab/apputils": "^0.9.0",
     "@phosphor/coreutils": "^1.2.0",
     "@phosphor/messaging": "^1.2.1",
-    "@phosphor/virtualdom": "^1.1.1"
+    "@phosphor/widgets": "^1.3.0"
   },
   "devDependencies": {
+    "@types/node": "^7.0.11",
     "rimraf": "^2.5.2",
     "typescript": "~2.4.1"
   },

+ 68 - 16
packages/faq-extension/src/index.ts

@@ -14,11 +14,16 @@ import {
 } from '@phosphor/coreutils';
 
 import {
-  FaqModel, FaqWidget
-} from './widget';
+  Message
+} from '@phosphor/messaging';
+
+import {
+  PanelLayout, Widget
+} from '@phosphor/widgets';
 
 import '../style/index.css';
 
+
 /**
  * The command IDs used by the FAQ plugin.
  */
@@ -45,15 +50,59 @@ const plugin: JupyterLabPlugin<void> = {
 export default plugin;
 
 
+/**
+ * The faq page source.
+ */
+const SOURCE = require('../faq.md');
+
+
+/**
+ * A widget which is an faq viewer.
+ */
+class FAQWidget extends Widget {
+  /**
+   * Construct a new `AppWidget`.
+   */
+  constructor(content: Widget) {
+    super();
+    this.addClass('jp-FAQ');
+    this.title.closable = true;
+    this.node.tabIndex = -1;
+    this.id = 'faq';
+    this.title.label = 'FAQ';
+
+    let toolbar = new Widget();
+    toolbar.addClass('jp-FAQ-toolbar');
+
+    let layout = this.layout = new PanelLayout();
+    layout.addWidget(toolbar);
+    layout.addWidget(content);
+  }
+
+  /**
+   * Handle `close-request` events for the widget.
+   */
+  onCloseRequest(message: Message): void {
+    this.dispose();
+  }
+
+  /**
+   * Handle `activate-request` events for the widget.
+   */
+  onActivateRequest(message: Message): void {
+    this.node.focus();
+  }
+}
+
+
 /**
  * Activate the FAQ plugin.
  */
 function activate(app: JupyterLab, palette: ICommandPalette, restorer: ILayoutRestorer): void {
   const category = 'Help';
   const command = CommandIDs.open;
-  const model = new FaqModel();
-  const { commands, shell } = app;
-  const tracker = new InstanceTracker<FaqWidget>({ namespace: 'faq' });
+  const { commands, shell, rendermime } = app;
+  const tracker = new InstanceTracker<Widget>({ namespace: 'faq' });
 
   // Handle state restoration.
   restorer.restore(tracker, {
@@ -62,23 +111,26 @@ function activate(app: JupyterLab, palette: ICommandPalette, restorer: ILayoutRe
     name: () => 'faq'
   });
 
-  let widget: FaqWidget;
+  let createWidget = () => {
+    let content = rendermime.createRenderer('text/markdown');
+    const model = rendermime.createModel({
+      data: { 'text/markdown': SOURCE }
+    });
+    content.renderModel(model);
+    content.addClass('jp-FAQ-content');
+    return new FAQWidget(content);
+  };
 
-  function newWidget(): FaqWidget {
-    let widget = new FaqWidget({ linker: app.commandLinker });
-    widget.model = model;
-    widget.id = 'faq';
-    widget.title.label = 'FAQ';
-    widget.title.closable = true;
-    tracker.add(widget);
-    return widget;
-  }
+  let widget: FAQWidget;
 
   commands.addCommand(command, {
     label: 'Open FAQ',
     execute: () => {
       if (!widget || widget.isDisposed) {
-        widget = newWidget();
+        widget = createWidget();
+      }
+      if (!tracker.has(widget)) {
+        tracker.add(widget);
         shell.addToMainArea(widget);
       }
       shell.activateById(widget.id);

+ 0 - 409
packages/faq-extension/src/widget.ts

@@ -1,409 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-
-import {
-  CommandLinker, VDomModel, VDomRenderer
-} from '@jupyterlab/apputils';
-
-import {
-  Message
-} from '@phosphor/messaging';
-
-import {
-  h, VirtualNode
-} from '@phosphor/virtualdom';
-
-
-/**
- * The class name added to the FAQ plugin.
- */
-const FAQ_CLASS = 'jp-FAQ';
-
-/**
- * The id name added to the header section element.
- */
-const HEADER_ID = 'faq-header';
-
-/**
- * The class name added to the title.
- */
-const TITLE_CLASS = 'jp-FAQ-title';
-
-/**
- * The class name added to h1 elements.
- */
-const HEADER_CLASS = 'jp-FAQ-h1';
-
-/**
- * The class name added to h2 elements.
- */
-const SUBHEADER_CLASS = 'jp-FAQ-h2';
-
-/**
- * The class name added for the question mark icon from default-theme.
- */
-const QUESTIONMARK_ICON_CLASS = 'jp-QuestionMarkIcon';
-
-/**
- * The class named added the question mark icon.
- */
-const QUESTIONMARK_CLASS = 'jp-FAQ-QuestionMark';
-
-/**
- * The class name added to faq content.
- */
-const CONTENT_CLASS = 'jp-FAQ-content';
-
-/**
- * The class name added to unordered list elements.
- */
-const FAQ_LIST_CLASS = 'jp-FAQ-ul';
-
-/**
- * The class name added to table of contents elements.
- */
-const TOC_CLASS = 'jp-FAQ-toc';
-
-/**
- * The class name added to questions.
- */
-const QUESTION_CLASS = 'jp-FAQ-question';
-
-/**
- * The class name added to answers.
- */
-const ANSWER_CLASS = 'jp-FAQ-answer';
-
-/**
- * The class name added to anchor elements.
- */
-const ANCHOR_CLASS = 'jp-FAQ-a';
-
-/**
- * Title of the FAQ plugin.
- */
-const TITLE = 'Frequently Asked Questions';
-
-/**
- * Contain subheadings for each section.
- */
-const SUBHEADINGS = ['THE BASICS', 'FEATURES', 'DEVELOPER'];
-
-/**
- * Contain questions for `the basics` section.
- */
-const BASIC_QUESTIONS = [
-  'What is JupyterLab?',
-  'What is a Jupyter Notebook?',
-  'How stable is JupyterLab?',
-  `I'm confused with the interface. How do I navigate around JupyterLab?`
-];
-
-/**
- * Contain questions for the `features` section.
- */
-const FEATURES_QUESTIONS = [
-  'How do I add more kernels/languages to JupyterLab?',
-  'How can I share my notebooks?'
-];
-
-/**
- * Contain questions for the `developer` section.
- */
-const DEVELOPER_QUESTIONS = [
-  'How do I report a bug?',
-  'I have security concerns about JupyterLab.',
-  'How can I contribute?'
-];
-
-
-/**
- * FaqModel holds data which the FaqWidget will render.
- */
-export
-class FaqModel extends VDomModel {
-  /**
-   * Title of the FAQ plugin.
-   */
-  readonly title = TITLE;
-
-  /**
-   * Contain subheadings for each section.
-   */
-  readonly subheadings = SUBHEADINGS;
-
-  /**
-   * Contain questions for `the basics` section.
-   */
-  readonly basicsQuestions = BASIC_QUESTIONS;
-
-  /**
-   * Contain questions for the `features` section.
-   */
-  readonly featuresQuestions = FEATURES_QUESTIONS;
-
-  /**
-   * Contain questions for the `developer` section.
-   */
-  readonly developerQuestions = DEVELOPER_QUESTIONS;
-}
-
-/**
- * A virtual-DOM-based widget for the FAQ plugin.
- */
-export
-class FaqWidget extends VDomRenderer<FaqModel> {
-  /**
-   * Construct a new faq widget.
-   */
-  constructor(options: FaqWidget.IOptions) {
-    super();
-    this._linker = options.linker;
-    this.addClass(FAQ_CLASS);
-  }
-
-  /**
-   * Render the faq plugin to virtual DOM nodes.
-   */
-  protected render(): VirtualNode[] {
-    let model = this.model;
-    if (!model) {
-      return [];
-    }
-    let linker = this._linker;
-    let subheadings = model.subheadings;
-    let basicsQuestions = model.basicsQuestions;
-    let featuresQuestions = model.featuresQuestions;
-    let developerQuestions = model.developerQuestions;
-
-    // Create Frequently Asked Questions Header Section.
-    let faqHeader =
-    h.section({ id: HEADER_ID },
-      h.span({
-        className: QUESTIONMARK_ICON_CLASS + ' ' + QUESTIONMARK_CLASS
-      }),
-      h.h1({ className: HEADER_CLASS },
-        h.span({ className: TITLE_CLASS }, model.title)
-      )
-    );
-
-    // Create a section element that holds Table of Contents.
-    let questionList =
-    h.section({ className: CONTENT_CLASS },
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[0]),
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#basicsQ1' }, basicsQuestions[0])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#basicsQ2' }, basicsQuestions[1])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#basicsQ3' }, basicsQuestions[2])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#basicsQ4' }, basicsQuestions[3])
-        )
-      ),
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[1]),
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#featuresQ1' }, featuresQuestions[0])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#featuresQ2' }, featuresQuestions[1])
-        )
-      ),
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[2]),
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#developerQ1' }, developerQuestions[0])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#developerQ2' }, developerQuestions[1])
-        ),
-        h.li({ className: QUESTION_CLASS + ' ' + TOC_CLASS },
-          h.a({ href: '#developerQ3' }, developerQuestions[2])
-        )
-      )
-    );
-
-    // Create a section element that all other FAQ Content will go under.
-    let questionAnswerList =
-    h.section({ className: CONTENT_CLASS },
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[0]),
-      // Create list of questions/answers under the Basics section.
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({ className: QUESTION_CLASS, id: 'basicsQ1' }, basicsQuestions[0]),
-        h.li({ className: ANSWER_CLASS },
-          `JupyterLab allows users to arrange multiple Jupyter notebooks,
-          text editors, terminals, output areas, etc. on a single page with
-          multiple panels and tabs into one application. The codebase and UI of
-          JupyterLab is based on a flexible plugin system that makes it easy to
-          extend with new components.`
-        ),
-        h.li({ className: QUESTION_CLASS, id: 'basicsQ2' }, basicsQuestions[1]),
-        h.li({ className: ANSWER_CLASS },
-          `Central to the project is the Jupyter Notebook, a web-based
-          platform that allows users to combine live code, equations, narrative
-          text, visualizations, interactive dashboards and other media. Together
-          these building blocks make science and data reproducible across over
-          40 programming languages and combine to form what we call a
-          computational narrative.`
-        ),
-        h.li({ className: QUESTION_CLASS, id: 'basicsQ3' }, basicsQuestions[2]),
-        h.li({ className: ANSWER_CLASS },
-          `JupyterLab is currently in an alpha release and not ready for public
-          use as new features and bug fixes are being added very frequently. We
-          strongly recommend to back up your work before using JupyterLab.
-          However, testing, development, and user feedback are greatly
-          appreciated.`
-        ),
-        h.li({ className: QUESTION_CLASS, id: 'basicsQ4' }, basicsQuestions[3]),
-        h.li({ className: ANSWER_CLASS },
-          'Check out the JupyterLab tour ',
-          h.a({
-            className: ANCHOR_CLASS,
-            dataset: linker.populateVNodeDataset('about-jupyterlab:open')
-          }, 'here')
-        )
-      ),
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[1]),
-      // Create list of questions/answers under the Features section.
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({
-          className: QUESTION_CLASS,
-          id: 'featuresQ1'
-        }, featuresQuestions[0]),
-        h.li({ className: ANSWER_CLASS },
-          `To add more languages to the JupyterLab you must install a new
-          kernel. Installing a kernel is usually fairly simple and can be done
-          with a couple terminal commands. However the instructions for
-          installing kernels is different for each language. For further
-          instructions, click`,
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://jupyter.readthedocs.io/en/latest/install-kernel.html',
-            target: '_blank'
-          }, 'this'),
-          ' link.'
-        ),
-        h.li({
-          className: QUESTION_CLASS,
-          id: 'featuresQ2'
-        }, featuresQuestions[1]),
-        h.li({ className: ANSWER_CLASS },
-          `You can either publish your notebooks on GitHub or use a free service
-          such as `,
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://nbviewer.jupyter.org/',
-            target: '_blank'
-          }, 'nbviewer.org'),
-          ' to render your notebooks online.'
-        )
-      ),
-      h.h2({ className: SUBHEADER_CLASS }, subheadings[2]),
-      // Create list of questions/answers under the Developer section.
-      h.ul({ className: FAQ_LIST_CLASS },
-        h.li({
-          className: QUESTION_CLASS,
-          id: 'developerQ1'
-        }, developerQuestions[0]),
-        h.li({ className: ANSWER_CLASS },
-          'You can open an issue on our ',
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://github.com/jupyterlab/jupyterlab/issues',
-            target: '_blank'
-          }, 'GitHub repository'),
-          '. Please check already opened issues before posting.'
-        ),
-        h.li({
-          className: QUESTION_CLASS,
-          id: 'developerQ2'
-        }, developerQuestions[1]),
-        h.li({ className: ANSWER_CLASS },
-          `If you have any inquiries, concerns, or thought you found a security
-          vulnerability, please write to use at `,
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'mailto:security@jupyter.org'
-          }, 'security@jupyter.org'),
-          '. We will do our best to repond to you promptly.'
-        ),
-        h.li({
-          className: QUESTION_CLASS,
-          id: 'developerQ3'
-        }, developerQuestions[2]),
-        h.li({ className: ANSWER_CLASS },
-          `There are many ways to contribute to JupyterLab. Whether you are an
-          experienced Python programmer or a newcomer, any interested developers
-          are welcome. You can learn about the JupyterLab codebase by going
-          through our`,
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://jupyterlab-tutorial.readthedocs.io/en/latest/index.html',
-            target: '_blank'
-          }, 'tutorial walkthrough' ),
-          ' and ',
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://jupyterlab.github.io/jupyterlab/',
-            target: '_blank'
-          }, 'documentation'),
-          '. Also, feel free to ask questions on our ',
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'https://github.com/jupyterlab/jupyterlab',
-            target: '_blank'
-          }, 'github'),
-          ' or through any of our ',
-          h.a({
-            className: ANCHOR_CLASS,
-            href: 'http://jupyter.org/community.html',
-            target: '_blank'
-          }, 'community resources'),
-          '.'
-        )
-      )
-    );
-    return [faqHeader, questionList, questionAnswerList];
-  }
-
-  /**
-   * Handle `'activate-request'` messages.
-   */
-  protected onActivateRequest(msg: Message): void {
-    this.node.tabIndex = -1;
-    this.node.focus();
-  }
-
-  /**
-   * Handle `'close-request'` messages.
-   */
-  protected onCloseRequest(msg: Message): void {
-    super.onCloseRequest(msg);
-    this.dispose();
-  }
-
-  private _linker: CommandLinker;
-}
-
-
-/**
- * A namespace for `FaqWidget` statics.
- */
-export
-namespace FaqWidget {
-  /**
-   * Instantiation options for the FAQ widget.
-   */
-  export
-  interface IOptions {
-    /**
-     * A command linker instance.
-     */
-    linker: CommandLinker;
-  }
-}

+ 6 - 117
packages/faq-extension/style/index.css

@@ -7,128 +7,17 @@
 .jp-FAQ {
   font-family: var(--jp-ui-font-family);
   outline: none;
-  overflow-y: auto;
-}
-
-#faq-header {
-  width: 412px;
   display: flex;
-  margin: 40px auto 40px auto;
-  text-align: center;
-}
-
-
-.jp-FAQ-content {
-  margin: 0 auto 70px auto;
-  display: block;
-  width: 94%;
-  max-width: 700px;
-}
-
-
-.jp-FAQ-h1 {
-  font-size: 26px;
-  font-weight: 400;
-  color: #313940;
-  margin: 10px 0 0 20px;
-}
-
-
-.jp-FAQ-h2 {
-  font-size: 14px;
-  font-weight: 400;
-  color: var(--md-grey-500);
-  margin: 28px 0 0 0;
-  margin-bottom: 8px;
-}
-
-
-.jp-FAQ-ul {
-  margin: 0 0 0 0;
-  padding: 0;
-}
-
-
-.jp-FAQ-question:target {
-  animation: highlight 0.9s cubic-bezier(0.15, 0.08, 0.54, 0.85);
-}
-
-
-@keyframes highlight {
-    0%   {background-color: transparent;}
-    50%  {background-color: var(--md-blue-100);}
-    100% {background-color: transparent;}
-}
-
-
-.jp-FAQ-question {
-  color: var(--jp-ui-font-color0);
-  list-style-type: none;
-  padding: 4px 16px;
-  margin-left: -16px;
-  display: inline-block;
-}
-
-
-.jp-FAQ-question > a {
-  font-size: 20px;
-  color: var(--jp-ui-font-color0);
-  font-weight: 400;
-  line-height: 150%;
-  list-style-type: none;
-  margin: 28px 0 0 0;
-  cursor: pointer;
-  text-decoration: none;
-}
-
-
-.jp-FAQ-question > a:hover {
-  color: var(--md-blue-500);
-}
-
-
-.jp-FAQ-answer{
-  font-size: 15px;
-  font-weight: 200;
-  color: var(--md-grey-500);
-  line-height: 160%;
-  border-left: 2px solid var(--md-grey-500);
-  padding-left: 25px;
-  list-style-type: none;
-  margin: 15px 0 15px 20px;
-}
-
-
-.jp-FAQ-toc {
-  display: block;
-}
-
-
-.jp-FAQ-toc:focus{
-  color: var(--md-blue-500);
-}
-
-
-li > jp-FAQ-answer {
-  color: var(--md-grey-500);
-  text-decoration: none;
-}
-
-
-.jp-FAQ-title {
-  color: var(--jp-ui-font-color0);
-  display: block;
+  flex-direction: column;
 }
 
 
-.jp-FAQ-a {
-    text-decoration: none;
-    cursor: pointer;
-    color: var(--jp-brand-color1);
+.jp-FAQ-toolbar {
+  min-height: var(--jp-toolbar-micro-height);
 }
 
 
-.jp-QuestionMarkIcon.jp-FAQ-QuestionMark {
-  height: 49px;
-  width: 49px;
+.jp-FAQ-content {
+  overflow-y: auto;
+  padding: 12px;
 }

+ 1 - 1
packages/faq-extension/tsconfig.json

@@ -9,7 +9,7 @@
     "target": "ES5",
     "outDir": "./lib",
     "lib": ["ES5", "ES2015.Promise", "DOM", "ES2015.Collection"],
-    "types": []
+    "types": ["node"]
   },
   "include": ["src/*"]
 }

+ 24 - 2
packages/rendermime/src/renderers.ts

@@ -75,6 +75,9 @@ function renderHTML(options: renderHTML.IOptions): Promise<void> {
   //   Private.evalInnerHTMLScriptTags(host);
   // }
 
+  // Handle default behavior of nodes.
+  Private.handleDefaults(host);
+
   // Patch the urls if a resolver is available.
   let promise: Promise<void>;
   if (resolver) {
@@ -324,6 +327,9 @@ function renderMarkdown(options: renderMarkdown.IRenderOptions): Promise<void> {
     //   Private.evalInnerHTMLScriptTags(host);
     // }
 
+    // Handle default behavior of nodes.
+    Private.handleDefaults(host);
+
     // Apply ids to the header nodes.
     Private.headerAnchors(host);
 
@@ -661,6 +667,23 @@ namespace Private {
     });
   }
 
+  /**
+   * Handle the default behavior of nodes.
+   */
+  export
+  function handleDefaults(node: HTMLElement): void {
+    // Handle anchor elements.
+    let anchors = node.getElementsByTagName('a');
+    for (let i = 0; i < anchors.length; i++) {
+      let path = anchors[i].href;
+      if (URLExt.isLocal(path)) {
+        anchors[i].target = '_self';
+      } else {
+        anchors[i].target = '_blank';
+      }
+    }
+  }
+
   /**
    * Resolve the relative urls in element `src` and `href` attributes.
    *
@@ -683,7 +706,7 @@ namespace Private {
       promises.push(handleAttr(nodes[i] as HTMLElement, 'src', resolver));
     }
 
-    // Handle achor elements.
+    // Handle anchor elements.
     let anchors = node.getElementsByTagName('a');
     for (let i = 0; i < anchors.length; i++) {
       promises.push(handleAnchor(anchors[i], resolver, linkHandler));
@@ -740,7 +763,6 @@ namespace Private {
    * Handle an anchor node.
    */
   function handleAnchor(anchor: HTMLAnchorElement, resolver: IRenderMime.IResolver, linkHandler: IRenderMime.ILinkHandler | null): Promise<void> {
-    anchor.target = '_blank';
     // Get the link path without the location prepended.
     // (e.g. "./foo.md#Header 1" vs "http://localhost:8888/foo.md#Header 1")
     let href = anchor.getAttribute('href');

+ 15 - 0
packages/rendermime/src/rendermime.ts

@@ -22,6 +22,10 @@ import {
   ReadonlyJSONObject
 } from '@phosphor/coreutils';
 
+import {
+  MimeModel
+} from './mimemodel';
+
 
 /**
  * An object which manages mime renderer factories.
@@ -131,6 +135,17 @@ class RenderMime {
     });
   }
 
+  /**
+   * Create a new mime model.  This is a convenience method.
+   *
+   * @options - The options used to create the model.
+   *
+   * @returns A new mime model.
+   */
+  createModel(options: MimeModel.IOptions = {}): MimeModel {
+    return new MimeModel(options);
+  }
+
   /**
    * Create a clone of this rendermime instance.
    *