merge tailwind and upgraded a17t

a17t v0.10 became a tailwind plugin rather than standalone css, and made
some other changes. Much of the original custom CSS now uses tailwind
classes, and there have been some other UI changes.
This commit is contained in:
Harvey Tindall 2022-01-04 20:12:19 +00:00
commit 9874dce520
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
43 changed files with 3928 additions and 1555 deletions

1
.gitignore vendored
View File

@ -18,4 +18,5 @@ instructions-debian.txt
cl.md
./telegram/
mautrix/
tempts/
matacc.txt

View File

@ -13,24 +13,31 @@ before:
- npm install
- npm install esbuild
- mkdir -p data/web/css
- npx esbuild --bundle css/base.css --outfile=./data/web/css/bundle.css --external:remixicon.css --minify
- cp node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 data/web/css/
- cp -r html data/
- node scripts/missing-colors.js html data/html
- cp -r lang data/
- cp LICENSE data/
- cp jfa-go.service data/
- python3 scripts/enumerate_config.py -i config/config-base.json -o data/config-base.json
- python3 scripts/generate_ini.py -i config/config-base.json -o data/config-default.ini
- python3 scripts/compile_mjml.py -o data/
- npx esbuild --bundle ts/admin.ts --outfile=./data/web/js/admin.js --minify
- npx esbuild --bundle ts/pwr.ts --outfile=./data/web/js/pwr.js --minify
- npx esbuild --bundle ts/form.ts --outfile=./data/web/js/form.js --minify
- npx esbuild --bundle ts/setup.ts --outfile=./data/web/js/setup.js --minify
- npx esbuild --bundle ts/crash.ts --outfile=./data/crash.js --minify
- rm -rf tempts
- cp -r ts tempts
- scripts/dark-variant.sh tempts
- scripts/dark-variant.sh tempts/modules
- npx esbuild --bundle tempts/admin.ts --outfile=./data/web/js/admin.js --minify
- npx esbuild --bundle tempts/pwr.ts --outfile=./data/web/js/pwr.js --minify
- npx esbuild --bundle tempts/form.ts --outfile=./data/web/js/form.js --minify
- npx esbuild --bundle tempts/setup.ts --outfile=./data/web/js/setup.js --minify
- npx esbuild --bundle tempts/crash.ts --outfile=./data/crash.js --minify
- rm -r tempts
- npx esbuild --bundle css/base.css --outfile=./data/web/css/bundle.css --external:remixicon.css --minify
- cp html/crash.html data/
- npx uncss data/crash.html --csspath web/css --output data/bundle.css
- node scripts/inline.js root data data/crash.html data/crash.html
- rm data/bundle.css
- npx tailwindcss -i data/web/css/bundle.css -o data/web/css/bundle.css
- mv data/crash.html data/html/
- go get -u github.com/swaggo/swag/cmd/swag
- swag init -g main.go

View File

@ -6,7 +6,7 @@ RUN apt-get update -y \
&& apt-get install build-essential python3-pip curl software-properties-common sed -y \
&& (curl -sL https://deb.nodesource.com/setup_14.x | bash -) \
&& apt-get install nodejs \
&& (cd /opt/build; make configuration npm email typescript bundle-css inline swagger copy INTERNAL=off GOESBUILD=on) \
&& (cd /opt/build; make configuration npm email typescript variants-html bundle-css inline swagger copy INTERNAL=off GOESBUILD=on) \
&& sed -i 's#id="password_resets-watch_directory" placeholder="/config/jellyfin"#id="password_resets-watch_directory" value="/jf" disabled#g' /opt/build/build/data/html/setup.html

View File

@ -52,14 +52,17 @@ ifeq ($(DEBUG), on)
SOURCEMAP := --sourcemap
TYPECHECK := tsc -noEmit --project ts/tsconfig.json
# jank
COPYTS := rm -r $(DATA)/web/js/ts; cp -r ts $(DATA)/web/js
COPYTS := rm -r $(DATA)/web/js/ts; cp -r tempts $(DATA)/web/js/ts
UNCSS := cp $(DATA)/web/css/bundle.css $(DATA)/bundle.css
TAILWIND := --content ""
else
LDFLAGS := -s -w $(LDFLAGS)
SOURCEMAP :=
COPYTS :=
TYPECHECK :=
UNCSS := npx uncss $(DATA)/crash.html --csspath web/css --output $(DATA)/bundle.css
UNCSS := npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/bundle.css --content "html/crash.html"
# UNCSS := npx uncss $(DATA)/crash.html --csspath web/css --output $(DATA)/bundle.css
TAILWIND :=
endif
RACE ?= off
@ -91,13 +94,18 @@ email:
typescript:
$(TYPECHECK)
$(adding dark variants to typescript)
-rm -r tempts
cp -r ts tempts
scripts/dark-variant.sh tempts
scripts/dark-variant.sh tempts/modules
$(info compiling typescript)
-mkdir -p $(DATA)/web/js
-$(ESBUILD) --bundle ts/admin.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/admin.js --minify
-$(ESBUILD) --bundle ts/pwr.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/pwr.js --minify
-$(ESBUILD) --bundle ts/form.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/form.js --minify
-$(ESBUILD) --bundle ts/setup.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/setup.js --minify
-$(ESBUILD) --bundle ts/crash.ts --outfile=./$(DATA)/crash.js --minify
-$(ESBUILD) --bundle tempts/admin.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/admin.js --minify
-$(ESBUILD) --bundle tempts/pwr.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/pwr.js --minify
-$(ESBUILD) --bundle tempts/form.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/form.js --minify
-$(ESBUILD) --bundle tempts/setup.ts $(SOURCEMAP) --outfile=./$(DATA)/web/js/setup.js --minify
-$(ESBUILD) --bundle tempts/crash.ts --outfile=./$(DATA)/crash.js --minify
$(COPYTS)
swagger:
@ -118,6 +126,8 @@ bundle-css:
-mkdir -p $(DATA)/web/css
$(info bundling css)
$(ESBUILD) --bundle css/base.css --outfile=$(DATA)/web/css/bundle.css --external:remixicon.css --minify
npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/web/css/bundle.css $(TAILWIND)
# npx postcss -o $(DATA)/web/css/bundle.css $(DATA)/web/css/bundle.css
inline:
cp html/crash.html $(DATA)/crash.html
@ -125,11 +135,16 @@ inline:
node scripts/inline.js root $(DATA) $(DATA)/crash.html $(DATA)/crash.html
rm $(DATA)/bundle.css
variants-html:
$(info copying html)
cp -r html $(DATA)/
$(info adding dark variants to html)
node scripts/missing-colors.js html $(DATA)/html
copy:
$(info copying fonts)
cp -r node_modules/remixicon/fonts/remixicon.css node_modules/remixicon/fonts/remixicon.woff2 $(DATA)/web/css/
$(info copying html)
cp -r html $(DATA)/
$(info copying crash page)
mv $(DATA)/crash.html $(DATA)/html/
$(info copying static data)
-mkdir -p $(DATA)/web
@ -159,4 +174,4 @@ clean:
-rm docs/docs.go docs/swagger.json docs/swagger.yaml
go clean
all: configuration npm email typescript bundle-css inline swagger copy compile
all: configuration npm email typescript variants-html bundle-css inline swagger copy compile

View File

@ -1,4 +1,7 @@
@import "../node_modules/a17t/dist/a17t.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "remixicon.css";
@import "./modal.css";
@import "./dark.css";
@ -12,14 +15,39 @@
--border-width-8: 8px;
}
.light-theme {
.light {
--settings-section-button-filter: 90%;
}
.body {
.dark {
--settings-section-button-filter: 80%;
}
.dark body {
background-color: #101010;
}
.dark select, .dark option, .dark input {
background: #202020;
}
html:not(.dark) .card.\@low:not(.\~neutral):not(.\~positive):not(.\~urge):not(.\~warning):not(.\~info):not(.\~critical) {
--color-fill: va(--color-fill);
--color-content: var(--color-content);
--color-accent: var(--color-accent);
--color-muted: var(--color-muted);
background-color: #fff;
color: inherit;
}
.light-only {
display: none;
}
.dark-only {
display: initial;
}
.page-container {
margin: 5% 20% 5% 20%;
}
@ -27,6 +55,7 @@
@media (max-width: 1100px) {
.page-container {
margin: 2%;
margin-top: 5rem;
}
}
@ -50,6 +79,8 @@
.banner.header {
margin-bottom: var(--spacing-4,1rem);
max-width: calc(100% + 2.2rem); /* no idea why this works */
margin-left: -1.1rem;
}
.banner.footer {
@ -70,54 +101,6 @@ div.card:contains(section.banner.footer) {
font-size: 2rem;
}
.mb-half {
margin-bottom: 0.5rem;
}
.mb-1 {
margin-bottom: 1rem;
}
.mb-2 {
margin-bottom: 2rem;
}
.mt-half {
margin-top: 0.5rem;
}
.mt-1 {
margin-top: 1rem;
}
.ml-1 {
margin-left: 1rem;
}
.ml-half {
margin-left: 0.5rem;
}
.mr-half {
margin-right: 0.5rem;
}
.mr-1 {
margin-right: 1rem;
}
.p-1 {
padding: 1rem;
}
.pb-1 {
padding-bottom: 1rem;
}
.pl-1 {
padding-left: 1rem;
}
.al {
text-align: left;
}
@ -203,15 +186,6 @@ span.sm:not(.heading) {
}
}
.fr {
float: right;
}
.monospace {
background-color: inherit; /* so we can use a17t code blocks */
font-family: Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;
}
sup.\~critical, .text-critical {
color: var(--color-critical-normal-content);
}
@ -377,10 +351,8 @@ sup.\~critical, .text-critical {
}
.settings-section-button {
box-sizing: border-box;
width: 100%;
height: 2.5rem;
background-color: rgba(0,0,0,0);
}
.settings-section-button:hover, .settings-section-button:focus {
@ -392,8 +364,6 @@ sup.\~critical, .text-critical {
}
.settings-section-button.selected {
background-color: var(--color-neutral-normal-fill);
--buton-filter-brightness: var(--settings-section-button-filter);
filter: brightness(var(--settings-section-button-filter)) !important;
}
@ -421,11 +391,21 @@ select, textarea {
-moz-appearance: none;
}
html.dark textarea {
background-color: #202020
}
input {
color: inherit;
border: 0 solid var(--color-neutral-300);
}
table {
color: var(--color-content);
}
p.top {
margin-top: 0px;
}
@ -462,6 +442,8 @@ pre {
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
background-color: var(--color-content-high) !important;
overflow-x: scroll;
}
.circle {
@ -543,10 +525,18 @@ div.card:contains(section.banner.footer) {
padding: var(--spacing-4, 1rem);
}
.button.discord.\!normal {
.button.discord.\@low {
background-color: rgba(88, 101, 242,60%);
}
.button.discord.\!normal:not(.lang-link) {
.button.discord.\@low:not(.lang-link) {
color: rgba(38, 51, 192, 90%);
}
.pb-0i {
padding-bottom: 0px !important
}
.text-center-i {
text-align: center !important;
}

View File

@ -1,91 +0,0 @@
.dark-theme {
--settings-section-button-filter: 110%;
--color-neutral-900: rgba(255, 255, 255, 0.87);
--color-neutral-800: rgba(255, 255, 255, 0.8);
--color-neutral-700: rgba(255, 255, 255, 0.73);
--color-neutral-600: rgba(255, 255, 255, 0.66);
--color-neutral-500: rgb(153, 153, 153);
--color-neutral-400: #383838;
--color-neutral-300: #303030;
--color-neutral-200: #292929;
--color-neutral-100: #242424;
--color-neutral-50: #202020;
--color-neutral-000: #101010;
--color-critical-900: #fef2f2;
--color-critical-800: #fee2e2;
--color-critical-700: #fecaca;
--color-critical-600: #fca5a5;
--color-critical-500: #f87171;
--color-critical-400: #ef4444;
--color-critical-300: #dc2626;
--color-critical-200: #b91c1c;
--color-critical-100: #991b1b;
--color-critical-50: #7f1d1d;
--color-critical-000: #441313;
--color-warning-900: #fffbeb;
--color-warning-800: #fef3c7;
--color-warning-700: #fde68a;
--color-warning-600: #fcd34d;
--color-warning-500: #fbbf24;
--color-warning-400: #f59e0b;
--color-warning-300: #d97706;
--color-warning-200: #b45309;
--color-warning-100: #92400e;
--color-warning-50: #783900;
--color-warning-000: #411e01;
--color-positive-900: #f0fdf4;
--color-positive-800: #dcfce7;
--color-positive-700: #bbf7d0;
--color-positive-600: #86efac;
--color-positive-500: #4ade80;
--color-positive-400: #22c55e;
--color-positive-300: #16a34a;
--color-positive-200: #15803d;
--color-positive-100: #166534;
--color-positive-50: #14532d;
--color-positive-000: #0f2e1b;
--color-urge-900: #e0ffff;
--color-urge-800: #c0fbff;
--color-urge-700: #a0f4ff;
--color-urge-600: #80e9ff;
--color-urge-500: #60dbfb;
--color-urge-400: #40cbf3;
--color-urge-300: #20b9e9;
--color-urge-200: #00a4dc; /* tab buttons */
--color-urge-100: #0054bc;
--color-urge-50: #00169a;
--color-urge-000: #050076;
--color-info-900: #f5f3ff;
--color-info-800: #ede9fe;
--color-info-700: #ddd6fe;
--color-info-600: #c4b5fd;
--color-info-500: #a78bfa;
--color-info-400: #8b5cf6;
--color-info-300: #7c3aed;
--color-info-200: #6d28d9;
--color-info-100: #5b21b6;
--color-info-50: #4c1d95;
--color-info-000: #240e44;
--color-neutral-normal-content: #ffffff;
}
.light-only {
display: none;
}
.dark-only {
display: initial;
}
.dark-theme select option {
background: #202020;
}

351
css/dark.js Normal file
View File

@ -0,0 +1,351 @@
var c = {
inherit: 'inherit',
current: 'currentColor',
transparent: 'transparent',
black: '#000',
white: '#fff',
d_neutral: {
900: "rgba(255, 255, 255, 0.87)",
800: "rgba(255, 255, 255, 0.8)",
700: "rgba(255, 255, 255, 0.73)",
600: "rgba(255, 255, 255, 0.66)",
500: "rgb(153, 153, 153)",
400: "#383838",
300: "#303030",
200: "#292929",
100: "#242424",
50: "#202020",
000: "#101010"
},
d_critical: {
900: "#fef2f2",
800: "#fee2e2",
700: "#fecaca",
600: "#fca5a5",
500: "#f87171",
400: "#ef4444",
300: "#dc2626",
200: "#b91c1c",
100: "#991b1b",
50: "#7f1d1d",
000: "#441313"
},
d_warning: {
900: "#fffbeb",
800: "#fef3c7",
700: "#fde68a",
600: "#fcd34d",
500: "#fbbf24",
400: "#f59e0b",
300: "#d97706",
200: "#b45309",
100: "#92400e",
50: "#783900",
000: "#411e01"
},
d_positive: {
900: "#f0fdf4",
800: "#dcfce7",
700: "#bbf7d0",
600: "#86efac",
500: "#4ade80",
400: "#22c55e",
300: "#16a34a",
200: "#15803d",
100: "#166534",
50: "#14532d",
000: "#0f2e1b"
},
d_urge: {
900: "#e0ffff",
800: "#c0fbff",
700: "#a0f4ff",
600: "#80e9ff",
500: "#60dbfb",
400: "#40cbf3",
300: "#20b9e9",
200: "#00a4dc",
100: "#0054bc",
50: "#00169a",
000: "#050076"
},
d_info: {
900: "#f5f3ff",
800: "#ede9fe",
700: "#ddd6fe",
600: "#c4b5fd",
500: "#a78bfa",
400: "#8b5cf6",
300: "#7c3aed",
200: "#6d28d9",
100: "#5b21b6",
50: "#4c1d95",
000: "#240e44"
},
slate: {
50: '#f8fafc',
100: '#f1f5f9',
200: '#e2e8f0',
300: '#cbd5e1',
400: '#94a3b8',
500: '#64748b',
600: '#475569',
700: '#334155',
800: '#1e293b',
900: '#0f172a'
},
gray: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
400: '#9ca3af',
500: '#6b7280',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827'
},
zinc: {
50: '#fafafa',
100: '#f4f4f5',
200: '#e4e4e7',
300: '#d4d4d8',
400: '#a1a1aa',
500: '#71717a',
600: '#52525b',
700: '#3f3f46',
800: '#27272a',
900: '#18181b'
},
neutral: {
50: '#fafafa',
100: '#f5f5f5',
200: '#e5e5e5',
300: '#d4d4d4',
400: '#a3a3a3',
500: '#737373',
600: '#525252',
700: '#404040',
800: '#262626',
900: '#171717'
},
stone: {
50: '#fafaf9',
100: '#f5f5f4',
200: '#e7e5e4',
300: '#d6d3d1',
400: '#a8a29e',
500: '#78716c',
600: '#57534e',
700: '#44403c',
800: '#292524',
900: '#1c1917'
},
red: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d'
},
orange: {
50: '#fff7ed',
100: '#ffedd5',
200: '#fed7aa',
300: '#fdba74',
400: '#fb923c',
500: '#f97316',
600: '#ea580c',
700: '#c2410c',
800: '#9a3412',
900: '#7c2d12'
},
amber: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f'
},
yellow: {
50: '#fefce8',
100: '#fef9c3',
200: '#fef08a',
300: '#fde047',
400: '#facc15',
500: '#eab308',
600: '#ca8a04',
700: '#a16207',
800: '#854d0e',
900: '#713f12'
},
lime: {
50: '#f7fee7',
100: '#ecfccb',
200: '#d9f99d',
300: '#bef264',
400: '#a3e635',
500: '#84cc16',
600: '#65a30d',
700: '#4d7c0f',
800: '#3f6212',
900: '#365314'
},
green: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d'
},
emerald: {
50: '#ecfdf5',
100: '#d1fae5',
200: '#a7f3d0',
300: '#6ee7b7',
400: '#34d399',
500: '#10b981',
600: '#059669',
700: '#047857',
800: '#065f46',
900: '#064e3b'
},
teal: {
50: '#f0fdfa',
100: '#ccfbf1',
200: '#99f6e4',
300: '#5eead4',
400: '#2dd4bf',
500: '#14b8a6',
600: '#0d9488',
700: '#0f766e',
800: '#115e59',
900: '#134e4a'
},
cyan: {
50: '#ecfeff',
100: '#cffafe',
200: '#a5f3fc',
300: '#67e8f9',
400: '#22d3ee',
500: '#06b6d4',
600: '#0891b2',
700: '#0e7490',
800: '#155e75',
900: '#164e63'
},
sky: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e'
},
blue: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a'
},
indigo: {
50: '#eef2ff',
100: '#e0e7ff',
200: '#c7d2fe',
300: '#a5b4fc',
400: '#818cf8',
500: '#6366f1',
600: '#4f46e5',
700: '#4338ca',
800: '#3730a3',
900: '#312e81'
},
violet: {
50: '#f5f3ff',
100: '#ede9fe',
200: '#ddd6fe',
300: '#c4b5fd',
400: '#a78bfa',
500: '#8b5cf6',
600: '#7c3aed',
700: '#6d28d9',
800: '#5b21b6',
900: '#4c1d95'
},
purple: {
50: '#faf5ff',
100: '#f3e8ff',
200: '#e9d5ff',
300: '#d8b4fe',
400: '#c084fc',
500: '#a855f7',
600: '#9333ea',
700: '#7e22ce',
800: '#6b21a8',
900: '#581c87'
},
fuchsia: {
50: '#fdf4ff',
100: '#fae8ff',
200: '#f5d0fe',
300: '#f0abfc',
400: '#e879f9',
500: '#d946ef',
600: '#c026d3',
700: '#a21caf',
800: '#86198f',
900: '#701a75'
},
pink: {
50: '#fdf2f8',
100: '#fce7f3',
200: '#fbcfe8',
300: '#f9a8d4',
400: '#f472b6',
500: '#ec4899',
600: '#db2777',
700: '#be185d',
800: '#9d174d',
900: '#831843'
},
rose: {
50: '#fff1f2',
100: '#ffe4e6',
200: '#fecdd3',
300: '#fda4af',
400: '#fb7185',
500: '#f43f5e',
600: '#e11d48',
700: '#be123c',
800: '#9f1239',
900: '#881337'
}
}
module.exports = c;

