diff --git a/README.md b/README.md index 639a0d25..d3d438ec 100755 --- a/README.md +++ b/README.md @@ -1,107 +1,147 @@ -## SUI -*a startpage for your server and / or new tab page* +## SUI Extended + +_A startpage for your server and / or new tab page_ ![screenshot](https://i.imgur.com/J4d7Q3D.png) [More screenshots](https://imgur.com/a/FDVRIyw) +[Extended Options](https://imgur.com/a/69CwxxW) + ### Deploy with Docker compose #### Prerequisites: - - Docker: [Linux](https://docs.docker.com/install/linux/docker-ce/debian/), [Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac), [Windows](https://hub.docker.com/editions/community/docker-ce-desktop-windows) - - [Docker-compose](https://docs.docker.com/compose/install/) -#### Install: +- Docker: [Linux](https://docs.docker.com/install/linux/docker-ce/debian/), [Mac](https://hub.docker.com/editions/community/docker-ce-desktop-mac), [Windows](https://hub.docker.com/editions/community/docker-ce-desktop-windows) +- [Docker-compose](https://docs.docker.com/compose/install/) - - `git clone` this repository - - Build and bring up with `docker-compose up -d` - - The page should be available at `http://localhost:4000` +#### Install: -To run at a different port open edit docker-compose.yml: +- `git clone` this repository +- Build and bring up with `docker compose up -d` +- The page should be available at `http://localhost:4000` - ports: - - 4000:80 +To run at a different port edit the docker-compose.yml +```yaml + ports: + - 4000:80 +``` #### Install pull from git variant: - - refreshs source code every 5 minutes from master branch you provided - convenience feature for lacy devs - - `git clone` this repository - - build image `docker build -f DockerfilePullFromGit -t sui:latest .` - - run image with `docker run -e GITURL='https://x:ghp_x@github.com/jeroenpardon/sui.git' -p 8081:80 sui:latest` - - can be run also with a private repository by setting username:api-key@ in the url (see above example). Otherwise remove this part of the url. - - +- refresh the source code every 5 minutes from master branch you provided - convenience feature for lazy devs +- `git clone` this repository +- build image `docker build -f DockerfilePullFromGit -t sui:latest .` +- run image with `docker run -e GITURL='https://x:ghp_x@github.com/jeroenpardon/sui.git' -p 8081:80 sui:latest` +- can be run also with a private repository by setting username:api-key@ in the url (see above example). Otherwise remove this part of the url. ### Customization +#### Config options + +Change the config.json to your likings. + +| Option | Default | Description | +| --------------------- | ---------------- | ----------------------------------------------- | +| `language` | `en-GB` | The language code for localization. | +| `greetings.morning` | `Good Morning` | Greeting text for the morning. | +| `greetings.afternoon` | `Good Afternoon` | Greeting text for the afternoon. | +| `greetings.evening` | `Good Evening` | Greeting text for the evening. | +| `greetings.night` | `Good Night` | Greeting text for the night. | +| `useOauth2` | `false` | Whether to use OAuth2 Proxy for authentication. | +| `useAppCategories` | `false` | Whether to categorize applications. | +| `oauth2UserInfoURL` | `""` | URL to call for user info. | +| `userPermissionKey` | `""` | Key used for user permissions on userinfo call. | +| `withApps` | `true` | Whether to show applications. | +| `withLinks` | `true` | Whether to show links. | +| `withSearch` | `true` | Whether to enable search functionality. | +| `hideSettings` | `false` | Whether to hide the settings option. | +| `labels.bookmarks` | `Bookmarks` | Label text for bookmarks. | +| `labels.applications` | `Applications` | Label text for applications. | +| `backgroundImage` | `""` | URL for the background image. | +| `defaultTheme` | `blackboard` | The default theme to use. | + +#### Theme Configuration + +Change any theme you want or at a new theme by adding a name and the 3 color values. + +| Option | Default | Description | +| ------------------ | --------- | -------------------------------- | +| `color-background` | `#1a1a1a` | Background color of the theme. | +| `color-text-pri` | `#FFFDEA` | Primary text color of the theme. | +| `color-text-acc` | `#5c5c5c` | Accent text color of the theme. | + #### Changing color themes - - Click the options button on the left bottom + +To change your theme click the options button on the bottom left and select your new theme. The selected theme will be stored in the local storage and loaded from there. #### Apps -Add your apps by editing apps.json: +Add your apps by editing the `apps.json`: + +```json +{ + "apps": [ + { "name": "Name of app 1", "url": "sub1.example.com", "icon": "icon-name" }, { - "apps" : [ - {"name":"Name of app 1","url":"sub1.example.com","icon":"icon-name"}, - {"name":"Name of app 2","url":"sub2.example.com","icon":"icon-name","target":"optionals"} - ] + "name": "Name of app 2", + "url": "sub2.example.com", + "icon": "icon-name", + "target": "optionals", + "groups": ["foo", "bar"], // works only if useOauth2Proxy is set to true + "category": "Test Category" // only used if useAppCategories is set to true } + ] +} +``` Please note: - - No `http://` in the URL - - No `,` at the end of the last app's line - - Find the names of icons to use at [Material Design Icons](https://materialdesignicons.com/) +- No `http://` in the URL +- No `,` at the end of the last app's line +- Find the names of the icons to use at [Material Design Icons](https://materialdesignicons.com/) #### Bookmarks -Add your bookmarks by editing links.json: -``` +Add your bookmarks by editing the `links.json`: + +```json { - "bookmarks":[ - { - "category":"Category1", - "links":[ - { - "name":"Link1", - "url":"http://example.com" - }, - { - "name":"Link2", - "url":"http://example.com", - "target":"optionals" - } - ] - }, - { - "category":"Category2", - "links":[ - { - "name":"Link1", - "url":"http://example.com" - }, - { - "name":"Link2", - "url":"http://example.com" - } - ] - } - ] + "bookmarks": [ + { + "category": "Category1", + "links": [ + { + "name": "Link1", + "url": "http://example.com" + }, + { + "name": "Link2", + "url": "http://example.com", + "target": "optionals" + } + ] + }, + { + "category": "Category2", + "links": [ + { + "name": "Link1", + "url": "http://example.com" + }, + { + "name": "Link2", + "url": "http://example.com" + } + ] + } + ] } ``` + Add names for the categories you wish to define and add the bookmarks for each category. Please note: - - No `http://` in the URL - - No `,` at the end of the last bookmark in a category and at the end of the last category - - -#### Color themes -These can be added or customized in the themer.js file. When changing the name of a theme or adding one, make sure to edit this section in index.html accordingly: - -``` -
-``` - -I might add a simpler way to edit themes at some point, but adding the current ones should be pretty straight forward. +- No `http://` in the URL +- No `,` at the end of the last bookmark in a category and at the end of the last category diff --git a/apps.json b/apps.json index 8958e0e5..31d4ab54 100755 --- a/apps.json +++ b/apps.json @@ -1,24 +1,89 @@ { - "apps" : [ - {"name":"Bazarr","url":"subs.example.com","icon":"message-video", "target": "_blank"}, - {"name":"CloudCMD","url":"files.example.com","icon":"folder-multiple-outline"}, - {"name":"Cockpit","url":"cp.example.com","icon":"airplane"}, - {"name":"Feedbin","url":"rss.example.com","icon":"rss"}, - {"name":"Filestash","url":"cloud.example.com","icon":"package"}, - {"name":"Jackett","url":"jackett.example.com","icon":"tshirt-crew-outline"}, - {"name":"Lidarr","url":"music.example.com","icon":"music"}, - {"name":"Minio","url":"minio.example.com","icon":"server"}, - {"name":"Mylar","url":"comics.example.com","icon":"book-open-variant"}, - {"name":"Nextcloud","url":"cloud.example.com","icon":"weather-cloudy"}, - {"name":"Ombi","url":"request.example.com","icon":"file-find-outline"}, - {"name":"Pi-hole","url":"pihole.example.com","icon":"do-not-disturb"}, - {"name":"Plex","url":"play.example.com","icon":"plex"}, - {"name":"Portainer","url":"port1.example.com","icon":"docker"}, - {"name":"Radarr","url":"movies.example.com","icon":"filmstrip"}, - {"name":"Sonarr","url":"tv.example.com","icon":"television-box"}, - {"name":"Stackedit","url":"md.example.com","icon":"markdown"}, - {"name":"Transmission","url":"dl.example.com","icon":"progress-download"}, - {"name":"Ubooquity","url":"opds.example.com","icon":"library-shelves"}, - {"name":"Youtube-DL","url":"yt.example.com","icon":"youtube"} - ] -} \ No newline at end of file + "apps": [ + { + "name": "Bazarr", + "url": "subs.example.com", + "icon": "message-video", + "target": "_blank", + "category": "Admin" + }, + { + "name": "CloudCMD", + "url": "files.example.com", + "icon": "folder-multiple-outline", + "category": "Admin" + }, + { + "name": "Cockpit", + "url": "cp.example.com", + "icon": "airplane", + "category": "Admin" + }, + { + "name": "Feedbin", + "url": "rss.example.com", + "icon": "rss", + "category": "Infrastructure" + }, + { + "name": "Filestash", + "url": "cloud.example.com", + "icon": "package", + "category": "Infrastructure" + }, + { + "name": "Jackett", + "url": "jackett.example.com", + "icon": "tshirt-crew-outline", + "category": "Infrastructure" + }, + { + "name": "Lidarr", + "url": "music.example.com", + "icon": "music", + "category": "Support" + }, + { + "name": "Minio", + "url": "minio.example.com", + "icon": "server", + "category": "Support" + }, + { + "name": "Mylar", + "url": "comics.example.com", + "icon": "book-open-variant" + }, + { + "name": "Nextcloud", + "url": "cloud.example.com", + "icon": "weather-cloudy" + }, + { + "name": "Ombi", + "url": "request.example.com", + "icon": "file-find-outline" + }, + { + "name": "Pi-hole", + "url": "pihole.example.com", + "icon": "do-not-disturb" + }, + { "name": "Plex", "url": "play.example.com", "icon": "plex" }, + { "name": "Portainer", "url": "port1.example.com", "icon": "docker" }, + { "name": "Radarr", "url": "movies.example.com", "icon": "filmstrip" }, + { "name": "Sonarr", "url": "tv.example.com", "icon": "television-box" }, + { "name": "Stackedit", "url": "md.example.com", "icon": "markdown" }, + { + "name": "Transmission", + "url": "dl.example.com", + "icon": "progress-download" + }, + { + "name": "Ubooquity", + "url": "opds.example.com", + "icon": "library-shelves" + }, + { "name": "Youtube-DL", "url": "yt.example.com", "icon": "youtube" } + ] +} diff --git a/assets/css/styles.css b/assets/css/styles.css index 338fc5f1..64cf7b82 100755 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -1,558 +1,534 @@ -html{ - box-sizing: border-box; - moz-box-sizing: border-box; - webkit-box-sizing: border-box; - webkit-text-size-adjust: none; +html { + box-sizing: border-box; + moz-box-sizing: border-box; + webkit-box-sizing: border-box; + webkit-text-size-adjust: none; } html, -body{ - background-color: var(--color-background); - color: var(--color-text-pri); - font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif; - font-size: 14px; - font-weight: 400; - height: auto; - letter-spacing: -.012em; - margin: 0; - padding: 0; - webkit-font-smoothing: antialiased; - width: 100vw; +body { + background-color: var(--color-background); + color: var(--color-text-pri); + font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, + sans-serif; + font-size: 14px; + font-weight: 400; + height: auto; + letter-spacing: -0.012em; + margin: 0; + padding: 0; + webkit-font-smoothing: antialiased; + max-width: 100vw; + overflow-x: hidden; } *, *:before, -*:after{ - box-sizing: inherit; - moz-box-sizing: inherit; - webkit-box-sizing: inherit; +*:after { + box-sizing: inherit; + moz-box-sizing: inherit; + webkit-box-sizing: inherit; } -:root{ - module-spacing: 3vh; +:root { + module-spacing: 3vh; } - /* TEXT STYLES */ -h1, h2{ - font-weight: 300; - margin: 0; - padding: 0; - text-align: left; +h1, +h2 { + font-weight: 300; + margin: 0; + padding: 0; + text-align: left; } -h2, h3, h4{ - text-transform: uppercase; +h2, +h3, +h4 { + text-transform: uppercase; } -h1{ - font-size: 4em; - font-weight: 700; - margin-bottom: 0.5em; +h1 { + font-size: 4em; + font-weight: 700; + margin-bottom: 0.5em; } -h2{ - font-size: 16px; - height: 30px; - +h2 { + font-size: 16px; + height: 30px; } -h3{ - font-size: 20px; - font-weight: 900; - height: 10px; +h3 { + font-size: 20px; + font-weight: 900; + height: 10px; } -h4{ - font-size: 1.1em; - font-weight: 400; - height: 10px; +h4 { + font-size: 1.1em; + font-weight: 400; + height: 10px; } -a{ - color: var(--color-text-pri); - text-decoration: none; +a { + color: var(--color-text-pri); + text-decoration: none; } -a:hover{ - text-decoration: underline; - webkit-text-decoration-color: var(--color-text-acc); - webkit-text-decoration-skip: true; +a:hover { + text-decoration: underline; + webkit-text-decoration-color: var(--color-text-acc); + webkit-text-decoration-skip: true; } -.icon{ - font-size: 2.5em; +.icon { + font-size: 2.5em; } - /* FORMS */ -input{ - background-color: transparent; - border: 0; - border-bottom: thin solid var(--color-text-acc); - color: var(--color-text-pri); - font-size: 0.8em; - height: 3.5em; - transition: all 0.4s ease; - width: 100%; +input { + background-color: transparent; + border: 0; + border-bottom: thin solid var(--color-text-acc); + color: var(--color-text-pri); + font-size: 0.8em; + height: 3.5em; + transition: all 0.4s ease; + width: 100%; } -input:focus{ - color-border: var(--color-text-pri); - outline: none; +input:focus { + color-border: var(--color-text-pri); + outline: none; } -input:focus{ - opacity: 1; +input:focus { + opacity: 1; } - /* TABLES */ -table{ - border: thin solid #e4e4e4; - border-collapse: collapse; - border-spacing: 0; - font-size: 1em; - text-align: left; - width: 100%; +table { + border: thin solid #e4e4e4; + border-collapse: collapse; + border-spacing: 0; + font-size: 1em; + text-align: left; + width: 100%; } -table td:nth-of-type(2){ - padding-right: 5em; +table td:nth-of-type(2) { + padding-right: 5em; } -table td{ - border: thin solid #e4e4e4; - color: #333333; - font-size: 1em; - overflow: hidden; - padding: 10px 5px; - word-break: normal; +table td { + border: thin solid #e4e4e4; + color: var(--color-text-pri); + font-size: 1em; + overflow: hidden; + padding: 10px 5px; + word-break: normal; } -table th{ - border: thin solid #e4e4e4; - color: #333333; - font-weight: bold; - padding: 10px 5px; +table th { + border: thin solid #e4e4e4; + color: var(--color-text-acc); + font-weight: bold; + padding: 10px 5px; } -table a{ - color: #333333; +table a { + color: var(--color-text-pri); } - /* ANIMATION */ -.fade{ - opacity: 0; +.fade { + opacity: 0; } -@keyframes fadeseq{ - 100% { - opacity: 1; - } +@keyframes fadeseq { + 100% { + opacity: 1; + } } -.fade{ - opacity: 0; +.fade { + opacity: 0; } -.fade{ - animation: fadeseq .3s forwards; +.fade { + animation: fadeseq 0.3s forwards; } -.fade:nth-child(2){ - animation-delay: .4s; +.fade:nth-child(2) { + animation-delay: 0.4s; } - /* LAYOUT */ -#container{ - align-items: stretch; - display: grid; - grid-column-gap: 20px; - grid-row-gap: 3vh; - grid-template-columns: 1fr; - grid-template-rows: 8vh auto; - justify-items: stretch; - margin-left: auto; - margin-right: auto; - margin-top: 5vh; - width: 60%; +#container { + align-items: stretch; + display: grid; + grid-column-gap: 20px; + grid-row-gap: 3vh; + grid-template-columns: 1fr; + grid-template-rows: 8vh auto; + justify-items: stretch; + margin-left: auto; + margin-right: auto; + margin-top: 5vh; + width: 60%; } - - /* SECTIONS */ -#header{ - border-bottom: 0px solid var(--color-text-acc); - z-index: 1; +#header { + border-bottom: 0px solid var(--color-text-acc); + z-index: 1; } -#apps_loop{ - border-bottom: 0px solid var(--color-text-acc); - display: grid; - grid-column-gap: 0px; - grid-row-gap: 0px; - grid-template-columns: 1fr 1fr 1fr 1fr; - grid-template-rows: 64px; - padding-bottom: var(--module-spacing); +#apps_loop { + border-bottom: 0px solid var(--color-text-acc); + display: grid; + grid-column-gap: 0px; + grid-row-gap: 0px; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: 64px; + padding-bottom: var(--module-spacing); } -.apps_icon{ - height: 64px; - margin-right: 1em; - padding-top: 15px; +.apps_icon { + height: 64px; + margin-right: 1em; + padding-top: 15px; } -.apps_icon span{ - font-size: 2.5em; - line-height: 3rem; +.apps_icon span { + font-size: 2.5em; + line-height: 3rem; } -.apps_item{ - display: flex; - flex-direction: row; - flex-wrap: wrap; - height: 64px; - margin: 0; +.apps_item { + display: flex; + flex-direction: row; + flex-wrap: wrap; + height: 64px; + margin: 0; } -.apps_text{ - display: flex; - flex-direction: column; - justify-content: center; - flex: 1; - overflow: hidden; +.apps_text { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; + overflow: hidden; } -.apps_text a{ - font-size: 1em; - font-weight: 500; - text-transform: uppercase; +.apps_text a { + font-size: 1em; + font-weight: 500; + text-transform: uppercase; } -.apps_text span{ - color: var(--color-text-acc); - font-size: 0.8em; - text-transform: uppercase; +.apps_text span { + color: var(--color-text-acc); + font-size: 0.8em; + text-transform: uppercase; } - -#links_loop{ - display: grid; - flex-wrap: nowrap; - grid-column-gap: 20px; - grid-row-gap: 0px; - grid-template-columns: 1fr 1fr 1fr 1fr; - grid-template-rows: auto; +#links_loop { + display: grid; + flex-wrap: nowrap; + grid-column-gap: 20px; + grid-row-gap: 0px; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: auto; } -#links_item{ - line-height: 1.5rem; - margin-bottom: 2em; - webkit-font-smoothing: antialiased; +#links_item { + line-height: 1.5rem; + margin-bottom: 2em; + webkit-font-smoothing: antialiased; } -#links_item h4{ - color: var(--color-text-acc); +#links_item h4 { + color: var(--color-text-acc); } -#links_item a{ - display: block; - line-height: 2; +#links_item a { + display: block; + line-height: 2; } - - - - - - - - - - - - - - /* MODAL */ - -#modal{ - overflow-y: auto; - bottom: 0; - left: 0; - opacity: 0; - pointer-events: none; - position: fixed; - right: 0; - top: 0; - transition: all 0.3s; - z-index: 20; +#modal { + overflow-y: auto; + bottom: 0; + left: 0; + opacity: 0; + pointer-events: none; + position: fixed; + right: 0; + top: 0; + transition: all 0.3s; + z-index: 20; } -#modal:target{ - opacity: 1; - pointer-events: auto; +#modal:target { + opacity: 1; + pointer-events: auto; } -#modal>div{ - background-color: #ffffff; - box-shadow: 0 14px 28px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.25); - margin-left: auto; - margin-right: auto; - padding: 2em; - margin-top: 5vh; - width: 50%; - display: flex; - flex-direction: column; +#modal > div { + background: hsl(from var(--color-background) h s calc(l + 5)); + box-shadow: 0 14px 28px rgba(0, 0, 0, 0.3), 0 15px 12px rgba(0, 0, 0, 0.25); + margin-left: auto; + margin-right: auto; + padding: 2em; + margin-top: 5vh; + width: 50%; + display: flex; + flex-direction: column; } -#modal h1{ - color: #333333; - font-size: 2em; +#modal h1 { + color: var(--color-text-acc); + font-size: 2em; } -#modal h2{ - margin-top:1.5em; +#modal h2 { + margin-top: 1.5em; } -#modal-header{ - display:flex; - justify-content: space-between; +#modal-header { + display: flex; + justify-content: space-between; } -#modal-footer{ - display:flex; - font-size:2em; - justify-content: flex-start; +#modal-footer { + display: flex; + font-size: 2em; + justify-content: flex-start; } -#modal-footer a{ - margin-right:0.25em; +#modal-footer a { + margin-right: 0.25em; } -.modal-close{ - color: #000000; - font-size: 1.5em; - text-align: center; - text-decoration: none; +.modal-close { + color: #000000; + font-size: 1.5em; + text-align: center; + text-decoration: none; } -.modal-close:hover{ - color: #000; +.modal-close:hover { + color: #000; } -#modal_init a{ - bottom: 1vh; - color: var(--color-text-acc); - left: 1vw; - position: fixed; +#modal_init a { + bottom: 1vh; + color: var(--color-text-acc); + left: 1vw; + position: fixed; } -#modal_init a:hover{ - color: var(--color-text-pri); +#modal_init a:hover { + color: var(--color-text-pri); } -#modal-theme{ - border-bottom: 0px solid var(--color-text-acc); - display: flex; - flex-wrap: wrap; - margin-bottom: 2em; +#modal-theme { + border-bottom: 0px solid var(--color-text-acc); + display: flex; + flex-wrap: wrap; + margin-bottom: 2em; } -#providers{ - margin-bottom: 2em; +#providers { + margin-bottom: 2em; } - /* THEMING */ -.theme-button{ - font-size: 0.8em; - margin: 2px; - width:128px; - line-height: 3em; - text-align: center; - text-transform: uppercase; +.theme-button { + font-size: 0.8em; + margin: 2px; + width: 128px; + line-height: 3em; + text-align: center; + text-transform: uppercase; } -.theme-blackboard{ - background-color: #000000; - border: 4px solid #5c5c5c; - color: #FFFDEA; +.theme-blackboard { + background-color: #000000; + border: 4px solid #5c5c5c; + color: #fffdea; } -.theme-gazette{ - background-color: #F2F7FF; - border: 4px solid #5c5c5c; - color: #000000; +.theme-gazette { + background-color: #f2f7ff; + border: 4px solid #5c5c5c; + color: #000000; } -.theme-espresso{ - background-color: #21211F; - border: 4px solid #4E4E4E; - color: #D1B59A; +.theme-espresso { + background-color: #21211f; + border: 4px solid #4e4e4e; + color: #d1b59a; } -.theme-cab{ - background-color: #FEED01; - border: 4px solid #424242; - color: #1F1F1F; +.theme-cab { + background-color: #feed01; + border: 4px solid #424242; + color: #1f1f1f; } -.theme-cloud{ - background-color: #f1f2f0; - border: 4px solid #35342f; - color: #37bbe4; +.theme-cloud { + background-color: #f1f2f0; + border: 4px solid #35342f; + color: #37bbe4; } -.theme-lime{ - background-color: #263238; - border: 4px solid #AABBC3; - color: #aeea00; +.theme-lime { + background-color: #263238; + border: 4px solid #aabbc3; + color: #aeea00; } -.theme-passion{ - background-color: #f5f5f5; - border: 4px solid #8e24aa; - color: #12005e; +.theme-passion { + background-color: #f5f5f5; + border: 4px solid #8e24aa; + color: #12005e; } -.theme-blues{ - background-color: #2B2C56; - border: 4px solid #6677EB; - color: #EFF1FC; +.theme-blues { + background-color: #2b2c56; + border: 4px solid #6677eb; + color: #eff1fc; } -.theme-chalk{ - background-color: #263238; - border: 4px solid #FF869A; - color: #AABBC3; +.theme-chalk { + background-color: #263238; + border: 4px solid #ff869a; + color: #aabbc3; } -.theme-tron{ - background-color: #242B33; - border: 4px solid #6EE2FF; - color: #EFFBFF; +.theme-tron { + background-color: #242b33; + border: 4px solid #6ee2ff; + color: #effbff; } -.theme-paper{ - background-color: #F8F6F1; - border: 4px solid #F5E1A4; - color: #4C432E; +.theme-paper { + background-color: #f8f6f1; + border: 4px solid #f5e1a4; + color: #4c432e; } - /* MEDIA QUERIES */ -@media screen and (max-width: 1260px) -{ - #container - { - align-items: stretch; - display: grid; - grid-column-gap: 10px; - grid-row-gap: 0px; - grid-template-columns: 1fr; - grid-template-rows: 80px auto; - justify-items: stretch; - margin-bottom: 1vh; - margin-left: auto; - margin-right: auto; - width: 90%; - } - - #apps_loop{ - grid-template-columns: 1fr 1fr 1fr; - width: 100vw; - } - - #links_loop { - grid-template-columns: 1fr 1fr 1fr; - } - - #modal>div{ - margin-left: auto; - margin-right: auto; - margin-top: 5vh; - width: 90%; - } -} - -@media screen and (max-width: 667px) -{ - html{ - font-size: calc(16px + 6 * ((100vw - 320px) / 680)); - } - - #container{ - align-items: stretch; - display: grid; - grid-column-gap: 20px; - grid-row-gap: 0px; - grid-template-columns: 1fr; - grid-template-rows: 80px auto; - justify-items: stretch; - margin-bottom: 1vh; - width: 90%; - } - - h1{ - font-size: 4em; - height: auto; - margin-bottom: 0em; - } - - h2{ - font-size: 1em; - height: auto; - margin-bottom: 0em; - } - - h3{ - font-size: 1em; - } - - #apps_loop{ - grid-column-gap: 0px; - grid-row-gap: 0px; - grid-template-columns: 1fr 1fr; - width: 100vw; - } - - .apps_icon{ - height: 64px; - margin-right: 0.8em; - padding-top: 14px; - } - - .apps_icon span{ - font-size: 2em; - line-height: 2.5rem; - } - - #links_loop{ - display: grid; - flex-wrap: nowrap; - grid-column-gap: 20px; - grid-row-gap: 0px; - grid-template-columns: 1fr 1fr; - grid-template-rows: auto; - } +@media screen and (max-width: 1260px) { + #container { + align-items: stretch; + display: grid; + grid-column-gap: 10px; + grid-row-gap: 0px; + grid-template-columns: 1fr; + grid-template-rows: 80px auto; + justify-items: stretch; + margin-bottom: 1vh; + margin-left: auto; + margin-right: auto; + width: 90%; + } + + #apps_loop { + grid-template-columns: 1fr 1fr 1fr; + width: 100vw; + } + + #links_loop { + grid-template-columns: 1fr 1fr 1fr; + } + + #modal > div { + margin-left: auto; + margin-right: auto; + margin-top: 5vh; + width: 90%; + } +} + +@media screen and (max-width: 667px) { + html { + font-size: calc(16px + 6 * ((100vw - 320px) / 680)); + } + + #container { + align-items: stretch; + display: grid; + grid-column-gap: 20px; + grid-row-gap: 0px; + grid-template-columns: 1fr; + grid-template-rows: 80px auto; + justify-items: stretch; + margin-bottom: 1vh; + width: 90%; + } + + h1 { + font-size: 4em; + height: auto; + margin-bottom: 0em; + } + + h2 { + font-size: 1em; + height: auto; + margin-bottom: 0em; + } + + h3 { + font-size: 1em; + } + + #apps_loop { + grid-column-gap: 0px; + grid-row-gap: 0px; + grid-template-columns: 1fr 1fr; + width: 100vw; + } + + .apps_icon { + height: 64px; + margin-right: 0.8em; + padding-top: 14px; + } + + .apps_icon span { + font-size: 2em; + line-height: 2.5rem; + } + + #links_loop { + display: grid; + flex-wrap: nowrap; + grid-column-gap: 20px; + grid-row-gap: 0px; + grid-template-columns: 1fr 1fr; + grid-template-rows: auto; + } } /* Small Screens */ @media only screen and (max-width: 400px) { - #app-address { - display: none; - } + #app-address { + display: none; + } } diff --git a/assets/js/data.js b/assets/js/data.js index 5f45926f..9a5008a5 100755 --- a/assets/js/data.js +++ b/assets/js/data.js @@ -1,16 +1,117 @@ -function fetchAndRender (name) { - fetch(name + '.json') - .then(response => response.json()) - .then(data => { - const mysource = document.getElementById(name + '-template').innerHTML; - const mytemplate = Handlebars.compile(mysource); - const myresult = mytemplate(data); - document.getElementById(name).innerHTML = myresult; - }); +let config = {} + +const renderTemplate = (fileName, data) => { + const src = document.getElementById(`${fileName}-template`).innerHTML + const template = Handlebars.compile(src) + const rendered = template({ ...data, labels: config.labels }) + + document.getElementById(fileName).innerHTML += rendered +} + +const fetchAndRender = async (fileName) => { + const res = await fetch(`${fileName}.json`) + const data = await res.json() + + if (fileName === 'apps' && config.useAppCategories) { + const categories = data.apps.reduce((acc, app) => { + if (!acc.includes(app.category)) acc.push(app.category) + return acc + }, []) + + const sortedData = categories.map((category) => { + return { + category, + apps: data.apps + .filter((app) => app.category === category) + .map((app) => { + return { + ...app, + url: + app.url.includes('http://') || app.url.includes('https://') + ? app.url + : `http://${app.url}`, + url_stripped: app.url.replace(/^\w+:\/\/(.+)\?.+/, '$1') + } + }) + } + }) + + sortedData.forEach((item) => renderTemplate(fileName, item)) + } else { + renderTemplate(fileName, data) + } + + return null +} + +const fetchUser = async () => { + const res = await fetch(oauth2UserInfoURL, { + credentials: 'include', + headers: { + 'Content-Type': 'application/json' + } + }) + + return await res.json() +} + +const fetchConfig = async () => { + const res = await fetch('config.json') + const data = await res.json() + + config = data + + for (const theme of Object.keys(config.themes)) { + const src = document.getElementById(`theme-button`).innerHTML + const template = Handlebars.compile(src) + const rendered = template({ theme }) + + document.getElementById('modal-theme').innerHTML += rendered + } + + if (config.backgroundImage && config.backgroundImage.length > 0) { + document.documentElement.style.setProperty( + 'background-image', + `url(${config.backgroundImage}` + ) + document.documentElement.style.setProperty('background-size', `cover`) + document.documentElement.style.setProperty('background-repeat', `no-repeat`) + document.documentElement.style.setProperty('background-attachment', `fixed`) + document.body.style.setProperty('background', `transparent`) + } + + return true } -document.addEventListener('DOMContentLoaded', () => { - fetchAndRender('apps'); - fetchAndRender('links'); - fetchAndRender('providers'); -}); +document.addEventListener('DOMContentLoaded', async () => { + await fetchConfig() + + if (config.hideSettings) { + document.getElementById('modal').style.display = 'none' + document.getElementById('modal_init').style.display = 'none' + } + + const user = config.useOauth2 ? await fetchUser() : null + + Handlebars.registerHelper('hasGroup', (groups) => { + if (!groups || groups.length === 0) return true + return user[config.userPermissionKey].some((g) => groups.includes(g)) + }) + + if (config.withApps) await fetchAndRender('apps') + + if (config.withLinks) await fetchAndRender('links') + + if (config.withSearch) { + await fetchAndRender('providers') + document.getElementById('search').style.display = 'block' + document.getElementById('provider-area').style.display = 'block' + } + + setValueFromLocalStorage('color-background') + setValueFromLocalStorage('color-text-pri') + setValueFromLocalStorage('color-text-acc') + + date() + greet() +}) diff --git a/assets/js/script.js b/assets/js/script.js index 516498ff..6b54ed2f 100755 --- a/assets/js/script.js +++ b/assets/js/script.js @@ -1,37 +1,42 @@ -function date() { - let currentDate = new Date(); - let dateOptions = { - weekday: "long", - year: "numeric", - month: "long", - day: "numeric" - }; - let date = currentDate.toLocaleDateString("en-GB", dateOptions); - document.getElementById("header_date").innerHTML = date; +const date = () => { + const currentDate = new Date() + const dateOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric' + } + const date = currentDate.toLocaleDateString(config.language, dateOptions) + document.getElementById('header_date').innerHTML = date } -function greet() { - let currentTime = new Date(); - let greet = Math.floor(currentTime.getHours() / 6); +const greet = async () => { + const currentTime = new Date() + const greet = Math.floor(currentTime.getHours() / 6) switch (greet) { case 0: - document.getElementById("header_greet").innerHTML = "Good night!"; - break; + document.getElementById('header_greet').innerHTML = config.greetings.night + break case 1: - document.getElementById("header_greet").innerHTML = "Good morning!"; - break; + document.getElementById('header_greet').innerHTML = + config.greetings.morning + break case 2: - document.getElementById("header_greet").innerHTML = "Good afternoon!"; - break; + document.getElementById('header_greet').innerHTML = + config.greetings.afternoon + break case 3: - document.getElementById("header_greet").innerHTML = "Good evening!"; - break; + document.getElementById('header_greet').innerHTML = + config.greetings.evening + break } -} - -function loadFunctions() { - date(); - greet(); -} + if (config.useOauth2Proxy) { + const user = await fetchUser() + document.getElementById( + 'header_greet' + ).innerHTML += `, ${user.preferredUsername}` + } + document.getElementById('header_greet').innerHTML += '!' +} diff --git a/assets/js/themer.js b/assets/js/themer.js index 9c19380d..66fb9cda 100755 --- a/assets/js/themer.js +++ b/assets/js/themer.js @@ -1,139 +1,49 @@ const setValue = (property, value) => { - if (value) { - document.documentElement.style.setProperty(`--${property}`, value); + if (!value) return - const input = document.querySelector(`#${property}`); - if (input) { - value = value.replace('px', ''); - input.value = value; - } - } -}; - -const setValueFromLocalStorage = property => { - let value = localStorage.getItem(property); - setValue(property, value); -}; + document.documentElement.style.setProperty(`--${property}`, value) -const setTheme = options => { - for (let option of Object.keys(options)) { - const property = option; - const value = options[option]; - - setValue(property, value); - localStorage.setItem(property, value); - } + const input = document.querySelector(`#${property}`) + if (input) { + value = value.replace('px', '') + input.value = value + } } -document.addEventListener('DOMContentLoaded', () => { - setValueFromLocalStorage('color-background'); - setValueFromLocalStorage('color-text-pri'); - setValueFromLocalStorage('color-text-acc'); -}); - -const dataThemeButtons = document.querySelectorAll('[data-theme]'); - -for (let i = 0; i < dataThemeButtons.length; i++) { - dataThemeButtons[i].addEventListener('click', () => { - const theme = dataThemeButtons[i].dataset.theme; - - switch (theme) { - case 'blackboard': - setTheme({ - 'color-background': '#1a1a1a', - 'color-text-pri': '#FFFDEA', - 'color-text-acc': '#5c5c5c' - }); - return; - - case 'gazette': - setTheme({ - 'color-background': '#F2F7FF', - 'color-text-pri': '#000000', - 'color-text-acc': '#5c5c5c' - }); - return; - - case 'espresso': - setTheme({ - 'color-background': '#21211F', - 'color-text-pri': '#D1B59A', - 'color-text-acc': '#4E4E4E' - }); - return; - - case 'cab': - setTheme({ - 'color-background': '#F6D305', - 'color-text-pri': '#1F1F1F', - 'color-text-acc': '#424242' - }); - return; - - case 'cloud': - setTheme({ - 'color-background': '#f1f2f0', - 'color-text-pri': '#35342f', - 'color-text-acc': '#37bbe4' - }); - return; - - case 'lime': - setTheme({ - 'color-background': '#263238', - 'color-text-pri': '#AABBC3', - 'color-text-acc': '#aeea00' - }); - return; +const setValueFromLocalStorage = (property) => { + console.log(config.defaultTheme) + const value = + localStorage.getItem(property) || + config.themes[config.defaultTheme][property] + setValue(property, value) +} - case 'white': - setTheme({ - 'color-background': '#ffffff', - 'color-text-pri': '#222222', - 'color-text-acc': '#dddddd' - }); - return; +const setTheme = (options) => { + for (const option of Object.keys(options)) { + const value = options[option] - case 'tron': - setTheme({ - 'color-background': '#242B33', - 'color-text-pri': '#EFFBFF', - 'color-text-acc': '#6EE2FF' - }); - return; - - case 'blues': - setTheme({ - 'color-background': '#2B2C56', - 'color-text-pri': '#EFF1FC', - 'color-text-acc': '#6677EB' - }); - return; - - case 'passion': - setTheme({ - 'color-background': '#f5f5f5', - 'color-text-pri': '#12005e', - 'color-text-acc': '#8e24aa' - }); - return; - - case 'chalk': - setTheme({ - 'color-background': '#263238', - 'color-text-pri': '#AABBC3', - 'color-text-acc': '#FF869A' - }); - return; - - case 'paper': - setTheme({ - 'color-background': '#F8F6F1', - 'color-text-pri': '#4C432E', - 'color-text-acc': '#AA9A73' - }); - return; + setValue(option, value) + localStorage.setItem(option, value) + } +} +const observer = new MutationObserver((mutationsList) => { + mutationsList.forEach((mutation) => { + if (mutation.type === 'childList') { + mutation.addedNodes.forEach((node) => { + if ( + node.nodeType === Node.ELEMENT_NODE && + node.hasAttribute('data-theme') + ) { + node.addEventListener('click', () => { + const theme = node.dataset.theme + + setTheme(config.themes[theme]) + }) } - }) -} \ No newline at end of file + }) + } + }) +}) + +observer.observe(document.body, { childList: true, subtree: true }) diff --git a/config.json b/config.json new file mode 100644 index 00000000..13822a89 --- /dev/null +++ b/config.json @@ -0,0 +1,85 @@ +{ + "language": "en-GB", + "greetings": { + "morning": "Good Morning", + "afternoon": "Good Afternoon", + "evening": "Good Evening", + "night": "Good Night" + }, + "useOauth2": false, + "useAppCategories": false, + "oauth2UserInfoURL": "", + "userPermissionKey": "", + "withApps": true, + "withLinks": true, + "withSearch": true, + "hideSettings": false, + "labels": { + "bookmarks": "Bookmarks", + "applications": "Applications" + }, + "backgroundImage": "", + "defaultTheme": "backboard", + "themes": { + "blackboard": { + "color-background": "#1a1a1a", + "color-text-pri": "#FFFDEA", + "color-text-acc": "#5c5c5c" + }, + "gazette": { + "color-background": "#F2F7FF", + "color-text-pri": "#000000", + "color-text-acc": "#5c5c5c" + }, + "espresso": { + "color-background": "#21211F", + "color-text-pri": "#D1B59A", + "color-text-acc": "#4E4E4E" + }, + "cab": { + "color-background": "#F6D305", + "color-text-pri": "#1F1F1F", + "color-text-acc": "#424242" + }, + "cloud": { + "color-background": "#f1f2f0", + "color-text-pri": "#35342f", + "color-text-acc": "#37bbe4" + }, + "lime": { + "color-background": "#263238", + "color-text-pri": "#AABBC3", + "color-text-acc": "#aeea00" + }, + "white": { + "color-background": "#ffffff", + "color-text-pri": "#222222", + "color-text-acc": "#dddddd" + }, + "tron": { + "color-background": "#242B33", + "color-text-pri": "#EFFBFF", + "color-text-acc": "#6EE2FF" + }, + "blues": { + "color-background": "#2B2C56", + "color-text-pri": "#EFF1FC", + "color-text-acc": "#6677EB" + }, + "passion": { + "color-background": "#f5f5f5", + "color-text-pri": "#12005e", + "color-text-acc": "#8e24aa" + }, + "chalk": { + "color-background": "#263238", + "color-text-pri": "#AABBC3", + "color-text-acc": "#FF869A" + }, + "paper": { + "color-background": "#F8F6F1", + "color-text-pri": "#4C432E", + "color-text-acc": "#AA9A73" + } + } +} diff --git a/fluffyv.dev b/fluffyv.dev new file mode 100644 index 00000000..11c7edc5 Binary files /dev/null and b/fluffyv.dev differ diff --git a/index.html b/index.html index bf85fda7..42dd31f7 100755 --- a/index.html +++ b/index.html @@ -1,127 +1,148 @@ - + SUI - - - - - - + + + + + + - - - + +
+ - + - - -
- -
- - +
+
+ + + + + + + + + - - - +