@mise-en-scene/server 0.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.
Files changed (98) hide show
  1. package/README.md +221 -0
  2. package/dist/export/index.d.ts +10 -0
  3. package/dist/export/index.d.ts.map +1 -0
  4. package/dist/export/index.js +53 -0
  5. package/dist/export/index.js.map +1 -0
  6. package/dist/export/pdf-export.d.ts +8 -0
  7. package/dist/export/pdf-export.d.ts.map +1 -0
  8. package/dist/export/pdf-export.js +17 -0
  9. package/dist/export/pdf-export.js.map +1 -0
  10. package/dist/export/presets.d.ts +4 -0
  11. package/dist/export/presets.d.ts.map +1 -0
  12. package/dist/export/presets.js +22 -0
  13. package/dist/export/presets.js.map +1 -0
  14. package/dist/export/puppeteer-manager.d.ts +8 -0
  15. package/dist/export/puppeteer-manager.d.ts.map +1 -0
  16. package/dist/export/puppeteer-manager.js +23 -0
  17. package/dist/export/puppeteer-manager.js.map +1 -0
  18. package/dist/export/raster-export.d.ts +11 -0
  19. package/dist/export/raster-export.d.ts.map +1 -0
  20. package/dist/export/raster-export.js +22 -0
  21. package/dist/export/raster-export.js.map +1 -0
  22. package/dist/export/svg-export.d.ts +2 -0
  23. package/dist/export/svg-export.d.ts.map +1 -0
  24. package/dist/export/svg-export.js +24 -0
  25. package/dist/export/svg-export.js.map +1 -0
  26. package/dist/feedback.d.ts +4 -0
  27. package/dist/feedback.d.ts.map +1 -0
  28. package/dist/feedback.js +23 -0
  29. package/dist/feedback.js.map +1 -0
  30. package/dist/http-server.d.ts +15 -0
  31. package/dist/http-server.d.ts.map +1 -0
  32. package/dist/http-server.js +113 -0
  33. package/dist/http-server.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +30 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/mcp-server.d.ts +435 -0
  39. package/dist/mcp-server.d.ts.map +1 -0
  40. package/dist/mcp-server.js +220 -0
  41. package/dist/mcp-server.js.map +1 -0
  42. package/dist/preview.d.ts +2 -0
  43. package/dist/preview.d.ts.map +1 -0
  44. package/dist/preview.js +12 -0
  45. package/dist/preview.js.map +1 -0
  46. package/dist/screenshot.d.ts +7 -0
  47. package/dist/screenshot.d.ts.map +1 -0
  48. package/dist/screenshot.js +20 -0
  49. package/dist/screenshot.js.map +1 -0
  50. package/dist/types.d.ts +84 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +2 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/typography/analyze-url.d.ts +10 -0
  55. package/dist/typography/analyze-url.d.ts.map +1 -0
  56. package/dist/typography/analyze-url.js +28 -0
  57. package/dist/typography/analyze-url.js.map +1 -0
  58. package/dist/typography/classify.d.ts +9 -0
  59. package/dist/typography/classify.d.ts.map +1 -0
  60. package/dist/typography/classify.js +15 -0
  61. package/dist/typography/classify.js.map +1 -0
  62. package/dist/typography/embed.d.ts +5 -0
  63. package/dist/typography/embed.d.ts.map +1 -0
  64. package/dist/typography/embed.js +33 -0
  65. package/dist/typography/embed.js.map +1 -0
  66. package/dist/typography/mood-match.d.ts +3 -0
  67. package/dist/typography/mood-match.d.ts.map +1 -0
  68. package/dist/typography/mood-match.js +21 -0
  69. package/dist/typography/mood-match.js.map +1 -0
  70. package/dist/typography/registry.d.ts +23 -0
  71. package/dist/typography/registry.d.ts.map +1 -0
  72. package/dist/typography/registry.js +103 -0
  73. package/dist/typography/registry.js.map +1 -0
  74. package/dist/typography/scale.d.ts +3 -0
  75. package/dist/typography/scale.d.ts.map +1 -0
  76. package/dist/typography/scale.js +45 -0
  77. package/dist/typography/scale.js.map +1 -0
  78. package/dist/typography/suggest.d.ts +9 -0
  79. package/dist/typography/suggest.d.ts.map +1 -0
  80. package/dist/typography/suggest.js +21 -0
  81. package/dist/typography/suggest.js.map +1 -0
  82. package/dist/versioning/gallery.d.ts +3 -0
  83. package/dist/versioning/gallery.d.ts.map +1 -0
  84. package/dist/versioning/gallery.js +57 -0
  85. package/dist/versioning/gallery.js.map +1 -0
  86. package/dist/versioning/versions.d.ts +19 -0
  87. package/dist/versioning/versions.d.ts.map +1 -0
  88. package/dist/versioning/versions.js +91 -0
  89. package/dist/versioning/versions.js.map +1 -0
  90. package/dist/watcher.d.ts +6 -0
  91. package/dist/watcher.d.ts.map +1 -0
  92. package/dist/watcher.js +15 -0
  93. package/dist/watcher.js.map +1 -0
  94. package/dist/websocket.d.ts +13 -0
  95. package/dist/websocket.d.ts.map +1 -0
  96. package/dist/websocket.js +30 -0
  97. package/dist/websocket.js.map +1 -0
  98. package/package.json +50 -0
