@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.
Files changed (160) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +108 -0
  3. package/dist/__tests__/content.test.d.ts +4 -0
  4. package/dist/__tests__/content.test.js +128 -0
  5. package/dist/client.d.ts +71 -0
  6. package/dist/client.js +307 -0
  7. package/dist/components/Comments.svelte +277 -0
  8. package/dist/components/Comments.svelte.d.ts +17 -0
  9. package/dist/components/DocumentCard.svelte +95 -0
  10. package/dist/components/DocumentCard.svelte.d.ts +11 -0
  11. package/dist/components/PublicationCard.svelte +54 -0
  12. package/dist/components/PublicationCard.svelte.d.ts +9 -0
  13. package/dist/components/StandardSiteLayout.svelte +102 -0
  14. package/dist/components/StandardSiteLayout.svelte.d.ts +18 -0
  15. package/dist/components/ThemeToggle.svelte +55 -0
  16. package/dist/components/ThemeToggle.svelte.d.ts +6 -0
  17. package/dist/components/common/DateDisplay.svelte +38 -0
  18. package/dist/components/common/DateDisplay.svelte.d.ts +11 -0
  19. package/dist/components/common/TagList.svelte +31 -0
  20. package/dist/components/common/TagList.svelte.d.ts +8 -0
  21. package/dist/components/common/ThemedCard.svelte +65 -0
  22. package/dist/components/common/ThemedCard.svelte.d.ts +11 -0
  23. package/dist/components/common/ThemedContainer.svelte +55 -0
  24. package/dist/components/common/ThemedContainer.svelte.d.ts +11 -0
  25. package/dist/components/common/ThemedText.svelte +75 -0
  26. package/dist/components/common/ThemedText.svelte.d.ts +11 -0
  27. package/dist/components/document/BlockRenderer.svelte +67 -0
  28. package/dist/components/document/BlockRenderer.svelte.d.ts +9 -0
  29. package/dist/components/document/CanvasRenderer.svelte +41 -0
  30. package/dist/components/document/CanvasRenderer.svelte.d.ts +22 -0
  31. package/dist/components/document/DocumentRenderer.svelte +68 -0
  32. package/dist/components/document/DocumentRenderer.svelte.d.ts +17 -0
  33. package/dist/components/document/InlineMath.svelte +41 -0
  34. package/dist/components/document/InlineMath.svelte.d.ts +7 -0
  35. package/dist/components/document/LeafletContentRenderer.svelte +64 -0
  36. package/dist/components/document/LeafletContentRenderer.svelte.d.ts +36 -0
  37. package/dist/components/document/LinearDocumentRenderer.svelte +45 -0
  38. package/dist/components/document/LinearDocumentRenderer.svelte.d.ts +18 -0
  39. package/dist/components/document/MarkdownRenderer.svelte +62 -0
  40. package/dist/components/document/MarkdownRenderer.svelte.d.ts +10 -0
  41. package/dist/components/document/RichText.svelte +272 -0
  42. package/dist/components/document/RichText.svelte.d.ts +18 -0
  43. package/dist/components/document/blocks/BlockquoteBlock.svelte +29 -0
  44. package/dist/components/document/blocks/BlockquoteBlock.svelte.d.ts +10 -0
  45. package/dist/components/document/blocks/BskyPostBlock.svelte +202 -0
  46. package/dist/components/document/blocks/BskyPostBlock.svelte.d.ts +13 -0
  47. package/dist/components/document/blocks/ButtonBlock.svelte +24 -0
  48. package/dist/components/document/blocks/ButtonBlock.svelte.d.ts +10 -0
  49. package/dist/components/document/blocks/CodeBlock.svelte +68 -0
  50. package/dist/components/document/blocks/CodeBlock.svelte.d.ts +12 -0
  51. package/dist/components/document/blocks/HeaderBlock.svelte +56 -0
  52. package/dist/components/document/blocks/HeaderBlock.svelte.d.ts +11 -0
  53. package/dist/components/document/blocks/HorizontalRuleBlock.svelte +14 -0
  54. package/dist/components/document/blocks/HorizontalRuleBlock.svelte.d.ts +6 -0
  55. package/dist/components/document/blocks/IframeBlock.svelte +32 -0
  56. package/dist/components/document/blocks/IframeBlock.svelte.d.ts +10 -0
  57. package/dist/components/document/blocks/ImageBlock.svelte +55 -0
  58. package/dist/components/document/blocks/ImageBlock.svelte.d.ts +25 -0
  59. package/dist/components/document/blocks/MathBlock.svelte +34 -0
  60. package/dist/components/document/blocks/MathBlock.svelte.d.ts +10 -0
  61. package/dist/components/document/blocks/PageBlock.svelte +66 -0
  62. package/dist/components/document/blocks/PageBlock.svelte.d.ts +10 -0
  63. package/dist/components/document/blocks/PollBlock.svelte +122 -0
  64. package/dist/components/document/blocks/PollBlock.svelte.d.ts +27 -0
  65. package/dist/components/document/blocks/TextBlock.svelte +26 -0
  66. package/dist/components/document/blocks/TextBlock.svelte.d.ts +11 -0
  67. package/dist/components/document/blocks/UnorderedListBlock.svelte +71 -0
  68. package/dist/components/document/blocks/UnorderedListBlock.svelte.d.ts +9 -0
  69. package/dist/components/document/blocks/WebsiteBlock.svelte +81 -0
  70. package/dist/components/document/blocks/WebsiteBlock.svelte.d.ts +21 -0
  71. package/dist/components/index.d.ts +11 -0
  72. package/dist/components/index.js +13 -0
  73. package/dist/config/env.d.ts +11 -0
  74. package/dist/config/env.js +26 -0
  75. package/dist/index.d.ts +20 -0
  76. package/dist/index.js +23 -0
  77. package/dist/publisher.d.ts +193 -0
  78. package/dist/publisher.js +349 -0
  79. package/dist/schemas.d.ts +626 -0
  80. package/dist/schemas.js +113 -0
  81. package/dist/stores/index.d.ts +1 -0
  82. package/dist/stores/index.js +1 -0
  83. package/dist/stores/theme.d.ts +11 -0
  84. package/dist/stores/theme.js +67 -0
  85. package/dist/styles/base.css +188 -0
  86. package/dist/styles/themes.css +5 -0
  87. package/dist/types.d.ts +106 -0
  88. package/dist/types.js +4 -0
  89. package/dist/utils/agents.d.ts +35 -0
  90. package/dist/utils/agents.js +96 -0
  91. package/dist/utils/at-uri.d.ts +50 -0
  92. package/dist/utils/at-uri.js +71 -0
  93. package/dist/utils/cache.d.ts +14 -0
  94. package/dist/utils/cache.js +33 -0
  95. package/dist/utils/comments.d.ts +61 -0
  96. package/dist/utils/comments.js +159 -0
  97. package/dist/utils/content.d.ts +94 -0
  98. package/dist/utils/content.js +178 -0
  99. package/dist/utils/document.d.ts +23 -0
  100. package/dist/utils/document.js +33 -0
  101. package/dist/utils/theme-helpers.d.ts +34 -0
  102. package/dist/utils/theme-helpers.js +63 -0
  103. package/dist/utils/theme.d.ts +18 -0
  104. package/dist/utils/theme.js +24 -0
  105. package/dist/utils/verification.d.ts +129 -0
  106. package/dist/utils/verification.js +157 -0
  107. package/package.json +139 -0
  108. package/src/lib/__tests__/content.test.ts +155 -0
  109. package/src/lib/client.ts +368 -0
  110. package/src/lib/components/Comments.svelte +277 -0
  111. package/src/lib/components/DocumentCard.svelte +95 -0
  112. package/src/lib/components/PublicationCard.svelte +54 -0
  113. package/src/lib/components/StandardSiteLayout.svelte +102 -0
  114. package/src/lib/components/ThemeToggle.svelte +55 -0
  115. package/src/lib/components/common/DateDisplay.svelte +38 -0
  116. package/src/lib/components/common/TagList.svelte +31 -0
  117. package/src/lib/components/common/ThemedCard.svelte +65 -0
  118. package/src/lib/components/common/ThemedContainer.svelte +55 -0
  119. package/src/lib/components/common/ThemedText.svelte +75 -0
  120. package/src/lib/components/document/BlockRenderer.svelte +67 -0
  121. package/src/lib/components/document/CanvasRenderer.svelte +41 -0
  122. package/src/lib/components/document/DocumentRenderer.svelte +68 -0
  123. package/src/lib/components/document/InlineMath.svelte +41 -0
  124. package/src/lib/components/document/LeafletContentRenderer.svelte +64 -0
  125. package/src/lib/components/document/LinearDocumentRenderer.svelte +45 -0
  126. package/src/lib/components/document/MarkdownRenderer.svelte +62 -0
  127. package/src/lib/components/document/RichText.svelte +272 -0
  128. package/src/lib/components/document/blocks/BlockquoteBlock.svelte +29 -0
  129. package/src/lib/components/document/blocks/BskyPostBlock.svelte +202 -0
  130. package/src/lib/components/document/blocks/ButtonBlock.svelte +24 -0
  131. package/src/lib/components/document/blocks/CodeBlock.svelte +68 -0
  132. package/src/lib/components/document/blocks/HeaderBlock.svelte +56 -0
  133. package/src/lib/components/document/blocks/HorizontalRuleBlock.svelte +14 -0
  134. package/src/lib/components/document/blocks/IframeBlock.svelte +32 -0
  135. package/src/lib/components/document/blocks/ImageBlock.svelte +55 -0
  136. package/src/lib/components/document/blocks/MathBlock.svelte +34 -0
  137. package/src/lib/components/document/blocks/PageBlock.svelte +66 -0
  138. package/src/lib/components/document/blocks/PollBlock.svelte +122 -0
  139. package/src/lib/components/document/blocks/TextBlock.svelte +26 -0
  140. package/src/lib/components/document/blocks/UnorderedListBlock.svelte +71 -0
  141. package/src/lib/components/document/blocks/WebsiteBlock.svelte +81 -0
  142. package/src/lib/components/index.ts +15 -0
  143. package/src/lib/config/env.ts +31 -0
  144. package/src/lib/index.ts +104 -0
  145. package/src/lib/publisher.ts +489 -0
  146. package/src/lib/schemas.ts +137 -0
  147. package/src/lib/stores/index.ts +1 -0
  148. package/src/lib/stores/theme.ts +80 -0
  149. package/src/lib/styles/base.css +188 -0
  150. package/src/lib/styles/themes.css +5 -0
  151. package/src/lib/types.ts +116 -0
  152. package/src/lib/utils/agents.ts +124 -0
  153. package/src/lib/utils/at-uri.ts +89 -0
  154. package/src/lib/utils/cache.ts +46 -0
  155. package/src/lib/utils/comments.ts +217 -0
  156. package/src/lib/utils/content.ts +234 -0
  157. package/src/lib/utils/document.ts +41 -0
  158. package/src/lib/utils/theme-helpers.ts +87 -0
  159. package/src/lib/utils/theme.ts +33 -0
  160. package/src/lib/utils/verification.ts +180 -0
@@ -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';
@@ -0,0 +1,5 @@
1
+ /* Color Theme System - Modular Theme Imports */
2
+ /* Slate theme is the default and included in base.css */
3
+ /* Add additional themes here as needed */
4
+
5
+ /* Default theme is Slate (230°) defined in base.css */
@@ -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,4 @@
1
+ /**
2
+ * Core types for site.standard.* lexicons
3
+ */
4
+ export {};
@@ -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;