detached 387d9d7

Refactor: Multiple Backend and Frontend Changes

Backend:
- Rename language file from `strings.yml` to `locstrings.yml` for more clarity.
- Remove image upload handling logic from `uploadimage.go`.

Frontend:
- Update padding in `admin/index.gohtml` and `admin/manager.gohtml`.
- Replace left menu with a generic menu in multiple admin templates.
- Add deferred loading of iconoir.css in `header.gohtml`.
- Remove unused templates `leftmenu.gohtml` and `strings.yml`.

Config:
- Update content paths in `tailwind.config.js`
commit
387d9d72b3b865bc983bad81374d5100d2e51dc9
author
bnema · · 2023-10-04 13:42 UTC
parent
185e5a4
signature
unsigned
9 files · +123 -21
M internal/app/app.go +1 -1
@@ -100,7 +100,7 @@ func NewApp() *App {
100100 }
101101
102102 // Open the strings.yml file containing the strings for the current language
103- file, err := templates.TemplateFS.Open("strings.yml")
103+ file, err := templates.TemplateFS.Open("locstrings.yml")
104104 if err != nil {
105105 log.Fatalf("Failed to open strings.yml: %v", err)
106106 }
@@ -100,7 +100,7 @@ func NewApp() *App {
100 }100 }
101 101
102 // Open the strings.yml file containing the strings for the current language102 // Open the strings.yml file containing the strings for the current language
103- file, err := templates.TemplateFS.Open("strings.yml")103+ file, err := templates.TemplateFS.Open("locstrings.yml")
104 if err != nil {104 if err != nil {
105 log.Fatalf("Failed to open strings.yml: %v", err)105 log.Fatalf("Failed to open strings.yml: %v", err)
106 }106 }
R internal/httpserve/handler/uploadimage.go → internal/httpserve/handler/uploadcontainerimage.go +0 -0 renamed, 100% similar

renamed, 100% similar

