Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated

[Created a consistent UI between all pages] > Removed all per page css files > Created a global css file with the all of the necessary files for all pages to work and have a consistent UI

+365 -600
-108
app/src/add-friend-page/add-friend.css
··· 1 - body { 2 - font-family: sans-serif; 3 - background: #f9fafb; 4 - margin: 0; 5 - display: flex; 6 - justify-content: center; 7 - } 8 - 9 - .container { 10 - display: flex; 11 - } 12 - 13 - .container div { 14 - flex: 1; 15 - text-align: center; 16 - margin: auto; 17 - } 18 - 19 - .contentHeader { 20 - text-align: center; 21 - border-bottom: 1px solid #e5e7eb; 22 - padding-bottom: 10px; 23 - } 24 - 25 - .icon-btn { 26 - border: none; 27 - background: none; 28 - cursor: pointer; 29 - padding: 0.4rem; 30 - font-size: 1rem; 31 - } 32 - 33 - .svg-icon { 34 - width: 25px; 35 - height: 25px; 36 - margin: auto; 37 - } 38 - 39 - .app { 40 - width: 100%; 41 - background: #f9fafb; 42 - min-height: 100vh; 43 - } 44 - 45 - header { 46 - background: #fff; 47 - border-bottom: 1px solid #e5e7eb; 48 - padding: 0.75rem 1rem; 49 - display: flex; 50 - justify-content: space-between; 51 - align-items: center; 52 - } 53 - 54 - .content { 55 - padding: 1rem; 56 - display: grid; 57 - gap: 0.75rem; 58 - } 59 - 60 - .card { 61 - background: #fff; 62 - border: 1px solid #e5e7eb; 63 - border-radius: 8px; 64 - padding: 1rem; 65 - } 66 - 67 - .row { 68 - display: flex; 69 - justify-content: space-between; 70 - gap: 1rem; 71 - align-items: center; 72 - } 73 - 74 - label { 75 - display: block; 76 - margin-top: 0.5rem; 77 - margin-bottom: 0.25rem; 78 - font-size: 0.9rem; 79 - color: #374151; 80 - } 81 - 82 - input { 83 - width: 100%; 84 - box-sizing: border-box; 85 - padding: 0.55rem 0.6rem; 86 - border-radius: 8px; 87 - border: 1px solid #d1d5db; 88 - outline: none; 89 - } 90 - 91 - input:focus { 92 - border-color: #3b82f6; 93 - } 94 - 95 - button.primary { 96 - margin-top: 0.75rem; 97 - width: 100%; 98 - padding: 0.6rem 0.75rem; 99 - border: none; 100 - border-radius: 8px; 101 - background: #3b82f6; 102 - color: white; 103 - cursor: pointer; 104 - } 105 - 106 - button.primary:hover { 107 - background: #2563eb; 108 - }
+1 -1
app/src/add-friend-page/add-friend.html
··· 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 5 <script type="module" src="./add-friend.ts"></script> 6 - <link rel="stylesheet" href="./add-friend.css" /> 6 + <link rel="stylesheet" href="../app.css" /> 7 7 </head> 8 8 9 9 <body>
+281
app/src/app.css
··· 1 + body { 2 + font-family: system-ui, sans-serif; 3 + background: #f9fafb; 4 + display: flex; 5 + align-items: center; 6 + justify-content: center; 7 + height: 100vh; 8 + margin: 0; 9 + } 10 + 11 + p { 12 + font-size: 0.9rem; 13 + color: #6b7280; 14 + } 15 + 16 + .hint { 17 + font-size: 0.75rem; 18 + } 19 + 20 + .container { 21 + display: flex; 22 + } 23 + 24 + .container div { 25 + flex: 1; 26 + text-align: center; 27 + margin: auto; 28 + } 29 + 30 + .contentHeader { 31 + text-align: center; 32 + border-bottom: 1px solid #e5e7eb; 33 + padding-bottom: 10px; 34 + } 35 + 36 + .icon-btn { 37 + border: none; 38 + background: none; 39 + cursor: pointer; 40 + padding: 0.4rem; 41 + font-size: 1rem; 42 + } 43 + 44 + .svg-icon { 45 + width: 25px; 46 + height: 25px; 47 + margin: auto; 48 + } 49 + 50 + .app { 51 + width: 100%; 52 + background: #f9fafb; 53 + min-height: 100vh; 54 + } 55 + 56 + header { 57 + background: #fff; 58 + border-bottom: 1px solid #e5e7eb; 59 + padding: 0.75rem 1rem; 60 + display: flex; 61 + align-items: center; 62 + } 63 + 64 + .content { 65 + padding: 1rem; 66 + display: grid; 67 + gap: 0.75rem; 68 + } 69 + 70 + .card { 71 + background: white; 72 + border: 1px solid #d1d5db; 73 + border-radius: 8px; 74 + padding: 1.5rem; 75 + } 76 + 77 + .row { 78 + display: flex; 79 + justify-content: space-between; 80 + gap: 1rem; 81 + align-items: center; 82 + } 83 + 84 + label { 85 + display: block; 86 + margin-top: 0.5rem; 87 + margin-bottom: 0.25rem; 88 + font-size: 0.9rem; 89 + color: #374151; 90 + } 91 + 92 + input { 93 + width: 100%; 94 + box-sizing: border-box; 95 + padding: 0.55rem 0.6rem; 96 + border-radius: 8px; 97 + border: 1px solid #d1d5db; 98 + outline: none; 99 + } 100 + 101 + input:focus { 102 + border-color: #3b82f6; 103 + } 104 + 105 + button.primary { 106 + margin-top: 0.75rem; 107 + width: 100%; 108 + padding: 0.6rem 0.75rem; 109 + border: none; 110 + border: 1px solid #d1d5db; 111 + border-radius: 8px; 112 + background: #3b82f6; 113 + color: white; 114 + cursor: pointer; 115 + } 116 + 117 + button.primary:hover { 118 + background: #2563eb; 119 + } 120 + 121 + button.secondary { 122 + margin-top: 0.75rem; 123 + width: 100%; 124 + padding: 0.6rem 0.75rem; 125 + border: 1px solid #d1d5db; 126 + border-radius: 8px; 127 + background: #ffffff; 128 + color: #000000; 129 + cursor: pointer; 130 + } 131 + 132 + button.secondary:hover { 133 + background: #d7d9de; 134 + } 135 + 136 + .switch { 137 + position: relative; 138 + display: inline-block; 139 + width: 46px; 140 + height: 26px; 141 + } 142 + 143 + .switch input { 144 + opacity: 0; 145 + width: 0; 146 + height: 0; 147 + } 148 + 149 + .slider { 150 + position: absolute; 151 + cursor: pointer; 152 + top: 0; 153 + left: 0; 154 + right: 0; 155 + bottom: 0; 156 + background-color: #ccc; 157 + -webkit-transition: 0.4s; 158 + transition: 0.4s; 159 + border-radius: 50px; 160 + } 161 + 162 + .slider:before { 163 + position: absolute; 164 + content: ""; 165 + height: 20px; 166 + width: 20px; 167 + left: 3px; 168 + bottom: 3px; 169 + background-color: white; 170 + -webkit-transition: 0.4s; 171 + transition: 0.4s; 172 + border-radius: 50%; 173 + } 174 + 175 + input:checked + .slider { 176 + background-color: #3b82f6; 177 + } 178 + 179 + input:focus + .slider { 180 + box-shadow: 0 0 1px #3b82f6; 181 + } 182 + 183 + input:checked + .slider:before { 184 + -webkit-transform: translateX(20px); 185 + -ms-transform: translateX(20px); 186 + transform: translateX(20px); 187 + } 188 + 189 + .status { 190 + background: #fff; 191 + border-bottom: 1px solid #f3f4f6; 192 + padding: 0.75rem 1rem; 193 + display: flex; 194 + justify-content: space-between; 195 + align-items: center; 196 + font-size: 0.9rem; 197 + } 198 + 199 + .friends-header { 200 + display: flex; 201 + align-items: center; 202 + gap: 0.5rem; 203 + margin-bottom: 1rem; 204 + } 205 + 206 + .friend-card { 207 + background: #fff; 208 + border: 1px solid #e5e7eb; 209 + border-radius: 8px; 210 + padding: 1rem; 211 + margin-bottom: 0.75rem; 212 + display: flex; 213 + justify-content: space-between; 214 + align-items: center; 215 + } 216 + 217 + .friend-actions { 218 + display: flex; 219 + align-items: center; 220 + gap: 0.1rem; 221 + } 222 + 223 + .friend-actions .view-btn { 224 + cursor: pointer; 225 + border-radius: 6px; 226 + border: 1px solid #d1d5db; 227 + background: #fff; 228 + padding: 0.3rem 0.5rem; 229 + font-size: 0.8rem; 230 + } 231 + 232 + .friend-actions .view-btn:hover { 233 + background: #f3f4f6; 234 + } 235 + 236 + .friend-actions { 237 + cursor: pointer; 238 + font-size: 1.2rem; 239 + color: #6b7280; 240 + padding: 0 0.3rem; 241 + user-select: none; 242 + } 243 + 244 + .friend-actions { 245 + color: #111827; 246 + } 247 + 248 + .empty-state { 249 + text-align: center; 250 + padding: 3rem 1rem; 251 + color: #6b7280; 252 + } 253 + 254 + .friend-actions button img { 255 + width: 16px; 256 + height: 16px; 257 + vertical-align: middle; 258 + margin-right: 4px; 259 + } 260 + 261 + .icon-circle { 262 + width: 64px; 263 + height: 64px; 264 + background: #dbeafe; 265 + border-radius: 50%; 266 + display: flex; 267 + align-items: center; 268 + justify-content: center; 269 + margin: 0 auto 1rem; 270 + } 271 + 272 + .icon-circle img { 273 + width: 64px; 274 + height: 64px; 275 + } 276 + 277 + button img { 278 + width: 16px; 279 + height: 16px; 280 + margin: auto; 281 + }
-152
app/src/home-page/home.css
··· 1 - .container-sl { 2 - display: flex; 3 - justify-content: space-between; /* This will align the elements on opposite sides of the container */ 4 - } 5 - 6 - .element-al, 7 - .element-ar { 8 - flex: 1; /* Make the elements take up equal width */ 9 - } 10 - 11 - .element-al { 12 - text-align: left; 13 - } 14 - 15 - .element-ar { 16 - text-align: right; 17 - } 18 - 19 - body { 20 - font-family: sans-serif; 21 - background: #f9fafb; 22 - margin: 0; 23 - display: flex; 24 - justify-content: center; 25 - } 26 - 27 - .app { 28 - width: 100%; 29 - background: #f9fafb; 30 - } 31 - 32 - .svg-icon { 33 - width: 25px; 34 - height: 25px; 35 - margin: auto; 36 - } 37 - 38 - header { 39 - background: #fff; 40 - border-bottom: 1px solid #e5e7eb; 41 - padding: 0.75rem 1rem; 42 - display: flex; 43 - justify-content: space-between; 44 - } 45 - 46 - header h1 { 47 - font-size: 1rem; 48 - margin: 0; 49 - display: flex; 50 - align-items: center; 51 - } 52 - 53 - header .icon-btn { 54 - border: none; 55 - background: none; 56 - cursor: pointer; 57 - padding: 0.4rem; 58 - } 59 - 60 - .status { 61 - background: #fff; 62 - border-bottom: 1px solid #f3f4f6; 63 - padding: 0.75rem 1rem; 64 - display: flex; 65 - justify-content: space-between; 66 - align-items: center; 67 - font-size: 0.9rem; 68 - } 69 - 70 - .content { 71 - flex: 1; 72 - overflow-y: auto; 73 - padding: 1rem; 74 - } 75 - 76 - .friends-header { 77 - display: flex; 78 - align-items: center; 79 - gap: 0.5rem; 80 - margin-bottom: 1rem; 81 - } 82 - 83 - .friend-card { 84 - background: #fff; 85 - border: 1px solid #e5e7eb; 86 - border-radius: 8px; 87 - padding: 1rem; 88 - margin-bottom: 0.75rem; 89 - display: flex; 90 - justify-content: space-between; 91 - align-items: center; 92 - } 93 - 94 - .friend-actions { 95 - display: flex; 96 - align-items: center; 97 - gap: 0.1rem; 98 - } 99 - 100 - .friend-actions .view-btn { 101 - cursor: pointer; 102 - border-radius: 6px; 103 - border: 1px solid #d1d5db; 104 - background: #fff; 105 - padding: 0.3rem 0.5rem; 106 - font-size: 0.8rem; 107 - } 108 - 109 - .friend-actions .view-btn:hover { 110 - background: #f3f4f6; 111 - } 112 - 113 - .menu-icon { 114 - width: 16px; 115 - height: 16px; 116 - margin: 0; 117 - } 118 - 119 - .friend-actions { 120 - cursor: pointer; 121 - font-size: 1.2rem; 122 - color: #6b7280; 123 - padding: 0 0.3rem; 124 - user-select: none; 125 - } 126 - 127 - .friend-actions { 128 - color: #111827; 129 - } 130 - 131 - .empty-state { 132 - text-align: center; 133 - padding: 3rem 1rem; 134 - color: #6b7280; 135 - } 136 - 137 - .empty-state button { 138 - margin-top: 0.75rem; 139 - padding: 0.5rem 1rem; 140 - border: none; 141 - border-radius: 6px; 142 - background: #3b82f6; 143 - color: white; 144 - cursor: pointer; 145 - } 146 - 147 - .friend-actions button img { 148 - width: 16px; 149 - height: 16px; 150 - vertical-align: middle; 151 - margin-right: 4px; 152 - }
+15 -7
app/src/home-page/home.html
··· 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 5 <script type="module" src="./home.ts"></script> 6 - <link rel="stylesheet" href="./home.css" /> 6 + <link rel="stylesheet" href="../app.css" /> 7 7 </head> 8 8 9 9 <body> 10 10 <div class="app" x-data="homePageState"> 11 11 <!-- Header --> 12 - <header> 13 - <h1>PrivacyPin</h1> 12 + <header style="justify-content: space-between"> 13 + <strong>PrivacyPin</strong> 14 14 <div> 15 - <!-- <@azom.dev> somehow the "+" emoji does not display in the code for me, but it's temporary anyways --> 16 - <!-- <@kishka.cc> we will need to replace these with svgs, as it's the font that messes up the emoji --> 17 15 <button class="icon-btn" @click="updateServer()"> 18 16 <img 19 17 class="svg-icon" ··· 97 95 /> 98 96 <h3>No friends yet</h3> 99 97 <p>Add friends to start sharing locations</p> 100 - <button @click="addFriend()">Add Friend</button> 98 + <button 99 + class="primary" 100 + style="width: auto" 101 + @click="addFriend()" 102 + > 103 + Add Friend 104 + </button> 101 105 </div> 102 106 </template> 103 107 </div> ··· 107 111 <div class="content" style="text-align: center"> 108 112 <h4>Admin Controls:</h4> 109 113 <div style="display: flex; justify-content: center"> 110 - <button @click="generateSignupKey()"> 114 + <button 115 + class="secondary" 116 + style="width: auto" 117 + @click="generateSignupKey()" 118 + > 111 119 Generate signup key 112 120 </button> 113 121 </div>
-161
app/src/settings-page/settings.css
··· 1 - body { 2 - font-family: sans-serif; 3 - background: #f9fafb; 4 - margin: 0; 5 - display: flex; 6 - justify-content: center; 7 - } 8 - 9 - .container { 10 - display: flex; 11 - } 12 - 13 - .container div { 14 - flex: 1; 15 - text-align: center; 16 - margin: auto; 17 - } 18 - 19 - .contentHeader { 20 - text-align: center; 21 - border-bottom: 1px solid #e5e7eb; 22 - padding-bottom: 10px; 23 - } 24 - 25 - .icon-btn { 26 - border: none; 27 - background: none; 28 - cursor: pointer; 29 - padding: 0.4rem; 30 - font-size: 1rem; 31 - } 32 - 33 - .svg-icon { 34 - width: 25px; 35 - height: 25px; 36 - margin: auto; 37 - } 38 - 39 - .app { 40 - width: 100%; 41 - background: #f9fafb; 42 - min-height: 100vh; 43 - } 44 - 45 - header { 46 - background: #fff; 47 - border-bottom: 1px solid #e5e7eb; 48 - padding: 0.75rem 1rem; 49 - display: flex; 50 - justify-content: space-between; 51 - align-items: center; 52 - } 53 - 54 - .content { 55 - padding: 1rem; 56 - display: grid; 57 - gap: 0.75rem; 58 - } 59 - 60 - .card { 61 - background: #fff; 62 - border: 1px solid #e5e7eb; 63 - border-radius: 8px; 64 - padding: 1rem; 65 - } 66 - 67 - .row { 68 - display: flex; 69 - justify-content: space-between; 70 - gap: 1rem; 71 - align-items: center; 72 - } 73 - 74 - label { 75 - display: block; 76 - margin-top: 0.5rem; 77 - margin-bottom: 0.25rem; 78 - font-size: 0.9rem; 79 - color: #374151; 80 - } 81 - 82 - input { 83 - width: 100%; 84 - box-sizing: border-box; 85 - padding: 0.55rem 0.6rem; 86 - border-radius: 8px; 87 - border: 1px solid #d1d5db; 88 - outline: none; 89 - } 90 - 91 - input:focus { 92 - border-color: #3b82f6; 93 - } 94 - 95 - button.primary { 96 - margin-top: 0.75rem; 97 - width: 100%; 98 - padding: 0.6rem 0.75rem; 99 - border: none; 100 - border-radius: 8px; 101 - background: #3b82f6; 102 - color: white; 103 - cursor: pointer; 104 - } 105 - 106 - button.primary:hover { 107 - background: #2563eb; 108 - } 109 - 110 - .switch { 111 - position: relative; 112 - display: inline-block; 113 - width: 46px; 114 - height: 26px; 115 - } 116 - 117 - .switch input { 118 - opacity: 0; 119 - width: 0; 120 - height: 0; 121 - } 122 - 123 - .slider { 124 - position: absolute; 125 - cursor: pointer; 126 - top: 0; 127 - left: 0; 128 - right: 0; 129 - bottom: 0; 130 - background-color: #ccc; 131 - -webkit-transition: 0.4s; 132 - transition: 0.4s; 133 - border-radius: 50px; 134 - } 135 - 136 - .slider:before { 137 - position: absolute; 138 - content: ""; 139 - height: 20px; 140 - width: 20px; 141 - left: 3px; 142 - bottom: 3px; 143 - background-color: white; 144 - -webkit-transition: 0.4s; 145 - transition: 0.4s; 146 - border-radius: 50%; 147 - } 148 - 149 - input:checked + .slider { 150 - background-color: #3b82f6; 151 - } 152 - 153 - input:focus + .slider { 154 - box-shadow: 0 0 1px #3b82f6; 155 - } 156 - 157 - input:checked + .slider:before { 158 - -webkit-transform: translateX(20px); 159 - -ms-transform: translateX(20px); 160 - transform: translateX(20px); 161 - }
+1 -1
app/src/settings-page/settings.html
··· 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 5 <script type="module" src="./settings.ts"></script> 6 - <link rel="stylesheet" href="./settings.css" /> 6 + <link rel="stylesheet" href="../app.css" /> 7 7 </head> 8 8 9 9 <body>
-116
app/src/signup-page/signup.css
··· 1 - body { 2 - font-family: system-ui, sans-serif; 3 - background: #f9fafb; 4 - display: flex; 5 - align-items: center; 6 - justify-content: center; 7 - height: 100vh; 8 - margin: 0; 9 - } 10 - 11 - .card { 12 - max-width: 90%; 13 - background: white; 14 - border: 1px solid #d1d5db; 15 - border-radius: 8px; 16 - padding: 1.5rem; 17 - box-sizing: border-box; 18 - } 19 - 20 - .header { 21 - text-align: center; 22 - margin-bottom: 1.5rem; 23 - } 24 - 25 - .icon-circle { 26 - width: 64px; 27 - height: 64px; 28 - background: #dbeafe; 29 - border-radius: 50%; 30 - display: flex; 31 - align-items: center; 32 - justify-content: center; 33 - margin: 0 auto 1rem; 34 - } 35 - 36 - .icon-circle img { 37 - width: 64px; 38 - height: 64px; 39 - } 40 - 41 - h1 { 42 - font-size: 1.5rem; 43 - } 44 - 45 - p { 46 - font-size: 0.9rem; 47 - color: #6b7280; 48 - } 49 - 50 - .actions { 51 - display: flex; 52 - flex-direction: column; 53 - gap: 1rem; 54 - } 55 - 56 - label { 57 - display: block; 58 - font-size: 0.85rem; 59 - font-weight: 600; 60 - margin-bottom: 0.25rem; 61 - } 62 - 63 - input { 64 - width: 100%; 65 - padding: 0.5rem 0.75rem; 66 - border: 1px solid #d1d5db; 67 - border-radius: 4px; 68 - font-size: 0.95rem; 69 - box-sizing: border-box; 70 - } 71 - 72 - input:focus { 73 - outline: none; 74 - border-color: #2563eb; 75 - } 76 - 77 - button { 78 - width: 100%; 79 - padding: 0.6rem; 80 - font-size: 0.95rem; 81 - border-radius: 4px; 82 - cursor: pointer; 83 - /*transition: background 0.2s ease;*/ 84 - display: flex; 85 - align-items: center; 86 - justify-content: center; 87 - } 88 - 89 - .btn-primary { 90 - background: #2563eb; 91 - color: white; 92 - border: none; 93 - } 94 - 95 - .btn-primary:hover { 96 - background: #1d4ed8; 97 - } 98 - 99 - .btn-qr { 100 - background: white; 101 - gap: 0.5rem; 102 - border: 1px solid #d1d5db; 103 - } 104 - 105 - .btn-qr:hover { 106 - background: #f3f4f6; 107 - } 108 - 109 - .btn-qr img { 110 - width: 20px; 111 - height: 20px; 112 - } 113 - 114 - .hint { 115 - font-size: 0.75rem; 116 - }
+67 -54
app/src/signup-page/signup.html
··· 3 3 <head> 4 4 <meta charset="UTF-8" /> 5 5 <script type="module" src="./signup.ts"></script> 6 - <link rel="stylesheet" href="./signup.css" /> 6 + <link rel="stylesheet" href="../app.css" /> 7 7 </head> 8 8 9 9 <body> 10 - <div class="card"> 11 - <div class="header"> 12 - <div class="icon-circle"> 13 - <img src="/src/assets/pin-location.svg" alt="Pin Icon" /> 14 - </div> 15 - <h1>PrivacyPin</h1> 16 - <p>Connect with a server to start sharing</p> 17 - </div> 10 + <div class="app" x-data="signupPageState"> 11 + <div class="content"> 12 + <div class="card" style="margin-top: 12.5%"> 13 + <div class="header"> 14 + <div class="icon-circle"> 15 + <img 16 + src="/src/assets/pin-location.svg" 17 + alt="Pin Icon" 18 + /> 19 + </div> 20 + <h1 style="text-align: center">PrivacyPin</h1> 21 + <p style="text-align: center"> 22 + Connect with a server to start sharing 23 + </p> 24 + </div> 18 25 19 - <!-- x-data connects this element to the signupPageState Alpine component, enabling its data (serverAddress and signupKey) and functions (signup and scanQR) to work within it :) --> 20 - <!-- TODO: make this a form instead? --> 26 + <!-- x-data connects this element to the signupPageState Alpine component, enabling its data (serverAddress and signupKey) and functions (signup and scanQR) to work within it :) --> 27 + <!-- TODO: make this a form instead? --> 21 28 22 - <!-- <input type="text" @keyup.shift.enter="alert('Submitted!')"> use something like this to submit? --> 23 - <div class="actions" x-data="signupPageState"> 24 - <div> 25 - <label for="server">Server Address</label> 26 - <input 27 - id="server" 28 - type="url" 29 - placeholder="https://your-server.com" 30 - x-model="serverAddress" 31 - required 32 - /> 33 - </div> 29 + <!-- <input type="text" @keyup.shift.enter="alert('Submitted!')"> use something like this to submit? --> 30 + <div class="actions" x-data="signupPageState"> 31 + <div> 32 + <label for="server">Server Address</label> 33 + <input 34 + id="server" 35 + type="url" 36 + placeholder="https://your-server.com" 37 + x-model="serverAddress" 38 + required 39 + /> 40 + </div> 34 41 35 - <div> 36 - <label for="key">Signup Key</label> 37 - <input 38 - id="key" 39 - type="password" 40 - placeholder="Enter your signup key" 41 - x-model="signupKey" 42 - required 43 - /> 44 - </div> 42 + <div> 43 + <label for="key">Signup Key</label> 44 + <input 45 + id="key" 46 + type="password" 47 + placeholder="Enter your signup key" 48 + x-model="signupKey" 49 + required 50 + /> 51 + </div> 45 52 46 - <p class="hint"> 47 - Scan a QR code to automatically fill both server address and 48 - signup key 49 - </p> 50 - <button 51 - type="button" 52 - x-bind:disabled="isDoingStuff" 53 - class="btn-qr" 54 - @click="await scanQR()" 55 - > 56 - <img src="/src/assets/scan-qr.svg" alt="QR Icon" /> 57 - Scan QR Code 58 - </button> 53 + <p class="hint"> 54 + Scan a QR code to automatically fill both server 55 + address and signup key 56 + </p> 57 + <button 58 + type="button" 59 + x-bind:disabled="isDoingStuff" 60 + class="secondary" 61 + @click="await scanQR()" 62 + > 63 + <img 64 + style="margin: auto" 65 + src="/src/assets/scan-qr.svg" 66 + alt="QR Icon" 67 + /> 68 + <e style="margin: auto">Scan QR Code</e> 69 + </button> 59 70 60 - <button 61 - class="btn-primary" 62 - x-bind:disabled="isDoingStuff" 63 - @click="await signup()" 64 - > 65 - <span x-show="isDoingStuff">Connecting...</span> 66 - <span x-show="!isDoingStuff">Connect</span> 67 - </button> 71 + <button 72 + class="primary" 73 + x-bind:disabled="isDoingStuff" 74 + @click="await signup()" 75 + > 76 + <span x-show="isDoingStuff">Connecting...</span> 77 + <span x-show="!isDoingStuff">Connect</span> 78 + </button> 79 + </div> 80 + </div> 68 81 </div> 69 82 </div> 70 83 </body>