View File

@ -1,6 +1,6 @@
.loader {
height: auto;
color: rgba(0, 0, 0, 0);
color: rgba(0, 0, 0, 0) !important;
}
.loader .dot {

3
go.mod
View File

@ -14,6 +14,7 @@ replace github.com/hrfee/jfa-go/linecache => ./linecache
require (
github.com/bwmarrin/discordgo v0.23.2
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
github.com/fatih/color v1.13.0
github.com/fsnotify/fsnotify v1.5.1
@ -49,7 +50,7 @@ require (
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
github.com/swaggo/gin-swagger v1.3.3
github.com/swaggo/swag v1.7.6 // indirect
github.com/swaggo/swag v1.7.8 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tidwall/sjson v1.2.4 // indirect
github.com/ugorji/go v1.2.6 // indirect

8
go.sum
View File

@ -24,6 +24,8 @@ github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt
github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -256,6 +258,8 @@ github.com/swaggo/swag v1.6.7/go.mod h1:xDhTyuFIujYiN3DKWC/H/83xcfHp+UE/IzWWampG
github.com/swaggo/swag v1.7.4/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI=
github.com/swaggo/swag v1.7.6 h1:UbAqHyXkW2J+cDjs5S43MkuYR7a6stB7Am7SK8NBmRg=
github.com/swaggo/swag v1.7.6/go.mod h1:7vLqNYEtYoIsD14wXgy9oDS65MNiDANrPtbk9rnLuj0=
github.com/swaggo/swag v1.7.8 h1:w249t0l/kc/DKMGlS0fppNJQxKyJ8heNaUWB6nsH3zc=
github.com/swaggo/swag v1.7.8/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
@ -288,6 +292,7 @@ github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9p
github.com/xhit/go-simple-mail/v2 v2.10.0 h1:nib6RaJ4qVh5HD9UE9QJqnUZyWp3upv+Z6CFxaMj0V8=
github.com/xhit/go-simple-mail/v2 v2.10.0/go.mod h1:kA1XbQfCI4JxQ9ccSN6VFyIEkkugOm7YiPkA5hKiQn4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -301,6 +306,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -347,6 +353,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
@ -366,6 +373,7 @@ golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -7,10 +7,12 @@
</head>
<body class="section">
<div class="page-container">
<h1 class="heading">Page not found.</h1>
<p class="content">
{{ .contactMessage }}
</p>
<div class="card">
<h1 class="heading">Page not found.</h1>
<p class="content">
{{ .contactMessage }}
</p>
</div>
</div>
</body>
</html>

View File

@ -22,23 +22,23 @@
<div id="modal-login" class="modal">
<form class="modal-content card" id="form-login" href="">
<span class="heading">{{ .strings.login }}</span>
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="login-user">
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="login-password">
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="login-user">
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="login-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.login }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.login }}</span>
</label>
</form>
</div>
<div id="modal-add-user" class="modal">
<form class="modal-content card" id="form-add-user" href="">
<span class="heading">{{ .strings.newUser }} <span class="modal-close">&times;</span></span>
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="add-user-user">
<input type="email" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}">
<input type="password" class="field input ~neutral !high mb-1" placeholder="{{ .strings.password }}" id="add-user-password">
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="add-user-user">
<input type="email" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.emailAddress }}">
<input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="add-user-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
</label>
</form>
</div>
@ -46,29 +46,29 @@
<div class="modal-content content card">
<img src="{{ .urlBase }}/banner.svg" class="banner header" alt="jfa-go banner">
<span class="heading"><span class="modal-close">&times;</span></span>
<p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p>
<p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
<p>{{ .strings.version }} <span class="code font-mono bg-inherit">{{ .version }}</span></p>
<p>{{ .strings.commitNoun }} <span class="code font-mono bg-inherit">{{ .commit }}</span></p>
<div class="row col flex">
<a class="button ~neutral mr-half mt-1 mb-1 lang-link" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line mr-half"></i>github</a>
<a class="button ~urge mt-1 mb-1 mr-half lang-link" href="https://wiki.jfa-go.com">wiki/docs</a>
<a class="button ~positive mt-1 mb-1 mr-half lang-link" href="https://weblate.jfa-go.com">translation</a>
<div class="dropdown mr-half" tabindex="0">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~info mt-1 mb-1 dropdown-button lang-link">
<i class="ri-hand-heart-line mr-half"></i>
<a class="button ~neutral mr-2 mt-4 mb-4 lang-link" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line mr-2"></i>github</a>
<a class="button ~urge mt-4 mb-4 mr-2 lang-link" href="https://wiki.jfa-go.com">wiki/docs</a>
<a class="button ~positive mt-4 mb-4 mr-2 lang-link" href="https://weblate.jfa-go.com">translation</a>
<div class="dropdown mr-2" tabindex="0">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~info mt-4 mb-4 dropdown-button lang-link">
<i class="ri-hand-heart-line mr-2"></i>
donate
<span class="ml-1 chev"></span>
<span class="ml-2 chev"></span>
</a>
<div class="dropdown-display">
<div class="card ~info !low">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">Ko-fi</a>
<div class="card ~neutral @low">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-2 lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-2 lang-link">Ko-fi</a>
</div>
</div>
</div>
<a class="button ~urge mt-1 mb-1 !normal discord lang-link" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-half"></i>discord</a>
<a class="button ~urge mt-4 mb-4 @low discord lang-link" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-2"></i>discord</a>
</div>
<p><a href="https://github.com/hrfee/jfa-go/blob/main/LICENSE">Available under the MIT License.</a></p>
<pre class="monospace">{{ .license }}</pre>
<pre class="font-mono bg-inherit">{{ .license }}</pre>
</div>
</div>
<div id="modal-logs" class="modal">
@ -80,45 +80,45 @@
<div id="modal-modify-user" class="modal">
<form class="modal-content card" id="form-modify-user" href="">
<span class="heading"><span id="header-modify-user"></span> <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.modifySettingsDescription }}</p>
<div class="flex-row mb-1">
<label class="flex-row-group mr-1">
<p class="content my-4">{{ .strings.modifySettingsDescription }}</p>
<div class="flex-row mb-4">
<label class="flex-row-group mr-2">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
<span class="button ~neutral !high supra full-width center">{{ .strings.profile }}</span>
<span class="button ~neutral @high supra full-width center">{{ .strings.profile }}</span>
</label>
<label class="flex-row-group ml-1">
<label class="flex-row-group ml-2">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-user">
<span class="button ~neutral !normal supra full-width center">{{ .strings.user }}</span>
<span class="button ~neutral @low supra full-width center">{{ .strings.user }}</span>
</label>
</div>
<div class="select ~neutral !normal mb-1">
<div class="select ~neutral @low mb-4">
<select id="modify-user-profiles"></select>
</div>
<div class="select ~neutral !normal mb-1 unfocused">
<div class="select ~neutral @low mb-4 unfocused">
<select id="modify-user-users"></select>
</div>
<label class="switch mb-1">
<label class="switch mb-4">
<input type="checkbox" id="modify-user-homescreen" checked>
<span>{{ .strings.applyHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.apply }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
</label>
</form>
</div>
<div id="modal-delete-user" class="modal">
<form class="modal-content card" id="form-delete-user" href="">
<span class="heading"><span id="header-delete-user"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<label class="switch mb-1">
<div class="content mt-8">
<label class="switch mb-4">
<input type="checkbox" id="delete-user-notify" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-delete-user" class="textarea full-width ~neutral !normal mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<textarea id="textarea-delete-user" class="textarea full-width ~neutral @low mb-4" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical !normal full-width center supra submit">{{ .strings.delete }}</span>
<span class="button ~critical @low full-width center supra submit">{{ .strings.delete }}</span>
</label>
</div>
</form>
@ -126,11 +126,11 @@
<div id="modal-extend-expiry" class="modal">
<form class="modal-content card" id="form-extend-expiry" href="">
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<div class="content mt-8">
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="extend-expiry-months">
<option>0</option>
</select>
@ -138,7 +138,7 @@
</div>
<div class="col">
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="extend-expiry-days">
<option>0</option>
</select>
@ -148,7 +148,7 @@
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="extend-expiry-hours">
<option>0</option>
</select>
@ -156,21 +156,21 @@
</div>
<div class="col">
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="extend-expiry-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
<label class="switch mb-1">
<label class="switch mb-4">
<input type="checkbox" id="expiry-extend-enable" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral !normal mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral @low mb-4" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical !normal full-width center supra submit">{{ .strings.submit }}</span>
<span class="button ~critical @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</div>
</form>
@ -179,33 +179,33 @@
<form class="modal-content wide card" id="form-announce" href="">
<span class="heading"><span id="header-announce"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<div class="col card ~neutral @low">
<div id="announce-details">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="announce-variables">
<span class="button ~urge !normal mb-1 mt-half" id="announce-variables-username" style="margin-left: 0.25rem; margin-right: 0.25rem;"><span class="monospace">{username}</span></span>
<span class="button ~urge @low mb-2 mt-4" id="announce-variables-username" style="margin-left: 0.25rem; margin-right: 0.25rem;"><span class="font-mono bg-inherit">{username}</span></span>
</div>
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
<input type="text" id="announce-subject" class="input ~neutral !normal mb-1 mt-half">
<input type="text" id="announce-subject" class="input ~neutral @low mb-2 mt-4">
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
<textarea id="textarea-announce" class="textarea full-width ~neutral !normal mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
<textarea id="textarea-announce" class="textarea full-width ~neutral @low mt-4 font-mono"></textarea>
<p class="support mt-4 mb-2">{{ .strings.markdownSupported }}</p>
</div>
<label class="label unfocused" id="announce-name"><p class="supra">{{ .strings.name }}</p>
<input type="text" class="input ~neutral !normal mb-1 mt-half">
<input type="text" class="input ~neutral @low mb-2 mt-4">
<p class="support">{{ .strings.templateEnterName }}</p>
</label>
<div class="row flex-expand">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal center supra submit">{{ .strings.send }}</span>
<span class="button ~urge @low center supra submit">{{ .strings.send }}</span>
</label>
<span class="button ~info !normal center supra" id="save-announce">{{ .strings.saveAsTemplate }}</span>
<span class="button ~info @low center supra" id="save-announce">{{ .strings.saveAsTemplate }}</span>
</div>
</div>
<div class="col card ~neutral !low">
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="announce-preview"></div>
<div class="mt-8" id="announce-preview"></div>
</div>
</div>
</form>
@ -213,13 +213,13 @@
<div id="modal-customize" class="modal">
<div class="modal-content card">
<span class="heading">{{ .strings.customizeMessages }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.customizeMessagesDescription }}</p>
<p class="content my-4">{{ .strings.customizeMessagesDescription }}</p>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ .strings.name }}</th>
<th>{{ .strings.reset }}</th>
<th class="table-inline justify-center">{{ .strings.reset }}</th>
<th>{{ .strings.edit }}</th>
</tr>
</thead>
@ -232,68 +232,68 @@
<form class="modal-content wide card" id="form-editor" href="">
<span class="heading"><span id="header-editor"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<div class="col card ~neutral @low">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="editor-variables"></div>
<div id="editor-variables" class="mt-4"></div>
<span class="label supra" for="editor-conditionals" id="label-editor-conditionals">{{ .strings.conditionals }}</span>
<div id="editor-conditionals"></div>
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral !normal mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral @low mt-4 font-mono"></textarea>
<p class="support mt-4 mb-2">{{ .strings.markdownSupported }}</p>
<div class="flex-row">
<label class="full-width ml-half">
<label class="full-width ml-2">
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</div>
</div>
<div class="col card ~neutral !low">
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="editor-preview"></div>
<div class="mt-8" id="editor-preview"></div>
</div>
</div>
</form>
</div>
<div id="modal-restart" class="modal">
<div class="modal-content card ~critical !low">
<div class="modal-content card ~critical @low">
<span class="heading">{{ .strings.settingsRestartRequired }} <span class="modal-close">&times;</span></span>
<p class="content pb-1">{{ .strings.settingsRestartRequiredDescription }}</p>
<div class="fr">
<span class="button ~info !normal mb-half" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
<span class="button ~critical !normal" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
<p class="content my-4">{{ .strings.settingsRestartRequiredDescription }}</p>
<div class="float-right">
<span class="button ~info @low mb-2" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
<span class="button ~critical @low" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
</div>
</div>
</div>
<div id="modal-refresh" class="modal">
<div class="modal-content card ~neutral !normal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.settingsApplied }}</span>
<p class="content">{{ .strings.settingsRefreshPage }}</p>
</div>
</div>
<div id="modal-send-pwr" class="modal">
<div class="modal-content card ~neutral !normal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.sendPWR }}</span>
<p class="content" id="send-pwr-note"></p>
<span class="button ~urge !normal mt-half" id="send-pwr-link">{{ .strings.copy }}</span>
<p class="content my-2" id="send-pwr-note"></p>
<span class="button ~urge @low mt-2" id="send-pwr-link">{{ .strings.copy }}</span>
</div>
</div>
<div id="modal-ombi-profile" class="modal">
<form class="modal-content card" id="form-ombi-defaults" href="">
<span class="heading">{{ .strings.ombiProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.ombiUserDefaultsDescription }}</p>
<div class="select ~neutral !normal mb-1">
<p class="content my-4">{{ .strings.ombiUserDefaultsDescription }}</p>
<div class="select ~neutral @low mb-4">
<select></select>
</div>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="modal-user-profiles" class="modal">
<div class="modal-content wide card">
<span class="heading">{{ .strings.userProfiles }} <span class="modal-close">&times;</span></span>
<p class="support lg">{{ .strings.userProfilesDescription }}</p>
<p class="content my-4">{{ .strings.userProfilesDescription }}</p>
<div class="table-responsive">
<table class="table">
<thead>
@ -305,7 +305,7 @@
{{ end }}
<th>{{ .strings.from }}</th>
<th>{{ .strings.userProfilesLibraries }}</th>
<th><span class="button ~neutral !high" id="button-profile-create">{{ .strings.create }}</span></th>
<th><span class="button ~neutral @high" id="button-profile-create">{{ .strings.create }}</span></th>
</tr>
</thead>
<tbody id="table-profiles"></tbody>
@ -316,23 +316,23 @@
<div id="modal-add-profile" class="modal">
<form class="modal-content card" id="form-add-profile" href="">
<span class="heading">{{ .strings.addProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.addProfileDescription }}</p>
<p class="content my-4">{{ .strings.addProfileDescription }}</p>
<label>
<span class="supra">{{ .strings.addProfileNameOf }} </span>
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.name }}" id="add-profile-name">
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.name }}" id="add-profile-name">
<label>
<span class="supra">{{ .strings.user }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<div class="select ~neutral @low mt-4 mb-2">
<select id="add-profile-user"></select>
</div>
</label>
<label class="switch mb-1">
<label class="switch mb-4">
<input type="checkbox" id="add-profile-homescreen" checked>
<span>{{ .strings.addProfileStoreHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.create }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
</label>
</form>
</div>
@ -341,41 +341,41 @@
<span class="heading">{{ .strings.updates }} <span class="modal-close">&times;</span></span>
<p class="content">
<h2>
<a id="update-version"></a> (<span class="monospace" id="update-commit"></span>)
<a id="update-version"></a> (<span class="font-mono bg-inherit" id="update-commit"></span>)
</h2>
<p class="content" id="update-description"></p>
<p class="support" id="update-date"></p>
<div class="content markdown-box" id="update-changelog"></div>
</p>
<span class="button ~info !normal full-width center" id="update-download">{{ .strings.download }}</span>
<span class="button ~urge !normal full-width center" id="update-update">{{ .strings.update }}</span>
<span class="button ~info @low full-width center" id="update-download">{{ .strings.download }}</span>
<span class="button ~urge @low full-width center" id="update-update">{{ .strings.update }}</span>
</div>
</div>
{{ if .telegramEnabled }}
<div id="modal-telegram" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.linkTelegram }}</span>
<p class="content mb-1">{{ .strings.sendPIN }}</p>
<span class="heading mb-4">{{ .strings.linkTelegram }}</span>
<p class="content mb-4">{{ .strings.sendPIN }}</p>
<h1 class="ac" id="telegram-pin"></h1>
<a class="subheading link-center" id="telegram-link" target="_blank">
<span class="shield ~info mr-1">
<span class="shield ~info mr-2">
<span class="icon">
<i class="ri-telegram-line"></i>
</span>
</span>
&#64;<span id="telegram-username">
</a>
<span class="button ~info !normal full-width center mt-1" id="telegram-waiting">{{ .strings.success }}</span>
<span class="button ~info @low full-width center mt-4" id="telegram-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="modal-content card">
<span class="heading mb-1"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content mb-1" id="discord-description"></p>
<span class="heading mb-4"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content mb-4" id="discord-description"></p>
<div class="row">
<input type="search" class="col sm field ~neutral !normal input" id="discord-search" placeholder="user#1234">
<input type="search" class="col sm field ~neutral @low input" id="discord-search" placeholder="user#1234">
</div>
<table class="table"><tbody id="discord-list"></tbody></table>
</div>
@ -384,76 +384,78 @@
<div id="modal-matrix" class="modal">
<form class="modal-content card" id="form-matrix" href="">
<span class="heading">{{ .strings.linkMatrix }}</span>
<p class="content">{{ .strings.linkMatrixDescription }}</p>
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="matrix-user">
<input type="password" class="field input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="matrix-password">
<p class="content my-4">{{ .strings.linkMatrixDescription }}</p>
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="matrix-user">
<input type="password" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.password }}" id="matrix-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">{{ .strings.submit }}</span>
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="notification-box"></div>
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-1 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral !low">
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-12h">
<span>{{ .strings.time12h }}</span>
</label>
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-24h">
<span>{{ .strings.time24h }}</span>
</label>
<div id="lang-list"></div>
<div class="top-4 left-4 absolute">
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-2 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<label class="switch pb-4">
<input type="radio" name="lang-time" id="lang-12h">
<span>{{ .strings.time12h }}</span>
</label>
<label class="switch pb-4">
<input type="radio" name="lang-time" id="lang-24h">
<span>{{ .strings.time24h }}</span>
</label>
<div id="lang-list"></div>
</div>
</div>
</div>
</span>
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
</span>
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
</div>
<div class="page-container">
<div class="mb-1">
<div class="mb-4">
<header class="flex flex-wrap items-center justify-between">
<div class="text-neutral-700">
<span id="button-tab-invites" class="tab-button portal">{{ .strings.invites }}</span>
<span id="button-tab-accounts" class="tab-button portal">{{ .strings.accounts }}</span>
<span id="button-tab-settings" class="tab-button portal">{{ .strings.settings }}</span>
<div>
<span id="button-tab-invites" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.invites }}</span>
<span id="button-tab-accounts" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.accounts }}</span>
<span id="button-tab-settings" class="text-3xl button portal ~neutral dark:~d_neutral @low mr-2 px-5">{{ .strings.settings }}</span>
</div>
</header>
</div>
<div class="mb-1">
<div class="text-neutral-700">
<span class="button ~critical !normal mb-1 unfocused" id="logout-button">{{ .strings.logout }}</span>
<div class="mb-4">
<div>
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
</div>
</div>
<div id="tab-invites">
<div class="card ~neutral !low invites mb-1">
<div class="card @low invites dark:~d_neutral mb-4">
<span class="heading">{{ .strings.invites }}</span>
<div id="invites"></div>
</div>
<div class="card ~neutral !low">
<div class="card @low dark:~d_neutral">
<span class="heading">{{ .strings.create }}</span>
<div class="row" id="create-inv">
<div class="card ~neutral !normal col">
<div class="row mb-1">
<label class="col mr-1">
<div class="card ~neutral @low col">
<div class="row mb-2">
<label class="col mr-2">
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
<span class="button ~neutral !high supra full-width center">{{ .strings.inviteDuration }}</span>
<span class="button ~neutral @high supra full-width center">{{ .strings.inviteDuration }}</span>
</label>
<label class="col ml-1">
<label class="col ml-2">
<input type="radio" name="duration" class="unfocused" id="radio-user-expiry">
<span class="button ~neutral !normal supra full-width center">{{ .strings.userExpiry }}</span>
<span class="button ~neutral @low supra full-width center">{{ .strings.userExpiry }}</span>
</label>
</div>
<div id="inv-duration">
<div class="row">
<div class="col">
<label class="label supra" for="create-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="create-months">
<option>0</option>
</select>
@ -461,7 +463,7 @@
</div>
<div class="col">
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="create-days">
<option>0</option>
</select>
@ -471,7 +473,7 @@
<div class="row">
<div class="col">
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="create-hours">
<option>0</option>
</select>
@ -479,7 +481,7 @@
</div>
<div class="col">
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="create-minutes">
<option>0</option>
</select>
@ -488,17 +490,17 @@
</div>
</div>
<div id="user-expiry" class="unfocused">
<p class="support">{{ .strings.userExpiryDescription }}</p>
<div class="mb-half">
<label for="create-user-expiry-enabled" class="button ~neutral !normal">
<p class="support mb-2">{{ .strings.userExpiryDescription }}</p>
<div class="mb-2">
<label for="create-user-expiry-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-user-expiry-enabled" aria-label="User duration enabled">
<span class="ml-half">{{ .strings.enabled }} </span>
<span class="ml-2">{{ .strings.enabled }} </span>
</label>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="user-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="user-months">
<option>0</option>
</select>
@ -506,7 +508,7 @@
</div>
<div class="col">
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="user-days">
<option>0</option>
</select>
@ -516,7 +518,7 @@
<div class="row">
<div class="col">
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="user-hours">
<option>0</option>
</select>
@ -524,7 +526,7 @@
</div>
<div class="col">
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="user-minutes">
<option>0</option>
</select>
@ -534,92 +536,92 @@
</div>
<div class="col">
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
<input type="text" id="create-label" class="input ~neutral !normal mb-1 mt-half">
<input type="text" id="create-label" class="input ~neutral @low mb-2 mt-4">
</div>
</div>
<div class="card ~neutral !normal col">
<div class="card ~neutral @low col">
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
<div class="flex-expand mb-1 mt-half">
<input type="number" min="0" id="create-uses" class="input ~neutral !normal mr-1" value=1>
<label for="create-inf-uses" class="button ~neutral !normal" title="Set uses to infinite">
<span></span>
<div class="flex-expand mb-2 mt-4">
<input type="number" min="0" id="create-uses" class="input ~neutral @low mr-2" value=1>
<label for="create-inf-uses" class="button ~neutral @low" title="Set uses to infinite">
<span>&infin;</span>
<input type="checkbox" class="unfocused" id="create-inf-uses" aria-label="Set uses to infinite">
</label>
</div>
<p class="support unfocused" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
<p class="support unfocused my-2" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
<label class="label supra">{{ .strings.profile }}</label>
<div class="select ~neutral !normal mb-1 mt-half">
<div class="select ~neutral @low mb-2 mt-4">
<select id="create-profile">
</select>
</div>
<div id="create-send-to-container">
<label class="label supra">{{ .strings.inviteSendToEmail }}</label>
<div class="flex-expand mb-1 mt-half">
<div class="flex-expand mb-2 mt-4">
{{ if .discordEnabled }}
<input type="text" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com | user#1234">
<span id="create-send-to-search" class="button ~neutral !normal mr-1">
<input type="text" id="create-send-to" class="input ~neutral @low mr-2" placeholder="example@example.com | user#1234">
<span id="create-send-to-search" class="button ~neutral @low mr-2">
<i class="icon ri-search-2-line" title="{{ .strings.search }}"></i>
</span>
{{ else }}
<input type="email" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com">
<input type="email" id="create-send-to" class="input ~neutral @low mr-2" placeholder="example@example.com">
{{ end }}
<label for="create-send-to-enabled" class="button ~neutral !normal">
<label for="create-send-to-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
</label>
</div>
</div>
<span class="button ~urge !normal supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
<span class="button ~urge @low supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
</div>
</div>
</div>
</div>
<div id="tab-accounts" class="unfocused">
<div class="card ~neutral !low accounts mb-1">
<div class="card @low dark:~d_neutral accounts mb-4">
<div class="flex-expand row">
<div class="row">
<span class="heading mr-1 col sm">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral !normal input search ml-1 mr-1" id="accounts-search" placeholder="{{ .strings.search }}">
<span class="text-3xl font-bold mr-2 col">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral @low input search ml-2 mr-2" id="accounts-search" placeholder="{{ .strings.search }}">
</div>
<div class="row">
<span class="col sm button ~neutral !normal center mb-half" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
<div id="accounts-announce-dropdown" class="col sm dropdown" tabindex="0">
<span class="h-100 sm button ~info !normal center mb-half" id="accounts-announce">{{ .strings.announce }}</span>
<span class="col sm button ~neutral @low center mb-2" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
<div id="accounts-announce-dropdown" class="col sm dropdown pb-0i" tabindex="0">
<span class="h-100 sm button ~info @low center mb-2" id="accounts-announce">{{ .strings.announce }}</span>
<div class="dropdown-display">
<div class="card ~neutral !low">
<div class="card ~neutral @low">
<span class="supra sm">{{ .strings.templates }}</span>
<div id="accounts-announce-templates"></div>
</div>
</div>
</div>
<span class="col sm button ~urge !normal center mb-half" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
<span class="col sm button ~warning !normal center mb-half" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<div id="accounts-disable-enable-dropdown" class="col sm dropdown manual" tabindex="0">
<span class="h-100 sm button ~positive !normal center mb-half" id="accounts-disable-enable">{{ .strings.disable }}</span>
<span class="col sm button ~urge @low center mb-2" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
<span class="col sm button ~warning @low center mb-2" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<div id="accounts-disable-enable-dropdown" class="col sm dropdown manual pb-0i" tabindex="0">
<span class="h-100 sm button ~positive @low center mb-2" id="accounts-disable-enable">{{ .strings.disable }}</span>
<div class="dropdown-display">
<div class="card ~neutral !low">
<span class="button ~neutral sm full-width accounts-announce-template-button" id="accounts-enable-expiry">{{ .strings.setExpiry }}</span>
<div class="card ~neutral @low">
<span class="button ~urge sm full-width accounts-announce-template-button" id="accounts-enable-expiry">{{ .strings.setExpiry }}</span>
</div>
</div>
</div>
<span class="col sm button ~info !normal center mb-half unfocused" id="accounts-send-pwr">{{ .strings.sendPWR }}</span>
<span class="col sm button ~critical !normal center mb-half" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
<span class="col sm button ~info @low center mb-2 unfocused" id="accounts-send-pwr">{{ .strings.sendPWR }}</span>
<span class="col sm button ~critical @low center mb-2" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
</div>
</div>
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
<table class="table">
<div class="card @low accounts-header table-responsive mt-8">
<table class="table text-base leading-4">
<thead>
<tr>
<th><input type="checkbox" value="" id="accounts-select-all"></th>
<th>{{ .strings.username }}</th>
<th class="table-inline my-2">{{ .strings.username }}</th>
<th>{{ .strings.emailAddress }}</th>
{{ if .telegramEnabled }}
<th>Telegram</th>
<th class="text-center-i">Telegram</th>
{{ end }}
{{ if .matrixEnabled }}
<th>Matrix</th>
<th class="text-center-i">Matrix</th>
{{ end }}
{{ if .discordEnabled }}
<th>Discord</th>
<th class="text-center-i">Discord</th>
{{ end }}
<th>{{ .strings.expiry }}</th>
<th>{{ .strings.lastActiveTime }}</th>
@ -631,28 +633,28 @@
</div>
</div>
<div id="tab-settings" class="unfocused">
<div class="card ~neutral !low settings overflow">
<div class="card @low dark:~d_neutral settings overflow">
<div class="flex-expand">
<div class="flex-row">
<span class="heading">{{ .strings.settings }}</span>
<label for="settings-advanced-enabled" class="button ~neutral !normal ml-1">
<label for="settings-advanced-enabled" class="button ~neutral @low ml-2">
<input type="checkbox" id="settings-advanced-enabled" aria-label="Advanced settings enabled">
<span class="ml-half">{{ .strings.advancedSettings }} </span>
<span class="ml-2">{{ .strings.advancedSettings }} </span>
</label>
</div>
<div>
<span class="button ~info !normal" id="settings-logs">{{ .strings.logs }}</span>
<span class="button ~neutral !normal" id="settings-restart">{{ .strings.settingsRestart }}</span>
<span class="button ~urge !normal unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
<span class="button ~info @low" id="settings-logs">{{ .strings.logs }}</span>
<span class="button ~neutral @low" id="settings-restart">{{ .strings.settingsRestart }}</span>
<span class="button ~urge @low unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
</div>
</div>
<div class="row">
<div class="card ~neutral !normal col" id="settings-sidebar">
<aside class="aside sm ~info mb-half" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info">R</span> indicates changes require a restart.</aside>
<span class="button ~neutral !low settings-section-button mb-half" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-half"></i></span></span>
<span class="button ~neutral !low settings-section-button mb-half" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-half"></i></span></span>
<div class="card @low dark:~d_neutral col" id="settings-sidebar">
<aside class="aside sm ~urge dark:~d_info mb-2 @low" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info dark:~d_warning">R</span> indicates changes require a restart.</aside>
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-2"></i></span></span>
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-2"></i></span></span>
</div>
<div class="card ~neutral !normal col overflow" id="settings-panel"></div>
<div class="card ~neutral @low col overflow" id="settings-panel"></div>
</div>
</div>
</div>

