@12ui/design 0.2.28 → 0.2.29

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 (85) hide show
  1. package/README.md +69 -0
  2. package/SKILL.md +1 -1
  3. package/dist/cli-arguments.d.ts.map +1 -1
  4. package/dist/cli-arguments.js +1 -0
  5. package/dist/cli-arguments.js.map +1 -1
  6. package/dist/cli-capabilities.d.ts +6 -0
  7. package/dist/cli-capabilities.d.ts.map +1 -1
  8. package/dist/cli-capabilities.js +10 -0
  9. package/dist/cli-capabilities.js.map +1 -1
  10. package/dist/cli-help/design.d.ts +10 -0
  11. package/dist/cli-help/design.d.ts.map +1 -1
  12. package/dist/cli-help/design.js +21 -0
  13. package/dist/cli-help/design.js.map +1 -1
  14. package/dist/cli-help/index.d.ts +10 -0
  15. package/dist/cli-help/index.d.ts.map +1 -1
  16. package/dist/cli-review-command.d.ts +8 -0
  17. package/dist/cli-review-command.d.ts.map +1 -0
  18. package/dist/cli-review-command.js +71 -0
  19. package/dist/cli-review-command.js.map +1 -0
  20. package/dist/cli-run.d.ts.map +1 -1
  21. package/dist/cli-run.js +8 -0
  22. package/dist/cli-run.js.map +1 -1
  23. package/dist/cli-usage.d.ts.map +1 -1
  24. package/dist/cli-usage.js +2 -0
  25. package/dist/cli-usage.js.map +1 -1
  26. package/dist/conversion-client.d.ts.map +1 -1
  27. package/dist/conversion-client.js +8 -1
  28. package/dist/conversion-client.js.map +1 -1
  29. package/dist/next-wait-lease.d.ts +2 -1
  30. package/dist/next-wait-lease.d.ts.map +1 -1
  31. package/dist/next-wait-lease.js +73 -22
  32. package/dist/next-wait-lease.js.map +1 -1
  33. package/dist/next-wait-process.d.ts +17 -0
  34. package/dist/next-wait-process.d.ts.map +1 -0
  35. package/dist/next-wait-process.js +40 -0
  36. package/dist/next-wait-process.js.map +1 -0
  37. package/dist/review-discovery.d.ts +10 -0
  38. package/dist/review-discovery.d.ts.map +1 -0
  39. package/dist/review-discovery.js +381 -0
  40. package/dist/review-discovery.js.map +1 -0
  41. package/dist/review-edit-html.d.ts +6 -0
  42. package/dist/review-edit-html.d.ts.map +1 -0
  43. package/dist/review-edit-html.js +11 -0
  44. package/dist/review-edit-html.js.map +1 -0
  45. package/dist/review-edit-source.d.ts +22 -0
  46. package/dist/review-edit-source.d.ts.map +1 -0
  47. package/dist/review-edit-source.js +103 -0
  48. package/dist/review-edit-source.js.map +1 -0
  49. package/dist/review-edit.d.ts +15 -0
  50. package/dist/review-edit.d.ts.map +1 -0
  51. package/dist/review-edit.js +192 -0
  52. package/dist/review-edit.js.map +1 -0
  53. package/dist/review-layerdoc-diff.d.ts +7 -0
  54. package/dist/review-layerdoc-diff.d.ts.map +1 -0
  55. package/dist/review-layerdoc-diff.js +92 -0
  56. package/dist/review-layerdoc-diff.js.map +1 -0
  57. package/dist/review-page-assets.d.ts +3 -0
  58. package/dist/review-page-assets.d.ts.map +1 -0
  59. package/dist/review-page-assets.js +43 -0
  60. package/dist/review-page-assets.js.map +1 -0
  61. package/dist/review-page.d.ts +3 -0
  62. package/dist/review-page.d.ts.map +1 -0
  63. package/dist/review-page.js +237 -0
  64. package/dist/review-page.js.map +1 -0
  65. package/dist/review-pricing.d.ts +14 -0
  66. package/dist/review-pricing.d.ts.map +1 -0
  67. package/dist/review-pricing.js +23 -0
  68. package/dist/review-pricing.js.map +1 -0
  69. package/dist/review-server.d.ts +15 -0
  70. package/dist/review-server.d.ts.map +1 -0
  71. package/dist/review-server.js +204 -0
  72. package/dist/review-server.js.map +1 -0
  73. package/dist/review-store.d.ts +11 -0
  74. package/dist/review-store.d.ts.map +1 -0
  75. package/dist/review-store.js +96 -0
  76. package/dist/review-store.js.map +1 -0
  77. package/dist/review-types.d.ts +128 -0
  78. package/dist/review-types.d.ts.map +1 -0
  79. package/dist/review-types.js +2 -0
  80. package/dist/review-types.js.map +1 -0
  81. package/dist/run-journal-commands.d.ts.map +1 -1
  82. package/dist/run-journal-commands.js +6 -0
  83. package/dist/run-journal-commands.js.map +1 -1
  84. package/open-design.json +2 -2
  85. package/package.json +1 -1
