@atmo-dev/kit 0.0.0-stage → 0.0.2

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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +9 -2
  3. package/dist/components/Avatar.svelte +35 -0
  4. package/dist/components/Avatar.svelte.d.ts +9 -0
  5. package/dist/components/Badge.svelte +21 -0
  6. package/dist/components/Badge.svelte.d.ts +7 -0
  7. package/dist/components/Button.svelte +94 -0
  8. package/dist/components/Button.svelte.d.ts +13 -0
  9. package/dist/components/Checkbox.svelte +29 -0
  10. package/dist/components/Checkbox.svelte.d.ts +8 -0
  11. package/dist/components/Heading.svelte +24 -0
  12. package/dist/components/Heading.svelte.d.ts +8 -0
  13. package/dist/components/Icon.svelte +24 -0
  14. package/dist/components/Icon.svelte.d.ts +8 -0
  15. package/dist/components/IconButton.svelte +24 -0
  16. package/dist/components/IconButton.svelte.d.ts +8 -0
  17. package/dist/components/IconRail.svelte +183 -0
  18. package/dist/components/IconRail.svelte.d.ts +26 -0
  19. package/dist/components/InlineEditor.svelte +120 -0
  20. package/dist/components/InlineEditor.svelte.d.ts +19 -0
  21. package/dist/components/Input.svelte +32 -0
  22. package/dist/components/Input.svelte.d.ts +9 -0
  23. package/dist/components/InsetImage.svelte +29 -0
  24. package/dist/components/InsetImage.svelte.d.ts +9 -0
  25. package/dist/components/Label.svelte +19 -0
  26. package/dist/components/Label.svelte.d.ts +7 -0
  27. package/dist/components/LinkCard.svelte +47 -0
  28. package/dist/components/LinkCard.svelte.d.ts +12 -0
  29. package/dist/components/Modal.svelte +93 -0
  30. package/dist/components/Modal.svelte.d.ts +12 -0
  31. package/dist/components/NestedComments.svelte +61 -0
  32. package/dist/components/NestedComments.svelte.d.ts +8 -0
  33. package/dist/components/PageTitle.svelte +22 -0
  34. package/dist/components/PageTitle.svelte.d.ts +8 -0
  35. package/dist/components/Popover.svelte +49 -0
  36. package/dist/components/Popover.svelte.d.ts +12 -0
  37. package/dist/components/PostComposer.svelte +41 -0
  38. package/dist/components/PostComposer.svelte.d.ts +16 -0
  39. package/dist/components/PostContent.svelte +62 -0
  40. package/dist/components/PostContent.svelte.d.ts +8 -0
  41. package/dist/components/PostVideo.svelte +74 -0
  42. package/dist/components/PostVideo.svelte.d.ts +9 -0
  43. package/dist/components/Select.svelte +34 -0
  44. package/dist/components/Select.svelte.d.ts +9 -0
  45. package/dist/components/Subheading.svelte +22 -0
  46. package/dist/components/Subheading.svelte.d.ts +8 -0
  47. package/dist/components/Surface.svelte +40 -0
  48. package/dist/components/Surface.svelte.d.ts +11 -0
  49. package/dist/components/Tabs.svelte +47 -0
  50. package/dist/components/Tabs.svelte.d.ts +16 -0
  51. package/dist/components/Textarea.svelte +20 -0
  52. package/dist/components/Textarea.svelte.d.ts +7 -0
  53. package/dist/components/ToggleGroup.svelte +32 -0
  54. package/dist/components/ToggleGroup.svelte.d.ts +12 -0
  55. package/dist/components/ToggleGroupItem.svelte +26 -0
  56. package/dist/components/ToggleGroupItem.svelte.d.ts +8 -0
  57. package/dist/components/WorkspaceSidebar.svelte +82 -0
  58. package/dist/components/WorkspaceSidebar.svelte.d.ts +10 -0
  59. package/dist/components/login/ActorSearchField.svelte +238 -0
  60. package/dist/components/login/ActorSearchField.svelte.d.ts +19 -0
  61. package/dist/components/login/AtmosphereApps.svelte +19 -0
  62. package/dist/components/login/AtmosphereApps.svelte.d.ts +18 -0
  63. package/dist/components/login/LoginDialog.svelte +78 -0
  64. package/dist/components/login/LoginDialog.svelte.d.ts +9 -0
  65. package/dist/components/login/LoginForm.svelte +402 -0
  66. package/dist/components/login/LoginForm.svelte.d.ts +28 -0
  67. package/dist/components/login/RecentAccounts.svelte +57 -0
  68. package/dist/components/login/RecentAccounts.svelte.d.ts +10 -0
  69. package/dist/components/login/brands/README.md +7 -0
  70. package/dist/components/login/brands/blacksky.svg +3 -0
  71. package/dist/components/login/brands/bluesky.svg +3 -0
  72. package/dist/components/login/brands/bookhive.svg +3 -0
  73. package/dist/components/login/brands/graze.svg +3 -0
  74. package/dist/components/login/brands/leaflet.svg +3 -0
  75. package/dist/components/login/brands/pckt.svg +3 -0
  76. package/dist/components/login/brands/surf.svg +3 -0
  77. package/dist/components/login/brands/tangled.svg +3 -0
  78. package/dist/components/login/handle.d.ts +3 -0
  79. package/dist/components/login/handle.js +11 -0
  80. package/dist/components/login/permissions.d.ts +3 -0
  81. package/dist/components/login/permissions.js +13 -0
  82. package/dist/components/login/recent-accounts.d.ts +16 -0
  83. package/dist/components/login/recent-accounts.js +87 -0
  84. package/dist/components/login/search.d.ts +9 -0
  85. package/dist/components/login/search.js +47 -0
  86. package/dist/components/login/types.d.ts +27 -0
  87. package/dist/components/login/types.js +1 -0
  88. package/dist/confetti.d.ts +2 -0
  89. package/dist/confetti.js +13 -0
  90. package/dist/index.d.ts +52 -0
  91. package/dist/index.js +40 -0
  92. package/dist/palettes.d.ts +2 -0
  93. package/dist/palettes.js +30 -0
  94. package/dist/social/mention-suggestions.d.ts +35 -0
  95. package/dist/social/mention-suggestions.js +122 -0
  96. package/dist/social/posts.d.ts +95 -0
  97. package/dist/social/posts.js +59 -0
  98. package/dist/social/richtext.d.ts +25 -0
  99. package/dist/social/richtext.js +100 -0
  100. package/dist/styles.d.ts +2 -0
  101. package/dist/styles.js +3 -0
  102. package/dist/theme.css +499 -0
  103. package/dist/theme.d.ts +2 -0
  104. package/dist/theme.js +23 -0
  105. package/dist/utils.d.ts +2 -0
  106. package/dist/utils.js +6 -0
  107. package/package.json +108 -3
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import type { PostData } from '../social/posts.js';
3
+ import { richTextSegments, safeWebUrl } from '../social/richtext.js';
4
+ import LinkCard from './LinkCard.svelte';
5
+ import PostVideo from './PostVideo.svelte';
6
+ import PostContent from './PostContent.svelte';
7
+ let { post }: { post: PostData } = $props();
8
+ </script>
9
+
10
+ <p class="text-sm leading-relaxed break-words whitespace-pre-wrap text-ink">
11
+ {#each richTextSegments(post.text, post.facets) as segment, index (index)}{#if segment.href}<a
12
+ href={segment.href}
13
+ target="_blank"
14
+ rel="noopener noreferrer nofollow"
15
+ class="text-action underline decoration-action/40 underline-offset-2 hover:decoration-action"
16
+ >{segment.text}</a
17
+ >{:else}{segment.text}{/if}{/each}
18
+ </p>
19
+ {#each post.embeds ?? [] as embed, index (index)}
20
+ <div class="mt-3">
21
+ {#if embed.type === 'external'}<LinkCard
22
+ href={embed.uri}
23
+ meta={{ title: embed.title, description: embed.description, image: embed.image }}
24
+ />
25
+ {:else if embed.type === 'images'}<div
26
+ class="grid gap-1 overflow-hidden rounded-ui border border-line"
27
+ class:grid-cols-2={embed.images.length > 1}
28
+ >
29
+ {#each embed.images as image (image.src)}<a
30
+ href={safeWebUrl(image.fullsize ?? image.src)}
31
+ target="_blank"
32
+ rel="noopener noreferrer"
33
+ ><img
34
+ src={safeWebUrl(image.src)}
35
+ alt={image.alt}
36
+ loading="lazy"
37
+ width={image.width}
38
+ height={image.height}
39
+ class="max-h-80 w-full object-cover"
40
+ class:aspect-square={embed.images.length > 1}
41
+ /></a
42
+ >{/each}
43
+ </div>
44
+ {:else if embed.type === 'video'}<PostVideo
45
+ playlist={embed.playlist}
46
+ poster={embed.poster}
47
+ label={embed.alt}
48
+ href={post.href}
49
+ />
50
+ {:else if embed.type === 'quote'}<div class="rounded-ui border border-line p-3">
51
+ {#if embed.post}<a
52
+ href={safeWebUrl(embed.post.href)}
53
+ target="_blank"
54
+ rel="noopener noreferrer"
55
+ class="mb-2 block text-xs font-medium text-muted"
56
+ >{embed.post.author.name} · @{embed.post.author.handle}</a
57
+ ><PostContent post={embed.post} />{:else}<p class="text-xs text-muted">
58
+ Post unavailable
59
+ </p>{/if}
60
+ </div>{/if}
61
+ </div>
62
+ {/each}
@@ -0,0 +1,8 @@
1
+ import type { PostData } from '../social/posts.js';
2
+ import PostContent from './PostContent.svelte';
3
+ type $$ComponentProps = {
4
+ post: PostData;
5
+ };
6
+ declare const PostContent: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type PostContent = ReturnType<typeof PostContent>;
8
+ export default PostContent;
@@ -0,0 +1,74 @@
1
+ <script lang="ts">
2
+ import { safeWebUrl } from '../social/richtext.js';
3
+ import Icon from './Icon.svelte';
4
+ import { ArrowUpRight } from '@sketchyicons/svelte';
5
+ let {
6
+ playlist,
7
+ poster,
8
+ label,
9
+ href
10
+ }: { playlist: string; poster?: string; label?: string; href: string } = $props();
11
+ let video = $state<HTMLVideoElement | null>(null);
12
+ // Writable derived state resets a playback failure when the source changes.
13
+ let failed = $derived(!safeWebUrl(playlist));
14
+ $effect(() => {
15
+ const source = safeWebUrl(playlist),
16
+ media = video;
17
+ if (!source || !media || failed) return;
18
+ let cancelled = false;
19
+ let destroy: (() => void) | undefined;
20
+ if (media.canPlayType('application/vnd.apple.mpegurl')) media.src = source;
21
+ else
22
+ void import('hls.js')
23
+ .then(({ default: Hls }) => {
24
+ if (cancelled) return;
25
+ if (!Hls.isSupported()) {
26
+ failed = true;
27
+ return;
28
+ }
29
+ const hls = new Hls({ autoStartLoad: false });
30
+ hls.loadSource(source);
31
+ hls.attachMedia(media);
32
+ const play = () => hls.startLoad();
33
+ media.addEventListener('play', play);
34
+ hls.on(Hls.Events.ERROR, (_event, data) => {
35
+ if (data.fatal && !cancelled) failed = true;
36
+ });
37
+ destroy = () => {
38
+ media.removeEventListener('play', play);
39
+ hls.destroy();
40
+ };
41
+ })
42
+ .catch(() => {
43
+ if (!cancelled) failed = true;
44
+ });
45
+ return () => {
46
+ cancelled = true;
47
+ destroy?.();
48
+ media.pause();
49
+ media.removeAttribute('src');
50
+ media.load();
51
+ };
52
+ });
53
+ </script>
54
+
55
+ {#if !failed}
56
+ <!-- Bluesky's video view supplies alt text, but no caption track. -->
57
+ <video
58
+ bind:this={video}
59
+ poster={safeWebUrl(poster)}
60
+ aria-label={label || 'Bluesky video'}
61
+ controls
62
+ playsinline
63
+ preload="none"
64
+ class="max-h-96 w-full rounded-ui bg-black"
65
+ onerror={() => (failed = true)}
66
+ ></video>
67
+ {/if}
68
+ <a
69
+ href={safeWebUrl(href)}
70
+ target="_blank"
71
+ rel="noopener noreferrer"
72
+ class="mt-1 inline-flex items-center gap-1 text-xs text-action hover:underline"
73
+ >Watch on Bluesky <Icon img={ArrowUpRight} size={14} /></a
74
+ >
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ playlist: string;
3
+ poster?: string;
4
+ label?: string;
5
+ href: string;
6
+ };
7
+ declare const PostVideo: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type PostVideo = ReturnType<typeof PostVideo>;
9
+ export default PostVideo;
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import type { HTMLSelectAttributes } from 'svelte/elements';
3
+ import { cn } from '../utils.js';
4
+ import { control } from '../styles.js';
5
+ import Icon from './Icon.svelte';
6
+ import { ChevronDown } from '@sketchyicons/svelte';
7
+ let {
8
+ class: className,
9
+ value = $bindable<string | undefined>(),
10
+ ref = $bindable(null),
11
+ children,
12
+ ...rest
13
+ }: Omit<HTMLSelectAttributes, 'value' | 'multiple'> & {
14
+ value?: string;
15
+ multiple?: never;
16
+ ref?: HTMLSelectElement | null;
17
+ } = $props();
18
+ </script>
19
+
20
+ <span class="relative block">
21
+ <select
22
+ {...rest}
23
+ bind:value
24
+ bind:this={ref}
25
+ data-slot="select"
26
+ class={cn(control, 'cursor-pointer appearance-none pr-11', className)}
27
+ >{@render children?.()}</select
28
+ >
29
+ <Icon
30
+ img={ChevronDown}
31
+ size={17}
32
+ class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-muted"
33
+ />
34
+ </span>
@@ -0,0 +1,9 @@
1
+ import type { HTMLSelectAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = Omit<HTMLSelectAttributes, 'value' | 'multiple'> & {
3
+ value?: string;
4
+ multiple?: never;
5
+ ref?: HTMLSelectElement | null;
6
+ };
7
+ declare const Select: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
8
+ type Select = ReturnType<typeof Select>;
9
+ export default Select;
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { cn } from '../utils.js';
4
+ let {
5
+ class: className,
6
+ level = 2,
7
+ ref = $bindable(null),
8
+ children,
9
+ ...rest
10
+ }: HTMLAttributes<HTMLHeadingElement> & {
11
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
12
+ ref?: HTMLHeadingElement | null;
13
+ } = $props();
14
+ </script>
15
+
16
+ <svelte:element
17
+ this={`h${level}`}
18
+ {...rest}
19
+ bind:this={ref}
20
+ class={cn('m-0 text-lg leading-snug font-medium tracking-tight text-ink', className)}
21
+ >{@render children?.()}</svelte:element
22
+ >
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = HTMLAttributes<HTMLHeadingElement> & {
3
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
4
+ ref?: HTMLHeadingElement | null;
5
+ };
6
+ declare const Subheading: import("svelte").Component<$$ComponentProps, {}, "ref">;
7
+ type Subheading = ReturnType<typeof Subheading>;
8
+ export default Subheading;
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import { cn } from '../utils.js';
4
+ import { focus } from '../styles.js';
5
+ let {
6
+ class: className,
7
+ as = 'div',
8
+ variant = 'recessed',
9
+ ref = $bindable(null),
10
+ type,
11
+ disabled,
12
+ children,
13
+ ...rest
14
+ }: HTMLAttributes<HTMLElement> & {
15
+ as?: 'div' | 'section' | 'article' | 'aside' | 'main' | 'button';
16
+ type?: 'button' | 'submit' | 'reset';
17
+ disabled?: boolean;
18
+ variant?: 'recessed' | 'plain';
19
+ ref?: HTMLElement | null;
20
+ } = $props();
21
+ </script>
22
+
23
+ <svelte:element
24
+ this={as}
25
+ {...rest}
26
+ type={as === 'button' ? (type ?? 'button') : undefined}
27
+ disabled={as === 'button' ? disabled : undefined}
28
+ bind:this={ref}
29
+ data-slot="surface"
30
+ class={cn(
31
+ 'rounded-panel p-6 text-ink',
32
+ variant === 'recessed' && 'border border-line bg-recess shadow-(--ui-recess-shadow)',
33
+ as === 'button' &&
34
+ cn(
35
+ 'cursor-pointer touch-manipulation disabled:pointer-events-none disabled:opacity-45',
36
+ focus
37
+ ),
38
+ className
39
+ )}>{@render children?.()}</svelte:element
40
+ >
@@ -0,0 +1,11 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = HTMLAttributes<HTMLElement> & {
3
+ as?: 'div' | 'section' | 'article' | 'aside' | 'main' | 'button';
4
+ type?: 'button' | 'submit' | 'reset';
5
+ disabled?: boolean;
6
+ variant?: 'recessed' | 'plain';
7
+ ref?: HTMLElement | null;
8
+ };
9
+ declare const Surface: import("svelte").Component<$$ComponentProps, {}, "ref">;
10
+ type Surface = ReturnType<typeof Surface>;
11
+ export default Surface;
@@ -0,0 +1,47 @@
1
+ <script lang="ts" module>
2
+ export type TabItem = { value: string; label: string; disabled?: boolean };
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import type { Snippet } from 'svelte';
7
+ import { Tabs as Primitive } from 'bits-ui';
8
+ import { cn } from '../utils.js';
9
+ import { focus } from '../styles.js';
10
+ let {
11
+ items,
12
+ value = $bindable(''),
13
+ label,
14
+ class: className,
15
+ children,
16
+ ...rest
17
+ }: Omit<Primitive.RootProps, 'children' | 'child' | 'value'> & {
18
+ items: readonly TabItem[];
19
+ value?: string;
20
+ label: string;
21
+ children: Snippet<[string]>;
22
+ } = $props();
23
+ $effect(() => {
24
+ if (!items.some((item) => item.value === value && !item.disabled))
25
+ value = items.find((item) => !item.disabled)?.value ?? '';
26
+ });
27
+ </script>
28
+
29
+ <Primitive.Root {...rest} bind:value class={cn('min-w-0', className)}>
30
+ <Primitive.List
31
+ aria-label={label}
32
+ class="flex w-fit max-w-full flex-wrap items-center gap-1 rounded-[12px_14px_11px_13px] border border-line bg-recess p-1 shadow-[inset_0_2px_4px_var(--ui-shadow)]"
33
+ >
34
+ {#each items as item (item.value)}<Primitive.Trigger
35
+ value={item.value}
36
+ disabled={item.disabled}
37
+ class={cn(
38
+ 'cursor-pointer rounded-[8px_10px_7px_9px] px-3.5 py-1.75 text-[.8125rem] leading-normal text-muted hover:text-ink disabled:cursor-default disabled:opacity-45 data-[state=active]:bg-surface data-[state=active]:text-ink',
39
+ focus
40
+ )}>{item.label}</Primitive.Trigger
41
+ >{/each}
42
+ </Primitive.List>
43
+ {#each items as item (item.value)}<Primitive.Content
44
+ value={item.value}
45
+ class={cn('rounded pt-6', focus)}>{@render children(item.value)}</Primitive.Content
46
+ >{/each}
47
+ </Primitive.Root>
@@ -0,0 +1,16 @@
1
+ export type TabItem = {
2
+ value: string;
3
+ label: string;
4
+ disabled?: boolean;
5
+ };
6
+ import type { Snippet } from 'svelte';
7
+ import { Tabs as Primitive } from 'bits-ui';
8
+ type $$ComponentProps = Omit<Primitive.RootProps, 'children' | 'child' | 'value'> & {
9
+ items: readonly TabItem[];
10
+ value?: string;
11
+ label: string;
12
+ children: Snippet<[string]>;
13
+ };
14
+ declare const Tabs: import("svelte").Component<$$ComponentProps, {}, "value">;
15
+ type Tabs = ReturnType<typeof Tabs>;
16
+ export default Tabs;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
3
+ import { cn } from '../utils.js';
4
+ import { control } from '../styles.js';
5
+ let {
6
+ class: className,
7
+ value = $bindable(''),
8
+ ref = $bindable(null),
9
+ rows = 4,
10
+ ...rest
11
+ }: HTMLTextareaAttributes & { ref?: HTMLTextAreaElement | null } = $props();
12
+ </script>
13
+
14
+ <textarea
15
+ {...rest}
16
+ {rows}
17
+ bind:value
18
+ bind:this={ref}
19
+ data-slot="textarea"
20
+ class={cn(control, 'min-h-28 resize-y', className)}></textarea>
@@ -0,0 +1,7 @@
1
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = HTMLTextareaAttributes & {
3
+ ref?: HTMLTextAreaElement | null;
4
+ };
5
+ declare const Textarea: import("svelte").Component<$$ComponentProps, {}, "value" | "ref">;
6
+ type Textarea = ReturnType<typeof Textarea>;
7
+ export default Textarea;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import { ToggleGroup } from 'bits-ui';
3
+ import { cn } from '../utils.js';
4
+ let {
5
+ value = $bindable(''),
6
+ size = 'sm',
7
+ class: className,
8
+ children,
9
+ ...rest
10
+ }: Omit<
11
+ Extract<ToggleGroup.RootProps, { type: 'single' }>,
12
+ 'type' | 'value' | 'children' | 'child'
13
+ > & {
14
+ type?: 'single';
15
+ value?: string;
16
+ size?: 'xs' | 'sm';
17
+ children?: import('svelte').Snippet;
18
+ } = $props();
19
+ </script>
20
+
21
+ <ToggleGroup.Root
22
+ {...rest}
23
+ type="single"
24
+ bind:value
25
+ data-size={size}
26
+ class={cn(
27
+ 'inline-flex flex-wrap gap-1 rounded-ui bg-recess p-1 [box-shadow:var(--ui-recess-shadow)]',
28
+ className
29
+ )}
30
+ >
31
+ {@render children?.()}
32
+ </ToggleGroup.Root>
@@ -0,0 +1,12 @@
1
+ import { ToggleGroup } from 'bits-ui';
2
+ type $$ComponentProps = Omit<Extract<ToggleGroup.RootProps, {
3
+ type: 'single';
4
+ }>, 'type' | 'value' | 'children' | 'child'> & {
5
+ type?: 'single';
6
+ value?: string;
7
+ size?: 'xs' | 'sm';
8
+ children?: import('svelte').Snippet;
9
+ };
10
+ declare const ToggleGroup: import("svelte").Component<$$ComponentProps, {}, "value">;
11
+ type ToggleGroup = ReturnType<typeof ToggleGroup>;
12
+ export default ToggleGroup;
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import { ToggleGroup } from 'bits-ui';
3
+ import { cn } from '../utils.js';
4
+ import { focus } from '../styles.js';
5
+ let {
6
+ class: className,
7
+ size = 'sm',
8
+ children,
9
+ ...rest
10
+ }: Omit<ToggleGroup.ItemProps, 'children'> & {
11
+ size?: 'xs' | 'sm';
12
+ children?: import('svelte').Snippet;
13
+ } = $props();
14
+ </script>
15
+
16
+ <ToggleGroup.Item
17
+ {...rest}
18
+ class={cn(
19
+ 'cursor-pointer rounded-ui px-3 py-1.5 text-xs font-medium text-muted transition-colors duration-(--atmo-duration) hover:text-ink disabled:pointer-events-none disabled:opacity-45 data-[state=on]:bg-surface data-[state=on]:text-action motion-reduce:transition-none',
20
+ focus,
21
+ size === 'xs' && 'px-2 py-1 text-[.6875rem]',
22
+ className
23
+ )}
24
+ >
25
+ {@render children?.()}
26
+ </ToggleGroup.Item>
@@ -0,0 +1,8 @@
1
+ import { ToggleGroup } from 'bits-ui';
2
+ type $$ComponentProps = Omit<ToggleGroup.ItemProps, 'children'> & {
3
+ size?: 'xs' | 'sm';
4
+ children?: import('svelte').Snippet;
5
+ };
6
+ declare const ToggleGroupItem: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ToggleGroupItem = ReturnType<typeof ToggleGroupItem>;
8
+ export default ToggleGroupItem;
@@ -0,0 +1,82 @@
1
+ <script lang="ts">
2
+ import { tick, type Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ import { MediaQuery } from 'svelte/reactivity';
5
+ import Surface from './Surface.svelte';
6
+ import Modal from './Modal.svelte';
7
+ import { cn } from '../utils.js';
8
+ let {
9
+ label,
10
+ open = $bindable(false),
11
+ children,
12
+ class: className,
13
+ style,
14
+ ...rest
15
+ }: Omit<HTMLAttributes<HTMLElement>, 'children'> & {
16
+ label: string;
17
+ open?: boolean;
18
+ children: Snippet;
19
+ } = $props();
20
+ // Native null means “remove the attribute”; Bits UI accepts undefined instead.
21
+ const modalAttributes = $derived(
22
+ Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== null))
23
+ );
24
+ let desktop = $state<HTMLElement | null>(null);
25
+ const mobile = new MediaQuery('(width < 48rem)', false);
26
+ let drawerWasOpen = false;
27
+ $effect(() => {
28
+ const leavingDrawer = !mobile.current && drawerWasOpen;
29
+ drawerWasOpen = mobile.current && open;
30
+ if (!mobile.current) open = false;
31
+ if (leavingDrawer) {
32
+ void tick().then(() => {
33
+ if (!mobile.current)
34
+ desktop
35
+ ?.querySelector<HTMLElement>(
36
+ 'a[href], button:not(:disabled), input:not(:disabled), [tabindex="0"]'
37
+ )
38
+ ?.focus();
39
+ });
40
+ }
41
+ });
42
+ </script>
43
+
44
+ {#if !mobile.current}
45
+ <Surface
46
+ {...rest}
47
+ as="aside"
48
+ bind:ref={desktop}
49
+ {style}
50
+ aria-label={label}
51
+ data-slot="workspace-sidebar"
52
+ class={cn(
53
+ 'fixed top-[calc(var(--atmo-nav-top,0px)+12px)] bottom-3 left-3 z-40 w-[var(--atmo-sidebar-width,238px)] overflow-hidden rounded-[21px_18px_23px_19px] p-0 max-md:hidden',
54
+ className
55
+ )}
56
+ >
57
+ <nav aria-label={label} class="h-full overflow-y-auto overscroll-contain px-3 py-4">
58
+ {@render children()}
59
+ </nav>
60
+ </Surface>
61
+ {:else}
62
+ <Modal
63
+ {...modalAttributes}
64
+ bind:open
65
+ title={label}
66
+ onCloseAutoFocus={(event) => {
67
+ if (!mobile.current) {
68
+ event.preventDefault();
69
+ }
70
+ }}
71
+ style={style ?? undefined}
72
+ data-slot="workspace-sidebar"
73
+ class={cn(
74
+ 'top-[calc(var(--atmo-nav-top,0px)+12px)] bottom-3 left-3 flex h-auto max-h-none w-[min(280px,calc(100%-24px))] max-w-none translate-x-0 translate-y-0 flex-col overflow-hidden rounded-[21px_18px_23px_19px] bg-recess p-3 shadow-(--ui-recess-shadow) sm:p-3 [&>header]:mb-3 [&>header]:px-2 [&>header]:pt-2 [&>header_h2]:text-sm',
75
+ className
76
+ )}
77
+ >
78
+ <nav aria-label={label} class="min-h-0 flex-1 overflow-y-auto overscroll-contain">
79
+ {@render children()}
80
+ </nav>
81
+ </Modal>
82
+ {/if}
@@ -0,0 +1,10 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type $$ComponentProps = Omit<HTMLAttributes<HTMLElement>, 'children'> & {
4
+ label: string;
5
+ open?: boolean;
6
+ children: Snippet;
7
+ };
8
+ declare const WorkspaceSidebar: import("svelte").Component<$$ComponentProps, {}, "open">;
9
+ type WorkspaceSidebar = ReturnType<typeof WorkspaceSidebar>;
10
+ export default WorkspaceSidebar;