View File

@ -7,41 +7,37 @@
</head>
<body>
<div class="page-container">
<div class="row">
<div class="col">
<div class="card ~critical sectioned">
<section class="section ~critical">
<span class="heading">Crash report for jfa-go</span>
{{ if .Err }}
<div class="monospace pre-line mt-1 mb-1">
Error: {{ .Err }}
</div>
{{ end }}
<a class="button ~critical mb-1" target="_blank" href="https://github.com/hrfee/jfa-go/issues/new/choose">Create an Issue</a>
</section>
<section class="section ~neutral !low">
<div class="flex-expand">
<span class="subheading">Full Log</span>
<span class="button ~urge ml-half" id="copy-log">Copy</span>
</div>
<div class="row mb-1">
<label class="col mr-1">
<span class="button ~neutral !high supra full-width center" id="button-log-normal">Normal</span>
</label>
<label class="col mr-1">
<span class="button ~neutral !normal supra full-width center" id="button-log-sanitized">Sanitized</span>
</label>
</div>
<div id="log-normal">
<pre class="monospace pre-line">{{ .Log }}</pre>
</div>
<div id="log-sanitized" class="unfocused">
<p class="subheading">An attempt has been made to remove sensitive info, but make sure to check yourself.</p>
<pre class="monospace pre-line">{{ .SanitizedLog }}</pre>
</div>
</section>
<div class="card ~critical sectioned">
<section class="section ~critical">
<span class="heading">Crash report for jfa-go</span>
{{ if .Err }}
<div class="font-mono bg-inherit pre-line mt-4 mb-4">
Error: {{ .Err }}
</div>
</div>
{{ end }}
<a class="button ~critical mb-4" target="_blank" href="https://github.com/hrfee/jfa-go/issues/new/choose">Create an Issue</a>
</section>
<section class="section ~neutral @low">
<div class="flex-expand">
<span class="subheading">Full Log</span>
<span class="button ~urge ml-4" id="copy-log">Copy</span>
</div>
<div class="row mb-4">
<label class="col mr-4">
<span class="button ~neutral @high supra full-width center" id="button-log-normal">Normal</span>
</label>
<label class="col mr-4">
<span class="button ~neutral @low supra full-width center" id="button-log-sanitized">Sanitized</span>
</label>
</div>
<div id="log-normal">
<pre class="font-mono bg-inherit pre-line">{{ .Log }}</pre>
</div>
<div id="log-sanitized" class="unfocused">
<p class="subheading">An attempt has been made to remove sensitive info, but make sure to check yourself.</p>
<pre class="font-mono bg-inherit pre-line">{{ .SanitizedLog }}</pre>
</div>
</section>
</div>
</div>
<script inline src="crash.js"></script>

