@agiflowai/style-system 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +1 -1
- package/dist/{stdio-BlNvX94v.cjs → stdio-D3tBpCoD.cjs} +76 -72
- package/dist/{stdio-CGaoEmM8.js → stdio-DvPtx0WS.js} +76 -72
- package/package.json +3 -3
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_stdio = require('./stdio-
|
|
2
|
+
const require_stdio = require('./stdio-D3tBpCoD.cjs');
|
|
3
3
|
let __agiflowai_aicode_utils = require("@agiflowai/aicode-utils");
|
|
4
4
|
__agiflowai_aicode_utils = require_stdio.__toESM(__agiflowai_aicode_utils);
|
|
5
5
|
let commander = require("commander");
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as ListAppComponentsTool, i as ListThemesTool, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, r as ListSharedComponentsTool, t as StdioTransportHandler, u as getBundlerServiceFromConfig } from "./stdio-
|
|
2
|
+
import { a as ListAppComponentsTool, i as ListThemesTool, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, r as ListSharedComponentsTool, t as StdioTransportHandler, u as getBundlerServiceFromConfig } from "./stdio-DvPtx0WS.js";
|
|
3
3
|
import { print } from "@agiflowai/aicode-utils";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
package/dist/index.cjs
CHANGED
package/dist/index.d.cts
CHANGED
|
@@ -641,19 +641,25 @@ declare class ComponentRendererService {
|
|
|
641
641
|
*/
|
|
642
642
|
renderComponent(componentInfo: ComponentInfo, options?: RenderOptions): Promise<RenderResult>;
|
|
643
643
|
/**
|
|
644
|
-
*
|
|
644
|
+
* Default maximum number of screenshot files to keep in temp directory.
|
|
645
645
|
* Prevents disk space issues on long-running servers.
|
|
646
646
|
*/
|
|
647
|
-
private static readonly
|
|
647
|
+
private static readonly DEFAULT_MAX_TEMP_FILES;
|
|
648
|
+
/**
|
|
649
|
+
* Number of recent files to keep when cleaning up on dispose.
|
|
650
|
+
*/
|
|
651
|
+
private static readonly DISPOSE_KEEP_COUNT;
|
|
648
652
|
/**
|
|
649
653
|
* Clean up old rendered files.
|
|
650
654
|
* Removes files older than the specified duration and enforces a max file count.
|
|
651
655
|
* @param olderThanMs - Remove files older than this duration (default: 1 hour)
|
|
656
|
+
* @param keepCount - Maximum number of recent files to keep (default: 100)
|
|
652
657
|
*/
|
|
653
|
-
cleanup(olderThanMs?: number): Promise<void>;
|
|
658
|
+
cleanup(olderThanMs?: number, keepCount?: number): Promise<void>;
|
|
654
659
|
/**
|
|
655
|
-
* Cleanup bundler server and temp files.
|
|
660
|
+
* Cleanup bundler server resources and old temp files.
|
|
656
661
|
* Called on service shutdown.
|
|
662
|
+
* Keeps the most recent files for caching purposes.
|
|
657
663
|
*/
|
|
658
664
|
dispose(): Promise<void>;
|
|
659
665
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -641,19 +641,25 @@ declare class ComponentRendererService {
|
|
|
641
641
|
*/
|
|
642
642
|
renderComponent(componentInfo: ComponentInfo, options?: RenderOptions): Promise<RenderResult>;
|
|
643
643
|
/**
|
|
644
|
-
*
|
|
644
|
+
* Default maximum number of screenshot files to keep in temp directory.
|
|
645
645
|
* Prevents disk space issues on long-running servers.
|
|
646
646
|
*/
|
|
647
|
-
private static readonly
|
|
647
|
+
private static readonly DEFAULT_MAX_TEMP_FILES;
|
|
648
|
+
/**
|
|
649
|
+
* Number of recent files to keep when cleaning up on dispose.
|
|
650
|
+
*/
|
|
651
|
+
private static readonly DISPOSE_KEEP_COUNT;
|
|
648
652
|
/**
|
|
649
653
|
* Clean up old rendered files.
|
|
650
654
|
* Removes files older than the specified duration and enforces a max file count.
|
|
651
655
|
* @param olderThanMs - Remove files older than this duration (default: 1 hour)
|
|
656
|
+
* @param keepCount - Maximum number of recent files to keep (default: 100)
|
|
652
657
|
*/
|
|
653
|
-
cleanup(olderThanMs?: number): Promise<void>;
|
|
658
|
+
cleanup(olderThanMs?: number, keepCount?: number): Promise<void>;
|
|
654
659
|
/**
|
|
655
|
-
* Cleanup bundler server and temp files.
|
|
660
|
+
* Cleanup bundler server resources and old temp files.
|
|
656
661
|
* Called on service shutdown.
|
|
662
|
+
* Keeps the most recent files for caching purposes.
|
|
657
663
|
*/
|
|
658
664
|
dispose(): Promise<void>;
|
|
659
665
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as TailwindCSSClassesService, a as ListAppComponentsTool, c as ThemeService, d as ViteReactBundlerService, f as BaseBundlerService, g as DEFAULT_STYLE_SYSTEM_CONFIG, h as CSSClassesServiceFactory, i as ListThemesTool, l as createDefaultBundlerService, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, p as StoriesIndexService, r as ListSharedComponentsTool, s as ComponentRendererService, t as StdioTransportHandler, u as getBundlerServiceFromConfig, v as BaseCSSClassesService } from "./stdio-
|
|
1
|
+
import { _ as TailwindCSSClassesService, a as ListAppComponentsTool, c as ThemeService, d as ViteReactBundlerService, f as BaseBundlerService, g as DEFAULT_STYLE_SYSTEM_CONFIG, h as CSSClassesServiceFactory, i as ListThemesTool, l as createDefaultBundlerService, m as GetCSSClassesTool, n as createServer, o as GetComponentVisualTool, p as StoriesIndexService, r as ListSharedComponentsTool, s as ComponentRendererService, t as StdioTransportHandler, u as getBundlerServiceFromConfig, v as BaseCSSClassesService } from "./stdio-DvPtx0WS.js";
|
|
2
2
|
|
|
3
3
|
export { BaseBundlerService, BaseCSSClassesService, CSSClassesServiceFactory, ComponentRendererService, DEFAULT_STYLE_SYSTEM_CONFIG, GetCSSClassesTool, GetComponentVisualTool, ListAppComponentsTool, ListSharedComponentsTool, ListThemesTool, StdioTransportHandler, StoriesIndexService, TailwindCSSClassesService, ThemeService, ViteReactBundlerService, createDefaultBundlerService, createServer, getBundlerServiceFromConfig };
|
|
@@ -733,11 +733,11 @@ var StoriesIndexService = class {
|
|
|
733
733
|
__agiflowai_aicode_utils.log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
|
|
734
734
|
const failures = [];
|
|
735
735
|
let successCount = 0;
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
const errorMessage =
|
|
736
|
+
const results = await Promise.allSettled(storyFiles.map((filePath) => this.indexStoryFile(filePath)));
|
|
737
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") successCount++;
|
|
738
|
+
else {
|
|
739
|
+
const filePath = storyFiles[index];
|
|
740
|
+
const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
741
741
|
__agiflowai_aicode_utils.log.error(`[StoriesIndexService] Error indexing ${filePath}: ${errorMessage}`);
|
|
742
742
|
failures.push({
|
|
743
743
|
filePath,
|
|
@@ -943,12 +943,14 @@ var AppComponentsService = class {
|
|
|
943
943
|
} catch {
|
|
944
944
|
throw new Error(`App path does not exist: ${resolvedAppPath}`);
|
|
945
945
|
}
|
|
946
|
-
const appName = await this.getAppName(resolvedAppPath);
|
|
947
|
-
const workspaceDependencies = await this.getWorkspaceDependencies(resolvedAppPath);
|
|
948
|
-
__agiflowai_aicode_utils.log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
|
|
949
|
-
const packageMap = await this.buildPackageMap(monorepoRoot);
|
|
950
946
|
const storiesIndex = new StoriesIndexService();
|
|
951
|
-
await
|
|
947
|
+
const [appName, workspaceDependencies, packageMap] = await Promise.all([
|
|
948
|
+
this.getAppName(resolvedAppPath),
|
|
949
|
+
this.getWorkspaceDependencies(resolvedAppPath),
|
|
950
|
+
this.buildPackageMap(monorepoRoot),
|
|
951
|
+
storiesIndex.initialize()
|
|
952
|
+
]);
|
|
953
|
+
__agiflowai_aicode_utils.log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
|
|
952
954
|
const allComponents = storiesIndex.getAllComponents();
|
|
953
955
|
const { appComponentsArray, packageComponents, totalPackageComponents } = this.categorizeComponents(allComponents, resolvedAppPath, workspaceDependencies, packageMap);
|
|
954
956
|
const totalComponents = appComponentsArray.length + totalPackageComponents;
|
|
@@ -1026,16 +1028,17 @@ var AppComponentsService = class {
|
|
|
1026
1028
|
} catch (error) {
|
|
1027
1029
|
throw new Error(`Failed to scan for package.json files in ${monorepoRoot}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1028
1030
|
}
|
|
1029
|
-
|
|
1031
|
+
const results = await Promise.allSettled(packageJsonFiles.map(async (pkgJsonPath) => {
|
|
1030
1032
|
const content = await node_fs.promises.readFile(pkgJsonPath, "utf-8");
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1033
|
+
return {
|
|
1034
|
+
pkgJsonPath,
|
|
1035
|
+
name: JSON.parse(content).name
|
|
1036
|
+
};
|
|
1037
|
+
}));
|
|
1038
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled" && result.value.name) {
|
|
1039
|
+
const pkgDir = node_path.default.dirname(result.value.pkgJsonPath);
|
|
1040
|
+
packageMap.set(result.value.name, pkgDir);
|
|
1041
|
+
} else if (result.status === "rejected") __agiflowai_aicode_utils.log.debug(`[AppComponentsService] Skipping invalid package.json at ${packageJsonFiles[index]}:`, result.reason);
|
|
1039
1042
|
return packageMap;
|
|
1040
1043
|
}
|
|
1041
1044
|
/**
|
|
@@ -2040,13 +2043,12 @@ var CSSThemeService = class extends BaseThemeService {
|
|
|
2040
2043
|
const cssFilePaths = this.resolveCSSFilePaths();
|
|
2041
2044
|
if (cssFilePaths.length === 0) throw new Error("No theme CSS files configured. Set themePath or cssFiles in project.json style-system config.");
|
|
2042
2045
|
const themes = [];
|
|
2043
|
-
|
|
2046
|
+
const results = await Promise.allSettled(cssFilePaths.map(async (cssFilePath) => {
|
|
2044
2047
|
await this.validatePath(cssFilePath);
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
}
|
|
2048
|
+
return this.extractThemesFromCSS(cssFilePath);
|
|
2049
|
+
}));
|
|
2050
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") themes.push(...result.value);
|
|
2051
|
+
else __agiflowai_aicode_utils.log.warn(`[CSSThemeService] Could not process ${cssFilePaths[index]}:`, result.reason);
|
|
2050
2052
|
const uniqueThemes = this.deduplicateThemes(themes);
|
|
2051
2053
|
return {
|
|
2052
2054
|
themes: uniqueThemes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -2209,13 +2211,14 @@ export default WrappedComponent;
|
|
|
2209
2211
|
*/
|
|
2210
2212
|
async getInlineStyles(darkMode = false) {
|
|
2211
2213
|
const cssFiles = await this.getThemeCSS();
|
|
2212
|
-
let styles =
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2214
|
+
let styles = (await Promise.all(cssFiles.map(async (cssFile) => {
|
|
2215
|
+
try {
|
|
2216
|
+
return await node_fs.promises.readFile(cssFile, "utf-8");
|
|
2217
|
+
} catch (error) {
|
|
2218
|
+
__agiflowai_aicode_utils.log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
|
|
2219
|
+
return "";
|
|
2220
|
+
}
|
|
2221
|
+
}))).filter(Boolean).join("\n");
|
|
2219
2222
|
if (darkMode && styles) styles = `.dark { ${styles} }`;
|
|
2220
2223
|
return styles;
|
|
2221
2224
|
}
|
|
@@ -2261,19 +2264,21 @@ export default WrappedComponent;
|
|
|
2261
2264
|
const themeFiles = (await node_fs.promises.readdir(configsPath)).filter((file) => file.endsWith(".json"));
|
|
2262
2265
|
const themes = [];
|
|
2263
2266
|
let activeBrand;
|
|
2264
|
-
|
|
2267
|
+
const results = await Promise.allSettled(themeFiles.map(async (file) => {
|
|
2265
2268
|
const filePath = node_path.default.join(configsPath, file);
|
|
2266
2269
|
const content = await node_fs.promises.readFile(filePath, "utf-8");
|
|
2267
2270
|
const themeData = JSON.parse(content);
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
name: themeName,
|
|
2271
|
+
return {
|
|
2272
|
+
name: file.replace(".json", ""),
|
|
2271
2273
|
fileName: file,
|
|
2272
2274
|
path: filePath,
|
|
2273
2275
|
colors: themeData.colors || themeData
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2276
|
+
};
|
|
2277
|
+
}));
|
|
2278
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") {
|
|
2279
|
+
themes.push(result.value);
|
|
2280
|
+
if (result.value.name === "lightTheme" || result.value.name === "agimonTheme") activeBrand = result.value.name;
|
|
2281
|
+
} else __agiflowai_aicode_utils.log.warn(`[ThemeService] Failed to process theme file ${themeFiles[index]}:`, result.reason);
|
|
2277
2282
|
return {
|
|
2278
2283
|
themes: themes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
2279
2284
|
activeBrand
|
|
@@ -2529,44 +2534,44 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
2529
2534
|
}
|
|
2530
2535
|
}
|
|
2531
2536
|
/**
|
|
2532
|
-
*
|
|
2537
|
+
* Default maximum number of screenshot files to keep in temp directory.
|
|
2533
2538
|
* Prevents disk space issues on long-running servers.
|
|
2534
2539
|
*/
|
|
2535
|
-
static
|
|
2540
|
+
static DEFAULT_MAX_TEMP_FILES = 100;
|
|
2541
|
+
/**
|
|
2542
|
+
* Number of recent files to keep when cleaning up on dispose.
|
|
2543
|
+
*/
|
|
2544
|
+
static DISPOSE_KEEP_COUNT = 20;
|
|
2536
2545
|
/**
|
|
2537
2546
|
* Clean up old rendered files.
|
|
2538
2547
|
* Removes files older than the specified duration and enforces a max file count.
|
|
2539
2548
|
* @param olderThanMs - Remove files older than this duration (default: 1 hour)
|
|
2549
|
+
* @param keepCount - Maximum number of recent files to keep (default: 100)
|
|
2540
2550
|
*/
|
|
2541
|
-
async cleanup(olderThanMs = 36e5) {
|
|
2551
|
+
async cleanup(olderThanMs = 36e5, keepCount = ComponentRendererService.DEFAULT_MAX_TEMP_FILES) {
|
|
2542
2552
|
try {
|
|
2543
2553
|
const files = await node_fs.promises.readdir(this.tmpDir);
|
|
2544
2554
|
const now = Date.now();
|
|
2545
2555
|
const componentFiles = [];
|
|
2546
|
-
|
|
2556
|
+
const componentFileNames = files.filter((f) => f.startsWith("component-"));
|
|
2557
|
+
const statResults = await Promise.allSettled(componentFileNames.map(async (file) => {
|
|
2547
2558
|
const filePath = node_path.default.join(this.tmpDir, file);
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
let deletedCount =
|
|
2558
|
-
for (const file of componentFiles) if (now - file.mtime > olderThanMs) {
|
|
2559
|
-
await node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
|
|
2560
|
-
deletedCount++;
|
|
2561
|
-
}
|
|
2559
|
+
return {
|
|
2560
|
+
name: file,
|
|
2561
|
+
path: filePath,
|
|
2562
|
+
mtime: (await node_fs.promises.stat(filePath)).mtimeMs
|
|
2563
|
+
};
|
|
2564
|
+
}));
|
|
2565
|
+
for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
|
|
2566
|
+
const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
|
|
2567
|
+
await Promise.all(oldFiles.map((file) => node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
2568
|
+
let deletedCount = oldFiles.length;
|
|
2562
2569
|
const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
|
|
2563
|
-
if (remainingFiles.length >
|
|
2570
|
+
if (remainingFiles.length > keepCount) {
|
|
2564
2571
|
remainingFiles.sort((a, b) => a.mtime - b.mtime);
|
|
2565
|
-
const toDelete = remainingFiles.slice(0, remainingFiles.length -
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
deletedCount++;
|
|
2569
|
-
}
|
|
2572
|
+
const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
|
|
2573
|
+
await Promise.all(toDelete.map((file) => node_fs.promises.unlink(file.path).catch((err) => __agiflowai_aicode_utils.log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
2574
|
+
deletedCount += toDelete.length;
|
|
2570
2575
|
}
|
|
2571
2576
|
if (deletedCount > 0) __agiflowai_aicode_utils.log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
2572
2577
|
} catch (error) {
|
|
@@ -2574,12 +2579,13 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
2574
2579
|
}
|
|
2575
2580
|
}
|
|
2576
2581
|
/**
|
|
2577
|
-
* Cleanup bundler server and temp files.
|
|
2582
|
+
* Cleanup bundler server resources and old temp files.
|
|
2578
2583
|
* Called on service shutdown.
|
|
2584
|
+
* Keeps the most recent files for caching purposes.
|
|
2579
2585
|
*/
|
|
2580
2586
|
async dispose() {
|
|
2581
2587
|
try {
|
|
2582
|
-
await this.cleanup(0);
|
|
2588
|
+
await this.cleanup(0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
2583
2589
|
const bundlerService = this.getBundlerService();
|
|
2584
2590
|
if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
|
|
2585
2591
|
} catch (error) {
|
|
@@ -2656,16 +2662,16 @@ var GetUiComponentService = class {
|
|
|
2656
2662
|
const { componentName, appPath, storyName = this.config.defaultStoryName, darkMode = this.config.defaultDarkMode } = input;
|
|
2657
2663
|
__agiflowai_aicode_utils.log.info(`[GetUiComponentService] Starting for component: ${componentName}, appPath: ${appPath}, storyName: ${storyName}`);
|
|
2658
2664
|
const storiesIndex = this.storiesIndexFactory();
|
|
2665
|
+
let designSystemConfig;
|
|
2659
2666
|
try {
|
|
2660
|
-
await storiesIndex.initialize();
|
|
2667
|
+
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
|
|
2661
2668
|
} catch (error) {
|
|
2662
|
-
throw new Error(`Failed to initialize stories index: ${error instanceof Error ? error.message : String(error)}`);
|
|
2669
|
+
throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2663
2670
|
}
|
|
2664
2671
|
const componentInfo = storiesIndex.findComponentByName(componentName);
|
|
2665
2672
|
if (!componentInfo) throw new Error(`Component "${componentName}" not found in stories index. Ensure the component has a .stories.tsx file and has been indexed.`);
|
|
2666
2673
|
__agiflowai_aicode_utils.log.info(`[GetUiComponentService] Found component: ${componentInfo.title}`);
|
|
2667
2674
|
const validStoryName = this.resolveStoryName(storyName, componentInfo.stories);
|
|
2668
|
-
const designSystemConfig = await getAppDesignSystemConfig(appPath);
|
|
2669
2675
|
__agiflowai_aicode_utils.log.info(`[GetUiComponentService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
2670
2676
|
__agiflowai_aicode_utils.log.info(`[GetUiComponentService] Design system type: ${designSystemConfig.type}`);
|
|
2671
2677
|
const renderer = this.rendererFactory(designSystemConfig, appPath);
|
|
@@ -3029,11 +3035,9 @@ var ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
3029
3035
|
const { cursor, tags: inputTags } = input;
|
|
3030
3036
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
3031
3037
|
const storiesIndex = new StoriesIndexService();
|
|
3032
|
-
await storiesIndex.initialize();
|
|
3038
|
+
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
3033
3039
|
const availableTags = storiesIndex.getAllTags();
|
|
3034
|
-
|
|
3035
|
-
if (inputTags !== void 0) filterTags = inputTags;
|
|
3036
|
-
else filterTags = await getSharedComponentTags();
|
|
3040
|
+
const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
|
|
3037
3041
|
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();
|
|
3038
3042
|
const totalComponents = allComponentNames.length;
|
|
3039
3043
|
const paginatedComponents = allComponentNames.slice(offset, offset + ListSharedComponentsTool.PAGE_SIZE);
|
|
@@ -694,11 +694,11 @@ var StoriesIndexService = class {
|
|
|
694
694
|
log.info(`[StoriesIndexService] Found ${storyFiles.length} story files`);
|
|
695
695
|
const failures = [];
|
|
696
696
|
let successCount = 0;
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const errorMessage =
|
|
697
|
+
const results = await Promise.allSettled(storyFiles.map((filePath) => this.indexStoryFile(filePath)));
|
|
698
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") successCount++;
|
|
699
|
+
else {
|
|
700
|
+
const filePath = storyFiles[index];
|
|
701
|
+
const errorMessage = result.reason instanceof Error ? result.reason.message : String(result.reason);
|
|
702
702
|
log.error(`[StoriesIndexService] Error indexing ${filePath}: ${errorMessage}`);
|
|
703
703
|
failures.push({
|
|
704
704
|
filePath,
|
|
@@ -904,12 +904,14 @@ var AppComponentsService = class {
|
|
|
904
904
|
} catch {
|
|
905
905
|
throw new Error(`App path does not exist: ${resolvedAppPath}`);
|
|
906
906
|
}
|
|
907
|
-
const appName = await this.getAppName(resolvedAppPath);
|
|
908
|
-
const workspaceDependencies = await this.getWorkspaceDependencies(resolvedAppPath);
|
|
909
|
-
log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
|
|
910
|
-
const packageMap = await this.buildPackageMap(monorepoRoot);
|
|
911
907
|
const storiesIndex = new StoriesIndexService();
|
|
912
|
-
await
|
|
908
|
+
const [appName, workspaceDependencies, packageMap] = await Promise.all([
|
|
909
|
+
this.getAppName(resolvedAppPath),
|
|
910
|
+
this.getWorkspaceDependencies(resolvedAppPath),
|
|
911
|
+
this.buildPackageMap(monorepoRoot),
|
|
912
|
+
storiesIndex.initialize()
|
|
913
|
+
]);
|
|
914
|
+
log.info(`[AppComponentsService] Found ${workspaceDependencies.length} workspace dependencies for ${appName}`);
|
|
913
915
|
const allComponents = storiesIndex.getAllComponents();
|
|
914
916
|
const { appComponentsArray, packageComponents, totalPackageComponents } = this.categorizeComponents(allComponents, resolvedAppPath, workspaceDependencies, packageMap);
|
|
915
917
|
const totalComponents = appComponentsArray.length + totalPackageComponents;
|
|
@@ -987,16 +989,17 @@ var AppComponentsService = class {
|
|
|
987
989
|
} catch (error) {
|
|
988
990
|
throw new Error(`Failed to scan for package.json files in ${monorepoRoot}: ${error instanceof Error ? error.message : String(error)}`);
|
|
989
991
|
}
|
|
990
|
-
|
|
992
|
+
const results = await Promise.allSettled(packageJsonFiles.map(async (pkgJsonPath) => {
|
|
991
993
|
const content = await promises.readFile(pkgJsonPath, "utf-8");
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
994
|
+
return {
|
|
995
|
+
pkgJsonPath,
|
|
996
|
+
name: JSON.parse(content).name
|
|
997
|
+
};
|
|
998
|
+
}));
|
|
999
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled" && result.value.name) {
|
|
1000
|
+
const pkgDir = path.dirname(result.value.pkgJsonPath);
|
|
1001
|
+
packageMap.set(result.value.name, pkgDir);
|
|
1002
|
+
} else if (result.status === "rejected") log.debug(`[AppComponentsService] Skipping invalid package.json at ${packageJsonFiles[index]}:`, result.reason);
|
|
1000
1003
|
return packageMap;
|
|
1001
1004
|
}
|
|
1002
1005
|
/**
|
|
@@ -2001,13 +2004,12 @@ var CSSThemeService = class extends BaseThemeService {
|
|
|
2001
2004
|
const cssFilePaths = this.resolveCSSFilePaths();
|
|
2002
2005
|
if (cssFilePaths.length === 0) throw new Error("No theme CSS files configured. Set themePath or cssFiles in project.json style-system config.");
|
|
2003
2006
|
const themes = [];
|
|
2004
|
-
|
|
2007
|
+
const results = await Promise.allSettled(cssFilePaths.map(async (cssFilePath) => {
|
|
2005
2008
|
await this.validatePath(cssFilePath);
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
}
|
|
2009
|
+
return this.extractThemesFromCSS(cssFilePath);
|
|
2010
|
+
}));
|
|
2011
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") themes.push(...result.value);
|
|
2012
|
+
else log.warn(`[CSSThemeService] Could not process ${cssFilePaths[index]}:`, result.reason);
|
|
2011
2013
|
const uniqueThemes = this.deduplicateThemes(themes);
|
|
2012
2014
|
return {
|
|
2013
2015
|
themes: uniqueThemes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
@@ -2170,13 +2172,14 @@ export default WrappedComponent;
|
|
|
2170
2172
|
*/
|
|
2171
2173
|
async getInlineStyles(darkMode = false) {
|
|
2172
2174
|
const cssFiles = await this.getThemeCSS();
|
|
2173
|
-
let styles =
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2175
|
+
let styles = (await Promise.all(cssFiles.map(async (cssFile) => {
|
|
2176
|
+
try {
|
|
2177
|
+
return await promises.readFile(cssFile, "utf-8");
|
|
2178
|
+
} catch (error) {
|
|
2179
|
+
log.warn(`[ThemeService] Could not read CSS file ${cssFile}:`, error);
|
|
2180
|
+
return "";
|
|
2181
|
+
}
|
|
2182
|
+
}))).filter(Boolean).join("\n");
|
|
2180
2183
|
if (darkMode && styles) styles = `.dark { ${styles} }`;
|
|
2181
2184
|
return styles;
|
|
2182
2185
|
}
|
|
@@ -2222,19 +2225,21 @@ export default WrappedComponent;
|
|
|
2222
2225
|
const themeFiles = (await promises.readdir(configsPath)).filter((file) => file.endsWith(".json"));
|
|
2223
2226
|
const themes = [];
|
|
2224
2227
|
let activeBrand;
|
|
2225
|
-
|
|
2228
|
+
const results = await Promise.allSettled(themeFiles.map(async (file) => {
|
|
2226
2229
|
const filePath = path.join(configsPath, file);
|
|
2227
2230
|
const content = await promises.readFile(filePath, "utf-8");
|
|
2228
2231
|
const themeData = JSON.parse(content);
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
name: themeName,
|
|
2232
|
+
return {
|
|
2233
|
+
name: file.replace(".json", ""),
|
|
2232
2234
|
fileName: file,
|
|
2233
2235
|
path: filePath,
|
|
2234
2236
|
colors: themeData.colors || themeData
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2237
|
+
};
|
|
2238
|
+
}));
|
|
2239
|
+
for (const [index, result] of results.entries()) if (result.status === "fulfilled") {
|
|
2240
|
+
themes.push(result.value);
|
|
2241
|
+
if (result.value.name === "lightTheme" || result.value.name === "agimonTheme") activeBrand = result.value.name;
|
|
2242
|
+
} else log.warn(`[ThemeService] Failed to process theme file ${themeFiles[index]}:`, result.reason);
|
|
2238
2243
|
return {
|
|
2239
2244
|
themes: themes.sort((a, b) => a.name.localeCompare(b.name)),
|
|
2240
2245
|
activeBrand
|
|
@@ -2490,44 +2495,44 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
2490
2495
|
}
|
|
2491
2496
|
}
|
|
2492
2497
|
/**
|
|
2493
|
-
*
|
|
2498
|
+
* Default maximum number of screenshot files to keep in temp directory.
|
|
2494
2499
|
* Prevents disk space issues on long-running servers.
|
|
2495
2500
|
*/
|
|
2496
|
-
static
|
|
2501
|
+
static DEFAULT_MAX_TEMP_FILES = 100;
|
|
2502
|
+
/**
|
|
2503
|
+
* Number of recent files to keep when cleaning up on dispose.
|
|
2504
|
+
*/
|
|
2505
|
+
static DISPOSE_KEEP_COUNT = 20;
|
|
2497
2506
|
/**
|
|
2498
2507
|
* Clean up old rendered files.
|
|
2499
2508
|
* Removes files older than the specified duration and enforces a max file count.
|
|
2500
2509
|
* @param olderThanMs - Remove files older than this duration (default: 1 hour)
|
|
2510
|
+
* @param keepCount - Maximum number of recent files to keep (default: 100)
|
|
2501
2511
|
*/
|
|
2502
|
-
async cleanup(olderThanMs = 36e5) {
|
|
2512
|
+
async cleanup(olderThanMs = 36e5, keepCount = ComponentRendererService.DEFAULT_MAX_TEMP_FILES) {
|
|
2503
2513
|
try {
|
|
2504
2514
|
const files = await promises.readdir(this.tmpDir);
|
|
2505
2515
|
const now = Date.now();
|
|
2506
2516
|
const componentFiles = [];
|
|
2507
|
-
|
|
2517
|
+
const componentFileNames = files.filter((f) => f.startsWith("component-"));
|
|
2518
|
+
const statResults = await Promise.allSettled(componentFileNames.map(async (file) => {
|
|
2508
2519
|
const filePath = path.join(this.tmpDir, file);
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
let deletedCount =
|
|
2519
|
-
for (const file of componentFiles) if (now - file.mtime > olderThanMs) {
|
|
2520
|
-
await promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err));
|
|
2521
|
-
deletedCount++;
|
|
2522
|
-
}
|
|
2520
|
+
return {
|
|
2521
|
+
name: file,
|
|
2522
|
+
path: filePath,
|
|
2523
|
+
mtime: (await promises.stat(filePath)).mtimeMs
|
|
2524
|
+
};
|
|
2525
|
+
}));
|
|
2526
|
+
for (const result of statResults) if (result.status === "fulfilled") componentFiles.push(result.value);
|
|
2527
|
+
const oldFiles = componentFiles.filter((f) => now - f.mtime > olderThanMs);
|
|
2528
|
+
await Promise.all(oldFiles.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
2529
|
+
let deletedCount = oldFiles.length;
|
|
2523
2530
|
const remainingFiles = componentFiles.filter((f) => now - f.mtime <= olderThanMs);
|
|
2524
|
-
if (remainingFiles.length >
|
|
2531
|
+
if (remainingFiles.length > keepCount) {
|
|
2525
2532
|
remainingFiles.sort((a, b) => a.mtime - b.mtime);
|
|
2526
|
-
const toDelete = remainingFiles.slice(0, remainingFiles.length -
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
deletedCount++;
|
|
2530
|
-
}
|
|
2533
|
+
const toDelete = remainingFiles.slice(0, remainingFiles.length - keepCount);
|
|
2534
|
+
await Promise.all(toDelete.map((file) => promises.unlink(file.path).catch((err) => log.warn("[ComponentRendererService] Failed to delete file:", file.path, err))));
|
|
2535
|
+
deletedCount += toDelete.length;
|
|
2531
2536
|
}
|
|
2532
2537
|
if (deletedCount > 0) log.info(`[ComponentRendererService] Cleaned up ${deletedCount} temp files`);
|
|
2533
2538
|
} catch (error) {
|
|
@@ -2535,12 +2540,13 @@ var ComponentRendererService = class ComponentRendererService {
|
|
|
2535
2540
|
}
|
|
2536
2541
|
}
|
|
2537
2542
|
/**
|
|
2538
|
-
* Cleanup bundler server and temp files.
|
|
2543
|
+
* Cleanup bundler server resources and old temp files.
|
|
2539
2544
|
* Called on service shutdown.
|
|
2545
|
+
* Keeps the most recent files for caching purposes.
|
|
2540
2546
|
*/
|
|
2541
2547
|
async dispose() {
|
|
2542
2548
|
try {
|
|
2543
|
-
await this.cleanup(0);
|
|
2549
|
+
await this.cleanup(0, ComponentRendererService.DISPOSE_KEEP_COUNT);
|
|
2544
2550
|
const bundlerService = this.getBundlerService();
|
|
2545
2551
|
if (!bundlerService.isServerRunning()) await bundlerService.cleanup();
|
|
2546
2552
|
} catch (error) {
|
|
@@ -2617,16 +2623,16 @@ var GetUiComponentService = class {
|
|
|
2617
2623
|
const { componentName, appPath, storyName = this.config.defaultStoryName, darkMode = this.config.defaultDarkMode } = input;
|
|
2618
2624
|
log.info(`[GetUiComponentService] Starting for component: ${componentName}, appPath: ${appPath}, storyName: ${storyName}`);
|
|
2619
2625
|
const storiesIndex = this.storiesIndexFactory();
|
|
2626
|
+
let designSystemConfig;
|
|
2620
2627
|
try {
|
|
2621
|
-
await storiesIndex.initialize();
|
|
2628
|
+
[, designSystemConfig] = await Promise.all([storiesIndex.initialize(), getAppDesignSystemConfig(appPath)]);
|
|
2622
2629
|
} catch (error) {
|
|
2623
|
-
throw new Error(`Failed to initialize stories index: ${error instanceof Error ? error.message : String(error)}`);
|
|
2630
|
+
throw new Error(`Failed to initialize stories index or design system config: ${error instanceof Error ? error.message : String(error)}`);
|
|
2624
2631
|
}
|
|
2625
2632
|
const componentInfo = storiesIndex.findComponentByName(componentName);
|
|
2626
2633
|
if (!componentInfo) throw new Error(`Component "${componentName}" not found in stories index. Ensure the component has a .stories.tsx file and has been indexed.`);
|
|
2627
2634
|
log.info(`[GetUiComponentService] Found component: ${componentInfo.title}`);
|
|
2628
2635
|
const validStoryName = this.resolveStoryName(storyName, componentInfo.stories);
|
|
2629
|
-
const designSystemConfig = await getAppDesignSystemConfig(appPath);
|
|
2630
2636
|
log.info(`[GetUiComponentService] Using theme provider: ${designSystemConfig.themeProvider}`);
|
|
2631
2637
|
log.info(`[GetUiComponentService] Design system type: ${designSystemConfig.type}`);
|
|
2632
2638
|
const renderer = this.rendererFactory(designSystemConfig, appPath);
|
|
@@ -2990,11 +2996,9 @@ var ListSharedComponentsTool = class ListSharedComponentsTool {
|
|
|
2990
2996
|
const { cursor, tags: inputTags } = input;
|
|
2991
2997
|
const { offset } = cursor ? this.decodeCursor(cursor) : { offset: 0 };
|
|
2992
2998
|
const storiesIndex = new StoriesIndexService();
|
|
2993
|
-
await storiesIndex.initialize();
|
|
2999
|
+
const [, defaultTags] = await Promise.all([storiesIndex.initialize(), getSharedComponentTags()]);
|
|
2994
3000
|
const availableTags = storiesIndex.getAllTags();
|
|
2995
|
-
|
|
2996
|
-
if (inputTags !== void 0) filterTags = inputTags;
|
|
2997
|
-
else filterTags = await getSharedComponentTags();
|
|
3001
|
+
const filterTags = inputTags !== void 0 ? inputTags : defaultTags;
|
|
2998
3002
|
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();
|
|
2999
3003
|
const totalComponents = allComponentNames.length;
|
|
3000
3004
|
const paginatedComponents = allComponentNames.slice(offset, offset + ListSharedComponentsTool.PAGE_SIZE);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agiflowai/style-system",
|
|
3
3
|
"description": "MCP server for exploring themes, Tailwind CSS classes, and UI components from Storybook with standalone component rendering capabilities",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"README.md"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@modelcontextprotocol/sdk": "1.
|
|
25
|
+
"@modelcontextprotocol/sdk": "1.25.2",
|
|
26
26
|
"@storybook/csf-tools": "^8.6.14",
|
|
27
27
|
"@tailwindcss/vite": "^4.1.16",
|
|
28
28
|
"chalk": "5.6.2",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"tailwindcss": "^4.1.11",
|
|
39
39
|
"vite": "^7.1.12",
|
|
40
40
|
"vite-plugin-singlefile": "^2.3.0",
|
|
41
|
-
"@agiflowai/aicode-utils": "1.0.
|
|
41
|
+
"@agiflowai/aicode-utils": "1.0.12"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@babel/parser": "^7.28.5",
|