├── .csscomb.json
├── .gitignore
├── .node-version
├── .travis.yml
├── LICENSE
├── README.md
├── css
├── layout.css
└── ratchet.min.css
├── images
├── Icon.png
├── Icon@1024.icns
├── Icon@2x.png
├── icon-comment.svg
├── icon-new.png
├── icon-new@2x.png
└── screenshot.png
├── package.json
├── script
├── compress
└── package
└── src
├── client
├── main.js
├── menu.js
├── spinner.js
├── story.js
├── story_box.js
└── story_list.js
├── index.html
├── index.js
├── logger.js
├── model
├── read_cache.js
├── story_manager_status.js
└── story_type.js
└── server
├── auto_update_manager.js
├── story_manager.js
└── tray_manager.js
/.csscomb.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | ".git/**",
4 | "node_modules/**"
5 | ],
6 | "always-semicolon": true,
7 | "block-indent": 2,
8 | "colon-space": ["", " "],
9 | "color-case": "lower",
10 | "color-shorthand": true,
11 | "combinator-space": [" ", " "],
12 | "element-case": "lower",
13 | "eof-newline": true,
14 | "leading-zero": false,
15 | "quotes": "single",
16 | "remove-empty-rulesets": true,
17 | "rule-indent": 2,
18 | "space-after-opening-brace": "\n",
19 | "space-before-opening-brace": 1,
20 | "space-before-closing-brace": "\n",
21 | "space-after-selector-delimiter": 1,
22 | "space-after-colon": 1,
23 | "space-before-colon": 0,
24 | "space-between-declarations": "\n",
25 | "sort-order": [
26 | [
27 | "$variable",
28 | "$include",
29 | "$import"
30 | ]
31 | ],
32 | "sort-order-fallback": "abc",
33 | "strip-spaces": true,
34 | "unitless-zero": true,
35 | "vendor-prefix-align": true
36 | }
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | Hacker Menu*
4 | newrelic.js
5 | build
6 |
--------------------------------------------------------------------------------
/.node-version:
--------------------------------------------------------------------------------
1 | v4.1.1
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | before_install:
3 | - brew update && brew install nvm
4 | - mkdir ~/.nvm && cp $(brew --prefix nvm)/nvm-exec ~/.nvm/
5 | - export NVM_DIR=~/.nvm && source $(brew --prefix nvm)/nvm.sh
6 | - nvm install 4.1.1
7 | - npm upgrade -g npm
8 | install:
9 | - npm install
10 | script:
11 | - npm test
12 | after_script:
13 | - npm run release
14 | os:
15 | - osx
16 | notifications:
17 | email:
18 | on_success: never
19 | on_failure: change
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Jingwen Owen Ou
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Hacker Menu
2 |
3 | [Hacker Menu](https://hackermenu.io/) stays on your menu bar and delivers the top news stories from [Y Combinator news aggregator](https://news.ycombinator.com/),
4 | built with love by [@jingweno](https://github.com/jingweno) & [@lokywin](https://github.com/lokywin). It's powered by [Electron](http://electron.atom.io/) and [Node.js](https://nodejs.org).
5 |
6 | Website: [https://hackermenu.io](https://hackermenu.io).
7 |
8 | Screenshot:
9 | 
10 |
11 | ## Installation
12 |
13 | Download the latest build for your platform from [releases](https://github.com/jingweno/hacker-menu/releases). We only have OSX build now, and we're working Windows and Linux builds. Feel free to contribute if you can't wait :heart:.
14 |
15 | ## Development
16 |
17 | You need to have the latest [io.js](https://iojs.org) or [node.js](https://nodejs.org/) installed.
18 |
19 | ```bash
20 | $ npm install # installs dependencies
21 | $ npm start # starts the app in the electron wrapper
22 | ```
23 |
24 | Other useful tasks:
25 |
26 | ```bash
27 | $ npm test # runs tests
28 | $ npm run build # builds the app
29 | $ npm run watch # watches and rebuilds the app
30 | $ npm run package # packages the Mac app
31 | $ npm run release # packages and zips the Mac app, it requires a cert to sign the app
32 | ```
33 |
34 | # Roadmap
35 |
36 | See [issues](https://github.com/jingweno/hacker-menu/issues?q=is%3Aopen+is%3Aissue+label%3Afeature).
37 |
38 |
39 | ## License
40 |
41 | See [LICENSE](https://github.com/jingweno/hacker-menu/blob/master/LICENSE).
42 |
--------------------------------------------------------------------------------
/css/layout.css:
--------------------------------------------------------------------------------
1 | /* overrides ratchet */
2 | .content {
3 | background: white;
4 | margin: 0 0 44px !important;
5 | }
6 |
7 | ul, code {
8 | font-size: 14px;
9 | }
10 |
11 | .bar .status {
12 | color: #979797;
13 | font-size: 12px;
14 | padding-top: 13px;
15 | }
16 |
17 | .comment {
18 | font-size: 11px;
19 | }
20 |
21 | /* global */
22 | body {
23 | font-family: 'Roboto', sans-serif;
24 | }
25 |
26 | .clickable {
27 | cursor: pointer;
28 | }
29 |
30 | .clickable:hover {
31 | color: #ff6601;
32 | }
33 |
34 | /* spinner */
35 | .spinner:before,
36 | .spinner:after,
37 | .spinner {
38 | border-radius: 50%;
39 | width: 1.5em;
40 | height: 1.5em;
41 | animation-fill-mode: both;
42 | animation: animate-spinner 1.8s infinite ease-in-out;
43 | }
44 |
45 | .spinner {
46 | font-size: 10px;
47 | margin: 50px auto;
48 | position: relative;
49 | text-indent: -9999em;
50 | transform: translateZ(0);
51 | animation-delay: -0.16s;
52 | }
53 |
54 | .spinner:before {
55 | left: -2.5em;
56 | animation-delay: -0.32s;
57 | }
58 |
59 | .spinner:after {
60 | left: 2.5em;
61 | }
62 |
63 | .spinner:before,
64 | .spinner:after {
65 | content: '';
66 | position: absolute;
67 | top: 0;
68 | }
69 |
70 | @keyframes animate-spinner {
71 | 0%,
72 | 80%,
73 | 100% {
74 | box-shadow: 0 2.5em 0 -1.3em #ff6601;
75 | }
76 | 40% {
77 | box-shadow: 0 2.5em 0 0 #ff6601;
78 | }
79 | }
80 |
81 | /* header */
82 | .bar-nav {
83 | background: #f0f0f0;
84 | }
85 |
86 | .bar.bar-nav {
87 | padding: 0;
88 | }
89 |
90 | .bar .segmented-control {
91 | background: transparent;
92 | border: 0;
93 | border-radius: 0;
94 | top: auto;
95 | }
96 |
97 | .segmented-control .control-item {
98 | cursor: pointer;
99 | padding-bottom: 14px;
100 | padding-top: 18px;
101 | text-transform: uppercase;
102 | }
103 |
104 | .segmented-control .control-item.active {
105 | background: white;
106 | }
107 |
108 | /* content */
109 | .table-view {
110 | border-bottom: 0;
111 | }
112 |
113 | .story {
114 | border-left: 4px solid #ff6601;
115 | color: inherit;
116 | margin: -11px -65px -11px -15px;
117 | overflow: hidden;
118 | padding: inherit;
119 | position: relative;
120 | }
121 |
122 | .story:hover {
123 | background: #fdfdfd;
124 | }
125 |
126 | .story.read {
127 | border-left-color: #bdbdbd;
128 | }
129 |
130 | .story .badge {
131 | background: #f0f0f0;
132 | font-size: .85rem;
133 | position: absolute;
134 | right: 15px;
135 | top: 27%;
136 | -webkit-transform: translateY(-50%);
137 | -ms-transform: translateY(-50%);
138 | transform: translateY(-50%);
139 | }
140 |
141 | .story-title {
142 | display: block;
143 | font-size: 16px;
144 | }
145 |
146 | .story-host {
147 | display: inline-block;
148 | font-size: 12px;
149 | font-style: italic;
150 | margin-top: .45rem;
151 | }
152 |
153 | .icon-comment:before {
154 | background: url(../images/icon-comment.svg) no-repeat center center;
155 | content: '';
156 | display: block;
157 | float: left;
158 | height: 20px;
159 | margin-right: 5px;
160 | width: 20px;
161 | }
162 |
163 | .story-poster {
164 | font-size: 12px;
165 | font-style: italic;
166 | }
167 |
168 | /* footer */
169 | .bar-footer {
170 | background: #f0f0f0;
171 | }
172 |
173 | .bar .btn {
174 | background: transparent;
175 | border: 0;
176 | border-left: 1px solid #979797;
177 | border-radius: 0;
178 | padding: 17px 14px 14px 23px;
179 | top: 0;
180 | }
181 | .bar .btn:active {
182 | outline: none;
183 | }
--------------------------------------------------------------------------------
/css/ratchet.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * =====================================================
3 | * Ratchet v2.0.2 (http://goratchet.com)
4 | * Copyright 2014 Connor Sears
5 | * Licensed under MIT (https://github.com/twbs/ratchet/blob/master/LICENSE)
6 | *
7 | * v2.0.2 designed by @connors.
8 | * =====================================================
9 | *//*! normalize.css v3.0.1 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}body{position:fixed;top:0;right:0;bottom:0;left:0;font-family:"Helvetica Neue",Helvetica,sans-serif;font-size:17px;line-height:21px;color:#000;background-color:#fff}a{color:#428bca;text-decoration:none;-webkit-tap-highlight-color:transparent}a:active{color:#3071a9}.content{position:absolute;top:0;right:0;bottom:0;left:0;overflow:auto;-webkit-overflow-scrolling:touch;background-color:#fff}.content>*{-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0)}.bar-nav~.content{padding-top:44px}.bar-header-secondary~.content{padding-top:88px}.bar-footer~.content{padding-bottom:44px}.bar-footer-secondary~.content{padding-bottom:88px}.bar-tab~.content{padding-bottom:50px}.bar-footer-secondary-tab~.content{padding-bottom:94px}.content-padded{margin:10px}.pull-left{float:left}.pull-right{float:right}.clearfix:after,.clearfix:before{display:table;content:" "}.clearfix:after{clear:both}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:10px;line-height:1}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{margin-top:20px;font-size:14px}.h6,h6{margin-top:20px;font-size:12px}p{margin-top:0;margin-bottom:10px;font-size:14px;color:#777}.btn{position:relative;display:inline-block;padding:6px 8px 7px;margin-bottom:0;font-size:12px;font-weight:400;line-height:1;color:#333;text-align:center;white-space:nowrap;vertical-align:top;cursor:pointer;background-color:#fff;border:1px solid #ccc;border-radius:3px}.btn.active,.btn:active{color:inherit;background-color:#ccc}.btn.disabled,.btn:disabled{opacity:.6}.btn-primary{color:#fff;background-color:#428bca;border:1px solid #428bca}.btn-primary.active,.btn-primary:active{color:#fff;background-color:#3071a9;border:1px solid #3071a9}.btn-positive{color:#fff;background-color:#5cb85c;border:1px solid #5cb85c}.btn-positive.active,.btn-positive:active{color:#fff;background-color:#449d44;border:1px solid #449d44}.btn-negative{color:#fff;background-color:#d9534f;border:1px solid #d9534f}.btn-negative.active,.btn-negative:active{color:#fff;background-color:#c9302c;border:1px solid #c9302c}.btn-outlined{background-color:transparent}.btn-outlined.btn-primary{color:#428bca}.btn-outlined.btn-positive{color:#5cb85c}.btn-outlined.btn-negative{color:#d9534f}.btn-outlined.btn-negative:active,.btn-outlined.btn-positive:active,.btn-outlined.btn-primary:active{color:#fff}.btn-link{padding-top:6px;padding-bottom:6px;color:#428bca;background-color:transparent;border:0}.btn-link.active,.btn-link:active{color:#3071a9;background-color:transparent}.btn-block{display:block;width:100%;padding:15px 0;margin-bottom:10px;font-size:18px}input[type=button],input[type=reset],input[type=submit]{width:100%}.btn .badge{margin:-2px -4px -2px 4px;font-size:12px;background-color:rgba(0,0,0,.15)}.btn .badge-inverted,.btn:active .badge-inverted{background-color:transparent}.btn-negative:active .badge-inverted,.btn-positive:active .badge-inverted,.btn-primary:active .badge-inverted{color:#fff}.btn-block .badge{position:absolute;right:0;margin-right:10px}.btn .icon{font-size:inherit}.bar{position:fixed;right:0;left:0;z-index:10;height:44px;padding-right:10px;padding-left:10px;background-color:#fff;border-bottom:1px solid #ddd;-webkit-backface-visibility:hidden;backface-visibility:hidden}.bar-header-secondary{top:44px}.bar-footer{bottom:0}.bar-footer-secondary{bottom:44px}.bar-footer-secondary-tab{bottom:50px}.bar-footer,.bar-footer-secondary,.bar-footer-secondary-tab{border-top:1px solid #ddd;border-bottom:0}.bar-nav{top:0}.title{position:absolute;display:block;width:100%;padding:0;margin:0 -10px;font-size:17px;font-weight:500;line-height:44px;color:#000;text-align:center;white-space:nowrap}.title a{color:inherit}.bar-tab{bottom:0;display:table;width:100%;height:50px;padding:0;table-layout:fixed;border-top:1px solid #ddd;border-bottom:0}.bar-tab .tab-item{display:table-cell;width:1%;height:50px;color:#929292;text-align:center;vertical-align:middle}.bar-tab .tab-item.active,.bar-tab .tab-item:active{color:#428bca}.bar-tab .tab-item .icon{top:3px;width:24px;height:24px;padding-top:0;padding-bottom:0}.bar-tab .tab-item .icon~.tab-label{display:block;font-size:11px}.bar .btn{position:relative;top:7px;z-index:20;padding:6px 12px 7px;margin-top:0;font-weight:400}.bar .btn.pull-right{margin-left:10px}.bar .btn.pull-left{margin-right:10px}.bar .btn-link{top:0;padding:0;font-size:16px;line-height:44px;color:#428bca;border:0}.bar .btn-link.active,.bar .btn-link:active{color:#3071a9}.bar .btn-block{top:6px;padding:7px 0;margin-bottom:0;font-size:16px}.bar .btn-nav.pull-left{margin-left:-5px}.bar .btn-nav.pull-left .icon-left-nav{margin-right:-3px}.bar .btn-nav.pull-right{margin-right:-5px}.bar .btn-nav.pull-right .icon-right-nav{margin-left:-3px}.bar .icon{position:relative;z-index:20;padding-top:10px;padding-bottom:10px;font-size:24px}.bar .btn .icon{top:3px;padding:0}.bar .title .icon{padding:0}.bar .title .icon.icon-caret{top:4px;margin-left:-5px}.bar input[type=search]{height:29px;margin:6px 0}.bar .segmented-control{top:7px;margin:0 auto}.badge{display:inline-block;padding:2px 9px 3px;font-size:12px;line-height:1;color:#333;background-color:rgba(0,0,0,.15);border-radius:100px}.badge.badge-inverted{padding:0 5px 0 0;background-color:transparent}.badge-primary{color:#fff;background-color:#428bca}.badge-primary.badge-inverted{color:#428bca}.badge-positive{color:#fff;background-color:#5cb85c}.badge-positive.badge-inverted{color:#5cb85c}.badge-negative{color:#fff;background-color:#d9534f}.badge-negative.badge-inverted{color:#d9534f}.card{margin:10px;overflow:hidden;background-color:#fff;border:1px solid #ddd;border-radius:6px}.card .table-view{margin-bottom:0;border-top:0;border-bottom:0}.card .table-view .table-view-divider:first-child{top:0;border-top-left-radius:6px;border-top-right-radius:6px}.card .table-view .table-view-divider:last-child{border-bottom-right-radius:6px;border-bottom-left-radius:6px}.card .table-view-cell:last-child{border-bottom:0}.table-view{padding-left:0;margin-top:0;margin-bottom:15px;list-style:none;background-color:#fff;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.table-view-cell{position:relative;padding:11px 65px 11px 15px;overflow:hidden;border-bottom:1px solid #ddd}.table-view-cell:last-child{border-bottom:0}.table-view-cell>a:not(.btn){position:relative;display:block;padding:inherit;margin:-11px -65px -11px -15px;overflow:hidden;color:inherit}.table-view-cell>a:not(.btn):active{background-color:#eee}.table-view-cell p{margin-bottom:0}.table-view-divider{padding-top:6px;padding-bottom:6px;padding-left:15px;margin-top:-1px;margin-left:0;font-weight:500;color:#999;background-color:#fafafa;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.table-view .media,.table-view .media-body{overflow:hidden}.table-view .media-object.pull-left{margin-right:10px}.table-view .media-object.pull-right{margin-left:10px}.table-view-cell>.badge,.table-view-cell>.btn,.table-view-cell>.toggle,.table-view-cell>a>.badge,.table-view-cell>a>.btn,.table-view-cell>a>.toggle{position:absolute;top:50%;right:15px;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.table-view-cell .navigate-left>.badge,.table-view-cell .navigate-left>.btn,.table-view-cell .navigate-left>.toggle,.table-view-cell .navigate-right>.badge,.table-view-cell .navigate-right>.btn,.table-view-cell .navigate-right>.toggle,.table-view-cell .push-left>.badge,.table-view-cell .push-left>.btn,.table-view-cell .push-left>.toggle,.table-view-cell .push-right>.badge,.table-view-cell .push-right>.btn,.table-view-cell .push-right>.toggle,.table-view-cell>a .navigate-left>.badge,.table-view-cell>a .navigate-left>.btn,.table-view-cell>a .navigate-left>.toggle,.table-view-cell>a .navigate-right>.badge,.table-view-cell>a .navigate-right>.btn,.table-view-cell>a .navigate-right>.toggle,.table-view-cell>a .push-left>.badge,.table-view-cell>a .push-left>.btn,.table-view-cell>a .push-left>.toggle,.table-view-cell>a .push-right>.badge,.table-view-cell>a .push-right>.btn,.table-view-cell>a .push-right>.toggle{right:35px}.content>.table-view:first-child{margin-top:15px}button,input,select,textarea{font-family:"Helvetica Neue",Helvetica,sans-serif;font-size:17px}input[type=color],input[type=date],input[type=datetime-local],input[type=datetime],input[type=email],input[type=month],input[type=number],input[type=password],input[type=search],input[type=tel],input[type=text],input[type=time],input[type=url],input[type=week],select,textarea{width:100%;height:35px;-webkit-appearance:none;padding:0 15px;margin-bottom:15px;line-height:21px;background-color:#fff;border:1px solid #ddd;border-radius:3px;outline:0}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0 10px;font-size:16px;border-radius:20px}input[type=search]:focus{text-align:left}textarea{height:auto}select{height:auto;font-size:14px;background-color:#f8f8f8;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:inset 0 1px 1px rgba(0,0,0,.1)}.input-group{background-color:#fff}.input-group input,.input-group textarea{margin-bottom:0;background-color:transparent;border-top:0;border-right:0;border-left:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.input-row{height:35px;overflow:hidden;border-bottom:1px solid #ddd}.input-row label{float:left;width:35%;padding:8px 15px;font-family:"Helvetica Neue",Helvetica,sans-serif;line-height:1.1}.input-row input{float:right;width:65%;padding-left:0;margin-bottom:0;border:0}.segmented-control{position:relative;display:table;overflow:hidden;font-size:12px;font-weight:400;background-color:#fff;border:1px solid #ccc;border-radius:3px}.segmented-control .control-item{display:table-cell;width:1%;padding-top:6px;padding-bottom:7px;overflow:hidden;line-height:1;color:#333;text-align:center;text-overflow:ellipsis;white-space:nowrap;border-left:1px solid #ccc}.segmented-control .control-item:first-child{border-left-width:0}.segmented-control .control-item:active{background-color:#eee}.segmented-control .control-item.active{background-color:#ccc}.segmented-control-primary{border-color:#428bca}.segmented-control-primary .control-item{color:#428bca;border-color:inherit}.segmented-control-primary .control-item:active{background-color:#cde1f1}.segmented-control-primary .control-item.active{color:#fff;background-color:#428bca}.segmented-control-positive{border-color:#5cb85c}.segmented-control-positive .control-item{color:#5cb85c;border-color:inherit}.segmented-control-positive .control-item:active{background-color:#d8eed8}.segmented-control-positive .control-item.active{color:#fff;background-color:#5cb85c}.segmented-control-negative{border-color:#d9534f}.segmented-control-negative .control-item{color:#d9534f;border-color:inherit}.segmented-control-negative .control-item:active{background-color:#f9e2e2}.segmented-control-negative .control-item.active{color:#fff;background-color:#d9534f}.control-content{display:none}.control-content.active{display:block}.popover{position:fixed;top:55px;left:50%;z-index:20;display:none;width:280px;margin-left:-140px;background-color:#fff;border-radius:6px;-webkit-box-shadow:0 0 15px rgba(0,0,0,.1);box-shadow:0 0 15px rgba(0,0,0,.1);opacity:0;-webkit-transition:all .25s linear;-moz-transition:all .25s linear;transition:all .25s linear;-webkit-transform:translate3d(0,-15px,0);-ms-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}.popover:before{position:absolute;top:-15px;left:50%;width:0;height:0;margin-left:-15px;content:'';border-right:15px solid transparent;border-bottom:15px solid #fff;border-left:15px solid transparent}.popover.visible{opacity:1;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.popover .bar~.table-view{padding-top:44px}.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:15;background-color:rgba(0,0,0,.3)}.popover .btn-block{margin-bottom:5px}.popover .btn-block:last-child{margin-bottom:0}.popover .bar-nav{border-bottom:1px solid #ddd;border-top-left-radius:12px;border-top-right-radius:12px;-webkit-box-shadow:none;box-shadow:none}.popover .table-view{max-height:300px;margin-bottom:0;overflow:auto;-webkit-overflow-scrolling:touch;background-color:#fff;border-top:0;border-bottom:0;border-radius:6px}.modal{position:fixed;top:0;z-index:11;width:100%;min-height:100%;overflow:hidden;background-color:#fff;opacity:0;-webkit-transition:-webkit-transform .25s,opacity 1ms .25s;-moz-transition:-moz-transform .25s,opacity 1ms .25s;transition:transform .25s,opacity 1ms .25s;-webkit-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}.modal.active{height:100%;opacity:1;-webkit-transition:-webkit-transform .25s;-moz-transition:-moz-transform .25s;transition:transform .25s;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.slider{width:100%;overflow:hidden;background-color:#000}.slider .slide-group{position:relative;font-size:0;white-space:nowrap;-webkit-transition:all 0s linear;-moz-transition:all 0s linear;transition:all 0s linear}.slider .slide-group .slide{display:inline-block;width:100%;height:100%;font-size:14px;vertical-align:top}.toggle{position:relative;display:block;width:74px;height:30px;background-color:#fff;border:2px solid #ddd;border-radius:20px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:background-color,border;-moz-transition-property:background-color,border;transition-property:background-color,border}.toggle .toggle-handle{position:absolute;top:-1px;left:-1px;z-index:2;width:28px;height:28px;background-color:#fff;border:1px solid #ddd;border-radius:100px;-webkit-transition-duration:.2s;-moz-transition-duration:.2s;transition-duration:.2s;-webkit-transition-property:-webkit-transform,border,width;-moz-transition-property:-moz-transform,border,width;transition-property:transform,border,width}.toggle:before{position:absolute;top:3px;right:11px;font-size:13px;color:#999;text-transform:uppercase;content:"Off"}.toggle.active{background-color:#5cb85c;border:2px solid #5cb85c}.toggle.active .toggle-handle{border-color:#5cb85c;-webkit-transform:translate3d(44px,0,0);-ms-transform:translate3d(44px,0,0);transform:translate3d(44px,0,0)}.toggle.active:before{right:auto;left:15px;color:#fff;content:"On"}.toggle input[type=checkbox]{display:none}.content.fade{left:0;opacity:0}.content.fade.in{opacity:1}.content.sliding{z-index:2;-webkit-transition:-webkit-transform .4s;-moz-transition:-moz-transform .4s;transition:transform .4s;-webkit-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.content.sliding.left{z-index:1;-webkit-transform:translate3d(-100%,0,0);-ms-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.content.sliding.right{z-index:3;-webkit-transform:translate3d(100%,0,0);-ms-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.navigate-left:after,.navigate-right:after,.push-left:after,.push-right:after{position:absolute;top:50%;display:inline-block;font-family:Ratchicons;font-size:inherit;line-height:1;color:#bbb;text-decoration:none;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%);-webkit-font-smoothing:antialiased}.navigate-left:after,.push-left:after{left:15px;content:'\e822'}.navigate-right:after,.push-right:after{right:15px;content:'\e826'}@font-face{font-family:Ratchicons;font-style:normal;font-weight:400;src:url(../fonts/ratchicons.eot);src:url(../fonts/ratchicons.eot?#iefix) format("embedded-opentype"),url(../fonts/ratchicons.woff) format("woff"),url(../fonts/ratchicons.ttf) format("truetype"),url(../fonts/ratchicons.svg#svgFontName) format("svg")}.icon{display:inline-block;font-family:Ratchicons;font-size:24px;line-height:1;text-decoration:none;-webkit-font-smoothing:antialiased}.icon-back:before{content:'\e80a'}.icon-bars:before{content:'\e80e'}.icon-caret:before{content:'\e80f'}.icon-check:before{content:'\e810'}.icon-close:before{content:'\e811'}.icon-code:before{content:'\e812'}.icon-compose:before{content:'\e813'}.icon-download:before{content:'\e815'}.icon-edit:before{content:'\e829'}.icon-forward:before{content:'\e82a'}.icon-gear:before{content:'\e821'}.icon-home:before{content:'\e82b'}.icon-info:before{content:'\e82c'}.icon-list:before{content:'\e823'}.icon-more-vertical:before{content:'\e82e'}.icon-more:before{content:'\e82f'}.icon-pages:before{content:'\e824'}.icon-pause:before{content:'\e830'}.icon-person:before{content:'\e832'}.icon-play:before{content:'\e816'}.icon-plus:before{content:'\e817'}.icon-refresh:before{content:'\e825'}.icon-search:before{content:'\e819'}.icon-share:before{content:'\e81a'}.icon-sound:before{content:'\e827'}.icon-sound2:before{content:'\e828'}.icon-sound3:before{content:'\e80b'}.icon-sound4:before{content:'\e80c'}.icon-star-filled:before{content:'\e81b'}.icon-star:before{content:'\e81c'}.icon-stop:before{content:'\e81d'}.icon-trash:before{content:'\e81e'}.icon-up-nav:before{content:'\e81f'}.icon-up:before{content:'\e80d'}.icon-right-nav:before{content:'\e818'}.icon-right:before{content:'\e826'}.icon-down-nav:before{content:'\e814'}.icon-down:before{content:'\e820'}.icon-left-nav:before{content:'\e82d'}.icon-left:before{content:'\e822'}
--------------------------------------------------------------------------------
/images/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/Icon.png
--------------------------------------------------------------------------------
/images/Icon@1024.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/Icon@1024.icns
--------------------------------------------------------------------------------
/images/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/Icon@2x.png
--------------------------------------------------------------------------------
/images/icon-comment.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/images/icon-new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/icon-new.png
--------------------------------------------------------------------------------
/images/icon-new@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/icon-new@2x.png
--------------------------------------------------------------------------------
/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/owenthereal/hacker-menu/8084c27a8e002c7817f7767ece3b2a81db1b2128/images/screenshot.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hacker-menu",
3 | "productName": "Hacker Menu",
4 | "version": "1.1.5",
5 | "description": "A menu to read Hacker News",
6 | "main": "./dist/index.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/jingweno/hacker-menu.git"
10 | },
11 | "engines": {
12 | "node": "4.1.1",
13 | "npm": "3.3.5"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/jingweno/hacker-menu/issues"
17 | },
18 | "license": "MIT",
19 | "scripts": {
20 | "build": "babel src -D -d dist --compact true",
21 | "csscomb": "csscomb css/layout.css",
22 | "watch": "babel src -D -d dist -w",
23 | "test": "standard",
24 | "prestart": "npm run build",
25 | "start": "electron ./",
26 | "compress": "script/compress",
27 | "package": "npm run build && node script/package",
28 | "package_sign": "npm run build && node script/package --sign",
29 | "release": "npm run package_sign && npm run compress"
30 | },
31 | "author": "Jingwen Owen Ou",
32 | "config": {
33 | "electron_version": "0.33.4"
34 | },
35 | "devDependencies": {
36 | "babel": "^5.8.23",
37 | "babel-eslint": "^4.1.3",
38 | "csscomb": "^3.1.8",
39 | "electron-packager": "^5.1.0",
40 | "electron-prebuilt": "^0.33.4",
41 | "shelljs": "^0.5.3",
42 | "standard": "^5.3.1"
43 | },
44 | "dependencies": {
45 | "async": "^1.4.2",
46 | "electron-rpc": "^1.0.3",
47 | "electron-window-state": "^1.0.0",
48 | "firebase": "^2.3.1",
49 | "fs-plus": "^2.8.1",
50 | "lodash": "^3.10.1",
51 | "lru-cache": "^2.7.0",
52 | "menubar": "^2.2.1",
53 | "moment": "^2.10.6",
54 | "newrelic-winston": "0.0.1",
55 | "react": "^0.13.3",
56 | "winston": "^1.0.2"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/script/compress:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | rm -rf release
6 | mkdir release
7 |
8 | cd 'build/Hacker Menu-darwin-x64'
9 | zip -9 -r --symlinks hacker-menu-mac.zip Hacker\ Menu.app
10 | mv hacker-menu-mac.zip ../../release
11 |
--------------------------------------------------------------------------------
/script/package:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var packager = require('electron-packager')
4 | var package = require('../package.json')
5 | var os = require('os')
6 | var _ = require('lodash')
7 | var path = require('path')
8 | var shell = require('shelljs/global')
9 |
10 | var platform = os.platform()
11 | var arch = os.arch()
12 | if (process.argv.indexOf('--all') !== -1) {
13 | arch = platform = 'all'
14 | }
15 |
16 | function parseDeps(current, deps) {
17 | deps = _.map(deps, function(p) {
18 | var pp = path.relative(current, p)
19 | return path.join('node_modules', path.basename(pp))
20 | })
21 |
22 | return _.uniq(deps)
23 | }
24 |
25 | var current = path.join(__dirname, '..', 'node_modules')
26 |
27 | var prodDeps = exec('npm list --production --parseable', {silent:true}).output.split('\n')
28 | prodDeps = parseDeps(current, prodDeps)
29 |
30 | var devDeps = exec('npm list --dev --parseable', {silent:true}).output.split('\n')
31 | devDeps = parseDeps(current, devDeps)
32 |
33 | devDeps = _.difference(devDeps, prodDeps)
34 |
35 | var opts = {
36 | dir: '.',
37 | name: 'Hacker Menu',
38 | overwrite: true,
39 | icon: 'images/Icon@1024.icns',
40 | platform: platform,
41 | arch: arch,
42 | out: 'build',
43 | version: package.config.electron_version,
44 | ignore: _.union(devDeps, [
45 | 'src',
46 | 'script',
47 | 'build',
48 | 'release',
49 | 'images/(Icon@1024.icns|screenshot.png)'
50 | ])
51 | }
52 | if (!process.env.CI && process.argv.indexOf('--sign') !== -1) {
53 | opts.sign = 'Developer ID Application: Jingwen Ou'
54 | }
55 | packager(opts, function done (err, appPaths) {
56 | if (err) {
57 | if (err.message) {
58 | console.error(err.message)
59 | } else {
60 | console.error(err, err.stack)
61 | }
62 |
63 | process.exit(1)
64 | }
65 |
66 | if (appPaths.length > 1) {
67 | console.error('Wrote new apps to:\n' + appPaths.join('\n'))
68 | } else if (appPaths.length === 1) {
69 | console.error('Wrote new app to', appPaths[0])
70 | }
71 | })
72 |
--------------------------------------------------------------------------------
/src/client/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import StoryBox from './story_box.js'
3 |
4 | React.render(
5 |
43 | 44 | {story.descendants} 45 | – 46 | 47 | {story.by} 48 | – 49 | 50 | {story.timeAgo} 51 | 52 |
53 |