@ewanc26/svelte-standard-site 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +108 -0
- package/dist/__tests__/content.test.d.ts +4 -0
- package/dist/__tests__/content.test.js +128 -0
- package/dist/client.d.ts +71 -0
- package/dist/client.js +307 -0
- package/dist/components/Comments.svelte +277 -0
- package/dist/components/Comments.svelte.d.ts +17 -0
- package/dist/components/DocumentCard.svelte +95 -0
- package/dist/components/DocumentCard.svelte.d.ts +11 -0
- package/dist/components/PublicationCard.svelte +54 -0
- package/dist/components/PublicationCard.svelte.d.ts +9 -0
- package/dist/components/StandardSiteLayout.svelte +102 -0
- package/dist/components/StandardSiteLayout.svelte.d.ts +18 -0
- package/dist/components/ThemeToggle.svelte +55 -0
- package/dist/components/ThemeToggle.svelte.d.ts +6 -0
- package/dist/components/common/DateDisplay.svelte +38 -0
- package/dist/components/common/DateDisplay.svelte.d.ts +11 -0
- package/dist/components/common/TagList.svelte +31 -0
- package/dist/components/common/TagList.svelte.d.ts +8 -0
- package/dist/components/common/ThemedCard.svelte +65 -0
- package/dist/components/common/ThemedCard.svelte.d.ts +11 -0
- package/dist/components/common/ThemedContainer.svelte +55 -0
- package/dist/components/common/ThemedContainer.svelte.d.ts +11 -0
- package/dist/components/common/ThemedText.svelte +75 -0
- package/dist/components/common/ThemedText.svelte.d.ts +11 -0
- package/dist/components/document/BlockRenderer.svelte +67 -0
- package/dist/components/document/BlockRenderer.svelte.d.ts +9 -0
- package/dist/components/document/CanvasRenderer.svelte +41 -0
- package/dist/components/document/CanvasRenderer.svelte.d.ts +22 -0
- package/dist/components/document/DocumentRenderer.svelte +68 -0
- package/dist/components/document/DocumentRenderer.svelte.d.ts +17 -0
- package/dist/components/document/InlineMath.svelte +41 -0
- package/dist/components/document/InlineMath.svelte.d.ts +7 -0
- package/dist/components/document/LeafletContentRenderer.svelte +64 -0
- package/dist/components/document/LeafletContentRenderer.svelte.d.ts +36 -0
- package/dist/components/document/LinearDocumentRenderer.svelte +45 -0
- package/dist/components/document/LinearDocumentRenderer.svelte.d.ts +18 -0
- package/dist/components/document/MarkdownRenderer.svelte +62 -0
- package/dist/components/document/MarkdownRenderer.svelte.d.ts +10 -0
- package/dist/components/document/RichText.svelte +272 -0
- package/dist/components/document/RichText.svelte.d.ts +18 -0
- package/dist/components/document/blocks/BlockquoteBlock.svelte +29 -0
- package/dist/components/document/blocks/BlockquoteBlock.svelte.d.ts +10 -0
- package/dist/components/document/blocks/BskyPostBlock.svelte +202 -0
- package/dist/components/document/blocks/BskyPostBlock.svelte.d.ts +13 -0
- package/dist/components/document/blocks/ButtonBlock.svelte +24 -0
- package/dist/components/document/blocks/ButtonBlock.svelte.d.ts +10 -0
- package/dist/components/document/blocks/CodeBlock.svelte +68 -0
- package/dist/components/document/blocks/CodeBlock.svelte.d.ts +12 -0
- package/dist/components/document/blocks/HeaderBlock.svelte +56 -0
- package/dist/components/document/blocks/HeaderBlock.svelte.d.ts +11 -0
- package/dist/components/document/blocks/HorizontalRuleBlock.svelte +14 -0
- package/dist/components/document/blocks/HorizontalRuleBlock.svelte.d.ts +6 -0
- package/dist/components/document/blocks/IframeBlock.svelte +32 -0
- package/dist/components/document/blocks/IframeBlock.svelte.d.ts +10 -0
- package/dist/components/document/blocks/ImageBlock.svelte +55 -0
- package/dist/components/document/blocks/ImageBlock.svelte.d.ts +25 -0
- package/dist/components/document/blocks/MathBlock.svelte +34 -0
- package/dist/components/document/blocks/MathBlock.svelte.d.ts +10 -0
- package/dist/components/document/blocks/PageBlock.svelte +66 -0
- package/dist/components/document/blocks/PageBlock.svelte.d.ts +10 -0
- package/dist/components/document/blocks/PollBlock.svelte +122 -0
- package/dist/components/document/blocks/PollBlock.svelte.d.ts +27 -0
- package/dist/components/document/blocks/TextBlock.svelte +26 -0
- package/dist/components/document/blocks/TextBlock.svelte.d.ts +11 -0
- package/dist/components/document/blocks/UnorderedListBlock.svelte +71 -0
- package/dist/components/document/blocks/UnorderedListBlock.svelte.d.ts +9 -0
- package/dist/components/document/blocks/WebsiteBlock.svelte +81 -0
- package/dist/components/document/blocks/WebsiteBlock.svelte.d.ts +21 -0
- package/dist/components/index.d.ts +11 -0
- package/dist/components/index.js +13 -0
- package/dist/config/env.d.ts +11 -0
- package/dist/config/env.js +26 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +23 -0
- package/dist/publisher.d.ts +193 -0
- package/dist/publisher.js +349 -0
- package/dist/schemas.d.ts +626 -0
- package/dist/schemas.js +113 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.js +1 -0
- package/dist/stores/theme.d.ts +11 -0
- package/dist/stores/theme.js +67 -0
- package/dist/styles/base.css +188 -0
- package/dist/styles/themes.css +5 -0
- package/dist/types.d.ts +106 -0
- package/dist/types.js +4 -0
- package/dist/utils/agents.d.ts +35 -0
- package/dist/utils/agents.js +96 -0
- package/dist/utils/at-uri.d.ts +50 -0
- package/dist/utils/at-uri.js +71 -0
- package/dist/utils/cache.d.ts +14 -0
- package/dist/utils/cache.js +33 -0
- package/dist/utils/comments.d.ts +61 -0
- package/dist/utils/comments.js +159 -0
- package/dist/utils/content.d.ts +94 -0
- package/dist/utils/content.js +178 -0
- package/dist/utils/document.d.ts +23 -0
- package/dist/utils/document.js +33 -0
- package/dist/utils/theme-helpers.d.ts +34 -0
- package/dist/utils/theme-helpers.js +63 -0
- package/dist/utils/theme.d.ts +18 -0
- package/dist/utils/theme.js +24 -0
- package/dist/utils/verification.d.ts +129 -0
- package/dist/utils/verification.js +157 -0
- package/package.json +139 -0
- package/src/lib/__tests__/content.test.ts +155 -0
- package/src/lib/client.ts +368 -0
- package/src/lib/components/Comments.svelte +277 -0
- package/src/lib/components/DocumentCard.svelte +95 -0
- package/src/lib/components/PublicationCard.svelte +54 -0
- package/src/lib/components/StandardSiteLayout.svelte +102 -0
- package/src/lib/components/ThemeToggle.svelte +55 -0
- package/src/lib/components/common/DateDisplay.svelte +38 -0
- package/src/lib/components/common/TagList.svelte +31 -0
- package/src/lib/components/common/ThemedCard.svelte +65 -0
- package/src/lib/components/common/ThemedContainer.svelte +55 -0
- package/src/lib/components/common/ThemedText.svelte +75 -0
- package/src/lib/components/document/BlockRenderer.svelte +67 -0
- package/src/lib/components/document/CanvasRenderer.svelte +41 -0
- package/src/lib/components/document/DocumentRenderer.svelte +68 -0
- package/src/lib/components/document/InlineMath.svelte +41 -0
- package/src/lib/components/document/LeafletContentRenderer.svelte +64 -0
- package/src/lib/components/document/LinearDocumentRenderer.svelte +45 -0
- package/src/lib/components/document/MarkdownRenderer.svelte +62 -0
- package/src/lib/components/document/RichText.svelte +272 -0
- package/src/lib/components/document/blocks/BlockquoteBlock.svelte +29 -0
- package/src/lib/components/document/blocks/BskyPostBlock.svelte +202 -0
- package/src/lib/components/document/blocks/ButtonBlock.svelte +24 -0
- package/src/lib/components/document/blocks/CodeBlock.svelte +68 -0
- package/src/lib/components/document/blocks/HeaderBlock.svelte +56 -0
- package/src/lib/components/document/blocks/HorizontalRuleBlock.svelte +14 -0
- package/src/lib/components/document/blocks/IframeBlock.svelte +32 -0
- package/src/lib/components/document/blocks/ImageBlock.svelte +55 -0
- package/src/lib/components/document/blocks/MathBlock.svelte +34 -0
- package/src/lib/components/document/blocks/PageBlock.svelte +66 -0
- package/src/lib/components/document/blocks/PollBlock.svelte +122 -0
- package/src/lib/components/document/blocks/TextBlock.svelte +26 -0
- package/src/lib/components/document/blocks/UnorderedListBlock.svelte +71 -0
- package/src/lib/components/document/blocks/WebsiteBlock.svelte +81 -0
- package/src/lib/components/index.ts +15 -0
- package/src/lib/config/env.ts +31 -0
- package/src/lib/index.ts +104 -0
- package/src/lib/publisher.ts +489 -0
- package/src/lib/schemas.ts +137 -0
- package/src/lib/stores/index.ts +1 -0
- package/src/lib/stores/theme.ts +80 -0
- package/src/lib/styles/base.css +188 -0
- package/src/lib/styles/themes.css +5 -0
- package/src/lib/types.ts +116 -0
- package/src/lib/utils/agents.ts +124 -0
- package/src/lib/utils/at-uri.ts +89 -0
- package/src/lib/utils/cache.ts +46 -0
- package/src/lib/utils/comments.ts +217 -0
- package/src/lib/utils/content.ts +234 -0
- package/src/lib/utils/document.ts +41 -0
- package/src/lib/utils/theme-helpers.ts +87 -0
- package/src/lib/utils/theme.ts +33 -0
- package/src/lib/utils/verification.ts +180 -0
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for standard.site lexicons and configuration
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
/**
|
|
6
|
+
* AT Protocol collections
|
|
7
|
+
*/
|
|
8
|
+
export const COLLECTIONS = {
|
|
9
|
+
DOCUMENT: 'site.standard.document',
|
|
10
|
+
PUBLICATION: 'site.standard.publication'
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* RGB Color schema
|
|
14
|
+
*/
|
|
15
|
+
export const RGBColorSchema = z.object({
|
|
16
|
+
r: z.number().int().min(0).max(255),
|
|
17
|
+
g: z.number().int().min(0).max(255),
|
|
18
|
+
b: z.number().int().min(0).max(255)
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Basic Theme schema
|
|
22
|
+
*/
|
|
23
|
+
export const BasicThemeSchema = z.object({
|
|
24
|
+
$type: z.literal('site.standard.theme.basic').optional(),
|
|
25
|
+
background: RGBColorSchema,
|
|
26
|
+
foreground: RGBColorSchema,
|
|
27
|
+
accent: RGBColorSchema,
|
|
28
|
+
accentForeground: RGBColorSchema
|
|
29
|
+
});
|
|
30
|
+
/**
|
|
31
|
+
* Publication Preferences schema
|
|
32
|
+
*/
|
|
33
|
+
export const PublicationPreferencesSchema = z.object({
|
|
34
|
+
showInDiscover: z.boolean().optional(),
|
|
35
|
+
showComments: z.boolean().optional(),
|
|
36
|
+
showMentions: z.boolean().optional(),
|
|
37
|
+
showPrevNext: z.boolean().optional(),
|
|
38
|
+
showRecommends: z.boolean().optional()
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* AT Protocol Blob schema
|
|
42
|
+
*/
|
|
43
|
+
export const AtProtoBlobSchema = z.object({
|
|
44
|
+
$type: z.literal('blob'),
|
|
45
|
+
ref: z.object({
|
|
46
|
+
$link: z.string()
|
|
47
|
+
}),
|
|
48
|
+
mimeType: z.string(),
|
|
49
|
+
size: z.number().int().positive()
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Strong Reference schema
|
|
53
|
+
*/
|
|
54
|
+
export const StrongRefSchema = z.object({
|
|
55
|
+
uri: z.string(),
|
|
56
|
+
cid: z.string()
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Publication schema
|
|
60
|
+
*/
|
|
61
|
+
export const PublicationSchema = z.object({
|
|
62
|
+
$type: z.literal('site.standard.publication'),
|
|
63
|
+
name: z.string(),
|
|
64
|
+
url: z.string().url(),
|
|
65
|
+
description: z.string().optional(),
|
|
66
|
+
icon: AtProtoBlobSchema.optional(),
|
|
67
|
+
basicTheme: BasicThemeSchema.optional(),
|
|
68
|
+
preferences: PublicationPreferencesSchema.optional()
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* Document schema
|
|
72
|
+
*/
|
|
73
|
+
export const DocumentSchema = z.object({
|
|
74
|
+
$type: z.literal('site.standard.document'),
|
|
75
|
+
site: z.string(),
|
|
76
|
+
title: z.string(),
|
|
77
|
+
publishedAt: z.string().datetime(),
|
|
78
|
+
path: z.string().optional(),
|
|
79
|
+
description: z.string().optional(),
|
|
80
|
+
updatedAt: z.string().datetime().optional(),
|
|
81
|
+
tags: z.array(z.string()).optional(),
|
|
82
|
+
coverImage: AtProtoBlobSchema.optional(),
|
|
83
|
+
textContent: z.string().optional(),
|
|
84
|
+
content: z.unknown().optional(),
|
|
85
|
+
bskyPostRef: StrongRefSchema.optional(),
|
|
86
|
+
preferences: PublicationPreferencesSchema.optional()
|
|
87
|
+
});
|
|
88
|
+
/**
|
|
89
|
+
* Publisher configuration schema
|
|
90
|
+
*/
|
|
91
|
+
export const PublisherConfigSchema = z.object({
|
|
92
|
+
identifier: z.string(), // handle or DID
|
|
93
|
+
password: z.string(),
|
|
94
|
+
service: z.string().url().optional()
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Reader/Client configuration schema
|
|
98
|
+
*/
|
|
99
|
+
export const ReaderConfigSchema = z.object({
|
|
100
|
+
did: z.string(),
|
|
101
|
+
pds: z.string().url().optional(),
|
|
102
|
+
cacheTTL: z.number().int().positive().optional()
|
|
103
|
+
});
|
|
104
|
+
/**
|
|
105
|
+
* Loader configuration schema
|
|
106
|
+
*/
|
|
107
|
+
export const LoaderConfigSchema = z.object({
|
|
108
|
+
repo: z.string(),
|
|
109
|
+
excludeSite: z.string().url().optional(),
|
|
110
|
+
publication: z.string().optional(),
|
|
111
|
+
limit: z.number().int().positive().default(100),
|
|
112
|
+
service: z.string().url().default('https://public.api.bsky.app')
|
|
113
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { themeStore } from './theme.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { themeStore } from './theme.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface ThemeState {
|
|
2
|
+
isDark: boolean;
|
|
3
|
+
mounted: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare const themeStore: {
|
|
6
|
+
subscribe: (this: void, run: import("svelte/store").Subscriber<ThemeState>, invalidate?: () => void) => import("svelte/store").Unsubscriber;
|
|
7
|
+
init: () => (() => void) | undefined;
|
|
8
|
+
toggle: () => void;
|
|
9
|
+
setTheme: (isDark: boolean) => void;
|
|
10
|
+
};
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { writable } from 'svelte/store';
|
|
2
|
+
import { browser } from '$app/environment';
|
|
3
|
+
function createThemeStore() {
|
|
4
|
+
const { subscribe, set, update } = writable({
|
|
5
|
+
isDark: false,
|
|
6
|
+
mounted: false
|
|
7
|
+
});
|
|
8
|
+
return {
|
|
9
|
+
subscribe,
|
|
10
|
+
init: () => {
|
|
11
|
+
if (!browser)
|
|
12
|
+
return;
|
|
13
|
+
const stored = localStorage.getItem('theme');
|
|
14
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
15
|
+
const isDark = stored === 'dark' || (!stored && prefersDark);
|
|
16
|
+
update((state) => ({
|
|
17
|
+
...state,
|
|
18
|
+
isDark,
|
|
19
|
+
mounted: true
|
|
20
|
+
}));
|
|
21
|
+
applyTheme(isDark);
|
|
22
|
+
// Listen for system preference changes
|
|
23
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
24
|
+
const handleChange = (e) => {
|
|
25
|
+
if (!localStorage.getItem('theme')) {
|
|
26
|
+
update((state) => ({ ...state, isDark: e.matches }));
|
|
27
|
+
applyTheme(e.matches);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
31
|
+
return () => {
|
|
32
|
+
mediaQuery.removeEventListener('change', handleChange);
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
toggle: () => {
|
|
36
|
+
if (!browser)
|
|
37
|
+
return;
|
|
38
|
+
update((state) => {
|
|
39
|
+
const newIsDark = !state.isDark;
|
|
40
|
+
localStorage.setItem('theme', newIsDark ? 'dark' : 'light');
|
|
41
|
+
applyTheme(newIsDark);
|
|
42
|
+
return { ...state, isDark: newIsDark };
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
setTheme: (isDark) => {
|
|
46
|
+
if (!browser)
|
|
47
|
+
return;
|
|
48
|
+
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
|
49
|
+
applyTheme(isDark);
|
|
50
|
+
update((state) => ({ ...state, isDark }));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function applyTheme(isDark) {
|
|
55
|
+
if (!browser)
|
|
56
|
+
return;
|
|
57
|
+
const htmlElement = document.documentElement;
|
|
58
|
+
if (isDark) {
|
|
59
|
+
htmlElement.classList.add('dark');
|
|
60
|
+
htmlElement.style.colorScheme = 'dark';
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
htmlElement.classList.remove('dark');
|
|
64
|
+
htmlElement.style.colorScheme = 'light';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export const themeStore = createThemeStore();
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
2
|
+
@import 'tailwindcss';
|
|
3
|
+
@import './themes.css';
|
|
4
|
+
|
|
5
|
+
@theme {
|
|
6
|
+
/* Font Family */
|
|
7
|
+
--font-family-sans:
|
|
8
|
+
'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
9
|
+
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
10
|
+
|
|
11
|
+
/* Ink - Slate-tinted text (230°) */
|
|
12
|
+
--color-ink-50: light-dark(oklch(17.5% 0.012 230), oklch(97.6% 0.008 230));
|
|
13
|
+
--color-ink-100: light-dark(oklch(25% 0.022 230), oklch(93.2% 0.017 230));
|
|
14
|
+
--color-ink-200: light-dark(oklch(38.5% 0.037 230), oklch(85.2% 0.032 230));
|
|
15
|
+
--color-ink-300: light-dark(oklch(50.5% 0.052 230), oklch(75.2% 0.048 230));
|
|
16
|
+
--color-ink-400: light-dark(oklch(62% 0.065 230), oklch(65.2% 0.062 230));
|
|
17
|
+
--color-ink-500: light-dark(oklch(73% 0.078 230), oklch(55.2% 0.078 230));
|
|
18
|
+
--color-ink-600: light-dark(oklch(78% 0.062 230), oklch(45.2% 0.065 230));
|
|
19
|
+
--color-ink-700: light-dark(oklch(83.5% 0.048 230), oklch(35.2% 0.052 230));
|
|
20
|
+
--color-ink-800: light-dark(oklch(89% 0.032 230), oklch(25.2% 0.037 230));
|
|
21
|
+
--color-ink-900: light-dark(oklch(94.5% 0.017 230), oklch(18.2% 0.022 230));
|
|
22
|
+
--color-ink-950: light-dark(oklch(97.6% 0.008 230), oklch(12.5% 0.012 230));
|
|
23
|
+
|
|
24
|
+
/* Canvas - Slate-tinted backgrounds (230°) */
|
|
25
|
+
--color-canvas-50: light-dark(oklch(17.8% 0.014 230), oklch(98.6% 0.005 230));
|
|
26
|
+
--color-canvas-100: light-dark(oklch(25.8% 0.025 230), oklch(96.6% 0.011 230));
|
|
27
|
+
--color-canvas-200: light-dark(oklch(39.5% 0.042 230), oklch(92.5% 0.024 230));
|
|
28
|
+
--color-canvas-300: light-dark(oklch(52% 0.058 230), oklch(86.5% 0.038 230));
|
|
29
|
+
--color-canvas-400: light-dark(oklch(64% 0.072 230), oklch(80.5% 0.055 230));
|
|
30
|
+
--color-canvas-500: light-dark(oklch(75.5% 0.085 230), oklch(75.5% 0.068 230));
|
|
31
|
+
--color-canvas-600: light-dark(oklch(80.5% 0.055 230), oklch(64% 0.072 230));
|
|
32
|
+
--color-canvas-700: light-dark(oklch(86.5% 0.038 230), oklch(52% 0.058 230));
|
|
33
|
+
--color-canvas-800: light-dark(oklch(92.5% 0.024 230), oklch(39.5% 0.042 230));
|
|
34
|
+
--color-canvas-900: light-dark(oklch(96.6% 0.011 230), oklch(25.8% 0.025 230));
|
|
35
|
+
--color-canvas-950: light-dark(oklch(98.6% 0.005 230), oklch(17.8% 0.014 230));
|
|
36
|
+
|
|
37
|
+
/* Slate - Primary colors (230°) */
|
|
38
|
+
--color-primary-50: light-dark(oklch(18.2% 0.018 230), oklch(97.8% 0.012 230));
|
|
39
|
+
--color-primary-100: light-dark(oklch(26.5% 0.03 230), oklch(94.8% 0.022 230));
|
|
40
|
+
--color-primary-200: light-dark(oklch(40.5% 0.048 230), oklch(89.5% 0.042 230));
|
|
41
|
+
--color-primary-300: light-dark(oklch(54% 0.065 230), oklch(79.5% 0.062 230));
|
|
42
|
+
--color-primary-400: light-dark(oklch(66.5% 0.08 230), oklch(69.5% 0.078 230));
|
|
43
|
+
--color-primary-500: light-dark(oklch(78.5% 0.095 230), oklch(59.5% 0.095 230));
|
|
44
|
+
--color-primary-600: light-dark(oklch(82.2% 0.078 230), oklch(49.5% 0.08 230));
|
|
45
|
+
--color-primary-700: light-dark(oklch(86.5% 0.062 230), oklch(39.5% 0.065 230));
|
|
46
|
+
--color-primary-800: light-dark(oklch(91% 0.042 230), oklch(29.5% 0.048 230));
|
|
47
|
+
--color-primary-900: light-dark(oklch(95.8% 0.022 230), oklch(21.5% 0.03 230));
|
|
48
|
+
--color-primary-950: light-dark(oklch(98% 0.012 230), oklch(15.2% 0.018 230));
|
|
49
|
+
|
|
50
|
+
/* Steel Grey - Secondary colors (215°) */
|
|
51
|
+
--color-secondary-50: light-dark(oklch(18.5% 0.02 215), oklch(97.9% 0.013 215));
|
|
52
|
+
--color-secondary-100: light-dark(oklch(26.8% 0.033 215), oklch(95% 0.024 215));
|
|
53
|
+
--color-secondary-200: light-dark(oklch(41% 0.052 215), oklch(89.8% 0.045 215));
|
|
54
|
+
--color-secondary-300: light-dark(oklch(54.5% 0.07 215), oklch(80.2% 0.065 215));
|
|
55
|
+
--color-secondary-400: light-dark(oklch(67% 0.087 215), oklch(70.2% 0.082 215));
|
|
56
|
+
--color-secondary-500: light-dark(oklch(79% 0.103 215), oklch(60.2% 0.103 215));
|
|
57
|
+
--color-secondary-600: light-dark(oklch(82.8% 0.082 215), oklch(50.2% 0.087 215));
|
|
58
|
+
--color-secondary-700: light-dark(oklch(87% 0.065 215), oklch(40.2% 0.07 215));
|
|
59
|
+
--color-secondary-800: light-dark(oklch(91.5% 0.045 215), oklch(30.5% 0.052 215));
|
|
60
|
+
--color-secondary-900: light-dark(oklch(96% 0.024 215), oklch(22.2% 0.033 215));
|
|
61
|
+
--color-secondary-950: light-dark(oklch(98.2% 0.013 215), oklch(15.8% 0.02 215));
|
|
62
|
+
|
|
63
|
+
/* Charcoal - Accent colors (240°) */
|
|
64
|
+
--color-accent-50: light-dark(oklch(18.5% 0.022 240), oklch(98% 0.014 240));
|
|
65
|
+
--color-accent-100: light-dark(oklch(26.8% 0.036 240), oklch(95.2% 0.026 240));
|
|
66
|
+
--color-accent-200: light-dark(oklch(41% 0.058 240), oklch(90% 0.048 240));
|
|
67
|
+
--color-accent-300: light-dark(oklch(54.5% 0.078 240), oklch(80.8% 0.072 240));
|
|
68
|
+
--color-accent-400: light-dark(oklch(67% 0.097 240), oklch(71% 0.092 240));
|
|
69
|
+
--color-accent-500: light-dark(oklch(79% 0.115 240), oklch(61% 0.115 240));
|
|
70
|
+
--color-accent-600: light-dark(oklch(82.8% 0.092 240), oklch(51% 0.097 240));
|
|
71
|
+
--color-accent-700: light-dark(oklch(87% 0.072 240), oklch(41% 0.078 240));
|
|
72
|
+
--color-accent-800: light-dark(oklch(91.5% 0.048 240), oklch(31% 0.058 240));
|
|
73
|
+
--color-accent-900: light-dark(oklch(96% 0.026 240), oklch(22.5% 0.036 240));
|
|
74
|
+
--color-accent-950: light-dark(oklch(98.2% 0.014 240), oklch(16.2% 0.022 240));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@layer base {
|
|
78
|
+
/* Base styles for consistent typography and accessibility */
|
|
79
|
+
html {
|
|
80
|
+
scroll-behavior: smooth;
|
|
81
|
+
overflow-x: hidden;
|
|
82
|
+
width: 100%;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
html {
|
|
87
|
+
scroll-behavior: auto;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
*,
|
|
91
|
+
*::before,
|
|
92
|
+
*::after {
|
|
93
|
+
animation-duration: 0.01ms !important;
|
|
94
|
+
animation-iteration-count: 1 !important;
|
|
95
|
+
transition-duration: 0.01ms !important;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
body {
|
|
100
|
+
font-family: var(--font-family-sans);
|
|
101
|
+
text-rendering: optimizeLegibility;
|
|
102
|
+
-webkit-font-smoothing: antialiased;
|
|
103
|
+
-moz-osx-font-smoothing: grayscale;
|
|
104
|
+
overflow-x: hidden;
|
|
105
|
+
width: 100%;
|
|
106
|
+
max-width: 100vw;
|
|
107
|
+
background-color: var(--color-canvas-50);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (prefers-color-scheme: dark) {
|
|
111
|
+
body {
|
|
112
|
+
background-color: var(--color-canvas-950);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Skip to content link for keyboard navigation */
|
|
117
|
+
.skip-to-content {
|
|
118
|
+
position: absolute;
|
|
119
|
+
left: -9999px;
|
|
120
|
+
z-index: 999;
|
|
121
|
+
padding: 1rem 1.5rem;
|
|
122
|
+
background-color: var(--color-primary-600);
|
|
123
|
+
color: white;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
text-decoration: none;
|
|
126
|
+
border-radius: 0.5rem;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.skip-to-content:focus {
|
|
130
|
+
left: 1rem;
|
|
131
|
+
top: 1rem;
|
|
132
|
+
outline: 2px solid var(--color-primary-800);
|
|
133
|
+
outline-offset: 2px;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Focus visible styles for accessibility - Enhanced for better visibility */
|
|
137
|
+
*:focus-visible {
|
|
138
|
+
outline: 3px solid var(--color-primary-600);
|
|
139
|
+
outline-offset: 2px;
|
|
140
|
+
border-radius: 0.25rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* High contrast mode support */
|
|
144
|
+
@media (prefers-contrast: high) {
|
|
145
|
+
*:focus-visible {
|
|
146
|
+
outline-width: 4px;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Ensure all elements stay within viewport */
|
|
151
|
+
* {
|
|
152
|
+
min-width: 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
img,
|
|
156
|
+
video,
|
|
157
|
+
iframe,
|
|
158
|
+
embed,
|
|
159
|
+
object {
|
|
160
|
+
max-width: 100%;
|
|
161
|
+
height: auto;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Improve link accessibility */
|
|
165
|
+
a {
|
|
166
|
+
text-decoration-skip-ink: auto;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Better button accessibility */
|
|
170
|
+
button:disabled {
|
|
171
|
+
cursor: not-allowed;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Screen reader only utility */
|
|
175
|
+
.sr-only {
|
|
176
|
+
position: absolute;
|
|
177
|
+
width: 1px;
|
|
178
|
+
height: 1px;
|
|
179
|
+
padding: 0;
|
|
180
|
+
margin: -1px;
|
|
181
|
+
overflow: hidden;
|
|
182
|
+
clip: rect(0, 0, 0, 0);
|
|
183
|
+
white-space: nowrap;
|
|
184
|
+
border-width: 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@plugin '@tailwindcss/typography';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for site.standard.* lexicons
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* AT Protocol blob reference
|
|
6
|
+
*/
|
|
7
|
+
export interface AtProtoBlob {
|
|
8
|
+
$type: 'blob';
|
|
9
|
+
ref: {
|
|
10
|
+
$link: string;
|
|
11
|
+
};
|
|
12
|
+
mimeType: string;
|
|
13
|
+
size: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Strong reference to another AT Protocol record
|
|
17
|
+
*/
|
|
18
|
+
export interface StrongRef {
|
|
19
|
+
uri: string;
|
|
20
|
+
cid: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* RGB Color
|
|
24
|
+
*/
|
|
25
|
+
export interface RGBColor {
|
|
26
|
+
r: number;
|
|
27
|
+
g: number;
|
|
28
|
+
b: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Basic theme for publications
|
|
32
|
+
*/
|
|
33
|
+
export interface BasicTheme {
|
|
34
|
+
$type?: 'site.standard.theme.basic';
|
|
35
|
+
background: RGBColor;
|
|
36
|
+
foreground: RGBColor;
|
|
37
|
+
accent: RGBColor;
|
|
38
|
+
accentForeground: RGBColor;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Publication preferences
|
|
42
|
+
*/
|
|
43
|
+
export interface PublicationPreferences {
|
|
44
|
+
showInDiscover?: boolean;
|
|
45
|
+
showComments?: boolean;
|
|
46
|
+
showMentions?: boolean;
|
|
47
|
+
showPrevNext?: boolean;
|
|
48
|
+
showRecommends?: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Site Standard Publication record
|
|
52
|
+
*/
|
|
53
|
+
export interface Publication {
|
|
54
|
+
$type: 'site.standard.publication';
|
|
55
|
+
url: string;
|
|
56
|
+
name: string;
|
|
57
|
+
icon?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
basicTheme?: BasicTheme;
|
|
60
|
+
preferences?: PublicationPreferences;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Site Standard Document record
|
|
64
|
+
*/
|
|
65
|
+
export interface Document {
|
|
66
|
+
$type: 'site.standard.document';
|
|
67
|
+
site: string;
|
|
68
|
+
title: string;
|
|
69
|
+
path?: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
coverImage?: string;
|
|
72
|
+
content?: any;
|
|
73
|
+
textContent?: string;
|
|
74
|
+
bskyPostRef?: StrongRef;
|
|
75
|
+
tags?: string[];
|
|
76
|
+
publishedAt: string;
|
|
77
|
+
updatedAt?: string;
|
|
78
|
+
preferences?: PublicationPreferences;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* AT Protocol record response
|
|
82
|
+
*/
|
|
83
|
+
export interface AtProtoRecord<T = any> {
|
|
84
|
+
uri: string;
|
|
85
|
+
cid: string;
|
|
86
|
+
value: T;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolved identity from PDS resolution
|
|
90
|
+
*/
|
|
91
|
+
export interface ResolvedIdentity {
|
|
92
|
+
did: string;
|
|
93
|
+
pds: string;
|
|
94
|
+
handle?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Configuration for the library
|
|
98
|
+
*/
|
|
99
|
+
export interface SiteStandardConfig {
|
|
100
|
+
/** The DID to fetch records from */
|
|
101
|
+
did: string;
|
|
102
|
+
/** Optional custom PDS endpoint */
|
|
103
|
+
pds?: string;
|
|
104
|
+
/** Cache TTL in milliseconds (default: 5 minutes) */
|
|
105
|
+
cacheTTL?: number;
|
|
106
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { AtpAgent } from '@atproto/api';
|
|
2
|
+
import type { ResolvedIdentity } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates an AtpAgent with optional fetch function injection
|
|
5
|
+
*/
|
|
6
|
+
export declare function createAgent(service: string, fetchFn?: typeof fetch): AtpAgent;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves a DID to find its PDS endpoint using Slingshot
|
|
9
|
+
* @param did - DID to resolve
|
|
10
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
11
|
+
* @returns Resolved identity with PDS endpoint
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveIdentity(did: string, fetchFn?: typeof fetch): Promise<ResolvedIdentity>;
|
|
14
|
+
/**
|
|
15
|
+
* Gets or creates a PDS-specific agent
|
|
16
|
+
* @param did - DID to resolve PDS for
|
|
17
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
18
|
+
* @returns AtpAgent configured for the user's PDS
|
|
19
|
+
*/
|
|
20
|
+
export declare function getPDSAgent(did: string, fetchFn?: typeof fetch): Promise<AtpAgent>;
|
|
21
|
+
/**
|
|
22
|
+
* Executes a function with automatic fallback
|
|
23
|
+
* @param did - The DID to resolve
|
|
24
|
+
* @param operation - The operation to execute
|
|
25
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
26
|
+
*/
|
|
27
|
+
export declare function withFallback<T>(did: string, operation: (agent: AtpAgent) => Promise<T>, fetchFn?: typeof fetch): Promise<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Build a PDS blob URL
|
|
30
|
+
* @param pds - PDS endpoint
|
|
31
|
+
* @param did - Repository DID
|
|
32
|
+
* @param cid - Blob CID
|
|
33
|
+
* @returns Full blob URL
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildPdsBlobUrl(pds: string, did: string, cid: string): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AtpAgent } from '@atproto/api';
|
|
2
|
+
import { cache } from './cache.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates an AtpAgent with optional fetch function injection
|
|
5
|
+
*/
|
|
6
|
+
export function createAgent(service, fetchFn) {
|
|
7
|
+
const wrappedFetch = fetchFn
|
|
8
|
+
? async (url, init) => {
|
|
9
|
+
const urlStr = url instanceof URL ? url.toString() : url;
|
|
10
|
+
const response = await fetchFn(urlStr, init);
|
|
11
|
+
const headers = new Headers(response.headers);
|
|
12
|
+
if (!headers.has('content-type')) {
|
|
13
|
+
headers.set('content-type', 'application/json');
|
|
14
|
+
}
|
|
15
|
+
return new Response(response.body, {
|
|
16
|
+
status: response.status,
|
|
17
|
+
statusText: response.statusText,
|
|
18
|
+
headers
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
: undefined;
|
|
22
|
+
return new AtpAgent({
|
|
23
|
+
service,
|
|
24
|
+
...(wrappedFetch && { fetch: wrappedFetch })
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Resolves a DID to find its PDS endpoint using Slingshot
|
|
29
|
+
* @param did - DID to resolve
|
|
30
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
31
|
+
* @returns Resolved identity with PDS endpoint
|
|
32
|
+
*/
|
|
33
|
+
export async function resolveIdentity(did, fetchFn) {
|
|
34
|
+
const cacheKey = `identity:${did}`;
|
|
35
|
+
const cached = cache.get(cacheKey);
|
|
36
|
+
if (cached)
|
|
37
|
+
return cached;
|
|
38
|
+
const _fetch = fetchFn ?? globalThis.fetch;
|
|
39
|
+
const response = await _fetch(`https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=${encodeURIComponent(did)}`);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Failed to resolve DID: ${response.status} ${response.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
const rawText = await response.text();
|
|
44
|
+
const data = JSON.parse(rawText);
|
|
45
|
+
if (!data.did || !data.pds) {
|
|
46
|
+
throw new Error('Invalid response from identity resolver');
|
|
47
|
+
}
|
|
48
|
+
cache.set(cacheKey, data);
|
|
49
|
+
return data;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Gets or creates a PDS-specific agent
|
|
53
|
+
* @param did - DID to resolve PDS for
|
|
54
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
55
|
+
* @returns AtpAgent configured for the user's PDS
|
|
56
|
+
*/
|
|
57
|
+
export async function getPDSAgent(did, fetchFn) {
|
|
58
|
+
const resolved = await resolveIdentity(did, fetchFn);
|
|
59
|
+
return createAgent(resolved.pds, fetchFn);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Executes a function with automatic fallback
|
|
63
|
+
* @param did - The DID to resolve
|
|
64
|
+
* @param operation - The operation to execute
|
|
65
|
+
* @param fetchFn - Optional fetch function for SSR
|
|
66
|
+
*/
|
|
67
|
+
export async function withFallback(did, operation, fetchFn) {
|
|
68
|
+
const agents = [
|
|
69
|
+
() => getPDSAgent(did, fetchFn),
|
|
70
|
+
() => Promise.resolve(fetchFn
|
|
71
|
+
? createAgent('https://public.api.bsky.app', fetchFn)
|
|
72
|
+
: createAgent('https://public.api.bsky.app'))
|
|
73
|
+
];
|
|
74
|
+
let lastError;
|
|
75
|
+
for (const getAgent of agents) {
|
|
76
|
+
try {
|
|
77
|
+
const agent = await getAgent();
|
|
78
|
+
return await operation(agent);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
lastError = error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw lastError;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build a PDS blob URL
|
|
88
|
+
* @param pds - PDS endpoint
|
|
89
|
+
* @param did - Repository DID
|
|
90
|
+
* @param cid - Blob CID
|
|
91
|
+
* @returns Full blob URL
|
|
92
|
+
*/
|
|
93
|
+
export function buildPdsBlobUrl(pds, did, cid) {
|
|
94
|
+
const pdsBase = pds.replace(/\/$/, '');
|
|
95
|
+
return `${pdsBase}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`;
|
|
96
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT URI parsing and conversion utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Parsed AT URI components
|
|
6
|
+
*/
|
|
7
|
+
export interface ParsedAtUri {
|
|
8
|
+
did: string;
|
|
9
|
+
collection: string;
|
|
10
|
+
rkey: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse an AT URI into its components
|
|
14
|
+
* @param atUri - AT URI in format: at://did:plc:xxx/collection/rkey
|
|
15
|
+
* @returns Parsed components or null if invalid
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseAtUri(atUri: string): ParsedAtUri | null;
|
|
18
|
+
/**
|
|
19
|
+
* Convert AT URI to HTTPS URL for com.atproto.repo.getRecord
|
|
20
|
+
* @param atUri - AT URI in format: at://did:plc:xxx/collection/rkey
|
|
21
|
+
* @param pdsEndpoint - PDS endpoint (e.g., "https://cortinarius.us-west.host.bsky.network")
|
|
22
|
+
* @returns HTTPS URL for getRecord XRPC call
|
|
23
|
+
*/
|
|
24
|
+
export declare function atUriToHttps(atUri: string, pdsEndpoint: string): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Construct an AT URI from components
|
|
27
|
+
* @param did - DID of the repository
|
|
28
|
+
* @param collection - Collection name
|
|
29
|
+
* @param rkey - Record key
|
|
30
|
+
* @returns AT URI string
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildAtUri(did: string, collection: string, rkey: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Extract rkey from an AT URI
|
|
35
|
+
* @param atUri - AT URI
|
|
36
|
+
* @returns rkey or null if invalid
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractRkey(atUri: string): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Validate if string is a valid AT URI
|
|
41
|
+
* @param uri - String to validate
|
|
42
|
+
* @returns true if valid AT URI
|
|
43
|
+
*/
|
|
44
|
+
export declare function isAtUri(uri: string): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Convert DID to PDS hostname format
|
|
47
|
+
* @param did - DID to convert
|
|
48
|
+
* @returns PDS hostname or null if unable to derive
|
|
49
|
+
*/
|
|
50
|
+
export declare function didToPdsHostname(did: string): string | null;
|