forked from mirrors/amnezia-wg-easy
		
	Add dark/light mode toggle. Add chart toggle
Change dark/light mode detection Add saving uiShowCharts to local storage Add saving uiTheme to local storage Add ui buttons
This commit is contained in:
		
							parent
							
								
									4d5a5c9e0d
								
							
						
					
					
						commit
						f3a8ff6490
					
				
					 5 changed files with 355 additions and 252 deletions
				
			
		| 
						 | 
				
			
			@ -3,7 +3,7 @@
 | 
			
		|||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  darkMode: 'media',
 | 
			
		||||
  darkMode: 'selector',
 | 
			
		||||
  content: ['./www/**/*.{html,js}'],
 | 
			
		||||
  theme: {
 | 
			
		||||
    screens: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -590,6 +590,18 @@ video {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.sr-only {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  width: 1px;
 | 
			
		||||
  height: 1px;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  margin: -1px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  clip: rect(0, 0, 0, 0);
 | 
			
		||||
  white-space: nowrap;
 | 
			
		||||
  border-width: 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.visible {
 | 
			
		||||
  visibility: visible;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -692,8 +704,8 @@ video {
 | 
			
		|||
  margin-bottom: 2.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mb-2 {
 | 
			
		||||
  margin-bottom: 0.5rem;
 | 
			
		||||
.mb-4 {
 | 
			
		||||
  margin-bottom: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mb-5 {
 | 
			
		||||
| 
						 | 
				
			
			@ -736,6 +748,10 @@ video {
 | 
			
		|||
  margin-top: 1.25rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mt-6 {
 | 
			
		||||
  margin-top: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.mt-px {
 | 
			
		||||
  margin-top: 1px;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -804,10 +820,18 @@ video {
 | 
			
		|||
  height: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.h-5 {
 | 
			
		||||
  height: 1.25rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.h-6 {
 | 
			
		||||
  height: 1.5rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.h-8 {
 | 
			
		||||
  height: 2rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.min-h-screen {
 | 
			
		||||
  min-height: 100vh;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -925,6 +949,10 @@ video {
 | 
			
		|||
  flex-wrap: wrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.items-end {
 | 
			
		||||
  align-items: flex-end;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.items-center {
 | 
			
		||||
  align-items: center;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1087,6 +1115,14 @@ video {
 | 
			
		|||
  --tw-bg-opacity: 0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fill-gray-400 {
 | 
			
		||||
  fill: #9ca3af;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fill-gray-600 {
 | 
			
		||||
  fill: #4b5563;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.p-1 {
 | 
			
		||||
  padding: 0.25rem;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1271,6 +1307,11 @@ video {
 | 
			
		|||
  color: rgb(17 24 39 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-neutral-400 {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(163 163 163 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.text-red-600 {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(220 38 38 / var(--tw-text-opacity));
 | 
			
		||||
| 
						 | 
				
			
			@ -1458,6 +1499,10 @@ video {
 | 
			
		|||
  opacity: 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.peer:checked ~ .peer-checked\:fill-gray-600 {
 | 
			
		||||
  fill: #4b5563;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (min-width: 450px) {
 | 
			
		||||
  .xxs\:flex-row {
 | 
			
		||||
    flex-direction: row;
 | 
			
		||||
| 
						 | 
				
			
			@ -1607,208 +1652,222 @@ video {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (prefers-color-scheme: dark) {
 | 
			
		||||
  .dark\:border-neutral-500 {
 | 
			
		||||
.dark\:border-neutral-500:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(115 115 115 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:border-neutral-600 {
 | 
			
		||||
.dark\:border-neutral-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(82 82 82 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:border-neutral-800 {
 | 
			
		||||
.dark\:border-neutral-800:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(38 38 38 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:border-red-600 {
 | 
			
		||||
.dark\:border-red-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(220 38 38 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-black {
 | 
			
		||||
.dark\:bg-black:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(0 0 0 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-neutral-400 {
 | 
			
		||||
.dark\:bg-neutral-400:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(163 163 163 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-neutral-500 {
 | 
			
		||||
.dark\:bg-neutral-500:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(115 115 115 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-neutral-600 {
 | 
			
		||||
.dark\:bg-neutral-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(82 82 82 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-neutral-700 {
 | 
			
		||||
.dark\:bg-neutral-700:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(64 64 64 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-neutral-800 {
 | 
			
		||||
.dark\:bg-neutral-800:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(38 38 38 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-red-100 {
 | 
			
		||||
.dark\:bg-red-100:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(254 226 226 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-red-600 {
 | 
			
		||||
.dark\:bg-red-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(220 38 38 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:bg-red-800 {
 | 
			
		||||
.dark\:bg-red-800:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(153 27 27 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-gray-500 {
 | 
			
		||||
.dark\:fill-neutral-400:where(.dark, .dark *) {
 | 
			
		||||
  fill: #a3a3a3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:fill-neutral-600:where(.dark, .dark *) {
 | 
			
		||||
  fill: #525252;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dark\:text-gray-500:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(107 114 128 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-200 {
 | 
			
		||||
.dark\:text-neutral-200:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(229 229 229 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-300 {
 | 
			
		||||
.dark\:text-neutral-300:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(212 212 212 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-400 {
 | 
			
		||||
.dark\:text-neutral-400:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(163 163 163 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-50 {
 | 
			
		||||
.dark\:text-neutral-50:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(250 250 250 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-500 {
 | 
			
		||||
.dark\:text-neutral-500:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(115 115 115 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-neutral-600 {
 | 
			
		||||
.dark\:text-neutral-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(82 82 82 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-red-300 {
 | 
			
		||||
.dark\:text-red-300:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(252 165 165 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-red-600 {
 | 
			
		||||
.dark\:text-red-600:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(220 38 38 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:text-white {
 | 
			
		||||
.dark\:text-white:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:opacity-50 {
 | 
			
		||||
.dark\:opacity-50:where(.dark, .dark *) {
 | 
			
		||||
  opacity: 0.5;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:placeholder\:text-neutral-400::-moz-placeholder {
 | 
			
		||||
.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::-moz-placeholder {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(163 163 163 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:placeholder\:text-neutral-400::placeholder {
 | 
			
		||||
.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::placeholder {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(163 163 163 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:placeholder\:text-neutral-500::-moz-placeholder {
 | 
			
		||||
.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::-moz-placeholder {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(115 115 115 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:placeholder\:text-neutral-500::placeholder {
 | 
			
		||||
.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::placeholder {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(115 115 115 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:border-neutral-600:hover {
 | 
			
		||||
.dark\:hover\:border-neutral-600:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(82 82 82 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:border-red-600:hover {
 | 
			
		||||
.dark\:hover\:border-red-600:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(220 38 38 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:bg-neutral-500:hover {
 | 
			
		||||
.dark\:hover\:bg-neutral-500:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(115 115 115 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:bg-neutral-600:hover {
 | 
			
		||||
.dark\:hover\:bg-neutral-600:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(82 82 82 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:bg-red-600:hover {
 | 
			
		||||
.dark\:hover\:bg-red-600:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(220 38 38 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:bg-red-700:hover {
 | 
			
		||||
.dark\:hover\:bg-red-700:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(185 28 28 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:bg-red-800:hover {
 | 
			
		||||
.dark\:hover\:bg-red-800:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-bg-opacity: 1;
 | 
			
		||||
  background-color: rgb(153 27 27 / var(--tw-bg-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:text-neutral-700:hover {
 | 
			
		||||
.dark\:hover\:text-neutral-700:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(64 64 64 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:text-red-100:hover {
 | 
			
		||||
.dark\:hover\:text-red-100:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(254 226 226 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:hover\:text-white:hover {
 | 
			
		||||
.dark\:hover\:text-white:hover:where(.dark, .dark *) {
 | 
			
		||||
  --tw-text-opacity: 1;
 | 
			
		||||
  color: rgb(255 255 255 / var(--tw-text-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:focus\:border-neutral-500:focus {
 | 
			
		||||
.dark\:focus\:border-neutral-500:focus:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(115 115 115 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .dark\:focus\:border-red-800:focus {
 | 
			
		||||
.dark\:focus\:border-red-800:focus:where(.dark, .dark *) {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(153 27 27 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  .focus\:dark\:border-neutral-500:focus {
 | 
			
		||||
.focus\:dark\:border-neutral-500:where(.dark, .dark *):focus {
 | 
			
		||||
  --tw-border-opacity: 1;
 | 
			
		||||
  border-color: rgb(115 115 115 / var(--tw-border-opacity));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.group:hover .group-hover\:dark\:fill-neutral-500:where(.dark, .dark *) {
 | 
			
		||||
  fill: #737373;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.peer:checked ~ .peer-checked\:dark\:fill-neutral-400:where(.dark, .dark *) {
 | 
			
		||||
  fill: #a3a3a3;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,25 +18,54 @@
 | 
			
		|||
 | 
			
		||||
<body class="bg-gray-50 dark:bg-neutral-800">
 | 
			
		||||
  <div id="app">
 | 
			
		||||
    <div v-cloak class="container mx-auto max-w-3xl px-3 md:px-0">
 | 
			
		||||
    <div v-cloak class="container mx-auto max-w-3xl px-3 md:px-0 mt-6">
 | 
			
		||||
      <div v-if="authenticated === true">
 | 
			
		||||
        <div class="flex flex-row flex-auto items-center items-end gap-3">
 | 
			
		||||
          <h1 class="text-4xl dark:text-neutral-200 font-medium flex-grow mb-4">
 | 
			
		||||
            <img src="./img/logo.png" width="32" class="inline align-middle dark:bg mr-2" /><span class="align-middle">WireGuard</span>
 | 
			
		||||
          </h1>
 | 
			
		||||
          <!-- Dark / light theme -->
 | 
			
		||||
          <button @click="toggleTheme"
 | 
			
		||||
            class="flex items-center justify-center w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 transition" :title="$t(`theme.${uiTheme}`)">
 | 
			
		||||
            <svg v-if="uiTheme === 'light'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
 | 
			
		||||
              class="w-5 h-5">
 | 
			
		||||
              <path stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
 | 
			
		||||
            </svg>
 | 
			
		||||
            <svg v-else-if="uiTheme === 'dark'" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
 | 
			
		||||
              class="w-5 h-5 text-neutral-400">
 | 
			
		||||
              <path stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
 | 
			
		||||
            </svg>
 | 
			
		||||
            <svg v-else xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" 
 | 
			
		||||
              class="w-5 h-5 fill-gray-600 dark:fill-neutral-400">
 | 
			
		||||
              <path
 | 
			
		||||
                d="M12,2.2c-5.4,0-9.8,4.4-9.8,9.8s4.4,9.8,9.8,9.8s9.8-4.4,9.8-9.8S17.4,2.2,12,2.2z M3.8,12c0-4.5,3.7-8.2,8.2-8.2v16.5C7.5,20.2,3.8,16.5,3.8,12z" />
 | 
			
		||||
            </svg>
 | 
			
		||||
              <path stroke-linecap="round" stroke-linejoin="round"
 | 
			
		||||
                d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25" />
 | 
			
		||||
            </svg>
 | 
			
		||||
          </button>
 | 
			
		||||
          <!-- Show / hide charts -->
 | 
			
		||||
          <label v-if="uiChartType > 0" class="inline-flex items-center justify-center cursor-pointer w-8 h-8 rounded-full bg-gray-200 hover:bg-gray-300 dark:bg-neutral-700 dark:hover:bg-neutral-600 transition group" :title="$t('toggleCharts')">
 | 
			
		||||
            <input type="checkbox" value="" class="sr-only peer" v-model="uiShowCharts" @change="toggleCharts">
 | 
			
		||||
            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" fill="currentColor"
 | 
			
		||||
              class="w-5 h-5 peer fill-gray-400 peer-checked:fill-gray-600 dark:fill-neutral-600 peer-checked:dark:fill-neutral-400 group-hover:dark:fill-neutral-500 transition">
 | 
			
		||||
                <path
 | 
			
		||||
                  d="M18.375 2.25c-1.035 0-1.875.84-1.875 1.875v15.75c0 1.035.84 1.875 1.875 1.875h.75c1.035 0 1.875-.84 1.875-1.875V4.125c0-1.036-.84-1.875-1.875-1.875h-.75ZM9.75 8.625c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v11.25c0 1.035-.84 1.875-1.875 1.875h-.75a1.875 1.875 0 0 1-1.875-1.875V8.625ZM3 13.125c0-1.036.84-1.875 1.875-1.875h.75c1.036 0 1.875.84 1.875 1.875v6.75c0 1.035-.84 1.875-1.875 1.875h-.75A1.875 1.875 0 0 1 3 19.875v-6.75Z" />
 | 
			
		||||
            </svg>
 | 
			
		||||
          </label>
 | 
			
		||||
          <span v-if="requiresPassword"
 | 
			
		||||
          class="text-sm text-gray-400 dark:text-neutral-400 mb-10 mr-2 mt-3 cursor-pointer hover:underline float-right"
 | 
			
		||||
            class="text-sm text-gray-400 dark:text-neutral-400 cursor-pointer hover:underline"
 | 
			
		||||
            @click="logout">
 | 
			
		||||
            {{$t("logout")}}
 | 
			
		||||
 | 
			
		||||
          <svg class="h-3 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
 | 
			
		||||
            stroke="currentColor">
 | 
			
		||||
            <svg class="h-3 inline" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
 | 
			
		||||
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
			
		||||
                d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
 | 
			
		||||
            </svg>
 | 
			
		||||
          </span>
 | 
			
		||||
        <h1 class="text-4xl dark:text-neutral-200 font-medium mt-2 mb-2">
 | 
			
		||||
          <img src="./img/logo.png" width="32" class="inline align-middle dark:bg" />
 | 
			
		||||
          <span class="align-middle">WireGuard</span>
 | 
			
		||||
        </h1>
 | 
			
		||||
        <h2 class="text-sm text-gray-400 dark:text-neutral-400 mb-10"></h2>
 | 
			
		||||
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="text-sm text-gray-400 dark:text-neutral-400 mb-5"></div>
 | 
			
		||||
        <div v-if="latestRelease"
 | 
			
		||||
          class="bg-red-800 dark:bg-red-100 p-4 text-white dark:text-red-600 text-sm font-small mb-10 rounded-md shadow-lg"
 | 
			
		||||
          :title="`v${currentRelease} → v${latestRelease.version}`">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,9 +23,6 @@ function bytes(bytes, decimals, kib, maxunit) {
 | 
			
		|||
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
 | 
			
		||||
const theme = darkModeMediaQuery.matches ? 'dark' : 'light';
 | 
			
		||||
 | 
			
		||||
const i18n = new VueI18n({
 | 
			
		||||
  locale: localStorage.getItem('lang') || 'en',
 | 
			
		||||
  fallbackLocale: 'en',
 | 
			
		||||
| 
						 | 
				
			
			@ -40,9 +37,9 @@ const UI_CHART_TYPES = [
 | 
			
		|||
];
 | 
			
		||||
 | 
			
		||||
const CHART_COLORS = {
 | 
			
		||||
  rx: { light: 'rgba(0,0,0,0.2)', dark: 'rgba(255,255,255,0.3)' },
 | 
			
		||||
  tx: { light: 'rgba(0,0,0,0.3)', dark: 'rgba(255,255,255,0.5)' },
 | 
			
		||||
  gradient: { light: ['rgba(0,0,0,0.1)', 'rgba(0,0,0,0)'], dark: ['rgba(255,255,255,0.1)', 'rgba(255,255,255,0)'] },
 | 
			
		||||
  rx: { light: 'rgba(128,128,128,0.3)', dark: 'rgba(255,255,255,0.3)' },
 | 
			
		||||
  tx: { light: 'rgba(128,128,128,0.4)', dark: 'rgba(255,255,255,0.3)' },
 | 
			
		||||
  gradient: { light: ['rgba(0,0,0,1.0)', 'rgba(0,0,0,1.0)'], dark: ['rgba(128,128,128,0)', 'rgba(128,128,128,0)'] },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
new Vue({
 | 
			
		||||
| 
						 | 
				
			
			@ -71,10 +68,12 @@ new Vue({
 | 
			
		|||
    currentRelease: null,
 | 
			
		||||
    latestRelease: null,
 | 
			
		||||
 | 
			
		||||
    isDark: null,
 | 
			
		||||
    uiTrafficStats: false,
 | 
			
		||||
 | 
			
		||||
    uiChartType: 0,
 | 
			
		||||
    uiShowCharts: localStorage.getItem('uiShowCharts') === "1" ? true : false,
 | 
			
		||||
    uiTheme: localStorage.theme || 'auto',
 | 
			
		||||
    prefersDarkScheme: window.matchMedia('(prefers-color-scheme: dark)'),
 | 
			
		||||
 | 
			
		||||
    chartOptions: {
 | 
			
		||||
      chart: {
 | 
			
		||||
| 
						 | 
				
			
			@ -100,11 +99,10 @@ new Vue({
 | 
			
		|||
        gradient: {
 | 
			
		||||
          shade: 'dark',
 | 
			
		||||
          type: 'vertical',
 | 
			
		||||
          shadeIntensity: 1,
 | 
			
		||||
          gradientToColors: CHART_COLORS.gradient[theme],
 | 
			
		||||
          inverseColors: true,
 | 
			
		||||
          opacityFrom: 1,
 | 
			
		||||
          opacityTo: 1,
 | 
			
		||||
          shadeIntensity: 0,
 | 
			
		||||
          gradientToColors: CHART_COLORS.gradient[this.theme],
 | 
			
		||||
          inverseColors: false,
 | 
			
		||||
          opacityTo: 0,
 | 
			
		||||
          stops: [0, 100],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -301,15 +299,26 @@ new Vue({
 | 
			
		|||
        .finally(() => this.refresh().catch(console.error));
 | 
			
		||||
    },
 | 
			
		||||
    toggleTheme() {
 | 
			
		||||
      if (this.isDark) {
 | 
			
		||||
        localStorage.theme = 'light';
 | 
			
		||||
        document.documentElement.classList.remove('dark');
 | 
			
		||||
      } else {
 | 
			
		||||
        localStorage.theme = 'dark';
 | 
			
		||||
        document.documentElement.classList.add('dark');
 | 
			
		||||
      }
 | 
			
		||||
      this.isDark = !this.isDark;
 | 
			
		||||
      const themes = ['light', 'dark', 'auto'];
 | 
			
		||||
      const currentIndex = themes.indexOf(this.uiTheme);
 | 
			
		||||
      const newIndex = (currentIndex + 1) % themes.length;
 | 
			
		||||
      this.uiTheme = themes[newIndex];
 | 
			
		||||
      localStorage.theme = this.uiTheme;
 | 
			
		||||
      this.setTheme(this.uiTheme);
 | 
			
		||||
    },
 | 
			
		||||
    setTheme(theme) {
 | 
			
		||||
      const { classList } = document.documentElement;
 | 
			
		||||
      const shouldAddDarkClass = theme === 'dark' || (theme === 'auto' && this.prefersDarkScheme.matches);
 | 
			
		||||
      classList.toggle('dark', shouldAddDarkClass);
 | 
			
		||||
    },
 | 
			
		||||
    handlePrefersChange(e) {
 | 
			
		||||
      if (localStorage.theme === 'auto') {
 | 
			
		||||
        this.setTheme(e.matches ? 'dark' : 'light');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    toggleCharts() {
 | 
			
		||||
      localStorage.setItem('uiShowCharts', this.uiShowCharts ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  filters: {
 | 
			
		||||
    bytes,
 | 
			
		||||
| 
						 | 
				
			
			@ -318,10 +327,8 @@ new Vue({
 | 
			
		|||
    },
 | 
			
		||||
  },
 | 
			
		||||
  mounted() {
 | 
			
		||||
    this.isDark = false;
 | 
			
		||||
    if (localStorage.theme === 'dark') {
 | 
			
		||||
      this.isDark = true;
 | 
			
		||||
    }
 | 
			
		||||
    this.prefersDarkScheme.addListener(this.handlePrefersChange);
 | 
			
		||||
    this.setTheme(this.uiTheme);
 | 
			
		||||
 | 
			
		||||
    this.api = new API();
 | 
			
		||||
    this.api.getSession()
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +402,7 @@ new Vue({
 | 
			
		|||
    chartOptionsTX() {
 | 
			
		||||
      const opts = {
 | 
			
		||||
        ...this.chartOptions,
 | 
			
		||||
        colors: [CHART_COLORS.tx[theme]],
 | 
			
		||||
        colors: [CHART_COLORS.tx[this.theme]],
 | 
			
		||||
      };
 | 
			
		||||
      opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false;
 | 
			
		||||
      opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth;
 | 
			
		||||
| 
						 | 
				
			
			@ -404,14 +411,20 @@ new Vue({
 | 
			
		|||
    chartOptionsRX() {
 | 
			
		||||
      const opts = {
 | 
			
		||||
        ...this.chartOptions,
 | 
			
		||||
        colors: [CHART_COLORS.rx[theme]],
 | 
			
		||||
        colors: [CHART_COLORS.rx[this.theme]],
 | 
			
		||||
      };
 | 
			
		||||
      opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false;
 | 
			
		||||
      opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth;
 | 
			
		||||
      return opts;
 | 
			
		||||
    },
 | 
			
		||||
    updateCharts() {
 | 
			
		||||
      return this.uiChartType > 0;
 | 
			
		||||
      return this.uiChartType > 0 && this.uiShowCharts;
 | 
			
		||||
    },
 | 
			
		||||
    theme() {
 | 
			
		||||
      if (this.uiTheme === 'auto') {
 | 
			
		||||
        return this.prefersDarkScheme.matches ? 'dark' : 'light';
 | 
			
		||||
      }
 | 
			
		||||
      return this.uiTheme;
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,8 @@ const messages = { // eslint-disable-line no-unused-vars
 | 
			
		|||
    downloadConfig: 'Download Configuration',
 | 
			
		||||
    madeBy: 'Made by',
 | 
			
		||||
    donate: 'Donate',
 | 
			
		||||
    toggleCharts: 'Show/hide Charts',
 | 
			
		||||
    theme: { dark: 'Dark theme', light: 'Light theme', auto: 'Auto theme' }
 | 
			
		||||
  },
 | 
			
		||||
  ua: {
 | 
			
		||||
    name: 'Ім`я',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue