aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/PULL_REQUEST_TEMPLATE.md4
-rw-r--r--README.md9
-rw-r--r--app.css644
-rw-r--r--app.js49
-rw-r--r--app.scss24
-rw-r--r--index.html8
6 files changed, 373 insertions, 365 deletions
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index e0bcfe2..d32ca78 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,15 +6,13 @@ Fixes # (issue)
6 6
7## Type of change 7## Type of change
8 8
9Please delete options that are not relevant.
10
11- [ ] Bug fix (non-breaking change which fixes an issue) 9- [ ] Bug fix (non-breaking change which fixes an issue)
12- [ ] New feature (non-breaking change which adds functionality) 10- [ ] New feature (non-breaking change which adds functionality)
13- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 11- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
14 12
15## Checklist: 13## Checklist:
16 14
17- [ ] I read & comply with the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/master/.github/CONTRIBUTING.md) 15- [ ] I read & comply with the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/master/CONTRIBUTING.md)
18- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers. 16- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
19- [ ] I have made corresponding changes the documentation (README.md). 17- [ ] I have made corresponding changes the documentation (README.md).
20- [ ] I've check my modifications for any breaking change, especially in the `config.yml` file 18- [ ] I've check my modifications for any breaking change, especially in the `config.yml` file
diff --git a/README.md b/README.md
index 5ab7c26..3a621d5 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,14 @@
1# Homer 1# Homer
2A dead simple static **HOM**epage for your serv**ER** to keep your services on hand, from a simple `yaml` configuration file. 2A dead simple static **HOM**epage for your serv**ER** to keep your services on hand, from a simple `yaml` configuration file.
3 3
4**Check out the live demo [here](https://homer-demo.netlify.com/).** 4**Check out the live demo [here](https://homer-demo.netlify.app).**
5
6It supports keyboard shortcuts:
7
8* `/` Start searching.
9* `Escape` Stop searching.
10* `Enter` Open the first matching result (respects the bookmark's `_target` property).
11* `Alt`/`Option` + `Enter` Open the first matching result in a new tab.
5 12
6If you need authentication support, you're on your own (it can be secured using a web server auth module or exposing it only through a VPN network / SSH tunnel, ...) 13If you need authentication support, you're on your own (it can be secured using a web server auth module or exposing it only through a VPN network / SSH tunnel, ...)
7 14
diff --git a/app.css b/app.css
index 60b41e8..628b0ce 100644
--- a/app.css
+++ b/app.css
@@ -1,372 +1,308 @@
1@charset "UTF-8";
2/* raleway-regular - latin */ 1/* raleway-regular - latin */
3@font-face { 2@font-face {
4 font-family: "Raleway"; 3 font-family: 'Raleway';
5 font-style: normal; 4 font-style: normal;
6 font-weight: 400; 5 font-weight: 400;
7 font-display: swap; 6 font-display: swap;
8 src: local("Raleway"), local("Raleway-Regular"), url("./webfonts/raleway/raleway-v14-latin-regular.woff2") format("woff2"), url("./webfonts/raleway/raleway-v14-latin-regular.woff") format("woff"); 7 src: local("Raleway"), local("Raleway-Regular"), url("./webfonts/raleway/raleway-v14-latin-regular.woff2") format("woff2"), url("./webfonts/raleway/raleway-v14-latin-regular.woff") format("woff");
9 /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 8 /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ }
10} 9
11/* lato-regular - latin */ 10/* lato-regular - latin */
12@font-face { 11@font-face {
13 font-family: "Lato"; 12 font-family: 'Lato';
14 font-style: normal; 13 font-style: normal;
15 font-weight: 400; 14 font-weight: 400;
16 font-display: swap; 15 font-display: swap;
17 src: local("Lato Regular"), local("Lato-Regular"), url("./webfonts/lato/lato-v16-latin-regular.woff2") format("woff2"), url("./webfonts/lato/lato-v16-latin-regular.woff") format("woff"); 16 src: local("Lato Regular"), local("Lato-Regular"), url("./webfonts/lato/lato-v16-latin-regular.woff2") format("woff2"), url("./webfonts/lato/lato-v16-latin-regular.woff") format("woff");
18 /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 17 /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ }
19} 18
20html { 19html {
21 height: 100%; 20 height: 100%; }
22}
23 21
24body { 22body {
25 font-family: "Raleway", sans-serif; 23 font-family: 'Raleway', sans-serif;
26 height: 100%; 24 height: 100%; }
27}
28body #app {
29 min-height: 100%;
30 transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
31 background-color: #f5f5f5;
32 color: #363636;
33}
34body #app a:hover {
35 color: #363636;
36}
37body #app .title {
38 color: #303030;
39}
40body #app .subtitle {
41 color: #424242;
42}
43body #app .card {
44 background-color: #ffffff;
45 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
46}
47body #app .card:hover {
48 background-color: #ffffff;
49}
50body #app .footer {
51 background-color: #ffffff;
52 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
53}
54@media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
55 body #app { 25 body #app {
26 min-height: 100%;
27 transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
56 background-color: #f5f5f5; 28 background-color: #f5f5f5;
57 color: #363636; 29 color: #363636; }
58 } 30 body #app a:hover {
59 body #app a:hover { 31 color: #363636; }
60 color: #363636; 32 body #app .title {
61 } 33 color: #303030; }
62 body #app .title { 34 body #app .subtitle {
63 color: #303030; 35 color: #424242; }
64 } 36 body #app .card {
65 body #app .subtitle { 37 background-color: #ffffff;
66 color: #424242; 38 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
67 } 39 body #app .card:hover {
68 body #app .card { 40 background-color: #ffffff; }
69 background-color: #ffffff; 41 body #app .message {
42 background-color: #ffffff; }
43 body #app .message .message-body {
44 color: #363636; }
45 body #app .footer {
46 background-color: #ffffff;
47 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
48 @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
49 body #app {
50 background-color: #f5f5f5;
51 color: #363636; }
52 body #app a:hover {
53 color: #363636; }
54 body #app .title {
55 color: #303030; }
56 body #app .subtitle {
57 color: #424242; }
58 body #app .card {
59 background-color: #ffffff;
60 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
61 body #app .card:hover {
62 background-color: #ffffff; }
63 body #app .message {
64 background-color: #ffffff; }
65 body #app .message .message-body {
66 color: #363636; }
67 body #app .footer {
68 background-color: #ffffff;
69 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); } }
70 @media (prefers-color-scheme: dark) {
71 body #app {
72 background-color: #131313;
73 color: #eaeaea; }
74 body #app a:hover {
75 color: #ffdd57; }
76 body #app .title {
77 color: #fafafa; }
78 body #app .subtitle {
79 color: #f5f5f5; }
80 body #app .card {
81 background-color: #2b2b2b;
82 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); }
83 body #app .card:hover {
84 background-color: #2b2b2b; }
85 body #app .message {
86 background-color: #2b2b2b; }
87 body #app .message .message-body {
88 color: #eaeaea; }
89 body #app .footer {
90 background-color: #2b2b2b;
91 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); } }
92 body #app.is-light {
93 background-color: #f5f5f5;
94 color: #363636; }
95 body #app.is-light a:hover {
96 color: #363636; }
97 body #app.is-light .title {
98 color: #303030; }
99 body #app.is-light .subtitle {
100 color: #424242; }
101 body #app.is-light .card {
102 background-color: #ffffff;
103 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
104 body #app.is-light .card:hover {
105 background-color: #ffffff; }
106 body #app.is-light .message {
107 background-color: #ffffff; }
108 body #app.is-light .message .message-body {
109 color: #363636; }
110 body #app.is-light .footer {
111 background-color: #ffffff;
112 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
113 body #app.is-dark {
114 background-color: #131313;
115 color: #eaeaea; }
116 body #app.is-dark a:hover {
117 color: #ffdd57; }
118 body #app.is-dark .title {
119 color: #fafafa; }
120 body #app.is-dark .subtitle {
121 color: #f5f5f5; }
122 body #app.is-dark .card {
123 background-color: #2b2b2b;
124 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); }
125 body #app.is-dark .card:hover {
126 background-color: #2b2b2b; }
127 body #app.is-dark .message {
128 background-color: #2b2b2b; }
129 body #app.is-dark .message .message-body {
130 color: #eaeaea; }
131 body #app.is-dark .footer {
132 background-color: #2b2b2b;
133 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); }
134 body h1, body h2, body h3, body h4, body h5, body h6 {
135 font-family: 'Lato', sans-serif; }
136 body h1 {
137 font-size: 2rem; }
138 body h2 {
139 font-size: 1.7rem;
140 margin-top: 2rem;
141 margin-bottom: 1rem; }
142 body h2 .fas, body h2 .fab, body h2 .far {
143 margin-right: 10px; }
144 body h2 span {
145 font-weight: bold;
146 color: #4285f4; }
147 body [v-cloak] {
148 display: none; }
149 body #bighead {
150 color: #ffffff; }
151 body #bighead .dashboard-title {
152 padding: 6px 0 0 80px; }
153 body #bighead .first-line {
154 height: 100px;
155 vertical-align: center;
156 background-color: #3367d6; }
157 body #bighead .first-line h1 {
158 margin-top: -12px;
159 font-size: 2rem; }
160 body #bighead .first-line .headline {
161 margin-top: 5px;
162 font-size: 0.9rem; }
163 body #bighead .first-line .container {
164 height: 80px;
165 padding: 10px 0; }
166 body #bighead .first-line .logo {
167 float: left; }
168 body #bighead .first-line .logo i {
169 vertical-align: top;
170 padding: 8px 15px;
171 font-size: 50px; }
172 body #bighead .first-line .logo img {
173 padding: 10px;
174 max-height: 70px;
175 max-width: 70px; }
176 body #bighead .navbar, body #bighead .navbar-menu {
177 background-color: #4285f4; }
178 body #bighead .navbar a, body #bighead .navbar-menu a {
179 color: #ffffff; }
180 body #bighead .navbar a:hover, body #bighead .navbar a:focus, body #bighead .navbar-menu a:hover, body #bighead .navbar-menu a:focus {
181 color: #ffffff;
182 background-color: #5a95f5; }
183 body #bighead .navbar-end {
184 text-align: right; }
185 body #main-section {
186 margin-bottom: 2rem;
187 padding: 0; }
188 body #main-section h2 {
189 border-bottom: 1px dashed #ccc;
190 padding-bottom: 10px;
191 white-space: nowrap;
192 overflow: hidden;
193 text-overflow: ellipsis; }
194 body #main-section .title {
195 font-size: 1.1em;
196 white-space: nowrap;
197 overflow: hidden;
198 text-overflow: ellipsis; }
199 body #main-section .subtitle {
200 font-size: .9em;
201 white-space: nowrap;
202 overflow: hidden;
203 text-overflow: ellipsis; }
204 body #main-section .container {
205 padding: 1.2rem .75rem; }
206 body #main-section .message {
207 margin-top: 45px;
208 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); }
209 body #main-section .message .message-header {
210 font-weight: bold; }
211 body #main-section .message .message-body {
212 border: none; }
213 body .media-content {
214 overflow: hidden;
215 text-overflow: inherit; }
216 body .tag {
217 color: #4285f4;
218 background-color: #4285f4;
219 position: absolute;
220 top: 1rem;
221 right: -0.2rem;
222 width: 3px;
223 overflow: hidden;
224 transition: all 0.2s ease-out;
225 padding: 0; }
226 body .tag .tag-text {
227 display: none; }
228 body .card {
229 border-radius: 5px;
230 border: none;
70 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); 231 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
71 } 232 transition: cubic-bezier(0.165, 0.84, 0.44, 1) 300ms; }
72 body #app .card:hover { 233 body .card a {
73 background-color: #ffffff; 234 outline: none; }
74 } 235 body .card:hover {
75 body #app .footer { 236 transform: translate(0, -3px); }
76 background-color: #ffffff; 237 body .card:hover .tag {
77 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); 238 width: auto;
78 } 239 color: #ffffff;
79} 240 padding: 0 0.75em; }
80@media (prefers-color-scheme: dark) { 241 body .card:hover .tag .tag-text {
81 body #app { 242 display: block; }
82 background-color: #131313; 243 body .card-content {
83 color: #eaeaea; 244 height: 85px;
84 } 245 padding: 1.3rem; }
85 body #app a:hover { 246 body .layout-vertical .card {
86 color: #ffdd57; 247 border-radius: 0; }
87 } 248 body .layout-vertical .column div:first-of-type .card {
88 body #app .title { 249 border-radius: 5px 5px 0 0; }
89 color: #fafafa; 250 body .layout-vertical .column div:last-child .card {
90 } 251 border-radius: 0 0 5px 5px; }
91 body #app .subtitle { 252 body .footer {
92 color: #f5f5f5; 253 position: fixed;
93 } 254 left: 0;
94 body #app .card { 255 right: 0;
95 background-color: #2b2b2b; 256 bottom: 0;
96 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); 257 padding: 0.5rem;
97 } 258 text-align: left;
98 body #app .card:hover { 259 color: #676767;
99 background-color: #2b2b2b; 260 font-size: 0.85rem;
100 } 261 transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms; }
101 body #app .footer { 262 body .no-footer #main-section {
102 background-color: #2b2b2b; 263 margin-bottom: 0; }
103 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); 264 body .no-footer .footer {
104 } 265 display: none; }
105} 266 body .search-bar {
106body #app.is-light { 267 position: relative;
107 background-color: #f5f5f5; 268 display: inline-block; }
108 color: #363636; 269 body .search-bar #search {
109} 270 border: none;
110body #app.is-light a:hover { 271 background-color: #5f98f6;
111 color: #363636; 272 border-radius: 5px;
112} 273 padding: 2px 12px 2px 30px;
113body #app.is-light .title { 274 margin: 0 0 0 12px;
114 color: #303030; 275 transition: all 100ms linear;
115} 276 color: #ffffff;
116body #app.is-light .subtitle { 277 height: 30px;
117 color: #424242; 278 width: 100px; }
118} 279 body .search-bar #search:focus {
119body #app.is-light .card { 280 color: #000000;
120 background-color: #ffffff; 281 width: 250px;
121 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); 282 background-color: #ffffff; }
122} 283 body .search-bar .search-label::before {
123body #app.is-light .card:hover { 284 font-family: 'Font Awesome 5 Free';
124 background-color: #ffffff; 285 position: absolute;
125} 286 top: 4px;
126body #app.is-light .footer { 287 left: 16px;
127 background-color: #ffffff; 288 content: "\f002";
128 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1); 289 font-weight: 900;
129} 290 width: 20px;
130body #app.is-dark { 291 height: 20px;
131 background-color: #131313; 292 color: #ffffff; }
132 color: #eaeaea; 293 body .search-bar:focus-within .search-label::before {
133} 294 color: #4a4a4a; }
134body #app.is-dark a:hover { 295 body .icon-button {
135 color: #ffdd57; 296 display: inline-block;
136} 297 padding: 0 12px; }
137body #app.is-dark .title { 298 body .offline-message {
138 color: #fafafa; 299 text-align: center;
139} 300 margin: 35px 0; }
140body #app.is-dark .subtitle { 301 body .offline-message i {
141 color: #f5f5f5; 302 font-size: 2rem; }
142} 303 body .offline-message i.fa-redo-alt {
143body #app.is-dark .card { 304 font-size: 1.3rem;
144 background-color: #2b2b2b; 305 line-height: 1rem;
145 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4); 306 vertical-align: middle;
146} 307 cursor: pointer;
147body #app.is-dark .card:hover { 308 color: #3273dc; }
148 background-color: #2b2b2b;
149}
150body #app.is-dark .footer {
151 background-color: #2b2b2b;
152 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.4);
153}
154body h1, body h2, body h3, body h4, body h5, body h6 {
155 font-family: "Lato", sans-serif;
156}
157body h1 {
158 font-size: 2rem;
159}
160body h2 {
161 font-size: 1.7rem;
162 margin-top: 2rem;
163 margin-bottom: 1rem;
164}
165body h2 .fas, body h2 .fab, body h2 .far {
166 margin-right: 10px;
167}
168body h2 span {
169 font-weight: bold;
170 color: #4285f4;
171}
172body [v-cloak] {
173 display: none;
174}
175body #bighead {
176 color: #ffffff;
177}
178body #bighead .dashboard-title {
179 padding: 6px 0 0 80px;
180}
181body #bighead .first-line {
182 height: 100px;
183 vertical-align: center;
184 background-color: #3367d6;
185}
186body #bighead .first-line h1 {
187 margin-top: -12px;
188 font-size: 2rem;
189}
190body #bighead .first-line .headline {
191 margin-top: 5px;
192 font-size: 0.9rem;
193}
194body #bighead .first-line .container {
195 height: 80px;
196 padding: 10px 0;
197}
198body #bighead .first-line .logo {
199 float: left;
200}
201body #bighead .first-line .logo i {
202 vertical-align: top;
203 padding: 8px 15px;
204 font-size: 50px;
205}
206body #bighead .first-line .logo img {
207 padding: 10px;
208 max-height: 70px;
209 max-width: 70px;
210}
211body #bighead .navbar, body #bighead .navbar-menu {
212 background-color: #4285f4;
213}
214body #bighead .navbar a, body #bighead .navbar-menu a {
215 color: #ffffff;
216}
217body #bighead .navbar a:hover, body #bighead .navbar a:focus, body #bighead .navbar-menu a:hover, body #bighead .navbar-menu a:focus {
218 color: #ffffff;
219 background-color: #5a95f5;
220}
221body #bighead .navbar-end {
222 text-align: right;
223}
224body #main-section {
225 margin-bottom: 2rem;
226 padding: 0;
227}
228body #main-section h2 {
229 border-bottom: 1px dashed #ccc;
230 padding-bottom: 10px;
231}
232body #main-section .title {
233 font-size: 1.1em;
234}
235body #main-section .subtitle {
236 font-size: 0.9em;
237 white-space: nowrap;
238 overflow: hidden;
239 text-overflow: ellipsis;
240}
241body #main-section .container {
242 padding: 1.2rem 0.75rem;
243}
244body #main-section .message {
245 margin-top: 45px;
246 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
247}
248body #main-section .message .message-header {
249 font-weight: bold;
250}
251body #main-section .message .message-body {
252 border: none;
253}
254body .media-content {
255 overflow: inherit;
256}
257body .tag {
258 color: #4285f4;
259 background-color: #4285f4;
260 position: absolute;
261 top: 1rem;
262 right: -0.2rem;
263 width: 3px;
264 overflow: hidden;
265 transition: all 0.2s ease-out;
266 padding: 0;
267}
268body .tag .tag-text {
269 display: none;
270}
271body .card {
272 border-radius: 5px;
273 border: none;
274 box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
275 transition: cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
276}
277body .card a {
278 outline: none;
279}
280body .card:hover {
281 transform: translate(0, -3px);
282}
283body .card:hover .tag {
284 width: auto;
285 color: #ffffff;
286 padding: 0 0.75em;
287}
288body .card:hover .tag .tag-text {
289 display: block;
290}
291body .card-content {
292 height: 85px;
293 padding: 1.3rem;
294}
295body .layout-vertical .card {
296 border-radius: 0;
297}
298body .layout-vertical .column div:first-of-type .card {
299 border-radius: 5px 5px 0 0;
300}
301body .layout-vertical .column div:last-child .card {
302 border-radius: 0 0 5px 5px;
303}
304body .footer {
305 position: fixed;
306 left: 0;
307 right: 0;
308 bottom: 0;
309 padding: 0.5rem;
310 text-align: left;
311 color: #676767;
312 font-size: 0.85rem;
313 transition: background-color cubic-bezier(0.165, 0.84, 0.44, 1) 300ms;
314}
315body .no-footer #main-section {
316 margin-bottom: 0;
317}
318body .no-footer .footer {
319 display: none;
320}
321body .search-bar {
322 position: relative;
323 display: inline-block;
324}
325body .search-bar #search {
326 border: none;
327 background-color: #5f98f6;
328 border-radius: 5px;
329 padding: 2px 12px 2px 30px;
330 margin: 0 0 0 12px;
331 transition: all 100ms linear;
332 color: #ffffff;
333 height: 30px;
334 width: 100px;
335}
336body .search-bar #search:focus {
337 color: #000000;
338 width: 250px;
339 background-color: #ffffff;
340}
341body .search-bar .search-label::before {
342 font-family: "Font Awesome 5 Free";
343 position: absolute;
344 top: 4px;
345 left: 16px;
346 content: "";
347 font-weight: 900;
348 width: 20px;
349 height: 20px;
350 color: #ffffff;
351}
352body .search-bar:focus-within .search-label::before {
353 color: #4a4a4a;
354}
355body .icon-button {
356 display: inline-block;
357 padding: 0 12px;
358}
359body .offline-message {
360 text-align: center;
361 margin: 35px 0;
362}
363body .offline-message i {
364 font-size: 2rem;
365}
366body .offline-message i.fa-redo-alt {
367 font-size: 1.3rem;
368 line-height: 1rem;
369 vertical-align: middle;
370 cursor: pointer;
371 color: #3273dc;
372}
diff --git a/app.js b/app.js
index 6c83074..e796110 100644
--- a/app.js
+++ b/app.js
@@ -84,7 +84,56 @@ const app = new Vue({
84 }, 84 },
85 toggleMenu: function() { 85 toggleMenu: function() {
86 this.showMenu = !this.showMenu; 86 this.showMenu = !this.showMenu;
87 },
88 matchesFilter: function(item) {
89 return (item.name.toLowerCase().includes(this.filter.toLowerCase())
90 || (item.tag && item.tag.toLowerCase().includes(this.filter.toLowerCase())))
91 },
92 firstMatchingService: function() {
93 for (group of this.config.services) {
94 for (item of group.items) {
95 if (this.matchesFilter(item)) {
96 return item;
97 }
98 }
99 }
100 return null;
101 },
102 navigateToFirstService: function(target) {
103 service = this.firstMatchingService();
104 if (service) {
105 window.open(service.url, target || service.target || '_self');
106 }
107 }
108 },
109 mounted() {
110 function isSmallScreen() {
111 return window.matchMedia('screen and (max-width: 1023px)').matches;
87 } 112 }
113 this._keyListener = function(e) {
114 if (e.key === '/') {
115 if (isSmallScreen()) {
116 this.showMenu = true;
117 }
118 Vue.nextTick(() => {
119 this.$refs.search.focus();
120 });
121
122 e.preventDefault();
123 }
124 if (e.key === 'Escape') {
125 this.filter = '';
126 this.$refs.search.blur();
127 if (isSmallScreen()) {
128 this.showMenu = false;
129 }
130 }
131 }
132
133 document.addEventListener('keydown', this._keyListener.bind(this));
134 },
135 beforeDestroy() {
136 document.removeEventListener('keydown', this._keyListener);
88 } 137 }
89}); 138});
90 139
diff --git a/app.scss b/app.scss
index 69b1067..51110e1 100644
--- a/app.scss
+++ b/app.scss
@@ -68,12 +68,27 @@ $theme-dark: (
68 } 68 }
69 } 69 }
70 70
71 .message {
72 background-color: map-get($theme, "card-background");
73 .message-body {
74 color: map-get($theme, "text");
75 }
76 }
77
71 .footer { 78 .footer {
72 background-color: map-get($theme, "card-background"); 79 background-color: map-get($theme, "card-background");
73 box-shadow: 0 2px 15px 0 map-get($theme, "card-shadow"); 80 box-shadow: 0 2px 15px 0 map-get($theme, "card-shadow");
74 } 81 }
75} 82}
76 83
84
85@mixin ellipsis() {
86 white-space: nowrap;
87 overflow: hidden;
88 text-overflow: ellipsis;
89}
90
91
77html { 92html {
78 height: 100%; 93 height: 100%;
79} 94}
@@ -198,17 +213,17 @@ body {
198 h2 { 213 h2 {
199 border-bottom: 1px dashed #ccc; 214 border-bottom: 1px dashed #ccc;
200 padding-bottom: 10px; 215 padding-bottom: 10px;
216 @include ellipsis();
201 } 217 }
202 218
203 .title { 219 .title {
204 font-size: 1.1em; 220 font-size: 1.1em;
221 @include ellipsis();
205 } 222 }
206 223
207 .subtitle { 224 .subtitle {
208 font-size: .9em; 225 font-size: .9em;
209 white-space: nowrap; 226 @include ellipsis();
210 overflow: hidden;
211 text-overflow: ellipsis;
212 } 227 }
213 228
214 .container { 229 .container {
@@ -230,7 +245,8 @@ body {
230 } 245 }
231 246
232 .media-content { 247 .media-content {
233 overflow: inherit; 248 overflow: hidden;
249 text-overflow: inherit;
234 } 250 }
235 251
236 .tag { 252 .tag {
diff --git a/index.html b/index.html
index 9c1b531..d427002 100644
--- a/index.html
+++ b/index.html
@@ -59,7 +59,9 @@
59 :class="['fas', vlayout ? 'fa-list' : 'fa-columns']"></i></a> 59 :class="['fas', vlayout ? 'fa-list' : 'fa-columns']"></i></a>
60 <div class="search-bar"> 60 <div class="search-bar">
61 <label for="search" class="search-label"></label> 61 <label for="search" class="search-label"></label>
62 <input type="text" id="search" v-model="filter" /> 62 <input type="text" id="search" ref="search" v-model="filter"
63 v-on:keyup.enter.exact="navigateToFirstService()"
64 v-on:keyup.alt.enter="navigateToFirstService('_blank')" />
63 </div> 65 </div>
64 </div> 66 </div>
65 </div> 67 </div>
@@ -95,7 +97,7 @@
95 v-else>#</span> 97 v-else>#</span>
96 {{ group.name }}</h2> 98 {{ group.name }}</h2>
97 <service v-for="item in group.items" v-bind:item="item" class="column is-one-third-widescreen" 99 <service v-for="item in group.items" v-bind:item="item" class="column is-one-third-widescreen"
98 v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"> 100 v-if="!filter || (item && matchesFilter(item))">
99 </service> 101 </service>
100 </template> 102 </template>
101 </div> 103 </div>
@@ -106,7 +108,7 @@
106 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span> 108 <h2 v-if="!filter && group.name"><i v-if="group.icon" :class="group.icon"></i><span v-else>#</span>
107 {{ group.name }}</h2> 109 {{ group.name }}</h2>
108 <service v-for="item in group.items" v-bind:item="item" 110 <service v-for="item in group.items" v-bind:item="item"
109 v-if="!filter || (item && (item.name.toLowerCase().includes(filter.toLowerCase()) || (item.tag && item.tag.toLowerCase().includes(filter.toLowerCase()))))"> 111 v-if="!filter || (item && matchesFilter(item))">
110 </service> 112 </service>
111 </div> 113 </div>
112 </div> 114 </div>