@agimon-ai/po-style-system-tooling 0.2.14-alpha.2 → 0.2.14-alpha.21

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.
@@ -2,10 +2,11 @@ import { createRequire } from "node:module";
2
2
  import { Container, ContainerModule, inject, injectable, optional } from "inversify";
3
3
  import { accessSync, existsSync, promises, readFileSync, readdirSync } from "node:fs";
4
4
  import * as path$1 from "node:path";
5
- import path, { basename, dirname } from "node:path";
5
+ import path, { dirname } from "node:path";
6
6
  import os, * as os$1 from "node:os";
7
7
  import pino from "pino";
8
8
  import * as fs from "node:fs/promises";
9
+ import { writeFile } from "node:fs/promises";
9
10
  import * as yaml from "js-yaml";
10
11
  import postcss from "postcss";
11
12
  import { createHash, randomUUID } from "node:crypto";
@@ -13,18 +14,36 @@ import { loadCsf } from "storybook/internal/csf-tools";
13
14
  import { glob } from "glob";
14
15
  import { z } from "zod";
15
16
  import { chromium, firefox, webkit } from "playwright";
16
- import sharp from "sharp";
17
17
  import tailwindcss from "@tailwindcss/vite";
18
18
  import { viteSingleFile } from "vite-plugin-singlefile";
19
19
  import { fileURLToPath } from "node:url";
20
20
  import react from "@vitejs/plugin-react";
21
- import { execSync, spawn } from "node:child_process";
22
- import { registerManagedProcess } from "@agimon-ai/foundation-exec";
23
21
  import { Server } from "@modelcontextprotocol/server";
24
22
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
25
23
  //#region \0rolldown/runtime.js
26
24
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
27
25
  //#endregion
26
+ //#region src/container/types.ts
27
+ const STYLE_SYSTEM_TYPES = {
28
+ ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
29
+ GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
30
+ GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
31
+ ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
32
+ ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
33
+ ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
34
+ CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
35
+ StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
36
+ GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
37
+ AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
38
+ StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
39
+ RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
40
+ StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
41
+ WorkspaceRoot: Symbol.for("StyleSystem:WorkspaceRoot"),
42
+ DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
43
+ GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
44
+ AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
45
+ };
46
+ //#endregion
28
47
  //#region src/services/ThemeService/BaseThemeService.ts
29
48
  /**
30
49
  * BaseThemeService
@@ -95,12 +114,23 @@ const logger = pino({
95
114
  mkdir: true,
96
115
  sync: true
97
116
  }));
98
- const log = {
99
- debug: (msg, ...args) => logger.debug({ args }, msg),
100
- info: (msg, ...args) => logger.info({ args }, msg),
101
- warn: (msg, ...args) => logger.warn({ args }, msg),
102
- error: (msg, ...args) => logger.error({ args }, msg)
103
- };
117
+ function createLog(directory) {
118
+ const target = directory === void 0 ? logger : pino({
119
+ level: "debug",
120
+ timestamp: pino.stdTimeFunctions.isoTime
121
+ }, pino.destination({
122
+ dest: path$1.join(directory, "style-system.log"),
123
+ mkdir: true,
124
+ sync: true
125
+ }));
126
+ return {
127
+ debug: (msg, ...args) => target.debug({ args }, msg),
128
+ info: (msg, ...args) => target.info({ args }, msg),
129
+ warn: (msg, ...args) => target.warn({ args }, msg),
130
+ error: (msg, ...args) => target.error({ args }, msg)
131
+ };
132
+ }
133
+ const log = createLog();
104
134
  //#endregion
105
135
  //#region src/utils/fsHelpers.ts
106
136
  /** True when candidate is root itself or inside it (sibling prefixes like `<root>-evil` are outside). */
