├── .gitattributes ├── source ├── javascripts │ ├── all.js │ ├── app │ │ ├── _copy.js │ │ ├── _search.js │ │ ├── _toc.js │ │ └── _lang.js │ ├── all_nosearch.js │ └── lib │ │ ├── _jquery.highlight.js │ │ ├── _imagesloaded.min.js │ │ └── _energize.js ├── fonts │ ├── slate.eot │ ├── slate.ttf │ ├── slate.woff │ ├── slate.woff2 │ └── slate.svg ├── images │ ├── logo.png │ └── navbar.png ├── stylesheets │ ├── _icon-font.scss │ ├── print.css.scss │ ├── _rtl.scss │ ├── _variables.scss │ ├── _normalize.scss │ └── screen.css.scss ├── includes │ └── _errors.md ├── layouts │ └── layout.erb └── index.html.md ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug.md ├── dependabot.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── deploy.yml │ ├── build.yml │ ├── publish.yml │ └── dev_deploy.yml ├── Gemfile ├── .gitignore ├── .editorconfig ├── Dockerfile ├── lib ├── multilang.rb ├── nesting_unique_head.rb ├── toc_data.rb ├── unique_head.rb └── monokai_sublime_slate.rb ├── Vagrantfile ├── config.rb ├── CODE_OF_CONDUCT.md ├── Gemfile.lock ├── font-selection.json ├── README.md ├── deploy.sh ├── slate.sh ├── LICENSE └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | source/javascripts/lib/* linguist-vendored 2 | -------------------------------------------------------------------------------- /source/javascripts/all.js: -------------------------------------------------------------------------------- 1 | //= require ./all_nosearch 2 | //= require ./app/_search 3 | -------------------------------------------------------------------------------- /source/fonts/slate.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/fonts/slate.eot -------------------------------------------------------------------------------- /source/fonts/slate.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/fonts/slate.ttf -------------------------------------------------------------------------------- /source/fonts/slate.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/fonts/slate.woff -------------------------------------------------------------------------------- /source/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/images/logo.png -------------------------------------------------------------------------------- /source/fonts/slate.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/fonts/slate.woff2 -------------------------------------------------------------------------------- /source/images/navbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tanveerpot/slate/HEAD/source/images/navbar.png -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .github/ 3 | build/ 4 | .editorconfig 5 | .gitattributes 6 | .gitignore 7 | CHANGELOG.md 8 | CODE_OF_CONDUCT.md 9 | deploy.sh 10 | font-selection.json 11 | README.md 12 | Vagrantfile -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions, Ideas, Discussions 4 | url: https://github.com/slatedocs/slate/discussions 5 | about: Ask and answer questions, and propose new features. 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | target-branch: dev 9 | versioning-strategy: increase-if-necessary 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ruby '>= 2.6' 2 | source 'https://rubygems.org' 3 | 4 | # Middleman 5 | gem 'middleman', '~> 4.4' 6 | gem 'middleman-syntax', '~> 3.2' 7 | gem 'middleman-autoprefixer', '~> 3.0' 8 | gem 'middleman-sprockets', '~> 4.1' 9 | gem 'rouge', '~> 3.21' 10 | gem 'redcarpet', '~> 3.6.0' 11 | gem 'nokogiri', '~> 1.13.3' 12 | gem 'sass' 13 | gem 'webrick' 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | *.DS_STORE 15 | build/ 16 | .cache 17 | .vagrant 18 | .sass-cache 19 | 20 | # YARD artifacts 21 | .yardoc 22 | _yardoc 23 | doc/ 24 | .idea/ 25 | 26 | # Vagrant artifacts 27 | ubuntu-*-console.log 28 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | trim_trailing_whitespace = true 13 | 14 | [*.rb] 15 | charset = utf-8 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6-slim 2 | 3 | WORKDIR /srv/slate 4 | 5 | EXPOSE 4567 6 | 7 | COPY Gemfile . 8 | COPY Gemfile.lock . 9 | 10 | RUN apt-get update \ 11 | && apt-get install -y --no-install-recommends \ 12 | build-essential \ 13 | git \ 14 | nodejs \ 15 | && gem install bundler \ 16 | && bundle install \ 17 | && apt-get remove -y build-essential git \ 18 | && apt-get autoremove -y \ 19 | && rm -rf /var/lib/apt/lists/* 20 | 21 | COPY . /srv/slate 22 | 23 | RUN chmod +x /srv/slate/slate.sh 24 | 25 | ENTRYPOINT ["/srv/slate/slate.sh"] 26 | CMD ["build"] 27 | -------------------------------------------------------------------------------- /lib/multilang.rb: -------------------------------------------------------------------------------- 1 | module Multilang 2 | def block_code(code, full_lang_name) 3 | if full_lang_name 4 | parts = full_lang_name.split('--') 5 | rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified 6 | super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match| 7 | match + " tab-" + full_lang_name 8 | end 9 | else 10 | super(code, full_lang_name) 11 | end 12 | end 13 | end 14 | 15 | require 'middleman-core/renderers/redcarpet' 16 | Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Report a Bug 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Bug Description** 11 | A clear and concise description of what the bug is and how to reproduce it. 12 | 13 | **Screenshots** 14 | If applicable, add screenshots to help explain your problem. 15 | 16 | **Browser (please complete the following information):** 17 | - OS: [e.g. iOS] 18 | - Browser [e.g. chrome, safari] 19 | - Version [e.g. 22] 20 | 21 | **Last upstream Slate commit (run `git log --author="\(Robert Lord\)\|\(Matthew Peveler\)\|\(Mike Ralphson\)" | head -n 1`):** 22 | Put the commit hash here 23 | -------------------------------------------------------------------------------- /source/javascripts/app/_copy.js: -------------------------------------------------------------------------------- 1 | function copyToClipboard(container) { 2 | const el = document.createElement('textarea'); 3 | el.value = container.textContent.replace(/\n$/, ''); 4 | document.body.appendChild(el); 5 | el.select(); 6 | document.execCommand('copy'); 7 | document.body.removeChild(el); 8 | } 9 | 10 | function setupCodeCopy() { 11 | $('pre.highlight').prepend('
'); 12 | $('.copy-clipboard').on('click', function() { 13 | copyToClipboard(this.parentNode.children[1]); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/nesting_unique_head.rb: -------------------------------------------------------------------------------- 1 | # Nested unique header generation 2 | require 'middleman-core/renderers/redcarpet' 3 | 4 | class NestingUniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML 5 | def initialize 6 | super 7 | @@headers_history = {} if !defined?(@@headers_history) 8 | end 9 | 10 | def header(text, header_level) 11 | friendly_text = text.gsub(/<[^>]*>/,"").parameterize 12 | @@headers_history[header_level] = text.parameterize 13 | 14 | if header_level > 1 15 | for i in (header_level - 1).downto(1) 16 | friendly_text.prepend("#{@@headers_history[i]}-") if @@headers_history.key?(i) 17 | end 18 | end 19 | 20 | return "Slate helps you create beautiful, intelligent, responsive API documentation.
9 | 10 |
The example above was created with Slate. Check it out at slatedocs.github.io/slate.
13 | 14 | Features 15 | ------------ 16 | 17 | * **Clean, intuitive design** — With Slate, the description of your API is on the left side of your documentation, and all the code examples are on the right side. Inspired by [Stripe's](https://stripe.com/docs/api) and [PayPal's](https://developer.paypal.com/webapps/developer/docs/api/) API docs. Slate is responsive, so it looks great on tablets, phones, and even in print. 18 | 19 | * **Everything on a single page** — Gone are the days when your users had to search through a million pages to find what they wanted. Slate puts the entire documentation on a single page. We haven't sacrificed linkability, though. As you scroll, your browser's hash will update to the nearest header, so linking to a particular point in the documentation is still natural and easy. 20 | 21 | * **Slate is just Markdown** — When you write docs with Slate, you're just writing Markdown, which makes it simple to edit and understand. Everything is written in Markdown — even the code samples are just Markdown code blocks. 22 | 23 | * **Write code samples in multiple languages** — If your API has bindings in multiple programming languages, you can easily put in tabs to switch between them. In your document, you'll distinguish different languages by specifying the language name at the top of each code block, just like with GitHub Flavored Markdown. 24 | 25 | * **Out-of-the-box syntax highlighting** for [over 100 languages](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers), no configuration required. 26 | 27 | * **Automatic, smoothly scrolling table of contents** on the far left of the page. As you scroll, it displays your current position in the document. It's fast, too. We're using Slate at TripIt to build documentation for our new API, where our table of contents has over 180 entries. We've made sure that the performance remains excellent, even for larger documents. 28 | 29 | * **Let your users update your documentation for you** — By default, your Slate-generated documentation is hosted in a public GitHub repository. Not only does this mean you get free hosting for your docs with GitHub Pages, but it also makes it simple for other developers to make pull requests to your docs if they find typos or other problems. Of course, if you don't want to use GitHub, you're also welcome to host your docs elsewhere. 30 | 31 | * **RTL Support** Full right-to-left layout for RTL languages such as Arabic, Persian (Farsi), Hebrew etc. 32 | 33 | Getting started with Slate is super easy! Simply press the green "use this template" button above and follow the instructions below. Or, if you'd like to check out what Slate is capable of, take a look at the [sample docs](https://slatedocs.github.io/slate/). 34 | 35 | Getting Started with Slate 36 | ------------------------------ 37 | 38 | To get started with Slate, please check out the [Getting Started](https://github.com/slatedocs/slate/wiki#getting-started) 39 | section in our [wiki](https://github.com/slatedocs/slate/wiki). 40 | 41 | We support running Slate in three different ways: 42 | * [Natively](https://github.com/slatedocs/slate/wiki/Using-Slate-Natively) 43 | * [Using Vagrant](https://github.com/slatedocs/slate/wiki/Using-Slate-in-Vagrant) 44 | * [Using Docker](https://github.com/slatedocs/slate/wiki/Using-Slate-in-Docker) 45 | 46 | Companies Using Slate 47 | --------------------------------- 48 | 49 | * [NASA](https://api.nasa.gov) 50 | * [Sony](http://developers.cimediacloud.com) 51 | * [Best Buy](https://bestbuyapis.github.io/api-documentation/) 52 | * [Travis-CI](https://docs.travis-ci.com/api/) 53 | * [Greenhouse](https://developers.greenhouse.io/harvest.html) 54 | * [WooCommerce](http://woocommerce.github.io/woocommerce-rest-api-docs/) 55 | * [Dwolla](https://docs.dwolla.com/) 56 | * [Clearbit](https://clearbit.com/docs) 57 | * [Coinbase](https://developers.coinbase.com/api) 58 | * [Parrot Drones](http://developer.parrot.com/docs/bebop/) 59 | * [CoinAPI](https://docs.coinapi.io/) 60 | 61 | You can view more in [the list on the wiki](https://github.com/slatedocs/slate/wiki/Slate-in-the-Wild). 62 | 63 | Questions? Need Help? Found a bug? 64 | -------------------- 65 | 66 | If you've got questions about setup, deploying, special feature implementation in your fork, or just want to chat with the developer, please feel free to [start a thread in our Discussions tab](https://github.com/slatedocs/slate/discussions)! 67 | 68 | Found a bug with upstream Slate? Go ahead and [submit an issue](https://github.com/slatedocs/slate/issues). And, of course, feel free to submit pull requests with bug fixes or changes to the `dev` branch. 69 | 70 | Contributors 71 | -------------------- 72 | 73 | Slate was built by [Robert Lord](https://lord.io) while at [TripIt](https://www.tripit.com/). The project is now maintained by [Matthew Peveler](https://github.com/MasterOdin) and [Mike Ralphson](https://github.com/MikeRalphson). 74 | 75 | Thanks to the following people who have submitted major pull requests: 76 | 77 | - [@chrissrogers](https://github.com/chrissrogers) 78 | - [@bootstraponline](https://github.com/bootstraponline) 79 | - [@realityking](https://github.com/realityking) 80 | - [@cvkef](https://github.com/cvkef) 81 | 82 | Also, thanks to [Sauce Labs](http://saucelabs.com) for sponsoring the development of the responsive styles. 83 | -------------------------------------------------------------------------------- /source/javascripts/lib/_energize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * energize.js v0.1.0 3 | * 4 | * Speeds up click events on mobile devices. 5 | * https://github.com/davidcalhoun/energize.js 6 | */ 7 | 8 | (function() { // Sandbox 9 | /** 10 | * Don't add to non-touch devices, which don't need to be sped up 11 | */ 12 | if(!('ontouchstart' in window)) return; 13 | 14 | var lastClick = {}, 15 | isThresholdReached, touchstart, touchmove, touchend, 16 | click, closest; 17 | 18 | /** 19 | * isThresholdReached 20 | * 21 | * Compare touchstart with touchend xy coordinates, 22 | * and only fire simulated click event if the coordinates 23 | * are nearby. (don't want clicking to be confused with a swipe) 24 | */ 25 | isThresholdReached = function(startXY, xy) { 26 | return Math.abs(startXY[0] - xy[0]) > 5 || Math.abs(startXY[1] - xy[1]) > 5; 27 | }; 28 | 29 | /** 30 | * touchstart 31 | * 32 | * Save xy coordinates when the user starts touching the screen 33 | */ 34 | touchstart = function(e) { 35 | this.startXY = [e.touches[0].clientX, e.touches[0].clientY]; 36 | this.threshold = false; 37 | }; 38 | 39 | /** 40 | * touchmove 41 | * 42 | * Check if the user is scrolling past the threshold. 43 | * Have to check here because touchend will not always fire 44 | * on some tested devices (Kindle Fire?) 45 | */ 46 | touchmove = function(e) { 47 | // NOOP if the threshold has already been reached 48 | if(this.threshold) return false; 49 | 50 | this.threshold = isThresholdReached(this.startXY, [e.touches[0].clientX, e.touches[0].clientY]); 51 | }; 52 | 53 | /** 54 | * touchend 55 | * 56 | * If the user didn't scroll past the threshold between 57 | * touchstart and touchend, fire a simulated click. 58 | * 59 | * (This will fire before a native click) 60 | */ 61 | touchend = function(e) { 62 | // Don't fire a click if the user scrolled past the threshold 63 | if(this.threshold || isThresholdReached(this.startXY, [e.changedTouches[0].clientX, e.changedTouches[0].clientY])) { 64 | return; 65 | } 66 | 67 | /** 68 | * Create and fire a click event on the target element 69 | * https://developer.mozilla.org/en/DOM/event.initMouseEvent 70 | */ 71 | var touch = e.changedTouches[0], 72 | evt = document.createEvent('MouseEvents'); 73 | evt.initMouseEvent('click', true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null); 74 | evt.simulated = true; // distinguish from a normal (nonsimulated) click 75 | e.target.dispatchEvent(evt); 76 | }; 77 | 78 | /** 79 | * click 80 | * 81 | * Because we've already fired a click event in touchend, 82 | * we need to listed for all native click events here 83 | * and suppress them as necessary. 84 | */ 85 | click = function(e) { 86 | /** 87 | * Prevent ghost clicks by only allowing clicks we created 88 | * in the click event we fired (look for e.simulated) 89 | */ 90 | var time = Date.now(), 91 | timeDiff = time - lastClick.time, 92 | x = e.clientX, 93 | y = e.clientY, 94 | xyDiff = [Math.abs(lastClick.x - x), Math.abs(lastClick.y - y)], 95 | target = closest(e.target, 'A') || e.target, // needed for standalone apps 96 | nodeName = target.nodeName, 97 | isLink = nodeName === 'A', 98 | standAlone = window.navigator.standalone && isLink && e.target.getAttribute("href"); 99 | 100 | lastClick.time = time; 101 | lastClick.x = x; 102 | lastClick.y = y; 103 | 104 | /** 105 | * Unfortunately Android sometimes fires click events without touch events (seen on Kindle Fire), 106 | * so we have to add more logic to determine the time of the last click. Not perfect... 107 | * 108 | * Older, simpler check: if((!e.simulated) || standAlone) 109 | */ 110 | if((!e.simulated && (timeDiff < 500 || (timeDiff < 1500 && xyDiff[0] < 50 && xyDiff[1] < 50))) || standAlone) { 111 | e.preventDefault(); 112 | e.stopPropagation(); 113 | if(!standAlone) return false; 114 | } 115 | 116 | /** 117 | * Special logic for standalone web apps 118 | * See http://stackoverflow.com/questions/2898740/iphone-safari-web-app-opens-links-in-new-window 119 | */ 120 | if(standAlone) { 121 | window.location = target.getAttribute("href"); 122 | } 123 | 124 | /** 125 | * Add an energize-focus class to the targeted link (mimics :focus behavior) 126 | * TODO: test and/or remove? Does this work? 127 | */ 128 | if(!target || !target.classList) return; 129 | target.classList.add("energize-focus"); 130 | window.setTimeout(function(){ 131 | target.classList.remove("energize-focus"); 132 | }, 150); 133 | }; 134 | 135 | /** 136 | * closest 137 | * @param {HTMLElement} node current node to start searching from. 138 | * @param {string} tagName the (uppercase) name of the tag you're looking for. 139 | * 140 | * Find the closest ancestor tag of a given node. 141 | * 142 | * Starts at node and goes up the DOM tree looking for a 143 | * matching nodeName, continuing until hitting document.body 144 | */ 145 | closest = function(node, tagName){ 146 | var curNode = node; 147 | 148 | while(curNode !== document.body) { // go up the dom until we find the tag we're after 149 | if(!curNode || curNode.nodeName === tagName) { return curNode; } // found 150 | curNode = curNode.parentNode; // not found, so keep going up 151 | } 152 | 153 | return null; // not found 154 | }; 155 | 156 | /** 157 | * Add all delegated event listeners 158 | * 159 | * All the events we care about bubble up to document, 160 | * so we can take advantage of event delegation. 161 | * 162 | * Note: no need to wait for DOMContentLoaded here 163 | */ 164 | document.addEventListener('touchstart', touchstart, false); 165 | document.addEventListener('touchmove', touchmove, false); 166 | document.addEventListener('touchend', touchend, false); 167 | document.addEventListener('click', click, true); // TODO: why does this use capture? 168 | 169 | })(); -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit #abort if any command fails 3 | me=$(basename "$0") 4 | 5 | help_message="\ 6 | Usage: $me [-c FILE] [