+11
-5
packages/danaus/src/web/account/forms.ts
+11
-5
packages/danaus/src/web/account/forms.ts
···
1
-
import { signOperation, type UnsignedOperation } from '@atcute/did-plc';
2
import type { Did, Handle } from '@atcute/lexicons';
3
import { isHandle } from '@atcute/lexicons/syntax';
4
import { XRPCError } from '@atcute/xrpc-server';
···
193
194
// update PLC document for did:plc accounts
195
if (did.startsWith('did:plc:')) {
196
-
await updatePlcHandle(ctx, did as Did<'plc'>, handle);
197
}
198
199
// update local database and emit identity event
···
271
services: state.services,
272
};
273
274
-
// sign with PDS rotation key
275
const signedOp = await signOperation(unsignedOp, config.secrets.plcRotationKey);
276
-
277
-
// submit to PLC directory
278
await plcClient.submitOperation(did, signedOp);
279
}
···
1
+
import { PlcClientError, signOperation, type UnsignedOperation } from '@atcute/did-plc';
2
import type { Did, Handle } from '@atcute/lexicons';
3
import { isHandle } from '@atcute/lexicons/syntax';
4
import { XRPCError } from '@atcute/xrpc-server';
···
193
194
// update PLC document for did:plc accounts
195
if (did.startsWith('did:plc:')) {
196
+
try {
197
+
await updatePlcHandle(ctx, did as Did<'plc'>, handle);
198
+
} catch (err) {
199
+
if (err instanceof PlcClientError) {
200
+
invalid(`Unable to update DID document, please try again later`);
201
+
}
202
+
203
+
throw err;
204
+
}
205
}
206
207
// update local database and emit identity event
···
279
services: state.services,
280
};
281
282
+
// sign with PDS rotation key and submit to PLC directory
283
const signedOp = await signOperation(unsignedOp, config.secrets.plcRotationKey);
284
await plcClient.submitOperation(did, signedOp);
285
}
+219
-98
packages/danaus/src/web/account/index.tsx
+219
-98
packages/danaus/src/web/account/index.tsx
···
12
import AsideItem from '../admin/components/aside-item.tsx';
13
import { IdProvider } from '../components/id.tsx';
14
import { registerForms } from '../forms/index.ts';
15
import DotGrid1x3HorizontalOutlined from '../icons/central/dot-grid-1x3-horizontal-outlined.tsx';
16
import Key2Outlined from '../icons/central/key-2-outlined.tsx';
17
import PasskeysOutlined from '../icons/central/passkeys-outlined.tsx';
···
21
import PlusLargeOutlined from '../icons/central/plus-large-outlined.tsx';
22
import ShieldOutlined from '../icons/central/shield-outlined.tsx';
23
import UsbOutlined from '../icons/central/usb-outlined.tsx';
24
import Button from '../primitives/button.tsx';
25
import DialogActions from '../primitives/dialog-actions.tsx';
26
import DialogBody from '../primitives/dialog-body.tsx';
···
151
: 'custom';
152
const currentLocalPart = isServiceHandle ? currentHandle.slice(0, -currentDomain.length) : currentHandle;
153
154
return c.render(
155
<AccountLayout>
156
<title>My account - Danaus</title>
···
160
<h3 class="text-base-400 font-medium">Account overview</h3>
161
</div>
162
163
<div class="flex flex-col gap-8">
164
<div class="flex flex-col gap-2">
165
<h4 class="text-base-300 font-medium text-neutral-foreground-2">Your identity</h4>
···
180
181
<MenuPopover>
182
<MenuList>
183
-
<Dialog>
184
-
<DialogTrigger>
185
-
<MenuItem>Change handle</MenuItem>
186
-
</DialogTrigger>
187
-
188
-
<DialogSurface>
189
-
<DialogBody>
190
-
<DialogTitle>Change handle</DialogTitle>
191
192
-
<form {...updateHandleForm} class="contents">
193
-
<DialogContent class="flex flex-col gap-4">
194
-
<p class="text-base-300 text-neutral-foreground-3">
195
-
Your handle is your unique identity on the AT Protocol network.
196
-
</p>
197
-
198
-
<Field
199
-
label="Domain"
200
-
validationMessageText={
201
-
updateHandleForm.fields.domain.issues()[0]?.message
202
-
}
203
-
>
204
-
<Select
205
-
{...updateHandleForm.fields.domain.as('select')}
206
-
value={updateHandleForm.fields.domain.value() || currentDomain}
207
-
options={[
208
-
...ctx.config.identity.serviceHandleDomains.map((d) => ({
209
-
value: d,
210
-
label: d,
211
-
})),
212
-
{ value: 'custom', label: 'I have my own domain' },
213
-
]}
214
-
/>
215
-
</Field>
216
-
217
-
<Field
218
-
label="Handle"
219
-
required
220
-
validationMessageText={
221
-
updateHandleForm.fields.handle.issues()[0]?.message
222
-
}
223
-
>
224
-
<Input
225
-
{...updateHandleForm.fields.handle.as('text')}
226
-
value={updateHandleForm.fields.handle.value() || currentLocalPart}
227
-
placeholder="alice"
228
-
required
229
-
/>
230
-
</Field>
231
-
232
-
<p class="text-base-200 text-neutral-foreground-3">
233
-
Custom domains must have a DNS TXT record or .well-known file pointing to
234
-
your DID.
235
-
</p>
236
-
</DialogContent>
237
-
238
-
<DialogActions>
239
-
<DialogClose>
240
-
<Button>Cancel</Button>
241
-
</DialogClose>
242
-
243
-
<Button type="submit" variant="primary">
244
-
Save
245
-
</Button>
246
-
</DialogActions>
247
-
</form>
248
-
</DialogBody>
249
-
</DialogSurface>
250
-
</Dialog>
251
-
252
-
<Dialog>
253
-
<DialogTrigger>
254
-
<MenuItem>Request refresh</MenuItem>
255
-
</DialogTrigger>
256
-
257
-
<DialogSurface>
258
-
<DialogBody>
259
-
<DialogTitle>Request handle refresh</DialogTitle>
260
-
261
-
<form {...refreshHandleForm} class="contents">
262
-
<DialogContent>
263
-
<p class="text-base-300">
264
-
This will notify the network to re-verify your handle. Use this if apps
265
-
are marking your handle as invalid despite being set up correctly.
266
-
</p>
267
-
</DialogContent>
268
-
269
-
<DialogActions>
270
-
<DialogClose>
271
-
<Button>Cancel</Button>
272
-
</DialogClose>
273
-
274
-
<Button type="submit" variant="primary">
275
-
Refresh
276
-
</Button>
277
-
</DialogActions>
278
-
</form>
279
-
</DialogBody>
280
-
</DialogSurface>
281
-
</Dialog>
282
</MenuList>
283
</MenuPopover>
284
</Menu>
···
320
</div>
321
</div>
322
</div>
323
</AccountLayout>,
324
);
325
});
···
12
import AsideItem from '../admin/components/aside-item.tsx';
13
import { IdProvider } from '../components/id.tsx';
14
import { registerForms } from '../forms/index.ts';
15
+
import AtOutlined from '../icons/central/at-outlined.tsx';
16
import DotGrid1x3HorizontalOutlined from '../icons/central/dot-grid-1x3-horizontal-outlined.tsx';
17
import Key2Outlined from '../icons/central/key-2-outlined.tsx';
18
import PasskeysOutlined from '../icons/central/passkeys-outlined.tsx';
···
22
import PlusLargeOutlined from '../icons/central/plus-large-outlined.tsx';
23
import ShieldOutlined from '../icons/central/shield-outlined.tsx';
24
import UsbOutlined from '../icons/central/usb-outlined.tsx';
25
+
import AccordionHeader from '../primitives/accordion-header.tsx';
26
+
import AccordionItem from '../primitives/accordion-item.tsx';
27
+
import AccordionPanel from '../primitives/accordion-panel.tsx';
28
+
import Accordion from '../primitives/accordion.tsx';
29
import Button from '../primitives/button.tsx';
30
import DialogActions from '../primitives/dialog-actions.tsx';
31
import DialogBody from '../primitives/dialog-body.tsx';
···
156
: 'custom';
157
const currentLocalPart = isServiceHandle ? currentHandle.slice(0, -currentDomain.length) : currentHandle;
158
159
+
const updateHandleError = updateHandleForm.fields.allIssues().at(0);
160
+
const refreshHandleError = refreshHandleForm.fields.allIssues().at(0);
161
+
162
return c.render(
163
<AccountLayout>
164
<title>My account - Danaus</title>
···
168
<h3 class="text-base-400 font-medium">Account overview</h3>
169
</div>
170
171
+
{updateHandleError && (
172
+
<MessageBar intent="error" layout="singleline">
173
+
<MessageBarBody>{updateHandleError.message}</MessageBarBody>
174
+
</MessageBar>
175
+
)}
176
+
177
+
{refreshHandleError && (
178
+
<MessageBar intent="error" layout="singleline">
179
+
<MessageBarBody>{refreshHandleError.message}</MessageBarBody>
180
+
</MessageBar>
181
+
)}
182
+
183
<div class="flex flex-col gap-8">
184
<div class="flex flex-col gap-2">
185
<h4 class="text-base-300 font-medium text-neutral-foreground-2">Your identity</h4>
···
200
201
<MenuPopover>
202
<MenuList>
203
+
<MenuItem command="show-modal" commandfor="change-service-handle-dialog">
204
+
Change handle
205
+
</MenuItem>
206
207
+
<MenuItem command="show-modal" commandfor="refresh-handle-dialog">
208
+
Request refresh
209
+
</MenuItem>
210
</MenuList>
211
</MenuPopover>
212
</Menu>
···
248
</div>
249
</div>
250
</div>
251
+
252
+
<Dialog id="change-service-handle-dialog">
253
+
<DialogSurface>
254
+
<DialogBody>
255
+
<DialogTitle>Change handle</DialogTitle>
256
+
257
+
<form {...updateHandleForm} class="contents">
258
+
<DialogContent class="flex flex-col gap-4">
259
+
<p class="text-base-300 text-neutral-foreground-3">
260
+
Your handle is your unique identity on the AT Protocol network.
261
+
</p>
262
+
263
+
<Field label="Handle" required>
264
+
<div class="flex gap-2">
265
+
<Input
266
+
{...updateHandleForm.fields.handle.as('text')}
267
+
value={updateHandleForm.fields.handle.value() || currentLocalPart}
268
+
placeholder="alice"
269
+
contentBefore={<AtOutlined size={16} />}
270
+
class="grow"
271
+
/>
272
+
273
+
<Select
274
+
{...updateHandleForm.fields.domain.as('select')}
275
+
value={updateHandleForm.fields.domain.value() || currentDomain}
276
+
options={ctx.config.identity.serviceHandleDomains.map((d) => ({
277
+
value: d,
278
+
label: d,
279
+
}))}
280
+
/>
281
+
</div>
282
+
</Field>
283
+
284
+
<div></div>
285
+
</DialogContent>
286
+
287
+
<DialogActions>
288
+
<Button command="show-modal" commandfor="change-custom-handle-dialog">
289
+
Use my own domain
290
+
</Button>
291
+
292
+
<div class="grow"></div>
293
+
294
+
<DialogClose>
295
+
<Button>Cancel</Button>
296
+
</DialogClose>
297
+
298
+
<Button type="submit" variant="primary">
299
+
Change
300
+
</Button>
301
+
</DialogActions>
302
+
</form>
303
+
</DialogBody>
304
+
</DialogSurface>
305
+
</Dialog>
306
+
307
+
<Dialog id="refresh-handle-dialog">
308
+
<DialogSurface>
309
+
<DialogBody>
310
+
<DialogTitle>Request handle refresh</DialogTitle>
311
+
312
+
<form {...refreshHandleForm} class="contents">
313
+
<DialogContent>
314
+
<p class="text-base-300">
315
+
This will notify the network to re-verify your handle. Use this if apps are marking your
316
+
handle as invalid despite being set up correctly.
317
+
</p>
318
+
</DialogContent>
319
+
320
+
<DialogActions>
321
+
<DialogClose>
322
+
<Button>Cancel</Button>
323
+
</DialogClose>
324
+
325
+
<Button type="submit" variant="primary">
326
+
Refresh
327
+
</Button>
328
+
</DialogActions>
329
+
</form>
330
+
</DialogBody>
331
+
</DialogSurface>
332
+
</Dialog>
333
+
334
+
<Dialog id="change-custom-handle-dialog">
335
+
<DialogSurface>
336
+
<DialogBody>
337
+
<DialogTitle>Change handle</DialogTitle>
338
+
339
+
<form {...updateHandleForm} class="contents">
340
+
<DialogContent class="flex flex-col gap-4">
341
+
<p class="text-base-300 text-neutral-foreground-3">
342
+
Your handle is your unique identity on the AT Protocol network.
343
+
</p>
344
+
345
+
<Field label="Handle" required>
346
+
<Input
347
+
{...updateHandleForm.fields.handle.as('text')}
348
+
placeholder="alice.com"
349
+
contentBefore={<AtOutlined size={16} />}
350
+
/>
351
+
</Field>
352
+
353
+
<input {...updateHandleForm.fields.domain.as('hidden', 'custom')} />
354
+
355
+
<Accordion class="flex flex-col gap-2">
356
+
<AccordionItem name="handle-method" open>
357
+
<AccordionHeader>DNS record</AccordionHeader>
358
+
<AccordionPanel>
359
+
<div class="flex flex-col gap-3">
360
+
<p class="text-base-300 text-neutral-foreground-3">
361
+
Add the following DNS record to your domain:
362
+
</p>
363
+
364
+
<div class="flex flex-col gap-2 rounded-md bg-neutral-background-3 p-3">
365
+
<div class="flex flex-col gap-0.5">
366
+
<span class="text-base-200 text-neutral-foreground-3">Host</span>
367
+
<input
368
+
type="text"
369
+
readonly
370
+
value="_atproto.<your-domain>"
371
+
class="font-mono text-base-300 outline-none"
372
+
/>
373
+
</div>
374
+
<div class="flex flex-col gap-0.5">
375
+
<span class="text-base-200 text-neutral-foreground-3">Type</span>
376
+
<input
377
+
type="text"
378
+
readonly
379
+
value="TXT"
380
+
class="font-mono text-base-300 outline-none"
381
+
/>
382
+
</div>
383
+
<div class="flex flex-col gap-0.5">
384
+
<span class="text-base-200 text-neutral-foreground-3">Value</span>
385
+
<input
386
+
type="text"
387
+
readonly
388
+
value={`did=${session.did}`}
389
+
class="font-mono text-base-300 outline-none"
390
+
/>
391
+
</div>
392
+
</div>
393
+
</div>
394
+
</AccordionPanel>
395
+
</AccordionItem>
396
+
397
+
<AccordionItem name="handle-method">
398
+
<AccordionHeader>HTTP well-known entry</AccordionHeader>
399
+
<AccordionPanel>
400
+
<div class="flex flex-col gap-3">
401
+
<p class="text-base-300 text-neutral-foreground-3">
402
+
Upload a text file to the following URL:
403
+
</p>
404
+
405
+
<div class="flex flex-col gap-2 rounded-md bg-neutral-background-3 p-3">
406
+
<div class="flex flex-col gap-0.5">
407
+
<span class="text-base-200 text-neutral-foreground-3">URL</span>
408
+
<input
409
+
type="text"
410
+
readonly
411
+
value="https://<your-domain>/.well-known/atproto-did"
412
+
class="font-mono text-base-300 outline-none"
413
+
/>
414
+
</div>
415
+
<div class="flex flex-col gap-0.5">
416
+
<span class="text-base-200 text-neutral-foreground-3">Contents</span>
417
+
<input
418
+
type="text"
419
+
readonly
420
+
value={session.did}
421
+
class="font-mono text-base-300 outline-none"
422
+
/>
423
+
</div>
424
+
</div>
425
+
</div>
426
+
</AccordionPanel>
427
+
</AccordionItem>
428
+
</Accordion>
429
+
</DialogContent>
430
+
431
+
<DialogActions>
432
+
<DialogClose>
433
+
<Button>Cancel</Button>
434
+
</DialogClose>
435
+
436
+
<Button type="submit" variant="primary">
437
+
Change
438
+
</Button>
439
+
</DialogActions>
440
+
</form>
441
+
</DialogBody>
442
+
</DialogSurface>
443
+
</Dialog>
444
</AccountLayout>,
445
);
446
});
+18
packages/danaus/src/web/icons/central/at-outlined.tsx
+18
packages/danaus/src/web/icons/central/at-outlined.tsx
···
···
1
+
import type { IconProps } from './_types.ts';
2
+
3
+
const ArrowInboxOutlined = (props: IconProps) => {
4
+
const { size = 24, class: className } = props;
5
+
6
+
return (
7
+
<svg viewBox="0 0 24 24" width={size} height={size} fill="none" class={className}>
8
+
<path
9
+
d="M16.7368 19.6541C15.361 20.5073 13.738 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 13.9262 20.0428 15.9154 17.8101 15.7125C15.9733 15.5455 14.6512 13.8737 14.9121 12.0479L15.4274 8.5M14.8581 12.4675C14.559 14.596 12.8066 16.1093 10.9442 15.8476C9.08175 15.5858 7.81444 13.6481 8.11358 11.5196C8.41272 9.39109 10.165 7.87778 12.0275 8.13953C13.8899 8.40128 15.1573 10.339 14.8581 12.4675Z"
10
+
stroke="currentColor"
11
+
stroke-width="2"
12
+
stroke-linecap="round"
13
+
/>
14
+
</svg>
15
+
);
16
+
};
17
+
18
+
export default ArrowInboxOutlined;
+47
-17
packages/danaus/src/web/styles/main.out.css
+47
-17
packages/danaus/src/web/styles/main.out.css
···
322
.-mx-1\.25 {
323
margin-inline: calc(var(--spacing) * -1.25);
324
}
325
.-my-0\.5 {
326
margin-block: calc(var(--spacing) * -0.5);
327
}
···
416
.min-h-9 {
417
min-height: calc(var(--spacing) * 9);
418
}
419
.min-h-dvh {
420
min-height: 100dvh;
421
}
···
479
.flex-1 {
480
flex: 1;
481
}
482
.shrink-0 {
483
flex-shrink: 0;
484
}
485
.grow {
486
flex-grow: 1;
487
}
488
.cursor-pointer {
489
cursor: pointer;
490
}
491
.appearance-none {
492
appearance: none;
···
503
.flex-col {
504
flex-direction: column;
505
}
506
.flex-nowrap {
507
flex-wrap: nowrap;
508
}
···
610
.border {
611
border-style: var(--tw-border-style);
612
border-width: 1px;
613
}
614
.border-b {
615
border-bottom-style: var(--tw-border-style);
···
754
.pb-2 {
755
padding-bottom: calc(var(--spacing) * 2);
756
}
757
.pl-1 {
758
padding-left: calc(var(--spacing) * 1);
759
}
···
807
.font-semibold {
808
--tw-font-weight: var(--font-weight-semibold);
809
font-weight: var(--font-weight-semibold);
810
}
811
.wrap-break-word {
812
overflow-wrap: break-word;
···
880
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
881
transition-duration: var(--tw-duration, var(--default-transition-duration));
882
}
883
.duration-100 {
884
--tw-duration: 100ms;
885
transition-duration: 100ms;
···
891
.outline-none {
892
--tw-outline-style: none;
893
outline-style: none;
894
}
895
.select-none {
896
-webkit-user-select: none;
···
913
}
914
.try-flip-y {
915
position-try-fallbacks: flip-block;
916
}
917
.group-hover\/checkbox\:border-compound-brand-background-hover {
918
&:is(:where(.group\/checkbox):hover *) {
···
1107
display: flex;
1108
}
1109
}
1110
-
.open\:items-end {
1111
-
&:is([open], :popover-open, :open) {
1112
-
align-items: flex-end;
1113
-
}
1114
-
}
1115
-
.open\:justify-center {
1116
-
&:is([open], :popover-open, :open) {
1117
-
justify-content: center;
1118
-
}
1119
-
}
1120
.hover\:border-neutral-stroke-1-hover {
1121
&:hover {
1122
@media (hover: hover) {
···
1375
padding-top: calc(var(--spacing) * 24);
1376
}
1377
}
1378
-
.open\:sm\:items-center {
1379
-
&:is([open], :popover-open, :open) {
1380
-
@media (width >= 40rem) {
1381
-
align-items: center;
1382
-
}
1383
-
}
1384
-
}
1385
.lg\:grid {
1386
@media (width >= 64rem) {
1387
display: grid;
···
1420
.\@sm\/dialog-body\:justify-start {
1421
@container dialog-body (width >= 24rem) {
1422
justify-content: flex-start;
1423
}
1424
}
1425
}
···
322
.-mx-1\.25 {
323
margin-inline: calc(var(--spacing) * -1.25);
324
}
325
+
.-mx-3 {
326
+
margin-inline: calc(var(--spacing) * -3);
327
+
}
328
.-my-0\.5 {
329
margin-block: calc(var(--spacing) * -0.5);
330
}
···
419
.min-h-9 {
420
min-height: calc(var(--spacing) * 9);
421
}
422
+
.min-h-11 {
423
+
min-height: calc(var(--spacing) * 11);
424
+
}
425
.min-h-dvh {
426
min-height: 100dvh;
427
}
···
485
.flex-1 {
486
flex: 1;
487
}
488
+
.shrink {
489
+
flex-shrink: 1;
490
+
}
491
.shrink-0 {
492
flex-shrink: 0;
493
}
494
.grow {
495
flex-grow: 1;
496
+
}
497
+
.basis-0 {
498
+
flex-basis: calc(var(--spacing) * 0);
499
}
500
.cursor-pointer {
501
cursor: pointer;
502
+
}
503
+
.list-none {
504
+
list-style-type: none;
505
}
506
.appearance-none {
507
appearance: none;
···
518
.flex-col {
519
flex-direction: column;
520
}
521
+
.flex-row-reverse {
522
+
flex-direction: row-reverse;
523
+
}
524
.flex-nowrap {
525
flex-wrap: nowrap;
526
}
···
628
.border {
629
border-style: var(--tw-border-style);
630
border-width: 1px;
631
+
}
632
+
.border-0 {
633
+
border-style: var(--tw-border-style);
634
+
border-width: 0px;
635
}
636
.border-b {
637
border-bottom-style: var(--tw-border-style);
···
776
.pb-2 {
777
padding-bottom: calc(var(--spacing) * 2);
778
}
779
+
.pb-3 {
780
+
padding-bottom: calc(var(--spacing) * 3);
781
+
}
782
.pl-1 {
783
padding-left: calc(var(--spacing) * 1);
784
}
···
832
.font-semibold {
833
--tw-font-weight: var(--font-weight-semibold);
834
font-weight: var(--font-weight-semibold);
835
+
}
836
+
.wrap-anywhere {
837
+
overflow-wrap: anywhere;
838
}
839
.wrap-break-word {
840
overflow-wrap: break-word;
···
908
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
909
transition-duration: var(--tw-duration, var(--default-transition-duration));
910
}
911
+
.transition-transform {
912
+
transition-property: transform, translate, scale, rotate;
913
+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
914
+
transition-duration: var(--tw-duration, var(--default-transition-duration));
915
+
}
916
.duration-100 {
917
--tw-duration: 100ms;
918
transition-duration: 100ms;
···
924
.outline-none {
925
--tw-outline-style: none;
926
outline-style: none;
927
+
}
928
+
.select-all {
929
+
-webkit-user-select: all;
930
+
user-select: all;
931
}
932
.select-none {
933
-webkit-user-select: none;
···
950
}
951
.try-flip-y {
952
position-try-fallbacks: flip-block;
953
+
}
954
+
.group-open\/accordion-item\:rotate-180 {
955
+
&:is(:where(.group\/accordion-item):is([open], :popover-open, :open) *) {
956
+
rotate: 180deg;
957
+
}
958
}
959
.group-hover\/checkbox\:border-compound-brand-background-hover {
960
&:is(:where(.group\/checkbox):hover *) {
···
1149
display: flex;
1150
}
1151
}
1152
.hover\:border-neutral-stroke-1-hover {
1153
&:hover {
1154
@media (hover: hover) {
···
1407
padding-top: calc(var(--spacing) * 24);
1408
}
1409
}
1410
.lg\:grid {
1411
@media (width >= 64rem) {
1412
display: grid;
···
1445
.\@sm\/dialog-body\:justify-start {
1446
@container dialog-body (width >= 24rem) {
1447
justify-content: flex-start;
1448
+
}
1449
+
}
1450
+
.\[\&\:\:-webkit-details-marker\]\:hidden {
1451
+
&::-webkit-details-marker {
1452
+
display: none;
1453
}
1454
}
1455
}