@12ui/design 0.2.13 → 0.2.15

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 (188) hide show
  1. package/README.md +35 -9
  2. package/dist/bounded-concurrency.d.ts +13 -0
  3. package/dist/bounded-concurrency.d.ts.map +1 -0
  4. package/dist/bounded-concurrency.js +43 -0
  5. package/dist/bounded-concurrency.js.map +1 -0
  6. package/dist/branch-client.d.ts +155 -0
  7. package/dist/branch-client.d.ts.map +1 -0
  8. package/dist/branch-client.js +179 -0
  9. package/dist/branch-client.js.map +1 -0
  10. package/dist/branch-completion.d.ts +37 -0
  11. package/dist/branch-completion.d.ts.map +1 -0
  12. package/dist/branch-completion.js +137 -0
  13. package/dist/branch-completion.js.map +1 -0
  14. package/dist/branch-dispatch-loop.d.ts +84 -0
  15. package/dist/branch-dispatch-loop.d.ts.map +1 -0
  16. package/dist/branch-dispatch-loop.js +206 -0
  17. package/dist/branch-dispatch-loop.js.map +1 -0
  18. package/dist/branch-downloads.d.ts +53 -0
  19. package/dist/branch-downloads.d.ts.map +1 -0
  20. package/dist/branch-downloads.js +114 -0
  21. package/dist/branch-downloads.js.map +1 -0
  22. package/dist/branch-execution-types.d.ts +54 -0
  23. package/dist/branch-execution-types.d.ts.map +1 -0
  24. package/dist/branch-execution-types.js +2 -0
  25. package/dist/branch-execution-types.js.map +1 -0
  26. package/dist/branch-execution.d.ts +20 -0
  27. package/dist/branch-execution.d.ts.map +1 -0
  28. package/dist/branch-execution.js +380 -0
  29. package/dist/branch-execution.js.map +1 -0
  30. package/dist/branch-identity.d.ts +71 -0
  31. package/dist/branch-identity.d.ts.map +1 -0
  32. package/dist/branch-identity.js +72 -0
  33. package/dist/branch-identity.js.map +1 -0
  34. package/dist/branch-materialize.d.ts +33 -0
  35. package/dist/branch-materialize.d.ts.map +1 -0
  36. package/dist/branch-materialize.js +64 -0
  37. package/dist/branch-materialize.js.map +1 -0
  38. package/dist/branch-page-conversion.d.ts +127 -0
  39. package/dist/branch-page-conversion.d.ts.map +1 -0
  40. package/dist/branch-page-conversion.js +382 -0
  41. package/dist/branch-page-conversion.js.map +1 -0
  42. package/dist/branch-plan.d.ts +63 -0
  43. package/dist/branch-plan.d.ts.map +1 -0
  44. package/dist/branch-plan.js +95 -0
  45. package/dist/branch-plan.js.map +1 -0
  46. package/dist/branch-progress.d.ts +99 -0
  47. package/dist/branch-progress.d.ts.map +1 -0
  48. package/dist/branch-progress.js +188 -0
  49. package/dist/branch-progress.js.map +1 -0
  50. package/dist/branch-run-record.d.ts +164 -0
  51. package/dist/branch-run-record.d.ts.map +1 -0
  52. package/dist/branch-run-record.js +94 -0
  53. package/dist/branch-run-record.js.map +1 -0
  54. package/dist/branch-schedule.d.ts +74 -0
  55. package/dist/branch-schedule.d.ts.map +1 -0
  56. package/dist/branch-schedule.js +130 -0
  57. package/dist/branch-schedule.js.map +1 -0
  58. package/dist/branch-status.d.ts +11 -0
  59. package/dist/branch-status.d.ts.map +1 -0
  60. package/dist/branch-status.js +118 -0
  61. package/dist/branch-status.js.map +1 -0
  62. package/dist/branch-viewport-normalization.d.ts +74 -0
  63. package/dist/branch-viewport-normalization.d.ts.map +1 -0
  64. package/dist/branch-viewport-normalization.js +120 -0
  65. package/dist/branch-viewport-normalization.js.map +1 -0
  66. package/dist/cli-arguments.d.ts.map +1 -1
  67. package/dist/cli-arguments.js +5 -0
  68. package/dist/cli-arguments.js.map +1 -1
  69. package/dist/cli-branch-command.d.ts +55 -0
  70. package/dist/cli-branch-command.d.ts.map +1 -0
  71. package/dist/cli-branch-command.js +475 -0
  72. package/dist/cli-branch-command.js.map +1 -0
  73. package/dist/cli-capabilities.d.ts +51 -0
  74. package/dist/cli-capabilities.d.ts.map +1 -1
  75. package/dist/cli-capabilities.js +52 -1
  76. package/dist/cli-capabilities.js.map +1 -1
  77. package/dist/cli-create-command.d.ts +21 -0
  78. package/dist/cli-create-command.d.ts.map +1 -0
  79. package/dist/cli-create-command.js +319 -0
  80. package/dist/cli-create-command.js.map +1 -0
  81. package/dist/cli-detach.d.ts +62 -0
  82. package/dist/cli-detach.d.ts.map +1 -0
  83. package/dist/cli-detach.js +123 -0
  84. package/dist/cli-detach.js.map +1 -0
  85. package/dist/cli-draft-command.d.ts +63 -0
  86. package/dist/cli-draft-command.d.ts.map +1 -0
  87. package/dist/cli-draft-command.js +220 -0
  88. package/dist/cli-draft-command.js.map +1 -0
  89. package/dist/cli-export-command.d.ts.map +1 -1
  90. package/dist/cli-export-command.js +2 -3
  91. package/dist/cli-export-command.js.map +1 -1
  92. package/dist/cli-image-batch-command.d.ts +21 -0
  93. package/dist/cli-image-batch-command.d.ts.map +1 -1
  94. package/dist/cli-image-batch-command.js +65 -4
  95. package/dist/cli-image-batch-command.js.map +1 -1
  96. package/dist/cli-image-batch-resume.d.ts +7 -0
  97. package/dist/cli-image-batch-resume.d.ts.map +1 -1
  98. package/dist/cli-image-batch-resume.js +19 -2
  99. package/dist/cli-image-batch-resume.js.map +1 -1
  100. package/dist/cli-package-command.d.ts +29 -0
  101. package/dist/cli-package-command.d.ts.map +1 -0
  102. package/dist/cli-package-command.js +128 -0
  103. package/dist/cli-package-command.js.map +1 -0
  104. package/dist/cli-progress.d.ts +137 -0
  105. package/dist/cli-progress.d.ts.map +1 -1
  106. package/dist/cli-progress.js +156 -0
  107. package/dist/cli-progress.js.map +1 -1
  108. package/dist/cli-select-command.d.ts +12 -0
  109. package/dist/cli-select-command.d.ts.map +1 -0
  110. package/dist/cli-select-command.js +66 -0
  111. package/dist/cli-select-command.js.map +1 -0
  112. package/dist/cli.js +150 -7
  113. package/dist/cli.js.map +1 -1
  114. package/dist/conversion-receipt.d.ts +127 -0
  115. package/dist/conversion-receipt.d.ts.map +1 -0
  116. package/dist/conversion-receipt.js +184 -0
  117. package/dist/conversion-receipt.js.map +1 -0
  118. package/dist/create-identity.d.ts +25 -0
  119. package/dist/create-identity.d.ts.map +1 -0
  120. package/dist/create-identity.js +0 -0
  121. package/dist/create-identity.js.map +1 -0
  122. package/dist/draft-identity.d.ts +30 -0
  123. package/dist/draft-identity.d.ts.map +1 -0
  124. package/dist/draft-identity.js +0 -0
  125. package/dist/draft-identity.js.map +1 -0
  126. package/dist/draft-prompts.d.ts +35 -0
  127. package/dist/draft-prompts.d.ts.map +1 -0
  128. package/dist/draft-prompts.js +51 -0
  129. package/dist/draft-prompts.js.map +1 -0
  130. package/dist/draft-run.d.ts +32 -0
  131. package/dist/draft-run.d.ts.map +1 -0
  132. package/dist/draft-run.js +152 -0
  133. package/dist/draft-run.js.map +1 -0
  134. package/dist/draft-workspace.d.ts +90 -0
  135. package/dist/draft-workspace.d.ts.map +1 -0
  136. package/dist/draft-workspace.js +64 -0
  137. package/dist/draft-workspace.js.map +1 -0
  138. package/dist/image-batch-status.d.ts +42 -0
  139. package/dist/image-batch-status.d.ts.map +1 -0
  140. package/dist/image-batch-status.js +142 -0
  141. package/dist/image-batch-status.js.map +1 -0
  142. package/dist/image-dimensions.d.ts +12 -0
  143. package/dist/image-dimensions.d.ts.map +1 -0
  144. package/dist/image-dimensions.js +14 -0
  145. package/dist/image-dimensions.js.map +1 -0
  146. package/dist/image-generation-resume.d.ts +13 -0
  147. package/dist/image-generation-resume.d.ts.map +1 -1
  148. package/dist/image-generation-resume.js +13 -0
  149. package/dist/image-generation-resume.js.map +1 -1
  150. package/dist/index.d.ts +6 -1
  151. package/dist/index.d.ts.map +1 -1
  152. package/dist/index.js +5 -1
  153. package/dist/index.js.map +1 -1
  154. package/dist/legacy-skill-catalog.d.ts.map +1 -1
  155. package/dist/legacy-skill-catalog.js +140 -0
  156. package/dist/legacy-skill-catalog.js.map +1 -1
  157. package/dist/package-client.d.ts +28 -0
  158. package/dist/package-client.d.ts.map +1 -1
  159. package/dist/package-client.js +21 -1
  160. package/dist/package-client.js.map +1 -1
  161. package/dist/package-manifest-file.d.ts +21 -0
  162. package/dist/package-manifest-file.d.ts.map +1 -0
  163. package/dist/package-manifest-file.js +119 -0
  164. package/dist/package-manifest-file.js.map +1 -0
  165. package/dist/package-manifest-template.d.ts +23 -0
  166. package/dist/package-manifest-template.d.ts.map +1 -0
  167. package/dist/package-manifest-template.js +60 -0
  168. package/dist/package-manifest-template.js.map +1 -0
  169. package/dist/package-source-refusal.d.ts +17 -0
  170. package/dist/package-source-refusal.d.ts.map +1 -0
  171. package/dist/package-source-refusal.js +61 -0
  172. package/dist/package-source-refusal.js.map +1 -0
  173. package/dist/package-submission.d.ts +134 -0
  174. package/dist/package-submission.d.ts.map +1 -0
  175. package/dist/package-submission.js +196 -0
  176. package/dist/package-submission.js.map +1 -0
  177. package/dist/selection-record.d.ts +95 -0
  178. package/dist/selection-record.d.ts.map +1 -0
  179. package/dist/selection-record.js +121 -0
  180. package/dist/selection-record.js.map +1 -0
  181. package/package.json +1 -1
  182. package/skills/design/SKILL.md +43 -266
  183. package/skills/design-branch/SKILL.md +37 -116
  184. package/skills/design-convert/SKILL.md +62 -148
  185. package/skills/design-draft/SKILL.md +71 -135
  186. package/skills/design-search/SKILL.md +29 -78
  187. package/skills/design/references/continuation-contract.md +0 -60
  188. package/skills/design/references/selection-contract.md +0 -112
