Compare commits

..

842 Commits

Author SHA1 Message Date
Samy Pessé 1e4632080b Bump version to 1.2.0 2014-10-30 18:27:56 +01:00
Samy Pessé 5191106396 Update documentation in readme 2014-10-30 18:27:26 +01:00
Samy Pessé 6ab5cafaf3 Improve toc in ebook (Fixes #338, #322) 2014-10-30 18:14:06 +01:00
Samy Pessé d1c0cb9c71 Normalize template paths 2014-10-30 18:01:56 +01:00
Samy Pessé 38c750101c Fix #487: Use generated toc as base for ebook generation 2014-10-30 17:52:39 +01:00
Samy Pessé ba0f379a89 Add plugin infinitescroll 2014-10-30 16:19:19 +01:00
Samy Pessé 1d9509b3b6 Merge pull request #485 from citizenmatt/Nested_list_spacing
Fix bottom border on nested ul
2014-10-30 14:15:54 +01:00
Samy Pessé 3c8de4a8ab Add 'variables' to book.json doc 2014-10-29 17:20:30 +01:00
Samy Pessé 591d8fc9ee Use book.json to store variables 2014-10-29 17:19:19 +01:00
Samy Pessé d480e9cea4 Update highlight.js@8.3.0 2014-10-23 17:05:27 +02:00
Matt Ellis 32a71869da Fix bottom border on nested ul 2014-10-23 09:19:02 +01:00
Samy Pessé fdbfda64a6 Merge pull request #483 from dharmeshkakadia/patch-1
Corrected a small typo in Plugins description
2014-10-21 15:45:45 +02:00
Dharmesh Kakadia a6f48be39e Corrected a small typo in Plugins description 2014-10-21 00:47:37 +05:30
Aaron O'Mullan ce50b893e9 Merge pull request #482 from GitbookIO/feature/includes
Add include support, with variables and the whole shebang
2014-10-15 21:29:23 +02:00
Aaron O'Mullan 225ff27dd1 Fix small incompatibility mistake 2014-10-15 21:28:53 +02:00
Aaron O'Mullan 52e8361846 Add include support, with variables and the whole shebang
Fixes #462
2014-10-15 21:20:10 +02:00
Samy Pessé 49e3bbc3d6 Bump version to 1.1.1 2014-10-13 16:03:47 +02:00
Aaron O'Mullan c9ec911d0e Merge pull request #472 from GitbookIO/fix/quizzes
Fix/quizzes
2014-10-13 15:55:32 +02:00
Aaron O'Mullan 84d7662fd8 Allow quiz titles in validation 2014-10-13 15:55:59 +02:00
Aaron O'Mullan 676b2287ee Rewrite quiz logic to be more robust
Fixes #470
2014-10-13 15:50:12 +02:00
Samy Pessé 32a64be407 Fix #460: search for glossary term on all nodes 2014-10-13 15:23:51 +02:00
Samy Pessé 138cddf3d6 Ste GitBook as publisher 2014-10-13 15:23:00 +02:00
Aaron O'Mullan d730a3aac6 Split section classification to separate files
Maybe have a sections folder in the future and one per type
(quiz/exercise/normal) ?
2014-10-13 15:07:46 +02:00
Aaron O'Mullan ffb0b12012 Add unit test for quiz false positivies 2014-10-13 14:17:08 +02:00
Samy Pessé 78e25f8252 Add link to gitbook.gitignore 2014-10-13 11:10:28 +02:00
Samy Pessé 09f42f57e1 Improve comments 2014-10-13 11:01:00 +02:00
Samy Pessé 950ad1d060 Remove reference to single page output 2014-10-13 10:59:32 +02:00
Samy Pessé ed6b29c1a4 Adapt tests 2014-10-13 10:57:47 +02:00
Samy Pessé 36785120b1 Fix default for pdf templates 2014-10-13 10:56:14 +02:00
Samy Pessé 26fad5b888 Remove ref to github options 2014-10-13 10:53:51 +02:00
Samy Pessé f049576575 Merge branch 'alexandreaquiles-pdf_header_and_footer' 2014-10-13 10:48:33 +02:00
Samy Pessé a7ecbb87e4 Force type of header and footer templates 2014-10-13 10:48:02 +02:00
Samy Pessé 1da341741b Merge pull request #471 from GitbookIO/feature/multipages
Use multiple HTML files for ebook generation
2014-10-13 10:41:23 +02:00
Samy Pessé 2dd6192576 Remove old codes 2014-10-12 22:25:39 +02:00
Samy Pessé 52c9347b9c Improve style for exercises and quizzes in ebook 2014-10-12 22:07:37 +02:00
Samy Pessé e2295591ed Handle correctly quizzes 2014-10-12 11:42:32 +02:00
Samy Pessé 0c1cb724d7 Handle correctly exercises in ebook 2014-10-12 11:37:51 +02:00
Samy Pessé 7cee248426 Generate multiple page for ebook 2014-10-12 11:32:45 +02:00
Aaron O'Mullan 6ea5b2b539 Fix incorrect page breaks after h1 and h2 divs
Fixes #463, fixes #337, fixes #293
2014-10-08 20:08:22 +02:00
Alexandre Aquiles a56b559d43 Adding options to set header and footer in PDF generation 2014-10-06 13:17:34 -03:00
Samy Pessé df1a82e0e4 Add changelog for 1.1.0 2014-09-24 14:57:50 +02:00
Samy Pessé f15a1efe71 Bump version to 1.1.0 2014-09-24 14:56:09 +02:00
Samy Pessé b8379fc6fa Update kramed@0.4.3 2014-09-24 14:53:42 +02:00
Samy Pessé d69cc74647 Merge pull request #440 from GitbookIO/feature/ebookplugins
Apply plugins to ebook generation
2014-09-17 15:02:22 +02:00
Samy Pessé f83874a9ca Fix correct folder for assets 2014-09-17 14:16:04 +02:00
Samy Pessé 1968bb79c2 Use correct resources file for ebook 2014-09-17 13:17:30 +02:00
Samy Pessé 60c721db40 Enable css from plugins in page output 2014-09-17 13:10:36 +02:00
Samy Pessé 11b299653f Remove githubId
Enable hook for ebook
2014-09-17 13:08:30 +02:00
Samy Pessé c8c07fe8ce Add base to use plugins in page format 2014-09-17 13:00:28 +02:00
Samy Pessé be0a4f08e9 Merge pull request #435 from markcxz/master
fix radio input uncheck failed
2014-09-10 09:56:12 +02:00
markcxz 4da4e28d07 fix radio input uncheck failed 2014-09-10 10:27:27 +08:00
Samy Pessé 9dc19026c6 Add changelog for version 1.0.3 2014-09-05 17:15:04 +02:00
Samy Pessé 31ca9f7fcb Bump version to 1.0.3 2014-09-05 17:12:03 +02:00
Samy Pessé 6925d39e8d Fix GitbookIO/editor#136: update highlight.js@8.2.0 2014-09-05 17:10:47 +02:00
Samy Pessé a24cafd975 Update plugin-mathjax@0.0.6 2014-09-02 23:25:33 +02:00
Aaron O'Mullan e3fdf8b404 Bump version to 1.0.2 2014-08-20 17:03:31 -07:00
Aaron O'Mullan c861223378 Use gitbook-plugin-mathjax@0.0.5 2014-08-20 17:01:14 -07:00
Aaron O'Mullan f5c6d8df71 Bump version to 1.0.1 2014-08-20 16:30:04 -07:00
Aaron O'Mullan 452c846e90 Update framed to 0.4.2
This uses the new kramdown convention for inline math
2014-08-20 16:27:50 -07:00
Samy Pessé aedd875ab7 Fix url for sharing 2014-08-20 15:54:11 -07:00
Samy Pessé d2ec2f8f02 Add plugins exercises and quizzes by default 2014-08-20 10:33:34 -07:00
Samy Pessé 5af0df93c3 Normalize titles in readme 2014-08-19 16:39:54 -07:00
Samy Pessé 5b4b5e1af7 Bump version to 1.0.0 2014-08-19 16:18:29 -07:00
Samy Pessé 21704c4ac2 Add documentation for glossary format 2014-08-19 16:16:59 -07:00
Aaron O'Mullan 71aff10092 Merge pull request #413 from GitbookIO/feature/glossary
Glossary terms highlight in page
2014-08-19 16:09:47 -07:00
Samy Pessé 4b390667e5 Replace whole word with glossary term 2014-08-19 16:10:02 -07:00
Samy Pessé 14dda3148d Improve replace of words with glossary links 2014-08-19 16:05:57 -07:00
Samy Pessé 511dc9f805 Add action on glossary term in the page 2014-08-19 15:40:37 -07:00
Samy Pessé d0a14dcbcc Write correct glossary to disk 2014-08-19 10:36:27 -07:00
Samy Pessé ec8dc2cdb0 Improve design for glossary page 2014-08-18 22:00:34 -07:00
Samy Pessé 6f1ef585be Show index on glossary page 2014-08-18 21:53:07 -07:00
Samy Pessé d84acd0b82 Dump glossary index to glossary_index.json 2014-08-18 21:32:10 -07:00
Samy Pessé e1723ad3d5 Remove debug 2014-08-18 21:25:27 -07:00
Samy Pessé f77404afe1 Merge pull request #411 from GitbookIO/glossary-search
Glossary search
2014-08-18 19:14:14 -07:00
Aaron O'Mullan fbd7522748 Replace kramed-text-renderer references
with “kramed-text-renderer”
2014-08-18 18:36:38 -07:00
Aaron O'Mullan 90320fd26c Add glossary indexing 2014-08-18 17:25:26 -07:00
Aaron O'Mullan 4f9b474fdd Use kramed 0.4.1 2014-08-18 12:01:26 -07:00
Aaron O'Mullan 4c921bdc09 Use kramed-text-renderer (0.2.1) 2014-08-18 12:01:12 -07:00
Aaron O'Mullan 93e0864063 Fix dump typo in glossary's entryId func 2014-08-17 21:18:42 -07:00
Aaron O'Mullan 80e2f23714 Add "id" attribute to parsed glossary entries 2014-08-17 21:13:39 -07:00
Samy Pessé 00496dbd09 Add link to glossary if exists 2014-08-17 17:46:55 -07:00
Samy Pessé 54fb9a58d7 Add base less for glossary 2014-08-17 11:34:33 -07:00
Samy Pessé 6b0ad22b19 Base template for glossary 2014-08-17 11:26:20 -07:00
Samy Pessé 17f40540ae Parse glossary during build 2014-08-17 11:16:36 -07:00
Samy Pessé d337a500dc Merge pull request #408 from GitbookIO/feature-glossary
Feature glossary
2014-08-15 12:12:20 -07:00
Aaron O'Mullan cbe4e9edec Simplify output of glossary parsing to more simple objects 2014-08-15 12:09:36 -07:00
Samy Pessé 402e88f866 Add best practices for .gitignore 2014-08-15 11:55:01 -07:00
Aaron O'Mullan e0aed828e0 Add very basic glossary parsing test 2014-08-11 19:41:06 -07:00
Aaron O'Mullan 323facbdc8 Add glossary parser 2014-08-11 18:57:39 -07:00
Aaron O'Mullan 2a61c858e4 Add GLOSSARY.md fixture 2014-08-11 18:53:05 -07:00
Aaron O'Mullan d216661f27 Merge pull request #403 from GitbookIO/version/1.0.0
Version 1.0.0
2014-08-11 09:42:18 -07:00
Samy Pessé 50dfee99b6 Fix code coloration in book markdown 2014-08-11 09:37:25 -07:00
Samy Pessé 9e8ed9ac68 Use new markdown content css 2014-08-11 08:42:47 -07:00
Samy Pessé 3a54554713 Normalize markdown rendering with ebook (pdf, epub, mobi) 2014-08-11 08:38:11 -07:00
Samy Pessé fc113ce0c3 Fix style for table on night and sepia themes 2014-08-11 07:36:54 -07:00
Samy Pessé f605f0b6c8 Fix color or title for sepia and night 2014-08-11 07:36:54 -07:00
Samy Pessé 08788edfd3 Change list of plugins to a table 2014-08-11 07:36:54 -07:00
Samy Pessé 4a0e19935f Improve readme doc 2014-08-11 07:36:54 -07:00
Samy Pessé a3454bb7a8 Update preview 2014-08-11 07:36:53 -07:00
Samy Pessé 7245fa8ad7 Add icon for iOS 2014-08-11 07:36:53 -07:00
Samy Pessé d771ff341d Improve spacing between titles 2014-08-11 07:36:53 -07:00
Samy Pessé 8171241666 Remove quizzes handling from core 2014-08-11 07:36:53 -07:00
Samy Pessé 1125e89b49 Fix error with markdown rendering 2014-08-11 07:36:53 -07:00
Samy Pessé 67fd5a060b Add style for buttons 2014-08-11 07:36:52 -07:00
Samy Pessé 4f60eda3ee Add style for alerts
Remove style for exercises
2014-08-11 07:36:52 -07:00
Samy Pessé 6c62d75eae Remove exercises execution 2014-08-11 07:36:52 -07:00
Samy Pessé bcfcf3d979 Fix sharing to instapaper 2014-08-11 07:36:52 -07:00
Samy Pessé b4497029db Improve normalization of variables for dropdown 2014-08-11 07:36:52 -07:00
Samy Pessé 23ac4b846d Add sharing to instapaper 2014-08-11 07:36:51 -07:00
Samy Pessé 691d04e994 Improve dropdown to make it global
Add dropdown for sharing
2014-08-11 07:36:51 -07:00
Samy Pessé a54959ee4f Show title on hover of header 2014-08-11 07:36:51 -07:00
Samy Pessé 76ebca8c55 Fix url.js inclusion 2014-08-11 07:36:51 -07:00
Samy Pessé 786c4f147d Use fontawesome from bower 2014-08-11 07:36:51 -07:00
Samy Pessé 7028fc7118 Remove useless comments from less 2014-08-11 07:36:50 -07:00
Samy Pessé 1c3210e789 Fix display of input in search 2014-08-11 07:36:50 -07:00
Samy Pessé c28b804352 Remove bootstrap dependency 2014-08-11 07:36:50 -07:00
Samy Pessé 6d841c909b Remove appCache handling from client js 2014-08-11 07:36:50 -07:00
Samy Pessé e917fd28e0 Change term "Generated" to "Published" 2014-08-11 07:36:49 -07:00
Samy Pessé ab88459a56 Change default font 2014-08-11 07:36:49 -07:00
Samy Pessé fe95c361be Fix options parsing 2014-08-11 07:36:49 -07:00
Samy Pessé 7e78546516 Change links system for sidebar 2014-08-11 07:36:49 -07:00
Samy Pessé d07b9450b1 Fix defaults for configuration 2014-08-11 07:36:49 -07:00
Samy Pessé c30b431b7b Remove link home in header 2014-08-11 07:36:48 -07:00
Samy Pessé 0fe54fbd57 Fix navigation color for sepia and night 2014-08-11 07:36:48 -07:00
Samy Pessé 1bec19029f Fix navigation on mobile 2014-08-11 07:36:48 -07:00
Samy Pessé 21ccf7757e Improve padding on page content 2014-08-11 07:36:48 -07:00
Samy Pessé ceb766a124 Adapt to latest github markdown style 2014-08-11 07:36:48 -07:00
Samy Pessé 046b789fa8 Fix navigation links position 2014-08-11 07:36:47 -07:00
Samy Pessé cd50a1402f Fix border on search input (sepia and night) 2014-08-11 07:36:47 -07:00
Samy Pessé 1b0ae5a827 Fix border on dropdown selector 2014-08-11 07:36:47 -07:00
Samy Pessé 01b6ca76e8 Limit transitions to positions 2014-08-11 07:36:47 -07:00
Samy Pessé 06b9c345c9 Improve design of search bar 2014-08-11 07:36:47 -07:00
Samy Pessé 152276c1d7 Improve display of sidebar on mobile 2014-08-11 07:36:46 -07:00
Samy Pessé 275eedf076 Change button color on header 2014-08-11 07:36:46 -07:00
Samy Pessé 5e959d65a2 Start new design for website 2014-08-11 07:36:46 -07:00
Samy Pessé 66c1b89da2 Use only default config 2014-08-11 07:36:46 -07:00
Samy Pessé 78c68c7b9e Remove config for github urls 2014-08-11 07:36:46 -07:00
Samy Pessé d914eae40a Remove html5 cache manifest 2014-08-11 07:36:45 -07:00
Samy Pessé 74f95f4d0d Merge pull request #404 from Viv-Rajkumar/master
Fix path operator in Windows for index.html with multi lang
2014-08-11 07:31:10 -07:00
Viv-Rajkumar d803052461 Fix Langs path operator for Windows using 'links.js' 2014-08-11 12:09:07 +01:00
Aaron O'Mullan 633e52c4be Bump version to 0.7.1 2014-08-08 15:37:03 -07:00
Aaron O'Mullan 1512bcf808 Update fs-extra to 0.10.0
Fixes potential race condition on copies
2014-08-08 15:31:49 -07:00
Samy Pessé 4e46d088ca Add build for search results 2014-08-05 09:47:08 -07:00
Samy Pessé 388c7ffd32 Merge pull request #396 from codepiano/hold-search-keyword
keep the search result list after user click a link
2014-08-05 09:44:54 -07:00
codepiano 7ba8653935 keep the search result list after user click a link
Record search keyword to LocalStorage, when navigate to a new url,fetch
keyword from LocalStorage, init the search input element,then filter the
sidebar menu.
So User can iterator the result list without type the keyword every
time.
2014-08-05 15:14:10 +08:00
Samy Pessé a4403aad1e Add changes for 0.7.0 2014-08-01 14:01:20 -07:00
Samy Pessé b629b78dba Bump version to 0.7.0 2014-08-01 13:58:36 -07:00
Samy Pessé f62c218735 Fix #278: Add page break between chapters/articles 2014-08-01 13:55:19 -07:00
Aaron O'Mullan 725510028f Use marked-text-renderer@0.1.0 2014-08-01 00:09:45 -07:00
Aaron O'Mullan ad7927242d Add comments to default introduction chapter code
Fixes #388
2014-07-31 08:43:57 -07:00
Aaron O'Mullan 5692bdd229 Merge pull request #386 from codepiano/china-social-network-sharing
add sharing to weibo.com.
2014-07-31 01:41:56 -07:00
codepiano 3402746719 add sharing to weibo.com.
google、facebook and twitter are block by GFW in China,Chinese can
not visit these websites.
add 'weibo' config in option links.sharing,the share link will not be
used by default,users should set the value of 'weibo' to true to turn
on sharing to weibo.com.
2014-07-31 16:31:31 +08:00
Aaron O'Mullan 30a8414b06 Switch to graceful-fs, fix #373 2014-07-30 20:45:33 -07:00
Aaron O'Mullan 9d46899ea7 Replace "marked" with "kramed" 2014-07-30 16:17:47 -07:00
Samy Pessé 58f14fd9ca Fix font settings apply 2014-07-30 11:17:28 -07:00
Samy Pessé b6af14b13e Remove github count stars and watchers 2014-07-30 10:43:10 -07:00
Aaron O'Mullan ba2134c480 Bump version to 0.6.3 2014-07-30 10:23:11 -07:00
Aaron O'Mullan 4705548860 Fix resource normalization in when loading plugins
Fixes #384
2014-07-30 10:17:07 -07:00
Samy Pessé 7d51ae3689 Add changelogs for 0.6.2 2014-07-30 00:26:44 -07:00
Aaron O'Mullan b9360de3e3 Bump version to 0.6.2 2014-07-30 00:22:49 -07:00
Aaron O'Mullan 9f241457bb Fix minor issues with new plugin additions 2014-07-29 23:45:54 -07:00
Aaron O'Mullan 60a7c28e66 Support generating a plugin's book info dynamically 2014-07-29 21:50:26 -07:00
Samy Pessé 5dff9b795e Fix #345: Improve navigation on dark theme 2014-07-29 21:33:47 -07:00
Aaron O'Mullan 0134677e0e Improve path normalization when parsing SUMMARY.md 2014-07-29 20:49:11 -07:00
Aaron O'Mullan 39ef4fa199 Add invalid paths in SUMMARY.md fixture
To test for correct path normalization
2014-07-29 20:46:14 -07:00
Samy Pessé b507a30301 Bump version to 0.6.1 2014-07-23 16:49:57 -07:00
Samy Pessé 539c2d0c57 Update plugin-mathjax@0.0.4 2014-07-23 16:28:38 -07:00
Samy Pessé aa6f3530f9 Fix readme spacing 2014-07-22 23:28:13 -07:00
Samy Pessé 98057af219 Merge pull request #372 from justanotheranonymoususer/patch-1
Update summary.html (fixed an HTML typo)
2014-07-20 14:04:12 -07:00
justanotheranonymoususer bb48c8233c Update summary.html 2014-07-20 23:57:56 +03:00
Aaron O'Mullan 8f14db64df Merge pull request #370 from nightwing/patch-1
Add keyboard shortcut for submitting exercise
2014-07-15 21:33:58 -07:00
Harutyun Amirjanyan 37d40dc66c Add keyboard shortcut for submitting exercise 2014-07-16 08:07:22 +04:00
Samy Pessé 6c96680cca Merge pull request #366 from shaform/ignore
Ignore config files by default in fs.list
2014-07-14 10:08:29 -07:00
Shaform 4e475857d5 Ignore config files by default in fs.list 2014-07-13 08:40:23 -07:00
Samy Pessé f8ff0453dd Merge pull request #359 from jcouyang/patch-1
add jsbin embedded plugin
2014-07-12 07:02:18 -07:00
Samy Pessé 3cc5c47535 Bump version to 0.6.0 2014-07-12 23:28:07 +02:00
Samy Pessé a2046814b4 Change command "git:push" to "publish" 2014-07-12 23:25:40 +02:00
Samy Pessé 82f814fdbd Fix #303: generate header id the same as github 2014-07-12 23:23:03 +02:00
Jichao Ouyang cd382dc8fc add jsbin embedded support 2014-07-12 21:42:52 +08:00
Samy Pessé 3842e5bf4a Merge pull request #358 from GitbookIO/featureplatform
Add base command git:push and git:remote
2014-07-11 17:42:16 -07:00
Samy Pessé f05b138fd6 Add base command git:push and git:remote 2014-07-12 09:23:15 +02:00
Samy Pessé 1b87cec893 Merge pull request #266 from shaform/ref_links
Add reference links support
2014-07-11 17:20:43 -07:00
Samy Pessé 4a894b1e18 Merge pull request #268 from rlmv/master
Fix page.path for plugins
2014-07-11 17:19:26 -07:00
Samy Pessé c7c98009e7 Merge pull request #347 from rlmv/custom_links
Add custom links to TOC
2014-07-11 17:15:37 -07:00
Samy Pessé 356322e0ed Merge pull request #353 from citizenmatt/active_page
Highlight active page in summary
2014-07-11 17:14:36 -07:00
Samy Pessé 7a47049367 Merge pull request #354 from citizenmatt/summary_hooks
Plugin hooks for summary data
2014-07-11 17:13:40 -07:00
Samy Pessé a9f473260c Merge pull request #356 from GitbookIO/feature/includes
Support importing code snippets
2014-07-10 17:59:34 -07:00
Aaron O'Mullan d16577e8bf Add tests for includes 2014-07-10 17:49:50 +02:00
Aaron O'Mullan 15b4053d6b Support code includes for exercises 2014-07-10 17:30:12 +02:00
Aaron O'Mullan 925a9b1388 Support importing code snippets
Allowing to keep code in separate files, which can be cleaner in a
quite a few scenarios
2014-07-11 01:22:07 +02:00
Matt Ellis fc942fdeca Hooks for summary data 2014-07-09 17:17:40 +01:00
Matt Ellis 75a6ecbf52 Highlight active page in summary 2014-07-09 16:06:55 +01:00
Aaron O'Mullan 520045441b Merge pull request #352 from citizenmatt/links_with_fragments
Convert links with fragments from md to html
2014-07-08 21:45:01 +02:00
Matt Ellis 00c3e966d3 Convert links with fragments from md to html 2014-07-08 16:25:39 +01:00
Samy Pessé 8b6ebe86b8 Merge pull request #348 from rlmv/anchors_readme
Add anchor plugin to README
2014-07-06 17:12:57 +02:00
Bo Marchman d5bd3453e1 add heading anchors plugin to readme 2014-07-02 23:49:07 -04:00
Bo Marchman ddacbbe73b update readme with custom links 2014-07-02 23:40:57 -04:00
Bo Marchman f7d52f0eff Add custom link capability
Users can add custom links at the top of the table of contents by
adding a links.custom object to book.json. Issue #304
2014-07-02 23:38:14 -04:00
Robert Marchman 39c50640ea pass 'rawPath' page path to plugins 2014-06-24 12:33:19 -04:00
Samy Pessé 16128c7e60 Bump version to 0.5.6 2014-06-24 12:35:13 +02:00
Samy Pessé 8b6a80cffb Fix error when chapters list is empty
Fix GitbookIO/gitbook.io#101 and GitbookIO/gitbook.io#102
2014-06-24 12:34:29 +02:00
Aaron O'Mullan 86f8a9d8fb Remove useless (and bad) sort on progress
Fixes bad ordering of chapters on gitbook.io
2014-06-20 03:40:47 -07:00
Aaron O'Mullan 790dfd601e Add csharp language highlight mapping
Fixes #327
2014-06-18 11:26:50 -07:00
Aaron O'Mullan 3426428142 Bump version to 0.5.5 2014-06-18 00:25:23 -07:00
Aaron O'Mullan 6c5c8e6eb7 Fix LANGS.md parsing
And cleanup Summary parsing
2014-06-17 16:18:08 -07:00
Aaron O'Mullan 3aefcaea73 Bump version to 0.5.4 2014-06-17 10:56:45 -07:00
Aaron O'Mullan 04c025774d Improve robustness of link utility functions
Fixes #321, fixes #323
2014-06-17 10:56:13 -07:00
Aaron O'Mullan 80aac7f581 Support --verbose flag, fixes #325 2014-06-17 10:46:56 -07:00
Samy Pessé 266f7ca73d Add infos about debugging 2014-06-17 18:45:44 +02:00
Shaform 6bbefad24e Merge remote-tracking branch 'upstream/master' into ref_links 2014-06-17 22:08:46 +08:00
Aaron O'Mullan d6eb8e4c60 Support URLs in "github" config
Fixes GitbookIO/gitbook.io#90
2014-06-16 18:52:36 -07:00
Aaron O'Mullan c87edb155a Better string slicing in parse.git.githubID() 2014-06-16 17:48:18 -07:00
Samy Pessé ef0468ad5d Fix #310: improve display of long title in header 2014-06-16 14:03:28 +02:00
Samy Pessé f03153eb0c Remove page break between articles in ebook
Add title for exercise solutions sections
2014-06-16 13:55:07 +02:00
Samy Pessé 3d8b6e550a Merge pull request #319 from GitbookIO/enchancement/custom_intro
Enchancement/custom intro
2014-06-16 13:02:38 +02:00
Aaron O'Mullan 4c3dbca5ca Normalize intro node in Summary parsing 2014-06-16 00:26:11 -07:00
Aaron O'Mullan a258feea37 Remove special case in navigation
For intro node
2014-06-16 00:25:59 -07:00
Aaron O'Mullan 3a3332d0b9 Adapt summary tests to new summary parsing
We now insert default README.md (introduction) node in parsing the
SUMMARY.md logic
2014-06-16 00:25:33 -07:00
Aaron O'Mullan 4259283f32 Refactor Summary rendering
Introduction is no longer an edge case
2014-06-16 00:09:48 -07:00
Aaron O'Mullan 243d977cd6 Add test for level regarding #318 2014-06-15 23:36:40 -07:00
Aaron O'Mullan 94bbbecedf Support custom intro node, fixes #318 2014-06-15 23:30:43 -07:00
Aaron O'Mullan f49eab42a9 Fix test for #318 2014-06-15 23:29:24 -07:00
Aaron O'Mullan ce6b2bc47d Add extra navigation tests for #318 2014-06-15 23:19:28 -07:00
Aaron O'Mullan 3245d7e430 Drop Travis tests for Node 0.8 2014-06-15 22:49:15 -07:00
Aaron O'Mullan 6ee17c32ae Bump version to 0.5.3 2014-06-15 22:39:18 -07:00
Aaron O'Mullan bf7ffe6d4c Skip extra files and folders in copy phase of generation 2014-06-15 22:37:15 -07:00
Aaron O'Mullan ec7bc6b347 Add link to gitbook.io in readme 2014-06-15 22:36:51 -07:00
Aaron O'Mullan b425db0979 Allow external links in summary, fixes #300 2014-06-08 12:31:52 -07:00
Samy Pessé 30c6d1c9de Remove useless log 2014-06-08 02:21:05 +02:00
Samy Pessé cee1d90fc6 Bump version to 0.5.2 2014-06-08 01:09:51 +02:00
Samy Pessé 522342667c Fix font size for mono font on pdf output 2014-06-08 00:27:18 +02:00
Samy Pessé d1c1277bb9 Fix cover picture for multilangs book convertion to ebook 2014-06-08 00:11:50 +02:00
Aaron O'Mullan 9a66f56c79 Bump version to 0.5.1 2014-06-07 11:42:37 -07:00
Samy Pessé 087ca28ca2 Fix style for main languages index page 2014-06-07 16:30:11 +02:00
Samy Pessé 3d5281ff3a Fix GitbookIO/gitbook.io#69: copy cover files at multi-langs root 2014-06-07 16:27:40 +02:00
Samy Pessé f62cf340c5 Fix title and description for pages in website 2014-06-07 16:04:03 +02:00
Samy Pessé 7e12bd3588 Don't use cdn for ace 2014-06-07 16:02:02 +02:00
Samy Pessé 4dbdb95ebe Show quiz inside ebook 2014-06-06 22:20:02 +02:00
Samy Pessé 2c5ddbcd8f Use base style for ebook instead of bootstrap 2014-06-06 21:47:59 +02:00
Samy Pessé be4fd74f90 Improve navigation links design 2014-06-06 20:06:03 +02:00
Samy Pessé 57491e3a8b Improve meta tags for books 2014-06-06 20:00:38 +02:00
Samy Pessé f7a442d297 Improve font sizes and spacing on content 2014-06-06 19:33:41 +02:00
Samy Pessé cfebdc6f6b Fix #296: fix static links on windows 2014-06-06 18:37:35 +02:00
Samy Pessé 65319e8fd6 Bump version to 0.5.0 2014-06-03 22:30:42 +02:00
Samy Pessé 55db44a3b4 Fix #289: center images on ebook 2014-06-03 22:20:51 +02:00
Samy Pessé 13b6045300 Font Merriweather 600 doesn't exists 2014-06-03 20:45:55 +02:00
Samy Pessé b7ff879c31 Remove fonts Anonymous Pro and EB Garamond 2014-06-03 20:43:47 +02:00
Samy Pessé 57ade87fe1 Fix #275: fix emphasized font path 2014-06-03 20:30:04 +02:00
Samy Pessé 907e9ea5a7 Improve font on title
Set sans-serif as default
2014-06-03 20:14:44 +02:00
Samy Pessé 215488dc45 Improve design (more flat) 2014-06-03 18:43:13 +02:00
Samy Pessé a195b3eac2 Improve less files organisation 2014-06-03 18:11:03 +02:00
Samy Pessé 77c6f0e967 Merge pull request #291 from bebraw/book.js
Allow book.js to be used in addition to book.json
2014-06-03 17:20:26 +02:00
Juho Vepsalainen f0e18afec1 In case there's no config file, do not fail 2014-06-03 17:59:26 +03:00
Juho Vepsalainen b6d11d88b7 Allow book.js to be used in addition to book.json
Now it's possible to comment configuration properly and do some dynamic
things if needed.

Closes #287.
2014-06-03 17:42:30 +03:00
Samy Pessé c803270af8 Add option pdf.pageNumber
Fix GitbookIO/editor#38
2014-06-03 16:24:35 +02:00
Samy Pessé 5d51263a58 Remove mixpanel as a default addon 2014-06-03 15:29:15 +02:00
Samy Pessé 7e30720b2c Merge pull request #292 from bebraw/typofix
Fix build success message typo
2014-06-03 15:07:29 +02:00
Juho Vepsalainen 771c5e8b68 Fix build success message typo 2014-06-03 15:58:22 +03:00
Samy Pessé 8f53afab6b Fix #286: fix doc about formats 2014-06-02 20:12:56 +02:00
Samy Pessé 0ad0403d41 Merge pull request #283 from GitbookIO/feature/pdfoptions
Add more options for PDF
2014-06-02 18:22:07 +02:00
Samy Pessé 2515c1dd73 Remove useless command "gitbook ebook" 2014-06-02 18:17:02 +02:00
Samy Pessé 7dfcd102dd Fix calcul of output format for ebook 2014-06-02 18:16:44 +02:00
Samy Pessé a91245d575 Add options paperSize and margin for pdf 2014-06-02 17:57:47 +02:00
Samy Pessé 5f207f5e14 Add options pdf.toc and pdf.fontSize 2014-06-02 17:49:05 +02:00
Samy Pessé 718692de3b Langs index page now use direct link to html file 2014-06-02 14:49:40 +02:00
Samy Pessé 9856a51255 Fix #282: stop generation of langs index for page format 2014-06-02 14:13:52 +02:00
Samy Pessé 58eaaf5514 Fix error when plugin don't extend book 2014-06-02 12:51:22 +02:00
Samy Pessé c0ae0138f8 Improve documentation about book.json 2014-06-02 11:41:16 +02:00
Samy Pessé b1b9dc5b0a Fix error during generation 2014-06-02 11:37:25 +02:00
Samy Pessé f85c5a6498 Add error message when generator is invalid
Fixes #276, #277
2014-06-02 11:32:09 +02:00
Aaron O'Mullan 32eafefc45 Bump version to 0.4.8 2014-05-29 16:36:56 -07:00
Aaron O'Mullan 336999a34e Set default pdf font size to 12 2014-05-29 15:57:56 -07:00
Samy Pessé 1bf5f3b984 Force paper format to A4 fro pdf 2014-05-30 00:33:35 +02:00
Samy Pessé c76d070237 Merge pull request #274 from emmanuel-keller/patch-1
Adding a new plugin (Piwik Open Analytics plugin)
2014-05-30 00:11:38 +02:00
Emmanuel Keller 8c7da1351f Adding a new plugin.
I Just published a first version of the Piwik Open Analytics plugin, on both GIT and NPM.
2014-05-29 23:31:38 +02:00
Samy Pessé 34588c6092 Add margin on pdf output 2014-05-29 18:53:08 +02:00
Samy Pessé 9f00bf8060 Add toc at the end of pdf 2014-05-29 18:33:05 +02:00
Samy Pessé f4a17b182f Improve ebook generation (toc, ...) 2014-05-29 18:28:09 +02:00
Samy Pessé 5c3b345fa6 Generate valid first level table of content for ebook 2014-05-29 17:28:18 +02:00
Samy Pessé 46321fffd9 Use ebook-convert for pdf generation 2014-05-29 17:07:03 +02:00
Shaform 1f4cf33dd0 Improve code syntax for links assignment 2014-05-28 06:57:50 +08:00
Shaform 7274aa6940 fix links error 2014-05-27 22:24:23 +08:00
Shaform 125e804cd5 Add reference links support
This commit resolves #184
2014-05-27 17:24:47 +08:00
Aaron O'Mullan 89ba17de84 Bump version to 0.4.7 2014-05-26 12:14:32 -07:00
Aaron O'Mullan 115fba837c Merge pull request #265 from GitbookIO/feature/levels
More than two section level in summary
2014-05-26 11:27:27 -07:00
Samy Pessé 4df51546b2 Remove command log in ebook convert 2014-05-26 18:02:32 +02:00
Samy Pessé f68aaf7ff9 Fix cover option for ebook 2014-05-26 18:02:09 +02:00
Samy Pessé e3d1746ba3 Fix use of cover.jpg in ebook 2014-05-26 17:58:08 +02:00
Samy Pessé a0d5646034 Adapt navigation parsing to third and more levels in summary 2014-05-26 17:42:05 +02:00
Samy Pessé 2f8af5f136 Improve design of summary for deep articles 2014-05-26 17:33:10 +02:00
Samy Pessé 8b7abbd6f4 Render articles of level > 2 in summary 2014-05-26 17:31:35 +02:00
Samy Pessé f228f646eb Remove useless method parseArticle 2014-05-26 17:23:19 +02:00
Samy Pessé 45891d80c7 Parse more than two level in summary 2014-05-26 17:21:23 +02:00
Samy Pessé ac900aa37f Merge pull request #263 from jpoesen/patch-1
Minor typo, grammar and style corrections.
2014-05-26 10:44:25 +02:00
Joeri Poesen 422cbed796 Minor typo, grammar and style corrections. 2014-05-26 08:13:35 +02:00
Samy Pessé 38cbbe46aa Fix cache manifest generation 2014-05-25 20:47:57 +02:00
Aaron O'Mullan c1b57a387b Bump version to 0.4.6 2014-05-23 16:35:44 -07:00
Samy Pessé aaa9ae8bb7 Fix loading button display on navigation 2014-05-24 01:32:39 +02:00
Samy Pessé 6340a64cff Fix #235: search after navigation to another page 2014-05-23 22:28:36 +02:00
Samy Pessé 96bcfa4e20 Fix #218: keep state of font settings 2014-05-23 22:20:36 +02:00
Samy Pessé af0f56b26b Change position of menu action in header 2014-05-23 21:49:11 +02:00
Samy Pessé aed5befa48 Fix display of divider in lateral menu 2014-05-23 21:48:04 +02:00
Samy Pessé 8e37da672d Minor css improvements 2014-05-23 21:43:47 +02:00
Samy Pessé c0f68c45e2 Fix #249: fix links.toAbsolute on windows 2014-05-23 21:10:58 +02:00
Samy Pessé 47fdd73791 Fix cache manifest in the html tag
Serve command disable cache
2014-05-23 17:45:46 +02:00
Samy Pessé 7c0b88cf2e Plugins can extend html 2014-05-23 17:00:37 +02:00
Samy Pessé bf21633b6c Support multi-languages book in json format 2014-05-23 16:35:34 +02:00
Samy Pessé ad9b1fd6f7 Fix order of progress for level superior to x.9
Related to GitbookIO/gitbook.io#23
2014-05-21 23:31:57 +02:00
Samy Pessé d245895085 Add link to documentation 2014-05-21 20:22:28 +02:00
Samy Pessé c7439d12f7 Add livereload as default plugins only for serve 2014-05-21 17:34:36 +02:00
Samy Pessé 8d6923ce2c Merge pull request #234 from davidagraf/livereload
Livereload
2014-05-21 17:17:08 +02:00
David Graf b21fb24a82 gitbook-plugin-livereload dependency to npm 2014-05-21 16:35:55 +02:00
David Graf e7be132194 Merge branch 'master' into livereload 2014-05-21 16:24:07 +02:00
Samy Pessé 0e1717f4bb Improve grammar (thanks to @theoutlander ) 2014-05-21 10:46:26 +02:00
Aaron O'Mullan 276dbe29c7 Bump version to 0.4.5 2014-05-20 23:29:39 -07:00
Aaron O'Mullan 21fd5924b8 Fix generate failing on README parsing
Failed if no title or description could be extracted
2014-05-20 23:29:06 -07:00
Aaron O'Mullan 443def6cfd Bump version to 0.4.4 2014-05-20 22:59:26 -07:00
Samy Pessé 75464574d7 For multi-langs, run build one by one 2014-05-21 00:09:40 +02:00
Samy Pessé 86f05c68c5 Improve parsing of readme (extraction of title and description as plain text and not markdown) 2014-05-20 23:45:35 +02:00
Samy Pessé 8dd407eaa8 Fix merge of output for multi-languages books 2014-05-20 23:31:22 +02:00
Samy Pessé ea7ad10e8a Add support for multi-languages books in JSON format 2014-05-20 23:21:12 +02:00
Samy Pessé 62ec695dc8 Extract title and description from readme before switching to multi-languages handling 2014-05-20 23:07:41 +02:00
Samy Pessé ca37ac73a9 Add more infos about gitbook.io 2014-05-20 14:40:22 +02:00
Samy Pessé 96edd50c8f Add norm for small cover 2014-05-20 01:23:49 +02:00
Samy Pessé 3993c1e35a Change advised ratio for cover 2014-05-19 21:47:41 +02:00
Aaron O'Mullan 5298558925 Bump version to 0.4.3 2014-05-18 02:25:53 -07:00
Samy Pessé c6ba147917 Print stack only when env DEBUG is set 2014-05-17 20:35:41 +02:00
Samy Pessé 7efecea3d6 Exit process with -1 when error occurs 2014-05-17 20:35:13 +02:00
Aaron O'Mullan a73fed3ebd Merge pull request #240 from ajschumacher/patch-1
typo: "blockin" to "block in"
2014-05-16 09:06:15 -07:00
Aaron Schumacher 50ef7bcd23 typo: "blockin" to "block in" 2014-05-16 12:02:06 -04:00
Aaron O'Mullan 82e422cb89 Fix issue of disabling issues and contribute
Improve robustness of template display conditions
2014-05-15 21:35:09 -07:00
Aaron O'Mullan dab46c779d Allow github support to be disabled in book.json
Fixes #238
2014-05-15 21:24:28 -07:00
David Graf 7c65adc9b2 disable cache manifest import if cache is disabled 2014-05-15 12:07:51 +02:00
David Graf 9b96ef9bae added livereload dependency 2014-05-15 11:41:59 +02:00
David Graf e97d1941dc Introducing livereload server 2014-05-15 08:09:02 +02:00
David Graf 0b0ac46e34 Introducing option to disable cache manifest generation 2014-05-15 08:07:24 +02:00
David Graf 622bbcb8ea ignore vim swap files 2014-05-15 07:43:59 +02:00
Samy Pessé 58a5245d21 Merge pull request #233 from embreinhardt/master
Add LESS variable for sidebar full-width breakpoint
2014-05-14 20:10:02 +02:00
Erika Reinhardt 612f9a6dc7 Add LESS variable for sidebar full-width breakpoint 2014-05-14 11:01:57 -07:00
Samy Pessé 90d4dff263 Merge pull request #232 from embreinhardt/master
Make font-settings.html indentation consistent
2014-05-14 10:35:32 +02:00
Erika Reinhardt 0f38385c7d Make font-settings.html indentation consistent
[Minor] Was mix of tabs and spaces, which messed with text edit display
2014-05-14 01:25:18 -07:00
Samy Pessé 2b50616251 Merge pull request #231 from embreinhardt/master
Add HTML classes to make plugins easier to write
2014-05-14 10:20:42 +02:00
Erika Reinhardt 5b7f37f0d4 Add HTML classes to make plugins easier to write 2014-05-14 01:14:21 -07:00
Aaron O'Mullan 6ca8e26741 Merge pull request #229 from weitzman/patch-1
Fix recent typo in README.MD
2014-05-12 14:04:52 -07:00
Moshe Weitzman a3ada05598 Fix recent typo in README.MD 2014-05-12 16:18:50 -04:00
Samy Pessé 42eb11aaff Add infos about cover 2014-05-11 16:47:30 +02:00
Samy Pessé b581859afd Merge pull request #223 from cef62/fix-windows-paths
Force forward slashes on SUMMARY.md path for articles
2014-05-08 22:54:40 +02:00
Matteo Ronchi 2e22fae52a Force forward slashes on SUMMARY.md paths
this prevent windows user to generate invalid path on unix OS
Reference to issue #39 on gitbook-editor
2014-05-08 22:46:53 +02:00
Aaron O'Mullan 603ac8b91d Merge pull request #221 from frodsan/patch-1
Don't show divider even if githubId is present.
2014-05-08 09:58:06 -07:00
Francesco Rodríguez 5faa4841ca Don't show divider even if githubId is present. 2014-05-08 13:40:15 -03:00
Samy Pessé 7416b826e0 Fix #218: Add settings for client theme (font family, size, theme) 2014-05-08 13:16:30 +02:00
Aaron O'Mullan 82afc34755 Merge pull request #219 from BjoernSchotte/feature/doc-theme
added documentation for theme option
2014-05-07 23:08:53 -07:00
Björn Schotte da39631a5a added documentation for theme option 2014-05-08 07:57:33 +02:00
Aaron O'Mullan fc8685a7c1 Merge pull request #216 from ianmcburnie/accessibility
Accessibility: added aria-labels to header
2014-05-06 12:34:43 -07:00
Ian McBurnie 5e0204a980 Accessibility: added aria-labels to header
Feel free to change any of the label text that I have chosen :-)
2014-05-06 11:27:54 -07:00
Samy Pessé 6bb0f33f39 Fix #211: read config file before cleaning output folder 2014-05-06 12:08:27 +02:00
Aaron O'Mullan ed4fd565b2 Merge pull request #213 from ianmcburnie/accessibility
Accessibility: aria-label added to navigation links
2014-05-05 22:37:21 -07:00
Ian McBurnie 79cff28abd Accessibility: aria-label added to navigation links
Current behavior: Screenreader just reads ‘link’ when landing on the
previous and next navigation links, giving the user no indication of
what the link does or where it goes. New Behaviour: Screenreader reads
“Previous page: {page title}” and “Next page: {page title}”
respectively.
2014-05-05 22:29:22 -07:00
Samy Pessé 27329239f7 Fix #208: correct name for edit/contribute link in doc 2014-05-05 17:55:19 +02:00
Samy Pessé 9de86fc0e4 Add tests for book generation 2014-05-05 15:25:15 +02:00
Samy Pessé 7fa7826372 Merge pull request #203 from GitbookIO/improve/generate
Improve generate's reliability, fixes #202
2014-05-04 22:37:42 +02:00
Aaron O'Mullan 96ecfd098b Improve generate's reliability, fixes #202
This also improves speed on multi language books
2014-05-04 12:53:24 -07:00
Samy Pessé f7fe976c7e Fix #197: use URI.js for calculation of relative url
Use bower for client dependencies
2014-05-04 15:15:18 +02:00
Samy Pessé 756629b91d Add bower dependencies 2014-05-04 15:03:47 +02:00
Samy Pessé e640d124d4 Add bower vendors to gitignore 2014-05-04 14:58:45 +02:00
Samy Pessé 7f41466297 Remove vendors directory 2014-05-04 14:58:01 +02:00
Samy Pessé 57dfc062d2 Create CHANGES.md 2014-05-04 13:09:27 +02:00
Aaron O'Mullan 799815d85a Bump version to 0.4.2 2014-05-01 16:17:05 -07:00
Samy Pessé 2946246834 Fix #191: remove log for changes in files 2014-05-01 18:55:45 +02:00
Samy Pessé 6d276d1e79 Add option to remove home link 2014-05-01 18:53:49 +02:00
Samy Pessé 6a3a369b3e Add options to remove sharing links 2014-05-01 18:37:30 +02:00
Samy Pessé 5d8afa2135 Merge pull request #196 from mrpotes/new-plugins
New plugins
2014-05-01 18:33:30 +02:00
Samy Pessé ab3e6aa8ce Fix font-size for titles in markdown content 2014-05-01 18:29:42 +02:00
James Phillpotts 822092736e New plugins 2014-05-01 17:00:38 +01:00
Samy Pessé 7497acd2e3 Center navigation button on mobile on start page 2014-05-01 17:37:38 +02:00
Samy Pessé ae137885b8 Fix book content padding on mobile 2014-05-01 17:27:03 +02:00
Samy Pessé b964cc7009 Fix #162: hide watch/star buttons on mobile 2014-05-01 17:16:09 +02:00
Samy Pessé c2ea126c5f Fix #182: Replace all book body when page changes 2014-05-01 14:05:14 +02:00
Samy Pessé b668dba479 Update gaze version to 0.5.1 2014-05-01 13:26:50 +02:00
Samy Pessé 2cae5a0310 Secure .book position 2014-05-01 13:26:19 +02:00
Samy Pessé b6a0f3ff67 Fix #164: use position absolute instead of fixed 2014-04-30 17:01:25 +02:00
Samy Pessé fa00b52fb5 Fix #127: add configuration for changing links in sidebar 2014-04-30 16:46:24 +02:00
Samy Pessé 2999d90907 Merge pull request #188 from snipe/feature/json-doc
Added quotes for JSON example, per #127
2014-04-30 12:48:28 +02:00
snipe b6696bcad2 Added quotes for JSON example, per #127 2014-04-30 06:46:28 -04:00
Samy Pessé 728c87c525 Merge pull request #187 from snipe/feature/typo-fixes
Fixed typos
2014-04-30 12:05:12 +02:00
snipe ace394ee2d Fixed typos 2014-04-30 05:28:18 -04:00
Samy Pessé 076ee98f64 Fix #186: Plugins that don't have assets should be skipped 2014-04-30 11:15:01 +02:00
Samy Pessé 2e472654ca Fix error when calling hooks in site generation 2014-04-30 10:54:45 +02:00
Aaron O'Mullan d46bd14678 0.4.1 2014-04-29 10:23:54 -07:00
Samy Pessé b3e5b3d106 Fix #183: normalize hooks and transformation of page (content, progress, ..° 2014-04-29 17:45:54 +02:00
Samy Pessé b830ef5dca Merge pull request #179 from erixtekila/master
Update README.md
2014-04-29 08:23:33 +02:00
Eric Priou 45326c4ad2 Update README.md 2014-04-29 03:45:03 +02:00
Aaron O'Mullan 603b61e8c8 Bump version to 0.4.0 2014-04-28 13:26:40 -07:00
Samy Pessé efb066c8e3 Update doc about how to configure gitbook 2014-04-28 20:45:10 +02:00
Samy Pessé c30db427b1 Remove some options from command line (moved to book.json) 2014-04-28 20:33:25 +02:00
Samy Pessé 23056fc85d Use a global configuration file for a book (default to book.json) 2014-04-28 20:29:36 +02:00
Samy Pessé 5f1bfc0280 Fix #163: Move logic of git infos extraction to lib 2014-04-28 20:16:30 +02:00
Samy Pessé b715901f92 Add hooks "page:before" and "page:after" 2014-04-28 19:56:40 +02:00
Samy Pessé 38fea06cf9 Use hook to allow plugins to edit page content during generation (Fixes #176) 2014-04-28 17:21:06 +02:00
Aaron O'Mullan 4481b5f556 Bump version to 0.3.2 2014-04-27 13:46:20 -07:00
Samy Pessé 0e0d051f7c Merge pull request #173 from erixtekila/master
Update README.md
2014-04-27 22:26:56 +02:00
Eric Priou a1b649a279 Update README.md 2014-04-27 22:24:37 +02:00
Eric Priou e6bc0a0fb7 Update README.md
3rd party plugins done in the weekend
2014-04-27 22:18:09 +02:00
Samy Pessé 93e4016c09 Add link to disqus plugin 2014-04-27 21:55:50 +02:00
Samy Pessé 20c741a248 Fix stupid typo thanks to @erixtekila 2014-04-27 21:21:48 +02:00
Samy Pessé 54f530c7c3 Merge pull request #171 from GitbookIO/feature/offlineCache
Improve pages loading
2014-04-27 18:05:13 +02:00
Samy Pessé 9fdc5da22d Update page when new version of application cache 2014-04-27 17:57:34 +02:00
Samy Pessé 99ce48b146 Add base loading indicator for navigation
#161
2014-04-27 17:36:12 +02:00
Samy Pessé 6a33ad9ef7 Add base cache manifest 2014-04-27 17:14:57 +02:00
Samy Pessé ca94055adf Adapt tests for plugins 2014-04-27 15:45:16 +02:00
Samy Pessé 36a67e4c91 Fix #169: fix resolve of external plugins 2014-04-27 15:41:02 +02:00
Samy Pessé 69431e1bbb Fix #165: replace separator from ":" to "," 2014-04-27 09:14:39 +02:00
Samy Pessé a3fc4ee2b0 Fix display of code element in sepia/night 2014-04-25 18:14:41 +02:00
Samy Pessé e505fd6d54 Improve summary divider for theme sepia and night 2014-04-25 18:12:39 +02:00
Samy Pessé 93a3785e4c Improve highlight theme for sepia and night themes 2014-04-25 18:05:09 +02:00
Samy Pessé a02de84812 Change highlight theme to tomorrow 2014-04-25 17:56:48 +02:00
Samy Pessé c0521f0ec7 Improve display of pre in markdown content 2014-04-25 17:48:31 +02:00
Samy Pessé 83d8c16853 Add file watcher in "gitbook serve" command (Fix #47, #89, #140) 2014-04-25 16:23:13 +02:00
Samy Pessé 47e9506397 Fix #153 and Improve pushState navigation 2014-04-25 15:12:19 +02:00
Samy Pessé b8a3d0e254 Merge pull request #144 from attomos/master
Make quiz easier to click by surround it with label.
2014-04-25 14:29:04 +02:00
Samy Pessé 001d7ec44e Merge pull request #155 from pborreli/patch-1
Fixed typo
2014-04-24 22:44:36 +02:00
Pascal Borreli 5cb0ec447a Fixed typo 2014-04-24 21:43:32 +01:00
Aaron O'Mullan 67e7d77302 Bump version to 0.3.1 2014-04-24 10:45:55 -07:00
Aaron O'Mullan 8cbd8b2b48 Reorder plugin names
Improves #149 and improves GitBookIO/editor#12
2014-04-24 01:38:42 -07:00
Aaron O'Mullan 7c404dbf96 Fix plugin tests 2014-04-24 01:34:35 -07:00
Aaron O'Mullan c4a67b6f2f Require plugin names to contain gitbook-*
Fixes #149, fixes GitBookIO/editor#12
2014-04-24 01:28:04 -07:00
Samy Pessé 3b70347625 Fix #146: complete license 2014-04-24 09:00:15 +02:00
Aaron O'Mullan bb0a7935e9 Merge pull request #141 from frodsan/niceties
Niceties
2014-04-23 11:34:08 -07:00
Nattaphoom Ch 8f4b799d3c Make quiz easier to click by surround it with label. 2014-04-24 01:19:13 +07:00
Francesco Rodriguez 931e0c66c9 Update color themes for highlight.js link. 2014-04-23 12:30:05 -03:00
Francesco Rodriguez 0212084862 Fix typo. 2014-04-23 12:28:44 -03:00
Samy Pessé 9abbabbce3 Merge pull request #138 from Hermanya/patch-1
Add @Hermanya as a contributor
2014-04-22 23:47:13 +02:00
Hermanya ac610487e8 Add @Hermanya as a contributor 2014-04-22 17:45:41 -04:00
Aaron O'Mullan a9a0f89892 Bump version to 0.3.0 2014-04-22 14:05:52 -07:00
Samy Pessé 305f221077 Fix #136: replace links to .md by links to .html
Add more tests for links in page
2014-04-22 17:45:12 +02:00
Samy Pessé 53514f2602 Improve sepia theme sidebar 2014-04-22 14:27:48 +02:00
Samy Pessé 00327778cb Improve font-settings dropdown design 2014-04-22 14:12:53 +02:00
Samy Pessé 37fadec787 Merge branch 'master' of https://github.com/Hermanya/gitbook into Hermanya-master 2014-04-22 14:02:19 +02:00
Samy Pessé ada11d0b59 Add section in readme about plugins 2014-04-22 12:51:10 +02:00
Aaron O'Mullan 4c90fdb7b2 Merge pull request #123 from GitbookIO/feature/plugins
Fix #65: Plugins Architecture
2014-04-21 14:26:09 -07:00
Samy Pessé 98c133df3c Change plugins separator to "," 2014-04-21 23:23:53 +02:00
Samy Pessé f86c27b090 Add option --pluginsConfig 2014-04-21 22:58:51 +02:00
Samy Pessé b5b8ed72a5 Update default addons 2014-04-21 22:39:23 +02:00
Samy Pessé 5beff86548 Change js interface for plugins and add configuration 2014-04-21 22:20:41 +02:00
Samy Pessé dd395edae1 Use mathjax plugin version 0.0.2 2014-04-21 16:30:02 +02:00
Aaron O'Mullan d1c1d7b75b Bump version to 0.2.4 2014-04-20 16:41:09 -07:00
Samy Pessé 4a380e9279 Fix #36: add mathjax plugin as default 2014-04-20 15:20:21 +02:00
Samy Pessé 878c105def Use complete assets folder for plugins 2014-04-20 02:04:15 +02:00
Samy Pessé 74d628676b Add option to change templates from plugin 2014-04-20 00:10:46 +02:00
Samy Pessé 6fdd98ec3a Trigger event on document when ready 2014-04-19 20:39:58 +02:00
Samy Pessé 2ed1be3533 Add global js interface for plugins and remove mixpanel 2014-04-19 20:10:03 +02:00
Samy Pessé 0ac04e1584 Add call for hooks "init" and "finish" 2014-04-19 19:50:32 +02:00
Herman Starikov 2a38d33d8a Font settings, closes #102 2014-04-19 13:46:14 -04:00
Samy Pessé fb14caf5b3 Add resources to site build 2014-04-19 19:31:02 +02:00
Samy Pessé cf51772f54 Add command line option "plugins" 2014-04-19 18:00:24 +02:00
Samy Pessé cc8516df83 Load plugins during generation 2014-04-19 17:57:40 +02:00
Samy Pessé c6a624bc6b Add method to load a list of plugins from generator options 2014-04-19 17:54:15 +02:00
Samy Pessé 8be8b825ee Add base loading of default addons
Add mixpanel as default addon
2014-04-19 17:28:44 +02:00
Samy Pessé 56eb4a2897 Add base plugin object and test validation of plugin 2014-04-19 15:41:29 +02:00
Samy Pessé 7a461eaa61 Remove theme readme
Add semver dependency
2014-04-19 14:40:46 +02:00
Samy Pessé fd64c05809 Fix #118: fix single page generation when file from summary doesn't exists 2014-04-18 23:42:02 +02:00
Aaron O'Mullan 6bce93d893 Bump version to 0.2.3 2014-04-18 11:53:18 -07:00
Aaron O'Mullan c9db068227 Refactor navigation generation to be functional
This makes it a lot less error prone and far more robust. As well as a
lot easier to understand
2014-04-18 11:49:55 -07:00
Samy Pessé 8a6d36ac4d Improve stability of mixpanel tracking 2014-04-18 20:42:18 +02:00
Samy Pessé 64622c5945 Load css before content 2014-04-18 20:19:45 +02:00
Samy Pessé 837813edb0 Add warning about theme 2014-04-18 20:16:21 +02:00
Aaron O'Mullan 0d9d62f1a9 Merge pull request #117 from GitbookIO/fix/99
Fix #99
2014-04-18 10:17:20 -07:00
Samy Pessé f7e9abf984 Fix quiz interractivity 2014-04-18 19:10:13 +02:00
Samy Pessé dd0579b60d Update tests for relative images 2014-04-18 17:23:33 +02:00
Samy Pessé b5bb953e47 Fix image path calcul 2014-04-18 17:15:03 +02:00
Samy Pessé ef4a8ea1b6 Fix #116: update url before updating html content 2014-04-18 15:49:53 +02:00
Samy Pessé 49cde96ee7 Add contributors to package.json and change homepage 2014-04-18 15:00:40 +02:00
Samy Pessé 627be20e48 Add editor and twitter links in the readme 2014-04-18 14:48:03 +02:00
Samy Pessé e45bb84b02 Fix navigation on chrome for iOS: disable pushState 2014-04-18 13:09:16 +02:00
Aaron O'Mullan 4e8876c725 Update README.md 2014-04-17 16:39:06 -07:00
Aaron O'Mullan 6d5be15ec9 Bump version to 0.2.2 2014-04-17 15:31:57 -07:00
Samy Pessé 1a8db646f2 Improve styling for pdf and ebook 2014-04-17 12:15:28 +02:00
Samy Pessé d3aba2c906 Fix state update after navigation 2014-04-17 11:48:04 +02:00
Samy Pessé 457a83282f Improve style for pdf and ebook 2014-04-17 11:27:21 +02:00
Samy Pessé 48e94c937d Remove useless caracter from page format 2014-04-17 11:17:40 +02:00
Samy Pessé 6e613e9f71 Fix #107: Render all the page in single page format 2014-04-17 11:16:18 +02:00
Aaron O'Mullan b698d43d7d Bump version to 0.2.1 2014-04-17 01:10:11 -07:00
Aaron O'Mullan a36b9a6088 Normalize code language before highlighting 2014-04-17 01:09:26 -07:00
Samy Pessé 20acd89c8a Merge pull request #104 from GitbookIO/fix/auto_highlighting
Disable automatic highlighting
2014-04-17 09:24:56 +02:00
Aaron O'Mullan 7bed9a257d Merge pull request #105 from ptagell/master
Removed duplicate line of readme
2014-04-16 14:38:49 -07:00
Paul Tagell 19c83aa5a8 Removed duplicate line of readme 2014-04-17 07:35:28 +10:00
Aaron O'Mullan 52610d7d61 Disable automatic highlighting
Fallback to no highlighting rather that potentially funky highlighting
2014-04-16 13:15:35 -07:00
Aaron O'Mullan 9e4b393b7a In parse/page remove global marked settings
Highlighter settings were global, moved them to the render function
(where they should have been)
2014-04-16 13:07:35 -07:00
Samy Pessé a0ae34a3b7 Fix #100: fix section test isQuiz 2014-04-16 17:16:18 +02:00
Samy Pessé 413dc5d004 Add more test for quiz (related to #100) 2014-04-16 17:15:45 +02:00
Aaron O'Mullan c9eb27bfe7 Do not pass SUMMARY.md to generator, fixes #93
We want to parse SUMMARY.md but not generate a file from it, thus it
should be removed from the list of files to be processed by the
generator
2014-04-16 00:18:35 -07:00
Aaron O'Mullan b78505c290 Ignore .gitignore file by default in fs.list
Partial fix of #93
2014-04-16 00:11:54 -07:00
Samy Pessé 95f9ae83d0 Fix #97: keep scroll position during page changement 2014-04-15 17:42:46 +02:00
Samy Pessé 9304289315 Fix progress display when page change 2014-04-14 23:00:40 +02:00
Samy Pessé 1b726ca293 Update preview 2014-04-14 22:35:10 +02:00
Samy Pessé 656ca18929 Improve languages list design 2014-04-14 21:29:26 +02:00
Aaron O'Mullan b8cacaee00 Bump to version 0.2.0 2014-04-14 11:48:29 -07:00
Samy Pessé f349012f0f Add analytics on page changement 2014-04-14 20:33:45 +02:00
Samy Pessé ddd49509ce Improve design of header buttons 2014-04-14 20:21:57 +02:00
Samy Pessé 28257e1ce7 Fix search with pusState navigation 2014-04-14 20:15:04 +02:00
Samy Pessé 24d8e15ee2 Fix scrolling on new design 2014-04-14 20:08:55 +02:00
Samy Pessé d6d9223ea1 Revert "Fix ebook and pdf generator"
This reverts commit f43e630528.
2014-04-14 20:00:15 +02:00
Samy Pessé 05f1eed9ce Fix #91: Extract defaults title and description from README.md 2014-04-14 19:48:12 +02:00
Samy Pessé c0ca055bf4 Add readme parsing for extracting title and description 2014-04-14 19:21:43 +02:00
Samy Pessé f43e630528 Fix ebook and pdf generator 2014-04-14 19:05:23 +02:00
Samy Pessé e4fa8aaca7 Fix generate.file method 2014-04-14 18:56:20 +02:00
Samy Pessé 1b147d3e30 Merge branch 'master' of https://github.com/tcorral/gitbook into tcorral-master
Conflicts:
	theme/javascript/app.js
2014-04-14 16:44:11 +02:00
Samy Pessé 5848f5db51 Merge pull request #88 from GitbookIO/feature/clarity
Feature/clarity
2014-04-14 16:36:12 +02:00
Samy Pessé 9f1ba8483b Merge head styles so that ace stays stable 2014-04-14 16:34:19 +02:00
Samy Pessé 6da48764e8 Fix header interractive actions: buttons and github 2014-04-14 16:29:02 +02:00
Samy Pessé 8c4cfed609 Add complete refresh of head and body on page navigation 2014-04-14 16:16:34 +02:00
Samy Pessé 1ff7e88cea Focs and reset scroll on page body 2014-04-14 15:43:11 +02:00
Samy Pessé c3ba975d6c Improve quiz sections design 2014-04-14 15:37:13 +02:00
Samy Pessé 8c02e949fc Fix exercises binding when page changed
Add base binding for quiz
2014-04-14 12:43:45 +02:00
Samy Pessé 56e1803b34 Fix title update with pushState navigation 2014-04-14 12:32:22 +02:00
Samy Pessé c511037f46 New languages page 2014-04-14 01:04:58 +02:00
Samy Pessé 730fe982f5 Add @Nijikokun as a contributor 2014-04-14 00:49:25 +02:00
Samy Pessé ca71a1c9e6 Fix regression on github as an option 2014-04-14 00:49:09 +02:00
Samy Pessé 01bc283638 Add fonts locally 2014-04-14 00:40:02 +02:00
Samy Pessé 4ca719b108 Fix fonts path 2014-04-14 00:23:38 +02:00
Samy Pessé 889183ccc9 Improve search input design 2014-04-14 00:17:16 +02:00
Samy Pessé 4a6ae8d4e0 Switch to 4spaces indentations for less files 2014-04-14 00:15:35 +02:00
Samy Pessé b77debae24 Made progress bar static 2014-04-14 00:11:51 +02:00
Samy Pessé 9ffa462111 Add base from clarity theme from @Nijikokun 2014-04-14 00:06:58 +02:00
Samy Pessé 1bbef54015 Remove logs from core module 2014-04-13 23:55:13 +02:00
Samy Pessé 3c7cae6262 Move more build logic to the core generation module 2014-04-13 23:53:34 +02:00
tcorral 646a4513e3 Allow scroll in computers that put the navigation over scroll control. When the document is ready it will check the difference between the body-inner and the page-wrapper width to move the layer only this width allowing the user to scroll. 2014-04-13 04:24:07 +02:00
tcorral d9e34fc671 Allow scroll in computers that put the navigation over scroll control. When the document is ready it will check the difference between the body-inner and the page-wrapper width to move the layer only this width allowing the user to scroll. 2014-04-13 03:09:50 +02:00
tcorral 2242963258 Allow scroll in computers that put the navigation over scroll control. When the document is ready it will check the difference between the body-inner and the page-wrapper width to move the layer only this width allowing the user to scroll. 2014-04-13 03:07:44 +02:00
Aaron O'Mullan 555c0d63ee Add gitbook init command, fixes #67 2014-04-12 12:27:13 -07:00
Aaron O'Mullan fb05609e09 Refactor binary's buildCommand
Be more “commander-like”
2014-04-12 11:58:55 -07:00
Aaron O'Mullan 78cb9a56ba Merge pull request #85 from GitbookIO/pr/63
Pr/63
2014-04-12 01:51:06 -07:00
Aaron O'Mullan 6173fb76ad Refactor, document, improve section type detection
Especially for quiz types
2014-04-12 01:50:48 -07:00
Aaron O'Mullan ca25511d7a Fixup page lexing in test/page 2014-04-12 01:34:36 -07:00
James Phillpotts b07681a16a Quiz with GFM checkbox lists 2014-04-12 01:26:03 -07:00
James Phillpotts f6595f5111 Tabular quiz 2014-04-12 01:25:07 -07:00
Samy Pessé ab27725b99 Update AUTHORS.md 2014-04-11 23:31:39 +02:00
Samy Pessé 32b1be3dee Create AUTHORS.md 2014-04-11 23:30:40 +02:00
Samy Pessé d715b5b154 Merge pull request #84 from Tim-Zhang/master
Fix title problem when repo url doesn't have .git suffix
2014-04-11 23:15:30 +02:00
TimZhang 177ca32b9b Fix title problem when repo url doesn't have .git suffix 2014-04-11 18:04:01 +08:00
Aaron O'Mullan 892ec04f52 Merge pull request #78 from mrpotes/mocha-windows
Fix URL creation on windows
2014-04-10 11:44:33 -07:00
James Phillpotts 8f457a752e Use OS-agnostic path generation for images too 2014-04-10 11:00:52 +01:00
James Phillpotts 435a4b8c99 Use API from node 0.8 2014-04-10 10:45:54 +01:00
Samy Pessé 02d8272c74 Merge pull request #77 from GitbookIO/improve/sections_lexing
Improve/sections lexing
2014-04-10 11:34:56 +02:00
Aaron O'Mullan e54b808093 Improve section detection, fixes #68 2014-04-10 02:32:37 -07:00
James Phillpotts fb7653bd14 Fix URL creation on windows 2014-04-10 10:31:32 +01:00
Aaron O'Mullan 04774df372 Add better tests for ambiguous section detection 2014-04-10 02:27:42 -07:00
Aaron O'Mullan 619ded93ad Improve language normalization
Check for null before converting to lower case
2014-04-10 02:21:54 -07:00
Samy Pessé 2e85380cd8 Merge pull request #62 from oliverh855/master
Changes the default font from Helvetica to Open Sans
2014-04-09 11:13:56 +02:00
Oliver Hoffmann 241ad7cb0e Removes unused font-family appLight 2014-04-09 11:09:31 +02:00
Oliver Hoffmann 0171c32996 Removes helvetica font from repository 2014-04-09 11:08:39 +02:00
Oliver Hoffmann d275bd516a Changes the default font from Helvetica to Open Sans. See #14 2014-04-09 10:55:52 +02:00
Samy Pessé 99594791e0 Merge Optional github 2014-04-09 08:23:11 +02:00
Samy Pessé 1b432288f0 Merge branch 'optional-github' of https://github.com/mrpotes/gitbook into mrpotes-optional-github 2014-04-09 08:17:37 +02:00
Aaron O'Mullan 2cc23b7e34 Bump version to 0.1.3 2014-04-08 17:03:13 -07:00
Aaron O'Mullan 61b6848a95 Uglify output JS theme/assets/app.js 2014-04-08 17:01:43 -07:00
Aaron O'Mullan 9265b0185a Add context code support to exercises (optional)
Partial #52, fixes #49, fixes #46
2014-04-08 16:58:04 -07:00
Aaron O'Mullan 392573a990 Add lexing support for content code in exercises
#52, #49, #46
2014-04-08 15:01:12 -07:00
Aaron O'Mullan 554d6a6170 Fix URL rewriting error on empty URLs
Fixes #54, fixes #59
2014-04-08 14:47:36 -07:00
James Phillpotts fa2f82d52f Tidy up use of github, and make error more descriptive 2014-04-08 10:00:10 +01:00
Samy Pessé 2182baa2e9 Merge pull request #51 from calmdev/fix/gh-enterprise
Fixes --githubHost option for use with Github Enterprise.
2014-04-07 10:42:55 -07:00
Levi Lewis 7fe189907e Fixes --githubHost option for use with Github Enterprise. 2014-04-07 13:11:44 -04:00
James Phillpotts 58a90886da Whoops, missed a couple of extra uses of githubId 2014-04-07 10:51:41 +01:00
James Phillpotts 80794997a4 Make use of github optional 2014-04-07 10:38:06 +01:00
Samy Pessé 9d541a0a4d Bump version to 0.1.2 2014-04-07 01:22:58 -07:00
Samy Pessé 8f3df085d8 Center images in markdown body 2014-04-07 01:11:08 -07:00
Samy Pessé b7e04b5475 Don't use caching for search index 2014-04-07 01:08:11 -07:00
Samy Pessé ee8bf6dc4c Fix display of table in page body 2014-04-07 01:06:44 -07:00
Samy Pessé 9670f564d9 Improve new navigation on mobile 2014-04-07 01:03:35 -07:00
Samy Pessé b541df2b93 Add new lateral navigations 2014-04-07 00:44:30 -07:00
Samy Pessé cf3a641350 Fix progress link to README 2014-04-07 00:02:00 -07:00
Samy Pessé ec719891a1 Remove need for README.md for multi-languages 2014-04-06 23:53:39 -07:00
Samy Pessé 1bfa8c186c Fix overflow on languages page 2014-04-06 23:51:16 -07:00
Samy Pessé 5887f1de28 Merge pull request #42 from GitbookIO/feature/ebook
Fix #10: Add generation to ebook
2014-04-06 22:28:17 -07:00
Samy Pessé a9a66bc240 Add format ebook to README.md 2014-04-06 22:09:52 -07:00
Samy Pessé 742229a989 Add command "ebook" to generate an ebook 2014-04-06 22:03:44 -07:00
Samy Pessé 9fe80b878e Add option cover for ebook generation 2014-04-06 21:46:11 -07:00
Samy Pessé 4b64c87011 Add base ebook generator using ebook-convert 2014-04-06 19:10:22 -07:00
Samy Pessé 4c36c592c1 Merge pull request #40 from GitbookIO/feature/searchclient
Feature/searchclient
2014-04-06 17:13:32 -07:00
Samy Pessé 9ffb1a062f Use cache in localstorage to store search index 2014-04-06 17:13:10 -07:00
Samy Pessé 536c480969 Fix loading of search index in sub directories 2014-04-06 17:06:01 -07:00
Samy Pessé d14bd7245f Open sidebar when opening search 2014-04-06 16:51:14 -07:00
Samy Pessé 6fc8255b7f Reset search results when hidding search bar 2014-04-06 16:47:28 -07:00
Samy Pessé a42f287a92 Filter summary using search bar 2014-04-06 16:46:29 -07:00
Samy Pessé d2f62fd109 Move search bar as global 2014-04-06 16:20:31 -07:00
Samy Pessé f24b66f2c4 Blur search input when hidding search bar 2014-04-06 16:17:54 -07:00
Samy Pessé 607b45f172 Change norm of core/state 2014-04-06 16:10:25 -07:00
Aaron O'Mullan c166a9863e Resolve image URLs, fixes #38
Using outidr & dir options
2014-04-06 16:05:12 -07:00
Aaron O'Mullan 1a4b196851 Add outdir option to generators' generateFile 2014-04-06 16:04:28 -07:00
Samy Pessé ff52a2317b Hide the search bar when pressing ESC key 2014-04-06 16:02:43 -07:00
Samy Pessé b598c9a272 Add keyboard shortcut "f" for opening search bar 2014-04-06 15:58:00 -07:00
Samy Pessé 9cd7c9ad0e Focus search input when opening 2014-04-06 15:56:28 -07:00
Aaron O'Mullan 75a8d3069d Fix dir option in site generator
Use relative folder not absolute
2014-04-06 15:55:24 -07:00
Samy Pessé ed41429d58 Add base search bar in sidebar 2014-04-06 15:53:53 -07:00
Aaron O'Mullan eecb33baa8 Fix relative link detection in renderer 2014-04-06 15:46:20 -07:00
Aaron O'Mullan b8f2da734e Fix minor repetion in README
.gitignore was repeated twice
2014-04-06 15:25:39 -07:00
Aaron O'Mullan 3687a1701b Fix a few typos in README 2014-04-06 15:23:07 -07:00
Aaron O'Mullan c8f83e2684 Document ignores in README, fixes #37 2014-04-06 15:21:57 -07:00
Aaron O'Mullan aa7835e913 Add .bookignore to ignoreFiles #37 2014-04-06 15:19:31 -07:00
Samy Pessé 74edc4f279 Merge pull request #39 from GitbookIO/feature/search
Feature/search
2014-04-06 15:03:27 -07:00
Aaron O'Mullan 696cbdc3b1 Generate section.id in lexer
This allows generating correct URLs for our search index
2014-04-06 14:53:33 -07:00
Aaron O'Mullan 2f8543d710 Generate search_index.json when generating site
Fixes #24
2014-04-06 14:50:11 -07:00
Aaron O'Mullan aee33d67b6 Add lunr@0.5.2 dependency 2014-04-06 14:49:44 -07:00
Samy Pessé 52ccc2b46d Fix #33: copy files when generating pdf or single page 2014-04-06 14:37:33 -07:00
Aaron O'Mullan 9270d7949a In page parser, lex only if not already lexed 2014-04-06 14:13:48 -07:00
Samy Pessé 855c9b9aef Add documentation about multi-languages books 2014-04-06 14:08:11 -07:00
Aaron O'Mullan c98ba22b08 Move page lexer to separate module
Allow lexer to be used independently of page parser/renderer
2014-04-06 14:02:49 -07:00
Samy Pessé d1e55aef8e Add support of multi-languages book in command pdf 2014-04-06 13:59:58 -07:00
Aaron O'Mullan ef059c8737 Add marked-text-renderer@0.0.1 dependency 2014-04-06 13:50:44 -07:00
Samy Pessé fde66ed849 bin/build after generation return all the options 2014-04-06 13:49:51 -07:00
Samy Pessé abce29866e Return options after generate 2014-04-06 13:47:29 -07:00
Samy Pessé 014b2f9c69 Merge pull request #35 from GitbookIO/feature/multilangs
Fix #34: Feature/multilangs
2014-04-06 13:44:14 -07:00
Samy Pessé 65cb23feab Fix generation for page format 2014-04-06 13:43:20 -07:00
Samy Pessé 2b3c67a26b Fix commands options 2014-04-06 13:16:06 -07:00
Samy Pessé 174ce965d9 Fix command build 2014-04-06 13:09:42 -07:00
Samy Pessé adf20e6a16 Add base style for languages index page 2014-04-06 13:07:57 -07:00
Samy Pessé 53f714b7f3 Add base for generating languages index 2014-04-06 12:52:35 -07:00
Samy Pessé d19906d5b2 Add base for multi-languages book 2014-04-06 12:11:43 -07:00
Samy Pessé fb26b634ce Fix page format when chapters are not yet written 2014-04-06 01:59:53 -07:00
Samy Pessé a34dc458c9 Fix footer link when no chapters is ready yet 2014-04-06 00:36:44 -07:00
Aaron O'Mullan eeb715d5d6 Prevent renderer from mutating sections 2014-04-06 00:21:55 -07:00
Samy Pessé a8e026a642 Add information about output format 2014-04-05 23:30:48 -07:00
Samy Pessé cc096d2253 Rename Index.js to index.js 2014-04-06 05:25:22 +02:00
Samy Pessé a4c1cf9334 Refactor bin/gitbook.js 2014-04-05 20:22:27 -07:00
Samy Pessé 9ffec175a5 Add command gitbook pdf to generate PDF 2014-04-05 20:22:27 -07:00
Aaron O'Mullan 465188cae6 Remove useless console.log in test/summary.js 2014-04-05 20:09:50 -07:00
Aaron O'Mullan 4547ec73fd Merge pull request #31 from GitbookIO/feature/lang
Feature/lang
2014-04-05 20:06:38 -07:00
Aaron O'Mullan cae6267665 Adapt test to new code language normalization 2014-04-05 20:04:54 -07:00
Aaron O'Mullan 58bee15f37 Add basic code language normalization 2014-04-05 20:04:24 -07:00
Samy Pessé 761cfb4a2c Add pdf generator using gitbook-pdf 2014-04-05 19:52:51 -07:00
Samy Pessé 8e3339ead2 Fix link to introduction 2014-04-05 18:45:56 -07:00
Aaron O'Mullan b330277ed7 Normalize paths on Windows to Unix convention
Convert “\” to “/“ in relative paths in fs.list
2014-04-05 18:44:53 -07:00
Samy Pessé 12b5baf433 Start exercise index at 1 on pdf format 2014-04-05 18:44:17 -07:00
Samy Pessé f38d68c355 Improve style of exercises in pdf format 2014-04-05 18:42:21 -07:00
Samy Pessé 865bf40e1a Add exercise solutions at the end of the pdf book 2014-04-05 18:08:25 -07:00
Samy Pessé 562ee2b77f Add README introduction to page/pdf generation 2014-04-05 17:33:09 -07:00
Samy Pessé 8b0f2f6329 Fix rendering of code in exercises 2014-04-05 17:28:46 -07:00
Samy Pessé 44717a3e3e Generate an index.html instead of a README.html 2014-04-05 17:15:22 -07:00
Samy Pessé 6f7e3fda02 Fix conflict in app.js 2014-04-05 13:54:13 -07:00
Samy Pessé 511fc7bd46 Merge branch 'master' of https://github.com/GitbookIO/gitbook
Conflicts:
	theme/assets/app.js
2014-04-05 13:54:00 -07:00
Aaron O'Mullan 4987beec2b Add test for exercise language detection 2014-04-05 13:53:44 -07:00
Aaron O'Mullan e414cbd6d1 Detect exercise language
And ensure that all code blocks use the same language
2014-04-05 13:53:31 -07:00
Samy Pessé 694afeccc5 Add base for exercise for others languages 2014-04-05 13:53:22 -07:00
Aaron O'Mullan 959a2d830a Improve robustness of analytics.isAvailable
Fixes GitbookIO/javascript#18

Safari has issues with loading mixpanel, this adds and extra check so
Safari doesn’t fail on event tracking
2014-04-05 12:47:49 -07:00
Aaron O'Mullan 4850c0f302 Bump version to 0.1.1 2014-04-05 01:10:51 -07:00
Aaron O'Mullan ac258329ff Copy README.html to index.html instead of symlimk
Fixes #28
2014-04-05 01:05:31 -07:00
Samy Pessé d5bd08dcd2 Show exercise in page format 2014-04-04 20:12:22 -07:00
Samy Pessé 6c414ec400 Fix link for chapters in page format 2014-04-04 20:01:50 -07:00
Samy Pessé 881a0c6f6d Add info about --theme option 2014-04-04 18:21:40 -07:00
Samy Pessé 0071594300 Fix error with mixpanel loading 2014-04-04 18:18:50 -07:00
Samy Pessé 460799828f Merge pull request #27 from qrohlf/fix-sidebar-mobile
don't show the sidebar by default on mobile
2014-04-05 02:56:57 +02:00
Samy Pessé 02b75288d2 No need to include highlight twice (style.css and print.css) 2014-04-04 17:50:35 -07:00
Samy Pessé b108be0c3b Add separation line between pages 2014-04-04 17:46:59 -07:00
Samy Pessé 7ab285832b Improve style for page printing 2014-04-04 17:40:37 -07:00
Quinn Rohlf 0bc288fe96 don't show the sidebar by default on mobile 2014-04-04 16:44:25 -07:00
Samy Pessé 9fbe240843 Add summary to page generator 2014-04-04 16:40:21 -07:00
Samy Pessé 0c7e2b3557 Add base style for printing 2014-04-04 16:02:57 -07:00
Samy Pessé e34e65813c Fix pages order in page generator 2014-04-04 15:53:30 -07:00
Samy Pessé 63f946ced9 In page generator, add break for printing between pages 2014-04-04 15:47:45 -07:00
Samy Pessé af9f3cf89f Don't generate folder when using generator "page" 2014-04-04 15:41:22 -07:00
Samy Pessé 355094bbb3 Change option for changing generator to format 2014-04-04 15:38:01 -07:00
Samy Pessé b3adedb468 Add base for generating single page html 2014-04-04 14:40:27 -07:00
Samy Pessé 026e4362d8 Move mixpanel to app.js 2014-04-04 14:27:32 -07:00
Samy Pessé 9cf5384fbe Change template name for site generation to site 2014-04-04 14:22:48 -07:00
Samy Pessé 9bc0bf19da Add command line options for changing theme 2014-04-04 14:15:06 -07:00
Samy Pessé f49a1a484e Remove lex command 2014-04-04 14:11:43 -07:00
Samy Pessé 446d486a0f Add base for theming 2014-04-04 14:10:56 -07:00
Samy Pessé 07101deaef Move generator to specific folder 2014-04-04 13:57:07 -07:00
Samy Pessé 76b3fcb41c Add base for page generator 2014-04-04 13:55:43 -07:00
Samy Pessé febda9aba1 Add Travis CI badge 2014-04-04 20:07:05 +02:00
Aaron O'Mullan f952fa47b9 Use relative symlink between index & README
Fixes #19, fixes #21
2014-04-04 11:04:58 -07:00
Samy Pessé 1984660930 Rename PAGE.MD to PAGE.md 2014-04-04 20:04:49 +02:00
Aaron O'Mullan d475affd3b Merge pull request #25 from GitbookIO/feature/generators
Feature/generators
2014-04-04 10:59:13 -07:00
Samy Pessé fe3de93e06 Create .travis.yml 2014-04-04 19:57:51 +02:00
Samy Pessé 5cae311edf Log error in command line tool 2014-04-04 10:55:48 -07:00
Samy Pessé 26701a2a73 Remove logs from generation 2014-04-04 10:50:59 -07:00
Samy Pessé 5125e04814 Update tests 2014-04-04 10:47:44 -07:00
Samy Pessé 0a6194340f Remove file template.js 2014-04-04 10:46:11 -07:00
Samy Pessé e0d96b3163 Fix links in templates 2014-04-04 10:45:36 -07:00
Samy Pessé f656390783 Base for new site generator 2014-04-04 10:39:27 -07:00
Samy Pessé 88c480ba4f Complete json generator 2014-04-04 10:27:45 -07:00
Samy Pessé 3da53d56a2 Add option -g to choice generator
Add base json generator
2014-04-04 09:53:05 -07:00
Samy Pessé 91359fab1e _path is not used anymore 2014-04-04 09:47:34 -07:00
Samy Pessé a52a4bed1a Change normalization of parser to give link in .md 2014-04-04 09:47:06 -07:00
Samy Pessé 9dce855113 Add class BaseGenerator 2014-04-04 09:45:04 -07:00
Samy Pessé 57e2595a3e Merge pull request #20 from mintbridge/github-host-url
Adding config option for github host
2014-04-04 16:51:28 +02:00
Paul Dixon b1314273bb Adding config option for github host
- this config option allows the github host url to be set to
something other than github.com, mainly so that github enterprise
users can change the url to their private host but will also
work with bitbucket
2014-04-04 14:55:08 +01:00
Samy Pessé cb9a0b82ba Fix some typos in lib 2014-04-04 00:00:12 -07:00
Samy Pessé 57ecfe4a54 Make templates more modular 2014-04-03 23:54:03 -07:00
Samy Pessé 405792fd8f Fix #17: Remember state of book summary panel 2014-04-03 23:17:45 -07:00
Samy Pessé fc6d911340 Merge pull request #16 from wh0/patch-1
GiBook -> GitBook
2014-04-04 05:00:29 +02:00
wh0 1e6307b69b GiBook -> GitBook 2014-04-03 19:59:56 -07:00
Aaron O'Mullan af87dc8d7c Bump version to 0.1.0 2014-04-03 16:19:49 -07:00
Aaron O'Mullan 1699028688 Handle empty repoID and print error, fixes #11 2014-04-03 16:10:47 -07:00
Samy Pessé 55d9bfbf18 Enable use of spacebar for scrolling into book body 2014-04-03 15:53:50 -07:00
Samy Pessé c964b53137 Add keyboard shortcut 's' to toggle sidebar 2014-04-03 15:39:05 -07:00
Samy Pessé 510f7bacb4 Show sidebar by default 2014-04-03 15:29:34 -07:00
Samy Pessé c934238f3d Fix #9: add keyboard navigation using right/left 2014-04-03 15:24:57 -07:00
Samy Pessé ab48959968 Update screenshot preview 2014-04-02 21:25:33 -07:00
Samy Pessé eb6204aa42 Bump version to 0.0.4 2014-04-02 20:26:08 -07:00
Samy Pessé a70439f375 Update app.js build 2014-04-02 20:25:52 -07:00
Samy Pessé a3b562b4e8 Prevent default click action on sharing 2014-04-02 20:24:07 -07:00
Samy Pessé aa4e3abc33 Add sharing links in header 2014-04-02 20:17:46 -07:00
Samy Pessé f24a7fbb6e Fix selection of all buffer when clicking on "Solution" 2014-04-02 19:00:50 -07:00
Samy Pessé f3a49306b1 Use prefix for storage key 2014-04-02 18:57:36 -07:00
Samy Pessé f312472abd Add button to edit current chapter in sidebar 2014-04-02 18:53:58 -07:00
Aaron O'Mullan ecb07468f6 Resolve relative links to GitHub repo, fixes #6
Useful for linking to blobs and other files …
2014-04-02 18:40:53 -07:00
Aaron O'Mullan 8fc09e2e05 Add github link resolution test 2014-04-02 18:40:53 -07:00
Aaron O'Mullan ed7798237a Add GITHUB_LINKS.md fixture
For testing github link resolution
2014-04-02 18:40:52 -07:00
Samy Pessé faf63566df Hide articles in progress bar on mobile 2014-04-02 17:48:35 -07:00
Samy Pessé c88382aea0 Update favicon 2014-04-02 17:01:30 -07:00
Samy Pessé 9ee5f48aef Add meta tag generator 2014-04-02 16:53:28 -07:00
Samy Pessé e1dfe5c0f6 Improve meta tags 2014-04-02 16:48:12 -07:00
Aaron O'Mullan 3adcd77ae4 Merge sections together, fixes #7 2014-04-02 16:40:51 -07:00
Aaron O'Mullan 4bdcfd07a9 Add tests for section merging 2014-04-02 16:40:51 -07:00
Aaron O'Mullan a9967df6eb Add HR_PAGE.md fixture
This is for testing that non exercise sections are merged correctly
together and united with HRs
2014-04-02 16:40:51 -07:00
Samy Pessé 62a2e8f81c Fix #8: Improve page title 2014-04-02 16:38:24 -07:00
Aaron O'Mullan 6bcc2d8cf2 Expose titles in navigation of current nodes 2014-04-02 16:33:31 -07:00
Samy Pessé ad1867e4d8 Add preview on readme 2014-04-02 10:40:47 +02:00
Samy Pessé 445c2327c4 Bump version to 0.0.3 2014-04-02 01:36:03 -07:00
Samy Pessé 7a33881b3b Fix mixpanel tracking 2014-04-02 01:35:41 -07:00
Aaron O'Mullan 417ee789c4 Improve README.md, fix minor typos 2014-04-02 01:34:43 -07:00
Samy Pessé 22c118779c Add preview screenshot 2014-04-02 01:27:54 -07:00
Samy Pessé 53dc206d71 Fix format for exercise 2014-04-02 10:20:44 +02:00
Samy Pessé 3c9d8878df Add informations about exercises format 2014-04-02 01:14:15 -07:00
Samy Pessé 03cbe7b18f Add information about summary format 2014-04-02 01:04:01 -07:00
Samy Pessé 85f4c3be58 Add base information on installation in readme 2014-04-02 00:56:02 -07:00
Samy Pessé eb09f61029 Fix usage of highlight.js 2014-04-01 23:00:22 -07:00
Samy Pessé 2cee253be4 Bump version to 0.0.2 2014-04-01 19:32:55 -07:00
Samy Pessé 25162ceaa2 Fix #5: Add favicon 2014-04-01 19:20:38 -07:00
Samy Pessé 840113a48f Improve design of page content (sections and exercises) 2014-04-01 19:10:15 -07:00
Samy Pessé ddffd02b2e Add border radius to sections and footer navigation button 2014-04-01 18:59:59 -07:00
Samy Pessé 520ea847da Make chapter in progress bar clickable 2014-04-01 18:57:48 -07:00
Samy Pessé e77e3b724e Improve footer navigation 2014-04-01 18:49:20 -07:00
Samy Pessé d313589736 Mark chapter as read after the page is viewed 2014-04-01 18:42:43 -07:00
Samy Pessé 466ceb581c Fix execution when contains && 2014-04-01 18:21:01 -07:00
Samy Pessé 835953d8dd Improve lisibility of text in page 2014-04-01 17:57:20 -07:00
Samy Pessé a69e8ec328 Add highlight.js dependency 2014-04-01 17:39:22 -07:00
Samy Pessé 970107c656 Fix #3: Add syntax highlighting 2014-04-01 17:01:44 -07:00
Samy Pessé 7121b7efe3 Fix #4: Add option for defining description 2014-04-01 16:42:15 -07:00
Samy Pessé d02581a21c Add base analytic using mixpanel 2014-04-01 15:39:28 -07:00
Samy Pessé fd5c2f6706 Fix transition on header 2014-04-01 14:42:04 -07:00
Samy Pessé b93f67c84a Save sidebar state 2014-04-01 13:26:06 -07:00
Aaron O'Mullan 86cf2d9d9d Bump version to 0.0.1 2014-04-01 02:51:16 -07:00
Samy Pessé 1e719cf773 Improve footer navigation on mobile 2014-04-01 02:48:00 -07:00
Samy Pessé c66b37d91c Improve progress bar design 2014-04-01 02:38:19 -07:00
Samy Pessé ca2d499f63 Change style of bottom navigation 2014-04-01 02:36:36 -07:00
Samy Pessé b30132d8c8 Fix link to issues 2014-04-01 02:00:53 -07:00
Samy Pessé 71dee7717d Fix summary display on firefox 2014-04-01 01:58:04 -07:00
Samy Pessé 4d880fc05b Improve animation and display of progress bar 2014-04-01 01:51:18 -07:00
Samy Pessé 7b0d50fc45 Fix position of progress bar 2014-04-01 01:23:17 -07:00
Samy Pessé 256d1ba35a Add progress calcul 2014-04-01 01:21:44 -07:00
Aaron O'Mullan cc340db0fa Add cli command serve
This does a build, then spins up a HTTP server to serve the books
contents
2014-04-01 01:14:56 -07:00
Aaron O'Mullan f776748437 Don't add chapters/articles with null paths to navigation 2014-04-01 00:45:52 -07:00
Aaron O'Mullan bb96433fe3 Adjust summary test for new SUMMARY.md fixture 2014-04-01 00:45:51 -07:00
Aaron O'Mullan 6a63708939 Add tests for null paths in navigation
Unfinished chapters/articles should be put in navigation
2014-04-01 00:45:51 -07:00
Samy Pessé e08b09f3cf Fix execution of code 2014-04-01 00:24:09 -07:00
Samy Pessé b3c865ef58 Add execution of exercise 2014-04-01 00:12:26 -07:00
Aaron O'Mullan 2aebb38e58 Add jsrepl to static assets 2014-03-31 23:47:13 -07:00
Samy Pessé 93a0f52b85 Add base for full optimized markdown github style 2014-03-31 23:35:02 -07:00
Samy Pessé 202355729e Fix font-awesome path 2014-03-31 23:18:10 -07:00
Samy Pessé 70569affc2 Fix overflow on header 2014-03-31 23:06:08 -07:00
Samy Pessé d36dfee40b Mark as disabled articles not yet finished in the summary 2014-03-31 23:02:43 -07:00
Samy Pessé eed1dfb243 Only convert markdown file from the navigation 2014-03-31 22:54:22 -07:00
Samy Pessé aefbfaf057 Move getFiles to fs module 2014-03-31 22:45:45 -07:00
Samy Pessé 0f0128fa49 Show level in summary page 2014-03-31 22:35:59 -07:00
Samy Pessé 8edd5e4b3b Signal and mark progress 2014-03-31 22:27:25 -07:00
Samy Pessé 642a338cb0 Mark levels done in local storage 2014-03-31 19:41:29 -07:00
Aaron O'Mullan 77136705fc Add level attribute in navigation 2014-03-31 19:39:38 -07:00
Aaron O'Mullan b16f6d548c Add tests for level attribute in navigation 2014-03-31 19:39:38 -07:00
Samy Pessé 07c10da91f Add base for handling and storing progress 2014-03-31 19:34:50 -07:00
Aaron O'Mullan 0ad6533f12 Add level attribute to chapters/articles 2014-03-31 19:34:06 -07:00
Aaron O'Mullan 2234026153 Add tests for level attribute to chapters/articles 2014-03-31 19:33:55 -07:00
Aaron O'Mullan a6d18e89df Symlink README.html to index.html for book output 2014-03-31 19:25:14 -07:00
Aaron O'Mullan ec570ca68d Remove extra "q" dependency in package.json 2014-03-31 19:19:52 -07:00
Aaron O'Mullan 6bcd483351 Replace glob with better fstream-ignore
It ignores according to .gitignore and other folders
2014-03-31 19:19:52 -07:00
Samy Pessé 347ae52e67 Fix star and watch count 2014-03-31 19:08:06 -07:00
Samy Pessé 5ff6896ee5 Move all javascript to the bundle 2014-03-31 19:04:26 -07:00
Samy Pessé 68e35758c3 Add base for javascript compilation using requirejs 2014-03-31 19:01:01 -07:00
Aaron O'Mullan 84d703c6f5 Ensure destination dir exists before building 2014-03-31 18:28:51 -07:00
Aaron O'Mullan 138dc1f2de Add target _blank support in renderer for external links 2014-03-31 18:15:05 -07:00
Samy Pessé 1ab31ede81 Fix display of long title in summary 2014-03-31 18:01:35 -07:00
Samy Pessé e0a97f2f4b Improve footer navigation display 2014-03-31 17:49:21 -07:00
Samy Pessé 885e451054 Mark as active current chapter 2014-03-31 17:43:07 -07:00
Aaron O'Mullan a7b8e24e7a Fix tiny typo
In string not code
2014-03-31 17:41:37 -07:00
Aaron O'Mullan abf8c59946 Improve navigation from 1st chapter back to intro
1st chapter’s prev, now links to the intro page
2014-03-31 17:40:57 -07:00
Samy Pessé 91a916e146 Improve navigation on mobile resolutions 2014-03-31 17:38:28 -07:00
Aaron O'Mullan 752b26e963 Fix previous/next chapter navigation 2014-03-31 17:36:01 -07:00
Aaron O'Mullan 45574cf7a5 Add better tests for chapter navigation
Proves previous and next chapter logic is broken
2014-03-31 17:36:01 -07:00
Aaron O'Mullan 4607f406ab Add navigation tests for first page for README of a chapter 2014-03-31 17:36:01 -07:00
Samy Pessé bc5d963c5e Fix title position for small screen 2014-03-31 17:28:40 -07:00
Samy Pessé d85ae0a36e Improve title position with summary open in header 2014-03-31 17:27:14 -07:00
Samy Pessé 9c211ab214 Mark active chapter in summary 2014-03-31 17:25:00 -07:00
Samy Pessé ce3e103024 Add navigation parsed in footer 2014-03-31 17:17:34 -07:00
Samy Pessé 8ecaeb86f9 Improve spacing of navigation links in footer 2014-03-31 17:17:24 -07:00
Samy Pessé 1b00fc9620 Add footer for navigation links 2014-03-31 17:06:40 -07:00
Aaron O'Mullan 39f1ed055e Add parse.navigation module
With tests
2014-03-31 17:06:39 -07:00
Aaron O'Mullan 3f3bade58e Change summary data format
Simplify chapter objects, by making them identical to article objects
besides the .articles attribute
2014-03-31 17:06:39 -07:00
Samy Pessé 0821b10e18 Improve page section design 2014-03-31 16:22:28 -07:00
Samy Pessé 37aaab3bae Remove test.js 2014-03-31 16:08:10 -07:00
Aaron O'Mullan fcdb12f4ca Set size of exercise code editor according to solution
Use the number of lines in solution to set editor’s size
2014-03-31 15:55:06 -07:00
Aaron O'Mullan 2d29121e2e Add lex sub-command to gitbook binary
The lex command is useful for debugging files and seeing how they are
parsed
2014-03-31 15:54:26 -07:00
Aaron O'Mullan 36b961c539 Clean output folder before generating 2014-03-31 15:24:38 -07:00
Aaron O'Mullan e9e94a7344 Filter out empty sections when generating pages 2014-03-31 15:14:31 -07:00
Aaron O'Mullan a8e0de86e9 Generate a unique Id for each section 2014-03-31 15:03:06 -07:00
Samy Pessé 6261d42b95 Add base for full display of exercises 2014-03-31 15:01:02 -07:00
Aaron O'Mullan 8e22d80c7c Add content & code generation for exercise sections 2014-03-31 15:00:03 -07:00
Samy Pessé 82b99189b1 Add base display of exercise 2014-03-31 13:59:51 -07:00
Aaron O'Mullan 3f7f5667e4 Merge pull request #1 from GitbookIO/feature/cli
Initial gitbook binary
2014-03-31 13:52:41 -07:00
Aaron O'Mullan a88ea48206 Initial gitbook binary 2014-03-31 13:51:31 -07:00
Samy Pessé c3a181a50d Fix author link 2014-03-31 12:59:26 -07:00
Samy Pessé 0fdd31dc39 Fix link for watcher and stargazers 2014-03-31 12:54:53 -07:00
Samy Pessé d44fd91181 Get watcher and stargazer count using github api 2014-03-31 12:51:27 -07:00
Aaron O'Mullan 264a506e8b Fix bug with exercise classification 2014-03-31 12:40:53 -07:00
Samy Pessé d98669958d Fix header link and display 2014-03-31 12:39:16 -07:00
Samy Pessé 4210794de6 Fix all relative links 2014-03-31 12:31:01 -07:00
Samy Pessé 77a4c1042c Merge branch 'master' of https://github.com/GitbookIO/gitbook 2014-03-31 12:16:16 -07:00
Samy Pessé 6df59745b1 Fix link in summary (absolute to relative) 2014-03-31 12:15:45 -07:00
Samy Pessé 0b08663b70 Fix font path 2014-03-31 12:14:40 -07:00
Samy Pessé 19a3981475 Copy assets to build 2014-03-31 12:10:26 -07:00
Aaron O'Mullan 89593f46ab Normalize .md links to .html when rendering pages 2014-03-31 12:09:28 -07:00
Samy Pessé 1f3f062841 Add base for generation 2014-03-31 12:00:22 -07:00
Aaron O'Mullan 1d321207b0 Normalize chapter & article paths 2014-03-31 12:00:13 -07:00
Aaron O'Mullan bd7531f3cc Add tests for path normalization
.md to .html
2014-03-31 11:58:31 -07:00
Aaron O'Mullan 75073d47a2 Add link to PAGE.md fixture 2014-03-31 11:57:16 -07:00
Aaron O'Mullan 0af1886358 Move summary & page parsing code to lib/parse/ 2014-03-31 01:50:21 -07:00
Aaron O'Mullan d18da55a18 Improve section classification 2014-03-31 01:34:37 -07:00
Aaron O'Mullan 7ab78b420a Change lib entry point to lib/index.js 2014-03-31 01:02:35 -07:00
Aaron O'Mullan 5e183f24de Expose page module in index.js 2014-03-30 22:52:27 -07:00
Aaron O'Mullan 2c36a21938 Render normal sections' content to HTML 2014-03-30 22:51:30 -07:00
Aaron O'Mullan 7b4974209b Add tests for page content rendering 2014-03-30 22:51:02 -07:00
Aaron O'Mullan c21303c762 Add section type detection 2014-03-30 22:36:22 -07:00
Aaron O'Mullan 4c1435f27f Test for section types 2014-03-30 22:36:08 -07:00
Aaron O'Mullan 0199e56000 Add initial page parsing
(Split sections)
2014-03-30 22:30:45 -07:00
Aaron O'Mullan 9284eb4115 Add basic page tests 2014-03-30 22:30:30 -07:00
Aaron O'Mullan cfbdadd6e4 Add title and path parsing for chapters & articles 2014-03-30 22:02:07 -07:00
Aaron O'Mullan 603870ee07 Add test for titles & paths 2014-03-30 22:01:37 -07:00
Aaron O'Mullan 39a80421fc Add summary parsing 2014-03-30 21:30:33 -07:00
Aaron O'Mullan 1d28ab441a Add tests for summary parsing 2014-03-30 21:30:21 -07:00
Aaron O'Mullan d5070c0bf1 Add base dependencies 2014-03-30 20:13:47 -07:00
Aaron O'Mullan 71e9d7bd80 Initial package.json 2014-03-30 20:10:04 -07:00
Aaron O'Mullan 0a104b1f85 Initial commit 2014-03-30 20:01:56 -07:00
945 changed files with 9587 additions and 130468 deletions
-8
View File
@@ -1,8 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
-11
View File
@@ -1,11 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
-3
View File
@@ -1,3 +0,0 @@
# Changes to the API data cache functions can invalidate all existing data cache
# causing a massive amount of revalidation, impacting our API.
packages/gitbook/src/lib/data/api.ts @SamyPesse
-111
View File
@@ -1,111 +0,0 @@
# Welcome to GitBook's contributing guide!
> _For help, support, feature requests, and product questions - head to our [GitHub Community](https://github.com/orgs/GitbookIO/discussions) 🤖_
Thank you for investing your time in contributing to GitBook. Any contribution you make will be reviewed by our team. In this guide, you'll learn the different ways you can contribute.
## Types of Contributions
This repository contains code related to the rendering engine of GitBook's published content. Depending on what you'd like to contribute to, head to the section below to find the necessary steps.
### Add a feature
Because this portion of GitBook is open source and available for you to use - if you think you can provide extra value through a new feature - you're welcome to add it! If you plan to distribute the code, keep the source code public to comply with GNU GPLv3. To clone in a private repository, you must first acquire a [commercial license](https://www.gitbook.com/pricing).
### Create a new issue
If you spot a problem within a repository, [search if an issue already exists](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github/searching-issues-and-pull-requests#search-by-the-title-body-or-comments). If a related issue doesn't exist, you can open a new issue here!Please make sure any added issues are
- Descriptive
- Thoughtful
- Organized
We recommend adding as many relevant links, minimal reproductions of the issue, and other materials that will help our team solve the issue fast.
### Solve an issue
If you're interested in solving an issue in our repository, start by scanning through it's exisiting issues to find one that you're interested in working on. If you find an issue to work on, you are welcome to open a PR with a fix. See the following sections below for more information on contributing for specific sections.
### Documentation
The official documentation on GitBook open can be found directly in this Readme. Any updates or changes you would like to make, you can make directly to the README of this repository.
## Contributing
### Make changes locally
Any contribution you make can be made to the code located in this repository. In order to contribute, you'll need to start off of a local version of this repository.
#### 1. Fork the repository
##### Using GitHub Desktop:
- [Getting started with GitHub Desktop](https://docs.github.com/en/desktop/installing-and-configuring-github-desktop/getting-started-with-github-desktop) will guide you through setting up Desktop.
- Once Desktop is set up, you can use it to [fork the repo](https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/cloning-and-forking-repositories-from-github-desktop)!
##### Using the command line:
- [Fork the repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#fork-an-example-repository) so that you can make your changes without affecting the original project until you're ready to merge them.
#### 2. Create a working branch and start with your changes
After forking this repository, you'll want to [create a branch](https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-a-branch-for-an-issue) to work off of.
#### 3. Install dependencies and run the project locally
##### Prerequisites:
- Node.js (Version: >=20.6)
- Use `nvm` for easy Node management
- [Bun](https://bun.sh/) (Version: >=1.2.15)
- We use a text-based lockfile which isn't supported below 1.2.15
##### Setup steps:
1. Ensure you are using the project's version of Node:
```bash
nvm use
```
2. Install dependencies using Bun:
```bash
bun install
```
3. Start the development server:
```bash
bun dev
```
Additional development commands:
- `bun format`: Format the code using Biome
- `bun typecheck`: Run TypeScript type checking
- `bun unit`: Run unit tests
- `bun e2e`: Run end-to-end tests
#### 4. Preview your changes
When running the development server, published GitBook sites can be rendered through your local version at `http://localhost:3000/url`.
For example, our published docs can be viewed using the local version by visiting `http://localhost:3000/url/gitbook.com/docs` after running the development server.
You can visit any published GitBook site behind your development server. Please make sure your site is [published publicly](https://gitbook.com/docs/published-documentation/publish-your-content-as-a-docs-site) to ensure you can view the site correctly in your development version.
### Commit your update
[Commit your changes](https://github.com/git-guides/git-commit) once you are happy with them. See [Atom's contributing guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages) to know how to use emoji for commit messages!
Once your changes are ready, don't forget to self-review your code to double check that your chagnes are ready to be added.
### Pull Request
When you're finished with the changes, [create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request), also known as a PR.
- Don't forget to [link PR to issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) if you are solving one.
- Enable the checkbox to [allow maintainer edits](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork) so the branch can be updated for a merge. Once you submit your PR, a GitBook team member will review your proposal. We may ask questions or request for additional information.
- We may ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. You can apply suggested changes directly through the UI. You can make any other changes in your fork, then commit them to your branch.
- As you update your PR and apply changes, mark each conversation as [resolved](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/commenting-on-a-pull-request#resolving-conversations).
- If you run into any merge issues, checkout this [git tutorial](https://lab.github.com/githubtraining/managing-merge-conflicts) to help you resolve merge conflicts and other issues.
### Your PR is merged
Congratulations 🎉 Thank you for your contribution! Once your PR is merged, your contributions will be publicly visible on the relevant repository.
-18
View File
@@ -1,18 +0,0 @@
---
name: Bug report
about: Something not working as expected? Let us look into it
labels: bug
---
## Bug description
_Please describe._
_If this affects the front-end, screenshots would be of great help._
## How to reproduce
1.
2.
3.
## Additional context
@@ -1,83 +0,0 @@
name: Gradual Deploy to Cloudflare
description: Use gradual deployment to deploy to Cloudflare. This action will upload the middleware and server versions to Cloudflare and kept them bound together
inputs:
apiToken:
description: 'Cloudflare API token'
required: true
accountId:
description: 'Cloudflare account ID'
required: true
environment:
description: 'Cloudflare environment to deploy to (staging, production, preview)'
required: true
middlewareVersionId:
description: 'Middleware version ID to deploy'
required: true
serverVersionId:
description: 'Server version ID to deploy'
required: true
outputs:
deployment-url:
description: "Deployment URL"
value: ${{ steps.deploy_middleware.outputs.deployment-url }}
runs:
using: 'composite'
steps:
- id: wrangler_status
name: Check wrangler deployment status
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: deployments status --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
# This step is used to get the version ID that is currently deployed to Cloudflare.
- id: extract_current_version
name: Extract current version
shell: bash
run: |
version_id=$(echo "${{ steps.wrangler_status.outputs.command-output }}" | grep -A 3 "(100%)" | grep -oP '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
echo "version_id=$version_id" >> $GITHUB_OUTPUT
- id: deploy_server
name: Deploy server to Cloudflare at 0%
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ steps.extract_current_version.outputs.version_id }}@100% ${{ inputs.serverVersionId }}@0% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
# Since we use version overrides headers, we can directly deploy the middleware to 100%.
- id: deploy_middleware
name: Deploy middleware to Cloudflare at 100%
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.middlewareVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
- name: Deploy server to Cloudflare at 100%
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: versions deploy ${{ inputs.serverVersionId }}@100% -y --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
- name: Outputs
shell: bash
env:
DEPLOYMENT_URL: ${{ steps.deploy_middleware.outputs.deployment-url }}
run: |
echo "URL: ${{ steps.deploy_middleware.outputs.deployment-url }}"
@@ -1,138 +0,0 @@
name: 'Deploy cloudflare'
description: 'Deploy GitBook to Cloudflare'
inputs:
opItem:
description: '1Password item to load secrets from'
required: true
opServiceAccount:
description: '1Password service account token'
required: true
apiToken:
description: 'Cloudflare API token'
required: true
accountId:
description: 'Cloudflare account ID'
required: true
environment:
description: 'Cloudflare environment to deploy to (staging, production, preview)'
required: true
deploy:
description: 'Deploy as main version for all traffic instead of uploading versions'
required: true
commitTag:
description: 'Commit branch to associate with the deployment'
required: true
commitMessage:
description: 'Commit message to associate with the deployment'
required: true
outputs:
deployment-url:
description: "Deployment URL"
value: ${{ steps.upload_middleware.outputs.deployment-url }}
runs:
using: 'composite'
steps:
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
shell: bash
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Load secret
uses: 1password/load-secrets-action@v2
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ inputs.opServiceAccount }}
GITBOOK_URL: ${{ inputs.opItem }}/GITBOOK_URL
GITBOOK_ICONS_URL: ${{ inputs.opItem }}/GITBOOK_ICONS_URL
GITBOOK_ICONS_TOKEN: ${{ inputs.opItem }}/GITBOOK_ICONS_TOKEN
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY: ${{ inputs.opItem }}/NEXT_SERVER_ACTIONS_ENCRYPTION_KEY
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
GITBOOK_IMAGE_RESIZE_URL: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_URL
GITBOOK_IMAGE_RESIZE_MODE: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_MODE
GITBOOK_ASSETS_PREFIX: ${{ inputs.opItem }}/GITBOOK_ASSETS_PREFIX
GITBOOK_FONTS_URL: ${{ inputs.opItem }}/GITBOOK_FONTS_URL
- name: Build worker
run: bun run turbo build:cloudflare
env:
GITBOOK_RUNTIME: cloudflare
shell: bash
- name: Upload the DO worker
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: deploy --config ./packages/gitbook/openNext/customWorkers/doWrangler.jsonc
- id: upload_server
name: Upload server to Cloudflare
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/defaultWrangler.jsonc
- name: Extract server version worker ID
shell: bash
id: extract_server_version_id
run: |
version_id=$(echo '${{ steps.upload_server.outputs.command-output }}' | grep "Worker Version ID" | awk '{print $4}')
echo "version_id=$version_id" >> $GITHUB_OUTPUT
- name: Run updateWrangler scripts
shell: bash
run: |
bun run ./packages/gitbook/openNext/customWorkers/script/updateWrangler.ts ${{ steps.extract_server_version_id.outputs.version_id }}
- id: upload_middleware
name: Upload middleware to Cloudflare
uses: cloudflare/wrangler-action@v3.14.0
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
environment: ${{ inputs.environment }}
command: ${{ format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook/openNext/customWorkers/middlewareWrangler.jsonc
- name: Extract middleware version worker ID
shell: bash
id: extract_middleware_version_id
run: |
version_id=$(echo '${{ steps.upload_middleware.outputs.command-output }}' | grep "Worker Version ID" | awk '{print $4}')
echo "version_id=$version_id" >> $GITHUB_OUTPUT
- name: Deploy server and middleware to Cloudflare
if: ${{ inputs.deploy == 'true' }}
uses: ./.github/actions/gradual-deploy-cloudflare
with:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
opServiceAccount: ${{ inputs.opServiceAccount }}
opItem: ${{ inputs.opItem }}
environment: ${{ inputs.environment }}
serverVersionId: ${{ steps.extract_server_version_id.outputs.version_id }}
middlewareVersionId: ${{ steps.extract_middleware_version_id.outputs.version_id }}
deploy: ${{ inputs.deploy }}
- name: Outputs
shell: bash
env:
DEPLOYMENT_URL: ${{ steps.upload_middleware.outputs.deployment-url }}
run: |
echo "URL: ${{ steps.upload_middleware.outputs.deployment-url }}"
echo "Output server: ${{ steps.upload_server.outputs.command-output }}"
@@ -1,80 +0,0 @@
name: 'Deploy vercel'
description: 'Deploy GitBook to Vercel'
inputs:
vercelOrg:
description: 'Vercel organization'
required: true
vercelProject:
description: 'Vercel project'
required: true
vercelToken:
description: 'Vercel token'
required: true
opItem:
description: '1Password item to load secrets from'
required: true
opServiceAccount:
description: '1Password service account token'
required: true
environment:
description: 'Environment to deploy to'
required: true
outputs:
deployment-url:
description: "Deployment URL"
value: ${{ steps.deploy.outputs.deployment-url }}
runs:
using: 'composite'
steps:
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
shell: bash
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Pull Vercel Environment Information
run: bun run vercel pull --yes --environment=${{ inputs.environment }} --token=${{ inputs.vercelToken }}
shell: bash
env:
VERCEL_ORG_ID: ${{ inputs.vercelOrg }}
VERCEL_PROJECT_ID: ${{ inputs.vercelProject }}
- name: Load secret
uses: 1password/load-secrets-action@v2
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ inputs.opServiceAccount }}
GITBOOK_URL: ${{ inputs.opItem }}/GITBOOK_URL
GITBOOK_ICONS_URL: ${{ inputs.opItem }}/GITBOOK_ICONS_URL
GITBOOK_ICONS_TOKEN: ${{ inputs.opItem }}/GITBOOK_ICONS_TOKEN
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
GITBOOK_IMAGE_RESIZE_URL: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_URL
GITBOOK_IMAGE_RESIZE_MODE: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_MODE
GITBOOK_ASSETS_PREFIX: ${{ inputs.opItem }}/GITBOOK_ASSETS_PREFIX
GITBOOK_FONTS_URL: ${{ inputs.opItem }}/GITBOOK_FONTS_URL
- name: Build Project Artifacts
run: bun run vercel build --target=${{ inputs.environment }} --token=${{ inputs.vercelToken }}
shell: bash
env:
VERCEL_ORG_ID: ${{ inputs.vercelOrg }}
VERCEL_PROJECT_ID: ${{ inputs.vercelProject }}
GITBOOK_RUNTIME: vercel
- name: Deploy Project Artifacts to Vercel
id: deploy
shell: bash
run: |
DEPLOYMENT_URL=$(bun run vercel deploy --prebuilt --target=${{ inputs.environment }} --token=${{ inputs.vercelToken }})
echo "deployment-url=$DEPLOYMENT_URL" >> "$GITHUB_OUTPUT"
env:
VERCEL_ORG_ID: ${{ inputs.vercelOrg }}
VERCEL_PROJECT_ID: ${{ inputs.vercelProject }}
- name: Outputs
shell: bash
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
-9
View File
@@ -1,9 +0,0 @@
name: 'Setup Bun'
description: 'Install Bun and cache dependencies'
runs:
using: 'composite'
steps:
- name: Setup bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: 'package.json'
-68
View File
@@ -1,68 +0,0 @@
name: CI
on:
pull_request:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
format:
runs-on: ubuntu-latest
name: Format
timeout-minutes: 6
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- run: bun format:check
test:
runs-on: ubuntu-latest
name: Test
timeout-minutes: 6
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- run: bun unit
build-oss:
# CI to check that the repository builds correctly on a machine without the credentials
runs-on: ubuntu-latest
name: Build (Open Source)
timeout-minutes: 6
env:
NPM_TOKEN_READONLY: ''
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- run: bun run build
typecheck:
runs-on: ubuntu-latest
name: Typecheck
timeout-minutes: 6
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- run: bun typecheck
-202
View File
@@ -1,202 +0,0 @@
name: Preview
on:
pull_request:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v2-vercel:
name: Deploy v2 to Vercel (preview)
runs-on: ubuntu-latest
environment:
name: 2v-preview
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy to Vercel
id: deploy
uses: ./.github/composite/deploy-vercel
with:
environment: preview
vercelOrg: ${{ secrets.VERCEL_ORG_ID }}
vercelProject: ${{ secrets.VERCEL_PROJECT_ID }}
vercelToken: ${{ secrets.VERCEL_TOKEN }}
opItem: op://gitbook-open/2v-preview
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
deploy-v2-cloudflare:
name: Deploy v2 to Cloudflare Worker (preview)
runs-on: ubuntu-latest
environment:
name: 2c-preview
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url || steps.extract-worker-id.outputs.worker-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy to Cloudflare
id: deploy
uses: ./.github/composite/deploy-cloudflare
with:
environment: preview
deploy: ${{ github.ref == 'refs/heads/main' }}
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
opItem: op://gitbook-open/2c-preview
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
commitTag: ${{ github.ref == 'refs/heads/main' && 'main' || format('pr{0}', github.event.pull_request.number) }}
commitMessage: ${{ github.sha }}
- name: Extract Worker ID
id: extract-worker-id
if: ${{ !steps.deploy.outputs.deployment-url }}
run: |
if [[ "${{ steps.deploy.outputs.command-output }}" =~ Worker\ Version\ ID:\ ([0-9a-f]{8})-([0-9a-f-]+) ]]; then
WORKER_ID_FIRST_PART="${BASH_REMATCH[1]}"
echo "worker-url=https://${WORKER_ID_FIRST_PART}-gitbook-open-v2-preview.gitbook.workers.dev/" >> $GITHUB_OUTPUT
fi
- name: Outputs
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url || steps.extract-worker-id.outputs.worker-url }}"
comment-deployments:
runs-on: ubuntu-latest
name: Comment Deployments (preview)
if: always() && !startsWith(github.ref, 'refs/heads/main')
needs:
- deploy-v2-vercel
- deploy-v2-cloudflare
steps:
- name: Find GitHub Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: 'Summary of the deployments'
- name: Create or update GitHub comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Summary of the deployments:
| Version | URL | Status |
| --- | --- | --- |
| Vercel | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}](${{ needs.deploy-v2-vercel.outputs.deployment-url }}) | ${{ needs.deploy-v2-vercel.result == 'success' && '✅' || '❌' }} |
| Cloudflare | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}) | ${{ needs.deploy-v2-cloudflare.result == 'success' && '✅' || '❌' }} |
### Test content
| Site | `2v` | `2c` |
| --- | --- | --- |
| GitBook | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.com/docs) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.com/docs) |
| E2E | [${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) | [${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open](${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/gitbook.gitbook.io/test-gitbook-open) |
edit-mode: replace
visual-testing-v2-vercel:
runs-on: ubuntu-latest
name: Visual Testing v2
needs: deploy-v2-vercel
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e
env:
BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'v2-vercel'
visual-testing-v2-cloudflare:
runs-on: ubuntu-latest
name: Visual Testing v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e
env:
BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'v2-cloudflare'
visual-testing-customers-v2:
runs-on: ubuntu-latest
name: Visual Testing Customers v2
needs: deploy-v2-vercel
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e-customers
env:
BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'customers-v2'
visual-testing-customers-v2-cloudflare:
runs-on: ubuntu-latest
name: Visual Testing Customers v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run Playwright tests
run: bun e2e-customers
env:
BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}
SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_BUILD_NAME: 'customers-v2'
pagespeed-testing-v2:
runs-on: ubuntu-latest
name: PageSpeed Testing v1
needs: deploy-v2-vercel
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Run pagespeed tests
run: bun ./packages/gitbook/tests/pagespeed-testing.ts
env:
BASE_URL: ${{needs.deploy-v2-vercel.outputs.deployment-url}}
PAGESPEED_API_KEY: ${{ secrets.PAGESPEED_API_KEY }}
-55
View File
@@ -1,55 +0,0 @@
name: Production
on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v2-vercel:
name: Deploy v2 to Vercel (production)
runs-on: ubuntu-latest
environment:
name: 2v-production
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy
id: deploy
uses: ./.github/composite/deploy-vercel
with:
environment: production
vercelOrg: ${{ secrets.VERCEL_ORG_ID }}
vercelProject: ${{ secrets.VERCEL_PROJECT_ID }}
vercelToken: ${{ secrets.VERCEL_TOKEN }}
opItem: op://gitbook-open/2v-production
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
deploy-v2-cloudflare:
name: Deploy v2 to Cloudflare Worker (production)
runs-on: ubuntu-latest
environment:
name: 2c-production
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy
id: deploy
uses: ./.github/composite/deploy-cloudflare
with:
environment: production
deploy: true
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
opItem: op://gitbook-open/2c-production
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
commitTag: main
commitMessage: ${{ github.sha }}
- name: Outputs
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
-55
View File
@@ -1,55 +0,0 @@
name: Staging
on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
jobs:
deploy-v2-vercel:
name: Deploy v2 to Vercel (staging)
runs-on: ubuntu-latest
environment:
name: 2v-staging
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy
id: deploy
uses: ./.github/composite/deploy-vercel
with:
environment: staging
vercelOrg: ${{ secrets.VERCEL_ORG_ID }}
vercelProject: ${{ secrets.VERCEL_PROJECT_ID }}
vercelToken: ${{ secrets.VERCEL_TOKEN }}
opItem: op://gitbook-open/2v-staging
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
deploy-v2-cloudflare:
name: Deploy v2 to Cloudflare Worker (staging)
runs-on: ubuntu-latest
environment:
name: 2c-staging
url: ${{ steps.deploy.outputs.deployment-url }}
outputs:
deployment-url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy
id: deploy
uses: ./.github/composite/deploy-cloudflare
with:
environment: staging
deploy: true
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
opItem: op://gitbook-open/2c-staging
opServiceAccount: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
commitTag: main
commitMessage: ${{ github.sha }}
- name: Outputs
run: |
echo "URL: ${{ steps.deploy.outputs.deployment-url }}"
-42
View File
@@ -1,42 +0,0 @@
name: Publish
on:
push:
branches:
- main
env:
NPM_TOKEN_READONLY: ${{ secrets.NPM_TOKEN_READONLY }}
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish:
name: Publish
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0
- name: Setup Bun
uses: ./.github/composite/setup-bun
- name: Install dependencies
run: bun install --frozen-lockfile
env:
PUPPETEER_SKIP_DOWNLOAD: 1
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
publish: npm run release
version: npm run changeset-version
env:
# Using a PAT instead of GITHUB_TOKEN because we need to run workflows when releases are created
# https://github.com/orgs/community/discussions/26875#discussioncomment-3253761
GITHUB_TOKEN: ${{ secrets.GH_PERSONAL_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+27 -23
View File
@@ -1,27 +1,31 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# Logs
logs
*.log
# dependencies
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules
/.pnp
.pnp.js
# misc
.DS_Store
*.pem
theme/javascript/vendors
theme/vendors
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Turbo
.turbo
# Vercel
.vercel
# Env files
.env.local
# TypeScript
*.tsbuildinfo
# vim swapfile
*.swp
-1
View File
@@ -1 +0,0 @@
v20.6
+4
View File
@@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.11"
- "0.10"
-3
View File
@@ -1,3 +0,0 @@
{
"recommendations": ["biomejs.biome"]
}
-21
View File
@@ -1,21 +0,0 @@
{
"tailwindCSS.experimental.classRegex": [
["Style \\=([^;]*);", "'([^']*)'"],
["Style \\=([^;]*);", "\"([^\"]*)\""],
["Style \\=([^;]*);", "\\`([^\\`]*)\\`"],
["style \\=([^;]*);", "'([^']*)'"],
["style \\=([^;]*);", "\"([^\"]*)\""],
["style \\=([^;]*);", "\\`([^\\`]*)\\`"]
],
"tailwindCSS.classAttributes": ["class", "className", "style", ".*Style"],
"prettier.enable": false,
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit",
"source.fixAll.biome": "explicit"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
+18
View File
@@ -0,0 +1,18 @@
Authors
=======
Also see https://github.com/GitbookIO/gitbook/graphs/contributors.
Names below are ordered by first contribution.
Author
------
- Samy Pessé <samypesse@gmail.com>
- Aaron O'Mullan <aaron.omullan@gmail.com>
Contributors
------------
- Nijiko Yonskai
- Herman Starikov
+58
View File
@@ -0,0 +1,58 @@
# Release notes
## 1.2.0
- Improvements on ebook generation
- Fix incorrect follow of links in ebook generation
- Move Table of Contents at the beginning of the ebook
- Update to last highlight.js (includes Swift)
- Includes of templates and variables (from book.json)
## 1.1.1
- Rewrite quiz logic to be more robust
- Improve integration of glossary
- Improve generation of ebook by using a multiple HTML pages input source
- Fix incorrect page breaks after h1 and h2 divs
- New options to set header and footer in PDF generation
## 1.1.0
- Plugins can now extend the ebook generation (pdf, epub, mobi)
- Update `kramed` to version 0.4.3
## 1.0.3
- Update `mathjax` plugin and MathJAx to version 2.4
- Update `highlight.js` to 8.2.0
## 1.0.2
- Update `mathjax` plugin, fixes issues with inline math rendering (no longer wanted)
## 1.0.1
- New inline math convention (kramdown's), using `$$` rather than `$` as delimiters
- Fix instapaper sharing
- The `exercises` & `quizzes` plugins are now by default
## 1.0.0
- New design
- Support for glossary
- Support for sharing to instapaper
- Support for footnotes
## 0.7.1
- Update `fs-extra` to `0.10.0` (fixes potential race conditions)
## 0.7.0
- Add page break in ebook (pdf, epub, mobi) between chapters/articles
- Start using kramed instead of marked
- Fix display of inline math
- Switch to graceful-fs to fix EMFILE errors
- Add sharing to weibo.com
## 0.6.2
- Support generating a plugin's book info dynamically
- Improve navigation on dark theme
- Improve path normalization when parsing SUMMARY.md
## 0.6.0
- Generate header id the same as github
- Custom links can be added at top of sidebar
- Summary can now be transformed by plugins
- Support importing code snippets
+96
View File
@@ -0,0 +1,96 @@
module.exports = function (grunt) {
var path = require("path");
// Load NPM tasks
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.loadNpmTasks("grunt-bower-install-simple");
// Init GRUNT configuraton
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
'bower-install-simple': {
options: {
color: true,
production: false,
directory: "theme/vendors"
}
},
less: {
development: {
options: {
compress: true,
yuicompress: true,
optimization: 2
},
files: {
"theme/assets/style.css": "theme/stylesheets/website.less",
"theme/assets/print.css": "theme/stylesheets/ebook.less"
}
}
},
requirejs: {
compile: {
options: {
name: "gitbook",
baseUrl: "theme/javascript/",
out: "theme/assets/app.js",
preserveLicenseComments: false,
optimize: "uglify",
include: ["requireLib"],
paths: {
"jQuery": '../vendors/jquery/dist/jquery',
"lodash": '../vendors/lodash/dist/lodash',
"requireLib": '../vendors/requirejs/require',
"Mousetrap": '../vendors/mousetrap/mousetrap',
"lunr": '../vendors/lunr.js/lunr',
"URIjs": '../vendors/URIjs/src/',
"ace": '../vendors/ace-builds/src-noconflict/'
},
shim: {
'jQuery': {
exports: '$'
},
'lodash': {
exports: '_'
},
'Mousetrap': {
exports: 'Mousetrap'
},
'lunr': {
exports: 'lunr'
}
}
}
}
},
copy: {
vendors: {
files: [
{
expand: true,
cwd: 'theme/vendors/fontawesome/fonts/',
src: ['**'],
dest: 'theme/assets/fonts/fontawesome/',
filter: 'isFile'
}
]
}
}
});
grunt.registerTask("bower-install", [ "bower-install-simple" ]);
// Build
grunt.registerTask('build', [
'bower-install',
'less',
'requirejs',
'copy:vendors'
]);
grunt.registerTask('default', [
'build'
]);
};
+201 -674
View File
@@ -1,674 +1,201 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014 FriendCode Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+198 -127
View File
@@ -1,176 +1,247 @@
<h1 align="center">GitBook</h1>
GitBook
=======
<p align="center">
<a href="https://gitbook.com/docs/">Docs</a> - <a href="https://github.com/GitbookIO/community">Community</a> - <a href="https://developer.gitbook.com/">Developer Docs</a> - <a href="https://changelog.gitbook.com/">Changelog</a> - <a href="https://github.com/GitbookIO/gitbook/issues/new?assignees=&labels=bug&template=bug_report.md">Bug reports</a> - <a href="https://github.com/orgs/GitbookIO/discussions/categories/feature-requests">Feature requests</a>
</p>
[![Build Status](https://travis-ci.org/GitbookIO/gitbook.png?branch=master)](https://travis-ci.org/GitbookIO/gitbook)
[![NPM version](https://badge.fury.io/js/gitbook.svg)](http://badge.fury.io/js/gitbook)
<p align="center">
<a href="https://gitbook.com"><img src="https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=3F89A1"></a>
<a href="#"><img src="https://img.shields.io/badge/Open_Source-❤️-FDA599?"/></a>
<a href="/LICENSE"><img src="https://img.shields.io/badge/License-GNU_GPLv3-F4E28D"/></a>
<a href="/.github/CONTRIBUTING.md"><img src="https://img.shields.io/github/contributors/gitbookIO/gitbook"/></a>
<a href="https://github.com/gitbookIO/gitbook/issues"><img src="https://img.shields.io/github/issues/gitbookIO/gitbook"/></a>
</p>
GitBook is a command line tool (and Node.js library) for building beautiful books using GitHub/Git and Markdown. Here is an example: [Learn Javascript](https://www.gitbook.io/book/GitBookIO/javascript). You can publish book easily online using [gitbook.io](https://www.gitbook.io) and an [editor](https://github.com/GitbookIO/editor) is available for Windows, Mac and Linux. You can follow [@GitBookIO](https://twitter.com/GitBookIO) on Twitter. Complete documentation is available at [help.gitbook.io](http://help.gitbook.io/).
<p align="center">Welcome to GitBook, the platform for managing technical knowledge for teams.</p>
![Image](https://raw.github.com/GitbookIO/gitbook/master/preview.png)
<p align="center">This repository contains the open source code used to render GitBook's published content.</p>
## How to use it:
<p align="center">
<img alt="GitBook Open Published Site" src="./assets/published-site.png">
</p>
## Table of Contents
- [Getting Started](#getting-started)
- [Contributing](#contributing)
- [Types of contributions](#types-of-contributions)
- [Licensing](#license)
- [Acknowledgements](#acknowledgements)
- [Legacy GitBook](#legacy-gitbook-deprecated)
## Getting Started
To run a local version of this project, please follow these simple steps.
### Prerequisites
- Node.js (Version: >=20.6)
- Use nvm for easy Node management
- [Bun](https://bun.sh/) (Version: >=1.2.15)
- We use a text-based lockfile which isn't supported below 1.2.15
### Set up
1. Clone the repo into a **public** GitHub repository. If you plan to distribute the code, keep the source code public to comply with GNU GPLv3. To clone in a private repository, acquire a [commercial license](https://www.gitbook.com/pricing).
GitBook can be installed from **NPM** using:
```
git clone https://github.com/gitbookIO/gitbook.git
$ npm install gitbook -g
```
2. Ensure you are using the project's version of `node`. Running `nvm use` will change your local version to the correct one.
3. Install the project's dependencies through Bun.
You can serve a repository as a book using:
```
bun install
$ gitbook serve ./repository
```
4. Start your local development server.
Or simply build the static website using:
```
bun dev
$ gitbook build ./repository --output=./outputFolder
```
6. Open a published GitBook space in your web browser, prefixing it with `http://localhost:3000/url`.
examples:
- http://localhost:3000/url/gitbook.com/docs
- http://localhost:3000/url/open-source.gitbook.io/midjourney
Any published GitBook site can be accessed through your local development instance, and any updates you make to the codebase will be reflected in your browser.
### CI and testing
All pull-requests will be tested against both visual and performances testing to prevent regressions.
## Fonts and Icons
GitBook Open uses fontawesome. During development, your local environment will use the free version. However, only the pro version will be accepted by CI. If you see the following error:
Options for commands `build` and `serve` are:
```
The GitBook icon is missing. It indicates that the dependencies were installed without the correct font-awesome package. These changes have probably been persisted in the Bun lockfile. Read the README for more information.
-o, --output <directory> Path to output directory, defaults to ./_book
-f, --format <name> Change generation format, defaults to site, availables are: site, page, ebook, json
--config <config file> Configuration file to use, defaults to book.js or book.json
```
It means that you've changed the GBO dependencies and bundled in the free version. Only GitBook staff can help with this - if you're not on the GitBook team, please ping us in the PR and we'll help get things moving.
GitBook loads the default configuration from a `book.json` file in the repository if it exists.
If you are GitBook staff, you'll need our NPM token in your local environment.
Here are the options that can be stored in this file:
```
.env.local
```js
{
// Folders to use for output
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"output": null,
NPM_TOKEN_READONLY=xxx
// Generator to use for building
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"generator": "site",
// Book title and description (defaults are extracted from the README)
"title": null,
"description": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"extension": null,
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
// Global configuration for plugins
"pluginsConfig": {
"fontSettings": {
"theme": "sepia", "night" or "white",
"family": "serif" or "sans",
"size": 1 to 4
}
},
// Variables for templating
"variables": {},
// Links in template (null: default, false: remove, string: new value)
"links": {
// Custom links at top of sidebar
"sidebar": {
"Custom link name": "https://customlink.com"
},
// Sharing links
"sharing": {
"google": null,
"facebook": null,
"twitter": null,
"weibo": null,
"all": null
}
},
// Options for PDF generation
"pdf": {
// Add page numbers to the bottom of every page
"pageNumbers": false,
// Font size for the fiel content
"fontSize": 12,
// Paper size for the pdf
// Choices are [ua0, ua1, ua2, ua3, ua4, ua5, ua6, ub0, ub1, ub2, ub3, ub4, ub5, ub6, ulegal, uletter]
"paperSize": "a4",
// Margin (in pts)
// Note: 72 pts equals 1 inch
"margin": {
"right": 62,
"left": 62,
"top": 36,
"bottom": 36
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": null,
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": null
}
}
```
and then reinstall dependencies.
You can publish your books to our index by visiting [GitBook.io](http://www.gitbook.io)
## Contributing
## Output Formats
GitBook's rendering engine is fully open source and built on top of [Next.js](https://nextjs.org/). Head to our [contributing guide](https://github.com/GitbookIO/gitbook/blob/main/.github/CONTRIBUTING.md) to learn more about the workflow on adding your first Pull Request.
GitBook can generate your book in the following formats:
### Types of contributions
* **Static Website**: This is the default format. It generates a complete interactive static website that can be, for example, hosted on GitHub Pages.
* **eBook**: A complete eBook with exercise solutions at the end of the book. Generate this format using: ```gitbook ebook ./myrepo```. You need to have [ebook-convert](http://manual.calibre-ebook.com/cli/ebook-convert.html) installed. The output format could be **PDF**, **ePub** or **MOBI**.
* **JSON**: This format is used for debugging or extracting metadata from a book. Generate this format using: ```gitbook build ./myrepo -f json```.
We encourage you to contribute to GitBook to help us build the best tool for documenting technical knowledge. If you're looking for some quick ways to contribute, continue reading to learn more about popular contributions.
## Book Format
#### Translations
A book is a Git repository containing at least 2 files: `README.md` and `SUMMARY.md`.
The GitBook UI is rendered using a set of translation files found in [`packages/gitbook/src/intl/translations`](/packages/gitbook/src/intl/translations/). We welcome all additional translations for the UI.
#### README.md
#### Bugs
Typically, this should be the introduction for your book. It will be automatically added to the final summary.
Encounter a bug or find an issue you'd like to fix? Helping us fix issues related to GitBook greatly improves the experience for everyone. Head to the issues section of this repository to learn more about the types of bugs you can already help out with.
#### SUMMARY.md
## Deployment
The `SUMMARY.md` defines your book's structure. It should contain a list of chapters, linking to their respective pages.
> [!WARNING]
> While it is possible to self-host this project, we do not recommend this unless you are certain this option fits your need.
>
> _Looking to add a specific feature in GitBook? Head to our [contributing guide](https://github.com/GitbookIO/gitbook/blob/main/.github/CONTRIBUTING.md) to get started._
>
> Self-hosting this project puts the responsibility of maintaining and merging future updates on **you**. We cannot guarantee support, maintenance, or updates to forked and self-hosted instances of this project.
>
> We want to make it as easy as possible for our community to collaborate and push the future of GitBook, which is why we encourage you to contribute to our product directly instead of creating your own version.
Example:
This project allows you to self-host the rendering portion of your GitBook published content. Self-hosting has pros and cons.
```markdown
# Summary
On the pro side, you can customize the look and feel of your content, and better embed your documentation in your application.
This is the summary of my book.
On the con side, you become responsible for the reliability of your published site, and keeping the renderer up-to-date with the changes made to the GitBook platform.
## License
Distributed under the [GNU GPLv3 License](https://github.com/GitBookIO/gitbook/blob/main/LICENSE).
If you plan to distribute the code, you must make the source code public to comply with the GNU GPLv3. To clone in a private repository, acquire a [commercial license](https://www.gitbook.com/pricing).
See `LICENSE` for more information.
## Badges
<p align="left">
<a href="https://gitbook.com"><img src="https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=3F89A1"></a>
<a href="https://gitbook.com"><img src="https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=F4E28D"></a>
<a href="https://gitbook.com"><img src="https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=FDA599"></a>
</p>
```md
[![GitBook](https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=3F89A1)](https://www.gitbook.com/preview?utm_source=gitbook_readme_badge&utm_medium=organic&utm_campaign=preview_documentation&utm_content=link)
* [section 1](section1/README.md)
* [example 1](section1/example1.md)
* [example 2](section1/example2.md)
* [section 2](section2/README.md)
* [example 1](section2/example1.md)
```
```html
<a href="https://www.gitbook.com/preview?utm_source=gitbook_readme_badge&utm_medium=organic&utm_campaign=preview_documentation&utm_content=link">
<img
src="https://img.shields.io/static/v1?message=Documented%20on%20GitBook&logo=gitbook&logoColor=ffffff&label=%20&labelColor=5c5c5c&color=3F89A1"
/>
</a>
Files that are not included in `SUMMARY.md` will not be processed by `gitbook`.
#### Multi-Languages
GitBook supports building books written in multiple languages. Each language should be a sub-directory following the normal GitBook format, and a file named `LANGS.md` should be present at the root of the repository with the following format:
```markdown
* [English](en/)
* [French](fr/)
* [Español](es/)
```
## Acknowledgements
You can see a complete example with the [Learn Git](https://github.com/GitbookIO/git) book.
GitBook wouldn't be possible without these projects:
#### Glossary
- [Next.js](https://nextjs.org/)
- [Bun](https://bun.sh/)
- [Tailwind CSS](https://tailwindcss.com/)
- [Framer Motion](https://www.npmjs.com/package/framer-motion)
Allows you to specify terms and their respective definitions to be displayed in the glossary. Based on those terms, `gitbook` will automatically build an index and highlight those terms in pages.
## Contributors
The `GLOSSARY.md` format is very simple :
<a href="https://github.com/gitbookIO/gitbook/graphs/contributors">
<img src="https://contrib.rocks/image?repo=gitbookIO/gitbook" />
</a>
```markdown
# term
Definition for this term
## Legacy GitBook (Deprecated)
# Another term
With it's definition, this can contain bold text and all other kinds of inline markup ...
Our previous version of GitBook and it's CLI tool are now deprecated. You can still view the old repository and it's commits on this [branch](https://github.com/GitbookIO/gitbook/tree/legacy).
```
#### Ignoring files & folders
GitBook will read the `.gitignore`, `.bookignore` and `.ignore` files to get a list of files and folders to skip. (The format inside those files follows the same convention as `.gitignore`).
Best practices for the `.gitignore` is to ignore build files from **node.js** (`node_modules`, ...) and build files from GitBook: `_book`, `*.epub`, `*.mobi` and `*.pdf` ([Download GitBook.gitignore](https://github.com/github/gitignore/blob/master/GitBook.gitignore)).
#### Cover
A cover image can be set by creating a file: **/cover.jpg**.
The best resolution is **1800x2360**. The generation of the cover can be done automatically using the plugin [autocover](https://github.com/GitbookIO/plugin-autocover).
A small version of the cover can also be set by creating a file: **/cover_small.jpg**.
#### Publish your book
The platform [GitBook.io](https://www.gitbook.io/) is like an "Heroku for books": you can create a book on it (public, paid, or private) and update it using **git push**.
#### Plugins
Plugins can be used to extend your book's functionality. Read [GitbookIO/plugin](https://github.com/GitbookIO/plugin) for more information about how to build a plugin for GitBook.
##### Official plugins:
| Name | Description |
| ----- | ---- |
| [exercises](https://github.com/GitbookIO/plugin-exercises) | Add interactive exercises to your book. |
| [quizzes](https://github.com/GitbookIO/plugin-quizzes) | Add interactive quizzes to your book. |
| [mathjax](https://github.com/GitbookIO/plugin-mathjax) | Displays mathematical notation in the book. |
| [mixpanel](https://github.com/GitbookIO/plugin-mixpanel) | Mixpanel tracking for your book |
| [infinitescroll](https://github.com/GitbookIO/gitbook-plugin-infinitescroll) | Infinite Scrolling |
##### Other plugins:
| Name | Description |
| ----- | ---- |
| [Google Analytics](https://github.com/GitbookIO/plugin-ga) | Google Analytics tracking for your book |
| [Disqus](https://github.com/GitbookIO/plugin-disqus) | Disqus comments integration in your book |
| [Autocover](https://github.com/GitbookIO/plugin-autocover) | Generate a cover for your book |
| [Transform annoted quotes to notes](https://github.com/erixtekila/gitbook-plugin-richquotes) | Allow extra markdown markup to render blockquotes as nice notes |
| [Send code to console](https://github.com/erixtekila/gitbook-plugin-toconsole) | Evaluate javascript block in the browser inspector's console |
| [Revealable sections](https://github.com/mrpotes/gitbook-plugin-reveal) | Reveal sections of the page using buttons made from the first title in each section |
| [Markdown within HTML](https://github.com/mrpotes/gitbook-plugin-nestedmd) | Process markdown within HTML blocks - allows custom layout options for individual pages |
| [Bootstrap JavaScript plugins](https://github.com/mrpotes/gitbook-plugin-bootstrapjs) | Use the [Bootstrap JavaScript plugins](http://getbootstrap.com/javascript) in your online GitBook |
| [Piwik Open Analytics](https://github.com/emmanuel-keller/gitbook-plugin-piwik) | Piwik Open Analytics tracking for your book |
| [Heading Anchors](https://github.com/rlmv/gitbook-plugin-anchors) | Add linkable Github-style anchors to headings |
| [JSBin](https://github.com/jcouyang/gitbook-plugin-jsbin) | Embedded jsbin frame into your book |
#### Debugging
You can use the environment variable `DEBUG=true` to get better error messages (with stack trace). For example:
```
$ export DEBUG=true
$ gitbook build ./
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 KiB

+63
View File
@@ -0,0 +1,63 @@
var path = require('path');
var Q = require('q');
var _ = require('lodash');
var fs = require('fs');
var utils = require('./utils');
var generate = require("../lib/generate");
var parse = require("../lib/parse");
var generators = require("../lib/generate").generators;
var buildCommand = function(command) {
return command
.option('-v, --verbose', 'Activate verbose mode, useful for debugging errors')
.option('-o, --output <directory>', 'Path to output directory, defaults to ./_book')
.option('-f, --format <name>', 'Change generation format, defaults to site, availables are: '+_.keys(generators).join(", "))
.option('--config <config file>', 'Configuration file to use, defaults to book.js or book.json');
};
var buildEbookCommand = function(command) {
return buildCommand(command)
.option('-c, --cover <path>', 'Cover image, default is cover.jpg if exists');
};
var makeBuildFunc = function(converter) {
return function(dir, options) {
dir = dir || process.cwd();
outputDir = options.output;
// Set debugging
if(options.verbose) {
process.env.DEBUG = "true";
}
console.log('Starting build ...');
return converter(
_.extend({}, options || {}, {
input: dir,
output: outputDir,
generator: options.format,
configFile: options.config
})
)
.then(function(output) {
console.log("Successfully built!");
return output;
})
.fail(function(err) {
// Log error
utils.logError(err);
// Exit process with failure code
process.exit(-1);
});
};
};
module.exports = {
folder: makeBuildFunc(generate.folder),
file: makeBuildFunc(generate.file),
command: buildCommand,
commandEbook: buildEbookCommand
};
Executable
+137
View File
@@ -0,0 +1,137 @@
#! /usr/bin/env node
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var prog = require('commander');
var tinylr = require('tiny-lr-fork');
var pkg = require('../package.json');
var generators = require("../lib/generate").generators;
var initDir = require("../lib/generate/init");
var fs = require('../lib/generate/fs');
var utils = require('./utils');
var build = require('./build');
var Server = require('./server');
var platform = require("./platform");
// General options
prog
.version(pkg.version);
build.command(prog.command('build [source_dir]'))
.description('Build a gitbook from a directory')
.action(build.folder);
build.command(prog.command('serve [source_dir]'))
.description('Build then serve a gitbook from a directory')
.option('-p, --port <port>', 'Port for server to listen on', 4000)
.option('--no-watch', 'Disable restart with file watching')
.action(function(dir, options) {
var server = new Server();
// init livereload server
var lrOptions = {port: 35729};
var lrServer = tinylr(lrOptions);
var lrPath = undefined;
lrServer.listen(lrOptions.port, function(err) {
if (err) { return console.log(err); }
console.log('Live reload server started on port: ' + lrOptions.port);
});
var generate = function() {
if (server.isRunning()) console.log("Stopping server");
server.stop()
.then(function() {
return build.folder(dir, _.extend(options || {}, {
defaultsPlugins: ["livereload"]
}));
})
.then(function(_options) {
console.log();
console.log('Starting server ...');
return server.start(_options.output, options.port)
.then(function() {
console.log('Serving book on http://localhost:'+options.port);
if (lrPath) {
// trigger livereload
lrServer.changed({body:{files:[lrPath]}})
}
if (!options.watch) return;
return utils.watch(_options.input)
.then(function(filepath) {
// set livereload path
lrPath = filepath;
console.log("Restart after change in files");
console.log('');
return generate();
})
})
})
.fail(utils.logError);
};
console.log('Press CTRL+C to quit ...');
console.log('')
generate();
});
build.commandEbook(prog.command('pdf [source_dir]'))
.description('Build a gitbook as a PDF')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
extension: "pdf",
format: "ebook"
}));
});
build.commandEbook(prog.command('epub [source_dir]'))
.description('Build a gitbook as a ePub book')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
extension: "epub",
format: "ebook"
}));
});
build.commandEbook(prog.command('mobi [source_dir]'))
.description('Build a gitbook as a Mobi book')
.action(function(dir, options) {
build.file(dir, _.extend(options, {
extension: "mobi",
format: "ebook"
}));
});
prog
.command('init [source_dir]')
.description('Create files and folders based on contents of SUMMARY.md')
.action(function(dir) {
dir = dir || process.cwd();
return initDir(dir);
});
prog
.command('publish [source_dir]')
.description('Publish content to the associated gitbook.io book')
.action(function(dir) {
dir = dir || process.cwd();
return platform.publish(dir);
});
prog
.command('git:remote [source_dir] [book_id]')
.description('Adds a git remote to a book repository')
.action(function(dir, bookId) {
dir = dir || process.cwd();
return platform.remote(dir, bookId);
});
// Parse and fallback to help if no args
if(_.isEmpty(prog.parse(process.argv).args) && process.argv.length === 2) {
prog.help();
}
+53
View File
@@ -0,0 +1,53 @@
var Q = require("q");
var utils = require("./utils");
var publish = function(folder) {
if (!folder) {
console.log("Need a repository folder");
return process.exit(-1);
}
utils.gitCmd("push", ["gitbook", "master"])
.then(function(out) {
console.log(out.stdout);
}, function(err) {
if (err.code == 128) {
console.log("No book on gitbook.io is configured with this git repository.");
console.log("Run 'gitbook git:remote username/book' to intialize this repository.");
} else {
console.log(err.message);
}
process.exit(-1);
});
};
var remote = function(folder, bookId) {
if (!folder || !bookId) {
console.log("Need a repository folder and a book id");
return process.exit(-1);
}
var url = "https://push.gitbook.io/"+bookId+".git";
var addRemote = function() {
return utils.gitCmd("remote", ["add", "gitbook", url]);
}
addRemote()
.fail(function(err) {
if (err.code == 128) {
return utils.gitCmd("remote", ["rm", "gitbook"]).then(addRemote);
}
return Q.reject(err);
})
.then(function(out) {
console.log("Book remote '"+url+"' added to the folder");
}, function(err) {
console.log(err.message);
process.exit(-1);
});
};
module.exports = {
publish: publish,
remote: remote
};
+96
View File
@@ -0,0 +1,96 @@
var Q = require('q');
var _ = require('lodash');
var events = require('events');
var http = require('http');
var send = require('send');
var util = require('util');
var url = require('url');
var Server = function() {
this.running = null;
this.dir = null;
this.port = 0;
this.sockets = [];
};
util.inherits(Server, events.EventEmitter);
// Return true if the server is running
Server.prototype.isRunning = function() {
return this.running != null;
};
// Stop the server
Server.prototype.stop = function() {
var that = this;
if (!this.isRunning()) return Q();
var d = Q.defer();
this.running.close(function(err) {
that.running = null;
that.emit("state", false);
if (err) d.reject(err);
else d.resolve();
});
for (var i = 0; i < this.sockets.length; i++) {
this.sockets[i].destroy();
}
return d.promise;
};
Server.prototype.start = function(dir, port) {
var that = this, pre = Q();
port = port || 8004;
if (that.isRunning()) pre = this.stop();
return pre
.then(function() {
var d = Q.defer();
that.running = http.createServer(function(req, res){
// Render error
function error(err) {
res.statusCode = err.status || 500;
res.end(err.message);
}
// Redirect to directory's index.html
function redirect() {
res.statusCode = 301;
res.setHeader('Location', req.url + '/');
res.end('Redirecting to ' + req.url + '/');
}
// Send file
send(req, url.parse(req.url).pathname)
.root(dir)
.on('error', error)
.on('directory', redirect)
.pipe(res);
});
that.running.on('connection', function (socket) {
that.sockets.push(socket);
socket.setTimeout(4000);
socket.on('close', function () {
that.sockets.splice(that.sockets.indexOf(socket), 1);
});
});
that.running.listen(port, function(err) {
if (err) return d.reject(err);
that.port = port;
that.dir = dir;
that.emit("state", true);
d.resolve();
});
return d.promise;
});
}
module.exports = Server;
+66
View File
@@ -0,0 +1,66 @@
var Q = require('q');
var _ = require('lodash');
var exec = require('child_process').exec;
var http = require('http');
var send = require('send');
var path = require('path');
var Gaze = require('gaze').Gaze;
function watch(dir) {
var d = Q.defer();
dir = path.resolve(dir);
var gaze = new Gaze("**/*.md", {
cwd: dir
});
gaze.once("all", function(e, filepath) {
gaze.close();
d.resolve(filepath);
});
gaze.once("error", function(err) {
gaze.close();
d.reject(err);
});
return d.promise;
}
function logError(err) {
var message = err.message || err;
if (process.env.DEBUG != null) message = err.stack || message;
console.log(message);
return Q.reject(err);
};
function runGitCommand(command, args) {
var d = Q.defer(), child;
args = ["git", command].concat(args).join(" ");
child = exec(args, function (error, stdout, stderr) {
if (error !== null) {
error.stdout = stdout;
error.stderr = stderr;
d.reject(error);
} else {
d.resolve({
stdout: stdout,
stderr: stderr
})
}
});
return d.promise;
};
// Exports
module.exports = {
watch: watch,
logError: logError,
gitCmd: runGitCommand
};
-177
View File
@@ -1,177 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": [
"**/node_modules/**/*",
"**/dist/**/*",
"**/build/**/*",
"**/public/**/*",
"**/.next/**/*",
"**/.open-next/**/*",
"**/.turbo/**/*",
"**/.vercel/**/*",
"**/.cache/**/*",
"**/.wrangler/**/*",
"packages/embed/standalone/**/*",
"packages/openapi-parser/src/fixtures/**/*",
"packages/emoji-codepoints/index.ts",
"packages/icons/src/data/*.json",
"packages/gitbook/worker-configuration.d.ts",
"**/*.css"
]
},
"formatter": {
"enabled": true,
"useEditorconfig": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 4,
"lineEnding": "lf",
"lineWidth": 100,
"attributePosition": "auto",
"bracketSpacing": true
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"performance": {
"noDelete": "warn"
},
"security": {
"noDangerouslySetInnerHtml": "off"
},
"complexity": {
"noForEach": "off",
"noUselessFragments": "warn",
"noBannedTypes": "warn"
},
"correctness": {
"noUndeclaredVariables": "error",
"noUnusedVariables": "error",
"useArrayLiterals": "error",
"useHookAtTopLevel": "error",
"noUnusedImports": "error",
"noVoidElementsWithChildren": "warn",
"useJsxKeyInIterable": "warn",
"useExhaustiveDependencies": "warn",
"noUnknownFunction": "warn"
},
"style": {
"noNonNullAssertion": "warn",
"noParameterAssign": "off",
"useThrowOnlyError": "error"
},
"suspicious": {
"noConsole": {
"level": "warn",
"options": {
"allow": ["assert", "error", "warn"]
}
},
"noExplicitAny": "warn",
"noImplicitAnyLet": "warn",
"noConfusingVoidType": "warn",
"noControlCharactersInRegex": "warn",
"noPrototypeBuiltins": "warn",
"noAssignInExpressions": "warn",
"noArrayIndexKey": "warn"
},
"a11y": {
"useSemanticElements": "warn",
"useKeyWithClickEvents": "warn",
"noSvgWithoutTitle": "warn",
"useButtonType": "warn",
"useIframeTitle": "warn",
"useAltText": "warn",
"noPositiveTabindex": "warn",
"useFocusableInteractive": "warn",
"useAriaPropsForRole": "warn",
"useValidAnchor": "warn",
"noLabelWithoutControl": "warn",
"noNoninteractiveTabindex": "warn"
},
"nursery": {
"useSortedClasses": {
"level": "error",
"fix": "safe",
"options": {
"attributes": ["class", "className", "style"],
"functions": ["clsx", "tw"]
}
}
}
}
},
"javascript": {
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "es5",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSameLine": false,
"quoteStyle": "single",
"attributePosition": "auto",
"bracketSpacing": true
}
},
"overrides": [
{
"include": [
"packages/gitbook/**/*",
"packages/react-openapi/**/*",
"packages/react-math/**/*",
"packages/react-contentkit/**/*",
"packages/icons/**/*"
],
"javascript": {
"globals": ["React"]
}
},
{
"include": ["packages/gitbook/**/*"],
"javascript": {
"globals": ["React", "GitBookIntegrationEvent"]
}
},
{
"include": ["*.css"],
"javascript": {
"globals": ["theme"]
}
},
{
"include": ["*.test.ts", "packages/gitbook/tests/**/*"],
"javascript": {
"globals": ["Bun"]
}
},
{
"include": [
"packages/cache-do/**/*",
"packages/gitbook/cf-env.d.ts",
"packages/gitbook/src/cloudflare-entrypoint.ts"
],
"javascript": {
"globals": [
"DurableObjectLocationHint",
"DurableObjectNamespace",
"DurableObjectStub",
"ContinentCode",
"Fetcher",
"ExportedHandler"
]
}
}
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"name": "GitBook",
"version": "1.0.0",
"dependencies": {
"jquery": "2.1.1",
"lodash": "2.4.1",
"requirejs": "2.1.11",
"URIjs": "1.13.1",
"mousetrap": "1.4.6",
"lunr.js": "0.5.2",
"fontawesome": "4.1.0"
}
}
-5502
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1,2 +0,0 @@
[install.scopes]
"gitbook" = { token = "$NPM_TOKEN_READONLY", url = "https://registry.npmjs.org" }
+105
View File
@@ -0,0 +1,105 @@
var _ = require('lodash');
var path = require('path');
// Default configuration for gitbook
var CONFIG = {
// Folders to use for output
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"output": null,
// Generator to use for building
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"generator": "site",
// Configuration file to use
"configFile": "book",
// Book title and description (defaults are extracted from the README)
"title": null,
"description": null,
// For ebook format, the extension to use for generation (default is detected from output extension)
// "epub", "pdf", "mobi"
// Caution: it overrides the value from the command line
// It's not advised this option in the book.json
"extension": null,
// Plugins list, can contain "-name" for removing default plugins
"plugins": [],
// Global configuration for plugins
"pluginsConfig": {
"fontSettings": {
"theme": null, //"sepia", "night" or "white",
"family": "sans",// "serif" or "sans",
"size": 2 // 1 - 4
}
},
// Variables for templating
"variables": {},
// Set another theme with your own layout
// It's recommended to use plugins or add more options for default theme, though
// See https://github.com/GitbookIO/gitbook/issues/209
"theme": path.resolve(__dirname, '../../theme'),
// Links in template (null: default, false: remove, string: new value)
"links": {
// Custom links at top of sidebar
"sidebar": {
//"Custom link name": "https://customlink.com"
},
// Sharing links
"sharing": {
"google": null,
"facebook": null,
"twitter": null,
"weibo": null,
"all": null
}
},
// Options for PDF generation
"pdf": {
// Add toc at the end of the file
"toc": true,
// Add page numbers to the bottom of every page
"pageNumbers": false,
// Font size for the file content
"fontSize": 12,
// Paper size for the pdf
// Choices are [ua0, ua1, ua2, ua3, ua4, ua5, ua6, ub0, ub1, ub2, ub3, ub4, ub5, ub6, ulegal, uletter]
"paperSize": "a4",
// Margin (in pts)
// Note: 72 pts equals 1 inch
"margin": {
"right": 62,
"left": 62,
"top": 36,
"bottom": 36
},
//Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"headerTemplate": "",
//Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
"footerTemplate": ""
}
};
module.exports = {
CONFIG: CONFIG,
defaults: function(options) {
return _.merge(options || {}, CONFIG, _.defaults);
}
}
+86
View File
@@ -0,0 +1,86 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var exec = require('child_process').exec;
var fs = require('graceful-fs');
var parse = require("../../parse");
var BaseGenerator = require("../page");
var stringUtils = require("../../utils/string");
var Generator = function() {
BaseGenerator.apply(this, arguments);
};
util.inherits(Generator, BaseGenerator);
Generator.prototype.finish = function() {
var that = this;
return BaseGenerator.prototype.finish.apply(this)
.then(function() {
var d = Q.defer();
var format = that.options.extension || path.extname(that.options.output).replace("\.", "") || "pdf";
if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) {
that.options.cover = path.join(that.options.output, "cover.jpg");
}
var _options = {
"--cover": that.options.cover,
"--title": that.options.title,
"--comments": that.options.description,
"--authors": that.options.author,
"--publisher": "GitBook",
"--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]",
"--chapter-mark": "pagebreak",
"--page-breaks-before": "/",
"--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]",
"--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]",
"--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]",
"--no-chapters-in-toc": true,
"--max-levels": "1",
"--breadth-first": true
};
if (format == "pdf") {
var pdfOptions = that.options.pdf;
_.extend(_options, {
"--margin-left": String(pdfOptions.margin.left),
"--margin-right": String(pdfOptions.margin.right),
"--margin-top": String(pdfOptions.margin.top),
"--margin-bottom": String(pdfOptions.margin.bottom),
"--pdf-default-font-size": String(pdfOptions.fontSize),
"--pdf-mono-font-size": String(pdfOptions.fontSize),
"--paper-size": String(pdfOptions.paperSize),
"--pdf-page-numbers": Boolean(pdfOptions.pageNumbers),
"--pdf-header-template": String(pdfOptions.headerTemplate),
"--pdf-footer-template": String(pdfOptions.footerTemplate)
});
}
var command = [
"ebook-convert",
path.join(that.options.output, "SUMMARY.html"),
path.join(that.options.output, "index."+format),
stringUtils.optionsToShellArgs(_options)
].join(" ");
exec(command, function (error, stdout, stderr) {
if (error) {
if (error.code == 127) {
error.message = "Need to install ebook-convert from Calibre";
} else {
error.message = error.message + " "+stdout;
}
return d.reject(error);
}
d.resolve();
});
return d.promise;
});
};
module.exports = Generator;
+77
View File
@@ -0,0 +1,77 @@
var Q = require("q");
var fs = require('graceful-fs');
var fsExtra = require("fs-extra");
var Ignore = require("fstream-ignore");
var getFiles = function(path) {
var d = Q.defer();
// Our list of files
var files = [];
var ig = Ignore({
path: path,
ignoreFiles: ['.ignore', '.gitignore', '.bookignore']
});
// Add extra rules to ignore common folders
ig.addIgnoreRules([
// Skip Git stuff
'.git/',
'.gitignore',
// Skip OS X meta data
'.DS_Store',
// Skip stuff installed by plugins
'node_modules',
// Skip book outputs
'*.pdf',
'*.epub',
'*.mobi',
// Skip config files
'.ignore',
'.bookignore',
'book.json',
], '__custom_stuff');
// Push each file to our list
ig.on('child', function (c) {
files.push(
c.path.substr(c.root.path.length + 1) + (c.props.Directory === true ? '/' : '')
);
});
ig.on('end', function() {
// Normalize paths on Windows
if(process.platform === 'win32') {
return d.resolve(files.map(function(file) {
return file.replace(/\\/g, '/');
}));
}
// Simply return paths otherwise
return d.resolve(files);
});
ig.on('error', d.reject);
return d.promise;
};
module.exports = {
list: getFiles,
readFile: Q.denodeify(fs.readFile),
writeFile: Q.denodeify(fs.writeFile),
mkdirp: Q.denodeify(fsExtra.mkdirp),
copy: Q.denodeify(fsExtra.copy),
remove: Q.denodeify(fsExtra.remove),
symlink: Q.denodeify(fsExtra.symlink),
exists: function(path) {
var d = Q.defer();
fs.exists(path, d.resolve);
return d.promise;
},
};
+87
View File
@@ -0,0 +1,87 @@
var _ = require("lodash");
var path = require("path");
var Q = require("q");
var fs = require("./fs");
var Plugin = require("./plugin");
var BaseGenerator = function(options) {
this.options = options;
// Base for assets in plugins
this.pluginAssetsBase = "book";
this.options.plugins = Plugin.normalizeNames(this.options.plugins);
this.options.plugins = _.union(this.options.plugins, this.options.defaultsPlugins);
this.plugins = [];
};
BaseGenerator.prototype.callHook = function(name, data) {
return this.plugins.hook(name, data);
};
// Sets up generator
BaseGenerator.prototype.load = function() {
return this.loadPlugins();
};
BaseGenerator.prototype.loadPlugins = function() {
var that = this;
return Plugin.fromList(this.options.plugins, this.options.input, this, {
assetsBase: this.pluginAssetsBase
})
.then(function(_plugins) {
that.plugins = _plugins;
return that.callHook("init");
});
};
BaseGenerator.prototype.convertFile = function(content, input) {
return Q.reject(new Error("Could not convert "+input));
};
BaseGenerator.prototype.transferFile = function(input) {
return fs.copy(
path.join(this.options.input, input),
path.join(this.options.output, input)
);
};
BaseGenerator.prototype.transferFolder = function(input) {
return fs.mkdirp(
path.join(this.options.output, input)
);
};
BaseGenerator.prototype.copyCover = function() {
var that = this;
return Q.all([
fs.copy(path.join(this.options.input, "cover.jpg"), path.join(this.options.output, "cover.jpg")),
fs.copy(path.join(this.options.input, "cover_small.jpg"), path.join(this.options.output, "cover_small.jpg"))
])
.fail(function() {
// If orignally from multi-lang, try copy from originalInput
if (!that.options.originalInput) return;
return Q.all([
fs.copy(path.join(that.options.originalInput, "cover.jpg"), path.join(that.options.output, "cover.jpg")),
fs.copy(path.join(that.options.originalInput, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
]);
})
.fail(function(err) {
return Q();
});
};
BaseGenerator.prototype.langsIndex = function(langs) {
return Q.reject(new Error("Langs index is not supported in this generator"));
};
BaseGenerator.prototype.finish = function() {
return Q.reject(new Error("Could not finish generation"));
};
module.exports = BaseGenerator;
+378
View File
@@ -0,0 +1,378 @@
var Q = require("q");
var _ = require("lodash");
var path = require("path");
var tmp = require('tmp');
var swig = require('./template');
var fs = require("./fs");
var parse = require("../parse");
var Plugin = require("./plugin");
var defaultConfig = require("./config");
var generators = {
"site": require("./site"),
"page": require("./page"),
"ebook": require("./ebook"),
"json": require("./json")
};
var defaultDescription = "Book generated using GitBook";
var containsFiles = function(dir, files) {
return Q.all(_.map(files, function(file) {
return fs.exists(path.join(dir, file));
}))
.then(_.all);
};
// Test if generator exists
var checkGenerator = function(options) {
if (!generators[options.generator]) {
return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")"));
}
return Q();
};
// Create the generator and load plugins
var loadGenerator = function(options) {
return checkGenerator(options)
.then(function() {
var generator = new generators[options.generator](options);
return generator.load()
.then(_.constant(generator));
});
};
var generate = function(options) {
// Set defaults to options
options = defaultConfig.defaults(options);
// Validate options
if (!options.input) {
return Q.reject(new Error("Need option input (book input directory)"));
}
// Check files to get folder type (book, multilanguage book or neither)
return checkGenerator(options)
// Read config file
.then(function() {
try {
var _config = require(path.resolve(options.input, options.configFile));
options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
}
catch(err) {
// No config file: not a big deal
return Q();
}
})
// Read readme
.then(function() {
return fs.readFile(path.join(options.input, "README.md"), "utf-8")
.then(function(_readme) {
_readme = parse.readme(_readme);
options.title = options.title || _readme.title;
options.description = options.description || _readme.description || defaultDescription;
});
})
// Detect multi-languages book
.then(function() {
return containsFiles(options.input, ['LANGS.md'])
})
.then(function(isMultiLang) {
// Multi language book
if(isMultiLang) {
return generateMultiLang(options);
}
// Book
return generateBook(options);
});
};
var generateMultiLang = function(options) {
var langsSummary;
options.output = options.output || path.join(options.input, "_book");
return checkGenerator(options)
// Multi-languages book
.then(function() {
return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
})
// Clean output folder
.then(function(_langsSummary) {
langsSummary = _langsSummary;
return fs.remove(options.output);
})
.then(function() {
return fs.mkdirp(options.output);
})
// Generate sub-books
.then(function() {
options.langsSummary = parse.langs(langsSummary);
// Generated a book for each valid entry
return _.reduce(options.langsSummary.list, function(prev, entry) {
return prev.then(function() {
return generate(_.extend({}, options, {
input: path.join(options.input, entry.path),
output: path.join(options.output, entry.path),
originalInput: options.input,
originalOutput: options.output
}));
})
}, Q());
})
.then(function() {
return loadGenerator(options);
})
// Generate languages index
.then(function(generator) {
return generator.langsIndex(options.langsSummary);
})
// Copy cover file
.then(function() {
return Q.all([
fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")),
fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg"))
])
.fail(function() {
return Q();
})
})
// Return options to caller
.then(_.constant(options));
};
/*
* Use a specific generator to convert a gitbook to a site/pdf/ebook/
* output is always a folder
*/
var generateBook = function(options) {
var files;
options.output = options.output || path.join(options.input, "_book");
// Check if it's a book
return containsFiles(options.input, ['SUMMARY.md', 'README.md'])
// Fail if not a book
.then(function(isBook) {
if(!isBook) {
return Q.reject(new Error("Invalid gitbook repository, need SUMMARY.md and README.md"));
}
})
// Clean output folder
.then(function() {
return fs.remove(options.output);
})
.then(function() {
return fs.mkdirp(options.output);
})
// List all files in the repository
.then(function() {
return fs.list(options.input)
.then(function(_files) {
files = _files;
});
})
.then(function() {
return loadGenerator(options);
})
// Convert files
.then(function(generator) {
// Generate the book
return Q()
// Get summary
.then(function() {
var summary = {
path: path.join(options.input, "SUMMARY.md")
};
var _callHook = function(name) {
return generator.callHook(name, summary)
.then(function(_summary) {
summary = _summary;
return summary;
});
};
return fs.readFile(summary.path, "utf-8")
.then(function(_content) {
summary.content = _content;
return _callHook("summary:before");
})
.then(function() {
summary.content = parse.summary(summary.content);
return _callHook("summary:after");
})
.then(function() {
options.summary = summary.content;
options.navigation = parse.navigation(options.summary);
});
})
// Read glossary
.then(function() {
var glossary = {};
var _callHook = function(name) {
return generator.callHook(name, glossary)
.then(function(_glossary) {
glossary = _glossary;
return glossary;
});
};
return fs.readFile(path.join(options.input, "GLOSSARY.md"), "utf-8")
.fail(function() {
return "";
})
.then(function(_content) {
glossary.content = _content;
return _callHook("glossary:before");
})
.then(function() {
glossary.content = parse.glossary(glossary.content);
return _callHook("glossary:after");
})
.then(function() {
options.glossary = glossary.content;
});
})
// Skip processing some files
.then(function() {
files = _.filter(files, function (file) {
return !(
file === 'SUMMARY.md' ||
file === 'GLOSSARY.md'
);
});
})
// Copy file and replace markdown file
.then(function() {
return Q.all(
_.chain(files)
.map(function(file) {
if (!file) return;
if (file[file.length -1] == "/") {
return Q(generator.transferFolder(file));
} else if (path.extname(file) == ".md" && options.navigation[file] != null) {
return fs.readFile(path.join(options.input, file), "utf-8")
.then(function(content) {
return Q(generator.convertFile(content, file));
});
} else {
return Q(generator.transferFile(file));
}
})
.value()
);
})
// Finish generation
.then(function() {
return generator.finish();
})
.then(function() {
return generator.callHook("finish");
});
})
// Return all options
.then(function() {
return options;
});
};
/*
* Extract files from generate output in a temporary folder
*/
var generateFile = function(options) {
options = _.defaults(options || {}, {
input: null,
output: null,
extension: null
});
return Q.nfcall(tmp.dir)
.then(function(tmpDir) {
return generate(
_.extend({},
options,
{
output: tmpDir
})
)
.then(function(_options) {
var ext = options.extension;
var outputFile = options.output || path.resolve(options.input, "book."+ext);
var copyFile = function(lang) {
var _outputFile = outputFile;
var _tmpDir = tmpDir;
if (lang) {
_outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
_tmpDir = path.join(_tmpDir, lang);
}
return fs.copy(
path.join(_tmpDir, "index."+ext),
_outputFile
);
};
// Multi-langs book
return Q()
.then(function() {
if (_options.langsSummary) {
return Q.all(
_.map(_options.langsSummary.list, function(lang) {
return copyFile(lang.lang);
})
);
} else {
return copyFile();
}
})
.then(function() {
return fs.remove(tmpDir);
});
});
});
};
module.exports = {
generators: generators,
folder: generate,
file: generateFile,
book: generateBook,
Plugin: Plugin,
};
+69
View File
@@ -0,0 +1,69 @@
var Q = require('q');
var _ = require('lodash');
var path = require('path');
var fs = require('./fs');
var parse = require('../parse');
// Extract paths out of a summary
function paths(summary) {
return _.reduce(summary.chapters, function(accu, chapter) {
return accu.concat(
_.filter([chapter.path].concat(_.pluck(chapter.articles, 'path')))
);
}, []);
}
// Get the parent folders out of a group of files
function folders(files) {
return _.chain(files)
.map(function(file) {
return path.dirname(file);
})
.uniq()
.value();
}
function initDir(dir) {
return fs.readFile(path.join(dir, 'SUMMARY.md'), 'utf8')
.then(function(src) {
// Parse summary
return parse.summary(src);
})
.then(function(summary) {
// Extract paths from summary
return paths(summary);
})
.then(function(paths) {
// Convert to absolute paths
return _.map(paths, function(file) {
return path.resolve(file);
});
})
.then(function(files) {
// Create folders
return Q.all(_.map(folders(files), function(folder) {
return fs.mkdirp(folder);
}))
.then(_.constant(files));
})
.then(function(files) {
// Create files that don't exist
return Q.all(_.map(files, function(file) {
return fs.exists(file)
.then(function(exists) {
if(exists) return;
return fs.writeFile(file, '');
});
}));
})
.fail(function(err) {
console.error(err.stack);
});
}
// Exports
module.exports = initDir;
+76
View File
@@ -0,0 +1,76 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var fs = require("../fs");
var parse = require("../../parse");
var BaseGenerator = require("../generator");
var Generator = function() {
BaseGenerator.apply(this, arguments);
};
util.inherits(Generator, BaseGenerator);
Generator.prototype.transferFile = function(input) {
// ignore
};
Generator.prototype.convertFile = function(content, input) {
var that = this;
var json = {
progress: parse.progress(this.options.navigation, input)
};
return Q()
.then(function() {
return parse.page(content, {
dir: path.dirname(input) || '/'
});
})
.then(function(sections) {
json.sections = sections;
})
.then(function() {
return fs.writeFile(
path.join(that.options.output, input.replace(".md", ".json")),
JSON.stringify(json, null, 4)
);
});
};
// Generate languages index
// Contains the first languages readme and langs infos
Generator.prototype.langsIndex = function(langs) {
var that = this;
if (langs.list.length == 0) return Q.reject("Need at least one language");
var mainLang = _.first(langs.list).lang;
console.log("Main language is", mainLang);
return Q()
.then(function() {
return fs.readFile(
path.join(that.options.output, mainLang, "README.json")
);
})
.then(function(content) {
var json = JSON.parse(content);
_.extend(json, {
langs: langs.list
});
return fs.writeFile(
path.join(that.options.output, "README.json"),
JSON.stringify(json, null, 4)
);
});
};
Generator.prototype.finish = function() {
// ignore
};
module.exports = Generator;
+73
View File
@@ -0,0 +1,73 @@
var _ = require("lodash");
var util = require("util");
var path = require("path");
var Q = require("q");
var swig = require("../template");
var fs = require("../fs");
var parse = require("../../parse");
var BaseGenerator = require("../site");
var Generator = function() {
BaseGenerator.apply(this, arguments);
// Base for assets in plugins
this.pluginAssetsBase = "ebook";
// List of pages content
this.pages = {};
};
util.inherits(Generator, BaseGenerator);
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html')
);
this.summaryTemplate = swig.compileFile(
this.plugins.template("ebook:sumary") || path.resolve(this.options.theme, 'templates/ebook/summary.html')
);
};
// Generate table of contents
Generator.prototype.writeToc = function() {
var that = this;
var basePath = ".";
return this._writeTemplate(this.summaryTemplate, {
toc: parse.progress(this.options.navigation, "README.md").chapters,
basePath: basePath,
staticBase: path.join(basePath, "gitbook"),
}, path.join(this.options.output, "SUMMARY.html"));
};
Generator.prototype.finish = function() {
var that = this;
var basePath = ".";
var output = path.join(this.options.output, "index.html");
var progress = parse.progress(this.options.navigation, "README.md");
return Q()
// Write table of contents
.then(function() {
return that.writeToc();
})
// Copy cover
.then(function() {
return that.copyCover();
})
// Copy assets
.then(function() {
return that.copyAssets();
});
};
// Generate languages index
Generator.prototype.langsIndex = function(langs) {
return Q();
};
module.exports = Generator;
+284
View File
@@ -0,0 +1,284 @@
var _ = require("lodash");
var Q = require("q");
var semver = require("semver");
var path = require("path");
var url = require("url");
var fs = require("./fs");
var resolve = require('resolve');
var pkg = require("../../package.json");
var RESOURCES = ["js", "css"];
var Plugin = function(name, root, generator) {
this.name = name;
this.root = root;
this.packageInfos = {};
this.infos = {};
this.generator = generator;
// Bind methods
_.bindAll(this);
_.each([
"gitbook-plugin-"+name,
"gitbook-theme-"+name,
"gitbook-"+name,
name,
], function(_name) {
if (this.load(_name, __dirname)) return false;
if (this.load(_name, path.resolve(root))) return false;
}, this);
};
// Load from a name
Plugin.prototype.load = function(name, baseDir) {
try {
var res = resolve.sync(name+"/package.json", { basedir: baseDir });
this.baseDir = path.dirname(res);
this.packageInfos = require(res);
this.infos = require(resolve.sync(name, { basedir: baseDir }));
this.name = name;
return true;
} catch (e) {
return false;
}
};
Plugin.prototype.normalizeResource = function(resource) {
// Parse the resource path
var parsed = url.parse(resource);
// This is a remote resource
// so we will simply link to using it's URL
if (parsed.protocol) {
return {
"url": resource
};
}
// This will be copied over from disk
// and shipped with the book's build
return { "path": this.name+"/"+resource };
};
// Return resources
Plugin.prototype._getResources = function(base) {
base = base || "book";
var book = this.infos[base];
// Nothing specified, fallback to default
if (!book) {
return Q({});
}
// Dynamic function
if(typeof book === "function") {
// Call giving it the context of our generator
return Q().then(book.bind(this.generator));
}
// Plain data object
return Q(_.cloneDeep(book));
};
// Normalize resources and return them
Plugin.prototype.getResources = function(base) {
var that = this;
return this._getResources(base)
.then(function(resources) {
_.each(RESOURCES, function(resourceType) {
resources[resourceType] = (resources[resourceType] || []).map(that.normalizeResource);
});
return resources;
});
};
// Test if it's a valid plugin
Plugin.prototype.isValid = function() {
return (
this.packageInfos &&
this.packageInfos.name &&
this.packageInfos.engines &&
this.packageInfos.engines.gitbook &&
semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
);
};
// Resolve file path
Plugin.prototype.resolveFile = function(filename) {
return path.resolve(this.baseDir, filename);
};
// Resolve file path
Plugin.prototype.callHook = function(name, data) {
// Our generator will be the context to apply
var context = this.generator;
var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
data = data || {};
if (!hookFunc) return Q(data);
return Q()
.then(function() {
return hookFunc.apply(context, [data]);
});
};
// Copy plugin assets fodler
Plugin.prototype.copyAssets = function(out, options) {
var that = this;
options = _.defaults(options || {}, {
base: "book"
});
return this.getResources(options.base)
.get('assets')
.then(function(assets) {
// Assets are undefined
if(!assets) return false;
return fs.copy(
that.resolveFile(assets),
out
).then(_.constant(true));
}, _.constant(false));
};
// Normalize a list of plugin name to use
Plugin.normalizeNames = function(names) {
// Normalize list to an array
names = _.isString(names) ? names.split(",") : (names || []);
// List plugins to remove
var toremove = _.chain(names)
.filter(function(name) {
return name.length > 0 && name[0] == "-";
})
.map(function(name) {
return name.slice(1);
})
.value();
// Merge with defaults
names = _.chain(names)
.concat(Plugin.defaults)
.uniq()
.value();
// Remove plugins starting with
names = _.filter(names, function(name) {
return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
});
return names;
};
// Extract data from a list of plugin
Plugin.fromList = function(names, root, generator, options) {
options = _.defaults(options || {}, {
assetsBase: "book"
});
var failed = [];
// Load plugins
var plugins = _.map(names, function(name) {
var plugin = new Plugin(name, root, generator);
if (!plugin.isValid()) failed.push(name);
return plugin;
});
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
// The raw resources extracted from each plugin
var pluginResources;
// Get resources of plugins
return Q.all(_.map(plugins, function(plugin) {
return plugin.getResources(options.assetsBase);
}))
// Extract resources out
// css, js, etc ...
.then(function(resources) {
pluginResources = resources;
// Group by resource types
return _.chain(RESOURCES)
.map(function(resourceType) {
// Get resources from all the plugins for this current type
return [
// Key
resourceType,
// Value
_.chain(resources)
.pluck(resourceType)
.compact()
.flatten()
.value()
];
})
.object()
.value();
})
// Extract html snippets
.then(function(resources) {
// Map of html resources by name added by each plugin
resources.html = pluginResources.reduce(function(accu, resource) {
var html = (resource && resource.html) || {};
_.each(html, function(code, key) {
// Turn into function if not one already
if (!_.isFunction(code)) code = _.constant(code);
// Append
accu[key] = (accu[key] || []).concat([code]);
});
return accu;
}, {});
return resources;
})
// Return big multi-plugin object
.then(function(resources) {
return {
'list': plugins,
'resources': resources,
'hook': function(name, data) {
return _.reduce(plugins, function(prev, plugin) {
return prev.then(function(ret) {
return plugin.callHook(name, ret);
});
}, Q(data));
},
'template': function(name) {
var withTpl = _.find(plugins, function(plugin) {
return (
plugin.infos.templates &&
plugin.infos.templates[name]
);
});
if (!withTpl) return null;
return withTpl.resolveFile(withTpl.infos.templates[name]);
},
'html': function(tag, context, options) {
return _.map(resources.html[tag] || [], function(code) {
return code.call(context, options);
}).join("\n");
}
};
});
};
// Default plugins
Plugin.defaults = [
"mathjax"
];
module.exports = Plugin;
+101
View File
@@ -0,0 +1,101 @@
var _ = require("lodash");
var kramed = require('kramed');
var textRenderer = require('kramed-text-renderer');
var entryId = require('../../parse/glossary').entryId;
function Indexer(glossary) {
if(!(this instanceof Indexer)) {
return new Indexer(glossary);
}
_.bindAll(this);
this.glossary = glossary || [];
this.glossaryTerms = _.pluck(this.glossary, "id");
// Regex for searching for terms through body
this.termsRegex = new RegExp(
// Match any of the terms
"("+
this.glossaryTerms.map(regexEscape).join('|') +
")",
// Flags
"gi"
);
// page url => terms
this.idx = {
/*
"a/b.html": ["one word", "second word"]
*/
};
// term => page urls
this.invertedIdx = {
/*
"word1": ["page1.html", "page2.html"]
*/
};
// Use text renderer
this.renderer = textRenderer();
}
Indexer.prototype.text = function(nodes) {
// Copy section
var section = _.toArray(nodes);
// kramed's Render expects this, we don't use it yet
section.links = {};
var options = _.extend({}, kramed.defaults, {
renderer: this.renderer
});
return kramed.parser(section, options);
};
// Add page to glossary index
Indexer.prototype.add = function(sections, url) {
if(!(this.glossary && this.glossary.length > 0)) {
return;
}
var textblob =
_.where(sections, { type: 'normal' })
.map(this.text)
.join('\n');
var matches = _(textblob.match(this.termsRegex) || [])
.map(entryId)
.uniq()
.value();
// Add idx for book
this.idx[url] = matches;
// Add to inverted idx
matches.forEach(function(match) {
if(!this.invertedIdx[match]) {
this.invertedIdx[match] = [];
}
this.invertedIdx[match].push(url);
}.bind(this));
};
// Dump index as a string
Indexer.prototype.dump = function() {
return JSON.stringify(this.idx);
};
function regexEscape(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
// Exports
module.exports = Indexer;
+284
View File
@@ -0,0 +1,284 @@
var util = require("util");
var path = require("path");
var Q = require("q");
var _ = require("lodash");
var swig = require("../template");
var fs = require("../fs");
var parse = require("../../parse");
var BaseGenerator = require("../generator");
var links = require("../../utils/links");
var indexer = require('./search_indexer');
var glossaryIndexer = require('./glossary_indexer');
var Generator = function() {
BaseGenerator.apply(this, arguments);
// Attach methods to instance
_.bindAll(this);
this.revision = Date.now();
this.indexer = indexer();
};
util.inherits(Generator, BaseGenerator);
// Add template loading to load
Generator.prototype.load = function() {
var that = this;
return BaseGenerator.prototype.load.apply(this)
.then(function() {
return that.loadTemplates();
});
};
// Load all templates
Generator.prototype.loadTemplates = function() {
this.template = swig.compileFile(
this.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/book/page.html')
);
this.langsTemplate = swig.compileFile(
this.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/book/langs.html')
);
this.glossaryTemplate = swig.compileFile(
this.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/book/glossary.html')
);
};
// Generate a template
Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) {
var that = this;
interpolate = interpolate || _.identity;
return Q()
.then(function(sections) {
return tpl(_.extend({
revision: that.revision,
title: that.options.title,
description: that.options.description,
glossary: that.options.glossary,
summary: that.options.summary,
allNavigation: that.options.navigation,
plugins: that.plugins,
pluginsConfig: JSON.stringify(that.options.pluginsConfig),
htmlSnippet: _.partialRight(that.plugins.html, that, options),
options: that.options
}, options));
})
.then(interpolate)
.then(function(html) {
return fs.writeFile(
output,
html
);
});
};
Generator.prototype.indexPage = function(lexed, pagePath) {
// Setup glossary indexer if not yet setup
if(!this.glossaryIndexer) {
this.glossaryIndexer = glossaryIndexer(this.options.glossary);
}
this.indexer.add(lexed, pagePath);
this.glossaryIndexer.add(lexed, pagePath);
return Q();
};
// Convert a markdown file into a normalized data set
Generator.prototype.prepareFile = function(content, _input) {
var that = this;
var input = path.join(this.options.input, _input);
var page = {
path: _input,
rawPath: input,
content: content,
progress: parse.progress(this.options.navigation, _input)
};
var _callHook = function(name) {
return that.callHook(name, page)
.then(function(_page) {
page = _page;
return page;
});
};
return Q()
.then(function() {
// Send content to plugins
return _callHook("page:before");
})
.then(function() {
// Lex, parse includes and get
// Get HTML generated sections
return parse.page(page.content, {
// Local files path
dir: path.dirname(_input) || '/',
// Project's include folder
includes_dir: path.join(that.options.input, '_includes'),
// Output directory
outdir: path.dirname(_input) || '/',
// Templating variables
variables: that.options.variables,
});
})
.then(function(sections) {
page.sections = sections;
// Use plugin hook
return _callHook("page");
})
.then(function() {
return page;
});
};
// Convert a markdown file to html
Generator.prototype.convertFile = function(content, _input) {
var that = this;
var _output = _input.replace(".md", ".html");
if (_output == "README.html") _output = "index.html";
var output = path.join(this.options.output, _output);
var basePath = path.relative(path.dirname(output), this.options.output) || ".";
return this.prepareFile(content, _input)
.then(function(page) {
// Index page in search
return that.indexPage(page.lexed, _output).thenResolve(page);
})
.then(function(page) {
// Write file
return that._writeTemplate(that.template, {
progress: page.progress,
_input: page.path,
content: page.sections,
basePath: basePath,
staticBase: links.join(basePath, "gitbook"),
}, output, function(html) {
page.content = html;
return that.callHook("page:after", page).get("content")
});
});
};
// Generate languages index
Generator.prototype.langsIndex = function(langs) {
var that = this;
var basePath = ".";
return this._writeTemplate(this.langsTemplate, {
langs: langs.list,
basePath: basePath,
staticBase: path.join(basePath, "gitbook"),
}, path.join(this.options.output, "index.html"))
.then(function() {
// Copy assets
return that.copyAssets();
});
};
// Generate glossary
Generator.prototype.writeGlossary = function() {
var that = this;
var basePath = ".";
// No glossary
if (!this.glossaryIndexer) return Q();
// Transform the glossary to get term, description, files
var glossary = _.chain(this.glossaryIndexer.invertedIdx)
.map(function(links, id) {
var term = _.find(that.options.glossary, { 'id': id });
return {
id: id,
name: term.name,
description: term.description,
files: _.chain(links)
.map(function(link) {
var name = link.slice(0, -5);
if (name == "index") {
name = "README";
}
return that.options.navigation[name+".md"];
})
.sortBy("percent")
.value()
}
})
.sortBy("name")
.value();
return this._writeTemplate(this.glossaryTemplate, {
glossaryIndex: glossary,
basePath: basePath,
staticBase: path.join(basePath, "gitbook"),
}, path.join(this.options.output, "GLOSSARY.html"));
};
// Copy assets
Generator.prototype.copyAssets = function() {
var that = this;
// Copy gitbook assets
return fs.copy(
path.join(that.options.theme, "assets"),
path.join(that.options.output, "gitbook")
)
// Copy plugins assets
.then(function() {
return Q.all(
_.map(that.plugins.list, function(plugin) {
var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name);
return plugin.copyAssets(pluginAssets, {
base: that.pluginAssetsBase
});
})
);
});
};
// Dump search index to disk
Generator.prototype.writeSearchIndex = function() {
return fs.writeFile(
path.join(this.options.output, 'search_index.json'),
this.indexer.dump()
);
};
// Dump glossary index to disk
Generator.prototype.writeGlossaryIndex = function() {
if (!this.glossaryIndexer) return Q();
return fs.writeFile(
path.join(this.options.output, 'glossary_index.json'),
JSON.stringify(this.options.glossary)
);
};
Generator.prototype.finish = function() {
return this.copyAssets()
.then(this.copyCover)
.then(this.writeGlossary)
.then(this.writeGlossaryIndex)
.then(this.writeSearchIndex);
};
module.exports = Generator;
+71
View File
@@ -0,0 +1,71 @@
var Q = require("q");
var _ = require("lodash");
var lunr = require('lunr');
var kramed = require('kramed');
var textRenderer = require('kramed-text-renderer');
function Indexer() {
if(!(this instanceof Indexer)) {
return new Indexer();
}
_.bindAll(this);
// Setup lunr index
this.idx = lunr(function () {
this.ref('url');
this.field('title', { boost: 10 });
this.field('body');
});
this.renderer = textRenderer();
}
Indexer.prototype.text = function(nodes) {
// Copy section
var section = _.toArray(nodes);
// kramed's Render expects this, we don't use it yet
section.links = {};
var options = _.extend({}, kramed.defaults, {
renderer: this.renderer
});
return kramed.parser(section, options);
};
Indexer.prototype.addSection = function(path, section) {
var url = [path, section.id].join('#');
var title = this.text(
_.filter(section, {'type': 'heading'})
);
var body = this.text(
_.omit(section, {'type': 'heading'})
);
// Add to lunr index
this.idx.add({
url: url,
title: title,
body: body,
});
};
Indexer.prototype.add = function(lexedPage, url) {
var sections = lexedPage;
_.map(sections, _.partial(this.addSection, url));
};
Indexer.prototype.dump = function() {
return JSON.stringify(this.idx);
};
// Exports
module.exports = Indexer;
+52
View File
@@ -0,0 +1,52 @@
var path = require("path");
var swig = require('swig');
var hljs = require('highlight.js');
var links = require('../utils/').links;
var pkg = require('../../package.json');
swig.setDefaults({
locals: {
gitbook: {
version: pkg.version
}
}
});
// Swig filter for returning the count of lines in a code section
swig.setFilter('lines', function(content) {
return content.split('\n').length;
});
// Swig filter for returning a link to the associated html file of a markdown file
swig.setFilter('mdLink', function(link) {
var link = link.replace(".md", ".html");
if (link == "README.html") link = "index.html";
return link;
});
// Swig filter: highlight coloration
swig.setFilter('code', function(code, lang) {
try {
return hljs.highlight(lang, code).value;
} catch(e) {
return hljs.highlightAuto(code).value;
}
});
// Convert a level into a deep level
swig.setFilter('lvl', function(lvl) {
return lvl.split(".").length;
});
// Join path
swig.setFilter('pathJoin', function(base, _path) {
return links.join(base, _path);
});
// Is a link an absolute link
swig.setFilter('isExternalLink', function(link) {
return links.isExternal(link);
});
module.exports = swig;
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
parse: require('./parse/'),
generate: require('./generate/')
};
+48
View File
@@ -0,0 +1,48 @@
var _ = require('lodash');
var kramed = require('kramed');
// Get all the pairs of header + paragraph in a list of nodes
function groups(nodes) {
// A list of next nodes
var next = nodes.slice(1).concat(null);
return _.reduce(nodes, function(accu, node, idx) {
// Skip
if(!(
node.type === 'heading' &&
(next[idx] && next[idx].type === 'paragraph')
)) {
return accu;
}
// Add group
accu.push([
node,
next[idx]
]);
return accu;
}, []);
}
function parseGlossary(src) {
var nodes = kramed.lexer(src);
return groups(nodes)
.map(function(pair) {
// Simplify each group to a simple object with name/description
return {
name: pair[0].text,
id: entryId(pair[0].text),
description: pair[1].text,
};
});
}
// Normalizes a glossary entry's name to create an ID
function entryId(name) {
return name.toLowerCase();
}
module.exports = parseGlossary;
module.exports.entryId = entryId;
+48
View File
@@ -0,0 +1,48 @@
var _ = require('lodash');
var fs = require('graceful-fs');
var path = require('path');
// Include helper
function importInclude(name, paths) {
return paths
.map(function(folder) {
// Try including snippet from FS
try {
var fname = path.join(folder, name);
// Trim trailing newlines/space of imported snippets
return fs.readFileSync(fname, 'utf8').trimRight();
} catch(err) {}
})
.filter(Boolean)[0];
}
function includer(ctx, folders) {
return function(key) {
key = key.trim();
return ctx[key] || importInclude(key, folders);
};
}
module.exports = function(markdown, folder, ctx) {
// List of folders to search for includes
var folders = [];
// Handle folder arg (string or array)
if(_.isString(folder)) {
folders = [folder];
} else if(_.isArray(folder)) {
folders = folder;
}
// variable context
ctx = ctx || {};
// Memoized include function (to cache lookups)
var _include = _.memoize(includer(ctx, folders));
return markdown.replace(/{{([\s\S]+?)}}/g, function(match, key) {
// If fails leave content as is
return _include(key) || match;
});
};
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
summary: require('./summary'),
glossary: require('./glossary'),
langs: require('./langs'),
page: require('./page'),
lex: require('./lex'),
progress: require('./progress'),
navigation: require('./navigation'),
readme: require('./readme')
};
+17
View File
@@ -0,0 +1,17 @@
var _ = require('lodash');
function isExercise(nodes) {
var codeType = { type: 'code' };
// Number of code nodes in section
var len = _.filter(nodes, codeType).length;
return (
// Got 3 or 4 code blocks
(len === 3 || len === 4) &&
// Ensure all nodes are at the end
_.all(_.last(nodes, len), codeType)
);
}
module.exports = isExercise;
+87
View File
@@ -0,0 +1,87 @@
var _ = require('lodash');
function isQuizNode(node) {
return (/^[(\[][ x][)\]]/).test(node.text || node);
}
function isTableQuestion(nodes) {
var block = questionBlock(nodes);
return (
block.length === 1 &&
block[0].type === 'table' &&
_.all(block[0].cells[0].slice(1), isQuizNode)
);
}
function isListQuestion(nodes) {
var block = questionBlock(nodes);
// Counter of when we go in and out of lists
var inlist = 0;
// Number of lists we found
var lists = 0;
// Elements found outside a list
var outsiders = 0;
// Ensure that we have nothing except lists
_.each(block, function(node) {
if(node.type === 'list_start') {
inlist++;
} else if(node.type === 'list_end') {
inlist--;
lists++;
} else if(inlist === 0) {
// Found non list_start or list_end whilst outside a list
outsiders++;
}
});
return lists > 0 && outsiders === 0;
}
function isQuestion(nodes) {
return isListQuestion(nodes) || isTableQuestion(nodes);
}
// Remove (optional) paragraph header node and blockquote
function questionBlock(nodes) {
return nodes.slice(
nodes[0].type === 'paragraph' ? 1 : 0,
_.findIndex(nodes, { type: 'blockquote_start' })
);
}
function splitQuestions(nodes) {
// Represents nodes in current question
var buffer = [];
return _.reduce(nodes, function(accu, node) {
// Add node to buffer
buffer.push(node);
// Flush buffer once we hit the end of a question
if(node.type === 'blockquote_end') {
accu.push(buffer);
// Clear buffer
buffer = [];
}
return accu;
}, []);
}
function isQuiz(nodes) {
// Extract potential questions
var questions = splitQuestions(
// Skip quiz title if there
nodes.slice(
(nodes[0] && nodes[0].type) === 'paragraph' ? 1 : 0
)
);
// Nothing that looks like questions
if(questions.length === 0) {
return false;
}
// Ensure all questions are correctly structured
return _.all(questions, isQuestion);
}
module.exports = isQuiz;
+25
View File
@@ -0,0 +1,25 @@
var _ = require("lodash");
var parseEntries = require("./summary").entries;
var parseLangs = function(content) {
var entries = parseEntries(content);
return {
list: _.chain(entries)
.filter(function(entry) {
return Boolean(entry.path);
})
.map(function(entry) {
return {
title: entry.title,
path: entry.path,
lang: entry.path.replace("/", "")
};
})
.value()
};
};
module.exports = parseLangs;
+79
View File
@@ -0,0 +1,79 @@
var _ = require('lodash');
var kramed = require('kramed');
var isExercise = require('./is_exercise');
var isQuiz = require('./is_quiz');
// Split a page up into sections (lesson, exercises, ...)
function splitSections(nodes) {
var section = [];
return _.reduce(nodes, function(sections, el) {
if(el.type === 'hr') {
sections.push(section);
section = [];
} else {
section.push(el);
}
return sections;
}, []).concat([section]); // Add remaining nodes
}
// What is the type of this section
function sectionType(nodes, idx) {
if(isExercise(nodes)) {
return 'exercise';
} else if(isQuiz(nodes)) {
return 'quiz';
}
return 'normal';
}
// Generate a uniqueId to identify this section in our code
function sectionId(section, idx) {
return _.uniqueId('gitbook_');
}
function lexPage(src) {
// Lex file
var nodes = kramed.lexer(src);
return _.chain(splitSections(nodes))
.map(function(section, idx) {
// Detect section type
section.type = sectionType(section, idx);
return section;
})
.map(function(section, idx) {
// Give each section an ID
section.id = sectionId(section, idx);
return section;
})
.filter(function(section) {
return !_.isEmpty(section);
})
.reduce(function(sections, section) {
var last = _.last(sections);
// Merge normal sections together
if(last && last.type === section.type && last.type === 'normal') {
last.push.apply(last, [{'type': 'hr'}].concat(section));
} else {
// Add to list of sections
sections.push(section);
}
return sections;
}, [])
.map(function(section) {
section.links = nodes.links;
return section;
})
.value();
}
// Exports
module.exports = lexPage;
+64
View File
@@ -0,0 +1,64 @@
var _ = require('lodash');
// Cleans up an article/chapter object
// remove 'articles' attributes
function clean(obj) {
return obj && _.omit(obj, ['articles']);
}
function flattenChapters(chapters) {
return _.reduce(chapters, function(accu, chapter) {
return accu.concat([clean(chapter)].concat(flattenChapters(chapter.articles)));
}, []);
}
// Returns from a summary a map of
/*
{
"file/path.md": {
prev: ...,
next: ...,
},
...
}
*/
function navigation(summary, files) {
// Support single files as well as list
files = _.isArray(files) ? files : (_.isString(files) ? [files] : null);
// List of all navNodes
// Flatten chapters, then add in default README node if ndeeded etc ...
var navNodes = flattenChapters(summary.chapters);
var prevNodes = [null].concat(navNodes.slice(0, -1));
var nextNodes = navNodes.slice(1).concat([null]);
// Mapping of prev/next for a give path
var mapping = _.chain(_.zip(navNodes, prevNodes, nextNodes))
.map(function(nodes) {
var current = nodes[0], prev = nodes[1], next = nodes[2];
// Skip if no path
if(!current.path) return null;
return [current.path, {
title: current.title,
prev: prev,
next: next,
level: current.level,
}];
})
.filter()
.object()
.value();
// Filter for only files we want
if(files) {
return _.pick(mapping, files);
}
return mapping;
}
// Exports
module.exports = navigation;
+156
View File
@@ -0,0 +1,156 @@
var _ = require('lodash');
var kramed = require('kramed');
var hljs = require('highlight.js');
var lex = require('./lex');
var renderer = require('./renderer');
var include = require('./include');
var lnormalize = require('../utils/lang').normalize;
// Render a section using our custom renderer
function render(section, _options) {
// Copy section
var links = section.links || {};
section = _.toArray(section);
section.links = links;
// Build options using defaults and our custom renderer
var options = _.extend({}, kramed.defaults, {
renderer: renderer(null, _options),
// Synchronous highlighting with highlight.js
highlight: function (code, lang) {
if(!lang) return code;
// Normalize lang
lang = lnormalize(lang);
try {
return hljs.highlight(lang, code).value;
} catch(e) { }
return code;
}
});
return kramed.parser(section, options);
}
function quizQuestion(node) {
if (node.text) {
node.text = node.text.replace(/^([\[(])x([\])])/, "$1 $2");
} else {
return node.replace(/^([\[(])x([\])])/, "$1 $2");
}
}
function parsePage(src, options) {
options = options || {};
// Lex if not already lexed
return (_.isArray(src) ? src : lex(include(src, [options.dir, options.includes_dir], options.variables)))
.map(function(section) {
// Transform given type
if(section.type === 'exercise') {
var nonCodeNodes = _.reject(section, {
'type': 'code'
});
var codeNodes = _.filter(section, {
'type': 'code'
});
// Languages in code blocks
var langs = _.pluck(codeNodes, 'lang').map(lnormalize);
// Check that they are all the same
var validLangs = _.all(_.map(langs, function(lang) {
return lang && lang === langs[0];
}));
// Main language
var lang = validLangs ? langs[0] : null;
return {
id: section.id,
type: section.type,
content: render(nonCodeNodes, options),
lang: lang,
code: {
base: codeNodes[0].text,
solution: codeNodes[1].text,
validation: codeNodes[2].text,
// Context is optional
context: codeNodes[3] ? codeNodes[3].text : null,
}
};
} else if (section.type === 'quiz') {
var quiz = [], question, foundFeedback = false;
var nonQuizNodes = section[0].type === 'paragraph' && section[1].type !== 'list_start' ? [section[0]] : [];
var quizNodes = section.slice(0);
quizNodes.splice(0, nonQuizNodes.length);
for (var i = 0; i < quizNodes.length; i++) {
var node = quizNodes[i];
if (question && (((node.type === 'list_end' || node.type === 'blockquote_end') && i === quizNodes.length - 1)
|| node.type === 'table' || (node.type === 'paragraph' && !foundFeedback))) {
quiz.push({
base: render(question.questionNodes, options),
solution: render(question.solutionNodes, options),
feedback: render(question.feedbackNodes, options)
});
}
if (node.type === 'table' || (node.type === 'paragraph' && !foundFeedback)) {
question = { questionNodes: [], solutionNodes: [], feedbackNodes: [] };
}
if (node.type === 'blockquote_start') {
foundFeedback = true;
} else if (node.type === 'blockquote_end') {
foundFeedback = false;
}
if (node.type === 'table') {
question.solutionNodes.push(_.cloneDeep(node));
node.cells = node.cells.map(function(row) {
return row.map(quizQuestion);
});
question.questionNodes.push(node);
} else if (!/blockquote/.test(node.type)) {
if (foundFeedback) {
question.feedbackNodes.push(node);
} else if (node.type === 'paragraph' || node.type === 'text'){
question.solutionNodes.push(_.cloneDeep(node));
quizQuestion(node);
question.questionNodes.push(node);
} else {
question.solutionNodes.push(node);
question.questionNodes.push(node);
}
}
}
return {
id: section.id,
type: section.type,
content: render(nonQuizNodes, options),
quiz: quiz
};
}
// Render normal pages
return {
id: section.id,
type: section.type,
content: render(section, options)
};
});
}
// Exports
module.exports = parsePage;
+47
View File
@@ -0,0 +1,47 @@
var _ = require("lodash");
// Returns from a navigation and a current file, a snapshot of current detailed state
var calculProgress = function(navigation, current) {
var n = _.size(navigation);
var percent = 0, prevPercent = 0, currentChapter = null;
var done = true;
var chapters = _.chain(navigation)
.map(function(nav, path) {
nav.path = path;
return nav;
})
.map(function(nav, i) {
// Calcul percent
nav.percent = (i * 100) / Math.max((n - 1), 1);
// Is it done
nav.done = done;
if (nav.path == current) {
currentChapter = nav;
percent = nav.percent;
done = false;
} else if (done) {
prevPercent = nav.percent;
}
return nav;
})
.value();
return {
// Previous percent
prevPercent: prevPercent,
// Current percent
percent: percent,
// List of chapter with progress
chapters: chapters,
// Current chapter
current: currentChapter
};
}
module.exports = calculProgress;
+45
View File
@@ -0,0 +1,45 @@
var _ = require('lodash');
var kramed = require('kramed');
var textRenderer = require('kramed-text-renderer');
function extractFirstNode(nodes, nType) {
return _.chain(nodes)
.filter(function(node) {
return node.type == nType;
})
.pluck("text")
.first()
.value();
}
function parseReadme(src) {
var nodes, title, description;
var renderer = textRenderer();
// Parse content
nodes = kramed.lexer(src);
title = extractFirstNode(nodes, "heading") || '';
description = extractFirstNode(nodes, "paragraph") || '';
var convert = _.compose(
function(text) {
return _.unescape(text.replace(/(\r\n|\n|\r)/gm, ""));
},
function(text) {
return kramed.parse(text, _.extend({}, kramed.defaults, {
renderer: renderer
}));
}
);
return {
title: convert(title),
description: convert(description)
};
}
// Exports
module.exports = parseReadme;
+143
View File
@@ -0,0 +1,143 @@
var url = require('url');
var inherits = require('util').inherits;
var links = require('../utils').links;
var path = require('path');
var kramed = require('kramed');
var rendererId = 0;
function GitBookRenderer(options, extra_options) {
if(!(this instanceof GitBookRenderer)) {
return new GitBookRenderer(options, extra_options);
}
GitBookRenderer.super_.call(this, options);
this._extra_options = extra_options;
this.quizRowId = 0;
this.id = rendererId++;
this.quizIndex = 0;
}
inherits(GitBookRenderer, kramed.Renderer);
GitBookRenderer.prototype._unsanitized = function(href) {
var prot = '';
try {
prot = decodeURIComponent(unescape(href))
.replace(/[^\w:]/g, '')
.toLowerCase();
} catch (e) {
return true;
}
if(prot.indexOf('javascript:') === 0) {
return true;
}
return false;
};
GitBookRenderer.prototype.link = function(href, title, text) {
// Our "fixed" href
var _href = href;
// Don't build if it looks malicious
if (this.options.sanitize && this._unsanitized(href)) {
return text;
}
// Parsed version of the url
var parsed = url.parse(href);
var o = this._extra_options;
// Relative link, rewrite it to point to github repo
if(links.isRelative(_href) && path.extname(parsed.path) == ".md") {
_href = links.toAbsolute(_href, o.dir || "./", o.outdir || "./");
_href = _href.replace(".md", ".html");
}
// Generate HTML for link
var out = '<a href="' + _href + '"';
// Title if no null
if (title) {
out += ' title="' + title + '"';
}
// Target blank if external
if(parsed.protocol) {
out += ' target="_blank"';
}
out += '>' + text + '</a>';
return out;
};
GitBookRenderer.prototype.image = function(href, title, text) {
// Our "fixed" href
var _href = href;
// Parsed version of the url
var parsed = url.parse(href);
// Options
var o = this._extra_options;
// Relative image, rewrite it depending output
if(links.isRelative(href) && o && o.dir && o.outdir) {
// o.dir: directory parent of the file currently in rendering process
// o.outdir: directory parent from the html output
_href = links.toAbsolute(_href, o.dir, o.outdir);
}
return GitBookRenderer.super_.prototype.image.call(this, _href, title, text);
};
GitBookRenderer.prototype.tablerow = function(content) {
this.quizRowId += 1;
return GitBookRenderer.super_.prototype.tablerow(content);
};
var fieldRegex = /^([(\[])([ x])[\])]/;
GitBookRenderer.prototype._createCheckboxAndRadios = function(text) {
var match = fieldRegex.exec(text);
if (!match) {
return text;
}
//fix radio input uncheck failed
var quizFieldName='quiz-row-' + this.id + '-' + this.quizRowId ;
var quizIdentifier = quizFieldName + '-' + this.quizIndex++;
var field = "<input name='" + quizFieldName + "' id='" + quizIdentifier + "' type='";
field += match[1] === '(' ? "radio" : "checkbox";
field += match[2] === 'x' ? "' checked/>" : "'/>";
var splittedText = text.split(fieldRegex);
var length = splittedText.length;
var label = '<label class="quiz-label" for="' + quizIdentifier + '">' + splittedText[length - 1] + '</label>';
return text.replace(fieldRegex, field).replace(splittedText[length - 1], label);
};
GitBookRenderer.prototype.tablecell = function(content, flags) {
return GitBookRenderer.super_.prototype.tablecell(this._createCheckboxAndRadios(content), flags);
};
GitBookRenderer.prototype.listitem = function(text) {
return GitBookRenderer.super_.prototype.listitem(this._createCheckboxAndRadios(text));
};
GitBookRenderer.prototype.code = function(code, lang, escaped) {
return GitBookRenderer.super_.prototype.code.call(
this,
code,
lang,
escaped
);
};
GitBookRenderer.prototype.heading = function(text, level, raw) {
var id = this.options.headerPrefix + raw.toLowerCase().replace(/[^\w -]+/g, '').replace(/ /g, '-');
return '<h' + level + ' id="' + id + '">' + text + '</h' + level + '>\n';
};
// Exports
module.exports = GitBookRenderer;
+148
View File
@@ -0,0 +1,148 @@
var _ = require('lodash');
var kramed = require('kramed');
// Utility function for splitting a list into groups
function splitBy(list, starter, ender) {
var starts = 0;
var ends = 0;
var group = [];
// Groups
return _.reduce(list, function(groups, value) {
// Ignore start and end delimiters in resulted groups
if(starter(value)) {
starts++;
} else if(ender(value)) {
ends++;
}
// Add current value to group
group.push(value);
// We've got a matching
if(starts === ends && starts !== 0) {
// Add group to end groups
// (remove starter and ender token)
groups.push(group.slice(1, -1));
// Reset group
group = [];
}
return groups;
}, []);
}
function listSplit(nodes, start_type, end_type) {
return splitBy(nodes, function(el) {
return el.type === start_type;
}, function(el) {
return el.type === end_type;
});
}
// Get the biggest list
// out of a list of kramed nodes
function filterList(nodes) {
return _.chain(nodes)
.toArray()
.rest(function(el) {
// Get everything after list_start
return el.type !== 'list_start';
})
.reverse()
.rest(function(el) {
// Get everything after list_end (remember we're reversed)
return el.type !== 'list_end';
})
.reverse()
.value().slice(1, -1);
}
// Parses an Article or Chapter title
// supports extracting links
function parseTitle(src, nums) {
// Check if it's a link
var matches = kramed.InlineLexer.rules.link.exec(src);
var level = nums.join('.');
// Not a link, return plain text
if(!matches) {
return {
title: src,
level: level,
path: null,
};
}
return {
title: matches[1],
level: level,
// Normalize path
// 1. Convert Window's "\" to "/"
// 2. Remove leading "/" if exists
path: matches[2].replace(/\\/g, '/').replace(/^\/+/, ''),
};
}
function parseChapter(nodes, nums) {
// Convert single number to an array
nums = _.isArray(nums) ? nums : [nums];
return _.extend(parseTitle(_.first(nodes).text, nums), {
articles: _.map(listSplit(filterList(nodes), 'list_item_start', 'list_item_end'), function(nodes, i) {
return parseChapter(nodes, nums.concat(i + 1));
})
});
}
function defaultChapterList(chapterList) {
var first = _.first(chapterList);
// Check if introduction node was specified in SUMMARY.md
if (first) {
var chapter = parseChapter(first, [0]);
// Already have README node, we're good to go
if(chapter.path === 'README.md') {
return chapterList;
}
}
// It wasn't specified, so add in default
return [
[ { type: 'text', text: '[Introduction](README.md)' } ]
].concat(chapterList);
}
function listGroups(src) {
var nodes = kramed.lexer(src);
// Get out groups of lists
return listSplit(
filterList(nodes),
'list_item_start', 'list_item_end'
);
}
function parseSummary(src) {
// Split out chapter sections
var chapters = defaultChapterList(listGroups(src))
.map(parseChapter);
return {
chapters: chapters
};
}
function parseEntries (src) {
return listGroups(src).map(parseChapter);
}
// Exports
module.exports = parseSummary;
module.exports.entries = parseEntries;
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
lang: require('./lang'),
links: require('./links')
};
+19
View File
@@ -0,0 +1,19 @@
var MAP = {
'py': 'python',
'js': 'javascript',
'rb': 'ruby',
'csharp': 'cs',
};
function normalize(lang) {
if(!lang) { return null; }
var lower = lang.toLowerCase();
return MAP[lower] || lower;
}
// Exports
module.exports = {
normalize: normalize,
MAP: MAP
};
+60
View File
@@ -0,0 +1,60 @@
var url = require('url');
var path = require('path');
// Is the link an external link
var isExternal = function(href) {
try {
return Boolean(url.parse(href).protocol);
} catch(err) { }
return false;
};
// Return true if the link is relative
var isRelative = function(href) {
try {
var parsed = url.parse(href);
return !parsed.protocol && parsed.path && parsed.path[0] != '/';
} catch(err) {}
return true;
};
// Relative to absolute path
// dir: directory parent of the file currently in rendering process
// outdir: directory parent from the html output
var toAbsolute = function(_href, dir, outdir) {
// Absolute file in source
_href = path.join(dir, _href);
// make it relative to output
_href = path.relative(outdir, _href);
if (process.platform === 'win32') {
_href = _href.replace(/\\/g, '/');
}
return _href;
};
// Join links
var join = function() {
var _href = path.join.apply(path, arguments);
if (process.platform === 'win32') {
_href = _href.replace(/\\/g, '/');
}
return _href;
};
module.exports = {
isRelative: isRelative,
isExternal: isExternal,
toAbsolute: toAbsolute,
join: join
};
+26
View File
@@ -0,0 +1,26 @@
var _ = require("lodash");
function escapeShellArg(arg) {
var ret = '';
ret = arg.replace(/"/g, '\\"');
return "\"" + ret + "\"";
}
function optionsToShellArgs(options) {
return _.chain(options)
.map(function(value, key) {
if (value == null || value === false) return null;
if (value === true) return key;
return key+"="+escapeShellArg(value);
})
.compact()
.value()
.join(" ");
}
module.exports = {
escapeShellArg: escapeShellArg,
optionsToShellArgs: optionsToShellArgs
};
+62 -37
View File
@@ -1,45 +1,70 @@
{
"name": "gitbook",
"version": "0.1.0",
"version": "1.2.0",
"homepage": "http://www.gitbook.io/",
"description": "Library and cmd utility to generate GitBooks",
"main": "lib/index.js",
"dependencies": {
"q": "1.0.1",
"lodash": "2.4.1",
"kramed": "0.4.3",
"kramed-text-renderer": "0.2.1",
"lunr": "0.5.2",
"swig": "1.3.2",
"send": "0.2.0",
"fstream-ignore": "0.0.7",
"commander": "2.2.0",
"graceful-fs": "3.0.2",
"fs-extra": "0.10.0",
"highlight.js": "8.3.0",
"tmp": "0.0.23",
"semver": "2.2.1",
"gaze": "~0.5.1",
"resolve": "0.6.3",
"tiny-lr-fork": "0.0.5",
"gitbook-plugin": "0.0.2",
"gitbook-plugin-mathjax": "0.0.6",
"gitbook-plugin-livereload": "0.0.1",
"gitbook-plugin-exercises": "1.0.0",
"gitbook-plugin-quizzes": "1.0.0"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@changesets/cli": "^2.27.12",
"turbo": "^2.5.0",
"vercel": "^39.3.0"
"mocha": "1.18.2",
"grunt": "~0.4.2",
"grunt-cli": "0.1.11",
"grunt-contrib-copy": "0.5.0",
"grunt-contrib-less": "~0.5.0",
"grunt-contrib-requirejs": "0.4.1",
"grunt-bower-install-simple": "0.9.2"
},
"packageManager": "bun@1.2.15",
"overrides": {
"@codemirror/state": "6.4.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"esbuild": "0.24.2"
},
"private": true,
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"clean-deps": "rm -rf node_modules && rm -rf packages/*/node_modules",
"typecheck": "turbo run typecheck",
"format": "biome check --write ./",
"format:check": "biome check --diagnostic-level=error ./",
"unit": "turbo run unit",
"e2e": "turbo run e2e",
"e2e-customers": "turbo run e2e-customers",
"changeset": "changeset",
"changeset-version": "changeset version && bun run format",
"release": "turbo run release && changeset publish",
"download:env": "op read op://gitbook-x-dev/gitbook-open/.env.local >> .env.local",
"clean": "turbo run clean"
"test": "export TESTING=true; mocha --reporter list"
},
"workspaces": {
"packages": ["packages/*"],
"catalog": {
"@gitbook/api": "^0.136.0",
"bidc": "^0.0.2"
"bin": {
"gitbook": "./bin/gitbook.js"
},
"repository": {
"type": "git",
"url": "https://github.com/GitbookIO/gitbook.git"
},
"keywords": [
"git",
"book",
"gitbook"
],
"author": "FriendCode <contact@friendco.de>",
"license": "Apache 2",
"bugs": {
"url": "https://github.com/GitbookIO/gitbook/issues"
},
"contributors": [
{
"name": "Aaron O'Mullan",
"email": "aaron.omullan@gmail.com"
},
{
"name": "Samy Pessé",
"email": "samypesse@gmail.com"
}
},
"patchedDependencies": {
"decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch",
"@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch"
}
]
}
-1
View File
@@ -1 +0,0 @@
dist/
-13
View File
@@ -1,13 +0,0 @@
# @gitbook/browser-types
## 0.1.0
### Minor Changes
- cbc71a5: First version of the public package for typing script integrations.
### Patch Changes
- 854c448: Custom assistants followup
- Updated dependencies [25e2b40]
- @gitbook/icons@0.3.0
-3
View File
@@ -1,3 +0,0 @@
# `@gitbook/browser-types`
Typescript types for the global variables available in a GitBook website. These types can be used by integrations embedding scripts.
-24
View File
@@ -1,24 +0,0 @@
{
"name": "@gitbook/browser-types",
"description": "Typescript types for the global variables available in a GitBook website. These types can be used by integrations embedding scripts.",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"version": "0.1.0",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:"
},
"devDependencies": {
"typescript": "^5.5.3"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit"
},
"files": ["dist", "README.md", "CHANGELOG.md"]
}
-84
View File
@@ -1,84 +0,0 @@
import type { AIToolCallResult, AIToolDefinition } from '@gitbook/api';
import type { IconName } from '@gitbook/icons';
export type GitBookIntegrationEvent = 'load' | 'unload';
export type GitBookIntegrationEventCallback = (...args: any[]) => void;
export type GitBookIntegrationTool = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
*/
confirmation?: {
icon?: IconName;
label: string;
};
/**
* Callback when the tool is executed.
* The input is provided by the AI assistant following the input schema of the tool.
*/
execute: (input: object) => Promise<Pick<AIToolCallResult, 'output' | 'summary'>>;
};
export type GitBookAssistant = {
/**
* Name of the assistant displayed in the UI.
*/
label: string;
/**
* Icon of the assistant displayed in the UI.
* Any FontAwesome icon name is supported.
* @example 'sparkle'
*/
icon: string;
/**
* Callback when the assistant is opened.
*/
open: (query?: string) => void;
/**
* Whether to display the triggers for this assistant in the UI.
* @default true
*/
ui?: boolean;
};
export type GitBookGlobal = {
/**
* Register an event listener.
*/
addEventListener: (
type: GitBookIntegrationEvent,
func: GitBookIntegrationEventCallback
) => void;
/**
* Remove an event listener.
*/
removeEventListener: (
type: GitBookIntegrationEvent,
func: GitBookIntegrationEventCallback
) => void;
/**
* Register a custom tool to be exposed to the AI assistant.
*/
registerTool: (tool: GitBookIntegrationTool) => void;
/**
* Register a custom assistant to be available on the site.
*/
registerAssistant: (assistant: GitBookAssistant) => () => void;
};
declare global {
interface Window {
/**
* Global `window.GitBook` object accessible by integrations.
*/
GitBook?: GitBookGlobal;
}
}
-24
View File
@@ -1,24 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
-1
View File
@@ -1 +0,0 @@
dist/
-25
View File
@@ -1,25 +0,0 @@
# @gitbook/cache-tags
## 0.3.1
### Patch Changes
- 77397ca: Fix version of @gitbook/api referenced in package.json
## 0.3.0
### Minor Changes
- 116575c: Improve typing of getComputedContentSourceCacheTags to match latest API specification
## 0.2.0
### Minor Changes
- f32bf1f: Export function `getCacheTagForURL` to easily get the cache tag for a URL.
## 0.1.0
### Minor Changes
- 05ffd0e: Initial version of the package
-3
View File
@@ -1,3 +0,0 @@
# `@gitbook/cache-tags`
Utility to generate cache tags for GitBook Open.
-25
View File
@@ -1,25 +0,0 @@
{
"name": "@gitbook/cache-tags",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"version": "0.3.1",
"dependencies": {
"@gitbook/api": "catalog:",
"assert-never": "^1.2.1"
},
"devDependencies": {
"typescript": "^5.5.3"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"dev": "tsc -w"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
}
-213
View File
@@ -1,213 +0,0 @@
import type { ComputedContentSource } from '@gitbook/api';
import assertNever from 'assert-never';
/**
* Get a stringified cache tag for a given object.
*/
export function getCacheTag(
spec: /**
* All data related to a user
* @deprecated - in v2, no tag as this is an immutable data
*/
| {
tag: 'user';
user: string;
}
/**
* All data related to a space
*/
| {
tag: 'space';
space: string;
}
/**
* All data related to an integration.
*/
| {
tag: 'integration';
integration: string;
}
/**
* All data related to a change request
*/
| {
tag: 'change-request';
space: string;
changeRequest: string;
}
/**
* Immutable data related to a revision
* @deprecated - in v2, no tag as this is an immutable data
*/
| {
tag: 'revision';
space: string;
revision: string;
}
/**
* Immutable data related to a document
* @deprecated - in v2, no tag as this is an immutable data
*/
| {
tag: 'document';
space: string;
document: string;
}
/**
* Immutable data related to a computed document
* @deprecated - in v2, no tag as this is an immutable data
*/
| {
tag: 'computed-document';
space: string;
sourceType: string;
}
/**
* All data related to the URL of a content
*/
| {
tag: 'url';
hostname: string;
}
/**
* All data related to a site
*/
| {
tag: 'site';
site: string;
}
/**
* All data related to an OpenAPI spec
*/
| {
tag: 'openapi';
organization: string;
openAPISpec: string;
}
/**
* All data related to a translation
*/
| {
tag: 'translation';
organization: string;
translation: string;
}
): string {
switch (spec.tag) {
case 'user':
return `user:${spec.user}`;
case 'url':
return `url:${spec.hostname}`;
case 'space':
return `space:${spec.space}`;
case 'change-request':
return `space:${spec.space}:change-request:${spec.changeRequest}`;
case 'revision':
return `space:${spec.space}:revision:${spec.revision}`;
case 'document':
return `space:${spec.space}:document:${spec.document}`;
case 'computed-document':
return `space:${spec.space}:computed-document:${spec.sourceType}`;
case 'site':
return `site:${spec.site}`;
case 'integration':
return `integration:${spec.integration}`;
case 'openapi':
return `organization:${spec.organization}:openapi:${spec.openAPISpec}`;
case 'translation':
return `organization:${spec.organization}:translation:${spec.translation}`;
default:
assertNever(spec);
}
}
/**
* Get the cache tag for a given URL.
*/
export function getCacheTagForURL(url: string | URL) {
const parsedURL = url instanceof URL ? url : new URL(url);
return getCacheTag({
tag: 'url',
hostname: parsedURL.hostname,
});
}
/**
* Get the tags for a computed content source.
*/
export function getComputedContentSourceCacheTags(
inContext: {
spaceId: string;
organizationId: string;
},
source: ComputedContentSource
) {
const tags: string[] = [];
if (!('dependencies' in source)) {
return tags;
}
// We add the dependencies as tags, to ensure that the computed content is invalidated
// when the dependencies are updated.
const dependencies = Object.values(source.dependencies ?? {});
if (dependencies.length > 0) {
dependencies.forEach((dependency) => {
switch (dependency.ref.kind) {
case 'space':
tags.push(
getCacheTag({
tag: 'space',
space: dependency.ref.space,
})
);
break;
case 'openapi':
tags.push(
getCacheTag({
tag: 'openapi',
organization: inContext.organizationId,
openAPISpec: dependency.ref.spec,
})
);
break;
case 'translation':
tags.push(
getCacheTag({
tag: 'translation',
organization: inContext.organizationId,
translation: dependency.ref.translation,
})
);
break;
default:
// Do not throw for unknown dependency types
// as it might mean we are lacking behind the API version
break;
}
});
} else {
// Push a dummy tag, as the v1 is only using the first tag
tags.push(
getCacheTag({
tag: 'computed-document',
space: inContext.spaceId,
sourceType: source.type,
})
);
}
// We invalidate the computed content when a new version of the integration is deployed.
if (source.type.startsWith('integration:')) {
const integration = source.type.split(':')[1];
tags.push(
getCacheTag({
tag: 'integration',
integration,
})
);
}
return tags;
}
-24
View File
@@ -1,24 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
-1
View File
@@ -1 +0,0 @@
dist/
-33
View File
@@ -1,33 +0,0 @@
# @gitbook/colors
## 0.3.3
### Patch Changes
- c3f6b8c: Update chroma ratio per step
- 5e975ab: Fix code highlighting for HTTP
- f7a3470: Change lightness check for color step 9 to allow input colors with a higher-than-needed contrast
## 0.3.2
### Patch Changes
- cdffd7c: Desaturate text colors by decreasing chroma for the last steps of the color scale
## 0.3.1
### Patch Changes
- fb90eb0: Reduce chroma of first color scale step
## 0.3.0
### Minor Changes
- 4f0a772: Override tint lightness if supplied color is out of bounds
## 0.2.0
### Minor Changes
- 445baaa: Initial release
-3
View File
@@ -1,3 +0,0 @@
# `@gitbook/colors`
A set of default colors and transformation functions used throughout the GitBook Open and app.
-21
View File
@@ -1,21 +0,0 @@
{
"name": "@gitbook/colors",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"development": "./src/index.ts",
"default": "./dist/index.js"
}
},
"version": "0.3.3",
"devDependencies": {
"typescript": "^5.5.3"
},
"scripts": {
"build": "tsc",
"typecheck": "tsc --noEmit",
"dev": "tsc -w"
},
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
}
-39
View File
@@ -1,39 +0,0 @@
/**
* Default primary color throughout the GitBook ecosystem.
*/
export const DEFAULT_PRIMARY_COLOR = '#346DDB';
/**
* The darkest color that exists in GitBook, used as the relative minimum of every generated color scale.
*/
export const DARK_BASE = '#1D1D1D';
/**
* The lightest color that exists in GitBook, used as the relative maximum of every generated color scale.
*/
export const LIGHT_BASE = '#FFFFFF';
/**
* Used as the basis of all UI elements that are not colored by the primary color. Neutral gray by default, overridden by site customization.
*/
export const DEFAULT_TINT_COLOR = '#787878';
/**
* Used for informational messages and neutral alerts.
*/
export const DEFAULT_HINT_INFO_COLOR = '#787878';
/**
* Used for showing important information or non-critical warnings.
*/
export const DEFAULT_HINT_WARNING_COLOR = '#FE9A00';
/**
* Used for destructive actions or raising attention to critical information.
*/
export const DEFAULT_HINT_DANGER_COLOR = '#FB2C36';
/**
* Used for showing positive actions or achievements.
*/
export const DEFAULT_HINT_SUCCESS_COLOR = '#00C950';
-2
View File
@@ -1,2 +0,0 @@
export * from './colors';
export * from './transformations';
-440
View File
@@ -1,440 +0,0 @@
import { DARK_BASE, DEFAULT_TINT_COLOR, LIGHT_BASE } from './colors';
type ColorShades = {
[key: string]: string;
};
type RGBColor = [number, number, number];
type OKLABColor = { L: number; A: number; B: number };
type OKLCHColor = { L: number; C: number; H: number };
const D65 = [95.047, 100.0, 108.883]; // Reference white (D65)
export enum ColorCategory {
backgrounds = 'backgrounds',
components = 'components',
borders = 'borders',
accents = 'accents',
text = 'text',
}
type ColorSubScale = {
[key: string]: number;
};
/**
* Main color scale object.
*
* Each `ColorCategory` can be in/excluded in Tailwind's utility classes generation.
* Each subitem maps a semantic name within that category to a step in the scale.
*/
export const scale: Record<ColorCategory, ColorSubScale> = {
[ColorCategory.backgrounds]: {
/** Base background */
base: 1,
/** Accent background */
subtle: 2,
},
[ColorCategory.components]: {
/** Component background */
DEFAULT: 3,
/** Component hover background */
hover: 4,
/** Component active background */
active: 5,
},
[ColorCategory.borders]: {
/** Subtle borders, separators */
subtle: 6,
/** Element border, focus rings */
DEFAULT: 7,
/** Element hover border */
hover: 8,
},
[ColorCategory.accents]: {
/** Solid backgrounds */
solid: 9,
/** Hovered solid backgrounds */
'solid-hover': 10,
},
[ColorCategory.text]: {
/** Very low-contrast text
* Caution: this contrast does not meet accessiblity guidelines.
* Always check if you need to include a mitigating contrast-more style for users who need it. */
subtle: 9,
/** Low-contrast text */
DEFAULT: 11,
/** High-contrast text */
strong: 12,
},
};
/**
* The mix of foreground and background for every step in a colour scale.
* 0: 100% of the background color's luminosity, white in light mode
* 1: 100% of the foreground color's luminosity, black in light mode
*/
export const colorMixMapping = {
// bgs |components |borders |solid |text
light: [0, 0.02, 0.03, 0.05, 0.07, 0.1, 0.15, 0.2, 0.5, 0.55, 0.6, 1],
dark: [0, 0.03, 0.08, 0.1, 0.13, 0.15, 0.2, 0.25, 0.5, 0.55, 0.75, 1],
};
/**
* Convert a hex color to an RGB color.
*/
export function hexToRgb(hex: string): string {
const [r, g, b] = hexToRgbArray(hex);
// Return the RGB values separated by spaces
return `${r} ${g} ${b}`;
}
/**
* Convert a hex color to a RGBA color.
*/
export function hexToRgba(hex: string, alpha: number): string {
const [r, g, b] = hexToRgbArray(hex);
// Return the RGBA values separated by spaces
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
/**
* Generate Tailwind-compatible shades from a single color
* @param {string} hex The hex code to generate shades from
* @param {boolean} halfShades Generate additional shades, e.g. at 150
* @returns {{[key: number]: string}}
*/
export function shadesOfColor(hex: string, halfShades = false) {
const baseColor = hex;
const shades = [
50,
100,
200,
300,
400,
500,
600,
700,
800,
900,
...(halfShades ? [150, 250, 350, 450, 550, 650, 750, 850] : []),
].sort();
const result: ColorShades = {};
for (const shade of shades) {
const key = shade.toString();
if (shade === 500) {
result[key] = hex;
continue;
}
let shadeIndex = shade;
const isDarkShade = shadeIndex > 500;
if (isDarkShade) {
shadeIndex -= 500;
}
const percentage = shadeIndex / 500;
const startColor = isDarkShade ? DARK_BASE : baseColor;
const endColor = isDarkShade ? baseColor : LIGHT_BASE;
result[key] = getColor(percentage, hexToRgbArray(startColor), hexToRgbArray(endColor));
}
return result;
}
export type ColorScaleOptions = {
/** If set to `true`, inverts the scale (so 1 is black instead of white) and uses `colorMixMapping.dark` with different mix ratios per step. */
darkMode?: boolean;
/** Define a custom background color to use. If left undefined, the global `light`/`dark` values (in `colors.ts`) will be used. */
background?: string;
/** Define a custom foreground color to use. If left undefined, the global `light`/`dark` values (in `colors.ts`) will be used. */
foreground?: string;
mix?: {
/** If set to a hex code, this color will be additionally mixed into the generated scale according to `mix.ratio`. */
color: string;
/** Define a custom mix ratio to mix the `mix` color with. If left undefined, the default ratio will be used. */
ratio: number;
};
};
/**
* Generate a [Radix-like](https://www.radix-ui.com/colors/docs/palette-composition/understanding-the-scale) colour scale based of a hex colour.
* @param {string} hex The hex code to generate shades from
* @param {object} options
*/
export function colorScale(
hex: string,
{
darkMode = false,
background = darkMode ? DARK_BASE : LIGHT_BASE,
foreground = darkMode ? LIGHT_BASE : DARK_BASE,
mix,
}: ColorScaleOptions = {}
) {
const baseColor = rgbToOklch(hexToRgbArray(hex));
const mixColor = mix?.color ? rgbToOklch(hexToRgbArray(mix.color)) : null;
const foregroundColor = rgbToOklch(hexToRgbArray(foreground));
const backgroundColor = rgbToOklch(hexToRgbArray(background));
let mapping = darkMode ? colorMixMapping.dark : colorMixMapping.light;
if (mixColor && mix?.ratio && mix.ratio > 0) {
// If defined, we mix in a (tiny) bit of the mix color with the base color.
baseColor.L = mixColor.L * mix.ratio + baseColor.L * (1 - mix.ratio);
baseColor.C = mixColor.C * mix.ratio + baseColor.C * (1 - mix.ratio);
baseColor.H = mix.color === DEFAULT_TINT_COLOR ? baseColor.H : mixColor.H;
}
if (
(darkMode && baseColor.L < backgroundColor.L) ||
(!darkMode && baseColor.L > backgroundColor.L)
) {
// If the supplied color is outside of our lightness bounds, use the supplied color's lightness.
// This is mostly used to allow darker-than-dark backgrounds for brands that specifically want that look.
const difference = (backgroundColor.L - baseColor.L) / backgroundColor.L;
backgroundColor.L = baseColor.L;
// At the edges of the scale, the subtle lightness changes stop being perceptible. We need to amp up our mapping to still stand out.
const amplifier = 1;
mapping = mapping.map((step, index) =>
index < 9 ? step + step * amplifier * difference : step
);
}
const result = [];
for (let index = 0; index < mapping.length; index++) {
const targetL =
foregroundColor.L * mapping[index] + backgroundColor.L * (1 - mapping[index]);
if (
index === 8 &&
!mix &&
(darkMode ? targetL - baseColor.L < 0.2 : baseColor.L - targetL < 0.2)
) {
// Original colour is close enough to target, so let's use the original colour as step 9.
result.push(hex);
continue;
}
const chromaRatio = (() => {
switch (index) {
// Step 9 and 10 have max chroma, meaning they are fully saturated.
case 8:
case 9:
return 1;
// Step 11 and 12 have a reduced chroma
case 10:
return 0.4;
case 11:
return 0.1;
default:
return index * 0.05;
}
})();
const shade = {
L: targetL, // Blend lightness
C: baseColor.C * chromaRatio,
H: baseColor.H, // Maintain the hue from the base color
};
const newHex = rgbArrayToHex(oklchToRgb(shade));
result.push(newHex);
}
return result;
}
/**
* Convert a hex color to an RGB color set.
*/
export function hexToRgbArray(hex: string): RGBColor {
const originalHex = hex;
let value = hex.replace('#', '');
if (hex.length === 3) value = value + value;
const r = value.substring(0, 2);
const g = value.substring(2, 4);
const b = value.substring(4, 6);
const rgb = [r, g, b].map((channel) => {
try {
const channelInt = Number.parseInt(channel, 16);
if (channelInt < 0 || channelInt > 255) throw new Error();
return channelInt;
} catch {
throw new Error(`Invalid hex color provided: ${originalHex}`);
}
});
return rgb as RGBColor;
}
/**
* Convert a RGB color set to a hex color.
*/
export function rgbArrayToHex(rgb: RGBColor): string {
return `#${rgb
.map((channel) => {
const component = channel.toString(16);
if (component.length === 1) return `0${component}`;
return component;
})
.join('')}`;
}
export function getColor(percentage: number, start: RGBColor, end: RGBColor) {
const rgb = end.map((channel, index) => {
return Math.round(channel + percentage * (start[index] - channel));
});
return rgbArrayToHex(rgb as RGBColor);
}
// Utility constants and helper functions
export function rgbToLinear(rgb: RGBColor): [number, number, number] {
return rgb.map((v) => {
const scaled = v / 255;
return scaled <= 0.04045 ? scaled / 12.92 : ((scaled + 0.055) / 1.055) ** 2.4;
}) as [number, number, number];
}
export function linearToRgb(linear: [number, number, number]): RGBColor {
return linear.map((v) => {
const scaled = v <= 0.0031308 ? 12.92 * v : 1.055 * v ** (1 / 2.4) - 0.055;
return Math.round(Math.max(0, Math.min(1, scaled)) * 255);
}) as RGBColor;
}
export function rgbToOklab(rgb: RGBColor): OKLABColor {
const [r, g, b] = rgbToLinear(rgb);
const l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
const m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
const s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
const lRoot = Math.cbrt(l);
const mRoot = Math.cbrt(m);
const sRoot = Math.cbrt(s);
return {
L: 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot,
A: 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot,
B: 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot,
};
}
export function oklabToRgb(oklab: OKLABColor): RGBColor {
const { L, A, B } = oklab;
const lRoot = L + 0.3963377774 * A + 0.2158037573 * B;
const mRoot = L - 0.1055613458 * A - 0.0638541728 * B;
const sRoot = L - 0.0894841775 * A - 1.291485548 * B;
const l = lRoot ** 3;
const m = mRoot ** 3;
const s = sRoot ** 3;
const r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
const g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
const b = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
return linearToRgb([r, g, b]);
}
export function oklabToOklch(oklab: OKLABColor): OKLCHColor {
const { L, A, B } = oklab;
const C = Math.sqrt(A ** 2 + B ** 2);
const H = (Math.atan2(B, A) * 180) / Math.PI;
return { L, C, H: H < 0 ? H + 360 : H };
}
export function oklchToOklab(oklch: OKLCHColor): OKLABColor {
const { L, C, H } = oklch;
const rad = (H * Math.PI) / 180;
return {
L,
A: C * Math.cos(rad),
B: C * Math.sin(rad),
};
}
export function rgbToOklch(rgb: RGBColor): OKLCHColor {
return oklabToOklch(rgbToOklab(rgb));
}
export function oklchToRgb(oklch: OKLCHColor): RGBColor {
return oklabToRgb(oklchToOklab(oklch));
}
export function rgbToXyz(rgb: RGBColor): [number, number, number] {
const [r, g, b] = rgbToLinear(rgb);
return [
(r * 0.4124564 + g * 0.3575761 + b * 0.1804375) * 100,
(r * 0.2126729 + g * 0.7151522 + b * 0.072175) * 100,
(r * 0.0193339 + g * 0.119192 + b * 0.9503041) * 100,
];
}
export function xyzToLab65(xyz: [number, number, number]): {
L: number;
A: number;
B: number;
} {
const [x, y, z] = xyz.map((v, i) => {
const scaled = v / D65[i];
return scaled > 0.008856 ? Math.cbrt(scaled) : 7.787 * scaled + 16 / 116;
});
return {
L: 116 * y - 16,
A: 500 * (x - y),
B: 200 * (y - z),
};
}
export function rgbTolab65(rgb: RGBColor): { L: number; A: number; B: number } {
return xyzToLab65(rgbToXyz(rgb));
}
/*
Delta Phi Star perceptual lightness contrast by Andrew Somers:
https://github.com/Myndex/deltaphistar
*/
export const PHI = 0.5 + Math.sqrt(1.25);
export function dpsContrast(a: RGBColor, b: RGBColor) {
const dps = Math.abs(rgbTolab65(a).L ** PHI - rgbTolab65(b).L ** PHI);
const contrast = dps ** (1 / PHI) * Math.SQRT2 - 40;
return contrast < 7.5 ? 0 : contrast;
}
export function colorContrast(background: string, foreground: string[] = [LIGHT_BASE, DARK_BASE]) {
const bg = hexToRgbArray(background);
const best: { color?: RGBColor; contrast: number } = {
color: undefined,
contrast: 0,
};
for (const color of foreground) {
const c = hexToRgbArray(color);
const contrast = dpsContrast(c, bg);
if (contrast > best.contrast) {
best.color = c;
best.contrast = contrast;
}
}
return best.color ? rgbArrayToHex(best.color) : foreground[0];
}
-24
View File
@@ -1,24 +0,0 @@
{
"compilerOptions": {
"target": "esnext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": false,
"declaration": true,
"outDir": "dist",
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"types": [
"bun-types" // add Bun global
]
},
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["node_modules"]
}
-2
View File
@@ -1,2 +0,0 @@
dist/
standalone/
-13
View File
@@ -1,13 +0,0 @@
# @gitbook/embed
## 0.1.0
### Minor Changes
- 81a6bd7: Improve API to control the GitBook embed
### Patch Changes
- 8927e8f: Initial version of the embed SDK.
- Updated dependencies [25e2b40]
- @gitbook/icons@0.3.0
-49
View File
@@ -1,49 +0,0 @@
# `@gitbook/embed`
Embed the GitBook Docs Assistant in your product or website.
# Usage
## As a script from your docs site
All GitBook docs site includes a script to easily embed the docs assistant as a widget on your website.
The script is served at `https://docs.company.com/~gitbook/embed/script.js`.
You can find the embed script from your docs site settings, or you can copy the following and replace the `docs.company.com` by your docs site hostname.
```html
<script src="https://docs.company.com/~gitbook/embed/script.js"></script>
<script>
window.GitBook('show');
</script>
```
## As a package from NPM
Install the package: `npm install @gitbook/embed` and import it in your web application:
```tsx
import { createGitBook } from '@gitbook/embed';
const gitbook = createGitBook({
siteURL: 'https://docs.company.com'
});
const iframe = document.createElement('iframe');
iframe.src = gitbook.getFrameURL();
const frame = gitbook.createFrame(iframe);
```
## As React components
After installing the NPM package, you can import prebuilt React components:
```tsx
import { GitBookProvider, GitBookAssistantFrame } from '@gitbook/embed/react';
<GitBookProvider siteURL="https://docs.company.com">
<GitBookAssistantFrame />
</GitBookProvider>
```
-31
View File
@@ -1,31 +0,0 @@
{
"name": "@gitbook/embed",
"description": "Embeddable components for GitBook",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js",
"standalone": "./dist/standalone/index.js",
"react": "./dist/react/index.js"
}
},
"version": "0.1.0",
"dependencies": {
"@gitbook/api": "catalog:",
"@gitbook/icons": "workspace:",
"bidc": "catalog:"
},
"peerDependencies": {
"react": "^18.0.0"
},
"devDependencies": {
"typescript": "^5.5.3",
"react": "^19.0.0"
},
"scripts": {
"build": "tsc && bun build src/standalone/index.ts --bundle --minify --outdir=standalone",
"typecheck": "tsc --noEmit"
},
"files": ["dist", "README.md", "CHANGELOG.md", "standalone"]
}
@@ -1,61 +0,0 @@
import { type GitBookFrameClient, createGitBookFrame } from './createGitBookFrame';
export type CreateGitBookOptions = {
/**
* URL of the GitBook site to embed.
*/
siteURL: string;
};
export type GetFrameURLOptions = {
/**
* Authentication to use for the frame.
*/
visitor?: {
/**
* Signed JWT token for Adaptive Content or Visitor Authentication to use.
*/
token?: string;
/**
* Unsigned claims to pass to the frame.
* You can use these claims in dynamic expressions using `visitor.claims.unsigned.<claim-name>`.
*/
unsignedClaims?: Record<string, unknown>;
};
};
export type GitBookClient = {
/**
* Get the URL for a GitBook frame.
*/
getFrameURL: (options: GetFrameURLOptions) => string;
/**
* Create a new GitBook frame.
*/
createFrame: (iframe: HTMLIFrameElement) => GitBookFrameClient;
};
export function createGitBook(options: CreateGitBookOptions) {
const client: GitBookClient = {
getFrameURL: (frameOptions) => {
const url = new URL(options.siteURL);
url.pathname = `${url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`}~gitbook/embed/assistant`;
if (frameOptions.visitor?.token) {
url.searchParams.set('token', frameOptions.visitor.token);
}
if (frameOptions.visitor?.unsignedClaims) {
Object.entries(frameOptions.visitor.unsignedClaims).forEach(([key, value]) => {
url.searchParams.set(`visitor.${key}`, String(value));
});
}
return url.toString();
},
createFrame: (iframe) => createGitBookFrame(iframe),
};
return client;
}
@@ -1,98 +0,0 @@
import { createChannel } from 'bidc';
import type {
FrameToParentMessage,
GitBookEmbeddableConfiguration,
ParentToFrameMessage,
} from './protocol';
export type GitBookFrameClient = {
/**
* Navigate to a page by its path.
*/
navigateToPage: (path: string) => void;
/**
* Navigate to the assistant.
*/
navigateToAssistant: () => void;
/**
* Post a message to the chat.
*/
postUserMessage: (message: string) => void;
/**
* Clear the chat.
*/
clearChat: () => void;
/**
* Set the placeholder settings.
*/
configure: (settings: Partial<GitBookEmbeddableConfiguration>) => void;
/**
* Register an event listener.
*/
on: (event: string, listener: (...args: any[]) => void) => () => void;
};
/**
* Create a client to communicate with the GitBook Assistant frame.
*/
export function createGitBookFrame(iframe: HTMLIFrameElement): GitBookFrameClient {
if (!iframe.contentWindow) {
throw new Error('Iframe must have a content window');
}
const channel = createChannel(iframe.contentWindow);
channel.receive((message: FrameToParentMessage) => {
console.log('[gitbook:embed] received message', message);
if (message.type === 'close') {
const listeners = events.get('close') || [];
if (listeners) {
listeners.forEach((listener) => listener());
}
}
});
const sendToFrame = (message: ParentToFrameMessage) => {
console.log('[gitbook:embed] send message', message);
channel.send(message);
};
const events = new Map<string, Array<(...args: any[]) => void>>();
const configuration: GitBookEmbeddableConfiguration = {
buttons: [],
welcomeMessage: '',
suggestions: [],
tools: [],
};
return {
navigateToPage: (pagePath) => {
sendToFrame({ type: 'navigateToPage', pagePath });
},
navigateToAssistant: () => {
sendToFrame({ type: 'navigateToAssistant' });
},
postUserMessage: (message) => sendToFrame({ type: 'postUserMessage', message }),
configure: (settings) => {
Object.assign(configuration, settings);
sendToFrame({ type: 'configure', settings: configuration });
},
clearChat: () => sendToFrame({ type: 'clearChat' }),
on: (event, listener) => {
const listeners = events.get(event) || [];
listeners.push(listener);
events.set(event, listeners);
return () => {
events.set(
event,
listeners.filter((l) => l !== listener)
);
};
},
};
}
-3
View File
@@ -1,3 +0,0 @@
export * from './createGitBook';
export * from './createGitBookFrame';
export * from './protocol';
-90
View File
@@ -1,90 +0,0 @@
import type { AIToolCallResult, AIToolDefinition } from '@gitbook/api';
import type { IconName } from '@gitbook/icons';
/**
* Custom tool definition to be passed to the AI assistant.
*/
export type GitBookToolDefinition = AIToolDefinition & {
/**
* Confirmation action to be displayed to the user before executing the tool.
*/
confirmation?: {
icon?: IconName;
label: string;
};
/**
* Callback when the tool is executed.
* The input is provided by the AI assistant following the input schema of the tool.
*/
execute: (input: object) => Promise<Pick<AIToolCallResult, 'output' | 'summary'>>;
};
/**
* Custom button definition to be passed to the embeddable GitBook.
*/
export type GitBookEmbeddableButtonDefinition = {
/**
* Icon to be displayed in the button.
*/
icon: IconName;
/**
* Label to be displayed in the button.
*/
label: string;
/**
* Callback when the button is clicked.
*/
onClick: () => void | Promise<void>;
};
/**
* Overall configuration for the layout of the embeddable GitBook.
*/
export type GitBookEmbeddableConfiguration = {
/**
* Buttons to be displayed in the header of the embeddable GitBook.
*/
buttons: GitBookEmbeddableButtonDefinition[];
/** Message to be displayed in the welcome page. */
welcomeMessage: string;
/** Suggestions of questions to be displayed in the welcome page. */
suggestions: string[];
/** Tools to be provided to the assistant. */
tools: GitBookToolDefinition[];
};
/**
* Messages sent from the parent to the frame.
*/
export type ParentToFrameMessage =
| {
type: 'postUserMessage';
message: string;
}
| {
type: 'clearChat';
}
| {
type: 'configure';
settings: GitBookEmbeddableConfiguration;
}
| {
type: 'navigateToPage';
pagePath: string;
}
| {
type: 'navigateToAssistant';
};
/**
* Messages sent from the frame to the parent.
*/
export type FrameToParentMessage = {
type: 'close';
};
-1
View File
@@ -1 +0,0 @@
export * from './client';
-51
View File
@@ -1,51 +0,0 @@
import React from 'react';
import type {
GetFrameURLOptions,
GitBookEmbeddableConfiguration,
GitBookFrameClient,
} from '../client';
import { useGitBook } from './GitBookProvider';
export type GitBookFrameProps = {
className?: string;
} & GetFrameURLOptions &
GitBookEmbeddableConfiguration;
/**
* Render a frame with the GitBook Assistant in it.
*/
export function GitBookFrame(props: GitBookFrameProps) {
const { className, visitor, buttons, welcomeMessage, suggestions, tools } = props;
const frameRef = React.useRef<HTMLIFrameElement>(null);
const gitbook = useGitBook();
const [gitbookFrame, setGitbookFrame] = React.useState<GitBookFrameClient | null>(null);
const frameURL = React.useMemo(() => gitbook.getFrameURL({ visitor }), [gitbook, visitor]);
React.useEffect(() => {
if (frameRef.current) {
setGitbookFrame(gitbook.createFrame(frameRef.current));
}
}, [gitbook]);
React.useEffect(() => {
gitbookFrame?.configure({
buttons,
welcomeMessage,
suggestions,
tools,
});
}, [gitbookFrame, buttons, welcomeMessage, suggestions, tools]);
return (
<iframe
title="GitBook"
ref={frameRef}
src={frameURL}
width="100%"
height="100%"
className={className}
/>
);
}
@@ -1,36 +0,0 @@
'use client';
import * as React from 'react';
import { type CreateGitBookOptions, createGitBook } from '../client';
import { GitBookContext } from './context';
/**
* Provider for the GitBook client.
*/
export function GitBookProvider(props: React.PropsWithChildren<CreateGitBookOptions>) {
const { siteURL, children } = props;
const options = React.useMemo(
() => ({
siteURL,
}),
[siteURL]
);
const client = React.useMemo(() => createGitBook(options), [options]);
return <GitBookContext.Provider value={client}>{children}</GitBookContext.Provider>;
}
/**
* Hook to access the GitBook client.
*/
export function useGitBook() {
const context = React.useContext(GitBookContext);
if (!context) {
throw new Error('This component must be used within a <GitBookProvider />');
}
return context;
}
-6
View File
@@ -1,6 +0,0 @@
'use client';
import * as React from 'react';
import type { GitBookClient } from '../client';
export const GitBookContext = React.createContext<GitBookClient | null>(null);
-2
View File
@@ -1,2 +0,0 @@
export * from './GitBookProvider';
export * from './GitBookFrame';
-178
View File
@@ -1,178 +0,0 @@
import './style.css';
import {
type CreateGitBookOptions,
type GetFrameURLOptions,
type GitBookClient,
type GitBookEmbeddableConfiguration,
type GitBookFrameClient,
createGitBook,
} from '../client';
export type GitBook = () => void;
type StandaloneCalls =
// Initialize the widget
| ['init', CreateGitBookOptions, GetFrameURLOptions]
// Unload the widget
| ['unload']
// Show the widget
| ['show']
// Hide the widget
| ['hide']
// Open the window
| ['open']
// Close the window
| ['close']
// Toggle the window
| ['toggle']
// Post a user message
| ['postUserMessage', string]
// Clear the chat
| ['clearChat']
// Configure the embed
| ['configure', Partial<GitBookEmbeddableConfiguration>]
// Navigate to a page
| ['navigateToPage', string]
// Navigate to the assistant
| ['navigateToAssistant'];
export type GitBookStandalone = ((...args: StandaloneCalls) => void) & {
q?: StandaloneCalls[];
};
const widgetButton = document.createElement('button');
widgetButton.id = 'gitbook-widget-button';
widgetButton.addEventListener('click', () => {
GitBook('toggle');
});
widgetButton.innerHTML = `
<span id="gitbook-widget-button-icon"></span>
<span id="gitbook-widget-button-label">Ask</span>
`;
const widgetWindow = document.createElement('div');
widgetWindow.id = 'gitbook-widget-window';
widgetWindow.classList.add('hidden');
document.body.appendChild(widgetButton);
document.body.appendChild(widgetWindow);
let widgetIframe: HTMLIFrameElement | undefined;
let _client: GitBookClient | undefined;
let _frame: GitBookFrameClient | undefined;
let frameOptions: GetFrameURLOptions | undefined;
let frameConfiguration: GitBookEmbeddableConfiguration = {
buttons: [],
welcomeMessage: '',
suggestions: [],
tools: [],
};
function getClient() {
if (!_client) {
throw new Error(
'GitBook client not initialized. Call GitBook("init", { siteURL: "..." }) first.'
);
}
return _client;
}
function getIframe() {
if (!widgetIframe || !_frame) {
const client = getClient();
widgetIframe?.remove();
widgetIframe = document.createElement('iframe');
widgetIframe.id = 'gitbook-widget-iframe';
widgetIframe.src = client.getFrameURL({
...frameOptions,
});
widgetWindow.appendChild(widgetIframe);
_frame = client.createFrame(widgetIframe);
}
return { iframe: widgetIframe, frame: _frame };
}
const GitBook = (...args: StandaloneCalls) => {
switch (args[0]) {
case 'init':
if (_client) {
throw new Error(
'GitBook client already initialized. Call GitBook("unload") first.'
);
}
_client = createGitBook(args[1]);
frameOptions = args[2];
break;
case 'unload':
_client = undefined;
_frame = undefined;
widgetIframe?.remove();
widgetWindow.classList.add('hidden');
break;
case 'show':
widgetButton.classList.remove('hidden');
break;
case 'hide':
widgetButton.classList.add('hidden');
break;
case 'open':
widgetWindow.classList.remove('hidden');
widgetButton.classList.add('open');
getIframe();
break;
case 'toggle':
widgetWindow.classList.toggle('hidden');
widgetButton.classList.toggle('open');
getIframe();
break;
case 'close':
widgetWindow.classList.add('hidden');
widgetButton.classList.remove('open');
break;
case 'postUserMessage':
getIframe().frame.postUserMessage(args[1]);
break;
case 'configure':
frameConfiguration = {
...frameConfiguration,
...args[1],
};
getIframe().frame.configure({
...frameConfiguration,
buttons: [
...frameConfiguration.buttons,
// Always include a close button
{
icon: 'close',
label: 'Close',
onClick: () => {
GitBook('close');
},
},
],
});
break;
case 'clearChat':
getIframe().frame.clearChat();
break;
case 'navigateToPage':
getIframe().frame.navigateToPage(args[1]);
break;
case 'navigateToAssistant':
getIframe().frame.navigateToAssistant();
break;
}
};
// @ts-expect-error - GitBook is not defined in the global scope
const precalls = (window.GitBook as GitBookStandalone | undefined)?.q ?? [];
// @ts-expect-error - GitBook is not defined in the global scope
window.GitBook = GitBook;
precalls.forEach((call) => GitBook(...call));
GitBook('configure', {});

Some files were not shown because too many files have changed in this diff Show More