@bridge-ds/core 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { m as modeDisplayName, b as modesOf$1, i as isMultiMode, T as TIER_NAMES, c as TIER_AXIS, d as collectRefs, f as findReferenceProblems, e as evaluatePairs, g as contrastScope, h as contextLabel, S as SINGLE_MODE, j as isAlias$1, k as aliasTarget, l as pathId, v as valuesEqual, n as flattenTokens, s as serializeTokenFile, r as removeToken, u as upsertToken, o as threeWayDiff, q as emptyTokenFile, w as parseTokenFile, E as EXT_ID, x as EXT_SCOPES, y as parseColor, z as rgbaToHex, A as formatValue, B as resolve, C as moveToken, D as effectiveScopes, F as sameScopes, G as defaultScopes, H as readTokenAt, I as modeFile, J as defaultFile, K as selectTokenFiles, L as LayeredTokenSet, M as resolveBranchPreviews, N as loadConfig, O as tokensPath, P as toConfigJson, Q as validateLayered, R as parseContrastChecks, U as classifyRisk, V as CONFIG_FILE, W as RISK_LABELS, X as previewLinks, Y as findPreviewComment, Z as previewsSettled, _ as baseBranchOf, $ as DEFAULT_TOKEN_PATHS, a0 as layoutOptions, a1 as buildTokenSet, a2 as readPreviewComment, a3 as linkStatus, a4 as libraryPreviewState, a5 as parsePiecesRecord, a6 as libraryBranchPlan, a7 as NEEDED_BY_LABEL, a8 as withNeededBy, a9 as withLink, aa as withPiecesRecord, ab as productBranchSentence, ac as mergePiecesRecord, ad as productOfBranch, ae as defaultProduct, af as slugify, ag as defineConfig, ah as globToRegExp, ai as ConfigError, aj as DEFAULT_CHANGES_MODE, ak as DEFAULT_OVERRIDE, al as DEFAULT_PRODUCTS, am as isGuardrail } from "./prod-branch-CJZwMfVD.js";
2
- import { an, ao, ap, aq, ar, as, at, au, av, aw, ax, ay, az, aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM, aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ, a_, a$, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM, bN, bO, bP, bQ, bR, bS, p, bT, bU, bV, bW, bX, bY, bZ, b_, b$, c0, c1, a, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn, co, cp, cq, cr, t, cs, ct, cu, cv, cw, cx, cy, cz, cA } from "./prod-branch-CJZwMfVD.js";
3
- import { G as GitHubError, c as collectHistory, p as packageDirCandidates, a as parseChangelog, r as releasesFromTags, m as mergeReleases, b as buildReport, d as applyTokenEdits, e as buildHistoryEntry, f as coerceValue, g as checkLayeredPair, h as formatChangeset, P as PROSE_KEYS, i as markClaudeDraft, j as cleanDraftText, k as docsPrompt, D as DRAFT_LINE_MAX, R as REQUESTS_BRANCH, l as REQUEST_KINDS, s as sanitizeDraftLine, n as parseExplorationBody, E as EXPLORATION_PREFIX, o as explorationSlug, q as explorationStatus, t as explorationTryIt, u as isExplorationPull, v as promotionPlan, w as EXPLORATION_LABEL, x as explorationProblems, y as explorationNote, z as explorationNotePath, A as explorationPrTitle, B as explorationPrBody, C as compareSpecToMeasured, F as variantKeyOf, H as withoutAddedWrappers, I as pairParts, S as SPEC_FIELDS, J as COLOR_FIELDS, K as hashSpec, L as DRAFTED_BY_CLAUDE_LABEL, M as overrideComment, O as OVERRIDE_HEADING, N as overrideRefusedComment, Q as suggestionStateFromLabels, T as suggestionStatus, U as composeRequestIssue, V as EXPLORATION_NOTES_DIR, W as isExplorationBranch, X as diffLayered, Y as contrastDelta, Z as runLayeredContrast, _ as layeredImpact, $ as classifyLayered, a0 as composeProposalBody, a1 as createSuggestionOnGitHub, a2 as gitBlobSha, a3 as SUGGESTION_SCHEMA, a4 as branchWithJiraKey, a5 as titleWithJiraKey, a6 as figmaSpecPath, a7 as formatFigmaSpec, a8 as figmaDraftBrief, a9 as parseFigmaOrigin, aa as figmaPushStatus, ab as parseFigmaSpec, ac as FIGMA_SPEC_DIR, ad as FROM_FIGMA_LABEL, ae as withFigmaOrigin, af as WAITING_FOR_CLAUDE_LABEL, ag as planComponentPush, ah as figmaOriginSection, ai as figmaBranchTitle, aj as figmaSpecSlug, ak as figmaDraftBody, al as REQUEST_LABEL, am as figmaNodeUrl, an as parentDir, ao as parseComponentSpecs, ap as readRepoTexts, aq as normalizeRepoPath, ar as isUnder, as as componentMotion, at as GitHubClient, au as effectiveTier, av as parseRequestIssue, aw as requestStatus, ax as listSuggestionsOnGitHub, ay as getSuggestionOnGitHub, az as closingIssueNumbers, aA as requestIssueTitle, aB as pullState, aC as summarizeCi, aD as summarizeApprovals } from "./repo-source-CshjjC7x.js";
4
- import { aE as aE2, aF as aF2, aG as aG2, aH as aH2, aI as aI2, aJ as aJ2, aK as aK2, aL as aL2, aM as aM2, aN as aN2, aO as aO2, aP as aP2, aQ as aQ2, aR as aR2, aS as aS2, aT as aT2, aU as aU2, aV as aV2, aW as aW2, aX as aX2, aY as aY2, aZ as aZ2, a_ as a_2, a$ as a$2, b0 as b02, b1 as b12, b2 as b22, b3 as b32, b4 as b42, b5 as b52, b6 as b62, b7 as b72, b8 as b82, b9 as b92, ba as ba2, bb as bb2, bc as bc2, bd as bd2, be as be2, bf as bf2, bg as bg2, bh as bh2, bi as bi2, bj as bj2, bk as bk2, bl as bl2, bm as bm2, bn as bn2, bo as bo2, bp as bp2, bq as bq2, br as br2, bs as bs2, bt as bt2, bu as bu2, bv as bv2, bw as bw2, bx as bx2, by as by2, bz as bz2, bA as bA2, bB as bB2, bC as bC2, bD as bD2, bE as bE2, bF as bF2, bG as bG2, bH as bH2, bI as bI2, bJ as bJ2, bK as bK2, bL as bL2, bM as bM2, bN as bN2, bO as bO2, bP as bP2, bQ as bQ2, bR as bR2, bS as bS2, bT as bT2, bU as bU2, bV as bV2, bW as bW2, bX as bX2, bY as bY2, bZ as bZ2, b_ as b_2, b$ as b$2, c0 as c02, c1 as c12, c2 as c22, c3 as c32, c4 as c42, c5 as c52, c6 as c62, c7 as c72, c8 as c82, c9 as c92, ca as ca2, cb as cb2, cc as cc2, cd as cd2, ce as ce2, cf as cf2, cg as cg2, ch as ch2, ci as ci2, cj as cj2, ck as ck2, cl as cl2, cm as cm2, cn as cn2, co as co2, cp as cp2, cq as cq2, cr as cr2, cs as cs2, ct as ct2, cu as cu2, cv as cv2, cw as cw2, cx as cx2, cy as cy2, cz as cz2, cA as cA2, cB, cC, cD, cE, cF, cG, cH, cI, cJ, cK, cL, cM, cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ, c_, c$, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn, dp, dq, dr, ds, dt, du, dv, dw, dx, dy, dz, dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM, dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ, d_, d$, e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en, eo, ep, eq, er, es } from "./repo-source-CshjjC7x.js";
1
+ import { m as modeDisplayName, b as modesOf$1, i as isMultiMode, T as TIER_NAMES, c as TIER_AXIS, d as collectRefs, f as findReferenceProblems, e as evaluatePairs, g as contrastScope, h as contextLabel, S as SINGLE_MODE, j as isAlias$1, k as aliasTarget, l as pathId, v as valuesEqual, n as flattenTokens, s as serializeTokenFile, r as removeToken, u as upsertToken, o as threeWayDiff, q as emptyTokenFile, w as parseTokenFile, E as EXT_ID, x as EXT_SCOPES, y as parseColor, z as rgbaToHex, A as formatValue, B as resolve, C as moveToken, D as effectiveScopes, F as sameScopes, G as defaultScopes, H as readTokenAt, I as modeFile, J as defaultFile, K as selectTokenFiles, L as LayeredTokenSet, M as resolveBranchPreviews, N as loadConfig, O as tokensPath, P as toConfigJson, Q as validateLayered, R as parseContrastChecks, U as classifyRisk, V as CONFIG_FILE, W as RISK_LABELS, X as previewLinks, Y as findPreviewComment, Z as previewsSettled, _ as previewUrl, $ as productRepos, a0 as linkStatus, a1 as libraryPreviewState, a2 as parsePiecesRecord, a3 as libraryBranchPlan, a4 as NEEDED_BY_LABEL, a5 as withNeededBy, a6 as withLink, a7 as withPiecesRecord, a8 as productBranchSentence, a9 as productOfBranch, aa as pieceRequest, ab as mergePiecesRecord, ac as defaultProduct, ad as productOfRepo, ae as baseBranchOf, af as DEFAULT_TOKEN_PATHS, ag as layoutOptions, ah as buildTokenSet, ai as readPreviewComment, aj as slugify, ak as defineConfig, al as globToRegExp, am as ConfigError, an as DEFAULT_CHANGES_MODE, ao as DEFAULT_OVERRIDE, ap as DEFAULT_PRODUCTS, aq as isGuardrail } from "./prod-branch-JL5tmL6u.js";
2
+ import { ar, as, at, au, av, aw, ax, ay, az, aA, aB, aC, aD, aE, aF, aG, aH, aI, aJ, aK, aL, aM, aN, aO, aP, aQ, aR, aS, aT, aU, aV, aW, aX, aY, aZ, a_, a$, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, bA, bB, bC, bD, bE, bF, bG, bH, bI, bJ, bK, bL, bM, bN, bO, bP, bQ, bR, bS, bT, bU, bV, bW, p, bX, bY, bZ, b_, b$, c0, c1, c2, c3, c4, c5, c6, a, c7, c8, c9, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, cl, cm, cn, co, cp, cq, cr, cs, ct, cu, cv, cw, t, cx, cy, cz, cA, cB, cC, cD, cE, cF } from "./prod-branch-JL5tmL6u.js";
3
+ import { G as GitHubError, c as collectHistory, p as packageDirCandidates, a as parseChangelog, r as releasesFromTags, m as mergeReleases, b as buildReport, d as applyTokenEdits, e as buildHistoryEntry, f as coerceValue, g as checkLayeredPair, h as formatChangeset, P as PROSE_KEYS, i as markClaudeDraft, j as cleanDraftText, k as docsPrompt, D as DRAFT_LINE_MAX, R as REQUESTS_BRANCH, l as REQUEST_KINDS, s as sanitizeDraftLine, E as EXPLORATION_ARCHIVED_LABEL, n as parseExplorationBody, o as EXPLORATION_PREFIX, q as explorationSlug, t as isArchivedExploration, u as explorationStatus, v as explorationTryIt, w as isExplorationPull, x as promotionPlan, y as EXPLORATION_LABEL, z as explorationProblems, A as explorationNote, B as explorationNotePath, C as explorationPrTitle, F as explorationPrBody, H as suggestionStateFromLabels, I as suggestionStatus, J as composeRequestIssue, K as EXPLORATION_NOTES_DIR, L as isExplorationBranch, M as doneExploringPlan, N as withDoneExploring, O as explorationStartingPoint, Q as explorationRefLabel, S as compareSpecToMeasured, T as variantKeyOf, U as matchVariant, V as withoutAddedWrappers, W as pairParts, X as hashSpec, Y as SPEC_FIELDS, Z as COLOR_FIELDS, _ as DRAFTED_BY_CLAUDE_LABEL, $ as overrideComment, a0 as OVERRIDE_HEADING, a1 as overrideRefusedComment, a2 as diffLayered, a3 as contrastDelta, a4 as runLayeredContrast, a5 as layeredImpact, a6 as classifyLayered, a7 as composeProposalBody, a8 as createSuggestionOnGitHub, a9 as gitBlobSha, aa as SUGGESTION_SCHEMA, ab as branchWithJiraKey, ac as titleWithJiraKey, ad as figmaSpecPath, ae as formatFigmaSpec, af as figmaDraftBrief, ag as parseFigmaOrigin, ah as figmaPushStatus, ai as parseFigmaSpec, aj as FIGMA_SPEC_DIR, ak as FROM_FIGMA_LABEL, al as withFigmaOrigin, am as WAITING_FOR_CLAUDE_LABEL, an as planComponentPush, ao as figmaOriginSection, ap as figmaBranchTitle, aq as figmaSpecSlug, ar as figmaDraftBody, as as REQUEST_LABEL, at as figmaNodeUrl, au as parentDir, av as parseComponentSpecs, aw as readRepoTexts, ax as normalizeRepoPath, ay as isUnder, az as componentMotion, aA as GitHubClient, aB as effectiveTier, aC as parseRequestIssue, aD as requestStatus, aE as listSuggestionsOnGitHub, aF as getSuggestionOnGitHub, aG as closingIssueNumbers, aH as requestIssueTitle, aI as pullState, aJ as summarizeCi, aK as summarizeApprovals } from "./repo-source-CcWmqNBZ.js";
4
+ import { aL as aL2, aM as aM2, aN as aN2, aO as aO2, aP as aP2, aQ as aQ2, aR as aR2, aS as aS2, aT as aT2, aU as aU2, aV as aV2, aW as aW2, aX as aX2, aY as aY2, aZ as aZ2, a_ as a_2, a$ as a$2, b0 as b02, b1 as b12, b2 as b22, b3 as b32, b4 as b42, b5 as b52, b6 as b62, b7 as b72, b8 as b82, b9 as b92, ba as ba2, bb as bb2, bc as bc2, bd as bd2, be as be2, bf as bf2, bg as bg2, bh as bh2, bi as bi2, bj as bj2, bk as bk2, bl as bl2, bm as bm2, bn as bn2, bo as bo2, bp as bp2, bq as bq2, br as br2, bs as bs2, bt as bt2, bu as bu2, bv as bv2, bw as bw2, bx as bx2, by as by2, bz as bz2, bA as bA2, bB as bB2, bC as bC2, bD as bD2, bE as bE2, bF as bF2, bG as bG2, bH as bH2, bI as bI2, bJ as bJ2, bK as bK2, bL as bL2, bM as bM2, bN as bN2, bO as bO2, bP as bP2, bQ as bQ2, bR as bR2, bS as bS2, bT as bT2, bU as bU2, bV as bV2, bW as bW2, bX as bX2, bY as bY2, bZ as bZ2, b_ as b_2, b$ as b$2, c0 as c02, c1 as c12, c2 as c22, c3 as c32, c4 as c42, c5 as c52, c6 as c62, c7 as c72, c8 as c82, c9 as c92, ca as ca2, cb as cb2, cc as cc2, cd as cd2, ce as ce2, cf as cf2, cg as cg2, ch as ch2, ci as ci2, cj as cj2, ck as ck2, cl as cl2, cm as cm2, cn as cn2, co as co2, cp as cp2, cq as cq2, cr as cr2, cs as cs2, ct as ct2, cu as cu2, cv as cv2, cw as cw2, cx as cx2, cy as cy2, cz as cz2, cA as cA2, cB as cB2, cC as cC2, cD as cD2, cE as cE2, cF as cF2, cG, cH, cI, cJ, cK, cL, cM, cN, cO, cP, cQ, cR, cS, cT, cU, cV, cW, cX, cY, cZ, c_, c$, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df, dg, dh, di, dj, dk, dl, dm, dn, dp, dq, dr, ds, dt, du, dv, dw, dx, dy, dz, dA, dB, dC, dD, dE, dF, dG, dH, dI, dJ, dK, dL, dM, dN, dO, dP, dQ, dR, dS, dT, dU, dV, dW, dX, dY, dZ, d_, d$, e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef, eg, eh, ei, ej, ek, el, em, en, eo, ep, eq, er, es, et, eu, ev, ew, ex, ey, ez, eA, eB, eC, eD, eE, eF, eG } from "./repo-source-CcWmqNBZ.js";
5
5
  const issueKey$1 = (i) => `${i.code}|${i.path ?? ""}|${i.mode ?? ""}|${i.code === "file-problem" ? i.message : ""}`;
6
6
  const modeName = (mode) => mode === SINGLE_MODE ? "all modes" : modeDisplayName(mode);
7
7
  const isPalette = (t2) => t2.tier === "brand" && t2.path.startsWith("palette.");
@@ -1023,6 +1023,16 @@ async function branchPreviewUrls(gh, ref, config, opts = {}) {
1023
1023
  ]);
1024
1024
  return resolveBranchPreviews(apps, { branch: branch ?? "", ...pr !== void 0 ? { pr } : {}, ...sha ? { sha } : {} }, { comments, statuses, deployments });
1025
1025
  }
1026
+ async function productPreviewOnGitHub(gh, product, e) {
1027
+ if (!product.preview) return null;
1028
+ const [comments, statuses, deployments] = await Promise.all([
1029
+ gh.listIssueComments(e.number).catch(() => []),
1030
+ e.sha ? gh.listCommitStatuses(e.sha).catch(() => []) : Promise.resolve([]),
1031
+ e.sha ? gh.listDeployments(e.sha).catch(() => []) : Promise.resolve([])
1032
+ ]);
1033
+ const p2 = resolveBranchPreviews({ admin: product.preview }, { branch: e.branch, pr: e.number, ...e.sha ? { sha: e.sha } : {} }, { comments, statuses, deployments })[0];
1034
+ return p2 ? { ...p2, app: product.key, label: product.name } : null;
1035
+ }
1026
1036
  const STEPS = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950];
1027
1037
  const LIGHTNESS = { 50: 0.975, 100: 0.952, 200: 0.912, 300: 0.85, 400: 0.735, 500: 0.63, 600: 0.53, 700: 0.445, 800: 0.36, 900: 0.27, 950: 0.205 };
1028
1038
  const BELL = { 50: 0.12, 100: 0.22, 200: 0.4, 300: 0.62, 400: 0.85, 500: 1, 600: 1, 700: 0.92, 800: 0.8, 900: 0.62, 950: 0.5 };
@@ -2112,7 +2122,7 @@ class DraftPoller {
2112
2122
  function productPreview(apps, ctx, sources) {
2113
2123
  if (!apps.admin || ctx.fromFork) return null;
2114
2124
  const admin = resolveBranchPreviews(apps, { branch: ctx.branch, pr: ctx.pr, sha: ctx.sha }, sources).find((p2) => p2.app === "admin");
2115
- if (!admin?.url || admin.source === "template" || admin.state === "failed") return null;
2125
+ if (!admin?.url || admin.source === "template" || admin.state === "failed" || admin.skipped) return null;
2116
2126
  return admin.url;
2117
2127
  }
2118
2128
  function tryInProductUrl(preview, pr, screen) {
@@ -2135,6 +2145,7 @@ function localTryCommand(branch) {
2135
2145
  const name = /^[\w./-]+$/.test(branch) ? branch : `'${branch.replace(/'/g, `'\\''`)}'`;
2136
2146
  return `git fetch origin && git switch ${name}`;
2137
2147
  }
2148
+ const explorationStartMessage = (e) => [`chore(explore): start ${e.name.trim()}`, ...e.purpose?.trim() ? [e.purpose.trim()] : [], "Exploration: never merged as is. Promote it to review or close it."].join("\n\n");
2138
2149
  function explorationSummary(p2, _owner, _repo, s) {
2139
2150
  const e = parseExplorationBody(p2.body);
2140
2151
  const slug2 = p2.headRef?.startsWith(EXPLORATION_PREFIX) ? p2.headRef.slice(EXPLORATION_PREFIX.length) : e?.slug ?? explorationSlug(p2.title);
@@ -2146,7 +2157,8 @@ function explorationSummary(p2, _owner, _repo, s) {
2146
2157
  name: e?.name ?? p2.title.replace(/^[A-Z][A-Z0-9_]*-\d+\s+/, "").replace(/^Exploration:\s*/i, ""),
2147
2158
  purpose: e?.purpose ?? "",
2148
2159
  ...e?.useCase ? { useCase: e.useCase } : {},
2149
- status: explorationStatus({ state: p2.state, labels: p2.labels, headRef: p2.headRef, body: p2.body }),
2160
+ status: explorationStatus({ state: p2.state, labels: p2.labels, headRef: p2.headRef, body: p2.body, headSha: p2.headSha, draft: p2.draft }),
2161
+ archived: isArchivedExploration(p2.labels),
2150
2162
  draft: p2.draft,
2151
2163
  author: e?.author ?? p2.author,
2152
2164
  ...e?.source ? { source: e.source } : {},
@@ -2158,7 +2170,28 @@ function explorationSummary(p2, _owner, _repo, s) {
2158
2170
  }
2159
2171
  async function listExplorationsOnGitHub(gh, s, opts = {}) {
2160
2172
  const pulls = await gh.listPullRequests(opts.state ?? "open");
2161
- return pulls.filter((p2) => isExplorationPull({ state: p2.state, labels: p2.labels, headRef: p2.headRef, body: p2.body })).map((p2) => explorationSummary(p2, gh.owner, gh.name, s));
2173
+ const archived = opts.archived ?? "exclude";
2174
+ return pulls.filter((p2) => isExplorationPull({ state: p2.state, labels: p2.labels, headRef: p2.headRef, body: p2.body })).filter((p2) => archived === "include" || archived === "only" === isArchivedExploration(p2.labels)).map((p2) => explorationSummary(p2, gh.owner, gh.name, s));
2175
+ }
2176
+ const isHiddenRepoError = (err) => err instanceof GitHubError && (err.status === 404 || err.status === 409 || err.status === 403 && err.auth !== "rejected" && !err.resetsAt);
2177
+ async function listProductExplorationsOnGitHub(clientFor, products, designSystemRepo, opts = {}) {
2178
+ const unreachable = [];
2179
+ const lists = await Promise.all(
2180
+ productRepos(products, designSystemRepo).map(async ({ product, repo }) => {
2181
+ try {
2182
+ const list = await listExplorationsOnGitHub(clientFor(repo), { packages: [], apps: {} }, opts);
2183
+ return list.map((x) => inProductRepo(x, product, repo));
2184
+ } catch (err) {
2185
+ unreachable.push({ product: product.key, name: product.name, repo, reason: isHiddenRepoError(err) ? "not-connected" : "unavailable" });
2186
+ return [];
2187
+ }
2188
+ })
2189
+ );
2190
+ return { explorations: lists.flat(), unreachable };
2191
+ }
2192
+ function inProductRepo(x, product, repo) {
2193
+ const url = previewUrl(product.preview, { branch: x.branch, pr: x.number, sha: x.headSha });
2194
+ return { ...x, repo, product: product.key, previews: url ? [{ app: product.key, label: product.name, url }] : [] };
2162
2195
  }
2163
2196
  async function startExplorationOnGitHub(gh, e, s) {
2164
2197
  const problems = explorationProblems(e);
@@ -2172,14 +2205,10 @@ async function startExplorationOnGitHub(gh, e, s) {
2172
2205
  const commit = await x.commitToNewBranch({
2173
2206
  base: s.base,
2174
2207
  branch,
2175
- message: `chore(explore): start ${full.name.trim()}
2176
-
2177
- ${full.purpose.trim()}
2178
-
2179
- Exploration: never merged as is. Promote it to review or close it.`,
2208
+ message: explorationStartMessage(full),
2180
2209
  files: [{ path: explorationNotePath(slug2), content: explorationNote(full) }]
2181
2210
  });
2182
- const ctx = { owner: gh.owner, repo: gh.name, packages: s.packages, apps: s.apps, ...s.requests?.length ? { requests: s.requests } : {} };
2211
+ const ctx = { owner: gh.owner, repo: gh.name, packages: s.packages, apps: s.apps, ...s.requests?.length ? { requests: s.requests } : {}, ...s.inProduct ? { inProduct: s.inProduct } : {} };
2183
2212
  const title = full.jira ? `${full.jira.trim().toUpperCase()} ${explorationPrTitle(full)}` : explorationPrTitle(full);
2184
2213
  const pr = await x.createPullRequest({ title, head: branch, base: s.base, body: explorationPrBody(full, ctx), draft: true });
2185
2214
  await x.addLabels(pr.number, [EXPLORATION_LABEL, ...s.labels ?? []]);
@@ -2194,6 +2223,16 @@ Exploration: never merged as is. Promote it to review or close it.`,
2194
2223
  previews: previewLinks(s.apps, { branch, pr: pr.number, sha: commit.sha })
2195
2224
  };
2196
2225
  }
2226
+ async function productRepoAccess(gh) {
2227
+ try {
2228
+ const r = await gh.getRepo();
2229
+ if (r.permissions && r.permissions.push === false) return { ok: false, reason: "read-only" };
2230
+ return { ok: true, base: r.defaultBranch };
2231
+ } catch (err) {
2232
+ if (err instanceof GitHubError && (err.status === 404 || err.status === 403 && err.auth !== "rejected" && !err.resetsAt)) return { ok: false, reason: "not-connected" };
2233
+ throw err;
2234
+ }
2235
+ }
2197
2236
  async function explorationTryItOnGitHub(gh, e) {
2198
2237
  const [comments, head] = await Promise.all([gh.listIssueComments(e.number), gh.getBranchSha(e.branch).catch(() => void 0)]);
2199
2238
  return explorationTryIt({ comment: findPreviewComment(comments)?.body, headSha: head });
@@ -2241,12 +2280,470 @@ async function promoteExplorationOnGitHub(gh, ref, opts = {}) {
2241
2280
  });
2242
2281
  return out;
2243
2282
  }
2283
+ async function archiveExplorationOnGitHub(gh, ref, opts = {}) {
2284
+ if (!gh.canWrite) throw new GitHubError(401, "Archiving an exploration needs a GitHub token (pull requests or issues: write).");
2285
+ const p2 = await findExplorationOnGitHub(gh, ref);
2286
+ if (p2.state !== "open") throw new GitHubError(422, `#${p2.number} is ${p2.state === "merged" ? "shipped" : "closed"}: only an open exploration is archived. Its link already stays on GitHub.`);
2287
+ const archived = opts.archived ?? true;
2288
+ if (archived) await gh.addLabels(p2.number, [EXPLORATION_ARCHIVED_LABEL]);
2289
+ else
2290
+ await gh.removeLabel(p2.number, EXPLORATION_ARCHIVED_LABEL).catch((e) => {
2291
+ if (!(e instanceof GitHubError && e.status === 404)) throw e;
2292
+ });
2293
+ return { number: p2.number, url: p2.url, branch: p2.headRef, archived };
2294
+ }
2244
2295
  async function closeExplorationOnGitHub(gh, ref) {
2245
2296
  if (!gh.canWrite) throw new GitHubError(401, "Closing an exploration needs a GitHub token (pull requests: write).");
2246
2297
  const p2 = await findExplorationOnGitHub(gh, ref);
2247
2298
  await gh.updatePullRequest(p2.number, { state: "closed" });
2248
2299
  return { number: p2.number, url: p2.url, branch: p2.headRef };
2249
2300
  }