View File

@ -3,14 +3,14 @@
<head>
<link rel="stylesheet" type="text/css" href="css/bundle.css">
{{ template "header.html" . }}
<title>{{ .strings.successHeader }} - jfa-go</title>
<title>{{ .strings.successHeader }} - jfa-go</title>
</head>
<body class="section">
<div class="page-container">
<div class="card ~neutral !normal mb-1">
<span class="heading mb-1">{{ .strings.successHeader }}</span>
<p class="content mb-1">{{ .successMessage }}</p>
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.successContinueButton }}</a>
<div class="card ~neutral @low mb-4">
<span class="heading mb-4">{{ .strings.successHeader }}</span>
<p class="content my-4">{{ .successMessage }}</p>
<a class="button ~urge @high full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
</div>
<i class="content">{{ .contactMessage }}</i>
</div>

View File

@ -14,77 +14,79 @@
<body class="max-w-full overflow-x-hidden section">
<div id="modal-success" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}</span>
<p class="content mb-1">{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}</p>
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
<span class="heading mb-4">{{ if .passwordReset }}{{ .strings.passwordReset }}{{ else }}{{ .strings.successHeader }}{{ end }}</span>
<p class="content mb-4">{{ if .passwordReset }}{{ .strings.youCanLoginPassword }}{{ else }}{{ .successMessage }}{{ end }}</p>
<a class="button ~urge @low full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.continue }}</a>
</div>
</div>
<div id="modal-confirmation" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.confirmationRequired }}</span>
<p class="content mb-1">{{ .strings.confirmationRequiredMessage }}</p>
<span class="heading mb-4">{{ .strings.confirmationRequired }}</span>
<p class="content mb-4">{{ .strings.confirmationRequiredMessage }}</p>
</div>
</div>
{{ if .telegramEnabled }}
<div id="modal-telegram" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.linkTelegram }}</span>
<p class="content mb-1">{{ .strings.sendPIN }}</p>
<h1 class="ac">{{ .telegramPIN }}</h1>
<span class="heading mb-4">{{ .strings.linkTelegram }}</span>
<p class="content mb-4">{{ .strings.sendPIN }}</p>
<p class="text-center text-2xl mb-2">{{ .telegramPIN }}</p>
<a class="subheading link-center" href="{{ .telegramURL }}" target="_blank">
<span class="shield ~info mr-1">
<span class="shield ~info mr-4">
<span class="icon">
<i class="ri-telegram-line"></i>
</span>
</span>
&#64;{{ .telegramUsername }}
</a>
<span class="button ~info !normal full-width center mt-1" id="telegram-waiting">{{ .strings.success }}</span>
<span class="button ~info @low full-width center mt-4" id="telegram-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.linkDiscord }}</span>
<p class="content mb-1"> {{ .discordSendPINMessage }}</p>
<h1 class="ac">{{ .discordPIN }}</h1>
<span class="heading mb-4">{{ .strings.linkDiscord }}</span>
<p class="content mb-4"> {{ .discordSendPINMessage }}</p>
<h1 class="text-center text-2xl mb-2">{{ .discordPIN }}</h1>
<a id="discord-invite"></a>
<span class="button ~info !normal full-width center mt-1" id="discord-waiting">{{ .strings.success }}</span>
<span class="button ~info @low full-width center mt-4" id="discord-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}
{{ if .matrixEnabled }}
<div id="modal-matrix" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.linkMatrix }}</span>
<p class="content mb-1"> {{ .strings.matrixEnterUser }}</p>
<input type="text" class="input ~neutral !high" placeholder="@user:riot.im" id="matrix-userid">
<div class="subheading link-center mt-1">
<span class="shield ~info mr-1">
<span class="heading mb-4">{{ .strings.linkMatrix }}</span>
<p class="content mb-4"> {{ .strings.matrixEnterUser }}</p>
<input type="text" class="input ~neutral @high" placeholder="@user:riot.im" id="matrix-userid">
<div class="subheading link-center mt-4">
<span class="shield ~info mr-4">
<span class="icon">
<i class="ri-chat-3-line"></i>
</span>
</span>
{{ .matrixUser }}
</div>
<span class="button ~info !normal full-width center mt-1" id="matrix-send">{{ .strings.submit }}</span>
<span class="button ~info @low full-width center mt-4" id="matrix-send">{{ .strings.submit }}</span>
</div>
</div>
{{ end }}
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-1 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral !low" id="lang-list">
<div class="top-4 left-4 absolute">
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-2 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low" id="lang-list">
</div>
</div>
</div>
</span>
</span>
</div>
<div id="notification-box"></div>
<div class="page-container">
<div class="card ~neutral !low">
<div class="card dark:~d_neutral @low">
<div class="row baseline">
<span class="col heading">
{{ if .passwordReset }}
@ -106,41 +108,41 @@
{{ if .userExpiry }}
<aside class="col aside sm ~warning" id="user-expiry-message"></aside>
{{ end }}
<form class="card ~neutral !normal" id="form-create" href="">
<form class="card dark:~d_neutral @low" id="form-create" href="">
{{ if not .passwordReset }}
<label class="label supra">
{{ .strings.username }}
<input type="text" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
<input type="text" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
</label>
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
<input type="email" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
<input type="email" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
{{ if .telegramEnabled }}
<span class="button ~info !normal full-width center mb-1" id="link-telegram">{{ .strings.linkTelegram }}</span>
<span class="button ~info @low full-width center mb-4" id="link-telegram">{{ .strings.linkTelegram }}</span>
{{ end }}
{{ if .discordEnabled }}
<span class="button ~info !normal full-width center mb-1" id="link-discord">{{ .strings.linkDiscord }}</span>
<span class="button ~info @low full-width center mb-4" id="link-discord">{{ .strings.linkDiscord }}</span>
{{ end }}
{{ if .matrixEnabled }}
<span class="button ~info !normal full-width center mb-1" id="link-matrix">{{ .strings.linkMatrix }}</span>
<span class="button ~info @low full-width center mb-4" id="link-matrix">{{ .strings.linkMatrix }}</span>
{{ end }}
{{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
<div id="contact-via" class="unfocused">
<label class="row switch pb-1">
<label class="row switch pb-4">
<input type="radio" name="contact-via" value="email"><span>Contact through Email</span>
</label>
{{ if .telegramEnabled }}
<label class="row switch pb-1">
<label class="row switch pb-4">
<input type="radio" name="contact-via" value="telegram" id="contact-via-telegram"><span>Contact through Telegram</span>
</label>
{{ end }}
{{ if .discordEnabled }}
<label class="row switch pb-1">
<label class="row switch pb-4">
<input type="radio" name="contact-via" value="discord" id="contact-via-discord"><span>Contact through Discord</span>
</label>
{{ end }}
{{ if .matrixEnabled }}
<label class="row switch pb-1">
<label class="row switch pb-4">
<input type="radio" name="contact-via" value="matrix" id="contact-via-matrix"><span>Contact through Matrix</span>
</label>
{{ end }}
@ -148,13 +150,13 @@
{{ end }}
{{ end }}
<label class="label supra" for="create-password">{{ .strings.password }}</label>
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
<input type="password" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
<label class="label supra" for="create-reenter-password">{{ .strings.reEnterPassword }}</label>
<input type="password" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
<input type="password" class="input ~neutral @high mt-2 mb-4" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge !normal full-width center supra submit">
<span class="button ~urge @low full-width center supra submit">
{{ if .passwordReset }}
{{ .strings.reset }}
{{ else }}
@ -165,7 +167,7 @@
</form>
</div>
<div class="col">
<div class="card ~neutral !normal">
<div class="card ~neutral @low mb-4">
<span class="label supra" for="inv-uses">{{ .strings.passwordRequirementsHeader }}</span>
<ul>
{{ range $key, $value := .requirements }}
@ -176,7 +178,7 @@
</ul>
</div>
{{ if .contactMessage }}
<aside class="col aside sm ~info">{{ .contactMessage }}</aside>
<aside class="col aside sm ~info mt-4">{{ .contactMessage }}</aside>
{{ end }}
</div>
</div>

View File

@ -7,11 +7,13 @@
</head>
<body class="section">
<div class="page-container">
<h1 class="heading">Invalid invite code.</h1>
<p class="content">The code above was either incorrect, or has expired.</p>
<p class="content">
{{ .contactMessage }}
</p>
<div class="card">
<h1 class="text-3xl font-semibold">Invalid invite code.</h1>
<p class="content">The code above was either incorrect, or has expired.</p>
<p class="content">
{{ .contactMessage }}
</p>
</div>
</div>
</body>
</html>

View File

@ -12,15 +12,15 @@
</div>
{{ end }}
<div class="page-container">
<div class="card ~neutral !normal mb-1">
<span class="heading mb-1">
<div class="card ~neutral @low mb-4">
<span class="heading mb-4">
{{ if .success }}
{{ .strings.passwordReset }}
{{ else }}
{{ .strings.resetFailed }}
{{ end }}
</span>
<p class="content mb-1">
<p class="content mb-4">
{{ if .success }}
{{ if .ombiEnabled }}
{{ .strings.youCanLoginOmbi }}
@ -35,7 +35,7 @@
<aside class="aside ~warning">
{{ .strings.changeYourPassword }}
</aside>
<span class="button ~urge !normal full-width center supra p-1 mt-1" id="pin" title="{{ .strings.copy }}">{{ .pin }}</span>
<span class="button ~urge @low w-100 text-center text-xl p-1 mt-4" id="pin" title="{{ .strings.copy }}">{{ .pin }}</span>
{{ end }}
</div>
<i class="content">{{ .contactMessage }}</i>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="light-theme">
<html lang="en" class="light">
<head>
<link rel="stylesheet" type="text/css" href="css/bundle.css">
{{ template "header.html" . }}
@ -7,18 +7,20 @@
</head>
<body class="max-w-full overflow-x-hidden section">
<div id="notification-box"></div>
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-1 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral !low" id="lang-list">
<div class="top-4 left-4 absolute">
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-2 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low" id="lang-list">
</div>
</div>
</div>
</span>
</span>
</div>
<div class="page-container" id="page-container">
<div class="card ~neutral !low mb-1">
<div class="card ~neutral @low mb-2">
<div class="row">
<img class="banner header" src="banner.svg" alt="jfa-go" />
</div>
@ -26,70 +28,70 @@
<span class="heading welcome">{{ .lang.StartPage.welcome }}</span>
</div>
<div class="row col flex center">
<p class="content">{{ .lang.StartPage.pressStart }}</p>
<p class="content my-2">{{ .lang.StartPage.pressStart }}</p>
</div>
<section class="section ~neutral banner footer flex-expand middle">
<span class="support">{{ .lang.StartPage.httpsNotice }}</span>
<span class="button ~urge !normal next">{{ .lang.StartPage.start }}</span>
<span class="button ~urge @low next">{{ .lang.StartPage.start }}</span>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.Language.title }}</span>
<p class="content" id="language-description"></p>
<p class="content my-2" id="language-description"></p>
<label class="label">
<span class="mt-half">{{ .lang.Language.defaultAdminLang }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<span class="mt-4">{{ .lang.Language.defaultAdminLang }}</span>
<div class="select ~neutral @low mt-4 mb-2">
<select id="ui-language-admin">
</select>
</div>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Language.defaultFormLang }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<span class="mt-4">{{ .lang.Language.defaultFormLang }}</span>
<div class="select ~neutral @low mt-4 mb-2">
<select id="ui-language-form">
</select>
</div>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Language.defaultEmailLang }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<span class="mt-4">{{ .lang.Language.defaultEmailLang }}</span>
<div class="select ~neutral @low mt-4 mb-2">
<select id="email-language">
</select>
</div>
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.General.title }}</span>
<div class="row">
<div class="col">
<label class="label">
<span class="mt-half">{{ .lang.General.listenAddress }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="ui-host" value="0.0.0.0">
<span class="mt-4">{{ .lang.General.listenAddress }}</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ui-host" value="0.0.0.0">
</label>
<label class="row switch">
<input type="checkbox" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span>
<input type="checkbox" class="mr-2" id="advanced-tls"><span>{{ .lang.General.useHTTPS }}</span>
</label>
<p class="support mb-1">{{ .lang.General.useHTTPSNotice }}</p>
<p class="support mb-2 mt-1">{{ .lang.General.useHTTPSNotice }}</p>
<label class="label">
<span class="mt-half">{{ .lang.General.pathToCertificate }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_cert">
<span class="mt-4">{{ .lang.General.pathToCertificate }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_cert">
</label>
<label class="label">
<span class="mt-half">{{ .lang.General.pathToKeyFile }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_key">
<span class="mt-4">{{ .lang.General.pathToKeyFile }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_key">
</label>
<span class="heading">{{ .lang.Updates.title }}</span>
<p class="content" id="updates-description"></p>
<label class="row switch pb-1">
<input type="checkbox" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2" id="updates-description"></p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="updates-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span>{{ .lang.Updates.updateChannel }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<div class="select ~neutral @low mt-4 mb-2">
<select id="updates-channel">
<option value="stable">{{ .lang.Updates.stable }}</option>
<option value="unstable">{{ .lang.Updates.unstable }}</option>
@ -99,21 +101,21 @@
</div>
<div class="col">
<label class="label">
<span class="mt-half">{{ .lang.Strings.port }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="ui-port" value="8056">
<span class="mt-4">{{ .lang.Strings.port }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="ui-port" value="8056">
</label>
<label class="label">
<span class="mt-half">{{ .lang.General.httpsPort }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="advanced-tls_port" value="8057">
<span class="mt-4">{{ .lang.General.httpsPort }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="advanced-tls_port" value="8057">
</label>
<label class="label">
<span class="mt-half">{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
<input type="url" class="input ~neutral !normal mt-half" id="ui-url_base">
<p class="support mb-1">{{ .lang.General.urlBaseNotice }}</p>
<span class="mt-4">{{ .lang.General.urlBase }} ({{ .lang.Strings.optional }})</span>
<input type="url" class="input ~neutral @low mt-4" id="ui-url_base">
<p class="support mb-2 mt-1">{{ .lang.General.urlBaseNotice }}</p>
</label>
<label class="label">
<span>{{ .lang.Strings.theme }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<div class="select ~neutral @low mt-4 mb-2">
<select id="ui-theme">
<option value="Jellyfin (Dark)">{{ .lang.General.darkTheme }}</option>
<option value="Default (Light)">{{ .lang.General.lightTheme }}</option>
@ -123,142 +125,142 @@
</div>
</div>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.Login.title }}</span>
<p class="content">{{ .lang.Login.description }}</p>
<div class="pl-1">
<label class="row switch pb-1">
<input type="radio" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span>
<p class="content my-2">{{ .lang.Login.description }}</p>
<div class="pl-4">
<label class="row switch pb-4">
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="true" checked><span>{{ .lang.Login.authorizeWithJellyfin }}</span>
</label>
<label class="row switch pl-1 pb-1">
<input type="checkbox" id="ui-admin_only"><span>{{ .lang.Login.adminOnly }}</span>
<label class="row switch pl-4 pb-4">
<input type="checkbox" class="mr-2" id="ui-admin_only"><span>{{ .lang.Login.adminOnly }}</span>
</label>
<label class="row switch pb-1">
<input type="radio" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span>
<label class="row switch pb-4">
<input type="radio" class="mr-2" name="ui-jellyfin_login" value="false"><span>{{ .lang.Login.authorizeManual }}</span>
</label>
</div>
<div id="login-manual">
<label class="label">
<span class="mt-half">{{ .lang.Strings.username }}</span>
<input type="text" id="ui-username" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.username }}">
<span class="mt-4">{{ .lang.Strings.username }}</span>
<input type="text" id="ui-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
</label>
<label class="label">
<span>{{ .lang.Strings.password }}</span>
<input type="password" id="ui-password" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.password }}">
<input type="password" id="ui-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
</label>
<label class="label">
<span>{{ .lang.Strings.emailAddress }} ({{ .lang.Strings.optional }})</span>
<input type="email" id="ui-email" class="input ~neutral !normal mt-half" placeholder="email@address">
<span class="support mb-1">{{ .lang.Login.emailNotice }}</span>
<input type="email" id="ui-email" class="input ~neutral @low mt-4" placeholder="email@address">
<span class="support mb-2 mt-1">{{ .lang.Login.emailNotice }}</span>
</label>
</div>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.JellyfinEmby.title }}</span>
<p class="content">{{ .lang.JellyfinEmby.description }}</p>
<p class="content my-2">{{ .lang.JellyfinEmby.description }}</p>
<div class="row">
<div class="col">
<label class="label">
<span>{{ .lang.Strings.serverType }}</span>
<div class="select ~neutral !normal mt-half">
<div class="select ~neutral @low mt-4">
<select id="jellyfin-type">
<option value="jellyfin">Jellyfin</option>
<option value="emby">Emby</option>
</select>
</div>
<p class="support mb-1">{{ .lang.JellyfinEmby.embyNotice }}</p>
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.embyNotice }}</p>
</label>
<label class="label">
<span class="mt-half">{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
<input type="text" class="input ~neutral !normal mt-half" id="jellyfin-substitute_jellyfin_strings">
<p class="support mb-1">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
<span class="mt-4">{{ .lang.JellyfinEmby.replaceJellyfin }} ({{ .lang.Strings.optional }})</span>
<input type="text" class="input ~neutral @low mt-4" id="jellyfin-substitute_jellyfin_strings">
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.replaceJellyfinNotice }}</p>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.username }}</span>
<input type="text" id="jellyfin-username" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.username }}">
<span class="mt-4">{{ .lang.Strings.username }}</span>
<input type="text" id="jellyfin-username" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.username }}">
</label>
<label class="label">
<span>{{ .lang.Strings.password }}</span>
<input type="password" id="jellyfin-password" class="input ~neutral !normal mt-half mb-1" placeholder="{{ .lang.Strings.password }}">
<input type="password" id="jellyfin-password" class="input ~neutral @low mt-4 mb-2" placeholder="{{ .lang.Strings.password }}">
</label>
</div>
<div class="col">
<label class="label">
<span class="mt-half">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="jellyfin-server" placeholder="http://jellyf.in:80">
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.internal }})</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="jellyfin-server" placeholder="http://jellyf.in:80">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
<input type="url" class="input ~neutral !normal mt-half" id="jellyfin-public_server" placeholder="https://jellyf.in">
<p class="support mb-1">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
<span class="mt-4">{{ .lang.Strings.serverAddress }} ({{ .lang.JellyfinEmby.external }})</span>
<input type="url" class="input ~neutral @low mt-4" id="jellyfin-public_server" placeholder="https://jellyf.in">
<p class="support mb-2 mt-1">{{ .lang.JellyfinEmby.addressExternalNotice }}</p>
</label>
</div>
</div>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
<span class="button ~urge !normal next" disabled>{{ .lang.Strings.next }}</span>
<span class="button ~urge @low" id="jellyfin-test-connection">{{ .lang.JellyfinEmby.testConnection }}</span>
<span class="button ~urge @low next" disabled>{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.Ombi.title }}</span>
<p class="content">{{ .lang.Ombi.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.Ombi.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="ombi-enabled"><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.serverAddress }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="ombi-server" placeholder="ombi.jellyf.in">
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="ombi-server" placeholder="ombi.jellyf.in">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.apiKey }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="ombi-api_key">
<p class="support mb-1">{{ .lang.Ombi.apiKeyNotice }}</p>
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
<input type="text" class="input ~neutral @low mt-4" id="ombi-api_key">
<p class="support mb-2 mt-1">{{ .lang.Ombi.apiKeyNotice }}</p>
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.Messages.title }}</span>
<p class="content" id="messages-description"></p>
<label class="row switch pb-1">
<input type="checkbox" id="messages-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2" id="messages-description"></p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="messages-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Email.dateFormat }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="email-date_format" value="%d/%m/%y">
<p class="support mb-1" id="email-dateformat-notice"></p>
<span class="mt-4">{{ .lang.Email.dateFormat }}</span>
<input type="text" class="input ~neutral @low mt-4" id="email-date_format" value="%d/%m/%y">
<p class="support mb-2 mt-1" id="email-dateformat-notice"></p>
</label>
<div>
<label class="row switch pb-1">
<input type="radio" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span>
<label class="row switch pb-4">
<input type="radio" class="mr-2" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span>
</label>
<label class="row switch pb-1">
<input type="radio" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span>
<label class="row switch pb-4">
<input type="radio" class="mr-2" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span>
</label>
</div>
<div id="email-sect">
<span class="heading">{{ .lang.Email.title }}</span>
<p class="content" id="email-description"></p>
<p class="content my-2" id="email-description"></p>
<div class="row">
<div class="col">
<label class="label">
<span>{{ .lang.Email.method }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<div class="select ~neutral @low mt-4 mb-2">
<select id="email-method">
<option value="">{{ .lang.Strings.disabled }}</option>
<option value="smtp">SMTP</option>
@ -267,24 +269,24 @@
</div>
</label>
<label class="row switch">
<input type="checkbox" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span>
<p class="support mb-1">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
<input type="checkbox" class="mr-2" id="email-no_username"><span>{{ .lang.Email.useEmailAsUsername }}</span>
<p class="support mb-2 mt-1">{{ .lang.Email.useEmailAsUsernameNotice }}</p>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Email.fromAddress }}</span>
<input type="email" class="input ~neutral !normal mt-half mb-1" id="email-address" placeholder="mail@jellyf.in">
<span class="mt-4">{{ .lang.Email.fromAddress }}</span>
<input type="email" class="input ~neutral @low mt-4 mb-2" id="email-address" placeholder="mail@jellyf.in">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Email.senderName }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="email-from" value="Jellyfin">
<span class="mt-4">{{ .lang.Email.senderName }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="email-from" value="Jellyfin">
</label>
</div>
<div class="col">
<div id="email-smtp">
<p class="subheading">SMTP</p>
<p class="text-2xl font-semibold mb-2">SMTP</p>
<label class="label">
<span>{{ .lang.Email.encryption }}</span>
<div class="select ~neutral !normal mt-half mb-1">
<div class="select ~neutral @low mt-4 mb-2">
<select id="smtp-encryption">
<option value="starttls">STARTTLS ({{ .lang.Strings.port }} 587)</option>
<option value="ssl_tls">SSL/TLS ({{ .lang.Strings.port }} 465)</option>
@ -292,197 +294,197 @@
</div>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.serverAddress }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="smtp-server" placeholder="smtp.jellyf.in">
<span class="mt-4">{{ .lang.Strings.serverAddress }}</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="smtp-server" placeholder="smtp.jellyf.in">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.port }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="smtp-port" placeholder="587">
<span class="mt-4">{{ .lang.Strings.port }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="smtp-port" placeholder="587">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.username }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="smtp-username">
<span class="mt-4">{{ .lang.Strings.username }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="smtp-username">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.password }}</span>
<input type="password" class="input ~neutral !normal mt-half mb-1" id="smtp-password">
<span class="mt-4">{{ .lang.Strings.password }}</span>
<input type="password" class="input ~neutral @low mt-4 mb-2" id="smtp-password">
</label>
</div>
<div id="email-mailgun">
<p class="subheading">Mailgun</p>
<p class="text-2xl font-semibold mb-2">Mailgun</p>
<label class="label">
<span class="mt-half">{{ .lang.Email.mailgunApiURL }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
<span class="mt-4">{{ .lang.Email.mailgunApiURL }}</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.apiKey }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_key">
<span class="mt-4">{{ .lang.Strings.apiKey }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="mailgun-api_key">
</label>
</div>
</div>
</div>
</div>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused related-to-email">
<div class="card ~neutral @low mb-2 unfocused related-to-email">
<span class="heading">{{ .lang.Notifications.title }}</span>
<p class="content">{{ .lang.Notifications.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.Notifications.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="notifications-enabled"><span>{{ .lang.Strings.enabled }}</span>
</label>
<span class="heading">{{ .lang.WelcomeEmails.title }}</span>
<p class="content">{{ .lang.WelcomeEmails.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.WelcomeEmails.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="welcome_email-enabled"><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused related-to-email">
<div class="card ~neutral @low mb-2 unfocused related-to-email">
<span class="heading">{{ .lang.InviteEmails.title }}</span>
<p class="content">{{ .lang.InviteEmails.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.InviteEmails.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="invite_emails-enabled"><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.URL }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="invite_emails-url_base" placeholder="https://accounts.jellyf.in/invite">
<span class="mt-4">{{ .lang.Strings.URL }}</span>
<input type="url" class="input ~neutral @low mt-4 mb-2" id="invite_emails-url_base" placeholder="https://accounts.jellyf.in/invite">
</label>
<label class="label">
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="invite_emails-subject" placeholder="{{ .emailLang.InviteEmail.title }}">
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div id="password-resets" class="card ~neutral !low mb-1 unfocused related-to-email">
<div id="password-resets" class="card ~neutral @low mb-2 unfocused related-to-email">
<span class="heading">{{ .lang.PasswordResets.title }}</span>
<p class="content">{{ .lang.PasswordResets.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.PasswordResets.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="password_resets-enabled"><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordResets.pathToJellyfin }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="password_resets-watch_directory" placeholder="/config/jellyfin">
<p class="support mb-1">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
<span class="mt-4">{{ .lang.PasswordResets.pathToJellyfin }}</span>
<input type="text" class="input ~neutral @low mt-4" id="password_resets-watch_directory" placeholder="/config/jellyfin">
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.pathToJellyfinNotice }}</p>
</label>
<label class="switch">
<input type="checkbox" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span>
<p class="support mb-1">{{ .lang.PasswordResets.resetLinksNotice }}</p>
<input type="checkbox" class="mr-2" id="password_resets-link_reset"><span>{{ .lang.PasswordResets.resetLinks }}</span>
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.resetLinksNotice }}</p>
</label>
<label class="switch">
<input type="checkbox" id="password_resets-set_password"><span>{{ .lang.PasswordResets.setPassword }}</span>
<p class="support mb-1">{{ .lang.PasswordResets.setPasswordNotice }}</p>
<input type="checkbox" class="mr-2" id="password_resets-set_password"><span>{{ .lang.PasswordResets.setPassword }}</span>
<p class="support mb-2 mt-1">{{ .lang.PasswordResets.setPasswordNotice }}</p>
</label>
<label class="label">
<p class="mt-half">{{ .lang.PasswordResets.resetLinksLanguage }}</p>
<div class="select ~neutral !normal mt-half mb-1">
<p class="mt-4">{{ .lang.PasswordResets.resetLinksLanguage }}</p>
<div class="select ~neutral @low mt-4 mb-2">
<select id="password_resets-language">
</select>
</div>
</label>
<label class="row label">
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
<span class="mt-4">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral @low mt-4 mb-2" id="password_resets-subject" placeholder="{{ .emailLang.PasswordReset.title }}">
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.PasswordValidation.title }}</span>
<p class="content">{{ .lang.PasswordValidation.description }}</p>
<label class="row switch pb-1">
<input type="checkbox" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
<p class="content my-2">{{ .lang.PasswordValidation.description }}</p>
<label class="row switch pb-4">
<input type="checkbox" class="mr-2" id="password_validation-enabled" checked><span>{{ .lang.Strings.enabled }}</span>
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordValidation.length }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-min_length" value="8">
<span class="mt-4">{{ .lang.PasswordValidation.length }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-min_length" value="8">
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordValidation.uppercase }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-upper" value="1">
<span class="mt-4">{{ .lang.PasswordValidation.uppercase }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-upper" value="1">
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordValidation.lowercase }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-lower" value="0">
<span class="mt-4">{{ .lang.PasswordValidation.lowercase }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-lower" value="0">
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordValidation.numbers }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-number" value="0">
<span class="mt-4">{{ .lang.PasswordValidation.numbers }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-number" value="0">
</label>
<label class="label">
<span class="mt-half">{{ .lang.PasswordValidation.special }}</span>
<input type="number" class="input ~neutral !normal mt-half mb-1" id="password_validation-special" value="0">
<span class="mt-4">{{ .lang.PasswordValidation.special }}</span>
<input type="number" class="input ~neutral @low mt-4 mb-2" id="password_validation-special" value="0">
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<span class="heading">{{ .lang.HelpMessages.title }}</span>
<p class="content">{{ .lang.HelpMessages.description }}</p>
<p class="content my-2">{{ .lang.HelpMessages.description }}</p>
<label class="label">
<span class="mt-half">{{ .lang.HelpMessages.contactMessage }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="ui-contact_message">
<p class="support mb-1">{{ .lang.HelpMessages.contactMessageNotice }}</p>
<span class="mt-4">{{ .lang.HelpMessages.contactMessage }}</span>
<input type="text" class="input ~neutral @low mt-4" id="ui-contact_message">
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.contactMessageNotice }}</p>
</label>
<label class="label">
<span class="mt-half">{{ .lang.HelpMessages.helpMessage }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="ui-help_message">
<p class="support mb-1">{{ .lang.HelpMessages.helpMessageNotice }}</p>
<span class="mt-4">{{ .lang.HelpMessages.helpMessage }}</span>
<input type="text" class="input ~neutral @low mt-4" id="ui-help_message">
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.helpMessageNotice }}</p>
</label>
<label class="label">
<span class="mt-half">{{ .lang.HelpMessages.successMessage }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="ui-success_message">
<p class="support mb-1">{{ .lang.HelpMessages.successMessageNotice }}</p>
<span class="mt-4">{{ .lang.HelpMessages.successMessage }}</span>
<input type="text" class="input ~neutral @low mt-4" id="ui-success_message">
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.successMessageNotice }}</p>
</label>
<label class="label related-to-email">
<span class="mt-half">{{ .lang.HelpMessages.emailMessage }}</span>
<input type="text" class="input ~neutral !normal mt-half" id="email-message">
<p class="support mb-1">{{ .lang.HelpMessages.emailMessageNotice }}</p>
<span class="mt-4">{{ .lang.HelpMessages.emailMessage }}</span>
<input type="text" class="input ~neutral @low mt-4" id="email-message">
<p class="support mb-2 mt-1">{{ .lang.HelpMessages.emailMessageNotice }}</p>
</label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<span class="button ~neutral @low back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
<span class="button ~urge @low next">{{ .lang.Strings.next }}</span>
</div>
</section>
</div>
<div class="card ~neutral !low mb-1 unfocused">
<div class="card ~neutral @low mb-2 unfocused">
<div class="row col flex center">
<span class="heading">{{ .lang.EndPage.finished }}</span>
</div>
<div class="row col flex center">
<p class="content">{{ .lang.EndPage.restartMessage }}</p>
<p class="content my-2">{{ .lang.EndPage.restartMessage }}</p>
</div>
<div class="row col flex center">
<span class="button ~neutral !normal back mr-1">{{ .lang.Strings.back }}</span>
<span class="button ~urge !normal" id="restart">{{ .lang.Strings.submit }}</span>
<span class="button ~urge !normal unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
<span class="button ~neutral @low back mr-4">{{ .lang.Strings.back }}</span>
<span class="button ~urge @low" id="restart">{{ .lang.Strings.submit }}</span>
<span class="button ~urge @low unfocused" id="refresh">{{ .lang.EndPage.refreshPage }}</span>
</div>
</div>
</div>

