+20
-971
Diff
round #0
+2
-170
frontend/src/components/dashboard/DidDocumentContent.svelte
+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
+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
✎
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
+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
oyster.cafe
submitted
#0
1 commit
expand
collapse
refactor(frontend): extract security, sessions, DID document styles
expand 0 comments
pull request successfully merged