"timezone": "UTC",
"useBrowserLanguage": true,
"language": "en",
+ "darkModeAutoDetect": true,
+ "useDarkMode": false,
+ "background": "gradient",
+ "monochromeBackgroundColor": "#ececec",
}
}
const weekDays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
</script>
-<div class="datepicker">
+<div class="datepicker glassLight">
<div class="datepicker-header">
<button type="button" class="btn btnLeftRight" onclick={() => changeMonth(-1)}><</button>
<div class="date-selectors">
.datepicker {
display: inline-block;
font-family: Arial, sans-serif;
- border: 1px solid #ececec77;
+ /* border: 1px solid #ececec77; */
border-radius: 8px;
/* overflow: hidden; */
/* width: 300px; */
box-sizing: border-box;
- backdrop-filter: blur(8px) saturate(150%);
- background-color: rgba(219, 219, 219, 0.45);
+ /* backdrop-filter: blur(8px) saturate(150%);
+ background-color: rgba(219, 219, 219, 0.45); */
}
.datepicker-header {
display: flex;
<div class="search d-flex flex-column">
<form onsubmit={searchForString} class="input-group">
<button
- class="btnSearchPopover btn btn-outline-secondary"
+ class="btnSearchPopover btn btn-outline-secondary glassLight"
data-bs-toggle="popover"
data-bs-title="Suche"
data-bs-content="Du kannst nach <b>Text</b>, <b>Dateinamen</b> und <b>Tags</b> suchen.<br>
setTimeout(() => (showTagDropdown = false), 150);
}}
/>
- <button class="btn btn-outline-secondary" type="submit" id="search-button">
+ <button class="btn btn-outline-secondary glassLight" type="submit" id="search-button">
{#if $isSearching}
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{:else}
{/if}
</form>
{#if showTagDropdown}
- <div class="searchTagDropdown">
+ <div class="searchTagDropdown glass">
{#if filteredTags.length === 0}
<em style="padding: 0.2rem;">Kein Tag gefunden...</em>
{:else}
{/if}
</div>
{/if}
- <div class="list-group flex-grow-1 mb-2">
+ <div class="list-group flex-grow-1 mb-2 glassLight">
{#if $searchResults.length > 0}
{#each $searchResults as result}
<button
.searchTagDropdown {
position: absolute;
- background-color: white;
+ /* background-color: white; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
left: 60px;
margin-top: 38px;
max-height: 200px;
- overflow-y: scroll;
+ overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
border-top-right-radius: 0;
overflow-y: auto;
min-height: 250px;
- backdrop-filter: blur(8px) saturate(150%);
+ /* backdrop-filter: blur(8px) saturate(150%);
background-color: rgba(219, 219, 219, 0.45);
- border: 1px solid #ececec77;
+ border: 1px solid #ececec77; */
}
.input-group {
);
}
+function generateNeonMesh(dark) {
+ /* const baseColors = ['#ff00ff', '#00ffff', '#ffea00', '#ff0080', '#00ff80', '#ff4500']; */
+ const baseColors = ["#ff00ff", "#00ffff", "#ffea00", "#ff0080", "#00ff80", "#ff4500",
+ "#ff1493", "#00ffcc", "#ff3333", "#66ff66", "#3399ff", "#ffcc00",
+ "#ff6666", "#00ccff", "#cc33ff", "#33ffcc", "#ffff99", "#ff99ff",
+ "#99ff99", "#66ccff", "#ff9900", "#ff0066", "#66ffcc", "#ff33cc",
+ "#99ccff"]
+ const numGradients = Math.floor(Math.random() * 3) + 3; // 3–5 Radial Gradients
+ let gradients = [];
+
+ for (let i = 0; i < numGradients; i++) {
+ const baseColor = baseColors[Math.floor(Math.random() * baseColors.length)];
+ const alpha = Math.random() * 0.4 + 0.1; // random alpha between 0.1 and 0.5
+
+ // convert hex color to rgba
+ const hex = baseColor.substring(1);
+ const r = parseInt(hex.substring(0, 2), 16);
+ const g = parseInt(hex.substring(2, 4), 16);
+ const b = parseInt(hex.substring(4, 6), 16);
+ const color = `rgba(${r}, ${g}, ${b}, ${alpha})`;
+
+ const x = Math.floor(Math.random() * 100);
+ const y = Math.floor(Math.random() * 100);
+ const size = Math.floor(Math.random() * 30) + 30; // 30–60%
+ gradients.push(`radial-gradient(circle at ${x}% ${y}%, ${color}, transparent ${size}%)`);
+ }
+
+ document.querySelector('.background').style.background = dark ? gradients.join(', ') + ', #111' : gradients;
+ document.querySelector('.background').style.backgroundBlendMode = 'screen';
+}
+
function loadFlagEmoji(language) {
return json[language] || '';
}
-export { formatBytes, sameDate, loadFlagEmoji };
+export { formatBytes, sameDate, loadFlagEmoji, generateNeonMesh };
export let alwaysShowSidenav = writable(true);
import { API_URL } from '$lib/APIurl.js';
import { tags } from '$lib/tagStore.js';
import TagModal from '$lib/TagModal.svelte';
- import { alwaysShowSidenav, loadFlagEmoji } from '$lib/helpers.js';
+ import { alwaysShowSidenav, generateNeonMesh, loadFlagEmoji } from '$lib/helpers.js';
import { templates } from '$lib/templateStore';
import {
faRightFromBracket,
faTriangleExclamation,
faTrash,
faCopy,
- faCheck
+ faCheck,
+ faSun,
+ faMoon
} from '@fortawesome/free-solid-svg-icons';
import Tag from '$lib/Tag.svelte';
import SelectTimezone from '$lib/SelectTimezone.svelte';
.get(API_URL + '/users/getUserSettings')
.then((response) => {
$settings = response.data;
+ $tempSettings = JSON.parse(JSON.stringify($settings));
aLookBackYears = $settings.aLookBackYears.toString();
updateLanguage();
+
+ // set background
+ setBackground();
})
.catch((error) => {
console.error(error);
);
function updateLanguage() {
- console.log('updateLanguage()');
if ($settings.useBrowserLanguage) {
let browserLanguage = tolgeesMatchForBrowserLanguage();
$tolgee.changeLanguage(
return '';
}
+ function setBackground() {
+ if ($settings.background === 'monochrome') {
+ document.querySelector('.background').style.background = '';
+ document.body.style.backgroundColor = $settings.monochromeBackgroundColor;
+ } else if ($settings.background === 'gradient') {
+ document.body.style.backgroundColor = '';
+ generateNeonMesh();
+ }
+ }
+
let isSaving = $state(false);
function saveUserSettings() {
if (isSaving) return;
// update language
updateLanguage();
+ // set background
+ setBackground();
+
// show toast
const toast = new bootstrap.Toast(document.getElementById('toastSuccessSaveSettings'));
toast.show();
</script>
<div class="d-flex flex-column h-100">
- <nav class="navbar navbar-expand-lg bg-body-tertiary">
+ <nav class="navbar navbar-expand-lg glass">
<div class="row w-100">
<div class="col-lg-4 col-sm-5 col d-flex flex-row justify-content-start align-items-center">
{#if !$alwaysShowSidenav}
class="modal-dialog modal-dialog-scrollable modal-dialog-centered modal-xl modal-fullscreen-sm-down"
>
<!-- -->
- <div class="modal-content shadow-lg">
+ <div class="modal-content shadow-lg glass">
<div class="modal-header">
<h1>{$t('settings.title')}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<div id="appearance">
<h3 class="text-primary">🎨 Aussehen</h3>
<div id="lightdark">
- <h5>Light/Dark-Mode</h5>
- Bla<br />
- blub <br />
- bla <br />
- blub <br />
+ {#if $tempSettings.darkModeAutoDetect !== $settings.darkModeAutoDetect || $tempSettings.useDarkMode !== $settings.useDarkMode}
+ <div class="unsaved-changes" transition:slide></div>
+ {/if}
+ <h5>Light-/Dark-Mode</h5>
+ <div class="form-check mt-2">
+ <input
+ class="form-check-input"
+ type="radio"
+ name="darkMode"
+ id="darkModeAutoTrue"
+ value={true}
+ bind:group={$tempSettings.darkModeAutoDetect}
+ />
+ <label class="form-check-label" for="darkModeAutoTrue">
+ Light-/Dark-Mode automatisch erkennen (aktuell:
+ {#if window.matchMedia('(prefers-color-scheme: dark)').matches}
+ <b>Dark <Fa icon={faMoon} /></b>
+ {:else}
+ <b>Light <Fa icon={faSun} /></b>
+ {/if})
+ </label>
+ </div>
+ <div class="form-check mt-2">
+ <input
+ class="form-check-input"
+ type="radio"
+ name="darkMode"
+ id="darkModeAutoFalse"
+ value={false}
+ bind:group={$tempSettings.darkModeAutoDetect}
+ />
+ <label class="form-check-label" for="darkModeAutoFalse">
+ Light-/Dark-Mode manuell festlegen
+ </label>
+ {#if $tempSettings.darkModeAutoDetect === false}
+ <div class="form-check form-switch d-flex flex-row ps-0" transition:slide>
+ <label class="form-check-label me-5" for="darkModeSwitch">
+ <Fa icon={faSun} />
+ </label>
+ <input
+ class="form-check-input"
+ bind:checked={$tempSettings.useDarkMode}
+ type="checkbox"
+ role="switch"
+ id="darkModeSwitch"
+ />
+ <label class="form-check-label ms-2" for="darkModeSwitch">
+ <Fa icon={faMoon} />
+ </label>
+ </div>
+ {/if}
+ </div>
</div>
<div id="background">
+ {#if $tempSettings.background !== $settings.background || $tempSettings.monochromeBackgroundColor !== $settings.monochromeBackgroundColor}
+ <div class="unsaved-changes" transition:slide></div>
+ {/if}
+
<h5>Hintergrund</h5>
- <div class="d-flex flex-row justify-content-start">
- <label for="trianglifyOpacity" class="form-label"
- >Transparenz der Dreiecke</label
- >
+ <div class="form-check mt-2">
<input
- bind:value={$trianglifyOpacity}
- type="range"
- class="mx-3 form-range"
- id="trianglifyOpacity"
- min="0"
- max="1"
- step="0.01"
+ class="form-check-input"
+ type="radio"
+ name="background"
+ id="background_gradient"
+ value={'gradient'}
+ bind:group={$tempSettings.background}
/>
+ <label class="form-check-label" for="background_gradient">
+ Farbverlauf (wird bei jedem Seitenaufruf neu generiert)
+ </label>
+ </div>
+ <div class="form-check mt-2">
<input
- bind:value={$trianglifyOpacity}
- type="number"
- id="trianglifyOpacityNumber"
+ class="form-check-input"
+ type="radio"
+ name="background"
+ id="background_monochrome"
+ value={'monochrome'}
+ bind:group={$tempSettings.background}
/>
+ <label class="form-check-label" for="background_monochrome"> Einfarbig </label>
+ {#if $tempSettings.background === 'monochrome'}
+ <input
+ transition:slide
+ class="form-control form-control-color"
+ bind:value={$tempSettings.monochromeBackgroundColor}
+ type="color"
+ />
+ {/if}
</div>
</div>
</div>
}
.modal-content {
- backdrop-filter: blur(10px) saturate(150%);
+ /* backdrop-filter: blur(10px) saturate(150%);
+ background-color: rgba(208, 208, 208, 0.61); */
+
/* background-color: rgba(43, 56, 78, 0.75); */
- background-color: rgba(208, 208, 208, 0.61);
/* color: rgb(22, 22, 22); */
}
<!-- Center -->
<div class="d-flex flex-column pt-4 px-4 flex-grow-1" id="middle">
<!-- Input-Area -->
- <div class="d-flex flex-row textAreaHeader">
+ <div class="d-flex flex-row textAreaHeader glassLight">
<div class="flex-fill textAreaDate">
{new Date(
Date.UTC($selectedDate.year, $selectedDate.month - 1, $selectedDate.day)
</div>
<div id="right" class="d-flex flex-column">
- <div class="tags">
+ <div class="tags glass">
<div class="d-flex flex-row justify-content-between">
<div class="d-flex flex-row">
<h3>Tags</h3>
</div>
</div>
- <div class="files d-flex flex-column">
+ <div class="files d-flex flex-column glass">
<button
class="btn btn-secondary {filesOfDay?.length > 0 ? 'mb-2' : ''}"
id="uploadBtn"
z-index: 10;
padding: 0.5rem;
margin-bottom: 2rem;
- backdrop-filter: blur(8px) saturate(150%);
+ /* backdrop-filter: blur(8px) saturate(150%);
background-color: rgba(219, 219, 219, 0.45);
- border: 1px solid #ececec77;
+ border: 1px solid #ececec77; */
border-radius: 10px;
+ /* color: #ececec; */
}
.loadImageBtn {
margin-bottom: 1rem;
border-radius: 10px;
padding: 1rem;
- backdrop-filter: blur(8px) saturate(150%);
+ /* backdrop-filter: blur(8px) saturate(150%);
background-color: rgba(219, 219, 219, 0.45);
- border: 1px solid #ececec77;
+ border: 1px solid #ececec77; */
}
:global(#uploadIcon) {
}
.textAreaHeader {
- border-left: 1px solid #ccc;
- border-top: 1px solid #ccc;
- border-right: 1px solid #ccc;
+ border-left: 1px solid #6a6a6a;
+ border-top: 1px solid #6a6a6a;
+ border-right: 1px solid #6a6a6a;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.textAreaDate,
.textAreaWrittenAt,
.textAreaHistory {
- border-right: 1px solid #ccc;
+ border-right: 1px solid #6a6a6a;
padding: 0.25em;
}
<script>
import { blur } from 'svelte/transition';
import axios from 'axios';
- //import { dev } from '$app/environment';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import '../scss/styles.scss';
- import { useTrianglify, trianglifyOpacity } from '$lib/settingsStore.js';
+ import { trianglifyOpacity } from '$lib/settingsStore.js';
import { page } from '$app/state';
import { API_URL } from '$lib/APIurl.js';
- import trianglify from 'trianglify';
- import { alwaysShowSidenav } from '$lib/helpers.js';
+ import { alwaysShowSidenav, generateNeonMesh } from '$lib/helpers.js';
import * as bootstrap from 'bootstrap';
- import {
- TolgeeProvider,
- Tolgee,
- DevTools,
- LanguageDetector,
- LanguageStorage
- } from '@tolgee/svelte';
+ import { TolgeeProvider, Tolgee, DevTools, LanguageStorage } from '@tolgee/svelte';
import { FormatIcu } from '@tolgee/format-icu';
- import { use } from 'marked';
const tolgee = Tolgee()
.use(DevTools())
}
);
- function createBackground() {
- if ($useTrianglify) {
- //remove old canvas
- const oldCanvas = document.querySelector('canvas');
- if (oldCanvas) {
- oldCanvas.remove();
- }
-
- //xColors: ['#F3F3F3', '#FEFEFE', '#E5E5E5'],
- const canvas = trianglify({
- width: window.innerWidth,
- height: window.innerHeight,
- xColors: ['#FA2'],
- fill: false,
- strokeWidth: 1,
- cellSize: 100
- });
-
- document.body.appendChild(canvas.toCanvas());
- document.querySelector('canvas').style =
- 'position: fixed; top: 0; left: 0; z-index: -1; opacity: 0.4; width: 100%; height: 100%; background-color: #eaeaea;';
- }
- }
-
$effect(() => {
if ($trianglifyOpacity) {
if (document.querySelector('canvas')) {
});
onMount(() => {
- createBackground();
calculateResize();
+
+ // if on login page, generate neon mesh
+ if (page.url.pathname === '/login') {
+ generateNeonMesh();
+ }
});
let routeToFromLoginKey = $derived(page.url.pathname === '/login');
</script>
-<TolgeeProvider {tolgee}>
- <main class="d-flex flex-column">
- <div class="wrapper h-100">
+<main class="d-flex flex-column background" use:focus={generateNeonMesh}>
+ <TolgeeProvider {tolgee}>
+ <div class="wrapper h-100" transition:blur={{ duration: inDuration * 2 }}>
{#key routeToFromLoginKey}
<div
class="transition-wrapper h-100"
</div>
</div>
</div>
- </main>
-</TolgeeProvider>
+ </TolgeeProvider>
+</main>
<style>
main {
height: 100vh;
-
- /* background-image: linear-gradient(#ff8a00, #e52e71); */
- /* background-image: linear-gradient(to right, violet, darkred, purple); */
- /* background: linear-gradient(40deg, #38bdf8, #fb7185, #84cc16); */
}
.wrapper {
width: 100%;
height: 100%;
}
+
+ :global(.glass) {
+ backdrop-filter: blur(12px) saturate(130%);
+ /* background-color: rgba(219, 219, 219, 0.45); */
+ background-color: rgba(83, 83, 83, 0.73);
+ border: 1px solid #62626278;
+ color: #ececec;
+ }
+
+ :global(.glassLight) {
+ backdrop-filter: blur(8px) saturate(130%);
+ /* background-color: rgba(219, 219, 219, 0.45); */
+ background-color: rgba(83, 83, 83, 0.445);
+ border: 1px solid #62626278;
+ color: #ececec;
+ }
</style>