Skip to content

Commit 4bad014

Browse files
fix(web-ui): modernize UI
1 parent 76b3a85 commit 4bad014

19 files changed

Lines changed: 3529 additions & 456 deletions

docs/contributing.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Read our contribution guide in our organization level
44

55
## Recommended Tools
66

7-
| Tool | Description |
7+
| Tool | Description |
88
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
99
| <a href="https://www.jetbrains.com/clion/"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/CLion_icon.svg" width="30" height="30"></a><br>CLion | Recommended IDE for C and C++ development. Free for non-commercial use. |
1010

@@ -16,6 +16,7 @@ Read our contribution guide in our organization level
1616
* [EJS](https://www.npmjs.com/package/vite-plugin-ejs) is used as a templating system for the pages
1717
(check `template_header.html` and `template_header_main.html`).
1818
* The Style System is provided by [Bootstrap](https://getbootstrap.com).
19+
* Icons are provided by [Lucide](https://lucide.dev) and [Simple Icons](https://simpleicons.org).
1920
* The JS framework used by the more interactive pages is [Vus.js](https://vuejs.org).
2021

2122
#### Building

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
"type": "module",
1111
"dependencies": {
1212
"@lizardbyte/shared-web": "2025.922.181114",
13+
"date-fns": "4.1.0",
14+
"lucide-vue-next": "0.563.0",
1315
"marked": "17.0.1",
1416
"vue": "3.5.27",
15-
"vue-i18n": "11.2.8"
17+
"vue-i18n": "11.2.8",
18+
"vue3-simple-icons": "15.6.0"
1619
},
1720
"devDependencies": {
1821
"@codecov/vite-plugin": "1.9.1",

src/confighttp.cpp

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,26 @@ namespace confighttp {
387387
response->write(content, headers);
388388
}
389389

390+
/**
391+
* @brief Get the featured apps page.
392+
* @param response The HTTP response object.
393+
* @param request The HTTP request object.
394+
*/
395+
void getFeaturedPage(resp_https_t response, req_https_t request) {
396+
if (!authenticate(response, request)) {
397+
return;
398+
}
399+
400+
print_req(request);
401+
402+
std::string content = file_handler::read_file(WEB_DIR "featured.html");
403+
SimpleWeb::CaseInsensitiveMultimap headers;
404+
headers.emplace("Content-Type", "text/html; charset=utf-8");
405+
headers.emplace("X-Frame-Options", "DENY");
406+
headers.emplace("Content-Security-Policy", "frame-ancestors 'none';");
407+
response->write(content, headers);
408+
}
409+
390410
/**
391411
* @brief Get the password page.
392412
* @param response The HTTP response object.
@@ -955,9 +975,6 @@ namespace confighttp {
955975
* @api_examples{/api/covers/9999 | GET| null}
956976
*/
957977
void getCover(resp_https_t response, req_https_t request) {
958-
if (!check_content_type(response, request, "application/json")) {
959-
return;
960-
}
961978
if (!authenticate(response, request)) {
962979
return;
963980
}
@@ -986,6 +1003,13 @@ namespace confighttp {
9861003
// This handles extension validation, PNG signature validation, and path resolution
9871004
std::string validated_path = proc::validate_app_image_path(app_image_path);
9881005

1006+
// Check if we got the default image path (means validation failed or no image configured)
1007+
if (validated_path == DEFAULT_APP_IMAGE_PATH) {
1008+
BOOST_LOG(debug) << "Application at index " << index << " does not have a valid cover image";
1009+
not_found(response, request, "Cover image not found");
1010+
return;
1011+
}
1012+
9891013
// Open and stream the validated file
9901014
std::ifstream in(validated_path, std::ios::binary);
9911015
if (!in) {
@@ -1410,6 +1434,7 @@ namespace confighttp {
14101434
server.resource["^/apps/?$"]["GET"] = getAppsPage;
14111435
server.resource["^/clients/?$"]["GET"] = getClientsPage;
14121436
server.resource["^/config/?$"]["GET"] = getConfigPage;
1437+
server.resource["^/featured/?$"]["GET"] = getFeaturedPage;
14131438
server.resource["^/password/?$"]["GET"] = getPasswordPage;
14141439
server.resource["^/welcome/?$"]["GET"] = getWelcomePage;
14151440
server.resource["^/troubleshooting/?$"]["GET"] = getTroubleshootingPage;
Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<nav class="navbar navbar-light navbar-expand-lg navbar-background header">
2+
<nav class="navbar navbar-expand-lg navbar-sunshine">
33
<div class="container-fluid">
44
<a class="navbar-brand" href="./" title="Sunshine">
55
<img src="/images/logo-sunshine-45.png" height="45" alt="Sunshine">
@@ -11,22 +11,46 @@
1111
<div class="collapse navbar-collapse" id="navbarSupportedContent">
1212
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
1313
<li class="nav-item">
14-
<a class="nav-link" href="./"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
14+
<a class="nav-link" href="./">
15+
<Home :size="18" class="icon"></Home>
16+
{{ $t('navbar.home') }}
17+
</a>
1518
</li>
1619
<li class="nav-item">
17-
<a class="nav-link" href="./pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
20+
<a class="nav-link" href="./pin">
21+
<Lock :size="18" class="icon"></Lock>
22+
{{ $t('navbar.pin') }}
23+
</a>
1824
</li>
1925
<li class="nav-item">
20-
<a class="nav-link" href="./apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
26+
<a class="nav-link" href="./apps">
27+
<Layers :size="18" class="icon"></Layers>
28+
{{ $t('navbar.applications') }}
29+
</a>
2130
</li>
2231
<li class="nav-item">
23-
<a class="nav-link" href="./config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
32+
<a class="nav-link" href="./featured">
33+
<Star :size="18" class="icon"></Star>
34+
{{ $t('navbar.featured') }}
35+
</a>
2436
</li>
2537
<li class="nav-item">
26-
<a class="nav-link" href="./password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
38+
<a class="nav-link" href="./config">
39+
<Settings :size="18" class="icon"></Settings>
40+
{{ $t('navbar.configuration') }}
41+
</a>
2742
</li>
2843
<li class="nav-item">
29-
<a class="nav-link" href="./troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
44+
<a class="nav-link" href="./password">
45+
<Shield :size="18" class="icon"></Shield>
46+
{{ $t('navbar.password') }}
47+
</a>
48+
</li>
49+
<li class="nav-item">
50+
<a class="nav-link" href="./troubleshooting">
51+
<Info :size="18" class="icon"></Info>
52+
{{ $t('navbar.troubleshoot') }}
53+
</a>
3054
</li>
3155
<li class="nav-item">
3256
<ThemeToggle/>
@@ -38,10 +62,20 @@
3862
</template>
3963

4064
<script>
65+
import { Home, Lock, Layers, Star, Settings, Shield, Info } from 'lucide-vue-next'
4166
import ThemeToggle from './ThemeToggle.vue'
4267
4368
export default {
44-
components: { ThemeToggle },
69+
components: {
70+
ThemeToggle,
71+
Home,
72+
Lock,
73+
Layers,
74+
Star,
75+
Settings,
76+
Shield,
77+
Info
78+
},
4579
created() {
4680
console.log("Header mounted!")
4781
},
@@ -53,34 +87,8 @@ export default {
5387
</script>
5488

5589
<style>
56-
.navbar-background {
57-
background-color: #ffc400
58-
}
59-
60-
.header .nav-link {
61-
color: rgba(0, 0, 0, .65) !important;
62-
}
63-
64-
.header .nav-link.active {
65-
color: rgb(0, 0, 0) !important;
66-
font-weight: 500;
67-
}
68-
69-
.header .nav-link:hover {
70-
color: rgb(0, 0, 0) !important;
71-
font-weight: 500;
72-
}
73-
74-
.header .navbar-toggler {
75-
color: rgba(var(--bs-dark-rgb), .65) !important;
76-
border: var(--bs-border-width) solid rgba(var(--bs-dark-rgb), 0.15) !important;
77-
}
78-
79-
.header .navbar-toggler-icon {
80-
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
81-
}
82-
83-
.form-control::placeholder {
84-
opacity: 0.5;
90+
/* Navbar toggler icon for dark text on light background */
91+
.navbar-sunshine .navbar-toggler-icon {
92+
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
8593
}
8694
</style>
Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,59 @@
11
<template>
2-
<div class="card p-2">
2+
<div class="card">
33
<div class="card-body">
44
<h2>{{ $t('resource_card.resources') }}</h2>
5-
<br>
65
<p>{{ $t('resource_card.resources_desc') }}</p>
7-
<div class="card-group p-4 align-items-center">
8-
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">
9-
{{ $t('resource_card.lizardbyte_website') }}</a>
10-
<a class="btn btn-primary m-1" href="https://app.lizardbyte.dev/discord" target="_blank">
11-
<i class="fab fa-fw fa-discord"></i> Discord</a>
12-
<a class="btn btn-secondary m-1" href="https://github.com/orgs/LizardByte/discussions" target="_blank">
13-
<i class="fab fa-fw fa-github"></i> {{ $t('resource_card.github_discussions') }}</a>
6+
<div class="d-flex flex-wrap gap-2 mt-4">
7+
<a class="btn btn-success" href="https://app.lizardbyte.dev" target="_blank">
8+
<Globe :size="18" class="icon"></Globe>
9+
{{ $t('resource_card.lizardbyte_website') }}
10+
</a>
11+
<a class="btn btn-primary" href="https://app.lizardbyte.dev/discord" target="_blank">
12+
<SimpleIcon icon="Discord" :size="18" class="icon"></SimpleIcon>
13+
Discord
14+
</a>
15+
<a class="btn btn-secondary" href="https://github.com/orgs/LizardByte/discussions" target="_blank">
16+
<SimpleIcon icon="GitHub" :size="18" class="icon"></SimpleIcon>
17+
{{ $t('resource_card.github_discussions') }}
18+
</a>
1419
</div>
1520
</div>
1621
</div>
1722
<!-- Legal -->
18-
<div class="card p-2 mt-4">
23+
<div class="card mt-4">
1924
<div class="card-body">
2025
<h2>{{ $t('resource_card.legal') }}</h2>
21-
<br>
2226
<p>{{ $t('resource_card.legal_desc') }}</p>
23-
<div class="card-group p-4 align-items-center">
24-
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
27+
<div class="d-flex flex-wrap gap-2 mt-4">
28+
<a class="btn btn-danger" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
2529
target="_blank">
26-
<i class="fas fa-fw fa-file-alt"></i> {{ $t('resource_card.license') }}</a>
27-
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
30+
<FileText :size="18" class="icon"></FileText>
31+
{{ $t('resource_card.license') }}
32+
</a>
33+
<a class="btn btn-danger" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
2834
target="_blank">
29-
<i class="fas fa-fw fa-exclamation"></i> {{ $t('resource_card.third_party_notice') }}</a>
35+
<AlertCircle :size="18" class="icon"></AlertCircle>
36+
{{ $t('resource_card.third_party_notice') }}
37+
</a>
3038
</div>
3139
</div>
3240
</div>
3341
</template>
42+
43+
<script>
44+
import {
45+
AlertCircle,
46+
FileText,
47+
Globe,
48+
} from 'lucide-vue-next'
49+
import SimpleIcon from './SimpleIcon.vue'
50+
51+
export default {
52+
components: {
53+
SimpleIcon,
54+
AlertCircle,
55+
FileText,
56+
Globe,
57+
}
58+
}
59+
</script>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<template>
2+
<component
3+
v-if="iconComponent"
4+
:is="iconComponent"
5+
:size="size"
6+
:class="className"
7+
/>
8+
</template>
9+
10+
<script setup>
11+
import { computed } from 'vue'
12+
import { GitHubIcon, DiscordIcon } from 'vue3-simple-icons'
13+
14+
const props = defineProps({
15+
icon: {
16+
type: String,
17+
required: true,
18+
default: 'GitHub'
19+
},
20+
size: {
21+
type: [Number, String],
22+
default: 24
23+
},
24+
className: {
25+
type: String,
26+
default: ''
27+
}
28+
})
29+
30+
// Map icon names to actual components
31+
const iconMap = {
32+
'GitHub': GitHubIcon,
33+
'Discord': DiscordIcon,
34+
}
35+
36+
const iconComponent = computed(() => {
37+
const component = iconMap[props.icon]
38+
if (!component) {
39+
console.error(`Icon "${props.icon}" not found in SimpleIcon mapping`)
40+
return null
41+
}
42+
return component
43+
})
44+
</script>

0 commit comments

Comments
 (0)