window.nostr.js 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "window.nostr.js",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "main": "dist/window.nostr.js",
6
6
  "devDependencies": {
package/src/App.svelte CHANGED
@@ -25,14 +25,23 @@
25
25
  import Spinner from './Spinner.svelte'
26
26
 
27
27
  const mobileMode = mediaQueryStore('only screen and (max-width: 640px)')
28
+ const localStorageKeys = {
29
+ ORIGIN: 'wnj:origin',
30
+ CLIENT_SECRET: 'wnj:clientSecret',
31
+ Y_POS: 'wnj:ypos',
32
+ CALLBACK_TOKEN: 'wnj:callbackToken',
33
+ BUNKER_POINTER: 'wnj:bunkerPointer'
34
+ }
28
35
 
29
36
  let myself: HTMLDivElement
30
37
  export let accent: string
31
38
  export let position: 'top' | 'bottom' = 'top'
32
39
  $: origin = $mobileMode
33
40
  ? 'bottom'
34
- : (localStorage.getItem('wnj:origin') as 'top' | 'bottom' | null) ||
35
- position
41
+ : (localStorage.getItem(localStorageKeys.ORIGIN) as
42
+ | 'top'
43
+ | 'bottom'
44
+ | null) || position
36
45
 
37
46
  const win = window as any
38
47
  const pool = new SimplePool()
@@ -42,12 +51,15 @@
42
51
  let nameInputValue: string
43
52
  let chosenProvider: BunkerProfile | undefined
44
53
  let clientSecret: Uint8Array
45
- const local = localStorage.getItem('wnj:clientSecret')
54
+ const local = localStorage.getItem(localStorageKeys.CLIENT_SECRET)
46
55
  if (local) {
47
56
  clientSecret = hexToBytes(local)
48
57
  } else {
49
58
  clientSecret = generateSecretKey()
50
- localStorage.setItem('wnj:clientSecret', bytesToHex(clientSecret))
59
+ localStorage.setItem(
60
+ localStorageKeys.CLIENT_SECRET,
61
+ bytesToHex(clientSecret)
62
+ )
51
63
  }
52
64
 
53
65
  let state: 'opened' | 'closed' | 'justopened' | 'justclosed' = 'closed'
@@ -57,9 +69,13 @@
57
69
  let bunker: Promise<BunkerSigner>
58
70
  let connecting: boolean
59
71
  let connected: boolean
72
+ let showAuth: string | null = null
73
+ let showLogin: string | null = null
60
74
  let takingTooLong = false
61
75
  let creating: boolean
76
+ let awaitingCreation: boolean
62
77
  let errorMessage: string
78
+ let showInfo = false
63
79
  let identity: null | {
64
80
  pubkey: string
65
81
  npub: string
@@ -78,7 +94,7 @@
78
94
  export let right = 20
79
95
  $: ypos = $mobileMode
80
96
  ? BASE_YPOS
81
- : parseInt(localStorage.getItem('wnj:ypos') || '0') || BASE_YPOS
97
+ : parseInt(localStorage.getItem(localStorageKeys.Y_POS) || '0') || BASE_YPOS
82
98
  let dragStarted = false
83
99
  let hasMoved = false
84
100
  let insidePosition: number
@@ -88,10 +104,10 @@
88
104
  $: opened = state === 'justopened' || state === 'opened'
89
105
 
90
106
  $: movingStyle = hasMoved
91
- ? 'tw-cursor-grabbing tw-outline-dashed tw-outline-' +
107
+ ? 'cursor-grabbing outline-dashed outline-' +
92
108
  accent +
93
- '-500 tw-outline-1 tw-outline-offset-4'
94
- : 'tw-outline-none'
109
+ '-500 outline-1 outline-offset-4'
110
+ : 'outline-none'
95
111
 
96
112
  $: bunkerInputValueIsGood =
97
113
  bunkerInputValue &&
@@ -101,10 +117,18 @@
101
117
  const bunkerSignerParams: BunkerSignerParams = {
102
118
  pool,
103
119
  onauth(url: string) {
104
- window.open(url, 'window.nostr', `width=600,height=800,popup=yes`)
120
+ if (creating) {
121
+ showAuth = url
122
+ } else {
123
+ showLogin = url
124
+ }
105
125
  }
106
126
  }
107
127
 
128
+ function openAuthURLPopup(url: string | null): Window | null {
129
+ return window.open(url!, 'window.nostr', `width=600,height=800,popup=yes`)
130
+ }
131
+
108
132
  const delayedUpdateState = debounce(() => {
109
133
  switch (state) {
110
134
  case 'justopened':
@@ -182,13 +206,14 @@
182
206
  }
183
207
 
184
208
  onMount(() => {
185
- let data = localStorage.getItem('wnj:bunkerPointer')
186
- if (data) {
187
- bunkerPointer = JSON.parse(data)
188
- // we have a pointer, which means we can get the public key right away
189
- // but we will only try to connect when any other method is called on window.nostr
190
-
191
- identify()
209
+ if (!bunkerPointer) {
210
+ let data = localStorage.getItem(localStorageKeys.BUNKER_POINTER)
211
+ if (data) {
212
+ bunkerPointer = JSON.parse(data)
213
+ // we have a pointer, which means we can get the public key right away
214
+ // but we will only try to connect when any other method is called on window.nostr
215
+ identify()
216
+ }
192
217
  }
193
218
 
194
219
  if (win.nostr && !win.nostr.isWnj) {
@@ -234,6 +259,18 @@
234
259
  function handleCloseModal(ev: MouseEvent) {
235
260
  close()
236
261
  creating = false
262
+ showAuth = null
263
+ showLogin = null
264
+ ev.stopPropagation()
265
+ }
266
+
267
+ function handleShowInfo(ev: MouseEvent) {
268
+ showInfo = true
269
+ ev.stopPropagation()
270
+ }
271
+
272
+ function handleCloseInfo(ev: MouseEvent) {
273
+ showInfo = false
237
274
  ev.stopPropagation()
238
275
  }
239
276
 
@@ -269,7 +306,7 @@
269
306
 
270
307
  async function handleDisconnect(ev: MouseEvent) {
271
308
  ev.preventDefault()
272
- localStorage.removeItem('wnj:bunkerPointer')
309
+ localStorage.removeItem(localStorageKeys.BUNKER_POINTER)
273
310
  reset()
274
311
  }
275
312
 
@@ -294,12 +331,14 @@
294
331
  ev.preventDefault()
295
332
  if (!chosenProvider) return
296
333
 
334
+ awaitingCreation = true
297
335
  let bunker = await createAccount(
298
336
  chosenProvider,
299
337
  bunkerSignerParams,
300
338
  nameInput.value,
301
339
  chosenProvider.domain
302
340
  )
341
+ awaitingCreation = false
303
342
 
304
343
  open()
305
344
  creating = false
@@ -338,7 +377,10 @@
338
377
  try {
339
378
  await b.connect()
340
379
  connected = true
341
- localStorage.setItem('wnj:bunkerPointer', JSON.stringify(bunkerPointer))
380
+ localStorage.setItem(
381
+ localStorageKeys.BUNKER_POINTER,
382
+ JSON.stringify(bunkerPointer)
383
+ )
342
384
  close()
343
385
  resolveBunker(b)
344
386
  } catch (err: any) {
@@ -348,11 +390,13 @@
348
390
  clearTimeout(connectionTimeout)
349
391
  connecting = false
350
392
  takingTooLong = false
393
+ showAuth = null
394
+ showLogin = null
351
395
  }
352
396
  }
353
397
 
354
- function identify() {
355
- let pubkey = bunkerPointer.pubkey
398
+ function identify(onFirstMetadata: (() => void) | null = null) {
399
+ let pubkey = bunkerPointer!.pubkey
356
400
 
357
401
  identity = {
358
402
  pubkey: pubkey,
@@ -375,6 +419,8 @@
375
419
  identity!.event = evt
376
420
  identity!.name = name
377
421
  identity!.picture = picture
422
+ onFirstMetadata?.()
423
+ onFirstMetadata = null
378
424
  } catch (err) {
379
425
  /***/
380
426
  }
@@ -436,8 +482,8 @@
436
482
  ypos = BASE_YPOS
437
483
  }
438
484
 
439
- localStorage.setItem('wnj:origin', origin)
440
- localStorage.setItem('wnj:ypos', ypos.toString())
485
+ localStorage.setItem(localStorageKeys.ORIGIN, origin)
486
+ localStorage.setItem(localStorageKeys.Y_POS, ypos.toString())
441
487
  }
442
488
  }
443
489
  </script>
@@ -450,8 +496,8 @@
450
496
 
451
497
  <!-- svelte-ignore a11y-no-static-element-interactions -->
452
498
  <div
453
- class="tw-text-white tw-font-sans draggable tw-animate-fadein"
454
- class:tw-cursor-pointer={!identity && !opened}
499
+ class="draggable animate-fadein font-sans text-white"
500
+ class:cursor-pointer={!identity && !opened}
455
501
  style="position: fixed; {opened && $mobileMode
456
502
  ? 'width: 100%;'
457
503
  : ''}; right: {opened && $mobileMode
@@ -465,23 +511,23 @@
465
511
  <!-- Close status ################### -->
466
512
  {#if !opened}
467
513
  <div
468
- class="tw-px-4 tw-py-2 tw-bg-{accent}-700 hover:tw-bg-{accent}-800 tw-rounded tw-shadow-[0_0px_10px_0px_rgba(0,0,0,0.3)] tw-transition-all tw-duration-200 {movingStyle}"
514
+ class="rounded px-4 py-2 shadow-[0_0px_10px_0px_rgba(0,0,0,0.3)] transition-all duration-200 bg-{accent}-700 hover:bg-{accent}-800 {movingStyle}"
469
515
  >
470
516
  <!-- Connecting view ################### -->
471
517
  {#if connecting}
472
- <div class="tw-flex tw-items-center">
518
+ <div class="flex items-center">
473
519
  Connecting to bunker
474
520
  <Spinner />
475
521
  </div>
476
522
  {:else if !identity}
477
523
  Connect with Nostr
478
524
  {:else}
479
- <div class="tw-flex tw-items-center">
525
+ <div class="flex items-center">
480
526
  {#if identity.picture}
481
527
  <img
482
528
  src={identity.picture}
483
529
  alt=""
484
- class="tw-w-5 tw-h-5 tw-rounded-full tw-mr-2"
530
+ class="mr-2 h-5 w-5 rounded-full"
485
531
  />
486
532
  {:else}
487
533
 
@@ -495,84 +541,148 @@
495
541
  <!-- Open status ################### -->
496
542
  {:else}
497
543
  <div
498
- class="sm:tw-w-80 tw-px-6 tw-py-8 tw-bg-{accent}-700 tw-rounded-md tw-shadow-[0_0px_30px_0px_rgba(0,0,0,0.6)] tw-transition-all tw-animate-show {movingStyle}"
544
+ class="animate-show rounded-md bg-gradient-to-b px-8 py-8 shadow-[0_0px_30px_0px_rgba(0,0,0,0.6)] transition-all sm:w-96 from-{accent}-900 to-{accent}-700 {movingStyle}"
499
545
  >
500
546
  <button
501
- on:click={handleCloseModal}
502
- class="tw-absolute tw-top-0 tw-right-0.5 tw-bg-transparent tw-border-none tw-cursor-pointer tw-text-{accent}-950 tw-text-3xl"
547
+ on:click={showInfo ? handleCloseInfo : handleCloseModal}
548
+ class="absolute right-2 top-0 cursor-pointer bg-transparent text-3xl text-{accent}-600"
503
549
  >⤫</button
504
550
  >
505
- <!-- Create account view ################### -->
506
- {#if creating}
507
- <div class="tw-text-lg tw-text-center">Create a Nostr account</div>
508
- <form class="tw-mt-4 tw-mb-1" on:submit={handleCreate}>
509
- <div class="tw-flex items-center">
551
+
552
+ {#if !showInfo && !showAuth && !showLogin}
553
+ <button
554
+ on:click={handleShowInfo}
555
+ class="absolute bottom-1 right-3 cursor-pointer bg-transparent text-xl text-{accent}-600"
556
+ >?</button
557
+ >
558
+ {/if}
559
+
560
+ {#if showAuth}
561
+ <div class="m-auto w-full">
562
+ <div class="text-center text-lg">Create a Nostr account</div>
563
+ <div class="mt-4 text-center text-sm leading-4">
564
+ Now you a new window will bring you to <strong>{new URL(showAuth).host}</strong> where the account creation will take place. If nothing happens check that if your browser is blocking popups, pleaase.<br/>
565
+ After that you will be returned to this page.
566
+ </div>
567
+ <button
568
+ class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
569
+ on:click={() => openAuthURLPopup(showAuth)}>
570
+ Start account creation »
571
+ </button>
572
+ </div>
573
+
574
+ {:else if showLogin}
575
+ <div class="m-auto w-full">
576
+ <div class="text-center text-lg">Login into a Nostr account</div>
577
+ <div class="mt-4 text-center text-sm leading-4">
578
+ Now you a new window will bring you to <strong>{new URL(showLogin).host}</strong> where you can login and approve the permissions. If nothing happens check that if your browser is blocking popups, pleaase.<br/>
579
+ After that you will be returned to this page.
580
+ </div>
581
+ <button
582
+ class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
583
+ on:click={() => openAuthURLPopup(showLogin)}>
584
+ Login now »
585
+ </button>
586
+ </div>
587
+
588
+ <!-- Show info ################### -->
589
+ {:else if showInfo}
590
+ <div class="text-center text-lg">What is that?</div>
591
+ <div class="text-base leading-5">
592
+ <p class="mb mt-4">
593
+ This widget is created with <i>window.nostr.js</i>, a small script
594
+ you can drop in any page that already uses NIP-07 and make it also
595
+ work with NIP-46 automatically when the user doesn't have an
596
+ extension installed.
597
+ <br />
598
+ It adds a small floating button on the side of the window that users
599
+ can use to create Nostr accuonts or connect to their NIP-46 bunkers.
600
+ </p>
601
+ <p class="mt-4">
602
+ This tool is opensource, get the code from the <a
603
+ target="_blank"
604
+ class="underline"
605
+ href="https://github.com/fiatjaf/window.nostr.js"
606
+ >project's page</a
607
+ >.
608
+ </p>
609
+ <p class="mt-4">
610
+ You don't know what Nostr is?
611
+ <a target="_blank" class="underline" href="https://www.nostr.com"
612
+ >Learn more</a
613
+ >.
614
+ </p>
615
+ </div>
616
+
617
+ <!-- Create account view ################### -->
618
+ {:else if creating}
619
+ <div class="text-center text-lg">Create a Nostr account</div>
620
+ <form class="mb-1 mt-4" on:submit={handleCreate}>
621
+ <div class="flex flex-row">
510
622
  <!-- svelte-ignore a11y-autofocus -->
511
623
  <input
512
- class="tw-box-border tw-w-40 tw-px-2 tw-py-1 tw-rounded tw-text-lg tw-border-none tw-outline-none"
624
+ class="box-border w-40 rounded px-2 py-1 text-lg text-neutral-800 outline-none"
513
625
  placeholder="bob"
514
626
  bind:this={nameInput}
515
627
  bind:value={nameInputValue}
516
628
  on:input={checkNameInput}
517
629
  autofocus
630
+ autocapitalize="none"
518
631
  />
519
- <div class="tw-mx-2 tw-text-2xl">@</div>
632
+ <div class="mx-2 text-2xl">@</div>
520
633
  <select
521
- class="tw-w-full tw-box-border tw-px-2 tw-py-1 tw-rounded tw-text-lg tw-border-none tw-outline-none"
634
+ class="box-border w-full rounded px-2 py-1 text-lg text-neutral-800 outline-none"
522
635
  bind:value={chosenProvider}
523
636
  >
524
637
  {#each providers as prov}
525
638
  <option
526
639
  label={prov.domain}
527
640
  value={prov}
528
- class="tw-px-2 tw-py-1 tw-text-lg"
641
+ class="px-2 py-1 text-lg"
529
642
  />
530
643
  {/each}
531
644
  </select>
532
645
  </div>
533
- <div class="tw-text-sm tw-text-center tw-mt-4 tw-leading-4">
534
- A window from the selected provider will pop up to finalize the
535
- creation; if it doesn't display check if the browser is blocking it
536
- </div>
537
646
  <button
538
- class="tw-block tw-w-full tw-mt-4 tw-px-2 tw-py-1 tw-text-lg tw-rounded tw-border-0 tw-bg-{accent}-900 hover:tw-bg-{accent}-950 tw-hover:bg-indigo-900 tw-cursor-pointer tw-text-white disabled:tw-bg-neutral-400 disabled:tw-text-neutral-200 disabled:tw-cursor-default"
539
- disabled={!chosenProvider || !nameInputValue}
647
+ class="mt-4 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
648
+ disabled={!chosenProvider || !nameInputValue || awaitingCreation}
540
649
  >
541
- Create »
650
+ Continue »
542
651
  </button>
543
652
  </form>
544
- <div class="tw-mt-6 tw-text-center tw-text-sm tw-leading-3">
653
+ <div class="mt-6 text-center text-sm leading-3">
545
654
  Do you already have a Nostr address?<br />
546
655
  <button
547
- class="tw-border-0 tw-bg-transparent tw-text-white tw-cursor-pointer tw-underline tw-text-sm"
656
+ class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
548
657
  on:click={handleOpenLogin}>Login now</button
549
658
  >
550
659
  </div>
551
660
 
552
661
  <!-- Login view ################### -->
553
662
  {:else if !identity}
554
- <div class="tw-text-lg tw-text-center">
663
+ <div class="text-center text-lg">
555
664
  How do you want to connect to Nostr?
556
665
  </div>
557
- <form class="flex tw-mt-4 tw-mb-1" on:submit={handleConnect}>
666
+ <form class="mb-1 mt-4 flex flex-col" on:submit={handleConnect}>
558
667
  <!-- svelte-ignore a11y-autofocus -->
559
668
  <input
560
- class="tw-box-border tw-w-full tw-px-2 tw-py-1 tw-rounded tw-text-lg tw-border-none tw-outline-none"
669
+ class="box-border w-full rounded px-2 py-1 text-lg text-neutral-800 outline-none"
561
670
  placeholder="user@provider or bunker://..."
562
671
  bind:this={bunkerInput}
563
672
  bind:value={bunkerInputValue}
564
673
  autofocus
565
674
  disabled={connecting}
675
+ autocapitalize="none"
566
676
  />
567
677
  {#if errorMessage}
568
678
  <div
569
- class="tw-my-2 tw-p-2 tw-text-sm tw-leading-4 tw-text-red-400 tw-bg-yellow-100 tw-rounded"
679
+ class="my-2 rounded bg-yellow-100 p-2 text-center text-sm leading-4 text-red-400"
570
680
  >
571
681
  {errorMessage}
572
682
  </div>
573
683
  {/if}
574
684
  <button
575
- class="tw-flex tw-w-full tw-mt-4 tw-px-2 tw-py-1 tw-text-lg tw-rounded tw-border-0 tw-bg-{accent}-900 hover:tw-bg-{accent}-950 tw-hover:bg-indigo-900 tw-cursor-pointer tw-text-white disabled:tw-bg-neutral-400 disabled:tw-text-neutral-200 disabled:tw-cursor-default tw-items-center tw-justify-center"
685
+ class="mt-4 flex w-full cursor-pointer items-center justify-center rounded border-0 px-2 py-1 text-lg text-white disabled:cursor-default disabled:bg-neutral-400 disabled:text-neutral-200 bg-{accent}-900 hover:bg-{accent}-950"
576
686
  disabled={!bunkerInputValueIsGood || connecting}
577
687
  >
578
688
  {#if connecting}
@@ -583,62 +693,60 @@
583
693
  {/if}
584
694
  </button>
585
695
  {#if connecting && takingTooLong}
586
- <div class="tw-mt-6 tw-text-center tw-text-sm tw-leading-3">
696
+ <div class="mt-6 text-center text-sm leading-3">
587
697
  Waiting too much?
588
698
  <button
589
- class="tw-border-0 tw-bg-transparent tw-text-white tw-cursor-pointer tw-underline tw-text-sm"
699
+ class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
590
700
  on:click={handleAbortConnection}>Cancel the connection</button
591
701
  >
592
702
  </div>
593
703
  {/if}
594
704
  </form>
595
705
  {#if !connecting}
596
- <div class="tw-mt-6 tw-text-center tw-text-sm tw-leading-3">
706
+ <div class="mt-6 text-center text-sm leading-3">
597
707
  Do you need a Nostr account?<br />
598
708
  <button
599
- class="tw-border-0 tw-bg-transparent tw-text-white tw-cursor-pointer tw-underline tw-text-sm"
709
+ class="cursor-pointer border-0 bg-transparent text-sm text-white underline"
600
710
  on:click={handleOpenCreate}>Sign up now</button
601
711
  >
602
712
  </div>
603
713
  {/if}
604
714
 
605
715
  <!-- Connected view ################### -->
606
- {:else if identity}
607
- <div class="tw-text-center">
608
- <div class="tw-text-sm tw-mb-4">You are connected to Nostr as</div>
716
+ {:else}
717
+ <div class="text-center">
718
+ <div class="mb-4 text-sm">You are connected to Nostr as</div>
609
719
  <a
610
720
  target="_blank"
611
721
  href={'https://nosta.me/' + identity.npub}
612
- class="tw-text-white tw-no-underline tw-group"
722
+ class="group text-white no-underline"
613
723
  >
614
724
  {#if identity.picture || identity.name}
615
- <div
616
- class="tw-flex tw-items-center tw-justify-center tw-gap-2 tw-mb-2"
617
- >
725
+ <div class="mb-2 flex items-center justify-center gap-2">
618
726
  {#if identity.picture}
619
727
  <img
620
728
  src={identity.picture}
621
729
  alt=""
622
- class="tw-w-10 tw-h-10 tw-rounded-full tw-border-solid tw-border-2 tw-border-transparent group-hover:tw-border-{accent}-100"
730
+ class="h-10 w-10 rounded-full border-2 border-solid border-transparent group-hover:border-{accent}-100"
623
731
  />
624
732
  {/if}
625
733
  {#if identity.name}
626
734
  <div
627
- class="tw-text-3xl group-hover:tw-underline tw-decoration-2 tw-underline-offset-4"
735
+ class="text-3xl decoration-2 underline-offset-4 group-hover:underline"
628
736
  >
629
737
  {identity.name}
630
738
  </div>
631
739
  {/if}
632
740
  </div>
633
741
  {/if}
634
- <div class="tw-block tw-break-all">{identity.npub}</div>
742
+ <div class="block break-all">{identity.npub}</div>
635
743
  </a>
636
744
  </div>
637
745
  <button
638
- class="tw-block tw-w-full tw-my-2 tw-mt-6 tw-px-2 tw-py-1 tw-text-lg tw-rounded tw-border-0 tw-bg-{accent}-900 hover:tw-bg-{accent}-950 tw-hover:bg-indigo-900 tw-cursor-pointer tw-text-white"
746
+ class="my-2 mt-6 block w-full cursor-pointer rounded border-0 px-2 py-1 text-lg text-white bg-{accent}-900 hover:bg-{accent}-950"
639
747
  on:click={handleDisconnect}>Disconnect</button
640
748
  >
641
- <div class="tw-block tw-break-all tw-mt-6 tw-text-center tw-text-sm">
749
+ <div class="mt-6 block break-all text-center text-sm">
642
750
  This webpage is using the public key:<br />
643
751
  {getPublicKey(clientSecret)}
644
752
  </div>
@@ -646,13 +754,3 @@
646
754
  </div>
647
755
  {/if}
648
756
  </div>
649
-
650
- <!-- hack to preload tailwind colors:
651
- tw-bg-cyan-700 tw-bg-cyan-800 tw-bg-cyan-900 tw-bg-cyan-950 tw-text-cyan-950 tw-outline-cyan-500
652
- tw-bg-green-700 tw-bg-green-800 tw-bg-green-900 tw-bg-green-950 tw-text-green-950 tw-outline-green-500
653
- tw-bg-purple-700 tw-bg-purple-800 tw-bg-purple-900 tw-bg-purple-950 tw-text-purple-950 tw-outline-purple-500
654
- tw-bg-red-700 tw-bg-red-800 tw-bg-red-900 tw-bg-red-950 tw-text-red-950 tw-outline-red-500
655
- tw-bg-orange-700 tw-bg-orange-800 tw-bg-orange-900 tw-bg-orange-950 tw-text-orange-950 tw-outline-orange-500
656
- tw-bg-neutral-700 tw-bg-neutral-800 tw-bg-neutral-900 tw-bg-neutral-950 tw-text-neutral-950 tw-outline-neutral-500
657
- tw-bg-stone-700 tw-bg-stone-800 tw-bg-stone-900 tw-bg-stone-950 tw-text-stone-950 tw-outline-stone-500
658
- -->
@@ -1,11 +1,11 @@
1
1
  <svg
2
- class="tw-animate-spin tw-ml-2 tw-h-5 tw-w-5 tw-text-white"
2
+ class="ml-2 h-5 w-5 animate-spin text-white"
3
3
  xmlns="http://www.w3.org/2000/svg"
4
4
  fill="none"
5
5
  viewBox="0 0 24 24"
6
6
  >
7
7
  <circle
8
- class="tw-opacity-25"
8
+ class="opacity-25"
9
9
  cx="12"
10
10
  cy="12"
11
11
  r="10"
@@ -13,7 +13,7 @@
13
13
  stroke-width="4"
14
14
  ></circle>
15
15
  <path
16
- class="tw-opacity-75"
16
+ class="opacity-75"
17
17
  fill="currentColor"
18
18
  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
19
19
  ></path>
package/src/app.css CHANGED
@@ -1,2 +1,3 @@
1
+ @tailwind base;
1
2
  @tailwind components;
2
3
  @tailwind utilities;
@@ -21,5 +21,14 @@ export default {
21
21
  }
22
22
  },
23
23
  plugins: [],
24
- prefix: 'tw-'
24
+ prefix: '',
25
+ safelist: [
26
+ 'bg-cyan-700', 'hover:bg-cyan-700', 'bg-cyan-800', 'hover:bg-cyan-800', 'bg-cyan-900', 'hover:bg-cyan-900', 'bg-cyan-950', 'hover:bg-cyan-950', 'text-cyan-600', 'outline-cyan-500', 'from-cyan-900', 'to-cyan-700',
27
+ 'bg-green-700', 'hover:bg-green-700', 'bg-green-800', 'hover:bg-green-800', 'bg-green-900', 'hover:bg-green-900', 'bg-green-950', 'hover:bg-green-950', 'text-green-600', 'outline-green-500', 'from-green-900', 'to-green-700',
28
+ 'bg-purple-700', 'hover:bg-purple-700', 'bg-purple-800', 'hover:bg-purple-800', 'bg-purple-900', 'hover:bg-purple-900', 'bg-purple-950', 'hover:bg-purple-950', 'text-purple-600', 'outline-purple-500', 'from-purple-900', 'to-purple-700',
29
+ 'bg-red-700', 'hover:bg-red-700', 'bg-red-800', 'hover:bg-red-800', 'bg-red-900', 'hover:bg-red-900', 'bg-red-950', 'hover:bg-red-950', 'text-red-600', 'outline-red-500', 'from-red-900', 'to-red-700',
30
+ 'bg-orange-700', 'hover:bg-orange-700', 'bg-orange-800', 'hover:bg-orange-800', 'bg-orange-900', 'hover:bg-orange-900', 'bg-orange-950', 'hover:bg-orange-950', 'text-orange-600', 'outline-orange-500', 'from-orange-900', 'to-orange-700',
31
+ 'bg-neutral-700', 'hover:bg-neutral-700', 'bg-neutral-800', 'hover:bg-neutral-800', 'bg-neutral-900', 'hover:bg-neutral-900', 'bg-neutral-950', 'hover:bg-neutral-950', 'text-neutral-600', 'outline-neutral-500', 'from-neutral-900', 'to-neutral-700',
32
+ 'bg-stone-700', 'hover:bg-stone-700', 'bg-stone-800', 'hover:bg-stone-800', 'bg-stone-900', 'hover:bg-stone-900', 'bg-stone-950', 'hover:bg-stone-950', 'text-stone-600', 'outline-stone-500', 'from-stone-900', 'to-stone-700',
33
+ ]
25
34
  }