@getcoherent/core 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.d.ts +16 -5
- package/dist/index.js +248 -237
- package/package.json +8 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sergei Kovtun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
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,77 @@ 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
|
-
const
|
|
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
|
-
lines.push("}");
|
|
5143
|
-
return lines.join("\n");
|
|
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\\s*;?\\s*$/, '}')
|
|
5096
|
+
const json = JSON.parse(jsonStr)
|
|
5097
|
+
return (json.pages || []).map((p: any) => ({
|
|
5098
|
+
name: p.name || p.id,
|
|
5099
|
+
route: p.route || '/' + p.id,
|
|
5100
|
+
sections: p.pageAnalysis?.sections?.map((s: any) => s.name) || [],
|
|
5101
|
+
componentUsage: p.pageAnalysis?.componentUsage || {},
|
|
5102
|
+
iconCount: p.pageAnalysis?.iconCount ?? 0,
|
|
5103
|
+
layoutPattern: p.pageAnalysis?.layoutPattern || null,
|
|
5104
|
+
hasForm: p.pageAnalysis?.hasForm ?? false,
|
|
5105
|
+
}))
|
|
5106
|
+
} catch {
|
|
5107
|
+
return []
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
|
|
5111
|
+
export default function SitemapPage() {
|
|
5112
|
+
const pages = loadPages()
|
|
5113
|
+
|
|
5114
|
+
return (
|
|
5115
|
+
<div className="space-y-8">
|
|
5116
|
+
<div>
|
|
5117
|
+
<h1 className="text-3xl font-bold tracking-tight">Sitemap</h1>
|
|
5118
|
+
<p className="text-muted-foreground mt-1">All pages, sections, and component usage</p>
|
|
5119
|
+
</div>
|
|
5120
|
+
<div className="space-y-4">
|
|
5121
|
+
{pages.map((page: any) => (
|
|
5122
|
+
<div key={page.route} className="rounded-xl border p-4 space-y-3">
|
|
5123
|
+
<div className="flex items-center gap-3">
|
|
5124
|
+
<Link href={page.route} className="font-semibold text-sm hover:text-primary transition-colors">
|
|
5125
|
+
{page.name}
|
|
5126
|
+
</Link>
|
|
5127
|
+
<span className="text-xs text-muted-foreground font-mono">{page.route}</span>
|
|
5128
|
+
{page.layoutPattern && <span className="text-[10px] px-1.5 py-0.5 rounded bg-muted text-muted-foreground">{page.layoutPattern}</span>}
|
|
5129
|
+
{page.hasForm && <span className="text-[10px] px-1.5 py-0.5 rounded bg-primary/10 text-primary">form</span>}
|
|
5130
|
+
</div>
|
|
5131
|
+
{page.sections.length > 0 && (
|
|
5132
|
+
<div className="flex flex-wrap gap-1.5">
|
|
5133
|
+
{page.sections.map((s: string) => (
|
|
5134
|
+
<span key={s} className="text-[11px] px-2 py-0.5 rounded-full border text-muted-foreground">{s}</span>
|
|
5135
|
+
))}
|
|
5136
|
+
</div>
|
|
5137
|
+
)}
|
|
5138
|
+
{Object.keys(page.componentUsage).length > 0 && (
|
|
5139
|
+
<div className="flex flex-wrap gap-1.5">
|
|
5140
|
+
{Object.entries(page.componentUsage).filter(([,c]) => (c as number) > 0).map(([name, count]) => (
|
|
5141
|
+
<span key={name} className="text-[11px] px-2 py-0.5 rounded-full bg-muted text-muted-foreground">
|
|
5142
|
+
{name}<span className="text-[10px] text-muted-foreground/60 ml-0.5">{String.fromCharCode(215)}{count as number}</span>
|
|
5143
|
+
</span>
|
|
5144
|
+
))}
|
|
5145
|
+
{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>}
|
|
5146
|
+
</div>
|
|
5147
|
+
)}
|
|
5148
|
+
</div>
|
|
5149
|
+
))}
|
|
5150
|
+
</div>
|
|
5151
|
+
</div>
|
|
5152
|
+
)
|
|
5153
|
+
}
|
|
5154
|
+
`;
|
|
5144
5155
|
}
|
|
5145
5156
|
};
|
|
5146
5157
|
|
|
@@ -5195,7 +5206,6 @@ ${sections}
|
|
|
5195
5206
|
}
|
|
5196
5207
|
`;
|
|
5197
5208
|
}
|
|
5198
|
-
const metadata = this.generateMetadata(def);
|
|
5199
5209
|
return `import { Metadata } from 'next'
|
|
5200
5210
|
${imports}
|
|
5201
5211
|
|
|
@@ -5310,9 +5320,7 @@ ${sections}
|
|
|
5310
5320
|
if (component) {
|
|
5311
5321
|
const componentName = component.name;
|
|
5312
5322
|
const fileName = this.toKebabCase(componentName);
|
|
5313
|
-
imports.push(
|
|
5314
|
-
`import { ${componentName} } from '@/components/ui/${fileName}'`
|
|
5315
|
-
);
|
|
5323
|
+
imports.push(`import { ${componentName} } from '@/components/ui/${fileName}'`);
|
|
5316
5324
|
}
|
|
5317
5325
|
});
|
|
5318
5326
|
return imports.length > 0 ? imports.join("\n") : "";
|
|
@@ -5527,11 +5535,11 @@ ${sections}
|
|
|
5527
5535
|
*/
|
|
5528
5536
|
getContainerClass(layout) {
|
|
5529
5537
|
const layoutClasses = {
|
|
5530
|
-
|
|
5538
|
+
centered: "max-w-4xl mx-auto px-4 py-8",
|
|
5531
5539
|
"sidebar-left": "flex min-h-screen",
|
|
5532
5540
|
"sidebar-right": "flex flex-row-reverse min-h-screen",
|
|
5533
5541
|
"full-width": "w-full",
|
|
5534
|
-
|
|
5542
|
+
grid: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6"
|
|
5535
5543
|
};
|
|
5536
5544
|
return layoutClasses[layout] || "container mx-auto px-4 py-8";
|
|
5537
5545
|
}
|
|
@@ -5548,7 +5556,7 @@ ${sections}
|
|
|
5548
5556
|
/**
|
|
5549
5557
|
* Generate Next.js App Router root layout
|
|
5550
5558
|
*/
|
|
5551
|
-
generateNextJSLayout(
|
|
5559
|
+
generateNextJSLayout(_layout) {
|
|
5552
5560
|
const cssVars = buildCssVariables(this.config);
|
|
5553
5561
|
const navEnabled = this.config.navigation?.enabled;
|
|
5554
5562
|
const navRendered = navEnabled ? "<AppNav />" : "";
|
|
@@ -5611,9 +5619,20 @@ ${navEnabled ? ` ${navRendered}
|
|
|
5611
5619
|
if (!this.config.navigation?.enabled) {
|
|
5612
5620
|
return "";
|
|
5613
5621
|
}
|
|
5614
|
-
const authRoutes = /* @__PURE__ */ new Set([
|
|
5622
|
+
const authRoutes = /* @__PURE__ */ new Set([
|
|
5623
|
+
"/login",
|
|
5624
|
+
"/signin",
|
|
5625
|
+
"/sign-in",
|
|
5626
|
+
"/signup",
|
|
5627
|
+
"/sign-up",
|
|
5628
|
+
"/register",
|
|
5629
|
+
"/forgot-password",
|
|
5630
|
+
"/reset-password"
|
|
5631
|
+
]);
|
|
5615
5632
|
const authCheck = [...authRoutes].map((r) => `pathname === '${r}'`).join(" || ");
|
|
5616
|
-
const visibleItems = this.config.navigation.items.filter(
|
|
5633
|
+
const visibleItems = this.config.navigation.items.filter(
|
|
5634
|
+
(item) => !authRoutes.has(item.route) && !item.route.includes("[")
|
|
5635
|
+
);
|
|
5617
5636
|
const hasMultipleItems = visibleItems.length > 1;
|
|
5618
5637
|
const items = visibleItems.map(
|
|
5619
5638
|
(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 +5705,25 @@ export function AppNav() {
|
|
|
5686
5705
|
return (
|
|
5687
5706
|
<Fragment>
|
|
5688
5707
|
{!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
|
-
|
|
5708
|
+
<nav className="sticky top-0 z-50 shrink-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
|
5709
|
+
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
|
|
5710
|
+
<div className="flex items-center gap-6">
|
|
5711
|
+
<Link href="/" className="flex items-center gap-2.5 text-sm font-semibold text-foreground hover:text-foreground/90 transition-colors shrink-0">
|
|
5712
|
+
<CoherentLogo size={20} className="text-primary" />
|
|
5713
|
+
<span>Coherent Design Method</span>
|
|
5714
|
+
</Link>
|
|
5715
|
+
${navItemsBlock}
|
|
5716
|
+
</div>
|
|
5717
|
+
<div className="flex items-center gap-1">
|
|
5718
|
+
<ThemeToggle />
|
|
5719
|
+
<Link
|
|
5720
|
+
href="/design-system"
|
|
5721
|
+
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"
|
|
5722
|
+
>
|
|
5723
|
+
<CoherentLogo size={16} />
|
|
5724
|
+
Design System
|
|
5725
|
+
</Link>
|
|
5726
|
+
</div>
|
|
5706
5727
|
</div>
|
|
5707
5728
|
</nav>
|
|
5708
5729
|
)}
|
|
@@ -5722,7 +5743,7 @@ export function AppNav() {
|
|
|
5722
5743
|
/**
|
|
5723
5744
|
* Generate React SPA root layout
|
|
5724
5745
|
*/
|
|
5725
|
-
generateReactSPALayout(
|
|
5746
|
+
generateReactSPALayout(_layout) {
|
|
5726
5747
|
const navigation = this.config.navigation?.enabled ? this.generateNavigation("react-router") : "";
|
|
5727
5748
|
return `import { Outlet } from 'react-router-dom'
|
|
5728
5749
|
import './globals.css'
|
|
@@ -6098,8 +6119,7 @@ function getDefaultTemplate(componentName, type, name) {
|
|
|
6098
6119
|
const safeName = componentName.replace(/[^a-zA-Z0-9]/g, "") || "Block";
|
|
6099
6120
|
const lower = name.toLowerCase();
|
|
6100
6121
|
if (lower.includes("footer")) return FOOTER_PLACEHOLDER(safeName);
|
|
6101
|
-
if (type === "layout" || lower.includes("header") || lower.includes("nav"))
|
|
6102
|
-
return LAYOUT_PLACEHOLDER(safeName);
|
|
6122
|
+
if (type === "layout" || lower.includes("header") || lower.includes("nav")) return LAYOUT_PLACEHOLDER(safeName);
|
|
6103
6123
|
return SECTION_PLACEHOLDER(safeName);
|
|
6104
6124
|
}
|
|
6105
6125
|
async function generateSharedComponent(projectRoot, input) {
|
|
@@ -6161,9 +6181,7 @@ async function integrateSharedLayoutIntoRootLayout(projectRoot) {
|
|
|
6161
6181
|
else headers.push(pascal);
|
|
6162
6182
|
}
|
|
6163
6183
|
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
|
-
);
|
|
6184
|
+
const importLines = layoutComponents.map((e) => `import { ${toPascalCase(e.name)} } from '${getImportPath(e.name)}'`);
|
|
6167
6185
|
let result = content;
|
|
6168
6186
|
const globalsImport = "import './globals.css'";
|
|
6169
6187
|
const globalsIdx = result.indexOf(globalsImport);
|
|
@@ -6268,12 +6286,7 @@ var ProjectScaffolder = class _ProjectScaffolder {
|
|
|
6268
6286
|
* Create directory structure
|
|
6269
6287
|
*/
|
|
6270
6288
|
async createDirectories() {
|
|
6271
|
-
const dirs = [
|
|
6272
|
-
"app",
|
|
6273
|
-
"components",
|
|
6274
|
-
"lib",
|
|
6275
|
-
"public"
|
|
6276
|
-
];
|
|
6289
|
+
const dirs = ["app", "components", "lib", "public"];
|
|
6277
6290
|
for (const dir of dirs) {
|
|
6278
6291
|
const fullPath = join4(this.projectRoot, dir);
|
|
6279
6292
|
if (!existsSync3(fullPath)) {
|
|
@@ -6550,7 +6563,9 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
6550
6563
|
}
|
|
6551
6564
|
}
|
|
6552
6565
|
async generateDefaultPages() {
|
|
6553
|
-
await this.writeFile(
|
|
6566
|
+
await this.writeFile(
|
|
6567
|
+
"app/not-found.tsx",
|
|
6568
|
+
`import Link from 'next/link'
|
|
6554
6569
|
|
|
6555
6570
|
export default function NotFound() {
|
|
6556
6571
|
return (
|
|
@@ -6566,8 +6581,11 @@ export default function NotFound() {
|
|
|
6566
6581
|
</main>
|
|
6567
6582
|
)
|
|
6568
6583
|
}
|
|
6569
|
-
`
|
|
6570
|
-
|
|
6584
|
+
`
|
|
6585
|
+
);
|
|
6586
|
+
await this.writeFile(
|
|
6587
|
+
"app/loading.tsx",
|
|
6588
|
+
`export default function Loading() {
|
|
6571
6589
|
return (
|
|
6572
6590
|
<div className="flex min-h-[60vh] items-center justify-center">
|
|
6573
6591
|
<div className="space-y-4 w-full max-w-md px-4">
|
|
@@ -6582,8 +6600,11 @@ export default function NotFound() {
|
|
|
6582
6600
|
</div>
|
|
6583
6601
|
)
|
|
6584
6602
|
}
|
|
6585
|
-
`
|
|
6586
|
-
|
|
6603
|
+
`
|
|
6604
|
+
);
|
|
6605
|
+
await this.writeFile(
|
|
6606
|
+
"app/error.tsx",
|
|
6607
|
+
`'use client'
|
|
6587
6608
|
|
|
6588
6609
|
export default function ErrorPage({
|
|
6589
6610
|
error,
|
|
@@ -6607,35 +6628,22 @@ export default function ErrorPage({
|
|
|
6607
6628
|
</main>
|
|
6608
6629
|
)
|
|
6609
6630
|
}
|
|
6610
|
-
`
|
|
6631
|
+
`
|
|
6632
|
+
);
|
|
6611
6633
|
}
|
|
6612
6634
|
async generateFavicon() {
|
|
6613
|
-
const
|
|
6635
|
+
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";
|
|
6636
|
+
const logoSvg = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
6637
|
+
<path d="${logoPath}" fill="#FFA500"/>
|
|
6638
|
+
</svg>`;
|
|
6639
|
+
await this.writeFile("public/coherent-logo.svg", logoSvg);
|
|
6614
6640
|
const faviconSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
6615
|
-
<rect width="32" height="32" rx="8" fill="
|
|
6641
|
+
<rect width="32" height="32" rx="8" fill="#3B82F6"/>
|
|
6616
6642
|
<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>
|
|
6643
|
+
<path d="${logoPath}" fill="white"/>
|
|
6625
6644
|
</g>
|
|
6626
6645
|
</svg>`;
|
|
6627
6646
|
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
6647
|
}
|
|
6640
6648
|
/**
|
|
6641
6649
|
* Generate .gitignore
|
|
@@ -7730,7 +7738,9 @@ ${activitySection}
|
|
|
7730
7738
|
function onboardingTemplate(content, options) {
|
|
7731
7739
|
const { title, description, steps, totalSteps } = content;
|
|
7732
7740
|
const { pageName } = options;
|
|
7733
|
-
const stepBars = steps.map(
|
|
7741
|
+
const stepBars = steps.map(
|
|
7742
|
+
(_, _i) => " <div key={_i} className={`flex-1 h-2 rounded-full ${_i <= step ? 'bg-primary' : 'bg-muted'}`} />"
|
|
7743
|
+
).join("\n");
|
|
7734
7744
|
const stepContent = steps.map(
|
|
7735
7745
|
(s, i) => ` {step === ${i} && (
|
|
7736
7746
|
<div className="${D.card} p-6">
|
|
@@ -7931,7 +7941,7 @@ var FigmaClient = class {
|
|
|
7931
7941
|
async fetchWithRetry(url, retries = 2) {
|
|
7932
7942
|
const res = await fetch(url, {
|
|
7933
7943
|
method: "GET",
|
|
7934
|
-
headers: { "X-Figma-Token": this.token,
|
|
7944
|
+
headers: { "X-Figma-Token": this.token, Accept: "application/json" }
|
|
7935
7945
|
});
|
|
7936
7946
|
if (res.status === 429 && retries > 0) {
|
|
7937
7947
|
const retryAfter = parseInt(res.headers.get("Retry-After") ?? "", 10);
|
|
@@ -8142,8 +8152,7 @@ function extractTokensFromFigma(data) {
|
|
|
8142
8152
|
for (const ts of textStyles) {
|
|
8143
8153
|
const n = normalizeName(ts.name);
|
|
8144
8154
|
if (ts.fontSize != null) {
|
|
8145
|
-
if (/^(h1|heading\s*1|display|title\s*large)$/.test(n))
|
|
8146
|
-
fontSize["2xl"] = `${ts.fontSize}px`;
|
|
8155
|
+
if (/^(h1|heading\s*1|display|title\s*large)$/.test(n)) fontSize["2xl"] = `${ts.fontSize}px`;
|
|
8147
8156
|
else if (/^(h2|heading\s*2)$/.test(n)) fontSize["xl"] = `${ts.fontSize}px`;
|
|
8148
8157
|
else if (/^(body|paragraph|text)$/.test(n)) fontSize.base = `${ts.fontSize}px`;
|
|
8149
8158
|
else if (/^(small|caption)$/.test(n)) fontSize.sm = `${ts.fontSize}px`;
|
|
@@ -8599,9 +8608,11 @@ export {
|
|
|
8599
8608
|
TailwindConfigGenerator,
|
|
8600
8609
|
TypographyTokenSchema,
|
|
8601
8610
|
allocateNextCid,
|
|
8611
|
+
atomicWriteFile,
|
|
8602
8612
|
buildCssVariables,
|
|
8603
8613
|
componentExists,
|
|
8604
8614
|
createEntry,
|
|
8615
|
+
extractConfigObject,
|
|
8605
8616
|
extractTokensFromFigma,
|
|
8606
8617
|
figmaComponentNameToBaseId,
|
|
8607
8618
|
figmaRgbaToHex,
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.2.0",
|
|
7
7
|
"description": "Core design system engine for Coherent",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"main": "./dist/index.js",
|
|
@@ -32,12 +32,6 @@
|
|
|
32
32
|
],
|
|
33
33
|
"author": "Coherent Design Method",
|
|
34
34
|
"license": "MIT",
|
|
35
|
-
"scripts": {
|
|
36
|
-
"dev": "tsup --watch",
|
|
37
|
-
"build": "tsup",
|
|
38
|
-
"typecheck": "tsc --noEmit",
|
|
39
|
-
"test": "vitest"
|
|
40
|
-
},
|
|
41
35
|
"dependencies": {
|
|
42
36
|
"handlebars": "^4.7.8",
|
|
43
37
|
"zod": "^3.22.4"
|
|
@@ -47,6 +41,11 @@
|
|
|
47
41
|
"tsup": "^8.0.1",
|
|
48
42
|
"typescript": "^5.3.3",
|
|
49
43
|
"vitest": "^1.2.1"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsup --watch",
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"test": "vitest"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
51
|
+
}
|