@@ -0,0 +1,94 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ /**
5
+ * The two durable records of a branch run.
6
+ *
7
+ * `branch.attempt.json` is written BEFORE any spend, exclusively, so a plain
8
+ * re-run in the same directory refuses instead of buying a second run; only
9
+ * `--resume` may proceed. `branch.complete.json` is the sentinel: written once,
10
+ * atomically, only at a terminal state. Its ABSENCE means the run is still
11
+ * going — never that it failed. It is deliberately not named
12
+ * `generation.complete.json`, which is `image generate-batch`'s record and a
13
+ * different thing entirely.
14
+ */
15
+ export const BRANCH_ATTEMPT_FILENAME = 'branch.attempt.json';
16
+ export const BRANCH_COMPLETION_FILENAME = 'branch.complete.json';
17
+ export const BRANCH_PLAN_FILENAME = 'branch.plan.json';
18
+ export const BRANCH_SCREENS_DIRECTORY = 'screens';
19
+ export const BRANCH_PAGES_DIRECTORY = 'pages';
20
+ export const branchAttemptPath = (outDir) => (path.join(outDir, BRANCH_ATTEMPT_FILENAME));
21
+ export const branchCompletionPath = (outDir) => (path.join(outDir, BRANCH_COMPLETION_FILENAME));
22
+ export const branchPlanPath = (outDir) => (path.join(outDir, BRANCH_PLAN_FILENAME));
23
+ const writeJsonAtomically = async (file, value) => {
24
+ await mkdir(path.dirname(file), { recursive: true });
25
+ const temporary = `${file}.${randomUUID()}.tmp`;
26
+ try {
27
+ await writeFile(temporary, `${JSON.stringify(value, null, 2)}\n`, {
28
+ encoding: 'utf8',
29
+ flag: 'wx',
30
+ mode: 0o600,
31
+ });
32
+ await rename(temporary, file);
33
+ }
34
+ finally {
35
+ await rm(temporary, { force: true });
36
+ }
37
+ };
38
+ export const writeBranchAttempt = async (file, attempt, exclusive = false) => {
39
+ if (!exclusive) {
40
+ await writeJsonAtomically(file, attempt);
41
+ return;
42
+ }
43
+ await mkdir(path.dirname(file), { recursive: true });
44
+ await writeFile(file, `${JSON.stringify(attempt, null, 2)}\n`, {
45
+ encoding: 'utf8',
46
+ flag: 'wx',
47
+ mode: 0o600,
48
+ });
49
+ };
50
+ export const readBranchAttempt = async (file) => {
51
+ let source;
52
+ try {
53
+ source = await readFile(file, 'utf8');
54
+ }
55
+ catch (error) {
56
+ if (error.code === 'ENOENT')
57
+ return undefined;
58
+ throw error;
59
+ }
60
+ try {
61
+ const value = JSON.parse(source);
62
+ if (typeof value !== 'object' || value === null)
63
+ return undefined;
64
+ return value;
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ };
70
+ export const writeBranchPlanRecord = async (file, plan) => {
71
+ await rm(file, { force: true });
72
+ await writeJsonAtomically(file, { version: 1, ...plan });
73
+ };
74
+ export const writeBranchCompletion = async (file, completion) => {
75
+ await writeJsonAtomically(file, completion);
76
+ };
77
+ export const readBranchCompletion = async (file) => {
78
+ let source;
79
+ try {
80
+ source = await readFile(file, 'utf8');
81
+ }
82
+ catch (error) {
83
+ if (error.code === 'ENOENT')
84
+ return undefined;
85
+ throw error;
86
+ }
87
+ try {
88
+ return JSON.parse(source);
89
+ }
90
+ catch {
91
+ return undefined;
92
+ }
93
+ };
94
+ //# sourceMappingURL=branch-run-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-run-record.js","sourceRoot":"","sources":["../src/branch-run-record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The branch scheduler — the one place that decides which screens may be
3
+ * dispatched right now. It is a near-verbatim port of the web board's
4
+ * `apps/web/src/pages/branch/branchSchedule.ts`, which in turn mirrors the
5
+ * worker's dispatch gate (`workers/api/src/create/branchReferences.ts`). One
6
+ * policy, three surfaces: the worker stays the authority and answers a nested
7
+ * 409 when any client disagrees.
8
+ *
9
+ * Two rules gate a screen:
10
+ * 1. DEPENDENCY — the previous viewport of the same page must have SUCCEEDED,
11
+ * because it is the screen's continuation reference.
12
+ * 2. FOOTER SEED — the first footer-bearing slot in plan order draws the
13
+ * site's footer with no example; every later footer screen waits for it.
14
+ * Once the seed has FAILED they proceed without it rather than deadlock.
15
+ *
16
+ * Slots are assigned in plan order (page order then viewport order), so slot
17
+ * order IS plan order and the seed is simply the first footer-bearing slot.
18
+ *
19
+ * The one addition over the web copy is `branchBlockedSlots`, which reports the
20
+ * screens `branchRunSettled` counts as terminal-but-unreachable. It derives
21
+ * that from `branchSlotGate` rather than re-deciding it, so there is still only
22
+ * one gate predicate here.
23
+ */
24
+ export type BranchScheduleStatus = 'pending' | 'dispatched' | 'succeeded' | 'failed';
25
+ export type BranchScheduleCandidate = {
26
+ slot: string;
27
+ status: BranchScheduleStatus;
28
+ /** null on non-webpage surfaces, where footer semantics do not apply. */
29
+ footer?: boolean | null;
30
+ dependsOnSlot?: string | null;
31
+ };
32
+ export type BranchSlotGate = {
33
+ ready: true;
34
+ } | {
35
+ ready: false;
36
+ reason: 'dependency' | 'footer-seed';
37
+ waitingOn: string;
38
+ /** The awaited slot failed, so waiting can never resolve on its own. */
39
+ blocked: boolean;
40
+ };
41
+ /** THE footer seed: first footer-bearing slot in plan (= slot) order. */
42
+ export declare const branchFooterSeedSlot: (candidates: readonly BranchScheduleCandidate[]) => string | null;
43
+ /** Whether one screen's references are all in place, and what it waits on. */
44
+ export declare const branchSlotGate: (slot: string, candidates: readonly BranchScheduleCandidate[]) => BranchSlotGate;
45
+ export type BranchScheduleOptions = {
46
+ /** Slots this client already has a dispatch in flight for. */
47
+ inFlight?: ReadonlySet<string>;
48
+ /** Slot -> epoch ms before which a gate-refused slot is not retried. */
49
+ heldUntil?: Readonly<Record<string, number>>;
50
+ now?: number;
51
+ };
52
+ /**
53
+ * Every slot that may be dispatched now: still pending, not already in flight,
54
+ * not inside a gate-refusal backoff, and with both gates satisfied. Settled
55
+ * slots are never rescheduled — a regenerate is always an explicit user action.
56
+ */
57
+ export declare const branchDispatchableSlots: (candidates: readonly BranchScheduleCandidate[], options?: BranchScheduleOptions) => string[];
58
+ /** True while any screen is still expected to move without user action. */
59
+ export declare const branchRunActive: (candidates: readonly BranchScheduleCandidate[]) => boolean;
60
+ /** True once no screen can progress further without a user action. */
61
+ export declare const branchRunSettled: (candidates: readonly BranchScheduleCandidate[]) => boolean;
62
+ export type BranchBlockedSlot = {
63
+ slot: string;
64
+ reason: 'dependency' | 'footer-seed';
65
+ waitingOn: string;
66
+ };
67
+ /**
68
+ * The screens a failed dependency put permanently out of reach. These are the
69
+ * slots `branchRunSettled` treats as terminal without them ever running, so a
70
+ * caller must be able to name them and their cause rather than waiting on a
71
+ * status that will never change.
72
+ */
73
+ export declare const branchBlockedSlots: (candidates: readonly BranchScheduleCandidate[]) => BranchBlockedSlot[];
74
+ //# sourceMappingURL=branch-schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-schedule.d.ts","sourceRoot":"","sources":["../src/branch-schedule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,MAAM,oBAAoB,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;AAErF,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,CAAC;IAC7B,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,cAAc,GACtB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IACA,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAIJ,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,GAC/B,YAAY,SAAS,uBAAuB,EAAE,KAC7C,MAAM,GAAG,IAIX,CAAC;AA6EF,8EAA8E;AAC9E,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,YAAY,SAAS,uBAAuB,EAAE,KAC7C,cAKF,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC/B,wEAAwE;IACxE,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,GAClC,YAAY,SAAS,uBAAuB,EAAE,EAC9C,UAAS,qBAA0B,KAClC,MAAM,EAaR,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,eAAe,GAC1B,YAAY,SAAS,uBAAuB,EAAE,KAC7C,OAA4E,CAAC;AAEhF,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,GAC3B,YAAY,SAAS,uBAAuB,EAAE,KAC7C,OAWF,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,SAAS,uBAAuB,EAAE,KAC7C,iBAAiB,EAKyC,CAAC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The branch scheduler — the one place that decides which screens may be
3
+ * dispatched right now. It is a near-verbatim port of the web board's
4
+ * `apps/web/src/pages/branch/branchSchedule.ts`, which in turn mirrors the
5
+ * worker's dispatch gate (`workers/api/src/create/branchReferences.ts`). One
6
+ * policy, three surfaces: the worker stays the authority and answers a nested
7
+ * 409 when any client disagrees.
8
+ *
9
+ * Two rules gate a screen:
10
+ * 1. DEPENDENCY — the previous viewport of the same page must have SUCCEEDED,
11
+ * because it is the screen's continuation reference.
12
+ * 2. FOOTER SEED — the first footer-bearing slot in plan order draws the
13
+ * site's footer with no example; every later footer screen waits for it.
14
+ * Once the seed has FAILED they proceed without it rather than deadlock.
15
+ *
16
+ * Slots are assigned in plan order (page order then viewport order), so slot
17
+ * order IS plan order and the seed is simply the first footer-bearing slot.
18
+ *
19
+ * The one addition over the web copy is `branchBlockedSlots`, which reports the
20
+ * screens `branchRunSettled` counts as terminal-but-unreachable. It derives
21
+ * that from `branchSlotGate` rather than re-deciding it, so there is still only
22
+ * one gate predicate here.
23
+ */
24
+ const READY = { ready: true };
25
+ /** THE footer seed: first footer-bearing slot in plan (= slot) order. */
26
+ export const branchFooterSeedSlot = (candidates) => ([...candidates]
27
+ .sort((left, right) => left.slot.localeCompare(right.slot))
28
+ .find((candidate) => candidate.footer === true)?.slot ?? null);
29
+ /**
30
+ * Unreachability is TRANSITIVE. A screen waiting on a screen that itself can
31
+ * never run is just as terminal as one waiting on a screen that already failed
32
+ * — and a footer screen waiting on a seed in that state is the case that
33
+ * deadlocks a run without either of them ever being marked failed.
34
+ */
35
+ const neverSettles = (slot, candidates, bySlot, seen) => {
36
+ if (seen.has(slot))
37
+ return false;
38
+ const candidate = bySlot.get(slot);
39
+ if (!candidate || candidate.status !== 'pending')
40
+ return false;
41
+ const gate = gateFor(slot, candidates, bySlot, new Set(seen).add(slot));
42
+ return !gate.ready && gate.blocked;
43
+ };
44
+ const neverSucceeds = (slot, candidates, bySlot, seen) => {
45
+ const candidate = bySlot.get(slot);
46
+ if (!candidate)
47
+ return false;
48
+ if (candidate.status === 'failed')
49
+ return true;
50
+ return neverSettles(slot, candidates, bySlot, seen);
51
+ };
52
+ const gateFor = (slot, candidates, bySlot, seen) => {
53
+ const candidate = bySlot.get(slot);
54
+ if (!candidate)
55
+ return READY;
56
+ const dependsOn = candidate.dependsOnSlot ?? null;
57
+ if (dependsOn) {
58
+ const dependency = bySlot.get(dependsOn);
59
+ if (!dependency || dependency.status !== 'succeeded') {
60
+ return {
61
+ ready: false,
62
+ reason: 'dependency',
63
+ waitingOn: dependsOn,
64
+ blocked: neverSucceeds(dependsOn, candidates, bySlot, seen),
65
+ };
66
+ }
67
+ }
68
+ if (candidate.footer === true) {
69
+ const seedSlot = branchFooterSeedSlot(candidates);
70
+ if (seedSlot && seedSlot !== slot) {
71
+ const seed = bySlot.get(seedSlot);
72
+ // A failed seed releases the wave: later footers draw without an example.
73
+ // A seed that can never settle at all releases nothing, ever.
74
+ if (seed && seed.status !== 'succeeded' && seed.status !== 'failed') {
75
+ return {
76
+ ready: false,
77
+ reason: 'footer-seed',
78
+ waitingOn: seedSlot,
79
+ blocked: neverSettles(seedSlot, candidates, bySlot, seen),
80
+ };
81
+ }
82
+ }
83
+ }
84
+ return READY;
85
+ };
86
+ /** Whether one screen's references are all in place, and what it waits on. */
87
+ export const branchSlotGate = (slot, candidates) => gateFor(slot, candidates, new Map(candidates.map((candidate) => [candidate.slot, candidate])), new Set());
88
+ /**
89
+ * Every slot that may be dispatched now: still pending, not already in flight,
90
+ * not inside a gate-refusal backoff, and with both gates satisfied. Settled
91
+ * slots are never rescheduled — a regenerate is always an explicit user action.
92
+ */
93
+ export const branchDispatchableSlots = (candidates, options = {}) => {
94
+ const inFlight = options.inFlight ?? new Set();
95
+ const heldUntil = options.heldUntil ?? {};
96
+ const now = options.now ?? Date.now();
97
+ return candidates
98
+ .filter((candidate) => (candidate.status === 'pending'
99
+ && !inFlight.has(candidate.slot)
100
+ && !((heldUntil[candidate.slot] ?? 0) > now)
101
+ && branchSlotGate(candidate.slot, candidates).ready))
102
+ .map((candidate) => candidate.slot)
103
+ .sort((left, right) => left.localeCompare(right));
104
+ };
105
+ /** True while any screen is still expected to move without user action. */
106
+ export const branchRunActive = (candidates) => candidates.some((candidate) => candidate.status === 'dispatched');
107
+ /** True once no screen can progress further without a user action. */
108
+ export const branchRunSettled = (candidates) => (candidates.length > 0
109
+ && candidates.every((candidate) => (candidate.status === 'succeeded'
110
+ || candidate.status === 'failed'
111
+ // A pending screen whose dependency failed will never run itself.
112
+ || (candidate.status === 'pending' && (() => {
113
+ const gate = branchSlotGate(candidate.slot, candidates);
114
+ return !gate.ready && gate.blocked;
115
+ })()))));
116
+ /**
117
+ * The screens a failed dependency put permanently out of reach. These are the
118
+ * slots `branchRunSettled` treats as terminal without them ever running, so a
119
+ * caller must be able to name them and their cause rather than waiting on a
120
+ * status that will never change.
121
+ */
122
+ export const branchBlockedSlots = (candidates) => candidates.flatMap((candidate) => {
123
+ if (candidate.status !== 'pending')
124
+ return [];
125
+ const gate = branchSlotGate(candidate.slot, candidates);
126
+ if (gate.ready || !gate.blocked)
127
+ return [];
128
+ return [{ slot: candidate.slot, reason: gate.reason, waitingOn: gate.waitingOn }];
129
+ }).sort((left, right) => left.slot.localeCompare(right.slot));
130
+ //# sourceMappingURL=branch-schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-schedule.js","sourceRoot":"","sources":["../src/branch-schedule.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAsBH,MAAM,KAAK,GAAmB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAE9C,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,UAA8C,EAC/B,EAAE,CAAC,CAClB,CAAC,GAAG,UAAU,CAAC;KACZ,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;KAC1D,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,IAAI,CAChE,CAAC;AAIF;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CACnB,IAAY,EACZ,UAA8C,EAC9C,MAAiB,EACjB,IAAyB,EAChB,EAAE;IACX,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CACpB,IAAY,EACZ,UAA8C,EAC9C,MAAiB,EACjB,IAAyB,EAChB,EAAE;IACX,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,SAAS,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CACd,IAAY,EACZ,UAA8C,EAC9C,MAAiB,EACjB,IAAyB,EACT,EAAE;IAClB,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,IAAI,IAAI,CAAC;IAClD,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YACrD,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,YAAY;gBACpB,SAAS,EAAE,SAAS;gBACpB,OAAO,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC;aAC5D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC9B,MAAM,QAAQ,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAClC,0EAA0E;YAC1E,8DAA8D;YAC9D,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACpE,OAAO;oBACL,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,aAAa;oBACrB,SAAS,EAAE,QAAQ;oBACnB,OAAO,EAAE,YAAY,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC;iBAC1D,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,IAAY,EACZ,UAA8C,EAC9B,EAAE,CAAC,OAAO,CAC1B,IAAI,EACJ,UAAU,EACV,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EACnE,IAAI,GAAG,EAAU,CAClB,CAAC;AAUF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,UAA8C,EAC9C,UAAiC,EAAE,EACzB,EAAE;IACZ,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,GAAG,EAAU,CAAC;IACvD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;IAC1C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACtC,OAAO,UAAU;SACd,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CACrB,SAAS,CAAC,MAAM,KAAK,SAAS;WAC3B,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;WAC7B,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC;WACzC,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,KAAK,CACpD,CAAC;SACD,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;SAClC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,2EAA2E;AAC3E,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,UAA8C,EACrC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC;AAEhF,sEAAsE;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,UAA8C,EACrC,EAAE,CAAC,CACZ,UAAU,CAAC,MAAM,GAAG,CAAC;OAClB,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CACjC,SAAS,CAAC,MAAM,KAAK,WAAW;WAC7B,SAAS,CAAC,MAAM,KAAK,QAAQ;QAChC,kEAAkE;WAC/D,CAAC,SAAS,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,GAAG,EAAE;YAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC;QACrC,CAAC,CAAC,EAAE,CAAC,CACN,CAAC,CACH,CAAC;AAQF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,UAA8C,EACzB,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;IACzD,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,cAAc,CAAC,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAC3C,OAAO,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { BranchRunEnvelope } from './branch-client.js';
2
+ import { type BranchProgressScreen, type BranchStatusReport } from './branch-progress.js';
3
+ /** Fold a live run envelope onto the last local snapshot. */
4
+ export declare const mergeLiveBranchScreens: (screens: readonly BranchProgressScreen[], envelope: BranchRunEnvelope) => BranchProgressScreen[];
5
+ export declare const readBranchStatus: (args: {
6
+ outDir: string;
7
+ /** Optional live read of the run; omitted or failing falls back to disk. */
8
+ refresh?: (runId: string) => Promise<BranchRunEnvelope | null>;
9
+ now?: number;
10
+ }) => Promise<BranchStatusReport>;
11
+ //# sourceMappingURL=branch-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-status.d.ts","sourceRoot":"","sources":["../src/branch-status.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAmC9B,6DAA6D;AAC7D,eAAO,MAAM,sBAAsB,GACjC,SAAS,SAAS,oBAAoB,EAAE,EACxC,UAAU,iBAAiB,KAC1B,oBAAoB,EAkBtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,MAAM;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,KAAG,OAAO,CAAC,kBAAkB,CAyE7B,CAAC"}
@@ -0,0 +1,118 @@
1
+ import path from 'node:path';
2
+ import { branchBlockedSlots } from './branch-schedule.js';
3
+ import { branchProgressPath, readBranchProgress, summarizeBranchProgress, } from './branch-progress.js';
4
+ import { branchAttemptPath, branchCompletionPath, branchPlanPath, readBranchAttempt, readBranchCompletion, } from './branch-run-record.js';
5
+ /**
6
+ * `12ui branch status` — the answer to "is it still going?" that a second
7
+ * session, or the same agent after a sleep, can ask without touching the run.
8
+ * It always narrates motion: which screens are complete, which are generating
9
+ * and for how long against the typical duration, which are waiting on a
10
+ * dependency, and which are permanently blocked. It never answers with bare
11
+ * absence, because that is what makes a caller conclude the run died.
12
+ */
13
+ const phaseFor = (attemptStatus, progressPhase) => {
14
+ if (progressPhase)
15
+ return progressPhase;
16
+ switch (attemptStatus) {
17
+ case 'downloading': return 'downloading';
18
+ case 'converting': return 'converting';
19
+ case 'dispatching': return 'dispatching';
20
+ case 'succeeded':
21
+ case 'partial':
22
+ case 'failed':
23
+ return 'terminal';
24
+ default: return 'claiming';
25
+ }
26
+ };
27
+ /** Fold a live run envelope onto the last local snapshot. */
28
+ export const mergeLiveBranchScreens = (screens, envelope) => {
29
+ const live = new Map(envelope.candidates.map((candidate) => [candidate.slot, candidate]));
30
+ const blocked = new Map(branchBlockedSlots(envelope.candidates.map((candidate) => ({
31
+ slot: candidate.slot,
32
+ status: candidate.status,
33
+ footer: candidate.footer ?? null,
34
+ dependsOnSlot: candidate.dependsOnSlot ?? null,
35
+ }))).map((entry) => [entry.slot, entry]));
36
+ return screens.map((screen) => {
37
+ const candidate = live.get(screen.slot);
38
+ if (!candidate)
39
+ return screen;
40
+ const block = blocked.get(screen.slot);
41
+ return {
42
+ ...screen,
43
+ status: block ? 'blocked' : candidate.status,
44
+ ...(block ? { waitingOn: block.waitingOn } : {}),
45
+ };
46
+ });
47
+ };
48
+ export const readBranchStatus = async (args) => {
49
+ const outDir = path.resolve(args.outDir);
50
+ const attempt = await readBranchAttempt(branchAttemptPath(outDir));
51
+ const completion = await readBranchCompletion(branchCompletionPath(outDir));
52
+ const progress = await readBranchProgress(branchProgressPath(outDir));
53
+ if (!attempt && !completion && !progress) {
54
+ // Never started is not a failure and not a stall. Name the command that
55
+ // starts it rather than reporting an absence the caller has to interpret.
56
+ throw new Error(`This branch run has never been started — there is nothing to report, and nothing has`
57
+ + ` failed. ${outDir} holds no branch attempt, progress, or completion record.`
58
+ + ' Start one with `12ui branch execute --winner-run <crt-id> --winner-slot <slot>'
59
+ + ' --concept <text> --scope page|site --out-dir <directory>`, which blocks for the whole'
60
+ + ' run and streams progress.');
61
+ }
62
+ if (completion) {
63
+ return {
64
+ ...summarizeBranchProgress({
65
+ outDir,
66
+ ...(completion.runId ? { runId: completion.runId } : {}),
67
+ phase: 'terminal',
68
+ screens: (progress?.screens ?? []),
69
+ startedAt: completion.startedAt,
70
+ updatedAt: completion.completedAt,
71
+ completion,
72
+ now: args.now ?? Date.now(),
73
+ }),
74
+ completionPath: branchCompletionPath(outDir),
75
+ planPath: branchPlanPath(outDir),
76
+ };
77
+ }
78
+ const runId = progress?.runId ?? attempt?.runId;
79
+ let screens = progress?.screens ?? [];
80
+ if (runId && args.refresh) {
81
+ try {
82
+ const envelope = await args.refresh(runId);
83
+ if (envelope) {
84
+ screens = screens.length > 0
85
+ ? mergeLiveBranchScreens(screens, envelope)
86
+ : envelope.candidates.map((candidate) => ({
87
+ slot: candidate.slot,
88
+ status: candidate.status,
89
+ pageIndex: candidate.pageIndex ?? 0,
90
+ viewportOrdinal: candidate.viewportOrdinal ?? 0,
91
+ ...(candidate.dependsOnSlot ? { waitingOn: candidate.dependsOnSlot } : {}),
92
+ }));
93
+ }
94
+ }
95
+ catch {
96
+ // A status call must never fail because the network did; the local
97
+ // snapshot is still a truthful account of motion.
98
+ }
99
+ }
100
+ return {
101
+ ...summarizeBranchProgress({
102
+ outDir,
103
+ ...(runId ? { runId } : {}),
104
+ phase: phaseFor(attempt?.status, progress?.phase),
105
+ screens,
106
+ // The half of the run that takes the longest. Without it a poll during
107
+ // conversion answered with the same sentence every time.
108
+ ...(progress?.packages ? { packages: progress.packages } : {}),
109
+ ...(progress?.startedAt ?? attempt?.createdAt
110
+ ? { startedAt: progress?.startedAt ?? attempt.createdAt }
111
+ : {}),
112
+ ...(progress?.updatedAt ? { updatedAt: progress.updatedAt } : {}),
113
+ now: args.now ?? Date.now(),
114
+ }),
115
+ planPath: branchPlanPath(outDir),
116
+ };
117
+ };
118
+ //# sourceMappingURL=branch-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-status.js","sourceRoot":"","sources":["../src/branch-status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * One page stacks into one tall source, so every viewport of it must share one
3
+ * width. Branch pages mix two origins that cannot agree on their own:
4
+ *
5
+ * - the CONTINUATION screens, drawn by the server on a canvas fixed by the
6
+ * run's aspect (`CREATE_ASPECT_SIZES`, 1536x1024 for landscape). The build
7
+ * claim accepts `source`, `concept`, `aspect`, `winner` and
8
+ * `referenceImage` and nothing else — there is no width parameter — so the
9
+ * server side of the page CANNOT be moved to meet the winner.
10
+ * - the WINNER, viewport 1 of the source page, which came from a local image
11
+ * provider or an upload and is whatever size that produced. Measured across
12
+ * the 0.2.14 harness pass: 1672x941, 864x1821, 853x1844, 1024x1536, and in
13
+ * one run 1536x1024 — the only run whose page could have converted.
14
+ *
15
+ * So the canonical width is the server's, read off the screens it actually
16
+ * produced, and the local image is the one that moves. That is a declared fact
17
+ * on both sides rather than a tuned guess: the width is measured from the
18
+ * delivered pixels the package validator will itself inspect.
19
+ *
20
+ * Only the width is reconciled. Heights differ on purpose — a page is a stack
21
+ * of viewports of different depths — so the resize preserves aspect ratio and
22
+ * the viewport simply becomes as tall as that width implies.
23
+ */
24
+ /** The joined page must still fit the LayerDoc canvas and source-pixel limits. */
25
+ export declare const MAX_JOINED_PAGE_SIDE = 10000;
26
+ export declare const MAX_JOINED_PAGE_PIXELS = 8000000;
27
+ export type ViewportImage = {
28
+ id: string;
29
+ bytes: Uint8Array;
30
+ mediaType: 'image/png' | 'image/jpeg' | 'image/webp';
31
+ width: number;
32
+ height: number;
33
+ /** True for a screen the server drew: it defines the page's canonical width. */
34
+ canonical: boolean;
35
+ };
36
+ export type NormalizedViewportImage = ViewportImage & {
37
+ sha256: string;
38
+ normalized: boolean;
39
+ original?: {
40
+ width: number;
41
+ height: number;
42
+ sha256: string;
43
+ bytes: number;
44
+ };
45
+ };
46
+ export declare const digestOf: (bytes: Uint8Array) => string;
47
+ /**
48
+ * The width every viewport of this page is reconciled to: the width the server
49
+ * drew its screens at. With no server screen on the page there is nothing to
50
+ * reconcile and the page keeps the width it already has.
51
+ */
52
+ export declare const canonicalPageWidth: (viewports: readonly ViewportImage[]) => number | null;
53
+ export type PageNormalization = {
54
+ canonicalWidth: number | null;
55
+ /** True when the page was narrowed below the server's canvas to fit a limit. */
56
+ fitted: boolean;
57
+ viewports: NormalizedViewportImage[];
58
+ /** Ids that were resized, for the run record and the report. */
59
+ normalizedIds: string[];
60
+ };
61
+ /**
62
+ * The widest the page may be and still join inside both limits.
63
+ *
64
+ * Every viewport scales with the page, so the joined height is `width * K`
65
+ * where K is the sum of the viewports' height/width ratios. That makes the
66
+ * pixel limit `width² * K <= P` and the side limit `width * K <= S`, both
67
+ * solvable directly. Shrinking the whole page keeps it convertible and keeps
68
+ * every viewport in proportion; refusing would hand back nothing, and a tall
69
+ * approved screen stacked over three continuations is a shape the harness
70
+ * actually produced (864x1821 over three 1536x1024 screens is 9.7M pixels).
71
+ */
72
+ export declare const widthThatFits: (requested: number, viewports: readonly ViewportImage[]) => number;
73
+ export declare const normalizePageViewports: (viewports: readonly ViewportImage[]) => Promise<PageNormalization>;
74
+ //# sourceMappingURL=branch-viewport-normalization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch-viewport-normalization.d.ts","sourceRoot":"","sources":["../src/branch-viewport-normalization.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,WAAW,GAAG,YAAY,GAAG,YAAY,CAAC;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,aAAa,GAAG;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,OAAO,UAAU,KAAG,MAE5C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,WAAW,SAAS,aAAa,EAAE,KAAG,MAAM,GAAG,IAYjF,CAAC;AAoCF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,uBAAuB,EAAE,CAAC;IACrC,gEAAgE;IAChE,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GACxB,WAAW,MAAM,EACjB,WAAW,SAAS,aAAa,EAAE,KAClC,MASF,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,WAAW,SAAS,aAAa,EAAE,KAClC,OAAO,CAAC,iBAAiB,CAuB3B,CAAC"}