Mailspring/_data/sidebar.json

483 lines
90 KiB
JSON
Raw Normal View History

2015-10-03 01:57:40 +08:00
{
"sections": [
{
"name": "Getting Started",
"items": [
{
"html": "<style>\nh3 {\n line-height: 40px;\n color:#00B883;\n}\nh3.padded {\n padding-top: 80px;\n padding-bottom: 25px;\n}\nh3 .number {\n width:40px;\n height:40px;\n margin-right:20px;\n border-radius:50%;\n border:1px solid #00B883;\n float:left;\n color:#00B883;\n text-align: center;\n}\nh3.first {\n padding-top:40px;\n}\nh3.second {\n color: #00A180;\n}\nh3.second .number {\n border:1px solid #00A180;\n color: #00A180;\n}\nh3.third {\n color: #009899;\n}\nh3.third .number {\n border:1px solid #009899;\n color: #009899;\n}\nh4 {\n line-height: 26px;\n font-size: 16px;\n color:#89C9C8;\n margin-top: 30px;\n margin-bottom: 45px;\n}\nh4 .letter {\n width:22px;\n height:22px;\n margin-right:15px;\n font-size: 14px;\n background-color: #89C9C8;\n float:left;\n color:white;\n text-align: center;\n}\np {\n font-size:1.1em;\n line-height: 1.6em;\n color:rgba(0,0,0,0.5);\n}\nblockquote p {\n font-size: 15px;\n}\n</style>\n\n\n<div class=\"row\">\n <div class=\"col-md-12\">\n <h2>Start building on top of Nylas in minutes:</h2>\n <h3 class=\"first padded\"><div class=\"number\">1</div> Install N1</h3>\n <p>Download and install Nylas for <span id=\"platforms\"></span>. Open it and sign in to your email account.</p>\n </div>\n</div>\n\n<div class=\"row\">\n <div class=\"col-md-8\">\n <h3 class=\"second padded\"><div class=\"number\">2</div> Start a Package</h3>\n <p>Packages lie at the heart of N1. The thread list, composer and other core parts of the app are packages bundled with the app, and you have access to the same set of APIs. From the Developer Menu, choose <span class=\"instruction-literal\">Create a Package...</span> and name your new package.</p>\n </div>\n <div class=\"col-md-4\">\n <img src=\"./images/Step2-Menu@2x.png\" width=\"203\" height=\"194\" style=\"margin-top:88px;\"/>\n </div>\n</div>\n\n<div class=\"row\">\n <div class=\"col-md-12\">\n <h3 class=\"third padded\"><div class=\"number\">3</div> See it in Action</h3>\n <p>Your new package comes with some basic code that adds a section to the message sidebar, and it&#39;s already enabled! View a message to see it in action. If you make changes to the source, choose <span class=\"instruction-literal\">View &gt; Refresh</span> to see your changes in N1.</p>\n </div>\n</div>\n\n<hr/>\n\n<div class=\"row\">\n <div class=\"col-md-12\">\n <h2 class=\"continue\"><a href=\"getting-started-2\">Step 2: Build your first package</a></h2>\n </div>\n</div>\n\n<hr />\n\n<div class=\"row\">\n <div class=\"col-md-6\" style=\"padding-right:30px;\">\n <h4><div class=\"letter\">A</div> Explore the source</h4>\n <img src=\"./images/Illu-ExploreTheSource@2x.png\" width=\"121\" height=\"96\" style=\"margin:auto; margin-bottom:35px; display:block;\"/>\n <p>Nylas is built on the modern web - packages are written in CoffeeScript or JavaScript. Packages are a lot like node modules, with their own source, assets, and tests. Check out yours in <span class=\"instruction-literal\">~/.nylas/dev/packages</span>.</p>\n </div>\n <div class=\"col-md-6\" style=\"padding-left:30px;\">\n <h4><div class=\"letter\">B</div> Run the specs</h4>\n <img src=\"./images/illu-RunTheSpecs@2x.png\" width=\"139\" height=\"96\" style=\"margin:auto; margin-bottom:35px; display:block;\"/>\n <p>In N1, select <span class=\"instruction-literal\">Developer &gt; Run Package Specs...</span> from the menu to run your package&#39;s new specs. Nylas and its packages use the Jasmine testing framework.</p>\n </div>\n</div>\n\n<h2 class=\"gsg-header\">Step 2: Building your first package</h2>\n\n<p>If you followed the <a href=\"getting-started\">first part</a> of our Getting Started Guide, you should have a brand new package just waiting to be explored.</p>\n<p>This sample package simply adds the name of the currently focused contact to the sidebar:</p>\n<p><img class=\"gsg-center\" src=\"images/sidebar-example.png\"/></p>\n<p>We&#39;re going to build on this to show the sender&#39;s <a href=\"http://gravatar.com\">Gravat
"meta": {
"Title": "First Steps",
"TitleHidden": true,
"Section": "Getting Started",
"Order": 2,
"title": "First Steps",
"titlehidden": true,
"section": "Getting Started",
"order": 2
},
"name": "First Steps",
"filename": "index.md",
"link": "index.html"
}
]
},
{
"name": "Guides",
"items": [
{
2015-10-03 07:10:51 +08:00
"html": "<p>Packages lie at the heart of N1. Each part of the core experience is a separate package that uses the Nylas Package API to add functionality to the client. Want to make a read-only mail client? Remove the core <code>Composer</code> package and you&#39;ll see reply buttons and composer functionality disappear.</p>\n<p>Let&#39;s explore the files in a simple package that adds a Translate option to the Composer. When you tap the Translate button, we&#39;ll display a popup menu with a list of languages. When you pick a language, we&#39;ll make a web request and convert your reply into the desired language.</p>\n<h3 id=\"package-structure\">Package Structure</h3>\n<p>Each package is defined by a <code>package.json</code> file that includes its name, version and dependencies. Packages may also declare dependencies which are loaded from npm - in this case, the <a href=\"https://github.com/request/request\">request</a> library. You&#39;ll need to <code>npm install</code> these dependencies locally when developing the package.</p>\n<pre><code>{\n \"<span class=\"hljs-attribute\">name</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"translate\"</span></span>,\n \"<span class=\"hljs-attribute\">version</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"0.1.0\"</span></span>,\n \"<span class=\"hljs-attribute\">main</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"./lib/main\"</span></span>,\n \"<span class=\"hljs-attribute\">description</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"An example package for N1\"</span></span>,\n \"<span class=\"hljs-attribute\">license</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"Proprietary\"</span></span>,\n \"<span class=\"hljs-attribute\">engines</span>\": <span class=\"hljs-value\">{\n \"<span class=\"hljs-attribute\">atom</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"*\"</span>\n </span>}</span>,\n \"<span class=\"hljs-attribute\">dependencies</span>\": <span class=\"hljs-value\">{\n \"<span class=\"hljs-attribute\">request</span>\": <span class=\"hljs-value\"><span class=\"hljs-string\">\"^2.53\"</span>\n </span>}\n</span>}\n</code></pre><p>Our package also contains source files, a spec file with complete tests for the behavior the package adds, and a stylesheet for CSS:</p>\n<pre><code>-<span class=\"ruby\"> package.json\n</span>-<span class=\"ruby\"> lib/\n</span> -<span class=\"ruby\"> main.coffee\n</span> -<span class=\"ruby\"> translate-button.cjsx\n</span>-<span class=\"ruby\"> spec/\n</span> -<span class=\"ruby\"> main-spec.coffee\n</span>-<span class=\"ruby\"> stylesheets/\n</span> -<span class=\"ruby\"> translate.less</span>\n</code></pre><p><code>package.json</code> lists <code>lib/main</code> as the root file of our package. Since N1 runs NodeJS, we can <code>require</code> other source files, Node packages, etc.</p>\n<p>N1 can read <code>js</code>, <code>coffee</code>, <code>jsx</code>, and <code>cjsx</code> files automatically.</p>\n<p>Inside <code>main.coffee</code>, there are two important functions being exported:</p>\n<pre><code class=\"lang-coffee\"><span class=\"hljs-built_in\">require</span> <span class=\"hljs-string\">'./translate-button'</span>\n\n<span class=\"hljs-built_in\">module</span>.exports =\n\n <span class=\"hljs-comment\"># Activate is called when the package is loaded. If your package previously</span>\n <span class=\"hljs-comment\"># saved state using `serialize` it is provided.</span>\n <span class=\"hljs-comment\">#</span>\n <span class=\"hljs-attribute\">activate</span>: <span class=\"hljs-function\"><span class=\"hljs-params\">(<span class=\"hljs-property\">@state</span>)</span> -&gt;</span>\n ComponentRegistry.register TranslateButton,\n <span class=\"hljs-attribute\">role</span>: <span class=\"hljs-string\">'Composer:ActionButton'</span>\n\n <span class=\"hljs-comment\"># Serialize is called when your package is about to be unmounted.</span>\n <span class=\"hljs-comment\"># You can
2015-10-03 01:57:40 +08:00
"meta": {
2015-10-03 07:10:51 +08:00
"Title": "Building a Package",
2015-10-03 01:57:40 +08:00
"Section": "Guides",
"Order": 1,
2015-10-03 07:10:51 +08:00
"title": "Building a Package",
2015-10-03 01:57:40 +08:00
"section": "Guides",
"order": 1
},
2015-10-03 07:10:51 +08:00
"name": "Building a Package",
"filename": "PackageOverview.md",
"link": "PackageOverview.html"
2015-10-03 01:57:40 +08:00
},
{
2015-10-03 07:10:51 +08:00
"html": "<p>The N1 user interface is conceptually organized into Sheets. Each Sheet represents a window of content. For example, the <code>Threads</code> sheet lies at the heart of the application. When the user chooses the &quot;Files&quot; tab, a separate <code>Files</code> sheet is displayed in place of <code>Threads</code>. When the user clicks a thread in single-pane mode, a <code>Thread</code> sheet is pushed on to the workspace and appears after a brief transition.</p>\n<p><img src=\"./images/sheets.png\"></p>\n<p>The {WorkspaceStore} maintains the state of the application&#39;s workspace and the stack of sheets currently being displayed. Your packages can declare &quot;root&quot; sheets which are listed in the app&#39;s main sidebar, or push custom sheets on top of sheets to display data.</p>\n<p>The Nylas Workspace supports two display modes: <code>split</code> and <code>list</code>. Each Sheet describes it&#39;s appearance in each of the view modes it supports. For example, the <code>Threads</code> sheet describes a three column <code>split</code> view and a single column <code>list</code> view. Other sheets, like <code>Files</code> register for only one mode, and the user&#39;s mode preference is ignored.</p>\n<p>For each mode, Sheets register a set of column names.</p>\n<p><img src=\"./images/columns.png\"></p>\n<pre><code class=\"lang-coffee\"><span class=\"hljs-variable\">@defineSheet</span> <span class=\"hljs-string\">'Threads'</span>, {<span class=\"hljs-attribute\">root</span>: true},\n <span class=\"hljs-attribute\">split</span>: [<span class=\"hljs-string\">'RootSidebar'</span>, <span class=\"hljs-string\">'ThreadList'</span>, <span class=\"hljs-string\">'MessageList'</span>, <span class=\"hljs-string\">'MessageListSidebar'</span>]\n <span class=\"hljs-attribute\">list</span>: [<span class=\"hljs-string\">'RootSidebar'</span>, <span class=\"hljs-string\">'ThreadList'</span>]\n</code></pre>\n<p>Column names are important. Once you&#39;ve registered a sheet, your package (and other packages) register React components that appear in each column.</p>\n<p>Sheets also have a <code>Header</code> and <code>Footer</code> region that spans all of their content columns. You can register components to appear in these regions to display notifications, add bars beneath the toolbar, etc.</p>\n<pre><code class=\"lang-coffee\">ComponentRegistry<span class=\"hljs-class\">.register</span> AccountSidebar,\n location: WorkspaceStore<span class=\"hljs-class\">.Location</span><span class=\"hljs-class\">.RootSidebar</span>\n\n\nComponentRegistry<span class=\"hljs-class\">.register</span> NotificationsStickyBar,\n location: WorkspaceStore<span class=\"hljs-class\">.Sheet</span><span class=\"hljs-class\">.Threads</span><span class=\"hljs-class\">.Header</span>\n</code></pre>\n<p>Each column is laid out as a CSS Flexbox, making them extremely flexible. For more about layout using Flexbox, see Working with Flexbox.</p>\n<p>###Toolbars</p>\n<p>Toolbars in N1 are also powered by the {ComponentRegistry}. Though toolbars appear to be a single unit at the top of a sheet, they are divided into columns with the same widths as the columns in the sheet beneath them.</p>\n<p><img src=\"./images/toolbar.png\"></p>\n<p>Each Toolbar column is laid out using {Flexbox}. You can control where toolbar elements appear within the column using the CSS <code>order</code> attribute. To make it easy to position toolbar items on the left, right, or center of a column, we&#39;ve added two &quot;spacer&quot; elements with <code>order:50</code> and <code>order:-50</code> that evenly use up available space. Other CSS attributes allow you to control whether your items shrink or expand as the column&#39;s size changes.</p>\n<p><img src=\"./images/toolbar-column.png\"></p>\n<p>To add items to a toolbar, you inject them via the {ComponentRegistry}. There are several ways of describing the location of a toolbar component which are useful in different scenarios:</p>\n<ul>\n<li><p><code>&lt;Location&gt;.Toolbar</code>: This component will always a
2015-10-03 01:57:40 +08:00
"meta": {
2015-10-03 07:10:51 +08:00
"Title": "Interface Concepts",
2015-10-03 01:57:40 +08:00
"Section": "Guides",
"Order": 1,
2015-10-03 07:10:51 +08:00
"title": "Interface Concepts",
2015-10-03 01:57:40 +08:00
"section": "Guides",
"order": 1
},
2015-10-03 07:10:51 +08:00
"name": "Interface Concepts",
"filename": "InterfaceConcepts.md",
"link": "InterfaceConcepts.html"
2015-10-03 01:57:40 +08:00
},
{
2015-10-03 07:10:51 +08:00
"html": "<p>N1 uses <a href=\"https://facebook.github.io/react/\">React</a> to create a fast, responsive UI. Packages that want to extend the N1 interface should use React. Using React&#39;s <a href=\"https://facebook.github.io/react/jsx-in-depth.html\">JSX</a> syntax is optional, but both <a href=\"https://facebook.github.io/react/jsx-in-depth.html\">JSX</a> and <a href=\"https://github.com/jsdf/coffee-react\">CJSX</a> (CoffeeScript) are available.</p>\n<p>For a quick introduction to React, take a look at Facebook&#39;s <a href=\"https://facebook.github.io/react/getting-started.html\">Getting Started with React</a>.</p>\n<h4 id=\"react-components\">React Components</h4>\n<p>N1 provides a set of core React components you can use in your packages. Many of the standard components listen for key events, include considerations for different platforms, and have extensive CSS. Wrapping standard components makes it easy to build rich interfaces that are consistent with the rest of the N1 platform.</p>\n<p>To use a standard component, require it from <code>nylas-component-kit</code> and use it in your component&#39;s <code>render</code> method.</p>\n<p class=\"well\">Keep in mind that React&#39;s Component model is based on composition rather than inheritance. On other platforms, you might subclass {Popover} to create your own custom Popover. In React, you should wrap the standard Popover component in your own component, which provides the Popover with <code>props</code> and children to customize its behavior.</p>\n\n\n<p>Here&#39;s a quick look at standard components you can require from <code>nylas-component-kit</code>:</p>\n<ul>\n<li><p><strong>{Menu}</strong>: Allows you to display a list of items consistent with the rest of the N1 user experience.</p>\n</li>\n<li><p><strong>{Spinner}</strong>: Displays an indeterminate progress indicator centered within it&#39;s container.</p>\n</li>\n<li><p><strong>{Popover}</strong>: Component for creating menus and popovers that appear in response to a click and stay open until the user clicks outside them.</p>\n</li>\n<li><p><strong>{Flexbox}</strong>: Component for creating a Flexbox layout.</p>\n</li>\n<li><p><strong>{RetinaImg}</strong>: Replacement for standard <code>&lt;img&gt;</code> tags which automatically resolves the best version of the image for the user&#39;s display and can apply many image transforms.</p>\n</li>\n<li><p><strong>{ListTabular}</strong>: Component for creating a list of items backed by a paginating ModelView.</p>\n</li>\n<li><p><strong>{MultiselectList}</strong>: Component for creating a list that supports multi-selection. (Internally wraps ListTabular)</p>\n</li>\n<li><p><strong>{MultiselectActionBar}</strong>: Component for creating a contextual toolbar that is activated when the user makes a selection on a ModelView.</p>\n</li>\n<li><p><strong>{ResizableRegion}</strong>: Component that renders it&#39;s children inside a resizable region with a draggable handle.</p>\n</li>\n<li><p><strong>{TokenizingTextField}</strong>: Wraps a standard <code>&lt;input&gt;</code> and takes function props for tokenizing input values and displaying autocompletion suggestions.</p>\n</li>\n<li><p><strong>{EventedIFrame}</strong>: Replacement for the standard <code>&lt;iframe&gt;</code> tag which handles events directed at the iFrame to ensure a consistent user experience.</p>\n</li>\n</ul>\n<h2 id=\"react-component-injection\">React Component Injection</h2>\n<p>The N1 interface is composed at runtime from components added by different packages. The app&#39;s left sidebar contains components from the composer package, the source list package, the activity package, and more. You can leverage the flexiblity of this system to extend almost any part of N1&#39;s interface.</p>\n<h3 id=\"registering-components\">Registering Components</h3>\n<p>After you&#39;ve created React components in your package, you should register them with the {ComponentRegistry}. The Component Registry manages the dynamic injection of components that makes N1 so extensible. You can request that you
2015-10-03 01:57:40 +08:00
"meta": {
"Title": "Interface Components",
"Section": "Guides",
"Order": 2,
"title": "Interface Components",
"section": "Guides",
"order": 2
},
"name": "Interface Components",
"filename": "React.md",
"link": "React.html"
},
{
"html": "<p>N1 uses <a href=\"https://github.com/spoike/refluxjs\">Reflux</a>, a slim implementation of Facebook&#39;s <a href=\"https://facebook.github.io/flux/\">Flux Application Architecture</a> to coordinate the movement of data through the application. Flux is extremely well suited for applications that support third-party extension because it emphasizes loose coupling and well defined interfaces between components. It enforces:</p>\n<ul>\n<li><strong>Uni-directional data flow</strong></li>\n<li><strong>Loose coupling between components</strong></li>\n</ul>\n<p>For more information about the Flux pattern, check out <a href=\"https://facebook.github.io/flux/docs/overview.html#structure-and-data-flow\">this diagram</a>. For a bit more insight into why we chose Reflux over other Flux implementations, there&#39;s a great <a href=\"http://spoike.ghost.io/deconstructing-reactjss-flux/\">blog post</a> by the author of Reflux.</p>\n<p>There are several core stores in the application:</p>\n<ul>\n<li><p><strong>{AccountStore}</strong>: When the user signs in to N1, their auth token provides one or more accounts. The AccountStore manages the available Accounts, exposes the current Account, and allows you to observe changes to the current Account.</p>\n</li>\n<li><p><strong>{TaskQueue}</strong>: Manages Tasks, operations queued for processing on the backend. Task objects represent individual API actions and are persisted to disk, ensuring that they are performed eventually. Each Task may depend on other tasks, and Tasks are executed in order.</p>\n</li>\n<li><p><strong>{DatabaseStore}</strong>: The {DatabaseStore} marshalls data in and out of the local cache, and exposes an ActiveRecord-style query interface. You can observe the DatabaseStore to monitor the state of data in N1.</p>\n</li>\n<li><p><strong>{DraftStore}</strong>: Manages Drafts, which are {Message} objects the user is authoring. Drafts present a unique case in N1 because they may be updated frequently by disconnected parts of the application. You should use the {DraftStore} to create, edit, and send drafts.</p>\n</li>\n<li><p><strong>{FocusedContentStore}</strong>: Manages focus within the main applciation window. The {FocusedContentStore} allows you to query and monitor changes to the selected thread, tag, file, etc.</p>\n</li>\n</ul>\n<p>Most packages declare additional stores that subscribe to these Stores, as well as user Actions, and vend data to the package&#39;s React components.</p>\n<h3 id=\"actions\">Actions</h3>\n<p>In Flux applications, views fire {Actions}, which anyone in the application can subscribe to. Typically, <code>Stores</code> listen to actions to perform business logic and trigger updates to their corresponding views. For example, when you click &quot;Compose&quot; in the top left corner of N1, the React component for the button fires {Actions::composeNewBlankDraft}. The {DraftStore} listens to this action and opens a new composer window.</p>\n<p>This approach means that your packages can fire existing {Actions}, like {Actions::composeNewBlankDraft}, or observe actions to add functionality. (For example, we have an Analytics package that also listens for {Actions::composeNewBlankDraft} and counts how many times it&#39;s been fired.) You can also define your own actions for use within your package.</p>\n<p>For a complete list of available actions, see {Actions}.</p>\n",
"meta": {
"Title": "Application Architecture",
"Section": "Guides",
"Order": 3,
"title": "Application Architecture",
"section": "Guides",
"order": 3
},
"name": "Application Architecture",
"filename": "Architecture.md",
"link": "Architecture.html"
},
{
"html": "<h3 id=\"chromium-devtools\">Chromium DevTools</h3>\n<p>N1 is built on top of Electron, which runs the latest version of Chromium (at the time of writing, Chromium 43). You can access the standard <a href=\"https://developer.chrome.com/devtools\">Chrome DevTools</a> using the <code>Command-Option-I</code> (<code>Ctrl-Shift-I</code> on Windows/Linux) keyboard shortcut, including the Debugger, Profiler, and Console. You can find extensive information about the Chromium DevTools on <a href=\"https://developer.chrome.com/devtools\">developer.chrome.com</a>.</p>\n<p>Here are a few hidden tricks for getting the most out of the Chromium DevTools:</p>\n<ul>\n<li><p>You can use <code>Command-P</code> to &quot;Open Quickly&quot;, jumping to a particular source file from any tab.</p>\n</li>\n<li><p>You can set breakpoints by clicking the line number gutter while viewing a source file.</p>\n</li>\n<li><p>While on a breakpoint, you can toggle the console panel by pressing <code>Esc</code> and type commands which are executed in the current scope.</p>\n</li>\n<li><p>While viewing the DOM in the <code>Elements</code> panel, typing <code>$0</code> on the console refers to the currently selected DOM node.</p>\n</li>\n</ul>\n<h3 id=\"nylas-developer-panel\">Nylas Developer Panel</h3>\n<p>If you choose <code>Developer &gt; Relaunch with Debug Flags...</code> from the menu, you can enable the Nylas Developer Panel at the bottom of the main window.</p>\n<p>The Developer Panel provides three views which you can click to activate:</p>\n<ul>\n<li><p><code>Tasks</code>: This view allows you to inspect the {TaskQueue} and see the what tasks are pending and complete. Click a task to see its JSON representation and inspect it&#39;s values, including the last error it encountered.</p>\n</li>\n<li><p><code>Delta Stream</code>: This view allows you to see the streaming updates from the Nylas API that the app has received. You can click individual updates to see the exact JSON that was consumed by the app, and search in the lower left for updates pertaining to an object ID or type.</p>\n</li>\n<li><p><code>Requests</code>: This view shows the requests the app has made to the Nylas API in <code>curl</code>-equivalent form. (The app does not actually make <code>curl</code> requests). You can click &quot;Copy&quot; to copy a <code>curl</code> command to the clipboard, or &quot;Run&quot; to execute it in a new Terminal window.</p>\n</li>\n</ul>\n<p>The Developer Panel also allows you to toggle &quot;View Component Regions&quot;. Turning on component regions adds a red border to areas of the app that render dynamically injected components, and shows the props passed to React components in each one. See {react} for more information.</p>\n<h3 id=\"the-development-workflow\">The Development Workflow</h3>\n<p>If you&#39;re debugging a package, you&#39;ll be modifying your code and re-running N1 over and over again. There are a few things you can do to make this development workflow less time consuming:</p>\n<ul>\n<li><p><strong>Inline Changes</strong>: Using the Chromium DevTools, you can change the contents of your coffeescript and javascript source files, type <code>Command-S</code> to save, and hot-swap the code. This makes it easy to test small adjustments to your code without re-launching N1.</p>\n</li>\n<li><p><strong>View &gt; Refresh</strong>: From the View menu, choose &quot;Refresh&quot; to reload the N1 window just like a page in your browser. Refreshing is faster than restarting the app and allows you to iterate more quickly.</p>\n<blockquote>\n<p>Note: A bug in Electron causes the Chromium DevTools to become detatched if you refresh the app often. If you find that Chromium is not stopping at your breakpoints, quit N1 and re-launch it.</p>\n</blockquote>\n</li>\n</ul>\n<p>In the future, we&#39;ll support much richer hot-reloading of plugin components and code. Stay tuned!</p>\n",
"meta": {
"Title": "Debugging N1",
"Section": "Guides",
"Order": 4,
"title": "Debugging N1",
"section": "Guides",
"order": 4
},
"name": "Debugging N1",
"filename": "Debugging.md",
"link": "Debugging.html"
},
{
"html": "<p>N1 is built on top of a custom database layer modeled after ActiveRecord. For many parts of the application, the database is the source of truth. Data is retrieved from the API, written to the database, and changes to the database trigger Stores and components to refresh their contents. The illustration below shows this flow of data:</p>\n<p><img src=\"./images/database-flow.png\"></p>\n<p>The Database connection is managed by the {DatabaseStore}, a singleton object that exists in every window. All Database requests are asynchronous. Queries are forwarded to the application&#39;s <code>Browser</code> process via IPC and run in SQLite.</p>\n<h2 id=\"declaring-models\">Declaring Models</h2>\n<p>In N1, Models are thin wrappers around data with a particular schema. Each {Model} class declares a set of attributes that define the object&#39;s data. For example:</p>\n<pre><code class=\"lang-coffee\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Example</span> <span class=\"hljs-keyword\"><span class=\"hljs-keyword\">extends</span></span> <span class=\"hljs-title\">Model</span>\n</span>\n <span class=\"hljs-annotation\">@attributes</span>:\n <span class=\"hljs-symbol\">'i</span>d': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">String</span>\n queryable: <span class=\"hljs-literal\">true</span>\n modelKey: <span class=\"hljs-symbol\">'i</span>d'\n\n <span class=\"hljs-symbol\">'objec</span>t': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">String</span>\n modelKey: <span class=\"hljs-symbol\">'objec</span>t'\n\n <span class=\"hljs-symbol\">'namespaceI</span>d': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">String</span>\n queryable: <span class=\"hljs-literal\">true</span>\n modelKey: <span class=\"hljs-symbol\">'namespaceI</span>d'\n jsonKey: <span class=\"hljs-symbol\">'namespace_i</span>d'\n\n <span class=\"hljs-symbol\">'bod</span>y': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">JoinedData</span>\n modelTable: <span class=\"hljs-symbol\">'MessageBod</span>y'\n modelKey: <span class=\"hljs-symbol\">'bod</span>y'\n\n <span class=\"hljs-symbol\">'file</span>s': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">Collection</span>\n modelKey: <span class=\"hljs-symbol\">'file</span>s'\n itemClass: <span class=\"hljs-type\">File</span>\n\n <span class=\"hljs-symbol\">'unrea</span>d': <span class=\"hljs-type\">Attributes</span>.<span class=\"hljs-type\">Boolean</span>\n queryable: <span class=\"hljs-literal\">true</span>\n modelKey: <span class=\"hljs-symbol\">'unrea</span>d'\n</code></pre>\n<p>When models are inflated from JSON using <code>fromJSON</code> or converted to JSON using <code>toJSON</code>, only the attributes declared on the model are copied. The <code>modelKey</code> and <code>jsonKey</code> options allow you to specify where a particular key should be found. Attributes are also coerced to the proper types: String attributes will always be strings, Boolean attributes will always be <code>true</code> or <code>false</code>, etc. <code>null</code> is a valid value for all types.</p>\n<p>The {DatabaseStore} automatically maintains cache tables for storing Model objects. By default, models are stored in the cache as JSON blobs and basic attributes are not queryable. When the <code>queryable</code> option is specified on an attribute, it is given a separate column and index in the SQLite table for the model, and you can construct queries using the attribute:</p>\n<pre><code class=\"lang-coffee\">Thread<span class=\"hljs-class\">.attributes</span><span class=\"hljs-class\">.namespaceId</span><span class=\"hljs-class\">.equals</span>(<span class=\"hljs-string\">\"123\"</span>)\n<span class=\"hljs-comment\">// where namespace_id = '123'</span>\n\nThread<span class=\"hljs-class\">.attributes</span><span class=\"hljs-class\">.lastMessageTimestamp</span><span class=\"
"meta": {
"Title": "Accessing the Database",
"Section": "Guides",
"Order": 5,
"title": "Accessing the Database",
"section": "Guides",
"order": 5
},
"name": "Accessing the Database",
"filename": "Database.md",
"link": "Database.html"
},
{
"html": "<p>The composer lies at the heart of N1, and many improvements to the mail experience require deep integration with the composer. To enable these sort of plugins, the {DraftStore} exposes an extension API.</p>\n<p>This API allows your package to:</p>\n<ul>\n<li><p>Display warning messages before a draft is sent. (ie: &quot;Are you sure you want to send this without attaching a file?&quot;)</p>\n</li>\n<li><p>Intercept keyboard and mouse events to the composer&#39;s text editor.</p>\n</li>\n<li><p>Transform the draft and make additional changes before it is sent.</p>\n</li>\n</ul>\n<p>To create your own composer extensions, subclass {DraftStoreExtension} and override the methods your extension needs.</p>\n<p>In the sample packages repository, <a href=\"\">templates</a> is an example of a package which uses a DraftStoreExtension to enhance the composer experience.</p>\n<h3 id=\"example\">Example</h3>\n<p>This extension displays a warning before sending a draft that contains the names of competitors&#39; products. If the user proceeds to send the draft containing the words, it appends a disclaimer.</p>\n<pre><code class=\"lang-coffee\">{DraftStoreExtension} = <span class=\"hljs-built_in\">require</span> <span class=\"hljs-string\">'nylas-exports'</span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">ProductsExtension</span> <span class=\"hljs-keyword\">extends</span> <span class=\"hljs-title\">DraftStoreExtension</span></span>\n\n <span class=\"hljs-property\">@warningsForSending</span>: <span class=\"hljs-function\"><span class=\"hljs-params\">(draft)</span> -&gt;</span>\n words = [<span class=\"hljs-string\">'acme'</span>, <span class=\"hljs-string\">'anvil'</span>, <span class=\"hljs-string\">'tunnel'</span>, <span class=\"hljs-string\">'rocket'</span>, <span class=\"hljs-string\">'dynamite'</span>]\n body = draft.body.toLowercase()\n <span class=\"hljs-keyword\">for</span> word <span class=\"hljs-keyword\">in</span> words\n <span class=\"hljs-keyword\">if</span> body.indexOf(word) &gt; <span class=\"hljs-number\">0</span>\n <span class=\"hljs-keyword\">return</span> [<span class=\"hljs-string\">\"with the word '<span class=\"hljs-subst\">#{word}</span>'?\"</span>]\n <span class=\"hljs-keyword\">return</span> []\n\n <span class=\"hljs-property\">@finalizeSessionBeforeSending</span>: <span class=\"hljs-function\"><span class=\"hljs-params\">(session)</span> -&gt;</span>\n draft = session.draft()\n <span class=\"hljs-keyword\">if</span> <span class=\"hljs-property\">@warningsForSending</span>(draft)\n bodyWithWarning = draft.body += <span class=\"hljs-string\">\"&lt;br&gt;This email \\\n contains competitor's product names \\\n or trademarks used in context.\"</span>\n session.changes.add(<span class=\"hljs-attribute\">body</span>: bodyWithWarning)\n</code></pre>\n",
"meta": {
"Title": "Extending the Composer",
"Section": "Guides",
"Order": 6,
"title": "Extending the Composer",
"section": "Guides",
"order": 6
},
"name": "Extending the Composer",
"filename": "DraftStoreExtensions.md",
"link": "DraftStoreExtensions.html"
},
{
2015-10-03 07:10:51 +08:00
"html": "<p>Nylas uses <a href=\"http://jasmine.github.io/1.3/introduction.html\">Jasmine</a> as its spec framework. As a package developer, you can write specs using Jasmine 1.3 and get some quick wins. Jasmine specs can be run in N1 directly from the Developer menu, and the test environment provides you with helpful stubs. You can also require your own test framework, or use Jasmine for integration tests and your own framework for your existing business logic.</p>\n<p>This documentation describes using <a href=\"http://jasmine.github.io/1.3/introduction.html\">Jasmine 1.3</a> to write specs for a Nylas package.</p>\n<h3 id=\"running-specs\">Running Specs</h3>\n<p>You can run your package specs from <code>Developer &gt; Run Package Specs...</code>. Once you&#39;ve opened the spec window, you can see output and re-run your specs by clicking <code>Reload Specs</code>.</p>\n<h3 id=\"writing-specs\">Writing Specs</h3>\n<p>To create specs, place <code>js</code>, <code>coffee</code>, or <code>cjsx</code> files in the <code>spec</code> directory of your package. Spec files must end with the <code>-spec</code> suffix.</p>\n<p>Here&#39;s an annotated look at a typical Jasmine spec:</p>\n<pre><code class=\"lang-coffee\"><span class=\"hljs-comment\"># The `describe` method takes two arguments, a description and a function. If the description</span>\n<span class=\"hljs-comment\"># explains a behavior it typically begins with `when`; if it is more like a unit test it begins</span>\n<span class=\"hljs-comment\"># with the method name.</span>\ndescribe <span class=\"hljs-string\">\"when a test is written\"</span>,<span class=\"hljs-function\"> -&gt;</span>\n\n <span class=\"hljs-comment\"># The `it` method also takes two arguments, a description and a function. Try and make the</span>\n <span class=\"hljs-comment\"># description flow with the `it` method. For example, a description of `this should work`</span>\n <span class=\"hljs-comment\"># doesn't read well as `it this should work`. But a description of `should work` sounds</span>\n <span class=\"hljs-comment\"># great as `it should work`.</span>\n <span class=\"hljs-literal\">it</span> <span class=\"hljs-string\">\"has some expectations that should pass\"</span>,<span class=\"hljs-function\"> -&gt;</span>\n\n <span class=\"hljs-comment\"># The best way to learn about expectations is to read the Jasmine documentation:</span>\n <span class=\"hljs-comment\"># http://jasmine.github.io/1.3/introduction.html#section-Expectations</span>\n <span class=\"hljs-comment\"># Below is a simple example.</span>\n\n expect(<span class=\"hljs-string\">\"apples\"</span>).toEqual(<span class=\"hljs-string\">\"apples\"</span>)\n expect(<span class=\"hljs-string\">\"oranges\"</span>).<span class=\"hljs-keyword\">not</span>.toEqual(<span class=\"hljs-string\">\"apples\"</span>)\n\ndescribe <span class=\"hljs-string\">\"Editor::moveUp\"</span>,<span class=\"hljs-function\"> -&gt;</span>\n ...\n</code></pre>\n<h4 id=\"asynchronous-spcs\">Asynchronous Spcs</h4>\n<p>Writing Asynchronous specs can be tricky at first, but a combination of spec helpers can make things easy. Here are a few quick examples:</p>\n<h5 id=\"promises\">Promises</h5>\n<p>You can use the global <code>waitsForPromise</code> function to make sure that the test does not complete until the returned promise has finished, and run your expectations in a chained promise.</p>\n<pre><code class=\"lang-coffee\"> describe <span class=\"hljs-string\">\"when requesting a Draft Session\"</span>, -&gt;\n it <span class=\"hljs-string\">\"a session with the correct ID is returned\"</span>, -&gt;\n waitsForPromise -&gt;\n DraftStore.<span class=\"hljs-function\"><span class=\"hljs-title\">sessionForLocalId</span><span class=\"hljs-params\">(<span class=\"hljs-string\">'123'</span>)</span></span><span class=\"hljs-class\">.then</span> (session) -&gt;\n <span class=\"hljs-function\"><span class=\"hljs-title\">expect</span><span class=\"hljs-params\">(session.id)</span></span>.<span class=\"hljs-fun
2015-10-03 01:57:40 +08:00
"meta": {
"Title": "Writing Specs",
"TitleHidden": true,
"Section": "Guides",
"Order": 7,
"title": "Writing Specs",
"titlehidden": true,
"section": "Guides",
"order": 7
},
"name": "Writing Specs",
"filename": "WritingSpecs.md",
"link": "WritingSpecs.html"
},
{
"html": "<h3 id=\"do-i-have-to-use-react-\">Do I have to use React?</h3>\n<p>The short answer is yes, you need to use React. The {ComponentRegistry} expects React components, so you&#39;ll need to create them to extend the N1 interface.</p>\n<p>However, if you have significant code already written in another framework, like Angular or Backbone, it&#39;s possible to attach your application to a React component. See <a href=\"https://github.com/davidchang/ngReact/issues/80\">https://github.com/davidchang/ngReact/issues/80</a>.</p>\n<h3 id=\"can-i-write-a-package-that-does-x-\">Can I write a package that does X?</h3>\n<p>If you don&#39;t find documentation for the part of N1 you want to extend, let us know! We&#39;re constantly working to enable new workflows by making more of the application extensible.</p>\n<h3 id=\"can-i-distribute-my-package-\">Can I distribute my package?</h3>\n<p>Yes! We&#39;ll be sharing more information about publishing packages in the coming months. However, you can already publish and share packages by following the steps below:</p>\n<ol>\n<li><p>Create a Github repository for your package, and publish a <code>Tag</code> to the repository matching the version number in your <code>package.json</code> file. (Ex: <code>0.1.0</code>)</p>\n</li>\n<li><p>Make the CURL request below to the package manager server to register your package:</p>\n<pre><code> curl -<span class=\"hljs-keyword\">H</span> <span class=\"hljs-string\">\"Content-Type:application/json\"</span> -X <span class=\"hljs-keyword\">POST</span> -<span class=\"hljs-keyword\">d</span> '{<span class=\"hljs-string\">\"repository\"</span>:<span class=\"hljs-string\">\"https://github.com/&lt;username&gt;/&lt;repo&gt;\"</span>}' https:<span class=\"hljs-comment\">//edgehill-packages.nylas.com/api/packages</span>\n</code></pre></li>\n<li><p>Your package will now appear when users visit the N1 settings page and search for community packages.</p>\n</li>\n</ol>\n<p>Note: In the near future, we&#39;ll be formalizing the process of distributing packages, and packages you publish now may need to be resubmitted.</p>\n",
"meta": {
"Title": "FAQ",
"Section": "Guides",
"Order": 10,
"title": "FAQ",
"section": "Guides",
"order": 10
},
"name": "FAQ",
"filename": "FAQ.md",
"link": "FAQ.html"
}
]
},
{
"name": "Sample Code",
2015-10-03 07:10:51 +08:00
"link": "https://nylas.github.io/N1/examples",
"external": true
2015-10-03 01:57:40 +08:00
},
{
"name": "API Reference",
"items": [
{
"name": "General",
"items": [
{
"name": "Actions",
"link": "Actions.html"
},
{
"name": "Atom",
"link": "Atom.html"
},
{
"name": "BufferedNodeProcess",
"link": "BufferedNodeProcess.html"
},
{
"name": "BufferedProcess",
"link": "BufferedProcess.html"
},
{
"name": "ChangeFolderTask",
"link": "ChangeFolderTask.html"
},
{
"name": "ChangeLabelsTask",
"link": "ChangeLabelsTask.html"
},
{
"name": "Config",
"link": "Config.html"
},
{
"name": "DraggableImg",
"link": "DraggableImg.html"
},
{
"name": "FocusTrackingRegion",
"link": "FocusTrackingRegion.html"
},
{
"name": "Switch",
"link": "Switch.html"
},
{
"name": "Task",
"link": "Task.html"
},
{
"name": "TaskQueueStatusStore",
"link": "TaskQueueStatusStore.html"
}
]
},
{
"name": "Component Kit",
"items": [
{
"name": "EventedIFrame",
"link": "EventedIFrame.html"
},
{
"name": "Flexbox",
"link": "Flexbox.html"
},
{
"name": "InjectedComponent",
"link": "InjectedComponent.html"
},
{
"name": "InjectedComponentSet",
"link": "InjectedComponentSet.html"
},
{
"name": "Menu",
"link": "Menu.html"
},
{
"name": "MenuItem",
"link": "MenuItem.html"
},
{
"name": "MenuNameEmailItem",
"link": "MenuNameEmailItem.html"
},
{
"name": "MultiselectActionBar",
"link": "MultiselectActionBar.html"
},
{
"name": "MultiselectList",
"link": "MultiselectList.html"
},
{
"name": "Popover",
"link": "Popover.html"
},
{
"name": "ResizableRegion",
"link": "ResizableRegion.html"
},
{
"name": "RetinaImg",
"link": "RetinaImg.html"
},
{
"name": "Spinner",
"link": "Spinner.html"
},
{
"name": "TimeoutTransitionGroupChild",
"link": "TimeoutTransitionGroupChild.html"
},
{
"name": "UnsafeComponent",
"link": "UnsafeComponent.html"
}
]
},
{
"name": "Models",
"items": [
{
"name": "Account",
"link": "Account.html"
},
{
"name": "Calendar",
"link": "Calendar.html"
},
{
"name": "Contact",
"link": "Contact.html"
},
{
"name": "File",
"link": "File.html"
},
{
"name": "Folder",
"link": "Folder.html"
},
{
"name": "Label",
"link": "Label.html"
},
{
"name": "Message",
"link": "Message.html"
},
{
"name": "Model",
"link": "Model.html"
},
{
"name": "Thread",
"link": "Thread.html"
}
]
},
{
"name": "Stores",
"items": [
{
"name": "AccountStore",
"link": "AccountStore.html"
},
{
"name": "ComponentRegistry",
"link": "ComponentRegistry.html"
},
{
"name": "ContactStore",
"link": "ContactStore.html"
},
{
"name": "EventStore",
"link": "EventStore.html"
},
{
"name": "FocusedContentStore",
"link": "FocusedContentStore.html"
},
{
"name": "MessageStoreExtension",
"link": "MessageStoreExtension.html"
},
{
"name": "TaskQueue",
"link": "TaskQueue.html"
},
{
"name": "WorkspaceStore",
"link": "WorkspaceStore.html"
}
]
},
{
"name": "Database",
"items": [
{
"name": "Attribute",
"link": "Attribute.html"
},
{
"name": "AttributeBoolean",
"link": "AttributeBoolean.html"
},
{
"name": "AttributeCollection",
"link": "AttributeCollection.html"
},
{
"name": "AttributeDateTime",
"link": "AttributeDateTime.html"
},
{
"name": "AttributeJoinedData",
"link": "AttributeJoinedData.html"
},
{
"name": "AttributeNumber",
"link": "AttributeNumber.html"
},
{
"name": "AttributeObject",
"link": "AttributeObject.html"
},
{
"name": "AttributeServerId",
"link": "AttributeServerId.html"
},
{
"name": "AttributeString",
"link": "AttributeString.html"
},
{
"name": "DatabaseStore",
"link": "DatabaseStore.html"
},
{
"name": "DatabaseView",
"link": "DatabaseView.html"
},
{
"name": "Matcher",
"link": "Matcher.html"
},
{
"name": "ModelQuery",
"link": "ModelQuery.html"
},
{
"name": "SortOrder",
"link": "SortOrder.html"
}
]
},
{
"name": "Drafts",
"items": [
{
"name": "DraftChangeSet",
"link": "DraftChangeSet.html"
},
{
"name": "DraftStore",
"link": "DraftStore.html"
},
{
"name": "DraftStoreExtension",
"link": "DraftStoreExtension.html"
},
{
"name": "DraftStoreProxy",
"link": "DraftStoreProxy.html"
}
]
},
{
"name": "Atom",
"items": [
{
"name": "Clipboard",
"link": "Clipboard.html"
},
{
"name": "Color",
"link": "Color.html"
},
{
"name": "CommandRegistry",
"link": "CommandRegistry.html"
},
{
"name": "MenuManager",
"link": "MenuManager.html"
},
{
"name": "PackageManager",
"link": "PackageManager.html"
},
{
"name": "ScopeDescriptor",
"link": "ScopeDescriptor.html"
},
{
"name": "StyleManager",
"link": "StyleManager.html"
},
{
"name": "ThemeManager",
"link": "ThemeManager.html"
}
]
}
]
}
]
}