2301
+ const PRODUCT_FILE = /\.(css|scss|sass|less|tsx|jsx|ts|js|mjs|cjs|mts|cts|astro|vue|svelte)$/;
2302
+ const SKIP_FILE = /\.d\.[cm]?ts$|\.(test|spec|stories|story)\.[cm]?[jt]sx?$|\.min\.(css|js)$/;
2303
+ const PIECES_FILE_LIMIT = 40;
2304
+ const cleanPath = (p2) => p2.trim().replace(/^\.?\/+|\/+$/g, "");
2305
+ function ownProduct(products, repo) {
2306
+ const p2 = repo ? productOfRepo(products, repo) : void 0;
2307
+ return p2 && p2.key !== defaultProduct(products).key ? p2 : void 0;
2308
+ }
2309
+ const repoOfProductBranch = (gh, lib) => lib && lib.fullName.toLowerCase() !== gh.fullName.toLowerCase() ? gh.fullName : void 0;
2310
+ function productOf(products, pull, files, repo) {
2311
+ const own = ownProduct(products, repo);
2312
+ if (own) return own;
2313
+ const product = productOfBranch(products, { labels: pull.labels, files });
2314
+ if (product.key === defaultProduct(products).key) throw new GitHubError(422, `This branch is a library branch (${product.name}), not a product branch: missing pieces are checked on a branch started for a product.`);
2315
+ return product;
2316
+ }
2317
+ async function readProductBranchOnGitHub(gh, pr, opts) {
2318
+ const pull = await gh.getPullRequest(pr);
2319
+ const cmp = await gh.compareDetailed(pull.baseRef, pull.headSha);
2320
+ const product = productOf(opts.products, pull, cmp.files.map((f) => f.filename), gh.fullName);
2321
+ const dir = product.path && !ownProduct(opts.products, gh.fullName) ? `${cleanPath(product.path)}/` : "";
2322
+ const changed = cmp.files.filter((f) => f.status !== "removed" && (!dir || f.filename.startsWith(dir)) && PRODUCT_FILE.test(f.filename) && !SKIP_FILE.test(f.filename) && !f.filename.split("/").includes("node_modules"));
2323
+ const read = changed.slice(0, PIECES_FILE_LIMIT);
2324
+ const start = cmp.mergeBase ?? pull.baseRef;
2325
+ const files = await Promise.all(
2326
+ read.map(async (f) => {
2327
+ const [before, after] = await Promise.all([f.status === "added" ? Promise.resolve(void 0) : gh.getFileText(f.previous_filename ?? f.filename, start), gh.getFileText(f.filename, pull.headSha)]);
2328
+ return { path: f.filename, before: before ?? null, after: after ?? null };
2329
+ })
2330
+ );
2331
+ return { pull, product, files, skipped: changed.length - read.length, record: parsePiecesRecord(pull.body) };
2332
+ }
2333
+ async function recordPiecesOnGitHub(gh, pr, check) {
2334
+ const pull = await gh.getPullRequest(pr);
2335
+ const record = mergePiecesRecord(parsePiecesRecord(pull.body), { checkedAt: check.checkedAt ?? (/* @__PURE__ */ new Date()).toISOString(), headSha: check.headSha ?? pull.headSha, pieces: check.pieces, fixes: check.fixes });
2336
+ await gh.updatePullRequest(pr, { body: withPiecesRecord(pull.body, record) });
2337
+ return record;
2338
+ }
2339
+ const productWriter = (gh, branch) => isExplorationBranch(branch) ? gh.withBranchPrefix(EXPLORATION_PREFIX) : gh;
2340
+ async function useLibraryPreviewOnGitHub(gh, input) {
2341
+ const message = `chore(bridge): use the preview of ${input.libraryBranch} (#${input.libraryNumber})
2342
+
2343
+ The product branch takes the library branch before it ships. Switch to the published version once it does.`;
2344
+ try {
2345
+ const r = await productWriter(gh, input.productBranch).mergeIntoBranch(input.productBranch, input.libraryBranch, message);
2346
+ return { ...r, said: r.status === "merged" ? "Your branch now uses the preview of the library branch." : "Your branch already has the latest preview." };
2347
+ } catch (err) {
2348
+ if (err instanceof GitHubError && err.status === 409) return { status: "conflict", sha: null, said: `Your branch and the library branch change the same lines. A developer merges ${input.libraryBranch} into ${input.productBranch} by hand.` };
2349
+ throw err;
2350
+ }
2351
+ }
2352
+ function productBranchRef(pull, product, repo) {
2353
+ const name = /<!-- bridge-exploration (\{[\s\S]*?\}) -->/.exec(pull.body ?? "");
2354
+ let title = pull.title.replace(/^[A-Z][A-Z0-9_]*-\d+\s+/, "").replace(/^Exploration:\s*/i, "");
2355
+ let author = pull.author;
2356
+ if (name) {
2357
+ try {
2358
+ const e = JSON.parse(name[1]);
2359
+ if (e.name) title = e.name;
2360
+ if (e.author) author = e.author;
2361
+ } catch {
2362
+ }
2363
+ }
2364
+ return { pr: pull.number, title: title.charAt(0).toUpperCase() + title.slice(1), branch: pull.headRef, url: pull.url, productKey: product.key, productName: product.name, ...author ? { author } : {}, ...repo ? { repo } : {} };
2365
+ }
2366
+ async function openLibraryBranchOnGitHub(gh, input) {
2367
+ const pull = await gh.getPullRequest(input.pr);
2368
+ const record = parsePiecesRecord(pull.body);
2369
+ const piece = record?.pieces.find((p2) => p2.id === input.piece);
2370
+ if (!record || !piece) throw new GitHubError(404, record ? `No piece "${input.piece}" on #${input.pr}. Pieces: ${record.pieces.map((p2) => p2.id).join(", ") || "none"}.` : `#${input.pr} was not checked yet: find its missing pieces first (bridge_missing_pieces with record, or \`bridge pieces ${input.pr} --record\`).`);
2371
+ const product = productOf(input.products, pull, await gh.listPullRequestFiles(input.pr).then((f) => f.map((x) => x.filename), () => []), gh.fullName);
2372
+ const mode = input.mode ?? "direct";
2373
+ const lib = input.library ?? gh;
2374
+ const repo = repoOfProductBranch(gh, input.library);
2375
+ const plan = libraryBranchPlan(piece, productBranchRef(pull, product, repo), { source: input.source, requestedBy: input.openedBy, mode });
2376
+ const existing = record.links.find((l) => l.piece === piece.id);
2377
+ if (existing) return { status: "already-linked", plan, link: existing, preview: null, warnings: [] };
2378
+ if (input.dryRun) return { status: "dry-run", plan, link: null, preview: null, warnings: [] };
2379
+ if (!gh.canWrite || !lib.canWrite) throw new GitHubError(401, mode === "suggest" ? "Suggesting a library change needs a GitHub token (issues: write)." : "Opening a library branch needs a GitHub token (contents, pull requests and issues: write).");
2380
+ const warnings = [];
2381
+ const at2 = (/* @__PURE__ */ new Date()).toISOString();
2382
+ const who = { ...input.openedBy ? { openedBy: input.openedBy } : {}, ...input.agent ? { agent: input.agent } : {}, ...repo ? { repo: lib.fullName } : {} };
2383
+ let link;
2384
+ if (plan.kind === "token-change") {
2385
+ if (!input.writeTokens) throw new GitHubError(422, "This piece is a token: open it where the token files are read (Bridge Studio, bridge_open_library_branch or `bridge pieces open`).");
2386
+ const r = await input.writeTokens(plan, { mode });
2387
+ link = r.status === "suggested" ? { piece: piece.id, kind: "suggestion", number: r.number, url: r.url, title: plan.title, at: at2, ...who } : { piece: piece.id, kind: "token-change", number: r.number, url: r.url, title: plan.title, branch: r.branch, at: at2, ...who };
2388
+ } else if (plan.kind === "exploration") {
2389
+ const r = await startExplorationOnGitHub(lib, { ...plan.exploration, ...input.openedBy ? { author: input.openedBy } : {}, ...input.agent ? { agent: input.agent } : {} }, { base: input.base, ...input.exploration, labels: [NEEDED_BY_LABEL] });
2390
+ link = { piece: piece.id, kind: "exploration", number: r.pr, url: r.url, title: plan.title, branch: r.branch, at: at2, ...who };
2391
+ } else {
2392
+ const issue = composeRequestIssue(plan.request, { labels: [NEEDED_BY_LABEL] });
2393
+ const r = await lib.createIssue(issue);
2394
+ link = { piece: piece.id, kind: "request", number: r.number, url: r.url, title: plan.title, at: at2, ...who };
2395
+ }
2396
+ try {
2397
+ if (link.kind === "request" || link.kind === "suggestion") {
2398
+ const issue = await lib.getIssue(link.number);
2399
+ await lib.updateIssue(link.number, { body: withNeededBy(issue.body, plan.neededBy) });
2400
+ if (link.kind === "suggestion" && !issue.labels.includes(NEEDED_BY_LABEL)) await lib.addLabels(link.number, [NEEDED_BY_LABEL]);
2401
+ } else {
2402
+ const libPull = await lib.getPullRequest(link.number);
2403
+ await lib.updatePullRequest(link.number, { body: withNeededBy(libPull.body, plan.neededBy) });
2404
+ await lib.addLabels(link.number, [NEEDED_BY_LABEL]);
2405
+ }
2406
+ } catch (err) {
2407
+ if (err instanceof GitHubError && err.auth === "rejected") throw err;
2408
+ warnings.push(`The library side does not say who needs it yet (${err.message}). Add "Needed by ${repo ?? ""}#${input.pr}" to #${link.number} on GitHub.`);
2409
+ }
2410
+ try {
2411
+ const fresh = await gh.getPullRequest(input.pr);
2412
+ const next = withLink(parsePiecesRecord(fresh.body) ?? record, link);
2413
+ await gh.updatePullRequest(input.pr, { body: withPiecesRecord(fresh.body, next) });
2414
+ } catch (err) {
2415
+ if (mode !== "suggest") throw err;
2416
+ warnings.push(`#${input.pr} does not list #${link.number} yet (${err.message}). Add "Library: ${repo ? lib.fullName : ""}#${link.number}" to #${input.pr} on GitHub, so this piece is not sent twice.`);
2417
+ }
2418
+ let preview = null;
2419
+ if (!repo && (input.usePreview ?? true) && link.kind === "token-change" && link.branch) {
2420
+ preview = await useLibraryPreviewOnGitHub(gh, { productBranch: pull.headRef, libraryBranch: link.branch, libraryNumber: link.number }).catch((err) => {
2421
+ warnings.push(`Your branch does not use the preview yet: ${err.message}`);
2422
+ return null;
2423
+ });
2424
+ }
2425
+ return { status: link.kind === "suggestion" ? "suggested" : "opened", plan, link, preview, warnings };
2426
+ }
2427
+ function acrossRepos(p2, lifecycle, across) {
2428
+ if (!across || !p2.action) return p2;
2429
+ if (lifecycle === "merged") return { ...p2, sentence: "The library published it. Update @bridge-ds/react in your product to use it.", action: null };
2430
+ return { ...p2, action: null };
2431
+ }
2432
+ const isNote = (path) => path.startsWith(`${EXPLORATION_NOTES_DIR}/`);
2433
+ async function linkedLibraryBranchOnGitHub(gh, link, product, piece, opts = {}) {
2434
+ const nearest = piece?.nearest?.label;
2435
+ const behind = () => opts.across ? Promise.resolve(null) : gh.compare(product.baseRef, product.headSha).then((c) => c.behindBy, () => null);
2436
+ if (link.kind === "suggestion") {
2437
+ const issue = await gh.getIssue(link.number);
2438
+ const cached = suggestionStateFromLabels(issue.labels);
2439
+ const s = suggestionStatus({ state: issue.state, stateReason: issue.stateReason, applied: cached === "applying", review: cached === "applying" ? void 0 : cached });
2440
+ const status2 = linkStatus({ kind: "suggestion", status: s });
2441
+ const lifecycle2 = s === "applied" ? "merged" : issue.state === "closed" ? "closed" : "open";
2442
+ const behindMain2 = lifecycle2 === "merged" ? await behind() : null;
2443
+ const preview2 = libraryPreviewState({ kind: "suggestion", lifecycle: lifecycle2, hasChanges: false, missing: null, total: null, behindMain: behindMain2, nearest });
2444
+ return { link, piece, status: status2, preview: acrossRepos(preview2, lifecycle2, opts.across), people: status2.people };
2445
+ }
2446
+ if (link.kind === "request") {
2447
+ const issue = await gh.getIssue(link.number);
2448
+ const status2 = linkStatus({ kind: "request", state: issue.state, stateReason: issue.stateReason });
2449
+ const lifecycle2 = issue.state === "closed" ? issue.stateReason === "not_planned" ? "closed" : "merged" : "open";
2450
+ const behindMain2 = lifecycle2 === "merged" ? await behind() : null;
2451
+ const preview2 = libraryPreviewState({ kind: "request", lifecycle: lifecycle2, hasChanges: false, missing: null, total: null, behindMain: behindMain2, nearest });
2452
+ return { link, piece, status: status2, preview: acrossRepos(preview2, lifecycle2, opts.across), people: status2.people };
2453
+ }
2454
+ const lib = await gh.getPullRequest(link.number);
2455
+ const reviews = await gh.listReviews(link.number).catch(() => []);
2456
+ const status = linkStatus({ kind: link.kind, state: lib.state, draft: lib.draft, labels: lib.labels, requestedReviewers: lib.requestedReviewers, reviews: reviews.map((r) => ({ user: r.user, state: r.state })) });
2457
+ const lifecycle = lib.state === "merged" ? "merged" : lib.state === "open" ? "open" : "closed";
2458
+ let missing = null;
2459
+ let total = null;
2460
+ let behindMain = null;
2461
+ let hasChanges = true;
2462
+ if (lifecycle === "open") {
2463
+ const [files, own, inProduct] = await Promise.all([
2464
+ gh.listPullRequestFiles(link.number).catch(() => null),
2465
+ gh.compare(lib.baseRef, lib.headSha).catch(() => null),
2466
+ opts.across ? Promise.resolve(null) : gh.compare(product.headSha, lib.headSha).catch(() => null)
2467
+ ]);
2468
+ hasChanges = files ? files.some((f) => !isNote(f.filename)) : true;
2469
+ total = own?.aheadBy ?? null;
2470
+ missing = inProduct?.aheadBy ?? null;
2471
+ } else if (lifecycle === "merged") {
2472
+ behindMain = await behind();
2473
+ }
2474
+ const preview = libraryPreviewState({ kind: link.kind, lifecycle, hasChanges, missing, total, behindMain, nearest });
2475
+ return { link, piece, status, preview: acrossRepos(preview, lifecycle, opts.across), people: status.people };
2476
+ }
2477
+ async function productBranchViewOnGitHub(gh, pr, opts) {
2478
+ const pull = await gh.getPullRequest(pr);
2479
+ const files = await gh.listPullRequestFiles(pr).then((f) => f.map((x) => x.filename), () => []);
2480
+ const product = productOf(opts.products, pull, files, gh.fullName);
2481
+ const record = parsePiecesRecord(pull.body);
2482
+ const across = Boolean(repoOfProductBranch(gh, opts.library));
2483
+ const lib = opts.library ?? gh;
2484
+ const linked = await Promise.all(
2485
+ (record?.links ?? []).map((l) => linkedLibraryBranchOnGitHub(lib, l, { headSha: pull.headSha, baseRef: pull.baseRef }, record?.pieces.find((p2) => p2.id === l.piece), { across }))
2486
+ );
2487
+ const open = (record?.pieces ?? []).filter((p2) => !record.links.some((l) => l.piece === p2.id));
2488
+ return { pull, product, record, sentence: productBranchSentence(record, { viewer: opts.viewer, agentName: opts.agentName }), linked, open };
2489
+ }
2490
+ async function switchToPublishedOnGitHub(gh, pr) {
2491
+ if (!gh.canWrite) throw new GitHubError(401, "Switching needs a GitHub token (contents and pull requests: write).");
2492
+ const pull = await gh.getPullRequest(pr);
2493
+ if (pull.state !== "open") throw new GitHubError(422, `#${pr} is ${pull.state}: only an open branch switches.`);
2494
+ const cmp = await gh.compare(pull.baseRef, pull.headSha);
2495
+ if (cmp.behindBy === 0) return { status: "up-to-date", said: "Your branch already uses the published version." };
2496
+ try {
2497
+ await gh.updatePullRequestBranch(pr, pull.headSha);
2498
+ } catch (err) {
2499
+ if (err instanceof GitHubError && err.status === 422) throw new GitHubError(422, `Your branch and ${pull.baseRef} change the same lines, so Bridge cannot switch it alone. Use "Update from main" to pick the token values, or ask a developer.`, err.detail);
2500
+ throw err;
2501
+ }
2502
+ return { status: "switched", said: `Your branch now uses the published version from ${pull.baseRef}.` };
2503
+ }
2504
+ async function proposePiecesOnGitHub(gh, pr, input) {
2505
+ const out = { proposed: [], already: [], unknown: [], warnings: [] };
2506
+ const ids = [...new Set(input.pieces.map((p2) => p2.trim()).filter(Boolean))];
2507
+ if (!ids.length) return out;
2508
+ const pull = await gh.getPullRequest(pr);
2509
+ const record = parsePiecesRecord(pull.body);
2510
+ if (!record) throw new GitHubError(404, `#${pr} was not checked against the library yet: find its missing pieces first (bridge_missing_pieces with record, or \`bridge pieces ${pr} --record\`).`);
2511
+ const files = await gh.listPullRequestFiles(pr).then((f) => f.map((x) => x.filename), () => []);
2512
+ const lib = input.library ?? gh;
2513
+ const repo = repoOfProductBranch(gh, input.library);
2514
+ const ref = productBranchRef(pull, ownProduct(input.products, repo) ?? productOfBranch(input.products, { labels: pull.labels, files }), repo);
2515
+ const todo = [];
2516
+ for (const id of ids) {
2517
+ const piece = record.pieces.find((p2) => p2.id === id);
2518
+ const link = record.links.find((l) => l.piece === id);
2519
+ if (!piece) out.unknown.push(id);
2520
+ else if (link) out.already.push({ piece: id, number: link.number });
2521
+ else todo.push({ piece, ask: pieceRequest(piece, ref, { previewUrl: input.previewUrl, source: input.source, requestedBy: input.openedBy }) });
2522
+ }
2523
+ if (input.dryRun) return { ...out, proposed: todo.map((t2) => ({ piece: t2.piece.id, title: t2.ask.request.title, number: 0, url: "" })) };
2524
+ if (!todo.length) return out;
2525
+ if (!lib.canWrite) throw new GitHubError(401, "Sending pieces to the library needs a GitHub token (issues: write).");
2526
+ const at2 = (/* @__PURE__ */ new Date()).toISOString();
2527
+ const who = { ...input.openedBy ? { openedBy: input.openedBy } : {}, ...input.agent ? { agent: input.agent } : {}, ...repo ? { repo: lib.fullName } : {} };
2528
+ const links = [];
2529
+ for (const t2 of todo) {
2530
+ const issue = composeRequestIssue(t2.ask.request, { labels: [NEEDED_BY_LABEL] });
2531
+ const r = await lib.createIssue({ ...issue, body: withNeededBy(issue.body, t2.ask.neededBy) });
2532
+ links.push({ piece: t2.piece.id, kind: "request", number: r.number, url: r.url, title: t2.ask.request.title, at: at2, ...who });
2533
+ out.proposed.push({ piece: t2.piece.id, title: t2.ask.request.title, number: r.number, url: r.url });
2534
+ }
2535
+ try {
2536
+ const fresh = await gh.getPullRequest(pr);
2537
+ const next = links.reduce((rec, l) => withLink(rec, l), parsePiecesRecord(fresh.body) ?? record);
2538
+ await gh.updatePullRequest(pr, { body: withPiecesRecord(fresh.body, next) });
2539
+ } catch (err) {
2540
+ if (err instanceof GitHubError && err.auth === "rejected") throw err;
2541
+ out.warnings.push(`#${pr} does not list the requests yet (${err.message}). Add "Library: ${links.map((l) => `${repo ? lib.fullName : ""}#${l.number}`).join(", ")}" to #${pr} on GitHub, so they are not sent twice.`);
2542
+ }
2543
+ return out;
2544
+ }
2545
+ async function doneExploringOnGitHub(gh, ref, input = {}) {
2546
+ const p2 = await findExplorationOnGitHub(gh, ref);
2547
+ const status = explorationStatus({ state: p2.state, labels: p2.labels, headRef: p2.headRef, body: p2.body, headSha: p2.headSha, draft: p2.draft });
2548
+ if (status === "closed") throw new GitHubError(422, `Exploration #${p2.number} is closed. Reopen it on GitHub first: its preview comes back with it.`);
2549
+ if (status === "shipped") throw new GitHubError(422, `#${p2.number} is already shipped.`);
2550
+ if (status === "promoted") throw new GitHubError(422, `#${p2.number} was already sent to review.`);
2551
+ const fresh = status === "active";
2552
+ const files = fresh ? await gh.listPullRequestFiles(p2.number) : [];
2553
+ const plan = fresh ? doneExploringPlan({ body: p2.body, branch: p2.headRef, files: files.map((f) => ({ path: f.filename, status: f.status })), date: input.date }) : null;
2554
+ const take = plan?.take ?? ["git fetch origin", `git checkout ${p2.headRef}`];
2555
+ const checks = p2.headSha ? await Promise.resolve(p2.headSha).then((sha) => gh.getChecks(sha)).then((c) => c.state, () => null) : null;
2556
+ const out = { number: p2.number, url: p2.url, branch: p2.headRef, status: fresh ? "ready" : status, handedOff: fresh, plan, pieces: { proposed: [], already: [], unknown: [], warnings: [] }, take, checks, note: explorationStartingPoint(checks), warnings: [] };
2557
+ const products = input.products ?? [];
2558
+ const ask = { pieces: input.pieces ?? [], products, previewUrl: input.previewUrl, openedBy: input.openedBy, agent: input.agent, source: input.source, library: input.library };
2559
+ if (input.dryRun) {
2560
+ out.pieces = await proposePiecesOnGitHub(gh, p2.number, { ...ask, dryRun: true }).catch((e) => ({ proposed: [], already: [], unknown: [], warnings: [e.message] }));
2561
+ return out;
2562
+ }
2563
+ if (!gh.canWrite) throw new GitHubError(401, 'Saying "Done exploring" needs a GitHub token (contents, pull requests and issues: write).');
2564
+ out.pieces = await proposePiecesOnGitHub(gh, p2.number, ask);
2565
+ out.warnings.push(...out.pieces.warnings);
2566
+ if (!plan) return out;
2567
+ const x = gh.withBranchPrefix(EXPLORATION_PREFIX);
2568
+ let doneSha = p2.headSha;
2569
+ if (plan.removeFiles.length) {
2570
+ const c = await x.commitToBranch({ branch: p2.headRef, message: "chore(explore): done exploring, remove the exploration note", files: plan.removeFiles.map((path) => ({ path, content: null })) }).catch((e) => {
2571
+ if (e instanceof GitHubError && e.auth === "rejected") throw e;
2572
+ out.warnings.push(`The exploration note stays on the branch (${e.message}). Delete ${plan.removeFiles.join(", ")} before merging.`);
2573
+ return null;
2574
+ });
2575
+ if (c) doneSha = c.sha;
2576
+ }
2577
+ await x.addLabels(p2.number, plan.addLabels);
2578
+ await x.removeLabel(p2.number, plan.removeLabels[0]).catch((e) => {
2579
+ if (!(e instanceof GitHubError && e.status === 404)) out.warnings.push(`The exploration label is still on (${e.message}). Remove it on GitHub so developers can ship it later.`);
2580
+ });
2581
+ const latest = await gh.getPullRequest(p2.number).catch(() => p2);
2582
+ const doneAt = parseExplorationBody(plan.body)?.doneAt ?? (input.date ?? /* @__PURE__ */ new Date()).toISOString();
2583
+ await x.updatePullRequest(p2.number, { body: withDoneExploring(latest.body, { branch: p2.headRef, doneAt, doneSha, checks }) });
2584
+ return out;
2585
+ }
2586
+ const CONTINUE_PHRASE = "Continue Bridge exploration";
2587
+ const onlyNotes = (files) => Boolean(files?.length) && files.every((f) => f.startsWith(".bridge/"));
2588
+ function explorationStage(x) {
2589
+ if (x.status !== "active" || x.archived) return "handed";
2590
+ if (x.files && (x.files.length === 0 || onlyNotes(x.files))) return "not-started";
2591
+ return "exploring";
2592
+ }
2593
+ const continueRef = (x) => explorationRefLabel(x);
2594
+ function continueLines(x) {
2595
+ const ref = continueRef(x);
2596
+ const name = x.name.replace(/"/g, "'").trim();
2597
+ return {
2598
+ app: `${CONTINUE_PHRASE} ${ref} "${name}" in ${x.productName} and use the Bridge library.`,
2599
+ terminal: `claude "${CONTINUE_PHRASE} ${ref}"`
2600
+ };
2601
+ }
2602
+ function parseContinueLine(text) {
2603
+ const m = /continue\s+bridge\s+exploration\s+((?:[a-z0-9][\w.-]*)?#\d+)/i.exec(text);
2604
+ return m ? m[1].toLowerCase() : null;
2605
+ }
2606
+ function wordsOf(name) {
2607
+ const spaced = name.replace(/[-_]+/g, " ").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
2608
+ const words = spaced.split(/\s+/).filter(Boolean);
2609
+ return words.map((w, i) => i === 0 ? w.charAt(0).toUpperCase() + w.slice(1) : /^[A-Z]{2,}$/.test(w) ? w : w.toLowerCase()).join(" ");
2610
+ }
2611
+ const SKIP$1 = [/^\.bridge\//, /^\.changeset\//, /^\.github\//, /\.(test|spec|stories)\.[jt]sx?$/, /\.d\.ts$/, /(^|\/)package(-lock)?\.json$/, /\.md$/, /(^|\/)__snapshots__\//, /\.docs\.json$/];
2612
+ const CODE = /\.(tsx|jsx|ts|js|vue|svelte|astro|css|scss)$/;
2613
+ const SCREEN_DIRS = /(^|\/)(pages|routes|screens|views|app)\//;
2614
+ function thingOf(path) {
2615
+ if (SKIP$1.some((r) => r.test(path))) return null;
2616
+ if (/(^|\/)tokens\/src\//.test(path) || /\.tokens\.json$/.test(path)) return "Tokens";
2617
+ if (!CODE.test(path)) return null;
2618
+ const parts = path.split("/");
2619
+ const file = parts[parts.length - 1];
2620
+ const base = file.replace(/\.module\.(css|scss)$/, "").replace(/\.[^.]+$/, "");
2621
+ const ci3 = parts.lastIndexOf("components");
2622
+ if (ci3 >= 0 && ci3 < parts.length - 2 && /^[A-Z]/.test(parts[ci3 + 1])) return wordsOf(parts[ci3 + 1]);
2623
+ if (/^[A-Z][A-Za-z0-9]+$/.test(base)) return wordsOf(base);
2624
+ if (SCREEN_DIRS.test(path)) {
2625
+ const name = base === "index" || base === "page" ? parts[parts.length - 2] : base;
2626
+ if (name && !/^\[/.test(name) && !["pages", "routes", "screens", "views", "app", "src"].includes(name)) return `${wordsOf(name)} screen`;
2627
+ }
2628
+ return null;
2629
+ }
2630
+ function changedThings(files) {
2631
+ const out = [];
2632
+ for (const f of files) {
2633
+ const t2 = thingOf(f);
2634
+ if (t2 && !out.includes(t2)) out.push(t2);
2635
+ }
2636
+ return out;
2637
+ }
2638
+ function listThings(things, max = 2) {
2639
+ if (things.length <= max) return things.length === 2 ? `${things[0]} and ${things[1]}` : things[0] ?? "";
2640
+ return `${things.slice(0, max).join(", ")} and ${things.length - max} more`;
2641
+ }
2642
+ const PAST = {
2643
+ add: "added",
2644
+ build: "built",
2645
+ change: "changed",
2646
+ drop: "dropped",
2647
+ fix: "fixed",
2648
+ make: "made",
2649
+ move: "moved",
2650
+ polish: "polished",
2651
+ remove: "removed",
2652
+ rename: "renamed",
2653
+ replace: "replaced",
2654
+ rework: "reworked",
2655
+ set: "set",
2656
+ show: "showed",
2657
+ split: "split",
2658
+ swap: "swapped",
2659
+ try: "tried",
2660
+ tweak: "tweaked",
2661
+ update: "updated",
2662
+ use: "used",
2663
+ wire: "wired"
2664
+ };
2665
+ function isClaudeAssisted(c) {
2666
+ const m = c.commit.message;
2667
+ return /co-authored-by:\s*claude/i.test(m) || /generated with \[?claude/i.test(m);
2668
+ }
2669
+ const isClaudeAccount = (c) => /claude/i.test(c.author?.login ?? "") || /^claude\b/i.test(c.commit.author?.name ?? "");
2670
+ const isBot = (name) => /\[bot\]$|^github-actions\b|-bot$/i.test(name);
2671
+ function humanAuthorOf(c) {
2672
+ if (isClaudeAccount(c)) return null;
2673
+ const login = c.author?.login?.trim();
2674
+ if (login) return isBot(login) ? null : login;
2675
+ const name = c.commit.author?.name?.trim();
2676
+ return name && !isBot(name) ? name : null;
2677
+ }
2678
+ function isClaudeCommit(c) {
2679
+ return isClaudeAccount(c) || isClaudeAssisted(c) && !humanAuthorOf(c);
2680
+ }
2681
+ const isStartCommit = (c) => /^chore\(explore\): start /.test(c.commit.message);
2682
+ function subjectWords(subject) {
2683
+ const s = subject.replace(/^[a-z]+(\([^)]*\))?!?:\s*/i, "").replace(/\s*—\s*/g, ", ").replace(/\s*\(#\d+\)\s*$/, "").trim();
2684
+ if (!s) return "made a change";
2685
+ const [first2, ...rest] = s.split(/\s+/);
2686
+ const past = PAST[first2.toLowerCase()];
2687
+ if (past) return [past, ...rest].join(" ");
2688
+ if (/^(added|built|changed|fixed|made|moved|removed|renamed|replaced|updated|used|tried|swapped|tweaked)$/i.test(first2)) return [first2.toLowerCase(), ...rest].join(" ");
2689
+ return `changed ${/^[A-Z][a-z]/.test(s) ? s.charAt(0).toLowerCase() + s.slice(1) : s}`;
2690
+ }
2691
+ const dateOf = (c) => c.commit.author?.date ?? c.commit.committer?.date ?? "";
2692
+ function explorationActivity(input) {
2693
+ const commits = input.commits.filter((c) => !isStartCommit(c));
2694
+ const events = commits.map((c) => {
2695
+ const claude = isClaudeCommit(c);
2696
+ const withClaude = !claude && isClaudeAssisted(c);
2697
+ const who = claude ? "claude" : humanAuthorOf(c) ?? input.author;
2698
+ return { who, claude, ...withClaude ? { withClaude } : {}, text: subjectWords(c.commit.message.split("\n")[0]), at: dateOf(c), sha: c.sha };
2699
+ }).sort((a2, b) => b.at.localeCompare(a2.at));
2700
+ const start = input.commits.find(isStartCommit);
2701
+ const startedAt = input.startedAt ?? (start ? dateOf(start) : null);
2702
+ if (startedAt) events.push({ who: input.author, claude: false, text: "started the exploration", at: startedAt });
2703
+ return events.slice(0, input.limit ?? events.length);
2704
+ }
2705
+ function lastActivity(input) {
2706
+ const latest = explorationActivity({ commits: input.commits, author: input.author }).find((e) => e.text !== "started the exploration");
2707
+ if (!latest) return null;
2708
+ const things = changedThings(input.files ?? []);
2709
+ return { who: latest.who, claude: latest.claude, ...latest.withClaude ? { withClaude: true } : {}, text: things.length ? `changed ${listThings(things)}` : latest.text, at: latest.at };
2710
+ }
2711
+ function shortAgo(iso, now = /* @__PURE__ */ new Date()) {
2712
+ const t2 = new Date(iso).getTime();
2713
+ if (!Number.isFinite(t2)) return "";
2714
+ const s = Math.max(0, (now.getTime() - t2) / 1e3);
2715
+ if (s < 60) return "just now";
2716
+ if (s < 3600) return `${Math.round(s / 60)} min ago`;
2717
+ if (s < 86400) return `${Math.round(s / 3600)} h ago`;
2718
+ const days = Math.floor(s / 86400);
2719
+ if (days === 1) return "Yesterday";
2720
+ if (days < 7) return `${days} days ago`;
2721
+ return new Date(t2).toLocaleDateString("en", { month: "short", day: "numeric", ...new Date(t2).getFullYear() !== now.getFullYear() ? { year: "numeric" } : {} });
2722
+ }
2723
+ function startedWhen(iso, now = /* @__PURE__ */ new Date()) {
2724
+ const t2 = new Date(iso);
2725
+ if (!Number.isFinite(t2.getTime())) return "";
2726
+ const day = (d) => Math.floor(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) / 864e5);
2727
+ const diff = day(now) - day(t2);
2728
+ if (diff <= 0) return "today";
2729
+ if (diff === 1) return "yesterday";
2730
+ if (diff < 7) return `${diff} days ago`;
2731
+ return t2.toLocaleDateString("en", { month: "short", day: "numeric", ...t2.getFullYear() !== now.getFullYear() ? { year: "numeric" } : {} });
2732
+ }
2733
+ function actorName(e, names2) {
2734
+ if (e.claude) return "Claude";
2735
+ const n = names2?.[e.who] ?? e.who;
2736
+ return e.withClaude ? `${n} with Claude` : n;
2737
+ }
2738
+ function whereItStands(input) {
2739
+ if (input.stage === "not-started") return `"${input.name}" is started but nothing is built yet.`;
2740
+ if (input.stage === "handed") return `"${input.name}" is ${input.statusLabel ? input.statusLabel.toLowerCase() : "with the developers"}.`;
2741
+ const things = changedThings(input.files ?? []);
2742
+ const latest = input.events.find((e) => e.text !== "started the exploration");
2743
+ const who = (e) => actorName(e, input.names);
2744
+ const so = things.length ? `So far it changes ${listThings(things, 3)}.` : "Something changed already.";
2745
+ return latest ? `${so} Last: ${who(latest)} ${latest.text}, ${shortAgo(latest.at, input.now).toLowerCase()}.` : so;
2746
+ }
2250
2747
  const hex2 = (x) => Math.round(Math.max(0, Math.min(255, x))).toString(16).padStart(2, "0");
2251
2748
  function rgbOf(v) {
2252
2749
  const m = /^rgba?\(([^)]*)\)$/i.exec(v.trim());
@@ -3043,7 +3540,7 @@ function locate(parentBuilt, parentMeasured, axis, built, measured, builtParts,
3043
3540
  if (!stacked) {
3044
3541
  const tallest = [...kids].sort((a2, b) => b.m[len] - a2.m[len])[0];
3045
3542
  const change = tallest.b[len] - tallest.m[len];
3046
- if (Math.abs(change) >= 1) out.push(...ownSize(tallest, change));
3543
+ if (Math.abs(change) >= 1) out.push(...ownSize2(tallest, change));
3047
3544
  return out;
3048
3545
  }
3049
3546
  let prevB = pb[axis];
@@ -3052,13 +3549,13 @@ function locate(parentBuilt, parentMeasured, axis, built, measured, builtParts,
3052
3549
  const gap = k.b[axis] - prevB - (k.m[axis] - prevM);
3053
3550
  say(gap, i === 0 ? `${before} the ${partWords(k.p)}` : `${after} the ${partWords(kids[i - 1].p)}`);
3054
3551
  const change = k.b[len] - k.m[len];
3055
- if (Math.abs(change) >= 1) out.push(...ownSize(k, change));
3552
+ if (Math.abs(change) >= 1) out.push(...ownSize2(k, change));
3056
3553
  prevB = k.b[axis] + k.b[len];
3057
3554
  prevM = k.m[axis] + k.m[len];
3058
3555
  });
3059
3556
  say(pb[axis] + pb[len] - prevB - (pm[axis] + pm[len] - prevM), `${after} the ${partWords(kids.at(-1).p)}`);
3060
3557
  return out;
3061
- function ownSize(k, change) {
3558
+ function ownSize2(k, change) {
3062
3559
  const inner = depth < 3 ? locate(k.p.id, pairs.get(k.p.id), axis, built, measured, builtParts, pairs, depth + 1) : [];
3063
3560
  if (inner.length) return inner;
3064
3561
  return [{ amount: change, text: `the ${partWords(k.p)} is ${round$1(Math.abs(change))} px ${change < 0 ? axis === "y" ? "shorter" : "narrower" : axis === "y" ? "taller" : "wider"} in Figma` }];
@@ -3163,12 +3660,16 @@ function buildGapCase(input) {
3163
3660
  const checkedVariants = [];
3164
3661
  const specVariant = new Map((spec?.variants ?? []).map((v) => [variantKeyOf(v.props), v]));
3165
3662
  if (input.fidelity) {
3166
- for (const r of input.fidelity.variants) if (r.status !== "skipped") checkedVariants.push({ key: r.key, props: specVariant.get(r.key)?.props ?? propsOfKey(r.key), result: r, variant: specVariant.get(r.key) });
3663
+ for (const r of input.fidelity.variants) {
3664
+ if (r.status === "skipped") continue;
3665
+ const variant = matchVariant(spec, propsOfKey(r.key));
3666
+ checkedVariants.push({ key: r.key, props: variant?.props ?? propsOfKey(r.key), result: r, variant });
3667
+ }
3167
3668
  } else for (const [key2, v] of specVariant) checkedVariants.push({ key: key2, props: v.props, variant: v });
3168
3669
  const variants = [];
3169
3670
  for (const cv3 of checkedVariants) {
3170
3671
  if (cv3.result && cv3.result.status !== "differs") continue;
3171
- const shot = input.shots?.[cv3.key];
3672
+ const shot = input.shots?.[cv3.key] ?? (cv3.variant ? input.shots?.[variantKeyOf(cv3.variant.props)] : void 0);
3172
3673
  const story = spec ? storyForVariant(spec, cv3.props, stories, shot) : shot ? stories.find((s) => s.shot?.file === shot.file) : void 0;
3173
3674
  const gv = {
3174
3675
  key: cv3.key,
@@ -3257,744 +3758,803 @@ function gapDetail(component, variants, checked, referee) {
3257
3758
  }
3258
3759
  return lines.join("\n");
3259
3760
  }
3260
- const FIGMA_FIXES_VERSION = 1;
3261
- const DEFAULT_FIGMA_FIXES_FILE = "figma-fixes.json";
3262
- class FigmaFixesError extends Error {
3263
- constructor(problems) {
3264
- super(`figma-fixes.json: ${problems.join("; ")}`);
3265
- this.problems = problems;
3761
+ const NOT_AN_AXIS = /^(as|asChild|render|component|className|class|style|id|key|ref|role|type|name|form|dir|lang|tabIndex|slot|children|htmlFor|value|defaultValue|placeholder|href|target|rel|src|alt)$/i;
3762
+ const STATE_PROPS = /* @__PURE__ */ new Set(["disabled", "checked", "indeterminate"]);
3763
+ const STATES$1 = [
3764
+ { value: "Default", of: (s) => !s.state && s.props?.disabled !== true },
3765
+ { value: "Hover", of: (s) => s.state === "hover" && s.props?.disabled !== true },
3766
+ { value: "Pressed", of: (s) => s.state === "active" && s.props?.disabled !== true },
3767
+ { value: "Focus", of: (s) => s.state === "focus-visible" && s.props?.disabled !== true },
3768
+ { value: "Disabled", of: (s) => s.props?.disabled === true }
3769
+ ];
3770
+ const setNameOf$2 = (title) => title.split("/").at(-1).trim();
3771
+ const axisName = (prop) => prop.charAt(0).toUpperCase() + prop.slice(1).replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
3772
+ const propsOf$1 = (s) => ({ ...s.props ?? {}, ...s.overrides ?? {} });
3773
+ function checkedOf(s) {
3774
+ const p2 = propsOf$1(s);
3775
+ return p2.indeterminate === true ? "mixed" : String(p2.checked === true);
3776
+ }
3777
+ const LITERAL_FIELDS = ["fill", "textFill", "stroke", "fontFamily", "shadow"];
3778
+ function valuesOf(spec, partId, hasChildren, known, fills = false, own = { width: true, height: true }) {
3779
+ const sized = { ...hasChildren ? boxSized(spec, partId, own) : {}, ...spec.sized?.[partId] };
3780
+ const v = { ...spec.values[partId] ?? {} };
3781
+ const raw = spec.raw[partId] ?? {};
3782
+ for (const field of Object.keys(v)) {
3783
+ const value = v[field];
3784
+ if (typeof value !== "string" || value.startsWith("calc(") || known(value)) continue;
3785
+ const literal = raw[field];
3786
+ if (literal === void 0) delete v[field];
3787
+ else v[field] = literal;
3266
3788
  }
3267
- problems;
3789
+ for (const field of LITERAL_FIELDS) if (v[field] === void 0 && typeof raw[field] === "string") v[field] = raw[field];
3790
+ if (hasChildren) {
3791
+ if (typeof v.width === "number" && !fills) delete v.width;
3792
+ if (typeof v.height === "number") delete v.height;
3793
+ }
3794
+ if (sized?.width !== void 0) v.width = sized.width;
3795
+ if (sized?.height !== void 0) v.height = sized.height;
3796
+ return v;
3268
3797
  }
3269
- const isObj$3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
3270
- const STATES$1 = ["rest", "hover", "active", "focus-visible", "disabled"];
3271
- const KINDS = ["part", "axis", "story", "status"];
3272
- const BASE_KEYS = ["id", "component", "reason", "by", "where", "kind"];
3273
- const KEYS = {
3274
- part: ["part", "set", "width", "height", "direction", "align", "overlay", "hide"],
3275
- axis: ["action", "arg", "property", "values"],
3276
- story: ["use"],
3277
- status: ["status"]
3278
- };
3279
- const PRIMARY = ["MIN", "CENTER", "MAX", "SPACE_BETWEEN"];
3280
- const COUNTER = ["MIN", "CENTER", "MAX", "STRETCH"];
3281
- const plain = (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean";
3282
- function whereProblems(w, at2) {
3283
- if (!isObj$3(w)) return at2("where must be an object with args and/or state");
3284
- for (const k of Object.keys(w)) if (k !== "args" && k !== "state") at2(`where.${k} is not known (args, state)`);
3285
- if (w.args !== void 0 && (!isObj$3(w.args) || !Object.values(w.args).every(plain))) at2("where.args maps arg names to a text, number or true/false");
3286
- if (w.state !== void 0 && !STATES$1.includes(w.state)) at2(`where.state is one of ${STATES$1.join(", ")}`);
3287
- }
3288
- function validateFigmaFixes(data) {
3289
- const out = [];
3290
- if (!isObj$3(data)) return ["expected an object with version and fixes"];
3291
- if (data.version !== FIGMA_FIXES_VERSION) out.push(`version ${String(data.version)} is not supported (expected ${FIGMA_FIXES_VERSION})`);
3292
- for (const k of Object.keys(data)) if (!["$schema", "version", "fixes"].includes(k)) out.push(`${k} is not known (version, fixes)`);
3293
- if (!Array.isArray(data.fixes)) return [...out, "fixes must be a list"];
3294
- const ids = /* @__PURE__ */ new Set();
3295
- data.fixes.forEach((f, i) => {
3296
- const label = isObj$3(f) && typeof f.id === "string" ? `fix ${f.id}` : `fix ${i + 1}`;
3297
- const at2 = (why) => out.push(`${label}: ${why}`);
3298
- if (!isObj$3(f)) return at2("must be an object");
3299
- if (typeof f.id !== "string" || !/^[a-z0-9][a-z0-9-]{0,79}$/.test(f.id)) at2("id must be lower case letters, digits and dashes");
3300
- else if (ids.has(f.id)) at2("id is repeated");
3301
- else ids.add(f.id);
3302
- if (typeof f.component !== "string" || !f.component.trim()) at2('component is missing (the Figma set name, like "Toggle")');
3303
- if (typeof f.reason !== "string" || f.reason.trim().length < 8) at2("reason is missing: say why, in a sentence");
3304
- else if (/\u2014/.test(f.reason)) at2("reason uses an em dash: use a colon or a comma");
3305
- if (f.by !== void 0 && typeof f.by !== "string") at2("by must be a text");
3306
- if (f.where !== void 0) whereProblems(f.where, at2);
3307
- if (!KINDS.includes(f.kind)) return at2(`kind must be one of ${KINDS.join(", ")}`);
3308
- const kind = f.kind;
3309
- for (const k of Object.keys(f)) if (!BASE_KEYS.includes(k) && !KEYS[kind].includes(k)) at2(`${k} is not a key of a ${kind} fix`);
3310
- if (kind === "part") partProblems(f, at2);
3311
- if (kind === "axis") axisProblems(f, at2);
3312
- if (kind === "story") {
3313
- const u = f.use;
3314
- if (!isObj$3(u) || typeof u.story !== "string" || !u.story) at2("use.story is the story id to draw the variant from");
3315
- else {
3316
- for (const k of Object.keys(u)) if (!["story", "args", "state"].includes(k)) at2(`use.${k} is not known (story, args, state)`);
3317
- if (u.args !== void 0 && (!isObj$3(u.args) || !Object.values(u.args).every(plain))) at2("use.args maps arg names to a text, number or true/false");
3318
- if (u.state !== void 0 && !STATES$1.includes(u.state)) at2(`use.state is one of ${STATES$1.join(", ")}`);
3319
- }
3320
- if (!isObj$3(f.where) || !f.where.args && !f.where.state) at2("where names the variant (args and/or state) the story is for");
3321
- }
3322
- if (kind === "status") {
3323
- if (f.status === "matches") at2('status cannot be "matches": only the shot comparison says a component matches');
3324
- else if (f.status !== void 0 && f.status !== "differs" && f.status !== "cant-check") at2("status is differs or cant-check (absent: a note)");
3325
- }
3326
- });
3798
+ function boxSized(spec, partId, own) {
3799
+ const box = spec.boxes?.[partId];
3800
+ const kids = spec.parts.filter((c) => c.parent === partId && !c.pin && !c.cover && !c.overlay).map((c) => spec.boxes?.[c.id]).filter((b) => !!b);
3801
+ if (!box || !kids.length) return {};
3802
+ const raw = spec.raw[partId] ?? {};
3803
+ const num = (f) => typeof raw[f] === "number" ? raw[f] : 0;
3804
+ const left = Math.min(...kids.map((k) => k.x));
3805
+ const top = Math.min(...kids.map((k) => k.y));
3806
+ const side = (f) => typeof raw[f] === "number" ? raw[f] : num("strokeWeight");
3807
+ const contentW = Math.max(...kids.map((k) => k.x + k.width)) - left + num("paddingLeft") + num("paddingRight") + side("strokeLeftWeight") + side("strokeRightWeight");
3808
+ const contentH = Math.max(...kids.map((k) => k.y + k.height)) - top + num("paddingTop") + num("paddingBottom") + side("strokeTopWeight") + side("strokeBottomWeight");
3809
+ const out = {};
3810
+ if (own.width && box.width - contentW > 1) out.width = box.width;
3811
+ if (own.height && box.height - contentH > 1) out.height = box.height;
3327
3812
  return out;
3328
3813
  }
3329
- function partProblems(f, at2) {
3330
- if (typeof f.part !== "string" || !f.part.trim()) at2("part is a part id or a .class");
3331
- const changes = ["set", "width", "height", "direction", "align", "overlay", "hide"].filter((k) => f[k] !== void 0);
3332
- if (!changes.length) at2("changes nothing: give set, width, height, direction, align, overlay or hide");
3333
- if (f.hide !== void 0 && typeof f.hide !== "boolean") at2("hide is true or false");
3334
- if (f.hide === true && changes.length > 1) at2("hide goes alone: a hidden part has nothing else to change");
3335
- if (f.set !== void 0) {
3336
- if (!isObj$3(f.set) || !Object.keys(f.set).length) at2("set maps fields to values");
3337
- else
3338
- for (const [field, value] of Object.entries(f.set)) {
3339
- if (!SPEC_FIELDS.includes(field)) {
3340
- at2(`set.${field} is not a field (${SPEC_FIELDS.join(", ")})`);
3341
- continue;
3342
- }
3343
- const color = COLOR_FIELDS.has(field);
3344
- if (value === "full") {
3345
- if (field !== "cornerRadius") at2(`set.${field}: "full" is for cornerRadius only`);
3346
- } else if (value === null) {
3347
- if (!color) at2(`set.${field} cannot be none (only colors can)`);
3348
- } else if (typeof value === "number") {
3349
- if (color) at2(`set.${field} is a color: a token path or null, not a number`);
3350
- else if (!Number.isFinite(value)) at2(`set.${field} must be a finite number`);
3351
- } else if (typeof value !== "string" || !value.trim()) at2(`set.${field} must be a token path, a number or null`);
3352
- else if (field === "textDecoration" && !/^(UNDERLINE|STRIKETHROUGH|NONE)$/.test(value)) at2(`set.textDecoration is UNDERLINE, STRIKETHROUGH or NONE`);
3353
- }
3814
+ function setOf(name, stories, argTypes, source, known, hints = {}) {
3815
+ const propName = (prop) => hints.properties?.[prop] ?? axisName(prop);
3816
+ const figmaValue = (prop, value) => hints.values?.[prop]?.[value] ?? value;
3817
+ const lead = stories.find((s) => s.name === "Default" && !s.state) ?? stories[0];
3818
+ const baseline = (k, a2) => (lead ? propsOf$1(lead)[k] : void 0) ?? a2.default ?? false;
3819
+ const shown = stories.filter((s) => !/skeleton/i.test(s.name));
3820
+ const ordered = [...shown.length ? shown : stories].sort((a2, b) => Number(!!a2.gallery) - Number(!!b.gallery));
3821
+ const valueOf = (s, k, a2) => (propsOf$1(s)[k] ?? a2.default ?? false) === true;
3822
+ const flags = Object.entries(argTypes).filter(
3823
+ ([k, a2]) => a2.kind === "boolean" && !STATE_PROPS.has(k) && !NOT_AN_AXIS.test(k) && ordered.some((s) => valueOf(s, k, a2)) && ordered.some((s) => !valueOf(s, k, a2))
3824
+ );
3825
+ const plain2 = ordered.filter(
3826
+ (s) => Object.entries(argTypes).every(
3827
+ ([k, a2]) => a2.kind !== "boolean" || STATE_PROPS.has(k) || NOT_AN_AXIS.test(k) || flags.some(([f]) => f === k) || (propsOf$1(s)[k] ?? a2.default ?? false) === baseline(k, a2)
3828
+ )
3829
+ );
3830
+ if (!plain2.length) return void 0;
3831
+ const axes = [];
3832
+ for (const [prop, a2] of flags) {
3833
+ const def = String(baseline(prop, a2) === true);
3834
+ axes.push({ name: propName(prop), prop, values: [figmaValue(prop, def), figmaValue(prop, def === "true" ? "false" : "true")], default: figmaValue(prop, def) });
3354
3835
  }
3355
- for (const side of ["width", "height"]) {
3356
- const v = f[side];
3357
- if (v !== void 0 && !["hug", "fill", "fixed"].includes(v) && !(typeof v === "number" && v > 0)) at2(`${side} is hug, fill, fixed or a size in px`);
3836
+ for (const [prop, a2] of Object.entries(argTypes)) {
3837
+ if (a2.kind !== "enum" || NOT_AN_AXIS.test(prop) || !a2.options || a2.options.length < 2 || a2.options.length > 12) continue;
3838
+ const seen = new Set(plain2.map((s) => String(propsOf$1(s)[prop] ?? a2.default ?? a2.options[0])));
3839
+ const values = a2.options.filter((o) => seen.has(o));
3840
+ if (values.length < 2) continue;
3841
+ const def = a2.default !== void 0 && values.includes(String(a2.default)) ? String(a2.default) : values[0];
3842
+ axes.push({ name: propName(prop), prop, values: [...new Set(values.map((v) => figmaValue(prop, v)))], default: figmaValue(prop, def) });
3358
3843
  }
3359
- if (f.direction !== void 0 && f.direction !== "HORIZONTAL" && f.direction !== "VERTICAL") at2("direction is HORIZONTAL or VERTICAL");
3360
- if (f.align !== void 0) {
3361
- const a2 = f.align;
3362
- if (!isObj$3(a2) || a2.primary === void 0 && a2.counter === void 0) at2("align gives primary and/or counter");
3363
- else {
3364
- if (a2.primary !== void 0 && !PRIMARY.includes(a2.primary)) at2(`align.primary is ${PRIMARY.join(", ")}`);
3365
- if (a2.counter !== void 0 && !COUNTER.includes(a2.counter)) at2(`align.counter is ${COUNTER.join(", ")}`);
3366
- }
3844
+ if ("checked" in argTypes || plain2.some((s) => "checked" in propsOf$1(s))) {
3845
+ const values = ["false", "true", "mixed"].filter((v) => plain2.some((s) => checkedOf(s) === v));
3846
+ if (values.length > 1) axes.push({ name: "Checked", values, default: values[0] });
3367
3847
  }
3368
- if (f.overlay !== void 0 && typeof f.overlay !== "boolean") {
3369
- const o = f.overlay;
3370
- if (!isObj$3(o) || !Object.keys(o).length || !Object.entries(o).every(([k, v]) => ["top", "right", "bottom", "left"].includes(k) && typeof v === "number")) at2('overlay is true, false, or offsets in px ({ "top": 3, "left": 3 })');
3848
+ const states = STATES$1.filter((st) => plain2.some(st.of)).map((st) => st.value);
3849
+ if (states.length > 1) axes.push({ name: "State", values: states, default: states[0] });
3850
+ const keyOf = (s) => {
3851
+ const props = {};
3852
+ for (const axis of axes) {
3853
+ const flag = flags.find(([k]) => k === axis.prop);
3854
+ const value = axis.name === "State" ? STATES$1.find((st) => st.of(s))?.value : axis.name === "Checked" ? checkedOf(s) : flag ? figmaValue(flag[0], String(valueOf(s, flag[0], flag[1]))) : propsOf$1(s)[axis.prop] === void 0 ? axis.default : figmaValue(axis.prop, String(propsOf$1(s)[axis.prop]));
3855
+ if (value === void 0 || !axis.values.includes(value)) return void 0;
3856
+ props[axis.name] = value;
3857
+ }
3858
+ return props;
3859
+ };
3860
+ const candidates = /* @__PURE__ */ new Map();
3861
+ for (const s of plain2) {
3862
+ const props = keyOf(s);
3863
+ if (!props) continue;
3864
+ const k = JSON.stringify(props);
3865
+ if (!candidates.has(k)) candidates.set(k, { props, stories: [] });
3866
+ candidates.get(k).stories.push(s);
3371
3867
  }
3372
- if (f.part === "root" && (f.hide === true || f.overlay !== void 0)) at2("the root cannot be hidden or overlaid");
3373
- }
3374
- function axisProblems(f, at2) {
3375
- const action = f.action;
3376
- if (action !== "map" && action !== "ignore" && action !== "prune") return at2("action is map, ignore or prune");
3377
- if (action !== "prune" && (typeof f.arg !== "string" || !f.arg)) at2(`${action} needs arg (the story arg, like "kind")`);
3378
- if (action === "map") {
3379
- if (f.property === void 0 && f.values === void 0) at2("map gives property (the Figma name) and/or values");
3380
- if (f.property !== void 0 && (typeof f.property !== "string" || !f.property.trim())) at2("property is the Figma property name");
3381
- if (f.values !== void 0 && (!isObj$3(f.values) || !Object.values(f.values).every((v) => typeof v === "string" && v.trim()))) at2("values maps code values to Figma values (texts)");
3382
- else if (isObj$3(f.values)) {
3383
- const figma = Object.values(f.values);
3384
- if (new Set(figma).size !== figma.length) at2("values gives two code values the same Figma value: use prune for the one to drop");
3868
+ const storyFor = /* @__PURE__ */ new Map();
3869
+ if (axes.some((a2) => a2.name === "State")) {
3870
+ const rest = (p2) => JSON.stringify(Object.entries(p2).filter(([k]) => k !== "State"));
3871
+ const groups = /* @__PURE__ */ new Map();
3872
+ for (const c of candidates.values()) groups.set(rest(c.props), [...groups.get(rest(c.props)) ?? [], c]);
3873
+ for (const [key2, group] of groups) {
3874
+ const ids = [...new Set(group.flatMap((c) => c.stories.map((s) => s.id)))];
3875
+ const score = (id) => group.filter((c) => c.stories.some((s) => s.id === id)).length;
3876
+ storyFor.set(key2, ids.reduce((best, id) => score(id) > score(best) ? id : best, ids[0]));
3877
+ }
3878
+ for (const c of candidates.values()) {
3879
+ const same = c.stories.filter((s) => s.id === storyFor.get(rest(c.props)));
3880
+ if (same.length) c.stories = same;
3385
3881
  }
3386
3882
  }
3387
- if (action === "ignore" && (f.property !== void 0 || f.values !== void 0)) at2("ignore takes no property or values");
3388
- if (action === "prune") {
3389
- if (!isObj$3(f.where) || !f.where.args && !f.where.state) at2("prune needs where (the args and/or state of the variants to drop)");
3390
- if (f.property !== void 0 || f.values !== void 0) at2("prune takes no property or values");
3883
+ const byVariant = new Map([...candidates].map(([k, c]) => [k, { props: c.props, story: c.stories[0] }]));
3884
+ if (!byVariant.size) return void 0;
3885
+ const isDefault = (p2) => axes.every((a2) => p2[a2.name] === a2.default);
3886
+ const base = [...byVariant.values()].find((v) => isDefault(v.props)) ?? [...byVariant.values()][0];
3887
+ const baseSpec = base.story.spec;
3888
+ const parts = baseSpec.parts.map((p2) => baseSpec.vectors?.[p2.id] ? { ...p2, kind: "vector" } : { ...p2 });
3889
+ for (const p2 of parts) {
3890
+ if (!p2.overlay || p2.pin || p2.cover) continue;
3891
+ const pin = pinFromBoxes(baseSpec, p2);
3892
+ if (pin) p2.pin = pin;
3893
+ else p2.cover = true;
3391
3894
  }
3392
- }
3393
- function parseFigmaFixes(input) {
3394
- let data = input;
3395
- if (typeof input === "string") {
3396
- try {
3397
- data = JSON.parse(input);
3398
- } catch (e) {
3399
- throw new FigmaFixesError([`not JSON: ${e.message}`]);
3895
+ const paints = (id) => {
3896
+ const v = baseSpec.values[id] ?? {};
3897
+ return [v.fill, v.stroke].some((x) => x !== void 0 && x !== null);
3898
+ };
3899
+ for (const p2 of [...parts].reverse()) {
3900
+ if (!p2.parent || p2.kind !== "frame" || p2.pin || p2.cover || p2.overlay || paints(p2.id)) continue;
3901
+ const kids = parts.filter((c) => c.parent === p2.id);
3902
+ if (kids.length && kids.every((c) => c.overlay || c.pin || c.cover)) p2.cover = true;
3903
+ }
3904
+ const aligners = new Set(
3905
+ parts.filter((p2) => {
3906
+ const flow = parts.filter((c) => c.parent === p2.id && !c.pin && !c.cover && !c.overlay);
3907
+ return flow.length === 1 && !!p2.layout?.align?.[flow[0].id];
3908
+ }).map((p2) => p2.id)
3909
+ );
3910
+ for (const p2 of parts) if (p2.layout) p2.layout = alignedLayout(p2, parts);
3911
+ const parents = new Set(parts.filter((p2) => !p2.pin && !p2.cover).map((p2) => p2.parent).filter(Boolean));
3912
+ const variants = [];
3913
+ const axesMemo = /* @__PURE__ */ new Map();
3914
+ const shotOf = /* @__PURE__ */ new Map();
3915
+ const spacersOf = /* @__PURE__ */ new Map();
3916
+ const wrapsOf = /* @__PURE__ */ new Map();
3917
+ for (const { props, story: story2 } of byVariant.values()) {
3918
+ const m = story2.spec;
3919
+ const pairs = story2 === base.story ? new Map(parts.map((p2) => [p2.id, p2.id])) : pairParts(baseSpec.parts, m);
3920
+ const values = {};
3921
+ const vectors = {};
3922
+ const pins = {};
3923
+ const hidden = [];
3924
+ const spacers = {};
3925
+ const wraps = {};
3926
+ const layouts = {};
3927
+ for (const p2 of parts) {
3928
+ const mid = pairs.get(p2.id);
3929
+ if (mid === void 0) {
3930
+ hidden.push(p2.id);
3931
+ continue;
3932
+ }
3933
+ values[p2.id] = valuesOf(m, mid, parents.has(p2.id) && !aligners.has(p2.id), known, !p2.parent && !!p2.fill, ownAxes(p2, parts, baseSpec, axesMemo));
3934
+ if (m.spacers?.[mid]) spacers[p2.id] = m.spacers[mid];
3935
+ if (p2.kind === "icon" || p2.kind === "vector") delete values[p2.id].fill;
3936
+ const mp = m.parts.find((x) => x.id === mid);
3937
+ const pin = mp?.pin ?? (mp?.overlay ? pinFromBoxes(m, mp) : void 0);
3938
+ if (p2.pin && pin && JSON.stringify(pin) !== JSON.stringify(p2.pin)) pins[p2.id] = pin;
3939
+ if (mp?.layout && p2.layout) {
3940
+ const layout = alignedLayout({ ...mp, id: p2.id }, m.parts.map((x) => ({ ...x, parent: x.parent === mid ? p2.id : x.parent })));
3941
+ if (layout && (layout.primary !== p2.layout.primary || layout.counter !== p2.layout.counter || layout.direction !== p2.layout.direction)) layouts[p2.id] = layout;
3942
+ }
3943
+ if (mp?.wrap) wraps[p2.id] = mp.wrap.padding;
3944
+ const vec = p2.kind === "vector" ? m.vectors?.[mid] : void 0;
3945
+ if (vec) vectors[p2.id] = { svg: vec.svg, strokes: vec.strokes.every((v) => typeof v !== "string" || known(v)) ? [...vec.strokes] : [] };
3400
3946
  }
3947
+ const variant = { props, values, ...hidden.length ? { hidden } : {}, ...Object.keys(vectors).length ? { vectors } : {}, ...Object.keys(pins).length ? { pins } : {}, ...Object.keys(layouts).length ? { layout: layouts } : {} };
3948
+ variants.push(variant);
3949
+ spacersOf.set(variant, spacers);
3950
+ wrapsOf.set(variant, wraps);
3951
+ if (story2.shot) shotOf.set(variant, story2.shot);
3401
3952
  }
3402
- const problems = validateFigmaFixes(data);
3403
- if (problems.length) throw new FigmaFixesError(problems);
3404
- return data;
3953
+ textRootInFrame(parts, variants);
3954
+ spacerWrappers(parts, variants, spacersOf, wrapsOf);
3955
+ const properties = textProperties(parts, base.story, argTypes, new Set(axes.map((a2) => a2.name)));
3956
+ const kept = drawnAxes(axes, variants);
3957
+ const story = `${source.replace(/\/+$/, "")}/?path=/story/${base.story.id}`;
3958
+ const spec = { name, hash: "", source: [story], axes: kept.axes, properties, parts, variants: withoutIdleStates(kept.variants), unmapped: [] };
3959
+ const shots = {};
3960
+ kept.variants.forEach((v) => {
3961
+ const from = variants.find((x) => JSON.stringify(x.values) === JSON.stringify(v.values) && Object.entries(v.props).every(([k, val]) => x.props[k] === val));
3962
+ const shot = from && shotOf.get(from);
3963
+ if (shot) shots[variantKeyOf(v.props)] = shot;
3964
+ });
3965
+ return { spec: { ...spec, hash: hashSpec(spec) }, shots };
3405
3966
  }
3406
- const lc$1 = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
3407
- const setNameOf$2 = (title) => title.split("/").at(-1).trim();
3408
- const propsOf$1 = (s) => ({ ...s.props ?? {}, ...s.overrides ?? {} });
3409
- const sameArg = (a2, b) => a2 === b || a2 !== void 0 && b !== void 0 && String(a2).toLowerCase() === String(b).toLowerCase();
3410
- function storyMatchesWhere(s, where) {
3411
- if (!where) return true;
3412
- const p2 = propsOf$1(s);
3413
- if (where.args && !Object.entries(where.args).every(([k, v]) => sameArg(p2[k], v))) return false;
3414
- if (where.state && storyState(s) !== where.state) return false;
3415
- return true;
3967
+ const TEXT_FIELDS = /* @__PURE__ */ new Set(["textFill", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textDecoration"]);
3968
+ function textRootInFrame(parts, variants) {
3969
+ const root = parts.find((p2) => !p2.parent);
3970
+ if (!root || root.kind !== "text") return;
3971
+ const id = `${root.id}-text`;
3972
+ const text = { ...root, id, name: `${root.name} text`, parent: root.id };
3973
+ parts.splice(parts.indexOf(root), 1, { id: root.id, name: root.name, kind: "frame", ...root.source ? { source: root.source } : {}, layout: { direction: "HORIZONTAL", primary: "MIN", counter: "MIN" } }, text);
3974
+ for (const v of variants) {
3975
+ const all = v.values[root.id] ?? {};
3976
+ v.values[root.id] = Object.fromEntries(Object.entries(all).filter(([f, value]) => !TEXT_FIELDS.has(f) && !((f === "width" || f === "height") && typeof value === "number")));
3977
+ v.values[id] = Object.fromEntries(Object.entries(all).filter(([f]) => TEXT_FIELDS.has(f)));
3978
+ if (v.hidden?.includes(root.id)) v.hidden.push(id);
3979
+ }
3416
3980
  }
3417
- function partsNamed(spec, sel) {
3418
- if (sel.startsWith(".")) {
3419
- const cls = sel.slice(1);
3420
- return spec.parts.filter((p2) => (p2.source ?? "").split(/[\s,]+/).map((x) => x.replace(/^\./, "")).includes(cls));
3981
+ function ownAxes(part, parts, spec, memo = /* @__PURE__ */ new Map()) {
3982
+ const parent = parts.find((x) => x.id === part.parent);
3983
+ const dir = parent?.layout?.direction;
3984
+ const stretch = parent?.layout?.counter === "STRETCH";
3985
+ const sizedBy = (axis) => !parent || !spec || definiteAxis(parent, axis, parts, spec, memo);
3986
+ const acrossW = dir === "VERTICAL" && stretch && sizedBy("width");
3987
+ const alongW = dir === "HORIZONTAL" && !!part.grow && sizedBy("width");
3988
+ const acrossH = dir === "HORIZONTAL" && stretch && sizedBy("height");
3989
+ const alongH = dir === "VERTICAL" && !!part.grow && sizedBy("height");
3990
+ return {
3991
+ width: !(!part.parent && part.fill) && !acrossW && !alongW && !part.stretch?.width,
3992
+ height: !acrossH && !alongH && !part.stretch?.height
3993
+ };
3994
+ }
3995
+ function definiteAxis(part, axis, parts, spec, memo) {
3996
+ const key2 = `${part.id}:${axis}`;
3997
+ const known = memo.get(key2);
3998
+ if (known !== void 0) return known;
3999
+ memo.set(key2, false);
4000
+ let result;
4001
+ if (!part.parent) result = axis === "width" && !!part.fill;
4002
+ else if (spec.sized?.[part.id]?.[axis] !== void 0) result = true;
4003
+ else {
4004
+ const own = ownAxes(part, parts, spec, memo);
4005
+ const hasKids = parts.some((c) => c.parent === part.id && !c.pin && !c.cover && !c.overlay);
4006
+ result = !own[axis] || (hasKids ? boxSized(spec, part.id, own)[axis] !== void 0 : typeof spec.values[part.id]?.[axis] === "number");
3421
4007
  }
3422
- return spec.parts.filter((p2) => p2.id === sel);
4008
+ memo.set(key2, result);
4009
+ return result;
3423
4010
  }
3424
- const clone = (v) => JSON.parse(JSON.stringify(v));
3425
- function patchSpec(spec, fix) {
3426
- const targets = partsNamed(spec, fix.part);
3427
- if (!targets.length) return false;
3428
- for (const part of targets) {
3429
- const id = part.id;
3430
- const box = spec.boxes[id];
3431
- const parent = part.parent ? spec.parts.find((p2) => p2.id === part.parent) : void 0;
3432
- if (fix.hide) {
3433
- const gone = /* @__PURE__ */ new Set([id]);
3434
- for (const p2 of spec.parts) if (p2.parent && gone.has(p2.parent)) gone.add(p2.id);
3435
- spec.parts = spec.parts.filter((p2) => !gone.has(p2.id));
3436
- for (const map of [spec.values, spec.raw, spec.tokens, spec.boxes, spec.sized ?? {}, spec.spacers ?? {}, spec.vectors ?? {}]) for (const g of gone) delete map[g];
3437
- continue;
3438
- }
3439
- const values = spec.values[id] ??= {};
3440
- for (const [field, value] of Object.entries(fix.set ?? {})) {
3441
- values[field] = value === "full" ? box ? Math.min(box.width, box.height) / 2 : 9999 : value;
3442
- }
3443
- for (const side of ["width", "height"]) {
3444
- const how = fix[side];
3445
- if (how === void 0) continue;
3446
- const flowsAlong = parent?.layout?.direction === (side === "width" ? "HORIZONTAL" : "VERTICAL");
3447
- if (how === "hug") {
3448
- delete values[side];
3449
- if (spec.sized?.[id]) delete spec.sized[id][side];
3450
- if (part.stretch) delete part.stretch[side];
3451
- if (flowsAlong) delete part.grow;
3452
- if (!part.parent && side === "width") delete part.fill;
3453
- } else if (how === "fill") {
3454
- if (spec.sized?.[id]) delete spec.sized[id][side];
3455
- if (!part.parent) {
3456
- if (side === "width") part.fill = true;
3457
- } else if (flowsAlong) part.grow = true;
3458
- else part.stretch = { ...part.stretch ?? {}, [side]: true };
3459
- } else {
3460
- const px = how === "fixed" ? typeof values[side] === "number" ? values[side] : box?.[side] : how;
3461
- if (px === void 0) continue;
3462
- values[side] = px;
3463
- spec.sized = { ...spec.sized ?? {}, [id]: { ...spec.sized?.[id] ?? {}, [side]: px } };
3464
- if (part.stretch) delete part.stretch[side];
3465
- if (flowsAlong) delete part.grow;
3466
- if (!part.parent && side === "width") delete part.fill;
3467
- }
4011
+ function pinFromBoxes(spec, part) {
4012
+ const box = spec.boxes?.[part.id];
4013
+ const parent = part.parent ? spec.boxes?.[part.parent] : void 0;
4014
+ if (!box || !parent) return void 0;
4015
+ return { top: Math.round((box.y - parent.y) * 100) / 100, left: Math.round((box.x - parent.x) * 100) / 100 };
4016
+ }
4017
+ function alignedLayout(part, parts) {
4018
+ const layout = part.layout;
4019
+ if (!layout?.align) return layout;
4020
+ const { align, ...own } = layout;
4021
+ const flow = parts.filter((c) => c.parent === part.id && !c.pin && !c.cover && !c.overlay);
4022
+ const a2 = flow.length === 1 ? align[flow[0].id] : void 0;
4023
+ return a2 ? { ...own, primary: a2.primary ?? own.primary, counter: a2.counter ?? own.counter } : own;
4024
+ }
4025
+ function spacerWrappers(parts, variants, spacersOf, wrapsOf = /* @__PURE__ */ new Map()) {
4026
+ const wrapped = parts.filter((p2) => p2.parent && !p2.pin && !p2.cover && variants.some((v) => spacersOf.get(v)?.[p2.id] || wrapsOf.get(v)?.[p2.id]));
4027
+ for (const p2 of wrapped) {
4028
+ const parent = parts.find((x) => x.id === p2.parent);
4029
+ const row = parent?.layout?.direction === "HORIZONTAL";
4030
+ const id = `${p2.id}-wrapper`;
4031
+ const wrapper = { id, name: `${p2.name} wrapper`, kind: "frame", parent: p2.parent, layout: { direction: row ? "HORIZONTAL" : "VERTICAL", primary: "MIN", counter: "STRETCH" }, ...p2.grow ? { grow: p2.grow } : {} };
4032
+ parts.splice(parts.indexOf(p2), 0, wrapper);
4033
+ p2.parent = id;
4034
+ for (const v of variants) {
4035
+ const pad = wrapsOf.get(v)?.[p2.id];
4036
+ const space = spacersOf.get(v)?.[p2.id] ?? 0;
4037
+ const values = {};
4038
+ if (pad?.top) values.paddingTop = pad.top;
4039
+ if (pad?.right) values.paddingRight = pad.right;
4040
+ if (pad?.bottom) values.paddingBottom = pad.bottom;
4041
+ if (pad?.left) values.paddingLeft = pad.left;
4042
+ if (space) values[row ? "paddingLeft" : "paddingTop"] = (values[row ? "paddingLeft" : "paddingTop"] ?? 0) + space;
4043
+ v.values[id] = values;
4044
+ if (v.hidden?.includes(p2.id)) v.hidden.push(id);
3468
4045
  }
3469
- if (fix.direction || fix.align) {
3470
- part.layout = { direction: "HORIZONTAL", primary: "MIN", counter: "MIN", ...part.layout ?? {} };
3471
- if (fix.direction) part.layout.direction = fix.direction;
3472
- if (fix.align?.primary) part.layout.primary = fix.align.primary;
3473
- if (fix.align?.counter) part.layout.counter = fix.align.counter;
4046
+ }
4047
+ }
4048
+ function textProperties(parts, story, argTypes, axes) {
4049
+ const properties = [];
4050
+ const props = propsOf$1(story);
4051
+ for (const [k, a2] of Object.entries(argTypes)) {
4052
+ const value = props[k];
4053
+ if (a2.kind !== "text" || NOT_AN_AXIS.test(k) || typeof value !== "string" || !value.trim()) continue;
4054
+ const part = parts.find((p2) => p2.kind === "text" && !p2.text && p2.characters?.trim() === value.trim());
4055
+ const name = axisName(k);
4056
+ if (!part || axes.has(name) || properties.some((x) => x.name === name)) continue;
4057
+ part.text = name;
4058
+ properties.push({ name, type: "TEXT", default: value, prop: k });
4059
+ }
4060
+ return properties;
4061
+ }
4062
+ function withoutIdleStates(variants) {
4063
+ const look2 = (v) => JSON.stringify([v.values, v.hidden ?? [], v.vectors ?? {}, v.pins ?? {}]);
4064
+ const rest = (v) => JSON.stringify(Object.entries(v.props).filter(([k]) => k !== "State"));
4065
+ const resting = new Map(variants.filter((v) => v.props.State === "Default").map((v) => [rest(v), look2(v)]));
4066
+ return variants.filter((v) => !["Hover", "Pressed", "Focus"].includes(v.props.State ?? "") || resting.get(rest(v)) !== look2(v));
4067
+ }
4068
+ function drawnAxes(axes, variants) {
4069
+ const look2 = (v) => JSON.stringify([v.values, v.hidden ?? [], v.vectors ?? {}, v.pins ?? {}]);
4070
+ let current = { axes, variants };
4071
+ for (const axis of axes) {
4072
+ const others = (v) => JSON.stringify(current.axes.filter((a2) => a2 !== axis).map((a2) => v.props[a2.name]));
4073
+ const twin = /* @__PURE__ */ new Map();
4074
+ for (const v of current.variants) if (v.props[axis.name] === axis.default) twin.set(others(v), v);
4075
+ const hoverTwin = /* @__PURE__ */ new Map();
4076
+ if (axis.name === "State") {
4077
+ for (const v of current.variants) if (v.props.State === "Hover") hoverTwin.set(others(v), v);
3474
4078
  }
3475
- if (fix.overlay !== void 0) {
3476
- delete part.cover;
3477
- delete part.pin;
3478
- if (fix.overlay === true) part.cover = true;
3479
- else if (fix.overlay !== false) part.pin = { ...fix.overlay };
4079
+ const stand = (v) => current.variants.filter((x) => x.props[axis.name] !== v.props[axis.name] && others(x) === others(v));
4080
+ const differs = (v, from) => from.has(others(v)) ? look2(from.get(others(v))) !== look2(v) : from !== twin || stand(v).every((x) => look2(x) !== look2(v));
4081
+ const drawn = axis.values.filter(
4082
+ (value) => value === axis.default || current.variants.some((v) => v.props[axis.name] === value && differs(v, twin) && (!["Pressed", "Focus"].includes(value) || !hoverTwin.size || differs(v, hoverTwin)))
4083
+ );
4084
+ const values = axis.values.filter((value) => drawn.includes(value));
4085
+ const variants2 = current.variants.filter((v) => values.includes(v.props[axis.name]));
4086
+ const covered = new Set(variants2.filter((v) => v.props[axis.name] === axis.default).map(others));
4087
+ for (const v of current.variants) {
4088
+ if (values.includes(v.props[axis.name]) || covered.has(others(v))) continue;
4089
+ covered.add(others(v));
4090
+ variants2.push({ ...v, props: { ...v.props, [axis.name]: axis.default } });
3480
4091
  }
4092
+ if (values.length > 1) current = { axes: current.axes.map((a2) => a2 === axis ? { ...a2, values } : a2), variants: variants2 };
4093
+ else
4094
+ current = {
4095
+ axes: current.axes.filter((a2) => a2 !== axis),
4096
+ variants: variants2.map((v) => ({ ...v, props: Object.fromEntries(Object.entries(v.props).filter(([k]) => k !== axis.name)) }))
4097
+ };
3481
4098
  }
3482
- return true;
4099
+ return current;
3483
4100
  }
3484
- function applyFixes(measured, fixes) {
3485
- const list = Array.isArray(fixes) ? fixes : fixes.fixes;
3486
- const file = clone(measured);
3487
- const before = new Set((file.figmaFixes?.applied ?? []).filter((a2) => a2.result === "applied").map((a2) => a2.id));
3488
- const applied = [];
3489
- const entryOf = (component) => (file.components ?? []).find((c) => lc$1(setNameOf$2(c.title)) === lc$1(component));
3490
- const hints = (component) => {
3491
- const c = entryOf(component);
3492
- return c ? c.figma ??= {} : void 0;
3493
- };
3494
- const mark = (s, id) => {
3495
- s.fixes = [.../* @__PURE__ */ new Set([...s.fixes ?? [], id])];
3496
- };
3497
- for (const fix of list) {
3498
- const record = (result, stories, detail) => applied.push({ id: fix.id, kind: fix.kind, component: fix.component, result, stories, detail });
3499
- if (before.has(fix.id)) {
3500
- record("already", 0, "already applied to this file");
3501
- continue;
3502
- }
3503
- const mine = () => file.stories.filter((s) => lc$1(setNameOf$2(s.title)) === lc$1(fix.component));
3504
- if (!mine().length) {
3505
- record("not-found", 0, `no measured story of ${fix.component}`);
4101
+ function specsFromMeasure(file, opts = {}) {
4102
+ const known = opts.known ?? (() => true);
4103
+ const key2 = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
4104
+ const only = opts.only === void 0 ? void 0 : key2(opts.only);
4105
+ const groups = /* @__PURE__ */ new Map();
4106
+ for (const s of file.stories) {
4107
+ if (s.error || !s.spec) continue;
4108
+ const name = setNameOf$2(s.title);
4109
+ if (only !== void 0 && key2(name) !== only) continue;
4110
+ groups.set(name, [...groups.get(name) ?? [], s]);
4111
+ }
4112
+ const argTypesOf = (name) => Object.assign({}, ...(file.components ?? []).filter((c) => setNameOf$2(c.title) === name).map((c) => c.argTypes));
4113
+ const hints = {};
4114
+ for (const c of file.components ?? []) if (c.figma) hints[setNameOf$2(c.title)] = { ...hints[setNameOf$2(c.title)], ...c.figma };
4115
+ const components = [];
4116
+ const shots = {};
4117
+ const report = { sets: 0, variants: 0, stories: 0, skipped: [] };
4118
+ for (const [name, stories] of [...groups].sort(([a2], [b]) => a2.localeCompare(b))) {
4119
+ const built = setOf(name, stories, argTypesOf(name), file.storybook, known, hints[name]);
4120
+ if (!built) {
4121
+ report.skipped.push(name);
3506
4122
  continue;
3507
4123
  }
3508
- const scoped = () => mine().filter((s) => storyMatchesWhere(s, fix.where));
3509
- if (fix.kind === "part") {
3510
- let n = 0;
3511
- for (const s of scoped()) {
3512
- let hit = false;
3513
- for (const spec of [s.spec, ...s.instances ?? []]) if (spec && patchSpec(spec, fix)) hit = true;
3514
- if (hit) {
3515
- n++;
3516
- mark(s, fix.id);
3517
- }
3518
- }
3519
- if (n) record("applied", n, `${describePartFix(fix)} on ${n} stor${n === 1 ? "y" : "ies"}`);
3520
- else record("not-found", 0, `no part ${fix.part} in the ${scoped().length ? "stories it names" : "variants where names"}`);
3521
- } else if (fix.kind === "axis") {
3522
- const h2 = hints(fix.component);
3523
- if (fix.action === "prune") {
3524
- const drop = new Set(scoped());
3525
- if (!drop.size) {
3526
- record("not-found", 0, "no measured variant matches where");
3527
- continue;
3528
- }
3529
- file.stories = file.stories.filter((s) => !drop.has(s));
3530
- if (h2) h2.pruned = [...h2.pruned ?? [], fix.where];
3531
- record("applied", drop.size, `dropped ${drop.size} variant stor${drop.size === 1 ? "y" : "ies"}`);
3532
- } else {
3533
- const arg = fix.arg;
3534
- const entry = entryOf(fix.component);
3535
- const known = !!entry?.argTypes[arg] || mine().some((s) => arg in propsOf$1(s));
3536
- if (!known) {
3537
- record("not-found", 0, `${fix.component} has no arg ${arg}`);
4124
+ const spec = built.spec;
4125
+ components.push(spec);
4126
+ if (Object.keys(built.shots).length) shots[name] = built.shots;
4127
+ report.sets++;
4128
+ report.variants += spec.variants.length;
4129
+ report.stories += stories.length;
4130
+ }
4131
+ return { specs: { version: 1, generated: `Measured from ${file.storybook} on ${file.measuredAt} (bridge specs --measure).`, components }, report, shots, hints };
4132
+ }
4133
+ const FIGMA_FIXES_VERSION = 1;
4134
+ const DEFAULT_FIGMA_FIXES_FILE = "figma-fixes.json";
4135
+ class FigmaFixesError extends Error {
4136
+ constructor(problems) {
4137
+ super(`figma-fixes.json: ${problems.join("; ")}`);
4138
+ this.problems = problems;
4139
+ }
4140
+ problems;
4141
+ }
4142
+ const isObj$3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
4143
+ const STATES = ["rest", "hover", "active", "focus-visible", "disabled"];
4144
+ const KINDS = ["part", "axis", "story", "status"];
4145
+ const BASE_KEYS = ["id", "component", "reason", "by", "where", "kind"];
4146
+ const KEYS = {
4147
+ part: ["part", "set", "width", "height", "direction", "align", "overlay", "hide"],
4148
+ axis: ["action", "arg", "property", "values"],
4149
+ story: ["use"],
4150
+ status: ["status"]
4151
+ };
4152
+ const PRIMARY = ["MIN", "CENTER", "MAX", "SPACE_BETWEEN"];
4153
+ const COUNTER = ["MIN", "CENTER", "MAX", "STRETCH"];
4154
+ const plain = (v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean";
4155
+ function whereProblems(w, at2) {
4156
+ if (!isObj$3(w)) return at2("where must be an object with args and/or state");
4157
+ for (const k of Object.keys(w)) if (k !== "args" && k !== "state") at2(`where.${k} is not known (args, state)`);
4158
+ if (w.args !== void 0 && (!isObj$3(w.args) || !Object.values(w.args).every(plain))) at2("where.args maps arg names to a text, number or true/false");
4159
+ if (w.state !== void 0 && !STATES.includes(w.state)) at2(`where.state is one of ${STATES.join(", ")}`);
4160
+ }
4161
+ function validateFigmaFixes(data) {
4162
+ const out = [];
4163
+ if (!isObj$3(data)) return ["expected an object with version and fixes"];
4164
+ if (data.version !== FIGMA_FIXES_VERSION) out.push(`version ${String(data.version)} is not supported (expected ${FIGMA_FIXES_VERSION})`);
4165
+ for (const k of Object.keys(data)) if (!["$schema", "version", "fixes"].includes(k)) out.push(`${k} is not known (version, fixes)`);
4166
+ if (!Array.isArray(data.fixes)) return [...out, "fixes must be a list"];
4167
+ const ids = /* @__PURE__ */ new Set();
4168
+ data.fixes.forEach((f, i) => {
4169
+ const label = isObj$3(f) && typeof f.id === "string" ? `fix ${f.id}` : `fix ${i + 1}`;
4170
+ const at2 = (why) => out.push(`${label}: ${why}`);
4171
+ if (!isObj$3(f)) return at2("must be an object");
4172
+ if (typeof f.id !== "string" || !/^[a-z0-9][a-z0-9-]{0,79}$/.test(f.id)) at2("id must be lower case letters, digits and dashes");
4173
+ else if (ids.has(f.id)) at2("id is repeated");
4174
+ else ids.add(f.id);
4175
+ if (typeof f.component !== "string" || !f.component.trim()) at2('component is missing (the Figma set name, like "Toggle")');
4176
+ if (typeof f.reason !== "string" || f.reason.trim().length < 8) at2("reason is missing: say why, in a sentence");
4177
+ else if (/\u2014/.test(f.reason)) at2("reason uses an em dash: use a colon or a comma");
4178
+ if (f.by !== void 0 && typeof f.by !== "string") at2("by must be a text");
4179
+ if (f.where !== void 0) whereProblems(f.where, at2);
4180
+ if (!KINDS.includes(f.kind)) return at2(`kind must be one of ${KINDS.join(", ")}`);
4181
+ const kind = f.kind;
4182
+ for (const k of Object.keys(f)) if (!BASE_KEYS.includes(k) && !KEYS[kind].includes(k)) at2(`${k} is not a key of a ${kind} fix`);
4183
+ if (kind === "part") partProblems(f, at2);
4184
+ if (kind === "axis") axisProblems(f, at2);
4185
+ if (kind === "story") {
4186
+ const u = f.use;
4187
+ if (!isObj$3(u) || typeof u.story !== "string" || !u.story) at2("use.story is the story id to draw the variant from");
4188
+ else {
4189
+ for (const k of Object.keys(u)) if (!["story", "args", "state"].includes(k)) at2(`use.${k} is not known (story, args, state)`);
4190
+ if (u.args !== void 0 && (!isObj$3(u.args) || !Object.values(u.args).every(plain))) at2("use.args maps arg names to a text, number or true/false");
4191
+ if (u.state !== void 0 && !STATES.includes(u.state)) at2(`use.state is one of ${STATES.join(", ")}`);
4192
+ }
4193
+ if (!isObj$3(f.where) || !f.where.args && !f.where.state) at2("where names the variant (args and/or state) the story is for");
4194
+ }
4195
+ if (kind === "status") {
4196
+ if (f.status === "matches") at2('status cannot be "matches": only the shot comparison says a component matches');
4197
+ else if (f.status !== void 0 && f.status !== "differs" && f.status !== "cant-check") at2("status is differs or cant-check (absent: a note)");
4198
+ }
4199
+ });
4200
+ return out;
4201
+ }
4202
+ function partProblems(f, at2) {
4203
+ if (typeof f.part !== "string" || !f.part.trim()) at2("part is a part id or a .class");
4204
+ const changes = ["set", "width", "height", "direction", "align", "overlay", "hide"].filter((k) => f[k] !== void 0);
4205
+ if (!changes.length) at2("changes nothing: give set, width, height, direction, align, overlay or hide");
4206
+ if (f.hide !== void 0 && typeof f.hide !== "boolean") at2("hide is true or false");
4207
+ if (f.hide === true && changes.length > 1) at2("hide goes alone: a hidden part has nothing else to change");
4208
+ if (f.set !== void 0) {
4209
+ if (!isObj$3(f.set) || !Object.keys(f.set).length) at2("set maps fields to values");
4210
+ else
4211
+ for (const [field, value] of Object.entries(f.set)) {
4212
+ if (!SPEC_FIELDS.includes(field)) {
4213
+ at2(`set.${field} is not a field (${SPEC_FIELDS.join(", ")})`);
3538
4214
  continue;
3539
4215
  }
3540
- if (fix.action === "map") {
3541
- if (h2) {
3542
- if (fix.property) h2.properties = { ...h2.properties ?? {}, [arg]: fix.property };
3543
- if (fix.values) h2.values = { ...h2.values ?? {}, [arg]: { ...h2.values?.[arg] ?? {}, ...fix.values } };
3544
- }
3545
- record("applied", 0, `${arg} drawn as ${fix.property ?? arg}${fix.values ? ` (${Object.entries(fix.values).map(([c, f]) => `${c} as ${f}`).join(", ")})` : ""}`);
3546
- } else {
3547
- const at2 = entry?.argTypes[arg];
3548
- const lead = mine().find((s) => !s.overrides || !(arg in s.overrides));
3549
- const baseline = at2?.default ?? (lead ? propsOf$1(lead)[arg] : void 0) ?? (at2?.kind === "boolean" ? false : void 0);
3550
- const drop = new Set(scoped().filter((s) => baseline !== void 0 && propsOf$1(s)[arg] !== void 0 && !sameArg(propsOf$1(s)[arg], baseline)));
3551
- file.stories = file.stories.filter((s) => !drop.has(s));
3552
- if (entry) {
3553
- delete entry.argTypes[arg];
3554
- entry.figma ??= {};
3555
- entry.figma.ignored = [.../* @__PURE__ */ new Set([...entry.figma.ignored ?? [], arg])];
3556
- }
3557
- record("applied", drop.size, `${arg} is not a Figma property${drop.size ? `: ${drop.size} stor${drop.size === 1 ? "y" : "ies"} at another value left out` : ""}`);
3558
- }
3559
- }
3560
- } else if (fix.kind === "story") {
3561
- const src = file.stories.find(
3562
- (s) => s.id === fix.use.story && s.spec && !s.error && (!fix.use.args || Object.entries(fix.use.args).every(([k, v]) => sameArg(propsOf$1(s)[k], v))) && (!fix.use.state || storyState(s) === fix.use.state)
3563
- );
3564
- if (!src) {
3565
- record("not-found", 0, `no measured render of story ${fix.use.story}${fix.use.args ? ` with ${JSON.stringify(fix.use.args)}` : ""}`);
3566
- continue;
3567
- }
3568
- const target = fix.where ?? {};
3569
- const others = new Set(scoped().filter((s) => s !== src));
3570
- const copy = clone(src);
3571
- const overrides = { ...copy.overrides ?? {}, ...target.args ?? {} };
3572
- if (target.state === "disabled") overrides.disabled = true;
3573
- copy.overrides = overrides;
3574
- if (target.state && target.state !== "rest" && target.state !== "disabled") copy.state = target.state;
3575
- else if (target.state) delete copy.state;
3576
- if (lc$1(setNameOf$2(copy.title)) !== lc$1(fix.component)) copy.title = copy.title.split("/").slice(0, -1).concat(fix.component).join("/");
3577
- mark(copy, fix.id);
3578
- let placed = false;
3579
- file.stories = file.stories.flatMap((s) => others.has(s) ? placed ? [] : (placed = true, [copy]) : [s]);
3580
- if (!placed) file.stories.push(copy);
3581
- record("applied", others.size + 1, `variant ${whereWords(target)} drawn from ${src.id}${others.size ? `, in place of ${others.size} other stor${others.size === 1 ? "y" : "ies"}` : ""}`);
3582
- } else {
3583
- const h2 = hints(fix.component);
3584
- if (!h2) {
3585
- record("not-found", 0, `no component entry for ${fix.component} in the measure`);
3586
- continue;
4216
+ const color = COLOR_FIELDS.has(field);
4217
+ if (value === "full") {
4218
+ if (field !== "cornerRadius") at2(`set.${field}: "full" is for cornerRadius only`);
4219
+ } else if (value === null) {
4220
+ if (!color) at2(`set.${field} cannot be none (only colors can)`);
4221
+ } else if (typeof value === "number") {
4222
+ if (color) at2(`set.${field} is a color: a token path or null, not a number`);
4223
+ else if (!Number.isFinite(value)) at2(`set.${field} must be a finite number`);
4224
+ } else if (typeof value !== "string" || !value.trim()) at2(`set.${field} must be a token path, a number or null`);
4225
+ else if (field === "textDecoration" && !/^(UNDERLINE|STRIKETHROUGH|NONE)$/.test(value)) at2(`set.textDecoration is UNDERLINE, STRIKETHROUGH or NONE`);
3587
4226
  }
3588
- if (fix.status) {
3589
- h2.status = [...h2.status ?? [], { status: fix.status, reason: fix.reason, fix: fix.id, ...fix.where ? { where: fix.where } : {} }];
3590
- if (!fix.where) {
3591
- const st = (file.statuses ?? []).find((x) => lc$1(x.name) === lc$1(fix.component));
3592
- if (st && st.status !== fix.status) {
3593
- st.status = fix.status;
3594
- delete st.reason;
3595
- st.summary = fix.reason;
3596
- }
3597
- }
3598
- } else h2.notes = [...h2.notes ?? [], { text: fix.reason, fix: fix.id, ...fix.where ? { where: fix.where } : {} }];
3599
- record("applied", 0, fix.status ? `${fix.status === "differs" ? "Differs" : "Can't check"}: ${fix.reason}` : `note: ${fix.reason}`);
4227
+ }
4228
+ for (const side of ["width", "height"]) {
4229
+ const v = f[side];
4230
+ if (v !== void 0 && !["hug", "fill", "fixed"].includes(v) && !(typeof v === "number" && v > 0)) at2(`${side} is hug, fill, fixed or a size in px`);
4231
+ }
4232
+ if (f.direction !== void 0 && f.direction !== "HORIZONTAL" && f.direction !== "VERTICAL") at2("direction is HORIZONTAL or VERTICAL");
4233
+ if (f.align !== void 0) {
4234
+ const a2 = f.align;
4235
+ if (!isObj$3(a2) || a2.primary === void 0 && a2.counter === void 0) at2("align gives primary and/or counter");
4236
+ else {
4237
+ if (a2.primary !== void 0 && !PRIMARY.includes(a2.primary)) at2(`align.primary is ${PRIMARY.join(", ")}`);
4238
+ if (a2.counter !== void 0 && !COUNTER.includes(a2.counter)) at2(`align.counter is ${COUNTER.join(", ")}`);
3600
4239
  }
3601
- const h = hints(fix.component);
3602
- if (h && applied.at(-1).result === "applied") h.fixes = [.../* @__PURE__ */ new Set([...h.fixes ?? [], fix.id])];
3603
4240
  }
3604
- file.figmaFixes = { applied: [...(file.figmaFixes?.applied ?? []).filter((a2) => a2.result === "applied"), ...applied.filter((a2) => a2.result !== "already")] };
3605
- return { file, applied };
3606
- }
3607
- function whereWords(w) {
3608
- const args = Object.entries(w.args ?? {}).map(([k, v]) => `${k}=${String(v)}`);
3609
- if (w.state) args.push(`state=${w.state}`);
3610
- return args.join(", ") || "every variant";
4241
+ if (f.overlay !== void 0 && typeof f.overlay !== "boolean") {
4242
+ const o = f.overlay;
4243
+ if (!isObj$3(o) || !Object.keys(o).length || !Object.entries(o).every(([k, v]) => ["top", "right", "bottom", "left"].includes(k) && typeof v === "number")) at2('overlay is true, false, or offsets in px ({ "top": 3, "left": 3 })');
4244
+ }
4245
+ if (f.part === "root" && (f.hide === true || f.overlay !== void 0)) at2("the root cannot be hidden or overlaid");
3611
4246
  }
3612
- function describePartFix(f) {
3613
- if (f.hide) return `${f.part} hidden`;
3614
- const bits = [
3615
- ...Object.entries(f.set ?? {}).map(([k, v]) => `${k} ${v === null ? "none" : String(v)}`),
3616
- ...f.width !== void 0 ? [`width ${String(f.width)}`] : [],
3617
- ...f.height !== void 0 ? [`height ${String(f.height)}`] : [],
3618
- ...f.direction ? [`direction ${f.direction}`] : [],
3619
- ...f.align ? [`align ${[f.align.primary, f.align.counter].filter(Boolean).join(" ")}`] : [],
3620
- ...f.overlay !== void 0 ? [`overlay ${typeof f.overlay === "boolean" ? String(f.overlay) : JSON.stringify(f.overlay)}`] : []
3621
- ];
3622
- return `${f.part}: ${bits.join(", ")}`;
4247
+ function axisProblems(f, at2) {
4248
+ const action = f.action;
4249
+ if (action !== "map" && action !== "ignore" && action !== "prune") return at2("action is map, ignore or prune");
4250
+ if (action !== "prune" && (typeof f.arg !== "string" || !f.arg)) at2(`${action} needs arg (the story arg, like "kind")`);
4251
+ if (action === "map") {
4252
+ if (f.property === void 0 && f.values === void 0) at2("map gives property (the Figma name) and/or values");
4253
+ if (f.property !== void 0 && (typeof f.property !== "string" || !f.property.trim())) at2("property is the Figma property name");
4254
+ if (f.values !== void 0 && (!isObj$3(f.values) || !Object.values(f.values).every((v) => typeof v === "string" && v.trim()))) at2("values maps code values to Figma values (texts)");
4255
+ else if (isObj$3(f.values)) {
4256
+ const figma = Object.values(f.values);
4257
+ if (new Set(figma).size !== figma.length) at2("values gives two code values the same Figma value: use prune for the one to drop");
4258
+ }
4259
+ }
4260
+ if (action === "ignore" && (f.property !== void 0 || f.values !== void 0)) at2("ignore takes no property or values");
4261
+ if (action === "prune") {
4262
+ if (!isObj$3(f.where) || !f.where.args && !f.where.state) at2("prune needs where (the args and/or state of the variants to drop)");
4263
+ if (f.property !== void 0 || f.values !== void 0) at2("prune takes no property or values");
4264
+ }
3623
4265
  }
3624
- function appliedFixLines(applied) {
3625
- return applied.map((a2) => `${a2.result === "applied" ? "applied" : a2.result === "already" ? "already applied" : "not found"} ${a2.id} (${a2.component}, ${a2.kind}): ${a2.detail}`);
4266
+ function parseFigmaFixes(input) {
4267
+ let data = input;
4268
+ if (typeof input === "string") {
4269
+ try {
4270
+ data = JSON.parse(input);
4271
+ } catch (e) {
4272
+ throw new FigmaFixesError([`not JSON: ${e.message}`]);
4273
+ }
4274
+ }
4275
+ const problems = validateFigmaFixes(data);
4276
+ if (problems.length) throw new FigmaFixesError(problems);
4277
+ return data;
3626
4278
  }
3627
- const NOT_AN_AXIS = /^(as|asChild|render|component|className|class|style|id|key|ref|role|type|name|form|dir|lang|tabIndex|slot|children|htmlFor|value|defaultValue|placeholder|href|target|rel|src|alt)$/i;
3628
- const STATE_PROPS = /* @__PURE__ */ new Set(["disabled", "checked", "indeterminate"]);
3629
- const STATES = [
3630
- { value: "Default", of: (s) => !s.state && s.props?.disabled !== true },
3631
- { value: "Hover", of: (s) => s.state === "hover" && s.props?.disabled !== true },
3632
- { value: "Pressed", of: (s) => s.state === "active" && s.props?.disabled !== true },
3633
- { value: "Focus", of: (s) => s.state === "focus-visible" && s.props?.disabled !== true },
3634
- { value: "Disabled", of: (s) => s.props?.disabled === true }
3635
- ];
4279
+ const lc$1 = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
3636
4280
  const setNameOf$1 = (title) => title.split("/").at(-1).trim();
3637
- const axisName = (prop) => prop.charAt(0).toUpperCase() + prop.slice(1).replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase();
3638
4281
  const propsOf = (s) => ({ ...s.props ?? {}, ...s.overrides ?? {} });
3639
- function checkedOf(s) {
4282
+ const sameArg = (a2, b) => a2 === b || a2 !== void 0 && b !== void 0 && String(a2).toLowerCase() === String(b).toLowerCase();
4283
+ function storyMatchesWhere(s, where) {
4284
+ if (!where) return true;
3640
4285
  const p2 = propsOf(s);
3641
- return p2.indeterminate === true ? "mixed" : String(p2.checked === true);
4286
+ if (where.args && !Object.entries(where.args).every(([k, v]) => sameArg(p2[k], v))) return false;
4287
+ if (where.state && storyState(s) !== where.state) return false;
4288
+ return true;
3642
4289
  }
3643
- const LITERAL_FIELDS = ["fill", "textFill", "stroke", "fontFamily", "shadow"];
3644
- function valuesOf(spec, partId, hasChildren, known, fills = false, own = { width: true, height: true }) {
3645
- const sized = { ...hasChildren ? boxSized(spec, partId, own) : {}, ...spec.sized?.[partId] };
3646
- const v = { ...spec.values[partId] ?? {} };
3647
- const raw = spec.raw[partId] ?? {};
3648
- for (const field of Object.keys(v)) {
3649
- const value = v[field];
3650
- if (typeof value !== "string" || value.startsWith("calc(") || known(value)) continue;
3651
- const literal = raw[field];
3652
- if (literal === void 0) delete v[field];
3653
- else v[field] = literal;
3654
- }
3655
- for (const field of LITERAL_FIELDS) if (v[field] === void 0 && typeof raw[field] === "string") v[field] = raw[field];
3656
- if (hasChildren) {
3657
- if (typeof v.width === "number" && !fills) delete v.width;
3658
- if (typeof v.height === "number") delete v.height;
4290
+ function partsNamed(spec, sel) {
4291
+ if (sel.startsWith(".")) {
4292
+ const cls = sel.slice(1);
4293
+ return spec.parts.filter((p2) => (p2.source ?? "").split(/[\s,]+/).map((x) => x.replace(/^\./, "")).includes(cls));
3659
4294
  }
3660
- if (sized?.width !== void 0) v.width = sized.width;
3661
- if (sized?.height !== void 0) v.height = sized.height;
3662
- return v;
4295
+ return spec.parts.filter((p2) => p2.id === sel);
3663
4296
  }
3664
- function boxSized(spec, partId, own) {
3665
- const box = spec.boxes?.[partId];
3666
- const kids = spec.parts.filter((c) => c.parent === partId && !c.pin && !c.cover && !c.overlay).map((c) => spec.boxes?.[c.id]).filter((b) => !!b);
3667
- if (!box || !kids.length) return {};
3668
- const raw = spec.raw[partId] ?? {};
3669
- const num = (f) => typeof raw[f] === "number" ? raw[f] : 0;
3670
- const left = Math.min(...kids.map((k) => k.x));
3671
- const top = Math.min(...kids.map((k) => k.y));
3672
- const side = (f) => typeof raw[f] === "number" ? raw[f] : num("strokeWeight");
3673
- const contentW = Math.max(...kids.map((k) => k.x + k.width)) - left + num("paddingLeft") + num("paddingRight") + side("strokeLeftWeight") + side("strokeRightWeight");
3674
- const contentH = Math.max(...kids.map((k) => k.y + k.height)) - top + num("paddingTop") + num("paddingBottom") + side("strokeTopWeight") + side("strokeBottomWeight");
3675
- const out = {};
3676
- if (own.width && box.width - contentW > 1) out.width = box.width;
3677
- if (own.height && box.height - contentH > 1) out.height = box.height;
3678
- return out;
4297
+ const clone = (v) => JSON.parse(JSON.stringify(v));
4298
+ function wordsIn(spec, id) {
4299
+ const kids = spec.parts.filter((p2) => p2.parent === id);
4300
+ return kids.some((k) => k.kind === "text" && !!k.characters?.trim() || wordsIn(spec, k.id));
4301
+ }
4302
+ function ownSize(spec, part, side) {
4303
+ const id = part.id;
4304
+ const box = spec.boxes[id];
4305
+ const value = spec.values[id]?.[side];
4306
+ const measured = typeof value === "number" ? value : box?.[side];
4307
+ const skip = (why) => ({ part: id, side, why, ...measured !== void 0 && why !== "unmeasured" ? { measured } : {} });
4308
+ if (!part.parent && side === "width" && part.fill) return skip("fills");
4309
+ const css = spec.sized?.[id]?.[side];
4310
+ if (typeof css === "number") return css;
4311
+ if (side === "width" && box && wordsIn(spec, id) && boxSized(spec, id, { width: true, height: true }).width === void 0) return skip("words");
4312
+ return measured ?? skip("unmeasured");
3679
4313
  }
3680
- function setOf(name, stories, argTypes, source, known, hints = {}) {
3681
- const propName = (prop) => hints.properties?.[prop] ?? axisName(prop);
3682
- const figmaValue = (prop, value) => hints.values?.[prop]?.[value] ?? value;
3683
- const lead = stories.find((s) => s.name === "Default" && !s.state) ?? stories[0];
3684
- const baseline = (k, a2) => (lead ? propsOf(lead)[k] : void 0) ?? a2.default ?? false;
3685
- const shown = stories.filter((s) => !/skeleton/i.test(s.name));
3686
- const ordered = [...shown.length ? shown : stories].sort((a2, b) => Number(!!a2.gallery) - Number(!!b.gallery));
3687
- const valueOf = (s, k, a2) => (propsOf(s)[k] ?? a2.default ?? false) === true;
3688
- const flags = Object.entries(argTypes).filter(
3689
- ([k, a2]) => a2.kind === "boolean" && !STATE_PROPS.has(k) && !NOT_AN_AXIS.test(k) && ordered.some((s) => valueOf(s, k, a2)) && ordered.some((s) => !valueOf(s, k, a2))
3690
- );
3691
- const plain2 = ordered.filter(
3692
- (s) => Object.entries(argTypes).every(
3693
- ([k, a2]) => a2.kind !== "boolean" || STATE_PROPS.has(k) || NOT_AN_AXIS.test(k) || flags.some(([f]) => f === k) || (propsOf(s)[k] ?? a2.default ?? false) === baseline(k, a2)
3694
- )
3695
- );
3696
- if (!plain2.length) return void 0;
3697
- const axes = [];
3698
- for (const [prop, a2] of flags) {
3699
- const def = String(baseline(prop, a2) === true);
3700
- axes.push({ name: propName(prop), prop, values: [figmaValue(prop, def), figmaValue(prop, def === "true" ? "false" : "true")], default: figmaValue(prop, def) });
3701
- }
3702
- for (const [prop, a2] of Object.entries(argTypes)) {
3703
- if (a2.kind !== "enum" || NOT_AN_AXIS.test(prop) || !a2.options || a2.options.length < 2 || a2.options.length > 12) continue;
3704
- const seen = new Set(plain2.map((s) => String(propsOf(s)[prop] ?? a2.default ?? a2.options[0])));
3705
- const values = a2.options.filter((o) => seen.has(o));
3706
- if (values.length < 2) continue;
3707
- const def = a2.default !== void 0 && values.includes(String(a2.default)) ? String(a2.default) : values[0];
3708
- axes.push({ name: propName(prop), prop, values: [...new Set(values.map((v) => figmaValue(prop, v)))], default: figmaValue(prop, def) });
3709
- }
3710
- if ("checked" in argTypes || plain2.some((s) => "checked" in propsOf(s))) {
3711
- const values = ["false", "true", "mixed"].filter((v) => plain2.some((s) => checkedOf(s) === v));
3712
- if (values.length > 1) axes.push({ name: "Checked", values, default: values[0] });
3713
- }
3714
- const states = STATES.filter((st) => plain2.some(st.of)).map((st) => st.value);
3715
- if (states.length > 1) axes.push({ name: "State", values: states, default: states[0] });
3716
- const keyOf = (s) => {
3717
- const props = {};
3718
- for (const axis of axes) {
3719
- const flag = flags.find(([k]) => k === axis.prop);
3720
- const value = axis.name === "State" ? STATES.find((st) => st.of(s))?.value : axis.name === "Checked" ? checkedOf(s) : flag ? figmaValue(flag[0], String(valueOf(s, flag[0], flag[1]))) : propsOf(s)[axis.prop] === void 0 ? axis.default : figmaValue(axis.prop, String(propsOf(s)[axis.prop]));
3721
- if (value === void 0 || !axis.values.includes(value)) return void 0;
3722
- props[axis.name] = value;
3723
- }
3724
- return props;
3725
- };
3726
- const candidates = /* @__PURE__ */ new Map();
3727
- for (const s of plain2) {
3728
- const props = keyOf(s);
3729
- if (!props) continue;
3730
- const k = JSON.stringify(props);
3731
- if (!candidates.has(k)) candidates.set(k, { props, stories: [] });
3732
- candidates.get(k).stories.push(s);
3733
- }
3734
- const storyFor = /* @__PURE__ */ new Map();
3735
- if (axes.some((a2) => a2.name === "State")) {
3736
- const rest = (p2) => JSON.stringify(Object.entries(p2).filter(([k]) => k !== "State"));
3737
- const groups = /* @__PURE__ */ new Map();
3738
- for (const c of candidates.values()) groups.set(rest(c.props), [...groups.get(rest(c.props)) ?? [], c]);
3739
- for (const [key2, group] of groups) {
3740
- const ids = [...new Set(group.flatMap((c) => c.stories.map((s) => s.id)))];
3741
- const score = (id) => group.filter((c) => c.stories.some((s) => s.id === id)).length;
3742
- storyFor.set(key2, ids.reduce((best, id) => score(id) > score(best) ? id : best, ids[0]));
4314
+ function patchSpec(spec, fix) {
4315
+ const skipped = [];
4316
+ const targets = partsNamed(spec, fix.part);
4317
+ if (!targets.length) return { hit: false, skipped };
4318
+ let laid = false;
4319
+ for (const part of targets) {
4320
+ const id = part.id;
4321
+ const box = spec.boxes[id];
4322
+ const parent = part.parent ? spec.parts.find((p2) => p2.id === part.parent) : void 0;
4323
+ if (fix.hide) {
4324
+ const gone = /* @__PURE__ */ new Set([id]);
4325
+ for (const p2 of spec.parts) if (p2.parent && gone.has(p2.parent)) gone.add(p2.id);
4326
+ spec.parts = spec.parts.filter((p2) => !gone.has(p2.id));
4327
+ for (const map of [spec.values, spec.raw, spec.tokens, spec.boxes, spec.sized ?? {}, spec.spacers ?? {}, spec.vectors ?? {}]) for (const g of gone) delete map[g];
4328
+ laid = true;
4329
+ continue;
3743
4330
  }
3744
- for (const c of candidates.values()) {
3745
- const same = c.stories.filter((s) => s.id === storyFor.get(rest(c.props)));
3746
- if (same.length) c.stories = same;
4331
+ const values = spec.values[id] ??= {};
4332
+ for (const [field, value] of Object.entries(fix.set ?? {})) {
4333
+ values[field] = value === "full" ? box ? Math.min(box.width, box.height) / 2 : 9999 : value;
4334
+ laid = true;
3747
4335
  }
3748
- }
3749
- const byVariant = new Map([...candidates].map(([k, c]) => [k, { props: c.props, story: c.stories[0] }]));
3750
- if (!byVariant.size) return void 0;
3751
- const isDefault = (p2) => axes.every((a2) => p2[a2.name] === a2.default);
3752
- const base = [...byVariant.values()].find((v) => isDefault(v.props)) ?? [...byVariant.values()][0];
3753
- const baseSpec = base.story.spec;
3754
- const parts = baseSpec.parts.map((p2) => baseSpec.vectors?.[p2.id] ? { ...p2, kind: "vector" } : { ...p2 });
3755
- for (const p2 of parts) {
3756
- if (!p2.overlay || p2.pin || p2.cover) continue;
3757
- const pin = pinFromBoxes(baseSpec, p2);
3758
- if (pin) p2.pin = pin;
3759
- else p2.cover = true;
3760
- }
3761
- const paints = (id) => {
3762
- const v = baseSpec.values[id] ?? {};
3763
- return [v.fill, v.stroke].some((x) => x !== void 0 && x !== null);
3764
- };
3765
- for (const p2 of [...parts].reverse()) {
3766
- if (!p2.parent || p2.kind !== "frame" || p2.pin || p2.cover || p2.overlay || paints(p2.id)) continue;
3767
- const kids = parts.filter((c) => c.parent === p2.id);
3768
- if (kids.length && kids.every((c) => c.overlay || c.pin || c.cover)) p2.cover = true;
3769
- }
3770
- const aligners = new Set(
3771
- parts.filter((p2) => {
3772
- const flow = parts.filter((c) => c.parent === p2.id && !c.pin && !c.cover && !c.overlay);
3773
- return flow.length === 1 && !!p2.layout?.align?.[flow[0].id];
3774
- }).map((p2) => p2.id)
3775
- );
3776
- for (const p2 of parts) if (p2.layout) p2.layout = alignedLayout(p2, parts);
3777
- const parents = new Set(parts.filter((p2) => !p2.pin && !p2.cover).map((p2) => p2.parent).filter(Boolean));
3778
- const variants = [];
3779
- const axesMemo = /* @__PURE__ */ new Map();
3780
- const shotOf = /* @__PURE__ */ new Map();
3781
- const spacersOf = /* @__PURE__ */ new Map();
3782
- const wrapsOf = /* @__PURE__ */ new Map();
3783
- for (const { props, story: story2 } of byVariant.values()) {
3784
- const m = story2.spec;
3785
- const pairs = story2 === base.story ? new Map(parts.map((p2) => [p2.id, p2.id])) : pairParts(baseSpec.parts, m);
3786
- const values = {};
3787
- const vectors = {};
3788
- const pins = {};
3789
- const hidden = [];
3790
- const spacers = {};
3791
- const wraps = {};
3792
- const layouts = {};
3793
- for (const p2 of parts) {
3794
- const mid = pairs.get(p2.id);
3795
- if (mid === void 0) {
3796
- hidden.push(p2.id);
4336
+ for (const side of ["width", "height"]) {
4337
+ const how = fix[side];
4338
+ if (how === void 0) continue;
4339
+ const flowsAlong = parent?.layout?.direction === (side === "width" ? "HORIZONTAL" : "VERTICAL");
4340
+ const px = how === "fixed" ? ownSize(spec, part, side) : how;
4341
+ if (typeof px === "object") {
4342
+ skipped.push(px);
3797
4343
  continue;
3798
4344
  }
3799
- values[p2.id] = valuesOf(m, mid, parents.has(p2.id) && !aligners.has(p2.id), known, !p2.parent && !!p2.fill, ownAxes(p2, parts, baseSpec, axesMemo));
3800
- if (m.spacers?.[mid]) spacers[p2.id] = m.spacers[mid];
3801
- if (p2.kind === "icon" || p2.kind === "vector") delete values[p2.id].fill;
3802
- const mp = m.parts.find((x) => x.id === mid);
3803
- const pin = mp?.pin ?? (mp?.overlay ? pinFromBoxes(m, mp) : void 0);
3804
- if (p2.pin && pin && JSON.stringify(pin) !== JSON.stringify(p2.pin)) pins[p2.id] = pin;
3805
- if (mp?.layout && p2.layout) {
3806
- const layout = alignedLayout({ ...mp, id: p2.id }, m.parts.map((x) => ({ ...x, parent: x.parent === mid ? p2.id : x.parent })));
3807
- if (layout && (layout.primary !== p2.layout.primary || layout.counter !== p2.layout.counter || layout.direction !== p2.layout.direction)) layouts[p2.id] = layout;
4345
+ laid = true;
4346
+ if (px === "hug") {
4347
+ delete values[side];
4348
+ if (spec.sized?.[id]) delete spec.sized[id][side];
4349
+ if (part.stretch) delete part.stretch[side];
4350
+ if (flowsAlong) delete part.grow;
4351
+ if (!part.parent && side === "width") delete part.fill;
4352
+ } else if (px === "fill") {
4353
+ if (spec.sized?.[id]) delete spec.sized[id][side];
4354
+ if (!part.parent) {
4355
+ if (side === "width") part.fill = true;
4356
+ } else if (flowsAlong) part.grow = true;
4357
+ else part.stretch = { ...part.stretch ?? {}, [side]: true };
4358
+ } else {
4359
+ values[side] = px;
4360
+ spec.sized = { ...spec.sized ?? {}, [id]: { ...spec.sized?.[id] ?? {}, [side]: px } };
4361
+ if (part.stretch) delete part.stretch[side];
4362
+ if (flowsAlong) delete part.grow;
4363
+ if (!part.parent && side === "width") delete part.fill;
3808
4364
  }
3809
- if (mp?.wrap) wraps[p2.id] = mp.wrap.padding;
3810
- const vec = p2.kind === "vector" ? m.vectors?.[mid] : void 0;
3811
- if (vec) vectors[p2.id] = { svg: vec.svg, strokes: vec.strokes.every((v) => typeof v !== "string" || known(v)) ? [...vec.strokes] : [] };
3812
4365
  }
3813
- const variant = { props, values, ...hidden.length ? { hidden } : {}, ...Object.keys(vectors).length ? { vectors } : {}, ...Object.keys(pins).length ? { pins } : {}, ...Object.keys(layouts).length ? { layout: layouts } : {} };
3814
- variants.push(variant);
3815
- spacersOf.set(variant, spacers);
3816
- wrapsOf.set(variant, wraps);
3817
- if (story2.shot) shotOf.set(variant, story2.shot);
3818
- }
3819
- textRootInFrame(parts, variants);
3820
- spacerWrappers(parts, variants, spacersOf, wrapsOf);
3821
- const properties = textProperties(parts, base.story, argTypes, new Set(axes.map((a2) => a2.name)));
3822
- const kept = drawnAxes(axes, variants);
3823
- const story = `${source.replace(/\/+$/, "")}/?path=/story/${base.story.id}`;
3824
- const spec = { name, hash: "", source: [story], axes: kept.axes, properties, parts, variants: withoutIdleStates(kept.variants), unmapped: [] };
3825
- const shots = {};
3826
- kept.variants.forEach((v) => {
3827
- const from = variants.find((x) => JSON.stringify(x.values) === JSON.stringify(v.values) && Object.entries(v.props).every(([k, val]) => x.props[k] === val));
3828
- const shot = from && shotOf.get(from);
3829
- if (shot) shots[variantKeyOf(v.props)] = shot;
3830
- });
3831
- return { spec: { ...spec, hash: hashSpec(spec) }, shots };
3832
- }
3833
- const TEXT_FIELDS = /* @__PURE__ */ new Set(["textFill", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textDecoration"]);
3834
- function textRootInFrame(parts, variants) {
3835
- const root = parts.find((p2) => !p2.parent);
3836
- if (!root || root.kind !== "text") return;
3837
- const id = `${root.id}-text`;
3838
- const text = { ...root, id, name: `${root.name} text`, parent: root.id };
3839
- parts.splice(parts.indexOf(root), 1, { id: root.id, name: root.name, kind: "frame", ...root.source ? { source: root.source } : {}, layout: { direction: "HORIZONTAL", primary: "MIN", counter: "MIN" } }, text);
3840
- for (const v of variants) {
3841
- const all = v.values[root.id] ?? {};
3842
- v.values[root.id] = Object.fromEntries(Object.entries(all).filter(([f, value]) => !TEXT_FIELDS.has(f) && !((f === "width" || f === "height") && typeof value === "number")));
3843
- v.values[id] = Object.fromEntries(Object.entries(all).filter(([f]) => TEXT_FIELDS.has(f)));
3844
- if (v.hidden?.includes(root.id)) v.hidden.push(id);
4366
+ if (fix.direction || fix.align) {
4367
+ laid = true;
4368
+ part.layout = { direction: "HORIZONTAL", primary: "MIN", counter: "MIN", ...part.layout ?? {} };
4369
+ if (fix.direction) part.layout.direction = fix.direction;
4370
+ if (fix.align?.primary) part.layout.primary = fix.align.primary;
4371
+ if (fix.align?.counter) part.layout.counter = fix.align.counter;
4372
+ }
4373
+ if (fix.overlay !== void 0) {
4374
+ laid = true;
4375
+ delete part.cover;
4376
+ delete part.pin;
4377
+ if (fix.overlay === true) part.cover = true;
4378
+ else if (fix.overlay !== false) part.pin = { ...fix.overlay };
4379
+ }
3845
4380
  }
4381
+ return { hit: laid, skipped };
3846
4382
  }
3847
- function ownAxes(part, parts, spec, memo = /* @__PURE__ */ new Map()) {
3848
- const parent = parts.find((x) => x.id === part.parent);
3849
- const dir = parent?.layout?.direction;
3850
- const stretch = parent?.layout?.counter === "STRETCH";
3851
- const sizedBy = (axis) => !parent || !spec || definiteAxis(parent, axis, parts, spec, memo);
3852
- const acrossW = dir === "VERTICAL" && stretch && sizedBy("width");
3853
- const alongW = dir === "HORIZONTAL" && !!part.grow && sizedBy("width");
3854
- const acrossH = dir === "HORIZONTAL" && stretch && sizedBy("height");
3855
- const alongH = dir === "VERTICAL" && !!part.grow && sizedBy("height");
3856
- return {
3857
- width: !(!part.parent && part.fill) && !acrossW && !alongW && !part.stretch?.width,
3858
- height: !acrossH && !alongH && !part.stretch?.height
4383
+ function applyFixes(measured, fixes) {
4384
+ const list = Array.isArray(fixes) ? fixes : fixes.fixes;
4385
+ const file = clone(measured);
4386
+ const before = new Set((file.figmaFixes?.applied ?? []).filter((a2) => a2.result === "applied").map((a2) => a2.id));
4387
+ const applied = [];
4388
+ const entryOf = (component) => (file.components ?? []).find((c) => lc$1(setNameOf$1(c.title)) === lc$1(component));
4389
+ const hints = (component) => {
4390
+ const c = entryOf(component);
4391
+ return c ? c.figma ??= {} : void 0;
3859
4392
  };
3860
- }
3861
- function definiteAxis(part, axis, parts, spec, memo) {
3862
- const key2 = `${part.id}:${axis}`;
3863
- const known = memo.get(key2);
3864
- if (known !== void 0) return known;
3865
- memo.set(key2, false);
3866
- let result;
3867
- if (!part.parent) result = axis === "width" && !!part.fill;
3868
- else if (spec.sized?.[part.id]?.[axis] !== void 0) result = true;
3869
- else {
3870
- const own = ownAxes(part, parts, spec, memo);
3871
- const hasKids = parts.some((c) => c.parent === part.id && !c.pin && !c.cover && !c.overlay);
3872
- result = !own[axis] || (hasKids ? boxSized(spec, part.id, own)[axis] !== void 0 : typeof spec.values[part.id]?.[axis] === "number");
3873
- }
3874
- memo.set(key2, result);
3875
- return result;
3876
- }
3877
- function pinFromBoxes(spec, part) {
3878
- const box = spec.boxes?.[part.id];
3879
- const parent = part.parent ? spec.boxes?.[part.parent] : void 0;
3880
- if (!box || !parent) return void 0;
3881
- return { top: Math.round((box.y - parent.y) * 100) / 100, left: Math.round((box.x - parent.x) * 100) / 100 };
3882
- }
3883
- function alignedLayout(part, parts) {
3884
- const layout = part.layout;
3885
- if (!layout?.align) return layout;
3886
- const { align, ...own } = layout;
3887
- const flow = parts.filter((c) => c.parent === part.id && !c.pin && !c.cover && !c.overlay);
3888
- const a2 = flow.length === 1 ? align[flow[0].id] : void 0;
3889
- return a2 ? { ...own, primary: a2.primary ?? own.primary, counter: a2.counter ?? own.counter } : own;
3890
- }
3891
- function spacerWrappers(parts, variants, spacersOf, wrapsOf = /* @__PURE__ */ new Map()) {
3892
- const wrapped = parts.filter((p2) => p2.parent && !p2.pin && !p2.cover && variants.some((v) => spacersOf.get(v)?.[p2.id] || wrapsOf.get(v)?.[p2.id]));
3893
- for (const p2 of wrapped) {
3894
- const parent = parts.find((x) => x.id === p2.parent);
3895
- const row = parent?.layout?.direction === "HORIZONTAL";
3896
- const id = `${p2.id}-wrapper`;
3897
- const wrapper = { id, name: `${p2.name} wrapper`, kind: "frame", parent: p2.parent, layout: { direction: row ? "HORIZONTAL" : "VERTICAL", primary: "MIN", counter: "STRETCH" }, ...p2.grow ? { grow: p2.grow } : {} };
3898
- parts.splice(parts.indexOf(p2), 0, wrapper);
3899
- p2.parent = id;
3900
- for (const v of variants) {
3901
- const pad = wrapsOf.get(v)?.[p2.id];
3902
- const space = spacersOf.get(v)?.[p2.id] ?? 0;
3903
- const values = {};
3904
- if (pad?.top) values.paddingTop = pad.top;
3905
- if (pad?.right) values.paddingRight = pad.right;
3906
- if (pad?.bottom) values.paddingBottom = pad.bottom;
3907
- if (pad?.left) values.paddingLeft = pad.left;
3908
- if (space) values[row ? "paddingLeft" : "paddingTop"] = (values[row ? "paddingLeft" : "paddingTop"] ?? 0) + space;
3909
- v.values[id] = values;
3910
- if (v.hidden?.includes(p2.id)) v.hidden.push(id);
4393
+ const mark = (s, id) => {
4394
+ s.fixes = [.../* @__PURE__ */ new Set([...s.fixes ?? [], id])];
4395
+ };
4396
+ for (const fix of list) {
4397
+ const record = (result, stories, detail) => applied.push({ id: fix.id, kind: fix.kind, component: fix.component, result, stories, detail });
4398
+ if (before.has(fix.id)) {
4399
+ record("already", 0, "already applied to this file");
4400
+ continue;
4401
+ }
4402
+ const mine = () => file.stories.filter((s) => lc$1(setNameOf$1(s.title)) === lc$1(fix.component));
4403
+ if (!mine().length) {
4404
+ record("not-found", 0, `no measured story of ${fix.component}`);
4405
+ continue;
4406
+ }
4407
+ const scoped = () => mine().filter((s) => storyMatchesWhere(s, fix.where));
4408
+ if (fix.kind === "part") {
4409
+ let n = 0;
4410
+ let matched = false;
4411
+ const unpinned = [];
4412
+ for (const s of scoped()) {
4413
+ let hit = false;
4414
+ for (const spec of [s.spec, ...s.instances ?? []]) {
4415
+ if (!spec) continue;
4416
+ const r = patchSpec(spec, fix);
4417
+ if (r.hit) hit = true;
4418
+ if (r.hit || r.skipped.length) matched = true;
4419
+ for (const skip of r.skipped) unpinned.push({ story: s, skip });
4420
+ }
4421
+ if (hit) {
4422
+ n++;
4423
+ mark(s, fix.id);
4424
+ }
4425
+ }
4426
+ const skippedWords = unpinned.length ? skippedSizeWords(unpinned) : "";
4427
+ if (n) record("applied", n, `${describePartFix(fix)} on ${n} stor${n === 1 ? "y" : "ies"}${skippedWords ? `; ${skippedWords}` : ""}`);
4428
+ else if (matched) record("skipped", 0, `nothing laid on: ${skippedWords}`);
4429
+ else record("not-found", 0, `no part ${fix.part} in the ${scoped().length ? "stories it names" : "variants where names"}`);
4430
+ } else if (fix.kind === "axis") {
4431
+ const h2 = hints(fix.component);
4432
+ if (fix.action === "prune") {
4433
+ const drop = new Set(scoped());
4434
+ if (!drop.size) {
4435
+ record("not-found", 0, "no measured variant matches where");
4436
+ continue;
4437
+ }
4438
+ file.stories = file.stories.filter((s) => !drop.has(s));
4439
+ if (h2) h2.pruned = [...h2.pruned ?? [], fix.where];
4440
+ record("applied", drop.size, `dropped ${drop.size} variant stor${drop.size === 1 ? "y" : "ies"}`);
4441
+ } else {
4442
+ const arg = fix.arg;
4443
+ const entry = entryOf(fix.component);
4444
+ const known = !!entry?.argTypes[arg] || mine().some((s) => arg in propsOf(s));
4445
+ if (!known) {
4446
+ record("not-found", 0, `${fix.component} has no arg ${arg}`);
4447
+ continue;
4448
+ }
4449
+ if (fix.action === "map") {
4450
+ if (h2) {
4451
+ if (fix.property) h2.properties = { ...h2.properties ?? {}, [arg]: fix.property };
4452
+ if (fix.values) h2.values = { ...h2.values ?? {}, [arg]: { ...h2.values?.[arg] ?? {}, ...fix.values } };
4453
+ }
4454
+ record("applied", 0, `${arg} drawn as ${fix.property ?? arg}${fix.values ? ` (${Object.entries(fix.values).map(([c, f]) => `${c} as ${f}`).join(", ")})` : ""}`);
4455
+ } else {
4456
+ const at2 = entry?.argTypes[arg];
4457
+ const lead = mine().find((s) => !s.overrides || !(arg in s.overrides));
4458
+ const baseline = at2?.default ?? (lead ? propsOf(lead)[arg] : void 0) ?? (at2?.kind === "boolean" ? false : void 0);
4459
+ const drop = new Set(scoped().filter((s) => baseline !== void 0 && propsOf(s)[arg] !== void 0 && !sameArg(propsOf(s)[arg], baseline)));
4460
+ file.stories = file.stories.filter((s) => !drop.has(s));
4461
+ if (entry) {
4462
+ delete entry.argTypes[arg];
4463
+ entry.figma ??= {};
4464
+ entry.figma.ignored = [.../* @__PURE__ */ new Set([...entry.figma.ignored ?? [], arg])];
4465
+ }
4466
+ record("applied", drop.size, `${arg} is not a Figma property${drop.size ? `: ${drop.size} stor${drop.size === 1 ? "y" : "ies"} at another value left out` : ""}`);
4467
+ }
4468
+ }
4469
+ } else if (fix.kind === "story") {
4470
+ const src = file.stories.find(
4471
+ (s) => s.id === fix.use.story && s.spec && !s.error && (!fix.use.args || Object.entries(fix.use.args).every(([k, v]) => sameArg(propsOf(s)[k], v))) && (!fix.use.state || storyState(s) === fix.use.state)
4472
+ );
4473
+ if (!src) {
4474
+ record("not-found", 0, `no measured render of story ${fix.use.story}${fix.use.args ? ` with ${JSON.stringify(fix.use.args)}` : ""}`);
4475
+ continue;
4476
+ }
4477
+ const target = fix.where ?? {};
4478
+ const others = new Set(scoped().filter((s) => s !== src));
4479
+ const copy = clone(src);
4480
+ const overrides = { ...copy.overrides ?? {}, ...target.args ?? {} };
4481
+ if (target.state === "disabled") overrides.disabled = true;
4482
+ copy.overrides = overrides;
4483
+ if (target.state && target.state !== "rest" && target.state !== "disabled") copy.state = target.state;
4484
+ else if (target.state) delete copy.state;
4485
+ if (lc$1(setNameOf$1(copy.title)) !== lc$1(fix.component)) copy.title = copy.title.split("/").slice(0, -1).concat(fix.component).join("/");
4486
+ mark(copy, fix.id);
4487
+ let placed = false;
4488
+ file.stories = file.stories.flatMap((s) => others.has(s) ? placed ? [] : (placed = true, [copy]) : [s]);
4489
+ if (!placed) file.stories.push(copy);
4490
+ record("applied", others.size + 1, `variant ${whereWords(target)} drawn from ${src.id}${others.size ? `, in place of ${others.size} other stor${others.size === 1 ? "y" : "ies"}` : ""}`);
4491
+ } else {
4492
+ const h2 = hints(fix.component);
4493
+ if (!h2) {
4494
+ record("not-found", 0, `no component entry for ${fix.component} in the measure`);
4495
+ continue;
4496
+ }
4497
+ if (fix.status) {
4498
+ h2.status = [...h2.status ?? [], { status: fix.status, reason: fix.reason, fix: fix.id, ...fix.where ? { where: fix.where } : {} }];
4499
+ if (!fix.where) {
4500
+ const st = (file.statuses ?? []).find((x) => lc$1(x.name) === lc$1(fix.component));
4501
+ if (st && st.status !== fix.status) {
4502
+ st.status = fix.status;
4503
+ delete st.reason;
4504
+ st.summary = fix.reason;
4505
+ }
4506
+ }
4507
+ } else h2.notes = [...h2.notes ?? [], { text: fix.reason, fix: fix.id, ...fix.where ? { where: fix.where } : {} }];
4508
+ record("applied", 0, fix.status ? `${fix.status === "differs" ? "Differs" : "Can't check"}: ${fix.reason}` : `note: ${fix.reason}`);
3911
4509
  }
4510
+ const h = hints(fix.component);
4511
+ if (h && applied.at(-1).result === "applied") h.fixes = [.../* @__PURE__ */ new Set([...h.fixes ?? [], fix.id])];
3912
4512
  }
4513
+ file.figmaFixes = { applied: [...(file.figmaFixes?.applied ?? []).filter((a2) => a2.result === "applied"), ...applied.filter((a2) => a2.result !== "already")] };
4514
+ return { file, applied };
3913
4515
  }
3914
- function textProperties(parts, story, argTypes, axes) {
3915
- const properties = [];
3916
- const props = propsOf(story);
3917
- for (const [k, a2] of Object.entries(argTypes)) {
3918
- const value = props[k];
3919
- if (a2.kind !== "text" || NOT_AN_AXIS.test(k) || typeof value !== "string" || !value.trim()) continue;
3920
- const part = parts.find((p2) => p2.kind === "text" && !p2.text && p2.characters?.trim() === value.trim());
3921
- const name = axisName(k);
3922
- if (!part || axes.has(name) || properties.some((x) => x.name === name)) continue;
3923
- part.text = name;
3924
- properties.push({ name, type: "TEXT", default: value, prop: k });
4516
+ const variantWords = (s) => {
4517
+ const p2 = propsOf(s);
4518
+ const args = Object.keys(s.overrides ?? {}).filter((k) => k !== "disabled").map((k) => `${k}=${String(p2[k])}`);
4519
+ const state = storyState(s);
4520
+ return `${s.id}${args.length || state !== "rest" ? ` (${[...args, ...state !== "rest" ? [state] : []].join(", ")})` : ""}`;
4521
+ };
4522
+ function skippedSizeWords(list) {
4523
+ const why = {
4524
+ words: "it hugs its text in code, so the measured px is that story's words, not a width of its own",
4525
+ fills: "it fills its container in code, so the measured px is the container's",
4526
+ unmeasured: "the measure has no size for it in that story"
4527
+ };
4528
+ const groups = /* @__PURE__ */ new Map();
4529
+ for (const x of list) {
4530
+ const k = `${x.skip.part}|${x.skip.side}|${x.skip.why}`;
4531
+ groups.set(k, [...groups.get(k) ?? [], x]);
3925
4532
  }
3926
- return properties;
4533
+ return [...groups.values()].map((g) => {
4534
+ const { part, side, why: w } = g[0].skip;
4535
+ const stories = [...new Set(g.map((x) => `${variantWords(x.story)}${x.skip.measured !== void 0 ? ` ${Math.round(x.skip.measured * 100) / 100} px` : ""}`))];
4536
+ return `${part} ${side} not fixed on ${stories.length} stor${stories.length === 1 ? "y" : "ies"} (${why[w]}):${stories.join(", ")}`;
4537
+ }).join("; ");
3927
4538
  }
3928
- function withoutIdleStates(variants) {
3929
- const look2 = (v) => JSON.stringify([v.values, v.hidden ?? [], v.vectors ?? {}, v.pins ?? {}]);
3930
- const rest = (v) => JSON.stringify(Object.entries(v.props).filter(([k]) => k !== "State"));
3931
- const resting = new Map(variants.filter((v) => v.props.State === "Default").map((v) => [rest(v), look2(v)]));
3932
- return variants.filter((v) => !["Hover", "Pressed", "Focus"].includes(v.props.State ?? "") || resting.get(rest(v)) !== look2(v));
4539
+ function whereWords(w) {
4540
+ const args = Object.entries(w.args ?? {}).map(([k, v]) => `${k}=${String(v)}`);
4541
+ if (w.state) args.push(`state=${w.state}`);
4542
+ return args.join(", ") || "every variant";
3933
4543
  }
3934
- function drawnAxes(axes, variants) {
3935
- const look2 = (v) => JSON.stringify([v.values, v.hidden ?? [], v.vectors ?? {}, v.pins ?? {}]);
3936
- let current = { axes, variants };
3937
- for (const axis of axes) {
3938
- const others = (v) => JSON.stringify(current.axes.filter((a2) => a2 !== axis).map((a2) => v.props[a2.name]));
3939
- const twin = /* @__PURE__ */ new Map();
3940
- for (const v of current.variants) if (v.props[axis.name] === axis.default) twin.set(others(v), v);
3941
- const hoverTwin = /* @__PURE__ */ new Map();
3942
- if (axis.name === "State") {
3943
- for (const v of current.variants) if (v.props.State === "Hover") hoverTwin.set(others(v), v);
3944
- }
3945
- const stand = (v) => current.variants.filter((x) => x.props[axis.name] !== v.props[axis.name] && others(x) === others(v));
3946
- const differs = (v, from) => from.has(others(v)) ? look2(from.get(others(v))) !== look2(v) : from !== twin || stand(v).every((x) => look2(x) !== look2(v));
3947
- const drawn = axis.values.filter(
3948
- (value) => value === axis.default || current.variants.some((v) => v.props[axis.name] === value && differs(v, twin) && (!["Pressed", "Focus"].includes(value) || !hoverTwin.size || differs(v, hoverTwin)))
3949
- );
3950
- const values = axis.values.filter((value) => drawn.includes(value));
3951
- const variants2 = current.variants.filter((v) => values.includes(v.props[axis.name]));
3952
- const covered = new Set(variants2.filter((v) => v.props[axis.name] === axis.default).map(others));
3953
- for (const v of current.variants) {
3954
- if (values.includes(v.props[axis.name]) || covered.has(others(v))) continue;
3955
- covered.add(others(v));
3956
- variants2.push({ ...v, props: { ...v.props, [axis.name]: axis.default } });
3957
- }
3958
- if (values.length > 1) current = { axes: current.axes.map((a2) => a2 === axis ? { ...a2, values } : a2), variants: variants2 };
3959
- else
3960
- current = {
3961
- axes: current.axes.filter((a2) => a2 !== axis),
3962
- variants: variants2.map((v) => ({ ...v, props: Object.fromEntries(Object.entries(v.props).filter(([k]) => k !== axis.name)) }))
3963
- };
3964
- }
3965
- return current;
4544
+ function describePartFix(f) {
4545
+ if (f.hide) return `${f.part} hidden`;
4546
+ const bits = [
4547
+ ...Object.entries(f.set ?? {}).map(([k, v]) => `${k} ${v === null ? "none" : String(v)}`),
4548
+ ...f.width !== void 0 ? [`width ${String(f.width)}`] : [],
4549
+ ...f.height !== void 0 ? [`height ${String(f.height)}`] : [],
4550
+ ...f.direction ? [`direction ${f.direction}`] : [],
4551
+ ...f.align ? [`align ${[f.align.primary, f.align.counter].filter(Boolean).join(" ")}`] : [],
4552
+ ...f.overlay !== void 0 ? [`overlay ${typeof f.overlay === "boolean" ? String(f.overlay) : JSON.stringify(f.overlay)}`] : []
4553
+ ];
4554
+ return `${f.part}: ${bits.join(", ")}`;
3966
4555
  }
3967
- function specsFromMeasure(file, opts = {}) {
3968
- const known = opts.known ?? (() => true);
3969
- const key2 = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
3970
- const only = opts.only === void 0 ? void 0 : key2(opts.only);
3971
- const groups = /* @__PURE__ */ new Map();
3972
- for (const s of file.stories) {
3973
- if (s.error || !s.spec) continue;
3974
- const name = setNameOf$1(s.title);
3975
- if (only !== void 0 && key2(name) !== only) continue;
3976
- groups.set(name, [...groups.get(name) ?? [], s]);
3977
- }
3978
- const argTypesOf = (name) => Object.assign({}, ...(file.components ?? []).filter((c) => setNameOf$1(c.title) === name).map((c) => c.argTypes));
3979
- const hints = {};
3980
- for (const c of file.components ?? []) if (c.figma) hints[setNameOf$1(c.title)] = { ...hints[setNameOf$1(c.title)], ...c.figma };
3981
- const components = [];
3982
- const shots = {};
3983
- const report = { sets: 0, variants: 0, stories: 0, skipped: [] };
3984
- for (const [name, stories] of [...groups].sort(([a2], [b]) => a2.localeCompare(b))) {
3985
- const built = setOf(name, stories, argTypesOf(name), file.storybook, known, hints[name]);
3986
- if (!built) {
3987
- report.skipped.push(name);
3988
- continue;
3989
- }
3990
- const spec = built.spec;
3991
- components.push(spec);
3992
- if (Object.keys(built.shots).length) shots[name] = built.shots;
3993
- report.sets++;
3994
- report.variants += spec.variants.length;
3995
- report.stories += stories.length;
3996
- }
3997
- return { specs: { version: 1, generated: `Measured from ${file.storybook} on ${file.measuredAt} (bridge specs --measure).`, components }, report, shots, hints };
4556
+ function appliedFixLines(applied) {
4557
+ return applied.map((a2) => `${a2.result === "applied" ? "applied" : a2.result === "already" ? "already applied" : a2.result === "skipped" ? "skipped" : "not found"} ${a2.id} (${a2.component}, ${a2.kind}): ${a2.detail}`);
3998
4558
  }
3999
4559
  const DEFAULT_FIDELITY_RUN_FILE = "figma-fidelity.json";
4000
4560
  const FIX_LOOP_MAX_ROUNDS = 3;
@@ -4082,7 +4642,7 @@ function gapCaseFor(input) {
4082
4642
  }
4083
4643
  const FIX_SCHEMA_SUMMARY = `figma-fixes.json fixes (version ${FIGMA_FIXES_VERSION}). Each fix is one JSON object:
4084
4644
  - Every fix: "id" (lower case, digits, dashes, unique), "kind", "component" (the Figma set, e.g. "TextInput"), "reason" (one plain sentence, no em dash), optional "by": "claude", optional "where": { "args": { "<arg>": value }, "state": "rest" | "hover" | "active" | "focus-visible" | "disabled" } (absent: every variant).
4085
- - kind "part": "part" is a part id from the gap case ("root", "cds--text-input-label-wrapper") or ".class". At least one of: "set" { field: token path | px number | null (colors only) | "full" (cornerRadius only) }, "width" / "height": "fixed" (the measured size) | px number | "hug" | "fill", "direction": "HORIZONTAL" | "VERTICAL", "align": { "primary": MIN|CENTER|MAX|SPACE_BETWEEN, "counter": MIN|CENTER|MAX|STRETCH }, "overlay": true | false | { "top": 3, "left": 3 }, "hide": true (alone; never the root). Fields: fill, stroke, strokeWeight, strokeTopWeight, strokeRightWeight, strokeBottomWeight, strokeLeftWeight, cornerRadius, paddingTop, paddingRight, paddingBottom, paddingLeft, itemSpacing, counterAxisSpacing, width, height, minWidth, minHeight, opacity, textFill, fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, textDecoration.
4645
+ - kind "part": "part" is a part id from the gap case ("root", "cds--text-input-label-wrapper") or ".class". At least one of: "set" { field: token path | px number | null (colors only) | "full" (cornerRadius only) }, "width" / "height": "fixed" (each story's own measured size; a width that only hugs text in code is skipped, since it is the story's words) | px number | "hug" | "fill", "direction": "HORIZONTAL" | "VERTICAL", "align": { "primary": MIN|CENTER|MAX|SPACE_BETWEEN, "counter": MIN|CENTER|MAX|STRETCH }, "overlay": true | false | { "top": 3, "left": 3 }, "hide": true (alone; never the root). Fields: fill, stroke, strokeWeight, strokeTopWeight, strokeRightWeight, strokeBottomWeight, strokeLeftWeight, cornerRadius, paddingTop, paddingRight, paddingBottom, paddingLeft, itemSpacing, counterAxisSpacing, width, height, minWidth, minHeight, opacity, textFill, fontFamily, fontSize, fontWeight, lineHeight, letterSpacing, textDecoration.
4086
4646
  - kind "axis": "action" "map" ("arg", "property" and/or "values" { code value: Figma value }), "ignore" ("arg"), or "prune" ("where" names the variants to drop).
4087
4647
  - kind "story": "where" names the variant, "use": { "story": "<story id>", "args"?, "state"? } is the measured render to draw it from.
4088
4648
  - kind "status": "status": "differs" | "cant-check" with the reason; never "matches" (only the shots say a component matches). Without "status": a note.
@@ -4136,16 +4696,16 @@ function tryFigmaFixes(input) {
4136
4696
  const stories = storiesOfComponent(before.measured, component);
4137
4697
  const variants = [];
4138
4698
  for (const gv of before.gap.variants) {
4139
- const vb = before.spec?.variants.find((v) => variantKeyOf(v.props) === gv.key);
4140
- const want = before.spec ? renamedProps(gv.props, before.spec, afterSpec, hintsB, hintsA) : gv.props;
4141
- const va = afterSpec?.variants.find((v) => variantKeyOf(v.props) === gv.key) ?? afterSpec?.variants.find((v) => sameProps(v.props, want));
4699
+ const vb = matchVariant(before.spec, gv.props);
4700
+ const want = before.spec ? renamedProps(vb?.props ?? gv.props, before.spec, afterSpec, hintsB, hintsA) : gv.props;
4701
+ const va = matchVariant(afterSpec, gv.props) ?? matchVariant(afterSpec, want);
4142
4702
  if (!va || !afterSpec) {
4143
4703
  variants.push({ key: gv.key, ...gv.story ? { story: gv.story.id } : {}, verdict: "gone", differences: [], regressions: [] });
4144
4704
  continue;
4145
4705
  }
4146
4706
  const differences = gv.differences.map((d) => tryDifference(d, before.spec, vb, afterSpec, va, built?.[gv.key], resolve2));
4147
4707
  let regressions2 = [];
4148
- const story = before.spec ? storyForVariant(before.spec, gv.props, stories, before.shots[gv.key]) : void 0;
4708
+ const story = before.spec ? storyForVariant(before.spec, gv.props, stories, before.shots[gv.key] ?? (vb ? before.shots[variantKeyOf(vb.props)] : void 0)) : void 0;
4149
4709
  const m = story ? story.instances?.[0] ?? story.spec : void 0;
4150
4710
  if (m && before.spec && vb) {
4151
4711
  const key2 = (d) => `${d.part}|${d.property}`;
@@ -4158,7 +4718,7 @@ function tryFigmaFixes(input) {
4158
4718
  const all = variants.flatMap((v) => v.differences);
4159
4719
  const regressions = variants.reduce((n, v) => n + v.regressions.length, 0);
4160
4720
  const verdict = verdictOf(all, variants.flatMap((v) => v.regressions));
4161
- const notFound = applied.filter((a2) => a2.result === "not-found");
4721
+ const notFound = applied.filter((a2) => a2.result === "not-found" || a2.result === "skipped");
4162
4722
  return {
4163
4723
  status: "checked",
4164
4724
  component,
@@ -4172,7 +4732,6 @@ function tryFigmaFixes(input) {
4172
4732
  file
4173
4733
  };
4174
4734
  }
4175
- const sameProps = (a2, b) => Object.keys(a2).length === Object.keys(b).length && Object.entries(a2).every(([k, v]) => b[k] === v);
4176
4735
  function renamedProps(props, specB, specA, hintsB, hintsA) {
4177
4736
  const out = {};
4178
4737
  for (const [name, value] of Object.entries(props)) {
@@ -4303,7 +4862,9 @@ function sizeShift(field, specB, vb, specA, va, built, resolve2) {
4303
4862
  }
4304
4863
  function trySummary(component, gap, diffs, regressions, verdict, notFound, proposed, gone) {
4305
4864
  if (!gap.variants.length) return `${component} has no differing variant in the gap case: nothing for these fixes to move (the values referee needs a Check fidelity report or a difference in the measure).`;
4306
- const absent = notFound.length ? ` ${notFound.map((a2) => a2.id).join(", ")} found nothing in the measure.` : "";
4865
+ const missing = notFound.filter((a2) => a2.result === "not-found");
4866
+ const skipped = notFound.filter((a2) => a2.result === "skipped");
4867
+ const absent = (missing.length ? ` ${missing.map((a2) => a2.id).join(", ")} found nothing in the measure.` : "") + (skipped.length ? ` ${skipped.map((a2) => a2.id).join(", ")} laid nothing on: the measure has no size of the part's own to fix it at (see the detail).` : "");
4307
4868
  if (!diffs.length && gone) {
4308
4869
  const n = gap.variants.length;
4309
4870
  const which = gone < n ? `${gone} of the ${n} differing variants are not` : n === 1 ? "the differing variant is not" : `none of the ${n} differing variants is`;
@@ -5120,192 +5681,6 @@ async function readCombineBranch(gh, pull, opts) {
5120
5681
  );
5121
5682
  return { number: pull.number, title: pull.title, tokens: true, edits: changeEdits(source) };
5122
5683
  }
5123
- const PRODUCT_FILE = /\.(css|scss|sass|less|tsx|jsx|ts|js|mjs|cjs|mts|cts|astro|vue|svelte)$/;
5124
- const SKIP_FILE = /\.d\.[cm]?ts$|\.(test|spec|stories|story)\.[cm]?[jt]sx?$|\.min\.(css|js)$/;
5125
- const PIECES_FILE_LIMIT = 40;
5126
- const cleanPath = (p2) => p2.trim().replace(/^\.?\/+|\/+$/g, "");
5127
- function productOf(products, pull, files) {
5128
- const product = productOfBranch(products, { labels: pull.labels, files });
5129
- if (product.key === defaultProduct(products).key) throw new GitHubError(422, `This branch is a library branch (${product.name}), not a product branch: missing pieces are checked on a branch started for a product.`);
5130
- return product;
5131
- }
5132
- async function readProductBranchOnGitHub(gh, pr, opts) {
5133
- const pull = await gh.getPullRequest(pr);
5134
- const cmp = await gh.compareDetailed(pull.baseRef, pull.headSha);
5135
- const product = productOf(opts.products, pull, cmp.files.map((f) => f.filename));
5136
- const dir = product.path ? `${cleanPath(product.path)}/` : "";
5137
- const changed = cmp.files.filter((f) => f.status !== "removed" && (!dir || f.filename.startsWith(dir)) && PRODUCT_FILE.test(f.filename) && !SKIP_FILE.test(f.filename) && !f.filename.split("/").includes("node_modules"));
5138
- const read = changed.slice(0, PIECES_FILE_LIMIT);
5139
- const start = cmp.mergeBase ?? pull.baseRef;
5140
- const files = await Promise.all(
5141
- read.map(async (f) => {
5142
- const [before, after] = await Promise.all([f.status === "added" ? Promise.resolve(void 0) : gh.getFileText(f.previous_filename ?? f.filename, start), gh.getFileText(f.filename, pull.headSha)]);
5143
- return { path: f.filename, before: before ?? null, after: after ?? null };
5144
- })
5145
- );
5146
- return { pull, product, files, skipped: changed.length - read.length, record: parsePiecesRecord(pull.body) };
5147
- }
5148
- async function recordPiecesOnGitHub(gh, pr, check) {
5149
- const pull = await gh.getPullRequest(pr);
5150
- const record = mergePiecesRecord(parsePiecesRecord(pull.body), { checkedAt: check.checkedAt ?? (/* @__PURE__ */ new Date()).toISOString(), headSha: check.headSha ?? pull.headSha, pieces: check.pieces, fixes: check.fixes });
5151
- await gh.updatePullRequest(pr, { body: withPiecesRecord(pull.body, record) });
5152
- return record;
5153
- }
5154
- const productWriter = (gh, branch) => isExplorationBranch(branch) ? gh.withBranchPrefix(EXPLORATION_PREFIX) : gh;
5155
- async function useLibraryPreviewOnGitHub(gh, input) {
5156
- const message = `chore(bridge): use the preview of ${input.libraryBranch} (#${input.libraryNumber})
5157
-
5158
- The product branch takes the library branch before it ships. Switch to the published version once it does.`;
5159
- try {
5160
- const r = await productWriter(gh, input.productBranch).mergeIntoBranch(input.productBranch, input.libraryBranch, message);
5161
- return { ...r, said: r.status === "merged" ? "Your branch now uses the preview of the library branch." : "Your branch already has the latest preview." };
5162
- } catch (err) {
5163
- if (err instanceof GitHubError && err.status === 409) return { status: "conflict", sha: null, said: `Your branch and the library branch change the same lines. A developer merges ${input.libraryBranch} into ${input.productBranch} by hand.` };
5164
- throw err;
5165
- }
5166
- }
5167
- function productBranchRef(pull, product) {
5168
- const name = /<!-- bridge-exploration (\{[\s\S]*?\}) -->/.exec(pull.body ?? "");
5169
- let title = pull.title.replace(/^[A-Z][A-Z0-9_]*-\d+\s+/, "").replace(/^Exploration:\s*/i, "");
5170
- let author = pull.author;
5171
- if (name) {
5172
- try {
5173
- const e = JSON.parse(name[1]);
5174
- if (e.name) title = e.name;
5175
- if (e.author) author = e.author;
5176
- } catch {
5177
- }
5178
- }
5179
- return { pr: pull.number, title: title.charAt(0).toUpperCase() + title.slice(1), branch: pull.headRef, url: pull.url, productKey: product.key, productName: product.name, ...author ? { author } : {} };
5180
- }
5181
- async function openLibraryBranchOnGitHub(gh, input) {
5182
- const pull = await gh.getPullRequest(input.pr);
5183
- const record = parsePiecesRecord(pull.body);
5184
- const piece = record?.pieces.find((p2) => p2.id === input.piece);
5185
- if (!record || !piece) throw new GitHubError(404, record ? `No piece "${input.piece}" on #${input.pr}. Pieces: ${record.pieces.map((p2) => p2.id).join(", ") || "none"}.` : `#${input.pr} was not checked yet: find its missing pieces first (bridge_missing_pieces with record, or \`bridge pieces ${input.pr} --record\`).`);
5186
- const product = productOf(input.products, pull, await gh.listPullRequestFiles(input.pr).then((f) => f.map((x) => x.filename), () => []));
5187
- const mode = input.mode ?? "direct";
5188
- const plan = libraryBranchPlan(piece, productBranchRef(pull, product), { source: input.source, requestedBy: input.openedBy, mode });
5189
- const existing = record.links.find((l) => l.piece === piece.id);
5190
- if (existing) return { status: "already-linked", plan, link: existing, preview: null, warnings: [] };
5191
- if (input.dryRun) return { status: "dry-run", plan, link: null, preview: null, warnings: [] };
5192
- if (!gh.canWrite) throw new GitHubError(401, mode === "suggest" ? "Suggesting a library change needs a GitHub token (issues: write)." : "Opening a library branch needs a GitHub token (contents, pull requests and issues: write).");
5193
- const warnings = [];
5194
- const at2 = (/* @__PURE__ */ new Date()).toISOString();
5195
- const who = { ...input.openedBy ? { openedBy: input.openedBy } : {}, ...input.agent ? { agent: input.agent } : {} };
5196
- let link;
5197
- if (plan.kind === "token-change") {
5198
- if (!input.writeTokens) throw new GitHubError(422, "This piece is a token: open it where the token files are read (Bridge Studio, bridge_open_library_branch or `bridge pieces open`).");
5199
- const r = await input.writeTokens(plan, { mode });
5200
- link = r.status === "suggested" ? { piece: piece.id, kind: "suggestion", number: r.number, url: r.url, title: plan.title, at: at2, ...who } : { piece: piece.id, kind: "token-change", number: r.number, url: r.url, title: plan.title, branch: r.branch, at: at2, ...who };
5201
- } else if (plan.kind === "exploration") {
5202
- const r = await startExplorationOnGitHub(gh, { ...plan.exploration, ...input.openedBy ? { author: input.openedBy } : {}, ...input.agent ? { agent: input.agent } : {} }, { base: input.base, ...input.exploration, labels: [NEEDED_BY_LABEL] });
5203
- link = { piece: piece.id, kind: "exploration", number: r.pr, url: r.url, title: plan.title, branch: r.branch, at: at2, ...who };
5204
- } else {
5205
- const issue = composeRequestIssue(plan.request, { labels: [NEEDED_BY_LABEL] });
5206
- const r = await gh.createIssue(issue);
5207
- link = { piece: piece.id, kind: "request", number: r.number, url: r.url, title: plan.title, at: at2, ...who };
5208
- }
5209
- try {
5210
- if (link.kind === "request" || link.kind === "suggestion") {
5211
- const issue = await gh.getIssue(link.number);
5212
- await gh.updateIssue(link.number, { body: withNeededBy(issue.body, plan.neededBy) });
5213
- if (link.kind === "suggestion" && !issue.labels.includes(NEEDED_BY_LABEL)) await gh.addLabels(link.number, [NEEDED_BY_LABEL]);
5214
- } else {
5215
- const lib = await gh.getPullRequest(link.number);
5216
- await gh.updatePullRequest(link.number, { body: withNeededBy(lib.body, plan.neededBy) });
5217
- await gh.addLabels(link.number, [NEEDED_BY_LABEL]);
5218
- }
5219
- } catch (err) {
5220
- if (err instanceof GitHubError && err.auth === "rejected") throw err;
5221
- warnings.push(`The library side does not say who needs it yet (${err.message}). Add "Needed by #${input.pr}" to #${link.number} on GitHub.`);
5222
- }
5223
- try {
5224
- const fresh = await gh.getPullRequest(input.pr);
5225
- const next = withLink(parsePiecesRecord(fresh.body) ?? record, link);
5226
- await gh.updatePullRequest(input.pr, { body: withPiecesRecord(fresh.body, next) });
5227
- } catch (err) {
5228
- if (mode !== "suggest") throw err;
5229
- warnings.push(`#${input.pr} does not list #${link.number} yet (${err.message}). Add "Library: #${link.number}" to #${input.pr} on GitHub, so this piece is not sent twice.`);
5230
- }
5231
- let preview = null;
5232
- if ((input.usePreview ?? true) && link.kind === "token-change" && link.branch) {
5233
- preview = await useLibraryPreviewOnGitHub(gh, { productBranch: pull.headRef, libraryBranch: link.branch, libraryNumber: link.number }).catch((err) => {
5234
- warnings.push(`Your branch does not use the preview yet: ${err.message}`);
5235
- return null;
5236
- });
5237
- }
5238
- return { status: link.kind === "suggestion" ? "suggested" : "opened", plan, link, preview, warnings };
5239
- }
5240
- const isNote = (path) => path.startsWith(`${EXPLORATION_NOTES_DIR}/`);
5241
- async function linkedLibraryBranchOnGitHub(gh, link, product, piece) {
5242
- const nearest = piece?.nearest?.label;
5243
- if (link.kind === "suggestion") {
5244
- const issue = await gh.getIssue(link.number);
5245
- const cached = suggestionStateFromLabels(issue.labels);
5246
- const s = suggestionStatus({ state: issue.state, stateReason: issue.stateReason, applied: cached === "applying", review: cached === "applying" ? void 0 : cached });
5247
- const status2 = linkStatus({ kind: "suggestion", status: s });
5248
- const lifecycle2 = s === "applied" ? "merged" : issue.state === "closed" ? "closed" : "open";
5249
- const behindMain2 = lifecycle2 === "merged" ? await gh.compare(product.baseRef, product.headSha).then((c) => c.behindBy, () => null) : null;
5250
- const preview2 = libraryPreviewState({ kind: "suggestion", lifecycle: lifecycle2, hasChanges: false, missing: null, total: null, behindMain: behindMain2, nearest });
5251
- return { link, piece, status: status2, preview: preview2, people: status2.people };
5252
- }
5253
- if (link.kind === "request") {
5254
- const issue = await gh.getIssue(link.number);
5255
- const status2 = linkStatus({ kind: "request", state: issue.state, stateReason: issue.stateReason });
5256
- const lifecycle2 = issue.state === "closed" ? issue.stateReason === "not_planned" ? "closed" : "merged" : "open";
5257
- const behindMain2 = lifecycle2 === "merged" ? await gh.compare(product.baseRef, product.headSha).then((c) => c.behindBy, () => null) : null;
5258
- const preview2 = libraryPreviewState({ kind: "request", lifecycle: lifecycle2, hasChanges: false, missing: null, total: null, behindMain: behindMain2, nearest });
5259
- return { link, piece, status: status2, preview: preview2, people: status2.people };
5260
- }
5261
- const lib = await gh.getPullRequest(link.number);
5262
- const reviews = await gh.listReviews(link.number).catch(() => []);
5263
- const status = linkStatus({ kind: link.kind, state: lib.state, draft: lib.draft, labels: lib.labels, requestedReviewers: lib.requestedReviewers, reviews: reviews.map((r) => ({ user: r.user, state: r.state })) });
5264
- const lifecycle = lib.state === "merged" ? "merged" : lib.state === "open" ? "open" : "closed";
5265
- let missing = null;
5266
- let total = null;
5267
- let behindMain = null;
5268
- let hasChanges = true;
5269
- if (lifecycle === "open") {
5270
- const [files, own, inProduct] = await Promise.all([
5271
- gh.listPullRequestFiles(link.number).catch(() => null),
5272
- gh.compare(lib.baseRef, lib.headSha).catch(() => null),
5273
- gh.compare(product.headSha, lib.headSha).catch(() => null)
5274
- ]);
5275
- hasChanges = files ? files.some((f) => !isNote(f.filename)) : true;
5276
- total = own?.aheadBy ?? null;
5277
- missing = inProduct?.aheadBy ?? null;
5278
- } else if (lifecycle === "merged") {
5279
- behindMain = await gh.compare(product.baseRef, product.headSha).then((c) => c.behindBy, () => null);
5280
- }
5281
- const preview = libraryPreviewState({ kind: link.kind, lifecycle, hasChanges, missing, total, behindMain, nearest });
5282
- return { link, piece, status, preview, people: status.people };
5283
- }
5284
- async function productBranchViewOnGitHub(gh, pr, opts) {
5285
- const pull = await gh.getPullRequest(pr);
5286
- const files = await gh.listPullRequestFiles(pr).then((f) => f.map((x) => x.filename), () => []);
5287
- const product = productOf(opts.products, pull, files);
5288
- const record = parsePiecesRecord(pull.body);
5289
- const linked = await Promise.all(
5290
- (record?.links ?? []).map((l) => linkedLibraryBranchOnGitHub(gh, l, { headSha: pull.headSha, baseRef: pull.baseRef }, record?.pieces.find((p2) => p2.id === l.piece)))
5291
- );
5292
- const open = (record?.pieces ?? []).filter((p2) => !record.links.some((l) => l.piece === p2.id));
5293
- return { pull, product, record, sentence: productBranchSentence(record, { viewer: opts.viewer, agentName: opts.agentName }), linked, open };
5294
- }
5295
- async function switchToPublishedOnGitHub(gh, pr) {
5296
- if (!gh.canWrite) throw new GitHubError(401, "Switching needs a GitHub token (contents and pull requests: write).");
5297
- const pull = await gh.getPullRequest(pr);
5298
- if (pull.state !== "open") throw new GitHubError(422, `#${pr} is ${pull.state}: only an open branch switches.`);
5299
- const cmp = await gh.compare(pull.baseRef, pull.headSha);
5300
- if (cmp.behindBy === 0) return { status: "up-to-date", said: "Your branch already uses the published version." };
5301
- try {
5302
- await gh.updatePullRequestBranch(pr, pull.headSha);
5303
- } catch (err) {
5304
- if (err instanceof GitHubError && err.status === 422) throw new GitHubError(422, `Your branch and ${pull.baseRef} change the same lines, so Bridge cannot switch it alone. Use "Update from main" to pick the token values, or ask a developer.`, err.detail);
5305
- throw err;
5306
- }
5307
- return { status: "switched", said: `Your branch now uses the published version from ${pull.baseRef}.` };
5308
- }
5309
5684
  const issueKey = (i) => `${i.code}|${i.path ?? ""}|${i.file ?? ""}|${i.message}`;
5310
5685
  function planTokenProposal(input) {
5311
5686
  const { before, config } = input;
@@ -6507,14 +6882,14 @@ const semverDesc = (a2, b) => {
6507
6882
  for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return pb[i] - pa[i];
6508
6883
  return 0;
6509
6884
  };
6510
- function groupReleases(sections, dateOf, libraryPackage = "@bridge-ds/react") {
6885
+ function groupReleases(sections, dateOf2, libraryPackage = "@bridge-ds/react") {
6511
6886
  const all = sections;
6512
6887
  const parent = all.map((_, i) => i);
6513
6888
  const find = (i) => parent[i] === i ? i : parent[i] = find(parent[i]);
6514
6889
  const union = (a2, b) => {
6515
6890
  parent[find(a2)] = find(b);
6516
6891
  };
6517
- const dates = all.map((s) => dateOf(s.pkg, s.version));
6892
+ const dates = all.map((s) => dateOf2(s.pkg, s.version));
6518
6893
  const byKey = /* @__PURE__ */ new Map();
6519
6894
  all.forEach((s, i) => {
6520
6895
  const keys = [...dates[i] ? [`d:${dates[i]}`] : [], ...s.notes.map((n) => `n:${n.id}`)];
@@ -7770,8 +8145,8 @@ async function libraryArrivalOnGitHub(gh, n) {
7770
8145
  return libraryArrival({ state: pr.state, mergeable: pr.mergeable, ci: ci3, changesRequested: approvals.changesRequested.length });
7771
8146
  }
7772
8147
  export {
7773
- an as AAA_MIN,
7774
- ao as AA_MIN,
8148
+ ar as AAA_MIN,
8149
+ as as AA_MIN,
7775
8150
  ACTIVITY_FILE,
7776
8151
  ACTIVITY_LIMITS,
7777
8152
  ACTIVITY_MAX_AGE_MS,
@@ -7791,40 +8166,41 @@ export {
7791
8166
  BUILD_STALE_DAYS,
7792
8167
  BUILD_STATUS_LABEL,
7793
8168
  BUILD_STEPS,
7794
- aE2 as BULK_LIMIT,
8169
+ aL2 as BULK_LIMIT,
7795
8170
  CANT_CHECK_COPY,
7796
8171
  CHANGELOG_DETAIL_MAX,
7797
- ap as CHANGES_MODES,
7798
- aF2 as CI_STATE_LABEL,
8172
+ at as CHANGES_MODES,
8173
+ aM2 as CI_STATE_LABEL,
7799
8174
  COLOR_FIELDS,
7800
8175
  COMBINE_COMPARE_LIMIT,
7801
8176
  COMPARE_FILE_LIMIT,
7802
- aG2 as COMPONENT_SPECS_VERSION,
8177
+ aN2 as COMPONENT_SPECS_VERSION,
7803
8178
  CONFIG_FILE,
7804
- aH2 as CONFLICT_REASON,
7805
- aq as CONTRAST_CELL_MAX,
7806
- ar as CONTRAST_CHECKS_FILE,
8179
+ aO2 as CONFLICT_REASON,
8180
+ CONTINUE_PHRASE,
8181
+ au as CONTRAST_CELL_MAX,
8182
+ av as CONTRAST_CHECKS_FILE,
7807
8183
  CONTRAST_THRESHOLDS,
7808
8184
  ConfigError,
7809
8185
  DEFAULT_CHANGES_MODE,
7810
- aI2 as DEFAULT_COMPONENT_SPECS_FILE,
8186
+ aP2 as DEFAULT_COMPONENT_SPECS_FILE,
7811
8187
  DEFAULT_FIDELITY_RUN_FILE,
7812
8188
  DEFAULT_FIGMA_FIXES_FILE,
7813
- as as DEFAULT_GOVERNANCE,
8189
+ aw as DEFAULT_GOVERNANCE,
7814
8190
  DEFAULT_MEASURED_SPECS_FILE,
7815
8191
  DEFAULT_OVERRIDE,
7816
8192
  DEFAULT_PRODUCTS,
7817
- aJ2 as DEFAULT_PROTECTED_BRANCHES,
7818
- at as DEFAULT_RIGHTS,
8193
+ aQ2 as DEFAULT_PROTECTED_BRANCHES,
8194
+ ax as DEFAULT_RIGHTS,
7819
8195
  DEFAULT_TOKEN_PATHS,
7820
- au as DEFAULT_VISUAL_IMPACT,
8196
+ ay as DEFAULT_VISUAL_IMPACT,
7821
8197
  DEMO_RELEASES,
7822
- aK2 as DOCS_CHANGE_BUMP,
7823
- aL2 as DOCS_CLI_ORIGIN,
8198
+ aR2 as DOCS_CHANGE_BUMP,
8199
+ aS2 as DOCS_CLI_ORIGIN,
7824
8200
  DOCS_DRAFT_MODEL,
7825
- aM2 as DOCS_EDITOR_ORIGIN,
7826
- aN2 as DOCS_KEYS,
7827
- aO2 as DOCS_MCP_ORIGIN,
8201
+ aT2 as DOCS_EDITOR_ORIGIN,
8202
+ aU2 as DOCS_KEYS,
8203
+ aV2 as DOCS_MCP_ORIGIN,
7828
8204
  DOCS_SELECTION_MODES,
7829
8205
  DRAFTED_BY_CLAUDE_LABEL,
7830
8206
  DRAFTS_DIR,
@@ -7837,15 +8213,19 @@ export {
7837
8213
  DRAFT_SUMMARY_SENTENCES,
7838
8214
  DRAFT_TITLE_MAX,
7839
8215
  DRAFT_VOICE_RULES,
7840
- av as DTCG_TYPES,
8216
+ az as DTCG_TYPES,
7841
8217
  DraftPoller,
7842
- aP2 as EXPLORATION_CHECK,
8218
+ EXPLORATION_ARCHIVED_LABEL,
8219
+ aW2 as EXPLORATION_CHECK,
7843
8220
  EXPLORATION_LABEL,
7844
8221
  EXPLORATION_NOTES_DIR,
7845
8222
  EXPLORATION_PREFIX,
7846
- aQ2 as EXPLORATION_STATUS_LABEL,
7847
- aR2 as EXPLORATION_TIER_LABEL,
7848
- aS2 as EXPLORATION_USE_CASES,
8223
+ aX2 as EXPLORATION_READY_LABEL,
8224
+ aY2 as EXPLORATION_SLUG_MAX,
8225
+ aZ2 as EXPLORATION_STARTING_POINT,
8226
+ a_2 as EXPLORATION_STATUS_LABEL,
8227
+ a$2 as EXPLORATION_TIER_LABEL,
8228
+ b02 as EXPLORATION_USE_CASES,
7849
8229
  EXT_ID,
7850
8230
  EXT_SCOPES,
7851
8231
  FIDELITY_DEFAULTS,
@@ -7861,18 +8241,18 @@ export {
7861
8241
  FIX_LOOP_MAX_ROUNDS,
7862
8242
  FIX_LOOP_MODEL,
7863
8243
  FIX_SCHEMA_SUMMARY,
7864
- aT2 as FORMER_MEMBER,
8244
+ b12 as FORMER_MEMBER,
7865
8245
  FROM_FIGMA_LABEL,
7866
8246
  FigmaFixesError,
7867
8247
  GAP_CASE_VERSION,
7868
8248
  GitHubClient,
7869
8249
  GitHubError,
7870
8250
  HISTORY_MAX_CHANGES,
7871
- aw as JsonParseError,
8251
+ aA as JsonParseError,
7872
8252
  LIGHTNESS,
7873
- ax as LINK_STATUS_TONE,
8253
+ aB as LINK_STATUS_TONE,
7874
8254
  LLMS_MAX_TOKENS,
7875
- aU2 as LOOK_AGAIN_STATUS_LABEL,
8255
+ b22 as LOOK_AGAIN_STATUS_LABEL,
7876
8256
  LayeredTokenSet,
7877
8257
  MAX_TOGETHER,
7878
8258
  MEASURED_SHOTS_DIR,
@@ -7886,39 +8266,39 @@ export {
7886
8266
  MEASURE_WORKFLOW_HEADER,
7887
8267
  MIN_TOGETHER,
7888
8268
  MeasureMissingError,
7889
- aV2 as MeasuredSpecsError,
8269
+ b32 as MeasuredSpecsError,
7890
8270
  NEEDED_BY_LABEL,
7891
8271
  NEUTRAL_ANCHORS,
7892
8272
  OVERRIDE_HEADING,
7893
- ay as OWNER_GROUPS,
8273
+ aC as OWNER_GROUPS,
7894
8274
  OverrideMergeError,
7895
8275
  PIECES_FILE_LIMIT,
7896
8276
  PIN_TOLERANCE,
7897
- az as PREVIEW_APP_LABELS,
7898
- aA as PREVIEW_COMMENT_MARKER,
7899
- aB as PREVIEW_PLACEHOLDERS,
7900
- aC as PRIMITIVE_MODE,
7901
- aD as PRODUCT_LABEL_PREFIX,
8277
+ aD as PREVIEW_APP_LABELS,
8278
+ aE as PREVIEW_COMMENT_MARKER,
8279
+ aF as PREVIEW_PLACEHOLDERS,
8280
+ aG as PRIMITIVE_MODE,
8281
+ aH as PRODUCT_LABEL_PREFIX,
7902
8282
  PROSE_KEYS,
7903
8283
  PROTECTION_PARTS,
7904
8284
  PROTECTION_PART_PHRASE,
7905
8285
  PUBLISH_LABEL,
7906
8286
  PUBLISH_ROUTE_COPY,
7907
- aW2 as PULL_STATE_PILL,
7908
- aX2 as ProtectedBranchError,
8287
+ b42 as PULL_STATE_PILL,
8288
+ b52 as ProtectedBranchError,
7909
8289
  RADIUS_PRESETS,
7910
- aY2 as REDUCED_MOTION_NOTE,
8290
+ b62 as REDUCED_MOTION_NOTE,
7911
8291
  REPO_MODEL_DEFAULTS,
7912
8292
  REQUESTS_BRANCH,
7913
8293
  REQUEST_KINDS,
7914
8294
  REQUEST_LABEL,
7915
- aZ2 as REQUEST_STATUS_LABEL,
7916
- a_2 as REVIEW_STATUS_LABEL,
7917
- a$2 as REVIEW_STATUS_ORDER,
7918
- aE as RIGHTS,
7919
- aF as RIGHT_LABEL,
8295
+ b72 as REQUEST_STATUS_LABEL,
8296
+ b82 as REVIEW_STATUS_LABEL,
8297
+ b92 as REVIEW_STATUS_ORDER,
8298
+ aI as RIGHTS,
8299
+ aJ as RIGHT_LABEL,
7920
8300
  RISK_LABELS,
7921
- aG as RISK_TIER,
8301
+ aK as RISK_TIER,
7922
8302
  SHARE_EXPLAIN,
7923
8303
  SIMILARITY_THRESHOLD,
7924
8304
  SINGLE_MODE,
@@ -7933,38 +8313,38 @@ export {
7933
8313
  SOURCE_FIGMA_LABEL,
7934
8314
  SPEC_FIELDS,
7935
8315
  STEPS,
7936
- b02 as SUGGESTION_APPLIED_MARK,
7937
- b12 as SUGGESTION_APPLY_EXIT,
7938
- b22 as SUGGESTION_FINAL_LABELS,
7939
- b32 as SUGGESTION_KINDS,
7940
- b42 as SUGGESTION_LABEL,
7941
- b52 as SUGGESTION_LIMITS,
7942
- b62 as SUGGESTION_OVERRIDE_MARK,
7943
- b72 as SUGGESTION_REVIEW_MARK,
8316
+ ba2 as SUGGESTION_APPLIED_MARK,
8317
+ bb2 as SUGGESTION_APPLY_EXIT,
8318
+ bc2 as SUGGESTION_FINAL_LABELS,
8319
+ bd2 as SUGGESTION_KINDS,
8320
+ be2 as SUGGESTION_LABEL,
8321
+ bf2 as SUGGESTION_LIMITS,
8322
+ bg2 as SUGGESTION_OVERRIDE_MARK,
8323
+ bh2 as SUGGESTION_REVIEW_MARK,
7944
8324
  SUGGESTION_SCHEMA,
7945
- b82 as SUGGESTION_STATE_LABELS,
7946
- b92 as SUGGESTION_STATUS_LABEL,
7947
- ba2 as SUGGESTION_STATUS_LABELS,
7948
- bb2 as SUGGESTION_TOO_LARGE,
8325
+ bi2 as SUGGESTION_STATE_LABELS,
8326
+ bj2 as SUGGESTION_STATUS_LABEL,
8327
+ bk2 as SUGGESTION_STATUS_LABELS,
8328
+ bl2 as SUGGESTION_TOO_LARGE,
7949
8329
  SnapshotError,
7950
- bc2 as StorybookIndexError,
7951
- bd2 as SuggestionError,
7952
- aH as TEAM_NAME_MAX,
8330
+ bm2 as StorybookIndexError,
8331
+ bn2 as SuggestionError,
8332
+ aL as TEAM_NAME_MAX,
7953
8333
  TIER1_PUBLISH_HEADING,
7954
8334
  TIER_AXIS,
7955
8335
  TIER_NAMES,
7956
- aI as TIER_ORDER,
7957
- be2 as TOKEN_REJECTED_MESSAGE,
7958
- bf2 as TOKEN_SETTINGS_URL,
7959
- bg2 as TRIGGER_LABELS,
7960
- aJ as TeamDirectory,
7961
- bh2 as UNRELEASED,
7962
- aK as VISUAL_CHECKS_DOCS,
7963
- aL as VISUAL_CHECK_LABEL,
7964
- aM as VISUAL_WORKFLOW_FILES,
8336
+ aM as TIER_ORDER,
8337
+ bo2 as TOKEN_REJECTED_MESSAGE,
8338
+ bp2 as TOKEN_SETTINGS_URL,
8339
+ bq2 as TRIGGER_LABELS,
8340
+ aN as TeamDirectory,
8341
+ br2 as UNRELEASED,
8342
+ aO as VISUAL_CHECKS_DOCS,
8343
+ aP as VISUAL_CHECK_LABEL,
8344
+ aQ as VISUAL_WORKFLOW_FILES,
7965
8345
  WAITING_FOR_CLAUDE_LABEL,
7966
- bi2 as acceptDocsDraft,
7967
- bj2 as acceptDocsText,
8346
+ bs2 as acceptDocsDraft,
8347
+ bt2 as acceptDocsText,
7968
8348
  accessAtLeast,
7969
8349
  accessState,
7970
8350
  activityAge,
@@ -7975,47 +8355,50 @@ export {
7975
8355
  activityRawUrl,
7976
8356
  activityShaper,
7977
8357
  activityTreeFilter,
8358
+ actorName,
7978
8359
  addBrandToConfigText,
7979
8360
  addedComment,
7980
- aN as affectedContrast,
7981
- aO as aliasOf,
8361
+ aR as affectedContrast,
8362
+ aS as aliasOf,
7982
8363
  aliasTarget,
7983
- bk2 as allowedMergeMethods,
7984
- bl2 as anatomyLabel,
8364
+ bu2 as allowedMergeMethods,
8365
+ bv2 as anatomyLabel,
7985
8366
  anchoredRamp,
7986
8367
  anthropicReplyText,
7987
8368
  anthropicRequest,
7988
- aP as appForProject,
7989
- aQ as appUrl,
8369
+ aT as appForProject,
8370
+ aU as appUrl,
7990
8371
  appliedFixLines,
7991
- bm2 as appliedStatus,
8372
+ bw2 as appliedStatus,
7992
8373
  applyConfirmedRenames,
7993
8374
  applyDocsSections,
7994
8375
  applyFixes,
7995
8376
  applyTokenEdits,
7996
8377
  applyTokenMerge,
7997
- bn2 as approvedSuggestionNumber,
7998
- bo2 as approvedSuggestionOnGitHub,
7999
- aR as approverEntries,
8000
- aS as approverLists,
8378
+ bx2 as approvedSuggestionNumber,
8379
+ by2 as approvedSuggestionOnGitHub,
8380
+ aV as approverEntries,
8381
+ aW as approverLists,
8382
+ archiveExplorationOnGitHub,
8001
8383
  askOthersToReviewOnGitHub,
8002
8384
  assembleBuildRequests,
8003
- bp2 as assertSafeBranch,
8004
- bq2 as assignRequestOnGitHub,
8005
- br2 as assignVersions,
8385
+ bz2 as assertSafeBranch,
8386
+ bA2 as assignRequestOnGitHub,
8387
+ bB2 as assignVersions,
8006
8388
  auditId,
8007
- bs2 as base64ToUtf8,
8389
+ bC2 as base64ToUtf8,
8008
8390
  baseBranchOf,
8009
8391
  bellChroma,
8010
8392
  boundedCombos,
8011
- aT as branchName,
8393
+ boxSized,
8394
+ aX as branchName,
8012
8395
  branchPreviewUrls,
8013
- aU as branchSlug,
8014
- bt2 as branchVerdict,
8396
+ aY as branchSlug,
8397
+ bD2 as branchVerdict,
8015
8398
  branchWithJiraKey,
8016
8399
  brandDisplayName,
8017
8400
  brandPreviewVariables,
8018
- bu2 as bridgeMcpInstallCommand,
8401
+ bE2 as bridgeMcpInstallCommand,
8019
8402
  buildFidelityReport,
8020
8403
  buildGapCase,
8021
8404
  buildHistoryEntry,
@@ -8030,31 +8413,32 @@ export {
8030
8413
  buildTokenSet,
8031
8414
  buildUsageIndex,
8032
8415
  canAddTo,
8033
- bv2 as canOverride,
8416
+ bF2 as canOverride,
8034
8417
  canPublishTier1,
8035
8418
  canShare,
8036
- aV as canShip,
8037
- bw2 as canonicalJson,
8419
+ aZ as canShip,
8420
+ bG2 as canonicalJson,
8038
8421
  capHistory,
8039
8422
  changeEdits,
8040
- aW as changeKind,
8423
+ a_ as changeKind,
8041
8424
  changeList,
8042
- bx2 as changeTable,
8043
- aX as changedModes,
8044
- aY as changesMode,
8045
- by2 as changesetBump,
8425
+ bH2 as changeTable,
8426
+ a$ as changedModes,
8427
+ changedThings,
8428
+ b0 as changesMode,
8429
+ bI2 as changesetBump,
8046
8430
  checkBrandSlug,
8047
8431
  checkLayeredPair,
8048
8432
  choiceFor,
8049
- bz2 as classifyAuth,
8433
+ bJ2 as classifyAuth,
8050
8434
  classifyLayered,
8051
8435
  classifyRisk,
8052
- bA2 as claudeStage,
8053
- bB2 as cleanDocs,
8436
+ bK2 as claudeStage,
8437
+ bL2 as cleanDocs,
8054
8438
  cleanDocsText,
8055
8439
  cleanDraftText,
8056
- aZ as clearRawDeep,
8057
- bC2 as clearSharedEtags,
8440
+ b1 as clearRawDeep,
8441
+ bM2 as clearSharedEtags,
8058
8442
  closeExplorationOnGitHub,
8059
8443
  closeOnGitHub,
8060
8444
  closingIssueNumbers,
@@ -8064,138 +8448,148 @@ export {
8064
8448
  collectRefs,
8065
8449
  collectReleases,
8066
8450
  colorDelta,
8067
- a_ as colorToCss,
8068
- a$ as colorsEqual,
8451
+ b2 as colorToCss,
8452
+ b3 as colorsEqual,
8069
8453
  combineChanges,
8070
8454
  combineConflicts,
8071
- b0 as comboKey,
8455
+ b4 as comboKey,
8072
8456
  commentOnGitHub,
8073
- bD2 as commitFileOnGitHub,
8457
+ bN2 as commitFileOnGitHub,
8074
8458
  commitLibraryResolution,
8075
- b1 as commitMessage,
8076
- b2 as compactJson,
8459
+ b5 as commitMessage,
8460
+ b6 as compactJson,
8077
8461
  compareImages,
8078
8462
  compareSpecToMeasured,
8079
- bE2 as componentContrastPairs,
8463
+ bO2 as componentContrastPairs,
8080
8464
  componentForPath,
8081
8465
  componentMotion,
8082
8466
  componentSlug,
8083
8467
  componentSpecSuggestionPayload,
8084
8468
  componentStatus,
8085
- bF2 as componentsUsingMotionToken,
8469
+ bP2 as componentsUsingMotionToken,
8086
8470
  composeBuildRequestIssue,
8087
- bG2 as composeChangeset,
8471
+ bQ2 as composeChangeset,
8088
8472
  composeFiles,
8089
8473
  composeNewBrandBody,
8090
8474
  composePendingDrafts,
8091
- b3 as composePrBody,
8475
+ b7 as composePrBody,
8092
8476
  composeProposalBody,
8093
8477
  composeRequestIssue,
8094
- bH2 as composeSuggestionAppliedComment,
8095
- bI2 as composeSuggestionIssue,
8096
- bJ2 as composeSuggestionOverrideComment,
8097
- bK2 as composeSuggestionReviewComment,
8098
- b4 as composite,
8478
+ bR2 as composeSuggestionAppliedComment,
8479
+ bS2 as composeSuggestionIssue,
8480
+ bT2 as composeSuggestionOverrideComment,
8481
+ bU2 as composeSuggestionReviewComment,
8482
+ b8 as composite,
8099
8483
  conflictRoute,
8100
- b5 as contextFor,
8484
+ b9 as contextFor,
8101
8485
  contextLabel,
8102
- bL2 as continueDocsProposalOnGitHub,
8486
+ bV2 as continueDocsProposalOnGitHub,
8487
+ continueLines,
8488
+ continueRef,
8103
8489
  contrastDelta,
8104
- bM2 as contrastDeltaTable,
8105
- b6 as contrastLevel,
8106
- b7 as contrastPair,
8107
- b8 as contrastRatio,
8490
+ bW2 as contrastDeltaTable,
8491
+ ba as contrastLevel,
8492
+ bb as contrastPair,
8493
+ bc as contrastRatio,
8108
8494
  contrastScope,
8109
- bN2 as countChanges,
8110
- bO2 as coverageSummary,
8495
+ bX2 as countChanges,
8496
+ bY2 as coverageSummary,
8111
8497
  createBuildRequestOnGitHub,
8112
8498
  createDocsDraftSession,
8113
8499
  createPendingDrafts,
8114
8500
  createSuggestionOnGitHub,
8115
- bP2 as cssColorToRgb,
8116
- bQ2 as cssColorsToRgb,
8117
- bR2 as cssModuleClasses,
8501
+ bZ2 as cssColorToRgb,
8502
+ b_2 as cssColorsToRgb,
8503
+ b$2 as cssModuleClasses,
8118
8504
  cssOf,
8119
8505
  cssVarOf,
8120
8506
  cssVarsIn,
8121
- bS2 as declineComment,
8122
- bT2 as declineReasonProblems,
8123
- bU2 as declineRequestOnGitHub,
8124
- bV2 as declineSuggestionOnGitHub,
8507
+ c02 as declineComment,
8508
+ c12 as declineReasonProblems,
8509
+ c22 as declineRequestOnGitHub,
8510
+ c32 as declineSuggestionOnGitHub,
8125
8511
  decodePicks,
8126
8512
  defaultChoiceFor,
8127
- b9 as defaultContext,
8128
- bW2 as defaultDocsFooter,
8513
+ bd as defaultContext,
8514
+ c42 as defaultDocsFooter,
8129
8515
  defaultFile,
8130
8516
  defaultModelConfig,
8131
8517
  defaultProduct,
8132
8518
  defaultScopes,
8133
- ba as defaultTitle,
8519
+ be as defaultTitle,
8134
8520
  defineConfig,
8135
8521
  deletePendingDrafts,
8136
8522
  demoHistory,
8137
8523
  describeDiff,
8138
8524
  describePartFix,
8139
- bb as detectLayout,
8525
+ bf as detectLayout,
8140
8526
  diffLayered,
8141
8527
  dismissStaleFrom,
8142
- bX2 as docsAuthorLine,
8143
- bY2 as docsFileText,
8528
+ c52 as docsAuthorLine,
8529
+ c62 as docsFileText,
8144
8530
  docsPrompt,
8145
- bZ2 as docsProposal,
8146
- b_2 as docsSectionShapes,
8531
+ c72 as docsProposal,
8532
+ c82 as docsSectionShapes,
8147
8533
  docsSectionsAnswer,
8148
8534
  docsSectionsResult,
8149
8535
  docsSelectionPrompt,
8150
- b$2 as docsSlug,
8151
- c02 as docsSuggestionPayload,
8536
+ c92 as docsSlug,
8537
+ ca2 as docsSuggestionPayload,
8152
8538
  docsTaskKeys,
8153
8539
  docsTaskPrompt,
8154
- c12 as docsText,
8155
- c22 as draftChangeLine,
8540
+ cb2 as docsText,
8541
+ doneExploringOnGitHub,
8542
+ doneExploringPlan,
8543
+ cc2 as draftChangeLine,
8156
8544
  draftImagePath,
8157
8545
  draftItemBrief,
8158
- c32 as draftPushDescription,
8159
- c42 as draftRequestText,
8546
+ cd2 as draftPushDescription,
8547
+ ce2 as draftRequestText,
8160
8548
  draftSessionPath,
8161
8549
  dtcgColor,
8162
8550
  durationValue,
8163
8551
  easingValue,
8164
8552
  effectiveScopes,
8165
8553
  effectiveTier,
8166
- c52 as eligibleReviewers,
8554
+ cf2 as eligibleReviewers,
8167
8555
  emptyTokenFile,
8168
8556
  encodePicks,
8169
8557
  ensureRequestsBranch,
8170
- bc as entriesFor,
8171
- bd as entryLabel,
8172
- c62 as evalCalc,
8558
+ bg as entriesFor,
8559
+ bh as entryLabel,
8560
+ cg2 as evalCalc,
8173
8561
  evaluateContrastPairs,
8174
8562
  evaluatePairs,
8175
- be as expandEntries,
8176
- bf as expandOwners,
8177
- c72 as explorationBranch,
8563
+ bi as expandEntries,
8564
+ bj as expandOwners,
8565
+ explorationActivity,
8566
+ ch2 as explorationBranch,
8178
8567
  explorationNote,
8179
8568
  explorationNotePath,
8180
8569
  explorationPrBody,
8181
8570
  explorationPrTitle,
8182
8571
  explorationProblems,
8183
- c82 as explorationRules,
8184
- c92 as explorationShareMessage,
8572
+ explorationRefLabel,
8573
+ ci2 as explorationRules,
8574
+ cj2 as explorationShareMessage,
8185
8575
  explorationSlug,
8186
- ca2 as explorationSlugFromBranch,
8576
+ ck2 as explorationSlugFromBranch,
8577
+ explorationStage,
8578
+ explorationStartMessage,
8579
+ explorationStartingPoint,
8187
8580
  explorationStatus,
8188
8581
  explorationSummary,
8582
+ cl2 as explorationTakeCommands,
8189
8583
  explorationTestBuildOnGitHub,
8190
8584
  explorationTryIt,
8191
8585
  explorationTryItOnGitHub,
8192
- cb2 as fetchStorybookIndex,
8586
+ cm2 as fetchStorybookIndex,
8193
8587
  fidelityComponentLine,
8194
8588
  fidelityVerdict,
8195
- bg as fieldChanges,
8589
+ bk as fieldChanges,
8196
8590
  figmaAheadLine,
8197
8591
  figmaBranchTitle,
8198
- cc2 as figmaCell,
8592
+ cn2 as figmaCell,
8199
8593
  figmaChangeOnGitHub,
8200
8594
  figmaDraftBody,
8201
8595
  figmaDraftBrief,
@@ -8206,149 +8600,162 @@ export {
8206
8600
  figmaNodeUrl,
8207
8601
  figmaNodes,
8208
8602
  figmaOriginSection,
8209
- bh as figmaPushNextLine,
8603
+ bl as figmaPushNextLine,
8210
8604
  figmaPushOf,
8211
8605
  figmaPushScope,
8212
8606
  figmaPushStatus,
8213
8607
  figmaPushStatusesOnGitHub,
8214
- bi as figmaPushTierLabel,
8608
+ bm as figmaPushTierLabel,
8215
8609
  figmaShipReviewBody,
8216
8610
  figmaSpecPath,
8217
8611
  figmaSpecSlug,
8218
- cd2 as fileModes,
8219
- ce2 as filterHistory,
8612
+ co2 as fileModes,
8613
+ cp2 as filterHistory,
8220
8614
  findExplorationOnGitHub,
8221
8615
  findMeasure,
8222
- cf2 as findOpenDocsProposal,
8223
- cg2 as findOpenDocsProposalOnGitHub,
8616
+ cq2 as findOpenDocsProposal,
8617
+ cr2 as findOpenDocsProposalOnGitHub,
8224
8618
  findPreviewComment,
8225
- bj as findProduct,
8619
+ bn as findProduct,
8226
8620
  findReferenceProblems,
8227
- bk as findToken,
8228
- bl as findVercelComment,
8229
- ch2 as firstParagraph,
8621
+ bo as findToken,
8622
+ bp as findVercelComment,
8623
+ cs2 as firstParagraph,
8230
8624
  fitActivity,
8231
8625
  fixLoopOutcome,
8232
8626
  fixLoopRound,
8233
8627
  fixPrompt,
8234
8628
  fixWords,
8235
- bm as flattenLayerFile,
8629
+ bq as flattenLayerFile,
8236
8630
  flattenTokenFile,
8237
8631
  flattenTokens,
8238
8632
  formatChangeset,
8239
- ci2 as formatDocsJson,
8633
+ ct2 as formatDocsJson,
8240
8634
  formatFigmaSpec,
8241
- bn as formatNumber,
8242
- bo as formatOwnerEntry,
8635
+ br as formatNumber,
8636
+ bs as formatOwnerEntry,
8243
8637
  formatValue,
8244
8638
  freshness,
8245
- cj2 as friendlyMessage,
8639
+ cu2 as friendlyMessage,
8246
8640
  fromHex,
8247
- bp as fromPlain,
8641
+ bt as fromPlain,
8248
8642
  gapCaseFor,
8249
8643
  generateRamp,
8250
- bq as getChild,
8251
- br as getEntry,
8644
+ bu as getChild,
8645
+ bv as getEntry,
8252
8646
  getSuggestionOnGitHub,
8253
8647
  gitBlobSha,
8254
8648
  gitHubHistoryBackend,
8255
8649
  gitHubSource,
8256
- ck2 as githubSuggestionWorkspace,
8650
+ cv2 as githubSuggestionWorkspace,
8257
8651
  globBase,
8258
8652
  globToRegExp,
8259
8653
  groupReleases,
8260
- cl2 as groupStorybookComponents,
8261
- bs as groupsPhrase,
8262
- cm2 as hasOwnerApproval,
8263
- cn2 as hasSuggestionBlock,
8264
- bt as hasVisualWorkflow,
8265
- co2 as hasWriteAccess,
8654
+ cw2 as groupStorybookComponents,
8655
+ bw as groupsPhrase,
8656
+ cx2 as hasOwnerApproval,
8657
+ cy2 as hasSuggestionBlock,
8658
+ bx as hasVisualWorkflow,
8659
+ cz2 as hasWriteAccess,
8266
8660
  hashSpec,
8267
8661
  hexContrastRatio,
8268
8662
  hexLuminance,
8269
- bu as hexToRgba,
8270
- bv as holdersOf,
8271
- cp2 as iconComponentName,
8663
+ by as hexToRgba,
8664
+ bz as holdersOf,
8665
+ humanAuthorOf,
8666
+ cA2 as iconComponentName,
8272
8667
  idExtensionFor,
8273
8668
  impactedModules,
8274
8669
  importsIn,
8275
- bw as indexByPath,
8276
- bx as inheritedTypeAt,
8277
- by as installCommand,
8278
- bz as installCommandFromPreviewComment,
8279
- cq2 as installationFor,
8280
- cr2 as isActiveExploration,
8670
+ inProductRepo,
8671
+ bA as indexByPath,
8672
+ bB as inheritedTypeAt,
8673
+ bC as installCommand,
8674
+ bD as installCommandFromPreviewComment,
8675
+ cB2 as installationFor,
8676
+ cC2 as isActiveExploration,
8281
8677
  isAlias$1 as isAlias,
8282
- cs2 as isAutomationPull,
8678
+ isArchivedExploration,
8679
+ cD2 as isAutomationPull,
8283
8680
  isBuildRequestIssue,
8284
- ct2 as isDocsProposalBranch,
8681
+ isClaudeAssisted,
8682
+ isClaudeCommit,
8683
+ cE2 as isDocsProposalBranch,
8285
8684
  isDocsSelectionMode,
8286
- cu2 as isDraft,
8685
+ cF2 as isDraft,
8287
8686
  isDraftSessionId,
8288
8687
  isExplorationBranch,
8289
8688
  isExplorationPull,
8290
8689
  isFigmaPush,
8291
8690
  isGuardrail,
8292
- cv2 as isGuardrailPath,
8691
+ cG as isGuardrailPath,
8692
+ cH as isHandedOff,
8693
+ cI as isHeldExploration,
8293
8694
  isHex,
8294
- cw2 as isJiraKey,
8295
- cx2 as isMissingPermission,
8695
+ isHiddenRepoError,
8696
+ cJ as isJiraKey,
8697
+ cK as isMissingPermission,
8296
8698
  isMultiMode,
8297
8699
  isPlanRefusal,
8298
- cy2 as isPlatformRefusal,
8700
+ cL as isPlatformRefusal,
8299
8701
  isProseKey,
8300
- cz2 as isReleasePull,
8301
- bA as isTeamEntry,
8302
- bB as isTokenFile,
8702
+ cM as isReleasePull,
8703
+ isStartCommit,
8704
+ bE as isTeamEntry,
8705
+ bF as isTokenFile,
8303
8706
  isTokenPath,
8304
- cA2 as isTokenRejected,
8707
+ cN as isTokenRejected,
8305
8708
  isTokenValueChange,
8306
8709
  isUnder,
8307
- bC as isVercelComment,
8308
- cB as jiraIssueFields,
8309
- cC as jiraKeyOf,
8710
+ bG as isVercelComment,
8711
+ cO as jiraIssueFields,
8712
+ cP as jiraKeyOf,
8310
8713
  jiraKeyOfLabels,
8311
- cD as joinRepoPath,
8312
- bD as kindForMin,
8714
+ cQ as joinRepoPath,
8715
+ bH as kindForMin,
8716
+ lastActivity,
8313
8717
  layeredImpact,
8314
- bE as layeredParityIssues,
8718
+ bI as layeredParityIssues,
8315
8719
  layoutOptions,
8316
8720
  leadsPublishTier1On,
8317
8721
  levenshtein,
8318
8722
  libraryArrival,
8319
8723
  libraryArrivalOnGitHub,
8320
8724
  libraryBranchPlan,
8321
- bF as libraryIsPublic,
8725
+ bJ as libraryIsPublic,
8322
8726
  libraryPreviewState,
8323
- cE as linkRequestToExplorationOnGitHub,
8727
+ cR as linkRequestToExplorationOnGitHub,
8324
8728
  linkStatus,
8325
- cF as linkSuggestionPrOnGitHub,
8729
+ cS as linkSuggestionPrOnGitHub,
8326
8730
  linkedIssueNumbers,
8327
8731
  linkedLibraryBranchOnGitHub,
8328
- cG as linkedPullsOf,
8732
+ cT as linkedPullsOf,
8329
8733
  listBrands,
8330
8734
  listBuildRequestsOnGitHub,
8331
8735
  listExplorationsOnGitHub,
8332
8736
  listFigmaChangesOnGitHub,
8333
8737
  listPendingDrafts,
8738
+ listProductExplorationsOnGitHub,
8334
8739
  listStylesheets,
8335
8740
  listSuggestionsOnGitHub,
8741
+ listThings,
8336
8742
  loadConfig,
8337
8743
  loadGitHubHistory,
8338
8744
  loadProtection,
8339
- cH as loadRepoFiles,
8745
+ cU as loadRepoFiles,
8340
8746
  loadRepoModel,
8341
8747
  loadRequiredChecks,
8342
8748
  localTryCommand,
8343
- cI as lookAgainReviewers,
8344
- cJ as lowerFirstWord,
8345
- bG as luminance,
8749
+ cV as lookAgainReviewers,
8750
+ cW as lowerFirstWord,
8751
+ bK as luminance,
8346
8752
  mailtoHref,
8347
8753
  mapPropsToFigma,
8348
8754
  markClaudeDraft,
8349
8755
  markFigmaChangeDraftedOnGitHub,
8350
- cK as markSuggestionsAppliedOnGitHub,
8351
- cL as markUsedTokens,
8756
+ cX as markSuggestionsAppliedOnGitHub,
8757
+ cY as markUsedTokens,
8758
+ matchVariant,
8352
8759
  matchesBuildFilter,
8353
8760
  mayBeTokenValueChange,
8354
8761
  measureDir,
@@ -8357,158 +8764,169 @@ export {
8357
8764
  measureWorkflowOptionsFromConfig,
8358
8765
  measureWorkflowYaml,
8359
8766
  measuredResolver,
8360
- cM as memoryEtagStore,
8361
- cN as memorySource,
8767
+ cZ as memoryEtagStore,
8768
+ c_ as memorySource,
8362
8769
  mergeDocsDraft,
8363
8770
  mergeDraftResults,
8364
8771
  mergeFigmaFixes,
8365
- cO as mergeGate,
8772
+ c$ as mergeGate,
8366
8773
  mergeOnGitHub,
8367
8774
  mergePiecesRecord,
8368
8775
  mergeReleases,
8369
8776
  missingChoices,
8370
- cP as missingPermissionMessage,
8371
- cQ as missingProse,
8777
+ d0 as missingPermissionMessage,
8778
+ d1 as missingProse,
8372
8779
  modeDisplayName,
8373
8780
  modeFile,
8374
- cR as modeLabel,
8781
+ d2 as modeLabel,
8375
8782
  modesOf$1 as modesOf,
8376
8783
  motionDocs,
8377
8784
  motionMoments,
8378
8785
  moveToken,
8379
- bH as nearestQuestion,
8786
+ bL as nearestQuestion,
8380
8787
  nearestStep,
8381
- bI as neededBySection,
8382
- cS as needsDocsReview,
8788
+ bM as neededBySection,
8789
+ d3 as needsDocsReview,
8383
8790
  neutralChroma,
8384
8791
  neutralOffset,
8385
8792
  newBrandSection,
8386
8793
  newDraftSessionId,
8387
8794
  noOnlinePreview,
8388
- bJ as nodesEqual,
8795
+ bN as nodesEqual,
8389
8796
  normalizeRepoPath,
8390
- bK as normalizeRights,
8391
- cT as normalizeStorybookUrl,
8797
+ bO as normalizeRights,
8798
+ d4 as normalizeStorybookUrl,
8392
8799
  noteSentences,
8393
8800
  oklch,
8394
8801
  oklchToLinear,
8802
+ onlyNotes,
8395
8803
  onlyTokens,
8396
8804
  openBuildRequests,
8397
8805
  openComponentPushOnGitHub,
8398
8806
  openLibraryBranchOnGitHub,
8399
- cU as openPulls,
8807
+ d5 as openPulls,
8400
8808
  overlap,
8401
8809
  overrideComment,
8402
- cV as overrideHolders,
8403
- bL as overrideKey,
8810
+ d6 as overrideHolders,
8811
+ bP as overrideKey,
8404
8812
  overrideMergeOnGitHub,
8405
- cW as overrideReasonMin,
8406
- cX as overrideReasonProblem,
8813
+ d7 as overrideReasonMin,
8814
+ d8 as overrideReasonProblem,
8407
8815
  overrideRefusedComment,
8408
- cY as overrideSuggestionOnGitHub,
8409
- bM as ownerEntryProblems,
8410
- cZ as ownerOverridePolicy,
8816
+ d9 as overrideSuggestionOnGitHub,
8817
+ bQ as ownerEntryProblems,
8818
+ da as ownerOverridePolicy,
8411
8819
  packageDir,
8412
8820
  packageDirCandidates,
8413
- bN as pairKey,
8821
+ bR as pairKey,
8414
8822
  pairParts,
8415
8823
  parentDir,
8416
8824
  parseActivity,
8417
- c_ as parseBoxShadow,
8825
+ db as parseBoxShadow,
8418
8826
  parseBuildRequestIssue,
8419
8827
  parseBuildSpec,
8420
8828
  parseChangelog,
8421
8829
  parseChangelogSections,
8422
8830
  parseChangeset,
8423
- c$ as parseChangesetText,
8831
+ dc as parseChangesetText,
8424
8832
  parseColor,
8425
8833
  parseComponentCss,
8426
8834
  parseComponentFamilies,
8427
8835
  parseComponentSpecs,
8428
8836
  parseComponentStories,
8429
8837
  parseComponentTsx,
8838
+ parseContinueLine,
8430
8839
  parseContrastChecks,
8431
8840
  parseContrastSpecs,
8432
- bO as parseDimension,
8841
+ bS as parseDimension,
8433
8842
  parseDocsReply,
8434
8843
  parseDocsTaskAnswer,
8435
8844
  parseExplorationBody,
8845
+ dd as parseExplorationRef,
8436
8846
  parseFidelityRun,
8437
8847
  parseFigmaFixes,
8438
8848
  parseFigmaOrigin,
8439
8849
  parseFigmaSpec,
8440
8850
  parseFixReply,
8441
- d0 as parseJiraKey,
8442
- bP as parseJson,
8443
- d1 as parseMeasuredSpecsFile,
8851
+ de as parseJiraKey,
8852
+ bT as parseJson,
8853
+ df as parseMeasuredSpecsFile,
8444
8854
  parseModelConfig,
8445
- bQ as parseNeededBy,
8446
- d2 as parseOverrideComment,
8447
- bR as parseOwnerEntry,
8855
+ bU as parseNeededBy,
8856
+ dg as parseOverrideComment,
8857
+ bV as parseOwnerEntry,
8448
8858
  parsePendingDrafts,
8449
8859
  parsePiecesRecord,
8450
- d3 as parseRepo,
8860
+ dh as parseRepo,
8451
8861
  parseRequestIssue,
8452
8862
  parseSnapshot,
8453
- d4 as parseStorybookIndex,
8454
- d5 as parseStructureChange,
8455
- d6 as parseSuggestionAppliedComment,
8456
- d7 as parseSuggestionIssue,
8457
- d8 as parseSuggestionOverrideComment,
8458
- d9 as parseSuggestionReviewComment,
8459
- da as parseTier,
8863
+ di as parseStorybookIndex,
8864
+ dj as parseStructureChange,
8865
+ dk as parseSuggestionAppliedComment,
8866
+ dl as parseSuggestionIssue,
8867
+ dm as parseSuggestionOverrideComment,
8868
+ dn as parseSuggestionReviewComment,
8869
+ dp as parseTier,
8460
8870
  parseTokenFile,
8461
- bS as parseVercelComment,
8871
+ bW as parseVercelComment,
8462
8872
  partLabel,
8463
8873
  partWords,
8464
- db as patchDocsJson,
8874
+ dq as patchDocsJson,
8465
8875
  pathId,
8466
8876
  pathSimilarity,
8467
- dc as pendingApprovals,
8468
- dd as permissionLabel,
8877
+ dr as pendingApprovals,
8878
+ ds as permissionLabel,
8469
8879
  pickedOption,
8470
8880
  p as pieceId,
8471
- bT as piecesSection,
8472
- bU as plainEqual,
8881
+ pieceRequest,
8882
+ bX as pieceValueField,
8883
+ bY as piecesSection,
8884
+ bZ as plainEqual,
8473
8885
  planComponentPush,
8474
8886
  planNewBrand,
8475
8887
  planReference,
8476
8888
  planReferences,
8477
- de as planRestore,
8478
- df as planRevert,
8479
- dg as planSuggestionApply,
8480
- dh as planToJson,
8889
+ dt as planRestore,
8890
+ du as planRevert,
8891
+ dv as planSuggestionApply,
8892
+ dw as planToJson,
8481
8893
  planTokenMerge,
8482
8894
  planTokenProposal,
8483
8895
  planVariantMeasures,
8484
- di as prNumberFromSubject,
8485
- dj as prefillDocs,
8486
- bV as previewComment,
8896
+ dx as prNumberFromSubject,
8897
+ dy as prefillDocs,
8898
+ b_ as previewComment,
8487
8899
  previewLinks,
8488
- bW as previewLinksOf,
8900
+ b$ as previewLinksOf,
8489
8901
  previewOverrides,
8490
- bX as previewUrl,
8902
+ previewUrl,
8491
8903
  previewsSettled,
8492
8904
  productBranchRef,
8493
8905
  productBranchSentence,
8494
8906
  productBranchViewOnGitHub,
8495
- bY as productForPlace,
8496
- bZ as productLabel,
8497
- b_ as productMarks,
8907
+ c0 as productForPlace,
8908
+ c1 as productLabel,
8909
+ c2 as productMarks,
8498
8910
  productOfBranch,
8911
+ productOfRepo,
8499
8912
  productPreview,
8500
- b$ as productProblems,
8501
- c0 as productsOf,
8502
- c1 as projectOfLabel,
8913
+ productPreviewOnGitHub,
8914
+ c3 as productProblems,
8915
+ productRepoAccess,
8916
+ c4 as productRepoOf,
8917
+ productRepos,
8918
+ c5 as productsOf,
8919
+ c6 as projectOfLabel,
8503
8920
  promoteExplorationOnGitHub,
8504
8921
  promotionPlan,
8505
8922
  propertyWords,
8506
- dk as proposeDocsOnGitHub,
8923
+ dz as proposeDocsOnGitHub,
8924
+ proposePiecesOnGitHub,
8507
8925
  a as proposedTokenPath,
8508
- dl as propsTable,
8926
+ dA as propsTable,
8509
8927
  protectLibrary,
8510
8928
  protectRefusal,
8511
- c2 as protectedBranchesOf,
8929
+ c7 as protectedBranchesOf,
8512
8930
  protectionRuleset,
8513
8931
  protectionRulesetName,
8514
8932
  protectionSentence,
@@ -8516,8 +8934,8 @@ export {
8516
8934
  publishSummary,
8517
8935
  publishedWithoutReview,
8518
8936
  pullState,
8519
- dm as pushChangeRows,
8520
- dn as rateLimitMessage,
8937
+ dB as pushChangeRows,
8938
+ dC as rateLimitMessage,
8521
8939
  readCombineBranch,
8522
8940
  readLibraryConflicts,
8523
8941
  readPendingDrafts,
@@ -8533,24 +8951,24 @@ export {
8533
8951
  referenceImagePath,
8534
8952
  releaseAnnouncement,
8535
8953
  releasePackagesLabel,
8536
- dp as releasePull,
8954
+ dD as releasePull,
8537
8955
  releasesFromTags,
8538
8956
  remoteRenames,
8539
8957
  removeToken,
8540
8958
  removedSide,
8541
- c3 as renderedColor,
8542
- dq as reopenEligibility,
8959
+ c8 as renderedColor,
8960
+ dE as reopenEligibility,
8543
8961
  reopenOnGitHub,
8544
- dr as requestBrief,
8545
- ds as requestImagePath,
8962
+ dF as requestBrief,
8963
+ dG as requestImagePath,
8546
8964
  requestIssueTitle,
8547
8965
  requestStatus,
8548
- dt as requesterFirstName,
8549
- du as requesterLine,
8550
- dv as requesterOf,
8551
- dw as requiredApprovals,
8966
+ dH as requesterFirstName,
8967
+ dI as requesterLine,
8968
+ dJ as requesterOf,
8969
+ dK as requiredApprovals,
8552
8970
  requiredCheckNames,
8553
- dx as requiredPermission,
8971
+ dL as requiredPermission,
8554
8972
  resolutionMessage,
8555
8973
  resolve,
8556
8974
  resolveBranchPreviews,
@@ -8558,21 +8976,21 @@ export {
8558
8976
  resolveRepoAccess,
8559
8977
  resolvedTokenFiles,
8560
8978
  reviewOnGitHub,
8561
- dy as reviewQueue,
8562
- dz as reviewStatusKind,
8563
- dA as reviewStatusLabel,
8564
- dB as reviewSuggestionOnGitHub,
8565
- dC as reviewersToAsk,
8566
- c4 as rgbaToDtcg,
8979
+ dM as reviewQueue,
8980
+ dN as reviewStatusKind,
8981
+ dO as reviewStatusLabel,
8982
+ dP as reviewSuggestionOnGitHub,
8983
+ dQ as reviewersToAsk,
8984
+ c9 as rgbaToDtcg,
8567
8985
  rgbaToHex,
8568
- c5 as rightsProblems,
8986
+ ca as rightsProblems,
8569
8987
  round3,
8570
8988
  rulesSettingsUrl,
8571
8989
  rulesetTargetsDefault,
8572
8990
  runLayeredContrast,
8573
- c6 as sameProducts,
8991
+ cb as sameProducts,
8574
8992
  sameScopes,
8575
- c7 as sameToken,
8993
+ cc as sameToken,
8576
8994
  sanitizeDocsReplacement,
8577
8995
  sanitizeDocsResult,
8578
8996
  sanitizeDocsSections,
@@ -8581,22 +8999,23 @@ export {
8581
8999
  sanitizeDraftTitle,
8582
9000
  sanitizeResultLines,
8583
9001
  scanComponents,
8584
- dD as sectionsNeedingReview,
9002
+ dR as sectionsNeedingReview,
8585
9003
  selectTokenFiles,
8586
- c8 as serializeJson,
9004
+ cd as serializeJson,
8587
9005
  serializePendingDrafts,
8588
9006
  serializeTokenFile,
8589
- dE as sha256Hex,
9007
+ dS as sha256Hex,
8590
9008
  shadowedEdits,
8591
9009
  shareDescription,
8592
9010
  shareMessage,
8593
9011
  shareState,
8594
- dF as sharedTokenPrefix,
8595
- c9 as shipPolicy,
9012
+ dT as sharedTokenPrefix,
9013
+ ce as shipPolicy,
8596
9014
  shipsByItself,
9015
+ shortAgo,
8597
9016
  shortPackage,
8598
- dG as shortVariant,
8599
- dH as shortVariants,
9017
+ dU as shortVariant,
9018
+ dV as shortVariants,
8600
9019
  showShare,
8601
9020
  siteRepoFromConfig,
8602
9021
  sizeGap,
@@ -8607,68 +9026,71 @@ export {
8607
9026
  snapshotMotion,
8608
9027
  snapshotSource,
8609
9028
  snapshotSummary,
8610
- ca as snapshotUrl,
8611
- cb as soloApprover,
8612
- cc as soloOwnerOf,
8613
- cd as soloOwnerSentence,
9029
+ cf as snapshotUrl,
9030
+ cg as soloApprover,
9031
+ ch as soloOwnerOf,
9032
+ ci as soloOwnerSentence,
8614
9033
  sortChangelogEntries,
8615
- dI as specHash,
9034
+ dW as specHash,
8616
9035
  specsFromMeasure,
8617
- dJ as specsUsage,
9036
+ dX as specsUsage,
8618
9037
  splitNote,
8619
9038
  stableCommand,
8620
- dK as stalenessStatus,
9039
+ dY as stalenessStatus,
8621
9040
  startExplorationOnGitHub,
8622
9041
  startFixLoop,
9042
+ startedWhen,
8623
9043
  stateSentence,
8624
9044
  statusFixFor,
8625
9045
  storiesOfComponent,
8626
9046
  storyForVariant,
8627
9047
  storyMatchesWhere,
8628
9048
  storyState,
8629
- dL as storybookIframeUrl,
8630
- dM as storybookIndexUrls,
8631
- dN as storybookManagerUrl,
8632
- ce as storybookUrl,
9049
+ dZ as storybookIframeUrl,
9050
+ d_ as storybookIndexUrls,
9051
+ d$ as storybookManagerUrl,
9052
+ cj as storybookUrl,
8633
9053
  styleGlobs,
8634
- dO as subjectTitle,
8635
- cf as suggestNearestComment,
9054
+ e0 as subjectTitle,
9055
+ subjectWords,
9056
+ ck as suggestNearestComment,
8636
9057
  suggestTokenProposalOnGitHub,
8637
- dP as suggestionAllowedRoots,
8638
- dQ as suggestionAppliedComment,
8639
- dR as suggestionApplyTable,
8640
- dS as suggestionIsTokenValues,
8641
- dT as suggestionIssueTitle,
8642
- dU as suggestionKindFromLabels,
8643
- dV as suggestionLabelChanges,
8644
- dW as suggestionLabels,
8645
- dX as suggestionOverrideOf,
8646
- dY as suggestionOverrideProblem,
8647
- dZ as suggestionPathProblems,
8648
- d_ as suggestionPayloadSha,
8649
- d$ as suggestionReviewState,
8650
- e0 as suggestionShapeProblems,
8651
- e1 as suggestionStalenessOnGitHub,
9058
+ e1 as suggestionAllowedRoots,
9059
+ e2 as suggestionAppliedComment,
9060
+ e3 as suggestionApplyTable,
9061
+ e4 as suggestionIsTokenValues,
9062
+ e5 as suggestionIssueTitle,
9063
+ e6 as suggestionKindFromLabels,
9064
+ e7 as suggestionLabelChanges,
9065
+ e8 as suggestionLabels,
9066
+ e9 as suggestionOverrideOf,
9067
+ ea as suggestionOverrideProblem,
9068
+ eb as suggestionPathProblems,
9069
+ ec as suggestionPayloadSha,
9070
+ ed as suggestionReviewState,
9071
+ ee as suggestionShapeProblems,
9072
+ ef as suggestionStalenessOnGitHub,
8652
9073
  suggestionStateFromLabels,
8653
- e2 as suggestionStateLabel,
9074
+ eg as suggestionStateLabel,
8654
9075
  suggestionStatus,
8655
- e3 as suggestionSubject,
8656
- e4 as suggestionTouches,
9076
+ eh as suggestionSubject,
9077
+ ei as suggestionTouches,
8657
9078
  summarizeApprovals,
8658
9079
  summarizeCi,
8659
9080
  summarizeComponent,
8660
- e5 as summarizeSuggestionApprovals,
8661
- cg as summarizeTokenChange,
9081
+ ej as summarizeSuggestionApprovals,
9082
+ cl as summarizeTokenChange,
8662
9083
  switchToPublishedOnGitHub,
8663
- e6 as syncSuggestionStateLabel,
9084
+ ek as syncSuggestionStateLabel,
8664
9085
  takeMine,
8665
- ch as tarballAssetName,
8666
- ci as tarballUrl,
8667
- cj as teamKey,
8668
- ck as teamKeyOf,
8669
- cl as teamName,
8670
- cm as teamUnreadableMessage,
8671
- cn as teamsIn,
9086
+ cm as tarballAssetName,
9087
+ cn as tarballUrl,
9088
+ co as teamKey,
9089
+ cp as teamKeyOf,
9090
+ cq as teamName,
9091
+ cr as teamUnreadableMessage,
9092
+ cs as teamsIn,
9093
+ thingOf,
8672
9094
  threeWayDiff,
8673
9095
  tier1Domain,
8674
9096
  tier1PublishOf,
@@ -8676,23 +9098,23 @@ export {
8676
9098
  tier1PublishSentence,
8677
9099
  tier1PublishedBy,
8678
9100
  tintedNeutral,
8679
- e7 as titleOf,
9101
+ el as titleOf,
8680
9102
  titleWithJiraKey,
8681
- co as toAlias,
8682
- e8 as toChangeSummary,
9103
+ ct as toAlias,
9104
+ em as toChangeSummary,
8683
9105
  toConfigJson,
8684
9106
  toHex,
8685
- cp as toMap,
9107
+ cu as toMap,
8686
9108
  toOklch,
8687
- cq as toPlain,
8688
- cr as tokenBump,
8689
- e9 as tokenCssVar,
9109
+ cv as toPlain,
9110
+ cw as tokenBump,
9111
+ en as tokenCssVar,
8690
9112
  tokenFileChanges,
8691
9113
  tokenFilesOf,
8692
- ea as tokenFingerprint,
8693
- eb as tokenHistory,
9114
+ eo as tokenFingerprint,
9115
+ ep as tokenHistory,
8694
9116
  t as tokenLabel,
8695
- cs as tokenNode,
9117
+ cx as tokenNode,
8696
9118
  tokenPublishRoute,
8697
9119
  tokenRepoFromMeasure,
8698
9120
  tokenRowAxes,
@@ -8703,7 +9125,7 @@ export {
8703
9125
  tokenSuggestionPayload,
8704
9126
  tokenUsage,
8705
9127
  tokenValueCss,
8706
- ec as tokenVariablePath,
9128
+ eq as tokenVariablePath,
8707
9129
  tokensPath,
8708
9130
  touchesLayout,
8709
9131
  touchesPackages,
@@ -8712,50 +9134,53 @@ export {
8712
9134
  tryInProductUrl,
8713
9135
  tryTogetherUrl,
8714
9136
  unpicked,
8715
- ct as unreadableShipTeams,
8716
- cu as unreadableTeams,
8717
- ed as unsafePathProblem,
9137
+ cy as unreadableShipTeams,
9138
+ cz as unreadableTeams,
9139
+ er as unsafePathProblem,
8718
9140
  updatePendingDrafts,
8719
9141
  upsertToken,
8720
9142
  useLibraryPreviewOnGitHub,
8721
- ee as utf8ToBase64,
8722
- ef as validateComponentSpec,
8723
- eg as validateDocs,
9143
+ es as utf8ToBase64,
9144
+ et as validateComponentSpec,
9145
+ eu as validateDocs,
8724
9146
  validateDocsTask,
8725
9147
  validateFigmaFixes,
8726
- eh as validateIntegrations,
9148
+ ev as validateIntegrations,
8727
9149
  validateLayered,
8728
9150
  validatePendingDrafts,
8729
9151
  validateProposal,
8730
9152
  validateSet,
8731
- cv as valueIn,
8732
- cw as valueToCss,
9153
+ cA as valueIn,
9154
+ cB as valueToCss,
8733
9155
  valuesEqual,
8734
9156
  variantCombosFromMapping,
8735
9157
  variantDifferences,
8736
9158
  variantKeyOf,
8737
- ei as variantName,
9159
+ ew as variantName,
8738
9160
  variantValuesFromMapping,
8739
- ej as versionTimeline,
8740
- cx as visualImpactProblems,
8741
- cy as visualOffer,
8742
- ek as webStorageEtagStore,
8743
- cz as whoCanShipSentence,
8744
- cA as whyItExists,
8745
- el as withAuthorLine,
9161
+ ex as versionTimeline,
9162
+ cC as visualImpactProblems,
9163
+ cD as visualOffer,
9164
+ ey as webStorageEtagStore,
9165
+ whereItStands,
9166
+ cE as whoCanShipSentence,
9167
+ cF as whyItExists,
9168
+ ez as withAuthorLine,
9169
+ withDoneExploring,
8746
9170
  withFigmaOrigin,
8747
- em as withFigmaPushApprovals,
9171
+ eA as withFigmaPushApprovals,
8748
9172
  withLink,
8749
9173
  withNeededBy,
8750
- en as withOutdated,
9174
+ eB as withOutdated,
8751
9175
  withPiecesRecord,
8752
9176
  withRepoTokenPaths,
8753
- eo as withRequestExploration,
8754
- ep as withSrgbColors,
8755
- eq as withSuggestionApprovals,
8756
- er as withTokenValueApprovals,
9177
+ eC as withRequestExploration,
9178
+ eD as withSrgbColors,
9179
+ eE as withSuggestionApprovals,
9180
+ eF as withTokenValueApprovals,
8757
9181
  withoutAddedWrappers,
8758
- es as withoutJiraKey,
9182
+ eG as withoutJiraKey,
9183
+ wordsOf,
8759
9184
  writeDraftResults,
8760
9185
  writeTokenProposalOnGitHub
8761
9186
  };