M internal/templates/models/html/admin/index.gohtml +2 -2
@@ -2,8 +2,8 @@
22 <html lang="en">
33 {{template "header.gohtml" .}}
44
5- <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0">
6- {{template "leftmenu.gohtml" .}}
5+ <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-0">
6+ {{template "menu.gohtml" .}}
77 <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4">
88 <div class="hidden md:flex mb-4 justify-end">
99 <div class="w-72 h-10 bg-gray-300 rounded-lg">
@@ -2,8 +2,8 @@
2 <html lang="en">2 <html lang="en">
3 {{template "header.gohtml" .}}3 {{template "header.gohtml" .}}
4 4
5- <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0">5+ <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-0">
6- {{template "leftmenu.gohtml" .}}6+ {{template "menu.gohtml" .}}
7 <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4">7 <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4">
8 <div class="hidden md:flex mb-4 justify-end">8 <div class="hidden md:flex mb-4 justify-end">
9 <div class="w-72 h-10 bg-gray-300 rounded-lg">9 <div class="w-72 h-10 bg-gray-300 rounded-lg">
M internal/templates/models/html/admin/manager.gohtml +2 -2
@@ -3,8 +3,8 @@
33 <html lang="en">
44 {{template "header.gohtml" .}}
55
6- <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0">
7- {{template "leftmenu.gohtml" .}}
6+ <body class="bg-zinc-200 flex flex-col lg:flex-row p-0">
7+ {{template "menu.gohtml" .}}
88 <!-- Main content here -->
99 <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4">
1010 <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24">
@@ -3,8 +3,8 @@
3 <html lang="en">3 <html lang="en">
4 {{template "header.gohtml" .}}4 {{template "header.gohtml" .}}
5 5
6- <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0">6+ <body class="bg-zinc-200 flex flex-col lg:flex-row p-0">
7- {{template "leftmenu.gohtml" .}}7+ {{template "menu.gohtml" .}}
8 <!-- Main content here -->8 <!-- Main content here -->
9 <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4">9 <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4">
10 <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24">10 <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24">
M internal/templates/models/html/fragments/header.gohtml +11 -1
@@ -3,10 +3,20 @@
33 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
44 <title>{{ .header.meta.title }}</title>
55 <link rel="stylesheet" href="/assets/css/tailwind.css" />
6- <link rel="stylesheet" href="/libs/iconoir.css" />
6+
77 <script src="/libs/htmx.min.js"></script>
88 <script src="/libs/htmx-response-targets.js"></script>
99 <script src="/libs/htmx-debug.js"></script>
1010 <script src="/libs/htmx-sse.js"></script>
11+ <script defer src="https://unpkg.com/hyperscript.org@0.9.11"></script>
1112 <script defer src="/assets/js/custom.js"></script>
13+ <!-- script wait for the page to be loaded and insert <link rel="stylesheet" href="/libs/iconoir.css" /> -->
14+ <script>
15+ document.addEventListener("DOMContentLoaded", function () {
16+ var linkElement = document.createElement("link");
17+ linkElement.setAttribute("rel", "stylesheet");
18+ linkElement.setAttribute("href", "/libs/iconoir.css");
19+ document.head.appendChild(linkElement);
20+ });
21+ </script>
1222 </head>
@@ -3,10 +3,20 @@
3 <meta name="viewport" content="width=device-width, initial-scale=1.0" />3 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
4 <title>{{ .header.meta.title }}</title>4 <title>{{ .header.meta.title }}</title>
5 <link rel="stylesheet" href="/assets/css/tailwind.css" />5 <link rel="stylesheet" href="/assets/css/tailwind.css" />
6- <link rel="stylesheet" href="/libs/iconoir.css" />6+
7 <script src="/libs/htmx.min.js"></script>7 <script src="/libs/htmx.min.js"></script>
8 <script src="/libs/htmx-response-targets.js"></script>8 <script src="/libs/htmx-response-targets.js"></script>
9 <script src="/libs/htmx-debug.js"></script>9 <script src="/libs/htmx-debug.js"></script>
10 <script src="/libs/htmx-sse.js"></script>10 <script src="/libs/htmx-sse.js"></script>
11+ <script defer src="https://unpkg.com/hyperscript.org@0.9.11"></script>
11 <script defer src="/assets/js/custom.js"></script>12 <script defer src="/assets/js/custom.js"></script>
13+ <!-- script wait for the page to be loaded and insert <link rel="stylesheet" href="/libs/iconoir.css" /> -->
14+ <script>
15+ document.addEventListener("DOMContentLoaded", function () {
16+ var linkElement = document.createElement("link");
17+ linkElement.setAttribute("rel", "stylesheet");
18+ linkElement.setAttribute("href", "/libs/iconoir.css");
19+ document.head.appendChild(linkElement);
20+ });
21+ </script>
12 </head>22 </head>
R internal/templates/models/html/fragments/leftmenu.gohtml → internal/templates/models/html/fragments/menu.gohtml +12 -14 renamed, 59% similar
@@ -1,7 +1,5 @@
1-<div
2- class="w-full md:w-1/5 flex flex-col space-evenly md:p-0 max-w-fit min-w-fit"
3->
4- <div class="hidden md:flex justify-center p-4">
1+<div id="menubar" class="lg:w-64 w-full">
2+ <div class="hidden lg:flex justify-center p-4">
53 <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo" class="h-32" />
64 </div>
75 <div
@@ -9,7 +7,7 @@
97 >
108 <div class="w-full flex justify-center">
119 <h1
12- class="gordon-title text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter"
10+ class="gordon-title text-5xl text-gordonmascot-hblue tracking-tighter"
1311 >
1412 Gordon
1513 </h1>
@@ -17,28 +15,28 @@
1715 </div>
1816 <div class="w-full">
1917 <ul
20- class="flex flex-wrap md:flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl min-w-fit justify-center"
18+ class="flex flex-wrap lg:flex-col lg:space-y-6 lg:py-8 font-semibold text-gray-100 text-lg min-w-fit justify-center"
2119 >
22- <li class="flex items-center w-1/2 md:w-full">
23- <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>
20+ <li class="flex items-center lg:justify-start w-1/2 lg:w-full">
21+ <i class="iconoir-dashboard-speed mr-2 text-3xl"></i>
2422 <a href="/admin">Dashboard</a>
2523 </li>
26- <li class="flex items-center w-1/2 md:w-full">
27- <i class="iconoir-server mr-2 md:text-3xl"></i>
24+ <li class="flex items-center lg:justify-start w-1/2 lg:w-full">
25+ <i class="iconoir-server mr-2 text-3xl"></i>
2826 <a href="/admin/manager">Manager</a>
2927 </li>
3028 <li
31- class="flex items-center w-1/2 md:w-full text-gray-600"
29+ class="flex items-center w-1/2 lg:w-full text-gray-600"
3230 style="display: none"
3331 >
34- <i class="iconoir-community mr-2 md:text-3xl"></i>
32+ <i class="iconoir-community mr-2 text-3xl"></i>
3533 <a title="Disabled" href="#">Users</a>
3634 </li>
3735 <li
38- class="flex items-center w-1/2 md:w-full text-gray-600"
36+ class="flex items-center w-1/2 lg:w-full text-gray-600"
3937 style="display: none"
4038 >
41- <i class="iconoir-database-settings mr-2 md:text-3xl"></i>
39+ <i class="iconoir-database-settings mr-2 text-3xl"></i>
4240 <a title="Disabled" href="#">Settings</a>
4341 </li>
4442 </ul>
@@ -1,7 +1,5 @@
1-<div1+<div id="menubar" class="lg:w-64 w-full">
2- class="w-full md:w-1/5 flex flex-col space-evenly md:p-0 max-w-fit min-w-fit"2+ <div class="hidden lg:flex justify-center p-4">
3->
4- <div class="hidden md:flex justify-center p-4">
5 <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo" class="h-32" />3 <img src="/assets/imgs/gordon-mascot-hq-trsp.png" alt="logo" class="h-32" />
6 </div>4 </div>
7 <div5 <div
@@ -9,7 +7,7 @@
9 >7 >
10 <div class="w-full flex justify-center">8 <div class="w-full flex justify-center">
11 <h19 <h1
12- class="gordon-title text-4xl xl:text-5xl text-gordonmascot-hblue tracking-tighter"10+ class="gordon-title text-5xl text-gordonmascot-hblue tracking-tighter"
13 >11 >
14 Gordon12 Gordon
15 </h1>13 </h1>
@@ -17,28 +15,28 @@
17 </div>15 </div>
18 <div class="w-full">16 <div class="w-full">
19 <ul17 <ul
20- class="flex flex-wrap md:flex-col md:space-y-6 md:py-8 font-semibold text-gray-100 text-lg md:text-2xl min-w-fit justify-center"18+ class="flex flex-wrap lg:flex-col lg:space-y-6 lg:py-8 font-semibold text-gray-100 text-lg min-w-fit justify-center"
21 >19 >
22- <li class="flex items-center w-1/2 md:w-full">20+ <li class="flex items-center lg:justify-start w-1/2 lg:w-full">
23- <i class="iconoir-dashboard-speed mr-2 md:text-3xl"></i>21+ <i class="iconoir-dashboard-speed mr-2 text-3xl"></i>
24 <a href="/admin">Dashboard</a>22 <a href="/admin">Dashboard</a>
25 </li>23 </li>
26- <li class="flex items-center w-1/2 md:w-full">24+ <li class="flex items-center lg:justify-start w-1/2 lg:w-full">
27- <i class="iconoir-server mr-2 md:text-3xl"></i>25+ <i class="iconoir-server mr-2 text-3xl"></i>
28 <a href="/admin/manager">Manager</a>26 <a href="/admin/manager">Manager</a>
29 </li>27 </li>
30 <li28 <li
31- class="flex items-center w-1/2 md:w-full text-gray-600"29+ class="flex items-center w-1/2 lg:w-full text-gray-600"
32 style="display: none"30 style="display: none"
33 >31 >
34- <i class="iconoir-community mr-2 md:text-3xl"></i>32+ <i class="iconoir-community mr-2 text-3xl"></i>
35 <a title="Disabled" href="#">Users</a>33 <a title="Disabled" href="#">Users</a>
36 </li>34 </li>
37 <li35 <li
38- class="flex items-center w-1/2 md:w-full text-gray-600"36+ class="flex items-center w-1/2 lg:w-full text-gray-600"
39 style="display: none"37 style="display: none"
40 >38 >
41- <i class="iconoir-database-settings mr-2 md:text-3xl"></i>39+ <i class="iconoir-database-settings mr-2 text-3xl"></i>
42 <a title="Disabled" href="#">Settings</a>40 <a title="Disabled" href="#">Settings</a>
43 </li>41 </li>
44 </ul>42 </ul>
R internal/templates/models/strings.yml → internal/templates/models/locstrings.yml +0 -0 renamed, 100% similar

