@atmo-dev/kit 0.8.0 → 0.10.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.
Files changed (94) hide show
  1. package/README.md +27 -368
  2. package/dist/components/AccountMenu.svelte +102 -0
  3. package/dist/components/AccountMenu.svelte.d.ts +19 -0
  4. package/dist/components/Avatar.svelte +1 -1
  5. package/dist/components/Badge.svelte +3 -3
  6. package/dist/components/Button.svelte +5 -4
  7. package/dist/components/Checkbox.svelte +31 -1
  8. package/dist/{events → components}/DatePicker.svelte +3 -3
  9. package/dist/components/ExternalEmbed.svelte +10 -7
  10. package/dist/components/GrainAlbumPicker.svelte +18 -9
  11. package/dist/components/GrainAlbumView.svelte +15 -6
  12. package/dist/components/Heading.svelte +1 -1
  13. package/dist/components/IconRail.svelte +11 -9
  14. package/dist/components/InlineEditor.svelte +3 -3
  15. package/dist/components/InsetImage.svelte +1 -1
  16. package/dist/components/Label.svelte +1 -1
  17. package/dist/components/LinkCard.svelte +7 -5
  18. package/dist/components/Modal.svelte +3 -3
  19. package/dist/components/NestedComments.svelte +10 -7
  20. package/dist/components/PhotoGrid.svelte +1 -1
  21. package/dist/components/Popover.svelte +1 -1
  22. package/dist/components/PostComposer.svelte +2 -2
  23. package/dist/components/PostContent.svelte +12 -10
  24. package/dist/components/PostVideo.svelte +2 -2
  25. package/dist/components/RichText.svelte +19 -7
  26. package/dist/components/Select.svelte +5 -2
  27. package/dist/components/Select.svelte.d.ts +2 -0
  28. package/dist/components/SocialClientPicker.svelte +1 -1
  29. package/dist/components/StreamplacePicker.svelte +1 -1
  30. package/dist/components/Subheading.svelte +4 -2
  31. package/dist/components/Surface.svelte +3 -2
  32. package/dist/components/Tabs.svelte +2 -2
  33. package/dist/{events → components}/TimePicker.svelte +10 -7
  34. package/dist/{events → components}/TimezonePicker.svelte +15 -7
  35. package/dist/components/ToggleGroup.svelte +1 -1
  36. package/dist/components/ToggleGroupItem.svelte +1 -1
  37. package/dist/components/UserProfile.svelte +16 -9
  38. package/dist/components/WorkspaceSidebar.svelte +1 -1
  39. package/dist/components/legal/LegalDocument.svelte +9 -3
  40. package/dist/components/legal/LegalFooter.svelte +2 -2
  41. package/dist/components/legal/links.js +2 -9
  42. package/dist/components/login/ActorSearchField.svelte +10 -6
  43. package/dist/components/login/AtmosphereApps.svelte +1 -1
  44. package/dist/components/login/LoginForm.svelte +15 -9
  45. package/dist/components/login/PermissionFields.svelte +9 -2
  46. package/dist/components/login/RecentAccounts.svelte +5 -3
  47. package/dist/events/EventAttendees.svelte +9 -3
  48. package/dist/events/EventCard.svelte +7 -5
  49. package/dist/events/EventComments.svelte +3 -3
  50. package/dist/events/EventEditor.svelte +20 -9
  51. package/dist/events/EventRsvp.svelte +10 -6
  52. package/dist/events/EventView.svelte +21 -7
  53. package/dist/events/StarterPackButton.svelte +3 -3
  54. package/dist/events/ThemePicker.svelte +7 -7
  55. package/dist/events/ThumbnailPresets.svelte +1 -1
  56. package/dist/events/VodPlayer.svelte +1 -1
  57. package/dist/events/editor/LinksSection.svelte +7 -3
  58. package/dist/events/editor/LocationSection.svelte +1 -1
  59. package/dist/events/editor/RecurringModal.svelte +6 -4
  60. package/dist/events/editor/ThemeSection.svelte +3 -1
  61. package/dist/events/event-view/EventDateBlock.svelte +3 -3
  62. package/dist/events/event-view/EventHostedBy.svelte +6 -2
  63. package/dist/events/event-view/EventLinksList.svelte +13 -4
  64. package/dist/events/event-view/EventLocationBlock.svelte +9 -5
  65. package/dist/events/event-view/EventLocationMap.svelte +4 -2
  66. package/dist/events/event-view/ExternalRsvpNotice.svelte +20 -16
  67. package/dist/events/event-view/locations.js +1 -11
  68. package/dist/events/schedule/ConferenceTimetable.svelte +7 -7
  69. package/dist/events/schedule/DaySchedule.svelte +2 -2
  70. package/dist/events/schedule/ScheduleEventCell.svelte +2 -2
  71. package/dist/events/themes/Matrix.svelte +12 -6
  72. package/dist/events.d.ts +5 -5
  73. package/dist/events.js +6 -5
  74. package/dist/index.d.ts +7 -0
  75. package/dist/index.js +7 -0
  76. package/dist/richtext/document.d.ts +2 -1
  77. package/dist/richtext/document.js +3 -13
  78. package/dist/social/external-embeds.js +1 -1
  79. package/dist/social/mention-suggestions.js +4 -4
  80. package/dist/social/profile.js +1 -1
  81. package/dist/styles.d.ts +2 -2
  82. package/dist/styles.js +2 -2
  83. package/dist/theme.css +23 -44
  84. package/dist/theme.js +4 -12
  85. package/dist/urls.d.ts +9 -0
  86. package/dist/urls.js +39 -0
  87. package/package.json +5 -1
  88. /package/dist/{events → components}/DatePicker.svelte.d.ts +0 -0
  89. /package/dist/{events → components}/DateTimePicker.svelte +0 -0
  90. /package/dist/{events → components}/DateTimePicker.svelte.d.ts +0 -0
  91. /package/dist/{events → components}/ImageDropper.svelte +0 -0
  92. /package/dist/{events → components}/ImageDropper.svelte.d.ts +0 -0
  93. /package/dist/{events → components}/TimePicker.svelte.d.ts +0 -0
  94. /package/dist/{events → components}/TimezonePicker.svelte.d.ts +0 -0
