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 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