@getcoherent/core 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -5
- package/dist/index.js +258 -237
- package/package.json +1 -2
package/dist/index.d.ts
CHANGED
|
@@ -953,9 +953,9 @@ declare const PageAnalysisSchema: z.ZodOptional<z.ZodObject<{
|
|
|
953
953
|
}>>;
|
|
954
954
|
type PageAnalysis = z.infer<typeof PageAnalysisSchema>;
|
|
955
955
|
declare const PageDefinitionSchema: z.ZodObject<{
|
|
956
|
-
id: z.ZodString
|
|
956
|
+
id: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
957
957
|
name: z.ZodString;
|
|
958
|
-
route: z.ZodString
|
|
958
|
+
route: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
959
959
|
layout: z.ZodEnum<["centered", "sidebar-left", "sidebar-right", "full-width", "grid"]>;
|
|
960
960
|
sections: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
961
961
|
id: z.ZodString;
|
|
@@ -1916,9 +1916,9 @@ declare const DesignSystemConfigSchema: z.ZodObject<{
|
|
|
1916
1916
|
generatedCode?: string | undefined;
|
|
1917
1917
|
}>, "many">>>;
|
|
1918
1918
|
pages: z.ZodArray<z.ZodObject<{
|
|
1919
|
-
id: z.ZodString
|
|
1919
|
+
id: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
1920
1920
|
name: z.ZodString;
|
|
1921
|
-
route: z.ZodString
|
|
1921
|
+
route: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
|
|
1922
1922
|
layout: z.ZodEnum<["centered", "sidebar-left", "sidebar-right", "full-width", "grid"]>;
|
|
1923
1923
|
sections: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
1924
1924
|
id: z.ZodString;
|
|
@@ -2938,6 +2938,11 @@ declare class DesignSystemManager {
|
|
|
2938
2938
|
*/
|
|
2939
2939
|
reload(): Promise<void>;
|
|
2940
2940
|
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Extract the config object from a TypeScript source file using balanced-brace matching.
|
|
2943
|
+
* More robust than a greedy regex — correctly handles nested objects and trailing content.
|
|
2944
|
+
*/
|
|
2945
|
+
declare function extractConfigObject(source: string): string;
|
|
2941
2946
|
|
|
2942
2947
|
/**
|
|
2943
2948
|
* Component Manager
|
|
@@ -4264,6 +4269,12 @@ declare const CLI_VERSION = "0.1.0";
|
|
|
4264
4269
|
|
|
4265
4270
|
declare function buildCssVariables(config: DesignSystemConfig): string;
|
|
4266
4271
|
|
|
4272
|
+
/**
|
|
4273
|
+
* Atomic file write: writes to a temp file first, then renames.
|
|
4274
|
+
* Prevents data corruption if the process crashes mid-write.
|
|
4275
|
+
*/
|
|
4276
|
+
declare function atomicWriteFile(filePath: string, content: string): Promise<void>;
|
|
4277
|
+
|
|
4267
4278
|
/**
|
|
4268
4279
|
* Story 2.11 Part C: Consistency audit for shared components.
|
|
4269
4280
|
* - Verify usedIn vs actual imports
|
|
@@ -4294,4 +4305,4 @@ interface AuditResult {
|
|
|
4294
4305
|
*/
|
|
4295
4306
|
declare function runAudit(projectRoot: string): Promise<AuditResult>;
|
|
4296
4307
|
|
|
4297
|
-
export { type AuditEntryResult, type AuditResult, type BlogContent, CLI_VERSION, type ChangelogContent, type ColorToken, ColorTokenSchema, type ComponentCriteria, type ComponentDefinition, ComponentDefinitionSchema, type ComponentDependency, ComponentGenerator, ComponentManager, type ComponentSize, ComponentSizeSchema, type ComponentSpec, type ComponentVariant, ComponentVariantSchema, type ContactContent, type CreateSharedComponentInput, type DashboardContent, type DesignSystemConfig, DesignSystemConfigSchema, DesignSystemGenerator, DesignSystemManager, type DesignTokens, DesignTokensSchema, type DiscoveryResult, EXAMPLE_MULTIPAGE_CONFIG, EXAMPLE_SPA_CONFIG, FIGMA_BASE_IDS, FRAMEWORK_VERSIONS, type FaqContent, type Features, FeaturesSchema, type FigmaBaseId, FigmaClient, type FigmaClientOptions, type FigmaColorStyle, type FigmaComponentData, type FigmaComponentMap, type FigmaComponentMeta, type FigmaDocumentNode, type FigmaEffectStyle, type FigmaFileResponse, type FigmaIntermediateData, type FigmaLayout, type FigmaNode, type FigmaNormalizationResult, type FigmaNormalizedEntry, type FigmaPageData, type FigmaProperty, type FigmaRgba, type FigmaStyleMeta, type FigmaTextStyle, type FigmaTokenExtractionResult, type FigmaVariant, type GalleryContent, type GenerateSharedComponentInput, type GenerateSharedComponentResult, type GeneratedPage, type LandingContent, type LayoutBlockDefinition, LayoutBlockDefinitionSchema, type ListingContent, MANIFEST_FILENAME, type ModificationRequest, type ModificationResult, type Navigation, type NavigationItem, NavigationItemSchema, NavigationSchema, type OnboardingContent, type PageAnalysis, PageAnalysisSchema, type PageContent, type PageDefinition, PageDefinitionSchema, PageGenerator, type PageLayout, PageLayoutSchema, PageManager, type PageSection, PageSectionSchema, type PricingContent, type ProfileContent, ProjectScaffolder, type RadiusToken, RadiusTokenSchema, type SettingsContent, type SharedComponentEntry, SharedComponentEntrySchema, type SharedComponentType, SharedComponentTypeSchema, type SharedComponentsManifest, SharedComponentsManifestSchema, type SpacingToken, SpacingTokenSchema, TailwindConfigGenerator, type TemplateOptions, type TypographyToken, TypographyTokenSchema, allocateNextCid, buildCssVariables, componentExists, createEntry, extractTokensFromFigma, figmaComponentNameToBaseId, figmaRgbaToHex, findSharedComponent, findSharedComponentByIdOrName, formatCid, generatePageFromFrame, generatePagesFromFigma, generateSharedComponent, generateSharedComponentTsx, getComponent, getManifestPath, getPage, getPageFilePath, getSupportedPageTypes, getTemplateForPageType, integrateSharedLayoutIntoRootLayout, loadManifest, mergeExtractedColorsWithDefaults, normalizeFigmaComponents, pageRouteExists, parseCid, parseFigmaFileResponse, removeEntry, resolveUniqueName, runAudit, saveManifest, setSharedMapping, toSharedFileName, updateEntry, updateUsedIn, validateConfig, validatePartialConfig };
|
|
4308
|
+
export { type AuditEntryResult, type AuditResult, type BlogContent, CLI_VERSION, type ChangelogContent, type ColorToken, ColorTokenSchema, type ComponentCriteria, type ComponentDefinition, ComponentDefinitionSchema, type ComponentDependency, ComponentGenerator, ComponentManager, type ComponentSize, ComponentSizeSchema, type ComponentSpec, type ComponentVariant, ComponentVariantSchema, type ContactContent, type CreateSharedComponentInput, type DashboardContent, type DesignSystemConfig, DesignSystemConfigSchema, DesignSystemGenerator, DesignSystemManager, type DesignTokens, DesignTokensSchema, type DiscoveryResult, EXAMPLE_MULTIPAGE_CONFIG, EXAMPLE_SPA_CONFIG, FIGMA_BASE_IDS, FRAMEWORK_VERSIONS, type FaqContent, type Features, FeaturesSchema, type FigmaBaseId, FigmaClient, type FigmaClientOptions, type FigmaColorStyle, type FigmaComponentData, type FigmaComponentMap, type FigmaComponentMeta, type FigmaDocumentNode, type FigmaEffectStyle, type FigmaFileResponse, type FigmaIntermediateData, type FigmaLayout, type FigmaNode, type FigmaNormalizationResult, type FigmaNormalizedEntry, type FigmaPageData, type FigmaProperty, type FigmaRgba, type FigmaStyleMeta, type FigmaTextStyle, type FigmaTokenExtractionResult, type FigmaVariant, type GalleryContent, type GenerateSharedComponentInput, type GenerateSharedComponentResult, type GeneratedPage, type LandingContent, type LayoutBlockDefinition, LayoutBlockDefinitionSchema, type ListingContent, MANIFEST_FILENAME, type ModificationRequest, type ModificationResult, type Navigation, type NavigationItem, NavigationItemSchema, NavigationSchema, type OnboardingContent, type PageAnalysis, PageAnalysisSchema, type PageContent, type PageDefinition, PageDefinitionSchema, PageGenerator, type PageLayout, PageLayoutSchema, PageManager, type PageSection, PageSectionSchema, type PricingContent, type ProfileContent, ProjectScaffolder, type RadiusToken, RadiusTokenSchema, type SettingsContent, type SharedComponentEntry, SharedComponentEntrySchema, type SharedComponentType, SharedComponentTypeSchema, type SharedComponentsManifest, SharedComponentsManifestSchema, type SpacingToken, SpacingTokenSchema, TailwindConfigGenerator, type TemplateOptions, type TypographyToken, TypographyTokenSchema, allocateNextCid, atomicWriteFile, buildCssVariables, componentExists, createEntry, extractConfigObject, extractTokensFromFigma, figmaComponentNameToBaseId, figmaRgbaToHex, findSharedComponent, findSharedComponentByIdOrName, formatCid, generatePageFromFrame, generatePagesFromFigma, generateSharedComponent, generateSharedComponentTsx, getComponent, getManifestPath, getPage, getPageFilePath, getSupportedPageTypes, getTemplateForPageType, integrateSharedLayoutIntoRootLayout, loadManifest, mergeExtractedColorsWithDefaults, normalizeFigmaComponents, pageRouteExists, parseCid, parseFigmaFileResponse, removeEntry, resolveUniqueName, runAudit, saveManifest, setSharedMapping, toSharedFileName, updateEntry, updateUsedIn, validateConfig, validatePartialConfig };
|
package/dist/index.js
CHANGED
|
@@ -97,15 +97,7 @@ var ComponentDefinitionSchema = z.object({
|
|
|
97
97
|
id: z.string().regex(/^[a-z][a-z0-9-]*$/, "Must be kebab-case"),
|
|
98
98
|
name: z.string(),
|
|
99
99
|
// PascalCase (e.g., "Button", "InputField")
|
|
100
|
-
category: z.enum([
|
|
101
|
-
"form",
|
|
102
|
-
"layout",
|
|
103
|
-
"navigation",
|
|
104
|
-
"feedback",
|
|
105
|
-
"data-display",
|
|
106
|
-
"overlay",
|
|
107
|
-
"typography"
|
|
108
|
-
]),
|
|
100
|
+
category: z.enum(["form", "layout", "navigation", "feedback", "data-display", "overlay", "typography"]),
|
|
109
101
|
// Source
|
|
110
102
|
source: z.enum(["shadcn", "custom"]),
|
|
111
103
|
shadcnComponent: z.string().optional(),
|
|
@@ -167,10 +159,12 @@ var PageLayoutSchema = z.enum([
|
|
|
167
159
|
// CSS Grid layout
|
|
168
160
|
]);
|
|
169
161
|
var PageAnalysisSchema = z.object({
|
|
170
|
-
sections: z.array(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
162
|
+
sections: z.array(
|
|
163
|
+
z.object({
|
|
164
|
+
name: z.string(),
|
|
165
|
+
order: z.number()
|
|
166
|
+
})
|
|
167
|
+
).optional(),
|
|
174
168
|
componentUsage: z.record(z.string(), z.number()).optional(),
|
|
175
169
|
iconCount: z.number().optional(),
|
|
176
170
|
layoutPattern: z.string().optional(),
|
|
@@ -179,9 +173,11 @@ var PageAnalysisSchema = z.object({
|
|
|
179
173
|
}).optional();
|
|
180
174
|
var PageDefinitionSchema = z.object({
|
|
181
175
|
// Identity
|
|
182
|
-
id: z.string().
|
|
176
|
+
id: z.string().transform(
|
|
177
|
+
(s) => s.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "")
|
|
178
|
+
).pipe(z.string().regex(/^[a-z][a-z0-9-]*$/, "Must be kebab-case")),
|
|
183
179
|
name: z.string(),
|
|
184
|
-
route: z.string().regex(/^\/[a-z0-9
|
|
180
|
+
route: z.string().transform((r) => r.startsWith("/") ? r : `/${r}`).pipe(z.string().regex(/^\/[a-z0-9\-/[\]]*$/, "Must be a valid route (e.g. /page, /products/[id])")),
|
|
185
181
|
// Layout
|
|
186
182
|
layout: PageLayoutSchema,
|
|
187
183
|
sections: z.array(PageSectionSchema).default([]),
|
|
@@ -473,10 +469,24 @@ function parseCid(id) {
|
|
|
473
469
|
}
|
|
474
470
|
|
|
475
471
|
// src/managers/DesignSystemManager.ts
|
|
476
|
-
import { readFile
|
|
472
|
+
import { readFile } from "fs/promises";
|
|
473
|
+
|
|
474
|
+
// src/utils/atomicWrite.ts
|
|
475
|
+
import { writeFile, rename, mkdir } from "fs/promises";
|
|
477
476
|
import { dirname } from "path";
|
|
478
477
|
import { existsSync } from "fs";
|
|
479
|
-
import {
|
|
478
|
+
import { randomBytes } from "crypto";
|
|
479
|
+
async function atomicWriteFile(filePath, content) {
|
|
480
|
+
const dir = dirname(filePath);
|
|
481
|
+
if (!existsSync(dir)) {
|
|
482
|
+
await mkdir(dir, { recursive: true });
|
|
483
|
+
}
|
|
484
|
+
const tmpPath = `${filePath}.${randomBytes(4).toString("hex")}.tmp`;
|
|
485
|
+
await writeFile(tmpPath, content, "utf-8");
|
|
486
|
+
await rename(tmpPath, filePath);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// src/managers/DesignSystemManager.ts
|
|
480
490
|
var DesignSystemManager = class {
|
|
481
491
|
config = null;
|
|
482
492
|
configPath;
|
|
@@ -490,11 +500,7 @@ var DesignSystemManager = class {
|
|
|
490
500
|
async load() {
|
|
491
501
|
try {
|
|
492
502
|
const fileContent = await readFile(this.configPath, "utf-8");
|
|
493
|
-
const
|
|
494
|
-
if (!configMatch) {
|
|
495
|
-
throw new Error("Invalid config file format. Expected: export const config = { ... }");
|
|
496
|
-
}
|
|
497
|
-
const configJson = configMatch[1];
|
|
503
|
+
const configJson = extractConfigObject(fileContent);
|
|
498
504
|
const config = JSON.parse(configJson);
|
|
499
505
|
this.config = validateConfig(config);
|
|
500
506
|
this.loadComponentRegistry();
|
|
@@ -522,11 +528,7 @@ var DesignSystemManager = class {
|
|
|
522
528
|
|
|
523
529
|
export const config = ${JSON.stringify(this.config, null, 2)} as const
|
|
524
530
|
`;
|
|
525
|
-
|
|
526
|
-
if (!existsSync(dir)) {
|
|
527
|
-
await mkdir(dir, { recursive: true });
|
|
528
|
-
}
|
|
529
|
-
await writeFile(this.configPath, fileContent, "utf-8");
|
|
531
|
+
await atomicWriteFile(this.configPath, fileContent);
|
|
530
532
|
} catch (error) {
|
|
531
533
|
if (error instanceof Error) {
|
|
532
534
|
throw new Error(`Failed to save config to ${this.configPath}: ${error.message}`);
|
|
@@ -790,6 +792,44 @@ export const config = ${JSON.stringify(this.config, null, 2)} as const
|
|
|
790
792
|
await this.load();
|
|
791
793
|
}
|
|
792
794
|
};
|
|
795
|
+
function extractConfigObject(source) {
|
|
796
|
+
const marker = source.match(/export\s+const\s+config[^=]*=\s*/);
|
|
797
|
+
if (!marker || marker.index === void 0) {
|
|
798
|
+
throw new Error("Invalid config file format. Expected: export const config = { ... }");
|
|
799
|
+
}
|
|
800
|
+
const startSearch = marker.index + marker[0].length;
|
|
801
|
+
const openBrace = source.indexOf("{", startSearch);
|
|
802
|
+
if (openBrace === -1) {
|
|
803
|
+
throw new Error("Invalid config file format: no opening brace found after config =");
|
|
804
|
+
}
|
|
805
|
+
let depth = 0;
|
|
806
|
+
let inString = false;
|
|
807
|
+
let escape = false;
|
|
808
|
+
for (let i = openBrace; i < source.length; i++) {
|
|
809
|
+
const ch = source[i];
|
|
810
|
+
if (escape) {
|
|
811
|
+
escape = false;
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (ch === "\\" && inString) {
|
|
815
|
+
escape = true;
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
if (ch === '"' && !escape) {
|
|
819
|
+
inString = !inString;
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (inString) continue;
|
|
823
|
+
if (ch === "{") depth++;
|
|
824
|
+
else if (ch === "}") {
|
|
825
|
+
depth--;
|
|
826
|
+
if (depth === 0) {
|
|
827
|
+
return source.slice(openBrace, i + 1);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
throw new Error("Invalid config file format: unbalanced braces");
|
|
832
|
+
}
|
|
793
833
|
|
|
794
834
|
// src/managers/ComponentManager.ts
|
|
795
835
|
var ComponentManager = class {
|
|
@@ -904,10 +944,7 @@ var ComponentManager = class {
|
|
|
904
944
|
];
|
|
905
945
|
return {
|
|
906
946
|
success: true,
|
|
907
|
-
modified: [
|
|
908
|
-
`component:${id}`,
|
|
909
|
-
...affectedPages.map((page) => `page:${page}`)
|
|
910
|
-
],
|
|
947
|
+
modified: [`component:${id}`, ...affectedPages.map((page) => `page:${page}`)],
|
|
911
948
|
config: this.config,
|
|
912
949
|
message: `Updated component ${updated.name} (${id})`,
|
|
913
950
|
warnings: allWarnings.length > 0 ? allWarnings : void 0
|
|
@@ -932,10 +969,7 @@ var ComponentManager = class {
|
|
|
932
969
|
modified: [],
|
|
933
970
|
config: this.config,
|
|
934
971
|
message: `Cannot delete component used in ${component.usedInPages.length} page(s)`,
|
|
935
|
-
warnings: [
|
|
936
|
-
"Remove component from all pages before deleting",
|
|
937
|
-
`Used in: ${component.usedInPages.join(", ")}`
|
|
938
|
-
]
|
|
972
|
+
warnings: ["Remove component from all pages before deleting", `Used in: ${component.usedInPages.join(", ")}`]
|
|
939
973
|
};
|
|
940
974
|
}
|
|
941
975
|
this.config.components = this.config.components.filter((c) => c.id !== id);
|
|
@@ -959,9 +993,7 @@ var ComponentManager = class {
|
|
|
959
993
|
}
|
|
960
994
|
if (criteria.name) {
|
|
961
995
|
const nameLower = criteria.name.toLowerCase();
|
|
962
|
-
results = results.filter(
|
|
963
|
-
(c) => c.name.toLowerCase().includes(nameLower)
|
|
964
|
-
);
|
|
996
|
+
results = results.filter((c) => c.name.toLowerCase().includes(nameLower));
|
|
965
997
|
}
|
|
966
998
|
if (criteria.category) {
|
|
967
999
|
results = results.filter((c) => c.category === criteria.category);
|
|
@@ -970,24 +1002,16 @@ var ComponentManager = class {
|
|
|
970
1002
|
results = results.filter((c) => c.source === criteria.source);
|
|
971
1003
|
}
|
|
972
1004
|
if (criteria.shadcnComponent) {
|
|
973
|
-
results = results.filter(
|
|
974
|
-
(c) => c.shadcnComponent === criteria.shadcnComponent
|
|
975
|
-
);
|
|
1005
|
+
results = results.filter((c) => c.shadcnComponent === criteria.shadcnComponent);
|
|
976
1006
|
}
|
|
977
1007
|
if (criteria.usedInPage) {
|
|
978
|
-
results = results.filter(
|
|
979
|
-
(c) => c.usedInPages.includes(criteria.usedInPage)
|
|
980
|
-
);
|
|
1008
|
+
results = results.filter((c) => c.usedInPages.includes(criteria.usedInPage));
|
|
981
1009
|
}
|
|
982
1010
|
if (criteria.hasVariant) {
|
|
983
|
-
results = results.filter(
|
|
984
|
-
(c) => c.variants.some((v) => v.name === criteria.hasVariant)
|
|
985
|
-
);
|
|
1011
|
+
results = results.filter((c) => c.variants.some((v) => v.name === criteria.hasVariant));
|
|
986
1012
|
}
|
|
987
1013
|
if (criteria.hasSize) {
|
|
988
|
-
results = results.filter(
|
|
989
|
-
(c) => c.sizes.some((s) => s.name === criteria.hasSize)
|
|
990
|
-
);
|
|
1014
|
+
results = results.filter((c) => c.sizes.some((s) => s.name === criteria.hasSize));
|
|
991
1015
|
}
|
|
992
1016
|
return results;
|
|
993
1017
|
}
|
|
@@ -1040,9 +1064,7 @@ var ComponentManager = class {
|
|
|
1040
1064
|
if (index !== -1) {
|
|
1041
1065
|
component.usedInPages.splice(index, 1);
|
|
1042
1066
|
component.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1043
|
-
const configIndex = this.config.components.findIndex(
|
|
1044
|
-
(c) => c.id === componentId
|
|
1045
|
-
);
|
|
1067
|
+
const configIndex = this.config.components.findIndex((c) => c.id === componentId);
|
|
1046
1068
|
if (configIndex !== -1) {
|
|
1047
1069
|
this.config.components[configIndex] = component;
|
|
1048
1070
|
this.componentRegistry.set(componentId, component);
|
|
@@ -1126,9 +1148,7 @@ var ComponentManager = class {
|
|
|
1126
1148
|
}
|
|
1127
1149
|
if (requested.requiredVariants) {
|
|
1128
1150
|
const existingVariantNames = existing.variants.map((v) => v.name);
|
|
1129
|
-
const hasAllVariants = requested.requiredVariants.every(
|
|
1130
|
-
(v) => existingVariantNames.includes(v)
|
|
1131
|
-
);
|
|
1151
|
+
const hasAllVariants = requested.requiredVariants.every((v) => existingVariantNames.includes(v));
|
|
1132
1152
|
if (!hasAllVariants) {
|
|
1133
1153
|
return false;
|
|
1134
1154
|
}
|
|
@@ -1138,18 +1158,13 @@ var ComponentManager = class {
|
|
|
1138
1158
|
const isValidSize = (s) => {
|
|
1139
1159
|
return ["xs", "sm", "md", "lg", "xl"].includes(s);
|
|
1140
1160
|
};
|
|
1141
|
-
const hasAllSizes = requested.requiredSizes.every(
|
|
1142
|
-
(s) => isValidSize(s) && existingSizeNames.includes(s)
|
|
1143
|
-
);
|
|
1161
|
+
const hasAllSizes = requested.requiredSizes.every((s) => isValidSize(s) && existingSizeNames.includes(s));
|
|
1144
1162
|
if (!hasAllSizes) {
|
|
1145
1163
|
return false;
|
|
1146
1164
|
}
|
|
1147
1165
|
}
|
|
1148
1166
|
if (requested.baseClassName) {
|
|
1149
|
-
if (!this.isSimilarClassName(
|
|
1150
|
-
existing.baseClassName,
|
|
1151
|
-
requested.baseClassName
|
|
1152
|
-
)) {
|
|
1167
|
+
if (!this.isSimilarClassName(existing.baseClassName, requested.baseClassName)) {
|
|
1153
1168
|
return false;
|
|
1154
1169
|
}
|
|
1155
1170
|
}
|
|
@@ -1167,17 +1182,13 @@ var ComponentManager = class {
|
|
|
1167
1182
|
}
|
|
1168
1183
|
if (requested.name) {
|
|
1169
1184
|
const requestedName = requested.name.toLowerCase();
|
|
1170
|
-
const nameMatch = candidates.find(
|
|
1171
|
-
(c) => c.name.toLowerCase() === requestedName
|
|
1172
|
-
);
|
|
1185
|
+
const nameMatch = candidates.find((c) => c.name.toLowerCase() === requestedName);
|
|
1173
1186
|
if (nameMatch) {
|
|
1174
1187
|
return nameMatch;
|
|
1175
1188
|
}
|
|
1176
1189
|
}
|
|
1177
1190
|
if (requested.category) {
|
|
1178
|
-
const categoryMatch = candidates.find(
|
|
1179
|
-
(c) => c.category === requested.category
|
|
1180
|
-
);
|
|
1191
|
+
const categoryMatch = candidates.find((c) => c.category === requested.category);
|
|
1181
1192
|
if (categoryMatch) {
|
|
1182
1193
|
return categoryMatch;
|
|
1183
1194
|
}
|
|
@@ -1435,9 +1446,7 @@ var PageManager = class {
|
|
|
1435
1446
|
this.config.pages = this.config.pages.filter((p) => p.id !== id);
|
|
1436
1447
|
this.config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1437
1448
|
if (this.config.navigation?.enabled) {
|
|
1438
|
-
this.config.navigation.items = this.config.navigation.items.filter(
|
|
1439
|
-
(item) => item.route !== page.route
|
|
1440
|
-
);
|
|
1449
|
+
this.config.navigation.items = this.config.navigation.items.filter((item) => item.route !== page.route);
|
|
1441
1450
|
}
|
|
1442
1451
|
this.config = validateConfig(this.config);
|
|
1443
1452
|
return {
|
|
@@ -1602,9 +1611,7 @@ var PageManager = class {
|
|
|
1602
1611
|
});
|
|
1603
1612
|
}
|
|
1604
1613
|
});
|
|
1605
|
-
navigation.items = navigation.items.filter(
|
|
1606
|
-
(item) => this.config.pages.some((page) => page.route === item.route)
|
|
1607
|
-
);
|
|
1614
|
+
navigation.items = navigation.items.filter((item) => this.config.pages.some((page) => page.route === item.route));
|
|
1608
1615
|
navigation.items.sort((a, b) => a.order - b.order);
|
|
1609
1616
|
navigation.items.forEach((item, index) => {
|
|
1610
1617
|
item.order = index + 1;
|
|
@@ -1627,7 +1634,7 @@ var PageManager = class {
|
|
|
1627
1634
|
*/
|
|
1628
1635
|
generateNextJsPage(def) {
|
|
1629
1636
|
const imports = this.generateImports(def);
|
|
1630
|
-
const
|
|
1637
|
+
const _metadata = this.generateMetadata(def);
|
|
1631
1638
|
const sections = this.generateSections(def);
|
|
1632
1639
|
const containerClass = this.getContainerClass(def.layout);
|
|
1633
1640
|
return `import { Metadata } from 'next'
|
|
@@ -1745,17 +1752,13 @@ ${navigation ? ` ${navigation}
|
|
|
1745
1752
|
* Generate imports for page components
|
|
1746
1753
|
*/
|
|
1747
1754
|
generateImports(def) {
|
|
1748
|
-
const componentIds = new Set(
|
|
1749
|
-
def.sections.map((section) => section.componentId)
|
|
1750
|
-
);
|
|
1755
|
+
const componentIds = new Set(def.sections.map((section) => section.componentId));
|
|
1751
1756
|
const imports = [];
|
|
1752
1757
|
componentIds.forEach((componentId) => {
|
|
1753
1758
|
const component = getComponent(this.config, componentId);
|
|
1754
1759
|
if (component) {
|
|
1755
1760
|
const componentName = component.name;
|
|
1756
|
-
imports.push(
|
|
1757
|
-
`import { ${componentName} } from '@/components/${componentName}'`
|
|
1758
|
-
);
|
|
1761
|
+
imports.push(`import { ${componentName} } from '@/components/${componentName}'`);
|
|
1759
1762
|
}
|
|
1760
1763
|
});
|
|
1761
1764
|
return imports.join("\n");
|
|
@@ -1797,9 +1800,7 @@ ${navigation ? ` ${navigation}
|
|
|
1797
1800
|
if (!this.config.navigation?.enabled) {
|
|
1798
1801
|
return "";
|
|
1799
1802
|
}
|
|
1800
|
-
const items = this.config.navigation.items.map(
|
|
1801
|
-
(item) => ` <Link href="${item.route}">${item.label}</Link>`
|
|
1802
|
-
).join("\n");
|
|
1803
|
+
const items = this.config.navigation.items.map((item) => ` <Link href="${item.route}">${item.label}</Link>`).join("\n");
|
|
1803
1804
|
return `<nav className="navigation">
|
|
1804
1805
|
${items}
|
|
1805
1806
|
</nav>`;
|
|
@@ -1809,18 +1810,18 @@ ${items}
|
|
|
1809
1810
|
*/
|
|
1810
1811
|
getContainerClass(layout) {
|
|
1811
1812
|
const layoutClasses = {
|
|
1812
|
-
|
|
1813
|
+
centered: "max-w-4xl mx-auto px-4",
|
|
1813
1814
|
"sidebar-left": "flex",
|
|
1814
1815
|
"sidebar-right": "flex flex-row-reverse",
|
|
1815
1816
|
"full-width": "w-full",
|
|
1816
|
-
|
|
1817
|
+
grid: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
|
|
1817
1818
|
};
|
|
1818
1819
|
return layoutClasses[layout] || "";
|
|
1819
1820
|
}
|
|
1820
1821
|
/**
|
|
1821
1822
|
* Get body class based on layout type
|
|
1822
1823
|
*/
|
|
1823
|
-
getBodyClass(
|
|
1824
|
+
getBodyClass(_layout) {
|
|
1824
1825
|
return "min-h-screen bg-background text-foreground";
|
|
1825
1826
|
}
|
|
1826
1827
|
/**
|
|
@@ -3167,7 +3168,7 @@ ${componentName}.displayName = '${componentName}'`;
|
|
|
3167
3168
|
/**
|
|
3168
3169
|
* Apply design tokens to className string
|
|
3169
3170
|
*/
|
|
3170
|
-
applyTokens(className,
|
|
3171
|
+
applyTokens(className, _tokens) {
|
|
3171
3172
|
return className;
|
|
3172
3173
|
}
|
|
3173
3174
|
/**
|
|
@@ -4808,7 +4809,9 @@ export default function TokensPage() {
|
|
|
4808
4809
|
lines.push("import { useEffect, useState } from 'react'");
|
|
4809
4810
|
lines.push("");
|
|
4810
4811
|
lines.push("export default function ColorsPage() {");
|
|
4811
|
-
lines.push(
|
|
4812
|
+
lines.push(
|
|
4813
|
+
" const [tokens, setTokens] = useState<{ colors?: { light?: Record<string, string>; dark?: Record<string, string> } } | null>(null)"
|
|
4814
|
+
);
|
|
4812
4815
|
lines.push(" const [loading, setLoading] = useState(true)");
|
|
4813
4816
|
lines.push("");
|
|
4814
4817
|
lines.push(" useEffect(() => {");
|
|
@@ -4854,7 +4857,9 @@ export default function TokensPage() {
|
|
|
4854
4857
|
lines.push(" />");
|
|
4855
4858
|
lines.push(' <div className="min-w-0 flex-1">');
|
|
4856
4859
|
lines.push(' <div className="font-medium capitalize">{key}</div>');
|
|
4857
|
-
lines.push(
|
|
4860
|
+
lines.push(
|
|
4861
|
+
' <div className="text-xs text-muted-foreground font-mono">{value} \xB7 var({toCssVar(key)})</div>'
|
|
4862
|
+
);
|
|
4858
4863
|
lines.push(" </div>");
|
|
4859
4864
|
lines.push(" </div>");
|
|
4860
4865
|
lines.push(" )");
|
|
@@ -4941,7 +4946,9 @@ export default function TokensPage() {
|
|
|
4941
4946
|
lines.push(' <div key={name} className="space-y-2">');
|
|
4942
4947
|
lines.push(' <div className="text-sm font-medium capitalize">{name}</div>');
|
|
4943
4948
|
lines.push(' <div className="text-xs text-muted-foreground font-mono">{value as string}</div>');
|
|
4944
|
-
lines.push(
|
|
4949
|
+
lines.push(
|
|
4950
|
+
' <div className="text-lg" style={{ fontFamily: value as string }}>The quick brown fox jumps over the lazy dog</div>'
|
|
4951
|
+
);
|
|
4945
4952
|
lines.push(" </div>");
|
|
4946
4953
|
lines.push(" ))}");
|
|
4947
4954
|
lines.push(" </div>");
|
|
@@ -4953,9 +4960,13 @@ export default function TokensPage() {
|
|
|
4953
4960
|
lines.push(' <div key={name} className="space-y-1">');
|
|
4954
4961
|
lines.push(' <div className="flex items-baseline gap-2">');
|
|
4955
4962
|
lines.push(' <span className="text-sm font-medium">{name}</span>');
|
|
4956
|
-
lines.push(
|
|
4963
|
+
lines.push(
|
|
4964
|
+
' <span className="text-xs text-muted-foreground font-mono">{value as string} ({remToPx(value as string)})</span>'
|
|
4965
|
+
);
|
|
4957
4966
|
lines.push(" </div>");
|
|
4958
|
-
lines.push(
|
|
4967
|
+
lines.push(
|
|
4968
|
+
" <div style={{ fontSize: value as string }}>The quick brown fox jumps over the lazy dog</div>"
|
|
4969
|
+
);
|
|
4959
4970
|
lines.push(" </div>");
|
|
4960
4971
|
lines.push(" ))}");
|
|
4961
4972
|
lines.push(" </div>");
|
|
@@ -4980,7 +4991,9 @@ export default function TokensPage() {
|
|
|
4980
4991
|
lines.push(" {Object.entries(lineHeight).map(([name, value]) => (");
|
|
4981
4992
|
lines.push(' <div key={name} className="space-y-1">');
|
|
4982
4993
|
lines.push(' <div className="text-sm font-medium">{name} ({String(value)})</div>');
|
|
4983
|
-
lines.push(
|
|
4994
|
+
lines.push(
|
|
4995
|
+
' <div className="text-sm bg-muted/50 p-3 rounded max-w-md" style={{ lineHeight: value as number }}>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam.</div>'
|
|
4996
|
+
);
|
|
4984
4997
|
lines.push(" </div>");
|
|
4985
4998
|
lines.push(" ))}");
|
|
4986
4999
|
lines.push(" </div>");
|
|
@@ -5068,79 +5081,83 @@ export default function TokensPage() {
|
|
|
5068
5081
|
return lines.join("\n");
|
|
5069
5082
|
}
|
|
5070
5083
|
generateSitemapPage() {
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
})
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5084
|
+
return `import { readFileSync } from 'fs'
|
|
5085
|
+
import { join } from 'path'
|
|
5086
|
+
import Link from 'next/link'
|
|
5087
|
+
|
|
5088
|
+
function loadPages() {
|
|
5089
|
+
try {
|
|
5090
|
+
const raw = readFileSync(join(process.cwd(), 'design-system.config.ts'), 'utf-8')
|
|
5091
|
+
const jsonMatch = raw.match(/export\\s+const\\s+config\\s*=\\s*/)
|
|
5092
|
+
const jsonStart = jsonMatch ? raw.indexOf('{', raw.indexOf(jsonMatch[0])) : -1
|
|
5093
|
+
if (jsonStart === -1) return []
|
|
5094
|
+
let jsonStr = raw.slice(jsonStart)
|
|
5095
|
+
jsonStr = jsonStr.replace(/\\}\\s*(as\\s+const|satisfies\\s+\\w+)\\s*;?\\s*$/, '}')
|
|
5096
|
+
jsonStr = jsonStr.replace(/,\\s*([\\]\\}])/g, '$1')
|
|
5097
|
+
const json = JSON.parse(jsonStr)
|
|
5098
|
+
return (json.pages || [])
|
|
5099
|
+
.filter((p: any) => {
|
|
5100
|
+
const route = p.route || '/' + p.id
|
|
5101
|
+
return !route.includes('[') && !route.includes(']')
|
|
5102
|
+
})
|
|
5103
|
+
.map((p: any) => ({
|
|
5104
|
+
name: p.name || p.id,
|
|
5105
|
+
route: p.route || '/' + p.id,
|
|
5106
|
+
sections: p.pageAnalysis?.sections?.map((s: any) => s.name) || [],
|
|
5107
|
+
componentUsage: p.pageAnalysis?.componentUsage || {},
|
|
5108
|
+
iconCount: p.pageAnalysis?.iconCount ?? 0,
|
|
5109
|
+
layoutPattern: p.pageAnalysis?.layoutPattern || null,
|
|
5110
|
+
hasForm: p.pageAnalysis?.hasForm ?? false,
|
|
5111
|
+
}))
|
|
5112
|
+
} catch {
|
|
5113
|
+
return []
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
|
|
5117
|
+
export default function SitemapPage() {
|
|
5118
|
+
const pages = loadPages()
|
|
5119
|
+
|
|
5120
|
+
return (
|
|
5121
|
+
<div className="space-y-8">
|
|
5122
|
+
<div>
|
|
5123
|
+
<h1 className="text-3xl font-bold tracking-tight">Sitemap</h1>
|
|
5124
|
+
<p className="text-muted-foreground mt-1">All pages, sections, and component usage</p>
|
|
5125
|
+
</div>
|
|
5126
|
+
<div className="space-y-4">
|
|
5127
|
+
{pages.map((page: any) => (
|
|
5128
|
+
<div key={page.route} className="rounded-xl border p-4 space-y-3">
|
|
5129
|
+
<div className="flex items-center gap-3">
|
|
5130
|
+
<Link href={page.route} className="font-semibold text-sm hover:text-primary transition-colors">
|
|
5131
|
+
{page.name}
|
|
5132
|
+
</Link>
|
|
5133
|
+
<span className="text-xs text-muted-foreground font-mono">{page.route}</span>
|
|
5134
|
+
{page.layoutPattern && <span className="text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">{page.layoutPattern}</span>}
|
|
5135
|
+
{page.hasForm && <span className="text-[10px] px-1.5 py-0.5 rounded bg-primary/10 text-primary">form</span>}
|
|
5136
|
+
</div>
|
|
5137
|
+
{page.sections.length > 0 && (
|
|
5138
|
+
<div className="flex flex-wrap gap-1.5">
|
|
5139
|
+
{page.sections.map((s: string) => (
|
|
5140
|
+
<span key={s} className="text-[11px] px-2 py-0.5 rounded-full border text-muted-foreground">{s}</span>
|
|
5141
|
+
))}
|
|
5142
|
+
</div>
|
|
5143
|
+
)}
|
|
5144
|
+
{Object.keys(page.componentUsage).length > 0 && (
|
|
5145
|
+
<div className="flex flex-wrap gap-1.5">
|
|
5146
|
+
{Object.entries(page.componentUsage).filter(([,c]) => (c as number) > 0).map(([name, count]) => (
|
|
5147
|
+
<span key={name} className="text-[11px] px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
|
|
5148
|
+
{name}<span className="text-[10px] text-muted-foreground/60 ml-0.5">{String.fromCharCode(215)}{count as number}</span>
|
|
5149
|
+
</span>
|
|
5150
|
+
))}
|
|
5151
|
+
{page.iconCount > 0 && <span className="text-[11px] px-2 py-0.5 rounded-full bg-muted text-muted-foreground">Icons {String.fromCharCode(215)}{page.iconCount}</span>}
|
|
5152
|
+
</div>
|
|
5153
|
+
)}
|
|
5154
|
+
</div>
|
|
5155
|
+
))}
|
|
5156
|
+
</div>
|
|
5157
|
+
</div>
|
|
5158
|
+
)
|
|
5159
|
+
}
|
|
5160
|
+
`;
|
|
5144
5161
|
}
|
|
5145
5162
|
};
|
|
5146
5163
|
|
|
@@ -5195,7 +5212,6 @@ ${sections}
|
|
|
5195
5212
|
}
|
|
5196
5213
|
`;
|
|
5197
5214
|
}
|
|
5198
|
-
const metadata = this.generateMetadata(def);
|
|
5199
5215
|
return `import { Metadata } from 'next'
|
|
5200
5216
|
${imports}
|
|
5201
5217
|
|
|
@@ -5310,9 +5326,7 @@ ${sections}
|
|
|
5310
5326
|
if (component) {
|
|
5311
5327
|
const componentName = component.name;
|
|
5312
5328
|
const fileName = this.toKebabCase(componentName);
|
|
5313
|
-
imports.push(
|
|
5314
|
-
`import { ${componentName} } from '@/components/ui/${fileName}'`
|
|
5315
|
-
);
|
|
5329
|
+
imports.push(`import { ${componentName} } from '@/components/ui/${fileName}'`);
|
|
5316
5330
|
}
|
|
5317
5331
|
});
|
|
5318
5332
|
return imports.length > 0 ? imports.join("\n") : "";
|
|
@@ -5527,11 +5541,11 @@ ${sections}
|
|
|
5527
5541
|
*/
|
|
5528
5542
|
getContainerClass(layout) {
|
|
5529
5543
|
const layoutClasses = {
|
|
5530
|
-
|
|
5544
|
+
centered: "max-w-4xl mx-auto px-4 py-8",
|
|
5531
5545
|
"sidebar-left": "flex min-h-screen",
|
|
5532
5546
|
"sidebar-right": "flex flex-row-reverse min-h-screen",
|
|
5533
5547
|
"full-width": "w-full",
|
|
5534
|
-
|
|
5548
|
+
grid: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6"
|
|
5535
5549
|
};
|
|
5536
5550
|
return layoutClasses[layout] || "container mx-auto px-4 py-8";
|
|
5537
5551
|
}
|
|
@@ -5548,7 +5562,7 @@ ${sections}
|
|
|
5548
5562
|
/**
|
|
5549
5563
|
* Generate Next.js App Router root layout
|
|
5550
5564
|
*/
|
|
5551
|
-
generateNextJSLayout(
|
|
5565
|
+
generateNextJSLayout(_layout) {
|
|
5552
5566
|
const cssVars = buildCssVariables(this.config);
|
|
5553
5567
|
const navEnabled = this.config.navigation?.enabled;
|
|
5554
5568
|
const navRendered = navEnabled ? "<AppNav />" : "";
|
|
@@ -5611,9 +5625,20 @@ ${navEnabled ? ` ${navRendered}
|
|
|
5611
5625
|
if (!this.config.navigation?.enabled) {
|
|
5612
5626
|
return "";
|
|
5613
5627
|
}
|
|
5614
|
-
const authRoutes = /* @__PURE__ */ new Set([
|
|
5628
|
+
const authRoutes = /* @__PURE__ */ new Set([
|
|
5629
|
+
"/login",
|
|
5630
|
+
"/signin",
|
|
5631
|
+
"/sign-in",
|
|
5632
|
+
"/signup",
|
|
5633
|
+
"/sign-up",
|
|
5634
|
+
"/register",
|
|
5635
|
+
"/forgot-password",
|
|
5636
|
+
"/reset-password"
|
|
5637
|
+
]);
|
|
5615
5638
|
const authCheck = [...authRoutes].map((r) => `pathname === '${r}'`).join(" || ");
|
|
5616
|
-
const visibleItems = this.config.navigation.items.filter(
|
|
5639
|
+
const visibleItems = this.config.navigation.items.filter(
|
|
5640
|
+
(item) => !authRoutes.has(item.route) && !item.route.includes("[")
|
|
5641
|
+
);
|
|
5617
5642
|
const hasMultipleItems = visibleItems.length > 1;
|
|
5618
5643
|
const items = visibleItems.map(
|
|
5619
5644
|
(item) => `<Link href="${item.route}" className={\`text-sm font-medium px-3 py-2 rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring \${pathname === "${item.route}" ? 'bg-muted text-foreground' : 'text-muted-foreground hover:text-foreground hover:bg-muted/50'}\`}>${item.label}</Link>`
|
|
@@ -5686,23 +5711,25 @@ export function AppNav() {
|
|
|
5686
5711
|
return (
|
|
5687
5712
|
<Fragment>
|
|
5688
5713
|
{!hasSharedHeader && (
|
|
5689
|
-
<nav className="sticky top-0 z-50
|
|
5690
|
-
<div className="flex items-center
|
|
5691
|
-
<
|
|
5692
|
-
<
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
<
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5714
|
+
<nav className="sticky top-0 z-50 shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
5715
|
+
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
|
5716
|
+
<div className="flex items-center gap-6">
|
|
5717
|
+
<Link href="/" className="flex items-center gap-2.5 text-sm font-semibold text-foreground hover:text-foreground/90 transition-colors shrink-0">
|
|
5718
|
+
<CoherentLogo size={20} className="text-primary" />
|
|
5719
|
+
<span>Coherent Design Method</span>
|
|
5720
|
+
</Link>
|
|
5721
|
+
${navItemsBlock}
|
|
5722
|
+
</div>
|
|
5723
|
+
<div className="flex items-center gap-1">
|
|
5724
|
+
<ThemeToggle />
|
|
5725
|
+
<Link
|
|
5726
|
+
href="/design-system"
|
|
5727
|
+
className="flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
|
|
5728
|
+
>
|
|
5729
|
+
<CoherentLogo size={16} />
|
|
5730
|
+
Design System
|
|
5731
|
+
</Link>
|
|
5732
|
+
</div>
|
|
5706
5733
|
</div>
|
|
5707
5734
|
</nav>
|
|
5708
5735
|
)}
|
|
@@ -5722,7 +5749,7 @@ export function AppNav() {
|
|
|
5722
5749
|
/**
|
|
5723
5750
|
* Generate React SPA root layout
|
|
5724
5751
|
*/
|
|
5725
|
-
generateReactSPALayout(
|
|
5752
|
+
generateReactSPALayout(_layout) {
|
|
5726
5753
|
const navigation = this.config.navigation?.enabled ? this.generateNavigation("react-router") : "";
|
|
5727
5754
|
return `import { Outlet } from 'react-router-dom'
|
|
5728
5755
|
import './globals.css'
|
|
@@ -6098,8 +6125,7 @@ function getDefaultTemplate(componentName, type, name) {
|
|
|
6098
6125
|
const safeName = componentName.replace(/[^a-zA-Z0-9]/g, "") || "Block";
|
|
6099
6126
|
const lower = name.toLowerCase();
|
|
6100
6127
|
if (lower.includes("footer")) return FOOTER_PLACEHOLDER(safeName);
|
|
6101
|
-
if (type === "layout" || lower.includes("header") || lower.includes("nav"))
|
|
6102
|
-
return LAYOUT_PLACEHOLDER(safeName);
|
|
6128
|
+
if (type === "layout" || lower.includes("header") || lower.includes("nav")) return LAYOUT_PLACEHOLDER(safeName);
|
|
6103
6129
|
return SECTION_PLACEHOLDER(safeName);
|
|
6104
6130
|
}
|
|
6105
6131
|
async function generateSharedComponent(projectRoot, input) {
|
|
@@ -6161,9 +6187,7 @@ async function integrateSharedLayoutIntoRootLayout(projectRoot) {
|
|
|
6161
6187
|
else headers.push(pascal);
|
|
6162
6188
|
}
|
|
6163
6189
|
if (headers.length === 0 && footers.length === 0) return false;
|
|
6164
|
-
const importLines = layoutComponents.map(
|
|
6165
|
-
(e) => `import { ${toPascalCase(e.name)} } from '${getImportPath(e.name)}'`
|
|
6166
|
-
);
|
|
6190
|
+
const importLines = layoutComponents.map((e) => `import { ${toPascalCase(e.name)} } from '${getImportPath(e.name)}'`);
|
|
6167
6191
|
let result = content;
|
|
6168
6192
|
const globalsImport = "import './globals.css'";
|
|
6169
6193
|
const globalsIdx = result.indexOf(globalsImport);
|
|
@@ -6174,6 +6198,10 @@ async function integrateSharedLayoutIntoRootLayout(projectRoot) {
|
|
|
6174
6198
|
result = result.slice(0, lineEnd) + line + "\n" + result.slice(lineEnd);
|
|
6175
6199
|
}
|
|
6176
6200
|
}
|
|
6201
|
+
if (headers.length > 0) {
|
|
6202
|
+
result = result.replace(/\s*<AppNav\s*\/>\s*\n?/g, "\n");
|
|
6203
|
+
result = result.replace(/^import\s.*['"]\.\/AppNav['"]\s*;?\n?/gm, "");
|
|
6204
|
+
}
|
|
6177
6205
|
if (headers.length > 0) {
|
|
6178
6206
|
const bodyOpen = result.indexOf("<body");
|
|
6179
6207
|
if (bodyOpen === -1) return false;
|
|
@@ -6268,12 +6296,7 @@ var ProjectScaffolder = class _ProjectScaffolder {
|
|
|
6268
6296
|
* Create directory structure
|
|
6269
6297
|
*/
|
|
6270
6298
|
async createDirectories() {
|
|
6271
|
-
const dirs = [
|
|
6272
|
-
"app",
|
|
6273
|
-
"components",
|
|
6274
|
-
"lib",
|
|
6275
|
-
"public"
|
|
6276
|
-
];
|
|
6299
|
+
const dirs = ["app", "components", "lib", "public"];
|
|
6277
6300
|
for (const dir of dirs) {
|
|
6278
6301
|
const fullPath = join4(this.projectRoot, dir);
|
|
6279
6302
|
if (!existsSync3(fullPath)) {
|
|
@@ -6550,7 +6573,9 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
6550
6573
|
}
|
|
6551
6574
|
}
|
|
6552
6575
|
async generateDefaultPages() {
|
|
6553
|
-
await this.writeFile(
|
|
6576
|
+
await this.writeFile(
|
|
6577
|
+
"app/not-found.tsx",
|
|
6578
|
+
`import Link from 'next/link'
|
|
6554
6579
|
|
|
6555
6580
|
export default function NotFound() {
|
|
6556
6581
|
return (
|
|
@@ -6566,8 +6591,11 @@ export default function NotFound() {
|
|
|
6566
6591
|
</main>
|
|
6567
6592
|
)
|
|
6568
6593
|
}
|
|
6569
|
-
`
|
|
6570
|
-
|
|
6594
|
+
`
|
|
6595
|
+
);
|
|
6596
|
+
await this.writeFile(
|
|
6597
|
+
"app/loading.tsx",
|
|
6598
|
+
`export default function Loading() {
|
|
6571
6599
|
return (
|
|
6572
6600
|
<div className="flex min-h-[60vh] items-center justify-center">
|
|
6573
6601
|
<div className="space-y-4 w-full max-w-md px-4">
|
|
@@ -6582,8 +6610,11 @@ export default function NotFound() {
|
|
|
6582
6610
|
</div>
|
|
6583
6611
|
)
|
|
6584
6612
|
}
|
|
6585
|
-
`
|
|
6586
|
-
|
|
6613
|
+
`
|
|
6614
|
+
);
|
|
6615
|
+
await this.writeFile(
|
|
6616
|
+
"app/error.tsx",
|
|
6617
|
+
`'use client'
|
|
6587
6618
|
|
|
6588
6619
|
export default function ErrorPage({
|
|
6589
6620
|
error,
|
|
@@ -6607,35 +6638,22 @@ export default function ErrorPage({
|
|
|
6607
6638
|
</main>
|
|
6608
6639
|
)
|
|
6609
6640
|
}
|
|
6610
|
-
`
|
|
6641
|
+
`
|
|
6642
|
+
);
|
|
6611
6643
|
}
|
|
6612
6644
|
async generateFavicon() {
|
|
6613
|
-
const
|
|
6645
|
+
const logoPath = "M10 4C10.5523 4 11 4.44772 11 5V13H19C19.5523 13 20 13.4477 20 14V20.4287C19.9999 22.401 18.401 23.9999 16.4287 24H3.57129C1.59895 23.9999 7.5245e-05 22.401 0 20.4287V7.57129C7.53742e-05 5.59895 1.59895 4.00008 3.57129 4H10ZM2 20.4287C2.00008 21.2965 2.70352 21.9999 3.57129 22H9V15H2V20.4287ZM11 22H16.4287C17.2965 21.9999 17.9999 21.2965 18 20.4287V15H11V22ZM3.57129 6C2.70352 6.00008 2.00008 6.70352 2 7.57129V13H9V6H3.57129ZM20.5 0C22.433 0 24 1.567 24 3.5V9.90039C23.9998 10.5076 23.5076 10.9998 22.9004 11H14.0996C13.4924 10.9998 13.0002 10.5076 13 9.90039V1.09961C13.0002 0.492409 13.4924 0.000211011 14.0996 0H20.5ZM15 9H22V3.5C22 2.67157 21.3284 2 20.5 2H15V9Z";
|
|
6646
|
+
const logoSvg = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6647
|
+
<path d="${logoPath}" fill="#FFA500"/>
|
|
6648
|
+
</svg>`;
|
|
6649
|
+
await this.writeFile("public/coherent-logo.svg", logoSvg);
|
|
6614
6650
|
const faviconSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
6615
|
-
<rect width="32" height="32" rx="8" fill="
|
|
6651
|
+
<rect width="32" height="32" rx="8" fill="#3B82F6"/>
|
|
6616
6652
|
<g transform="translate(4, 4)">
|
|
6617
|
-
<
|
|
6618
|
-
<path d="M10 4C10.5523 4 11 4.44772 11 5V13H19C19.5523 13 20 13.4477 20 14V20.4287C19.9999 22.401 18.401 23.9999 16.4287 24H3.57129C1.59895 23.9999 7.5245e-05 22.401 0 20.4287V7.57129C7.53742e-05 5.59895 1.59895 4.00008 3.57129 4H10ZM2 20.4287C2.00008 21.2965 2.70352 21.9999 3.57129 22H9V15H2V20.4287ZM11 22H16.4287C17.2965 21.9999 17.9999 21.2965 18 20.4287V15H11V22ZM3.57129 6C2.70352 6.00008 2.00008 6.70352 2 7.57129V13H9V6H3.57129ZM20.5 0C22.433 0 24 1.567 24 3.5V9.90039C23.9998 10.5076 23.5076 10.9998 22.9004 11H14.0996C13.4924 10.9998 13.0002 10.5076 13 9.90039V1.09961C13.0002 0.492409 13.4924 0.000211011 14.0996 0H20.5ZM15 9H22V3.5C22 2.67157 21.3284 2 20.5 2H15V9Z" fill="white"/>
|
|
6619
|
-
</g>
|
|
6620
|
-
<defs>
|
|
6621
|
-
<clipPath id="clip0">
|
|
6622
|
-
<rect width="24" height="24" fill="white"/>
|
|
6623
|
-
</clipPath>
|
|
6624
|
-
</defs>
|
|
6653
|
+
<path d="${logoPath}" fill="white"/>
|
|
6625
6654
|
</g>
|
|
6626
6655
|
</svg>`;
|
|
6627
6656
|
await this.writeFile("public/favicon.svg", faviconSvg);
|
|
6628
|
-
const logoSvg = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6629
|
-
<g clip-path="url(#clip0_23738_31)">
|
|
6630
|
-
<path d="M10 4C10.5523 4 11 4.44772 11 5V13H19C19.5523 13 20 13.4477 20 14V20.4287C19.9999 22.401 18.401 23.9999 16.4287 24H3.57129C1.59895 23.9999 7.5245e-05 22.401 0 20.4287V7.57129C7.53742e-05 5.59895 1.59895 4.00008 3.57129 4H10ZM2 20.4287C2.00008 21.2965 2.70352 21.9999 3.57129 22H9V15H2V20.4287ZM11 22H16.4287C17.2965 21.9999 17.9999 21.2965 18 20.4287V15H11V22ZM3.57129 6C2.70352 6.00008 2.00008 6.70352 2 7.57129V13H9V6H3.57129ZM20.5 0C22.433 0 24 1.567 24 3.5V9.90039C23.9998 10.5076 23.5076 10.9998 22.9004 11H14.0996C13.4924 10.9998 13.0002 10.5076 13 9.90039V1.09961C13.0002 0.492409 13.4924 0.000211011 14.0996 0H20.5ZM15 9H22V3.5C22 2.67157 21.3284 2 20.5 2H15V9Z" fill="#FFA500"/>
|
|
6631
|
-
</g>
|
|
6632
|
-
<defs>
|
|
6633
|
-
<clipPath id="clip0_23738_31">
|
|
6634
|
-
<rect width="24" height="24" fill="white"/>
|
|
6635
|
-
</clipPath>
|
|
6636
|
-
</defs>
|
|
6637
|
-
</svg>`;
|
|
6638
|
-
await this.writeFile("public/coherent-logo.svg", logoSvg);
|
|
6639
6657
|
}
|
|
6640
6658
|
/**
|
|
6641
6659
|
* Generate .gitignore
|
|
@@ -7730,7 +7748,9 @@ ${activitySection}
|
|
|
7730
7748
|
function onboardingTemplate(content, options) {
|
|
7731
7749
|
const { title, description, steps, totalSteps } = content;
|
|
7732
7750
|
const { pageName } = options;
|
|
7733
|
-
const stepBars = steps.map(
|
|
7751
|
+
const stepBars = steps.map(
|
|
7752
|
+
(_, _i) => " <div key={_i} className={`flex-1 h-2 rounded-full ${_i <= step ? 'bg-primary' : 'bg-muted'}`} />"
|
|
7753
|
+
).join("\n");
|
|
7734
7754
|
const stepContent = steps.map(
|
|
7735
7755
|
(s, i) => ` {step === ${i} && (
|
|
7736
7756
|
<div className="${D.card} p-6">
|
|
@@ -7931,7 +7951,7 @@ var FigmaClient = class {
|
|
|
7931
7951
|
async fetchWithRetry(url, retries = 2) {
|
|
7932
7952
|
const res = await fetch(url, {
|
|
7933
7953
|
method: "GET",
|
|
7934
|
-
headers: { "X-Figma-Token": this.token,
|
|
7954
|
+
headers: { "X-Figma-Token": this.token, Accept: "application/json" }
|
|
7935
7955
|
});
|
|
7936
7956
|
if (res.status === 429 && retries > 0) {
|
|
7937
7957
|
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "", 10);
|
|
@@ -8142,8 +8162,7 @@ function extractTokensFromFigma(data) {
|
|
|
8142
8162
|
for (const ts of textStyles) {
|
|
8143
8163
|
const n = normalizeName(ts.name);
|
|
8144
8164
|
if (ts.fontSize != null) {
|
|
8145
|
-
if (/^(h1|heading\s*1|display|title\s*large)$/.test(n))
|
|
8146
|
-
fontSize["2xl"] = `${ts.fontSize}px`;
|
|
8165
|
+
if (/^(h1|heading\s*1|display|title\s*large)$/.test(n)) fontSize["2xl"] = `${ts.fontSize}px`;
|
|
8147
8166
|
else if (/^(h2|heading\s*2)$/.test(n)) fontSize["xl"] = `${ts.fontSize}px`;
|
|
8148
8167
|
else if (/^(body|paragraph|text)$/.test(n)) fontSize.base = `${ts.fontSize}px`;
|
|
8149
8168
|
else if (/^(small|caption)$/.test(n)) fontSize.sm = `${ts.fontSize}px`;
|
|
@@ -8599,9 +8618,11 @@ export {
|
|
|
8599
8618
|
TailwindConfigGenerator,
|
|
8600
8619
|
TypographyTokenSchema,
|
|
8601
8620
|
allocateNextCid,
|
|
8621
|
+
atomicWriteFile,
|
|
8602
8622
|
buildCssVariables,
|
|
8603
8623
|
componentExists,
|
|
8604
8624
|
createEntry,
|
|
8625
|
+
extractConfigObject,
|
|
8605
8626
|
extractTokensFromFigma,
|
|
8606
8627
|
figmaComponentNameToBaseId,
|
|
8607
8628
|
figmaRgbaToHex,
|
package/package.json
CHANGED