detached 85cf407

Refactor OAuth and login routes in router.go, also pass the buildversion from *App to the html template renderer

commit
85cf4078d3027b98584172e553ec5fec472276bd
author
bnema · · 2023-09-16 08:01 UTC
parent
c958baa
signature
unsigned
15 files · +282 -178
M .gitignore +1 -0
@@ -5,3 +5,4 @@ tmp/*
55 gordon.yml
66 bun.lockb
77 *.log
8+*.txt⏎̸
@@ -5,3 +5,4 @@ tmp/*
5 gordon.yml5 gordon.yml
6 bun.lockb6 bun.lockb
7 *.log7 *.log
8+*.txt⏎̸
M internal/gotemplate/models/html/admin/index.gohtml +76 -110
@@ -1,124 +1,90 @@
1-<!-- @format -->
2-
31 <!DOCTYPE html>
42 <html lang="en">
5- <head>
6- <meta charset="UTF-8" />
7- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8- <title>{{ .header.meta.title }}</title>
9- <meta name="description" content="{{ .Header.Meta.Description }}" />
10- <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />
11- <!-- Custom CSS + Tailwind -->
12- <link rel="stylesheet" href="/assets/css/tailwind.css" />
13- <!-- Iconoir -->
14- <link rel="stylesheet" href="/libs/iconoir.css" />
15- <!-- Alpine -->
16- <script src="/libs/alpine.min.js"></script>
17- <!-- HTMX min js -->
18- <script src="/libs/htmx.min.js"></script>
19- <!-- HTMX SSE -->
20- <script src="/libs/htmx-sse.js"></script>
21- <!-- Custom JS from assets/js -->
22- <script src="assets/js/custom.js"></script>
23- </head>
24- <body class="IBMPlexSans bg-zinc-200 md:p-8 flex">
25- <!-- Left Menu -->
26- <div class="w-1/5 flex flex-col space-evenly">
27- <!-- Logo -->
28- <div id="logo-mascot-leftnav" class="flex justify-center p-4">
29- <div class="circle-shadow flex justify-center w-100">
30- <img
31- src="/assets/imgs/gordon-mascot-hq-trsp.png"
32- alt="logo"
33- class="sm:h-24 h-32 xl:h-48"
34- />
35- </div>
3+
4+<head>
5+ <meta charset="UTF-8" />
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+ <title>{{ .header.meta.title }}</title>
8+ <link rel="stylesheet" href="/assets/css/tailwind.css" />
9+ <link rel="stylesheet" href="/libs/iconoir.css" />
10+ <script src="/libs/alpine.min.js"></script>
11+ <script src="/libs/htmx.min.js"></script>
12+ <script src="/libs/htmx-sse.js"></script>
13+ <script src="assets/js/custom.js"></script>
14+</head>
15+
16+<body class="bg-zinc-200 flex flex-col md:flex-row">
17+ <div class="md:hidden flex flex-col items-center space-y-4 p-4">
18+ <div id="logo-mascot-header" class="flex justify-center">
19+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo gordon" class="h-32" />
20+ </div>
21+ <div class="w-full h-10 bg-gray-300 rounded-lg">
22+ <div class="flex justify-between items-center h-full px-2">
23+ <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" />
24+ <i class="iconoir-search"></i>
3625 </div>
37- <!-- Site Name + Version -->
38- <div
39- class="p-4 flex flex-col bg-gordonmascot-lblue rounded-lg shadow space-y-4"
40- >
41- <div class="w-full flex justify-center">
42- <h1
43- class="gordon-title text-2xl lg:text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter"
44- >
45- Gordon
46- </h1>
47- <span class="gordon-v-subtitle text-gordonmascot-mblue text-sm"
48- >v0.0.1</span
49- >
50- </div>
51- <!--(Navigation) -->
52- <div id="menu-leftnav" class="w-full">
53- <ul
54- class="flex flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl md:pl-4"
55- >
56- <li class="flex items-center">
57- <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>
58- <a href="index.html">Dashboard</a>
59- </li>
60- <li class="flex items-center">
61- <i class="iconoir-server mr-2 md:text-3xl"></i>
62- <a href="#manager">Manager</a>
63- </li>
64- <li class="flex items-center">
65- <i class="iconoir-community mr-2 md:text-3xl"></i>
66- <a href="#users">Users</a>
67- </li>
68- <li class="flex items-center">
69- <i class="iconoir-database-settings mr-2 md:text-3xl"></i>
70- <a href="#gordon-settings">Settings</a>
71- </li>
72- </ul>
73- </div>
26+ </div>
27+ </div>
7428
75- <div class="w-full h-24 bg-gordonmascot-mblue rounded-lg"></div>
76- <!-- Mock menu item -->
29+ <div class="w-full md:w-1/5 flex flex-col space-evenly p-4 md:p-0">
30+ <div id="logo-mascot-leftnav" class="hidden md:flex justify-center p-4">
31+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo" class="h-32" />
32+ </div>
33+ <div class="p-4 flex flex-col bg-gordonmascot-lblue rounded-lg shadow space-y-4">
34+ <div class="w-full flex justify-center">
35+ <h1 class="gordon-title text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter">Gordon</h1>
36+ <span class="text-gordonmascot-mblue text-sm">v0.0.1</span>
37+ </div>
38+ <div id="menu-leftnav" class="w-full">
39+ <ul class="flex flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl md:pl-4">
40+ <li class="flex items-center">
41+ <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>
42+ <a href="index.html">Dashboard</a>
43+ </li>
44+ <li class="flex items-center">
45+ <i class="iconoir-server mr-2 md:text-3xl"></i>
46+ <a href="#manager">Manager</a>
47+ </li>
48+ <li class="flex items-center">
49+ <i class="iconoir-community mr-2 md:text-3xl"></i>
50+ <a href="#users">Users</a>
51+ </li>
52+ <li class="flex items-center">
53+ <i class="iconoir-database-settings mr-2 md:text-3xl"></i>
54+ <a href="#gordon-settings">Settings</a>
55+ </li>
56+ </ul>
7757 </div>
7858 </div>
59+ </div>
7960
80- <!-- Main Content including the navbar -->
81- <div class="flex-1 flex flex-col md:pt-10 space-y-4 ml-4">
82- <!-- Search Nav within the main content -->
83- <div class="mb-4 flex justify-end">
84- <div class="w-72 h-10 bg-gray-300 rounded-lg">
85- <!-- Search input with iconir search -->
86- <div class="flex justify-between items-center h-full px-2">
87- <input
88- type="text"
89- class="w-full h-full bg-transparent outline-none"
90- placeholder="Search"
91- />
92- <i class="iconoir-search"></i>
93- </div>
61+ <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4">
62+ <div class="hidden md:flex mb-4 justify-end">
63+ <div class="w-72 h-10 bg-gray-300 rounded-lg">
64+ <div class="flex justify-between items-center h-full px-2">
65+ <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" />
66+ <i class="iconoir-search"></i>
9467 </div>
95- <!-- Mock search or other element -->
9668 </div>
69+ </div>
9770
98- <!-- Cards section -->
99- <div class="flex space-x-4 flex-grow">
100- <!-- Main Card on the Left -->
101- <div class="w-4/6 bg-white p-4 rounded-md shadow mb-4">
102- <div class="w-full rounded-sm bg-gray-300"></div>
103- <!-- Mock main card -->
71+ <div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow">
72+ <div class="flex-grow bg-white p-4 rounded-md shadow mb-4">
73+ <div class="w-full rounded-sm bg-gray-300"></div>
74+ </div>
75+ <div class="w-full md:w-2/6 flex flex-col space-y-4">
76+ <div class="bg-white p-4 rounded-md shadow mb-4">
77+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
10478 </div>
105-
106- <!-- Two Cards on the Right -->
107- <div class="w-2/6 flex flex-col flex-justify-start">
108- <div class="bg-white p-4 rounded-md shadow mb-4">
109- <div class="w-full h-44 rounded-sm bg-gray-300"></div>
110- <!-- Mock card -->
111- </div>
112- <div class="bg-white p-4 rounded-md shadow mb-4">
113- <div class="w-full h-44 rounded-sm bg-gray-300"></div>
114- <!-- Mock card -->
115- </div>
116- <div class="bg-white p-4 rounded-md shadow mb-4">
117- <div class="w-full h-44 rounded-sm bg-gray-300"></div>
118- <!-- Mock card -->
119- </div>
79+ <div class="bg-white p-4 rounded-md shadow mb-4">
80+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
81+ </div>
82+ <div class="bg-white p-4 rounded-md shadow mb-4">
83+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
12084 </div>
12185 </div>
12286 </div>
123- </body>
124-</html>
87+ </div>
88+</body>
89+
90+</html>⏎̸
@@ -1,124 +1,90 @@
1-<!-- @format -->
2-
3 <!DOCTYPE html>1 <!DOCTYPE html>
4 <html lang="en">2 <html lang="en">
5- <head>3+
6- <meta charset="UTF-8" />4+<head>
7- <meta name="viewport" content="width=device-width, initial-scale=1.0" />5+ <meta charset="UTF-8" />
8- <title>{{ .header.meta.title }}</title>6+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9- <meta name="description" content="{{ .Header.Meta.Description }}" />7+ <title>{{ .header.meta.title }}</title>
10- <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />8+ <link rel="stylesheet" href="/assets/css/tailwind.css" />
11- <!-- Custom CSS + Tailwind -->9+ <link rel="stylesheet" href="/libs/iconoir.css" />
12- <link rel="stylesheet" href="/assets/css/tailwind.css" />10+ <script src="/libs/alpine.min.js"></script>
13- <!-- Iconoir -->11+ <script src="/libs/htmx.min.js"></script>
14- <link rel="stylesheet" href="/libs/iconoir.css" />12+ <script src="/libs/htmx-sse.js"></script>
15- <!-- Alpine -->13+ <script src="assets/js/custom.js"></script>
16- <script src="/libs/alpine.min.js"></script>14+</head>
17- <!-- HTMX min js -->15+
18- <script src="/libs/htmx.min.js"></script>16+<body class="bg-zinc-200 flex flex-col md:flex-row">
19- <!-- HTMX SSE -->17+ <div class="md:hidden flex flex-col items-center space-y-4 p-4">
20- <script src="/libs/htmx-sse.js"></script>18+ <div id="logo-mascot-header" class="flex justify-center">
21- <!-- Custom JS from assets/js -->19+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo gordon" class="h-32" />
22- <script src="assets/js/custom.js"></script>20+ </div>
23- </head>21+ <div class="w-full h-10 bg-gray-300 rounded-lg">
24- <body class="IBMPlexSans bg-zinc-200 md:p-8 flex">22+ <div class="flex justify-between items-center h-full px-2">
25- <!-- Left Menu -->23+ <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" />
26- <div class="w-1/5 flex flex-col space-evenly">24+ <i class="iconoir-search"></i>
27- <!-- Logo -->
28- <div id="logo-mascot-leftnav" class="flex justify-center p-4">
29- <div class="circle-shadow flex justify-center w-100">
30- <img
31- src="/assets/imgs/gordon-mascot-hq-trsp.png"
32- alt="logo"
33- class="sm:h-24 h-32 xl:h-48"
34- />
35- </div>
36 </div>25 </div>
37- <!-- Site Name + Version -->26+ </div>
38- <div27+ </div>
39- class="p-4 flex flex-col bg-gordonmascot-lblue rounded-lg shadow space-y-4"
40- >
41- <div class="w-full flex justify-center">
42- <h1
43- class="gordon-title text-2xl lg:text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter"
44- >
45- Gordon
46- </h1>
47- <span class="gordon-v-subtitle text-gordonmascot-mblue text-sm"
48- >v0.0.1</span
49- >
50- </div>
51- <!--(Navigation) -->
52- <div id="menu-leftnav" class="w-full">
53- <ul
54- class="flex flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl md:pl-4"
55- >
56- <li class="flex items-center">
57- <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>
58- <a href="index.html">Dashboard</a>
59- </li>
60- <li class="flex items-center">
61- <i class="iconoir-server mr-2 md:text-3xl"></i>
62- <a href="#manager">Manager</a>
63- </li>
64- <li class="flex items-center">
65- <i class="iconoir-community mr-2 md:text-3xl"></i>
66- <a href="#users">Users</a>
67- </li>
68- <li class="flex items-center">
69- <i class="iconoir-database-settings mr-2 md:text-3xl"></i>
70- <a href="#gordon-settings">Settings</a>
71- </li>
72- </ul>
73- </div>
74 28
75- <div class="w-full h-24 bg-gordonmascot-mblue rounded-lg"></div>29+ <div class="w-full md:w-1/5 flex flex-col space-evenly p-4 md:p-0">
76- <!-- Mock menu item -->30+ <div id="logo-mascot-leftnav" class="hidden md:flex justify-center p-4">
31+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo" class="h-32" />
32+ </div>
33+ <div class="p-4 flex flex-col bg-gordonmascot-lblue rounded-lg shadow space-y-4">
34+ <div class="w-full flex justify-center">
35+ <h1 class="gordon-title text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter">Gordon</h1>
36+ <span class="text-gordonmascot-mblue text-sm">v0.0.1</span>
37+ </div>
38+ <div id="menu-leftnav" class="w-full">
39+ <ul class="flex flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl md:pl-4">
40+ <li class="flex items-center">
41+ <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>
42+ <a href="index.html">Dashboard</a>
43+ </li>
44+ <li class="flex items-center">
45+ <i class="iconoir-server mr-2 md:text-3xl"></i>
46+ <a href="#manager">Manager</a>
47+ </li>
48+ <li class="flex items-center">
49+ <i class="iconoir-community mr-2 md:text-3xl"></i>
50+ <a href="#users">Users</a>
51+ </li>
52+ <li class="flex items-center">
53+ <i class="iconoir-database-settings mr-2 md:text-3xl"></i>
54+ <a href="#gordon-settings">Settings</a>
55+ </li>
56+ </ul>
77 </div>57 </div>
78 </div>58 </div>
59+ </div>
79 60
80- <!-- Main Content including the navbar -->61+ <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4">
81- <div class="flex-1 flex flex-col md:pt-10 space-y-4 ml-4">62+ <div class="hidden md:flex mb-4 justify-end">
82- <!-- Search Nav within the main content -->63+ <div class="w-72 h-10 bg-gray-300 rounded-lg">
83- <div class="mb-4 flex justify-end">64+ <div class="flex justify-between items-center h-full px-2">
84- <div class="w-72 h-10 bg-gray-300 rounded-lg">65+ <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" />
85- <!-- Search input with iconir search -->66+ <i class="iconoir-search"></i>
86- <div class="flex justify-between items-center h-full px-2">
87- <input
88- type="text"
89- class="w-full h-full bg-transparent outline-none"
90- placeholder="Search"
91- />
92- <i class="iconoir-search"></i>
93- </div>
94 </div>67 </div>
95- <!-- Mock search or other element -->
96 </div>68 </div>
69+ </div>
97 70
98- <!-- Cards section -->71+ <div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow">
99- <div class="flex space-x-4 flex-grow">72+ <div class="flex-grow bg-white p-4 rounded-md shadow mb-4">
100- <!-- Main Card on the Left -->73+ <div class="w-full rounded-sm bg-gray-300"></div>
101- <div class="w-4/6 bg-white p-4 rounded-md shadow mb-4">74+ </div>
102- <div class="w-full rounded-sm bg-gray-300"></div>75+ <div class="w-full md:w-2/6 flex flex-col space-y-4">
103- <!-- Mock main card -->76+ <div class="bg-white p-4 rounded-md shadow mb-4">
77+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
104 </div>78 </div>
105-79+ <div class="bg-white p-4 rounded-md shadow mb-4">
106- <!-- Two Cards on the Right -->80+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
107- <div class="w-2/6 flex flex-col flex-justify-start">81+ </div>
108- <div class="bg-white p-4 rounded-md shadow mb-4">82+ <div class="bg-white p-4 rounded-md shadow mb-4">
109- <div class="w-full h-44 rounded-sm bg-gray-300"></div>83+ <div class="w-full h-44 rounded-sm bg-gray-300"></div>
110- <!-- Mock card -->
111- </div>
112- <div class="bg-white p-4 rounded-md shadow mb-4">
113- <div class="w-full h-44 rounded-sm bg-gray-300"></div>
114- <!-- Mock card -->
115- </div>
116- <div class="bg-white p-4 rounded-md shadow mb-4">
117- <div class="w-full h-44 rounded-sm bg-gray-300"></div>
118- <!-- Mock card -->
119- </div>
120 </div>84 </div>
121 </div>85 </div>
122 </div>86 </div>
123- </body>87+ </div>
124-</html>88+</body>
89+
90+</html>⏎̸
M internal/gotemplate/models/html/login/index.gohtml +55 -0
@@ -0,0 +1,55 @@
1+<!DOCTYPE html>
2+<html lang="en">
3+
4+<head>
5+ <meta charset="UTF-8" />
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+ <title>{{ .header.meta.title }}</title>
8+ <meta name="description" content="{{ .Header.Meta.Description }}" />
9+ <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />
10+ <!-- Custom CSS + Tailwind -->
11+ <link rel="stylesheet" href="/assets/css/tailwind.css" />
12+ <!-- Iconoir -->
13+ <link rel="stylesheet" href="/libs/iconoir.css" />
14+ <!-- Alpine -->
15+ <script src="/libs/alpine.min.js"></script>
16+ <!-- HTMX min js -->
17+ <script src="/libs/htmx.min.js"></script>
18+ <!-- HTMX SSE -->
19+ <script src="/libs/htmx-sse.js"></script>
20+ <!-- Custom JS from assets/js -->
21+ <script src="/assets/js/custom.js"></script>
22+</head>
23+
24+<body class="IBMPlexSans bg-zinc-200 md:p-8 flex flex-col items-center" hx-boost="true">
25+ <!-- Logo and Title -->
26+ <div id="logo-mascot" class="flex flex-col justify-center items-center p-4 mb-4">
27+ <div class="circle-shadow flex justify-center w-100">
28+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo gordon" class="sm:h-24 h-32 xl:h-48" />
29+ </div>
30+ <div class="flex justify-center mt-4">
31+ <h1 class="gordon-title text-4xl lg:text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter">Gordon</h1>
32+ <span class="gordon-v-subtitle text-gordonmascot-mblue text-sm ml-2">{{ .BuildVersion }}</span>
33+ </div>
34+ </div>
35+
36+ <!-- Main Content -->
37+ <div class="bg-white p-4 rounded-md shadow mb-4 w-2/3">
38+ <!-- Login Section -->
39+ <div id="login-section">
40+ <div class="flex flex-col items-center">
41+ <h2 class="text-2xl font-semibold text-gray-800">Login</h2>
42+ <!-- GitHub OAuth -->
43+ <div class="mt-4 p-2 bg-gordonmascot-hblue text-white rounded-md flex justify-left">
44+ <a href="{{ .AdminPath }}/login/github" class="flex items-center">
45+ <img src="/assets/imgs/github/github-mark-white.png" alt="logo github" class="h-8 xl:h-12" />
46+ <span class="w-full p-2 text-white ">Login with GitHub</span>
47+ </a>
48+ </div>
49+ </div>
50+ </div>
51+ <!-- End Login Section -->
52+ </div>
53+</body>
54+
55+</html>⏎̸
@@ -0,0 +1,55 @@
1+<!DOCTYPE html>
2+<html lang="en">
3+
4+<head>
5+ <meta charset="UTF-8" />
6+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+ <title>{{ .header.meta.title }}</title>
8+ <meta name="description" content="{{ .Header.Meta.Description }}" />
9+ <meta name="keywords" content="{{ .Header.Meta.Keywords }}" />
10+ <!-- Custom CSS + Tailwind -->
11+ <link rel="stylesheet" href="/assets/css/tailwind.css" />
12+ <!-- Iconoir -->
13+ <link rel="stylesheet" href="/libs/iconoir.css" />
14+ <!-- Alpine -->
15+ <script src="/libs/alpine.min.js"></script>
16+ <!-- HTMX min js -->
17+ <script src="/libs/htmx.min.js"></script>
18+ <!-- HTMX SSE -->
19+ <script src="/libs/htmx-sse.js"></script>
20+ <!-- Custom JS from assets/js -->
21+ <script src="/assets/js/custom.js"></script>
22+</head>
23+
24+<body class="IBMPlexSans bg-zinc-200 md:p-8 flex flex-col items-center" hx-boost="true">
25+ <!-- Logo and Title -->
26+ <div id="logo-mascot" class="flex flex-col justify-center items-center p-4 mb-4">
27+ <div class="circle-shadow flex justify-center w-100">
28+ <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo gordon" class="sm:h-24 h-32 xl:h-48" />
29+ </div>
30+ <div class="flex justify-center mt-4">
31+ <h1 class="gordon-title text-4xl lg:text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter">Gordon</h1>
32+ <span class="gordon-v-subtitle text-gordonmascot-mblue text-sm ml-2">{{ .BuildVersion }}</span>
33+ </div>
34+ </div>
35+
36+ <!-- Main Content -->
37+ <div class="bg-white p-4 rounded-md shadow mb-4 w-2/3">
38+ <!-- Login Section -->
39+ <div id="login-section">
40+ <div class="flex flex-col items-center">
41+ <h2 class="text-2xl font-semibold text-gray-800">Login</h2>
42+ <!-- GitHub OAuth -->
43+ <div class="mt-4 p-2 bg-gordonmascot-hblue text-white rounded-md flex justify-left">
44+ <a href="{{ .AdminPath }}/login/github" class="flex items-center">
45+ <img src="/assets/imgs/github/github-mark-white.png" alt="logo github" class="h-8 xl:h-12" />
46+ <span class="w-full p-2 text-white ">Login with GitHub</span>
47+ </a>
48+ </div>
49+ </div>
50+ </div>
51+ <!-- End Login Section -->
52+ </div>
53+</body>
54+
55+</html>⏎̸
M internal/gotemplate/render/html.go +15 -5
@@ -3,15 +3,17 @@ package render
33 import (
44 "bytes"
55 "fmt"
6- "github.com/bnema/gordon/internal/app"
76 "html/template"
87 "io/fs"
98 "path"
9+
10+ "github.com/bnema/gordon/internal/app"
1011 )
1112
1213 type Renderer struct {
13- Template *template.Template
14- ParseError error
14+ Template *template.Template
15+ ParseError error
16+ BuildVersion string
1517 }
1618
1719 // Render function renders the template with the given data
@@ -23,9 +25,16 @@ func (r *Renderer) Render(data interface{}, a *app.App) (string, error) {
2325 if r.Template == nil {
2426 return "", fmt.Errorf("template is nil")
2527 }
28+ // Type assert data to map[string]interface{} to add BuildVersion
29+ dataMap, ok := data.(map[string]interface{})
30+ if !ok {
31+ return "", fmt.Errorf("data is not a map[string]interface{}")
32+ }
2633
34+ // Automatically add BuildVersion to the data map
35+ dataMap["BuildVersion"] = r.BuildVersion
2736 buf := new(bytes.Buffer)
28- err := r.Template.Execute(buf, data)
37+ err := r.Template.Execute(buf, dataMap)
2938 if err != nil {
3039 return "", fmt.Errorf("failed to execute template: %w", err)
3140 }
@@ -48,6 +57,7 @@ func GetHTMLRenderer(pathStr string, filename string, fs fs.FS, a *app.App) (*Re
4857 return nil, fmt.Errorf("failed to parse %s: %w", filename, err)
4958 }
5059 return &Renderer{
51- Template: tmpl,
60+ Template: tmpl,
61+ BuildVersion: a.BuildVersion,
5262 }, nil
5363 }
@@ -3,15 +3,17 @@ package render
3 import (3 import (
4 "bytes"4 "bytes"
5 "fmt"5 "fmt"
6- "github.com/bnema/gordon/internal/app"
7 "html/template"6 "html/template"
8 "io/fs"7 "io/fs"
9 "path"8 "path"
9+
10+ "github.com/bnema/gordon/internal/app"
10 )11 )
11 12
12 type Renderer struct {13 type Renderer struct {
13- Template *template.Template14+ Template *template.Template
14- ParseError error15+ ParseError error
16+ BuildVersion string
15 }17 }
16 18
17 // Render function renders the template with the given data19 // Render function renders the template with the given data
@@ -23,9 +25,16 @@ func (r *Renderer) Render(data interface{}, a *app.App) (string, error) {
23 if r.Template == nil {25 if r.Template == nil {
24 return "", fmt.Errorf("template is nil")26 return "", fmt.Errorf("template is nil")
25 }27 }
28+ // Type assert data to map[string]interface{} to add BuildVersion
29+ dataMap, ok := data.(map[string]interface{})
30+ if !ok {
31+ return "", fmt.Errorf("data is not a map[string]interface{}")
32+ }
26 33
34+ // Automatically add BuildVersion to the data map
35+ dataMap["BuildVersion"] = r.BuildVersion
27 buf := new(bytes.Buffer)36 buf := new(bytes.Buffer)
28- err := r.Template.Execute(buf, data)37+ err := r.Template.Execute(buf, dataMap)
29 if err != nil {38 if err != nil {
30 return "", fmt.Errorf("failed to execute template: %w", err)39 return "", fmt.Errorf("failed to execute template: %w", err)
31 }40 }
@@ -48,6 +57,7 @@ func GetHTMLRenderer(pathStr string, filename string, fs fs.FS, a *app.App) (*Re
48 return nil, fmt.Errorf("failed to parse %s: %w", filename, err)57 return nil, fmt.Errorf("failed to parse %s: %w", filename, err)
49 }58 }
50 return &Renderer{59 return &Renderer{
51- Template: tmpl,60+ Template: tmpl,
61+ BuildVersion: a.BuildVersion,
52 }, nil62 }, nil
53 }63 }
M internal/httpserve/handler/adminroutes.go +7 -1
@@ -24,7 +24,13 @@ func AdminRoute(c echo.Context, a *app.App) error {
2424 return err
2525 }
2626
27- renderedHTML, err := rendererData.Render(yamlData.CurrentLang, a)
27+ // Create a data map to pass to the renderer
28+ data := map[string]interface{}{
29+ "CurrentLang": yamlData.CurrentLang,
30+ // "BuildVersion" will be automatically added in the renderer
31+ }
32+
33+ renderedHTML, err := rendererData.Render(data, a)
2834 if err != nil {
2935 return err
3036 }
@@ -24,7 +24,13 @@ func AdminRoute(c echo.Context, a *app.App) error {
24 return err24 return err
25 }25 }
26 26
27- renderedHTML, err := rendererData.Render(yamlData.CurrentLang, a)27+ // Create a data map to pass to the renderer
28+ data := map[string]interface{}{
29+ "CurrentLang": yamlData.CurrentLang,
30+ // "BuildVersion" will be automatically added in the renderer
31+ }
32+
33+ renderedHTML, err := rendererData.Render(data, a)
28 if err != nil {34 if err != nil {
29 return err35 return err
30 }36 }
M internal/httpserve/handler/oauth.go +10 -4
@@ -1,6 +1,9 @@
11 package handler
22
33 import (
4+ "net/http"
5+ "os"
6+
47 "github.com/bnema/gordon/internal/app"
58 "github.com/bnema/gordon/internal/gotemplate/render"
69 "github.com/bnema/gordon/internal/httpserve/middleware"
@@ -11,8 +14,6 @@ import (
1114 "github.com/markbates/goth"
1215 "github.com/markbates/goth/gothic"
1316 "github.com/markbates/goth/providers/github"
14- "net/http"
15- "os"
1617 )
1718
1819 var providerNames []string
@@ -35,7 +36,6 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
3536 if err != nil {
3637 return err
3738 }
38-
3939 // Navigate inside the fs.FS to get the template
4040 path := "html/login"
4141 rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a)
@@ -43,7 +43,13 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
4343 return err
4444 }
4545
46- renderedHTML, err := rendererData.Render(yamlData.CurrentLang, a)
46+ // Create a data map to pass to the renderer
47+ data := map[string]interface{}{
48+ "CurrentLang": yamlData.CurrentLang,
49+ // "BuildVersion" will be automatically added in the renderer
50+ }
51+
52+ renderedHTML, err := rendererData.Render(data, a)
4753 if err != nil {
4854 return err
4955 }
@@ -1,6 +1,9 @@
1 package handler1 package handler
2 2
3 import (3 import (
4+ "net/http"
5+ "os"
6+
4 "github.com/bnema/gordon/internal/app"7 "github.com/bnema/gordon/internal/app"
5 "github.com/bnema/gordon/internal/gotemplate/render"8 "github.com/bnema/gordon/internal/gotemplate/render"
6 "github.com/bnema/gordon/internal/httpserve/middleware"9 "github.com/bnema/gordon/internal/httpserve/middleware"
@@ -11,8 +14,6 @@ import (
11 "github.com/markbates/goth"14 "github.com/markbates/goth"
12 "github.com/markbates/goth/gothic"15 "github.com/markbates/goth/gothic"
13 "github.com/markbates/goth/providers/github"16 "github.com/markbates/goth/providers/github"
14- "net/http"
15- "os"
16 )17 )
17 18
18 var providerNames []string19 var providerNames []string
@@ -35,7 +36,6 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
35 if err != nil {36 if err != nil {
36 return err37 return err
37 }38 }
38-
39 // Navigate inside the fs.FS to get the template39 // Navigate inside the fs.FS to get the template
40 path := "html/login"40 path := "html/login"
41 rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a)41 rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a)
@@ -43,7 +43,13 @@ func RenderLoginPage(c echo.Context, a *app.App) error {
43 return err43 return err
44 }44 }
45 45
46- renderedHTML, err := rendererData.Render(yamlData.CurrentLang, a)46+ // Create a data map to pass to the renderer
47+ data := map[string]interface{}{
48+ "CurrentLang": yamlData.CurrentLang,
49+ // "BuildVersion" will be automatically added in the renderer
50+ }
51+
52+ renderedHTML, err := rendererData.Render(data, a)
47 if err != nil {53 if err != nil {
48 return err54 return err
49 }55 }
A internal/httpserve/middleware/commondata.go +16 -0
@@ -0,0 +1,16 @@
1+package middleware
2+
3+import (
4+ "github.com/bnema/gordon/internal/app"
5+ "github.com/labstack/echo/v4"
6+)
7+
8+func SetCommonDataMiddleware(a *app.App) echo.MiddlewareFunc {
9+ return func(next echo.HandlerFunc) echo.HandlerFunc {
10+ return func(c echo.Context) error {
11+ c.Set("BuildVersion", a.BuildVersion)
12+ // Add more common data if needed
13+ return next(c)
14+ }
15+ }
16+}
@@ -0,0 +1,16 @@
1+package middleware
2+
3+import (
4+ "github.com/bnema/gordon/internal/app"
5+ "github.com/labstack/echo/v4"
6+)
7+
8+func SetCommonDataMiddleware(a *app.App) echo.MiddlewareFunc {
9+ return func(next echo.HandlerFunc) echo.HandlerFunc {
10+ return func(c echo.Context) error {
11+ c.Set("BuildVersion", a.BuildVersion)
12+ // Add more common data if needed
13+ return next(c)
14+ }
15+ }
16+}
M internal/httpserve/router.go +1 -1
@@ -15,8 +15,8 @@ import (
1515 // RegisterRoutes registers all routes for the application
1616 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
1717 AdminPath := a.AdminPath
18+ e.Use(middleware.SetCommonDataMiddleware(a))
1819 e.Use(middleware.ErrorHandler)
19- // Use session middleware
2020 // Add session middleware
2121 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))
2222 e.Use(middleware.ColorSchemeDetection)
@@ -15,8 +15,8 @@ import (
15 // RegisterRoutes registers all routes for the application15 // RegisterRoutes registers all routes for the application
16 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {16 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
17 AdminPath := a.AdminPath17 AdminPath := a.AdminPath
18+ e.Use(middleware.SetCommonDataMiddleware(a))
18 e.Use(middleware.ErrorHandler)19 e.Use(middleware.ErrorHandler)
19- // Use session middleware
20 // Add session middleware20 // Add session middleware
21 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))21 e.Use(session.Middleware(sessions.NewCookieStore([]byte(os.Getenv("SECRET")))))
22 e.Use(middleware.ColorSchemeDetection)22 e.Use(middleware.ColorSchemeDetection)
M internal/webui/public/assets/css/tailwind.css +94 -55
@@ -570,6 +570,10 @@ video {
570570 display: flex;
571571 }
572572
573+.hidden {
574+ display: none;
575+}
576+
573577 .h-10 {
574578 height: 2.5rem;
575579 }
@@ -594,10 +598,26 @@ video {
594598 height: 100%;
595599 }
596600
601+.h-16 {
602+ height: 4rem;
603+}
604+
605+.h-12 {
606+ height: 3rem;
607+}
608+
609+.h-8 {
610+ height: 2rem;
611+}
612+
597613 .w-1\/5 {
598614 width: 20%;
599615 }
600616
617+.w-2\/3 {
618+ width: 66.666667%;
619+}
620+
601621 .w-2\/6 {
602622 width: 33.333333%;
603623 }
@@ -614,18 +634,6 @@ video {
614634 width: 100%;
615635 }
616636
617-.w-2\/3 {
618- width: 66.666667%;
619-}
620-
621-.w-screen {
622- width: 100vw;
623-}
624-
625-.w-96 {
626- width: 24rem;
627-}
628-
629637 .flex-1 {
630638 flex: 1 1 0%;
631639 }
@@ -680,22 +688,6 @@ video {
680688 margin-bottom: calc(1rem * var(--tw-space-y-reverse));
681689 }
682690
683-.space-x-3 > :not([hidden]) ~ :not([hidden]) {
684- --tw-space-x-reverse: 0;
685- margin-right: calc(0.75rem * var(--tw-space-x-reverse));
686- margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
687-}
688-
689-.space-y-3 > :not([hidden]) ~ :not([hidden]) {
690- --tw-space-y-reverse: 0;
691- margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
692- margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
693-}
694-
695-.overflow-y-scroll {
696- overflow-y: scroll;
697-}
698-
699691 .rounded-lg {
700692 border-radius: 0.5rem;
701693 }
@@ -717,6 +709,11 @@ video {
717709 border-color: rgb(209 213 219 / var(--tw-border-opacity));
718710 }
719711
712+.bg-gordonmascot-hblue {
713+ --tw-bg-opacity: 1;
714+ background-color: rgb(1 30 92 / var(--tw-bg-opacity));
715+}
716+
720717 .bg-gordonmascot-lblue {
721718 --tw-bg-opacity: 1;
722719 background-color: rgb(0 123 192 / var(--tw-bg-opacity));
@@ -732,6 +729,11 @@ video {
732729 background-color: rgb(209 213 219 / var(--tw-bg-opacity));
733730 }
734731
732+.bg-gray-800 {
733+ --tw-bg-opacity: 1;
734+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
735+}
736+
735737 .bg-transparent {
736738 background-color: transparent;
737739 }
@@ -746,32 +748,22 @@ video {
746748 background-color: rgb(228 228 231 / var(--tw-bg-opacity));
747749 }
748750
749-.bg-gordonmascot-hblue {
750- --tw-bg-opacity: 1;
751- background-color: rgb(1 30 92 / var(--tw-bg-opacity));
752-}
753-
754-.bg-zinc-400 {
755- --tw-bg-opacity: 1;
756- background-color: rgb(161 161 170 / var(--tw-bg-opacity));
751+.p-2 {
752+ padding: 0.5rem;
757753 }
758754
759755 .p-4 {
760756 padding: 1rem;
761757 }
762758
763-.p-2 {
764- padding: 0.5rem;
765-}
766-
767759 .px-2 {
768760 padding-left: 0.5rem;
769761 padding-right: 0.5rem;
770762 }
771763
772-.px-6 {
773- padding-left: 1.5rem;
774- padding-right: 1.5rem;
764+.px-4 {
765+ padding-left: 1rem;
766+ padding-right: 1rem;
775767 }
776768
777769 .py-2 {
@@ -779,11 +771,6 @@ video {
779771 padding-bottom: 0.5rem;
780772 }
781773
782-.px-4 {
783- padding-left: 1rem;
784- padding-right: 1rem;
785-}
786-
787774 .text-2xl {
788775 font-size: 1.5rem;
789776 line-height: 2rem;
@@ -804,9 +791,9 @@ video {
804791 line-height: 1.25rem;
805792 }
806793
807-.text-xl {
808- font-size: 1.25rem;
809- line-height: 1.75rem;
794+.text-4xl {
795+ font-size: 2.25rem;
796+ line-height: 2.5rem;
810797 }
811798
812799 .font-semibold {
@@ -837,14 +824,14 @@ video {
837824 color: rgb(31 41 55 / var(--tw-text-opacity));
838825 }
839826
840-.text-slate-400 {
827+.text-slate-100 {
841828 --tw-text-opacity: 1;
842- color: rgb(148 163 184 / var(--tw-text-opacity));
829+ color: rgb(241 245 249 / var(--tw-text-opacity));
843830 }
844831
845-.text-slate-100 {
832+.text-white {
846833 --tw-text-opacity: 1;
847- color: rgb(241 245 249 / var(--tw-text-opacity));
834+ color: rgb(255 255 255 / var(--tw-text-opacity));
848835 }
849836
850837 .shadow {
@@ -982,13 +969,37 @@ video {
982969 .sm\:h-24 {
983970 height: 6rem;
984971 }
972+
973+ .sm\:h-12 {
974+ height: 3rem;
975+ }
985976 }
986977
987978 @media (min-width: 768px) {
979+ .md\:ml-4 {
980+ margin-left: 1rem;
981+ }
982+
983+ .md\:flex {
984+ display: flex;
985+ }
986+
987+ .md\:hidden {
988+ display: none;
989+ }
990+
988991 .md\:w-1\/2 {
989992 width: 50%;
990993 }
991994
995+ .md\:w-1\/5 {
996+ width: 20%;
997+ }
998+
999+ .md\:w-2\/6 {
1000+ width: 33.333333%;
1001+ }
1002+
9921003 .md\:flex-row {
9931004 flex-direction: row;
9941005 }
@@ -999,10 +1010,26 @@ video {
9991010 margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
10001011 }
10011012
1013+ .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
1014+ --tw-space-x-reverse: 0;
1015+ margin-right: calc(1rem * var(--tw-space-x-reverse));
1016+ margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
1017+ }
1018+
1019+ .md\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
1020+ --tw-space-y-reverse: 0;
1021+ margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
1022+ margin-bottom: calc(0px * var(--tw-space-y-reverse));
1023+ }
1024+
10021025 .md\:p-8 {
10031026 padding: 2rem;
10041027 }
10051028
1029+ .md\:p-0 {
1030+ padding: 0px;
1031+ }
1032+
10061033 .md\:py-8 {
10071034 padding-top: 2rem;
10081035 padding-bottom: 2rem;
@@ -1039,6 +1066,18 @@ video {
10391066 height: 12rem;
10401067 }
10411068
1069+ .xl\:h-24 {
1070+ height: 6rem;
1071+ }
1072+
1073+ .xl\:h-16 {
1074+ height: 4rem;
1075+ }
1076+
1077+ .xl\:h-12 {
1078+ height: 3rem;
1079+ }
1080+
10421081 .xl\:text-5xl {
10431082 font-size: 3rem;
10441083 line-height: 1;
@@ -570,6 +570,10 @@ video {
570 display: flex;570 display: flex;
571 }571 }
572 572
573+.hidden {
574+ display: none;
575+}
576+
573 .h-10 {577 .h-10 {
574 height: 2.5rem;578 height: 2.5rem;
575 }579 }
@@ -594,10 +598,26 @@ video {
594 height: 100%;598 height: 100%;
595 }599 }
596 600
601+.h-16 {
602+ height: 4rem;
603+}
604+
605+.h-12 {
606+ height: 3rem;
607+}
608+
609+.h-8 {
610+ height: 2rem;
611+}
612+
597 .w-1\/5 {613 .w-1\/5 {
598 width: 20%;614 width: 20%;
599 }615 }
600 616
617+.w-2\/3 {
618+ width: 66.666667%;
619+}
620+
601 .w-2\/6 {621 .w-2\/6 {
602 width: 33.333333%;622 width: 33.333333%;
603 }623 }
@@ -614,18 +634,6 @@ video {
614 width: 100%;634 width: 100%;
615 }635 }
616 636
617-.w-2\/3 {
618- width: 66.666667%;
619-}
620-
621-.w-screen {
622- width: 100vw;
623-}
624-
625-.w-96 {
626- width: 24rem;
627-}
628-
629 .flex-1 {637 .flex-1 {
630 flex: 1 1 0%;638 flex: 1 1 0%;
631 }639 }
@@ -680,22 +688,6 @@ video {
680 margin-bottom: calc(1rem * var(--tw-space-y-reverse));688 margin-bottom: calc(1rem * var(--tw-space-y-reverse));
681 }689 }
682 690
683-.space-x-3 > :not([hidden]) ~ :not([hidden]) {
684- --tw-space-x-reverse: 0;
685- margin-right: calc(0.75rem * var(--tw-space-x-reverse));
686- margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse)));
687-}
688-
689-.space-y-3 > :not([hidden]) ~ :not([hidden]) {
690- --tw-space-y-reverse: 0;
691- margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
692- margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
693-}
694-
695-.overflow-y-scroll {
696- overflow-y: scroll;
697-}
698-
699 .rounded-lg {691 .rounded-lg {
700 border-radius: 0.5rem;692 border-radius: 0.5rem;
701 }693 }
@@ -717,6 +709,11 @@ video {
717 border-color: rgb(209 213 219 / var(--tw-border-opacity));709 border-color: rgb(209 213 219 / var(--tw-border-opacity));
718 }710 }
719 711
712+.bg-gordonmascot-hblue {
713+ --tw-bg-opacity: 1;
714+ background-color: rgb(1 30 92 / var(--tw-bg-opacity));
715+}
716+
720 .bg-gordonmascot-lblue {717 .bg-gordonmascot-lblue {
721 --tw-bg-opacity: 1;718 --tw-bg-opacity: 1;
722 background-color: rgb(0 123 192 / var(--tw-bg-opacity));719 background-color: rgb(0 123 192 / var(--tw-bg-opacity));
@@ -732,6 +729,11 @@ video {
732 background-color: rgb(209 213 219 / var(--tw-bg-opacity));729 background-color: rgb(209 213 219 / var(--tw-bg-opacity));
733 }730 }
734 731
732+.bg-gray-800 {
733+ --tw-bg-opacity: 1;
734+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
735+}
736+
735 .bg-transparent {737 .bg-transparent {
736 background-color: transparent;738 background-color: transparent;
737 }739 }
@@ -746,32 +748,22 @@ video {
746 background-color: rgb(228 228 231 / var(--tw-bg-opacity));748 background-color: rgb(228 228 231 / var(--tw-bg-opacity));
747 }749 }
748 750
749-.bg-gordonmascot-hblue {751+.p-2 {
750- --tw-bg-opacity: 1;752+ padding: 0.5rem;
751- background-color: rgb(1 30 92 / var(--tw-bg-opacity));
752-}
753-
754-.bg-zinc-400 {
755- --tw-bg-opacity: 1;
756- background-color: rgb(161 161 170 / var(--tw-bg-opacity));
757 }753 }
758 754
759 .p-4 {755 .p-4 {
760 padding: 1rem;756 padding: 1rem;
761 }757 }
762 758
763-.p-2 {
764- padding: 0.5rem;
765-}
766-
767 .px-2 {759 .px-2 {
768 padding-left: 0.5rem;760 padding-left: 0.5rem;
769 padding-right: 0.5rem;761 padding-right: 0.5rem;
770 }762 }
771 763
772-.px-6 {764+.px-4 {
773- padding-left: 1.5rem;765+ padding-left: 1rem;
774- padding-right: 1.5rem;766+ padding-right: 1rem;
775 }767 }
776 768
777 .py-2 {769 .py-2 {
@@ -779,11 +771,6 @@ video {
779 padding-bottom: 0.5rem;771 padding-bottom: 0.5rem;
780 }772 }
781 773
782-.px-4 {
783- padding-left: 1rem;
784- padding-right: 1rem;
785-}
786-
787 .text-2xl {774 .text-2xl {
788 font-size: 1.5rem;775 font-size: 1.5rem;
789 line-height: 2rem;776 line-height: 2rem;
@@ -804,9 +791,9 @@ video {
804 line-height: 1.25rem;791 line-height: 1.25rem;
805 }792 }
806 793
807-.text-xl {794+.text-4xl {
808- font-size: 1.25rem;795+ font-size: 2.25rem;
809- line-height: 1.75rem;796+ line-height: 2.5rem;
810 }797 }
811 798
812 .font-semibold {799 .font-semibold {
@@ -837,14 +824,14 @@ video {
837 color: rgb(31 41 55 / var(--tw-text-opacity));824 color: rgb(31 41 55 / var(--tw-text-opacity));
838 }825 }
839 826
840-.text-slate-400 {827+.text-slate-100 {
841 --tw-text-opacity: 1;828 --tw-text-opacity: 1;
842- color: rgb(148 163 184 / var(--tw-text-opacity));829+ color: rgb(241 245 249 / var(--tw-text-opacity));
843 }830 }
844 831
845-.text-slate-100 {832+.text-white {
846 --tw-text-opacity: 1;833 --tw-text-opacity: 1;
847- color: rgb(241 245 249 / var(--tw-text-opacity));834+ color: rgb(255 255 255 / var(--tw-text-opacity));
848 }835 }
849 836
850 .shadow {837 .shadow {
@@ -982,13 +969,37 @@ video {
982 .sm\:h-24 {969 .sm\:h-24 {
983 height: 6rem;970 height: 6rem;
984 }971 }
972+
973+ .sm\:h-12 {
974+ height: 3rem;
975+ }
985 }976 }
986 977
987 @media (min-width: 768px) {978 @media (min-width: 768px) {
979+ .md\:ml-4 {
980+ margin-left: 1rem;
981+ }
982+
983+ .md\:flex {
984+ display: flex;
985+ }
986+
987+ .md\:hidden {
988+ display: none;
989+ }
990+
988 .md\:w-1\/2 {991 .md\:w-1\/2 {
989 width: 50%;992 width: 50%;
990 }993 }
991 994
995+ .md\:w-1\/5 {
996+ width: 20%;
997+ }
998+
999+ .md\:w-2\/6 {
1000+ width: 33.333333%;
1001+ }
1002+
992 .md\:flex-row {1003 .md\:flex-row {
993 flex-direction: row;1004 flex-direction: row;
994 }1005 }
@@ -999,10 +1010,26 @@ video {
999 margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));1010 margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
1000 }1011 }
1001 1012
1013+ .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
1014+ --tw-space-x-reverse: 0;
1015+ margin-right: calc(1rem * var(--tw-space-x-reverse));
1016+ margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse)));
1017+ }
1018+
1019+ .md\:space-y-0 > :not([hidden]) ~ :not([hidden]) {
1020+ --tw-space-y-reverse: 0;
1021+ margin-top: calc(0px * calc(1 - var(--tw-space-y-reverse)));
1022+ margin-bottom: calc(0px * var(--tw-space-y-reverse));
1023+ }
1024+
1002 .md\:p-8 {1025 .md\:p-8 {
1003 padding: 2rem;1026 padding: 2rem;
1004 }1027 }
1005 1028
1029+ .md\:p-0 {
1030+ padding: 0px;
1031+ }
1032+
1006 .md\:py-8 {1033 .md\:py-8 {
1007 padding-top: 2rem;1034 padding-top: 2rem;
1008 padding-bottom: 2rem;1035 padding-bottom: 2rem;
@@ -1039,6 +1066,18 @@ video {
1039 height: 12rem;1066 height: 12rem;
1040 }1067 }
1041 1068
1069+ .xl\:h-24 {
1070+ height: 6rem;
1071+ }
1072+
1073+ .xl\:h-16 {
1074+ height: 4rem;
1075+ }
1076+
1077+ .xl\:h-12 {
1078+ height: 3rem;
1079+ }
1080+
1042 .xl\:text-5xl {1081 .xl\:text-5xl {
1043 font-size: 3rem;1082 font-size: 3rem;
1044 line-height: 1;1083 line-height: 1;
A internal/webui/public/assets/imgs/github/github-mark-white.png +0 -0 binary file: no text diff

binary file: no text diff

A internal/webui/public/assets/imgs/github/github-mark-white.svg +1 -0
@@ -0,0 +1,1 @@
1+<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>⏎̸
@@ -0,0 +1,1 @@
1+<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#fff"/></svg>⏎̸
A internal/webui/public/assets/imgs/github/github-mark.png +0 -0 binary file: no text diff

binary file: no text diff

A internal/webui/public/assets/imgs/github/github-mark.svg +1 -0
@@ -0,0 +1,1 @@
1+<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>⏎̸
@@ -0,0 +1,1 @@
1+<svg width="98" height="96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="#24292f"/></svg>⏎̸
M package.json +1 -1
@@ -5,6 +5,6 @@
55 },
66 "scripts": {
77 "build:css": "tailwindcss build src/styles.css -o dist/styles.css",
8- "dev:css": "tailwindcss -i internal/ui/public/assets/css/custom.css -o internal/ui/public/assets/css/tailwind.css --watch"
8+ "dev:css": "tailwindcss -i internal/webui/public/assets/css/custom.css -o internal/webui/public/assets/css/tailwind.css --watch"
99 }
1010 }
@@ -5,6 +5,6 @@
5 },5 },
6 "scripts": {6 "scripts": {
7 "build:css": "tailwindcss build src/styles.css -o dist/styles.css",7 "build:css": "tailwindcss build src/styles.css -o dist/styles.css",
8- "dev:css": "tailwindcss -i internal/ui/public/assets/css/custom.css -o internal/ui/public/assets/css/tailwind.css --watch"8+ "dev:css": "tailwindcss -i internal/webui/public/assets/css/custom.css -o internal/webui/public/assets/css/tailwind.css --watch"
9 }9 }
10 }10 }
M tailwind.config.js +4 -1
@@ -1,6 +1,9 @@
11 /** @type {import('tailwindcss').Config} */
22 module.exports = {
3- content: ["./internal/ui/**/*.{gohtml,go}"],
3+ content: [
4+ "./internal/webui/**/*.{gohtml,go}",
5+ "./internal/gotemplate/models/html/**/*.{gohtml, go}"
6+ ],
47 theme: {
58 extend: {
69 colors: {
@@ -1,6 +1,9 @@
1 /** @type {import('tailwindcss').Config} */1 /** @type {import('tailwindcss').Config} */
2 module.exports = {2 module.exports = {
3- content: ["./internal/ui/**/*.{gohtml,go}"],3+ content: [
4+ "./internal/webui/**/*.{gohtml,go}",
5+ "./internal/gotemplate/models/html/**/*.{gohtml, go}"
6+ ],
4 theme: {7 theme: {
5 extend: {8 extend: {
6 colors: {9 colors: {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close