@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.
- package/README.md +27 -368
- package/dist/components/AccountMenu.svelte +102 -0
- package/dist/components/AccountMenu.svelte.d.ts +19 -0
- package/dist/components/Avatar.svelte +1 -1
- package/dist/components/Badge.svelte +3 -3
- package/dist/components/Button.svelte +5 -4
- package/dist/components/Checkbox.svelte +31 -1
- package/dist/{events → components}/DatePicker.svelte +3 -3
- package/dist/components/ExternalEmbed.svelte +10 -7
- package/dist/components/GrainAlbumPicker.svelte +18 -9
- package/dist/components/GrainAlbumView.svelte +15 -6
- package/dist/components/Heading.svelte +1 -1
- package/dist/components/IconRail.svelte +11 -9
- package/dist/components/InlineEditor.svelte +3 -3
- package/dist/components/InsetImage.svelte +1 -1
- package/dist/components/Label.svelte +1 -1
- package/dist/components/LinkCard.svelte +7 -5
- package/dist/components/Modal.svelte +3 -3
- package/dist/components/NestedComments.svelte +10 -7
- package/dist/components/PhotoGrid.svelte +1 -1
- package/dist/components/Popover.svelte +1 -1
- package/dist/components/PostComposer.svelte +2 -2
- package/dist/components/PostContent.svelte +12 -10
- package/dist/components/PostVideo.svelte +2 -2
- package/dist/components/RichText.svelte +19 -7
- package/dist/components/Select.svelte +5 -2
- package/dist/components/Select.svelte.d.ts +2 -0
- package/dist/components/SocialClientPicker.svelte +1 -1
- package/dist/components/StreamplacePicker.svelte +1 -1
- package/dist/components/Subheading.svelte +4 -2
- package/dist/components/Surface.svelte +3 -2
- package/dist/components/Tabs.svelte +2 -2
- package/dist/{events → components}/TimePicker.svelte +10 -7
- package/dist/{events → components}/TimezonePicker.svelte +15 -7
- package/dist/components/ToggleGroup.svelte +1 -1
- package/dist/components/ToggleGroupItem.svelte +1 -1
- package/dist/components/UserProfile.svelte +16 -9
- package/dist/components/WorkspaceSidebar.svelte +1 -1
- package/dist/components/legal/LegalDocument.svelte +9 -3
- package/dist/components/legal/LegalFooter.svelte +2 -2
- package/dist/components/legal/links.js +2 -9
- package/dist/components/login/ActorSearchField.svelte +10 -6
- package/dist/components/login/AtmosphereApps.svelte +1 -1
- package/dist/components/login/LoginForm.svelte +15 -9
- package/dist/components/login/PermissionFields.svelte +9 -2
- package/dist/components/login/RecentAccounts.svelte +5 -3
- package/dist/events/EventAttendees.svelte +9 -3
- package/dist/events/EventCard.svelte +7 -5
- package/dist/events/EventComments.svelte +3 -3
- package/dist/events/EventEditor.svelte +20 -9
- package/dist/events/EventRsvp.svelte +10 -6
- package/dist/events/EventView.svelte +21 -7
- package/dist/events/StarterPackButton.svelte +3 -3
- package/dist/events/ThemePicker.svelte +7 -7
- package/dist/events/ThumbnailPresets.svelte +1 -1
- package/dist/events/VodPlayer.svelte +1 -1
- package/dist/events/editor/LinksSection.svelte +7 -3
- package/dist/events/editor/LocationSection.svelte +1 -1
- package/dist/events/editor/RecurringModal.svelte +6 -4
- package/dist/events/editor/ThemeSection.svelte +3 -1
- package/dist/events/event-view/EventDateBlock.svelte +3 -3
- package/dist/events/event-view/EventHostedBy.svelte +6 -2
- package/dist/events/event-view/EventLinksList.svelte +13 -4
- package/dist/events/event-view/EventLocationBlock.svelte +9 -5
- package/dist/events/event-view/EventLocationMap.svelte +4 -2
- package/dist/events/event-view/ExternalRsvpNotice.svelte +20 -16
- package/dist/events/event-view/locations.js +1 -11
- package/dist/events/schedule/ConferenceTimetable.svelte +7 -7
- package/dist/events/schedule/DaySchedule.svelte +2 -2
- package/dist/events/schedule/ScheduleEventCell.svelte +2 -2
- package/dist/events/themes/Matrix.svelte +12 -6
- package/dist/events.d.ts +5 -5
- package/dist/events.js +6 -5
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/richtext/document.d.ts +2 -1
- package/dist/richtext/document.js +3 -13
- package/dist/social/external-embeds.js +1 -1
- package/dist/social/mention-suggestions.js +4 -4
- package/dist/social/profile.js +1 -1
- package/dist/styles.d.ts +2 -2
- package/dist/styles.js +2 -2
- package/dist/theme.css +23 -44
- package/dist/theme.js +4 -12
- package/dist/urls.d.ts +9 -0
- package/dist/urls.js +39 -0
- package/package.json +5 -1
- /package/dist/{events → components}/DatePicker.svelte.d.ts +0 -0
- /package/dist/{events → components}/DateTimePicker.svelte +0 -0
- /package/dist/{events → components}/DateTimePicker.svelte.d.ts +0 -0
- /package/dist/{events → components}/ImageDropper.svelte +0 -0
- /package/dist/{events → components}/ImageDropper.svelte.d.ts +0 -0
- /package/dist/{events → components}/TimePicker.svelte.d.ts +0 -0
- /package/dist/{events → components}/TimezonePicker.svelte.d.ts +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeHttpUrl as webUrl } from '../../urls.js';
|
|
1
2
|
function text(record, key) {
|
|
2
3
|
const value = record[key];
|
|
3
4
|
return typeof value === 'string' ? value.trim() || undefined : undefined;
|
|
@@ -20,17 +21,6 @@ function coordinateLabel({ lat, lng }) {
|
|
|
20
21
|
function mapsUrl(query) {
|
|
21
22
|
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(query)}`;
|
|
22
23
|
}
|
|
23
|
-
function webUrl(uri) {
|
|
24
|
-
try {
|
|
25
|
-
const url = new URL(uri);
|
|
26
|
-
if (url.protocol === 'https:' || url.protocol === 'http:')
|
|
27
|
-
return url.href;
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
// Invalid URLs render as non-interactive location text.
|
|
31
|
-
}
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
24
|
function formatLocation(value) {
|
|
35
25
|
if (!value || typeof value !== 'object')
|
|
36
26
|
return null;
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
{/if}
|
|
206
206
|
</div>
|
|
207
207
|
|
|
208
|
-
<p class="mb-4 text-sm text-
|
|
208
|
+
<p class="mb-4 text-sm text-base-600 dark:text-base-400">All times shown in {tzLabel}.</p>
|
|
209
209
|
|
|
210
210
|
{#if upNextAll.length > 0}
|
|
211
211
|
<div class="mb-8">
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
{ev.title}
|
|
236
236
|
</p>
|
|
237
237
|
{#if subtitle(ev)}
|
|
238
|
-
<p class="mt-0.5 truncate text-xs text-
|
|
238
|
+
<p class="mt-0.5 truncate text-xs text-base-600 dark:text-base-400">
|
|
239
239
|
{subtitle(ev)}
|
|
240
240
|
</p>
|
|
241
241
|
{/if}
|
|
@@ -243,12 +243,12 @@
|
|
|
243
243
|
<span
|
|
244
244
|
class="flex shrink-0 items-center gap-1.5 text-xs whitespace-nowrap {w.soon
|
|
245
245
|
? 'font-semibold text-accent-600 dark:text-accent-400'
|
|
246
|
-
: 'text-
|
|
246
|
+
: 'text-base-600 dark:text-base-400'}"
|
|
247
247
|
>
|
|
248
248
|
{#if rsvpStatuses[ev.uri] === 'going'}
|
|
249
|
-
<Icon img={Check} size={14} class="text-
|
|
249
|
+
<Icon img={Check} size={14} class="text-accent-700 dark:text-accent-300" />
|
|
250
250
|
{:else}
|
|
251
|
-
<Icon img={Star} size={14} class="text-
|
|
251
|
+
<Icon img={Star} size={14} class="text-accent-700 dark:text-accent-300" />
|
|
252
252
|
{/if}
|
|
253
253
|
{w.text}
|
|
254
254
|
</span>
|
|
@@ -273,7 +273,7 @@
|
|
|
273
273
|
<div class="mb-6 space-y-3">
|
|
274
274
|
{#if dayGroups.length > 1}
|
|
275
275
|
<div class="flex items-center gap-3">
|
|
276
|
-
<span class="w-14 text-xs text-
|
|
276
|
+
<span class="w-14 text-xs text-base-600 dark:text-base-400">Days</span>
|
|
277
277
|
<ToggleGroup
|
|
278
278
|
type="single"
|
|
279
279
|
bind:value={
|
|
@@ -293,7 +293,7 @@
|
|
|
293
293
|
{/if}
|
|
294
294
|
{#if loggedIn}
|
|
295
295
|
<div class="flex items-center gap-3">
|
|
296
|
-
<span class="w-14 text-xs text-
|
|
296
|
+
<span class="w-14 text-xs text-base-600 dark:text-base-400">Events</span>
|
|
297
297
|
<ToggleGroup
|
|
298
298
|
type="single"
|
|
299
299
|
bind:value={
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
onclick={() => (roomMenuOpen = false)}
|
|
98
98
|
></button>
|
|
99
99
|
<ul
|
|
100
|
-
class="absolute inset-x-0 top-full z-[10000] max-h-64 overflow-auto border-b border-
|
|
100
|
+
class="absolute inset-x-0 top-full z-[10000] max-h-64 overflow-auto border-b border-base-300 bg-base-50 dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))] dark:bg-base-925"
|
|
101
101
|
>
|
|
102
102
|
{#each rooms as room, i (room)}
|
|
103
103
|
<li>
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
>
|
|
210
210
|
{#each rooms as room (room)}
|
|
211
211
|
<div
|
|
212
|
-
class="px-2 py-2.5 text-center text-xs font-semibold tracking-wide text-
|
|
212
|
+
class="px-2 py-2.5 text-center text-xs font-semibold tracking-wide text-base-600 uppercase dark:text-base-400"
|
|
213
213
|
>
|
|
214
214
|
{room}
|
|
215
215
|
</div>
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<Modal bind:open={modalOpen} title={event.title}>
|
|
90
90
|
<div class="overflow-hidden">
|
|
91
91
|
{#if event.start}
|
|
92
|
-
<p class="mt-1 text-sm text-
|
|
92
|
+
<p class="mt-1 text-sm text-base-600 dark:text-base-400">
|
|
93
93
|
{formatTime(event.start, tz)}{event.end ? ` – ${formatTime(event.end, tz)}` : ''}
|
|
94
94
|
</p>
|
|
95
95
|
{/if}
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
{/if}
|
|
108
108
|
|
|
109
109
|
{#if event.description}
|
|
110
|
-
<p class="mt-3 line-clamp-3 text-sm break-words text-
|
|
110
|
+
<p class="mt-3 line-clamp-3 text-sm break-words text-base-600 dark:text-base-400">
|
|
111
111
|
{event.description}
|
|
112
112
|
</p>
|
|
113
113
|
{/if}
|
|
@@ -37,23 +37,28 @@
|
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
const accentColor = getComputedStyle(document.documentElement)
|
|
40
|
-
.getPropertyValue('--
|
|
40
|
+
.getPropertyValue('--accent-500')
|
|
41
41
|
.trim();
|
|
42
42
|
|
|
43
|
+
const root = document.documentElement;
|
|
44
|
+
const systemScheme = window.matchMedia('(prefers-color-scheme: dark)');
|
|
43
45
|
let bgColor = '';
|
|
44
46
|
function readBgColor() {
|
|
45
|
-
const isDark =
|
|
46
|
-
|
|
47
|
-
.
|
|
47
|
+
const isDark =
|
|
48
|
+
root.matches('.dark, [data-theme="dark"]') ||
|
|
49
|
+
(!root.matches('.light, [data-theme="light"]') && systemScheme.matches);
|
|
50
|
+
bgColor = getComputedStyle(root)
|
|
51
|
+
.getPropertyValue(isDark ? '--base-900' : '--base-50')
|
|
48
52
|
.trim();
|
|
49
53
|
}
|
|
50
54
|
readBgColor();
|
|
51
55
|
|
|
52
56
|
const themeObserver = new MutationObserver(readBgColor);
|
|
53
|
-
themeObserver.observe(
|
|
57
|
+
themeObserver.observe(root, {
|
|
54
58
|
attributes: true,
|
|
55
|
-
attributeFilter: ['class']
|
|
59
|
+
attributeFilter: ['class', 'data-theme', 'style']
|
|
56
60
|
});
|
|
61
|
+
systemScheme.addEventListener('change', readBgColor);
|
|
57
62
|
|
|
58
63
|
let lastResize = canvas.width;
|
|
59
64
|
let lastTime = performance.now();
|
|
@@ -144,6 +149,7 @@
|
|
|
144
149
|
cancelAnimationFrame(animId);
|
|
145
150
|
window.removeEventListener('resize', resize);
|
|
146
151
|
themeObserver.disconnect();
|
|
152
|
+
systemScheme.removeEventListener('change', readBgColor);
|
|
147
153
|
};
|
|
148
154
|
});
|
|
149
155
|
</script>
|
package/dist/events.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export { default as VodPlayer } from './events/VodPlayer.svelte';
|
|
|
12
12
|
export type { VodPlayerApi } from './events/VodPlayer.svelte';
|
|
13
13
|
export { default as VodTranscript } from './events/VodTranscript.svelte';
|
|
14
14
|
export { default as ConferenceTimetable } from './events/schedule/ConferenceTimetable.svelte';
|
|
15
|
-
export { default as DatePicker } from './
|
|
16
|
-
export { default as TimePicker } from './
|
|
17
|
-
export { default as DateTimePicker } from './
|
|
18
|
-
export { default as TimezonePicker } from './
|
|
19
|
-
export { default as ImageDropper } from './
|
|
15
|
+
export { default as DatePicker } from './components/DatePicker.svelte';
|
|
16
|
+
export { default as TimePicker } from './components/TimePicker.svelte';
|
|
17
|
+
export { default as DateTimePicker } from './components/DateTimePicker.svelte';
|
|
18
|
+
export { default as TimezonePicker } from './components/TimezonePicker.svelte';
|
|
19
|
+
export { default as ImageDropper } from './components/ImageDropper.svelte';
|
package/dist/events.js
CHANGED
|
@@ -10,8 +10,9 @@ export { default as StarterPackButton } from './events/StarterPackButton.svelte'
|
|
|
10
10
|
export { default as VodPlayer } from './events/VodPlayer.svelte';
|
|
11
11
|
export { default as VodTranscript } from './events/VodTranscript.svelte';
|
|
12
12
|
export { default as ConferenceTimetable } from './events/schedule/ConferenceTimetable.svelte';
|
|
13
|
-
|
|
14
|
-
export { default as
|
|
15
|
-
export { default as
|
|
16
|
-
export { default as
|
|
17
|
-
export { default as
|
|
13
|
+
// Compatibility aliases; general-purpose controls are owned by the main Kit entry.
|
|
14
|
+
export { default as DatePicker } from './components/DatePicker.svelte';
|
|
15
|
+
export { default as TimePicker } from './components/TimePicker.svelte';
|
|
16
|
+
export { default as DateTimePicker } from './components/DateTimePicker.svelte';
|
|
17
|
+
export { default as TimezonePicker } from './components/TimezonePicker.svelte';
|
|
18
|
+
export { default as ImageDropper } from './components/ImageDropper.svelte';
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,11 @@ export { default as Input } from './components/Input.svelte';
|
|
|
5
5
|
export type { InputProps } from './components/Input.svelte';
|
|
6
6
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
7
7
|
export { default as Select } from './components/Select.svelte';
|
|
8
|
+
export { default as DatePicker } from './components/DatePicker.svelte';
|
|
9
|
+
export { default as TimePicker } from './components/TimePicker.svelte';
|
|
10
|
+
export { default as DateTimePicker } from './components/DateTimePicker.svelte';
|
|
11
|
+
export { default as TimezonePicker } from './components/TimezonePicker.svelte';
|
|
12
|
+
export { default as ImageDropper } from './components/ImageDropper.svelte';
|
|
8
13
|
export { default as SocialClientPicker } from './components/SocialClientPicker.svelte';
|
|
9
14
|
export type { SocialClientPickerProps } from './components/SocialClientPicker.svelte';
|
|
10
15
|
export * from './social.js';
|
|
@@ -54,6 +59,7 @@ export type { IconNode, SketchyIconProps } from '@sketchyicons/svelte';
|
|
|
54
59
|
export { default as IconRail } from './components/IconRail.svelte';
|
|
55
60
|
export type { IconRailItem } from './components/IconRail.svelte';
|
|
56
61
|
export { default as WorkspaceSidebar } from './components/WorkspaceSidebar.svelte';
|
|
62
|
+
export { default as AccountMenu } from './components/AccountMenu.svelte';
|
|
57
63
|
export { default as LoginForm } from './components/login/LoginForm.svelte';
|
|
58
64
|
export type { LoginFormProps } from './components/login/LoginForm.svelte';
|
|
59
65
|
export { default as LoginDialog } from './components/login/LoginDialog.svelte';
|
|
@@ -76,6 +82,7 @@ export { default as InlineEditor } from './components/InlineEditor.svelte';
|
|
|
76
82
|
export { default as PostComposer } from './components/PostComposer.svelte';
|
|
77
83
|
export { default as NestedComments } from './components/NestedComments.svelte';
|
|
78
84
|
export { default as PostContent } from './components/PostContent.svelte';
|
|
85
|
+
export { safeHttpUrl, safeLinkHref } from './urls.js';
|
|
79
86
|
export { default as RichText } from './components/RichText.svelte';
|
|
80
87
|
export type { RichTextProps } from './components/RichText.svelte';
|
|
81
88
|
export type { RichTextDocument, RichTextLinkOptions } from './richtext/document.js';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,11 @@ export { default as IconButton } from './components/IconButton.svelte';
|
|
|
3
3
|
export { default as Input } from './components/Input.svelte';
|
|
4
4
|
export { default as Textarea } from './components/Textarea.svelte';
|
|
5
5
|
export { default as Select } from './components/Select.svelte';
|
|
6
|
+
export { default as DatePicker } from './components/DatePicker.svelte';
|
|
7
|
+
export { default as TimePicker } from './components/TimePicker.svelte';
|
|
8
|
+
export { default as DateTimePicker } from './components/DateTimePicker.svelte';
|
|
9
|
+
export { default as TimezonePicker } from './components/TimezonePicker.svelte';
|
|
10
|
+
export { default as ImageDropper } from './components/ImageDropper.svelte';
|
|
6
11
|
export { default as SocialClientPicker } from './components/SocialClientPicker.svelte';
|
|
7
12
|
export * from './social.js';
|
|
8
13
|
export { default as Checkbox } from './components/Checkbox.svelte';
|
|
@@ -34,6 +39,7 @@ export { default as Tabs } from './components/Tabs.svelte';
|
|
|
34
39
|
export { default as Icon } from './components/Icon.svelte';
|
|
35
40
|
export { default as IconRail } from './components/IconRail.svelte';
|
|
36
41
|
export { default as WorkspaceSidebar } from './components/WorkspaceSidebar.svelte';
|
|
42
|
+
export { default as AccountMenu } from './components/AccountMenu.svelte';
|
|
37
43
|
export { default as LoginForm } from './components/login/LoginForm.svelte';
|
|
38
44
|
export { default as LoginDialog } from './components/login/LoginDialog.svelte';
|
|
39
45
|
export { default as SignupPermissions } from './components/login/SignupPermissions.svelte';
|
|
@@ -51,6 +57,7 @@ export { default as InlineEditor } from './components/InlineEditor.svelte';
|
|
|
51
57
|
export { default as PostComposer } from './components/PostComposer.svelte';
|
|
52
58
|
export { default as NestedComments } from './components/NestedComments.svelte';
|
|
53
59
|
export { default as PostContent } from './components/PostContent.svelte';
|
|
60
|
+
export { safeHttpUrl, safeLinkHref } from './urls.js';
|
|
54
61
|
export { default as RichText } from './components/RichText.svelte';
|
|
55
62
|
export { default as ExternalEmbed } from './components/ExternalEmbed.svelte';
|
|
56
63
|
export { defaultExternalEmbedProviders, youtubeEmbedProvider, klipyEmbedProvider, resolveExternalEmbed } from './social/external-embeds.js';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { PreferredSocialClient } from '../social/preferred-client.js';
|
|
2
|
+
import { safeHttpUrl as safeWebUrl } from '../urls.js';
|
|
3
|
+
export { safeWebUrl };
|
|
2
4
|
/**
|
|
3
5
|
* Parsed rich text. It is plain JSON, so it can be produced on the server and sent in page
|
|
4
6
|
* data. Renderers still treat it as untrusted and validate every node and destination.
|
|
@@ -61,7 +63,6 @@ export type RichTextLinkOptions = {
|
|
|
61
63
|
/** App-owned mention destination; defaults to the selected social client. */
|
|
62
64
|
mentionUrl?: (identifier: string) => string;
|
|
63
65
|
};
|
|
64
|
-
export declare function safeWebUrl(url: unknown): string | undefined;
|
|
65
66
|
/** Only app-relative paths or HTTP(S), including for app-owned mention callbacks. */
|
|
66
67
|
export declare function mentionHref(actor: unknown, options: RichTextLinkOptions): string | undefined;
|
|
67
68
|
export declare function tagHref(tag: unknown, options: RichTextLinkOptions): string | undefined;
|
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
import { isActorIdentifier } from '@atcute/lexicons/syntax';
|
|
2
2
|
import { socialHashtagUrl, socialProfileUrl } from '../social/urls.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
const parsed = new URL(url);
|
|
8
|
-
return ['https:', 'http:'].includes(parsed.protocol) && !parsed.username && !parsed.password
|
|
9
|
-
? parsed.href
|
|
10
|
-
: undefined;
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
3
|
+
import { safeHttpUrl as safeWebUrl } from '../urls.js';
|
|
4
|
+
// Backwards-compatible export for existing richtext and social consumers.
|
|
5
|
+
export { safeWebUrl };
|
|
16
6
|
/** Only app-relative paths or HTTP(S), including for app-owned mention callbacks. */
|
|
17
7
|
export function mentionHref(actor, options) {
|
|
18
8
|
if (typeof actor !== 'string' || !isActorIdentifier(actor))
|
|
@@ -31,7 +31,7 @@ export function mentionSuggestions(search, host) {
|
|
|
31
31
|
empty.role = 'option';
|
|
32
32
|
empty.setAttribute('aria-disabled', 'true');
|
|
33
33
|
empty.setAttribute('aria-selected', 'false');
|
|
34
|
-
empty.className = 'p-3 text-xs text-
|
|
34
|
+
empty.className = 'p-3 text-xs text-base-600 dark:text-base-400';
|
|
35
35
|
empty.textContent = 'No accounts found';
|
|
36
36
|
menu.append(empty);
|
|
37
37
|
}
|
|
@@ -42,12 +42,12 @@ export function mentionSuggestions(search, host) {
|
|
|
42
42
|
option.id = `${menu.id}-${index}`;
|
|
43
43
|
option.setAttribute('aria-selected', String(index === selected));
|
|
44
44
|
option.className =
|
|
45
|
-
'block w-full rounded-ui px-3 py-2 text-left text-xs text-
|
|
45
|
+
'block w-full rounded-ui px-3 py-2 text-left text-xs text-base-950 dark:text-base-50 hover:bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] dark:hover:bg-base-950 aria-selected:bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] dark:aria-selected:bg-base-950';
|
|
46
46
|
const name = document.createElement('strong');
|
|
47
47
|
name.className = 'block truncate font-medium';
|
|
48
48
|
name.textContent = actor.displayName || actor.handle;
|
|
49
49
|
const handle = document.createElement('span');
|
|
50
|
-
handle.className = 'block truncate text-
|
|
50
|
+
handle.className = 'block truncate text-base-600 dark:text-base-400';
|
|
51
51
|
handle.textContent = `@${actor.handle}`;
|
|
52
52
|
option.append(name, handle);
|
|
53
53
|
option.onmousedown = (event) => event.preventDefault();
|
|
@@ -84,7 +84,7 @@ export function mentionSuggestions(search, host) {
|
|
|
84
84
|
menu.role = 'listbox';
|
|
85
85
|
menu.setAttribute('aria-label', 'Mention suggestions');
|
|
86
86
|
menu.className =
|
|
87
|
-
'absolute z-60 w-70 max-w-full overflow-hidden rounded-ui border border-
|
|
87
|
+
'absolute z-60 w-70 max-w-full overflow-hidden rounded-ui border border-base-300 dark:border-[color-mix(in_srgb,var(--base-800)_80%,var(--base-950))] bg-base-50 dark:bg-base-925 p-1 font-sans text-base-950 dark:text-base-50';
|
|
88
88
|
host().append(menu);
|
|
89
89
|
draw();
|
|
90
90
|
},
|
package/dist/social/profile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { detectTokens } from '../richtext/detect.js';
|
|
2
|
-
import { safeWebUrl } from '
|
|
2
|
+
import { safeHttpUrl as safeWebUrl } from '../urls.js';
|
|
3
3
|
import { socialHashtagUrl, socialProfileUrl } from './urls.js';
|
|
4
4
|
/** Only web URLs; never accept executable schemes or disguised credentials. */
|
|
5
5
|
export function safeProfileUrl(value) {
|
package/dist/styles.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const focus = "focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-focus";
|
|
2
|
-
export declare const control = "block min-h-11 w-full min-w-0 rounded-ui border border-
|
|
1
|
+
export declare const focus = "focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-accent-700 dark:focus-visible:outline-accent-300";
|
|
2
|
+
export declare const control = "block min-h-11 w-full min-w-0 rounded-ui border border-base-300 dark:border-base-800 bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] dark:bg-base-975 px-3 py-2.5 text-base leading-normal text-base-950 dark:text-base-50 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:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002] placeholder:text-base-600 dark:placeholder:text-base-400 transition-colors duration-(--atmo-duration) motion-reduce:transition-none hover:border-base-400 dark:hover:border-base-700 focus:ring-0 focus:ring-offset-0 focus-visible:border-accent-700 dark:focus-visible:border-accent-300 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-accent-700 dark:focus-visible:outline-accent-300 disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:border-base-300 dark:disabled:hover:border-base-800 aria-invalid:border-accent-700 dark:aria-invalid:border-accent-300 aria-invalid:hover:border-accent-700 dark:aria-invalid:hover:border-accent-300 sm:text-sm";
|
package/dist/styles.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Shared Tailwind recipes, not an additional component stylesheet.
|
|
2
|
-
export const focus = 'focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-focus';
|
|
3
|
-
export const control = 'block min-h-11 w-full min-w-0 rounded-ui border border-
|
|
2
|
+
export const focus = 'focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-accent-700 dark:focus-visible:outline-accent-300';
|
|
3
|
+
export const control = 'block min-h-11 w-full min-w-0 rounded-ui border border-base-300 dark:border-base-800 bg-[color-mix(in_srgb,var(--base-100)_75%,var(--base-200))] dark:bg-base-975 px-3 py-2.5 text-base leading-normal text-base-950 dark:text-base-50 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:shadow-[inset_0_2px_4px_#0006,inset_0_5px_12px_#0002] placeholder:text-base-600 dark:placeholder:text-base-400 transition-colors duration-(--atmo-duration) motion-reduce:transition-none hover:border-base-400 dark:hover:border-base-700 focus:ring-0 focus:ring-offset-0 focus-visible:border-accent-700 dark:focus-visible:border-accent-300 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-accent-700 dark:focus-visible:outline-accent-300 disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:border-base-300 dark:disabled:hover:border-base-800 aria-invalid:border-accent-700 dark:aria-invalid:border-accent-300 aria-invalid:hover:border-accent-700 dark:aria-invalid:hover:border-accent-300 sm:text-sm';
|
package/dist/theme.css
CHANGED
|
@@ -4,6 +4,18 @@
|
|
|
4
4
|
/* Resolve relative to this stylesheet in src/lib or the packaged dist directory. */
|
|
5
5
|
@source './';
|
|
6
6
|
|
|
7
|
+
/* Explicit document modes win; without one, follow the browser preference. */
|
|
8
|
+
@custom-variant dark {
|
|
9
|
+
&:where(.dark, .dark *, [data-theme='dark'], [data-theme='dark'] *) {
|
|
10
|
+
@slot;
|
|
11
|
+
}
|
|
12
|
+
@media (prefers-color-scheme: dark) {
|
|
13
|
+
&:not(:where(.light, .light *, [data-theme='light'], [data-theme='light'] *)) {
|
|
14
|
+
@slot;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
@theme inline {
|
|
8
20
|
--color-base-50: var(--base-50);
|
|
9
21
|
--color-base-100: var(--base-100);
|
|
@@ -15,7 +27,9 @@
|
|
|
15
27
|
--color-base-700: var(--base-700);
|
|
16
28
|
--color-base-800: var(--base-800);
|
|
17
29
|
--color-base-900: var(--base-900);
|
|
30
|
+
--color-base-925: var(--base-925);
|
|
18
31
|
--color-base-950: var(--base-950);
|
|
32
|
+
--color-base-975: var(--base-975);
|
|
19
33
|
--color-accent-50: var(--accent-50);
|
|
20
34
|
--color-accent-100: var(--accent-100);
|
|
21
35
|
--color-accent-200: var(--accent-200);
|
|
@@ -26,19 +40,9 @@
|
|
|
26
40
|
--color-accent-700: var(--accent-700);
|
|
27
41
|
--color-accent-800: var(--accent-800);
|
|
28
42
|
--color-accent-900: var(--accent-900);
|
|
43
|
+
--color-accent-925: var(--accent-925);
|
|
29
44
|
--color-accent-950: var(--accent-950);
|
|
30
|
-
--color-
|
|
31
|
-
--color-recess: var(--ui-recess);
|
|
32
|
-
--color-surface: var(--ui-surface);
|
|
33
|
-
--color-ink: var(--ui-ink);
|
|
34
|
-
--color-muted: var(--ui-muted);
|
|
35
|
-
--color-line: var(--ui-line);
|
|
36
|
-
--color-control-line: var(--ui-control-line);
|
|
37
|
-
--color-control-hover: var(--ui-control-hover);
|
|
38
|
-
--color-field: var(--ui-field);
|
|
39
|
-
--color-action: var(--ui-action);
|
|
40
|
-
--color-accent-soft: var(--ui-accent-soft);
|
|
41
|
-
--color-focus: var(--ui-focus);
|
|
45
|
+
--color-accent-975: var(--accent-975);
|
|
42
46
|
--font-sans: var(--atmo-font-sans);
|
|
43
47
|
--radius-ui: var(--atmo-radius-control);
|
|
44
48
|
--radius-panel: var(--atmo-radius-panel);
|
|
@@ -82,9 +86,8 @@
|
|
|
82
86
|
color-scheme: light;
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
/*
|
|
86
|
-
|
|
87
|
-
Add atmo-theme to a custom-ramp boundary to get the same behavior. */
|
|
89
|
+
/* Derive the extra shades at palette boundaries, including custom ramps
|
|
90
|
+
marked with atmo-theme. Ratios preserve the existing page/field colors. */
|
|
88
91
|
:where(
|
|
89
92
|
:root,
|
|
90
93
|
.atmo,
|
|
@@ -118,38 +121,14 @@
|
|
|
118
121
|
.pink,
|
|
119
122
|
.rose
|
|
120
123
|
) {
|
|
121
|
-
--
|
|
122
|
-
--
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
);
|
|
126
|
-
--ui-surface: light-dark(var(--base-50), var(--base-900));
|
|
127
|
-
--ui-ink: light-dark(var(--base-950), var(--base-50));
|
|
128
|
-
--ui-muted: light-dark(var(--base-600), var(--base-400));
|
|
129
|
-
--ui-line: light-dark(
|
|
130
|
-
var(--base-300),
|
|
131
|
-
color-mix(in srgb, var(--base-800) 80%, var(--base-950))
|
|
132
|
-
);
|
|
133
|
-
--ui-field: light-dark(
|
|
134
|
-
color-mix(in srgb, var(--base-100) 75%, var(--base-200)),
|
|
135
|
-
color-mix(in srgb, var(--base-950) 80%, black)
|
|
136
|
-
);
|
|
137
|
-
--ui-control-line: light-dark(var(--base-300), var(--base-800));
|
|
138
|
-
--ui-control-hover: light-dark(var(--base-400), var(--base-700));
|
|
139
|
-
--ui-action: light-dark(var(--accent-700), var(--accent-300));
|
|
140
|
-
--ui-accent-soft: light-dark(
|
|
141
|
-
var(--accent-50),
|
|
142
|
-
color-mix(in srgb, var(--accent-950) 65%, var(--base-950))
|
|
143
|
-
);
|
|
144
|
-
--ui-focus: light-dark(var(--accent-700), var(--accent-300));
|
|
145
|
-
--ui-shadow: light-dark(color-mix(in srgb, var(--base-950) 8%, transparent), #0006);
|
|
146
|
-
--ui-shadow-soft: light-dark(color-mix(in srgb, var(--base-950) 2%, transparent), #0002);
|
|
147
|
-
--ui-recess-shadow: inset 0 2px 4px var(--ui-shadow), inset 0 5px 12px var(--ui-shadow-soft);
|
|
124
|
+
--base-925: color-mix(in srgb, var(--base-900) 50%, var(--base-950));
|
|
125
|
+
--base-975: color-mix(in srgb, var(--base-950) 80%, black);
|
|
126
|
+
--accent-925: color-mix(in srgb, var(--accent-900) 50%, var(--accent-950));
|
|
127
|
+
--accent-975: color-mix(in srgb, var(--accent-950) 80%, black);
|
|
148
128
|
}
|
|
149
129
|
.atmo {
|
|
150
130
|
font-family: var(--atmo-font-sans);
|
|
151
|
-
|
|
152
|
-
background: var(--ui-page);
|
|
131
|
+
@apply bg-base-50 text-base-950 dark:bg-base-925 dark:text-base-50;
|
|
153
132
|
-webkit-font-smoothing: antialiased;
|
|
154
133
|
}
|
|
155
134
|
.gray {
|
package/dist/theme.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// Portals
|
|
2
|
-
//
|
|
1
|
+
// Portals inherit the document's light/dark mode normally. Only carry scoped
|
|
2
|
+
// palettes and typography, which would otherwise be lost outside their ancestry.
|
|
3
3
|
export function readTheme(element) {
|
|
4
4
|
const style = getComputedStyle(element);
|
|
5
|
-
const variables = Array.from(style).filter((name) => ['--base-', '--accent-', '--
|
|
5
|
+
const variables = Array.from(style).filter((name) => ['--base-', '--accent-', '--atmo-'].some((prefix) => name.startsWith(prefix)));
|
|
6
6
|
return [
|
|
7
|
-
`color-scheme:${style.colorScheme}`,
|
|
8
7
|
`font-family:${style.fontFamily}`,
|
|
9
8
|
...variables.map((name) => `${name}:${style.getPropertyValue(name)}`)
|
|
10
9
|
].join(';');
|
|
@@ -19,12 +18,5 @@ export function observeTheme(element, update) {
|
|
|
19
18
|
});
|
|
20
19
|
parent = parent.parentElement;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
// change with the system scheme even when no ancestor attribute changes.
|
|
24
|
-
const scheme = window.matchMedia('(prefers-color-scheme: dark)');
|
|
25
|
-
scheme.addEventListener('change', update);
|
|
26
|
-
return () => {
|
|
27
|
-
observer.disconnect();
|
|
28
|
-
scheme.removeEventListener('change', update);
|
|
29
|
-
};
|
|
21
|
+
return () => observer.disconnect();
|
|
30
22
|
}
|
package/dist/urls.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Untrusted external destinations: absolute HTTP(S) URLs without credentials.
|
|
2
|
+
* This validates browser destinations, not hosts for server-side fetching (SSRF).
|
|
3
|
+
*/
|
|
4
|
+
export declare function safeHttpUrl(value: unknown): string | undefined;
|
|
5
|
+
/** Navigation also supports root-relative app paths, fragments and mail/phone actions.
|
|
6
|
+
* Use safeHttpUrl instead when a record field must contain an external website.
|
|
7
|
+
* Invalid destinations return undefined so renderers omit href rather than falling back to it.
|
|
8
|
+
*/
|
|
9
|
+
export declare function safeLinkHref(value: unknown): string | undefined;
|
package/dist/urls.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Untrusted external destinations: absolute HTTP(S) URLs without credentials.
|
|
2
|
+
* This validates browser destinations, not hosts for server-side fetching (SSRF).
|
|
3
|
+
*/
|
|
4
|
+
export function safeHttpUrl(value) {
|
|
5
|
+
if (typeof value !== 'string' || !value)
|
|
6
|
+
return undefined;
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(value);
|
|
9
|
+
return (url.protocol === 'https:' || url.protocol === 'http:') && !url.username && !url.password
|
|
10
|
+
? url.href
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Navigation also supports root-relative app paths, fragments and mail/phone actions.
|
|
18
|
+
* Use safeHttpUrl instead when a record field must contain an external website.
|
|
19
|
+
* Invalid destinations return undefined so renderers omit href rather than falling back to it.
|
|
20
|
+
*/
|
|
21
|
+
export function safeLinkHref(value) {
|
|
22
|
+
const http = safeHttpUrl(value);
|
|
23
|
+
if (http)
|
|
24
|
+
return http;
|
|
25
|
+
if (typeof value !== 'string' || !value)
|
|
26
|
+
return undefined;
|
|
27
|
+
// Never normalize an ambiguous local/contact link into a different destination.
|
|
28
|
+
if (Array.from(value).some((c) => c === '\\' || c.charCodeAt(0) <= 0x20 || c.charCodeAt(0) === 0x7f))
|
|
29
|
+
return undefined;
|
|
30
|
+
if (value.startsWith('#') || (value.startsWith('/') && !value.startsWith('//')))
|
|
31
|
+
return value;
|
|
32
|
+
try {
|
|
33
|
+
const url = new URL(value);
|
|
34
|
+
return url.protocol === 'mailto:' || url.protocol === 'tel:' ? url.href : undefined;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atmo-dev/kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "The shared UI foundation for Atmo apps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"types": "./dist/media.d.ts",
|
|
57
57
|
"default": "./dist/media.js"
|
|
58
58
|
},
|
|
59
|
+
"./urls": {
|
|
60
|
+
"types": "./dist/urls.d.ts",
|
|
61
|
+
"default": "./dist/urls.js"
|
|
62
|
+
},
|
|
59
63
|
"./richtext": {
|
|
60
64
|
"types": "./dist/richtext.d.ts",
|
|
61
65
|
"default": "./dist/richtext.js"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|