@ewanc26/svelte-standard-site 0.2.2 → 0.2.4
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/dist/components/ActionBar.svelte +85 -0
- package/dist/components/ActionBar.svelte.d.ts +13 -0
- package/dist/components/Avatar.svelte +104 -0
- package/dist/components/Avatar.svelte.d.ts +19 -0
- package/dist/components/Comment.svelte +172 -0
- package/dist/components/Comment.svelte.d.ts +22 -0
- package/dist/components/CommentsSection.svelte +89 -0
- package/dist/components/DocumentCard.svelte +126 -56
- package/dist/components/DocumentCard.svelte.d.ts +51 -0
- package/dist/components/Footnotes.svelte +72 -0
- package/dist/components/Footnotes.svelte.d.ts +13 -0
- package/dist/components/RecommendButton.svelte +153 -0
- package/dist/components/RecommendButton.svelte.d.ts +17 -0
- package/dist/components/ThemeProvider.svelte +92 -0
- package/dist/components/ThemeProvider.svelte.d.ts +13 -0
- package/dist/components/Toast.svelte +177 -0
- package/dist/components/Toast.svelte.d.ts +32 -0
- package/dist/components/Watermark.svelte +100 -0
- package/dist/components/Watermark.svelte.d.ts +17 -0
- package/dist/components/common/ThemedCard.svelte +15 -15
- package/dist/components/common/ThemedCard.svelte.d.ts +5 -0
- package/dist/components/document/BlockRenderer.svelte +3 -0
- package/dist/components/document/DocumentRenderer.svelte +41 -1
- package/dist/components/document/RichText.svelte +87 -2
- package/dist/components/document/RichText.svelte.d.ts +2 -0
- package/dist/components/document/blocks/OrderedListBlock.svelte +152 -0
- package/dist/components/document/blocks/UnorderedListBlock.svelte +1 -1
- package/dist/components/index.d.ts +28 -0
- package/dist/components/index.js +30 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +6 -4
- package/dist/publisher.d.ts +73 -0
- package/dist/publisher.js +185 -0
- package/dist/schemas.d.ts +1162 -2
- package/dist/schemas.js +316 -0
- package/dist/types.d.ts +393 -2
- package/dist/types.js +1 -1
- package/dist/utils/native-comments.d.ts +68 -0
- package/dist/utils/native-comments.js +149 -0
- package/dist/utils/theme-helpers.d.ts +41 -1
- package/dist/utils/theme-helpers.js +98 -1
- package/dist/utils/theme.d.ts +48 -1
- package/dist/utils/theme.js +158 -0
- package/package.json +62 -65
- package/src/lib/components/ActionBar.svelte +85 -0
- package/src/lib/components/Avatar.svelte +104 -0
- package/src/lib/components/Comment.svelte +172 -0
- package/src/lib/components/CommentsSection.svelte +89 -0
- package/src/lib/components/DocumentCard.svelte +126 -56
- package/src/lib/components/Footnotes.svelte +72 -0
- package/src/lib/components/RecommendButton.svelte +153 -0
- package/src/lib/components/ThemeProvider.svelte +92 -0
- package/src/lib/components/Toast.svelte +177 -0
- package/src/lib/components/Watermark.svelte +100 -0
- package/src/lib/components/common/ThemedCard.svelte +15 -15
- package/src/lib/components/document/BlockRenderer.svelte +3 -0
- package/src/lib/components/document/DocumentRenderer.svelte +41 -1
- package/src/lib/components/document/RichText.svelte +87 -2
- package/src/lib/components/document/blocks/OrderedListBlock.svelte +152 -0
- package/src/lib/components/document/blocks/UnorderedListBlock.svelte +1 -1
- package/src/lib/components/index.ts +32 -0
- package/src/lib/index.ts +119 -5
- package/src/lib/publisher.ts +251 -0
- package/src/lib/schemas.ts +411 -0
- package/src/lib/types.ts +506 -2
- package/src/lib/utils/native-comments.ts +197 -0
- package/src/lib/utils/theme-helpers.ts +136 -3
- package/src/lib/utils/theme.ts +189 -1
- package/dist/components/document/blocks/UnorderedListBlock.svelte.d.ts +0 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { rgbToCSS } from './theme.js';
|
|
1
|
+
import { rgbToCSS, colorToCSS, isRGBA } from './theme.js';
|
|
2
2
|
/**
|
|
3
3
|
* Generate color-mix CSS for theme colors with transparency
|
|
4
4
|
*/
|
|
@@ -48,6 +48,57 @@ export function getThemedAccent(hasTheme, opacity) {
|
|
|
48
48
|
color: 'var(--theme-accent)'
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Get theme-aware page background
|
|
53
|
+
*/
|
|
54
|
+
export function getThemedPageBackground(hasTheme, showPageBackground) {
|
|
55
|
+
if (!hasTheme)
|
|
56
|
+
return {};
|
|
57
|
+
if (showPageBackground === false)
|
|
58
|
+
return {};
|
|
59
|
+
return { backgroundColor: 'var(--theme-page-background)' };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get background image styles
|
|
63
|
+
*/
|
|
64
|
+
export function getBackgroundImageStyles(bgImage) {
|
|
65
|
+
if (!bgImage?.url)
|
|
66
|
+
return {};
|
|
67
|
+
return {
|
|
68
|
+
backgroundImage: `url(${bgImage.url})`,
|
|
69
|
+
backgroundSize: 'cover',
|
|
70
|
+
backgroundPosition: 'center'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get font styles from theme
|
|
75
|
+
*/
|
|
76
|
+
export function getFontStyles(theme) {
|
|
77
|
+
if (!theme)
|
|
78
|
+
return {};
|
|
79
|
+
const headingFont = theme.headingFont;
|
|
80
|
+
const bodyFont = theme.bodyFont;
|
|
81
|
+
if (bodyFont) {
|
|
82
|
+
return { fontFamily: `"${bodyFont}", system-ui, sans-serif` };
|
|
83
|
+
}
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get heading font styles from theme
|
|
88
|
+
*/
|
|
89
|
+
export function getHeadingFontStyles(theme) {
|
|
90
|
+
if (!theme?.headingFont)
|
|
91
|
+
return {};
|
|
92
|
+
return { fontFamily: `"${theme.headingFont}", system-ui, sans-serif` };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get page width styles from theme
|
|
96
|
+
*/
|
|
97
|
+
export function getPageWidthStyles(theme) {
|
|
98
|
+
if (!theme?.pageWidth)
|
|
99
|
+
return {};
|
|
100
|
+
return { maxWidth: `${theme.pageWidth}px` };
|
|
101
|
+
}
|
|
51
102
|
/**
|
|
52
103
|
* Convert BasicTheme to CSS custom properties
|
|
53
104
|
*/
|
|
@@ -61,3 +112,49 @@ export function themeToCssVars(theme) {
|
|
|
61
112
|
'--theme-accent-foreground': rgbToCSS(theme.accentForeground)
|
|
62
113
|
};
|
|
63
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Convert ExtendedTheme to CSS custom properties
|
|
117
|
+
*/
|
|
118
|
+
export function extendedThemeToCssVars(theme) {
|
|
119
|
+
if (!theme)
|
|
120
|
+
return {};
|
|
121
|
+
const vars = {};
|
|
122
|
+
if (theme.backgroundColor) {
|
|
123
|
+
vars['--theme-background'] = colorToCSS(theme.backgroundColor);
|
|
124
|
+
}
|
|
125
|
+
// Extended theme uses different property names
|
|
126
|
+
if (theme.pageBackground) {
|
|
127
|
+
vars['--theme-page-background'] = colorToCSS(theme.pageBackground);
|
|
128
|
+
}
|
|
129
|
+
if (theme.primary) {
|
|
130
|
+
vars['--theme-accent'] = colorToCSS(theme.primary);
|
|
131
|
+
}
|
|
132
|
+
if (theme.accentBackground) {
|
|
133
|
+
vars['--theme-accent-background'] = colorToCSS(theme.accentBackground);
|
|
134
|
+
}
|
|
135
|
+
if (theme.accentText) {
|
|
136
|
+
vars['--theme-accent-foreground'] = colorToCSS(theme.accentText);
|
|
137
|
+
}
|
|
138
|
+
if (theme.headingFont) {
|
|
139
|
+
vars['--theme-heading-font'] = `"${theme.headingFont}", system-ui, sans-serif`;
|
|
140
|
+
}
|
|
141
|
+
if (theme.bodyFont) {
|
|
142
|
+
vars['--theme-body-font'] = `"${theme.bodyFont}", system-ui, sans-serif`;
|
|
143
|
+
}
|
|
144
|
+
if (theme.pageWidth) {
|
|
145
|
+
vars['--theme-page-width'] = `${theme.pageWidth}px`;
|
|
146
|
+
}
|
|
147
|
+
return vars;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Convert any theme to CSS custom properties
|
|
151
|
+
*/
|
|
152
|
+
export function anyThemeToCssVars(theme) {
|
|
153
|
+
if (!theme)
|
|
154
|
+
return {};
|
|
155
|
+
// Check for basic theme properties
|
|
156
|
+
if ('background' in theme && 'foreground' in theme) {
|
|
157
|
+
return themeToCssVars(theme);
|
|
158
|
+
}
|
|
159
|
+
return extendedThemeToCssVars(theme);
|
|
160
|
+
}
|
package/dist/utils/theme.d.ts
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import type { RGBColor } from '../types.js';
|
|
1
|
+
import type { RGBColor, RGBAColor, Color, BasicTheme, ExtendedTheme, BackgroundImage } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Type guard for RGBA color
|
|
4
|
+
*/
|
|
5
|
+
export declare function isRGBA(color: Color): color is RGBAColor;
|
|
2
6
|
/**
|
|
3
7
|
* Convert RGB color object to CSS rgb() string
|
|
4
8
|
*/
|
|
5
9
|
export declare function rgbToCSS(color: RGBColor): string;
|
|
10
|
+
/**
|
|
11
|
+
* Convert RGBA color object to CSS rgba() string
|
|
12
|
+
*/
|
|
13
|
+
export declare function rgbaToCSS(color: RGBAColor): string;
|
|
14
|
+
/**
|
|
15
|
+
* Convert Color (RGB or RGBA) to CSS string
|
|
16
|
+
*/
|
|
17
|
+
export declare function colorToCSS(color: Color): string;
|
|
6
18
|
/**
|
|
7
19
|
* Convert RGB color object to hex string
|
|
8
20
|
*/
|
|
9
21
|
export declare function rgbToHex(color: RGBColor): string;
|
|
22
|
+
/**
|
|
23
|
+
* Convert RGBA color object to hex string with alpha
|
|
24
|
+
*/
|
|
25
|
+
export declare function rgbaToHex(color: RGBAColor): string;
|
|
10
26
|
/**
|
|
11
27
|
* Get theme CSS variables from BasicTheme
|
|
12
28
|
*/
|
|
@@ -16,3 +32,34 @@ export declare function getThemeVars(theme: {
|
|
|
16
32
|
accent: RGBColor;
|
|
17
33
|
accentForeground: RGBColor;
|
|
18
34
|
}): Record<string, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Get CSS for background image
|
|
37
|
+
*/
|
|
38
|
+
export declare function getBackgroundImageCSS(bgImage: BackgroundImage | undefined): Record<string, string>;
|
|
39
|
+
/**
|
|
40
|
+
* Convert BasicTheme to CSS custom properties
|
|
41
|
+
*/
|
|
42
|
+
export declare function basicThemeToCssVars(theme: BasicTheme): Record<string, string>;
|
|
43
|
+
/**
|
|
44
|
+
* Convert ExtendedTheme to CSS custom properties
|
|
45
|
+
*/
|
|
46
|
+
export declare function extendedThemeToCssVars(theme: ExtendedTheme): Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* Convert any theme (Basic or Extended) to CSS custom properties
|
|
49
|
+
*/
|
|
50
|
+
export declare function themeToCssVars(theme: BasicTheme | ExtendedTheme | undefined): Record<string, string>;
|
|
51
|
+
/**
|
|
52
|
+
* Get font family CSS with fallbacks
|
|
53
|
+
*/
|
|
54
|
+
export declare function getFontFamilyCSS(fontName: string | undefined, fallback: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Generate Google Fonts URL for custom fonts
|
|
57
|
+
*/
|
|
58
|
+
export declare function getGoogleFontsUrl(fonts: {
|
|
59
|
+
headingFont?: string;
|
|
60
|
+
bodyFont?: string;
|
|
61
|
+
}): string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Get all theme CSS variables including defaults
|
|
64
|
+
*/
|
|
65
|
+
export declare function getAllThemeVars(theme?: BasicTheme | ExtendedTheme): Record<string, string>;
|
package/dist/utils/theme.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard for RGBA color
|
|
3
|
+
*/
|
|
4
|
+
export function isRGBA(color) {
|
|
5
|
+
return 'a' in color;
|
|
6
|
+
}
|
|
1
7
|
/**
|
|
2
8
|
* Convert RGB color object to CSS rgb() string
|
|
3
9
|
*/
|
|
4
10
|
export function rgbToCSS(color) {
|
|
5
11
|
return `rgb(${color.r}, ${color.g}, ${color.b})`;
|
|
6
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Convert RGBA color object to CSS rgba() string
|
|
15
|
+
*/
|
|
16
|
+
export function rgbaToCSS(color) {
|
|
17
|
+
// Alpha is 0-100, convert to 0-1
|
|
18
|
+
const alpha = color.a / 100;
|
|
19
|
+
return `rgba(${color.r}, ${color.g}, ${color.b}, ${alpha})`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Convert Color (RGB or RGBA) to CSS string
|
|
23
|
+
*/
|
|
24
|
+
export function colorToCSS(color) {
|
|
25
|
+
if (isRGBA(color)) {
|
|
26
|
+
return rgbaToCSS(color);
|
|
27
|
+
}
|
|
28
|
+
return rgbToCSS(color);
|
|
29
|
+
}
|
|
7
30
|
/**
|
|
8
31
|
* Convert RGB color object to hex string
|
|
9
32
|
*/
|
|
@@ -11,6 +34,14 @@ export function rgbToHex(color) {
|
|
|
11
34
|
const toHex = (n) => n.toString(16).padStart(2, '0');
|
|
12
35
|
return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
|
|
13
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Convert RGBA color object to hex string with alpha
|
|
39
|
+
*/
|
|
40
|
+
export function rgbaToHex(color) {
|
|
41
|
+
const toHex = (n) => n.toString(16).padStart(2, '0');
|
|
42
|
+
const alpha = Math.round((color.a / 100) * 255);
|
|
43
|
+
return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}${toHex(alpha)}`;
|
|
44
|
+
}
|
|
14
45
|
/**
|
|
15
46
|
* Get theme CSS variables from BasicTheme
|
|
16
47
|
*/
|
|
@@ -22,3 +53,130 @@ export function getThemeVars(theme) {
|
|
|
22
53
|
'--theme-accent-foreground': rgbToCSS(theme.accentForeground)
|
|
23
54
|
};
|
|
24
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Get CSS for background image
|
|
58
|
+
*/
|
|
59
|
+
export function getBackgroundImageCSS(bgImage) {
|
|
60
|
+
if (!bgImage?.url)
|
|
61
|
+
return {};
|
|
62
|
+
const styles = {
|
|
63
|
+
'background-image': `url(${bgImage.url})`,
|
|
64
|
+
'background-size': 'cover',
|
|
65
|
+
'background-position': 'center',
|
|
66
|
+
'background-repeat': 'no-repeat'
|
|
67
|
+
};
|
|
68
|
+
if (bgImage.opacity !== undefined) {
|
|
69
|
+
// For opacity, we need to use a pseudo-element or overlay
|
|
70
|
+
// This is handled separately in the component
|
|
71
|
+
}
|
|
72
|
+
if (bgImage.blur !== undefined) {
|
|
73
|
+
styles['background-blur'] = `${bgImage.blur}px`;
|
|
74
|
+
}
|
|
75
|
+
return styles;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Convert BasicTheme to CSS custom properties
|
|
79
|
+
*/
|
|
80
|
+
export function basicThemeToCssVars(theme) {
|
|
81
|
+
return {
|
|
82
|
+
'--theme-background': rgbToCSS(theme.background),
|
|
83
|
+
'--theme-foreground': rgbToCSS(theme.foreground),
|
|
84
|
+
'--theme-accent': rgbToCSS(theme.accent),
|
|
85
|
+
'--theme-accent-foreground': rgbToCSS(theme.accentForeground)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Convert ExtendedTheme to CSS custom properties
|
|
90
|
+
*/
|
|
91
|
+
export function extendedThemeToCssVars(theme) {
|
|
92
|
+
const vars = {};
|
|
93
|
+
if (theme.backgroundColor) {
|
|
94
|
+
vars['--theme-background'] = colorToCSS(theme.backgroundColor);
|
|
95
|
+
}
|
|
96
|
+
if (theme.pageBackground) {
|
|
97
|
+
vars['--theme-page-background'] = colorToCSS(theme.pageBackground);
|
|
98
|
+
}
|
|
99
|
+
if (theme.primary) {
|
|
100
|
+
vars['--theme-primary'] = colorToCSS(theme.primary);
|
|
101
|
+
}
|
|
102
|
+
if (theme.accentBackground) {
|
|
103
|
+
vars['--theme-accent-background'] = colorToCSS(theme.accentBackground);
|
|
104
|
+
}
|
|
105
|
+
if (theme.accentText) {
|
|
106
|
+
vars['--theme-accent-text'] = colorToCSS(theme.accentText);
|
|
107
|
+
}
|
|
108
|
+
if (theme.headingFont) {
|
|
109
|
+
vars['--theme-heading-font'] = theme.headingFont;
|
|
110
|
+
}
|
|
111
|
+
if (theme.bodyFont) {
|
|
112
|
+
vars['--theme-body-font'] = theme.bodyFont;
|
|
113
|
+
}
|
|
114
|
+
if (theme.pageWidth) {
|
|
115
|
+
vars['--theme-page-width'] = `${theme.pageWidth}px`;
|
|
116
|
+
}
|
|
117
|
+
// Map extended theme properties to standard theme variables for compatibility
|
|
118
|
+
if (theme.backgroundColor) {
|
|
119
|
+
vars['--theme-background'] = colorToCSS(theme.backgroundColor);
|
|
120
|
+
}
|
|
121
|
+
// Use primary as accent if accent is not defined
|
|
122
|
+
if (theme.primary && !theme.accentBackground) {
|
|
123
|
+
vars['--theme-accent'] = colorToCSS(theme.primary);
|
|
124
|
+
}
|
|
125
|
+
return vars;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Convert any theme (Basic or Extended) to CSS custom properties
|
|
129
|
+
*/
|
|
130
|
+
export function themeToCssVars(theme) {
|
|
131
|
+
if (!theme)
|
|
132
|
+
return {};
|
|
133
|
+
// Check if it's a basic theme
|
|
134
|
+
if ('background' in theme && 'foreground' in theme && 'accent' in theme && 'accentForeground' in theme) {
|
|
135
|
+
return basicThemeToCssVars(theme);
|
|
136
|
+
}
|
|
137
|
+
// Otherwise treat as extended theme
|
|
138
|
+
return extendedThemeToCssVars(theme);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get font family CSS with fallbacks
|
|
142
|
+
*/
|
|
143
|
+
export function getFontFamilyCSS(fontName, fallback) {
|
|
144
|
+
if (!fontName)
|
|
145
|
+
return fallback;
|
|
146
|
+
return `"${fontName}", ${fallback}`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Generate Google Fonts URL for custom fonts
|
|
150
|
+
*/
|
|
151
|
+
export function getGoogleFontsUrl(fonts) {
|
|
152
|
+
const families = [];
|
|
153
|
+
if (fonts.headingFont) {
|
|
154
|
+
families.push(`${encodeURIComponent(fonts.headingFont)}:wght@400;600;700`);
|
|
155
|
+
}
|
|
156
|
+
if (fonts.bodyFont) {
|
|
157
|
+
families.push(`${encodeURIComponent(fonts.bodyFont)}:wght@400;500;600`);
|
|
158
|
+
}
|
|
159
|
+
if (families.length === 0)
|
|
160
|
+
return null;
|
|
161
|
+
return `https://fonts.googleapis.com/css2?family=${families.join('&family=')}&display=swap`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get all theme CSS variables including defaults
|
|
165
|
+
*/
|
|
166
|
+
export function getAllThemeVars(theme) {
|
|
167
|
+
const defaults = {
|
|
168
|
+
'--theme-background': 'rgb(255, 255, 255)',
|
|
169
|
+
'--theme-foreground': 'rgb(0, 0, 0)',
|
|
170
|
+
'--theme-accent': 'rgb(0, 0, 225)',
|
|
171
|
+
'--theme-accent-foreground': 'rgb(255, 255, 255)',
|
|
172
|
+
'--theme-page-background': 'rgb(255, 255, 255)',
|
|
173
|
+
'--theme-primary': 'rgb(0, 0, 225)',
|
|
174
|
+
'--theme-accent-background': 'rgb(0, 0, 225)',
|
|
175
|
+
'--theme-accent-text': 'rgb(255, 255, 255)',
|
|
176
|
+
'--theme-heading-font': 'system-ui, -apple-system, sans-serif',
|
|
177
|
+
'--theme-body-font': 'system-ui, -apple-system, sans-serif',
|
|
178
|
+
'--theme-page-width': '800px'
|
|
179
|
+
};
|
|
180
|
+
const themeVars = themeToCssVars(theme);
|
|
181
|
+
return { ...defaults, ...themeVars };
|
|
182
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ewanc26/svelte-standard-site",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "SvelteKit library for reading and writing AT Protocol longform content via site.standard.* records — with a complete design system, federated comments, publishing tools, and content verification.",
|
|
5
|
+
"author": "Ewan Croft",
|
|
5
6
|
"license": "AGPL-3.0-only",
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "Ewan Croft",
|
|
8
|
-
"url": "https://github.com/ewanc26"
|
|
9
|
-
},
|
|
10
7
|
"type": "module",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"svelte",
|
|
10
|
+
"sveltekit",
|
|
11
|
+
"atproto",
|
|
12
|
+
"at-protocol",
|
|
13
|
+
"bluesky",
|
|
14
|
+
"site-standard",
|
|
15
|
+
"blog",
|
|
16
|
+
"cms",
|
|
17
|
+
"design-system",
|
|
18
|
+
"components",
|
|
19
|
+
"dark-mode",
|
|
20
|
+
"light-mode",
|
|
21
|
+
"theme",
|
|
22
|
+
"publishing",
|
|
23
|
+
"federation",
|
|
24
|
+
"comments"
|
|
25
|
+
],
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/ewanc26/pkgs.git",
|
|
29
|
+
"directory": "packages/svelte-standard-site"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/ewanc26/pkgs/tree/main/packages/svelte-standard-site",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/ewanc26/pkgs/issues"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist",
|
|
40
|
+
"src/lib",
|
|
41
|
+
"README.md"
|
|
42
|
+
],
|
|
43
|
+
"sideEffects": [
|
|
44
|
+
"**/*.css"
|
|
45
|
+
],
|
|
11
46
|
"svelte": "./dist/index.js",
|
|
12
47
|
"types": "./dist/index.d.ts",
|
|
13
48
|
"exports": {
|
|
@@ -25,8 +60,8 @@
|
|
|
25
60
|
"default": "./dist/utils/content.js"
|
|
26
61
|
},
|
|
27
62
|
"./comments": {
|
|
28
|
-
"types": "./dist/utils/comments.d.ts",
|
|
29
|
-
"default": "./dist/utils/comments.js"
|
|
63
|
+
"types": "./dist/utils/native-comments.d.ts",
|
|
64
|
+
"default": "./dist/utils/native-comments.js"
|
|
30
65
|
},
|
|
31
66
|
"./verification": {
|
|
32
67
|
"types": "./dist/utils/verification.d.ts",
|
|
@@ -47,23 +82,23 @@
|
|
|
47
82
|
"default": "./dist/styles/themes.css"
|
|
48
83
|
}
|
|
49
84
|
},
|
|
50
|
-
"
|
|
51
|
-
"dist",
|
|
52
|
-
"src/lib",
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
"peerDependencies": {
|
|
62
|
-
"@sveltejs/kit": "^2.0.0",
|
|
63
|
-
"svelte": "^5.0.0"
|
|
85
|
+
"scripts": {
|
|
86
|
+
"build": "svelte-kit sync && svelte-package -i src/lib -o dist && publint",
|
|
87
|
+
"dev": "svelte-kit sync && svelte-package -i src/lib -o dist --watch",
|
|
88
|
+
"dev:app": "vite dev",
|
|
89
|
+
"preview": "vite preview",
|
|
90
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
91
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
92
|
+
"test": "vitest run",
|
|
93
|
+
"test:watch": "vitest",
|
|
94
|
+
"format": "prettier --write .",
|
|
95
|
+
"lint": "prettier --check ."
|
|
64
96
|
},
|
|
65
97
|
"dependencies": {
|
|
66
98
|
"@atproto/api": "^0.19.3",
|
|
99
|
+
"@ewanc26/atproto": "workspace:*",
|
|
100
|
+
"@ewanc26/tid": "workspace:*",
|
|
101
|
+
"@ewanc26/utils": "workspace:*",
|
|
67
102
|
"@lucide/svelte": "^0.577.0",
|
|
68
103
|
"katex": "^0.16.38",
|
|
69
104
|
"rehype-slug": "^6.0.0",
|
|
@@ -73,10 +108,11 @@
|
|
|
73
108
|
"remark-rehype": "^11.1.2",
|
|
74
109
|
"shiki": "^3.21.0",
|
|
75
110
|
"unified": "^11.0.5",
|
|
76
|
-
"zod": "^3.24.0"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"@
|
|
111
|
+
"zod": "^3.24.0"
|
|
112
|
+
},
|
|
113
|
+
"peerDependencies": {
|
|
114
|
+
"@sveltejs/kit": "^2.0.0",
|
|
115
|
+
"svelte": "^5.0.0"
|
|
80
116
|
},
|
|
81
117
|
"devDependencies": {
|
|
82
118
|
"@sveltejs/adapter-auto": "^7.0.1",
|
|
@@ -96,44 +132,5 @@
|
|
|
96
132
|
"typescript": "^5.9.3",
|
|
97
133
|
"vite": "^7.2.6",
|
|
98
134
|
"vitest": "^4.1.0"
|
|
99
|
-
},
|
|
100
|
-
"keywords": [
|
|
101
|
-
"svelte",
|
|
102
|
-
"sveltekit",
|
|
103
|
-
"atproto",
|
|
104
|
-
"at-protocol",
|
|
105
|
-
"bluesky",
|
|
106
|
-
"site-standard",
|
|
107
|
-
"blog",
|
|
108
|
-
"cms",
|
|
109
|
-
"design-system",
|
|
110
|
-
"components",
|
|
111
|
-
"dark-mode",
|
|
112
|
-
"light-mode",
|
|
113
|
-
"theme",
|
|
114
|
-
"publishing",
|
|
115
|
-
"federation",
|
|
116
|
-
"comments"
|
|
117
|
-
],
|
|
118
|
-
"repository": {
|
|
119
|
-
"type": "git",
|
|
120
|
-
"url": "git+https://github.com/ewanc26/pkgs.git",
|
|
121
|
-
"directory": "packages/svelte-standard-site"
|
|
122
|
-
},
|
|
123
|
-
"homepage": "https://github.com/ewanc26/pkgs/tree/main/packages/svelte-standard-site",
|
|
124
|
-
"bugs": {
|
|
125
|
-
"url": "https://github.com/ewanc26/pkgs/issues"
|
|
126
|
-
},
|
|
127
|
-
"scripts": {
|
|
128
|
-
"build": "svelte-kit sync && svelte-package -i src/lib -o dist && publint",
|
|
129
|
-
"dev": "svelte-kit sync && svelte-package -i src/lib -o dist --watch",
|
|
130
|
-
"dev:app": "vite dev",
|
|
131
|
-
"preview": "vite preview",
|
|
132
|
-
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
133
|
-
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
134
|
-
"test": "vitest run",
|
|
135
|
-
"test:watch": "vitest",
|
|
136
|
-
"format": "prettier --write .",
|
|
137
|
-
"lint": "prettier --check ."
|
|
138
135
|
}
|
|
139
|
-
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Snippet for action items */
|
|
4
|
+
children: import('svelte').Snippet;
|
|
5
|
+
/** Position of the action bar */
|
|
6
|
+
position?: 'left' | 'bottom' | 'right';
|
|
7
|
+
/** Has theme applied */
|
|
8
|
+
hasTheme?: boolean;
|
|
9
|
+
/** Sticky positioning */
|
|
10
|
+
sticky?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { children, position = 'left', hasTheme = false, sticky = false }: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div
|
|
17
|
+
class="action-bar {position}"
|
|
18
|
+
class:themed={hasTheme}
|
|
19
|
+
class:sticky
|
|
20
|
+
>
|
|
21
|
+
{@render children()}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<style>
|
|
25
|
+
.action-bar {
|
|
26
|
+
display: flex;
|
|
27
|
+
gap: 0.5rem;
|
|
28
|
+
padding: 0.5rem;
|
|
29
|
+
border-radius: 0.5rem;
|
|
30
|
+
background-color: rgb(255 255 255);
|
|
31
|
+
box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.action-bar.themed {
|
|
35
|
+
background-color: var(--theme-page-background, var(--theme-background));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.action-bar.left {
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.action-bar.bottom {
|
|
44
|
+
flex-direction: row;
|
|
45
|
+
align-items: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.action-bar.right {
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
align-items: center;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.action-bar.sticky {
|
|
54
|
+
position: sticky;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.action-bar.left.sticky {
|
|
58
|
+
top: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.action-bar.bottom.sticky {
|
|
62
|
+
bottom: 1rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.action-bar :global(button) {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: center;
|
|
69
|
+
width: 2.5rem;
|
|
70
|
+
height: 2.5rem;
|
|
71
|
+
border-radius: 9999px;
|
|
72
|
+
background: transparent;
|
|
73
|
+
border: none;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
transition: background-color 0.15s;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.action-bar :global(button:hover) {
|
|
79
|
+
background-color: rgb(243 244 246);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.action-bar.themed :global(button:hover) {
|
|
83
|
+
background-color: color-mix(in srgb, var(--theme-foreground) 10%, transparent);
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
/** User's DID */
|
|
4
|
+
did: string;
|
|
5
|
+
/** User's handle (optional, for display) */
|
|
6
|
+
handle?: string;
|
|
7
|
+
/** User's display name */
|
|
8
|
+
displayName?: string;
|
|
9
|
+
/** Avatar image URL */
|
|
10
|
+
src?: string;
|
|
11
|
+
/** Size in pixels */
|
|
12
|
+
size?: number;
|
|
13
|
+
/** Whether to link to profile */
|
|
14
|
+
link?: boolean;
|
|
15
|
+
/** Has theme applied */
|
|
16
|
+
hasTheme?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const {
|
|
20
|
+
did,
|
|
21
|
+
handle,
|
|
22
|
+
displayName,
|
|
23
|
+
src,
|
|
24
|
+
size = 40,
|
|
25
|
+
link = true,
|
|
26
|
+
hasTheme = false
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
// Generate initials from display name or handle
|
|
30
|
+
const initials = $derived(
|
|
31
|
+
(displayName || handle || did)
|
|
32
|
+
.split(/[.\s_@]+/)
|
|
33
|
+
.slice(0, 2)
|
|
34
|
+
.map((s) => s.charAt(0).toUpperCase())
|
|
35
|
+
.join('')
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Profile URL
|
|
39
|
+
const profileUrl = $derived(`https://leaflet.pub/p/${did}`);
|
|
40
|
+
|
|
41
|
+
// Size classes
|
|
42
|
+
const sizeStyle = $derived(`width: ${size}px; height: ${size}px; font-size: ${size * 0.4}px;`);
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
{#if link}
|
|
46
|
+
<a
|
|
47
|
+
href={profileUrl}
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noopener noreferrer"
|
|
50
|
+
class="avatar-link"
|
|
51
|
+
title={displayName || handle || did}
|
|
52
|
+
>
|
|
53
|
+
{#if src}
|
|
54
|
+
<img
|
|
55
|
+
src={src}
|
|
56
|
+
alt={displayName || handle || 'Avatar'}
|
|
57
|
+
class="avatar rounded-full object-cover"
|
|
58
|
+
style={sizeStyle}
|
|
59
|
+
loading="lazy"
|
|
60
|
+
/>
|
|
61
|
+
{:else}
|
|
62
|
+
<div
|
|
63
|
+
class="avatar-placeholder rounded-full flex items-center justify-center font-medium"
|
|
64
|
+
class:themed={hasTheme}
|
|
65
|
+
style={sizeStyle}
|
|
66
|
+
>
|
|
67
|
+
{initials}
|
|
68
|
+
</div>
|
|
69
|
+
{/if}
|
|
70
|
+
</a>
|
|
71
|
+
{:else if src}
|
|
72
|
+
<img
|
|
73
|
+
src={src}
|
|
74
|
+
alt={displayName || handle || 'Avatar'}
|
|
75
|
+
class="avatar rounded-full object-cover"
|
|
76
|
+
style={sizeStyle}
|
|
77
|
+
loading="lazy"
|
|
78
|
+
/>
|
|
79
|
+
{:else}
|
|
80
|
+
<div
|
|
81
|
+
class="avatar-placeholder rounded-full flex items-center justify-center font-medium"
|
|
82
|
+
class:themed={hasTheme}
|
|
83
|
+
style={sizeStyle}
|
|
84
|
+
>
|
|
85
|
+
{initials}
|
|
86
|
+
</div>
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<style>
|
|
90
|
+
.avatar-link {
|
|
91
|
+
display: inline-block;
|
|
92
|
+
text-decoration: none;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.avatar-placeholder {
|
|
96
|
+
background-color: rgb(229 231 235);
|
|
97
|
+
color: rgb(107 114 128);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.avatar-placeholder.themed {
|
|
101
|
+
background-color: color-mix(in srgb, var(--theme-accent) 20%, transparent);
|
|
102
|
+
color: var(--theme-accent);
|
|
103
|
+
}
|
|
104
|
+
</style>
|