@@ -86,7 +86,7 @@
86
86
 
87
87
  <a
88
88
  href={eventUrl(event, actor)}
89
- class="group grid grid-cols-[5rem_minmax(0,1fr)] gap-3 rounded-ui outline-offset-4 transition-colors focus-visible:outline-2 focus-visible:outline-focus sm:grid-cols-[6rem_minmax(0,1fr)] sm:gap-3.5"
89
+ class="group grid grid-cols-[5rem_minmax(0,1fr)] gap-3 rounded-ui outline-offset-4 transition-colors focus-visible:outline-2 focus-visible:outline-accent-700 sm:grid-cols-[6rem_minmax(0,1fr)] sm:gap-3.5 dark:focus-visible:outline-accent-300"
90
90
  >
91
91
  <div class="w-full">
92
92
  {#if thumbnail}
@@ -94,11 +94,11 @@
94
94
  src={thumbnail.url}
95
95
  alt={thumbnail.alt}
96
96
  loading="lazy"
97
- class="block aspect-square w-full rounded-[14px_12px_15px_13px] border border-line object-cover"
97
+ class="block aspect-square w-full rounded-[14px_12px_15px_13px] border border-base-300 object-cover dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))]"
98
98
  />
99
99
  {:else}
100
100
  <div
101
- class="aspect-square w-full overflow-hidden rounded-[14px_12px_15px_13px] border border-line [&>svg]:h-full [&>svg]:w-full"
101
+ class="aspect-square w-full overflow-hidden rounded-[14px_12px_15px_13px] border border-base-300 dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))] [&>svg]:h-full [&>svg]:w-full"
102
102
  >
103
103
  <Avatar
104
104
  size={96}
@@ -112,7 +112,7 @@
112
112
  </div>
113
113
 
114
114
  <div class="min-w-0 self-center">
115
- <p class="flex items-center gap-1.5 text-xs font-medium text-muted">
115
+ <p class="flex items-center gap-1.5 text-xs font-medium text-base-600 dark:text-base-400">
116
116
  <Icon img={CalendarDays} size={14} class="shrink-0" />
117
117
  {#if isOngoing && event.endsAt}
118
118
  Ends {formatEndsAt(event.endsAt)}
@@ -132,7 +132,9 @@
132
132
  <span class="line-clamp-2">{event.name}</span>
133
133
  </h3>
134
134
  {#if location || mode}
135
- <p class="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted">
135
+ <p
136
+ class="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-base-600 dark:text-base-400"
137
+ >
136
138
  {#if location}
137
139
  <span class="inline-flex max-w-full min-w-0 items-center gap-1.5" title={location}>
138
140
  <Icon img={MapPin} size={14} class="shrink-0" />
@@ -80,11 +80,11 @@
80
80
  {/if}
81
81
 
82
82
  {#if loading}
83
- <p class="text-sm text-muted">Loading comments…</p>
83
+ <p class="text-sm text-base-600 dark:text-base-400">Loading comments…</p>
84
84
  {:else if errorMessage}
85
- <p class="text-sm text-muted">{errorMessage}</p>
85
+ <p class="text-sm text-base-600 dark:text-base-400">{errorMessage}</p>
86
86
  {:else if comments.length === 0}
87
- <p class="text-sm text-muted">No comments yet.</p>
87
+ <p class="text-sm text-base-600 dark:text-base-400">No comments yet.</p>
88
88
  {:else}
89
89
  <div class="not-prose comments-no-divider">
90
90
  <NestedComments {comments} {socialClient} />
@@ -4,8 +4,8 @@
4
4
  import { Avatar, Button, ToggleGroup, ToggleGroupItem } from '../index.js';
5
5
  import { onMount, untrack } from 'svelte';
6
6
  import { InlineEditor, type Editor } from '../index.js';
7
- import DateTimePicker from './DateTimePicker.svelte';
8
- import TimezonePicker from './TimezonePicker.svelte';
7
+ import DateTimePicker from '../components/DateTimePicker.svelte';
8
+ import TimezonePicker from '../components/TimezonePicker.svelte';
9
9
  import { parseDateTime } from '@internationalized/date';
10
10
  import { isoToDatetimeLocalInTz } from './date-format.js';
11
11
  import type { FlatEventRecord } from './contrail.js';
@@ -386,7 +386,7 @@
386
386
  <ToggleGroupItem value="public">Public</ToggleGroupItem>
387
387
  <ToggleGroupItem value="unlisted">Unlisted</ToggleGroupItem>
388
388
  </ToggleGroup>
389
- <div class="mt-1.5 text-xs text-muted">
389
+ <div class="mt-1.5 text-xs text-base-600 dark:text-base-400">
390
390
  {#if visibility === 'public'}
391
391
  Anyone can view and it appears in discovery.
392
392
  {:else}
@@ -398,11 +398,11 @@
398
398
  <!-- Date row -->
399
399
  <div class="mb-5 grid min-w-0 gap-2">
400
400
  <div class="flex min-w-0 items-center gap-2">
401
- <span class="w-9 shrink-0 text-sm text-muted">Start</span>
401
+ <span class="w-9 shrink-0 text-sm text-base-600 dark:text-base-400">Start</span>
402
402
  <DateTimePicker bind:value={startsAt} label="Start" required />
403
403
  </div>
404
404
  <div class="flex min-w-0 items-center gap-2">
405
- <span class="w-9 shrink-0 text-sm text-muted">End</span>
405
+ <span class="w-9 shrink-0 text-sm text-base-600 dark:text-base-400">End</span>
406
406
  <DateTimePicker
407
407
  bind:value={endsAt}
408
408
  label="End"
@@ -419,7 +419,11 @@
419
419
 
420
420
  <!-- About Event -->
421
421
  <div class="mt-8 mb-8">
422
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">About</p>
422
+ <p
423
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
424
+ >
425
+ About
426
+ </p>
423
427
  <textarea
424
428
  aria-label="Event description"
425
429
  bind:value={description}
@@ -427,11 +431,14 @@
427
431
  placeholder="What's this event about? @mentions, #hashtags and links will be detected automatically."
428
432
  class="w-full resize-none border-0 bg-transparent px-0 leading-relaxed text-base-700 placeholder:text-base-500 focus:border-0 focus:ring-0 focus:outline-none dark:text-base-300 dark:placeholder:text-base-500"
429
433
  style="field-sizing: content;"></textarea>
430
- <p class="mt-2 text-xs text-muted">
434
+ <p class="mt-2 text-xs text-base-600 dark:text-base-400">
431
435
  Add a hashtag like <span class="font-medium">#webdev</span> or
432
436
  <span class="font-medium">#music</span>
433
437
  to have this event appear under a
434
- <a href="/topics" class="text-action underline underline-offset-2">topic</a>.
438
+ <a
439
+ href="/topics"
440
+ class="text-accent-700 underline underline-offset-2 dark:text-accent-300">topic</a
441
+ >.
435
442
  </p>
436
443
  </div>
437
444
 
@@ -462,7 +469,11 @@
462
469
 
463
470
  <!-- Hosted By -->
464
471
  <div class="order-3 md:order-0 md:col-start-1">
465
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Hosted By</p>
472
+ <p
473
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
474
+ >
475
+ Hosted By
476
+ </p>
466
477
  <div class="flex items-center gap-2.5">
467
478
  <Avatar src={viewer.avatar} name={hostName} class="size-8 shrink-0" />
468
479
  <span class="truncate text-sm font-medium text-base-900 dark:text-base-100">
@@ -98,7 +98,7 @@
98
98
 
99
99
  {#if rsvpExpected !== false || (viewer.isLoggedIn && rsvpStatus)}
100
100
  <div
101
- class="mt-7 mb-2 flex min-h-25 flex-col justify-center rounded-panel bg-recess p-4 [box-shadow:var(--ui-recess-shadow)]"
101
+ class="mt-7 mb-2 flex min-h-25 flex-col justify-center rounded-panel bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] p-4 shadow-[inset_0_2px_4px_color-mix(in_srgb,var(--base-950)_8%,transparent),inset_0_5px_12px_color-mix(in_srgb,var(--base-950)_2%,transparent)] dark:bg-base-950 dark:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002]"
102
102
  >
103
103
  {#if !viewer.isLoggedIn}
104
104
  <div class="flex items-center justify-between gap-4">
@@ -114,8 +114,10 @@
114
114
  {:else if rsvpStatus === 'going'}
115
115
  <div class="flex items-center justify-between">
116
116
  <div class="flex items-center gap-3">
117
- <div class="flex size-8 items-center justify-center rounded-full bg-accent-soft">
118
- <Icon img={Check} size={16} class="text-action" />
117
+ <div
118
+ class="flex size-8 items-center justify-center rounded-full bg-accent-50 dark:bg-[color-mix(in_srgb,var(--accent-950)_65%,var(--base-950))]"
119
+ >
120
+ <Icon img={Check} size={16} class="text-accent-700 dark:text-accent-300" />
119
121
  </div>
120
122
  <p class="font-semibold text-base-900 dark:text-base-50">You're Going</p>
121
123
  </div>
@@ -124,8 +126,10 @@
124
126
  {:else if rsvpStatus === 'interested'}
125
127
  <div class="flex items-center justify-between">
126
128
  <div class="flex items-center gap-3">
127
- <div class="flex size-8 items-center justify-center rounded-full bg-accent-soft">
128
- <Icon img={Star} size={16} class="text-action" />
129
+ <div
130
+ class="flex size-8 items-center justify-center rounded-full bg-accent-50 dark:bg-[color-mix(in_srgb,var(--accent-950)_65%,var(--base-950))]"
131
+ >
132
+ <Icon img={Star} size={16} class="text-accent-700 dark:text-accent-300" />
129
133
  </div>
130
134
  <p class="font-semibold text-base-900 dark:text-base-50">You're Interested</p>
131
135
  </div>
@@ -146,7 +150,7 @@
146
150
  {:else}
147
151
  {#if viewer.isLoggedIn}
148
152
  <div class="mb-4 flex items-center gap-2">
149
- <span class="text-sm text-muted">Attend as</span>
153
+ <span class="text-sm text-base-600 dark:text-base-400">Attend as</span>
150
154
  <Avatar
151
155
  src={viewer.avatar}
152
156
  name={viewer.displayName || viewer.handle || viewer.did || 'Account'}
@@ -260,7 +260,7 @@
260
260
  src={displayImage.url}
261
261
  alt={displayImage.alt}
262
262
  loading="eager"
263
- class="mb-8 block w-full rounded-panel border border-line object-cover"
263
+ class="mb-8 block w-full rounded-panel border border-base-300 object-cover dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))]"
264
264
  style:aspect-ratio={frameAspectRatio}
265
265
  />
266
266
  {/if}
@@ -277,12 +277,12 @@
277
277
  src={displayImage.url}
278
278
  alt={displayImage.alt}
279
279
  loading="eager"
280
- class="block w-full rounded-panel border border-line object-cover"
280
+ class="block w-full rounded-panel border border-base-300 object-cover dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))]"
281
281
  style:aspect-ratio={frameAspectRatio}
282
282
  />
283
283
  {:else if !showBanner}
284
284
  <div
285
- class="aspect-square w-full overflow-hidden rounded-panel border border-line [&>svg]:h-full [&>svg]:w-full"
285
+ class="aspect-square w-full overflow-hidden rounded-panel border border-base-300 dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))] [&>svg]:h-full [&>svg]:w-full"
286
286
  >
287
287
  <Avatar
288
288
  size={256}
@@ -328,7 +328,11 @@
328
328
  <div
329
329
  class="mt-8 mb-2 justify-center rounded-2xl border border-base-200 bg-base-100 p-4 dark:border-base-800 dark:bg-base-950/50"
330
330
  >
331
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Part of</p>
331
+ <p
332
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
333
+ >
334
+ Part of
335
+ </p>
332
336
  <EventCard event={data.parentEvent} actor={parentActor} />
333
337
  <Button
334
338
  href={data.parentScheduleUrl ?? `/p/${parentActor}/e/${data.parentEvent.rkey}`}
@@ -361,7 +365,11 @@
361
365
  <!-- Live stream -->
362
366
  {#if isOngoing && streamPlaceHandle}
363
367
  <div class="mt-8 mb-8">
364
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Live now</p>
368
+ <p
369
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
370
+ >
371
+ Live now
372
+ </p>
365
373
  <StreamPlacePlayer handle={streamPlaceHandle} title={eventData.name} />
366
374
  </div>
367
375
  {/if}
@@ -387,7 +395,9 @@
387
395
  >{/each}
388
396
  </ToggleGroup>
389
397
  {:else}
390
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">
398
+ <p
399
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
400
+ >
391
401
  {contentTabs[0].label}
392
402
  </p>
393
403
  {/if}
@@ -423,7 +433,11 @@
423
433
  <!-- Recording -->
424
434
  {#if data.vod}
425
435
  <div class="mt-8 mb-8">
426
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Recording</p>
436
+ <p
437
+ class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400"
438
+ >
439
+ Recording
440
+ </p>
427
441
  <VodPlayer
428
442
  playlistUrl={data.vod.playlistUrl}
429
443
  title={eventData.name}
@@ -46,13 +46,13 @@
46
46
  </script>
47
47
 
48
48
  <div class="mt-4">
49
- <p class="mb-1 text-xs text-muted">Follow attendees on {clientLabel}.</p>
49
+ <p class="mb-1 text-xs text-base-600 dark:text-base-400">Follow attendees on {clientLabel}.</p>
50
50
  {#if packUrl}
51
51
  <a
52
52
  href={packUrl}
53
53
  target="_blank"
54
54
  rel="noopener noreferrer"
55
- class="rounded text-sm text-action underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-focus"
55
+ class="rounded text-sm text-accent-700 underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-accent-700 dark:text-accent-300 dark:focus-visible:outline-accent-300"
56
56
  >Starter pack ↗</a
57
57
  >
58
58
  {:else}
@@ -67,6 +67,6 @@
67
67
  </Button>
68
68
  {/if}
69
69
  {#if message}
70
- <p role="alert" class="mt-2 text-sm text-muted">{message}</p>
70
+ <p role="alert" class="mt-2 text-sm text-base-600 dark:text-base-400">{message}</p>
71
71
  {/if}
72
72
  </div>
@@ -45,7 +45,7 @@
45
45
  <div class="flex flex-col gap-6">
46
46
  <!-- Theme background -->
47
47
  <div>
48
- <p class="mb-2 text-xs font-medium text-muted">Background style</p>
48
+ <p class="mb-2 text-xs font-medium text-base-600 dark:text-base-400">Background style</p>
49
49
  <div class="flex flex-wrap gap-2">
50
50
  {#each bgKeys as key (key)}
51
51
  <button
@@ -66,7 +66,7 @@
66
66
 
67
67
  <!-- Accent color -->
68
68
  <div>
69
- <p class="mb-2 text-xs font-medium text-muted">Accent color</p>
69
+ <p class="mb-2 text-xs font-medium text-base-600 dark:text-base-400">Accent color</p>
70
70
  <div class="flex flex-wrap gap-2">
71
71
  {#each accentColors as color (color.label)}
72
72
  <button
@@ -75,8 +75,8 @@
75
75
  aria-pressed={theme.accentColor === color.label}
76
76
  class="size-7 cursor-pointer rounded-full border-2 transition-all {color.cls}
77
77
  {theme.accentColor === color.label
78
- ? 'border-white ring-2 ring-control-line'
79
- : 'border-transparent hover:border-control-line'}"
78
+ ? 'border-white ring-2 ring-base-300 dark:ring-base-800'
79
+ : 'border-transparent hover:border-base-300 dark:hover:border-base-800'}"
80
80
  onclick={() => (theme = { ...theme, accentColor: color.label })}
81
81
  ></button>
82
82
  {/each}
@@ -85,7 +85,7 @@
85
85
 
86
86
  <!-- Base color -->
87
87
  <div>
88
- <p class="mb-2 text-xs font-medium text-muted">Base color</p>
88
+ <p class="mb-2 text-xs font-medium text-base-600 dark:text-base-400">Base color</p>
89
89
  <div class="flex flex-wrap gap-2">
90
90
  {#each baseColors as color (color.label)}
91
91
  <button
@@ -94,8 +94,8 @@
94
94
  aria-pressed={theme.baseColor === color.label}
95
95
  class="size-7 cursor-pointer rounded-full border-2 transition-all {color.cls}
96
96
  {theme.baseColor === color.label
97
- ? 'border-white ring-2 ring-control-line'
98
- : 'border-transparent hover:border-control-line'}"
97
+ ? 'border-white ring-2 ring-base-300 dark:ring-base-800'
98
+ : 'border-transparent hover:border-base-300 dark:hover:border-base-800'}"
99
99
  onclick={() => (theme = { ...theme, baseColor: color.label })}
100
100
  ></button>
101
101
  {/each}
@@ -49,7 +49,7 @@
49
49
  </script>
50
50
 
51
51
  <div class="flex flex-col gap-3">
52
- <p class="text-xs font-medium text-muted">Preset thumbnails</p>
52
+ <p class="text-xs font-medium text-base-600 dark:text-base-400">Preset thumbnails</p>
53
53
  <div class="grid grid-cols-3 gap-2" bind:this={containerEl}>
54
54
  {#each presetKeys as key (key)}
55
55
  <button
@@ -136,7 +136,7 @@
136
136
  <div
137
137
  class="flex aspect-video w-full items-center justify-center rounded-xl border border-base-200 bg-base-100 dark:border-base-800 dark:bg-base-900"
138
138
  >
139
- <p class="text-sm text-muted">Failed to load video</p>
139
+ <p class="text-sm text-base-600 dark:text-base-400">Failed to load video</p>
140
140
  </div>
141
141
  {:else}
142
142
  <div
@@ -65,7 +65,7 @@
65
65
  <Icon
66
66
  img={provider === 'grain' ? Camera : provider === 'stream' ? Video : LinkIcon}
67
67
  size={16}
68
- class="shrink-0 text-muted"
68
+ class="shrink-0 text-base-600 dark:text-base-400"
69
69
  />
70
70
  <div class="min-w-0 flex-1">
71
71
  <p class="truncate text-sm">
@@ -89,13 +89,17 @@
89
89
 
90
90
  <div>
91
91
  {#if ordinary.length}
92
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Links</p>
92
+ <p class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
93
+ Links
94
+ </p>
93
95
  <div class="mb-4 space-y-3">
94
96
  {#each ordinary as link, i (i)}{@render row(link)}{/each}
95
97
  </div>
96
98
  {/if}
97
99
  {#if linked.length}
98
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Linked records</p>
100
+ <p class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
101
+ Linked records
102
+ </p>
99
103
  <div class="mb-4 space-y-3">
100
104
  {#each linked as link, i (i)}{@render row(
101
105
  link,
@@ -136,7 +136,7 @@
136
136
  <p class="font-medium text-base-900 dark:text-base-50">
137
137
  {getLocationDisplayString(result.location)}
138
138
  </p>
139
- <p class="mt-0.5 truncate text-xs text-muted">
139
+ <p class="mt-0.5 truncate text-xs text-base-600 dark:text-base-400">
140
140
  {result.displayName}
141
141
  </p>
142
142
  </div>
@@ -219,7 +219,9 @@
219
219
  </script>
220
220
 
221
221
  <Modal bind:open title="Recurring events" dismissible={!creating}>
222
- <p class="mt-1 text-sm text-muted">Create multiple copies of this event at regular intervals.</p>
222
+ <p class="mt-1 text-sm text-base-600 dark:text-base-400">
223
+ Create multiple copies of this event at regular intervals.
224
+ </p>
223
225
 
224
226
  <div class="mt-4 space-y-4">
225
227
  <div>
@@ -276,7 +278,7 @@
276
278
  <Checkbox bind:checked={numberInTitle} disabled={creating} aria-label="Number in title" />
277
279
  <span class="text-sm font-medium text-base-700 dark:text-base-300">Number in title</span>
278
280
  </div>
279
- <p class="mt-1 text-xs text-muted">
281
+ <p class="mt-1 text-xs text-base-600 dark:text-base-400">
280
282
  {#if numberInTitle && detectedStartNumber !== null}
281
283
  Titles will count up from #{detectedStartNumber + 1}
282
284
  {:else if numberInTitle}
@@ -293,13 +295,13 @@
293
295
  {/if}
294
296
 
295
297
  {#if creating && created > 0}
296
- <p class="mt-4 text-sm text-muted">
298
+ <p class="mt-4 text-sm text-base-600 dark:text-base-400">
297
299
  Created {created} of {count} events...
298
300
  </p>
299
301
  {/if}
300
302
 
301
303
  {#if created > 0 && !creating}
302
- <p class="mt-4 text-sm text-action">
304
+ <p class="mt-4 text-sm text-accent-700 dark:text-accent-300">
303
305
  Successfully created {created} recurring events!
304
306
  </p>
305
307
  {/if}
@@ -10,7 +10,9 @@
10
10
  </script>
11
11
 
12
12
  <div>
13
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Theme</p>
13
+ <p class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
14
+ Theme
15
+ </p>
14
16
  <Button variant="secondary" size="sm" onclick={() => (showModal = true)}>
15
17
  <Icon img={Palette} size={16} />
16
18
  {themeBackgrounds[theme.name] || theme.name}
@@ -14,9 +14,9 @@
14
14
 
15
15
  <div class="mb-4 flex items-center gap-4">
16
16
  <div
17
- class="flex size-12 shrink-0 flex-col items-center justify-center overflow-hidden rounded-ui bg-recess [box-shadow:var(--ui-recess-shadow)]"
17
+ class="flex size-12 shrink-0 flex-col items-center justify-center overflow-hidden rounded-ui bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] shadow-[inset_0_2px_4px_color-mix(in_srgb,var(--base-950)_8%,transparent),inset_0_5px_12px_color-mix(in_srgb,var(--base-950)_2%,transparent)] dark:bg-base-950 dark:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002]"
18
18
  >
19
- <span class="text-[9px] leading-none font-semibold text-muted">
19
+ <span class="text-[9px] leading-none font-semibold text-base-600 dark:text-base-400">
20
20
  {formatMonth(startDate)}
21
21
  </span>
22
22
  <span class="text-lg leading-tight font-bold text-base-900 dark:text-base-50">
@@ -30,7 +30,7 @@
30
30
  - {formatWeekday(endDate)}, {formatFullDate(endDate)}
31
31
  {/if}
32
32
  </p>
33
- <p class="flex flex-wrap items-center gap-2 text-sm text-muted">
33
+ <p class="flex flex-wrap items-center gap-2 text-sm text-base-600 dark:text-base-400">
34
34
  <span>
35
35
  {formatTime(startDate)}
36
36
  {#if endDate && isSameDay}
@@ -18,7 +18,9 @@
18
18
  </script>
19
19
 
20
20
  <div>
21
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Hosted By</p>
21
+ <p class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
22
+ Hosted By
23
+ </p>
22
24
  <a
23
25
  href={hostUrl}
24
26
  class="flex items-center gap-2.5 font-medium text-base-900 transition-opacity hover:opacity-80 dark:text-base-100"
@@ -36,7 +38,9 @@
36
38
 
37
39
  {#if speakers.length > 0}
38
40
  <div>
39
- <p class="mb-3 text-xs font-semibold tracking-wider text-muted uppercase">Speakers</p>
41
+ <p class="mb-3 text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
42
+ Speakers
43
+ </p>
40
44
  <div class="space-y-2">
41
45
  {#each speakers as speaker, i (speaker.id || i)}
42
46
  {#if speaker.handle}
@@ -1,19 +1,28 @@
1
1
  <script lang="ts">
2
2
  import { Icon } from '../../index.js';
3
3
  import { Link } from '@sketchyicons/svelte';
4
+ import { safeHttpUrl } from '../../urls.js';
4
5
  let { uris = [] }: { uris?: Array<{ uri: string; name?: string }> } = $props();
6
+ let links = $derived(
7
+ uris.flatMap((link) => {
8
+ const uri = safeHttpUrl(link?.uri);
9
+ return uri ? [{ ...link, uri }] : [];
10
+ })
11
+ );
5
12
  </script>
6
13
 
7
- {#if uris.length}
14
+ {#if links.length}
8
15
  <div>
9
- <p class="mb-3 text-xs font-medium text-muted">Links</p>
16
+ <p class="mb-3 text-xs font-medium text-base-600 dark:text-base-400">Links</p>
10
17
  <div class="space-y-3">
11
- {#each uris as link (link.name + link.uri)}
18
+ <!-- These rows are stateless; equivalent URLs and exact duplicate entries are valid. -->
19
+ <!-- eslint-disable-next-line svelte/require-each-key -->
20
+ {#each links as link}
12
21
  <a
13
22
  href={link.uri}
14
23
  target="_blank"
15
24
  rel="noopener noreferrer"
16
- class="flex items-center gap-2 rounded-sm text-sm text-muted transition-colors hover:text-action"
25
+ class="flex items-center gap-2 rounded-sm text-sm text-base-600 transition-colors hover:text-accent-700 dark:text-base-400 dark:hover:text-accent-300"
17
26
  ><Icon img={Link} size={15} /><span class="truncate"
18
27
  >{link.name || link.uri.replace(/^https?:\/\//, '')}</span
19
28
  ></a
@@ -13,13 +13,17 @@
13
13
  {#if physicalLines.length}
14
14
  <li class="flex min-w-0 items-center gap-4">
15
15
  <div
16
- class="flex size-12 shrink-0 items-center justify-center rounded-ui bg-recess text-muted [box-shadow:var(--ui-recess-shadow)]"
16
+ class="flex size-12 shrink-0 items-center justify-center rounded-ui bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] text-base-600 shadow-[inset_0_2px_4px_color-mix(in_srgb,var(--base-950)_8%,transparent),inset_0_5px_12px_color-mix(in_srgb,var(--base-950)_2%,transparent)] dark:bg-base-950 dark:text-base-400 dark:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002]"
17
17
  >
18
18
  <Icon img={MapPin} size={22} />
19
19
  </div>
20
20
  <div class="min-w-0 [overflow-wrap:anywhere]">
21
21
  {#each physicalLines as line, i (i)}
22
- <p class={line.muted ? 'text-sm text-muted' : 'font-semibold text-ink'}>
22
+ <p
23
+ class={line.muted
24
+ ? 'text-sm text-base-600 dark:text-base-400'
25
+ : 'font-semibold text-base-950 dark:text-base-50'}
26
+ >
23
27
  <svelte:element
24
28
  this={line.href ? 'a' : 'span'}
25
29
  href={line.href}
@@ -45,14 +49,14 @@
45
49
  : ''}"
46
50
  >
47
51
  <div
48
- class="flex size-12 shrink-0 items-center justify-center rounded-ui bg-recess text-muted [box-shadow:var(--ui-recess-shadow)]"
52
+ class="flex size-12 shrink-0 items-center justify-center rounded-ui bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] text-base-600 shadow-[inset_0_2px_4px_color-mix(in_srgb,var(--base-950)_8%,transparent),inset_0_5px_12px_color-mix(in_srgb,var(--base-950)_2%,transparent)] dark:bg-base-950 dark:text-base-400 dark:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002]"
49
53
  >
50
54
  <Icon img={Link} size={22} />
51
55
  </div>
52
56
  <div class="min-w-0 [overflow-wrap:anywhere]">
53
- <p class="font-semibold text-ink">{location.label}</p>
57
+ <p class="font-semibold text-base-950 dark:text-base-50">{location.label}</p>
54
58
  {#if location.detail}
55
- <p class="text-sm text-muted">{location.detail}</p>
59
+ <p class="text-sm text-base-600 dark:text-base-400">{location.detail}</p>
56
60
  {/if}
57
61
  </div>
58
62
  </svelte:element>
@@ -23,7 +23,9 @@
23
23
  {#if geoLocation}
24
24
  <div class="mt-8 mb-8">
25
25
  <div class="mb-3 flex items-baseline gap-2">
26
- <p class="text-xs font-semibold tracking-wider text-muted uppercase">Location:</p>
26
+ <p class="text-xs font-semibold tracking-wider text-base-600 uppercase dark:text-base-400">
27
+ Location:
28
+ </p>
27
29
  <button
28
30
  type="button"
29
31
  onclick={copyCoords}
@@ -42,7 +44,7 @@
42
44
  <p class="mt-3 text-sm [overflow-wrap:anywhere] text-base-700 dark:text-base-200">
43
45
  {geoLocation.label}
44
46
  </p>
45
- <p class="mt-1 text-xs text-muted">
47
+ <p class="mt-1 text-xs text-base-600 dark:text-base-400">
46
48
  Open in
47
49
  <a
48
50
  href={geoLocation.googleMapsUrl}
@@ -1,34 +1,38 @@
1
1
  <script lang="ts">
2
2
  import { ArrowUpRight } from '@sketchyicons/svelte';
3
3
  import { Button, Icon } from '../../index.js';
4
+ import { safeHttpUrl } from '../../urls.js';
4
5
 
5
6
  // Shown in place of the RSVP controls for imported events whose host opted out
6
7
  // of atmo RSVPs (additionalData.externalSource.rsvpMode === 'external_only').
7
8
  // Directs attendees to the original event page instead.
8
9
  let { url }: { url: string } = $props();
9
10
 
11
+ let href = $derived(safeHttpUrl(url));
10
12
  let host = $derived.by(() => {
11
13
  try {
12
- return new URL(url).hostname.replace(/^www\./, '');
14
+ return new URL(href ?? '').hostname.replace(/^www\./, '');
13
15
  } catch {
14
16
  return null;
15
17
  }
16
18
  });
17
19
  </script>
18
20
 
19
- <div
20
- class="mt-8 mb-2 flex flex-col gap-3 rounded-2xl border border-base-200 bg-base-100 p-4 sm:flex-row sm:items-center sm:justify-between dark:border-base-800 dark:bg-base-950/50"
21
- >
22
- <div class="min-w-0">
23
- <p class="text-sm font-semibold text-base-900 dark:text-base-50">RSVP on the original page</p>
24
- <p class="mt-0.5 text-xs text-base-600 dark:text-base-400">
25
- {host
26
- ? `This event is hosted on ${host}. Head there to RSVP.`
27
- : 'This event is hosted elsewhere. Open the original page to RSVP.'}
28
- </p>
21
+ {#if href}
22
+ <div
23
+ class="mt-8 mb-2 flex flex-col gap-3 rounded-2xl border border-base-200 bg-base-100 p-4 sm:flex-row sm:items-center sm:justify-between dark:border-base-800 dark:bg-base-950/50"
24
+ >
25
+ <div class="min-w-0">
26
+ <p class="text-sm font-semibold text-base-900 dark:text-base-50">RSVP on the original page</p>
27
+ <p class="mt-0.5 text-xs text-base-600 dark:text-base-400">
28
+ {host
29
+ ? `This event is hosted on ${host}. Head there to RSVP.`
30
+ : 'This event is hosted elsewhere. Open the original page to RSVP.'}
31
+ </p>
32
+ </div>
33
+ <Button {href} target="_blank" rel="noopener noreferrer" class="shrink-0">
34
+ {host ? `RSVP on ${host}` : 'Open original event'}
35
+ <Icon img={ArrowUpRight} size={16} class="shrink-0" />
36
+ </Button>
29
37
  </div>
30
- <Button href={url} target="_blank" rel="noopener noreferrer" class="shrink-0">
31
- {host ? `RSVP on ${host}` : 'Open original event'}
32
- <Icon img={ArrowUpRight} size={16} class="shrink-0" />
33
- </Button>
34
- </div>
38
+ {/if}