@daviesayo/mise-en-scene 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 (186) hide show
  1. package/dist/commands/clean.d.ts +7 -0
  2. package/dist/commands/clean.d.ts.map +1 -0
  3. package/dist/commands/clean.js +54 -0
  4. package/dist/commands/clean.js.map +1 -0
  5. package/dist/commands/export.d.ts +11 -0
  6. package/dist/commands/export.d.ts.map +1 -0
  7. package/dist/commands/export.js +30 -0
  8. package/dist/commands/export.js.map +1 -0
  9. package/dist/commands/gallery.d.ts +2 -0
  10. package/dist/commands/gallery.d.ts.map +1 -0
  11. package/dist/commands/gallery.js +23 -0
  12. package/dist/commands/gallery.js.map +1 -0
  13. package/dist/commands/init.d.ts +8 -0
  14. package/dist/commands/init.d.ts.map +1 -0
  15. package/dist/commands/init.js +105 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/serve.d.ts +7 -0
  18. package/dist/commands/serve.d.ts.map +1 -0
  19. package/dist/commands/serve.js +25 -0
  20. package/dist/commands/serve.js.map +1 -0
  21. package/dist/commands/stop.d.ts +6 -0
  22. package/dist/commands/stop.d.ts.map +1 -0
  23. package/dist/commands/stop.js +18 -0
  24. package/dist/commands/stop.js.map +1 -0
  25. package/dist/commands/taste.d.ts +7 -0
  26. package/dist/commands/taste.d.ts.map +1 -0
  27. package/dist/commands/taste.js +33 -0
  28. package/dist/commands/taste.js.map +1 -0
  29. package/dist/config-writer.d.ts +2 -0
  30. package/dist/config-writer.d.ts.map +1 -0
  31. package/dist/config-writer.js +33 -0
  32. package/dist/config-writer.js.map +1 -0
  33. package/dist/detect-platform.d.ts +16 -0
  34. package/dist/detect-platform.d.ts.map +1 -0
  35. package/dist/detect-platform.js +41 -0
  36. package/dist/detect-platform.js.map +1 -0
  37. package/dist/hooks-installer.d.ts +8 -0
  38. package/dist/hooks-installer.d.ts.map +1 -0
  39. package/dist/hooks-installer.js +62 -0
  40. package/dist/hooks-installer.js.map +1 -0
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +83 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/scaffold.d.ts +2 -0
  46. package/dist/scaffold.d.ts.map +1 -0
  47. package/dist/scaffold.js +38 -0
  48. package/dist/scaffold.js.map +1 -0
  49. package/dist/server/export/index.d.ts +10 -0
  50. package/dist/server/export/index.d.ts.map +1 -0
  51. package/dist/server/export/index.js +65 -0
  52. package/dist/server/export/index.js.map +1 -0
  53. package/dist/server/export/pdf-export.d.ts +8 -0
  54. package/dist/server/export/pdf-export.d.ts.map +1 -0
  55. package/dist/server/export/pdf-export.js +17 -0
  56. package/dist/server/export/pdf-export.js.map +1 -0
  57. package/dist/server/export/presets.d.ts +4 -0
  58. package/dist/server/export/presets.d.ts.map +1 -0
  59. package/dist/server/export/presets.js +22 -0
  60. package/dist/server/export/presets.js.map +1 -0
  61. package/dist/server/export/puppeteer-manager.d.ts +8 -0
  62. package/dist/server/export/puppeteer-manager.d.ts.map +1 -0
  63. package/dist/server/export/puppeteer-manager.js +24 -0
  64. package/dist/server/export/puppeteer-manager.js.map +1 -0
  65. package/dist/server/export/raster-export.d.ts +11 -0
  66. package/dist/server/export/raster-export.d.ts.map +1 -0
  67. package/dist/server/export/raster-export.js +28 -0
  68. package/dist/server/export/raster-export.js.map +1 -0
  69. package/dist/server/export/svg-export.d.ts +2 -0
  70. package/dist/server/export/svg-export.d.ts.map +1 -0
  71. package/dist/server/export/svg-export.js +17 -0
  72. package/dist/server/export/svg-export.js.map +1 -0
  73. package/dist/server/feedback.d.ts +4 -0
  74. package/dist/server/feedback.d.ts.map +1 -0
  75. package/dist/server/feedback.js +29 -0
  76. package/dist/server/feedback.js.map +1 -0
  77. package/dist/server/http-server.d.ts +19 -0
  78. package/dist/server/http-server.d.ts.map +1 -0
  79. package/dist/server/http-server.js +199 -0
  80. package/dist/server/http-server.js.map +1 -0
  81. package/dist/server/index.d.ts +3 -0
  82. package/dist/server/index.d.ts.map +1 -0
  83. package/dist/server/index.js +44 -0
  84. package/dist/server/index.js.map +1 -0
  85. package/dist/server/mcp-server.d.ts +433 -0
  86. package/dist/server/mcp-server.d.ts.map +1 -0
  87. package/dist/server/mcp-server.js +248 -0
  88. package/dist/server/mcp-server.js.map +1 -0
  89. package/dist/server/preview.d.ts +2 -0
  90. package/dist/server/preview.d.ts.map +1 -0
  91. package/dist/server/preview.js +15 -0
  92. package/dist/server/preview.js.map +1 -0
  93. package/dist/server/screenshot.d.ts +7 -0
  94. package/dist/server/screenshot.d.ts.map +1 -0
  95. package/dist/server/screenshot.js +23 -0
  96. package/dist/server/screenshot.js.map +1 -0
  97. package/dist/server/serve.d.ts +3 -0
  98. package/dist/server/serve.d.ts.map +1 -0
  99. package/dist/server/serve.js +39 -0
  100. package/dist/server/serve.js.map +1 -0
  101. package/dist/server/types.d.ts +84 -0
  102. package/dist/server/types.d.ts.map +1 -0
  103. package/dist/server/types.js +2 -0
  104. package/dist/server/types.js.map +1 -0
  105. package/dist/server/typography/analyze-url.d.ts +10 -0
  106. package/dist/server/typography/analyze-url.d.ts.map +1 -0
  107. package/dist/server/typography/analyze-url.js +28 -0
  108. package/dist/server/typography/analyze-url.js.map +1 -0
  109. package/dist/server/typography/classify.d.ts +9 -0
  110. package/dist/server/typography/classify.d.ts.map +1 -0
  111. package/dist/server/typography/classify.js +15 -0
  112. package/dist/server/typography/classify.js.map +1 -0
  113. package/dist/server/typography/embed.d.ts +5 -0
  114. package/dist/server/typography/embed.d.ts.map +1 -0
  115. package/dist/server/typography/embed.js +34 -0
  116. package/dist/server/typography/embed.js.map +1 -0
  117. package/dist/server/typography/mood-match.d.ts +3 -0
  118. package/dist/server/typography/mood-match.d.ts.map +1 -0
  119. package/dist/server/typography/mood-match.js +21 -0
  120. package/dist/server/typography/mood-match.js.map +1 -0
  121. package/dist/server/typography/registry.d.ts +23 -0
  122. package/dist/server/typography/registry.d.ts.map +1 -0
  123. package/dist/server/typography/registry.js +93 -0
  124. package/dist/server/typography/registry.js.map +1 -0
  125. package/dist/server/typography/scale.d.ts +3 -0
  126. package/dist/server/typography/scale.d.ts.map +1 -0
  127. package/dist/server/typography/scale.js +43 -0
  128. package/dist/server/typography/scale.js.map +1 -0
  129. package/dist/server/typography/suggest.d.ts +9 -0
  130. package/dist/server/typography/suggest.d.ts.map +1 -0
  131. package/dist/server/typography/suggest.js +21 -0
  132. package/dist/server/typography/suggest.js.map +1 -0
  133. package/dist/server/versioning/gallery.d.ts +3 -0
  134. package/dist/server/versioning/gallery.d.ts.map +1 -0
  135. package/dist/server/versioning/gallery.js +57 -0
  136. package/dist/server/versioning/gallery.js.map +1 -0
  137. package/dist/server/versioning/versions.d.ts +19 -0
  138. package/dist/server/versioning/versions.d.ts.map +1 -0
  139. package/dist/server/versioning/versions.js +91 -0
  140. package/dist/server/versioning/versions.js.map +1 -0
  141. package/dist/server/watcher.d.ts +6 -0
  142. package/dist/server/watcher.d.ts.map +1 -0
  143. package/dist/server/watcher.js +23 -0
  144. package/dist/server/watcher.js.map +1 -0
  145. package/dist/server/websocket.d.ts +13 -0
  146. package/dist/server/websocket.d.ts.map +1 -0
  147. package/dist/server/websocket.js +36 -0
  148. package/dist/server/websocket.js.map +1 -0
  149. package/dist/skill/SKILL.md +280 -0
  150. package/dist/taste-memory/db.d.ts +5 -0
  151. package/dist/taste-memory/db.d.ts.map +1 -0
  152. package/dist/taste-memory/db.js +64 -0
  153. package/dist/taste-memory/db.js.map +1 -0
  154. package/dist/taste-memory/embeddings.d.ts +3 -0
  155. package/dist/taste-memory/embeddings.d.ts.map +1 -0
  156. package/dist/taste-memory/embeddings.js +24 -0
  157. package/dist/taste-memory/embeddings.js.map +1 -0
  158. package/dist/taste-memory/index.d.ts +3 -0
  159. package/dist/taste-memory/index.d.ts.map +1 -0
  160. package/dist/taste-memory/index.js +113 -0
  161. package/dist/taste-memory/index.js.map +1 -0
  162. package/dist/taste-memory/profile.d.ts +6 -0
  163. package/dist/taste-memory/profile.d.ts.map +1 -0
  164. package/dist/taste-memory/profile.js +33 -0
  165. package/dist/taste-memory/profile.js.map +1 -0
  166. package/dist/taste-memory/search.d.ts +16 -0
  167. package/dist/taste-memory/search.d.ts.map +1 -0
  168. package/dist/taste-memory/search.js +53 -0
  169. package/dist/taste-memory/search.js.map +1 -0
  170. package/dist/taste-memory/server.d.ts +43 -0
  171. package/dist/taste-memory/server.d.ts.map +1 -0
  172. package/dist/taste-memory/server.js +64 -0
  173. package/dist/taste-memory/server.js.map +1 -0
  174. package/dist/taste-memory/store.d.ts +21 -0
  175. package/dist/taste-memory/store.d.ts.map +1 -0
  176. package/dist/taste-memory/store.js +72 -0
  177. package/dist/taste-memory/store.js.map +1 -0
  178. package/dist/taste-memory/types.d.ts +95 -0
  179. package/dist/taste-memory/types.d.ts.map +1 -0
  180. package/dist/taste-memory/types.js +2 -0
  181. package/dist/taste-memory/types.js.map +1 -0
  182. package/dist/types.d.ts +13 -0
  183. package/dist/types.d.ts.map +1 -0
  184. package/dist/types.js +2 -0
  185. package/dist/types.js.map +1 -0
  186. package/package.json +36 -0