@@ -0,0 +1,237 @@
1
+ import path from 'node:path';
2
+ import { reviewFileUrl } from './review-discovery.js';
3
+ import { REVIEW_PAGE_SCRIPT, REVIEW_PAGE_STYLES } from './review-page-assets.js';
4
+ import { usd } from './review-pricing.js';
5
+ const escapeHtml = (value) => value
6
+ .replaceAll('&', '&')
7
+ .replaceAll('<', '&lt;')
8
+ .replaceAll('>', '&gt;')
9
+ .replaceAll('"', '&quot;')
10
+ .replaceAll("'", '&#39;');
11
+ const groupLabels = {
12
+ drafts: 'Draft candidates',
13
+ screens: 'Branch screens',
14
+ 'branch-pages': 'Branch design',
15
+ pages: 'Exported pages',
16
+ conversions: 'Conversions',
17
+ };
18
+ const mediaAt = (file) => {
19
+ const lower = file.toLowerCase();
20
+ if (lower.endsWith('.html'))
21
+ return 'html';
22
+ if (/\.(?:png|jpe?g|webp)$/u.test(lower))
23
+ return 'image';
24
+ return 'layerdoc';
25
+ };
26
+ const mediaFrame = (args) => {
27
+ const url = reviewFileUrl(args.run, args.media.path);
28
+ if (args.media.kind === 'image') {
29
+ return `<a class="preview preview--image" href="${url}" target="_blank" rel="noreferrer">
30
+ <img src="${url}" alt="${escapeHtml(args.label)}">
31
+ </a>`;
32
+ }
33
+ if (args.media.kind === 'html') {
34
+ return `<div class="preview preview--html">
35
+ <iframe src="${url}" title="${escapeHtml(args.label)}" loading="lazy" sandbox="allow-scripts"></iframe>
36
+ <a class="open" href="${url}" target="_blank" rel="noreferrer">Open</a>
37
+ </div>`;
38
+ }
39
+ return `<div class="preview preview--layerdoc">
40
+ <span class="layerdoc-mark" aria-hidden="true">{ }</span>
41
+ <strong>LayerDoc ready</strong>
42
+ <span>Apply an edit to render a comparison.</span>
43
+ </div>`;
44
+ };
45
+ const changedRegionStrip = (edit) => {
46
+ const regions = edit.changedRegions ?? [];
47
+ if (regions.length === 0) {
48
+ return '<div class="diff-strip"><div class="diff-region"><strong>No declared region change found</strong><span>The original and edited results remain available for visual inspection.</span></div></div>';
49
+ }
50
+ return `<div class="diff-strip" aria-label="Changed-region diff">${regions.map((region) => {
51
+ const box = region.box ? ` · ${region.box.map((value) => Math.round(value)).join(', ')}` : '';
52
+ const contentChanged = region.before !== region.after;
53
+ return `<div class="diff-region">
54
+ <strong>${escapeHtml(region.viewport)} · ${escapeHtml(region.label)}</strong>
55
+ <span>${escapeHtml(region.fields.join(', '))}${escapeHtml(box)}</span>
56
+ ${contentChanged && region.before ? `<del>${escapeHtml(region.before)}</del>` : ''}
57
+ ${contentChanged && region.after ? `<ins>${escapeHtml(region.after)}</ins>` : ''}
58
+ </div>`;
59
+ }).join('')}</div>`;
60
+ };
61
+ const comparison = (run, item) => {
62
+ if (!item.latestEdit)
63
+ return '';
64
+ const originalPath = path.resolve(run.root, item.latestEdit.original);
65
+ const editedPath = path.resolve(run.root, item.latestEdit.resultPath ?? item.latestEdit.html);
66
+ const operations = item.latestEdit.editOperationIds ?? (item.latestEdit.editOperationId ? [item.latestEdit.editOperationId] : []);
67
+ const cost = item.latestEdit.cost ? usd(item.latestEdit.cost.baseUsd) : 'price not recorded';
68
+ return `<div class="comparison">
69
+ <section>
70
+ <p class="eyebrow">Original</p>
71
+ ${mediaFrame({
72
+ run,
73
+ media: { kind: mediaAt(originalPath), path: originalPath },
74
+ label: `${item.title}, original`,
75
+ })}
76
+ </section>
77
+ <section>
78
+ <p class="eyebrow">Edited</p>
79
+ ${mediaFrame({
80
+ run,
81
+ media: { kind: 'html', path: editedPath },
82
+ label: `${item.title}, edited`,
83
+ })}
84
+ </section>
85
+ </div>
86
+ ${changedRegionStrip(item.latestEdit)}
87
+ <p class="edit-receipt">Applied “${escapeHtml(item.latestEdit.prompt)}” · ${escapeHtml(cost)} base · ${operations.length} ${operations.length === 1 ? 'edit' : 'edits'}${operations.length > 0 ? ` · ${escapeHtml(operations.join(', '))}` : ''}</p>`;
88
+ };
89
+ const branchSequence = (run, item) => {
90
+ if (!item.branchPage || item.branchPage.screens.length === 0)
91
+ return '';
92
+ return `<section class="screen-sequence">
93
+ <p class="eyebrow">Plan sequence</p>
94
+ <div class="sequence-track">${item.branchPage.screens.map((screen) => `<figure class="screen">
95
+ ${mediaFrame({ run, media: screen.media, label: `${item.title}, ${screen.label}` })}
96
+ <figcaption>
97
+ <p class="eyebrow">${screen.winner ? 'Winner' : escapeHtml(screen.label)}</p>
98
+ <h5>${escapeHtml(screen.direction)}</h5>
99
+ ${screen.caption ? `<p>${escapeHtml(screen.caption)}</p>` : ''}
100
+ </figcaption>
101
+ </figure>`).join('')}</div>
102
+ </section>`;
103
+ };
104
+ const feedbackPanel = (run, item) => {
105
+ const mark = item.feedback?.mark ?? null;
106
+ return `<form class="feedback" data-feedback data-run="${escapeHtml(run.id)}" data-item="${escapeHtml(item.id)}" data-mark="${mark ?? ''}">
107
+ <div class="mark-row" role="group" aria-label="Review mark">
108
+ <button type="button" data-mark="pick" aria-pressed="${mark === 'pick'}" class="${mark === 'pick' ? 'active' : ''}">Pick</button>
109
+ <button type="button" data-mark="approve" aria-pressed="${mark === 'approve'}" class="${mark === 'approve' ? 'active' : ''}">Approve</button>
110
+ <button type="button" data-mark="" class="clear">Clear</button>
111
+ </div>
112
+ <label>
113
+ <span>Feedback</span>
114
+ <textarea name="note" maxlength="8192" placeholder="Say what to keep or change">${escapeHtml(item.feedback?.note ?? '')}</textarea>
115
+ </label>
116
+ <div class="save-row">
117
+ <button type="submit" class="save">Save</button>
118
+ <span class="status" role="status">${item.feedback ? 'Saved' : ''}</span>
119
+ </div>
120
+ </form>`;
121
+ };
122
+ const editPanel = (model, run, item) => {
123
+ if (!item.editSource || !item.editQuote)
124
+ return '';
125
+ const draft = item.editQuote.mode === 'convert-then-edit';
126
+ const price = `${usd(item.editQuote.baseUsd)}+`;
127
+ const explanation = draft
128
+ ? 'Raw image. This converts once, then edits the resulting LayerDoc; the image is not edited in place.'
129
+ : item.editSource.kind === 'package-page'
130
+ ? `Edits this page directly through its ${item.editSource.viewportCount} stored screen LayerDocs.`
131
+ : 'Edits this stored LayerDoc directly.';
132
+ if (!model.editAvailable) {
133
+ return `<div class="edit edit--disabled">
134
+ <p class="eyebrow">${draft ? 'Convert, then edit' : 'Direct edit'} · ${escapeHtml(price)}</p>
135
+ <p class="edit-note">${escapeHtml(explanation)} Run <code>12ui auth login</code> to enable paid actions.</p>
136
+ </div>`;
137
+ }
138
+ return `<form class="edit" data-edit data-mode="${item.editQuote.mode}" data-run="${escapeHtml(run.id)}" data-item="${escapeHtml(item.id)}">
139
+ <p class="eyebrow">${draft ? 'Convert, then edit' : 'Direct edit'}</p>
140
+ <p class="edit-note">${escapeHtml(explanation)}</p>
141
+ <label>
142
+ <span>Instruction</span>
143
+ <textarea name="prompt" maxlength="8192" required placeholder="Make the title shorter and increase its size"></textarea>
144
+ </label>
145
+ <p class="cost"><strong>${escapeHtml(usd(item.editQuote.baseUsd))} base after daily allowances.</strong> Image actions can add ${escapeHtml(usd(item.editQuote.possibleImageActionUsd.minimum))}–${escapeHtml(usd(item.editQuote.possibleImageActionUsd.maximum))} each. Comparison renders are included.</p>
146
+ <div class="save-row">
147
+ <button type="submit" class="apply">${draft ? 'Convert and edit' : 'Apply edit'} · ${escapeHtml(price)}</button>
148
+ <span class="status" role="status"></span>
149
+ </div>
150
+ </form>`;
151
+ };
152
+ const historyPanel = (run, item) => {
153
+ const history = item.editHistory ?? [];
154
+ if (history.length === 0)
155
+ return '';
156
+ return `<details class="history"><summary>${history.length} ${history.length === 1 ? 'edit' : 'edits'} saved in review.json</summary>
157
+ <ol>${history.map((edit) => {
158
+ const result = path.resolve(run.root, edit.resultPath ?? edit.html);
159
+ const cost = edit.cost ? usd(edit.cost.baseUsd) : 'price not recorded';
160
+ return `<li>“${escapeHtml(edit.prompt)}” · ${escapeHtml(cost)} · <a href="${reviewFileUrl(run, result)}" target="_blank" rel="noreferrer">Open result</a></li>`;
161
+ }).join('')}</ol>
162
+ </details>`;
163
+ };
164
+ const itemCard = (model, run, item) => {
165
+ const branch = item.group === 'branch-pages';
166
+ const mode = item.editQuote?.mode === 'convert-then-edit' ? 'Converts first' : item.editSource ? 'Edits directly' : '';
167
+ return `<article class="item ${branch || item.group === 'pages' || item.group === 'conversions' ? 'item--wide' : ''}" id="${escapeHtml(item.id)}">
168
+ <header class="item-head">
169
+ <div>
170
+ ${branch ? `<p class="eyebrow">Page ${(item.branchPage?.index ?? 0) + 1}</p>` : ''}
171
+ <h4>${escapeHtml(item.title)}</h4>
172
+ ${item.detail ? `<p>${escapeHtml(item.detail)}</p>` : ''}
173
+ </div>
174
+ <div class="item-badges">
175
+ ${mode ? `<span class="edit-mode">${escapeHtml(mode)}</span>` : ''}
176
+ ${item.feedback?.mark ? `<span class="mark-chip">${escapeHtml(item.feedback.mark)}</span>` : ''}
177
+ </div>
178
+ </header>
179
+ ${branchSequence(run, item)}
180
+ ${branch
181
+ ? `<section class="published-page"><p class="eyebrow">Exported page · drag the lower edge to resize</p>${mediaFrame({ run, media: item.media, label: item.title })}</section>${comparison(run, item)}`
182
+ : item.latestEdit ? comparison(run, item) : mediaFrame({ run, media: item.media, label: item.title })}
183
+ <div class="controls">
184
+ ${feedbackPanel(run, item)}
185
+ ${editPanel(model, run, item)}
186
+ ${historyPanel(run, item)}
187
+ </div>
188
+ </article>`;
189
+ };
190
+ const branchNavigation = (run) => {
191
+ if (run.branch?.scope !== 'site')
192
+ return '';
193
+ return `<nav class="site-nav" aria-label="Branch pages">${run.branch.pageItemIds.map((id, index) => {
194
+ const item = run.items.find((candidate) => candidate.id === id);
195
+ return item ? `<a href="#${escapeHtml(id)}">${index + 1}. ${escapeHtml(item.title)}</a>` : '';
196
+ }).join('')}</nav>`;
197
+ };
198
+ const runSection = (model, run) => {
199
+ const groups = [...new Set(run.items.map((item) => item.group))];
200
+ return `<section class="run" id="${escapeHtml(run.id)}">
201
+ <header class="run-head">
202
+ <div>
203
+ <p class="eyebrow">${run.branch ? `${run.branch.scope} branch` : 'Run'}</p>
204
+ <h2>${escapeHtml(run.label)}</h2>
205
+ ${run.branch ? `<p class="run-purpose">${escapeHtml(run.branch.concept)}</p>` : ''}
206
+ ${run.branch?.sitePurpose ? `<p><strong>Site intent:</strong> ${escapeHtml(run.branch.sitePurpose)}</p>` : ''}
207
+ <p>${escapeHtml(run.detail)}</p>
208
+ ${branchNavigation(run)}
209
+ </div>
210
+ <span class="count">${run.items.length} ${run.items.length === 1 ? 'item' : 'items'}</span>
211
+ </header>
212
+ ${groups.map((group) => `<section class="group group--${group}">
213
+ <h3>${groupLabels[group]}</h3>
214
+ <div class="items">${run.items.filter((item) => item.group === group).map((item) => itemCard(model, run, item)).join('')}</div>
215
+ </section>`).join('')}
216
+ </section>`;
217
+ };
218
+ export const renderReviewPage = (model) => {
219
+ const itemCount = model.runs.reduce((total, run) => total + run.items.length, 0);
220
+ const marked = model.runs.reduce((total, run) => (total + run.items.filter((item) => item.feedback?.mark).length), 0);
221
+ const body = model.runs.length > 0
222
+ ? model.runs.map((run) => runSection(model, run)).join('')
223
+ : '<section class="empty"><h2>No stored runs found</h2><p>Pass a run directory, or use <code>12ui review --all</code> from the project that owns its <code>.12ui</code> runs.</p></section>';
224
+ return `<!doctype html>
225
+ <html lang="en">
226
+ <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>12ui review</title><style>${REVIEW_PAGE_STYLES}</style></head>
227
+ <body>
228
+ <header class="mast">
229
+ <div class="brand"><span class="brand-mark">12</span><div><h1>12ui review</h1><p>Review the design, edit what is structured, and leave exact feedback.</p></div></div>
230
+ <div class="summary"><span>${model.runs.length} ${model.runs.length === 1 ? 'run' : 'runs'}</span><span>${itemCount} ${itemCount === 1 ? 'item' : 'items'}</span><span>${marked} marked</span></div>
231
+ </header>
232
+ <main class="shell">${body}</main>
233
+ <script>${REVIEW_PAGE_SCRIPT}</script>
234
+ </body>
235
+ </html>`;
236
+ };
237
+ //# sourceMappingURL=review-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-page.js","sourceRoot":"","sources":["../src/review-page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ReviewEditCost, ReviewEditQuote } from './review-types.js';
2
+ export declare const REVIEW_CONVERSION_USD = 1;
3
+ export declare const REVIEW_EDIT_USD = 0.03;
4
+ export declare const REVIEW_IMAGE_ACTION_USD: {
5
+ readonly minimum: 0.02;
6
+ readonly maximum: 0.3;
7
+ };
8
+ export declare const reviewEditQuote: (args: {
9
+ conversions?: number;
10
+ edits?: number;
11
+ }) => ReviewEditQuote;
12
+ export declare const reviewEditCost: (quote: ReviewEditQuote) => ReviewEditCost;
13
+ export declare const usd: (value: number) => string;
14
+ //# sourceMappingURL=review-pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-pricing.d.ts","sourceRoot":"","sources":["../src/review-pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzE,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,eAAO,MAAM,uBAAuB;;;CAA2C,CAAC;AAEhF,eAAO,MAAM,eAAe,GAAI,MAAM;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,KAAG,eAUH,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,OAAO,eAAe,KAAG,cAMtD,CAAC;AAEH,eAAO,MAAM,GAAG,GAAI,OAAO,MAAM,KAAG,MAAgC,CAAC"}
@@ -0,0 +1,23 @@
1
+ export const REVIEW_CONVERSION_USD = 1;
2
+ export const REVIEW_EDIT_USD = 0.03;
3
+ export const REVIEW_IMAGE_ACTION_USD = { minimum: 0.02, maximum: 0.3 };
4
+ export const reviewEditQuote = (args) => {
5
+ const conversionUsd = REVIEW_CONVERSION_USD * (args.conversions ?? 0);
6
+ const editUsd = REVIEW_EDIT_USD * (args.edits ?? 1);
7
+ return {
8
+ mode: conversionUsd > 0 ? 'convert-then-edit' : 'direct',
9
+ conversionUsd,
10
+ editUsd,
11
+ baseUsd: conversionUsd + editUsd,
12
+ possibleImageActionUsd: { ...REVIEW_IMAGE_ACTION_USD },
13
+ };
14
+ };
15
+ export const reviewEditCost = (quote) => ({
16
+ currency: 'USD',
17
+ conversionUsd: quote.conversionUsd,
18
+ editUsd: quote.editUsd,
19
+ baseUsd: quote.baseUsd,
20
+ possibleImageActionUsd: { ...quote.possibleImageActionUsd },
21
+ });
22
+ export const usd = (value) => `$${value.toFixed(2)}`;
23
+ //# sourceMappingURL=review-pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-pricing.js","sourceRoot":"","sources":["../src/review-pricing.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC;AACvC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AACpC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAW,CAAC;AAEhF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAG/B,EAAmB,EAAE;IACpB,MAAM,aAAa,GAAG,qBAAqB,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,eAAe,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;IACpD,OAAO;QACL,IAAI,EAAE,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,QAAQ;QACxD,aAAa;QACb,OAAO;QACP,OAAO,EAAE,aAAa,GAAG,OAAO;QAChC,sBAAsB,EAAE,EAAE,GAAG,uBAAuB,EAAE;KACvD,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAsB,EAAkB,EAAE,CAAC,CAAC;IACzE,QAAQ,EAAE,KAAK;IACf,aAAa,EAAE,KAAK,CAAC,aAAa;IAClC,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,OAAO,EAAE,KAAK,CAAC,OAAO;IACtB,sBAAsB,EAAE,EAAE,GAAG,KAAK,CAAC,sBAAsB,EAAE;CAC5D,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ReviewEditRunner } from './review-edit.js';
2
+ export type ReviewServer = {
3
+ url: string;
4
+ port: number;
5
+ close: () => Promise<void>;
6
+ };
7
+ export declare const startReviewServer: (options: {
8
+ target?: string;
9
+ all?: boolean;
10
+ cwd?: string;
11
+ tempRoot?: string;
12
+ port?: number;
13
+ editRunner?: ReviewEditRunner;
14
+ }) => Promise<ReviewServer>;
15
+ //# sourceMappingURL=review-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-server.d.ts","sourceRoot":"","sources":["../src/review-server.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAyGzD,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,SAAS;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,KAAG,OAAO,CAAC,YAAY,CAsGvB,CAAC"}
@@ -0,0 +1,204 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { realpath, stat } from 'node:fs/promises';
3
+ import http from 'node:http';
4
+ import path from 'node:path';
5
+ import { discoverReviewModel } from './review-discovery.js';
6
+ import { renderReviewPage } from './review-page.js';
7
+ import { saveReviewFeedback } from './review-store.js';
8
+ const CONTENT_TYPES = {
9
+ '.avif': 'image/avif',
10
+ '.css': 'text/css; charset=utf-8',
11
+ '.gif': 'image/gif',
12
+ '.html': 'text/html; charset=utf-8',
13
+ '.jpeg': 'image/jpeg',
14
+ '.jpg': 'image/jpeg',
15
+ '.js': 'text/javascript; charset=utf-8',
16
+ '.json': 'application/json; charset=utf-8',
17
+ '.png': 'image/png',
18
+ '.svg': 'image/svg+xml',
19
+ '.ttf': 'font/ttf',
20
+ '.webp': 'image/webp',
21
+ '.woff': 'font/woff',
22
+ '.woff2': 'font/woff2',
23
+ };
24
+ const sendJson = (response, status, value) => {
25
+ response.writeHead(status, {
26
+ 'content-type': 'application/json; charset=utf-8',
27
+ 'cache-control': 'no-store',
28
+ });
29
+ response.end(`${JSON.stringify(value)}\n`);
30
+ };
31
+ const sendPage = (response, page, head = false) => {
32
+ const bytes = Buffer.from(page, 'utf8');
33
+ response.writeHead(200, {
34
+ 'content-type': 'text/html; charset=utf-8',
35
+ 'content-length': bytes.byteLength,
36
+ 'cache-control': 'no-store',
37
+ 'content-security-policy': "default-src 'self'; img-src 'self' data: https:; frame-src 'self'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; connect-src 'self'",
38
+ });
39
+ response.end(head ? undefined : bytes);
40
+ };
41
+ const readJsonBody = async (request) => {
42
+ const chunks = [];
43
+ let bytes = 0;
44
+ for await (const chunk of request) {
45
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
46
+ bytes += buffer.byteLength;
47
+ if (bytes > 16 * 1024)
48
+ throw new Error('Request body must be 16 KiB or smaller');
49
+ chunks.push(buffer);
50
+ }
51
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
52
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
53
+ throw new Error('Request body must be a JSON object');
54
+ }
55
+ return parsed;
56
+ };
57
+ const requested = (model, runId, itemId) => {
58
+ if (typeof runId !== 'string' || typeof itemId !== 'string') {
59
+ throw new Error('Run and item are required');
60
+ }
61
+ const run = model.runs.find((candidate) => candidate.id === runId);
62
+ const item = run?.items.find((candidate) => candidate.id === itemId);
63
+ if (!run || !item)
64
+ throw new Error('This review item no longer exists');
65
+ return { run, item };
66
+ };
67
+ const serveRunFile = async (args) => {
68
+ const segments = args.pathname.split('/').slice(2).map((segment) => decodeURIComponent(segment));
69
+ const runId = segments.shift();
70
+ const run = args.model.runs.find((candidate) => candidate.id === runId);
71
+ if (!run || segments.length === 0 || segments.some((segment) => !segment || segment === '..')) {
72
+ args.response.writeHead(404).end('not found');
73
+ return;
74
+ }
75
+ const root = await realpath(run.root).catch(() => null);
76
+ const candidate = path.resolve(run.root, ...segments);
77
+ const file = await realpath(candidate).catch(() => null);
78
+ if (!root || !file || (file !== root && !file.startsWith(`${root}${path.sep}`))) {
79
+ args.response.writeHead(403).end('forbidden');
80
+ return;
81
+ }
82
+ const details = await stat(file).catch(() => null);
83
+ if (!details?.isFile()) {
84
+ args.response.writeHead(404).end('not found');
85
+ return;
86
+ }
87
+ args.response.writeHead(200, {
88
+ 'content-type': CONTENT_TYPES[path.extname(file).toLowerCase()] ?? 'application/octet-stream',
89
+ 'content-length': details.size,
90
+ 'cache-control': 'no-store',
91
+ });
92
+ if (args.method === 'HEAD')
93
+ args.response.end();
94
+ else
95
+ createReadStream(file).pipe(args.response);
96
+ };
97
+ export const startReviewServer = async (options) => {
98
+ const discovery = {
99
+ ...(options.target ? { target: options.target } : {}),
100
+ ...(options.all ? { all: true } : {}),
101
+ ...(options.cwd ? { cwd: options.cwd } : {}),
102
+ ...(options.tempRoot ? { tempRoot: options.tempRoot } : {}),
103
+ editAvailable: options.editRunner !== undefined,
104
+ };
105
+ let model = await discoverReviewModel(discovery);
106
+ const refresh = async () => {
107
+ model = await discoverReviewModel(discovery);
108
+ return model;
109
+ };
110
+ const server = http.createServer(async (request, response) => {
111
+ const method = request.method ?? 'GET';
112
+ let pathname;
113
+ try {
114
+ pathname = new URL(request.url ?? '/', 'http://127.0.0.1').pathname;
115
+ }
116
+ catch {
117
+ response.writeHead(400).end('bad request');
118
+ return;
119
+ }
120
+ try {
121
+ if ((method === 'GET' || method === 'HEAD') && (pathname === '/' || pathname === '/index.html')) {
122
+ sendPage(response, renderReviewPage(await refresh()), method === 'HEAD');
123
+ return;
124
+ }
125
+ if ((method === 'GET' || method === 'HEAD') && pathname.startsWith('/files/')) {
126
+ await serveRunFile({ model, pathname, method, response });
127
+ return;
128
+ }
129
+ if (method === 'POST' && pathname === '/api/feedback') {
130
+ const body = await readJsonBody(request);
131
+ const { run, item } = requested(model, body.runId, body.itemId);
132
+ const mark = body.mark === null || body.mark === 'pick' || body.mark === 'approve'
133
+ ? body.mark
134
+ : undefined;
135
+ if (mark === undefined || typeof body.note !== 'string') {
136
+ throw new Error('Feedback needs a note and a pick, approve, or empty mark');
137
+ }
138
+ const feedback = await saveReviewFeedback({
139
+ runRoot: run.root,
140
+ itemId: item.id,
141
+ note: body.note,
142
+ mark,
143
+ });
144
+ item.feedback = feedback;
145
+ sendJson(response, 200, { ok: true, feedback });
146
+ return;
147
+ }
148
+ if (method === 'POST' && pathname === '/api/edit') {
149
+ if (!options.editRunner)
150
+ throw new Error('Run 12ui auth login to enable paid edits');
151
+ const body = await readJsonBody(request);
152
+ const { run, item } = requested(model, body.runId, body.itemId);
153
+ if (!item.editSource)
154
+ throw new Error('This item has no editable design source');
155
+ if (typeof body.prompt !== 'string')
156
+ throw new Error('Write an edit instruction first');
157
+ const previousLayerDocs = item.latestEdit?.layerdocs
158
+ ?? (item.latestEdit?.layerdoc ? [item.latestEdit.layerdoc] : []);
159
+ const edit = await options.editRunner({
160
+ runRoot: run.root,
161
+ itemId: item.id,
162
+ source: previousLayerDocs.length > 0
163
+ ? {
164
+ kind: 'layerdocs',
165
+ layerdocPaths: previousLayerDocs.map((file) => path.resolve(run.root, file)),
166
+ }
167
+ : item.editSource,
168
+ prompt: body.prompt,
169
+ });
170
+ item.latestEdit = edit;
171
+ item.editHistory = [...(item.editHistory ?? []), edit];
172
+ sendJson(response, 200, { ok: true, edit });
173
+ return;
174
+ }
175
+ response.writeHead(method === 'GET' || method === 'HEAD' ? 404 : 405).end(method === 'GET' || method === 'HEAD' ? 'not found' : 'method not allowed');
176
+ }
177
+ catch (error) {
178
+ sendJson(response, 400, {
179
+ ok: false,
180
+ error: error instanceof Error ? error.message : String(error),
181
+ });
182
+ }
183
+ });
184
+ await new Promise((resolve, reject) => {
185
+ server.once('error', reject);
186
+ server.listen(options.port ?? 0, '127.0.0.1', () => resolve());
187
+ });
188
+ const address = server.address();
189
+ if (!address || typeof address === 'string') {
190
+ await new Promise((resolve) => server.close(() => resolve()));
191
+ throw new Error('Review server did not bind a local TCP port');
192
+ }
193
+ return {
194
+ url: `http://127.0.0.1:${address.port}`,
195
+ port: address.port,
196
+ close: () => new Promise((resolve, reject) => {
197
+ server.close((error) => { if (error)
198
+ reject(error);
199
+ else
200
+ resolve(); });
201
+ }),
202
+ };
203
+ };
204
+ //# sourceMappingURL=review-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-server.js","sourceRoot":"","sources":["../src/review-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { type ReviewFeedback, type ReviewMark, type ReviewRecord } from './review-types.js';
2
+ export declare const reviewRecordPath: (runRoot: string) => string;
3
+ export declare const readReviewRecord: (runRoot: string) => Promise<ReviewRecord>;
4
+ export declare const updateReviewRecord: (runRoot: string, update: (record: ReviewRecord) => void) => Promise<ReviewRecord>;
5
+ export declare const saveReviewFeedback: (args: {
6
+ runRoot: string;
7
+ itemId: string;
8
+ note: string;
9
+ mark: ReviewMark | null;
10
+ }) => Promise<ReviewFeedback>;
11
+ //# sourceMappingURL=review-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-store.d.ts","sourceRoot":"","sources":["../src/review-store.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAoB3B,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAElD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,YAAY,CAiB5E,CAAC;AAoBF,eAAO,MAAM,kBAAkB,GAC7B,SAAS,MAAM,EACf,QAAQ,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,KACrC,OAAO,CAAC,YAAY,CAiBtB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,MAAM;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB,KAAG,OAAO,CAAC,cAAc,CAazB,CAAC"}