@@ -248,9 +278,9 @@ var CSSThemeService = class extends BaseThemeService {
248
278
  * Creates a new CSSThemeService instance
249
279
  * @param config - Theme service configuration with themePath or cssFiles
250
280
  */
251
- constructor(config) {
281
+ constructor(config, workspaceRoot) {
252
282
  super(config);
253
- this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
283
+ this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
254
284
  }
255
285
  /**
256
286
  * Get the source identifier for this service
@@ -366,6 +396,18 @@ const DEFAULT_THEME_SERVICE_CONFIG = {
366
396
  customServicePath: void 0
367
397
  };
368
398
  //#endregion
399
+ //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateMetadata.js
400
+ function __decorateMetadata(k, v) {
401
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
402
+ }
403
+ //#endregion
404
+ //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
405
+ function __decorateParam(paramIndex, decorator) {
406
+ return function(target, key) {
407
+ decorator(target, key, paramIndex);
408
+ };
409
+ }
410
+ //#endregion
369
411
  //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorate.js
370
412
  function __decorate(decorators, target, key, desc) {
371
413
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -392,6 +434,10 @@ function __decorate(decorators, target, key, desc) {
392
434
  * - Hardcoded service paths
393
435
  */
394
436
  let ThemeServiceFactory = class ThemeServiceFactory {
437
+ workspaceRoot;
438
+ constructor(workspaceRoot) {
439
+ this.workspaceRoot = workspaceRoot;
440
+ }
395
441
  /**
396
442
  * Create a theme service based on configuration.
397
443
  *
@@ -409,7 +455,7 @@ let ThemeServiceFactory = class ThemeServiceFactory {
409
455
  ...config
410
456
  };
411
457
  if (resolvedConfig.customServicePath) return this.loadCustomService(resolvedConfig);
412
- return new CSSThemeService(resolvedConfig);
458
+ return new CSSThemeService(resolvedConfig, this.workspaceRoot);
413
459
  }
414
460
  /**
415
461
  * Load a custom theme service from user-specified path.
@@ -436,7 +482,12 @@ let ThemeServiceFactory = class ThemeServiceFactory {
436
482
  }
437
483
  }
438
484
  };
439
- ThemeServiceFactory = __decorate([injectable()], ThemeServiceFactory);
485
+ ThemeServiceFactory = __decorate([
486
+ injectable(),
487
+ __decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
488
+ __decorateParam(0, optional()),
489
+ __decorateMetadata("design:paramtypes", [String])
490
+ ], ThemeServiceFactory);
440
491
  //#endregion
441
492
  //#region src/services/CssClasses/BaseCSSClassesService.ts
442
493
  /**
@@ -764,6 +815,10 @@ const VALID_SERVICE_EXTENSIONS$1 = [
764
815
  ".cjs"
765
816
  ];
766
817
  let CSSClassesServiceFactory = class CSSClassesServiceFactory {
818
+ workspaceRoot;
819
+ constructor(workspaceRoot) {
820
+ this.workspaceRoot = workspaceRoot;
821
+ }
767
822
  /**
768
823
  * Create a CSS classes service based on configuration.
769
824
  *
@@ -804,7 +859,7 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
804
859
  async loadCustomService(config) {
805
860
  const servicePath = config.customServicePath;
806
861
  if (!servicePath) throw new Error("customServicePath is required for custom service loading");
807
- const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
862
+ const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
808
863
  const resolvedPath = path.resolve(monorepoRoot, servicePath);
809
864
  const normalizedWorkspaceRoot = path.resolve(monorepoRoot);
810
865
  if (!resolvedPath.startsWith(normalizedWorkspaceRoot + path.sep)) throw new Error(`Security error: customServicePath "${servicePath}" resolves outside workspace root`);
@@ -825,12 +880,12 @@ let CSSClassesServiceFactory = class CSSClassesServiceFactory {
825
880
  }
826
881
  }
827
882
  };
828
- CSSClassesServiceFactory = __decorate([injectable()], CSSClassesServiceFactory);
829
- //#endregion
830
- //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateMetadata.js
831
- function __decorateMetadata(k, v) {
832
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
833
- }
883
+ CSSClassesServiceFactory = __decorate([
884
+ injectable(),
885
+ __decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
886
+ __decorateParam(0, optional()),
887
+ __decorateMetadata("design:paramtypes", [String])
888
+ ], CSSClassesServiceFactory);
834
889
  //#endregion
835
890
  //#region src/services/StoriesIndexService/StoriesIndexService.ts
836
891
  /**
@@ -885,6 +940,7 @@ let StoriesIndexService = class StoriesIndexService {
885
940
  */
886
941
  async initialize() {
887
942
  if (this.initialized && this.lastInitResult) return this.lastInitResult;
943
+ if (this.config.workspaceRoot) this.monorepoRoot = await promises.realpath(this.monorepoRoot);
888
944
  log.info("[StoriesIndexService] Initializing story index...");
889
945
  const storyFiles = await this.findStoryFiles();
890
946
  log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
@@ -956,6 +1012,7 @@ let StoriesIndexService = class StoriesIndexService {
956
1012
  * parse is shared by content hash across service instances and concurrent rules.
957
1013
  */
958
1014
  async indexStoryFile(filePath) {
1015
+ await this.assertContainedStory(filePath);
959
1016
  const content = await promises.readFile(filePath, "utf-8");
960
1017
  const fileHash = this.hashContent(content);
961
1018
  const cached = storyParseCache.get(filePath);
@@ -982,12 +1039,19 @@ let StoriesIndexService = class StoriesIndexService {
982
1039
  });
983
1040
  if (component) this.storeComponent(component);
984
1041
  }
1042
+ async assertContainedStory(filePath) {
1043
+ if (!this.config.workspaceRoot) return;
1044
+ const [root, story] = await Promise.all([promises.realpath(this.monorepoRoot), promises.realpath(filePath)]);
1045
+ if (!isContained(root, story)) throw new Error("Story must remain inside the configured workspace root.");
1046
+ }
985
1047
  storeComponent(component) {
986
1048
  this.componentIndex.set(component.title, component);
987
1049
  this.componentFileIndex.set(path.resolve(component.filePath), component);
988
1050
  }
989
1051
  parseStoryFile(filePath, content, fileHash) {
990
- const csf = loadCsf(content, {
1052
+ const alias = /^export\s+default\s+([A-Za-z_$][\w$]*)\s*;?\s*$/m.exec(content)?.[1];
1053
+ const parsable = alias ? content.replace(new RegExp(`^export\\s+(const|let|var)\\s+(${alias.replaceAll("$", "\\$")})\\b`, "m"), "$1 $2") : content;
1054
+ const csf = loadCsf(parsable, {
991
1055
  fileName: filePath,
992
1056
  makeTitle: (title) => title
993
1057
  });
@@ -1081,6 +1145,7 @@ let StoriesIndexService = class StoriesIndexService {
1081
1145
  * @returns True if file was updated, false if unchanged
1082
1146
  */
1083
1147
  async refreshFile(filePath) {
1148
+ await this.assertContainedStory(filePath);
1084
1149
  const content = await promises.readFile(filePath, "utf-8");
1085
1150
  const newHash = this.hashContent(content);
1086
1151
  const existingComponent = Array.from(this.componentIndex.values()).find((c) => c.filePath === filePath);
@@ -1120,27 +1185,6 @@ let StoriesIndexService = class StoriesIndexService {
1120
1185
  };
1121
1186
  StoriesIndexService = __decorate([injectable(), __decorateMetadata("design:paramtypes", [Object])], StoriesIndexService);
1122
1187
  //#endregion
1123
- //#region src/container/types.ts
1124
- const STYLE_SYSTEM_TYPES = {
1125
- ListThemesTool: Symbol.for("StyleSystem:ListThemesTool"),
1126
- GetCSSClassesTool: Symbol.for("StyleSystem:GetCSSClassesTool"),
1127
- GetComponentVisualTool: Symbol.for("StyleSystem:GetComponentVisualTool"),
1128
- ListSharedComponentsTool: Symbol.for("StyleSystem:ListSharedComponentsTool"),
1129
- ListAppComponentsTool: Symbol.for("StyleSystem:ListAppComponentsTool"),
1130
- PolishComponentTool: Symbol.for("StyleSystem:PolishComponentTool"),
1131
- ThemeServiceFactory: Symbol.for("StyleSystem:ThemeServiceFactory"),
1132
- CSSClassesServiceFactory: Symbol.for("StyleSystem:CSSClassesServiceFactory"),
1133
- StoriesIndexService: Symbol.for("StyleSystem:StoriesIndexService"),
1134
- GetUiComponentService: Symbol.for("StyleSystem:GetUiComponentService"),
1135
- AppComponentsService: Symbol.for("StyleSystem:AppComponentsService"),
1136
- StoriesIndexFactory: Symbol.for("StyleSystem:StoriesIndexFactory"),
1137
- RendererFactory: Symbol.for("StyleSystem:RendererFactory"),
1138
- StyleSystemConfigLoader: Symbol.for("StyleSystem:StyleSystemConfigLoader"),
1139
- DefaultThemePath: Symbol.for("StyleSystem:DefaultThemePath"),
1140
- GetUiComponentServiceConfig: Symbol.for("StyleSystem:GetUiComponentServiceConfig"),
1141
- AppComponentsServiceConfig: Symbol.for("StyleSystem:AppComponentsServiceConfig")
1142
- };
1143
- //#endregion
1144
1188
  //#region src/config/types.ts
1145
1189
  /**
1146
1190
  * Style system configuration types.
@@ -1306,9 +1350,9 @@ const EXPO_APP_CONFIG_FILE = "app.json";
1306
1350
  * values, and warns when it is the one that resolves.
1307
1351
  */
1308
1352
  async function resolveStyleSystemThemePath(input) {
1309
- const hostRoot = TemplatesManagerService.getWorkspaceRootSync();
1353
+ const hostRoot = input.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
1310
1354
  const resolvedAppPath = input.appPath ? path.isAbsolute(input.appPath) ? path.normalize(input.appPath) : path.join(hostRoot, input.appPath) : hostRoot;
1311
- const monorepoRoot = input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot;
1355
+ const monorepoRoot = input.workspaceRoot ?? (input.appPath ? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath) : hostRoot);
1312
1356
  const workspaceCandidate = path.join(monorepoRoot, input.themePath);
1313
1357
  const candidates = path.isAbsolute(input.themePath) ? [input.themePath] : [workspaceCandidate, path.resolve(resolvedAppPath, input.themePath)];
1314
1358
  const uniqueCandidates = [...new Set(candidates)];
@@ -1358,19 +1402,23 @@ function isNotFound(error) {
1358
1402
  return error instanceof Error && "code" in error && error.code === "ENOENT";
1359
1403
  }
1360
1404
  let StyleSystemConfigLoader = class StyleSystemConfigLoader {
1405
+ workspaceRoot;
1361
1406
  /**
1362
1407
  * Promises rather than values, so concurrent resolves de-duplicate. Several
1363
1408
  * callers hit config from inside Promise.all/allSettled.
1364
1409
  */
1365
1410
  rootPromises = /* @__PURE__ */ new Map();
1366
1411
  projectCache = /* @__PURE__ */ new Map();
1412
+ constructor(workspaceRoot) {
1413
+ this.workspaceRoot = workspaceRoot;
1414
+ }
1367
1415
  /** Drop all cached config state. */
1368
1416
  invalidate() {
1369
1417
  this.rootPromises.clear();
1370
1418
  this.projectCache.clear();
1371
1419
  }
1372
1420
  /** Load and cache the workspace root config. */
1373
- async loadRoot(workspaceRoot = TemplatesManagerService.getWorkspaceRootSync()) {
1421
+ async loadRoot(workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync()) {
1374
1422
  let pending = this.rootPromises.get(workspaceRoot);
1375
1423
  if (!pending) {
1376
1424
  pending = this.readRoot(workspaceRoot);
@@ -1386,8 +1434,8 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
1386
1434
  /** Resolve the merged configuration for a project. */
1387
1435
  async resolveProject(appPath) {
1388
1436
  if (!appPath || typeof appPath !== "string") throw new Error("appPath is required and must be a non-empty string");
1389
- const resolvedAppPath = path.isAbsolute(appPath) ? path.normalize(appPath) : path.join(TemplatesManagerService.getWorkspaceRootSync(), appPath);
1390
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
1437
+ const resolvedAppPath = path.isAbsolute(appPath) ? path.normalize(appPath) : path.join(this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), appPath);
1438
+ const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath);
1391
1439
  const cached = this.projectCache.get(resolvedAppPath);
1392
1440
  if (cached) return cached;
1393
1441
  const pending = this.readProject(resolvedAppPath, workspaceRoot);
@@ -1454,7 +1502,12 @@ let StyleSystemConfigLoader = class StyleSystemConfigLoader {
1454
1502
  };
1455
1503
  }
1456
1504
  };
1457
- StyleSystemConfigLoader = __decorate([injectable()], StyleSystemConfigLoader);
1505
+ StyleSystemConfigLoader = __decorate([
1506
+ injectable(),
1507
+ __decorateParam(0, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
1508
+ __decorateParam(0, optional()),
1509
+ __decorateMetadata("design:paramtypes", [String])
1510
+ ], StyleSystemConfigLoader);
1458
1511
  //#endregion
1459
1512
  //#region src/config/index.ts
1460
1513
  /**
@@ -1552,6 +1605,17 @@ const browserLaunchConfigs = [
1552
1605
  ];
1553
1606
  const STORY_ROOT_SELECTOR = "#root";
1554
1607
  const VITE_ERROR_OVERLAY_SELECTOR = "vite-error-overlay";
1608
+ const STORY_MOUNT_TIMEOUT_MS = 1e4;
1609
+ /**
1610
+ * Runs in the page (this package has no DOM types): resolves once web fonts and every image the
1611
+ * story rendered have loaded (or failed), then two frames have painted.
1612
+ */
1613
+ const FONTS_IMAGES_AND_TWO_FRAMES = `Promise.all([
1614
+ document.fonts.ready,
1615
+ ...Array.from(document.images, (image) =>
1616
+ image.complete ? null : new Promise((resolve) => { image.addEventListener('load', resolve, { once: true }); image.addEventListener('error', resolve, { once: true }); }),
1617
+ ),
1618
+ ]).then(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))`;
1555
1619
  /**
1556
1620
  * Attempts to launch a browser, trying multiple configurations in order of preference.
1557
1621
  * @returns Launched browser instance
@@ -1573,26 +1637,39 @@ async function launchBrowserWithFallback() {
1573
1637
  throw new Error(`No browser available. Tried:\n${errors.join("\n")}\n\nPlease install Chrome, or run 'npx playwright install chromium'`);
1574
1638
  }
1575
1639
  async function takeScreenshot(options) {
1576
- const { url, output, width = 1280, height = 800, fullPage = false, browser: browserName = "chromium", waitTime = 1e3, darkMode = false, mobile = false, generateThumbnail = false, thumbnailWidth = 400, thumbnailQuality = 80, base64 = false } = options;
1640
+ const { url, output, width = 1280, height = 800, fullPage = false, browser: browserName = "chromium", waitTime = 1e3, darkMode = false, mobile = false, generateThumbnail = false, thumbnailWidth = 400, thumbnailQuality = 80, base64 = false, sharedBrowser } = options;
1577
1641
  let browser = null;
1642
+ let context = null;
1643
+ let release;
1578
1644
  try {
1579
- if (browserName === "chromium") browser = await launchBrowserWithFallback();
1645
+ if (sharedBrowser) {
1646
+ const borrowed = await sharedBrowser();
1647
+ if ("browser" in borrowed) {
1648
+ browser = borrowed.browser;
1649
+ release = () => borrowed.release();
1650
+ } else browser = borrowed;
1651
+ } else if (browserName === "chromium") browser = await launchBrowserWithFallback();
1580
1652
  else {
1581
1653
  const browserType = browsers[browserName];
1582
1654
  if (!browserType) throw new Error(`Unsupported browser: ${browserName}`);
1583
1655
  browser = await browserType.launch({ headless: true });
1584
1656
  }
1585
- const page = await (await browser.newContext({
1657
+ context = await browser.newContext({
1586
1658
  viewport: {
1587
1659
  width,
1588
1660
  height
1589
1661
  },
1590
1662
  colorScheme: darkMode ? "dark" : "light",
1591
1663
  isMobile: mobile
1592
- })).newPage();
1664
+ });
1665
+ const page = await context.newPage();
1593
1666
  const runtimeErrors = [];
1594
- page.on("pageerror", (error) => runtimeErrors.push(error.stack ?? error.message));
1595
- await page.goto(url, { waitUntil: "networkidle" });
1667
+ page.on("pageerror", (error) => runtimeErrors.push(error.stack || error.message || String(error)));
1668
+ await page.goto(url, { waitUntil: "load" });
1669
+ if (await page.waitForSelector(`${STORY_ROOT_SELECTOR} > *, ${VITE_ERROR_OVERLAY_SELECTOR}`, {
1670
+ state: "attached",
1671
+ timeout: STORY_MOUNT_TIMEOUT_MS
1672
+ }).then(() => true).catch(() => false)) await page.evaluate(FONTS_IMAGES_AND_TWO_FRAMES);
1596
1673
  if (waitTime > 0) await page.waitForTimeout(waitTime);
1597
1674
  const hasViteErrorOverlay = await page.locator(VITE_ERROR_OVERLAY_SELECTOR).count();
1598
1675
  const renderedChildCount = await page.locator(`${STORY_ROOT_SELECTOR} > *`).count();
@@ -1604,7 +1681,27 @@ async function takeScreenshot(options) {
1604
1681
  path: output,
1605
1682
  fullPage
1606
1683
  });
1607
- const screenshotMetadata = await sharp(screenshotBuffer).metadata();
1684
+ const screenshotMetadata = await page.evaluate(`(async () => {
1685
+ const bytes = Uint8Array.from(atob(${JSON.stringify(screenshotBuffer.toString("base64"))}), (char) => char.charCodeAt(0));
1686
+ const image = await createImageBitmap(new Blob([bytes]));
1687
+ try {
1688
+ const result = { width: image.width, height: image.height };
1689
+ if (${generateThumbnail}) {
1690
+ const width = ${JSON.stringify(thumbnailWidth)};
1691
+ const quality = ${JSON.stringify(thumbnailQuality)};
1692
+ if (!Number.isSafeInteger(width) || width <= 0 || !Number.isInteger(quality) || quality < 1 || quality > 100)
1693
+ throw new Error('Invalid thumbnail width or quality.');
1694
+ const canvas = document.createElement('canvas');
1695
+ canvas.width = width;
1696
+ canvas.height = Math.max(1, Math.round(image.height * width / image.width));
1697
+ canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height);
1698
+ result.thumbnail = canvas.toDataURL('image/jpeg', quality / 100).split(',')[1];
1699
+ }
1700
+ return result;
1701
+ } finally {
1702
+ image.close();
1703
+ }
1704
+ })()`);
1608
1705
  const result = {
1609
1706
  imagePath: output,
1610
1707
  width: screenshotMetadata.width ?? width,
@@ -1613,13 +1710,18 @@ async function takeScreenshot(options) {
1613
1710
  if (generateThumbnail) {
1614
1711
  const ext = path.extname(output);
1615
1712
  const thumbnailPath = output.replace(ext, `-thumb${ext}`);
1616
- await sharp(screenshotBuffer).resize(thumbnailWidth).jpeg({ quality: thumbnailQuality }).toFile(thumbnailPath.replace(ext, ".jpg"));
1713
+ await writeFile(thumbnailPath.replace(ext, ".jpg"), Buffer.from(screenshotMetadata.thumbnail, "base64"));
1617
1714
  result.thumbnailPath = thumbnailPath.replace(ext, ".jpg");
1618
1715
  }
1619
1716
  if (base64) result.base64 = screenshotBuffer.toString("base64");
1620
1717
  return result;
1621
1718
  } finally {
1622
- if (browser) await browser.close();
1719
+ try {
1720
+ if (sharedBrowser) await context?.close();
1721
+ else if (browser) await browser.close();
1722
+ } finally {
1723
+ release?.();
1724
+ }
1623
1725
  }
1624
1726
  }
1625
1727
  //#endregion
@@ -1869,6 +1971,37 @@ function createStoryEntryPlugin(getStoryConfig, generateCode) {
1869
1971
  }
1870
1972
  };
1871
1973
  }
1974
+ /** Where a package the app depends on is installed, resolved from the app rather than from this tool. */
1975
+ function appPackageDir(appPath, packageName) {
1976
+ return path.dirname(createRequire(path.join(appPath, "package.json")).resolve(`${packageName}/package.json`));
1977
+ }
1978
+ /**
1979
+ * React and react-dom are peers of this tool: every story bundle takes the app's own pair, so the
1980
+ * story, the generated entry and any workspace package it imports share one React that matches its
1981
+ * react-dom. A pair resolved from the tool fails in the browser with React error #527.
1982
+ */
1983
+ function appReactAliases(appPath) {
1984
+ const reactDir = appPackageDir(appPath, "react");
1985
+ const reactDomDir = appPackageDir(appPath, "react-dom");
1986
+ return [
1987
+ {
1988
+ find: /^react$/,
1989
+ replacement: reactDir
1990
+ },
1991
+ {
1992
+ find: /^react\/(.*)$/,
1993
+ replacement: path.join(reactDir, "$1")
1994
+ },
1995
+ {
1996
+ find: /^react-dom$/,
1997
+ replacement: reactDomDir
1998
+ },
1999
+ {
2000
+ find: /^react-dom\/(.*)$/,
2001
+ replacement: path.join(reactDomDir, "$1")
2002
+ }
2003
+ ];
2004
+ }
1872
2005
  /**
1873
2006
  * Helper to create a Vite plugin that resolves the module specifiers TanStack Start's
1874
2007
  * `tanstackStart()` plugin normally provides to inert client-safe stubs, so stories in TanStack
@@ -1909,6 +2042,7 @@ function createTanStackStartStubPlugin() {
1909
2042
  * ```
1910
2043
  */
1911
2044
  var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerService {
2045
+ workspaceRoot;
1912
2046
  static instance = null;
1913
2047
  server = null;
1914
2048
  monorepoRoot;
@@ -1925,9 +2059,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
1925
2059
  * Use getInstance() for singleton access.
1926
2060
  * @param config - Service configuration options
1927
2061
  */
1928
- constructor(config = {}) {
2062
+ constructor(config = {}, workspaceRoot) {
1929
2063
  super(config);
1930
- this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
2064
+ this.workspaceRoot = workspaceRoot;
2065
+ this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
1931
2066
  }
1932
2067
  /**
1933
2068
  * Get the singleton instance of ViteReactBundlerService.
@@ -2028,6 +2163,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2028
2163
  */
2029
2164
  async doStartDevServer(resolvedAppPath) {
2030
2165
  const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
2166
+ let createdServer;
2031
2167
  try {
2032
2168
  await promises.mkdir(tmpDir, { recursive: true });
2033
2169
  const { createServer } = await import("vite");
@@ -2042,6 +2178,33 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2042
2178
  tailwindcss(),
2043
2179
  createTanStackStartStubPlugin(),
2044
2180
  ...this.getAdditionalVitePlugins(resolvedAppPath),
2181
+ {
2182
+ name: "style-system-preview-html",
2183
+ configureServer: (server) => {
2184
+ server.middlewares.use(async (req, res, next) => {
2185
+ const url = req.url || "/";
2186
+ const match = url.match(PREVIEW_ROUTE_PATTERN);
2187
+ if (match) {
2188
+ const storyId = match[1];
2189
+ const config = this.storyConfigs.get(storyId);
2190
+ if (config) try {
2191
+ const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
2192
+ const transformedHtml = await server.transformIndexHtml(url, htmlTemplate);
2193
+ res.statusCode = HTTP_STATUS_OK;
2194
+ res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
2195
+ res.end(transformedHtml);
2196
+ return;
2197
+ } catch (e) {
2198
+ const err = e;
2199
+ log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
2200
+ next(err);
2201
+ return;
2202
+ }
2203
+ }
2204
+ next();
2205
+ });
2206
+ }
2207
+ },
2045
2208
  createStoryEntryPlugin((id) => this.storyConfigs.get(id), (opts) => this.generateEntryFile(opts))
2046
2209
  ],
2047
2210
  resolve: this.getViteResolveConfig(resolvedAppPath),
@@ -2053,28 +2216,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2053
2216
  open: false
2054
2217
  }
2055
2218
  });
2056
- this.server.middlewares.use(async (req, res, next) => {
2057
- const url = req.url || "/";
2058
- const match = url.match(PREVIEW_ROUTE_PATTERN);
2059
- if (match) {
2060
- const storyId = match[1];
2061
- const config = this.storyConfigs.get(storyId);
2062
- if (config) try {
2063
- const htmlTemplate = this.generateHtmlTemplate(`${VIRTUAL_STORY_ENTRY_SCRIPT_PREFIX}${storyId}`, config.darkMode);
2064
- const transformedHtml = await this.server?.transformIndexHtml(url, htmlTemplate);
2065
- res.statusCode = HTTP_STATUS_OK;
2066
- res.setHeader(CONTENT_TYPE_HEADER, TEXT_HTML_CONTENT_TYPE);
2067
- res.end(transformedHtml);
2068
- return;
2069
- } catch (e) {
2070
- const err = e;
2071
- log.error(`[ViteMiddleware] Error serving preview: ${err.message}`);
2072
- next(err);
2073
- return;
2074
- }
2075
- }
2076
- next();
2077
- });
2219
+ createdServer = this.server;
2078
2220
  await this.server.listen();
2079
2221
  const address = this.server.httpServer?.address();
2080
2222
  if (!address || typeof address === "string") throw new Error("Failed to start Vite dev server. Ensure no other process is using the port.");
@@ -2089,6 +2231,15 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2089
2231
  port
2090
2232
  };
2091
2233
  } catch (error) {
2234
+ try {
2235
+ await createdServer?.close();
2236
+ } catch (closeError) {
2237
+ log.warn(`[ViteReactBundlerService] Failed to close server after startup failure: ${String(closeError)}`);
2238
+ }
2239
+ this.server = null;
2240
+ this.serverUrl = null;
2241
+ this.serverPort = null;
2242
+ this.currentAppPath = null;
2092
2243
  const err = /* @__PURE__ */ new Error(`Failed to start dev server for ${resolvedAppPath}: ${error instanceof Error ? error.message : String(error)}`);
2093
2244
  err.cause = error;
2094
2245
  throw err;
@@ -2107,7 +2258,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2107
2258
  validateComponentPath(componentPath);
2108
2259
  validateArgsSize(args);
2109
2260
  const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(this.monorepoRoot, appPath);
2110
- validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
2261
+ validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
2111
2262
  if (this.currentAppPath !== resolvedAppPath) throw new Error(`Dev server is running for ${this.currentAppPath} but requested ${resolvedAppPath}.`);
2112
2263
  const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
2113
2264
  try {
@@ -2155,7 +2306,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2155
2306
  validateComponentPath(componentPath);
2156
2307
  validateArgsSize(args);
2157
2308
  const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(this.monorepoRoot, appPath);
2158
- validateCssFiles(cssFiles, TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
2309
+ validateCssFiles(cssFiles, this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(resolvedAppPath));
2159
2310
  const tmpDir = path.join(resolvedAppPath, TMP_DIR_NAME);
2160
2311
  try {
2161
2312
  await promises.mkdir(tmpDir, { recursive: true });
@@ -2181,9 +2332,10 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2181
2332
  * Subclasses can override to add platform-specific aliases (e.g., react-native-web).
2182
2333
  */
2183
2334
  getViteResolveConfig(appPath) {
2184
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
2335
+ const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
2185
2336
  const frontendPackagePath = (packageDir, sourcePath = "index.ts") => path.join(workspaceRoot, WORKSPACE_PACKAGE_DIR_PREFIX, "frontend", packageDir, APP_SOURCE_DIR$1, sourcePath);
2186
2337
  return { alias: [
2338
+ ...appReactAliases(appPath),
2187
2339
  {
2188
2340
  find: APP_SOURCE_ALIAS_PATTERN,
2189
2341
  replacement: `${path.join(appPath, APP_SOURCE_DIR$1)}/`
@@ -2290,7 +2442,7 @@ var ViteReactBundlerService = class ViteReactBundlerService extends BaseBundlerS
2290
2442
  */
2291
2443
  async generateWrapperCss(appPath, cssFiles) {
2292
2444
  const resolvedCssFiles = cssFiles.length > 0 ? cssFiles : await this.getDefaultCssFiles(appPath);
2293
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync(appPath);
2445
+ const workspaceRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(appPath);
2294
2446
  const cssImportStatements = resolvedCssFiles.map((cssFile) => {
2295
2447
  if (path.isAbsolute(cssFile)) return `@import '${cssFile}';`;
2296
2448
  if (cssFile.startsWith(PACKAGE_IMPORT_PREFIX) || cssFile.startsWith(TAILWIND_IMPORT_PREFIX)) return `@import '${cssFile}';`;
@@ -2512,20 +2664,16 @@ mount();
2512
2664
  *
2513
2665
  * Extends ViteReactBundlerService with React Native → web aliases
2514
2666
  * so native-ui components can be rendered in a browser via Playwright.
2515
- * Uses React Native Web and the app's real Unistyles web runtime.
2667
+ * Uses the app's own React Native Web and Unistyles web runtime.
2516
2668
  *
2517
- * All shim paths resolve relative to this package — no monorepo-root assumptions.
2669
+ * Shim paths resolve relative to this package; React, react-dom and
2670
+ * react-native-web are peers and always resolve from the app.
2518
2671
  */
2519
2672
  const __dirname = dirname(fileURLToPath(import.meta.url));
2520
- const require$1 = createRequire(import.meta.url);
2521
- const REACT_PACKAGE = "react";
2522
- const REACT_DOM_PACKAGE = "react-dom";
2523
2673
  const REACT_NATIVE_PACKAGE = "react-native";
2524
2674
  const REACT_NATIVE_WEB_PACKAGE = "react-native-web";
2525
- const REACT_NATIVE_INDEX_SUBPATH = "react-native/index.js";
2526
2675
  const REACT_NATIVE_UNISTYLES_PACKAGE = "react-native-unistyles";
2527
2676
  const REACT_NATIVE_UNISTYLES_BABEL_PLUGIN = "react-native-unistyles/plugin";
2528
- const REACT_NATIVE_REANIMATED_PACKAGE = "react-native-reanimated";
2529
2677
  const REACT_NATIVE_REANIMATED_BABEL_PLUGIN = "react-native-reanimated/plugin";
2530
2678
  const BABEL_CORE_PACKAGE = "@babel/core";
2531
2679
  const BABEL_PLUGIN_SYNTAX_JSX = "@babel/plugin-syntax-jsx";
@@ -2539,8 +2687,7 @@ const APP_SOURCE_ALIAS = "@";
2539
2687
  const APP_SOURCE_DIR = "src";
2540
2688
  const VITE_PLUGIN_ENFORCE_PRE = "pre";
2541
2689
  const VITE_REACT_NATIVE_BUNDLER_ID = "vite-react-native";
2542
- const REACT_NATIVE_REDIRECT_PLUGIN_NAME = "vite-redirect-react-native";
2543
- const REACT_NATIVE_REANIMATED_REDIRECT_PLUGIN_NAME = "vite-redirect-react-native-reanimated";
2690
+ const REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME = "vite-react-native-optimize-deps";
2544
2691
  const EXPO_ICONS_REDIRECT_PLUGIN_NAME = "vite-redirect-expo-vector-icons";
2545
2692
  const NATIVE_BABEL_TRANSFORM_PLUGIN_NAME = "vite-react-native-babel";
2546
2693
  const EXPO_VECTOR_ICONS_RESOLVED_MODULE_PREFIX = `\0virtual:style-system-expo-vector-icons`;
@@ -2551,10 +2698,8 @@ const EXPO_VECTOR_ICONS_SHIM_FILE = "expo-vector-icons.ts";
2551
2698
  const EXPO_SYMBOLS_SHIM_FILE = "expo-symbols.ts";
2552
2699
  const REQUIRED_REACT_NATIVE_SHIM_FILES = [EXPO_VECTOR_ICONS_SHIM_FILE, EXPO_SYMBOLS_SHIM_FILE];
2553
2700
  const PACKAGE_JSON_SUBPATH = "package.json";
2554
- const REACT_NATIVE_WEB_DIST_INDEX = "dist/index.js";
2555
2701
  const REACT_NATIVE_UNISTYLES_WEB_INDEX = "lib/module/index.js";
2556
2702
  const REACT_NATIVE_UNISTYLES_NATIVE_COMPONENTS = "lib/module/components/native";
2557
- const REACT_NATIVE_REANIMATED_WEB_INDEX = "lib/module/index.js";
2558
2703
  const EXPO_VECTOR_ICONS_GLYPHMAP_DIR = "build/vendor/react-native-vector-icons/glyphmaps";
2559
2704
  const EXPO_VECTOR_ICONS_FONT_DIR = "build/vendor/react-native-vector-icons/Fonts";
2560
2705
  const GLYPHMAP_EXTENSION = ".json";
@@ -2573,6 +2718,8 @@ const REACT_NATIVE_DEV_GLOBAL = "__DEV__";
2573
2718
  const REACT_NATIVE_DEV_VALUE = "false";
2574
2719
  const REACT_NATIVE_GLOBAL = "global";
2575
2720
  const REACT_NATIVE_GLOBAL_VALUE = "globalThis";
2721
+ const EXPO_OS_DEFINE = "process.env.EXPO_OS";
2722
+ const EXPO_OS_WEB_VALUE = JSON.stringify("web");
2576
2723
  const VITE_RESOLVE_EXTENSIONS = [
2577
2724
  ".web.tsx",
2578
2725
  ".web.ts",
@@ -2595,18 +2742,13 @@ const SHIMS_DIR_CANDIDATES = [
2595
2742
  path.resolve(__dirname, "../src/shims")
2596
2743
  ];
2597
2744
  const SHIMS_DIR = resolveShimsDir();
2598
- const REACT_NATIVE_WEB_DIR = resolvePackageDir(REACT_NATIVE_WEB_PACKAGE);
2599
2745
  function resolveExistingDir(candidates, label, isValidDir = () => true) {
2600
2746
  const existing = candidates.find((candidate) => existsSync(candidate) && isValidDir(candidate));
2601
2747
  if (existing) return existing;
2602
2748
  throw new Error(`Could not resolve ${label}. Tried: ${candidates.join(", ")}`);
2603
2749
  }
2604
- function resolvePackageDir(packageName) {
2605
- return dirname(require$1.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
2606
- }
2607
2750
  function resolvePackageDirFrom(fromPath, packageName) {
2608
- const fromRequire = createRequire(path.join(fromPath, PACKAGE_JSON_SUBPATH));
2609
- return dirname(fromRequire.resolve(`${packageName}/${PACKAGE_JSON_SUBPATH}`));
2751
+ return appPackageDir(fromPath, packageName);
2610
2752
  }
2611
2753
  function resolvePackageSubpathFrom(fromPath, subpath) {
2612
2754
  return createRequire(path.join(fromPath, PACKAGE_JSON_SUBPATH)).resolve(subpath);
@@ -2701,6 +2843,7 @@ function createNativeBabelTransformPlugin(appPath) {
2701
2843
  name: NATIVE_BABEL_TRANSFORM_PLUGIN_NAME,
2702
2844
  enforce: VITE_PLUGIN_ENFORCE_PRE,
2703
2845
  async transform(code, id) {
2846
+ id = id.split(VITE_QUERY_SEPARATOR)[0] ?? id;
2704
2847
  const isFlowModule = toViteImportPath(id).includes("/@react-native-segmented-control/segmented-control/");
2705
2848
  const isSupportedNodeModule = isFlowModule || id.includes(REACT_NATIVE_MARKDOWN_DISPLAY_PATH_SEGMENT);
2706
2849
  if (id.includes(NODE_MODULES_PATH_SEGMENT) && !isSupportedNodeModule || !BABEL_TRANSFORM_EXTENSION_PATTERN.test(id)) return null;
@@ -2842,24 +2985,20 @@ var ViteReactNativeBundlerService = class ViteReactNativeBundlerService extends
2842
2985
  return VITE_REACT_NATIVE_BUNDLER_ID;
2843
2986
  }
2844
2987
  getAdditionalVitePlugins(appPath) {
2845
- const reactNativeWebEntry = path.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX);
2846
- const reactNativeReanimatedEntry = appPath ? path.join(resolvePackageDirFrom(appPath, REACT_NATIVE_REANIMATED_PACKAGE), REACT_NATIVE_REANIMATED_WEB_INDEX) : null;
2847
2988
  return [
2848
2989
  {
2849
- name: REACT_NATIVE_REDIRECT_PLUGIN_NAME,
2850
- enforce: VITE_PLUGIN_ENFORCE_PRE,
2851
- resolveId(source) {
2852
- if (source === REACT_NATIVE_PACKAGE || source === REACT_NATIVE_INDEX_SUBPATH) return reactNativeWebEntry;
2853
- return null;
2854
- }
2855
- },
2856
- {
2857
- name: REACT_NATIVE_REANIMATED_REDIRECT_PLUGIN_NAME,
2858
- enforce: VITE_PLUGIN_ENFORCE_PRE,
2859
- resolveId(source) {
2860
- if (source === REACT_NATIVE_REANIMATED_PACKAGE) return reactNativeReanimatedEntry;
2861
- return null;
2862
- }
2990
+ name: REACT_NATIVE_OPTIMIZE_DEPS_PLUGIN_NAME,
2991
+ config: (config) => ({ optimizeDeps: {
2992
+ rolldownOptions: { resolve: { extensions: config.resolve?.extensions } },
2993
+ exclude: [
2994
+ "@react-native-segmented-control/segmented-control",
2995
+ SAFE_AREA_CONTEXT_PACKAGE,
2996
+ "expo-haptics",
2997
+ "expo-modules-core",
2998
+ "react-native-gesture-handler"
2999
+ ],
3000
+ include: ["react-native-gesture-handler > hoist-non-react-statics", "react-native-gesture-handler > invariant"]
3001
+ } })
2863
3002
  },
2864
3003
  createExpoTypeDeclarationStubPlugin(),
2865
3004
  createSvgComponentPlugin(),
@@ -2895,7 +3034,8 @@ const styleSystemSafeAreaMetrics = {
2895
3034
  getViteDefineConfig() {
2896
3035
  return {
2897
3036
  [REACT_NATIVE_DEV_GLOBAL]: REACT_NATIVE_DEV_VALUE,
2898
- [REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE
3037
+ [REACT_NATIVE_GLOBAL]: REACT_NATIVE_GLOBAL_VALUE,
3038
+ [EXPO_OS_DEFINE]: EXPO_OS_WEB_VALUE
2899
3039
  };
2900
3040
  }
2901
3041
  generatePreviewStyles() {
@@ -2920,38 +3060,18 @@ const styleSystemSafeAreaMetrics = {
2920
3060
  getViteResolveConfig(appPath) {
2921
3061
  const reactNativeUnistylesDir = resolvePackageDirFrom(appPath, REACT_NATIVE_UNISTYLES_PACKAGE);
2922
3062
  const reactNativeUnistylesEntry = path.join(reactNativeUnistylesDir, REACT_NATIVE_UNISTYLES_WEB_INDEX);
2923
- const reactDir = resolvePackageDirFrom(appPath, REACT_PACKAGE);
2924
- const reactDomDir = resolvePackageDirFrom(appPath, REACT_DOM_PACKAGE);
3063
+ const reactNativeWebDir = resolvePackageDirFrom(appPath, REACT_NATIVE_WEB_PACKAGE);
2925
3064
  return {
2926
3065
  extensions: [...VITE_RESOLVE_EXTENSIONS],
2927
3066
  alias: [
2928
- {
2929
- find: /^react$/,
2930
- replacement: reactDir
2931
- },
2932
- {
2933
- find: /^react\/(.*)$/,
2934
- replacement: path.join(reactDir, "$1")
2935
- },
2936
- {
2937
- find: /^react-dom$/,
2938
- replacement: reactDomDir
2939
- },
2940
- {
2941
- find: /^react-dom\/(.*)$/,
2942
- replacement: path.join(reactDomDir, "$1")
2943
- },
3067
+ ...appReactAliases(appPath),
2944
3068
  {
2945
3069
  find: APP_SOURCE_ALIAS,
2946
3070
  replacement: path.join(appPath, APP_SOURCE_DIR)
2947
3071
  },
2948
3072
  {
2949
- find: REACT_NATIVE_PACKAGE,
2950
- replacement: REACT_NATIVE_WEB_DIR
2951
- },
2952
- {
2953
- find: REACT_NATIVE_INDEX_SUBPATH,
2954
- replacement: path.join(REACT_NATIVE_WEB_DIR, REACT_NATIVE_WEB_DIST_INDEX)
3073
+ find: /^react-native$/,
3074
+ replacement: reactNativeWebDir
2955
3075
  },
2956
3076
  {
2957
3077
  find: /^react-native-unistyles\/components\/native\/(.+)$/,
@@ -3014,8 +3134,8 @@ const VALID_SERVICE_EXTENSIONS = [
3014
3134
  * await bundler.startDevServer('apps/my-app');
3015
3135
  * ```
3016
3136
  */
3017
- function createDefaultBundlerService() {
3018
- return ViteReactBundlerService.getInstance();
3137
+ function createDefaultBundlerService(workspaceRoot) {
3138
+ return workspaceRoot ? new ViteReactBundlerService({}, workspaceRoot) : ViteReactBundlerService.getInstance();
3019
3139
  }
3020
3140
  /**
3021
3141
  * Registry of available bundler service factories.
@@ -3050,13 +3170,17 @@ bundlerRegistry.set("vite-react-native", () => ViteReactNativeBundlerService.get
3050
3170
  * const bundler = getBundlerService('unknown'); // Returns ViteReactBundlerService
3051
3171
  * ```
3052
3172
  */
3053
- function getBundlerService(key) {
3173
+ function getBundlerService(key, workspaceRoot) {
3174
+ if (workspaceRoot) {
3175
+ if (key === "vite-react-native") return new ViteReactNativeBundlerService({}, workspaceRoot);
3176
+ if (key === "vite-react") return new ViteReactBundlerService({}, workspaceRoot);
3177
+ }
3054
3178
  const factory = bundlerRegistry.get(key);
3055
3179
  if (factory) return factory();
3056
- return createDefaultBundlerService();
3180
+ return createDefaultBundlerService(workspaceRoot);
3057
3181
  }
3058
3182
  /** Cached bundler service instance loaded from config */
3059
- let cachedBundlerService = null;
3183
+ let cachedDefaultBundlerService = null;
3060
3184
  /**
3061
3185
  * Get bundler service based on the workspace style-system.config.yaml.
3062
3186
  *
@@ -3080,25 +3204,31 @@ let cachedBundlerService = null;
3080
3204
  * await bundler.startDevServer('apps/my-app');
3081
3205
  * ```
3082
3206
  */
3083
- async function getBundlerServiceFromConfig() {
3084
- if (cachedBundlerService) return cachedBundlerService;
3085
- const config = await getBundlerConfig();
3207
+ async function getBundlerServiceFromConfig(options) {
3208
+ if (options) return loadBundlerServiceFromConfig(options);
3209
+ cachedDefaultBundlerService ??= await loadBundlerServiceFromConfig();
3210
+ return cachedDefaultBundlerService;
3211
+ }
3212
+ async function loadBundlerServiceFromConfig(options) {
3213
+ let cachedBundlerService = null;
3214
+ const fallback = options?.defaultFactory ?? (() => createDefaultBundlerService(options?.workspaceRoot));
3215
+ const config = options ? (await (options.configLoader ?? new StyleSystemConfigLoader(options.workspaceRoot)).loadRoot()).bundler : await getBundlerConfig();
3086
3216
  if (!config?.customService) {
3087
- cachedBundlerService = createDefaultBundlerService();
3217
+ cachedBundlerService = fallback();
3088
3218
  return cachedBundlerService;
3089
3219
  }
3090
- const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
3220
+ const monorepoRoot = options?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
3091
3221
  const customServicePath = path.resolve(monorepoRoot, config.customService);
3092
3222
  const normalizedWorkspaceRoot = path.resolve(monorepoRoot);
3093
3223
  if (!customServicePath.startsWith(normalizedWorkspaceRoot + path.sep)) {
3094
3224
  log.error(`[BundlerServiceFactory] Security error: customService path "${config.customService}" resolves outside workspace root`);
3095
- cachedBundlerService = createDefaultBundlerService();
3225
+ cachedBundlerService = fallback();
3096
3226
  return cachedBundlerService;
3097
3227
  }
3098
3228
  const ext = path.extname(customServicePath).toLowerCase();
3099
3229
  if (!VALID_SERVICE_EXTENSIONS.includes(ext)) {
3100
3230
  log.error(`[BundlerServiceFactory] Invalid file extension "${ext}" for customService. Expected one of: ${VALID_SERVICE_EXTENSIONS.join(", ")}`);
3101
- cachedBundlerService = createDefaultBundlerService();
3231
+ cachedBundlerService = fallback();
3102
3232
  return cachedBundlerService;
3103
3233
  }
3104
3234
  try {
@@ -3130,7 +3260,7 @@ async function getBundlerServiceFromConfig() {
3130
3260
  const message = error instanceof Error ? error.message : String(error);
3131
3261
  log.error(`[BundlerServiceFactory] Failed to load custom bundler service: ${message}`);
3132
3262
  log.info("[BundlerServiceFactory] Falling back to default ViteReactBundlerService");
3133
- cachedBundlerService = createDefaultBundlerService();
3263
+ cachedBundlerService = fallback();
3134
3264
  return cachedBundlerService;
3135
3265
  }
3136
3266
  }
@@ -3175,8 +3305,8 @@ var ThemeService = class {
3175
3305
  * Creates a new ThemeService instance
3176
3306
  * @param config - Design system configuration
3177
3307
  */
3178
- constructor(config) {
3179
- this.monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
3308
+ constructor(config, workspaceRoot) {
3309
+ this.monorepoRoot = workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
3180
3310
  this.config = config;
3181
3311
  if (this.config.themeProvider) log.info(`[ThemeService] Using theme provider: ${this.config.themeProvider}`);
3182
3312
  log.info(`[ThemeService] Design system type: ${this.config.type}`);
@@ -3339,7 +3469,8 @@ export { WrappedComponent };
3339
3469
  */
3340
3470
  const DEFAULT_RENDER_WIDTH = 1280;
3341
3471
  const DEFAULT_RENDER_HEIGHT = 800;
3342
- const SCREENSHOT_WAIT_TIME_MS = 2e3;
3472
+ /** Settle time after the readiness check in takeScreenshot (mount, fonts, two frames). */
3473
+ const SCREENSHOT_WAIT_TIME_MS = 100;
3343
3474
  const DEFAULT_CLEANUP_AGE_MS = 36e5;
3344
3475
  const COMPONENT_FILE_PREFIX = "component-";
3345
3476
  const FILE_URL_PREFIX = "file://";
@@ -3379,6 +3510,9 @@ var ComponentRendererService = class ComponentRendererService {
3379
3510
  themeService;
3380
3511
  appPath;
3381
3512
  bundlerFactory;
3513
+ log;
3514
+ preserveOutput;
3515
+ sharedBrowser;
3382
3516
  /**
3383
3517
  * Creates a new ComponentRendererService instance
3384
3518
  * @param designSystemConfig - Design system configuration
@@ -3388,8 +3522,12 @@ var ComponentRendererService = class ComponentRendererService {
3388
3522
  constructor(designSystemConfig, appPath, options = {}) {
3389
3523
  if (!appPath) throw new Error("appPath is required for ComponentRendererService");
3390
3524
  this.appPath = appPath;
3391
- this.tmpDir = path.join(os.tmpdir(), "style-system");
3392
- this.themeService = new ThemeService(designSystemConfig);
3525
+ this.sharedBrowser = options.sharedBrowser;
3526
+ this.tmpDir = options.temporaryDirectory ?? path.join(os.tmpdir(), "style-system");
3527
+ this.preserveOutput = options.temporaryDirectory !== void 0;
3528
+ if (options.temporaryDirectory !== void 0 && !path.isAbsolute(options.temporaryDirectory)) throw new Error("Renderer temporary directory must be absolute.");
3529
+ this.log = log;
3530
+ this.themeService = new ThemeService(designSystemConfig, options.workspaceRoot);
3393
3531
  if (options.bundlerFactory) this.bundlerFactory = options.bundlerFactory;
3394
3532
  else if (designSystemConfig.bundler) this.bundlerFactory = () => getBundlerService(designSystemConfig.bundler);
3395
3533
  else this.bundlerFactory = createDefaultBundlerService;
@@ -3412,17 +3550,25 @@ var ComponentRendererService = class ComponentRendererService {
3412
3550
  async renderComponent(componentInfo, options = {}) {
3413
3551
  const { storyName = componentInfo.stories[0] || "Default", args = {}, darkMode = false, width = DEFAULT_RENDER_WIDTH, height = DEFAULT_RENDER_HEIGHT } = options;
3414
3552
  try {
3415
- log.info(`[ComponentRendererService] Rendering ${componentInfo.title} - ${storyName}`);
3416
- await promises.mkdir(this.tmpDir, { recursive: true });
3553
+ await promises.mkdir(this.tmpDir, {
3554
+ recursive: true,
3555
+ mode: 448
3556
+ });
3557
+ if (this.preserveOutput) {
3558
+ const info = await promises.lstat(this.tmpDir);
3559
+ if (!info.isDirectory() || info.isSymbolicLink() || await promises.realpath(this.tmpDir) !== this.tmpDir) throw new Error("Unsafe renderer temporary directory.");
3560
+ if (this.log === log) this.log = createLog(this.tmpDir);
3561
+ }
3562
+ this.log.info(`[ComponentRendererService] Rendering ${componentInfo.title} - ${storyName}`);
3417
3563
  const designSystemConfig = this.themeService.getConfig();
3418
- log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
3419
- log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
3420
- if (!await this.themeService.validateThemeProvider()) log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
3564
+ this.log.info(`[ComponentRendererService] Using theme provider: ${designSystemConfig.themeProvider}`);
3565
+ this.log.info(`[ComponentRendererService] Design system type: ${designSystemConfig.type}`);
3566
+ if (!await this.themeService.validateThemeProvider()) this.log.warn(`[ComponentRendererService] Theme provider path may not exist: ${designSystemConfig.themeProvider}`);
3421
3567
  const bundlerService = this.getBundlerService();
3422
3568
  let componentUrl;
3423
3569
  let htmlFilePath;
3424
3570
  if (bundlerService.isServerRunning()) {
3425
- log.info("[ComponentRendererService] Using dev server for fast rendering");
3571
+ this.log.info("[ComponentRendererService] Using dev server for fast rendering");
3426
3572
  try {
3427
3573
  const result = await bundlerService.serveComponent({
3428
3574
  componentPath: componentInfo.filePath,
@@ -3437,9 +3583,9 @@ var ComponentRendererService = class ComponentRendererService {
3437
3583
  });
3438
3584
  componentUrl = result.url;
3439
3585
  htmlFilePath = result.htmlFilePath;
3440
- log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
3586
+ this.log.info(`[ComponentRendererService] Component served at: ${componentUrl}`);
3441
3587
  } catch (devServerError) {
3442
- log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
3588
+ this.log.warn(`[ComponentRendererService] Dev server failed, falling back to static build: ${devServerError instanceof Error ? devServerError.message : String(devServerError)}`);
3443
3589
  const result = await bundlerService.prerenderComponent({
3444
3590
  componentPath: componentInfo.filePath,
3445
3591
  storyName,
@@ -3453,10 +3599,10 @@ var ComponentRendererService = class ComponentRendererService {
3453
3599
  });
3454
3600
  componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
3455
3601
  htmlFilePath = result.htmlFilePath;
3456
- log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
3602
+ this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
3457
3603
  }
3458
3604
  } else {
3459
- log.info("[ComponentRendererService] No dev server running, using static build");
3605
+ this.log.info("[ComponentRendererService] No dev server running, using static build");
3460
3606
  const result = await bundlerService.prerenderComponent({
3461
3607
  componentPath: componentInfo.filePath,
3462
3608
  storyName,
@@ -3470,7 +3616,7 @@ var ComponentRendererService = class ComponentRendererService {
3470
3616
  });
3471
3617
  componentUrl = `${FILE_URL_PREFIX}${result.htmlFilePath}`;
3472
3618
  htmlFilePath = result.htmlFilePath;
3473
- log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
3619
+ this.log.info(`[ComponentRendererService] HTML built to: ${htmlFilePath}`);
3474
3620
  }
3475
3621
  const timestamp = Date.now();
3476
3622
  const imageName = `${COMPONENT_FILE_PREFIX}${componentInfo.title.split("/").pop()}-${timestamp}-${randomUUID()}.png`;
@@ -3480,20 +3626,21 @@ var ComponentRendererService = class ComponentRendererService {
3480
3626
  output: imagePath,
3481
3627
  width,
3482
3628
  height,
3483
- fullPage: true,
3629
+ fullPage: false,
3484
3630
  browser: "chromium",
3485
3631
  waitTime: SCREENSHOT_WAIT_TIME_MS,
3486
3632
  darkMode,
3487
3633
  mobile: false,
3488
3634
  generateThumbnail: false,
3489
- base64: false
3635
+ base64: false,
3636
+ sharedBrowser: this.sharedBrowser
3490
3637
  });
3491
- log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
3638
+ this.log.info(`[ComponentRendererService] Screenshot saved to: ${imagePath}`);
3492
3639
  let html = "";
3493
3640
  if (htmlFilePath) {
3494
3641
  html = await promises.readFile(htmlFilePath, "utf-8");
3495
- log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
3496
- } else log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
3642
+ this.log.info(`[ComponentRendererService] HTML file kept at: ${htmlFilePath}`);
3643
+ } else this.log.warn("[ComponentRendererService] No HTML file path available, returning empty HTML content");
3497
3644
  return {
3498
3645
  imagePath,
3499
3646
  html,
@@ -3536,20 +3683,20 @@ var ComponentRendererService = class ComponentRendererService {
3536
3683
  };
3537
3684
  }));
3538
3685
  for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
3539
- else log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
3686
+ else this.log.warn("[ComponentRendererService] Failed to inspect component file during cleanup:", result.reason);
3540
3687
  const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
3541
- await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
3688
+ await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
3542
3689
  let deletedCount = oldFiles.length;
3543
3690
  const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
3544
3691
  if (remainingFiles.length > keepCount) {
3545
3692
  remainingFiles.sort((a, b) => a.mtime - b.mtime);
3546
3693
  const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
3547
- await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
3694
+ await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => this.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
3548
3695
  deletedCount += toDelete.length;
3549
3696
  }
3550
- if (deletedCount > 0) log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
3697
+ if (deletedCount > 0) this.log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
3551
3698
  } catch (error) {
3552
- log.error("[ComponentRendererService] Cleanup error:", error);
3699
+ this.log.error("[ComponentRendererService] Cleanup error:", error);
3553
3700
  }
3554
3701
  }
3555
3702
  /**
@@ -3559,11 +3706,11 @@ var ComponentRendererService = class ComponentRendererService {
3559
3706
  */
3560
3707
  async dispose() {
3561
3708
  try {
3562
- await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
3709
+ if (!this.preserveOutput) await this.cleanup(void 0, ComponentRendererService.DISPOSE_KEEP_COUNT);
3563
3710
  const bundlerService = this.getBundlerService();
3564
3711
  if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
3565
3712
  } catch (error) {
3566
- log.error("[ComponentRendererService] Dispose error:", error);
3713
+ this.log.error("[ComponentRendererService] Dispose error:", error);
3567
3714
  }
3568
3715
  }
3569
3716
  };
@@ -3579,13 +3726,6 @@ const DEFAULT_GET_UI_COMPONENT_CONFIG = {
3579
3726
  defaultHeight: 800
3580
3727
  };
3581
3728
  //#endregion
3582
- //#region \0@oxc-project+runtime@0.153.0/helpers/esm/decorateParam.js
3583
- function __decorateParam(paramIndex, decorator) {
3584
- return function(target, key) {
3585
- decorator(target, key, paramIndex);
3586
- };
3587
- }
3588
- //#endregion
3589
3729
  //#region src/services/GetUiComponentService/GetUiComponentService.ts
3590
3730
  /**
3591
3731
  * GetUiComponentService
@@ -3610,6 +3750,7 @@ const NATIVE_DEFAULT_VIEWPORT = {
3610
3750
  height: 852
3611
3751
  };
3612
3752
  let GetUiComponentService = class GetUiComponentService {
3753
+ configLoader;
3613
3754
  config;
3614
3755
  storiesIndexFactory;
3615
3756
  rendererFactory;
@@ -3619,7 +3760,8 @@ let GetUiComponentService = class GetUiComponentService {
3619
3760
  * @param storiesIndexFactory - Factory for creating StoriesIndexService (for DI/testing)
3620
3761
  * @param rendererFactory - Factory for creating ComponentRendererService (for DI/testing)
3621
3762
  */
3622
- constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p)) {
3763
+ constructor(config = {}, storiesIndexFactory = () => new StoriesIndexService(), rendererFactory = (c, p) => new ComponentRendererService(c, p), configLoader) {
3764
+ this.configLoader = configLoader;
3623
3765
  this.config = {
3624
3766
  ...DEFAULT_GET_UI_COMPONENT_CONFIG,
3625
3767
  ...config
@@ -3651,7 +3793,7 @@ let GetUiComponentService = class GetUiComponentService {
3651
3793
  const storiesIndex = this.storiesIndexFactory();
3652
3794
  let designSystemConfig;
3653
3795
  try {
3654
- [, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
3796
+ [, designSystemConfig] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.resolveProject(appPath).then((resolved) => resolved.config) : getAppDesignSystemConfig(appPath)]);
3655
3797
  } catch (error) {
3656
3798
  throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
3657
3799
  }
@@ -3751,10 +3893,13 @@ GetUiComponentService = __decorate([
3751
3893
  __decorateParam(1, optional()),
3752
3894
  __decorateParam(2, inject(STYLE_SYSTEM_TYPES.RendererFactory)),
3753
3895
  __decorateParam(2, optional()),
3896
+ __decorateParam(3, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
3897
+ __decorateParam(3, optional()),
3754
3898
  __decorateMetadata("design:paramtypes", [
3755
3899
  Object,
3756
3900
  Object,
3757
- Object
3901
+ Object,
3902
+ typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
3758
3903
  ])
3759
3904
  ], GetUiComponentService);
3760
3905
  //#endregion
@@ -3783,12 +3928,14 @@ const DEFAULT_APP_COMPONENTS_CONFIG = { pageSize: 50 };
3783
3928
  * - Direct tool implementation (services should be tool-agnostic)
3784
3929
  */
3785
3930
  let AppComponentsService = class AppComponentsService {
3931
+ workspaceRoot;
3786
3932
  config;
3787
3933
  /**
3788
3934
  * Creates a new AppComponentsService instance.
3789
3935
  * @param config - Service configuration options
3790
3936
  */
3791
- constructor(config = {}) {
3937
+ constructor(config = {}, workspaceRoot) {
3938
+ this.workspaceRoot = workspaceRoot;
3792
3939
  this.config = {
3793
3940
  ...DEFAULT_APP_COMPONENTS_CONFIG,
3794
3941
  ...config
@@ -3805,14 +3952,14 @@ let AppComponentsService = class AppComponentsService {
3805
3952
  if (input.cursor !== void 0 && typeof input.cursor !== "string") throw new Error("cursor must be a string");
3806
3953
  const { appPath, cursor } = input;
3807
3954
  const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
3808
- const monorepoRoot = TemplatesManagerService.getWorkspaceRootSync();
3955
+ const monorepoRoot = this.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync();
3809
3956
  const resolvedAppPath = path.isAbsolute(appPath) ? appPath : path.join(monorepoRoot, appPath);
3810
3957
  try {
3811
3958
  await promises.access(resolvedAppPath);
3812
3959
  } catch {
3813
3960
  throw new Error(`App path does not exist: ${resolvedAppPath}`);
3814
3961
  }
3815
- const storiesIndex = new StoriesIndexService();
3962
+ const storiesIndex = new StoriesIndexService({ workspaceRoot: monorepoRoot });
3816
3963
  const [appName, workspaceDependencies, packageMap] = await Promise.all([
3817
3964
  this.getAppName(resolvedAppPath),
3818
3965
  this.getWorkspaceDependencies(resolvedAppPath),
@@ -4015,18 +4162,42 @@ AppComponentsService = __decorate([
4015
4162
  injectable(),
4016
4163
  __decorateParam(0, inject(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig)),
4017
4164
  __decorateParam(0, optional()),
4018
- __decorateMetadata("design:paramtypes", [Object])
4165
+ __decorateParam(1, inject(STYLE_SYSTEM_TYPES.WorkspaceRoot)),
4166
+ __decorateParam(1, optional()),
4167
+ __decorateMetadata("design:paramtypes", [Object, String])
4019
4168
  ], AppComponentsService);
4020
4169
  //#endregion
4021
4170
  //#region src/tools/ListThemesTool.ts
4171
+ /**
4172
+ * ListThemesTool
4173
+ *
4174
+ * DESIGN PATTERNS:
4175
+ * - Tool pattern with getDefinition() and execute() methods
4176
+ * - Factory pattern for service creation
4177
+ * - JSON Schema validation for inputs
4178
+ *
4179
+ * CODING STANDARDS:
4180
+ * - Implement Tool interface from ../types
4181
+ * - Use TOOL_NAME constant with snake_case (e.g., 'list_themes')
4182
+ * - Return CallToolResult with content array
4183
+ * - Handle errors with isError flag
4184
+ * - Delegate complex logic to services
4185
+ *
4186
+ * AVOID:
4187
+ * - Complex business logic in execute method
4188
+ * - Unhandled promise rejections
4189
+ * - Missing input validation
4190
+ */
4022
4191
  var _ListThemesTool;
4023
4192
  let ListThemesTool = class ListThemesTool {
4024
4193
  static {
4025
4194
  _ListThemesTool = this;
4026
4195
  }
4196
+ configLoader;
4027
4197
  static TOOL_NAME = "list_themes";
4028
4198
  serviceFactory;
4029
- constructor(serviceFactory) {
4199
+ constructor(serviceFactory, configLoader) {
4200
+ this.configLoader = configLoader;
4030
4201
  this.serviceFactory = serviceFactory;
4031
4202
  }
4032
4203
  getDefinition() {
@@ -4048,12 +4219,13 @@ let ListThemesTool = class ListThemesTool {
4048
4219
  let themePath;
4049
4220
  let cssFiles;
4050
4221
  if (input.appPath) {
4051
- const appConfig = await getAppDesignSystemConfig(input.appPath);
4222
+ const appConfig = this.configLoader ? (await this.configLoader.resolveProject(input.appPath)).config : await getAppDesignSystemConfig(input.appPath);
4052
4223
  themePath = appConfig.themePath ? await resolveStyleSystemThemePath({
4053
4224
  appPath: input.appPath,
4054
- themePath: appConfig.themePath
4225
+ themePath: appConfig.themePath,
4226
+ workspaceRoot: this.configLoader?.workspaceRoot
4055
4227
  }) : void 0;
4056
- cssFiles = themePath ? void 0 : appConfig.cssFiles;
4228
+ cssFiles = themePath ? void 0 : appConfig.cssFiles?.map((file) => path.resolve(this.configLoader?.workspaceRoot ?? TemplatesManagerService.getWorkspaceRootSync(), input.appPath, file));
4057
4229
  }
4058
4230
  const result = await (await this.serviceFactory.createService({
4059
4231
  themePath,
@@ -4077,7 +4249,9 @@ let ListThemesTool = class ListThemesTool {
4077
4249
  ListThemesTool = _ListThemesTool = __decorate([
4078
4250
  injectable(),
4079
4251
  __decorateParam(0, inject(STYLE_SYSTEM_TYPES.ThemeServiceFactory)),
4080
- __decorateMetadata("design:paramtypes", [typeof ThemeServiceFactory === "undefined" ? Object : ThemeServiceFactory])
4252
+ __decorateParam(1, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
4253
+ __decorateParam(1, optional()),
4254
+ __decorateMetadata("design:paramtypes", [typeof ThemeServiceFactory === "undefined" ? Object : ThemeServiceFactory, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
4081
4255
  ], ListThemesTool);
4082
4256
  //#endregion
4083
4257
  //#region src/tools/GetCSSClassesTool.ts
@@ -4104,6 +4278,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
4104
4278
  static {
4105
4279
  _GetCSSClassesTool = this;
4106
4280
  }
4281
+ configLoader;
4107
4282
  static TOOL_NAME = "get_css_classes";
4108
4283
  static CSS_REUSE_INSTRUCTION = "IMPORTANT: Always reuse these existing CSS classes from the theme as much as possible instead of creating custom styles. This ensures design consistency and reduces CSS bloat.";
4109
4284
  serviceFactory;
@@ -4113,7 +4288,8 @@ let GetCSSClassesTool = class GetCSSClassesTool {
4113
4288
  * @param serviceFactory - Injected CSSClassesServiceFactory
4114
4289
  * @param defaultThemePath - Default path to theme CSS file (relative to workspace root)
4115
4290
  */
4116
- constructor(serviceFactory, defaultThemePath) {
4291
+ constructor(serviceFactory, defaultThemePath, configLoader) {
4292
+ this.configLoader = configLoader;
4117
4293
  this.serviceFactory = serviceFactory;
4118
4294
  this.defaultThemePath = defaultThemePath;
4119
4295
  }
@@ -4158,7 +4334,7 @@ let GetCSSClassesTool = class GetCSSClassesTool {
4158
4334
  const category = input.category || "all";
4159
4335
  if (!isValidCategory(category)) throw new Error(`Invalid category: '${category}'. Must be one of: ${VALID_CATEGORIES.join(", ")}`);
4160
4336
  const themePath = await this.resolveThemePath(input.appPath);
4161
- const workspaceConfig = await getGetCssClassesConfig();
4337
+ const workspaceConfig = this.configLoader ? (await this.configLoader.loadRoot()).getCssClasses : await getGetCssClassesConfig();
4162
4338
  const result = await (await this.serviceFactory.createService({ customServicePath: workspaceConfig?.customService })).extractClasses(category, themePath);
4163
4339
  return { content: [{
4164
4340
  type: "text",
@@ -4187,13 +4363,17 @@ let GetCSSClassesTool = class GetCSSClassesTool {
4187
4363
  */
4188
4364
  async resolveThemePath(appPath) {
4189
4365
  if (appPath) {
4190
- const config = await getAppDesignSystemConfig(appPath);
4366
+ const config = this.configLoader ? (await this.configLoader.resolveProject(appPath)).config : await getAppDesignSystemConfig(appPath);
4191
4367
  if (config.themePath) return await resolveStyleSystemThemePath({
4192
4368
  appPath,
4193
- themePath: config.themePath
4369
+ themePath: config.themePath,
4370
+ workspaceRoot: this.configLoader?.workspaceRoot
4194
4371
  });
4195
4372
  }
4196
- if (this.defaultThemePath) return await resolveStyleSystemThemePath({ themePath: this.defaultThemePath });
4373
+ if (this.defaultThemePath) return await resolveStyleSystemThemePath({
4374
+ themePath: this.defaultThemePath,
4375
+ workspaceRoot: this.configLoader?.workspaceRoot
4376
+ });
4197
4377
  throw new Error("No theme CSS path configured. Pass --theme-path, or set themePath in the target app style-system.config.yaml.");
4198
4378
  }
4199
4379
  };
@@ -4202,156 +4382,15 @@ GetCSSClassesTool = _GetCSSClassesTool = __decorate([
4202
4382
  __decorateParam(0, inject(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
4203
4383
  __decorateParam(1, inject(STYLE_SYSTEM_TYPES.DefaultThemePath)),
4204
4384
  __decorateParam(1, optional()),
4205
- __decorateMetadata("design:paramtypes", [typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory, String])
4385
+ __decorateParam(2, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
4386
+ __decorateParam(2, optional()),
4387
+ __decorateMetadata("design:paramtypes", [
4388
+ typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
4389
+ String,
4390
+ typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader
4391
+ ])
4206
4392
  ], GetCSSClassesTool);
4207
4393
  //#endregion
4208
- //#region src/services/AntigravityCliService.ts
4209
- const DEFAULT_MODEL = "gemini-3.6-flash-high";
4210
- const DEFAULT_TIMEOUT = 18e5;
4211
- const MAX_BUFFER = 104857600;
4212
- const FAILURE_SNIPPET_LENGTH = 400;
4213
- const ANTIGRAVITY_CLI_COMMAND = "agy";
4214
- const ANTIGRAVITY_PROVIDER_NAME = "antigravity";
4215
- const ANTIGRAVITY_UNAVAILABLE_MESSAGE = "Antigravity CLI not found. Run `agy install` to configure it.";
4216
- function sanitizePrompt(raw) {
4217
- return raw.replace(/\u0000/g, "").replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])/g, "").replace(/(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "").replace(/[\u0001-\u0008\u000B\u000C\u000E-\u001F\u007F\u0080-\u009F\uFEFF]/g, "");
4218
- }
4219
- function tailSnippet(value) {
4220
- const trimmed = value.trim();
4221
- if (!trimmed) return "";
4222
- return trimmed.length <= FAILURE_SNIPPET_LENGTH ? trimmed : `...${trimmed.slice(-400)}`;
4223
- }
4224
- var AntigravityCliService = class {
4225
- isAvailable() {
4226
- try {
4227
- execSync(`${ANTIGRAVITY_CLI_COMMAND} --version`, {
4228
- stdio: "ignore",
4229
- timeout: 5e3,
4230
- env: process.env
4231
- });
4232
- return true;
4233
- } catch {
4234
- return false;
4235
- }
4236
- }
4237
- async edit(params) {
4238
- if (!this.isAvailable()) throw new Error(ANTIGRAVITY_UNAVAILABLE_MESSAGE);
4239
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
4240
- const model = params.model ?? DEFAULT_MODEL;
4241
- const timeout = params.timeout ?? DEFAULT_TIMEOUT;
4242
- let fullPrompt = sanitizePrompt(params.prompt);
4243
- if (params.systemPrompt) fullPrompt = `${sanitizePrompt(params.systemPrompt)}\n\n${fullPrompt}`;
4244
- if (params.screenshotPaths?.length) fullPrompt = `${params.screenshotPaths.map((path) => `@${basename(path)}`).join(" ")}\n\n${fullPrompt}`;
4245
- const args = [
4246
- "--new-project",
4247
- "--dangerously-skip-permissions",
4248
- "--print",
4249
- "--print-timeout",
4250
- `${Math.ceil(timeout / 1e3)}s`,
4251
- "--model",
4252
- model,
4253
- fullPrompt
4254
- ];
4255
- return {
4256
- content: (await this.execAntigravity(args, workspaceRoot, model, timeout)).trim(),
4257
- model
4258
- };
4259
- }
4260
- execAntigravity(args, cwd, model, timeout) {
4261
- return new Promise((resolve, reject) => {
4262
- const chunks = [];
4263
- let totalBytes = 0;
4264
- let settled = false;
4265
- let stderrContent = "";
4266
- const registeredArgs = [...args];
4267
- registeredArgs[registeredArgs.length - 1] = "<prompt>";
4268
- const child = spawn(ANTIGRAVITY_CLI_COMMAND, args, {
4269
- stdio: [
4270
- "ignore",
4271
- "pipe",
4272
- "pipe"
4273
- ],
4274
- env: {
4275
- ...process.env,
4276
- OTEL_SERVICE_NAME: ANTIGRAVITY_PROVIDER_NAME
4277
- },
4278
- cwd
4279
- });
4280
- let releaseChild;
4281
- let registrationPromise;
4282
- if (child.pid) registrationPromise = registerManagedProcess({
4283
- repositoryPath: cwd,
4284
- serviceName: `style-system-antigravity-${child.pid}`,
4285
- serviceType: "tool",
4286
- environment: process.env["NODE_ENV"] ?? "development",
4287
- pid: child.pid,
4288
- command: ANTIGRAVITY_CLI_COMMAND,
4289
- args: registeredArgs,
4290
- metadata: {
4291
- model,
4292
- parentPid: process.pid
4293
- },
4294
- killOnRelease: true,
4295
- releasePortOnRelease: false
4296
- }).then((release) => {
4297
- releaseChild = release;
4298
- }).catch((error) => {
4299
- log.debug(`[AntigravityCliService] Process registration failed: ${error instanceof Error ? error.message : String(error)}`);
4300
- });
4301
- const finalize = async (callback) => {
4302
- try {
4303
- await registrationPromise;
4304
- await releaseChild?.();
4305
- } catch (error) {
4306
- log.debug(`[AntigravityCliService] Process cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
4307
- } finally {
4308
- callback();
4309
- }
4310
- };
4311
- const timer = setTimeout(() => {
4312
- if (settled) return;
4313
- settled = true;
4314
- child.kill("SIGTERM");
4315
- finalize(() => reject(/* @__PURE__ */ new Error(`Antigravity CLI timed out after ${timeout}ms.`)));
4316
- }, timeout);
4317
- child.stdout.on("data", (chunk) => {
4318
- totalBytes += chunk.length;
4319
- if (totalBytes > MAX_BUFFER) {
4320
- if (!settled) {
4321
- settled = true;
4322
- child.kill("SIGTERM");
4323
- finalize(() => reject(/* @__PURE__ */ new Error("Antigravity CLI output exceeded maximum buffer size.")));
4324
- }
4325
- return;
4326
- }
4327
- chunks.push(chunk);
4328
- });
4329
- child.stderr.on("data", (chunk) => {
4330
- stderrContent += chunk.toString();
4331
- });
4332
- child.on("error", (error) => {
4333
- clearTimeout(timer);
4334
- if (settled) return;
4335
- settled = true;
4336
- const message = error.code === "ENOENT" ? ANTIGRAVITY_UNAVAILABLE_MESSAGE : `Failed to invoke Antigravity CLI: ${error.message}`;
4337
- finalize(() => reject(new Error(message)));
4338
- });
4339
- child.on("close", (code) => {
4340
- clearTimeout(timer);
4341
- if (settled) return;
4342
- settled = true;
4343
- const stdoutContent = Buffer.concat(chunks).toString("utf-8");
4344
- if (code !== 0) {
4345
- const detail = tailSnippet(stderrContent) || tailSnippet(stdoutContent) || `exit code ${code}`;
4346
- finalize(() => reject(new Error(detail)));
4347
- return;
4348
- }
4349
- finalize(() => resolve(stdoutContent));
4350
- });
4351
- });
4352
- }
4353
- };
4354
- //#endregion
4355
4394
  //#region src/tools/GetComponentVisualTool.ts
4356
4395
  var _GetComponentVisualTool;
4357
4396
  /**
@@ -4480,11 +4519,13 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
4480
4519
  static {
4481
4520
  _ListSharedComponentsTool = this;
4482
4521
  }
4522
+ configLoader;
4483
4523
  static TOOL_NAME = "list_shared_components";
4484
4524
  static PAGE_SIZE = 50;
4485
4525
  static COMPONENT_REUSE_INSTRUCTION = "IMPORTANT: Before creating new components, check if a similar component already exists in the list above. Always reuse existing shared components to maintain consistency and reduce code duplication.";
4486
4526
  storiesIndexFactory;
4487
- constructor(storiesIndexFactory) {
4527
+ constructor(storiesIndexFactory, configLoader) {
4528
+ this.configLoader = configLoader;
4488
4529
  this.storiesIndexFactory = storiesIndexFactory;
4489
4530
  }
4490
4531
  /**
@@ -4543,7 +4584,7 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
4543
4584
  const { cursor, tags: inputTags } = input;
4544
4585
  const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
4545
4586
  const storiesIndex = this.storiesIndexFactory();
4546
- const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
4587
+ const [, defaultTags] = await Promise.all([storiesIndex.initialize(), this.configLoader ? this.configLoader.loadRoot().then((root) => root.sharedComponentTags ?? DEFAULT_SHARED_COMPONENT_TAGS) : getSharedComponentTags()]);
4547
4588
  const availableTags = storiesIndex.getAllTags();
4548
4589
  const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
4549
4590
  const allComponentNames = storiesIndex.getComponentsByTags(filterTags.length > 0 ? filterTags : void 0).map((component) => component.title.split("/").pop() || component.title).filter((name, index, self) => self.indexOf(name) === index).sort();
@@ -4581,7 +4622,9 @@ let ListSharedComponentsTool = class ListSharedComponentsTool {
4581
4622
  ListSharedComponentsTool = _ListSharedComponentsTool = __decorate([
4582
4623
  injectable(),
4583
4624
  __decorateParam(0, inject(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
4584
- __decorateMetadata("design:paramtypes", [Object])
4625
+ __decorateParam(1, inject(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader)),
4626
+ __decorateParam(1, optional()),
4627
+ __decorateMetadata("design:paramtypes", [Object, typeof StyleSystemConfigLoader === "undefined" ? Object : StyleSystemConfigLoader])
4585
4628
  ], ListSharedComponentsTool);
4586
4629
  //#endregion
4587
4630
  //#region src/tools/ListAppComponentsTool.ts
@@ -4654,309 +4697,31 @@ ListAppComponentsTool = _ListAppComponentsTool = __decorate([
4654
4697
  __decorateMetadata("design:paramtypes", [typeof AppComponentsService === "undefined" ? Object : AppComponentsService])
4655
4698
  ], ListAppComponentsTool);
4656
4699
  //#endregion
4657
- //#region src/tools/PolishComponentTool.ts
4658
- /**
4659
- * PolishComponentTool
4660
- *
4661
- * DESIGN PATTERNS:
4662
- * - Tool pattern with getDefinition() and execute() methods
4663
- * - Service delegation for business logic
4664
- * - JSON Schema validation for inputs
4665
- *
4666
- * CODING STANDARDS:
4667
- * - Implement Tool interface from ../types
4668
- * - Use TOOL_NAME constant with snake_case
4669
- * - Return CallToolResult with content array
4670
- * - Handle errors with isError flag
4671
- * - Delegate complex logic to services
4672
- *
4673
- * AVOID:
4674
- * - Complex business logic in execute method
4675
- * - Unhandled promise rejections
4676
- * - Missing input validation
4677
- */
4678
- var _PolishComponentTool;
4679
- let PolishComponentTool = class PolishComponentTool {
4680
- static {
4681
- _PolishComponentTool = this;
4682
- }
4683
- static TOOL_NAME = "polish_component";
4684
- antigravityService;
4685
- getUiComponentService;
4686
- cssClassesServiceFactory;
4687
- storiesIndexFactory;
4688
- constructor(getUiComponentService, cssClassesServiceFactory, storiesIndexFactory) {
4689
- this.antigravityService = new AntigravityCliService();
4690
- this.getUiComponentService = getUiComponentService;
4691
- this.cssClassesServiceFactory = cssClassesServiceFactory;
4692
- this.storiesIndexFactory = storiesIndexFactory;
4693
- }
4694
- getDefinition() {
4695
- return {
4696
- name: _PolishComponentTool.TOOL_NAME,
4697
- description: "Polish a UI component by running Antigravity to directly edit the component file. Takes a .tsx file path, finds the collocated stories file for visual context, gathers design principles, design tokens, and shared components, then runs Antigravity to edit in place.",
4698
- inputSchema: {
4699
- type: "object",
4700
- properties: {
4701
- filePath: {
4702
- type: "string",
4703
- minLength: 1,
4704
- description: "Path to the component .tsx file to polish (e.g., \"packages/frontend/web-atoms/src/components/Button/index.tsx\")"
4705
- },
4706
- instructions: {
4707
- type: "string",
4708
- description: "Specific polishing instructions (e.g., \"improve spacing\", \"add hover states\", \"fix dark mode\")"
4709
- }
4710
- },
4711
- required: ["filePath"],
4712
- additionalProperties: false
4713
- }
4714
- };
4715
- }
4716
- async execute(input) {
4717
- try {
4718
- if (!this.antigravityService.isAvailable()) return {
4719
- content: [{
4720
- type: "text",
4721
- text: "Error: Antigravity CLI is not installed. Run `agy install` to configure it."
4722
- }],
4723
- isError: true
4724
- };
4725
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
4726
- const { instructions } = input;
4727
- const filePath = path.isAbsolute(input.filePath) ? input.filePath : path.join(workspaceRoot, input.filePath);
4728
- const componentDir = path.dirname(filePath);
4729
- const storyFile = await this.findStoriesFile(componentDir);
4730
- if (!storyFile) return {
4731
- content: [{
4732
- type: "text",
4733
- text: `Error: No .stories.tsx file found in ${componentDir}`
4734
- }],
4735
- isError: true
4736
- };
4737
- const componentName = path.basename(componentDir);
4738
- const appPath = await this.findAppPath(filePath, workspaceRoot);
4739
- const dsConfig = appPath ? await getAppDesignSystemConfig(appPath).catch((error) => {
4740
- log.debug(`[PolishComponentTool] Could not load design system config for ${appPath}: ${error instanceof Error ? error.message : String(error)}`);
4741
- return null;
4742
- }) : null;
4743
- const platform = this.detectPlatform(dsConfig);
4744
- log.info(`[PolishComponentTool] Polishing ${filePath}, stories: ${storyFile}, appPath: ${appPath}, platform: ${platform}`);
4745
- const [componentVisual, designPrinciples, sharedComponents, componentSource, designTokens] = await Promise.allSettled([
4746
- appPath ? this.getComponentVisual(componentName, appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
4747
- appPath ? this.getDesignPrinciples(appPath) : Promise.reject(/* @__PURE__ */ new Error("No appPath")),
4748
- this.getSharedComponents(),
4749
- promises.readFile(filePath, "utf-8"),
4750
- this.getDesignTokens(platform, dsConfig, appPath)
4751
- ]);
4752
- const contextSections = [];
4753
- if (componentSource.status === "fulfilled") contextSections.push(`## Component Source\n\nFile: ${filePath}\n\n\`\`\`tsx\n${componentSource.value}\n\`\`\``);
4754
- const storyContent = await promises.readFile(storyFile, "utf-8").catch((error) => {
4755
- log.debug(`[PolishComponentTool] Could not read stories file ${storyFile}: ${error instanceof Error ? error.message : String(error)}`);
4756
- return null;
4757
- });
4758
- if (storyContent) contextSections.push(`## Stories File\n\nFile: ${storyFile}\n\n\`\`\`tsx\n${storyContent}\n\`\`\``);
4759
- if (designPrinciples.status === "fulfilled" && designPrinciples.value.length > 0) {
4760
- const principlesText = designPrinciples.value.map((p) => `### ${p.variant}\n${yaml.dump(p.content, {
4761
- indent: 2,
4762
- lineWidth: 120
4763
- })}`).join("\n");
4764
- contextSections.push(`## Design Principles\n\n${principlesText}`);
4765
- }
4766
- if (designTokens.status === "fulfilled") contextSections.push(designTokens.value);
4767
- if (sharedComponents.status === "fulfilled") contextSections.push(`## Available Shared Components\n\nReuse these existing components where possible:\n${sharedComponents.value}`);
4768
- const systemPrompt = this.buildSystemPrompt(platform);
4769
- const userPrompt = [
4770
- `# Polish Component`,
4771
- "",
4772
- instructions ? `## Instructions\n\n${instructions}\n` : "",
4773
- ...contextSections,
4774
- "",
4775
- `Edit the component file at: ${filePath}`,
4776
- "Polish this component following the design system. Edit the files directly."
4777
- ].filter(Boolean).join("\n");
4778
- const screenshotPaths = componentVisual.status === "fulfilled" ? [componentVisual.value.imagePath] : [];
4779
- log.info(`[PolishComponentTool] Running Antigravity to edit component...`);
4780
- const response = await this.antigravityService.edit({
4781
- prompt: userPrompt,
4782
- systemPrompt,
4783
- screenshotPaths
4784
- });
4785
- log.info(`[PolishComponentTool] Antigravity finished editing (model: ${response.model})`);
4786
- const reviewPrompt = [
4787
- "\n## Review the changes",
4788
- "",
4789
- "Antigravity has edited the component files directly. Review the result with `pnpm exec style-system get-ui-component`:",
4790
- ` - componentName: "${componentName}"`,
4791
- appPath ? ` - appPath: "${appPath}"` : "",
4792
- "",
4793
- `Component file: ${filePath}`,
4794
- `Stories file: ${storyFile}`
4795
- ].filter(Boolean).join("\n");
4796
- return { content: [{
4797
- type: "text",
4798
- text: response.content
4799
- }, {
4800
- type: "text",
4801
- text: reviewPrompt
4802
- }] };
4803
- } catch (error) {
4804
- return {
4805
- content: [{
4806
- type: "text",
4807
- text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`
4808
- }],
4809
- isError: true
4810
- };
4811
- }
4812
- }
4813
- detectPlatform(config) {
4814
- if (config?.bundler?.includes("native")) return "native";
4815
- return "web";
4816
- }
4817
- buildSystemPrompt(platform) {
4818
- const base = [
4819
- "You are a senior frontend designer and developer.",
4820
- "You polish UI components to improve their visual quality, consistency, and adherence to the design system.",
4821
- "You receive the component source code, a screenshot of its current rendering, the design principles, available design tokens, and shared components.",
4822
- "Your job is to directly edit the component files to improve them.",
4823
- "",
4824
- "CRITICAL RULES:"
4825
- ];
4826
- if (platform === "native") base.push("- This is a React Native component using react-native-unistyles v3 for styling.", "- ONLY use theme tokens from the UnistylesTheme interface listed in the \"Unistyles Theme Tokens\" section.", "- Access tokens via StyleSheet.create((theme) => ({...})) — e.g., theme.colors.primary, theme.spacing.md, theme.borderRadius.lg, theme.fontSize.base.", "- NEVER use hardcoded color hex values, pixel numbers, or inline styles when a theme token exists.", "- If a semantic value is not available in the theme tokens, use the closest match or leave it unchanged.");
4827
- else base.push("- ONLY use CSS classes listed in the \"Available CSS Classes\" section. NEVER use raw Tailwind color/spacing classes (e.g., green-500, red-600, p-4) that are not in the provided design tokens.", "- If a semantic color or spacing value is not available in the design tokens, use the closest match from the provided list or leave it unchanged. Do NOT invent new color values.");
4828
- base.push("- Reuse shared components instead of creating custom markup", "- Follow the design principles strictly", "- Edit the files directly — do not just suggest changes", "- Explain what you changed after editing");
4829
- if (platform === "native") base.push("", "VIOLATION EXAMPLES (DO NOT DO THIS):", "- { color: \"#22c55e\" } → hardcoded hex, use theme.colors.success instead", "- { padding: 16 } → hardcoded pixel, use theme.spacing.md instead", "- { borderRadius: 8 } → hardcoded pixel, use theme.borderRadius.lg instead");
4830
- else base.push("", "VIOLATION EXAMPLES (DO NOT DO THIS):", "- bg-green-500/10 text-green-700 → raw colors, not from design tokens", "- border-red-600/30 → raw color, not from design tokens", "- Instead use semantic classes like bg-destructive, text-destructive-foreground, bg-accent, etc.");
4831
- return base.join("\n");
4832
- }
4833
- /**
4834
- * Read design tokens from config.themePath.
4835
- * For web: extracts Tailwind CSS classes. For native: reads the TypeScript types file.
4836
- */
4837
- async getDesignTokens(platform, config, appPath) {
4838
- if (!config?.themePath) return `No themePath configured in style-system.config.yaml.`;
4839
- const themePath = await resolveStyleSystemThemePath({
4840
- appPath,
4841
- themePath: config.themePath
4842
- });
4843
- if (platform === "native") return `## Unistyles Theme Tokens\n\nUse these tokens via \`StyleSheet.create((theme) => ({...}))\`:\n\n\`\`\`typescript\n${await promises.readFile(themePath, "utf-8")}\n\`\`\``;
4844
- const toolkitConfig = await getGetCssClassesConfig();
4845
- const result = await (await this.cssClassesServiceFactory.createService({ customServicePath: toolkitConfig?.customService })).extractClasses("all", themePath);
4846
- return `## Available CSS Classes (Design Tokens)\n\n${JSON.stringify(result, null, 2)}`;
4847
- }
4848
- async findStoriesFile(dir) {
4849
- for (const pattern of [
4850
- "*.stories.tsx",
4851
- "*.stories.ts",
4852
- "*.stories.jsx",
4853
- "*.stories.js"
4854
- ]) {
4855
- const matches = await glob(pattern, {
4856
- cwd: dir,
4857
- absolute: true
4858
- });
4859
- if (matches.length > 0) return matches[0];
4860
- }
4861
- return null;
4862
- }
4863
- /**
4864
- * Find the app that owns a file, for style-system context.
4865
- * Only apps declaring their own style-system.config.yaml are eligible.
4866
- */
4867
- async findAppPath(filePath, workspaceRoot) {
4868
- const parts = path.relative(workspaceRoot, filePath).split(path.sep);
4869
- if (parts[0] === "apps" && parts.length > 1) {
4870
- const appPath = path.join(parts[0], parts[1]);
4871
- if (await hasStyleSystemProjectConfig(appPath)) return appPath;
4872
- log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
4873
- }
4874
- const appsDir = path.join(workspaceRoot, "apps");
4875
- let candidates;
4876
- try {
4877
- candidates = (await promises.readdir(appsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory() && entry.name.endsWith("-app")).map((entry) => entry.name).sort();
4878
- } catch (error) {
4879
- log.debug(`[PolishComponentTool] Could not scan apps dir: ${error instanceof Error ? error.message : String(error)}`);
4880
- return null;
4881
- }
4882
- for (const name of candidates) {
4883
- const appPath = `apps/${name}`;
4884
- if (await hasStyleSystemProjectConfig(appPath)) return appPath;
4885
- log.debug(`[PolishComponentTool] ${appPath} has no style-system config`);
4886
- }
4887
- return null;
4888
- }
4889
- async getComponentVisual(componentName, appPath) {
4890
- return { imagePath: (await this.getUiComponentService.getComponent({
4891
- componentName,
4892
- appPath,
4893
- darkMode: true
4894
- })).imagePath };
4895
- }
4896
- async getDesignPrinciples(appPath) {
4897
- const workspaceRoot = TemplatesManagerService.getWorkspaceRootSync();
4898
- const product = path.basename(appPath).replace(/-app$|-native-app$|-mcp-app$|-e2e$/, "");
4899
- const principlesDir = path.join(workspaceRoot, "docs", "architecture", product, "design-principles");
4900
- const results = [];
4901
- try {
4902
- const files = await promises.readdir(principlesDir);
4903
- for (const file of files) {
4904
- if (!file.endsWith(".yaml")) continue;
4905
- try {
4906
- const content = await promises.readFile(path.join(principlesDir, file), "utf-8");
4907
- const parsed = yaml.load(content);
4908
- const variant = file.replace(".yaml", "");
4909
- results.push({
4910
- variant,
4911
- content: parsed
4912
- });
4913
- } catch (error) {
4914
- log.debug(`[PolishComponentTool] Skipping unreadable design principle ${file}: ${error instanceof Error ? error.message : String(error)}`);
4915
- }
4916
- }
4917
- } catch (error) {
4918
- log.debug(`[PolishComponentTool] No design principles at ${principlesDir}: ${error instanceof Error ? error.message : String(error)}`);
4919
- }
4920
- return results;
4921
- }
4922
- async getSharedComponents() {
4923
- const storiesIndex = this.storiesIndexFactory();
4924
- const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
4925
- return storiesIndex.getComponentsByTags(defaultTags.length > 0 ? defaultTags : void 0).map((c) => c.title.split("/").pop() || c.title).filter((name, index, self) => self.indexOf(name) === index).sort().map((n) => `- ${n}`).join("\n");
4926
- }
4927
- };
4928
- PolishComponentTool = _PolishComponentTool = __decorate([
4929
- injectable(),
4930
- __decorateParam(0, inject(STYLE_SYSTEM_TYPES.GetUiComponentService)),
4931
- __decorateParam(1, inject(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory)),
4932
- __decorateParam(2, inject(STYLE_SYSTEM_TYPES.StoriesIndexFactory)),
4933
- __decorateMetadata("design:paramtypes", [
4934
- typeof GetUiComponentService === "undefined" ? Object : GetUiComponentService,
4935
- typeof CSSClassesServiceFactory === "undefined" ? Object : CSSClassesServiceFactory,
4936
- Object
4937
- ])
4938
- ], PolishComponentTool);
4939
- //#endregion
4940
4700
  //#region src/container/module.ts
4941
4701
  function createStyleSystemModule(optionsInput = {}) {
4942
4702
  return new ContainerModule((options) => {
4703
+ options.bind(STYLE_SYSTEM_TYPES.WorkspaceRoot).toConstantValue(optionsInput.workspaceRoot);
4943
4704
  options.bind(STYLE_SYSTEM_TYPES.StyleSystemConfigLoader).to(StyleSystemConfigLoader).inSingletonScope();
4944
4705
  options.bind(STYLE_SYSTEM_TYPES.DefaultThemePath).toConstantValue(optionsInput.defaultThemePath);
4945
4706
  options.bind(STYLE_SYSTEM_TYPES.GetUiComponentServiceConfig).toConstantValue(DEFAULT_GET_UI_COMPONENT_CONFIG);
4946
4707
  options.bind(STYLE_SYSTEM_TYPES.AppComponentsServiceConfig).toConstantValue(DEFAULT_APP_COMPONENTS_CONFIG);
4947
4708
  options.bind(STYLE_SYSTEM_TYPES.ThemeServiceFactory).to(ThemeServiceFactory).inSingletonScope();
4948
4709
  options.bind(STYLE_SYSTEM_TYPES.CSSClassesServiceFactory).to(CSSClassesServiceFactory).inSingletonScope();
4949
- options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).to(StoriesIndexService).inTransientScope();
4710
+ options.bind(STYLE_SYSTEM_TYPES.StoriesIndexService).toDynamicValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
4950
4711
  options.bind(STYLE_SYSTEM_TYPES.AppComponentsService).to(AppComponentsService).inTransientScope();
4951
4712
  options.bind(STYLE_SYSTEM_TYPES.GetUiComponentService).to(GetUiComponentService).inSingletonScope();
4952
- options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService());
4953
- options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath));
4713
+ options.bind(STYLE_SYSTEM_TYPES.StoriesIndexFactory).toConstantValue(() => new StoriesIndexService({ workspaceRoot: optionsInput.workspaceRoot }));
4714
+ options.bind(STYLE_SYSTEM_TYPES.RendererFactory).toConstantValue((config, appPath) => new ComponentRendererService(config, appPath, {
4715
+ workspaceRoot: optionsInput.workspaceRoot,
4716
+ temporaryDirectory: optionsInput.temporaryDirectory,
4717
+ sharedBrowser: optionsInput.sharedBrowser,
4718
+ ...optionsInput.bundlerFactory ? { bundlerFactory: () => optionsInput.bundlerFactory(config) } : {}
4719
+ }));
4954
4720
  options.bind(STYLE_SYSTEM_TYPES.ListThemesTool).to(ListThemesTool).inSingletonScope();
4955
4721
  options.bind(STYLE_SYSTEM_TYPES.GetCSSClassesTool).to(GetCSSClassesTool).inSingletonScope();
4956
4722
  options.bind(STYLE_SYSTEM_TYPES.GetComponentVisualTool).to(GetComponentVisualTool).inSingletonScope();
4957
4723
  options.bind(STYLE_SYSTEM_TYPES.ListSharedComponentsTool).to(ListSharedComponentsTool).inSingletonScope();
4958
4724
  options.bind(STYLE_SYSTEM_TYPES.ListAppComponentsTool).to(ListAppComponentsTool).inSingletonScope();
4959
- options.bind(STYLE_SYSTEM_TYPES.PolishComponentTool).to(PolishComponentTool).inSingletonScope();
4960
4725
  });
4961
4726
  }
4962
4727
  //#endregion
@@ -4968,7 +4733,7 @@ function createContainer(options) {
4968
4733
  }
4969
4734
  //#endregion
4970
4735
  //#region package.json
4971
- var version = "0.2.14-alpha.2";
4736
+ var version = "0.2.14-alpha.21";
4972
4737
  //#endregion
4973
4738
  //#region src/metadata.ts
4974
4739
  const STYLE_SYSTEM_CLI_NAME = "style-system";
@@ -5002,7 +4767,6 @@ function createServer(containerOrThemePath) {
5002
4767
  const getComponentVisualTool = container.get(STYLE_SYSTEM_TYPES.GetComponentVisualTool);
5003
4768
  const listSharedComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListSharedComponentsTool);
5004
4769
  const listAppComponentsTool = container.get(STYLE_SYSTEM_TYPES.ListAppComponentsTool);
5005
- const polishComponentTool = container.get(STYLE_SYSTEM_TYPES.PolishComponentTool);
5006
4770
  server.setRequestHandler("tools/list", async () => {
5007
4771
  return { tools: [
5008
4772
  withCapabilities(listThemesTool.getDefinition(), [
@@ -5030,12 +4794,6 @@ function createServer(containerOrThemePath) {
5030
4794
  "components",
5031
4795
  "app-components",
5032
4796
  "component-discovery"
5033
- ]),
5034
- withCapabilities(polishComponentTool.getDefinition(), [
5035
- "components",
5036
- "design-review",
5037
- "polish",
5038
- "antigravity"
5039
4797
  ])
5040
4798
  ].map((tool) => {
5041
4799
  if (tool.inputSchema.type !== "object") throw new Error(`Invalid schema for ${tool.name}`);
@@ -5052,8 +4810,7 @@ function createServer(containerOrThemePath) {
5052
4810
  getCSSClassesTool,
5053
4811
  getComponentVisualTool,
5054
4812
  listSharedComponentsTool,
5055
- listAppComponentsTool,
5056
- polishComponentTool
4813
+ listAppComponentsTool
5057
4814
  ].find((candidate) => candidate.getDefinition().name === name);
5058
4815
  if (!tool) return {
5059
4816
  content: [{
@@ -5240,4 +4997,4 @@ var StdioTransportHandler = class {
5240
4997
  }
5241
4998
  };
5242
4999
  //#endregion
5243
- export { STYLE_SYSTEM_TYPES as A, getAppDesignSystemConfig as C, resolveAppDesignSystemConfig as D, resetStyleSystemConfigCache as E, BaseCSSClassesService as F, TemplatesManagerService as I, isContained as L, CSSClassesServiceFactory as M, DEFAULT_STYLE_SYSTEM_CONFIG as N, StyleSystemConfigLoader as O, TailwindCSSClassesService as P, log as R, BaseBundlerService as S, hasStyleSystemProjectConfig as T, ThemeService as _, STYLE_SYSTEM_CLI_NAME as a, getBundlerServiceFromConfig as b, createStyleSystemModule as c, ListSharedComponentsTool as d, GetComponentVisualTool as f, ComponentRendererService as g, GetUiComponentService as h, createServer as i, StoriesIndexService as j, resolveStyleSystemThemePath as k, PolishComponentTool as l, ListThemesTool as m, extractCustomTokenNames as n, STYLE_SYSTEM_VERSION as o, GetCSSClassesTool as p, generateRestrictedClassesConfig as r, createContainer as s, StdioTransportHandler as t, ListAppComponentsTool as u, createDefaultBundlerService as v, getSharedComponentTags as w, ViteReactBundlerService as x, getBundlerService as y };
5000
+ export { resolveStyleSystemThemePath as A, STYLE_SYSTEM_TYPES as B, launchBrowserWithFallback as C, resetStyleSystemConfigCache as D, hasStyleSystemProjectConfig as E, TailwindCSSClassesService as F, BaseCSSClassesService as I, TemplatesManagerService as L, StoriesIndexService as M, CSSClassesServiceFactory as N, resolveAppDesignSystemConfig as O, DEFAULT_STYLE_SYSTEM_CONFIG as P, isContained as R, BaseBundlerService as S, getSharedComponentTags as T, createDefaultBundlerService as _, STYLE_SYSTEM_CLI_NAME as a, ViteReactNativeBundlerService as b, createStyleSystemModule as c, GetComponentVisualTool as d, GetCSSClassesTool as f, ThemeService as g, ComponentRendererService as h, createServer as i, MAX_STYLE_SYSTEM_VIEWPORT_DIMENSION as j, StyleSystemConfigLoader as k, ListAppComponentsTool as l, GetUiComponentService as m, extractCustomTokenNames as n, STYLE_SYSTEM_VERSION as o, ListThemesTool as p, generateRestrictedClassesConfig as r, createContainer as s, StdioTransportHandler as t, ListSharedComponentsTool as u, getBundlerService as v, getAppDesignSystemConfig as w, ViteReactBundlerService as x, getBundlerServiceFromConfig as y, log as z };