renamed, 100% similar

M internal/webui/public/assets/css/tailwind.css +94 -0
@@ -575,6 +575,10 @@ video {
575575 }
576576 }
577577
578+.fixed {
579+ position: fixed;
580+}
581+
578582 .my-2 {
579583 margin-top: 0.5rem;
580584 margin-bottom: 0.5rem;
@@ -672,6 +676,10 @@ video {
672676 width: 100%;
673677 }
674678
679+.w-10 {
680+ width: 2.5rem;
681+}
682+
675683 .min-w-fit {
676684 min-width: -moz-fit-content;
677685 min-width: fit-content;
@@ -682,6 +690,10 @@ video {
682690 max-width: fit-content;
683691 }
684692
693+.max-w-screen-lg {
694+ max-width: 1024px;
695+}
696+
685697 .flex-1 {
686698 flex: 1 1 0%;
687699 }
@@ -718,6 +730,10 @@ video {
718730 justify-content: space-between;
719731 }
720732
733+.justify-evenly {
734+ justify-content: space-evenly;
735+}
736+
721737 .justify-items-center {
722738 justify-items: center;
723739 }
@@ -897,6 +913,11 @@ video {
897913 line-height: 1.75rem;
898914 }
899915
916+.text-3xl {
917+ font-size: 1.875rem;
918+ line-height: 2.25rem;
919+}
920+
900921 .font-bold {
901922 font-weight: 700;
902923 }
@@ -1133,6 +1154,10 @@ video {
11331154 display: flex;
11341155 }
11351156
1157+ .md\:hidden {
1158+ display: none;
1159+ }
1160+
11361161 .md\:w-1\/2 {
11371162 width: 50%;
11381163 }
@@ -1149,6 +1174,22 @@ video {
11491174 width: 100%;
11501175 }
11511176
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+
11521193 .md\:flex-row {
11531194 flex-direction: row;
11541195 }
@@ -1209,10 +1250,63 @@ video {
12091250 }
12101251
12111252 @media (min-width: 1024px) {
1253+ .lg\:flex {
1254+ display: flex;
1255+ }
1256+
1257+ .lg\:w-72 {
1258+ width: 18rem;
1259+ }
1260+
1261+ .lg\:w-64 {
1262+ width: 16rem;
1263+ }
1264+
1265+ .lg\:w-full {
1266+ width: 100%;
1267+ }
1268+
1269+ .lg\:flex-row {
1270+ flex-direction: row;
1271+ }
1272+
1273+ .lg\:flex-col {
1274+ flex-direction: column;
1275+ }
1276+
1277+ .lg\:justify-start {
1278+ justify-content: flex-start;
1279+ }
1280+
1281+ .lg\:justify-center {
1282+ justify-content: center;
1283+ }
1284+
1285+ .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
1286+ --tw-space-y-reverse: 0;
1287+ margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
1288+ margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
1289+ }
1290+
1291+ .lg\:py-8 {
1292+ padding-top: 2rem;
1293+ padding-bottom: 2rem;
1294+ }
1295+
12121296 .lg\:text-4xl {
12131297 font-size: 2.25rem;
12141298 line-height: 2.5rem;
12151299 }
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+ }
12161310 }
12171311
12181312 @media (min-width: 1280px) {
@@ -575,6 +575,10 @@ video {
575 }575 }
576 }576 }
577 577
578+.fixed {
579+ position: fixed;
580+}
581+
578 .my-2 {582 .my-2 {
579 margin-top: 0.5rem;583 margin-top: 0.5rem;
580 margin-bottom: 0.5rem;584 margin-bottom: 0.5rem;
@@ -672,6 +676,10 @@ video {
672 width: 100%;676 width: 100%;
673 }677 }
674 678
679+.w-10 {
680+ width: 2.5rem;
681+}
682+
675 .min-w-fit {683 .min-w-fit {
676 min-width: -moz-fit-content;684 min-width: -moz-fit-content;
677 min-width: fit-content;685 min-width: fit-content;
@@ -682,6 +690,10 @@ video {
682 max-width: fit-content;690 max-width: fit-content;
683 }691 }
684 692
693+.max-w-screen-lg {
694+ max-width: 1024px;
695+}
696+
685 .flex-1 {697 .flex-1 {
686 flex: 1 1 0%;698 flex: 1 1 0%;
687 }699 }
@@ -718,6 +730,10 @@ video {
718 justify-content: space-between;730 justify-content: space-between;
719 }731 }
720 732
733+.justify-evenly {
734+ justify-content: space-evenly;
735+}
736+
721 .justify-items-center {737 .justify-items-center {
722 justify-items: center;738 justify-items: center;
723 }739 }
@@ -897,6 +913,11 @@ video {
897 line-height: 1.75rem;913 line-height: 1.75rem;
898 }914 }
899 915
916+.text-3xl {
917+ font-size: 1.875rem;
918+ line-height: 2.25rem;
919+}
920+
900 .font-bold {921 .font-bold {
901 font-weight: 700;922 font-weight: 700;
902 }923 }
@@ -1133,6 +1154,10 @@ video {
1133 display: flex;1154 display: flex;
1134 }1155 }
1135 1156
1157+ .md\:hidden {
1158+ display: none;
1159+ }
1160+
1136 .md\:w-1\/2 {1161 .md\:w-1\/2 {
1137 width: 50%;1162 width: 50%;
1138 }1163 }
@@ -1149,6 +1174,22 @@ video {
1149 width: 100%;1174 width: 100%;
1150 }1175 }
1151 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+
1152 .md\:flex-row {1193 .md\:flex-row {
1153 flex-direction: row;1194 flex-direction: row;
1154 }1195 }
@@ -1209,10 +1250,63 @@ video {
1209 }1250 }
1210 1251
1211 @media (min-width: 1024px) {1252 @media (min-width: 1024px) {
1253+ .lg\:flex {
1254+ display: flex;
1255+ }
1256+
1257+ .lg\:w-72 {
1258+ width: 18rem;
1259+ }
1260+
1261+ .lg\:w-64 {
1262+ width: 16rem;
1263+ }
1264+
1265+ .lg\:w-full {
1266+ width: 100%;
1267+ }
1268+
1269+ .lg\:flex-row {
1270+ flex-direction: row;
1271+ }
1272+
1273+ .lg\:flex-col {
1274+ flex-direction: column;
1275+ }
1276+
1277+ .lg\:justify-start {
1278+ justify-content: flex-start;
1279+ }
1280+
1281+ .lg\:justify-center {
1282+ justify-content: center;
1283+ }
1284+
1285+ .lg\:space-y-6 > :not([hidden]) ~ :not([hidden]) {
1286+ --tw-space-y-reverse: 0;
1287+ margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
1288+ margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
1289+ }
1290+
1291+ .lg\:py-8 {
1292+ padding-top: 2rem;
1293+ padding-bottom: 2rem;
1294+ }
1295+
1212 .lg\:text-4xl {1296 .lg\:text-4xl {
1213 font-size: 2.25rem;1297 font-size: 2.25rem;
1214 line-height: 2.5rem;1298 line-height: 2.5rem;
1215 }1299 }
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+ }
1216 }1310 }
1217 1311
1218 @media (min-width: 1280px) {1312 @media (min-width: 1280px) {
M tailwind.config.js +1 -1
@@ -2,7 +2,7 @@
22 module.exports = {
33 content: [
44 "./internal/webui/**/*.{gohtml,go}",
5- "./internal/gotemplate/models/html/**/*.{gohtml, go}"
5+ "./internal/templates/models/html/**/*.{gohtml, go}"
66 ],
77 theme: {
88 extend: {
@@ -2,7 +2,7 @@
2 module.exports = {2 module.exports = {
3 content: [3 content: [
4 "./internal/webui/**/*.{gohtml,go}",4 "./internal/webui/**/*.{gohtml,go}",
5- "./internal/gotemplate/models/html/**/*.{gohtml, go}"5+ "./internal/templates/models/html/**/*.{gohtml, go}"
6 ],6 ],
7 theme: {7 theme: {
8 extend: {8 extend: {

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