Our Personal Data Server from scratch! tranquil.farm
atproto pds rust postgresql fun oauth

refactor(frontend): extract security, sessions, DID document styles #59

merged opened by oyster.cafe targeting main from refactor/extract-scoped-styles
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:3fwecdnvtcscjnrx2p4n7alz/sh.tangled.repo.pull/3mhdc5fbtya22
+20 -971
Diff #0
+2 -170
frontend/src/components/dashboard/DidDocumentContent.svelte
··· 150 150 </div> 151 151 <code class="item-key">{vm.publicKeyMultibase}</code> 152 152 </div> 153 - <button type="button" class="remove-btn" onclick={() => removeVerificationMethod(vm.id)}> 153 + <button type="button" class="sm danger-outline" onclick={() => removeVerificationMethod(vm.id)}> 154 154 {$_('didEditor.removeKey')} 155 155 </button> 156 156 </li> ··· 194 194 {#each alsoKnownAs as handle} 195 195 <li class="list-item"> 196 196 <span class="item-handle">{handle}</span> 197 - <button type="button" class="remove-btn" onclick={() => removeHandle(handle)}> 197 + <button type="button" class="sm danger-outline" onclick={() => removeHandle(handle)}> 198 198 {$_('didEditor.removeHandle')} 199 199 </button> 200 200 </li> ··· 245 245 </div> 246 246 {/if} 247 247 </div> 248 - 249 - <style> 250 - .did-editor { 251 - max-width: var(--width-lg); 252 - } 253 - 254 - .loading, 255 - .empty { 256 - color: var(--text-secondary); 257 - padding: var(--space-4); 258 - } 259 - 260 - section { 261 - background: var(--bg-secondary); 262 - padding: var(--space-5); 263 - border-radius: var(--radius-lg); 264 - margin-bottom: var(--space-5); 265 - } 266 - 267 - section h3 { 268 - margin: 0 0 var(--space-2) 0; 269 - font-size: var(--text-base); 270 - } 271 - 272 - .help-section { 273 - background: var(--bg-card); 274 - border: 1px solid var(--border-color); 275 - } 276 - 277 - .help-text { 278 - color: var(--text-secondary); 279 - font-size: var(--text-sm); 280 - margin: 0; 281 - line-height: 1.5; 282 - } 283 - 284 - .description { 285 - color: var(--text-secondary); 286 - font-size: var(--text-sm); 287 - margin: 0 0 var(--space-4) 0; 288 - } 289 - 290 - .list { 291 - list-style: none; 292 - padding: 0; 293 - margin: 0 0 var(--space-4) 0; 294 - display: flex; 295 - flex-direction: column; 296 - gap: var(--space-2); 297 - } 298 - 299 - .list-item { 300 - display: flex; 301 - justify-content: space-between; 302 - align-items: flex-start; 303 - padding: var(--space-3); 304 - background: var(--bg-card); 305 - border: 1px solid var(--border-color); 306 - border-radius: var(--radius-md); 307 - gap: var(--space-3); 308 - } 309 - 310 - .item-info { 311 - display: flex; 312 - flex-direction: column; 313 - gap: var(--space-1); 314 - min-width: 0; 315 - } 316 - 317 - .item-header { 318 - display: flex; 319 - align-items: center; 320 - gap: var(--space-2); 321 - } 322 - 323 - .item-id { 324 - font-weight: var(--font-medium); 325 - font-family: var(--font-mono); 326 - font-size: var(--text-sm); 327 - } 328 - 329 - .item-type { 330 - font-size: var(--text-xs); 331 - padding: var(--space-1) var(--space-2); 332 - background: var(--accent); 333 - color: var(--text-inverse); 334 - border-radius: var(--radius-sm); 335 - } 336 - 337 - .item-key { 338 - font-family: var(--font-mono); 339 - font-size: var(--text-xs); 340 - color: var(--text-secondary); 341 - word-break: break-all; 342 - } 343 - 344 - .item-handle { 345 - font-family: var(--font-mono); 346 - font-size: var(--text-sm); 347 - } 348 - 349 - .remove-btn { 350 - flex-shrink: 0; 351 - padding: var(--space-2) var(--space-3); 352 - font-size: var(--text-sm); 353 - background: transparent; 354 - border: 1px solid var(--error-border); 355 - color: var(--error-text); 356 - border-radius: var(--radius-md); 357 - cursor: pointer; 358 - } 359 - 360 - .remove-btn:hover { 361 - background: var(--error-bg); 362 - } 363 - 364 - .field { 365 - display: flex; 366 - flex-direction: column; 367 - gap: var(--space-1); 368 - } 369 - 370 - .field label { 371 - font-size: var(--text-sm); 372 - font-weight: var(--font-medium); 373 - color: var(--text-secondary); 374 - } 375 - 376 - .add-form { 377 - display: grid; 378 - grid-template-columns: 1fr 2fr auto; 379 - gap: var(--space-3); 380 - align-items: end; 381 - } 382 - 383 - .add-form.single { 384 - grid-template-columns: 1fr auto; 385 - } 386 - 387 - .preview-section pre { 388 - background: var(--bg-card); 389 - border: 1px solid var(--border-color); 390 - border-radius: var(--radius-md); 391 - padding: var(--space-4); 392 - overflow-x: auto; 393 - font-size: var(--text-xs); 394 - font-family: var(--font-mono); 395 - } 396 - 397 - .actions { 398 - display: flex; 399 - justify-content: flex-end; 400 - } 401 - 402 - @media (max-width: 600px) { 403 - .add-form { 404 - grid-template-columns: 1fr; 405 - } 406 - 407 - .list-item { 408 - flex-direction: column; 409 - } 410 - 411 - .remove-btn { 412 - width: 100%; 413 - } 414 - } 415 - </style>
+15 -632
frontend/src/components/dashboard/SecurityContent.svelte
··· 631 631 {#if editingPasskeyId === passkey.id} 632 632 <div class="passkey-edit"> 633 633 <input type="text" bind:value={editPasskeyName} placeholder={$_('security.passkeyName')} /> 634 - <button type="button" class="small" onclick={handleSavePasskeyName}>{$_('common.save')}</button> 635 - <button type="button" class="small secondary" onclick={cancelEditPasskey}>{$_('common.cancel')}</button> 634 + <button type="button" class="sm" onclick={handleSavePasskeyName}>{$_('common.save')}</button> 635 + <button type="button" class="sm secondary" onclick={cancelEditPasskey}>{$_('common.cancel')}</button> 636 636 </div> 637 637 {:else} 638 638 <div class="passkey-info"> ··· 645 645 </span> 646 646 </div> 647 647 <div class="passkey-actions"> 648 - <button type="button" class="small secondary" onclick={() => startEditPasskey(passkey)}>{$_('security.rename')}</button> 648 + <button type="button" class="sm secondary" onclick={() => startEditPasskey(passkey)}>{$_('security.rename')}</button> 649 649 {#if hasPassword || passkeys.length > 1} 650 - <button type="button" class="small danger" onclick={() => handleDeletePasskey(passkey.id)}>{$_('security.deletePasskey')}</button> 650 + <button type="button" class="sm danger-outline" onclick={() => handleDeletePasskey(passkey.id)}>{$_('security.deletePasskey')}</button> 651 651 {/if} 652 652 </div> 653 653 {/if} ··· 690 690 <form class="inline-form" onsubmit={handleRegenerateBackupCodes}> 691 691 <h4>{$_('security.regenerateBackupCodes')}</h4> 692 692 <p class="warning-text">{$_('security.regenerateConfirm')}</p> 693 - <div class="field"> 693 + <div> 694 694 <label for="regen-password">{$_('security.password')}</label> 695 695 <input 696 696 id="regen-password" ··· 701 701 required 702 702 /> 703 703 </div> 704 - <div class="field"> 704 + <div> 705 705 <label for="regen-code">{$_('security.totpCode')}</label> 706 706 <input 707 707 id="regen-code" ··· 729 729 <form class="inline-form danger-form" onsubmit={handleDisableTotp}> 730 730 <h4>{$_('security.disableTotp')}</h4> 731 731 <p class="warning-text">{$_('security.disableTotpWarning')}</p> 732 - <div class="field"> 732 + <div> 733 733 <label for="disable-password">{$_('security.password')}</label> 734 734 <input 735 735 id="disable-password" ··· 740 740 required 741 741 /> 742 742 </div> 743 - <div class="field"> 743 + <div> 744 744 <label for="disable-code">{$_('security.totpCode')}</label> 745 745 <input 746 746 id="disable-code" ··· 833 833 {#if showChangePasswordForm} 834 834 <form class="inline-form" onsubmit={handleChangePassword}> 835 835 <h4>{$_('security.changePassword')}</h4> 836 - <div class="field"> 836 + <div> 837 837 <label for="current-password">{$_('security.currentPassword')}</label> 838 838 <input 839 839 id="current-password" ··· 844 844 required 845 845 /> 846 846 </div> 847 - <div class="field"> 847 + <div> 848 848 <label for="new-password">{$_('security.newPassword')}</label> 849 849 <input 850 850 id="new-password" ··· 856 856 minlength="8" 857 857 /> 858 858 </div> 859 - <div class="field"> 859 + <div> 860 860 <label for="confirm-password">{$_('security.confirmPassword')}</label> 861 861 <input 862 862 id="confirm-password" ··· 902 902 {:else} 903 903 <form class="inline-form" onsubmit={handleSetPassword}> 904 904 <h4>{$_('security.setPassword')}</h4> 905 - <div class="field"> 905 + <div> 906 906 <label for="set-new-password">{$_('security.newPassword')}</label> 907 907 <input 908 908 id="set-new-password" ··· 914 914 minlength="8" 915 915 /> 916 916 </div> 917 - <div class="field"> 917 + <div> 918 918 <label for="set-confirm-password">{$_('security.confirmPassword')}</label> 919 919 <input 920 920 id="set-confirm-password" ··· 958 958 </div> 959 959 <button 960 960 type="button" 961 - class="small danger" 961 + class="sm danger-outline" 962 962 onclick={() => handleUnlinkAccount(account.id)} 963 963 disabled={unlinkingId === account.id} 964 964 > ··· 1064 1064 </div> 1065 1065 {:else} 1066 1066 <span class="device-name">{device.friendlyName || parseUserAgent(device.userAgent)}</span> 1067 - <button type="button" class="icon-btn" onclick={() => startEditDevice(device)} title={$_('security.rename')}> 1067 + <button type="button" class="icon" onclick={() => startEditDevice(device)} title={$_('security.rename')}> 1068 1068 &#9998; 1069 1069 </button> 1070 1070 {/if} ··· 1109 1109 onSuccess={handleReauthSuccess} 1110 1110 onCancel={handleReauthCancel} 1111 1111 /> 1112 - 1113 - <style> 1114 - .security { 1115 - max-width: var(--width-lg); 1116 - } 1117 - 1118 - .loading { 1119 - color: var(--text-secondary); 1120 - padding: var(--space-4); 1121 - } 1122 - 1123 - section { 1124 - background: var(--bg-secondary); 1125 - padding: var(--space-5); 1126 - border-radius: var(--radius-lg); 1127 - margin-bottom: var(--space-5); 1128 - } 1129 - 1130 - section h3 { 1131 - margin: 0 0 var(--space-4) 0; 1132 - font-size: var(--text-base); 1133 - } 1134 - 1135 - .status { 1136 - display: block; 1137 - padding: var(--space-2) var(--space-3); 1138 - border-radius: var(--radius-md); 1139 - font-size: var(--text-sm); 1140 - margin-bottom: var(--space-4); 1141 - width: fit-content; 1142 - } 1143 - 1144 - .status.success { 1145 - background: var(--success-bg); 1146 - color: var(--success-text); 1147 - } 1148 - 1149 - .status.warning { 1150 - background: var(--warning-bg); 1151 - color: var(--warning-text); 1152 - } 1153 - 1154 - .status.info { 1155 - background: var(--accent-muted); 1156 - color: var(--accent); 1157 - } 1158 - 1159 - .passkey-list { 1160 - list-style: none; 1161 - padding: 0; 1162 - margin: 0 0 var(--space-4) 0; 1163 - display: flex; 1164 - flex-direction: column; 1165 - gap: var(--space-2); 1166 - } 1167 - 1168 - .passkey-item { 1169 - display: flex; 1170 - justify-content: space-between; 1171 - align-items: center; 1172 - padding: var(--space-3); 1173 - background: var(--bg-card); 1174 - border: 1px solid var(--border-color); 1175 - border-radius: var(--radius-md); 1176 - gap: var(--space-3); 1177 - } 1178 - 1179 - .passkey-info { 1180 - display: flex; 1181 - flex-direction: column; 1182 - gap: var(--space-1); 1183 - min-width: 0; 1184 - } 1185 - 1186 - .passkey-name { 1187 - font-weight: var(--font-medium); 1188 - } 1189 - 1190 - .passkey-meta { 1191 - font-size: var(--text-xs); 1192 - color: var(--text-secondary); 1193 - } 1194 - 1195 - .passkey-actions { 1196 - display: flex; 1197 - gap: var(--space-2); 1198 - flex-shrink: 0; 1199 - } 1200 - 1201 - .passkey-edit { 1202 - display: flex; 1203 - gap: var(--space-2); 1204 - align-items: center; 1205 - width: 100%; 1206 - } 1207 - 1208 - .passkey-edit input { 1209 - flex: 1; 1210 - } 1211 - 1212 - .add-passkey { 1213 - display: flex; 1214 - gap: var(--space-2); 1215 - margin-top: var(--space-4); 1216 - padding-top: var(--space-4); 1217 - border-top: 1px solid var(--border-color); 1218 - } 1219 - 1220 - .add-passkey input { 1221 - flex: 1; 1222 - } 1223 - 1224 - .password-actions { 1225 - display: flex; 1226 - gap: var(--space-2); 1227 - flex-wrap: wrap; 1228 - } 1229 - 1230 - .remove-password-form { 1231 - background: var(--error-bg); 1232 - border: 1px solid var(--error-border); 1233 - border-radius: var(--radius-lg); 1234 - padding: var(--space-4); 1235 - } 1236 - 1237 - .remove-password-form .warning-text { 1238 - color: var(--error-text); 1239 - font-size: var(--text-sm); 1240 - margin: 0 0 var(--space-4) 0; 1241 - } 1242 - 1243 - .remove-password-form .actions { 1244 - display: flex; 1245 - gap: var(--space-2); 1246 - } 1247 - 1248 - button.small { 1249 - padding: var(--space-2) var(--space-3); 1250 - font-size: var(--text-sm); 1251 - } 1252 - 1253 - button.small.danger { 1254 - background: transparent; 1255 - border: 1px solid var(--error-border); 1256 - color: var(--error-text); 1257 - } 1258 - 1259 - button.small.danger:hover { 1260 - background: var(--error-bg); 1261 - } 1262 - 1263 - .setup-step { 1264 - background: var(--bg-card); 1265 - border: 1px solid var(--border-color); 1266 - border-radius: var(--radius-md); 1267 - padding: var(--space-4); 1268 - } 1269 - 1270 - .setup-step p { 1271 - color: var(--text-secondary); 1272 - font-size: var(--text-sm); 1273 - margin: 0 0 var(--space-4) 0; 1274 - } 1275 - 1276 - .setup-step h4 { 1277 - margin: 0 0 var(--space-2) 0; 1278 - } 1279 - 1280 - .qr-container { 1281 - display: flex; 1282 - justify-content: center; 1283 - margin: var(--space-4) 0; 1284 - } 1285 - 1286 - .qr-code { 1287 - width: 180px; 1288 - height: 180px; 1289 - image-rendering: pixelated; 1290 - } 1291 - 1292 - .manual-entry { 1293 - margin-bottom: var(--space-4); 1294 - font-size: var(--text-sm); 1295 - } 1296 - 1297 - .manual-entry summary { 1298 - cursor: pointer; 1299 - color: var(--accent); 1300 - } 1301 - 1302 - .secret-code { 1303 - display: block; 1304 - margin-top: var(--space-2); 1305 - padding: var(--space-2); 1306 - background: var(--bg-input); 1307 - border-radius: var(--radius-md); 1308 - word-break: break-all; 1309 - font-size: var(--text-xs); 1310 - } 1311 - 1312 - .code-input { 1313 - font-size: var(--text-xl); 1314 - letter-spacing: 0.3em; 1315 - text-align: center; 1316 - max-width: 180px; 1317 - margin: 0 auto var(--space-4) auto; 1318 - display: block; 1319 - } 1320 - 1321 - .actions { 1322 - display: flex; 1323 - gap: var(--space-2); 1324 - margin-top: var(--space-4); 1325 - } 1326 - 1327 - .warning-text { 1328 - color: var(--error-text); 1329 - font-size: var(--text-sm); 1330 - margin-bottom: var(--space-4); 1331 - } 1332 - 1333 - .backup-codes { 1334 - display: grid; 1335 - grid-template-columns: repeat(2, 1fr); 1336 - gap: var(--space-2); 1337 - margin-bottom: var(--space-4); 1338 - } 1339 - 1340 - .backup-code { 1341 - padding: var(--space-2); 1342 - background: var(--bg-input); 1343 - border-radius: var(--radius-md); 1344 - text-align: center; 1345 - font-size: var(--text-sm); 1346 - font-family: var(--font-mono); 1347 - } 1348 - 1349 - .totp-actions { 1350 - display: flex; 1351 - gap: var(--space-2); 1352 - flex-wrap: wrap; 1353 - } 1354 - 1355 - .inline-form { 1356 - background: var(--bg-card); 1357 - border: 1px solid var(--border-color); 1358 - border-radius: var(--radius-md); 1359 - padding: var(--space-4); 1360 - margin-top: var(--space-4); 1361 - } 1362 - 1363 - .inline-form.danger-form { 1364 - border-color: var(--error-border); 1365 - } 1366 - 1367 - .inline-form h4 { 1368 - margin: 0 0 var(--space-3) 0; 1369 - font-size: var(--text-base); 1370 - } 1371 - 1372 - .field { 1373 - margin-bottom: var(--space-3); 1374 - } 1375 - 1376 - .field label { 1377 - display: block; 1378 - font-size: var(--text-sm); 1379 - font-weight: var(--font-medium); 1380 - margin-bottom: var(--space-1); 1381 - } 1382 - 1383 - .field input { 1384 - width: 100%; 1385 - } 1386 - 1387 - .danger-outline { 1388 - background: transparent; 1389 - border: 1px solid var(--error-border); 1390 - color: var(--error-text); 1391 - } 1392 - 1393 - .danger-outline:hover { 1394 - background: var(--error-bg); 1395 - } 1396 - 1397 - button.danger { 1398 - background: var(--error-text); 1399 - border: 1px solid var(--error-text); 1400 - color: white; 1401 - } 1402 - 1403 - button.danger:hover:not(:disabled) { 1404 - background: var(--error-border); 1405 - } 1406 - 1407 - .empty { 1408 - color: var(--text-secondary); 1409 - font-size: var(--text-sm); 1410 - margin-bottom: var(--space-4); 1411 - } 1412 - 1413 - .sso-list { 1414 - list-style: none; 1415 - padding: 0; 1416 - margin: 0 0 var(--space-4) 0; 1417 - display: flex; 1418 - flex-direction: column; 1419 - gap: var(--space-2); 1420 - } 1421 - 1422 - .sso-item { 1423 - display: flex; 1424 - justify-content: space-between; 1425 - align-items: center; 1426 - padding: var(--space-3); 1427 - background: var(--bg-card); 1428 - border: 1px solid var(--border-color); 1429 - border-radius: var(--radius-md); 1430 - gap: var(--space-3); 1431 - } 1432 - 1433 - .sso-info { 1434 - display: flex; 1435 - flex-direction: column; 1436 - gap: var(--space-1); 1437 - } 1438 - 1439 - .sso-provider { 1440 - font-weight: var(--font-medium); 1441 - } 1442 - 1443 - .sso-id { 1444 - font-size: var(--text-xs); 1445 - color: var(--text-secondary); 1446 - } 1447 - 1448 - .sso-meta { 1449 - font-size: var(--text-xs); 1450 - color: var(--text-secondary); 1451 - } 1452 - 1453 - .sso-providers { 1454 - padding-top: var(--space-4); 1455 - border-top: 1px solid var(--border-color); 1456 - } 1457 - 1458 - .sso-providers h4 { 1459 - margin: 0 0 var(--space-3) 0; 1460 - font-size: var(--text-sm); 1461 - color: var(--text-secondary); 1462 - } 1463 - 1464 - .provider-buttons { 1465 - display: flex; 1466 - flex-wrap: wrap; 1467 - gap: var(--space-2); 1468 - } 1469 - 1470 - .provider-btn { 1471 - display: flex; 1472 - align-items: center; 1473 - gap: var(--space-2); 1474 - padding: var(--space-2) var(--space-4); 1475 - background: var(--bg-card); 1476 - border: 1px solid var(--border-color); 1477 - border-radius: var(--radius-md); 1478 - cursor: pointer; 1479 - color: var(--text-primary); 1480 - } 1481 - 1482 - .provider-btn:hover:not(:disabled) { 1483 - border-color: var(--accent); 1484 - } 1485 - 1486 - .provider-btn:disabled { 1487 - opacity: 0.6; 1488 - cursor: not-allowed; 1489 - } 1490 - 1491 - .linked-badge { 1492 - font-size: var(--text-xs); 1493 - padding: var(--space-1) var(--space-2); 1494 - background: var(--success-bg); 1495 - color: var(--success-text); 1496 - border-radius: var(--radius-sm); 1497 - } 1498 - 1499 - .section-description { 1500 - color: var(--text-secondary); 1501 - font-size: var(--text-sm); 1502 - margin: 0 0 var(--space-4) 0; 1503 - } 1504 - 1505 - .toggle-row { 1506 - display: flex; 1507 - justify-content: space-between; 1508 - align-items: center; 1509 - padding: var(--space-3); 1510 - background: var(--bg-card); 1511 - border: 1px solid var(--border-color); 1512 - border-radius: var(--radius-md); 1513 - margin-bottom: var(--space-4); 1514 - } 1515 - 1516 - .toggle-info { 1517 - display: flex; 1518 - flex-direction: column; 1519 - gap: var(--space-1); 1520 - } 1521 - 1522 - .toggle-label { 1523 - font-weight: var(--font-medium); 1524 - } 1525 - 1526 - .toggle-description { 1527 - font-size: var(--text-sm); 1528 - color: var(--text-secondary); 1529 - } 1530 - 1531 - .toggle-button { 1532 - position: relative; 1533 - width: 52px; 1534 - height: 28px; 1535 - padding: 0; 1536 - background: var(--border-color); 1537 - border: none; 1538 - border-radius: 14px; 1539 - cursor: pointer; 1540 - transition: background 0.2s ease; 1541 - flex-shrink: 0; 1542 - } 1543 - 1544 - .toggle-button.on { 1545 - background: var(--accent); 1546 - } 1547 - 1548 - .toggle-button:disabled { 1549 - opacity: 0.6; 1550 - cursor: not-allowed; 1551 - } 1552 - 1553 - .toggle-slider { 1554 - position: absolute; 1555 - top: 2px; 1556 - left: 2px; 1557 - width: 24px; 1558 - height: 24px; 1559 - background: white; 1560 - border-radius: 50%; 1561 - transition: transform 0.2s ease; 1562 - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); 1563 - } 1564 - 1565 - .toggle-button.on .toggle-slider { 1566 - transform: translateX(24px); 1567 - } 1568 - 1569 - .warning-box { 1570 - background: var(--warning-bg); 1571 - border: 1px solid var(--warning-border); 1572 - border-radius: var(--radius-md); 1573 - padding: var(--space-3); 1574 - margin-bottom: var(--space-4); 1575 - } 1576 - 1577 - .warning-box strong { 1578 - color: var(--warning-text); 1579 - font-size: var(--text-sm); 1580 - } 1581 - 1582 - .info-box { 1583 - background: var(--bg-card); 1584 - border: 1px solid var(--border-color); 1585 - border-radius: var(--radius-md); 1586 - padding: var(--space-3); 1587 - } 1588 - 1589 - .info-box strong { 1590 - font-size: var(--text-sm); 1591 - display: block; 1592 - margin-bottom: var(--space-1); 1593 - } 1594 - 1595 - .info-box p { 1596 - font-size: var(--text-sm); 1597 - color: var(--text-secondary); 1598 - margin: 0; 1599 - } 1600 - 1601 - .empty-hint { 1602 - color: var(--text-secondary); 1603 - font-size: var(--text-sm); 1604 - margin: 0; 1605 - } 1606 - 1607 - .hint-text { 1608 - color: var(--text-secondary); 1609 - font-size: var(--text-sm); 1610 - margin: var(--space-2) 0 0 0; 1611 - } 1612 - 1613 - .device-list { 1614 - display: flex; 1615 - flex-direction: column; 1616 - gap: var(--space-3); 1617 - } 1618 - 1619 - .device-card { 1620 - display: flex; 1621 - align-items: center; 1622 - gap: var(--space-3); 1623 - padding: var(--space-3); 1624 - background: var(--bg-card); 1625 - border: 1px solid var(--border-color); 1626 - border-radius: var(--radius-md); 1627 - } 1628 - 1629 - .device-header { 1630 - display: flex; 1631 - align-items: center; 1632 - gap: var(--space-2); 1633 - flex: 1; 1634 - min-width: 0; 1635 - } 1636 - 1637 - .device-name { 1638 - font-weight: var(--font-medium); 1639 - white-space: nowrap; 1640 - overflow: hidden; 1641 - text-overflow: ellipsis; 1642 - } 1643 - 1644 - .edit-name-input { 1645 - flex: 1; 1646 - padding: var(--space-2); 1647 - font-size: var(--text-sm); 1648 - min-width: 0; 1649 - } 1650 - 1651 - .edit-actions { 1652 - display: flex; 1653 - gap: var(--space-2); 1654 - flex-shrink: 0; 1655 - } 1656 - 1657 - .icon-btn { 1658 - background: none; 1659 - border: none; 1660 - padding: var(--space-1); 1661 - cursor: pointer; 1662 - color: var(--text-secondary); 1663 - font-size: var(--text-sm); 1664 - } 1665 - 1666 - .icon-btn:hover { 1667 - color: var(--accent); 1668 - } 1669 - 1670 - .device-details { 1671 - display: flex; 1672 - gap: var(--space-3); 1673 - flex-shrink: 0; 1674 - } 1675 - 1676 - .device-details .detail { 1677 - font-size: var(--text-xs); 1678 - color: var(--text-secondary); 1679 - white-space: nowrap; 1680 - } 1681 - 1682 - .device-details .expiring-soon { 1683 - color: var(--warning-text); 1684 - } 1685 - 1686 - button.danger-outline { 1687 - background: transparent; 1688 - border: 1px solid var(--error-border); 1689 - color: var(--error-text); 1690 - } 1691 - 1692 - button.danger-outline:hover { 1693 - background: var(--error-bg); 1694 - } 1695 - 1696 - @media (max-width: 500px) { 1697 - .passkey-item { 1698 - flex-direction: column; 1699 - align-items: stretch; 1700 - } 1701 - 1702 - .passkey-actions { 1703 - width: 100%; 1704 - } 1705 - 1706 - .passkey-actions button { 1707 - flex: 1; 1708 - } 1709 - 1710 - .add-passkey { 1711 - flex-direction: column; 1712 - } 1713 - 1714 - .device-card { 1715 - flex-direction: column; 1716 - align-items: stretch; 1717 - } 1718 - 1719 - .device-details { 1720 - flex-direction: column; 1721 - gap: var(--space-1); 1722 - } 1723 - 1724 - .device-card > button { 1725 - width: 100%; 1726 - } 1727 - } 1728 - </style>
+3 -169
frontend/src/components/dashboard/SessionsContent.svelte
··· 127 127 </div> 128 128 <button 129 129 type="button" 130 - class="revoke-btn" 130 + class="sm secondary" 131 131 class:danger={!s.isCurrent} 132 132 onclick={() => revokeSession(s.id, s.isCurrent)} 133 133 > ··· 137 137 {/each} 138 138 </div> 139 139 <div class="actions-bar"> 140 - <button type="button" class="refresh-btn" onclick={loadSessions}>{$_('common.refresh')}</button> 140 + <button type="button" class="sm tertiary" onclick={loadSessions}>{$_('common.refresh')}</button> 141 141 {#if sessions.filter(s => !s.isCurrent).length > 0} 142 - <button type="button" class="revoke-all-btn" onclick={revokeAllSessions}>{$_('sessions.revokeAll')}</button> 142 + <button type="button" class="sm danger-outline" onclick={revokeAllSessions}>{$_('sessions.revokeAll')}</button> 143 143 {/if} 144 144 </div> 145 145 {/if} 146 146 </div> 147 - 148 - <style> 149 - .sessions { 150 - max-width: var(--width-lg); 151 - } 152 - 153 - .loading, 154 - .empty { 155 - color: var(--text-secondary); 156 - padding: var(--space-6); 157 - text-align: center; 158 - } 159 - 160 - .sessions-list { 161 - display: flex; 162 - flex-direction: column; 163 - gap: var(--space-4); 164 - } 165 - 166 - .session-card { 167 - background: var(--bg-secondary); 168 - border: 1px solid var(--border-color); 169 - border-radius: var(--radius-xl); 170 - padding: var(--space-4); 171 - display: flex; 172 - justify-content: space-between; 173 - align-items: center; 174 - gap: var(--space-4); 175 - } 176 - 177 - .session-card.current { 178 - border-color: var(--accent); 179 - background: var(--bg-card); 180 - } 181 - 182 - .session-info { 183 - flex: 1; 184 - min-width: 0; 185 - } 186 - 187 - .session-header { 188 - margin-bottom: var(--space-2); 189 - display: flex; 190 - align-items: center; 191 - gap: var(--space-2); 192 - flex-wrap: wrap; 193 - } 194 - 195 - .client-name { 196 - font-weight: var(--font-medium); 197 - color: var(--text-primary); 198 - } 199 - 200 - .badge { 201 - display: inline-block; 202 - padding: var(--space-1) var(--space-2); 203 - border-radius: var(--radius-md); 204 - font-size: var(--text-xs); 205 - font-weight: var(--font-medium); 206 - } 207 - 208 - .badge.current { 209 - background: var(--accent); 210 - color: var(--text-inverse); 211 - } 212 - 213 - .badge.type { 214 - background: var(--bg-secondary); 215 - color: var(--text-secondary); 216 - border: 1px solid var(--border-color); 217 - } 218 - 219 - .badge.type.oauth { 220 - background: var(--success-bg); 221 - color: var(--success-text); 222 - border-color: var(--success-border); 223 - } 224 - 225 - .session-details { 226 - display: flex; 227 - flex-direction: column; 228 - gap: var(--space-1); 229 - } 230 - 231 - .detail { 232 - font-size: var(--text-sm); 233 - } 234 - 235 - .detail .label { 236 - color: var(--text-secondary); 237 - margin-right: var(--space-2); 238 - } 239 - 240 - .detail .value { 241 - color: var(--text-primary); 242 - } 243 - 244 - .revoke-btn { 245 - flex-shrink: 0; 246 - padding: var(--space-2) var(--space-4); 247 - border: 1px solid var(--border-color); 248 - border-radius: var(--radius-md); 249 - background: transparent; 250 - color: var(--text-primary); 251 - cursor: pointer; 252 - font-size: var(--text-sm); 253 - } 254 - 255 - .revoke-btn:hover { 256 - background: var(--bg-card); 257 - } 258 - 259 - .revoke-btn.danger { 260 - border-color: var(--error-text); 261 - color: var(--error-text); 262 - } 263 - 264 - .revoke-btn.danger:hover { 265 - background: var(--error-bg); 266 - } 267 - 268 - .actions-bar { 269 - margin-top: var(--space-4); 270 - display: flex; 271 - gap: var(--space-2); 272 - flex-wrap: wrap; 273 - } 274 - 275 - .refresh-btn { 276 - padding: var(--space-2) var(--space-4); 277 - background: transparent; 278 - border: 1px solid var(--border-color); 279 - border-radius: var(--radius-md); 280 - cursor: pointer; 281 - color: var(--text-primary); 282 - } 283 - 284 - .refresh-btn:hover { 285 - background: var(--bg-card); 286 - border-color: var(--accent); 287 - } 288 - 289 - .revoke-all-btn { 290 - padding: var(--space-2) var(--space-4); 291 - background: transparent; 292 - border: 1px solid var(--error-text); 293 - border-radius: var(--radius-md); 294 - cursor: pointer; 295 - color: var(--error-text); 296 - } 297 - 298 - .revoke-all-btn:hover { 299 - background: var(--error-bg); 300 - } 301 - 302 - @media (max-width: 500px) { 303 - .session-card { 304 - flex-direction: column; 305 - align-items: stretch; 306 - } 307 - 308 - .revoke-btn { 309 - width: 100%; 310 - } 311 - } 312 - </style>

History

1 round 0 comments
sign up or login to add to the discussion
oyster.cafe submitted #0
1 commit
expand
refactor(frontend): extract security, sessions, DID document styles
expand 0 comments
pull request successfully merged