@@ -0,0 +1,21 @@
1
+ // src/typography/suggest.ts
2
+ import { CURATED_PAIRINGS } from './registry.js';
3
+ import { textSimilarity } from './mood-match.js';
4
+ export function suggestFonts(mood, opts) {
5
+ const avoided = new Set((opts?.avoided_fonts ?? []).map((f) => f.toLowerCase()));
6
+ const scored = Object.entries(CURATED_PAIRINGS)
7
+ .map(([key, pairing]) => {
8
+ if (avoided.has(pairing.display.family.toLowerCase()) ||
9
+ avoided.has(pairing.body.family.toLowerCase())) {
10
+ return null;
11
+ }
12
+ const score = textSimilarity(mood, `${key} ${pairing.mood}`);
13
+ return { ...pairing, _score: score };
14
+ })
15
+ .filter((x) => x !== null)
16
+ .sort((a, b) => b._score - a._score)
17
+ .slice(0, 3)
18
+ .map(({ _score, ...rest }) => rest);
19
+ return scored;
20
+ }
21
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.js","sourceRoot":"","sources":["../../src/typography/suggest.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AASjD,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,IAAqB;IAErB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAEjF,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE;QACtB,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACnD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IACvC,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAA8B,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;SACrD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;SACnC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAqB,CAAC;IAE1D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { VersionMeta } from '../types.js';
2
+ export declare function generateGalleryHtml(meta: VersionMeta, serverPort: number): string;
3
+ //# sourceMappingURL=gallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.d.ts","sourceRoot":"","sources":["../../src/versioning/gallery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA2DjF"}
@@ -0,0 +1,57 @@
1
+ export function generateGalleryHtml(meta, serverPort) {
2
+ const starSet = new Set(meta.stars.map((s) => `${s.branch}:${s.version}`));
3
+ let branchCards = '';
4
+ for (const [name, info] of Object.entries(meta.branches)) {
5
+ const versionCards = info.versions
6
+ .map((v) => {
7
+ const isStarred = starSet.has(`${name}:${v}`);
8
+ const isCurrent = meta.current.branch === name && meta.current.version === v;
9
+ return `
10
+ <div class="version-card ${isCurrent ? 'current' : ''}">
11
+ <div class="version-thumbnail">
12
+ <iframe src="http://localhost:${serverPort}/version/${name}/${v}" sandbox></iframe>
13
+ </div>
14
+ <div class="version-label">
15
+ v${v} ${isStarred ? '★' : ''}
16
+ </div>
17
+ </div>
18
+ `;
19
+ })
20
+ .join('');
21
+ const forkedLabel = info.forkedFrom
22
+ ? `<span class="forked-from">forked from ${info.forkedFrom.branch}/v${info.forkedFrom.version}</span>`
23
+ : '';
24
+ branchCards += `
25
+ <div class="branch-section">
26
+ <h2>${name} ${forkedLabel}</h2>
27
+ <div class="version-grid">${versionCards}</div>
28
+ </div>
29
+ `;
30
+ }
31
+ return `<!DOCTYPE html>
32
+ <html lang="en">
33
+ <head>
34
+ <meta charset="UTF-8">
35
+ <title>Mise en Scène — Gallery</title>
36
+ <style>
37
+ * { margin: 0; padding: 0; box-sizing: border-box; }
38
+ body { background: #0a0a0a; color: #e0e0e0; font-family: system-ui; padding: 2rem; }
39
+ h1 { font-size: 1.5rem; margin-bottom: 2rem; color: #fff; }
40
+ h2 { font-size: 1.1rem; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
41
+ .forked-from { font-size: 0.75rem; color: #666; font-weight: normal; }
42
+ .branch-section { margin-bottom: 3rem; }
43
+ .version-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1rem; }
44
+ .version-card { background: #141414; border-radius: 8px; overflow: hidden; border: 1px solid #222; }
45
+ .version-card.current { border-color: #e94560; }
46
+ .version-thumbnail { aspect-ratio: 4/3; overflow: hidden; background: #0a0a0a; }
47
+ .version-thumbnail iframe { width: 400%; height: 400%; transform: scale(0.25); transform-origin: top left; border: none; pointer-events: none; }
48
+ .version-label { padding: 0.5rem 0.75rem; font-size: 0.85rem; display: flex; justify-content: space-between; }
49
+ </style>
50
+ </head>
51
+ <body>
52
+ <h1>✦ mise en scène — gallery</h1>
53
+ ${branchCards}
54
+ </body>
55
+ </html>`;
56
+ }
57
+ //# sourceMappingURL=gallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gallery.js","sourceRoot":"","sources":["../../src/versioning/gallery.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,mBAAmB,CAAC,IAAiB,EAAE,UAAkB;IACvE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAE3E,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,KAAK,CAAC,CAAC;YAC7E,OAAO;qCACsB,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;;8CAEjB,UAAU,YAAY,IAAI,IAAI,CAAC;;;iBAG5D,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;;SAGjC,CAAC;QACJ,CAAC,CAAC;aACD,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU;YACjC,CAAC,CAAC,yCAAyC,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,CAAC,OAAO,SAAS;YACtG,CAAC,CAAC,EAAE,CAAC;QAEP,WAAW,IAAI;;cAEL,IAAI,IAAI,WAAW;oCACG,YAAY;;KAE3C,CAAC;IACJ,CAAC;IAED,OAAO;;;;;;;;;;;;;;;;;;;;;;IAsBL,WAAW;;QAEP,CAAC;AACT,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { VersionMeta, BranchInfo } from '../types.js';
2
+ export declare class VersionManager {
3
+ private metaPath;
4
+ private baseDir;
5
+ constructor(baseDir: string);
6
+ getMeta(): VersionMeta;
7
+ private writeMeta;
8
+ private getBranchDir;
9
+ saveVersion(html: string): {
10
+ branch: string;
11
+ version: number;
12
+ };
13
+ createBranch(name: string, fromBranch?: string, fromVersion?: number): void;
14
+ checkout(branch: string, version?: number): void;
15
+ star(branch: string, version: number): void;
16
+ getVersion(branch: string, version: number): string | null;
17
+ listBranches(): Record<string, BranchInfo>;
18
+ }
19
+ //# sourceMappingURL=versions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.d.ts","sourceRoot":"","sources":["../../src/versioning/versions.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAkB3B,OAAO,IAAI,WAAW;IAItB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAMpB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAoB9D,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAmB3E,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAahD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAM3C,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAM1D,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC;CAG3C"}
@@ -0,0 +1,91 @@
1
+ // src/versioning/versions.ts
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ export class VersionManager {
5
+ metaPath;
6
+ baseDir;
7
+ constructor(baseDir) {
8
+ this.baseDir = baseDir;
9
+ this.metaPath = path.join(baseDir, 'meta.json');
10
+ if (!fs.existsSync(this.metaPath)) {
11
+ const meta = {
12
+ branches: {
13
+ main: { name: 'main', versions: [], created_at: new Date().toISOString() },
14
+ },
15
+ stars: [],
16
+ current: { branch: 'main', version: 0 },
17
+ };
18
+ fs.mkdirSync(path.join(baseDir, 'main'), { recursive: true });
19
+ fs.mkdirSync(path.join(baseDir, 'branches'), { recursive: true });
20
+ this.writeMeta(meta);
21
+ }
22
+ }
23
+ getMeta() {
24
+ return JSON.parse(fs.readFileSync(this.metaPath, 'utf-8'));
25
+ }
26
+ writeMeta(meta) {
27
+ fs.writeFileSync(this.metaPath, JSON.stringify(meta, null, 2));
28
+ }
29
+ getBranchDir(branch) {
30
+ return branch === 'main'
31
+ ? path.join(this.baseDir, 'main')
32
+ : path.join(this.baseDir, 'branches', branch);
33
+ }
34
+ saveVersion(html) {
35
+ const meta = this.getMeta();
36
+ const branch = meta.current.branch;
37
+ const branchInfo = meta.branches[branch];
38
+ const nextVersion = (branchInfo.versions.length > 0
39
+ ? Math.max(...branchInfo.versions)
40
+ : 0) + 1;
41
+ const dir = this.getBranchDir(branch);
42
+ fs.mkdirSync(dir, { recursive: true });
43
+ fs.writeFileSync(path.join(dir, `v${nextVersion}.html`), html);
44
+ branchInfo.versions.push(nextVersion);
45
+ meta.current.version = nextVersion;
46
+ this.writeMeta(meta);
47
+ return { branch, version: nextVersion };
48
+ }
49
+ createBranch(name, fromBranch, fromVersion) {
50
+ const meta = this.getMeta();
51
+ const sourceBranch = fromBranch ?? meta.current.branch;
52
+ const sourceVersion = fromVersion ?? meta.current.version;
53
+ meta.branches[name] = {
54
+ name,
55
+ forkedFrom: { branch: sourceBranch, version: sourceVersion },
56
+ versions: [],
57
+ created_at: new Date().toISOString(),
58
+ };
59
+ const dir = this.getBranchDir(name);
60
+ fs.mkdirSync(dir, { recursive: true });
61
+ meta.current = { branch: name, version: 0 };
62
+ this.writeMeta(meta);
63
+ }
64
+ checkout(branch, version) {
65
+ const meta = this.getMeta();
66
+ if (!meta.branches[branch]) {
67
+ throw new Error(`Branch "${branch}" does not exist`);
68
+ }
69
+ const versions = meta.branches[branch].versions;
70
+ meta.current = {
71
+ branch,
72
+ version: version ?? (versions.length > 0 ? Math.max(...versions) : 0),
73
+ };
74
+ this.writeMeta(meta);
75
+ }
76
+ star(branch, version) {
77
+ const meta = this.getMeta();
78
+ meta.stars.push({ branch, version });
79
+ this.writeMeta(meta);
80
+ }
81
+ getVersion(branch, version) {
82
+ const filePath = path.join(this.getBranchDir(branch), `v${version}.html`);
83
+ if (!fs.existsSync(filePath))
84
+ return null;
85
+ return fs.readFileSync(filePath, 'utf-8');
86
+ }
87
+ listBranches() {
88
+ return this.getMeta().branches;
89
+ }
90
+ }
91
+ //# sourceMappingURL=versions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"versions.js","sourceRoot":"","sources":["../../src/versioning/versions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ interface WatcherInstance {
2
+ close: () => Promise<void>;
3
+ }
4
+ export declare function createWatcher(dir: string, onChange: (filename: string) => void): WatcherInstance;
5
+ export {};
6
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAIA,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GACnC,eAAe,CAqBjB"}
@@ -0,0 +1,23 @@
1
+ // src/watcher.ts
2
+ import { watch } from 'chokidar';
3
+ import path from 'path';
4
+ export function createWatcher(dir, onChange) {
5
+ const watcher = watch(dir, {
6
+ ignoreInitial: true,
7
+ depth: 0,
8
+ });
9
+ watcher.on('add', (filePath) => {
10
+ if (path.extname(filePath) === '.html') {
11
+ onChange(path.basename(filePath));
12
+ }
13
+ });
14
+ watcher.on('change', (filePath) => {
15
+ if (path.extname(filePath) === '.html') {
16
+ onChange(path.basename(filePath));
17
+ }
18
+ });
19
+ return {
20
+ close: () => watcher.close(),
21
+ };
22
+ }
23
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,iBAAiB;AACjB,OAAO,EAAE,KAAK,EAAkB,MAAM,UAAU,CAAC;AACjD,OAAO,IAAI,MAAM,MAAM,CAAC;AAMxB,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,QAAoC;IAEpC,MAAM,OAAO,GAAc,KAAK,CAAC,GAAG,EAAE;QACpC,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,CAAC;KACT,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE;QAChC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,OAAO,EAAE,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;KAC7B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ interface WsServerOptions {
2
+ port: number;
3
+ host?: string;
4
+ }
5
+ export interface WsServerInstance {
6
+ port: number;
7
+ broadcast: (msg: Record<string, unknown>) => void;
8
+ onMessage: (handler: (msg: Record<string, unknown>) => void) => void;
9
+ close: () => Promise<void>;
10
+ }
11
+ export declare function createWebSocketServer(opts: WsServerOptions): Promise<WsServerInstance>;
12
+ export {};
13
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAGA,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClD,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC;IACrE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuC5F"}
@@ -0,0 +1,36 @@
1
+ // src/websocket.ts
2
+ import { WebSocketServer, WebSocket } from 'ws';
3
+ export async function createWebSocketServer(opts) {
4
+ const { host = '127.0.0.1' } = opts;
5
+ const wss = new WebSocketServer({ port: opts.port, host });
6
+ const messageHandlers = [];
7
+ await new Promise((resolve) => wss.on('listening', resolve));
8
+ wss.on('connection', (socket) => {
9
+ socket.on('message', (data) => {
10
+ try {
11
+ const msg = JSON.parse(data.toString());
12
+ messageHandlers.forEach((h) => h(msg));
13
+ }
14
+ catch {
15
+ // Ignore malformed messages
16
+ }
17
+ });
18
+ });
19
+ const addr = wss.address();
20
+ return {
21
+ port: typeof addr === 'object' ? (addr.port ?? opts.port) : opts.port,
22
+ broadcast(msg) {
23
+ const data = JSON.stringify(msg);
24
+ wss.clients.forEach((client) => {
25
+ if (client.readyState === WebSocket.OPEN) {
26
+ client.send(data);
27
+ }
28
+ });
29
+ },
30
+ onMessage(handler) {
31
+ messageHandlers.push(handler);
32
+ },
33
+ close: () => new Promise((resolve) => wss.close(() => resolve())),
34
+ };
35
+ }
36
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AAAA,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAchD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAqB;IAC/D,MAAM,EAAE,IAAI,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC;IAEpC,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,MAAM,eAAe,GAAkD,EAAE,CAAC;IAE1E,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAEnE,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;QAC9B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACxC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,4BAA4B;YAC9B,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAgC,CAAC;IAEzD,OAAO;QACL,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QAErE,SAAS,CAAC,GAAG;YACX,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACjC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBAC7B,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;oBACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAED,SAAS,CAAC,OAAO;YACf,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QAED,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;KACxE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,280 @@
1
+ ---
2
+ name: mise-en-scene
3
+ description: Use when designing visual assets (posters, logos, mockups, social graphics, infographics) using HTML+CSS+SVG as the rendering medium. Triggers on design requests, visual creation tasks, brand work, or when user drops reference images for analysis.
4
+ ---
5
+
6
+ # Mise en Scène
7
+
8
+ **The arrangement of everything in the frame.**
9
+
10
+ A generalist visual design skill that uses HTML + CSS + SVG as its rendering medium. Creates posters, logos, mockups, social media graphics, infographics, and any visual asset with high aesthetic fidelity.
11
+
12
+ This skill develops *taste*. It doesn't just execute — it analyzes references deeply, builds an evolving aesthetic vocabulary, understands cultural and historical design context, and designs from a coherent taste profile. You are a creative director, not a rendering engine.
13
+
14
+ ## Available Tools
15
+
16
+ Two MCP servers provide your hands:
17
+
18
+ **Taste Memory** (`@mise-en-scene/taste-memory`):
19
+ | Tool | Purpose |
20
+ |------|---------|
21
+ | `taste_remember(type, data)` | Store a taste observation |
22
+ | `taste_recall(query)` | Search taste memory |
23
+ | `taste_profile(scope)` | Get taste profile (user/project/merged) |
24
+ | `taste_decide(chosen, rejected, reason)` | Record a design decision |
25
+ | `taste_reference(project, analysis)` | Store reference analysis |
26
+ | `taste_system(project, tokens?)` | Store/retrieve design tokens |
27
+
28
+ **Design Studio** (`@mise-en-scene/server`):
29
+ | Tool | Purpose |
30
+ |------|---------|
31
+ | `studio_preview(html, filename)` | Write design to preview, trigger hot-reload |
32
+ | `studio_screenshot(selector?)` | Capture screenshot for self-critique |
33
+ | `studio_export(format, options)` | Export (SVG/PNG/JPG/PDF) |
34
+ | `studio_branch(name, from?)` | Create version branch |
35
+ | `studio_checkout(branch, version?)` | Switch active view |
36
+ | `studio_gallery()` | Open gallery view |
37
+ | `studio_feedback()` | Read user annotations/selections |
38
+ | `studio_font_suggest(mood, context)` | Typography pairing suggestions |
39
+ | `studio_font_analyze_url(url)` | Extract fonts from any website |
40
+ | `studio_font_scale(base_size, ratio)` | Generate modular type scale |
41
+ | `studio_font_embed(fonts[])` | Embed fonts for SVG export |
42
+
43
+ You are the orchestrator. The servers don't talk to each other — you coordinate:
44
+ ```
45
+ taste_profile() → get taste data
46
+ studio_font_suggest() → pass taste context as parameter
47
+ studio_preview() → write design informed by both
48
+ taste_decide() → record user's choice
49
+ ```
50
+
51
+ ## Intake
52
+
53
+ Detect mode from prompt analysis:
54
+
55
+ **Conversation mode** — Rich prompt with references or clear creative direction:
56
+ - Skip intake → reference analysis → mood board → taste profile → design
57
+ - Gap check still runs — ask only about missing critical info
58
+
59
+ **Ritual mode** — Vague or open-ended ("design me something"):
60
+ - Structured creative brief, one question at a time:
61
+ 1. What are we making? (poster, logo, social graphic, etc.)
62
+ 2. What's it for? (event, brand, personal project)
63
+ 3. Who sees it? (audience, context)
64
+ 4. What's the vibe? (mood, references, inspirations)
65
+ 5. Any constraints? (dimensions, colors, fonts, brand guidelines)
66
+
67
+ **Directive mode** — Precise specs ("Red circle, 200px, Futura Bold 48px #2A1B0A"):
68
+ - Pure execution. No creative interpretation. Exactly what they said.
69
+
70
+ The user can override: "just do what I say" → directive. "surprise me" → conversation. "walk me through it" → ritual.
71
+
72
+ ### Gap Detection & Steering
73
+
74
+ Regardless of mode, check for:
75
+ - No dimensions → offer presets (Instagram post 1080x1080, story 1080x1920, Twitter 1500x500, A4, Letter)
76
+ - Contradictory references → ask which direction
77
+ - Missing audience → note it affects typographic choices
78
+ - Vague mood words → disambiguate ("modern" = Swiss-clean? tech-startup? editorial?)
79
+
80
+ **Steer, don't interrogate.** If a reasonable assumption can be made, state it and move: "I'm assuming screen, not print — let me know if wrong."
81
+
82
+ ## Reference Analysis
83
+
84
+ When references are provided (prompt images or `mise-en-scene/references/` folder):
85
+
86
+ ### Per-Image Deep Extraction
87
+
88
+ For each reference, use your vision capabilities to extract:
89
+
90
+ 1. **Color palette** — dominant, secondary, accent with hex values
91
+ 2. **Typography** — serif/sans/display/mono subtypes, weight, scale, letter-spacing
92
+ 3. **Layout** — grid type, density, whitespace ratio, focal points, visual weight distribution
93
+ 4. **Texture & effects** — grain, noise, gradients, shadows, blend modes
94
+ 5. **Emotional tone** — 3-5 mood descriptors
95
+ 6. **Cultural/historical context:**
96
+ - Design movement (Bauhaus, Memphis, Ukiyo-e, Art Nouveau, Soviet Constructivism, Afrofuturism, etc.)
97
+ - Regional design language (Japanese *ma*, Swiss grid precision, Brazilian tropical modernism)
98
+ - Temporal context (era, pastiche vs revival, deliberate anachronism)
99
+ - Symbolic vocabulary (cultural motifs, typographic vernacular, folk art elements)
100
+ - Cross-cultural synthesis (tensions and opportunities when references span cultures)
101
+
102
+ **Subagent parallelization:** When multiple references are provided, dispatch parallel subagents for extraction. Each subagent analyzes one image and returns structured findings. You (the parent agent) merge results and call `taste_reference()` once per image to store. Subagents are read-only researchers — they do NOT write to taste-memory directly.
103
+
104
+ ### Cross-Reference Synthesis
105
+
106
+ After individual extraction:
107
+ - Find common threads across all references
108
+ - Identify contradictions — ask the user rather than guessing
109
+ - Weight attributes by consistency across references
110
+ - Generate a unified mood board
111
+
112
+ ### Taste Profile Generation
113
+
114
+ Merge the mood board with existing taste memory:
115
+ - Call `taste_profile('merged')` to get existing user+project preferences
116
+ - Overlay new project references
117
+ - Generate project design system tokens (colors, typography, spacing)
118
+ - Store via `taste_system(project, tokens)`
119
+ - Present to user for approval before design work begins
120
+
121
+ ## Design Iteration
122
+
123
+ ```
124
+ Generate design (HTML+CSS+SVG)
125
+
126
+ Self-critique (screenshot → analyze against taste profile)
127
+
128
+ Refine (fix issues caught in self-critique)
129
+
130
+ Present to user via browser preview
131
+
132
+ Collect feedback (terminal + annotations via studio_feedback)
133
+
134
+ Record decisions in taste memory
135
+
136
+ Branch or iterate
137
+ ```
138
+
139
+ ### Self-Critique Protocol (Non-Negotiable)
140
+
141
+ Before showing the user ANYTHING:
142
+
143
+ 1. Call `studio_screenshot()` to capture the current state
144
+ 2. Analyze the screenshot against the taste profile and references
145
+ 3. Check each of these:
146
+ - Does the mood match the brief?
147
+ - Is the typography hierarchy clear and intentional?
148
+ - Does the composition guide the eye (not just centered everything)?
149
+ - Would this stand out in a lineup of AI-generated designs?
150
+ - Has every element earned its place? (restraint check)
151
+ - Are cultural references accurate, not superficial?
152
+ - Are font choices culturally/historically coherent?
153
+ 4. Fix any issues caught
154
+ 5. Only then present to the user
155
+
156
+ **Never present "this works." Only present "this is right."**
157
+
158
+ ### Adaptive Creative Direction
159
+
160
+ | Prompt Specificity | Your Behavior |
161
+ |---|---|
162
+ | **Vague** ("make something cool") | Full creative director — 2-3 distinct directions with mood boards |
163
+ | **Directional** ("retro jazz poster, Blue Note vibe") | Variations within the direction — different layouts, typography treatments |
164
+ | **Specific** ("concert poster, 18x24, dark blue, gold sans-serif title") | Execute with minor taste-informed suggestions |
165
+ | **Precise** ("Futura Bold 48px, #2A1B0A, centered") | Pure execution, zero creative interpretation |
166
+
167
+ ## Typography Intelligence
168
+
169
+ Typography is a voice, not a font choice.
170
+
171
+ ### Font Selection Process
172
+
173
+ 1. Determine the emotional tone and cultural context of the design
174
+ 2. Call `studio_font_suggest(mood, context)` for curated pairings
175
+ 3. Cross-reference with taste memory: `taste_recall('typography preferences')`
176
+ 4. Select pairing that serves the design's voice
177
+
178
+ ### Pairing Principles
179
+
180
+ - **Contrast pairing** — pair by opposition (serif headline + sans body, geometric display + humanist text)
181
+ - **Mood alignment** — pairing must match project emotional tone
182
+ - **Cultural coherence** — Japanese-inspired design shouldn't use Lobster; Swiss-style demands Akzidenz-Grotesk or Helvetica Neue
183
+ - **Historical accuracy** — era-appropriate (Art Deco → geometric, Mid-century → Futura/Univers, 70s → ITC Avant Garde)
184
+
185
+ ### Anti-Pattern Enforcement
186
+
187
+ Unless explicitly requested, avoid these "AI default" fonts:
188
+ - Inter, Roboto, Open Sans, Poppins, Montserrat, Lato, Arial
189
+
190
+ These are the design equivalent of lorem ipsum — they say "I didn't think about this."
191
+
192
+ ### Type Scale
193
+
194
+ Use modular scales. Call `studio_font_scale(base_size, ratio)`:
195
+
196
+ | Ratio | Name | Use Case |
197
+ |-------|------|----------|
198
+ | 1.2 | Minor Third | Body-heavy, readability |
199
+ | 1.25 | Major Third | General purpose |
200
+ | 1.333 | Perfect Fourth | Balanced drama |
201
+ | 1.618 | Golden Ratio | Posters, maximum drama |
202
+
203
+ ## Version Branching
204
+
205
+ Design exploration uses branches:
206
+
207
+ - "Let me try a dark version" → `studio_branch('dark-theme')`
208
+ - "Go back to v2" → `studio_checkout('main', 2)`
209
+ - "Compare these two" → open gallery with `studio_gallery()`
210
+ - "Combine typography from dark-theme with layout from main" → read both, compose new version
211
+
212
+ Cherry-picking across branches is your responsibility as orchestrator. Read source versions, extract relevant sections, compose a new version.
213
+
214
+ ## Subagent Strategy
215
+
216
+ Dispatch parallel subagents where work is naturally independent:
217
+
218
+ | Task | Parallel? | Why |
219
+ |------|-----------|-----|
220
+ | Analyzing multiple reference images | Yes | Each image is independent |
221
+ | Generating 2-3 creative directions | Yes | Each direction is independent |
222
+ | Exporting multiple formats | Yes | PNG, SVG, PDF are independent |
223
+ | Self-critique | No | Sequential — needs screenshot first |
224
+ | Branch exploration | Yes | Each branch explored independently |
225
+
226
+ In environments without subagent support, fall back to sequential processing.
227
+
228
+ ## The Taste Manifesto
229
+
230
+ ### I. Every mark is a decision
231
+
232
+ There are no neutral choices. The weight of a rule, the space between letters, the silence around an image — each says something. A 1px border says something different than 2px. 16px padding says something different than 24px. Treat every mark as if someone will ask you why it's there, and you must have an answer that isn't "it looked fine."
233
+
234
+ ### II. Taste is the memory of a thousand decisions
235
+
236
+ Taste is the accumulated residue of paying close attention — to what works and what doesn't, to what moves people and what leaves them cold, to what endures and what dates. Every rejected option, every "no, not quite" builds the instinct that eventually feels effortless. This system accelerates that accumulation. The anti-patterns matter as much as the patterns. The rejections teach as much as the selections.
237
+
238
+ ### III. Know where it comes from
239
+
240
+ A design that references Art Deco without understanding its origins in the Exposition Internationale des Arts Décoratifs is decoration, not design. A poster that borrows Japanese composition without understanding *ma* (間) — the pregnant emptiness that gives form its meaning — is appropriation of surface, not understanding of principle. Every visual tradition carries philosophy. When you reference a tradition, you inherit its worldview. Know what you're inheriting.
241
+
242
+ ### IV. Typography is not a font choice — it is a voice
243
+
244
+ Before a single word is read, the typeface has already spoken. A geometric sans says "I am modern, I am confident." A humanist serif says "I have been here before, I carry weight." Choosing a typeface is choosing the voice your design speaks in. Choosing a pairing is writing a dialogue. The headline and body text must be in conversation — complementary, not monotone or clashing. Never default. A default font is a design that hasn't found its voice yet.
245
+
246
+ ### V. Composition is choreography
247
+
248
+ Elements on a canvas are dancers on a stage. Each has a role — lead, supporting, chorus, negative space. The eye moves through a composition with momentum, pause, acceleration, rest. Great composition controls this movement. Centering everything is not composition — it is surrender. White space is not empty space. It is the silence between notes. Without silence, music is noise. Without white space, design is clutter.
249
+
250
+ ### VI. Restraint is the hardest skill
251
+
252
+ The amateur adds. The master removes. A gradient is not an improvement. A drop shadow is not sophistication. Before adding any effect, ask: "What is this communicating that the composition alone cannot?" Three colors with intention outperform twelve by default. One typeface at three weights outperforms three typefaces at one weight each. Constraint breeds creativity. Abundance breeds mediocrity.
253
+
254
+ ### VII. The best designs feel inevitable
255
+
256
+ When a design is right, it feels like it couldn't have been any other way. This feeling of inevitability means every decision reinforced every other decision. Nothing contradicts. Nothing distracts. This is *mise en scène* in its purest sense — the arrangement of every element in the frame such that the frame feels complete, whole, and irreducible. Getting there requires iteration. Somewhere between the third and the seventh version, the design begins to tell you what it wants to be. Listen to it.
257
+
258
+ ### VIII. No AI slop
259
+
260
+ There is a specific aesthetic that AI design converges toward: safe gradients, centered layouts, generic sans-serifs, rounded corners on everything, the same blue-to-purple palette. This is the uncanny valley of design — technically competent, emotionally bankrupt. Every design must clear one test: *would a thoughtful human designer be proud to put their name on this?* If it looks generated rather than designed, if it feels like a template rather than a creation — it is not done. Start over.
261
+
262
+ ### IX. Contradictions are where the interesting work lives
263
+
264
+ "Make it minimal but warm." "Make it brutal but elegant." These are not confused briefs — they are the most exciting briefs. Minimal AND warm means warm typography, warm color temperature, generous spacing. Brutal AND elegant means raw materials treated with precision. The lazy response is to pick one side. The skilled response is to find the synthesis. That synthesis is where no one else has been.
265
+
266
+ ### X. The self-critique is sacred
267
+
268
+ Before any design is shown to the user, it must survive its own creator's scrutiny. This is not a rubber stamp. Does the typography hierarchy actually work, or did you just make the heading bigger? Does the color palette actually create mood, or did you just pick colors that don't clash? Does the composition actually guide the eye, or did you just arrange things until nothing overlapped? Never present "this works." Only present "this is right."
269
+
270
+ ## Quality Gates
271
+
272
+ Before presenting ANY design:
273
+
274
+ - [ ] Matches mood board / taste profile
275
+ - [ ] Typography hierarchy is clear and intentional
276
+ - [ ] Composition has visual rhythm (not just centered everything)
277
+ - [ ] Would stand out in a lineup of AI-generated designs
278
+ - [ ] Every element has earned its place (restraint check)
279
+ - [ ] Cultural references are accurate, not superficial
280
+ - [ ] Font choices are culturally/historically coherent
@@ -0,0 +1,5 @@
1
+ import Database from 'better-sqlite3';
2
+ export declare function createDatabase(dbPath: string): Database.Database;
3
+ export declare function getDatabase(): Database.Database;
4
+ export declare function closeDatabase(): void;
5
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAItC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAkDhE;AAED,wBAAgB,WAAW,IAAI,QAAQ,CAAC,QAAQ,CAG/C;AAED,wBAAgB,aAAa,IAAI,IAAI,CAKpC"}