opentwig 1.0.5 → 1.1.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/AGENTS.md +323 -0
- package/API.md +582 -0
- package/CODE_OF_CONDUCT.md +91 -0
- package/CONTRIBUTING.md +312 -0
- package/README.md +171 -7
- package/SECURITY.md +56 -0
- package/THEME_DEVELOPMENT.md +388 -0
- package/package.json +19 -3
- package/src/constants.js +14 -2
- package/src/index.js +14 -2
- package/src/live-ui/editor.js +173 -0
- package/src/live-ui/preview.js +77 -0
- package/src/live-ui/sidebar.js +523 -0
- package/src/utils/escapeHTML.js +10 -0
- package/src/utils/generateOGImage.js +51 -10
- package/src/utils/parseArgs.js +33 -2
- package/src/utils/readImageAsBase64.js +16 -4
- package/src/utils/setupWatcher.js +69 -0
- package/src/utils/showHelp.js +15 -2
- package/src/utils/startLiveServer.js +218 -0
- package/src/utils/websocketServer.js +53 -0
- package/test-og.js +40 -0
- package/theme/dark/style.css +1 -0
- package/theme/default/index.js +10 -8
- package/validateConfig.js +59 -0
- package/vitest.config.js +11 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# OpenTwig Agent Guidelines
|
|
2
|
+
|
|
3
|
+
This file provides guidelines for agentic coding assistants working on the OpenTwig codebase.
|
|
4
|
+
|
|
5
|
+
## Build / Lint / Test Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Run the CLI tool
|
|
9
|
+
npm start
|
|
10
|
+
# or: node src/index.js
|
|
11
|
+
|
|
12
|
+
# CLI options
|
|
13
|
+
npm start -- --help # Show help information
|
|
14
|
+
npm start -- --init # Create sample config.json
|
|
15
|
+
npm start -- --validate-config # Validate config.json
|
|
16
|
+
npm start -- --live # Start live preview with config editor
|
|
17
|
+
|
|
18
|
+
# Test Open Graph image generation
|
|
19
|
+
npm run test-og
|
|
20
|
+
|
|
21
|
+
# Run automated tests (Vitest)
|
|
22
|
+
npm test # Run tests in watch mode
|
|
23
|
+
npm run test:run # Run tests once
|
|
24
|
+
npm run test:coverage # Run tests with coverage report
|
|
25
|
+
|
|
26
|
+
# Start live preview mode
|
|
27
|
+
npm run live
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Project Overview
|
|
31
|
+
|
|
32
|
+
OpenTwig is a Node.js CLI tool that generates static "link in bio" pages. It uses CommonJS modules and generates HTML, CSS, QR codes, and Open Graph images from a JSON configuration file.
|
|
33
|
+
|
|
34
|
+
**Core technologies:** Node.js (v14+), CommonJS, PostCSS, Sharp, qrcode, html-minifier-terser, Express, WebSocket, Chokidar
|
|
35
|
+
|
|
36
|
+
## Code Style Guidelines
|
|
37
|
+
|
|
38
|
+
### Module System
|
|
39
|
+
- **Use CommonJS exclusively** - no ES6 imports/exports
|
|
40
|
+
- Import: `const moduleName = require('./path/to/module');`
|
|
41
|
+
- Export: `module.exports = functionName;` or `module.exports = { name, value };`
|
|
42
|
+
- Main entry point includes shebang: `#! /usr/bin/env node`
|
|
43
|
+
|
|
44
|
+
### File Organization
|
|
45
|
+
- **Single function per file** preferred in `src/utils/`
|
|
46
|
+
- Use descriptive filenames in camelCase (e.g., `buildPage.js`, `generateQR.js`)
|
|
47
|
+
- Theme structure: `theme/{themeName}/index.js`, `style.css`, `components/`
|
|
48
|
+
- Utilities in `src/utils/`, constants in `src/constants.js`
|
|
49
|
+
|
|
50
|
+
### Naming Conventions
|
|
51
|
+
- **Variables/Functions**: camelCase (`const buildPage = async (config) => {}`)
|
|
52
|
+
- **Constants**: UPPER_SNAKE_CASE (`const OUTPUT_FILES = { HTML: 'index.html' }`)
|
|
53
|
+
- **Files**: camelCase for JS files (e.g., `loadConfig.js`)
|
|
54
|
+
- **Destructured params**: Use descriptive names (`function({title, url, name})`)
|
|
55
|
+
|
|
56
|
+
### Code Patterns
|
|
57
|
+
|
|
58
|
+
**Function exports:**
|
|
59
|
+
```javascript
|
|
60
|
+
// Pattern: Immediately export a function
|
|
61
|
+
module.exports = async function(config) {
|
|
62
|
+
// implementation
|
|
63
|
+
};
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Async/await:**
|
|
67
|
+
```javascript
|
|
68
|
+
const buildPage = async (config) => {
|
|
69
|
+
try {
|
|
70
|
+
const theme = loadTheme(config);
|
|
71
|
+
const html = await generateHTML(config, theme);
|
|
72
|
+
return { html, theme };
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`Failed to build page: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Template literals for HTML:**
|
|
80
|
+
```javascript
|
|
81
|
+
module.exports = function({title, name}) {
|
|
82
|
+
return `<!DOCTYPE html>
|
|
83
|
+
<html>
|
|
84
|
+
<head><title>${escapeHTML(title)}</title></head>
|
|
85
|
+
<body><h1>${escapeHTML(name)}</h1></body>
|
|
86
|
+
</html>`;
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Error Handling
|
|
91
|
+
- Always use try-catch for async functions
|
|
92
|
+
- Log errors with `console.error()`
|
|
93
|
+
- Use `process.exit(1)` on fatal errors, `process.exit(0)` on success
|
|
94
|
+
- Prefix error messages with context (e.g., `ERROR_PREFIX`)
|
|
95
|
+
- Centralize error messages in `src/constants.js`
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
try {
|
|
99
|
+
const result = await someAsyncOperation();
|
|
100
|
+
return result;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(`Operation failed: ${error.message}`);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Security
|
|
107
|
+
- Always escape HTML output using `escapeHTML()` utility
|
|
108
|
+
- Always escape XML for OG images using `escapeXml()`
|
|
109
|
+
- Validate config fields in `configDefaults.js`
|
|
110
|
+
- Sanitize all user-generated content
|
|
111
|
+
|
|
112
|
+
### Documentation
|
|
113
|
+
- Add JSDoc comments for functions with @param and @returns
|
|
114
|
+
- Add module-level docstrings describing purpose
|
|
115
|
+
- Comment non-obvious logic
|
|
116
|
+
- Example:
|
|
117
|
+
```javascript
|
|
118
|
+
/**
|
|
119
|
+
* Load theme configuration from theme directory
|
|
120
|
+
* @param {Object} config - User configuration object
|
|
121
|
+
* @returns {Function} Theme template function
|
|
122
|
+
*/
|
|
123
|
+
module.exports = function(config) { ... };
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Formatting
|
|
127
|
+
- **JavaScript**: 4-space indentation
|
|
128
|
+
- **CSS**: 2-space indentation
|
|
129
|
+
- Use meaningful variable names
|
|
130
|
+
- Keep functions focused and small
|
|
131
|
+
- Prefer template literals over string concatenation
|
|
132
|
+
- No trailing whitespace
|
|
133
|
+
|
|
134
|
+
### Constants
|
|
135
|
+
Centralize all constants in `src/constants.js`:
|
|
136
|
+
- File paths and names
|
|
137
|
+
- CLI options
|
|
138
|
+
- Messages (ERROR_PREFIX, SUCCESS_PREFIX, etc.)
|
|
139
|
+
- Default values
|
|
140
|
+
- Configuration validation requirements
|
|
141
|
+
|
|
142
|
+
### Component Pattern (Themes)
|
|
143
|
+
Theme components in `theme/*/components/` export functions:
|
|
144
|
+
```javascript
|
|
145
|
+
module.exports = function({link}) {
|
|
146
|
+
return `<a href="${link.url}" target="_blank" rel="noopener">
|
|
147
|
+
<span>${link.title}</span>
|
|
148
|
+
</a>`;
|
|
149
|
+
};
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### File Paths
|
|
153
|
+
- Use `path.join()` for path construction
|
|
154
|
+
- Use `path.dirname(require.main.filename)` for package directory (NPX compatibility)
|
|
155
|
+
- Use `process.cwd()` for current working directory
|
|
156
|
+
- Check file existence with `fs.existsSync()` before operations
|
|
157
|
+
|
|
158
|
+
## Key Files Reference
|
|
159
|
+
|
|
160
|
+
- `src/index.js` - Main CLI entry point with argument parsing
|
|
161
|
+
- `src/constants.js` - Centralized constants (including LIVE_MODE settings)
|
|
162
|
+
- `src/utils/loadConfig.js` - Load and validate config.json
|
|
163
|
+
- `src/utils/buildPage.js` - Orchestrate page generation
|
|
164
|
+
- `src/utils/generateHTML.js` - HTML generation with minification
|
|
165
|
+
- `src/utils/processCSS.js` - CSS processing with PostCSS
|
|
166
|
+
- `src/utils/generateOGImage.js` - Open Graph image generation
|
|
167
|
+
- `src/utils/generateQR.js` - QR code generation
|
|
168
|
+
- `src/utils/saveFiles.js` - Save all output files to dist/
|
|
169
|
+
- `src/utils/configDefaults.js` - Default values and validation
|
|
170
|
+
- `src/utils/startLiveServer.js` - Live preview server (Express + WebSocket)
|
|
171
|
+
- `src/utils/websocketServer.js` - WebSocket connection management
|
|
172
|
+
- `src/utils/setupWatcher.js` - Config file change watcher
|
|
173
|
+
- `src/live-ui/index.html` - Live editor main page
|
|
174
|
+
- `src/live-ui/styles.css` - Live editor styles
|
|
175
|
+
- `src/live-ui/preview.js` - Preview iframe management
|
|
176
|
+
- `src/live-ui/editor.js` - Config editor logic
|
|
177
|
+
- `src/live-ui/sidebar.js` - Sidebar components and form rendering
|
|
178
|
+
- `theme/*/index.js` - Theme-specific HTML templates
|
|
179
|
+
- `validateConfig.js` - Config validation utility
|
|
180
|
+
|
|
181
|
+
## Output Files
|
|
182
|
+
|
|
183
|
+
All generated files go to `dist/` directory:
|
|
184
|
+
- `index.html` - Main page
|
|
185
|
+
- `style.css` - Processed CSS
|
|
186
|
+
- `avatar.{ext}` - User avatar (if configured)
|
|
187
|
+
- `og-image.jpg` - Open Graph preview image
|
|
188
|
+
- `qr.svg` - QR code
|
|
189
|
+
|
|
190
|
+
## Live Mode Architecture
|
|
191
|
+
|
|
192
|
+
### Overview
|
|
193
|
+
Live mode (`--live` flag) provides an interactive development environment with real-time preview and configuration editing.
|
|
194
|
+
|
|
195
|
+
### Components
|
|
196
|
+
|
|
197
|
+
**Backend (Node.js):**
|
|
198
|
+
- `startLiveServer.js` - Express server with WebSocket support
|
|
199
|
+
- HTTP endpoints: `/api/config`, `/api/themes`, `/api/avatar`, `/api/validate`
|
|
200
|
+
- Static file serving for `dist/` and `src/live-ui/`
|
|
201
|
+
- File upload handling (multer)
|
|
202
|
+
- `websocketServer.js` - WebSocket server for real-time updates
|
|
203
|
+
- Broadcast events: `reload`, `config-update`, `theme-change`
|
|
204
|
+
- Client connection management
|
|
205
|
+
- `setupWatcher.js` - Config file watcher
|
|
206
|
+
- Chokidar-based file watching
|
|
207
|
+
- Debounced change detection
|
|
208
|
+
- Auto rebuild on config changes
|
|
209
|
+
|
|
210
|
+
**Frontend (Vanilla JS):**
|
|
211
|
+
- `index.html` - Main editor page (Preview on left, Sidebar on right)
|
|
212
|
+
- `preview.js` - Preview iframe management and WebSocket client
|
|
213
|
+
- `editor.js` - Config API communication and auto-save
|
|
214
|
+
- `sidebar.js` - Dynamic form generation and event handling
|
|
215
|
+
|
|
216
|
+
### Features
|
|
217
|
+
|
|
218
|
+
**Editor Features:**
|
|
219
|
+
- Theme selection (default, dark, minimal, colorful)
|
|
220
|
+
- Profile editing (URL, name, bio, avatar upload)
|
|
221
|
+
- Links management (add, edit, delete, reorder)
|
|
222
|
+
- Footer links management (URL or modal content)
|
|
223
|
+
- Share settings configuration
|
|
224
|
+
- Advanced settings (minify CSS)
|
|
225
|
+
- Auto-save with debounce (500ms)
|
|
226
|
+
- Export config as JSON
|
|
227
|
+
|
|
228
|
+
**Real-time Updates:**
|
|
229
|
+
- Config changes → WebSocket broadcast → Preview reload
|
|
230
|
+
- File watcher → Rebuild → WebSocket broadcast
|
|
231
|
+
- Avatar upload → Save → Rebuild → Preview update
|
|
232
|
+
|
|
233
|
+
### Layout
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
┌─────────────────────────────────────────────┐
|
|
237
|
+
│ Header: OpenTwig Live Editor [Save][Export] │
|
|
238
|
+
├──────────────────────────────┬──────────────┤
|
|
239
|
+
│ │ │
|
|
240
|
+
│ Preview Iframe │ Sidebar │
|
|
241
|
+
│ (dist/index.html embedded) │ │
|
|
242
|
+
│ │ - Theme │
|
|
243
|
+
│ │ - Profile │
|
|
244
|
+
│ │ - Links │
|
|
245
|
+
│ │ - Footer │
|
|
246
|
+
│ │ - Share │
|
|
247
|
+
│ │ - Advanced │
|
|
248
|
+
│ │ │
|
|
249
|
+
├──────────────────────────────┴──────────────┤
|
|
250
|
+
│ Status: Connected | Auto-save: ON │
|
|
251
|
+
└─────────────────────────────────────────────┘
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### WebSocket Events
|
|
255
|
+
|
|
256
|
+
**Server → Client:**
|
|
257
|
+
```json
|
|
258
|
+
{
|
|
259
|
+
"type": "reload"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
{
|
|
263
|
+
"type": "config-update",
|
|
264
|
+
"config": { ... }
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
{
|
|
268
|
+
"type": "theme-change",
|
|
269
|
+
"theme": "dark"
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**API Endpoints:**
|
|
274
|
+
|
|
275
|
+
**GET /api/config**
|
|
276
|
+
- Returns current config from config.json
|
|
277
|
+
- Creates sample config if not exists
|
|
278
|
+
|
|
279
|
+
**POST /api/config**
|
|
280
|
+
- Accepts config JSON
|
|
281
|
+
- Validates and saves to config.json
|
|
282
|
+
- Triggers rebuild and WebSocket broadcast
|
|
283
|
+
|
|
284
|
+
**POST /api/avatar**
|
|
285
|
+
- Accepts multipart form data with avatar file
|
|
286
|
+
- Saves to working directory
|
|
287
|
+
- Returns file path
|
|
288
|
+
|
|
289
|
+
**GET /api/themes**
|
|
290
|
+
- Returns array of available theme names
|
|
291
|
+
|
|
292
|
+
**GET /api/validate?config=...**
|
|
293
|
+
- Validates config JSON
|
|
294
|
+
- Returns errors and warnings
|
|
295
|
+
|
|
296
|
+
**GET /api/status**
|
|
297
|
+
- Returns server status (connected clients, config path, etc.)
|
|
298
|
+
|
|
299
|
+
## Testing Approach
|
|
300
|
+
|
|
301
|
+
The project uses Vitest for automated testing:
|
|
302
|
+
1. Run `npm test` to run tests in watch mode
|
|
303
|
+
2. Run `npm run test:run` to run tests once
|
|
304
|
+
3. Run `npm run test:coverage` to run tests with coverage report
|
|
305
|
+
|
|
306
|
+
For manual testing:
|
|
307
|
+
1. Create a config.json using `npm start -- --init`
|
|
308
|
+
2. Run `npm start` to generate the page
|
|
309
|
+
3. Verify `dist/` output files are generated correctly
|
|
310
|
+
4. Test all themes by modifying config.json
|
|
311
|
+
5. Test edge cases: missing avatar, empty config, long text
|
|
312
|
+
6. Verify HTML is valid (open in browser)
|
|
313
|
+
7. Check CSS renders correctly
|
|
314
|
+
8. Confirm images are processed properly
|
|
315
|
+
|
|
316
|
+
## When Making Changes
|
|
317
|
+
|
|
318
|
+
1. **Always read the file first** - Use the Read tool before editing
|
|
319
|
+
2. **Follow existing patterns** - Mimic code style and structure
|
|
320
|
+
3. **Test thoroughly** - Manual testing after changes
|
|
321
|
+
4. **Add JSDoc comments** - Document new functions
|
|
322
|
+
5. **Update constants** if needed - Add new constants to `src/constants.js`
|
|
323
|
+
6. **Keep it simple** - One function per file, focused responsibilities
|