detached fab3374

Add more localization support and optimize container listing UI

1. Backend Changes:
    - Renamed uploadimage.go to uploadcontainerimage.go.
    - Removed unused `CreatedStr` field from HumanReadableContainer struct in `containers.go`.
    - Added localization support by fetching localized YAML data to be used in the container manager UI.

2. HTML Template Updates:
    - Replaced static titles and labels with dynamic localized strings in `containerlist.gohtml` and `manager.gohtml`.
    - Modified error divs to use the HTML `hidden` attribute for cleaner HTML in `manager.gohtml`.
    - Added table headers and made layout adjustments for better readability in `containerlist.gohtml`.

3. Localization:
    - Added English and French translations for container management UI in `locstrings.yml`.
commit
fab33749f2347d31c6574bc82923558df9916b14
author
bnema · · 2023-10-04 14:29 UTC
parent
56b3f70
signature
unsigned
5 files · +83 -124
M internal/httpserve/handler/containers.go +5 -3
@@ -118,8 +118,6 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
118118
119119 for _, container := range containers {
120120 localContainer := container // Make a local copy
121- createdTime := time.Unix(container.Created, 0)
122- createdStr := humanize.TimeAgo(createdTime)
123121 sizeStr := humanize.BytesToReadableSize(container.SizeRw)
124122 stateColor := "green"
125123
@@ -136,7 +134,6 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
136134 name = name[1:]
137135 humanReadableContainers = append(humanReadableContainers, HumanReadableContainer{
138136 Container: &localContainer,
139- CreatedStr: createdStr,
140137 SizeStr: sizeStr,
141138 UpSince: humanize.TimeAgo(time.Unix(container.Created, 0)),
142139 StateColor: stateColor,
@@ -145,7 +142,12 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
145142 })
146143 }
147144 }
145+ yamlData, err := GetLocalizedData(c, a)
146+ if err != nil {
147+ return err
148+ }
148149 data := map[string]interface{}{
150+ "Lang": yamlData["Lang"],
149151 "containers": humanReadableContainers,
150152 }
151153
@@ -118,8 +118,6 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
118 118
119 for _, container := range containers {119 for _, container := range containers {
120 localContainer := container // Make a local copy120 localContainer := container // Make a local copy
121- createdTime := time.Unix(container.Created, 0)
122- createdStr := humanize.TimeAgo(createdTime)
123 sizeStr := humanize.BytesToReadableSize(container.SizeRw)121 sizeStr := humanize.BytesToReadableSize(container.SizeRw)
124 stateColor := "green"122 stateColor := "green"
125 123
@@ -136,7 +134,6 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
136 name = name[1:]134 name = name[1:]
137 humanReadableContainers = append(humanReadableContainers, HumanReadableContainer{135 humanReadableContainers = append(humanReadableContainers, HumanReadableContainer{
138 Container: &localContainer,136 Container: &localContainer,
139- CreatedStr: createdStr,
140 SizeStr: sizeStr,137 SizeStr: sizeStr,
141 UpSince: humanize.TimeAgo(time.Unix(container.Created, 0)),138 UpSince: humanize.TimeAgo(time.Unix(container.Created, 0)),
142 StateColor: stateColor,139 StateColor: stateColor,
@@ -145,7 +142,12 @@ func ContainerManagerComponent(c echo.Context, a *app.App) error {
145 })142 })
146 }143 }
147 }144 }
145+ yamlData, err := GetLocalizedData(c, a)
146+ if err != nil {
147+ return err
148+ }
148 data := map[string]interface{}{149 data := map[string]interface{}{
150+ "Lang": yamlData["Lang"],
149 "containers": humanReadableContainers,151 "containers": humanReadableContainers,
150 }152 }
151 153
M internal/templates/models/html/admin/manager.gohtml +2 -2
@@ -20,7 +20,7 @@
2020 hx-target-error="container-manager-error"
2121 hx-poll="5s"
2222 ></div>
23- <div id="container-manager-error" style="display: none"></div>
23+ <div id="container-manager-error" hidden></div>
2424 <!-- List all the images -->
2525 <div
2626 id="image-manager"
@@ -31,7 +31,7 @@
3131 hx-target-error="image-manager-error"
3232 hx-poll="5s"
3333 ></div>
34- <div id="image-manager-error" style="display: none"></div>
34+ <div id="image-manager-error" hidden></div>
3535 </div>
3636 </div>
3737
@@ -20,7 +20,7 @@
20 hx-target-error="container-manager-error"20 hx-target-error="container-manager-error"
21 hx-poll="5s" 21 hx-poll="5s"
22 ></div>22 ></div>
23- <div id="container-manager-error" style="display: none"></div>23+ <div id="container-manager-error" hidden></div>
24 <!-- List all the images -->24 <!-- List all the images -->
25 <div25 <div
26 id="image-manager"26 id="image-manager"
@@ -31,7 +31,7 @@
31 hx-target-error="image-manager-error"31 hx-target-error="image-manager-error"
32 hx-poll="5s" 32 hx-poll="5s"
33 ></div>33 ></div>
34- <div id="image-manager-error" style="display: none"></div>34+ <div id="image-manager-error" hidden></div>
35 </div>35 </div>
36 </div>36 </div>
37 37
M internal/templates/models/html/fragments/containerlist.gohtml +23 -22
@@ -1,33 +1,34 @@
11 <div class="flex flex-col p-4" hx-boost="true">
2- <h1 class="text-lg md:text-xl font-bold">Containers</h1>
3- <table class="p-4 border-t-2" hx-ext="response-targets">
4- <!-- <thead>
5- <tr>
6- <th>Name</th>
7- <th>Command</th>
8- <th>Created</th>
9- <th>Status</th>
10- <th>Port</th>
11- <th>State</th>
12- <th>Action</th>
2+ <h1 class="text-lg md:text-xl font-bold">
3+ {{index .Lang "manager" "content" "h1-containers"}}
4+ </h1>
5+ <table class="p-4 border-t-2 table-auto" hx-ext="response-targets">
6+ <thead>
7+ <tr class="text-center text-gray-400">
8+ <th>{{index .Lang "manager" "content" "thName"}}</th>
9+ <th>{{index .Lang "manager" "content" "thPorts"}}</th>
10+ <th>{{index .Lang "manager" "content" "thStatus"}}</th>
11+ <th>{{index .Lang "manager" "content" "thState"}}</th>
12+ <th>{{index .Lang "manager" "content" "thActions"}}</th>
1313 </tr>
14- </thead> -->
14+ </thead>
1515 <tbody>
16+ {{ $Lang := .Lang }}
1617 {{
1718 range.containers
1819 }}
19- <tr id="container-{{.ID}}">
20+ <tr id="container-{{.ID}}" class="text-center">
2021 <td>
21- <span title="{{.Container.ID}}">{{.Name}}</span>
22- </td>
23- <td class="command-container" style="text-decoration: underline">
24- <span title="{{.Command}}">{{.Command}}</span>
22+ <span
23+ title="{{.Container.ID}}"
24+ class="font-bold"
25+ >{{.Name}}</span
26+ >
2527 </td>
26- <td>{{.CreatedStr}}</td>
2728 <td class="port-container">{{ .Ports }}</td>
2829 <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td>
2930 <td style="color: {{.StateColor}};">{{.State}}</td>
30- <td align="right">
31+ <td>
3132 <!-- iconoir start -->
3233 <button
3334 id="start-button-{{.ID}}"
@@ -46,7 +47,7 @@
4647 hx-target="#container-{{.ID}}"
4748 hx-target-*="#error-message-{{.ID}}"
4849 hx-swap="innerHTML"
49- hx-confirm="Are you sure you want to stop this container?"
50+ hx-confirm="{{index $Lang "manager" "content" "confirm-container-stop"}}"
5051 class="py-2 px-4"
5152 >
5253 <span class="iconoir-system-shut text-xl font-bold"></span>
@@ -70,7 +71,7 @@
7071 hx-target="#container-{{.ID}}"
7172 hx-target-*="#error-message-{{.ID}}"
7273 hx-swap="innerHTML"
73- hx-confirm="Are you sure you want to delete this container image?"
74+ hx-confirm="{{index $Lang "manager" "content" "confirm-delete"}}"
7475 class="py-2 px-4"
7576 >
7677 <!-- iconoir trash -->
@@ -90,7 +91,7 @@
9091 </tr>
9192 {{ else }}
9293 <tr>
93- <td colspan="7" class="mt-2">No available containers.</td>
94+ <td colspan="7" class="mt-2">{{index $Lang "manager" "content" "no-containers"}}.</td>
9495 </tr>
9596 {{
9697 end
@@ -1,33 +1,34 @@
1 <div class="flex flex-col p-4" hx-boost="true">1 <div class="flex flex-col p-4" hx-boost="true">
2- <h1 class="text-lg md:text-xl font-bold">Containers</h1>2+ <h1 class="text-lg md:text-xl font-bold">
3- <table class="p-4 border-t-2" hx-ext="response-targets">3+ {{index .Lang "manager" "content" "h1-containers"}}
4- <!-- <thead>4+ </h1>
5- <tr>5+ <table class="p-4 border-t-2 table-auto" hx-ext="response-targets">
6- <th>Name</th>6+ <thead>
7- <th>Command</th>7+ <tr class="text-center text-gray-400">
8- <th>Created</th>8+ <th>{{index .Lang "manager" "content" "thName"}}</th>
9- <th>Status</th>9+ <th>{{index .Lang "manager" "content" "thPorts"}}</th>
10- <th>Port</th>10+ <th>{{index .Lang "manager" "content" "thStatus"}}</th>
11- <th>State</th>11+ <th>{{index .Lang "manager" "content" "thState"}}</th>
12- <th>Action</th>12+ <th>{{index .Lang "manager" "content" "thActions"}}</th>
13 </tr>13 </tr>
14- </thead> -->14+ </thead>
15 <tbody>15 <tbody>
16+ {{ $Lang := .Lang }}
16 {{17 {{
17 range.containers18 range.containers
18 }}19 }}
19- <tr id="container-{{.ID}}">20+ <tr id="container-{{.ID}}" class="text-center">
20 <td>21 <td>
21- <span title="{{.Container.ID}}">{{.Name}}</span>22+ <span
22- </td>23+ title="{{.Container.ID}}"
23- <td class="command-container" style="text-decoration: underline">24+ class="font-bold"
24- <span title="{{.Command}}">{{.Command}}</span>25+ >{{.Name}}</span
26+ >
25 </td>27 </td>
26- <td>{{.CreatedStr}}</td>
27 <td class="port-container">{{ .Ports }}</td>28 <td class="port-container">{{ .Ports }}</td>
28 <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td>29 <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td>
29 <td style="color: {{.StateColor}};">{{.State}}</td>30 <td style="color: {{.StateColor}};">{{.State}}</td>
30- <td align="right">31+ <td>
31 <!-- iconoir start -->32 <!-- iconoir start -->
32 <button33 <button
33 id="start-button-{{.ID}}"34 id="start-button-{{.ID}}"
@@ -46,7 +47,7 @@
46 hx-target="#container-{{.ID}}"47 hx-target="#container-{{.ID}}"
47 hx-target-*="#error-message-{{.ID}}"48 hx-target-*="#error-message-{{.ID}}"
48 hx-swap="innerHTML"49 hx-swap="innerHTML"
49- hx-confirm="Are you sure you want to stop this container?"50+ hx-confirm="{{index $Lang "manager" "content" "confirm-container-stop"}}"
50 class="py-2 px-4"51 class="py-2 px-4"
51 >52 >
52 <span class="iconoir-system-shut text-xl font-bold"></span>53 <span class="iconoir-system-shut text-xl font-bold"></span>
@@ -70,7 +71,7 @@
70 hx-target="#container-{{.ID}}"71 hx-target="#container-{{.ID}}"
71 hx-target-*="#error-message-{{.ID}}"72 hx-target-*="#error-message-{{.ID}}"
72 hx-swap="innerHTML"73 hx-swap="innerHTML"
73- hx-confirm="Are you sure you want to delete this container image?"74+ hx-confirm="{{index $Lang "manager" "content" "confirm-delete"}}"
74 class="py-2 px-4"75 class="py-2 px-4"
75 >76 >
76 <!-- iconoir trash -->77 <!-- iconoir trash -->
@@ -90,7 +91,7 @@
90 </tr>91 </tr>
91 {{ else }}92 {{ else }}
92 <tr>93 <tr>
93- <td colspan="7" class="mt-2">No available containers.</td>94+ <td colspan="7" class="mt-2">{{index $Lang "manager" "content" "no-containers"}}.</td>
94 </tr>95 </tr>
95 {{96 {{
96 end97 end
M internal/templates/models/locstrings.yml +23 -1
@@ -20,6 +20,17 @@ en:
2020 title: Gordon - Container Engine Manager
2121 content:
2222 title: Container Engine Manager
23+ h1-containers: Containers
24+ h1-images: Images
25+ button-upload: Upload
26+ confirm-delete: Are you sure you want to delete this?
27+ confirm-container-stop: Are you sure you want to stop this container?
28+ no-containers: No available containers.
29+ thName: Name
30+ thPorts: Ports
31+ thStatus: Status
32+ thState: State
33+ thActions: Actions
2334 settings:
2435 meta:
2536 title: Gordon - Settings
@@ -32,7 +43,7 @@ fr:
3243 description: Interface d'administration pour Gordon
3344 keywords: gordon, admin, interface
3445 header:
35- someKey: Bonjour le monde!
46+ someKey: Bonjour le monde !
3647 navmenu:
3748 dashboard: Tableau de bord
3849 manager: Gestionnaire
@@ -48,6 +59,17 @@ fr:
4859 title: Gordon - Gestionnaire de moteur de conteneur
4960 content:
5061 title: Gestionnaire de moteur de conteneur
62+ h1-containers: Conteneurs
63+ h1-images: Images
64+ button-upload: Télécharger
65+ confirm-delete: Êtes-vous sûr de vouloir supprimer ceci?
66+ confirm-container-stop: Êtes-vous sûr de vouloir arrêter ce conteneur?
67+ no-containers: Aucun conteneur disponible.
68+ thName: Nom
69+ thPorts: Ports
70+ thStatus: Statut
71+ thState: État
72+ thActions: Actions
5173 settings:
5274 meta:
5375 title: Gordon - Paramètres
@@ -20,6 +20,17 @@ en:
20 title: Gordon - Container Engine Manager20 title: Gordon - Container Engine Manager
21 content:21 content:
22 title: Container Engine Manager22 title: Container Engine Manager
23+ h1-containers: Containers
24+ h1-images: Images
25+ button-upload: Upload
26+ confirm-delete: Are you sure you want to delete this?
27+ confirm-container-stop: Are you sure you want to stop this container?
28+ no-containers: No available containers.
29+ thName: Name
30+ thPorts: Ports
31+ thStatus: Status
32+ thState: State
33+ thActions: Actions
23 settings:34 settings:
24 meta:35 meta:
25 title: Gordon - Settings36 title: Gordon - Settings
@@ -32,7 +43,7 @@ fr:
32 description: Interface d'administration pour Gordon43 description: Interface d'administration pour Gordon
33 keywords: gordon, admin, interface44 keywords: gordon, admin, interface
34 header:45 header:
35- someKey: Bonjour le monde!46+ someKey: Bonjour le monde !
36 navmenu:47 navmenu:
37 dashboard: Tableau de bord48 dashboard: Tableau de bord
38 manager: Gestionnaire49 manager: Gestionnaire
@@ -48,6 +59,17 @@ fr:
48 title: Gordon - Gestionnaire de moteur de conteneur59 title: Gordon - Gestionnaire de moteur de conteneur
49 content:60 content:
50 title: Gestionnaire de moteur de conteneur61 title: Gestionnaire de moteur de conteneur
62+ h1-containers: Conteneurs
63+ h1-images: Images
64+ button-upload: Télécharger
65+ confirm-delete: Êtes-vous sûr de vouloir supprimer ceci?
66+ confirm-container-stop: Êtes-vous sûr de vouloir arrêter ce conteneur?
67+ no-containers: Aucun conteneur disponible.
68+ thName: Nom
69+ thPorts: Ports
70+ thStatus: Statut
71+ thState: État
72+ thActions: Actions
51 settings:73 settings:
52 meta:74 meta:
53 title: Gordon - Paramètres75 title: Gordon - Paramètres
M internal/webui/public/assets/css/tailwind.css +30 -96
@@ -575,10 +575,6 @@ video {
575575 }
576576 }
577577
578-.fixed {
579- position: fixed;
580-}
581-
582578 .my-2 {
583579 margin-top: 0.5rem;
584580 margin-bottom: 0.5rem;
@@ -676,24 +672,11 @@ video {
676672 width: 100%;
677673 }
678674
679-.w-10 {
680- width: 2.5rem;
681-}
682-
683675 .min-w-fit {
684676 min-width: -moz-fit-content;
685677 min-width: fit-content;
686678 }
687679
688-.max-w-fit {
689- max-width: -moz-fit-content;
690- max-width: fit-content;
691-}
692-
693-.max-w-screen-lg {
694- max-width: 1024px;
695-}
696-
697680 .flex-1 {
698681 flex: 1 1 0%;
699682 }
@@ -702,6 +685,14 @@ video {
702685 flex-grow: 1;
703686 }
704687
688+.table-auto {
689+ table-layout: auto;
690+}
691+
692+.table-fixed {
693+ table-layout: fixed;
694+}
695+
705696 .flex-row {
706697 flex-direction: row;
707698 }
@@ -730,10 +721,6 @@ video {
730721 justify-content: space-between;
731722 }
732723
733-.justify-evenly {
734- justify-content: space-evenly;
735-}
736-
737724 .justify-items-center {
738725 justify-items: center;
739726 }
@@ -879,6 +866,14 @@ video {
879866 padding-bottom: 1rem;
880867 }
881868
869+.text-left {
870+ text-align: left;
871+}
872+
873+.text-center {
874+ text-align: center;
875+}
876+
882877 .align-middle {
883878 vertical-align: middle;
884879 }
@@ -888,6 +883,11 @@ video {
888883 line-height: 2rem;
889884 }
890885
886+.text-3xl {
887+ font-size: 1.875rem;
888+ line-height: 2.25rem;
889+}
890+
891891 .text-4xl {
892892 font-size: 2.25rem;
893893 line-height: 2.5rem;
@@ -913,15 +913,14 @@ video {
913913 line-height: 1.75rem;
914914 }
915915
916-.text-3xl {
917- font-size: 1.875rem;
918- line-height: 2.25rem;
919-}
920-
921916 .font-bold {
922917 font-weight: 700;
923918 }
924919
920+.font-light {
921+ font-weight: 300;
922+}
923+
925924 .font-semibold {
926925 font-weight: 600;
927926 }
@@ -955,6 +954,11 @@ video {
955954 color: rgb(243 244 246 / var(--tw-text-opacity));
956955 }
957956
957+.text-gray-400 {
958+ --tw-text-opacity: 1;
959+ color: rgb(156 163 175 / var(--tw-text-opacity));
960+}
961+
958962 .text-gray-500 {
959963 --tw-text-opacity: 1;
960964 color: rgb(107 114 128 / var(--tw-text-opacity));
@@ -990,10 +994,6 @@ video {
990994 color: rgb(255 255 255 / var(--tw-text-opacity));
991995 }
992996
993-.underline {
994- text-decoration-line: underline;
995-}
996-
997997 .shadow {
998998 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
999999 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1154,50 +1154,18 @@ video {
11541154 display: flex;
11551155 }
11561156
1157- .md\:hidden {
1158- display: none;
1159- }
1160-
11611157 .md\:w-1\/2 {
11621158 width: 50%;
11631159 }
11641160
1165- .md\:w-1\/5 {
1166- width: 20%;
1167- }
1168-
11691161 .md\:w-2\/6 {
11701162 width: 33.333333%;
11711163 }
11721164
1173- .md\:w-full {
1174- width: 100%;
1175- }
1176-
1177- .md\:w-4\/5 {
1178- width: 80%;
1179- }
1180-
1181- .md\:w-72 {
1182- width: 18rem;
1183- }
1184-
1185- .md\:w-64 {
1186- width: 16rem;
1187- }
1188-
1189- .md\:max-w-screen-lg {
1190- max-width: 1024px;
1191- }
1192-
11931165 .md\:flex-row {
11941166 flex-direction: row;
11951167 }
11961168
1197- .md\:flex-col {
1198- flex-direction: column;
1199- }
1200-
12011169 .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
12021170 --tw-space-x-reverse: 0;
12031171 margin-right: calc(1rem * var(--tw-space-x-reverse));
@@ -1210,12 +1178,6 @@ video {
12101178 margin-bottom: calc(0px * var(--tw-space-y-reverse));
12111179 }
12121180
1213- .md\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
1214- --tw-space-y-reverse: 0;
1215- margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
1216- margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
1217- }
1218-
12191181 .md\:p-0 {
12201182 padding: 0px;
12211183 }
@@ -1224,11 +1186,6 @@ video {
12241186 padding: 2rem;
12251187 }
12261188
1227- .md\:py-8 {
1228- padding-top: 2rem;
1229- padding-bottom: 2rem;
1230- }
1231-
12321189 .md\:pt-10 {
12331190 padding-top: 2.5rem;
12341191 }
@@ -1238,11 +1195,6 @@ video {
12381195 line-height: 2rem;
12391196 }
12401197
1241- .md\:text-3xl {
1242- font-size: 1.875rem;
1243- line-height: 2.25rem;
1244- }
1245-
12461198 .md\:text-xl {
12471199 font-size: 1.25rem;
12481200 line-height: 1.75rem;
@@ -1254,10 +1206,6 @@ video {
12541206 display: flex;
12551207 }
12561208
1257- .lg\:w-72 {
1258- width: 18rem;
1259- }
1260-
12611209 .lg\:w-64 {
12621210 width: 16rem;
12631211 }
@@ -1278,10 +1226,6 @@ video {
12781226 justify-content: flex-start;
12791227 }
12801228
1281- .lg\:justify-center {
1282- justify-content: center;
1283- }
1284-
12851229 .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
12861230 --tw-space-y-reverse: 0;
12871231 margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1297,16 +1241,6 @@ video {
12971241 font-size: 2.25rem;
12981242 line-height: 2.5rem;
12991243 }
1300-
1301- .lg\:text-2xl {
1302- font-size: 1.5rem;
1303- line-height: 2rem;
1304- }
1305-
1306- .lg\:text-3xl {
1307- font-size: 1.875rem;
1308- line-height: 2.25rem;
1309- }
13101244 }
13111245
13121246 @media (min-width: 1280px) {
@@ -575,10 +575,6 @@ video {
575 }575 }
576 }576 }
577 577
578-.fixed {
579- position: fixed;
580-}
581-
582 .my-2 {578 .my-2 {
583 margin-top: 0.5rem;579 margin-top: 0.5rem;
584 margin-bottom: 0.5rem;580 margin-bottom: 0.5rem;
@@ -676,24 +672,11 @@ video {
676 width: 100%;672 width: 100%;
677 }673 }
678 674
679-.w-10 {
680- width: 2.5rem;
681-}
682-
683 .min-w-fit {675 .min-w-fit {
684 min-width: -moz-fit-content;676 min-width: -moz-fit-content;
685 min-width: fit-content;677 min-width: fit-content;
686 }678 }
687 679
688-.max-w-fit {
689- max-width: -moz-fit-content;
690- max-width: fit-content;
691-}
692-
693-.max-w-screen-lg {
694- max-width: 1024px;
695-}
696-
697 .flex-1 {680 .flex-1 {
698 flex: 1 1 0%;681 flex: 1 1 0%;
699 }682 }
@@ -702,6 +685,14 @@ video {
702 flex-grow: 1;685 flex-grow: 1;
703 }686 }
704 687
688+.table-auto {
689+ table-layout: auto;
690+}
691+
692+.table-fixed {
693+ table-layout: fixed;
694+}
695+
705 .flex-row {696 .flex-row {
706 flex-direction: row;697 flex-direction: row;
707 }698 }
@@ -730,10 +721,6 @@ video {
730 justify-content: space-between;721 justify-content: space-between;
731 }722 }
732 723
733-.justify-evenly {
734- justify-content: space-evenly;
735-}
736-
737 .justify-items-center {724 .justify-items-center {
738 justify-items: center;725 justify-items: center;
739 }726 }
@@ -879,6 +866,14 @@ video {
879 padding-bottom: 1rem;866 padding-bottom: 1rem;
880 }867 }
881 868
869+.text-left {
870+ text-align: left;
871+}
872+
873+.text-center {
874+ text-align: center;
875+}
876+
882 .align-middle {877 .align-middle {
883 vertical-align: middle;878 vertical-align: middle;
884 }879 }
@@ -888,6 +883,11 @@ video {
888 line-height: 2rem;883 line-height: 2rem;
889 }884 }
890 885
886+.text-3xl {
887+ font-size: 1.875rem;
888+ line-height: 2.25rem;
889+}
890+
891 .text-4xl {891 .text-4xl {
892 font-size: 2.25rem;892 font-size: 2.25rem;
893 line-height: 2.5rem;893 line-height: 2.5rem;
@@ -913,15 +913,14 @@ video {
913 line-height: 1.75rem;913 line-height: 1.75rem;
914 }914 }
915 915
916-.text-3xl {
917- font-size: 1.875rem;
918- line-height: 2.25rem;
919-}
920-
921 .font-bold {916 .font-bold {
922 font-weight: 700;917 font-weight: 700;
923 }918 }
924 919
920+.font-light {
921+ font-weight: 300;
922+}
923+
925 .font-semibold {924 .font-semibold {
926 font-weight: 600;925 font-weight: 600;
927 }926 }
@@ -955,6 +954,11 @@ video {
955 color: rgb(243 244 246 / var(--tw-text-opacity));954 color: rgb(243 244 246 / var(--tw-text-opacity));
956 }955 }
957 956
957+.text-gray-400 {
958+ --tw-text-opacity: 1;
959+ color: rgb(156 163 175 / var(--tw-text-opacity));
960+}
961+
958 .text-gray-500 {962 .text-gray-500 {
959 --tw-text-opacity: 1;963 --tw-text-opacity: 1;
960 color: rgb(107 114 128 / var(--tw-text-opacity));964 color: rgb(107 114 128 / var(--tw-text-opacity));
@@ -990,10 +994,6 @@ video {
990 color: rgb(255 255 255 / var(--tw-text-opacity));994 color: rgb(255 255 255 / var(--tw-text-opacity));
991 }995 }
992 996
993-.underline {
994- text-decoration-line: underline;
995-}
996-
997 .shadow {997 .shadow {
998 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);998 --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
999 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);999 --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -1154,50 +1154,18 @@ video {
1154 display: flex;1154 display: flex;
1155 }1155 }
1156 1156
1157- .md\:hidden {
1158- display: none;
1159- }
1160-
1161 .md\:w-1\/2 {1157 .md\:w-1\/2 {
1162 width: 50%;1158 width: 50%;
1163 }1159 }
1164 1160
1165- .md\:w-1\/5 {
1166- width: 20%;
1167- }
1168-
1169 .md\:w-2\/6 {1161 .md\:w-2\/6 {
1170 width: 33.333333%;1162 width: 33.333333%;
1171 }1163 }
1172 1164
1173- .md\:w-full {
1174- width: 100%;
1175- }
1176-
1177- .md\:w-4\/5 {
1178- width: 80%;
1179- }
1180-
1181- .md\:w-72 {
1182- width: 18rem;
1183- }
1184-
1185- .md\:w-64 {
1186- width: 16rem;
1187- }
1188-
1189- .md\:max-w-screen-lg {
1190- max-width: 1024px;
1191- }
1192-
1193 .md\:flex-row {1165 .md\:flex-row {
1194 flex-direction: row;1166 flex-direction: row;
1195 }1167 }
1196 1168
1197- .md\:flex-col {
1198- flex-direction: column;
1199- }
1200-
1201 .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) {1169 .md\:space-x-4 > :not([hidden]) ~ :not([hidden]) {
1202 --tw-space-x-reverse: 0;1170 --tw-space-x-reverse: 0;
1203 margin-right: calc(1rem * var(--tw-space-x-reverse));1171 margin-right: calc(1rem * var(--tw-space-x-reverse));
@@ -1210,12 +1178,6 @@ video {
1210 margin-bottom: calc(0px * var(--tw-space-y-reverse));1178 margin-bottom: calc(0px * var(--tw-space-y-reverse));
1211 }1179 }
1212 1180
1213- .md\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
1214- --tw-space-y-reverse: 0;
1215- margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
1216- margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
1217- }
1218-
1219 .md\:p-0 {1181 .md\:p-0 {
1220 padding: 0px;1182 padding: 0px;
1221 }1183 }
@@ -1224,11 +1186,6 @@ video {
1224 padding: 2rem;1186 padding: 2rem;
1225 }1187 }
1226 1188
1227- .md\:py-8 {
1228- padding-top: 2rem;
1229- padding-bottom: 2rem;
1230- }
1231-
1232 .md\:pt-10 {1189 .md\:pt-10 {
1233 padding-top: 2.5rem;1190 padding-top: 2.5rem;
1234 }1191 }
@@ -1238,11 +1195,6 @@ video {
1238 line-height: 2rem;1195 line-height: 2rem;
1239 }1196 }
1240 1197
1241- .md\:text-3xl {
1242- font-size: 1.875rem;
1243- line-height: 2.25rem;
1244- }
1245-
1246 .md\:text-xl {1198 .md\:text-xl {
1247 font-size: 1.25rem;1199 font-size: 1.25rem;
1248 line-height: 1.75rem;1200 line-height: 1.75rem;
@@ -1254,10 +1206,6 @@ video {
1254 display: flex;1206 display: flex;
1255 }1207 }
1256 1208
1257- .lg\:w-72 {
1258- width: 18rem;
1259- }
1260-
1261 .lg\:w-64 {1209 .lg\:w-64 {
1262 width: 16rem;1210 width: 16rem;
1263 }1211 }
@@ -1278,10 +1226,6 @@ video {
1278 justify-content: flex-start;1226 justify-content: flex-start;
1279 }1227 }
1280 1228
1281- .lg\:justify-center {
1282- justify-content: center;
1283- }
1284-
1285 .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {1229 .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
1286 --tw-space-y-reverse: 0;1230 --tw-space-y-reverse: 0;
1287 margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));1231 margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1297,16 +1241,6 @@ video {
1297 font-size: 2.25rem;1241 font-size: 2.25rem;
1298 line-height: 2.5rem;1242 line-height: 2.5rem;
1299 }1243 }
1300-
1301- .lg\:text-2xl {
1302- font-size: 1.5rem;
1303- line-height: 2rem;
1304- }
1305-
1306- .lg\:text-3xl {
1307- font-size: 1.875rem;
1308- line-height: 2.25rem;
1309- }
1310 }1244 }
1311 1245
1312 @media (min-width: 1280px) {1246 @media (min-width: 1280px) {

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