webstudio 0.290.0 → 0.291.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/lib/cli.js CHANGED
@@ -560,85 +560,88 @@ const getFontFormat = (asset2) => {
560
560
  const extension2 = asset2.name.slice(asset2.name.lastIndexOf(".") + 1);
561
561
  return FONT_FORMATS.has(extension2) ? FONT_FORMATS.get(extension2) ?? asset2.format : FONT_FORMATS.get(asset2.format) ?? asset2.format;
562
562
  };
563
- const formatFace = (asset2, format2, url2) => {
563
+ const formatFace = ({ asset: asset2, format: format2, style, url: url2 }) => {
564
+ const face = {
565
+ fontFamily: asset2.meta.family,
566
+ fontStyle: style,
567
+ fontDisplay: "swap",
568
+ src: `url(${sanitizeCssUrl$1(url2)}) format("${format2}")`
569
+ };
564
570
  if ("variationAxes" in asset2.meta) {
565
- const { wght, wdth } = asset2.meta?.variationAxes ?? {};
571
+ const { wght, wdth } = asset2.meta.variationAxes;
566
572
  return {
567
- fontFamily: asset2.meta.family,
568
- fontStyle: "normal",
569
- fontDisplay: "swap",
570
- src: `url(${sanitizeCssUrl$1(url2)}) format("${format2}")`,
573
+ ...face,
571
574
  fontStretch: wdth ? `${wdth.min}% ${wdth.max}%` : void 0,
572
575
  fontWeight: wght ? `${wght.min} ${wght.max}` : void 0
573
576
  };
574
577
  }
575
578
  return {
576
- fontFamily: asset2.meta.family,
577
- fontStyle: asset2.meta.style,
578
- fontWeight: asset2.meta.weight,
579
- fontDisplay: "swap",
580
- src: `url(${sanitizeCssUrl$1(url2)}) format("${format2}")`
579
+ ...face,
580
+ fontWeight: asset2.meta.weight
581
581
  };
582
582
  };
583
- const getKey = (asset2) => {
583
+ const getKey = (asset2, style) => {
584
584
  if ("variationAxes" in asset2.meta) {
585
- return asset2.meta.family + Object.values(asset2.meta.variationAxes).join("");
585
+ const { wght, wdth } = asset2.meta.variationAxes;
586
+ return JSON.stringify([asset2.meta.family, style, wght, wdth]);
587
+ }
588
+ return JSON.stringify([asset2.meta.family, style, asset2.meta.weight]);
589
+ };
590
+ const getStyles$1 = (meta2) => {
591
+ const styles = /* @__PURE__ */ new Set([meta2.style]);
592
+ if ("variationAxes" in meta2) {
593
+ const { ital, slnt } = meta2.variationAxes;
594
+ if (ital !== void 0 && ital.min <= 0 && ital.max >= 1) {
595
+ styles.add("normal");
596
+ styles.add("italic");
597
+ }
598
+ if (slnt !== void 0 && slnt.min < slnt.max && slnt.min <= 0 && slnt.max >= 0) {
599
+ styles.add("normal");
600
+ styles.add("oblique");
601
+ }
586
602
  }
587
- return asset2.meta.family + asset2.meta.style + asset2.meta.weight;
603
+ return Array.from(styles);
588
604
  };
589
605
  const getFontFaces = (assets, options) => {
590
606
  const { assetBaseUrl } = options;
591
607
  const faces = /* @__PURE__ */ new Map();
592
- const seenUrls = /* @__PURE__ */ new Set();
608
+ const seenSources = /* @__PURE__ */ new Set();
593
609
  for (const asset2 of assets) {
594
610
  const url2 = `${assetBaseUrl}${asset2.name}`;
595
- const assetKey = getKey(asset2);
596
- if (seenUrls.has(url2)) {
597
- continue;
598
- }
599
- seenUrls.add(url2);
600
- const format2 = getFontFormat(asset2);
601
- const sources = faces.get(assetKey) ?? /* @__PURE__ */ new Map();
602
- const existing = sources.get(format2);
603
- if (existing === void 0 || url2 < existing.url) {
604
- sources.set(format2, { asset: asset2, format: format2, url: url2 });
611
+ for (const style of getStyles$1(asset2.meta)) {
612
+ const sourceKey = JSON.stringify([url2, style]);
613
+ if (seenSources.has(sourceKey)) {
614
+ continue;
615
+ }
616
+ seenSources.add(sourceKey);
617
+ const assetKey = getKey(asset2, style);
618
+ const format2 = getFontFormat(asset2);
619
+ const sources = faces.get(assetKey) ?? /* @__PURE__ */ new Map();
620
+ const existing = sources.get(format2);
621
+ if (existing === void 0 || url2 < existing.url) {
622
+ sources.set(format2, { asset: asset2, format: format2, style, url: url2 });
623
+ }
624
+ faces.set(assetKey, sources);
605
625
  }
606
- faces.set(assetKey, sources);
607
626
  }
608
627
  return Array.from(faces.values(), (sources) => {
609
- const [{ asset: asset2, format: format2, url: url2 }, ...fallbacks] = Array.from(
610
- sources.values()
611
- ).sort(
628
+ const [source, ...fallbacks] = Array.from(sources.values()).sort(
612
629
  (left, right) => fontFormatOrder.indexOf(left.format) - fontFormatOrder.indexOf(right.format)
613
630
  );
614
- const face = formatFace(asset2, format2, url2);
631
+ const face = formatFace(source);
615
632
  face.src += fallbacks.map(
616
633
  ({ format: fallbackFormat, url: fallbackUrl }) => `, url(${sanitizeCssUrl$1(fallbackUrl)}) format("${fallbackFormat}")`
617
634
  ).join("");
618
635
  return face;
619
636
  });
620
637
  };
638
+ const fontStyle = z$4.enum(FONT_STYLES);
621
639
  const fontFormat = z$4.union([
622
640
  z$4.literal("ttf"),
623
641
  z$4.literal("woff"),
624
642
  z$4.literal("woff2")
625
643
  ]);
626
- const axisName = z$4.enum([
627
- "wght",
628
- "wdth",
629
- "slnt",
630
- "opsz",
631
- "ital",
632
- "GRAD",
633
- "XTRA",
634
- "XOPQ",
635
- "YOPQ",
636
- "YTLC",
637
- "YTUC",
638
- "YTAS",
639
- "YTDE",
640
- "YTFI"
641
- ]);
644
+ const axisName = z$4.string().regex(/^[A-Za-z][A-Za-z0-9]* *$/).length(4);
642
645
  const variationAxes = z$4.partialRecord(
643
646
  axisName,
644
647
  z$4.object({
@@ -650,33 +653,38 @@ const variationAxes = z$4.partialRecord(
650
653
  );
651
654
  const fontMetaStatic = z$4.object({
652
655
  family: z$4.string(),
653
- style: z$4.enum(FONT_STYLES),
656
+ style: fontStyle,
654
657
  weight: z$4.number()
655
658
  });
656
659
  const fontMetaVariable = z$4.object({
657
660
  family: z$4.string(),
661
+ style: fontStyle.default("normal"),
658
662
  variationAxes
659
663
  });
660
664
  const fontMeta = z$4.union([fontMetaStatic, fontMetaVariable]);
661
665
  const fontMetaUpdate = z$4.union([
662
666
  fontMetaStatic.partial().strict(),
663
- fontMetaVariable.partial().strict()
667
+ z$4.object({
668
+ family: fontMetaVariable.shape.family.optional(),
669
+ style: fontStyle.optional(),
670
+ variationAxes: fontMetaVariable.shape.variationAxes.optional()
671
+ }).strict()
664
672
  ]);
665
673
  const mergeFontMeta = (current4, override) => {
666
674
  const result2 = fontMetaUpdate.safeParse(override);
667
675
  if (result2.success === false) {
668
676
  return;
669
677
  }
670
- const { family } = result2.data;
678
+ const { family, style } = result2.data;
671
679
  if ("variationAxes" in current4) {
672
680
  const variationAxes2 = "variationAxes" in result2.data ? result2.data.variationAxes : void 0;
673
681
  return {
674
682
  ...current4,
675
683
  ...family === void 0 ? {} : { family },
684
+ ...style === void 0 ? {} : { style },
676
685
  ...variationAxes2 === void 0 ? {} : { variationAxes: variationAxes2 }
677
686
  };
678
687
  }
679
- const style = "style" in result2.data ? result2.data.style : void 0;
680
688
  const weight = "weight" in result2.data ? result2.data.weight : void 0;
681
689
  return {
682
690
  ...current4,
@@ -18134,6 +18142,17 @@ const pages = z$4.object({
18134
18142
  }).superRefine((pages2, context) => {
18135
18143
  const homePage = pages2.pages.get(pages2.homePageId);
18136
18144
  const rootFolder = pages2.folders.get(pages2.rootFolderId);
18145
+ const folderIndexPageIds = /* @__PURE__ */ new Set();
18146
+ for (const [folderId2, folder2] of pages2.folders) {
18147
+ if (folderId2 === pages2.rootFolderId) {
18148
+ continue;
18149
+ }
18150
+ for (const childId of folder2.children) {
18151
+ if (pages2.pages.has(childId)) {
18152
+ folderIndexPageIds.add(childId);
18153
+ }
18154
+ }
18155
+ }
18137
18156
  if (homePage === void 0) {
18138
18157
  context.addIssue({
18139
18158
  code: z$4.ZodIssueCode.custom,
@@ -18163,7 +18182,7 @@ const pages = z$4.object({
18163
18182
  message: "Page id must match its record key"
18164
18183
  });
18165
18184
  }
18166
- if (pageId2 !== pages2.homePageId && page2.path === "") {
18185
+ if (pageId2 !== pages2.homePageId && page2.path === "" && folderIndexPageIds.has(pageId2) === false) {
18167
18186
  context.addIssue({
18168
18187
  code: z$4.ZodIssueCode.custom,
18169
18188
  path: ["pages", pageId2, "path"],
@@ -19018,14 +19037,14 @@ class FontFaceRule {
19018
19037
  return this.#cached;
19019
19038
  }
19020
19039
  const decls = [];
19021
- const { fontFamily, fontStyle, fontWeight, fontDisplay, src } = this.#options;
19040
+ const { fontFamily, fontStyle: fontStyle2, fontWeight, fontDisplay, src } = this.#options;
19022
19041
  const value2 = toValue(
19023
19042
  { type: "fontFamily", value: [fontFamily] },
19024
19043
  // Avoids adding a fallback automatically which needs to happen for font family in general but not for font face.
19025
19044
  (value22) => value22
19026
19045
  );
19027
19046
  decls.push(`font-family: ${value2}`);
19028
- decls.push(`font-style: ${fontStyle}`);
19047
+ decls.push(`font-style: ${fontStyle2}`);
19029
19048
  decls.push(`font-weight: ${fontWeight}`);
19030
19049
  decls.push(`font-display: ${fontDisplay}`);
19031
19050
  decls.push(`src: ${src}`);
@@ -24912,10 +24931,10 @@ const extractFontMetadata = (asset2) => {
24912
24931
  return;
24913
24932
  }
24914
24933
  const metadata = {
24915
- family: asset2.meta.family
24934
+ family: asset2.meta.family,
24935
+ style: asset2.meta.style
24916
24936
  };
24917
- if ("style" in asset2.meta) {
24918
- metadata.style = asset2.meta.style;
24937
+ if ("weight" in asset2.meta) {
24919
24938
  metadata.weight = asset2.meta.weight;
24920
24939
  }
24921
24940
  return metadata;
@@ -30881,26 +30900,18 @@ const runtimeOperationContractData = [
30881
30900
  family: {
30882
30901
  type: "string"
30883
30902
  },
30903
+ style: {
30904
+ default: "normal",
30905
+ type: "string",
30906
+ enum: ["normal", "italic", "oblique"]
30907
+ },
30884
30908
  variationAxes: {
30885
30909
  type: "object",
30886
30910
  propertyNames: {
30887
30911
  type: "string",
30888
- enum: [
30889
- "wght",
30890
- "wdth",
30891
- "slnt",
30892
- "opsz",
30893
- "ital",
30894
- "GRAD",
30895
- "XTRA",
30896
- "XOPQ",
30897
- "YOPQ",
30898
- "YTLC",
30899
- "YTUC",
30900
- "YTAS",
30901
- "YTDE",
30902
- "YTFI"
30903
- ]
30912
+ minLength: 4,
30913
+ maxLength: 4,
30914
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
30904
30915
  },
30905
30916
  additionalProperties: {
30906
30917
  type: "object",
@@ -33981,26 +33992,18 @@ const runtimeOperationContractData = [
33981
33992
  family: {
33982
33993
  type: "string"
33983
33994
  },
33995
+ style: {
33996
+ default: "normal",
33997
+ type: "string",
33998
+ enum: ["normal", "italic", "oblique"]
33999
+ },
33984
34000
  variationAxes: {
33985
34001
  type: "object",
33986
34002
  propertyNames: {
33987
34003
  type: "string",
33988
- enum: [
33989
- "wght",
33990
- "wdth",
33991
- "slnt",
33992
- "opsz",
33993
- "ital",
33994
- "GRAD",
33995
- "XTRA",
33996
- "XOPQ",
33997
- "YOPQ",
33998
- "YTLC",
33999
- "YTUC",
34000
- "YTAS",
34001
- "YTDE",
34002
- "YTFI"
34003
- ]
34004
+ minLength: 4,
34005
+ maxLength: 4,
34006
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
34004
34007
  },
34005
34008
  additionalProperties: {
34006
34009
  type: "object",
@@ -37092,26 +37095,18 @@ const runtimeOperationContractData = [
37092
37095
  family: {
37093
37096
  type: "string"
37094
37097
  },
37098
+ style: {
37099
+ default: "normal",
37100
+ type: "string",
37101
+ enum: ["normal", "italic", "oblique"]
37102
+ },
37095
37103
  variationAxes: {
37096
37104
  type: "object",
37097
37105
  propertyNames: {
37098
37106
  type: "string",
37099
- enum: [
37100
- "wght",
37101
- "wdth",
37102
- "slnt",
37103
- "opsz",
37104
- "ital",
37105
- "GRAD",
37106
- "XTRA",
37107
- "XOPQ",
37108
- "YOPQ",
37109
- "YTLC",
37110
- "YTUC",
37111
- "YTAS",
37112
- "YTDE",
37113
- "YTFI"
37114
- ]
37107
+ minLength: 4,
37108
+ maxLength: 4,
37109
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
37115
37110
  },
37116
37111
  additionalProperties: {
37117
37112
  type: "object",
@@ -40192,26 +40187,18 @@ const runtimeOperationContractData = [
40192
40187
  family: {
40193
40188
  type: "string"
40194
40189
  },
40190
+ style: {
40191
+ default: "normal",
40192
+ type: "string",
40193
+ enum: ["normal", "italic", "oblique"]
40194
+ },
40195
40195
  variationAxes: {
40196
40196
  type: "object",
40197
40197
  propertyNames: {
40198
40198
  type: "string",
40199
- enum: [
40200
- "wght",
40201
- "wdth",
40202
- "slnt",
40203
- "opsz",
40204
- "ital",
40205
- "GRAD",
40206
- "XTRA",
40207
- "XOPQ",
40208
- "YOPQ",
40209
- "YTLC",
40210
- "YTUC",
40211
- "YTAS",
40212
- "YTDE",
40213
- "YTFI"
40214
- ]
40199
+ minLength: 4,
40200
+ maxLength: 4,
40201
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
40215
40202
  },
40216
40203
  additionalProperties: {
40217
40204
  type: "object",
@@ -46000,26 +45987,22 @@ const runtimeOperationContractData = [
46000
45987
  family: {
46001
45988
  type: "string"
46002
45989
  },
45990
+ style: {
45991
+ default: "normal",
45992
+ type: "string",
45993
+ enum: [
45994
+ "normal",
45995
+ "italic",
45996
+ "oblique"
45997
+ ]
45998
+ },
46003
45999
  variationAxes: {
46004
46000
  type: "object",
46005
46001
  propertyNames: {
46006
46002
  type: "string",
46007
- enum: [
46008
- "wght",
46009
- "wdth",
46010
- "slnt",
46011
- "opsz",
46012
- "ital",
46013
- "GRAD",
46014
- "XTRA",
46015
- "XOPQ",
46016
- "YOPQ",
46017
- "YTLC",
46018
- "YTUC",
46019
- "YTAS",
46020
- "YTDE",
46021
- "YTFI"
46022
- ]
46003
+ minLength: 4,
46004
+ maxLength: 4,
46005
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
46023
46006
  },
46024
46007
  additionalProperties: {
46025
46008
  type: "object",
@@ -49141,26 +49124,22 @@ const runtimeOperationContractData = [
49141
49124
  family: {
49142
49125
  type: "string"
49143
49126
  },
49127
+ style: {
49128
+ default: "normal",
49129
+ type: "string",
49130
+ enum: [
49131
+ "normal",
49132
+ "italic",
49133
+ "oblique"
49134
+ ]
49135
+ },
49144
49136
  variationAxes: {
49145
49137
  type: "object",
49146
49138
  propertyNames: {
49147
49139
  type: "string",
49148
- enum: [
49149
- "wght",
49150
- "wdth",
49151
- "slnt",
49152
- "opsz",
49153
- "ital",
49154
- "GRAD",
49155
- "XTRA",
49156
- "XOPQ",
49157
- "YOPQ",
49158
- "YTLC",
49159
- "YTUC",
49160
- "YTAS",
49161
- "YTDE",
49162
- "YTFI"
49163
- ]
49140
+ minLength: 4,
49141
+ maxLength: 4,
49142
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
49164
49143
  },
49165
49144
  additionalProperties: {
49166
49145
  type: "object",
@@ -63312,26 +63291,18 @@ const runtimeOperationContractData = [
63312
63291
  family: {
63313
63292
  type: "string"
63314
63293
  },
63294
+ style: {
63295
+ default: "normal",
63296
+ type: "string",
63297
+ enum: ["normal", "italic", "oblique"]
63298
+ },
63315
63299
  variationAxes: {
63316
63300
  type: "object",
63317
63301
  propertyNames: {
63318
63302
  type: "string",
63319
- enum: [
63320
- "wght",
63321
- "wdth",
63322
- "slnt",
63323
- "opsz",
63324
- "ital",
63325
- "GRAD",
63326
- "XTRA",
63327
- "XOPQ",
63328
- "YOPQ",
63329
- "YTLC",
63330
- "YTUC",
63331
- "YTAS",
63332
- "YTDE",
63333
- "YTFI"
63334
- ]
63303
+ minLength: 4,
63304
+ maxLength: 4,
63305
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
63335
63306
  },
63336
63307
  additionalProperties: {
63337
63308
  type: "object",
@@ -66206,9 +66177,11 @@ const runtimeOperationContractData = [
66206
66177
  },
66207
66178
  conflictResolution: {
66208
66179
  type: "string",
66209
- enum: ["ours", "theirs", "merge"]
66180
+ enum: ["ours", "theirs", "merge"],
66181
+ description: 'How to resolve incoming design tokens that share a name with an existing token. "ours" keeps the existing token styles and id, "theirs" uses incoming styles, and "merge" combines both.'
66210
66182
  },
66211
66183
  contentMode: {
66184
+ description: "Apply content-mode copy restrictions to props and styles while reusing existing tokens and breakpoints.",
66212
66185
  type: "boolean"
66213
66186
  },
66214
66187
  mode: {
@@ -66484,26 +66457,18 @@ const runtimeOperationContractData = [
66484
66457
  family: {
66485
66458
  type: "string"
66486
66459
  },
66460
+ style: {
66461
+ default: "normal",
66462
+ type: "string",
66463
+ enum: ["normal", "italic", "oblique"]
66464
+ },
66487
66465
  variationAxes: {
66488
66466
  type: "object",
66489
66467
  propertyNames: {
66490
66468
  type: "string",
66491
- enum: [
66492
- "wght",
66493
- "wdth",
66494
- "slnt",
66495
- "opsz",
66496
- "ital",
66497
- "GRAD",
66498
- "XTRA",
66499
- "XOPQ",
66500
- "YOPQ",
66501
- "YTLC",
66502
- "YTUC",
66503
- "YTAS",
66504
- "YTDE",
66505
- "YTFI"
66506
- ]
66469
+ minLength: 4,
66470
+ maxLength: 4,
66471
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
66507
66472
  },
66508
66473
  additionalProperties: {
66509
66474
  type: "object",
@@ -72764,26 +72729,18 @@ const runtimeOperationContractData = [
72764
72729
  family: {
72765
72730
  type: "string"
72766
72731
  },
72732
+ style: {
72733
+ default: "normal",
72734
+ type: "string",
72735
+ enum: ["normal", "italic", "oblique"]
72736
+ },
72767
72737
  variationAxes: {
72768
72738
  type: "object",
72769
72739
  propertyNames: {
72770
72740
  type: "string",
72771
- enum: [
72772
- "wght",
72773
- "wdth",
72774
- "slnt",
72775
- "opsz",
72776
- "ital",
72777
- "GRAD",
72778
- "XTRA",
72779
- "XOPQ",
72780
- "YOPQ",
72781
- "YTLC",
72782
- "YTUC",
72783
- "YTAS",
72784
- "YTDE",
72785
- "YTFI"
72786
- ]
72741
+ minLength: 4,
72742
+ maxLength: 4,
72743
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
72787
72744
  },
72788
72745
  additionalProperties: {
72789
72746
  type: "object",
@@ -78581,26 +78538,18 @@ const runtimeOperationContractData = [
78581
78538
  family: {
78582
78539
  type: "string"
78583
78540
  },
78541
+ style: {
78542
+ default: "normal",
78543
+ type: "string",
78544
+ enum: ["normal", "italic", "oblique"]
78545
+ },
78584
78546
  variationAxes: {
78585
78547
  type: "object",
78586
78548
  propertyNames: {
78587
78549
  type: "string",
78588
- enum: [
78589
- "wght",
78590
- "wdth",
78591
- "slnt",
78592
- "opsz",
78593
- "ital",
78594
- "GRAD",
78595
- "XTRA",
78596
- "XOPQ",
78597
- "YOPQ",
78598
- "YTLC",
78599
- "YTUC",
78600
- "YTAS",
78601
- "YTDE",
78602
- "YTFI"
78603
- ]
78550
+ minLength: 4,
78551
+ maxLength: 4,
78552
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
78604
78553
  },
78605
78554
  additionalProperties: {
78606
78555
  type: "object",
@@ -81360,9 +81309,11 @@ const runtimeOperationContractData = [
81360
81309
  },
81361
81310
  conflictResolution: {
81362
81311
  type: "string",
81363
- enum: ["ours", "theirs", "merge"]
81312
+ enum: ["ours", "theirs", "merge"],
81313
+ description: 'How to resolve incoming design tokens that share a name with an existing token. "ours" keeps the existing token styles and id, "theirs" uses incoming styles, and "merge" combines both.'
81364
81314
  },
81365
81315
  contentMode: {
81316
+ description: "Apply content-mode copy restrictions to props and styles while reusing existing tokens and breakpoints.",
81366
81317
  type: "boolean"
81367
81318
  },
81368
81319
  mode: {
@@ -97239,26 +97190,17 @@ const runtimeOperationContractData = [
97239
97190
  family: {
97240
97191
  type: "string"
97241
97192
  },
97193
+ style: {
97194
+ type: "string",
97195
+ enum: ["normal", "italic", "oblique"]
97196
+ },
97242
97197
  variationAxes: {
97243
97198
  type: "object",
97244
97199
  propertyNames: {
97245
97200
  type: "string",
97246
- enum: [
97247
- "wght",
97248
- "wdth",
97249
- "slnt",
97250
- "opsz",
97251
- "ital",
97252
- "GRAD",
97253
- "XTRA",
97254
- "XOPQ",
97255
- "YOPQ",
97256
- "YTLC",
97257
- "YTUC",
97258
- "YTAS",
97259
- "YTDE",
97260
- "YTFI"
97261
- ]
97201
+ minLength: 4,
97202
+ maxLength: 4,
97203
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
97262
97204
  },
97263
97205
  additionalProperties: {
97264
97206
  type: "object",
@@ -97305,7 +97247,7 @@ const runtimeOperationContractData = [
97305
97247
  required: []
97306
97248
  }
97307
97249
  ],
97308
- description: "Type-specific metadata: family/style/weight or variationAxes for fonts, width/height for images, and no fields for generic files."
97250
+ description: "Type-specific metadata: family and style plus weight for static fonts or variationAxes for variable fonts, width/height for images, and no fields for generic files."
97309
97251
  }
97310
97252
  },
97311
97253
  required: []
@@ -97498,26 +97440,18 @@ const runtimeOperationContractData = [
97498
97440
  family: {
97499
97441
  type: "string"
97500
97442
  },
97443
+ style: {
97444
+ default: "normal",
97445
+ type: "string",
97446
+ enum: ["normal", "italic", "oblique"]
97447
+ },
97501
97448
  variationAxes: {
97502
97449
  type: "object",
97503
97450
  propertyNames: {
97504
97451
  type: "string",
97505
- enum: [
97506
- "wght",
97507
- "wdth",
97508
- "slnt",
97509
- "opsz",
97510
- "ital",
97511
- "GRAD",
97512
- "XTRA",
97513
- "XOPQ",
97514
- "YOPQ",
97515
- "YTLC",
97516
- "YTUC",
97517
- "YTAS",
97518
- "YTDE",
97519
- "YTFI"
97520
- ]
97452
+ minLength: 4,
97453
+ maxLength: 4,
97454
+ pattern: "^[A-Za-z][A-Za-z0-9]* *$"
97521
97455
  },
97522
97456
  additionalProperties: {
97523
97457
  type: "object",
@@ -99227,7 +99161,7 @@ const assetUpdateInput = z$4.object({
99227
99161
  description: z$4.union([z$4.string(), z$4.null()]).optional(),
99228
99162
  folderId: z$4.union([z$4.string().min(1), z$4.null()]).optional(),
99229
99163
  meta: assetMetaUpdate.describe(
99230
- "Type-specific metadata: family/style/weight or variationAxes for fonts, width/height for images, and no fields for generic files."
99164
+ "Type-specific metadata: family and style plus weight for static fonts or variationAxes for variable fonts, width/height for images, and no fields for generic files."
99231
99165
  ).optional()
99232
99166
  }).refine(
99233
99167
  (values) => Object.keys(values).length > 0,
@@ -118200,7 +118134,7 @@ const expandBorderImage = function* (value2) {
118200
118134
  yield ["border-image-repeat", repeat ?? createInitialNode()];
118201
118135
  };
118202
118136
  const expandFont = function* (value2) {
118203
- const [fontStyle, fontVariant, fontWeight, fontWidth, config] = parseUnordered(
118137
+ const [fontStyle2, fontVariant, fontWeight, fontWidth, config] = parseUnordered(
118204
118138
  [
118205
118139
  "<'font-style'>",
118206
118140
  "<font-variant-css21>",
@@ -118224,7 +118158,7 @@ const expandFont = function* (value2) {
118224
118158
  fontFamily = createValueNode(fontFamilyNodes);
118225
118159
  }
118226
118160
  }
118227
- yield ["font-style", fontStyle ?? createInitialNode()];
118161
+ yield ["font-style", fontStyle2 ?? createInitialNode()];
118228
118162
  yield ["font-variant-caps", fontVariant ?? createInitialNode()];
118229
118163
  yield ["font-weight", fontWeight ?? createInitialNode()];
118230
118164
  yield ["font-stretch", fontWidth ?? createInitialNode()];
@@ -148088,20 +148022,20 @@ const renderTemplate = (root2, generateId, initialBreakpoints = [], options = {}
148088
148022
  assets: []
148089
148023
  };
148090
148024
  };
148091
- const createProxy = (prefix) => {
148025
+ const createProxy = (prefix, transformName = (name2) => name2) => {
148092
148026
  return new Proxy(
148093
148027
  {},
148094
148028
  {
148095
148029
  get(_target, prop2) {
148096
148030
  const component = () => void 0;
148097
- component.displayName = `${prefix}${prop2}`;
148031
+ component.displayName = `${prefix}${transformName(String(prop2))}`;
148098
148032
  return component;
148099
148033
  }
148100
148034
  }
148101
148035
  );
148102
148036
  };
148103
148037
  const $$3 = createProxy("");
148104
- const ws = createProxy("ws:");
148038
+ const ws = createProxy("ws:", kebabCase);
148105
148039
  var jsxRuntime = { exports: {} };
148106
148040
  var reactJsxRuntime_production_min = {};
148107
148041
  var hasRequiredReactJsxRuntime_production_min;
@@ -209235,8 +209169,8 @@ function _matchesScope(scopeName, scopePattern) {
209235
209169
  return scopePattern === scopeName || scopeName.startsWith(scopePattern) && scopeName[scopePattern.length] === ".";
209236
209170
  }
209237
209171
  var StyleAttributes = class {
209238
- constructor(fontStyle, foregroundId, backgroundId) {
209239
- this.fontStyle = fontStyle;
209172
+ constructor(fontStyle2, foregroundId, backgroundId) {
209173
+ this.fontStyle = fontStyle2;
209240
209174
  this.foregroundId = foregroundId;
209241
209175
  this.backgroundId = backgroundId;
209242
209176
  }
@@ -209266,24 +209200,24 @@ function parseTheme(source) {
209266
209200
  } else {
209267
209201
  scopes = [""];
209268
209202
  }
209269
- let fontStyle = -1;
209203
+ let fontStyle2 = -1;
209270
209204
  if (typeof entry2.settings.fontStyle === "string") {
209271
- fontStyle = 0;
209205
+ fontStyle2 = 0;
209272
209206
  let segments = entry2.settings.fontStyle.split(" ");
209273
209207
  for (let j2 = 0, lenJ = segments.length; j2 < lenJ; j2++) {
209274
209208
  let segment = segments[j2];
209275
209209
  switch (segment) {
209276
209210
  case "italic":
209277
- fontStyle = fontStyle | 1;
209211
+ fontStyle2 = fontStyle2 | 1;
209278
209212
  break;
209279
209213
  case "bold":
209280
- fontStyle = fontStyle | 2;
209214
+ fontStyle2 = fontStyle2 | 2;
209281
209215
  break;
209282
209216
  case "underline":
209283
- fontStyle = fontStyle | 4;
209217
+ fontStyle2 = fontStyle2 | 4;
209284
209218
  break;
209285
209219
  case "strikethrough":
209286
- fontStyle = fontStyle | 8;
209220
+ fontStyle2 = fontStyle2 | 8;
209287
209221
  break;
209288
209222
  }
209289
209223
  }
@@ -209309,7 +209243,7 @@ function parseTheme(source) {
209309
209243
  scope2,
209310
209244
  parentScopes,
209311
209245
  i,
209312
- fontStyle,
209246
+ fontStyle2,
209313
209247
  foreground,
209314
209248
  background
209315
209249
  );
@@ -209318,11 +209252,11 @@ function parseTheme(source) {
209318
209252
  return result2;
209319
209253
  }
209320
209254
  var ParsedThemeRule = class {
209321
- constructor(scope2, parentScopes, index2, fontStyle, foreground, background) {
209255
+ constructor(scope2, parentScopes, index2, fontStyle2, foreground, background) {
209322
209256
  this.scope = scope2;
209323
209257
  this.parentScopes = parentScopes;
209324
209258
  this.index = index2;
209325
- this.fontStyle = fontStyle;
209259
+ this.fontStyle = fontStyle2;
209326
209260
  this.foreground = foreground;
209327
209261
  this.background = background;
209328
209262
  }
@@ -209419,10 +209353,10 @@ var ThemeTrieElementRule = class _ThemeTrieElementRule {
209419
209353
  fontStyle;
209420
209354
  foreground;
209421
209355
  background;
209422
- constructor(scopeDepth, parentScopes, fontStyle, foreground, background) {
209356
+ constructor(scopeDepth, parentScopes, fontStyle2, foreground, background) {
209423
209357
  this.scopeDepth = scopeDepth;
209424
209358
  this.parentScopes = parentScopes || emptyParentScopes;
209425
- this.fontStyle = fontStyle;
209359
+ this.fontStyle = fontStyle2;
209426
209360
  this.foreground = foreground;
209427
209361
  this.background = background;
209428
209362
  }
@@ -209436,14 +209370,14 @@ var ThemeTrieElementRule = class _ThemeTrieElementRule {
209436
209370
  }
209437
209371
  return r2;
209438
209372
  }
209439
- acceptOverwrite(scopeDepth, fontStyle, foreground, background) {
209373
+ acceptOverwrite(scopeDepth, fontStyle2, foreground, background) {
209440
209374
  if (this.scopeDepth > scopeDepth) {
209441
209375
  console.log("how did this happen?");
209442
209376
  } else {
209443
209377
  this.scopeDepth = scopeDepth;
209444
209378
  }
209445
- if (fontStyle !== -1) {
209446
- this.fontStyle = fontStyle;
209379
+ if (fontStyle2 !== -1) {
209380
+ this.fontStyle = fontStyle2;
209447
209381
  }
209448
209382
  if (foreground !== 0) {
209449
209383
  this.foreground = foreground;
@@ -209505,9 +209439,9 @@ var ThemeTrieElement = class _ThemeTrieElement {
209505
209439
  rules.sort(_ThemeTrieElement._cmpBySpecificity);
209506
209440
  return rules;
209507
209441
  }
209508
- insert(scopeDepth, scope2, parentScopes, fontStyle, foreground, background) {
209442
+ insert(scopeDepth, scope2, parentScopes, fontStyle2, foreground, background) {
209509
209443
  if (scope2 === "") {
209510
- this._doInsertHere(scopeDepth, parentScopes, fontStyle, foreground, background);
209444
+ this._doInsertHere(scopeDepth, parentScopes, fontStyle2, foreground, background);
209511
209445
  return;
209512
209446
  }
209513
209447
  let dotIndex = scope2.indexOf(".");
@@ -209527,22 +209461,22 @@ var ThemeTrieElement = class _ThemeTrieElement {
209527
209461
  child = new _ThemeTrieElement(this._mainRule.clone(), ThemeTrieElementRule.cloneArr(this._rulesWithParentScopes));
209528
209462
  this._children[head2] = child;
209529
209463
  }
209530
- child.insert(scopeDepth + 1, tail, parentScopes, fontStyle, foreground, background);
209464
+ child.insert(scopeDepth + 1, tail, parentScopes, fontStyle2, foreground, background);
209531
209465
  }
209532
- _doInsertHere(scopeDepth, parentScopes, fontStyle, foreground, background) {
209466
+ _doInsertHere(scopeDepth, parentScopes, fontStyle2, foreground, background) {
209533
209467
  if (parentScopes === null) {
209534
- this._mainRule.acceptOverwrite(scopeDepth, fontStyle, foreground, background);
209468
+ this._mainRule.acceptOverwrite(scopeDepth, fontStyle2, foreground, background);
209535
209469
  return;
209536
209470
  }
209537
209471
  for (let i = 0, len = this._rulesWithParentScopes.length; i < len; i++) {
209538
209472
  let rule2 = this._rulesWithParentScopes[i];
209539
209473
  if (strArrCmp(rule2.parentScopes, parentScopes) === 0) {
209540
- rule2.acceptOverwrite(scopeDepth, fontStyle, foreground, background);
209474
+ rule2.acceptOverwrite(scopeDepth, fontStyle2, foreground, background);
209541
209475
  return;
209542
209476
  }
209543
209477
  }
209544
- if (fontStyle === -1) {
209545
- fontStyle = this._mainRule.fontStyle;
209478
+ if (fontStyle2 === -1) {
209479
+ fontStyle2 = this._mainRule.fontStyle;
209546
209480
  }
209547
209481
  if (foreground === 0) {
209548
209482
  foreground = this._mainRule.foreground;
@@ -209550,7 +209484,7 @@ var ThemeTrieElement = class _ThemeTrieElement {
209550
209484
  if (background === 0) {
209551
209485
  background = this._mainRule.background;
209552
209486
  }
209553
- this._rulesWithParentScopes.push(new ThemeTrieElementRule(scopeDepth, parentScopes, fontStyle, foreground, background));
209487
+ this._rulesWithParentScopes.push(new ThemeTrieElementRule(scopeDepth, parentScopes, fontStyle2, foreground, background));
209554
209488
  }
209555
209489
  };
209556
209490
  var EncodedTokenMetadata = class _EncodedTokenMetadata {
@@ -209560,13 +209494,13 @@ var EncodedTokenMetadata = class _EncodedTokenMetadata {
209560
209494
  static print(encodedTokenAttributes) {
209561
209495
  const languageId = _EncodedTokenMetadata.getLanguageId(encodedTokenAttributes);
209562
209496
  const tokenType2 = _EncodedTokenMetadata.getTokenType(encodedTokenAttributes);
209563
- const fontStyle = _EncodedTokenMetadata.getFontStyle(encodedTokenAttributes);
209497
+ const fontStyle2 = _EncodedTokenMetadata.getFontStyle(encodedTokenAttributes);
209564
209498
  const foreground = _EncodedTokenMetadata.getForeground(encodedTokenAttributes);
209565
209499
  const background = _EncodedTokenMetadata.getBackground(encodedTokenAttributes);
209566
209500
  console.log({
209567
209501
  languageId,
209568
209502
  tokenType: tokenType2,
209569
- fontStyle,
209503
+ fontStyle: fontStyle2,
209570
209504
  foreground,
209571
209505
  background
209572
209506
  });
@@ -209593,7 +209527,7 @@ var EncodedTokenMetadata = class _EncodedTokenMetadata {
209593
209527
  * Updates the fields in `metadata`.
209594
209528
  * A value of `0`, `NotSet` or `null` indicates that the corresponding field should be left as is.
209595
209529
  */
209596
- static set(encodedTokenAttributes, languageId, tokenType2, containsBalancedBrackets, fontStyle, foreground, background) {
209530
+ static set(encodedTokenAttributes, languageId, tokenType2, containsBalancedBrackets, fontStyle2, foreground, background) {
209597
209531
  let _languageId = _EncodedTokenMetadata.getLanguageId(encodedTokenAttributes);
209598
209532
  let _tokenType = _EncodedTokenMetadata.getTokenType(encodedTokenAttributes);
209599
209533
  let _containsBalancedBracketsBit = _EncodedTokenMetadata.containsBalancedBrackets(encodedTokenAttributes) ? 1 : 0;
@@ -209609,8 +209543,8 @@ var EncodedTokenMetadata = class _EncodedTokenMetadata {
209609
209543
  if (containsBalancedBrackets !== null) {
209610
209544
  _containsBalancedBracketsBit = containsBalancedBrackets ? 1 : 0;
209611
209545
  }
209612
- if (fontStyle !== -1) {
209613
- _fontStyle = fontStyle;
209546
+ if (fontStyle2 !== -1) {
209547
+ _fontStyle = fontStyle2;
209614
209548
  }
209615
209549
  if (foreground !== 0) {
209616
209550
  _foreground = foreground;
@@ -211472,11 +211406,11 @@ var AttributedScopeStack = class _AttributedScopeStack {
211472
211406
  } while (true);
211473
211407
  }
211474
211408
  static mergeAttributes(existingTokenAttributes, basicScopeAttributes, styleAttributes) {
211475
- let fontStyle = -1;
211409
+ let fontStyle2 = -1;
211476
211410
  let foreground = 0;
211477
211411
  let background = 0;
211478
211412
  if (styleAttributes !== null) {
211479
- fontStyle = styleAttributes.fontStyle;
211413
+ fontStyle2 = styleAttributes.fontStyle;
211480
211414
  foreground = styleAttributes.foregroundId;
211481
211415
  background = styleAttributes.backgroundId;
211482
211416
  }
@@ -211485,7 +211419,7 @@ var AttributedScopeStack = class _AttributedScopeStack {
211485
211419
  basicScopeAttributes.languageId,
211486
211420
  basicScopeAttributes.tokenType,
211487
211421
  null,
211488
- fontStyle,
211422
+ fontStyle2,
211489
211423
  foreground,
211490
211424
  background
211491
211425
  );
@@ -212605,12 +212539,12 @@ function _tokenizeWithTheme(code2, grammar, theme, colorMap, options) {
212605
212539
  if (startIndex === nextStartIndex) continue;
212606
212540
  const metadata = result2.tokens[2 * j2 + 1];
212607
212541
  const color2 = applyColorReplacements(colorMap[EncodedTokenMetadata.getForeground(metadata)], colorReplacements);
212608
- const fontStyle = EncodedTokenMetadata.getFontStyle(metadata);
212542
+ const fontStyle2 = EncodedTokenMetadata.getFontStyle(metadata);
212609
212543
  const token2 = {
212610
212544
  content: line.substring(startIndex, nextStartIndex),
212611
212545
  offset: lineOffset + startIndex,
212612
212546
  color: color2,
212613
- fontStyle
212547
+ fontStyle: fontStyle2
212614
212548
  };
212615
212549
  if (includeExplanation === "tokenType") token2.type = EncodedTokenMetadata.getTokenType(metadata);
212616
212550
  else if (includeExplanation) {
@@ -214217,17 +214151,17 @@ function tokenizeAnsiWithTheme(theme, fileContents, options) {
214217
214151
  color2 = applyColorReplacements(color2, colorReplacements);
214218
214152
  bgColor = applyColorReplacements(bgColor, colorReplacements);
214219
214153
  if (token2.decorations.has("dim")) color2 = dimColor(color2);
214220
- let fontStyle = FontStyle.None;
214221
- if (token2.decorations.has("bold")) fontStyle |= FontStyle.Bold;
214222
- if (token2.decorations.has("italic")) fontStyle |= FontStyle.Italic;
214223
- if (token2.decorations.has("underline")) fontStyle |= FontStyle.Underline;
214224
- if (token2.decorations.has("strikethrough")) fontStyle |= FontStyle.Strikethrough;
214154
+ let fontStyle2 = FontStyle.None;
214155
+ if (token2.decorations.has("bold")) fontStyle2 |= FontStyle.Bold;
214156
+ if (token2.decorations.has("italic")) fontStyle2 |= FontStyle.Italic;
214157
+ if (token2.decorations.has("underline")) fontStyle2 |= FontStyle.Underline;
214158
+ if (token2.decorations.has("strikethrough")) fontStyle2 |= FontStyle.Strikethrough;
214225
214159
  return {
214226
214160
  content: token2.value,
214227
214161
  offset: line[1],
214228
214162
  color: color2,
214229
214163
  bgColor,
214230
- fontStyle
214164
+ fontStyle: fontStyle2
214231
214165
  };
214232
214166
  }));
214233
214167
  }
@@ -229680,7 +229614,9 @@ const findPageAndSelectorByInstanceId = (pages2, instances, startingInstanceId)
229680
229614
  }
229681
229615
  return { pageId: pages2.homePageId, instanceSelector };
229682
229616
  };
229683
- const conflictResolutionInput = z$4.enum(["ours", "theirs", "merge"]);
229617
+ const conflictResolutionInput = z$4.enum(["ours", "theirs", "merge"]).describe(
229618
+ 'How to resolve incoming design tokens that share a name with an existing token. "ours" keeps the existing token styles and id, "theirs" uses incoming styles, and "merge" combines both.'
229619
+ );
229684
229620
  const insertComponentInput = z$4.object({
229685
229621
  parentInstanceId: z$4.string(),
229686
229622
  component: instanceComponent.describe(
@@ -229696,7 +229632,9 @@ const insertFragmentInput = z$4.object({
229696
229632
  "Structured Webstudio fragment produced by Webstudio JSX/template helpers. Runtime remaps fragment ids to generated project ids."
229697
229633
  ),
229698
229634
  conflictResolution: conflictResolutionInput.optional(),
229699
- contentMode: z$4.boolean().optional(),
229635
+ contentMode: z$4.boolean().optional().describe(
229636
+ "Apply content-mode copy restrictions to props and styles while reusing existing tokens and breakpoints."
229637
+ ),
229700
229638
  mode: instanceInsertModeInput.optional(),
229701
229639
  insertIndex: insertIndexInput.optional()
229702
229640
  }).superRefine((input2, context) => {
@@ -247617,11 +247555,71 @@ const errorDiagnostic = (error) => {
247617
247555
  details: redactProjectSessionValue(error)
247618
247556
  };
247619
247557
  };
247558
+ const getCommitConfirmationFailureCode = (error) => {
247559
+ if (typeof error !== "object" || error === null || !("confirmationFailure" in error)) {
247560
+ return;
247561
+ }
247562
+ const failure = error.confirmationFailure;
247563
+ if (typeof failure !== "object" || failure === null || !("code" in failure) || typeof failure.code !== "string") {
247564
+ return;
247565
+ }
247566
+ return failure.code;
247567
+ };
247568
+ const isAuthorizationErrorCode = (code2) => code2 === "UNAUTHORIZED" || code2 === "FORBIDDEN" || code2 === "PLAN_REQUIRED";
247620
247569
  const shouldRefreshPermissionsAfterError = (error) => {
247621
- const code2 = getProjectSessionErrorCode(error);
247622
- return code2 === "UNAUTHORIZED" || code2 === "FORBIDDEN" || code2 === "PLAN_REQUIRED";
247570
+ return isAuthorizationErrorCode(getProjectSessionErrorCode(error)) || isAuthorizationErrorCode(getCommitConfirmationFailureCode(error));
247623
247571
  };
247624
247572
  const isVersionConflictError = (error) => getProjectSessionErrorCode(error) === "CONFLICT";
247573
+ const createCommitConfirmationConflictError = ({
247574
+ conflictError,
247575
+ confirmationError
247576
+ }) => Object.assign(
247577
+ new Error(
247578
+ conflictError instanceof Error ? conflictError.message : "Build version conflict",
247579
+ { cause: confirmationError }
247580
+ ),
247581
+ {
247582
+ code: "CONFLICT",
247583
+ confirmationFailure: {
247584
+ code: getProjectSessionErrorCode(confirmationError),
247585
+ message: confirmationError instanceof Error ? confirmationError.message : "Unknown error"
247586
+ }
247587
+ }
247588
+ );
247589
+ const commitWithConflictConfirmation = async ({
247590
+ commit,
247591
+ input: input2
247592
+ }) => {
247593
+ try {
247594
+ return {
247595
+ result: await commit(input2),
247596
+ confirmedAfterRetry: false
247597
+ };
247598
+ } catch (conflictError) {
247599
+ if (isVersionConflictError(conflictError) === false) {
247600
+ throw conflictError;
247601
+ }
247602
+ try {
247603
+ return {
247604
+ result: await commit(input2),
247605
+ confirmedAfterRetry: true
247606
+ };
247607
+ } catch (confirmationError) {
247608
+ if (isVersionConflictError(confirmationError)) {
247609
+ throw confirmationError;
247610
+ }
247611
+ throw createCommitConfirmationConflictError({
247612
+ conflictError,
247613
+ confirmationError
247614
+ });
247615
+ }
247616
+ }
247617
+ };
247618
+ const commitRetryConfirmedDiagnostic = {
247619
+ level: "info",
247620
+ code: "COMMIT_RETRY_CONFIRMED",
247621
+ message: "The transaction was confirmed committed after retrying the same transaction ID."
247622
+ };
247625
247623
  const isProjectSessionBusyError = (error) => getProjectSessionErrorCode(error) === "PROJECT_SESSION_BUSY";
247626
247624
  const createProjectSessionBusyError = (cause) => new ProjectSessionBusyError({ cause });
247627
247625
  const hasProjectSessionPermit = (permissions, permit) => {
@@ -247822,12 +247820,16 @@ class ProjectSession {
247822
247820
  transaction
247823
247821
  });
247824
247822
  }
247825
- const commit = await this.#options.transport.commitRestorePoint({
247826
- projectId: snapshot.projectId,
247827
- buildId: snapshot.buildId,
247828
- baseVersion: snapshot.version,
247829
- transactions: [transaction]
247823
+ const { result: commit, confirmedAfterRetry } = await commitWithConflictConfirmation({
247824
+ commit: (input2) => this.#options.transport.commitRestorePoint(input2),
247825
+ input: {
247826
+ projectId: snapshot.projectId,
247827
+ buildId: snapshot.buildId,
247828
+ baseVersion: snapshot.version,
247829
+ transactions: [transaction]
247830
+ }
247830
247831
  });
247832
+ const diagnostics = confirmedAfterRetry ? [commitRetryConfirmedDiagnostic] : [];
247831
247833
  const applied = applyBuilderPatchTransactions(snapshot.state, [
247832
247834
  transaction
247833
247835
  ]);
@@ -247847,7 +247849,7 @@ class ProjectSession {
247847
247849
  const persisted = await this.#synchronizeAfterCommit({
247848
247850
  snapshot: committedSnapshot,
247849
247851
  namespaces: restorePointNamespaces,
247850
- diagnostics: []
247852
+ diagnostics
247851
247853
  });
247852
247854
  return this.createEnvelope({
247853
247855
  source: "local",
@@ -248128,7 +248130,7 @@ class ProjectSession {
248128
248130
  code: "CONFLICT_REFRESHED",
248129
248131
  message: "Remote build changed. Required namespaces were refreshed; rerun the operation against the latest snapshot."
248130
248132
  });
248131
- if (contract.retryOnConflict && options.dryRun !== true) {
248133
+ if (contract.retryOnConflict && options.dryRun !== true && getCommitConfirmationFailureCode(error) === void 0) {
248132
248134
  return await this.commitMutation({
248133
248135
  operationId,
248134
248136
  input: input2,
@@ -248219,12 +248221,17 @@ class ProjectSession {
248219
248221
  input2
248220
248222
  );
248221
248223
  }
248222
- const commit = await this.#options.transport.commitPatch({
248224
+ const commitInput = {
248223
248225
  projectId: snapshot.projectId,
248224
248226
  buildId: snapshot.buildId,
248225
248227
  baseVersion: snapshot.version,
248226
248228
  transactions: [transaction]
248229
+ };
248230
+ const { result: commit, confirmedAfterRetry } = await commitWithConflictConfirmation({
248231
+ commit: (input22) => this.#options.transport.commitPatch(input22),
248232
+ input: commitInput
248227
248233
  });
248234
+ const commitDiagnostics = confirmedAfterRetry ? [...diagnostics, commitRetryConfirmedDiagnostic] : diagnostics;
248228
248235
  const applied = applyBuilderPatchTransactions(snapshot.state, [
248229
248236
  transaction
248230
248237
  ]);
@@ -248256,7 +248263,7 @@ class ProjectSession {
248256
248263
  contract.writeNamespaces,
248257
248264
  mutation.invalidatesNamespaces
248258
248265
  ),
248259
- diagnostics
248266
+ diagnostics: commitDiagnostics
248260
248267
  });
248261
248268
  return this.createEnvelope({
248262
248269
  source: "local",
@@ -253868,6 +253875,7 @@ const screenshotWaitUntilValues = [
253868
253875
  const defaultScreenshotWaitUntil = "load";
253869
253876
  const defaultScreenshotWaitForTimeout = 250;
253870
253877
  const defaultScreenshotTimeout = 3e4;
253878
+ const defaultBrowserStartupTimeout = 1e4;
253871
253879
  const isScreenshotWaitUntil = (value2) => typeof value2 === "string" && screenshotWaitUntilValues.includes(value2);
253872
253880
  const withTimeout$1 = async (operation, timeout, createTimeoutError2) => {
253873
253881
  let timeoutId;
@@ -254650,6 +254658,13 @@ const getScreenshotCaptureParams = async ({
254650
254658
  }
254651
254659
  };
254652
254660
  };
254661
+ class BrowserStartupError extends Error {
254662
+ code = "BROWSER_STARTUP_FAILED";
254663
+ constructor(message, options) {
254664
+ super(message, options);
254665
+ this.name = "BrowserStartupError";
254666
+ }
254667
+ }
254653
254668
  class BrowserSessionClosedError extends Error {
254654
254669
  }
254655
254670
  const getBrowserExitMessage = (message, reason) => `${message}${reason === void 0 ? "" : ` (${reason})`}. Check the browser installation or provide a supported Chromium executable.`;
@@ -254680,6 +254695,7 @@ const stopBrowserProcess = async ({
254680
254695
  ).catch(() => void 0);
254681
254696
  };
254682
254697
  const startBrowserRuntimeOnce = async (options, dependencies2) => {
254698
+ const startupTimeout = options.startupTimeout ?? options.timeout;
254683
254699
  const userDataDir = await dependencies2.mkdtemp(
254684
254700
  join$1(tmpdir(), "vision-browser-")
254685
254701
  );
@@ -254718,7 +254734,7 @@ const startBrowserRuntimeOnce = async (options, dependencies2) => {
254718
254734
  });
254719
254735
  try {
254720
254736
  const { port } = await Promise.race([
254721
- waitForDevToolsPort(userDataDir, dependencies2, options.timeout),
254737
+ waitForDevToolsPort(userDataDir, dependencies2, startupTimeout),
254722
254738
  browserClosed.then((reason) => {
254723
254739
  throw new BrowserSessionClosedError(
254724
254740
  getBrowserExitMessage(
@@ -254759,7 +254775,7 @@ const startBrowserRuntimeOnce = async (options, dependencies2) => {
254759
254775
  browserProcess,
254760
254776
  browserClosed,
254761
254777
  running,
254762
- gracePeriodMs: Math.min(options.timeout, 2e3)
254778
+ gracePeriodMs: Math.min(startupTimeout, 2e3)
254763
254779
  });
254764
254780
  await dependencies2.rm(userDataDir, { recursive: true, force: true }).catch(() => void 0);
254765
254781
  throw error;
@@ -254770,10 +254786,20 @@ const startBrowserRuntime = async (options, dependencies2) => {
254770
254786
  return await startBrowserRuntimeOnce(options, dependencies2);
254771
254787
  } catch (error) {
254772
254788
  if (error instanceof BrowserSessionClosedError === false) {
254773
- throw error;
254789
+ throw new BrowserStartupError(
254790
+ error instanceof Error ? error.message : String(error),
254791
+ { cause: error }
254792
+ );
254774
254793
  }
254775
254794
  }
254776
- return await startBrowserRuntimeOnce(options, dependencies2);
254795
+ try {
254796
+ return await startBrowserRuntimeOnce(options, dependencies2);
254797
+ } catch (error) {
254798
+ throw new BrowserStartupError(
254799
+ error instanceof Error ? error.message : String(error),
254800
+ { cause: error }
254801
+ );
254802
+ }
254777
254803
  };
254778
254804
  const capturePageWithBrowserRuntime = async (runtime, optionsList, dependencies2) => {
254779
254805
  const firstOptions = optionsList[0];
@@ -257045,43 +257071,6 @@ const workflowNextInputSchema = {
257045
257071
  },
257046
257072
  required: []
257047
257073
  };
257048
- const insertFragmentMcpInputSchema = {
257049
- ...emptyInputSchema,
257050
- description: "Insert a Webstudio fragment from a Webstudio JSX string. The CLI converts JSX into structured Webstudio data before mutation.",
257051
- additionalProperties: false,
257052
- properties: {
257053
- parentInstanceId: {
257054
- type: "string",
257055
- description: "Parent instance id where the fragment is inserted."
257056
- },
257057
- fragment: {
257058
- type: "string",
257059
- description: webstudioJsxFragmentInputDescription
257060
- },
257061
- mode: {
257062
- type: "string",
257063
- enum: ["append", "prepend", "replace"],
257064
- description: "Append, prepend, or replace parent children before inserting the fragment."
257065
- },
257066
- insertIndex: {
257067
- type: "number",
257068
- description: "Zero-based child index for insertion."
257069
- }
257070
- },
257071
- required: ["parentInstanceId", "fragment"]
257072
- };
257073
- const insertFragmentVerifiedMcpInputSchema = {
257074
- ...insertFragmentMcpInputSchema,
257075
- description: "Insert a Webstudio fragment and immediately verify its persisted bindings in one call.",
257076
- properties: {
257077
- ...insertFragmentMcpInputSchema.properties,
257078
- pagePath: {
257079
- type: "string",
257080
- description: "Concrete page path used for post-commit binding verification, for example /account."
257081
- }
257082
- },
257083
- required: ["parentInstanceId", "fragment", "pagePath"]
257084
- };
257085
257074
  const insertCollectionMcpInput = insertCollectionInput.omit({ itemFragment: true }).extend({
257086
257075
  itemFragment: z$4.string().describe(
257087
257076
  `${webstudioJsxFragmentInputDescription} Pass exactly one root instance. Expressions may reference collectionItem and collectionItemKey, for example {expression\`collectionItem.name\`}.`
@@ -257261,6 +257250,26 @@ const getZodObjectSchema = (schema2) => {
257261
257250
  };
257262
257251
  };
257263
257252
  const getZodMcpInputSchema = (schema2) => getHandshakeInputSchema(getZodObjectSchema(schema2)).inputSchema;
257253
+ const insertFragmentMcpInput = z$4.object({
257254
+ ...insertFragmentInput.shape,
257255
+ parentInstanceId: insertFragmentInput.shape.parentInstanceId.unwrap().describe("Parent instance id where the fragment is inserted."),
257256
+ fragment: z$4.string().describe(webstudioJsxFragmentInputDescription)
257257
+ }).describe(
257258
+ "Insert a Webstudio fragment from a Webstudio JSX string. The CLI converts JSX into structured Webstudio data before mutation."
257259
+ );
257260
+ const insertFragmentMcpInputSchema = getZodObjectSchema(insertFragmentMcpInput);
257261
+ const insertFragmentVerifiedMcpInputSchema = {
257262
+ ...insertFragmentMcpInputSchema,
257263
+ description: "Insert a Webstudio fragment and immediately verify its persisted bindings in one call.",
257264
+ properties: {
257265
+ ...insertFragmentMcpInputSchema.properties,
257266
+ pagePath: {
257267
+ type: "string",
257268
+ description: "Concrete page path used for post-commit binding verification, for example /account."
257269
+ }
257270
+ },
257271
+ required: ["parentInstanceId", "fragment", "pagePath"]
257272
+ };
257264
257273
  const insertCollectionMcpInputSchema = getOperationInputSchema({
257265
257274
  inputSchema: getInputSchemaMetadata(insertCollectionMcpInput).inputJsonSchema
257266
257275
  });
@@ -257884,6 +257893,19 @@ const previewInputSchema = {
257884
257893
  type: "array",
257885
257894
  description: "External image hostnames allowed by the generated preview optimizer, for example storage.example.com.",
257886
257895
  items: { type: "string" }
257896
+ },
257897
+ maxDurationMs: {
257898
+ type: "integer",
257899
+ minimum: 1,
257900
+ description: "Maximum acceptable duration. Slow work returns a confirmation preflight instead of starting when it cannot reasonably fit this budget."
257901
+ },
257902
+ confirmSlow: {
257903
+ type: "boolean",
257904
+ description: "Must be true after explicit user consent for the unchanged slow-operation preflight."
257905
+ },
257906
+ confirmationToken: {
257907
+ type: "string",
257908
+ description: "Short-lived token returned by the unchanged slow-operation preflight."
257887
257909
  }
257888
257910
  }
257889
257911
  };
@@ -258718,7 +258740,25 @@ const previewStatusDataSchema = {
258718
258740
  };
258719
258741
  const previewDataSchema = {
258720
258742
  ...previewStatusDataSchema,
258721
- required: ["url", "running", "mode"]
258743
+ properties: {
258744
+ ...previewStatusDataSchema.properties,
258745
+ confirmationRequired: { type: "boolean" },
258746
+ operation: { type: "string" },
258747
+ estimatedDuration: { type: "string" },
258748
+ reason: { type: "string" },
258749
+ confirmationToken: { type: "string" },
258750
+ fasterAlternative: {
258751
+ type: "object",
258752
+ properties: {
258753
+ operation: { type: "string" },
258754
+ estimatedDuration: { type: "string" },
258755
+ limitations: { type: "string" }
258756
+ },
258757
+ required: ["operation", "estimatedDuration", "limitations"],
258758
+ additionalProperties: false
258759
+ }
258760
+ },
258761
+ required: ["running", "mode"]
258722
258762
  };
258723
258763
  const restorePointSummaryDataSchema = getZodObjectSchema(
258724
258764
  projectSessionRestorePointSummarySchema
@@ -259531,11 +259571,12 @@ const hiddenMcpOperationCommands = /* @__PURE__ */ new Set([
259531
259571
  // MCP callers use duplicate-page or serializable page-transfer workflows.
259532
259572
  "copy-page"
259533
259573
  ]);
259534
- const compactMcpOperationCommands = /* @__PURE__ */ new Set([
259535
- "create-assets-resource",
259536
- "update-assets-resource",
259537
- "validate-asset-query",
259538
- "preview-asset-query"
259574
+ const mcpOperationSchemaInlineSizes = /* @__PURE__ */ new Map([
259575
+ ["insert-fragment", 1e3],
259576
+ ["create-assets-resource", 2500],
259577
+ ["update-assets-resource", 2500],
259578
+ ["validate-asset-query", 2500],
259579
+ ["preview-asset-query", 2500]
259539
259580
  ]);
259540
259581
  const detailedMcpInputSchemas = /* @__PURE__ */ new WeakMap();
259541
259582
  const getDetailedProjectSessionMcpInputSchema = (tool) => detailedMcpInputSchemas.get(tool) ?? tool.inputSchema;
@@ -259550,7 +259591,7 @@ const listProjectSessionMcpTools = (operations, options = {}) => [
259550
259591
  });
259551
259592
  const { inputSchema, detailedInputSchema } = getHandshakeInputSchema(
259552
259593
  operationInputSchema,
259553
- compactMcpOperationCommands.has(operation.command) ? 2500 : void 0
259594
+ mcpOperationSchemaInlineSizes.get(operation.command)
259554
259595
  );
259555
259596
  const tool = createProjectSessionMcpTool({
259556
259597
  name: operation.command,
@@ -259919,23 +259960,11 @@ const getInsertFragmentInput = async (input2) => {
259919
259960
  `insert-fragment requires fragment as a Webstudio JSX string, for example {"fragment":"<ws.element ws:tag='section' />"}.`
259920
259961
  );
259921
259962
  }
259922
- const fragment = await parseWebstudioJsxFragment(input2.fragment);
259923
- const mode = input2.mode;
259924
- if (mode !== void 0 && mode !== "append" && mode !== "prepend" && mode !== "replace") {
259925
- throw new Error(
259926
- 'insert-fragment mode must be "append", "prepend", or "replace".'
259927
- );
259928
- }
259929
- const insertIndex = input2.insertIndex;
259930
- if (insertIndex !== void 0 && typeof insertIndex !== "number") {
259931
- throw new Error("insert-fragment insertIndex must be a number.");
259932
- }
259933
- return {
259934
- parentInstanceId: input2.parentInstanceId,
259935
- fragment,
259936
- mode,
259937
- insertIndex
259938
- };
259963
+ const { fragment: fragmentSource, ...runtimeInput } = insertFragmentMcpInput.parse(input2);
259964
+ return insertFragmentInput.parse({
259965
+ ...runtimeInput,
259966
+ fragment: await parseWebstudioJsxFragment(fragmentSource)
259967
+ });
259939
259968
  };
259940
259969
  const getInsertCollectionInput = async (input2) => {
259941
259970
  const { itemFragment, ...parsedInput } = insertCollectionMcpInput.parse(input2);
@@ -260320,7 +260349,7 @@ const filterCapabilities = (tools) => {
260320
260349
  return capabilities;
260321
260350
  };
260322
260351
  const startupGuidance = [
260323
- "For any multi-step authoring task, first call meta.guide with the user's objective and follow its complete workflow, including final audit, preview, and screenshot steps.",
260352
+ "For any multi-step authoring task, first call meta.guide with the user's objective and follow its proportional workflow. Small corrections stop after focused assertions; preview and screenshots are required only when the requested outcome is visual or runtime-dependent.",
260324
260353
  "Mutation responses can include meta.next. Treat those steps as required before reporting completion.",
260325
260354
  "Every local-capable mutation exposes dryRun. Destructive delete, replace, and replace-all calls plan first; review the transaction, ask the user to confirm, then retry the unchanged call with confirmDestructive: true and meta.confirmation.token. Never retry blindly because changed input, version, or plan invalidates the short-lived token.",
260326
260355
  readProjectBuildDoc("mcp-startup-guidance").trim()
@@ -260468,7 +260497,8 @@ const getComponentSummaryEntry = ({
260468
260497
  }
260469
260498
  const [parsedNamespace, exportName] = parseComponentName(component);
260470
260499
  const namespace2 = parsedNamespace ?? "global";
260471
- const jsxNamespace = parsedNamespace === "@webstudio-is/sdk-components-react-radix" ? "radix" : parsedNamespace === "@webstudio-is/sdk-components-animation" ? "animation" : "$";
260500
+ const jsxNamespace = parsedNamespace === radixComponentNamespace ? "radix" : parsedNamespace === animationComponentNamespace ? "animation" : parsedNamespace === "ws" ? "ws" : "$";
260501
+ const jsxExportName = parsedNamespace === "ws" ? camelCase(exportName) : exportName;
260472
260502
  const template = templateMeta?.template;
260473
260503
  const hasTemplate = template !== void 0;
260474
260504
  const instancesById = new Map(
@@ -260507,7 +260537,7 @@ const getComponentSummaryEntry = ({
260507
260537
  component,
260508
260538
  exportName,
260509
260539
  namespace: namespace2,
260510
- jsxElement: `<${jsxNamespace}.${exportName} />`,
260540
+ jsxElement: `<${jsxNamespace}.${jsxExportName} />`,
260511
260541
  label: meta2.label,
260512
260542
  category: visibleCategory,
260513
260543
  contentCategory: meta2.contentModel?.category,
@@ -261500,6 +261530,29 @@ const metaGoalGuides = [
261500
261530
  ];
261501
261531
  const metaGuideDetailedInputSchemaTools = /* @__PURE__ */ new Set(["update-styles"]);
261502
261532
  const metaGuideExampleTools = /* @__PURE__ */ new Set(["upload-assets"]);
261533
+ const classifyTaskScope = (brief) => {
261534
+ if (/\b(project[- ]wide|site[- ]wide|all pages|every page|migration|migrate)\b/i.test(
261535
+ brief
261536
+ )) {
261537
+ return "project-wide-migration";
261538
+ }
261539
+ if (/\b(visual|layout|responsive|screenshot|typography|spacing|color|design)\b/i.test(
261540
+ brief
261541
+ )) {
261542
+ return "visual-change";
261543
+ }
261544
+ if (/\b(create|insert|delete|remove|move|wrap|unwrap|duplicate|reparent)\b.*\b(page|section|component|instance|element|tree|folder)\b/i.test(
261545
+ brief
261546
+ )) {
261547
+ return "structural-project-change";
261548
+ }
261549
+ if (/\b(correct|correction|fix|replace|update|change|typo)\b.*\b(value|text|copy|reference|asset|url|link|title|description|metadata|prop|binding)\b/i.test(
261550
+ brief
261551
+ )) {
261552
+ return "small-value-or-reference-correction";
261553
+ }
261554
+ return "focused-page-change";
261555
+ };
261503
261556
  const serializeMetaGuideTool = (tool, includeHandshakeFields) => includeHandshakeFields ? {
261504
261557
  name: tool.name,
261505
261558
  use: tool.description,
@@ -261515,13 +261568,17 @@ const serializeMetaGuideTool = (tool, includeHandshakeFields) => includeHandshak
261515
261568
  ...metaGuideDetailedInputSchemaTools.has(tool.name) ? { inputSchema: getDetailedProjectSessionMcpInputSchema(tool) } : {}
261516
261569
  };
261517
261570
  const getMetaGuide = (brief, tools, guidance) => {
261571
+ const taskScope = classifyTaskScope(brief);
261518
261572
  const goalGuide = metaGoalGuides.find(({ pattern }) => pattern.test(brief));
261519
261573
  const matches2 = goalGuide === void 0 ? getMatchingTools(brief, tools).slice(0, 12) : getExactToolSelection(goalGuide.tools, tools).tools;
261520
261574
  const canVerifyVisually = tools.some((tool) => tool.name === "preview.start") && tools.some((tool) => tool.name === "screenshot");
261521
261575
  const canDiffScreenshots = tools.some(
261522
261576
  (tool) => tool.name === "screenshot.diff"
261523
261577
  );
261578
+ const isSmallCorrection = taskScope === "small-value-or-reference-correction";
261579
+ const needsVisualVerification = taskScope === "visual-change";
261524
261580
  const generalWorkflow = [
261581
+ isSmallCorrection ? "Focused search → atomic edit → targeted assertions." : void 0,
261525
261582
  "Use the fewest discovery calls needed for the immediate action.",
261526
261583
  "Call permissions or status only when the task depends on capabilities or local session freshness.",
261527
261584
  matches2.some((tool) => tool.annotations.localCapable) && matches2.some((tool) => tool.name === "verify-font-assets") === false ? "Call refresh if cached namespaces may be stale." : void 0,
@@ -261529,11 +261586,26 @@ const getMetaGuide = (brief, tools, guidance) => {
261529
261586
  "Use the smallest semantic mutation tool that matches the requested change.",
261530
261587
  valuesVsBindingsRule,
261531
261588
  "Use apply-patch only when no semantic mutation tool fits.",
261532
- canVerifyVisually && guidance !== void 0 ? guidance.getVisionWorkflowSummary({ includeDiff: canDiffScreenshots }) : void 0
261589
+ needsVisualVerification && canVerifyVisually && guidance !== void 0 ? guidance.getVisionWorkflowSummary({ includeDiff: canDiffScreenshots }) : void 0
261533
261590
  ].filter(Boolean);
261534
261591
  return {
261592
+ taskScope,
261535
261593
  delegatedAgentRule: "Do not spend the whole phase on discovery. If you are delegated/non-streaming and the parent asks for status within 30 seconds, run exactly one shortcut command such as webstudio meta.index or one explicit webstudio mcp single-op-call command, report its command/result, and wait before the next MCP command.",
261536
261594
  workflow: goalGuide?.workflow ?? generalWorkflow,
261595
+ visionLoop: needsVisualVerification && canVerifyVisually && guidance !== void 0 ? guidance.getVisionVerificationLoop({
261596
+ includeDiff: canDiffScreenshots
261597
+ }) : [],
261598
+ slowOperation: {
261599
+ confirmationRequired: true,
261600
+ operation: "full production preview",
261601
+ estimatedDuration: "30–60 seconds",
261602
+ reason: "Builds complete rendered output and is unnecessary for a focused value correction unless the requested outcome depends on layout or runtime behavior.",
261603
+ fasterAlternative: {
261604
+ operation: "targeted route validation",
261605
+ estimatedDuration: "2–5 seconds",
261606
+ limitations: "Does not visually inspect layout."
261607
+ }
261608
+ },
261537
261609
  tools: matches2.map(
261538
261610
  (tool) => serializeMetaGuideTool(tool, goalGuide === void 0)
261539
261611
  ),
@@ -262386,6 +262458,43 @@ const getPreviewInput = (input2) => {
262386
262458
  imageDomains
262387
262459
  };
262388
262460
  };
262461
+ const slowOperationConfirmationTtlMs = 5 * 60 * 1e3;
262462
+ const getProductionPreviewPreflight = async (input2) => {
262463
+ if (isRecord(input2) === false || input2.mode !== "production") {
262464
+ return;
262465
+ }
262466
+ const previewInput = getPreviewInput(input2);
262467
+ const confirmationPayload = {
262468
+ operation: "production preview build",
262469
+ input: previewInput,
262470
+ maxDurationMs: typeof input2.maxDurationMs === "number" ? input2.maxDurationMs : void 0
262471
+ };
262472
+ if (input2.confirmSlow === true && await validateConfirmationToken(
262473
+ typeof input2.confirmationToken === "string" ? input2.confirmationToken : void 0,
262474
+ confirmationPayload
262475
+ )) {
262476
+ return;
262477
+ }
262478
+ const { token: token2 } = await createConfirmationToken(
262479
+ confirmationPayload,
262480
+ slowOperationConfirmationTtlMs
262481
+ );
262482
+ const maxDurationMs = typeof input2.maxDurationMs === "number" ? input2.maxDurationMs : void 0;
262483
+ return {
262484
+ running: false,
262485
+ mode: "production",
262486
+ confirmationRequired: true,
262487
+ operation: "production preview build",
262488
+ estimatedDuration: "30–60 seconds",
262489
+ reason: maxDurationMs !== void 0 && maxDurationMs < 3e4 ? `A full production preview cannot reasonably finish within the ${maxDurationMs}ms budget.` : "A full production preview builds complete rendered output and is expected to exceed 10 seconds.",
262490
+ confirmationToken: token2,
262491
+ fasterAlternative: {
262492
+ operation: "targeted route validation",
262493
+ estimatedDuration: "2–5 seconds",
262494
+ limitations: "Does not visually inspect layout."
262495
+ }
262496
+ };
262497
+ };
262389
262498
  const getImportInput = (input2) => {
262390
262499
  if (isRecord(input2) === false) {
262391
262500
  throw new Error("import input must be an object.");
@@ -262947,6 +263056,10 @@ const createProjectSessionMcpCore = ({
262947
263056
  return toMetaResult(await installOcr());
262948
263057
  }
262949
263058
  if (name2 === "preview.start" && startPreview !== void 0) {
263059
+ const preflight = await getProductionPreviewPreflight(input2);
263060
+ if (preflight !== void 0) {
263061
+ return toMetaResult(preflight);
263062
+ }
262950
263063
  return toMetaResult(
262951
263064
  await startPreview(getPreviewInput(input2), {
262952
263065
  report: (message) => {
@@ -263200,9 +263313,9 @@ const createProjectSessionMcpCore = ({
263200
263313
  "After changing props, variables, or resources, run verify-bindings for the changed page or instance and resolve every finding."
263201
263314
  ] : [],
263202
263315
  ...needsVisualVerification ? [
263203
- "Before reporting completion, run audit for the changed page or project.",
263316
+ "For a small fixed-value or reference correction, re-read the changed value and run only relevant focused assertions; stop when they pass. Run audit when the change is structural or broader.",
263204
263317
  ...startPreview !== void 0 && captureScreenshot2 !== void 0 ? [
263205
- "For visual changes, start a session preview and capture desktop and mobile screenshots before reporting completion."
263318
+ "Only when the requested outcome is visual or runtime-dependent, start a session preview and capture the necessary screenshots. Production preview requires a slow-operation preflight and explicit confirmation."
263206
263319
  ] : []
263207
263320
  ] : []
263208
263321
  ];
@@ -267951,7 +268064,7 @@ class HandledCliError extends Error {
267951
268064
  }
267952
268065
  const isHandledCliError = (error) => error instanceof HandledCliError;
267953
268066
  const name = "webstudio";
267954
- const version$1 = "0.290.0";
268067
+ const version$1 = "0.291.0";
267955
268068
  const description = "Webstudio CLI";
267956
268069
  const author = "Webstudio <github@webstudio.is>";
267957
268070
  const homepage = "https://webstudio.is";
@@ -280214,8 +280327,6 @@ Please check webstudio --help for more details`
280214
280327
  const pages2 = migratePages(siteData.build.pages);
280215
280328
  const publishablePages = getPublishablePages(pages2);
280216
280329
  const generatedPages = options.includeDraftPages ? getAllPages(pages2) : publishablePages;
280217
- const publishablePageIds = new Set(publishablePages.map((page2) => page2.id));
280218
- const assetSitemapPaths = /* @__PURE__ */ new Set();
280219
280330
  await writeWsAuthResources(
280220
280331
  generatedDir,
280221
280332
  pages2,
@@ -280556,11 +280667,6 @@ Please check webstudio --help for more details`
280556
280667
  index: siteData.assetIndex,
280557
280668
  requireCompleteEnumeration: options.template.includes("ssg")
280558
280669
  });
280559
- if (publishablePageIds.has(page2.id) && isPageEligibleForSitemap(page2)) {
280560
- for (const path2 of prerenderPaths) {
280561
- assetSitemapPaths.add(path2);
280562
- }
280563
- }
280564
280670
  for (const { file: file2, template } of getTemplates({
280565
280671
  pagePath: pagePath2,
280566
280672
  prerenderPaths
@@ -280607,21 +280713,10 @@ Please check webstudio --help for more details`
280607
280713
  await writeGeneratedFile(file2, content2);
280608
280714
  }
280609
280715
  const sitemap = getStaticSiteMapXml(pages2, siteData.build.updatedAt);
280610
- const sitemapPaths = new Set(sitemap.map(({ path: path2 }) => path2));
280611
- for (const path2 of [...assetSitemapPaths].sort()) {
280612
- if (sitemapPaths.has(path2)) {
280613
- continue;
280614
- }
280615
- sitemapPaths.add(path2);
280616
- sitemap.push({
280617
- path: path2,
280618
- lastModified: siteData.build.updatedAt.split("T")[0]
280619
- });
280620
- }
280621
280716
  await writeGeneratedFile(
280622
280717
  join$1(generatedDir, "$resources.sitemap.xml.ts"),
280623
280718
  `
280624
- export const sitemap = ${JSON.stringify(sitemap, null, 2)};
280719
+ export const sitemap: Array<{ path: string; lastModified: string }> = ${JSON.stringify(sitemap, null, 2)};
280625
280720
  `
280626
280721
  );
280627
280722
  const assetsById = Object.fromEntries(
@@ -293321,8 +293416,7 @@ const uniqueCandidates = (candidates) => {
293321
293416
  return true;
293322
293417
  });
293323
293418
  };
293324
- const resolveScreenshotBrowser = async (options, dependencies2 = defaultScreenshotDependencies) => {
293325
- const checked = [];
293419
+ const getScreenshotBrowserCandidates = async (options, dependencies2) => {
293326
293420
  const candidates = [];
293327
293421
  if (options.browserPath !== void 0) {
293328
293422
  candidates.push({
@@ -293330,6 +293424,7 @@ const resolveScreenshotBrowser = async (options, dependencies2 = defaultScreensh
293330
293424
  source: "option",
293331
293425
  browser: options.browser === "auto" ? inferBrowser(options.browserPath) : options.browser
293332
293426
  });
293427
+ return candidates;
293333
293428
  }
293334
293429
  const envBrowserPath = dependencies2.env.WEBSTUDIO_BROWSER_PATH;
293335
293430
  if (envBrowserPath !== void 0 && envBrowserPath.length > 0) {
@@ -293371,7 +293466,17 @@ const resolveScreenshotBrowser = async (options, dependencies2 = defaultScreensh
293371
293466
  (left, right) => browserPreference[left.browser] - browserPreference[right.browser]
293372
293467
  )
293373
293468
  );
293374
- for (const candidate of uniqueCandidates(candidates)) {
293469
+ return uniqueCandidates(candidates);
293470
+ };
293471
+ const resolveScreenshotBrowserCandidate = async (options, dependencies2, excludedPaths) => {
293472
+ const checked = [];
293473
+ for (const candidate of await getScreenshotBrowserCandidates(
293474
+ options,
293475
+ dependencies2
293476
+ )) {
293477
+ if (excludedPaths.has(candidate.path)) {
293478
+ continue;
293479
+ }
293375
293480
  checked.push(candidate.path);
293376
293481
  if (await isExecutable(candidate.path, dependencies2)) {
293377
293482
  return candidate;
@@ -293502,27 +293607,34 @@ const getScreenshotOutputPath = ({
293502
293607
  output ?? `${tmpdir()}/webstudio-screenshot-${now}${suffix}.${format2 === "jpeg" ? "jpg" : format2}`
293503
293608
  );
293504
293609
  let screenshotBatchSequence = 0;
293505
- const getBrowserScreenshotOptions = (options, browserPath, output, dependencies2) => ({
293506
- browserPath,
293507
- output,
293508
- width: options.width,
293509
- height: options.height,
293510
- fullPage: options.fullPage,
293511
- includeImageMetrics: options.includeImageMetrics,
293512
- includeResourceMetrics: options.includeResourceMetrics,
293513
- includeContrastMetrics: options.includeContrastMetrics,
293514
- ...webstudioPageInstrumentation,
293515
- url: options.url,
293516
- httpCredentials: options.httpCredentials,
293517
- uid: dependencies2.getuid(),
293518
- waitUntil: options.waitUntil ?? defaultScreenshotWaitUntil,
293519
- waitForSelector: options.waitForSelector,
293520
- waitForTimeout: options.waitForTimeout ?? defaultScreenshotWaitForTimeout,
293521
- timeout: options.timeout ?? defaultScreenshotTimeout,
293522
- format: options.format,
293523
- quality: options.quality,
293524
- scale: options.scale
293525
- });
293610
+ const getBrowserScreenshotOptions = (options, browserPath, output, dependencies2) => {
293611
+ const timeout = options.timeout ?? defaultScreenshotTimeout;
293612
+ return {
293613
+ browserPath,
293614
+ output,
293615
+ width: options.width,
293616
+ height: options.height,
293617
+ fullPage: options.fullPage,
293618
+ includeImageMetrics: options.includeImageMetrics,
293619
+ includeResourceMetrics: options.includeResourceMetrics,
293620
+ includeContrastMetrics: options.includeContrastMetrics,
293621
+ ...webstudioPageInstrumentation,
293622
+ url: options.url,
293623
+ httpCredentials: options.httpCredentials,
293624
+ uid: dependencies2.getuid(),
293625
+ waitUntil: options.waitUntil ?? defaultScreenshotWaitUntil,
293626
+ waitForSelector: options.waitForSelector,
293627
+ waitForTimeout: options.waitForTimeout ?? defaultScreenshotWaitForTimeout,
293628
+ timeout,
293629
+ startupTimeout: Math.max(
293630
+ 1,
293631
+ Math.min(defaultBrowserStartupTimeout, Math.floor(timeout / 2))
293632
+ ),
293633
+ format: options.format,
293634
+ quality: options.quality,
293635
+ scale: options.scale
293636
+ };
293637
+ };
293526
293638
  const validateScreenshotArtifact = async (output, dependencies2) => {
293527
293639
  let bytesRead;
293528
293640
  try {
@@ -293574,36 +293686,80 @@ const captureResolvedScreenshot = async (options, browser, dependencies2, browse
293574
293686
  ...layout === void 0 ? {} : { layout }
293575
293687
  };
293576
293688
  };
293577
- const captureScreenshot = async (options, dependencies2 = defaultScreenshotDependencies) => {
293578
- const browser = await resolveScreenshotBrowser(options, dependencies2);
293579
- return await captureResolvedScreenshot(options, browser, dependencies2);
293689
+ const captureScreenshot = async (options, dependencies2 = defaultScreenshotDependencies) => await withBrowserStartupFallback({
293690
+ options,
293691
+ dependencies: dependencies2,
293692
+ state: createBrowserStartupState(),
293693
+ attempt: async (browser) => await captureResolvedScreenshot(options, browser, dependencies2)
293694
+ });
293695
+ const createBrowserStartupError = (failures) => Object.assign(
293696
+ new BrowserStartupError(
293697
+ [
293698
+ "Unable to start any discovered Chromium-family browser.",
293699
+ ...failures.map(
293700
+ ({ browser, error }) => `- ${browser.browser} (${browser.source}): ${browser.path}: ${error instanceof Error ? error.message : String(error)}`
293701
+ )
293702
+ ].join("\n")
293703
+ ),
293704
+ {
293705
+ attempts: failures.map(({ browser }) => browser)
293706
+ }
293707
+ );
293708
+ const createBrowserStartupState = () => ({
293709
+ failedPaths: /* @__PURE__ */ new Set(),
293710
+ failures: []
293711
+ });
293712
+ const withBrowserStartupFallback = async ({
293713
+ options,
293714
+ dependencies: dependencies2,
293715
+ state,
293716
+ attempt,
293717
+ validateBrowser,
293718
+ isCancelled = () => false
293719
+ }) => {
293720
+ while (true) {
293721
+ let browser;
293722
+ try {
293723
+ browser = await resolveScreenshotBrowserCandidate(
293724
+ options,
293725
+ dependencies2,
293726
+ state.failedPaths
293727
+ );
293728
+ } catch (error) {
293729
+ if (error instanceof BrowserNotFoundError && state.failures.length > 0) {
293730
+ throw createBrowserStartupError(state.failures);
293731
+ }
293732
+ throw error;
293733
+ }
293734
+ validateBrowser?.(browser);
293735
+ try {
293736
+ return await attempt(browser);
293737
+ } catch (error) {
293738
+ if (error instanceof BrowserStartupError === false || isCancelled()) {
293739
+ throw error;
293740
+ }
293741
+ if (state.failedPaths.has(browser.path) === false) {
293742
+ state.failures.push({ browser, error });
293743
+ state.failedPaths.add(browser.path);
293744
+ }
293745
+ if (options.browserPath !== void 0) {
293746
+ throw createBrowserStartupError(state.failures);
293747
+ }
293748
+ }
293749
+ }
293580
293750
  };
293581
293751
  const createScreenshotCaptureSession = (dependencies2 = defaultScreenshotDependencies) => {
293582
- let browserPromise;
293583
293752
  let browserSession;
293753
+ let browserSessionCandidate;
293584
293754
  let browserSessionPromise;
293585
293755
  let closePromise;
293586
293756
  let closed = false;
293757
+ const startupState = createBrowserStartupState();
293587
293758
  const assertOpen = () => {
293588
293759
  if (closed) {
293589
293760
  throw new Error("Screenshot capture session is closed.");
293590
293761
  }
293591
293762
  };
293592
- const getBrowser = async (options) => {
293593
- assertOpen();
293594
- const pendingBrowser = browserPromise ?? resolveScreenshotBrowser(options, dependencies2);
293595
- browserPromise = pendingBrowser;
293596
- try {
293597
- const browser = await pendingBrowser;
293598
- assertOpen();
293599
- return browser;
293600
- } catch (error) {
293601
- if (browserPromise === pendingBrowser) {
293602
- browserPromise = void 0;
293603
- }
293604
- throw error;
293605
- }
293606
- };
293607
293763
  const getBrowserSession = async (options) => {
293608
293764
  assertOpen();
293609
293765
  if (browserSession !== void 0) {
@@ -293626,27 +293782,48 @@ const createScreenshotCaptureSession = (dependencies2 = defaultScreenshotDepende
293626
293782
  throw error;
293627
293783
  }
293628
293784
  };
293629
- return {
293630
- async capture(options) {
293631
- const resolvedBrowser = await getBrowser(options);
293632
- if (options.browserPath !== void 0 && options.browserPath !== resolvedBrowser.path || options.browser !== "auto" && options.browser !== resolvedBrowser.browser) {
293785
+ const getReadyBrowser = async (options, compatibleOptions = [options]) => {
293786
+ const validateBrowser = (browser) => {
293787
+ if (compatibleOptions.some(
293788
+ (candidateOptions) => candidateOptions.browserPath !== void 0 && candidateOptions.browserPath !== browser.path || candidateOptions.browser !== "auto" && candidateOptions.browser !== browser.browser
293789
+ )) {
293633
293790
  throw new Error(
293634
293791
  "A reusable screenshot session cannot switch browser executables."
293635
293792
  );
293636
293793
  }
293637
- const activeBrowserSession = await getBrowserSession(
293638
- getBrowserScreenshotOptions(
293639
- options,
293640
- resolvedBrowser.path,
293641
- options.output ?? "",
293642
- dependencies2
293643
- )
293644
- );
293794
+ };
293795
+ if (browserSession !== void 0 && browserSessionCandidate !== void 0) {
293796
+ validateBrowser(browserSessionCandidate);
293797
+ return { browser: browserSessionCandidate, session: browserSession };
293798
+ }
293799
+ return await withBrowserStartupFallback({
293800
+ options,
293801
+ dependencies: dependencies2,
293802
+ state: startupState,
293803
+ validateBrowser,
293804
+ async attempt(browser) {
293805
+ const session = await getBrowserSession(
293806
+ getBrowserScreenshotOptions(
293807
+ options,
293808
+ browser.path,
293809
+ options.output ?? "",
293810
+ dependencies2
293811
+ )
293812
+ );
293813
+ browserSessionCandidate = browser;
293814
+ return { browser, session };
293815
+ },
293816
+ isCancelled: () => closed
293817
+ });
293818
+ };
293819
+ return {
293820
+ async capture(options) {
293821
+ const { browser, session } = await getReadyBrowser(options);
293645
293822
  return await captureResolvedScreenshot(
293646
293823
  options,
293647
- resolvedBrowser,
293824
+ browser,
293648
293825
  dependencies2,
293649
- activeBrowserSession
293826
+ session
293650
293827
  );
293651
293828
  },
293652
293829
  async capturePage(optionsList) {
@@ -293655,14 +293832,10 @@ const createScreenshotCaptureSession = (dependencies2 = defaultScreenshotDepende
293655
293832
  if (firstOptions === void 0) {
293656
293833
  return [];
293657
293834
  }
293658
- const resolvedBrowser = await getBrowser(firstOptions);
293659
- if (optionsList.some(
293660
- (options) => options.browserPath !== void 0 && options.browserPath !== resolvedBrowser.path || options.browser !== "auto" && options.browser !== resolvedBrowser.browser
293661
- )) {
293662
- throw new Error(
293663
- "A reusable screenshot session cannot switch browser executables."
293664
- );
293665
- }
293835
+ const { browser, session } = await getReadyBrowser(
293836
+ firstOptions,
293837
+ optionsList
293838
+ );
293666
293839
  const startedAt = dependencies2.now();
293667
293840
  const batchSequence = screenshotBatchSequence;
293668
293841
  screenshotBatchSequence += 1;
@@ -293680,17 +293853,14 @@ const createScreenshotCaptureSession = (dependencies2 = defaultScreenshotDepende
293680
293853
  output,
293681
293854
  browserOptions: getBrowserScreenshotOptions(
293682
293855
  options,
293683
- resolvedBrowser.path,
293856
+ browser.path,
293684
293857
  output,
293685
293858
  dependencies2
293686
293859
  )
293687
293860
  };
293688
293861
  })
293689
293862
  );
293690
- const activeBrowserSession = await getBrowserSession(
293691
- captures[0].browserOptions
293692
- );
293693
- const layouts = await activeBrowserSession.capturePage(
293863
+ const layouts = await session.capturePage(
293694
293864
  captures.map((capture) => capture.browserOptions)
293695
293865
  );
293696
293866
  await Promise.all(
@@ -293706,7 +293876,7 @@ const createScreenshotCaptureSession = (dependencies2 = defaultScreenshotDepende
293706
293876
  const layout = toWebstudioLayout(capturedLayout);
293707
293877
  return {
293708
293878
  output,
293709
- browser: resolvedBrowser,
293879
+ browser,
293710
293880
  viewport: { width: options.width, height: options.height },
293711
293881
  fullPage: options.fullPage === true,
293712
293882
  elapsedMs: layout.timings?.wallMs ?? 0,