1
0
Fork 0
mirror of https://github.com/bastienwirtz/homer.git synced 2026-07-25 08:54:09 +00:00
This commit is contained in:
Finn Johannes Hartmann 2026-07-18 01:08:46 -07:00 committed by GitHub
commit 265eadc994
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 208 additions and 13 deletions

View file

@ -82,6 +82,14 @@
"dark"
],
"description": "One of 'auto', 'light', or 'dark'"
},
"density": {
"enum": [
"normal",
"compact",
"ultracompact"
],
"description": "Density of the dashboard, one of 'normal', 'compact', or 'ultracompact'"
}
},
"title": "Defaults"

View file

@ -44,6 +44,12 @@
@updated="vlayout = $event"
/>
<DensityToggle
name="homer-density"
:default-value="configDensity"
@updated="onDensityUpdated"
/>
<SearchInput
class="navbar-item is-inline-block-mobile"
:hotkey="searchHotkey()"
@ -114,6 +120,7 @@ import SearchInput from "./components/SearchInput.vue";
import SettingToggle from "./components/SettingToggle.vue";
import DarkMode from "./components/DarkMode.vue";
import DynamicTheme from "./components/DynamicTheme.vue";
import DensityToggle from "./components/DensityToggle.vue";
import defaultConfig from "./assets/defaults.yml?raw";
@ -129,6 +136,7 @@ export default {
SettingToggle,
DarkMode,
DynamicTheme,
DensityToggle,
},
provide() {
return {
@ -153,6 +161,14 @@ export default {
configurationNeeded: function () {
return (this.loaded && !this.services) || this.configNotFound;
},
configDensity: function() {
// Lese die Density aus der Config, Fallback auf "normal"
const density = this.config?.defaults?.density || "normal";
// Validiere den Wert
const validDensities = ["normal", "compact", "ultracompact"];
return validDensities.includes(density) ? density : "normal";
},
},
created: async function () {
this.buildDashboard();
@ -189,6 +205,9 @@ export default {
this.config = merge(defaults, config);
this.services = this.config.services;
const savedDensity = localStorage.getItem("homer-density") || localStorage["homer-density"];
document.documentElement.setAttribute("data-density", savedDensity);
document.title =
this.config.documentTitle ||
[this.config.title, this.config.subtitle].filter(Boolean).join(" | ");

View file

@ -9,6 +9,54 @@
}
@layer base {
/* =========================================================
Normal (current Style)
========================================================= */
html[data-density='normal'] {
--density-card-height: 85px;
--density-card-padding: 1.3rem;
--density-title-size: 1.1em;
--density-subtitle-size: 0.9em;
--density-group-spacing: 1.2rem;
--density-column-gap: 0.75rem;
--density-tag-offset: 1rem;
--density-icon-size: 32px;
--density-icon-box: 48px;
}
/* =========================================================
Compact
========================================================= */
html[data-density='compact'] {
--density-card-height: 63.75px;
--density-card-padding: 0.9rem;
--density-title-size: 0.825em;
--density-subtitle-size: 0.675em;
--density-group-spacing: 0.9rem;
--density-column-gap: 0.5625rem;
--density-tag-offset: 0.75rem;
--density-icon-size: 24px;
--density-icon-box: 36px;
}
/* =========================================================
Ultra Compact
========================================================= */
html[data-density='ultracompact'] {
--density-card-height: 42.5px;
--density-card-padding: 0.65rem;
--density-title-size: 0.55em;
--density-subtitle-size: 0.45em;
--density-group-spacing: 0.6rem;
--density-column-gap: 0.375rem;
--density-tag-offset: 0.5rem;
--density-icon-size: 16px;
--density-icon-box: 24px;
}
html,
body,
body #app-mount,
@ -35,6 +83,7 @@
a {
color: var(--link);
&:hover {
color: var(--link-hover);
}
@ -44,6 +93,7 @@
font-weight: 500;
color: var(--text-title);
}
.subtitle {
color: var(--text-subtitle);
}
@ -51,10 +101,15 @@
.card {
background-color: var(--card-background);
box-shadow: 0 2px 15px 0 var(--card-shadow);
&:hover {
background-color: var(--card-background);
}
transition: all 0.2s ease-in-out;
height: 100%;
}
.component-error .card {
border: 1px solid rgba(255, 33, 33, 0.664);
background-color: rgba(255, 58, 58, 0.24);
@ -128,6 +183,7 @@
.logo {
float: left;
i {
vertical-align: top;
padding: 8px 15px;
@ -141,22 +197,26 @@
}
}
}
.navbar {
background-color: var(--highlight-secondary);
a {
color: var(--text-header);
padding: 8px 12px;
&:hover,
&:focus {
color: var(--text-header);
background-color: var(--highlight-hover);
}
}
.navbar-menu {
background-color: inherit;
}
}
.navbar-end {
text-align: right;
}
@ -168,13 +228,14 @@
h2 {
padding-bottom: 0px;
@include ellipsis();
i {
color: var(--highlight-primary);
}
}
.title {
font-size: 1.1em;
font-size: var(--density-title-size);
line-height: 1.3em;
font-weight: 500;
margin-bottom: 3px;
@ -182,13 +243,13 @@
}
.subtitle {
font-size: 0.9em;
font-size: var(--density-subtitle-size);
font-weight: 300;
@include ellipsis();
}
.container {
padding: 1.2rem 0.75rem;
padding: var(--density-group-spacing) var(--density-column-gap);
}
.message {
@ -221,7 +282,7 @@
.tag {
color: #ffffff;
position: absolute;
bottom: 1rem;
bottom: var(--density-tag-offset);
right: -0.2rem;
width: 3px;
overflow: hidden;
@ -263,13 +324,13 @@
}
.card-content {
height: 85px;
padding: 1.3rem;
height: var(--density-card-height);
padding: var(--density-card-padding);
}
.layout-vertical {
h2.group-title {
padding-bottom: 0.75rem;
padding-bottom: var(--density-group-spacing);
}
.card {
@ -313,6 +374,7 @@
.search-bar {
position: relative;
display: inline-block;
input {
border: none;
background-color: var(--highlight-hover);
@ -369,4 +431,4 @@
.group-logo {
float: left;
}
}
}

View file

@ -15,6 +15,8 @@ defaults:
layout: columns
# auto, light, dark
colorTheme: auto
# normal, compact, ultracompact
density: normal
theme: default
colors: ~

View file

@ -0,0 +1,92 @@
<template>
<a
class="navbar-item is-inline-block-mobile"
@click.prevent="cycleDensity()"
:title="densityLabel"
>
<span>
<i :class="['fas', 'fa-fw', densityIcon]"></i>
</span>
</a>
</template>
<script>
export default {
name: "DensityToggle",
props: {
name: {
type: String,
default: "density",
},
defaultValue: {
type: String,
default: "normal",
validator: (value) => ["normal", "compact", "ultracompact"].includes(value),
},
},
emits: ["updated"],
data() {
return {
densities: ["normal", "compact", "ultracompact"],
currentDensity: "normal",
icons: {
normal: "fa-th-large",
compact: "fa-th",
ultracompact: "fa-bars",
},
labels: {
normal: "Normal density",
compact: "Compact density",
ultracompact: "Ultra compact density",
},
};
},
computed: {
densityIcon() {
return this.icons[this.currentDensity] || "fa-th-large";
},
densityLabel() {
return this.labels[this.currentDensity] || "Normal density";
},
},
created() {
this.initializeDensity();
},
mounted() {
this.applyDensity(this.currentDensity);
},
methods: {
initializeDensity() {
let density = this.defaultValue;
// Priority: localStorage > defaultValue (from config.yml)
if (this.name && localStorage[this.name]) {
const stored = localStorage[this.name];
if (this.densities.includes(stored)) {
density = stored;
}
}
this.currentDensity = density;
console.log(`[DensityToggle] Initialized with: ${this.currentDensity}`);
},
applyDensity(density) {
document.documentElement.setAttribute("data-density", density);
this.$emit("updated", density);
},
cycleDensity() {
const currentIndex = this.densities.indexOf(this.currentDensity);
const nextIndex = (currentIndex + 1) % this.densities.length;
const newDensity = this.densities[nextIndex];
this.currentDensity = newDensity;
if (this.name) {
localStorage[this.name] = newDensity;
}
this.applyDensity(newDensity);
},
},
};
</script>

View file

@ -2,7 +2,10 @@
<h2 :class="group.class">
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
<div v-else-if="group.logo" class="group-logo media-left">
<figure class="image is-48x48">
<figure
class="image"
style="width: var(--density-icon-box); height: var(--density-icon-box);"
>
<img :src="group.logo" :alt="`${group.name} logo`" />
</figure>
</div>

View file

@ -6,13 +6,22 @@
<div :class="mediaClass">
<slot name="icon">
<div v-if="item.logo" class="media-left">
<figure class="image is-48x48">
<figure
class="image"
style="width: var(--density-icon-box); height: var(--density-icon-box);"
>
<img :src="item.logo" :alt="`${item.name} logo`" />
</figure>
</div>
<div v-if="item.icon" class="media-left">
<figure class="image is-48x48">
<i style="font-size: 32px" :class="['fa-fw', item.icon]"></i>
<figure
class="image"
style="width: var(--density-icon-box); height: var(--density-icon-box);"
>
<i
style="font-size: var(--density-icon-size)"
:class="['fa-fw', item.icon]"
></i>
</figure>
</div>
</slot>

View file

@ -9,7 +9,7 @@
<div class="card-content">
<div class="media">
<div v-if="icon" class="media-left" :class="item.background">
<figure class="image is-48x48">
<figure class="image" style="width: var(--density-icon-box); height: var(--density-icon-box);">
<img
:src="`https://openweathermap.org/img/wn/${icon}@2x.png`"
:alt="conditions"