package/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # @mise-en-scene/server
2
+
3
+ Design studio MCP + HTTP server. Serves HTML designs in the browser with hot-reload, exports to SVG/PNG/JPG/PDF, manages design versions with branching, and provides typography intelligence.
4
+
5
+ ## MCP Tools
6
+
7
+ ### Core
8
+
9
+ | Tool | Input | Description |
10
+ |------|-------|-------------|
11
+ | `studio_preview` | `html`, `filename?` | Write HTML+CSS+SVG to preview server, triggers hot-reload |
12
+ | `studio_feedback` | — | Read user interaction feedback (click selections) |
13
+ | `studio_screenshot` | `selector?` | Capture screenshot for self-critique (returns image) |
14
+ | `studio_export` | `format`, `width?`, `height?`, `preset?`, `quality?` | Export to SVG, PNG, JPG, or PDF |
15
+
16
+ ### Versioning
17
+
18
+ | Tool | Input | Description |
19
+ |------|-------|-------------|
20
+ | `studio_branch` | `name`, `from_branch?`, `from_version?` | Create a design branch |
21
+ | `studio_checkout` | `branch`, `version?` | Switch to a branch or version |
22
+ | `studio_gallery` | — | Open the version gallery in browser |
23
+
24
+ ### Typography
25
+
26
+ | Tool | Input | Description |
27
+ |------|-------|-------------|
28
+ | `studio_font_suggest` | `mood`, `context?` | Get curated font pairing suggestions |
29
+ | `studio_font_analyze_url` | `url` | Extract font stack from any website |
30
+ | `studio_font_scale` | `base_size?`, `ratio?` | Generate a modular type scale |
31
+ | `studio_font_embed` | `fonts[]` | Generate @font-face CSS with base64 woff2 |
32
+
33
+ ## Usage
34
+
35
+ ### As an MCP Server
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "mise-en-scene-server": {
41
+ "command": "npx",
42
+ "args": ["@mise-en-scene/server"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Environment Variables
49
+
50
+ | Variable | Default | Description |
51
+ |----------|---------|-------------|
52
+ | `MISE_PORT` | `3333` | HTTP server port |
53
+ | `MISE_DESIGN_DIR` | `mise-en-scene/main` | Directory for design files |
54
+
55
+ ## Components
56
+
57
+ ### HTTP Server
58
+
59
+ Zero-dependency HTTP server (no Express). Serves the newest `.html` file from the design directory with runtime injection — a `<script>` tag and `<link>` to the client-side runtime are added automatically.
60
+
61
+ Routes:
62
+
63
+ | Route | Description |
64
+ |-------|-------------|
65
+ | `GET /` | Serve newest HTML design with runtime injection |
66
+ | `GET /gallery` | Version gallery with thumbnail grid |
67
+ | `GET /mise-en-scene.js` | Client-side runtime |
68
+ | `GET /styles.css` | Selection highlight styles |
69
+
70
+ ### WebSocket Server
71
+
72
+ Real-time communication between the browser and the MCP server:
73
+
74
+ - **Server → Browser**: `reload` (trigger page refresh), `highlight` (visual feedback)
75
+ - **Browser → Server**: `select` (user clicked an element — captured as feedback)
76
+
77
+ ### File Watcher
78
+
79
+ Uses chokidar to watch the design directory for `.html` file changes. Triggers WebSocket `reload` broadcast on new or modified files.
80
+
81
+ ### Export Pipeline
82
+
83
+ Lazy-loads Puppeteer on first use. Supports four export formats:
84
+
85
+ | Format | Engine | Notes |
86
+ |--------|--------|-------|
87
+ | SVG | SVGO | Extracts inline SVGs, optimizes with multipass |
88
+ | PNG | Puppeteer | Full-page or selector-based capture |
89
+ | JPG | Puppeteer | Configurable quality (1-100) |
90
+ | PDF | Puppeteer | Configurable dimensions, print media |
91
+
92
+ #### Dimension Presets
93
+
94
+ 13 built-in presets across three categories:
95
+
96
+ **Social:**
97
+ `instagram-post` (1080x1080), `instagram-story` (1080x1920), `twitter-post` (1200x675), `twitter-header` (1500x500), `facebook-post` (1200x630), `linkedin-post` (1200x627), `og-image` (1200x630)
98
+
99
+ **Web:**
100
+ `desktop-hd` (1920x1080), `desktop` (1440x900), `tablet` (768x1024), `mobile` (375x812)
101
+
102
+ **Print:**
103
+ `a4` (2480x3508 @ 300dpi), `letter` (2550x3300 @ 300dpi)
104
+
105
+ ### Screenshot
106
+
107
+ Captures the current design state as a PNG image. Used by the agent for self-critique — screenshot the design, analyze it against the taste profile, fix issues before presenting to the user.
108
+
109
+ ```
110
+ studio_screenshot() → full page capture
111
+ studio_screenshot("h1") → capture specific element
112
+ studio_screenshot(".header") → capture by CSS selector
113
+ ```
114
+
115
+ Returns image data as base64 in the MCP response (not written to disk).
116
+
117
+ ### Version Manager
118
+
119
+ Git-inspired branching for design iterations:
120
+
121
+ ```
122
+ studio_preview(html) → saves as next version on current branch
123
+ studio_branch("dark-theme") → fork from current version
124
+ studio_checkout("main", 2) → switch to main/v2
125
+ studio_gallery() → view all branches and versions
126
+ ```
127
+
128
+ Versions are stored as flat HTML files (`v1.html`, `v2.html`, ...) organized by branch. Metadata is persisted in `meta.json`:
129
+
130
+ ```json
131
+ {
132
+ "branches": {
133
+ "main": { "versions": [1, 2, 3] },
134
+ "dark-theme": { "forkedFrom": { "branch": "main", "version": 2 }, "versions": [1] }
135
+ },
136
+ "stars": [{ "branch": "main", "version": 2 }],
137
+ "current": { "branch": "main", "version": 3 }
138
+ }
139
+ ```
140
+
141
+ ### Typography Intelligence
142
+
143
+ #### Font Registry
144
+
145
+ 35+ fonts classified by category and subtype:
146
+
147
+ | Category | Subtypes |
148
+ |----------|----------|
149
+ | Serif | old-style, transitional, modern, slab |
150
+ | Sans-serif | grotesque, neo-grotesque, geometric, humanist |
151
+ | Display | creative, bold, futuristic |
152
+ | Monospace | technical, terminal |
153
+
154
+ Each font entry includes `category`, `subtype`, `era`, and `mood` descriptors.
155
+
156
+ #### Curated Pairings
157
+
158
+ 8 aesthetic presets with display + body font pairings, Google Fonts import URLs, and mood descriptions:
159
+
160
+ | Aesthetic | Display | Body | Mood |
161
+ |-----------|---------|------|------|
162
+ | dark-premium | Fraunces | Outfit | Elegant, editorial |
163
+ | clean-minimal | Satoshi | Satoshi | Confident, modern |
164
+ | neobrutalism | Space Grotesk | Space Mono | Technical, raw |
165
+ | editorial | Instrument Serif | Instrument Sans | Refined, readable |
166
+ | y2k-cyber | Orbitron | JetBrains Mono | Sci-fi, technical |
167
+ | warm-scandinavian | DM Serif Display | DM Sans | Approachable, refined |
168
+ | creative-playful | Bricolage Grotesque | Bricolage Grotesque | Expressive, bold |
169
+ | luxury | Playfair Display | Source Sans 3 | Classic, timeless |
170
+
171
+ #### Font Suggestion Engine
172
+
173
+ `studio_font_suggest(mood)` scores pairings against the mood query using token-based text similarity, filters out avoided fonts, and returns the top 3 matches.
174
+
175
+ #### Type Scale Generator
176
+
177
+ `studio_font_scale(base_size, ratio)` generates CSS custom properties for a modular type scale:
178
+
179
+ | Ratio | Name | Use Case |
180
+ |-------|------|----------|
181
+ | 1.2 | Minor Third | Body-heavy, readability |
182
+ | 1.25 | Major Third | General purpose |
183
+ | 1.333 | Perfect Fourth | Balanced drama |
184
+ | 1.618 | Golden Ratio | Posters, maximum drama |
185
+
186
+ Produces 11 named steps (xs through 7xl) with size, line-height, and letter-spacing. Headlines get tighter line-height and tracking automatically.
187
+
188
+ #### URL Font Analyzer
189
+
190
+ `studio_font_analyze_url(url)` launches Puppeteer, navigates to the URL, and extracts computed font families, weights, and styles from common HTML elements.
191
+
192
+ #### Font Embedder
193
+
194
+ `studio_font_embed(fonts)` fetches woff2 files from URLs and returns `@font-face` CSS with base64-inlined font data — for self-contained SVG exports.
195
+
196
+ ## Feedback System
197
+
198
+ User interactions in the browser are captured as JSON Lines in a `.feedback` file:
199
+
200
+ ```jsonl
201
+ {"type":"select","element":{"tag":"h1","classes":["title"],"text":"Hello"},"timestamp":1710...}
202
+ ```
203
+
204
+ The agent reads feedback via `studio_feedback`, interprets selections, and records design decisions in taste memory.
205
+
206
+ ## Client Runtime
207
+
208
+ The browser runtime (`mise-en-scene.js`) provides:
209
+
210
+ - **WebSocket connection** with auto-reconnect (2s backoff)
211
+ - **Live reload** on design file changes
212
+ - **Click-to-select** — clicking any element sends a `select` event to the server
213
+ - **Visual feedback** — selected elements get a `.mise-selected` outline
214
+
215
+ ## Development
216
+
217
+ ```bash
218
+ npm test # Run tests (42 unit + 3 integration + 6 Puppeteer)
219
+ npm run build # Compile TypeScript
220
+ npm run test:watch # Watch mode
221
+ ```
@@ -0,0 +1,10 @@
1
+ import { PuppeteerManager } from './puppeteer-manager.js';
2
+ import type { ExportOptions } from '../types.js';
3
+ export declare class ExportPipeline {
4
+ private puppeteer;
5
+ export(html: string, outputDir: string, opts: ExportOptions): Promise<string>;
6
+ screenshot(url: string, selector?: string): Promise<Buffer>;
7
+ getPuppeteerManager(): PuppeteerManager;
8
+ shutdown(): Promise<void>;
9
+ }
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAA0B;IAErC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAkC7E,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKjE,mBAAmB,IAAI,gBAAgB;IAEjC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAChC"}
@@ -0,0 +1,53 @@
1
+ import { PuppeteerManager } from './puppeteer-manager.js';
2
+ import { extractAndOptimizeSvg } from './svg-export.js';
3
+ import { exportRaster } from './raster-export.js';
4
+ import { exportPdf } from './pdf-export.js';
5
+ import { getPreset } from './presets.js';
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ export class ExportPipeline {
9
+ puppeteer = new PuppeteerManager();
10
+ async export(html, outputDir, opts) {
11
+ fs.mkdirSync(outputDir, { recursive: true });
12
+ let width = opts.width;
13
+ let height = opts.height;
14
+ if (opts.preset) {
15
+ const preset = getPreset(opts.preset);
16
+ if (preset) {
17
+ width = width ?? preset.width;
18
+ height = height ?? preset.height;
19
+ }
20
+ }
21
+ const timestamp = Date.now();
22
+ switch (opts.format) {
23
+ case 'svg': {
24
+ const svg = await extractAndOptimizeSvg(html);
25
+ const outPath = path.join(outputDir, `export-${timestamp}.svg`);
26
+ fs.writeFileSync(outPath, svg);
27
+ return outPath;
28
+ }
29
+ case 'png':
30
+ case 'jpg': {
31
+ const buffer = await exportRaster(this.puppeteer, html, { format: opts.format, width, height, quality: opts.quality, dpi: opts.dpi });
32
+ const ext = opts.format === 'jpg' ? 'jpg' : 'png';
33
+ const outPath = path.join(outputDir, `export-${timestamp}.${ext}`);
34
+ fs.writeFileSync(outPath, buffer);
35
+ return outPath;
36
+ }
37
+ case 'pdf': {
38
+ const buffer = await exportPdf(this.puppeteer, html, { width, height });
39
+ const outPath = path.join(outputDir, `export-${timestamp}.pdf`);
40
+ fs.writeFileSync(outPath, buffer);
41
+ return outPath;
42
+ }
43
+ default: throw new Error(`Unsupported format: ${opts.format}`);
44
+ }
45
+ }
46
+ async screenshot(url, selector) {
47
+ const { takeScreenshot } = await import('../screenshot.js');
48
+ return takeScreenshot(this.puppeteer, url, { selector });
49
+ }
50
+ getPuppeteerManager() { return this.puppeteer; }
51
+ async shutdown() { await this.puppeteer.shutdown(); }
52
+ }
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/export/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAe,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,OAAO,cAAc;IACjB,SAAS,GAAG,IAAI,gBAAgB,EAAE,CAAC;IAE3C,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,SAAiB,EAAE,IAAmB;QAC/D,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACvB,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,MAAM,EAAE,CAAC;gBAAC,KAAK,GAAG,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC;gBAAC,MAAM,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;YAAC,CAAC;QAClF,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,GAAG,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,SAAS,MAAM,CAAC,CAAC;gBAChE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBAC/B,OAAO,OAAO,CAAC;YACjB,CAAC;YACD,KAAK,KAAK,CAAC;YACX,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBACtI,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;gBAClD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,SAAS,IAAI,GAAG,EAAE,CAAC,CAAC;gBACnE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAClC,OAAO,OAAO,CAAC;YACjB,CAAC;YACD,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBACxE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,SAAS,MAAM,CAAC,CAAC;gBAChE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAClC,OAAO,OAAO,CAAC;YACjB,CAAC;YACD,OAAO,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,GAAW,EAAE,QAAiB;QAC7C,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;QAC5D,OAAO,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,mBAAmB,KAAuB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAElE,KAAK,CAAC,QAAQ,KAAoB,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;CACrE"}
@@ -0,0 +1,8 @@
1
+ import type { PuppeteerManager } from './puppeteer-manager.js';
2
+ interface PdfOptions {
3
+ width?: number;
4
+ height?: number;
5
+ }
6
+ export declare function exportPdf(manager: PuppeteerManager, html: string, opts?: PdfOptions): Promise<Buffer>;
7
+ export {};
8
+ //# sourceMappingURL=pdf-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-export.d.ts","sourceRoot":"","sources":["../../src/export/pdf-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,UAAU,UAAU;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAAE;AAEzD,wBAAsB,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAc3G"}
@@ -0,0 +1,17 @@
1
+ export async function exportPdf(manager, html, opts) {
2
+ const page = await manager.getPage();
3
+ try {
4
+ await page.setContent(html, { waitUntil: 'networkidle0', timeout: 10000 });
5
+ const buffer = await page.pdf({
6
+ width: opts?.width ? `${opts.width}px` : undefined,
7
+ height: opts?.height ? `${opts.height}px` : undefined,
8
+ printBackground: true,
9
+ margin: { top: 0, right: 0, bottom: 0, left: 0 },
10
+ });
11
+ return Buffer.from(buffer);
12
+ }
13
+ finally {
14
+ await page.close();
15
+ }
16
+ }
17
+ //# sourceMappingURL=pdf-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf-export.js","sourceRoot":"","sources":["../../src/export/pdf-export.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,OAAyB,EAAE,IAAY,EAAE,IAAiB;IACxF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC;YAC5B,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS;YAClD,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS;YACrD,eAAe,EAAE,IAAI;YACrB,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE;SACjD,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DimensionPreset } from '../types.js';
2
+ export declare function getPreset(name: string): DimensionPreset | null;
3
+ export declare function listPresets(): DimensionPreset[];
4
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/export/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAkBnD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAE9D;AAED,wBAAgB,WAAW,IAAI,eAAe,EAAE,CAE/C"}
@@ -0,0 +1,22 @@
1
+ const PRESETS = {
2
+ 'instagram-post': { name: 'Instagram Post', width: 1080, height: 1080, category: 'social' },
3
+ 'instagram-story': { name: 'Instagram Story', width: 1080, height: 1920, category: 'social' },
4
+ 'twitter-header': { name: 'Twitter Header', width: 1500, height: 500, category: 'social' },
5
+ 'twitter-post': { name: 'Twitter Post', width: 1200, height: 675, category: 'social' },
6
+ 'facebook-cover': { name: 'Facebook Cover', width: 1640, height: 624, category: 'social' },
7
+ 'linkedin-banner': { name: 'LinkedIn Banner', width: 1584, height: 396, category: 'social' },
8
+ 'og-image': { name: 'OG Image', width: 1200, height: 630, category: 'web' },
9
+ 'favicon': { name: 'Favicon', width: 512, height: 512, category: 'web' },
10
+ 'hd': { name: 'HD 1080p', width: 1920, height: 1080, category: 'web' },
11
+ 'a4': { name: 'A4 (300dpi)', width: 2480, height: 3508, category: 'print' },
12
+ 'letter': { name: 'US Letter (300dpi)', width: 2550, height: 3300, category: 'print' },
13
+ 'poster-18x24': { name: 'Poster 18×24 (150dpi)', width: 2700, height: 3600, category: 'print' },
14
+ 'business-card': { name: 'Business Card (300dpi)', width: 1050, height: 600, category: 'print' },
15
+ };
16
+ export function getPreset(name) {
17
+ return PRESETS[name] ?? null;
18
+ }
19
+ export function listPresets() {
20
+ return Object.values(PRESETS);
21
+ }
22
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/export/presets.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAoC;IAC/C,gBAAgB,EAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC9F,iBAAiB,EAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC/F,gBAAgB,EAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC7F,cAAc,EAAO,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC3F,gBAAgB,EAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC7F,iBAAiB,EAAI,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE;IAC9F,UAAU,EAAW,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE;IACpF,SAAS,EAAY,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE;IAClF,IAAI,EAAiB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE;IACrF,IAAI,EAAiB,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE;IAC1F,QAAQ,EAAa,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE;IACjG,cAAc,EAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE;IACpG,eAAe,EAAM,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE;CACrG,CAAC;AAEF,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type Page } from 'puppeteer';
2
+ export declare class PuppeteerManager {
3
+ private browser;
4
+ isRunning(): boolean;
5
+ getPage(): Promise<Page>;
6
+ shutdown(): Promise<void>;
7
+ }
8
+ //# sourceMappingURL=puppeteer-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppeteer-manager.d.ts","sourceRoot":"","sources":["../../src/export/puppeteer-manager.ts"],"names":[],"mappings":"AAAA,OAAkB,EAAgB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE/D,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAAwB;IAEvC,SAAS,IAAI,OAAO;IAId,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUxB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAMhC"}
@@ -0,0 +1,23 @@
1
+ import puppeteer from 'puppeteer';
2
+ export class PuppeteerManager {
3
+ browser = null;
4
+ isRunning() {
5
+ return this.browser !== null && this.browser.connected;
6
+ }
7
+ async getPage() {
8
+ if (!this.browser || !this.browser.connected) {
9
+ this.browser = await puppeteer.launch({
10
+ headless: true,
11
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
12
+ });
13
+ }
14
+ return this.browser.newPage();
15
+ }
16
+ async shutdown() {
17
+ if (this.browser) {
18
+ await this.browser.close();
19
+ this.browser = null;
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=puppeteer-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppeteer-manager.js","sourceRoot":"","sources":["../../src/export/puppeteer-manager.ts"],"names":[],"mappings":"AAAA,OAAO,SAAsC,MAAM,WAAW,CAAC;AAE/D,MAAM,OAAO,gBAAgB;IACnB,OAAO,GAAmB,IAAI,CAAC;IAEvC,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC;gBACpC,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,CAAC,cAAc,EAAE,0BAA0B,CAAC;aACnD,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { PuppeteerManager } from './puppeteer-manager.js';
2
+ interface RasterOptions {
3
+ format: 'png' | 'jpg';
4
+ width?: number;
5
+ height?: number;
6
+ quality?: number;
7
+ dpi?: number;
8
+ }
9
+ export declare function exportRaster(manager: PuppeteerManager, html: string, opts: RasterOptions): Promise<Buffer>;
10
+ export {};
11
+ //# sourceMappingURL=raster-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raster-export.d.ts","sourceRoot":"","sources":["../../src/export/raster-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,UAAU,aAAa;IACrB,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBhH"}
@@ -0,0 +1,22 @@
1
+ export async function exportRaster(manager, html, opts) {
2
+ const page = await manager.getPage();
3
+ try {
4
+ const width = opts.width ?? 1200;
5
+ const height = opts.height ?? 800;
6
+ await page.setViewport({ width, height, deviceScaleFactor: opts.dpi ?? 1 });
7
+ await page.setContent(html, { waitUntil: 'networkidle0', timeout: 10000 });
8
+ const screenshotOpts = {
9
+ type: opts.format === 'jpg' ? 'jpeg' : 'png',
10
+ fullPage: false,
11
+ clip: { x: 0, y: 0, width, height },
12
+ };
13
+ if (opts.format === 'jpg' && opts.quality)
14
+ screenshotOpts.quality = opts.quality;
15
+ const buffer = await page.screenshot(screenshotOpts);
16
+ return Buffer.from(buffer);
17
+ }
18
+ finally {
19
+ await page.close();
20
+ }
21
+ }
22
+ //# sourceMappingURL=raster-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"raster-export.js","sourceRoot":"","sources":["../../src/export/raster-export.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAyB,EAAE,IAAY,EAAE,IAAmB;IAC7F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;QAClC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3E,MAAM,cAAc,GAAQ;YAC1B,IAAI,EAAE,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK;YAC5C,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE;SACpC,CAAC;QACF,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO;YAAE,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function extractAndOptimizeSvg(html: string): Promise<string>;
2
+ //# sourceMappingURL=svg-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-export.d.ts","sourceRoot":"","sources":["../../src/export/svg-export.ts"],"names":[],"mappings":"AAEA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBzE"}
@@ -0,0 +1,24 @@
1
+ import { optimize } from 'svgo';
2
+ export async function extractAndOptimizeSvg(html) {
3
+ const svgMatch = html.match(/<svg[\s\S]*?<\/svg>/i);
4
+ if (!svgMatch)
5
+ throw new Error('No SVG element found in the provided HTML');
6
+ const rawSvg = svgMatch[0];
7
+ const result = optimize(rawSvg, {
8
+ multipass: true,
9
+ plugins: [
10
+ 'removeDoctype', 'removeXMLProcInst', 'removeComments', 'removeMetadata',
11
+ 'removeEditorsNSData', 'cleanupAttrs', 'mergeStyles', 'inlineStyles',
12
+ 'minifyStyles', 'cleanupIds', 'removeUselessDefs', 'cleanupNumericValues',
13
+ 'convertColors', 'removeUnknownsAndDefaults', 'removeNonInheritableGroupAttrs',
14
+ 'removeUselessStrokeAndFill', 'cleanupEnableBackground', 'removeHiddenElems',
15
+ 'removeEmptyText', 'convertShapeToPath', 'convertEllipseToCircle',
16
+ 'moveElemsAttrsToGroup', 'moveGroupAttrsToElems', 'collapseGroups',
17
+ 'convertPathData', 'convertTransform', 'removeEmptyAttrs',
18
+ 'removeEmptyContainers', 'removeDimensions', 'removeStyleElement',
19
+ 'removeScriptElement', 'sortAttrs', 'sortDefsChildren',
20
+ ],
21
+ });
22
+ return result.data;
23
+ }
24
+ //# sourceMappingURL=svg-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg-export.js","sourceRoot":"","sources":["../../src/export/svg-export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AAEhC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACpD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE;QAC9B,SAAS,EAAE,IAAI;QACf,OAAO,EAAE;YACP,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,gBAAgB;YACxE,qBAAqB,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc;YACpE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB;YACzE,eAAe,EAAE,2BAA2B,EAAE,gCAAgC;YAC9E,4BAA4B,EAAE,yBAAyB,EAAE,mBAAmB;YAC5E,iBAAiB,EAAE,oBAAoB,EAAE,wBAAwB;YACjE,uBAAuB,EAAE,uBAAuB,EAAE,gBAAgB;YAClE,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB;YACzD,uBAAuB,EAAE,kBAAkB,EAAE,oBAAoB;YACjE,qBAAqB,EAAE,WAAW,EAAE,kBAAkB;SACvD;KACF,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FeedbackEvent } from './types.js';
2
+ export declare function readFeedback(designDir: string): FeedbackEvent[];
3
+ export declare function appendFeedback(designDir: string, event: Record<string, unknown>): void;
4
+ //# sourceMappingURL=feedback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAQ/D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAGtF"}
@@ -0,0 +1,23 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export function readFeedback(designDir) {
4
+ const feedbackPath = path.join(designDir, '.feedback');
5
+ if (!fs.existsSync(feedbackPath))
6
+ return [];
7
+ const content = fs.readFileSync(feedbackPath, 'utf-8').trim();
8
+ if (!content)
9
+ return [];
10
+ return content.split('\n').filter((line) => line.trim()).map((line) => {
11
+ try {
12
+ return JSON.parse(line);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }).filter(Boolean);
18
+ }
19
+ export function appendFeedback(designDir, event) {
20
+ fs.mkdirSync(designDir, { recursive: true });
21
+ fs.appendFileSync(path.join(designDir, '.feedback'), JSON.stringify(event) + '\n');
22
+ }
23
+ //# sourceMappingURL=feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback.js","sourceRoot":"","sources":["../src/feedback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACpE,IAAI,CAAC;YAAC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IACzD,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAoB,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,KAA8B;IAC9E,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import http from 'http';
2
+ interface ServerOptions {
3
+ port: number;
4
+ designDir: string;
5
+ host?: string;
6
+ }
7
+ interface ServerInstance {
8
+ port: number;
9
+ server: http.Server;
10
+ close: () => Promise<void>;
11
+ }
12
+ export declare function getNewestHtmlFile(dir: string): string | null;
13
+ export declare function createHttpServer(opts: ServerOptions): Promise<ServerInstance>;
14
+ export {};
15
+ //# sourceMappingURL=http-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAMxB,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D;AA4BD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAwEnF"}