View File

@ -1,13 +1,5 @@
{
"meta": {
"name": ""
},
"strings": {
"passwordReset": "",
"resetFailed": "",
"tryAgain": "",
"youCanLogin": "",
"youCanLoginOmbi": "",
"changeYourPassword": ""
"name": "English (GB)"
}
}

View File

@ -82,7 +82,7 @@ type appContext struct {
configBase settings
dataPath string
webFS httpFS
cssClass string // Default theme, "light-theme"|"dark-theme".
cssClass string // Default theme, "light"|"dark".
jellyfinLogin bool
users []User
invalidTokens []string

View File

@ -40,8 +40,8 @@ func migrateProfiles(app *appContext) {
// Migrate pre-0.2.5 bootstrap theme choice to a17t version.
func migrateBootstrap(app *appContext) {
themes := map[string]string{
"Jellyfin (Dark)": "dark-theme",
"Default (Light)": "light-theme",
"Jellyfin (Dark)": "dark",
"Default (Light)": "light",
}
if app.config.Section("ui").Key("theme").String() == "Bootstrap (Light)" {

3361
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,18 +19,21 @@
"dependencies": {
"@ts-stack/markdown": "^1.3.0",
"@types/node": "^15.0.1",
"a17t": "^0.4.0",
"a17t": "^0.10.1",
"browserslist": "^4.16.6",
"esbuild": "^0.8.57",
"inline-source": "^7.2.0",
"jsdom": "^19.0.0",
"lodash": "^4.17.21",
"mjml": "^4.8.0",
"nightwind": "github:yonson2/nightwind",
"remixicon": "^2.5.0",
"remove-markdown": "^0.3.0",
"typescript": "^4.0.3",
"uncss": "^0.17.3"
},
"devDependencies": {
"live-server": "^1.2.1"
"live-server": "^1.2.1",
"tailwindcss": "^3.0.8"
}
}

50
scripts/dark-variant.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
# scan all typescript and automatically add dark variants to color tags if they're not already present.
for f in $2/*.ts; do
# FIXME: inline html
for l in $(grep -n "~neutral\|~positive\|~urge\|~warning\|~info\|~critical" $f | sed -e 's/:.*//g'); do
# for l in $(sed -n '/classList/=' $f); do
line=$(sed -n "${l}p" $f)
echo $line | grep "classList" &> /dev/null
if [ $? -eq 0 ]; then
echo $line | sed 's/.*classList//; s/).*//' | grep "~neutral\|~positive\|~urge\|~warning\|~info\|~critical" &> /dev/null
if [ $? -eq 0 ]; then
# echo "found classList @ " $l
echo $line | grep "dark:" &>/dev/null
if [ $? -ne 0 ]; then
for color in neutral positive urge warning info critical; do
sed -i "${l},${l}s/\"~${color}\"/\"~${color}\", \"dark:~d_${color}\"/g" $f
done
fi
else
echo "FIX: classList found, but color tag wasn't in it"
fi
else
echo $line | grep "querySelector" &> /dev/null
if [ $? -ne 0 ]; then
# echo "found inline in " $f " @ " $l ", " $(sed -n "${l}p" $f)
echo $line | grep "dark:" &>/dev/null
if [ $? -ne 0 ]; then
for color in neutral positive urge warning info critical; do
sed -i "${l},${l}s/~${color}/~${color} dark:~d_${color}/g" $f
done
fi
else
echo $line | sed 's/.*querySelector//; s/).*//' | grep "~neutral\|~positive\|~urge\|~warning\|~info\|~critical" &> /dev/null
if [ $? -ne 0 ]; then
echo $line | grep "dark:" &>/dev/null
if [ $? -ne 0 ]; then
# echo "found inline in " $f " @ " $l ", " $(sed -n "${l}p" $f)
for color in neutral positive urge warning info critical; do
sed -i "${l},${l}s/~${color}/~${color} dark:~d_${color}/g" $f
done
fi
#else
#echo "FIX: querySelector found, but color tag wasn't in it: " $line
fi
fi
fi
done
done

56
scripts/missing-colors.js Normal file
View File

@ -0,0 +1,56 @@
let parser = require("jsdom");
let fs = require("fs");
let path = require("path");
const hasDark = (item) => {
for (let i = 0; i < item.classList.length; i++) {
if (item.classList[i].substring(0,5) == "dark:") {
return true;
}
}
return false;
};
const fixHTML = (infile, outfile) => {
console.log(infile, outfile)
let doc = new parser.JSDOM(fs.readFileSync(infile));
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card", "field", "textarea"]) {
let items = doc.window.document.body.querySelectorAll("."+item);
for (let i = 0; i < items.length; i++) {
let hasColor = false;
for (let color of ["neutral", "positive", "urge", "warning", "info", "critical"]) {
//console.log(color);
if (items[i].classList.contains("~"+color)) {
hasColor = true;
// console.log("adding to", items[i].classList)
if (!hasDark(items[i])) {
items[i].classList.add("dark:~d_"+color);
}
break;
}
}
if (!hasColor) {
if (!hasDark(items[i])) {
// card without ~neutral look different than with.
if (item != "card") items[i].classList.add("~neutral");
items[i].classList.add("dark:~d_neutral");
}
}
if (!items[i].classList.contains("@low") && !items[i].classList.contains("@high")) {
items[i].classList.add("@low");
}
}
}
fs.writeFileSync(outfile, doc.window.document.documentElement.outerHTML);
};
let inpath = process.argv[process.argv.length-2];
let outpath = process.argv[process.argv.length-1];
let files = fs.readdirSync(inpath);
for (let i = 0; i < files.length; i++) {
if (files[i].indexOf(".html")>=0) {
fixHTML(path.join(inpath, files[i]), path.join(outpath, files[i]));
}
}

View File

@ -143,6 +143,7 @@ func (st *Storage) loadLangSetup(filesystems ...fs.FS) error {
patchLang(&lang.Login, &english.Login)
patchLang(&lang.JellyfinEmby, &english.JellyfinEmby)
patchLang(&lang.Email, &english.Email)
patchLang(&lang.Messages, &english.Messages)
patchLang(&lang.Notifications, &english.Notifications)
patchLang(&lang.PasswordResets, &english.PasswordResets)
patchLang(&lang.InviteEmails, &english.InviteEmails)

View File

@ -53,7 +53,7 @@ sudo apt-get install jfa-go-tray
</div>
</div>
<div class="page-container" id="page-container">
<div class="card ~neutral !low mb-1">
<div class="card ~neutral @low mb-1">
<div class="row col flex center">
<span class="heading welcome">jellyfin-accounts (go)</span>
</div>
@ -84,19 +84,19 @@ sudo apt-get install jfa-go-tray
<span class="ml-1 chev"></span>
</a>
<div class="dropdown-display">
<div class="card ~info !low">
<div class="card ~info @low">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">Ko-fi</a>
</div>
</div>
</div>
<a class="button ~urge mt-1 mb-1 !normal discord" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-half"></i>discord</a>
<a class="button ~urge mt-1 mb-1 @low discord" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-half"></i>discord</a>
</div>
<p class="row col flex center supra">downloads</p>
<p class="row col flex center support">instructions can be found&nbsp<a target="_blank" href="https://github.com/hrfee/jfa-go#install">here</a></p>
<p class="row col flex center text-center support">note: tray icon builds should only be used on systems with a Desktop Interface, and require extra dependencies on linux, see the github README for more info.</p>
<div class="row col flex center">
<span class="button ~neutral !high mr-1 mt-1" id="download-stable">Stable</span>
<span class="button ~neutral @high mr-1 mt-1" id="download-stable">Stable</span>
<span class="button ~neutral mt-1 mr-1" id="download-unstable">Unstable</span>
</div>
<div class="mt-1" id="sect-stable">

View File

@ -29,8 +29,8 @@ const dockerUnstable = document.getElementById("docker-unstable");
stableButton.onclick = () => {
debUnstable.classList.add("unfocused");
dockerUnstable.classList.add("unfocused");
stableButton.classList.add("!high");
unstableButton.classList.remove("!high");
stableButton.classList.add("@high");
unstableButton.classList.remove("@high");
stableSect.classList.remove("unfocused");
unstableSect.classList.add("unfocused");
@ -39,8 +39,8 @@ stableButton.onclick = () => {
unstableButton.onclick = () => {
debUnstable.classList.remove("unfocused");
dockerUnstable.classList.remove("unfocused");
unstableButton.classList.add("!high");
stableButton.classList.remove("!high");
unstableButton.classList.add("@high");
stableButton.classList.remove("@high");
stableSect.classList.add("unfocused");
unstableSect.classList.remove("unfocused");
}

View File

@ -39,7 +39,7 @@ export const loadBuilds = () => {
<span class="ml-half chev"></span>
</span>
<div class="dropdown-display above">
<div class="card ~info !low">
<div class="card ~info @low">
`;
for (let arch in categories[buildName]) {
innerHTML += `

26
tailwind.config.js Normal file
View File

@ -0,0 +1,26 @@
let colors = require("tailwindcss/colors")
let dark = require("./css/dark");
module.exports = {
content: ["./data/html/*.html", "./build/data/html/*.html", "./ts/*.ts", "./ts/modules/*.ts"],
darkMode: 'class',
theme: {
extend: {
colors: {
neutral: colors.slate,
positive: colors.green,
urge: colors.violet,
warning: colors.yellow,
info: colors.blue,
critical: colors.red,
d_neutral: dark.d_neutral,
d_positive: dark.d_positive,
d_urge: dark.d_urge,
d_warning: dark.d_warning,
d_info: dark.d_info,
d_critical: dark.d_critical
}
}
},
plugins: [require("a17t")],
}

View File

@ -1,4 +1,4 @@
import { toggleTheme, loadTheme } from "./modules/theme.js";
import { nightwind } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { Tabs } from "./modules/tabs.js";
@ -9,26 +9,27 @@ import { ProfileEditor } from "./modules/profiles.js";
import { _get, _post, notificationBox, whichAnimationEvent, toggleLoader } from "./modules/common.js";
import { Updater } from "./modules/update.js";
loadTheme();
let theme = new nightwind();
const themeButton = document.getElementById('button-theme') as HTMLSpanElement;
const switchThemeIcon = () => {
const icon = themeButton.childNodes[0] as HTMLElement;
if (document.documentElement.classList.contains("dark-theme")) {
if (document.documentElement.classList.contains("dark")) {
icon.classList.add("ri-sun-line");
icon.classList.remove("ri-moon-line");
themeButton.classList.add("~warning");
themeButton.classList.remove("~neutral");
themeButton.classList.remove("!high");
themeButton.classList.remove("@high");
} else {
icon.classList.add("ri-moon-line");
icon.classList.remove("ri-sun-line");
themeButton.classList.add("!high");
themeButton.classList.add("@high");
themeButton.classList.add("~neutral");
themeButton.classList.remove("~warning");
}
};
themeButton.onclick = () => {
toggleTheme();
theme.toggle();
switchThemeIcon();
}
switchThemeIcon();
@ -119,10 +120,10 @@ window.notifications = new notificationBox(document.getElementById('notification
const user = document.getElementById('radio-use-user') as HTMLInputElement;
const profileSelect = document.getElementById('modify-user-profiles') as HTMLDivElement;
const userSelect = document.getElementById('modify-user-users') as HTMLDivElement;
(user.nextElementSibling as HTMLSpanElement).classList.toggle('!normal');
(user.nextElementSibling as HTMLSpanElement).classList.toggle('!high');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('!normal');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('!high');
(user.nextElementSibling as HTMLSpanElement).classList.toggle('@low');
(user.nextElementSibling as HTMLSpanElement).classList.toggle('@high');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('@low');
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('@high');
profileSelect.classList.toggle('unfocused');
userSelect.classList.toggle('unfocused');
}*/

View File

@ -11,17 +11,17 @@ const buttonChange = (type: string) => {
if (type == "normal") {
logSanitized.classList.add("unfocused");
logNormal.classList.remove("unfocused");
buttonNormal.classList.add("!high");
buttonNormal.classList.remove("!normal");
buttonSanitized.classList.add("!normal");
buttonSanitized.classList.remove("!high");
buttonNormal.classList.add("@high");
buttonNormal.classList.remove("@low");
buttonSanitized.classList.add("@low");
buttonSanitized.classList.remove("@high");
} else {
logNormal.classList.add("unfocused");
logSanitized.classList.remove("unfocused");
buttonSanitized.classList.add("!high");
buttonSanitized.classList.remove("!normal");
buttonNormal.classList.add("!normal");
buttonNormal.classList.remove("!high");
buttonSanitized.classList.add("@high");
buttonSanitized.classList.remove("@low");
buttonNormal.classList.add("@low");
buttonNormal.classList.remove("@high");
}
}
buttonNormal.onclick = () => buttonChange("normal");

View File

@ -101,7 +101,7 @@ if (window.discordEnabled) {
link.classList.add("subheading", "link-center");
link.href = inv.invite;
link.target = "_blank";
link.innerHTML = `<span class="img-circle lg mr-1"><img class="img-circle" src="${inv.icon}" width="64" height="64"></span>${window.discordServerName}`;
link.innerHTML = `<span class="img-circle lg mr-4"><img class="img-circle" src="${inv.icon}" width="64" height="64"></span>${window.discordServerName}`;
}
});
}

View File

@ -86,10 +86,10 @@ class user implements User {
get admin(): boolean { return this._admin.classList.contains("chip"); }
set admin(state: boolean) {
if (state) {
this._admin.classList.add("chip", "~info", "ml-1");
this._admin.classList.add("chip", "~info", "ml-4");
this._admin.textContent = window.lang.strings("admin");
} else {
this._admin.classList.remove("chip", "~info", "ml-1");
this._admin.classList.remove("chip", "~info", "ml-4");
this._admin.textContent = "";
}
}
@ -97,10 +97,10 @@ class user implements User {
get disabled(): boolean { return this._disabled.classList.contains("chip"); }
set disabled(state: boolean) {
if (state) {
this._disabled.classList.add("chip", "~warning", "ml-1");
this._disabled.classList.add("chip", "~warning", "ml-4");
this._disabled.textContent = window.lang.strings("disabled");
} else {
this._disabled.classList.remove("chip", "~warning", "ml-1");
this._disabled.classList.remove("chip", "~warning", "ml-4");
this._disabled.textContent = "";
}
}
@ -130,30 +130,30 @@ class user implements User {
const matrix = this._matrixID != "";
if (!telegram && !discord && !matrix) return;
let innerHTML = `
<i class="icon ri-settings-2-line ml-half dropdown-button"></i>
<i class="icon ri-settings-2-line ml-2 dropdown-button"></i>
<div class="dropdown manual">
<div class="dropdown-display lg">
<div class="card ~neutral !low">
<div class="card ~neutral @low">
<span class="supra sm">${window.lang.strings("contactThrough")}</span>
<label class="row switch pb-1 mt-half">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-email">
<label class="row switch pb-4 mt-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-email mr-2">
</span>Email</span>
</label>
<div class="accounts-area-telegram">
<label class="row switch pb-1">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-telegram">
<label class="row switch pb-4">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-telegram mr-2">
<span>Telegram</span>
</label>
</div>
<div class="accounts-area-discord">
<label class="row switch pb-1">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-discord">
<label class="row switch pb-4">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-discord mr-2">
<span>Discord</span>
</label>
</div>
<div class="accounts-area-matrix">
<label class="row switch pb-1">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-matrix">
<label class="row switch pb-4">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-matrix mr-2">
<span>Matrix</span>
</label>
</div>
@ -193,8 +193,10 @@ class user implements User {
if (!u) {
this._notifyDropdown.querySelector(".accounts-area-matrix").classList.add("unfocused");
this._matrix.innerHTML = `
<span class="chip btn !low">${window.lang.strings("add")}</span>
<input type="text" class="input ~neutral !normal stealth-input unfocused" placeholder="@user:riot.im">
<div class="table-inline justify-center">
<span class="chip btn @low"><i class="ri-link" alt="${window.lang.strings("add")}"></i></span>
<input type="text" class="input ~neutral @low stealth-input unfocused" placeholder="@user:riot.im">
</div>
`;
(this._matrix.querySelector("span") as HTMLSpanElement).onclick = this._addMatrix;
} else {
@ -212,15 +214,20 @@ class user implements User {
private _addMatrix = () => {
const addButton = this._matrix.querySelector(".btn") as HTMLSpanElement;
const icon = this._matrix.querySelector("i");
const input = this._matrix.querySelector("input.stealth-input") as HTMLInputElement;
const addIcon = addButton.querySelector("i");
if (addButton.classList.contains("chip")) {
input.classList.remove("unfocused");
addButton.innerHTML = `<i class="ri-check-line"></i>`;
addIcon.classList.add("ri-check-line");
addIcon.classList.remove("ri-link");
addButton.classList.remove("chip")
if (icon) {
icon.classList.add("unfocused");
}
const outerClickListener = (event: Event) => {
if (!(event.target instanceof HTMLElement && (this._matrix.contains(event.target) || addButton.contains(event.target)))) {
document.dispatchEvent(new CustomEvent("accounts-reload"));
document.removeEventListener("click", outerClickListener);
}
};
document.addEventListener("click", outerClickListener);
} else {
if (input.value.charAt(0) != "@" || !input.value.includes(":")) return;
const send = {
@ -257,7 +264,7 @@ class user implements User {
this._telegramUsername = u;
if (!u) {
this._notifyDropdown.querySelector(".accounts-area-telegram").classList.add("unfocused");
this._telegram.innerHTML = `<span class="chip btn !low">${window.lang.strings("add")}</span>`;
this._telegram.innerHTML = `<div class="table-inline justify-center"><span class="chip btn @low"><i class="ri-link" alt="${window.lang.strings("add")}"></i></span></div>`;
(this._telegram.querySelector("span") as HTMLSpanElement).onclick = this._addTelegram;
} else {
this._notifyDropdown.querySelector(".accounts-area-telegram").classList.remove("unfocused");
@ -322,7 +329,7 @@ class user implements User {
const lastNotifyMethod = this.lastNotifyMethod() == "discord";
this._discordUsername = u;
if (!u) {
this._discord.innerHTML = `<span class="chip btn !low">Add</span>`;
this._discord.innerHTML = `<div class="table-inline justify-center"><span class="chip btn @low"><i class="ri-link" alt="${window.lang.strings("add")}"></i></span></div>`;
(this._discord.querySelector("span") as HTMLSpanElement).onclick = () => addDiscord(this.id);
this._notifyDropdown.querySelector(".accounts-area-discord").classList.add("unfocused");
} else {
@ -380,8 +387,8 @@ class user implements User {
this._row = document.createElement("tr") as HTMLTableRowElement;
let innerHTML = `
<td><input type="checkbox" value=""></td>
<td><div class="table-inline"><span class="accounts-username"></span> <span class="accounts-admin"></span> <span class="accounts-disabled"></span></span></td>
<td><div class="table-inline"><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-half"></span></div></td>
<td><div class="table-inline"><span class="accounts-username py-2"></span> <span class="accounts-admin"></span> <span class="accounts-disabled"></span></span></td>
<td><div class="table-inline"><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-2"></span></div></td>
`;
if (window.telegramEnabled) {
innerHTML += `
@ -400,10 +407,10 @@ class user implements User {
}
innerHTML += `
<td class="accounts-expiry"></td>
<td class="accounts-last-active"></td>
<td class="accounts-last-active whitespace-nowrap"></td>
`;
this._row.innerHTML = innerHTML;
const emailEditor = `<input type="email" class="input ~neutral !normal stealth-input">`;
const emailEditor = `<input type="email" class="input ~neutral @low stealth-input">`;
this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement;
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
@ -423,10 +430,10 @@ class user implements User {
if (this._emailEditButton.classList.contains("ri-edit-line")) {
this._email.innerHTML = emailEditor;
this._email.querySelector("input").value = this._emailAddress;
this._email.classList.remove("ml-half");
this._email.classList.remove("ml-2");
} else {
this._email.textContent = this._emailAddress;
this._email.classList.add("ml-half");
this._email.classList.add("ml-2");
}
this._emailEditButton.classList.toggle("ri-check-line");
this._emailEditButton.classList.toggle("ri-edit-line");
@ -902,7 +909,7 @@ export class accountsList {
if (req.readyState == 4) {
const preview = document.getElementById("announce-preview") as HTMLDivElement;
if (req.status != 200) {
preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
preview.innerHTML = `<pre class="preview-content" class="font-mono bg-inherit"></pre>`;
window.modals.announce.show();
this._previewLoaded = false;
return;
@ -910,7 +917,7 @@ export class accountsList {
let templ = req.response as templateEmail;
if (!templ.html) {
preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
preview.innerHTML = `<pre class="preview-content" class="font-mono bg-inherit"></pre>`;
this._previewLoaded = false;
} else {
preview.innerHTML = templ.html;
@ -938,9 +945,9 @@ export class accountsList {
dList.textContent = '';
for (let name of list) {
const el = document.createElement("div") as HTMLDivElement;
el.classList.add("flex-expand", "ellipsis", "mt-half");
el.classList.add("flex-expand", "ellipsis", "mt-2");
el.innerHTML = `
<span class="button ~neutral sm full-width accounts-announce-template-button">${name}</span><span class="button ~critical fr ml-1 accounts-announce-template-delete">&times;</span>
<span class="button ~neutral sm full-width accounts-announce-template-button">${name}</span><span class="button ~critical fr ml-4 accounts-announce-template-delete">&times;</span>
`;
(el.querySelector("span.accounts-announce-template-button") as HTMLSpanElement).onclick = () => {
_get("/users/announce/" + name, null, (req: XMLHttpRequest) => {
@ -1281,17 +1288,17 @@ export class accountsList {
if (this._modifySettingsProfile.checked) {
this._userSelect.parentElement.classList.add("unfocused");
this._profileSelect.parentElement.classList.remove("unfocused")
profileSpan.classList.add("!high");
profileSpan.classList.remove("!normal");
userSpan.classList.remove("!high");
userSpan.classList.add("!normal");
profileSpan.classList.add("@high");
profileSpan.classList.remove("@low");
userSpan.classList.remove("@high");
userSpan.classList.add("@low");
} else {
this._userSelect.parentElement.classList.remove("unfocused");
this._profileSelect.parentElement.classList.add("unfocused");
userSpan.classList.add("!high");
userSpan.classList.remove("!normal");
profileSpan.classList.remove("!high");
profileSpan.classList.add("!normal");
userSpan.classList.add("@high");
userSpan.classList.remove("@low");
profileSpan.classList.remove("@high");
profileSpan.classList.add("@low");
}
};
this._modifySettingsProfile.onchange = checkSource;

View File

@ -104,14 +104,14 @@ export class notificationBox implements NotificationBox {
private _error = (message: string): HTMLElement => {
const noti = document.createElement('aside');
noti.classList.add("aside", "~critical", "!normal", "mt-half", "notification-error");
noti.classList.add("aside", "~critical", "@low", "mt-2", "notification-error");
let error = "";
if (window.lang) {
error = window.lang.strings("error") + ":"
}
noti.innerHTML = `<strong>${error}</strong> ${message}`;
const closeButton = document.createElement('span') as HTMLSpanElement;
closeButton.classList.add("button", "~critical", "!low", "ml-1");
closeButton.classList.add("button", "~critical", "@low", "ml-4");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => { this._box.removeChild(noti); };
noti.appendChild(closeButton);
@ -120,10 +120,10 @@ export class notificationBox implements NotificationBox {
private _positive = (bold: string, message: string): HTMLElement => {
const noti = document.createElement('aside');
noti.classList.add("aside", "~positive", "!normal", "mt-half", "notification-positive");
noti.classList.add("aside", "~positive", "@low", "mt-2", "notification-positive");
noti.innerHTML = `<strong>${bold}</strong> ${message}`;
const closeButton = document.createElement('span') as HTMLSpanElement;
closeButton.classList.add("button", "~positive", "!low", "ml-1");
closeButton.classList.add("button", "~positive", "@low", "ml-4");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => { this._box.removeChild(noti); };
noti.appendChild(closeButton);

View File

@ -22,13 +22,13 @@ export function newDiscordSearch(title: string, description: string, buttonText:
}
list.innerHTML = ``;
addLoader(list);
list.parentElement.classList.add("mb-1", "mt-1");
list.parentElement.classList.add("mb-4", "mt-4");
timer = setTimeout(() => {
_get("/users/discord/" + input.value, null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
if (req.status != 200) {
removeLoader(list);
list.parentElement.classList.remove("mb-1", "mt-1");
list.parentElement.classList.remove("mb-4", "mt-4");
return;
}
const users = req.response["users"] as Array<DiscordUser>;
@ -39,18 +39,18 @@ export function newDiscordSearch(title: string, description: string, buttonText:
<td class="img-circle sm">
<img class="img-circle" src="${users[i].avatar_url}" width="32" height="32">
</td>
<td class="w-100 sm">
<td class="sm">
<p class="content">${users[i].name}</p>
</td>
<td class="sm">
<span id="discord-user-${users[i].id}" class="button ~info !high">${buttonText}</span>
<td class="sm float-right">
<span id="discord-user-${users[i].id}" class="button ~info @high">${buttonText}</span>
</td>
</tr>
`;
}
list.innerHTML = innerHTML;
removeLoader(list);
list.parentElement.classList.remove("mb-1", "mt-1");
list.parentElement.classList.remove("mb-4", "mt-4");
for (let i = 0; i < users.length; i++) {
const button = document.getElementById(`discord-user-${users[i].id}`) as HTMLInputElement;
button.onclick = () => buttonFunction(users[i], event.detail);

View File

@ -93,14 +93,14 @@ class DOMInvite implements Invite {
const chip = container.querySelector("span.inv-email-chip");
const tooltip = container.querySelector("span.content") as HTMLSpanElement;
if (address == "") {
container.classList.remove("mr-1");
container.classList.remove("mr-4");
icon.classList.remove("ri-mail-line");
icon.classList.remove("ri-mail-close-line");
chip.classList.remove("~neutral");
chip.classList.remove("~critical");
chip.classList.remove("chip");
} else {
container.classList.add("mr-1");
container.classList.add("mr-4");
chip.classList.add("chip");
if (address.includes("Failed")) {
icon.classList.remove("ri-mail-line");
@ -251,14 +251,14 @@ class DOMInvite implements Invite {
this._header = document.createElement('div') as HTMLDivElement;
this._container.appendChild(this._header);
this._header.classList.add("card", "~neutral", "!normal", "inv-header", "elem-pad", "no-pad", "flex-expand", "row", "mt-half", "overflow-y");
this._header.classList.add("card", "dark:~d_neutral", "@low", "inv-header", "elem-pad", "no-pad", "flex-expand", "row", "mt-2", "overflow-y");
this._codeArea = document.createElement('div') as HTMLDivElement;
this._header.appendChild(this._codeArea);
this._codeArea.classList.add("inv-codearea");
this._codeArea.innerHTML = `
<a class="invite-link code monospace mr-1" href=""></a>
<span class="button ~info !normal" title="${window.lang.strings("copy")}"><i class="ri-file-copy-line"></i></span>
<a class="invite-link code font-mono bg-inherit mr-4" href=""></a>
<span class="button ~info @low" title="${window.lang.strings("copy")}"><i class="ri-file-copy-line"></i></span>
`;
const copyButton = this._codeArea.querySelector("span.button") as HTMLSpanElement;
copyButton.onclick = () => {
@ -284,8 +284,8 @@ class DOMInvite implements Invite {
<span class="inv-email-chip"><i></i></span>
<span class="content sm"></span>
</div>
<span class="inv-duration mr-1"></span>
<span class="button ~critical !normal inv-delete">${window.lang.strings("delete")}</span>
<span class="inv-duration mr-4"></span>
<span class="button ~critical @low inv-delete">${window.lang.strings("delete")}</span>
<label>
<i class="icon clickable ri-arrow-down-s-line not-rotated"></i>
<input class="inv-toggle-details unfocused" type="checkbox">
@ -304,7 +304,7 @@ class DOMInvite implements Invite {
this._details = document.createElement('div') as HTMLDivElement;
this._container.appendChild(this._details);
this._details.classList.add("card", "~neutral", "!normal", "mt-half", "no-pad", "inv-details");
this._details.classList.add("card", "~neutral", "@low", "mt-2", "no-pad", "inv-details");
const detailsInner = document.createElement('div') as HTMLDivElement;
this._details.appendChild(detailsInner);
detailsInner.classList.add("inv-row", "flex-expand", "row", "elem-pad", "align-top");
@ -313,8 +313,8 @@ class DOMInvite implements Invite {
detailsInner.appendChild(this._left);
this._left.classList.add("inv-profilearea");
let innerHTML = `
<p class="supra mb-1 top">${window.lang.strings("profile")}</p>
<div class="select ~neutral !normal inv-profileselect inline-block">
<p class="supra mb-2 top">${window.lang.strings("profile")}</p>
<div class="select ~neutral @low inv-profileselect inline-block mb-2">
<select>
<option value="noProfile" selected>${window.lang.strings("inviteNoProfile")}</option>
</select>
@ -322,7 +322,7 @@ class DOMInvite implements Invite {
`;
if (window.notificationsEnabled) {
innerHTML += `
<p class="label supra">${window.lang.strings("notifyEvent")}</p>
<p class="label supra mb-2">${window.lang.strings("notifyEvent")}</p>
<label class="switch block">
<input class="inv-notify-expiry" type="checkbox">
<span>${window.lang.strings("notifyInviteExpiry")}</span>
@ -348,14 +348,14 @@ class DOMInvite implements Invite {
detailsInner.appendChild(this._middle);
this._middle.classList.add("block");
this._middle.innerHTML = `
<p class="supra mb-1 top">${window.lang.strings("inviteDateCreated")} <strong class="inv-created"></strong></p>
<p class="supra mb-1">${window.lang.strings("inviteRemainingUses")} <strong class="inv-remaining"></strong></p>
<p class="supra mb-1"><span class="user-expiry"></span> <strong class="user-expiry-time"></strong></p>
<p class="supra mb-4 top">${window.lang.strings("inviteDateCreated")} <strong class="inv-created"></strong></p>
<p class="supra mb-4">${window.lang.strings("inviteRemainingUses")} <strong class="inv-remaining"></strong></p>
<p class="supra mb-4"><span class="user-expiry"></span> <strong class="user-expiry-time"></strong></p>
`;
this._right = document.createElement('div') as HTMLDivElement;
detailsInner.appendChild(this._right);
this._right.classList.add("card", "~neutral", "!low", "inv-created-users");
this._right.classList.add("card", "~neutral", "@low", "inv-created-users");
this._right.innerHTML = `<strong class="supra table-header">${window.lang.strings("inviteUsersCreated")}</strong>`;
this._userTable = document.createElement('div') as HTMLDivElement;
this._right.appendChild(this._userTable);
@ -425,9 +425,9 @@ export class inviteList implements inviteList {
this._list.classList.add("empty");
this._list.innerHTML = `
<div class="inv inv-empty">
<div class="card ~neutral !normal inv-header flex-expand mt-half">
<div class="card dark:~d_neutral @low inv-header flex-expand mt-2">
<div class="inv-codearea">
<span class="code monospace">${window.lang.strings("inviteNoInvites")}</span>
<span class="code font-mono bg-inherit">${window.lang.strings("inviteNoInvites")}</span>
</div>
</div>
</div>
@ -787,17 +787,17 @@ export class createInvite {
if (this._invDurationButton.checked) {
this._invDuration.classList.remove("unfocused");
this._userExpiry.classList.add("unfocused");
invSpan.classList.add("!high");
invSpan.classList.remove("!normal");
userSpan.classList.add("!normal");
userSpan.classList.remove("!high");
invSpan.classList.add("@high");
invSpan.classList.remove("@low");
userSpan.classList.add("@low");
userSpan.classList.remove("@high");
} else if (this._userExpiryButton.checked) {
this._userExpiry.classList.remove("unfocused");
this._invDuration.classList.add("unfocused");
invSpan.classList.add("!normal");
invSpan.classList.remove("!high");
userSpan.classList.add("!high");
userSpan.classList.remove("!normal");
invSpan.classList.add("@low");
invSpan.classList.remove("@high");
userSpan.classList.add("@high");
userSpan.classList.remove("@low");
}
};

View File

@ -68,6 +68,8 @@ export const loadLangSelector = (page: string) => {
t12.checked = false;
}
}
let queryString = new URLSearchParams(window.location.search);
if (queryString.has("lang")) queryString.delete("lang");
_get("/lang/" + page, null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
if (req.status != 200) {
@ -77,7 +79,9 @@ export const loadLangSelector = (page: string) => {
const list = document.getElementById("lang-list") as HTMLDivElement;
let innerHTML = '';
for (let code in req.response) {
innerHTML += `<a href="?lang=${code}" class="button input ~neutral field mb-half lang-link">${req.response[code]}</a>`;
queryString.set("lang", code);
innerHTML += `<a href="?${queryString.toString()}" class="button w-100 al justify-start ~neutral mb-2 lang-link">${req.response[code]}</a>`;
queryString.delete("lang");
}
list.innerHTML = innerHTML;
}

View File

@ -23,10 +23,10 @@ class profile implements Profile {
get admin(): boolean { return this._adminChip.classList.contains("chip"); }
set admin(state: boolean) {
if (state) {
this._adminChip.classList.add("chip", "~info", "ml-half");
this._adminChip.classList.add("chip", "~info", "ml-2");
this._adminChip.textContent = "Admin";
} else {
this._adminChip.classList.remove("chip", "~info", "ml-half");
this._adminChip.classList.remove("chip", "~info", "ml-2");
this._adminChip.textContent = "";
}
}
@ -62,12 +62,12 @@ class profile implements Profile {
<td><input type="radio" name="profile-default"></td>
`;
if (window.ombiEnabled) innerHTML += `
<td><span class="button !normal profile-ombi"></span></td>
<td><span class="button @low profile-ombi"></span></td>
`;
innerHTML += `
<td class="profile-from ellipsis"></td>
<td class="profile-libraries"></td>
<td><span class="button ~critical !normal">${window.lang.strings("delete")}</span></td>
<td><span class="button ~critical @low">${window.lang.strings("delete")}</span></td>
`;
this._row.innerHTML = innerHTML;
this._name = this._row.querySelector("b.profile-name");

View File

@ -91,10 +91,10 @@ class DOMInput {
get requires_restart(): boolean { return this._restart.classList.contains("badge"); }
set requires_restart(state: boolean) {
if (state) {
this._restart.classList.add("badge", "~info");
this._restart.classList.add("badge", "~info", "dark:~d_warning");
this._restart.textContent = "R";
} else {
this._restart.classList.remove("badge", "~info");
this._restart.classList.remove("badge", "~info", "dark:~d_warning");
this._restart.textContent = "";
}
}
@ -109,7 +109,7 @@ class DOMInput {
<i class="icon ri-information-line"></i>
<span class="content sm"></span>
</div>
<input type="${inputType}" class="input ~neutral !normal mt-half mb-half">
<input type="${inputType}" class="input ~neutral @low mt-2 mb-2">
</label>
`;
this._tooltip = this._container.querySelector("div.setting-tooltip") as HTMLDivElement;
@ -250,10 +250,10 @@ class DOMBool implements SBool {
get requires_restart(): boolean { return this._restart.classList.contains("badge"); }
set requires_restart(state: boolean) {
if (state) {
this._restart.classList.add("badge", "~info");
this._restart.classList.add("badge", "~info", "dark:~d_warning");
this._restart.textContent = "R";
} else {
this._restart.classList.remove("badge", "~info");
this._restart.classList.remove("badge", "~info", "dark:~d_warning");
this._restart.textContent = "";
}
}
@ -263,7 +263,7 @@ class DOMBool implements SBool {
this._container = document.createElement("div");
this._container.classList.add("setting");
this._container.innerHTML = `
<label class="switch mb-half">
<label class="switch mb-2">
<input type="checkbox">
<span class="setting-label"></span> <span class="setting-required"></span> <span class="setting-restart"></span>
<div class="setting-tooltip tooltip right unfocused">
@ -372,10 +372,10 @@ class DOMSelect implements SSelect {
get requires_restart(): boolean { return this._restart.classList.contains("badge"); }
set requires_restart(state: boolean) {
if (state) {
this._restart.classList.add("badge", "~info");
this._restart.classList.add("badge", "~info", "dark:~d_warning");
this._restart.textContent = "R";
} else {
this._restart.classList.remove("badge", "~info");
this._restart.classList.remove("badge", "~info", "dark:~d_warning");
this._restart.textContent = "";
}
}
@ -403,7 +403,7 @@ class DOMSelect implements SSelect {
<i class="icon ri-information-line"></i>
<span class="content sm"></span>
</div>
<div class="select ~neutral !normal mt-half mb-half">
<div class="select ~neutral @low mt-2 mb-2">
<select class="settings-select"></select>
</div>
</label>
@ -436,7 +436,7 @@ class DOMSelect implements SSelect {
message.innerHTML = window.lang.var("strings",
"settingsRequiredOrRestartMessage",
`<span class="badge ~critical">*</span>`,
`<span class="badge ~info">R</span>`
`<span class="badge ~info dark:~d_warning">R</span>`
);
this.update(setting);
@ -472,7 +472,7 @@ class sectionPanel {
this._section.classList.add("settings-section", "unfocused");
this._section.innerHTML = `
<span class="heading">${s.meta.name}</span>
<p class="support lg">${s.meta.description}</p>
<p class="support lg my-2">${s.meta.description}</p>
`;
this.update(s);
@ -549,7 +549,7 @@ export class settingsList {
this._sections[name] = section;
this._panel.appendChild(this._sections[name].asElement());
const button = document.createElement("span") as HTMLSpanElement;
button.classList.add("button", "~neutral", "!low", "settings-section-button", "mb-half");
button.classList.add("button", "~neutral", "@low", "settings-section-button", "justify-between", "mb-2");
button.textContent = s.meta.name;
if (subButton) { button.appendChild(subButton); }
button.onclick = () => { this._showPanel(name); };
@ -717,7 +717,7 @@ export class settingsList {
const editButton = document.createElement("div");
editButton.classList.add("tooltip", "left");
editButton.innerHTML = `
<span class="button ~neutral !normal">
<span class="button ~neutral @low">
<i class="icon ri-edit-line"></i>
</span>
<span class="content sm">
@ -738,7 +738,7 @@ export class settingsList {
const addButton = document.createElement("div");
addButton.classList.add("tooltip", "left");
addButton.innerHTML = `
<span class="button ~neutral !normal">+</span>
<span class="button ~neutral @low">+</span>
<span class="content sm">
${window.lang.strings("linkMatrix")}
</span>
@ -804,7 +804,7 @@ class EmailEditor {
this._templ = req.response as templateEmail;
this._textArea.value = this._templ.content;
if (this._templ.html == "") {
this._preview.innerHTML = `<pre class="preview-content" class="monospace"></pre>`;
this._preview.innerHTML = `<pre class="preview-content" class="font-mono bg-inherit"></pre>`;
} else {
this._preview.innerHTML = this._templ.html;
}
@ -815,7 +815,7 @@ class EmailEditor {
let innerHTML = '';
for (let i = 0; i < this._templ.variables.length; i++) {
let ci = i % colors.length;
innerHTML += '<span class="button ~' + colors[ci] +' !normal mb-1" style="margin-left: 0.25rem; margin-right: 0.25rem;"></span>'
innerHTML += '<span class="button ~' + colors[ci] +' @low mb-4" style="margin-left: 0.25rem; margin-right: 0.25rem;"></span>'
}
if (this._templ.variables.length == 0) {
this._variablesLabel.classList.add("unfocused");
@ -825,7 +825,7 @@ class EmailEditor {
this._variables.innerHTML = innerHTML
let buttons = this._variables.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
for (let i = 0; i < this._templ.variables.length; i++) {
buttons[i].innerHTML = `<span class="monospace">` + this._templ.variables[i] + `</span>`;
buttons[i].innerHTML = `<span class="font-mono bg-inherit">` + this._templ.variables[i] + `</span>`;
buttons[i].onclick = () => {
insertText(this._textArea, this._templ.variables[i]);
this.loadPreview();
@ -839,13 +839,13 @@ class EmailEditor {
} else {
for (let i = this._templ.conditionals.length-1; i >= 0; i--) {
let ci = i % colors.length;
innerHTML += '<span class="button ~' + colors[ci] +' !normal mb-1" style="margin-left: 0.25rem; margin-right: 0.25rem;"></span>'
innerHTML += '<span class="button ~' + colors[ci] +' @low mb-4" style="margin-left: 0.25rem; margin-right: 0.25rem;"></span>'
}
this._conditionalsLabel.classList.remove("unfocused");
this._conditionals.innerHTML = innerHTML
buttons = this._conditionals.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
for (let i = 0; i < this._templ.conditionals.length; i++) {
buttons[i].innerHTML = `<span class="monospace">{if ` + this._templ.conditionals[i].slice(1) + `</span>`;
buttons[i].innerHTML = `<span class="font-mono bg-inherit">{if ` + this._templ.conditionals[i].slice(1) + `</span>`;
buttons[i].onclick = () => {
insertText(this._textArea, "{if " + this._templ.conditionals[i].slice(1) + "{endif}");
this.loadPreview();
@ -902,15 +902,16 @@ class EmailEditor {
}
tr.innerHTML = `
<td>${this._names[id].name}</td>
<td>${resetButton}</td>
<td><span class="button ~info !normal" title="${window.lang.get("strings", "edit")}"><i class="icon ri-edit-line"></i></span></td>
<td class="table-inline justify-center"><span class="customize-reset">${resetButton}</span></td>
<td><span class="button ~info @low" title="${window.lang.get("strings", "edit")}"><i class="icon ri-edit-line"></i></span></td>
`;
(tr.querySelector("span.button") as HTMLSpanElement).onclick = () => {
window.modals.customizeEmails.close()
this.loadEditor(id);
};
if (this._names[id].enabled) {
const rb = tr.querySelector("i.ri-restart-line") as HTMLElement;
const rb = tr.querySelector("span.customize-reset") as HTMLElement;
rb.classList.add("button");
rb.onclick = () => _post("/config/emails/" + id + "/state/disable", null, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
if (req.status != 200 && req.status != 204) {

View File

@ -1,19 +1,67 @@
export function toggleTheme() {
document.documentElement.classList.toggle('dark-theme');
document.documentElement.classList.toggle('light-theme');
localStorage.setItem('theme', document.documentElement.classList.contains('dark-theme') ? "dark" : "light");
document.documentElement.classList.toggle('dark');
document.documentElement.classList.toggle('light');
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
}
export function loadTheme() {
const theme = localStorage.getItem("theme");
if (theme == "dark") {
document.documentElement.classList.add('dark-theme');
document.documentElement.classList.remove('light-theme');
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else if (theme == "light") {
document.documentElement.classList.add('light-theme');
document.documentElement.classList.remove('dark-theme');
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
} else if (window.matchMedia('(prefers-color-scheme: dark)').media !== 'not all') {
document.documentElement.classList.add('dark-theme');
document.documentElement.classList.remove('light-theme');
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
}
}
export class nightwind {
beforeTransition = () => {
const doc = document.documentElement;
const onTransitionDone = () => {
doc.classList.remove('nightwind');
doc.removeEventListener('transitionend', onTransitionDone);
}
doc.addEventListener('transitionend', onTransitionDone);
if (!doc.classList.contains('nightwind')) {
doc.classList.add('nightwind');
}
};
constructor() {
const theme = localStorage.getItem("theme");
if (theme == "dark") {
this.enable(true);
} else if (theme == "light") {
this.enable(false);
} else if (window.matchMedia('(prefers-color-scheme: dark)').media !== 'not all') {
this.enable(true);
}
}
toggle = () => {
this.beforeTransition();
if (!document.documentElement.classList.contains('dark')) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
};
enable = (dark: boolean) => {
const mode = dark ? "dark" : "light";
const opposite = dark ? "light" : "dark";
localStorage.setItem('theme', dark ? "dark" : "light");
this.beforeTransition();
if (document.documentElement.classList.contains(opposite)) {
document.documentElement.classList.remove(opposite);
}
document.documentElement.classList.add(mode);
};
}

View File

@ -38,9 +38,9 @@ export class Updater implements updater {
const el = document.getElementById("update-description") as HTMLParagraphElement;
el.textContent = description;
if (this.version == "git") {
el.classList.add("monospace");
el.classList.add("font-mono", "bg-inherit");
} else {
el.classList.remove("monospace");
el.classList.remove("font-mono", "bg-inherit");
}
}

View File

@ -1,6 +1,7 @@
import { Modal } from "./modules/modal.js";
import { initValidator } from "./modules/validator.js";
import { _post, addLoader, removeLoader } from "./modules/common.js";
import { loadLangSelector } from "./modules/lang.js";
interface formWindow extends Window {
invalidPassword: string;
@ -29,6 +30,8 @@ interface formWindow extends Window {
userExpiryMessage: string;
}
loadLangSelector("pwr");
declare var window: formWindow;
const form = document.getElementById("form-create") as HTMLFormElement;