History

Add settings page

Thibaut Courouble committed 9 years ago

Commit e94af971 parent ce8079f59 files · +477 −375Browse files

Unified · Split

59 files
  1. assets/images/icons.png +0 −0
  2. assets/images/icons@2x.png +0 −0
  3. assets/javascripts/app/appcache.coffee +6 −1
  4. assets/javascripts/app/router.coffee +15 −10
  5. assets/javascripts/app/settings.coffee +3 −0
  6. assets/javascripts/news.json +3 −3
  7. assets/javascripts/templates/notice_tmpl.coffee +1 −1
  8. assets/javascripts/templates/notif_tmpl.coffee +1 −1
  9. assets/javascripts/templates/pages/offline_tmpl.coffee +1 −1
  10. assets/javascripts/templates/pages/root_tmpl.coffee.erb +12 −12
  11. assets/javascripts/templates/pages/settings_tmpl.coffee +27 −0
  12. assets/javascripts/templates/sidebar_tmpl.coffee +2 −14
  13. assets/javascripts/views/content/content.coffee +8 −5
  14. assets/javascripts/views/content/settings_page.coffee +52 −0
  15. assets/javascripts/views/layout/document.coffee +11 −26
  16. assets/javascripts/views/layout/mobile.coffee +42 −10
  17. assets/javascripts/views/layout/resizer.coffee +0 −7
  18. assets/javascripts/views/layout/settings.coffee +80 −0
  19. assets/javascripts/views/search/search.coffee +0 −8
  20. assets/javascripts/views/sidebar/doc_picker.coffee +2 −42
  21. assets/javascripts/views/sidebar/sidebar.coffee +2 −26
  22. assets/stylesheets/application-dark.css.scss +1 −0
  23. assets/stylesheets/application.css.scss +1 −0
  24. assets/stylesheets/components/_content.scss +1 −4
  25. assets/stylesheets/components/_header.scss +3 −5
  26. assets/stylesheets/components/_mobile.scss +20 −19
  27. assets/stylesheets/components/_notice.scss +2 −1
  28. assets/stylesheets/components/_path.scss +1 −1
  29. assets/stylesheets/components/_settings.scss +146 −0
  30. assets/stylesheets/components/_sidebar.scss +5 −162
  31. assets/stylesheets/global/_base.scss +11 −0
  32. assets/stylesheets/global/_icons.scss +8 −18
  33. assets/stylesheets/global/_variables-dark.scss +1 −1
  34. assets/stylesheets/global/_variables.scss +1 −1
  35. lib/app.rb +1 −1
  36. public/icons/ui/check-white/SOURCE +0 −1
  37. public/icons/ui/check-white/check-white.png +0 −0
  38. public/icons/ui/check-white/check-white@2x.png +0 −0
  39. public/icons/ui/check/SOURCE +0 −1
  40. public/icons/ui/check/check.png +0 −0
  41. public/icons/ui/check/check@2x.png +0 −0
  42. public/icons/ui/contract/16.png +0 −0
  43. public/icons/ui/contract/16@2x.png +0 −0
  44. public/icons/ui/expand/16.png +0 −0
  45. public/icons/ui/expand/16@2x.png +0 −0
  46. public/icons/ui/light-white/SOURCE +0 −1
  47. public/icons/ui/light-white/light.png +0 −0
  48. public/icons/ui/light-white/light@2x.png +0 −0
  49. public/icons/ui/light/SOURCE +0 −1
  50. public/icons/ui/light/light.png +0 −0
  51. public/icons/ui/light/light@2x.png +0 −0
  52. public/icons/ui/settings-white/SOURCE +0 −1
  53. public/icons/ui/settings-white/settings-white.png +0 −0
  54. public/icons/ui/settings-white/settings-white@2x.png +0 −0
  55. public/icons/ui/settings/SOURCE +0 −1
  56. public/icons/ui/settings/settings.png +0 −0
  57. public/icons/ui/settings/settings@2x.png +0 −0
  58. public/robots.txt +1 −1
  59. views/app.erb +24 −6
assets/images/icons.png+0−0

Binary file not shown.

assets/images/icons@2x.png+0−0

Binary file not shown.

assets/javascripts/app/appcache.coffee+6−1
@@ -16,21 +16,26 @@ class app.AppCache · 15 unmodified lines
1616
1717 update: ->
1818 @notifyUpdate = true
19+ @notifyProgress = true
1920 try @cache.update() catch
2021 return
2122
2223 updateInBackground: ->
2324 @notifyUpdate = false
25+ @notifyProgress = false
2426 try @cache.update() catch
2527 return
2628
2729 reload: ->
2830 $.on @cache, 'updateready noupdate error', -> window.location = '/'
2931 @updateInBackground()
32+ @notifyUpdate = false
33+ @notifyProgress = true
34+ @cache.update()
3035 return
3136
3237 onProgress: (event) =>
33− @trigger 'progress', event
38+ @trigger 'progress', event if @notifyProgress
3439 return
3540
3641 onUpdateReady: =>
assets/javascripts/app/router.coffee+15−10
@@ -2,16 +2,17 @@ class app.Router · 1 unmodified line
22 $.extend @prototype, Events
33
44 @routes: [
5− ['*', 'before' ]
6− ['/', 'root' ]
7− ['/offline', 'offline' ]
8− ['/about', 'about' ]
9− ['/news', 'news' ]
10− ['/help', 'help' ]
11− ['/:doc-:type/', 'type' ]
12− ['/:doc/', 'doc' ]
13− ['/:doc/:path(*)', 'entry' ]
14− ['*', 'notFound']
5+ ['*', 'before' ]
6+ ['/', 'root' ]
7+ ['/settings', 'settings' ]
8+ ['/offline', 'offline' ]
9+ ['/about', 'about' ]
10+ ['/news', 'news' ]
11+ ['/help', 'help' ]
12+ ['/:doc-:type/', 'type' ]
13+ ['/:doc/', 'doc' ]
14+ ['/:doc/:path(*)', 'entry' ]
15+ ['*', 'notFound' ]
1516 ]
1617
1718 constructor: ->
@@ -76,6 +77,10 @@ class app.Router · 58 unmodified lines
7677 @triggerRoute 'root'
7778 return
7879
80+ settings: ->
81+ @triggerRoute 'settings'
82+ return
83+
7984 offline: ->
8085 @triggerRoute 'offline'
8186 return
assets/javascripts/app/settings.coffee+3−0
@@ -57,6 +57,9 @@ class app.Settings · 56 unmodified lines
5757 @store.set DARK_KEY, !!value
5858 return
5959
60+ getDark: ->
61+ @store.get DARK_KEY
62+
6063 setLayout: (name, enable) ->
6164 layout = (@store.get(LAYOUT_KEY) || '').split(' ')
6265 $.arrayDelete(layout, '')
assets/javascripts/news.json+3−3
@@ -64,7 +64,7 @@ · 63 unmodified lines
6464 "New documentations: <a href=\"/erlang/\">Erlang</a> and <a href=\"/tcl_tk/\">Tcl/Tk</a>"
6565 ], [
6666 "2016-01-24",
67− "&ldquo;Multi-version support&rdquo; has landed!\nClick <a href=\"#\" data-pick-docs>Select documentation</a> to pick which versions to use. More versions will be added in the coming weeks.\nIf you notice any bugs, please report them on <a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">GitHub</a>."
67+ "&ldquo;Multi-version support&rdquo; has landed!"
6868 ], [
6969 "2015-11-22",
7070 "New documentations: <a href=\"/phoenix/\">Phoenix</a>, <a href=\"/dojo/\">Dojo</a>, <a href=\"/relay/\">Relay</a> and <a href=\"/flow/\">Flow</a>"
@@ -117,10 +117,10 @@ · 46 unmodified lines
117117 "New <a href=\"/iojs/\">io.js</a>, <a href=\"/symfony/\">Symfony</a>, <a href=\"/clojure/\">Clojure</a>, <a href=\"/lua/\">Lua</a> and <a href=\"/yii1/\">Yii 1.1</a> documentations"
118118 ], [
119119 "2015-02-08",
120− "New dark theme\nClick the icon in the bottom left corner to activate.\n<a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">Feedback</a> welcome :)"
120+ "New dark theme"
121121 ], [
122122 "2015-01-13",
123− "<a href=\"/offline\">Offline mode</a> has landed!\nIf you notice any bugs, please report them on <a href=\"https://github.com/Thibaut/devdocs/issues\" target=\"_blank\" rel=\"noopener\">GitHub</a>."
123+ "<a href=\"/offline\">Offline mode</a> has landed!"
124124 ], [
125125 "2014-12-21",
126126 "New <a href=\"/react/\">React</a>, <a href=\"/rethinkdb/\">RethinkDB</a>, <a href=\"/socketio/\">Socket.IO</a>, <a href=\"/modernizr/\">Modernizr</a> and <a href=\"/bower/\">Bower</a> documentations"
assets/javascripts/templates/notice_tmpl.coffee+1−1
@@ -6,4 +6,4 @@ app.templates.singleDocNotice = (doc) -> · 5 unmodified lines
66
77 app.templates.disabledDocNotice = ->
88 notice """ <strong>This documentation is disabled.</strong>
9− To enable it, click <a href="#" class="_notice-link" data-pick-docs>Select documentation</a>. """
9+ To enable it, go to <a href="/settings" class="_notice-link">Preferences</a>. """
assets/javascripts/templates/notif_tmpl.coffee+1−1
@@ -47,7 +47,7 @@ app.templates.notifUpdates = (docs, disabledDocs) -> · 46 unmodified lines
4747 for doc in disabledDocs
4848 html += "<li>#{doc.name}"
4949 html += " <code>&rarr;</code> #{doc.release}" if doc.release
50− html += """<span class="_notif-info"><a data-pick-docs>Enable</a></span>"""
50+ html += """<span class="_notif-info"><a href="/settings">Enable</a></span>"""
5151 html += '</ul></div>'
5252
5353 notif 'Updates', "#{html}</div>"
assets/javascripts/templates/pages/offline_tmpl.coffee+1−1
@@ -35,7 +35,7 @@ app.templates.offlinePage = (docs) -> """ · 34 unmodified lines
3535 <dt>How do I uninstall/reset the app?
3636 <dd>Click <a href="#" data-behavior="reset">here</a>.
3737 <dt>Why aren't all documentations listed above?
38− <dd>You have to <a href="#" data-pick-docs>enable</a> them first.
38+ <dd>You have to <a href="/settings">enable</a> them first.
3939 </dl>
4040 """
4141
assets/javascripts/templates/pages/root_tmpl.coffee.erb+12−12
@@ -32,14 +32,14 @@ app.templates.intro = """ · 31 unmodified lines
3232 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
3333 Here's what you should know before you start:
3434 <ol class="_intro-list">
35− <li>To enable more docs, click <a class="_intro-link" data-pick-docs>Select documentation</a> in the bottom left corner
36− <li>You don't have to use your mouse &mdash; see the list of <a href="/help#shortcuts">keyboard shortcuts</a>
37− <li>The search supports fuzzy matching (e.g. "bgcp" brings up "background-clip")
38− <li>To search a specific documentation, type its name (or an abbreviation), then Tab
39− <li>You can search using your browser's address bar &mdash; <a href="/help#browser_search">learn how</a>
35+ <li>Open the <a href="/settings">Preferences</a> to enable more docs and customize the UI.
36+ <li>You don't have to use your mouse &mdash; see the list of <a href="/help#shortcuts">keyboard shortcuts</a>.
37+ <li>The search supports fuzzy matching (e.g. "bgcp" brings up "background-clip").
38+ <li>To search a specific documentation, type its name (or an abbr.), then Tab.
39+ <li>You can search using your browser's address bar &mdash; <a href="/help#browser_search">learn how</a>.
4040 <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>.
41− <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>
42− <li>DevDocs is free and <a href="https://github.com/Thibaut/devdocs">open source</a>
41+ <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
42+ <li>DevDocs is free and <a href="https://github.com/Thibaut/devdocs">open source</a>.
4343 <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>
4444 <li>If you like the app, please consider supporting the project on <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks!
4545 </ol>
@@ -54,11 +54,11 @@ app.templates.mobileIntro = """ · 8 unmodified lines
5454 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
5555 Here's what you should know before you start:
5656 <ol class="_intro-list">
57− <li>To pick your docs, click <a data-pick-docs>Select documentation</a> at the bottom of the menu
58− <li>The search supports fuzzy matching (e.g. "bgcp" matches "background-clip")
59− <li>To search a specific documentation, type its name (or an abbreviation), then Space
60− <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>
61− <li>DevDocs is <a href="https://github.com/Thibaut/devdocs">open source</a>
57+ <li>Pick your docs in the <a href="/settings">Preferences</a>.
58+ <li>The search supports fuzzy matching.
59+ <li>To search a specific documentation, type its name (or an abbr.), then Space.
60+ <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
61+ <li>DevDocs is <a href="https://github.com/Thibaut/devdocs">open source</a>.
6262 </ol>
6363 <p>Happy coding!
6464 <a class="_intro-hide" data-hide-intro>Stop showing this message</a>
assets/javascripts/templates/pages/settings_tmpl.coffee+27−0
@@ -0,0 +1,27 @@
1+app.templates.settingsPage = (settings) -> """
2+ <h1 class="_lined-heading">Preferences</h1>
3+
4+ <div class="_settings-fieldset">
5+ <h2 class="_settings-legend">General:</h2>
6+
7+ <div class="_settings-inputs">
8+ <label class="_settings-label">
9+ <input type="checkbox" name="dark" value="1"#{if settings.dark then ' checked' else ''}>Enable dark theme
10+ </label>
11+ <label class="_settings-label _settings-max-width">
12+ <input type="checkbox" name="layout" value="_max-width"#{if settings['_max-width'] then ' checked' else ''}>Enable fixed-width layout
13+ </label>
14+ <label class="_settings-label">
15+ <input type="checkbox" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
16+ </label>
17+ </div>
18+ </div>
19+
20+ <div class="_settings-fieldset">
21+ <h2 class="_settings-legend">Advanced:</h2>
22+
23+ <div class="_settings-inputs">
24+ <a href="#" class="_settings-link" data-behavior="reset">Reset all settings and data</a>
25+ </div>
26+ </div>
27+"""
assets/javascripts/templates/sidebar_tmpl.coffee+2−14
@@ -30,7 +30,7 @@ templates.sidebarResult = (entry) -> · 29 unmodified lines
3030 templates.sidebarNoResults = ->
3131 html = """ <div class="_list-note">No results.</div> """
3232 html += """
33− <div class="_list-note">Note: documentations must be <a href="#" class="_list-note-link" data-pick-docs>enabled</a> to appear in the search.</div>
33+ <div class="_list-note">Note: documentations must be <a href="/settings" class="_list-note-link">enabled</a> to appear in the search.</div>
3434 """ unless app.isSingleDoc() or app.disabledDocs.isEmpty()
3535 html
3636
@@ -58,19 +58,7 @@ templates.sidebarDisabledList = (html) -> · 21 unmodified lines
5858 templates.sidebarDisabledVersionedDoc = (doc, versions) ->
5959 """<a class="_list-item _list-dir _icon-#{doc.icon} _list-disabled" data-slug="#{doc.slug_without_version}" tabindex="-1"><span class="_list-arrow"></span>#{doc.name}</a><div class="_list _list-sub">#{versions}</div>"""
6060
61−templates.sidebarPickerNote = """
61+templates.docPickerNote = """
6262 <div class="_list-note">Tip: for faster and better search results, select only the docs you need.</div>
6363 <a href="https://trello.com/b/6BmTulfx/devdocs-documentation" class="_list-link" target="_blank" rel="noopener">Vote for new documentation</a>
6464 """
65−
66−sidebarFooter = (html) -> """<div class="_sidebar-footer">#{html}</div>"""
67−
68−templates.sidebarSettings = ->
69− sidebarFooter """
70− <button type="button" class="_sidebar-footer-link _sidebar-footer-edit" data-pick-docs>Select documentation</button>
71− <button type="button" class="_sidebar-footer-link _sidebar-footer-light" title="Toggle light" data-light>Toggle light</button>
72− <button type="button" class="_sidebar-footer-link _sidebar-footer-layout" title="Toggle layout" data-layout>Toggle layout</button>
73− """
74−
75−templates.sidebarSave = ->
76− sidebarFooter """<a class="_sidebar-footer-link _sidebar-footer-save" role="button">Save</a>"""
assets/javascripts/views/content/content.coffee+8−5
@@ -23,11 +23,12 @@ class app.views.Content extends app.View · 22 unmodified lines
2323 @scrollMap = {}
2424 @scrollStack = []
2525
26− @rootPage = new app.views.RootPage
27− @staticPage = new app.views.StaticPage
28− @offlinePage = new app.views.OfflinePage
29− @typePage = new app.views.TypePage
30− @entryPage = new app.views.EntryPage
26+ @rootPage = new app.views.RootPage
27+ @staticPage = new app.views.StaticPage
28+ @settingsPage = new app.views.SettingsPage
29+ @offlinePage = new app.views.OfflinePage
30+ @typePage = new app.views.TypePage
31+ @entryPage = new app.views.EntryPage
3132
3233 @entryPage
3334 .on 'loading', @onEntryLoading
@@ -148,6 +149,8 @@ class app.views.Content extends app.View · 114 unmodified lines
148149 @show @entryPage
149150 when 'type'
150151 @show @typePage
152+ when 'settings'
153+ @show @settingsPage
151154 when 'offline'
152155 @show @offlinePage
153156 else
assets/javascripts/views/content/settings_page.coffee+52−0
@@ -0,0 +1,52 @@
1+class app.views.SettingsPage extends app.View
2+ LAYOUTS = ['_max-width', '_sidebar-hidden']
3+ SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4+
5+ @className: '_static'
6+
7+ @events:
8+ change: 'onChange'
9+
10+ render: ->
11+ @html @tmpl('settingsPage', @currentSettings())
12+ return
13+
14+ currentSettings: ->
15+ settings = {}
16+ settings.dark = app.settings.getDark()
17+ settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS
18+ settings
19+
20+ getTitle: ->
21+ 'Preferences'
22+
23+ toggleDark: (enable) ->
24+ css = $('link[rel="stylesheet"][data-alt]')
25+ alt = css.getAttribute('data-alt')
26+ css.setAttribute('data-alt', css.getAttribute('href'))
27+ css.setAttribute('href', alt)
28+ app.settings.setDark(enable)
29+ app.appCache?.updateInBackground()
30+ return
31+
32+ toggleLayout: (layout, enable) ->
33+ app.el.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT
34+ app.settings.setLayout(layout, enable)
35+ app.appCache?.updateInBackground()
36+ return
37+
38+ onChange: (event) =>
39+ input = event.target
40+ switch input.name
41+ when 'dark'
42+ @toggleDark input.checked
43+ when 'layout'
44+ @toggleLayout input.value, input.checked
45+ return
46+
47+ onRoute: (route) =>
48+ if app.isSingleDoc()
49+ window.location = "/#/#{route.path}"
50+ else
51+ @render()
52+ return
assets/javascripts/views/layout/document.coffee+11−26
@@ -1,7 +1,4 @@
11 class app.views.Document extends app.View
2− MAX_WIDTH_LAYOUT = '_max-width'
3− SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4−
52 @el: document
63
74 @events:
@@ -13,44 +10,32 @@ class app.views.Document extends app.View · 5 unmodified lines
1310 superLeft: 'onBack'
1411 superRight: 'onForward'
1512
13+ @routes:
14+ after: 'afterRoute'
15+
1616 init: ->
1717 @addSubview @menu = new app.views.Menu,
1818 @addSubview @sidebar = new app.views.Sidebar
1919 @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported()
2020 @addSubview @content = new app.views.Content
2121 @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile()
22+ @settings = new app.views.Settings unless app.isSingleDoc()
2223
2324 $.on document.body, 'click', @onClick
2425
2526 @activate()
2627 return
2728
28− toggleLight: ->
29− css = $('link[rel="stylesheet"][data-alt]')
30− alt = css.getAttribute('data-alt')
31− css.setAttribute('data-alt', css.getAttribute('href'))
32− css.setAttribute('href', alt)
33− app.settings.setDark(alt.indexOf('dark') > 0)
34− app.appCache?.updateInBackground()
35− return
36−
37− toggleLayout: ->
38− wantsMaxWidth = !app.el.classList.contains(MAX_WIDTH_LAYOUT)
39− app.el.classList[if wantsMaxWidth then 'add' else 'remove'](MAX_WIDTH_LAYOUT)
40− app.settings.setLayout(MAX_WIDTH_LAYOUT, wantsMaxWidth)
41− app.appCache?.updateInBackground()
42− return
43−
44− toggleSidebarLayout: ->
45− shouldHide = !app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
46− app.el.classList[if shouldHide then 'add' else 'remove'](SIDEBAR_HIDDEN_LAYOUT)
47− app.settings.setLayout(SIDEBAR_HIDDEN_LAYOUT, shouldHide)
48− app.appCache?.updateInBackground()
49− return
50−
5129 setTitle: (title) ->
5230 @el.title = if title then "DevDocs — #{title}" else 'DevDocs API Documentation'
5331
32+ afterRoute: (route) =>
33+ if route is 'settings'
34+ @settings?.activate()
35+ else
36+ @settings?.deactivate()
37+ return
38+
5439 onVisibilityChange: =>
5540 return unless @el.visibilityState is 'visible'
5641 @delay ->
assets/javascripts/views/layout/mobile.coffee+42−10
@@ -2,9 +2,10 @@ class app.views.Mobile extends app.View · 1 unmodified line
22 @className: '_mobile'
33
44 @elements:
5− body: 'body'
6− content: '._container'
7− sidebar: '._sidebar'
5+ body: 'body'
6+ content: '._container'
7+ sidebar: '._sidebar'
8+ docPicker: '._settings ._sidebar'
89
910 @routes:
1011 after: 'afterRoute'
@@ -34,7 +35,6 @@ class app.views.Mobile extends app.View · 23 unmodified lines
3435 init: ->
3536 FastClick.attach @body
3637
37− $.on @body, 'click', @onClick
3838 $.on $('._search'), 'touchend', @onTapSearch
3939
4040 @toggleSidebar = $('button[data-toggle-sidebar]')
@@ -49,6 +49,14 @@ class app.views.Mobile extends app.View · 8 unmodified lines
4949 @forward.removeAttribute('hidden')
5050 $.on @forward, 'click', @onClickForward
5151
52+ @docPickerTab = $('a[data-tab="doc-picker"]')
53+ @docPickerTab.removeAttribute('hidden')
54+ $.on @docPickerTab, 'click', @onClickDocPickerTab
55+
56+ @settingsTab = $('a[data-tab="settings"]')
57+ @settingsTab.removeAttribute('hidden')
58+ $.on @settingsTab, 'click', @onClickSettingsTab
59+
5260 app.document.sidebar.search
5361 .on 'searching', @showSidebar
5462
@@ -82,11 +90,6 @@ class app.views.Mobile extends app.View · 27 unmodified lines
8290 isSidebarShown: ->
8391 @sidebar.style.display isnt 'none'
8492
85− onClick: (event) =>
86− if event.target.hasAttribute 'data-pick-docs'
87− @showSidebar()
88− return
89−
9093 onClickBack: =>
9194 history.back()
9295
@@ -97,12 +100,41 @@ class app.views.Mobile extends app.View · 4 unmodified lines
97100 if @isSidebarShown() then @hideSidebar() else @showSidebar()
98101 return
99102
103+ onClickDocPickerTab: (event) =>
104+ $.stopEvent(event)
105+ @showDocPicker()
106+ return
107+
108+ onClickSettingsTab: (event) =>
109+ $.stopEvent(event)
110+ @showSettings()
111+ return
112+
113+ showDocPicker: ->
114+ @docPickerTab.classList.add 'active'
115+ @settingsTab.classList.remove 'active'
116+ @docPicker.style.display = 'block'
117+ @content.style.display = 'none'
118+ return
119+
120+ showSettings: ->
121+ @docPickerTab.classList.remove 'active'
122+ @settingsTab.classList.add 'active'
123+ @docPicker.style.display = 'none'
124+ @content.style.display = 'block'
125+ return
126+
100127 onTapSearch: =>
101128 window.scrollTo 0, 0
102129
103− afterRoute: =>
130+ afterRoute: (route) =>
104131 @hideSidebar()
105132
133+ if route is 'settings'
134+ @showDocPicker()
135+ else
136+ @content.style.display = 'block'
137+
106138 if page.canGoBack()
107139 @back.removeAttribute('disabled')
108140 else
assets/javascripts/views/layout/resizer.coffee+0−7
@@ -1,12 +1,9 @@
11 class app.views.Resizer extends app.View
22 @className: '_resizer'
3− @attributes:
4− title: 'Click to toggle sidebar on/off'
53
64 @events:
75 dragstart: 'onDragStart'
86 dragend: 'onDragEnd'
9− click: 'onClick'
107
118 @isSupported: ->
129 'ondragstart' of document.createElement('div') and !app.isMobile()
@@ -34,10 +31,6 @@ class app.views.Resizer extends app.View · 21 unmodified lines
3431 app.appCache?.updateInBackground()
3532 return
3633
37− onClick: ->
38− app.document.toggleSidebarLayout()
39− return
40−
4134 onDragStart: (event) =>
4235 @style.removeAttribute('disabled')
4336 event.dataTransfer.effectAllowed = 'link'
assets/javascripts/views/layout/settings.coffee+80−0
@@ -0,0 +1,80 @@
1+class app.views.Settings extends app.View
2+ SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
3+
4+ @el: '._settings'
5+
6+ @elements:
7+ sidebar: '._sidebar'
8+ saveBtn: 'button[type="submit"]'
9+ backBtn: 'button[data-back]'
10+
11+ @events:
12+ submit: 'onSubmit'
13+ click: 'onClick'
14+ focus: 'onFocus'
15+
16+ @shortcuts:
17+ enter: 'onEnter'
18+
19+ init: ->
20+ @addSubview @docPicker = new app.views.DocPicker
21+ return
22+
23+ activate: ->
24+ if super
25+ @render()
26+ app.el.classList.remove(SIDEBAR_HIDDEN_LAYOUT)
27+ app.appCache?.on 'progress', @onAppCacheProgress
28+ return
29+
30+ deactivate: ->
31+ if super
32+ @resetClass()
33+ @docPicker.detach()
34+ app.el.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
35+ app.appCache?.off 'progress', @onAppCacheProgress
36+ return
37+
38+ render: ->
39+ @docPicker.appendTo @sidebar
40+ @refreshElements()
41+ @addClass '_in'
42+ return
43+
44+ save: ->
45+ unless @saving
46+ @saving = true
47+ docs = @docPicker.getSelectedDocs()
48+ app.settings.setDocs(docs)
49+ @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'
50+ disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
51+ disabledDocs.uninstall ->
52+ app.db.migrate()
53+ app.reload()
54+ return
55+
56+ onEnter: =>
57+ @save()
58+ return
59+
60+ onSubmit: (event) =>
61+ event.preventDefault()
62+ @save()
63+ return
64+
65+ onClick: (event) =>
66+ return if event.which isnt 1
67+ if event.target is @backBtn
68+ $.stopEvent(event)
69+ app.router.show '/'
70+ return
71+
72+ onFocus: (event) =>
73+ $.scrollTo event.target, @el, 'continuous', bottomGap: 2
74+ return
75+
76+ onAppCacheProgress: (event) =>
77+ if event.lengthComputable
78+ percentage = Math.round event.loaded * 100 / event.total
79+ @saveBtn.textContent = "Downloading\u2026 (#{percentage}%)"
80+ return
assets/javascripts/views/search/search.coffee+0−8
@@ -51,14 +51,6 @@ class app.views.Search extends app.View · 50 unmodified lines
5151 @autoFocus()
5252 return
5353
54− disable: ->
55− @input.setAttribute('disabled', 'disabled')
56− return
57−
58− enable: ->
59− @input.removeAttribute('disabled')
60− return
61−
6254 onReady: =>
6355 @value = ''
6456 @delay @onInput
assets/javascripts/views/sidebar/doc_picker.coffee+2−42
@@ -1,18 +1,9 @@
11 class app.views.DocPicker extends app.View
22 @className: '_list _list-picker'
3− @attributes:
4− role: 'form'
5−
6− @elements:
7− saveLink: '._sidebar-footer-save'
83
94 @events:
10− click: 'onClick'
115 mousedown: 'onMouseDown'
126
13− @shortcuts:
14− enter: 'onEnter'
15−
167 init: ->
178 @addSubview @listFold = new app.views.ListFold(@el)
189 return
@@ -20,14 +11,12 @@ class app.views.DocPicker extends app.View · 1 unmodified line
2011 activate: ->
2112 if super
2213 @render()
23− app.appCache?.on 'progress', @onAppCacheProgress
2414 $.on @el, 'focus', @onDOMFocus, true
2515 return
2616
2717 deactivate: ->
2818 if super
2919 @empty()
30− app.appCache?.off 'progress', @onAppCacheProgress
3120 $.off @el, 'focus', @onDOMFocus, true
3221 @focusEl = null
3322 return
@@ -43,8 +32,7 @@ class app.views.DocPicker extends app.View · 9 unmodified lines
4332 else
4433 html += @tmpl('sidebarLabel', doc, checked: app.docs.contains(doc))
4534
46− @html html + @tmpl('sidebarPickerNote') + @tmpl('sidebarSave')
47− @refreshElements()
35+ @html html + @tmpl('docPickerNote')
4836
4937 $.requestAnimationFrame =>
5038 @addClass '_in'
@@ -68,31 +56,13 @@ class app.views.DocPicker extends app.View · 17 unmodified lines
6856 super
6957 return
7058
71− save: ->
72− unless @saving
73− @saving = true
74− docs = @getSelectedDocs()
75− app.settings.setDocs(docs)
76− @saveLink.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026'
77− disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
78− disabledDocs.uninstall ->
79− app.db.migrate()
80− app.reload()
81− return
82−
8359 getSelectedDocs: ->
8460 for input in @findAllByTag 'input' when input?.checked
8561 input.name
8662
87− onClick: (event) =>
88− return if event.which isnt 1
89− if event.target is @saveLink
90− $.stopEvent(event)
91− @save()
92− return
93−
9463 onMouseDown: =>
9564 @mouseDown = Date.now()
65+ return
9666
9767 onDOMFocus: (event) =>
9868 target = event.target
@@ -112,13 +82,3 @@ class app.views.DocPicker extends app.View · 13 unmodified lines
11282 @delay -> $('input', target.nextElementSibling).focus()
11383 @focusEl = target
11484 return
115−
116− onEnter: =>
117− @save()
118− return
119−
120− onAppCacheProgress: (event) =>
121− if event.lengthComputable
122− percentage = Math.round event.loaded * 100 / event.total
123− @saveLink.textContent = "Downloading\u2026 (#{percentage}%)"
124− return
assets/javascripts/views/sidebar/sidebar.coffee+2−26
@@ -22,10 +22,8 @@ class app.views.Sidebar extends app.View · 21 unmodified lines
2222
2323 @results = new app.views.Results @, @search
2424 @docList = new app.views.DocList
25− @docPicker = new app.views.DocPicker unless app.isSingleDoc()
2625
2726 app.on 'ready', @onReady
28− $.on document, 'click', @onGlobalClick if @docPicker
2927 return
3028
3129 display: ->
@@ -45,23 +43,18 @@ class app.views.Sidebar extends app.View · 13 unmodified lines
4543 @render()
4644 @view.activate()
4745 @restoreScrollPosition()
48− if view is @docPicker then @search.disable() else @search.enable()
4946 return
5047
5148 render: ->
5249 @html @view
53− @append @tmpl('sidebarSettings') if @view is @docList and @docPicker
5450 return
5551
5652 showDocList: ->
5753 @showView @docList
5854 return
5955
60− showDocPicker: =>
61− @showView @docPicker
62− return
63−
6456 showResults: =>
57+ @display()
6558 @showView @results
6659 return
6760
@@ -101,7 +94,6 @@ class app.views.Sidebar extends app.View · 33 unmodified lines
10194 return
10295
10396 onSearching: =>
104− @display()
10597 @showResults()
10698 return
10799
@@ -124,23 +116,6 @@ class app.views.Sidebar extends app.View · 16 unmodified lines
124116 if event.target.hasAttribute? 'data-reset-list'
125117 $.stopEvent(event)
126118 @onAltR()
127− else if event.target.hasAttribute? 'data-light'
128− $.stopEvent(event)
129− document.activeElement?.blur()
130− app.document.toggleLight()
131− else if event.target.hasAttribute? 'data-layout'
132− $.stopEvent(event)
133− document.activeElement?.blur()
134− app.document.toggleLayout()
135− return
136−
137− onGlobalClick: (event) =>
138− return if event.which isnt 1
139− if event.target.hasAttribute? 'data-pick-docs'
140− $.stopEvent(event)
141− @showDocPicker()
142− else if @view is @docPicker
143− @showDocList() unless $.hasChild @el, event.target
144119 return
145120
146121 onAltR: =>
@@ -157,3 +132,4 @@ class app.views.Sidebar extends app.View · 10 unmodified lines
157132 onDocEnabled: ->
158133 @docList.onEnabled()
159134 @reset()
135+ return
assets/stylesheets/application-dark.css.scss+1−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
2020 'components/header',
2121 'components/notif',
2222 'components/sidebar',
23+ 'components/settings',
2324 'components/content',
2425 'components/page',
2526 'components/fail',
assets/stylesheets/application.css.scss+1−0
@@ -20,6 +20,7 @@ · 19 unmodified lines
2020 'components/header',
2121 'components/notif',
2222 'components/sidebar',
23+ 'components/settings',
2324 'components/content',
2425 'components/page',
2526 'components/fail',
assets/stylesheets/components/_content.scss+1−4
@@ -12,7 +12,7 @@ · 11 unmodified lines
1212
1313 @media #{$mediumScreen} { margin-left: $sidebarMediumWidth; }
1414
15− ._sidebar-hidden & { margin-left: $sidebarHiddenWidth; }
15+ ._sidebar-hidden & { margin-left: 0; }
1616 }
1717
1818 ._content {
@@ -283,7 +283,6 @@ · 264 unmodified lines
283283 }
284284
285285 ._docs-label {
286− display: block;
287286 overflow: hidden;
288287 margin: 1px 0;
289288 padding: .375rem .5rem;
@@ -292,8 +291,6 @@ · 2 unmodified lines
292291 display: inline-block;
293292 vertical-align: top;
294293 margin: .25rem;
295− width: 1rem;
296− height: 1rem;
297294 }
298295 }
299296
assets/stylesheets/components/_header.scss+3−5
@@ -12,6 +12,8 @@ · 11 unmodified lines
1212 height: $headerHeight;
1313 background: $headerBackground;
1414 border-bottom: 1px solid $headerBorder;
15+ border-right: 1px solid $headerBorder;
16+ @extend %border-box;
1517 @extend %user-select-none;
1618
1719 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
@@ -55,17 +57,13 @@ · 37 unmodified lines
5557 // Menu
5658 //
5759
58−._menu-btn {
59− border-right: 1px solid $headerBorder;
60−}
61−
6260 ._menu {
6361 position: absolute;
6462 z-index: 1;
6563 top: .25rem;
6664 right: .25rem;
6765 width: 8rem;
68− height: calc(11.5rem + 1px);
66+ height: calc(13.75rem + 1px);
6967 font-size: .875rem;
7068 background: $contentBackground;
7169 border: 1px solid $headerBorder;
assets/stylesheets/components/_mobile.scss+20−19
@@ -11,30 +11,42 @@ · 10 unmodified lines
1111 body { -ms-overflow-style: -ms-autohiding-scrollbar; }
1212
1313 ._app, ._content { overflow: visible; }
14− ._app { padding-top: $headerHeight; }
15− ._container { margin: 0; }
14+
15+ ._container, ._sidebar {
16+ margin: 0;
17+ padding-top: $headerHeight;
18+ }
1619
1720 ._content {
1821 position: static;
1922 height: auto;
2023 margin: 0;
21− padding: .75rem 1rem 2.5rem;
24+ padding: .75rem 1rem 2rem;
2225
2326 &:before { content: none; }
2427 }
2528
2629 ._booting:before, ._content-loading:before { font-size: 3rem; }
2730
28− // Header
29−
30− ._header {
31+ ._header, ._footer {
3132 position: fixed;
32− max-width: 100vw;
33+ width: 100%;
3334 }
3435
36+ ._header, ._list, ._footer {
37+ width: 100%;
38+ border-right: 0;
39+ box-shadow: none;
40+ }
41+
42+ ._settings { position: static; }
43+ ._settings ._sidebar { padding-bottom: $headerHeight; }
44+ ._settings-tabs { display: block; }
45+
46+ // Header
47+
3548 ._header-btn { width: 2.5rem; }
3649 ._header-btn[hidden] { display: block; }
37− ._menu-btn { border-right: 0; }
3850
3951 ._search {
4052 padding-right: .125rem;
@@ -53,8 +65,6 @@ · 12 unmodified lines
5365 overflow: visible;
5466 }
5567
56− ._header, ._list, ._sidebar-footer { width: 100%; }
57−
5868 ._list-item {
5969 white-space: normal;
6070 word-wrap: break-word;
@@ -72,15 +82,6 @@ · 11 unmodified lines
7282 }
7383 }
7484
75− ._list-link { display: none; }
76−
77− ._sidebar-footer { box-shadow: none; }
78−
79− ._sidebar-footer-save {
80− margin: 0;
81− box-shadow: 0 1px $noteGreenBorder, 0 -1px $noteGreenBorder;
82− }
83−
8485 // Notice
8586
8687 ._notice {
assets/stylesheets/components/_notice.scss+2−1
@@ -11,7 +11,8 @@ · 10 unmodified lines
1111
1212 @media #{$mediumScreen} { left: $sidebarMediumWidth; }
1313
14− ._sidebar-hidden & { left: $sidebarHiddenWidth; }
14+ ._sidebar-hidden & { left: 0; }
15+
1516 ~ ._container { padding-bottom: 2.5rem; }
1617 }
1718
assets/stylesheets/components/_path.scss+1−1
@@ -13,7 +13,7 @@ · 12 unmodified lines
1313
1414 @media #{$mediumScreen} { left: $sidebarMediumWidth; }
1515
16− ._sidebar-hidden & { left: $sidebarHiddenWidth; }
16+ ._sidebar-hidden & { left: 0; }
1717
1818 ~ ._container { padding-bottom: 2rem; }
1919 a:focus { outline: 0; }
assets/stylesheets/components/_settings.scss+146−0
@@ -0,0 +1,146 @@
1+//
2+// Settings
3+//
4+
5+._settings {
6+ display: none;
7+ position: absolute;
8+ top: 0;
9+ bottom: 0;
10+ z-index: $headerZ;
11+
12+ &._in { display: block; }
13+
14+ ._sidebar {
15+ bottom: $headerHeight;
16+
17+ ._sidebar-hidden & { display: block; }
18+ }
19+
20+ ._header {
21+ justify-content: space-between;
22+ }
23+}
24+
25+._settings-fieldset {
26+ display: flex;
27+ margin: 1.5rem 0;
28+ line-height: 1.5rem;
29+}
30+
31+._settings-legend {
32+ flex: 0 1 10rem;
33+ margin: 0;
34+ padding-right: .5rem;
35+ line-height: inherit;
36+ font-size: inherit;
37+ font-weight: $boldFontWeight;
38+ text-align: right;
39+ @extend %border-box;
40+}
41+
42+._settings-inputs {
43+ flex: 1 1 20rem;
44+}
45+
46+._settings-label {
47+ margin: 0 0 .375rem;
48+
49+ > small {
50+ display: block;
51+ color: $textColorLight;
52+ margin-left: 1.75rem;
53+ }
54+
55+ input[type=checkbox] {
56+ display: inline-block;
57+ vertical-align: top;
58+ margin: .25rem .375rem;
59+ }
60+}
61+
62+@media (max-width: $maxWidth) {
63+ ._settings-max-width { display: none; }
64+}
65+
66+._settings-link {
67+ display: inline-block;
68+ vertical-align: top;
69+ margin-left: .375rem;
70+
71+ &[data-behavior=reset] {
72+ font-size: .75rem;
73+ color: $textColorRed;
74+ }
75+}
76+
77+._footer {
78+ position: absolute;
79+ z-index: $headerZ;
80+ bottom: 0;
81+ left: 0;
82+ width: $sidebarWidth;
83+ height: $headerHeight;
84+ background: $noteGreenBackground;
85+ border-top: 1px solid $noteGreenBorder;
86+ border-right: 1px solid $noteGreenBorder;
87+ @extend %border-box;
88+ @extend %user-select-none;
89+
90+ @media #{$mediumScreen} { width: $sidebarMediumWidth; }
91+}
92+
93+._settings-btn {
94+ display: block;
95+ height: 100%;
96+ line-height: 1.5rem;
97+ padding: 0 .75rem;
98+ font-size: .875rem;
99+ font-weight: $boldFontWeight;
100+ color: inherit;
101+ text-align: left;
102+ cursor: pointer;
103+ @extend %border-box;
104+
105+ > svg {
106+ display: inline-block;
107+ vertical-align: top;
108+ width: 1.5rem;
109+ height: 1.5rem;
110+ margin-right: .125rem;
111+ fill: currentColor;
112+ pointer-events: none;
113+ }
114+}
115+
116+._save-btn { width: 100%; }
117+
118+//
119+// Header tabs
120+//
121+
122+._settings-tabs {
123+ display: none; // mobile only
124+ margin-right: .5rem;
125+ line-height: $headerHeight;
126+}
127+
128+._settings-tab {
129+ position: relative;
130+ display: inline-block;
131+ vertical-align: top;
132+ padding: 0 .75rem;
133+ cursor: pointer;
134+
135+ &, &:hover {
136+ color: $textColorLight;
137+ text-decoration: none;
138+ }
139+
140+ &.active {
141+ color: $textColor;
142+ font-weight: $boldFontWeight;
143+ text-decoration: none;
144+ box-shadow: inset 0 -2px $linkColor;
145+ }
146+}
assets/stylesheets/components/_sidebar.scss+5−162
@@ -8,13 +8,13 @@ · 7 unmodified lines
88 top: $headerHeight;
99 bottom: 0;
1010 left: 0;
11− margin-top: 1px;
1211 overflow-x: hidden;
1312 overflow-y: scroll;
1413 background: $sidebarBackground;
1514 background-clip: content-box;
1615 -webkit-overflow-scrolling: touch;
1716 -ms-overflow-style: none; // IE 10 doesn't support pointer-events
17+ @extend %border-box;
1818 @extend %user-select-none;
1919
2020 &::-webkit-scrollbar { -webkit-appearance: none; width: 10px; }
@@ -48,26 +48,7 @@ · 27 unmodified lines
4848 width: 3px;
4949 cursor: col-resize;
5050
51− ._sidebar-hidden & {
52− left: 0;
53− margin-left: 0;
54− background: $headerBackground;
55− border-right: 1px solid $headerBorder;
56− width: 8px;
57− cursor: pointer;
58−
59− &:before {
60− content: '';
61− position: absolute;
62− top: 50%;
63− left: 3px;
64− margin-top: -.5rem;
65− width: 1px;
66− height: 1rem;
67− border-left: 1px solid $textColorLighter;
68− border-right: 1px solid $textColorLighter;
69− }
70− }
51+ ._sidebar-hidden & { display: none; }
7152 }
7253
7354 //
@@ -84,10 +65,7 @@ · 10 unmodified lines
8465
8566 @media #{$mediumScreen} { width: $sidebarMediumWidth; }
8667
87− ._sidebar > & {
88− min-height: 100%;
89− padding-bottom: 3.5rem;
90− }
68+ ._sidebar > & { min-height: 100%; }
9169
9270 a:focus { outline: 0; }
9371 }
@@ -342,7 +320,7 @@ · 248 unmodified lines
342320 ._list-picker {
343321 > ._list, > ._list-item {
344322 opacity: 0;
345− transition: .2s;
323+ transition: opacity .2s;
346324 }
347325 &._in { > ._list, > ._list-item { opacity: 1; } }
348326
@@ -354,14 +332,11 @@ · 5 unmodified lines
354332 position: absolute;
355333 top: .5rem;
356334 right: .75rem;
357− width: 1rem;
358− height: 1rem;
359− transition: .2s;
360335 }
361336
362337 ._list-link {
363338 display: block;
364− margin-top: .75rem;
339+ padding: .75rem 0;
365340 font-size: .8125rem;
366341 text-align: center;
367342 @extend %external-link;
@@ -369,135 +344,3 @@ · 1 unmodified line
369344 &:after { visibility: hidden; }
370345 &:hover:after { visibility: visible; }
371346 }
372−
373−//
374−// Footer
375−//
376−
377−._sidebar-footer {
378− position: fixed;
379− bottom: 0;
380− left: 0;
381− width: $sidebarWidth;
382− background: $sidebarBackground;
383− box-shadow: inset -1px 0 $sidebarBorder;
384− -webkit-transform: translateZ(0);
385− transform: translateZ(0);
386−
387− @media #{$mediumScreen} { width: $sidebarMediumWidth; }
388−
389− ._max-width & {
390− left: calc(50% - #{$maxWidth} / 2);
391− @media (max-width: #{$maxWidth}) { left: 0; }
392− }
393−
394− &:before {
395− content: '';
396− position: absolute;
397− bottom: 100%;
398− left: 0;
399− right: 1px;
400− height: 1em;
401− background-image: -webkit-linear-gradient(top, rgba($sidebarBackground, 0), rgba($sidebarBackground, .95));
402− background-image: linear-gradient(to bottom, rgba($sidebarBackground, 0), rgba($sidebarBackground, .95));
403− pointer-events: none;
404− }
405−}
406−
407−._sidebar-footer-link {
408− position: relative;
409− display: block;
410− overflow: hidden;
411− height: 2.5rem;
412− line-height: 1rem;
413− padding: .75rem .25rem .75rem .75rem;
414− font-size: .875em;
415− cursor: pointer;
416− @extend %border-box;
417−
418− &, &:hover {
419− color: inherit;
420− text-decoration: none;
421− }
422−
423− &:before {
424− float: left;
425− margin-right: .625rem;
426− @extend %icon;
427− }
428−}
429−
430−._sidebar-footer-edit {
431− display: inline-block;
432−
433− @if $style == 'dark' {
434− &:before { @extend %icon-settings-white; }
435− } @else {
436− &:before { @extend %icon-settings; }
437− }
438−}
439−
440−._sidebar-footer-light {
441− float: right;
442− width: 2rem;
443− padding: 0;
444− opacity: .65;
445− @extend %hide-text;
446−
447− &:before {
448− float: none;
449− position: absolute;
450− top: .75rem;
451− left: .25rem;
452−
453− @if $style == 'dark' {
454− @extend %icon-light-white;
455− } @else {
456− @extend %icon-light;
457− }
458− }
459−}
460−
461−._sidebar-footer-layout {
462− float: right;
463− width: 2rem;
464− padding: 0;
465− opacity: .65;
466− @extend %hide-text;
467−
468− &:before {
469− float: none;
470− position: absolute;
471− top: .75rem;
472− left: .375rem;
473− @if $style == 'dark' {
474− @extend %icon-expand-white;
475− } @else {
476− @extend %icon-expand;
477− }
478−
479− ._max-width & {
480− @if $style == 'dark' {
481− @extend %icon-contract-white;
482− } @else {
483− @extend %icon-contract;
484− }
485− }
486− }
487−
488− @media (max-width: #{$maxWidth + .1rem}) { display: none; }
489−}
490−
491−._sidebar-footer-save {
492− margin-right: 1px;
493− font-weight: $boldFontWeight;
494− background: $noteGreenBackground;
495− box-shadow: inset 0 1px $noteGreenBorder,
496− 1px 0 $noteGreenBorder;
497−
498− @if $style == 'dark' {
499− &:before { @extend %icon-check-white; }
500− } @else {
501− &:before { @extend %icon-check; }
502− }
503−}
assets/stylesheets/global/_base.scss+11−0
@@ -166,6 +166,11 @@ section, main { · 165 unmodified lines
166166 outline: 0;
167167 }
168168
169+label {
170+ display: block;
171+ @extend %user-select-none;
172+}
173+
169174 input, button {
170175 margin: 0;
171176 font-family: inherit;
@@ -175,6 +180,12 @@ input, button { · 3 unmodified lines
175180 @extend %border-box;
176181 }
177182
183+input[type=checkbox] {
184+ width: 1rem;
185+ height: 1rem;
186+ cursor: pointer;
187+}
188+
178189 button {
179190 padding: 0;
180191 background: none;
assets/stylesheets/global/_icons.scss+8−18
@@ -4,7 +4,7 @@ · 3 unmodified lines
44 width: 1rem;
55 height: 1rem;
66 background-image: image-url('icons.png');
7− background-size: 4rem 6rem;
7+ background-size: 4rem 3rem;
88 }
99
1010 @media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
@@ -35,23 +35,13 @@ · 24 unmodified lines
3535 %icon-search { background-position: -1rem 0; }
3636 %icon-link { background-position: -2.25rem -.25rem; }
3737 %icon-clear { background-position: -3rem 0; }
38−%icon-settings { background-position: 0 -1rem; }
39−%icon-check { background-position: -1rem -1rem; }
40−%icon-path { background-position: 0 -2rem; }
41−%icon-search-white { background-position: -1rem -2rem; }
42−%icon-dir-white { background-position: -2rem -2rem; }
43−%icon-link-white { background-position: -3.25rem -2.25rem; }
44−%icon-settings-white { background-position: 0 -3rem; }
45−%icon-check-white { background-position: -1rem -3rem; }
46−%icon-light { background-position: -2rem -3rem; }
47−%icon-light-white { background-position: -3rem -3rem; }
48−%icon-expand { background-position: 0 -4rem; }
49−%icon-contract { background-position: -1rem -4rem; }
50−%icon-expand-white { background-position: -2rem -4rem; }
51−%icon-contract-white { background-position: -3rem -4rem; }
52−%icon-clipboard { background-position: 0 -5rem; }
53−%icon-clipboard-white { background-position: -1rem -5rem; }
54−%icon-close-white { background-position: -2rem -5rem; }
38+%icon-path { background-position: 0 -1rem; }
39+%icon-search-white { background-position: -1rem -1rem; }
40+%icon-dir-white { background-position: -2rem -1rem; }
41+%icon-link-white { background-position: -3.25rem -1.25rem; }
42+%icon-clipboard { background-position: 0 -2rem; }
43+%icon-clipboard-white { background-position: -1rem -2rem; }
44+%icon-close-white { background-position: -2rem -2rem; }
5545
5646 ._icon-codeceptjs:before { background-position: -3rem 0; }
5747 ._icon-codeception:before { background-position: -4rem 0; }
assets/stylesheets/global/_variables-dark.scss+1−1
@@ -9,7 +9,6 @@ $maxWidth: 80rem; · 8 unmodified lines
99 $headerHeight: 3rem;
1010 $sidebarWidth: 20rem;
1111 $sidebarMediumWidth: 16rem;
12−$sidebarHiddenWidth: 9px;
1312
1413 $contentBackground: #33373a;
1514 $documentBackground: $contentBackground;
@@ -17,6 +16,7 @@ $documentBackground: $contentBackground; · 1 unmodified line
1716 $textColor: #cbd0d0;
1817 $textColorLight: #9da5ad;
1918 $textColorLighter: #77787a;
19+$textColorRed: #f44336;
2020
2121 $inputFocusBorder: false;
2222
assets/stylesheets/global/_variables.scss+1−1
@@ -9,7 +9,6 @@ $maxWidth: 80rem; · 8 unmodified lines
99 $headerHeight: 3rem;
1010 $sidebarWidth: 20rem;
1111 $sidebarMediumWidth: 16rem;
12−$sidebarHiddenWidth: 9px;
1312
1413 $contentBackground: #fff;
1514 $documentBackground: #fafafa;
@@ -17,6 +16,7 @@ $documentBackground: #fafafa; · 1 unmodified line
1716 $textColor: #333;
1817 $textColorLight: #666;
1918 $textColorLighter: #888;
19+$textColorRed: #f44336;
2020
2121 $inputFocusBorder: #35b5f4;
2222
lib/app.rb+1−1
@@ -235,7 +235,7 @@ class App < Sinatra::Application · 234 unmodified lines
235235 erb :index
236236 end
237237
238− %w(offline about news help).each do |page|
238+ %w(settings offline about news help).each do |page|
239239 get "/#{page}" do
240240 if supports_js_redirection?
241241 redirect_via_js "/#{page}"
public/icons/ui/check-white/SOURCE+0−1
@@ -1 +0,0 @@
1−http://happytodesign.com/hicons/
public/icons/ui/check-white/check-white.png+0−0

Binary file not shown.

public/icons/ui/check-white/check-white@2x.png+0−0

Binary file not shown.

public/icons/ui/check/SOURCE+0−1
@@ -1 +0,0 @@
1−http://happytodesign.com/hicons/
public/icons/ui/check/check.png+0−0

Binary file not shown.

public/icons/ui/check/check@2x.png+0−0

Binary file not shown.

public/icons/ui/contract/16.png+0−0

Binary file not shown.

public/icons/ui/contract/16@2x.png+0−0

Binary file not shown.

public/icons/ui/expand/16.png+0−0

Binary file not shown.

public/icons/ui/expand/16@2x.png+0−0

Binary file not shown.

public/icons/ui/light-white/SOURCE+0−1
@@ -1 +0,0 @@
1−http://www.entypo.com/
public/icons/ui/light-white/light.png+0−0

Binary file not shown.

public/icons/ui/light-white/light@2x.png+0−0

Binary file not shown.

public/icons/ui/light/SOURCE+0−1
@@ -1 +0,0 @@
1−http://www.entypo.com/
public/icons/ui/light/light.png+0−0

Binary file not shown.

public/icons/ui/light/light@2x.png+0−0

Binary file not shown.

public/icons/ui/settings-white/SOURCE+0−1
@@ -1 +0,0 @@
1−http://gemicon.net/
public/icons/ui/settings-white/settings-white.png+0−0

Binary file not shown.

public/icons/ui/settings-white/settings-white@2x.png+0−0

Binary file not shown.

public/icons/ui/settings/SOURCE+0−1
@@ -1 +0,0 @@
1−http://gemicon.net/
public/icons/ui/settings/settings.png+0−0

Binary file not shown.

public/icons/ui/settings/settings@2x.png+0−0

Binary file not shown.

public/robots.txt+1−1
@@ -1,2 +1,2 @@
11 User-agent: *
2−Allow: /
2+Disallow: /settings
views/app.erb+24−6
@@ -1,7 +1,7 @@
11 <div class="_app<%= " #{app_layout}" if app_layout %>" role="application">
22 <header class="_header" role="banner">
33 <button type="button" aria-label="Toggle navigation" class="_header-btn" data-toggle-sidebar hidden>
4− <svg viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"></path></svg>
4+ <svg viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" /></svg>
55 </button>
66 <form class="_search" role="search">
77 <input type="search" name="q" class="_search-input" placeholder="Search&hellip;" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" maxlength="30" aria-label="Search">
@@ -9,17 +9,18 @@ · 1 unmodified line
99 <div class="_search-tag"></div>
1010 </form>
1111 <button type="button" aria-label="Back" class="_header-btn" data-back hidden>
12− <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"></svg>
12+ <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /></svg>
1313 </button>
1414 <button type="button" aria-label="Forward" class="_header-btn _forward-btn" data-forward hidden>
15− <svg viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"></svg>
15+ <svg viewBox="0 0 24 24"><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" /></svg>
1616 </button>
1717 <button type="button" aria-label="Toggle menu" title="Toggle menu" class="_header-btn _menu-btn" data-toggle-menu>
18− <svg viewBox="0 0 24 24"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"></path></svg>
18+ <svg viewBox="0 0 24 24"><path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" /></svg>
1919 </button>
2020 <nav class="_menu" role="navigation">
2121 <h1 class="_menu-title"><a href="/" class="_menu-title-link">DevDocs</a></h1>
22− <a href="/offline" class="_menu-link">Offline</a>
22+ <a href="/settings" class="_menu-link">Preferences</a>
23+ <a href="/offline" class="_menu-link">Offline Data</a>
2324 <a href="/news" class="_menu-link">Changelog</a>
2425 <a href="/help" class="_menu-link">Help</a>
2526 <a href="/about" class="_menu-link">About</a>
@@ -37,10 +38,27 @@ · 11 unmodified lines
3738 <div class="_container" role="document">
3839 <main class="_content _content-loading" role="main"></main>
3940 </div>
41+ <form class="_settings" id="settings">
42+ <div class="_header">
43+ <button type="button" aria-label="Back" class="_settings-btn" data-back>
44+ <svg viewBox="0 0 24 24"><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z" /></svg> Back
45+ </button>
46+ <nav class="_settings-tabs">
47+ <a class="_settings-tab active" data-tab="doc-picker" hidden>Docs</a>
48+ <a class="_settings-tab" data-tab="settings" hidden>Settings</a>
49+ </nav>
50+ </div>
51+ <div class="_sidebar"></div>
52+ <div class="_footer">
53+ <button type="submit" class="_settings-btn _save-btn">
54+ <svg viewBox="0 0 24 24"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" /></svg> Save
55+ </button>
56+ </div>
57+ </form>
4058 </div>
4159 <style data-size="<%= app_size %>" data-resizer>
4260 ._container { margin-left: <%= app_size %>; }
43− ._header, ._list, ._sidebar-footer { width: <%= app_size %>; }
61+ ._header, ._list, ._footer { width: <%= app_size %>; }
4462 ._list-hover.clone { min-width: <%= app_size %>; }
4563 ._notice, ._path, ._resizer { left: <%= app_size %>; }
4664 </style>

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t