opentwig 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -5
- package/package.json +1 -1
- package/src/utils/configDefaults.js +5 -12
- package/src/utils/generateOGImage.js +1 -1
- package/src/utils/processCSS.js +12 -5
- package/src/utils/readImageAsBase64.js +2 -2
- package/src/utils/saveFiles.js +5 -2
- package/theme/default/components/avatar.js +12 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ OpenTwig is an open source personal link page generator that creates beautiful,
|
|
|
8
8
|
- 📱 **Mobile Responsive**: Optimized for all devices with mobile-first design
|
|
9
9
|
- 🚀 **Fast & Lightweight**: Generates static HTML/CSS with minimal dependencies
|
|
10
10
|
- 🔗 **Easy Link Management**: Simple JSON configuration for all your links
|
|
11
|
-
- 🖼️ **Avatar Support**: Custom profile pictures with automatic processing
|
|
11
|
+
- 🖼️ **Optional Avatar Support**: Custom profile pictures with automatic processing (completely optional)
|
|
12
12
|
- 📊 **Open Graph Images**: Auto-generated social media preview images
|
|
13
13
|
- 📱 **QR Code Generation**: Built-in QR codes for easy mobile sharing
|
|
14
14
|
- 🎭 **Modal Dialogs**: Support for rich content in footer links
|
|
@@ -38,6 +38,7 @@ npx opentwig
|
|
|
38
38
|
|
|
39
39
|
OpenTwig uses a simple JSON configuration file (`config.json`) to define your page. Here's the complete configuration structure:
|
|
40
40
|
|
|
41
|
+
### With Avatar (Optional)
|
|
41
42
|
```json
|
|
42
43
|
{
|
|
43
44
|
"theme": "default",
|
|
@@ -77,6 +78,39 @@ OpenTwig uses a simple JSON configuration file (`config.json`) to define your pa
|
|
|
77
78
|
}
|
|
78
79
|
```
|
|
79
80
|
|
|
81
|
+
### Without Avatar (Minimal Configuration)
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"theme": "default",
|
|
85
|
+
"url": "https://links.yourwebsite.com",
|
|
86
|
+
"title": "Your Name - opentwig 🌿",
|
|
87
|
+
"name": "Your Name",
|
|
88
|
+
"content": "Hello World! Here is my bio.",
|
|
89
|
+
"minify": true,
|
|
90
|
+
"links": [
|
|
91
|
+
{
|
|
92
|
+
"url": "https://twitter.com",
|
|
93
|
+
"title": "Twitter"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"url": "https://instagram.com",
|
|
97
|
+
"title": "Instagram"
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
"footerLinks": [
|
|
101
|
+
{
|
|
102
|
+
"title": "Contact",
|
|
103
|
+
"url": "mailto:mail@mail.com"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"share": {
|
|
107
|
+
"title": "Your Name - opentwig 🌿",
|
|
108
|
+
"url": "https://links.yourwebsite.com",
|
|
109
|
+
"text": "Share"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
80
114
|
### Configuration Options
|
|
81
115
|
|
|
82
116
|
| Option | Type | Description |
|
|
@@ -87,12 +121,38 @@ OpenTwig uses a simple JSON configuration file (`config.json`) to define your pa
|
|
|
87
121
|
| `name` | string | Your display name |
|
|
88
122
|
| `content` | string | Bio/description text |
|
|
89
123
|
| `minify` | boolean | Enable CSS minification (default: `true`) |
|
|
90
|
-
| `avatar` | object | Avatar image configuration |
|
|
91
|
-
| `avatar.path` | string | Path to your avatar image |
|
|
124
|
+
| `avatar` | object | **Optional** Avatar image configuration |
|
|
125
|
+
| `avatar.path` | string | **Optional** Path to your avatar image (supports PNG, JPG, JPEG, WebP) |
|
|
92
126
|
| `links` | array | Array of link objects with `url` and `title` |
|
|
93
127
|
| `footerLinks` | array | Footer links (can be URLs or modal dialogs) |
|
|
94
128
|
| `share` | object | Web Share API configuration |
|
|
95
129
|
|
|
130
|
+
### 🖼️ Avatar Configuration
|
|
131
|
+
|
|
132
|
+
The avatar feature is completely optional. If you don't include the `avatar` object in your configuration, no avatar will be displayed on your page.
|
|
133
|
+
|
|
134
|
+
**Supported image formats:**
|
|
135
|
+
- PNG
|
|
136
|
+
- JPG/JPEG
|
|
137
|
+
- WebP
|
|
138
|
+
|
|
139
|
+
**Avatar processing:**
|
|
140
|
+
- Images are automatically optimized and resized
|
|
141
|
+
- Processed avatar is saved as `avatar.png` in the output directory
|
|
142
|
+
- Original aspect ratio is preserved
|
|
143
|
+
- Images are compressed for optimal web performance
|
|
144
|
+
|
|
145
|
+
**Example avatar configuration:**
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"avatar": {
|
|
149
|
+
"path": "./my-photo.jpg"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Note:** If you don't want an avatar, simply omit the `avatar` object from your configuration entirely.
|
|
155
|
+
|
|
96
156
|
## 🎨 Themes
|
|
97
157
|
|
|
98
158
|
OpenTwig includes 4 beautiful themes:
|
|
@@ -103,7 +163,7 @@ OpenTwig includes 4 beautiful themes:
|
|
|
103
163
|
- **Colorful**: Vibrant color scheme
|
|
104
164
|
|
|
105
165
|
All themes are mobile-responsive and include:
|
|
106
|
-
-
|
|
166
|
+
- Optional custom avatar display
|
|
107
167
|
- Link buttons with hover effects
|
|
108
168
|
- Modal dialogs for rich content
|
|
109
169
|
- QR code integration
|
|
@@ -128,7 +188,7 @@ OpenTwig generates the following files in the `dist/` directory:
|
|
|
128
188
|
|
|
129
189
|
- `index.html` - Main HTML page
|
|
130
190
|
- `style.css` - Processed and optimized CSS
|
|
131
|
-
- `avatar.png` - Processed avatar image
|
|
191
|
+
- `avatar.png` - Processed avatar image *(only generated if avatar is configured)*
|
|
132
192
|
- `og-image.jpg` - Open Graph image for social sharing
|
|
133
193
|
- `qr.svg` - QR code for mobile sharing
|
|
134
194
|
|
package/package.json
CHANGED
|
@@ -16,10 +16,7 @@ const DEFAULT_CONFIG = {
|
|
|
16
16
|
content: 'Hello World! Here is my bio.',
|
|
17
17
|
url: 'https://links.yourwebsite.com',
|
|
18
18
|
|
|
19
|
-
// Avatar settings
|
|
20
|
-
avatar: {
|
|
21
|
-
path: './avatar.png'
|
|
22
|
-
},
|
|
19
|
+
// Avatar settings - no default, user must explicitly define if they want an avatar
|
|
23
20
|
|
|
24
21
|
// Links settings
|
|
25
22
|
links: [],
|
|
@@ -94,19 +91,15 @@ const SAMPLE_CONFIG = {
|
|
|
94
91
|
const applyDefaults = (config) => {
|
|
95
92
|
const result = { ...config };
|
|
96
93
|
|
|
97
|
-
// Apply defaults for each key
|
|
94
|
+
// Apply defaults for each key (excluding avatar which has special handling)
|
|
98
95
|
Object.keys(DEFAULT_CONFIG).forEach(key => {
|
|
99
|
-
if (result[key] === undefined || result[key] === null) {
|
|
96
|
+
if (key !== 'avatar' && (result[key] === undefined || result[key] === null)) {
|
|
100
97
|
result[key] = DEFAULT_CONFIG[key];
|
|
101
98
|
}
|
|
102
99
|
});
|
|
103
100
|
|
|
104
|
-
// Special handling for
|
|
105
|
-
|
|
106
|
-
result.avatar = { ...DEFAULT_CONFIG.avatar, ...result.avatar };
|
|
107
|
-
} else if (!result.avatar) {
|
|
108
|
-
result.avatar = { ...DEFAULT_CONFIG.avatar };
|
|
109
|
-
}
|
|
101
|
+
// Special handling for avatar - no defaults, user must explicitly define
|
|
102
|
+
// If avatar is undefined, null, false, or an object, leave it as is
|
|
110
103
|
|
|
111
104
|
if (result.share && typeof result.share === 'object') {
|
|
112
105
|
result.share = { ...DEFAULT_CONFIG.share, ...result.share };
|
|
@@ -2,7 +2,7 @@ const sharp = require('sharp');
|
|
|
2
2
|
const readImageAsBase64 = require('./readImageAsBase64');
|
|
3
3
|
|
|
4
4
|
module.exports = async function({name, content, avatar}) {
|
|
5
|
-
const avatarBase64 = readImageAsBase64(avatar.path);
|
|
5
|
+
const avatarBase64 = avatar && avatar.path ? readImageAsBase64(avatar.path) : null;
|
|
6
6
|
// Create SVG with the same dimensions and styling as the original HTML
|
|
7
7
|
const svg = `
|
|
8
8
|
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
|
package/src/utils/processCSS.js
CHANGED
|
@@ -6,11 +6,18 @@ const postcss = require('postcss');
|
|
|
6
6
|
const minify = require('postcss-minify');
|
|
7
7
|
|
|
8
8
|
module.exports = async function(config) {
|
|
9
|
-
//
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
// Try package directory first (for NPX), then current directory (for local dev)
|
|
10
|
+
const packageDir = path.dirname(require.main.filename);
|
|
11
|
+
const currentDir = process.cwd();
|
|
12
|
+
|
|
13
|
+
const cssPaths = [
|
|
14
|
+
path.join(packageDir, '..', 'theme', config.theme, 'style.css'), // NPX package
|
|
15
|
+
path.join(currentDir, 'theme', config.theme, 'style.css') // Local development
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const cssPath = cssPaths.find(p => fs.existsSync(p));
|
|
19
|
+
|
|
20
|
+
if (!cssPath) {
|
|
14
21
|
return null;
|
|
15
22
|
}
|
|
16
23
|
|
|
@@ -9,8 +9,8 @@ module.exports = function(filePath) {
|
|
|
9
9
|
|
|
10
10
|
const absolutePath = path.join(cwd, filePath);
|
|
11
11
|
if (!fs.existsSync(absolutePath)) {
|
|
12
|
-
console.
|
|
13
|
-
|
|
12
|
+
console.warn(`Avatar file not found: ${absolutePath}. Continuing without avatar.`);
|
|
13
|
+
return '';
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const extension = path.extname(absolutePath).toLowerCase();
|
package/src/utils/saveFiles.js
CHANGED
|
@@ -17,8 +17,11 @@ module.exports = function(html, css, avatar,ogImage, qrImage) {
|
|
|
17
17
|
fs.writeFileSync(path.join(distDir, 'style.css'), css);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
if (avatar
|
|
21
|
-
|
|
20
|
+
if (avatar && avatar.path && fs.existsSync(path.join(cwd, avatar.path))) {
|
|
21
|
+
// Get the original file extension from the avatar path
|
|
22
|
+
const originalExtension = path.extname(avatar.path);
|
|
23
|
+
const avatarFileName = `avatar${originalExtension}`;
|
|
24
|
+
fs.copyFileSync(path.join(cwd, avatar.path), path.join(distDir, avatarFileName));
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
// Write the generated OG Image
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
module.exports = function() {
|
|
1
|
+
module.exports = function({avatar}) {
|
|
2
|
+
// If no avatar is defined or avatar path is not provided, return empty string
|
|
3
|
+
if (!avatar || !avatar.path) {
|
|
4
|
+
return '';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Get the original file extension from the avatar path
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const originalExtension = path.extname(avatar.path);
|
|
10
|
+
const avatarFileName = `avatar${originalExtension}`;
|
|
11
|
+
|
|
2
12
|
return `
|
|
3
13
|
<div class="avatar">
|
|
4
|
-
<img src="
|
|
14
|
+
<img src="./${avatarFileName}" alt="Avatar" />
|
|
5
15
|
</div>
|
|
6
16
|
`;
|
|
7
17
|
}
|