@nghitrum/dsforge 0.1.5-alpha.11 → 0.1.5-alpha.13
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/README.md +0 -1
- package/dist/{chunk-YUPXTQZ5.js → chunk-5YT3VNE6.js} +0 -26
- package/dist/cli/index.js +79 -106
- package/dist/{html-XGJ22SXB.js → html-4DD6GOHE.js} +19 -61
- package/dist/index.js +54 -91
- package/package.json +1 -1
- /package/{LICENSE → LICENSE.md} +0 -0
package/README.md
CHANGED
|
@@ -194,7 +194,6 @@ Run `dsforge` with no arguments for an interactive menu.
|
|
|
194
194
|
## Coming next
|
|
195
195
|
|
|
196
196
|
- Additional components: Modal, Table, Tooltip, DatePicker
|
|
197
|
-
- **Pro**: AI-assisted token generation from brand guidelines or a Figma file
|
|
198
197
|
- Figma Variables API sync
|
|
199
198
|
- CI integration — fail builds on governance violations
|
|
200
199
|
|
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
// src/lib/license.ts
|
|
2
|
-
import { readFileSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
function readKeyFromDotEnv() {
|
|
5
|
-
try {
|
|
6
|
-
const content = readFileSync(join(process.cwd(), ".env"), "utf8");
|
|
7
|
-
for (const raw of content.split("\n")) {
|
|
8
|
-
const line = raw.trim();
|
|
9
|
-
if (!line || line.startsWith("#")) continue;
|
|
10
|
-
const eq = line.indexOf("=");
|
|
11
|
-
if (eq === -1) continue;
|
|
12
|
-
const key = line.slice(0, eq).trim();
|
|
13
|
-
if (key !== "DSFORGE_KEY") continue;
|
|
14
|
-
const val = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
|
|
15
|
-
return val || void 0;
|
|
16
|
-
}
|
|
17
|
-
} catch {
|
|
18
|
-
}
|
|
19
|
-
return void 0;
|
|
20
|
-
}
|
|
21
|
-
function isProUnlocked() {
|
|
22
|
-
const key = process.env["DSFORGE_KEY"] ?? readKeyFromDotEnv();
|
|
23
|
-
return typeof key === "string" && key.length > 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
1
|
// src/presets/index.ts
|
|
27
2
|
var PRESETS = [
|
|
28
3
|
"compact",
|
|
@@ -107,7 +82,6 @@ function applyPreset(config, preset) {
|
|
|
107
82
|
}
|
|
108
83
|
|
|
109
84
|
export {
|
|
110
|
-
isProUnlocked,
|
|
111
85
|
PRESETS,
|
|
112
86
|
SPACING_PRESETS,
|
|
113
87
|
RADIUS_PRESETS,
|
package/dist/cli/index.js
CHANGED
|
@@ -11,9 +11,8 @@ import {
|
|
|
11
11
|
RADIUS_PRESETS,
|
|
12
12
|
SPACING_PRESETS,
|
|
13
13
|
applyPreset,
|
|
14
|
-
buildSemanticSpacing
|
|
15
|
-
|
|
16
|
-
} from "../chunk-YUPXTQZ5.js";
|
|
14
|
+
buildSemanticSpacing
|
|
15
|
+
} from "../chunk-5YT3VNE6.js";
|
|
17
16
|
|
|
18
17
|
// src/cli/index.ts
|
|
19
18
|
import { program } from "commander";
|
|
@@ -738,15 +737,7 @@ async function runInit(cwd, options) {
|
|
|
738
737
|
}
|
|
739
738
|
const name = rawName.replace(/\s+/g, "-").toLowerCase();
|
|
740
739
|
let preset;
|
|
741
|
-
if (
|
|
742
|
-
if (options.preset && options.preset !== "comfortable") {
|
|
743
|
-
logger.hint(
|
|
744
|
-
`Preset "${options.preset}" requires dsforge Pro`,
|
|
745
|
-
`Set DSFORGE_KEY to unlock compact and spacious. Using comfortable.`
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
preset = "comfortable";
|
|
749
|
-
} else if (options.preset && VALID_PRESETS.includes(options.preset)) {
|
|
740
|
+
if (options.preset && VALID_PRESETS.includes(options.preset)) {
|
|
750
741
|
preset = options.preset;
|
|
751
742
|
} else {
|
|
752
743
|
const answer = await ask(
|
|
@@ -3925,62 +3916,7 @@ function generateThemeProvider(config) {
|
|
|
3925
3916
|
const themeNames = Object.keys(config.themes ?? { light: {}, dark: {} });
|
|
3926
3917
|
const defaultTheme = themeNames.includes("light") ? "light" : themeNames[0] ?? "light";
|
|
3927
3918
|
const themeType = themeNames.map((t) => `"${t}"`).join(" | ");
|
|
3928
|
-
const isPro = isProUnlocked();
|
|
3929
3919
|
const defaultDensity = config.meta.preset ?? "comfortable";
|
|
3930
|
-
const densityImport = isPro ? `
|
|
3931
|
-
import "../tokens/density.css";` : "";
|
|
3932
|
-
const densityTypes = isPro ? `
|
|
3933
|
-
export type DensityName = "compact" | "comfortable" | "spacious";
|
|
3934
|
-
` : "";
|
|
3935
|
-
const densityContextTypes = isPro ? `
|
|
3936
|
-
export interface DensityContextValue {
|
|
3937
|
-
density: DensityName;
|
|
3938
|
-
setDensity: (density: DensityName) => void;
|
|
3939
|
-
}
|
|
3940
|
-
` : "";
|
|
3941
|
-
const densityContext = isPro ? `
|
|
3942
|
-
export const DensityContext = React.createContext<DensityContextValue>({
|
|
3943
|
-
density: "${defaultDensity}",
|
|
3944
|
-
setDensity: () => undefined,
|
|
3945
|
-
});
|
|
3946
|
-
|
|
3947
|
-
/**
|
|
3948
|
-
* Hook to read and change the current density.
|
|
3949
|
-
* Must be used inside a <ThemeProvider>.
|
|
3950
|
-
*/
|
|
3951
|
-
export function useDensity(): DensityContextValue {
|
|
3952
|
-
return React.useContext(DensityContext);
|
|
3953
|
-
}
|
|
3954
|
-
` : "";
|
|
3955
|
-
const densityProp = isPro ? `
|
|
3956
|
-
/** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
|
|
3957
|
-
density?: DensityName;` : "";
|
|
3958
|
-
const densityOnChangeProp = isPro ? `
|
|
3959
|
-
/** Called when setDensity is invoked. */
|
|
3960
|
-
onDensityChange?: (density: DensityName) => void;` : "";
|
|
3961
|
-
const densityState = isPro ? `
|
|
3962
|
-
const [density, setDensityState] = React.useState<DensityName>(initialDensity);
|
|
3963
|
-
|
|
3964
|
-
React.useEffect(() => {
|
|
3965
|
-
setDensityState(initialDensity);
|
|
3966
|
-
}, [initialDensity]);
|
|
3967
|
-
|
|
3968
|
-
const setDensity = React.useCallback(
|
|
3969
|
-
(next: DensityName) => {
|
|
3970
|
-
setDensityState(next);
|
|
3971
|
-
onDensityChange?.(next);
|
|
3972
|
-
},
|
|
3973
|
-
[onDensityChange],
|
|
3974
|
-
);
|
|
3975
|
-
` : "";
|
|
3976
|
-
const densityDestructure = isPro ? `,
|
|
3977
|
-
density: initialDensity = "${defaultDensity}",
|
|
3978
|
-
onDensityChange,` : "";
|
|
3979
|
-
const densityProviderOpen = isPro ? `
|
|
3980
|
-
<DensityContext.Provider value={{ density, setDensity }}>` : "";
|
|
3981
|
-
const densityDataAttr = isPro ? ` data-density={density}` : "";
|
|
3982
|
-
const densityProviderClose = isPro ? `
|
|
3983
|
-
</DensityContext.Provider>` : "";
|
|
3984
3920
|
return `/**
|
|
3985
3921
|
* ThemeProvider \u2014 ${config.meta.name}
|
|
3986
3922
|
*
|
|
@@ -3992,27 +3928,39 @@ export function useDensity(): DensityContextValue {
|
|
|
3992
3928
|
* import "@${config.meta.name}/tokens/light.css"; // or dark.css
|
|
3993
3929
|
* import { ThemeProvider } from "@${config.meta.name}";
|
|
3994
3930
|
*
|
|
3995
|
-
* <ThemeProvider theme="light"
|
|
3931
|
+
* <ThemeProvider theme="light" density="${defaultDensity}">
|
|
3996
3932
|
* <App />
|
|
3997
3933
|
* </ThemeProvider>
|
|
3998
3934
|
*/
|
|
3999
3935
|
|
|
4000
|
-
import React from "react"
|
|
3936
|
+
import React from "react";
|
|
3937
|
+
import "../tokens/density.css";
|
|
4001
3938
|
|
|
4002
3939
|
// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4003
3940
|
|
|
4004
3941
|
export type ThemeName = ${themeType};
|
|
4005
|
-
|
|
3942
|
+
|
|
3943
|
+
export type DensityName = "compact" | "comfortable" | "spacious";
|
|
3944
|
+
|
|
4006
3945
|
export interface ThemeContextValue {
|
|
4007
3946
|
theme: ThemeName;
|
|
4008
3947
|
setTheme: (theme: ThemeName) => void;
|
|
4009
3948
|
}
|
|
4010
|
-
|
|
3949
|
+
|
|
3950
|
+
export interface DensityContextValue {
|
|
3951
|
+
density: DensityName;
|
|
3952
|
+
setDensity: (density: DensityName) => void;
|
|
3953
|
+
}
|
|
3954
|
+
|
|
4011
3955
|
export interface ThemeProviderProps {
|
|
4012
3956
|
/** Initial theme. Defaults to "${defaultTheme}". */
|
|
4013
3957
|
theme?: ThemeName;
|
|
4014
3958
|
/** Called when setTheme is invoked \u2014 use to persist theme preference. */
|
|
4015
|
-
onThemeChange?: (theme: ThemeName) => void
|
|
3959
|
+
onThemeChange?: (theme: ThemeName) => void;
|
|
3960
|
+
/** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
|
|
3961
|
+
density?: DensityName;
|
|
3962
|
+
/** Called when setDensity is invoked. */
|
|
3963
|
+
onDensityChange?: (density: DensityName) => void;
|
|
4016
3964
|
children: React.ReactNode;
|
|
4017
3965
|
}
|
|
4018
3966
|
|
|
@@ -4030,12 +3978,27 @@ export const ThemeContext = React.createContext<ThemeContextValue>({
|
|
|
4030
3978
|
export function useTheme(): ThemeContextValue {
|
|
4031
3979
|
return React.useContext(ThemeContext);
|
|
4032
3980
|
}
|
|
4033
|
-
|
|
3981
|
+
|
|
3982
|
+
export const DensityContext = React.createContext<DensityContextValue>({
|
|
3983
|
+
density: "${defaultDensity}",
|
|
3984
|
+
setDensity: () => undefined,
|
|
3985
|
+
});
|
|
3986
|
+
|
|
3987
|
+
/**
|
|
3988
|
+
* Hook to read and change the current density.
|
|
3989
|
+
* Must be used inside a <ThemeProvider>.
|
|
3990
|
+
*/
|
|
3991
|
+
export function useDensity(): DensityContextValue {
|
|
3992
|
+
return React.useContext(DensityContext);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
4034
3995
|
// \u2500\u2500\u2500 Provider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4035
3996
|
|
|
4036
3997
|
export function ThemeProvider({
|
|
4037
3998
|
theme: initialTheme = "${defaultTheme}",
|
|
4038
|
-
onThemeChange
|
|
3999
|
+
onThemeChange,
|
|
4000
|
+
density: initialDensity = "${defaultDensity}",
|
|
4001
|
+
onDensityChange,
|
|
4039
4002
|
children,
|
|
4040
4003
|
}: ThemeProviderProps) {
|
|
4041
4004
|
const [theme, setThemeState] = React.useState<ThemeName>(initialTheme);
|
|
@@ -4051,13 +4014,29 @@ export function ThemeProvider({
|
|
|
4051
4014
|
},
|
|
4052
4015
|
[onThemeChange],
|
|
4053
4016
|
);
|
|
4054
|
-
|
|
4055
|
-
|
|
4017
|
+
|
|
4018
|
+
const [density, setDensityState] = React.useState<DensityName>(initialDensity);
|
|
4019
|
+
|
|
4020
|
+
React.useEffect(() => {
|
|
4021
|
+
setDensityState(initialDensity);
|
|
4022
|
+
}, [initialDensity]);
|
|
4023
|
+
|
|
4024
|
+
const setDensity = React.useCallback(
|
|
4025
|
+
(next: DensityName) => {
|
|
4026
|
+
setDensityState(next);
|
|
4027
|
+
onDensityChange?.(next);
|
|
4028
|
+
},
|
|
4029
|
+
[onDensityChange],
|
|
4030
|
+
);
|
|
4031
|
+
|
|
4032
|
+
return (
|
|
4033
|
+
<DensityContext.Provider value={{ density, setDensity }}>
|
|
4056
4034
|
<ThemeContext.Provider value={{ theme, setTheme }}>
|
|
4057
|
-
<div data-theme={theme}
|
|
4035
|
+
<div data-theme={theme} data-density={density} style={{ display: "contents" }}>
|
|
4058
4036
|
{children}
|
|
4059
4037
|
</div>
|
|
4060
|
-
</ThemeContext.Provider
|
|
4038
|
+
</ThemeContext.Provider>
|
|
4039
|
+
</DensityContext.Provider>
|
|
4061
4040
|
);
|
|
4062
4041
|
}
|
|
4063
4042
|
`;
|
|
@@ -4714,13 +4693,11 @@ async function runGenerate(cwd, options) {
|
|
|
4714
4693
|
await writeFile(path3.join(tokensDir, filename), content);
|
|
4715
4694
|
logger.dim(` \u2192 tokens/${filename}`);
|
|
4716
4695
|
}
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
logger.dim(` \u2192 tokens/density.css`);
|
|
4723
|
-
}
|
|
4696
|
+
await writeFile(
|
|
4697
|
+
path3.join(tokensDir, "density.css"),
|
|
4698
|
+
emitDensityCss(config)
|
|
4699
|
+
);
|
|
4700
|
+
logger.dim(` \u2192 tokens/density.css`);
|
|
4724
4701
|
const tokenFiles = reactAdapter.generateTokenFiles(config, resolution);
|
|
4725
4702
|
for (const { filename, content } of tokenFiles) {
|
|
4726
4703
|
await writeFile(path3.join(tokensDir, filename), content);
|
|
@@ -4771,14 +4748,12 @@ async function runGenerate(cwd, options) {
|
|
|
4771
4748
|
JSON.stringify(componentJson, null, 2)
|
|
4772
4749
|
);
|
|
4773
4750
|
logger.dim(` \u2192 components/${pascalName}/${pascalName}.json`);
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
logger.dim(` \u2192 components/${pascalName}/${pascalName}.metadata.json`);
|
|
4781
|
-
}
|
|
4751
|
+
const metadata = generateComponentMetadata(pascalName);
|
|
4752
|
+
await writeFile(
|
|
4753
|
+
path3.join(componentSubDir, `${pascalName}.metadata.json`),
|
|
4754
|
+
JSON.stringify(metadata, null, 2)
|
|
4755
|
+
);
|
|
4756
|
+
logger.dim(` \u2192 components/${pascalName}/${pascalName}.metadata.json`);
|
|
4782
4757
|
} catch (err) {
|
|
4783
4758
|
logger.warn(
|
|
4784
4759
|
`[dsforge] Could not generate ${componentName} \u2014 ${err.message}`
|
|
@@ -4798,14 +4773,12 @@ async function runGenerate(cwd, options) {
|
|
|
4798
4773
|
JSON.stringify(tpJson, null, 2)
|
|
4799
4774
|
);
|
|
4800
4775
|
logger.dim(` \u2192 components/ThemeProvider/ThemeProvider.json`);
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
logger.dim(` \u2192 components/ThemeProvider/ThemeProvider.metadata.json`);
|
|
4808
|
-
}
|
|
4776
|
+
const tpMeta = generateComponentMetadata("ThemeProvider");
|
|
4777
|
+
await writeFile(
|
|
4778
|
+
path3.join(tpDir, "ThemeProvider.metadata.json"),
|
|
4779
|
+
JSON.stringify(tpMeta, null, 2)
|
|
4780
|
+
);
|
|
4781
|
+
logger.dim(` \u2192 components/ThemeProvider/ThemeProvider.metadata.json`);
|
|
4809
4782
|
} catch {
|
|
4810
4783
|
}
|
|
4811
4784
|
const { filename: idxFile, content: idxContent } = reactAdapter.generateComponentIndex(config, generatedNames);
|
|
@@ -4838,8 +4811,8 @@ async function runGenerate(cwd, options) {
|
|
|
4838
4811
|
}
|
|
4839
4812
|
logger.success(`Package files written`);
|
|
4840
4813
|
}
|
|
4841
|
-
if (
|
|
4842
|
-
logger.step("Writing
|
|
4814
|
+
if (!only || only === "components") {
|
|
4815
|
+
logger.step("Writing registry + AI outputs...");
|
|
4843
4816
|
const generatedJsons = globalThis["__dsforgGeneratedJsons"] ?? [];
|
|
4844
4817
|
const { generateRegistry } = await import("../generateRegistry-3MEZDJAJ.js");
|
|
4845
4818
|
const {
|
|
@@ -4888,10 +4861,10 @@ async function runGenerate(cwd, options) {
|
|
|
4888
4861
|
generateCopilotInstructions(systemName)
|
|
4889
4862
|
);
|
|
4890
4863
|
logger.dim(` \u2192 copilot-instructions.md`);
|
|
4891
|
-
logger.success(`
|
|
4864
|
+
logger.success(`Registry + AI outputs written`);
|
|
4892
4865
|
}
|
|
4893
4866
|
logger.step("Generating showcase...");
|
|
4894
|
-
const { generateShowcase } = await import("../html-
|
|
4867
|
+
const { generateShowcase } = await import("../html-4DD6GOHE.js");
|
|
4895
4868
|
const showcaseHtml = generateShowcase(config, resolution);
|
|
4896
4869
|
await writeFile(path3.join(outRoot, "showcase.html"), showcaseHtml);
|
|
4897
4870
|
logger.dim(` \u2192 showcase.html`);
|
|
@@ -5278,7 +5251,7 @@ async function runMenu() {
|
|
|
5278
5251
|
// package.json
|
|
5279
5252
|
var package_default = {
|
|
5280
5253
|
name: "@nghitrum/dsforge",
|
|
5281
|
-
version: "0.1.5-alpha.
|
|
5254
|
+
version: "0.1.5-alpha.13",
|
|
5282
5255
|
description: "AI-native design system generator \u2014 tokens \u2192 components \u2192 docs \u2192 npm",
|
|
5283
5256
|
keywords: [
|
|
5284
5257
|
"design-system",
|
|
@@ -2,9 +2,8 @@ import {
|
|
|
2
2
|
PRESETS,
|
|
3
3
|
RADIUS_PRESETS,
|
|
4
4
|
SPACING_PRESETS,
|
|
5
|
-
buildSemanticSpacing
|
|
6
|
-
|
|
7
|
-
} from "./chunk-YUPXTQZ5.js";
|
|
5
|
+
buildSemanticSpacing
|
|
6
|
+
} from "./chunk-5YT3VNE6.js";
|
|
8
7
|
import {
|
|
9
8
|
COMPONENT_JSON_DEFINITIONS,
|
|
10
9
|
COMPONENT_METADATA_DEFINITIONS
|
|
@@ -215,13 +214,6 @@ function buildMotionSection(config) {
|
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
// src/generators/showcase/page.ts
|
|
218
|
-
var lockedPanel = (label) => `
|
|
219
|
-
<div class="locked-panel">
|
|
220
|
-
<div class="locked-icon">\u2298</div>
|
|
221
|
-
<div class="locked-title">${label} \u2014 dsforge Pro</div>
|
|
222
|
-
<p class="locked-desc">This tab is available with a dsforge Pro license.</p>
|
|
223
|
-
<p class="locked-hint">Set the <code>DSFORGE_KEY</code> environment variable to unlock.</p>
|
|
224
|
-
</div>`;
|
|
225
217
|
function buildPropsTable(props) {
|
|
226
218
|
const sorted = [...props].sort((a, b) => (b.required ? 1 : 0) - (a.required ? 1 : 0));
|
|
227
219
|
return `
|
|
@@ -364,7 +356,7 @@ function buildAiMetaHtml(id, meta) {
|
|
|
364
356
|
</div>`;
|
|
365
357
|
}
|
|
366
358
|
function buildComponentPage(input) {
|
|
367
|
-
const { id, description, overviewHtml, json, showcaseExamples, a11yContract, a11yItems, metadata
|
|
359
|
+
const { id, description, overviewHtml, json, showcaseExamples, a11yContract, a11yItems, metadata } = input;
|
|
368
360
|
const tabId = (tab) => `${id}-tab-${tab}`;
|
|
369
361
|
const panelId = (tab) => `${id}-panel-${tab}`;
|
|
370
362
|
const overviewContent = `
|
|
@@ -380,13 +372,13 @@ function buildComponentPage(input) {
|
|
|
380
372
|
</div>
|
|
381
373
|
<p class="a11y-desc">${esc(item.description)}</p>
|
|
382
374
|
</div>`).join("")}</div>`;
|
|
383
|
-
const aiContent =
|
|
375
|
+
const aiContent = metadata ? buildAiMetaHtml(id, metadata) : "";
|
|
384
376
|
const tabs = [
|
|
385
|
-
{ id: "overview", label: "Overview", content: overviewContent
|
|
386
|
-
{ id: "props", label: "Props", content: propsContent
|
|
387
|
-
{ id: "examples", label: "Examples", content: examplesContent
|
|
388
|
-
{ id: "accessibility", label: "Accessibility", content: a11yContent
|
|
389
|
-
{ id: "ai-metadata", label: "AI Metadata", content: aiContent
|
|
377
|
+
{ id: "overview", label: "Overview", content: overviewContent },
|
|
378
|
+
{ id: "props", label: "Props", content: propsContent },
|
|
379
|
+
{ id: "examples", label: "Examples", content: examplesContent },
|
|
380
|
+
{ id: "accessibility", label: "Accessibility", content: a11yContent },
|
|
381
|
+
{ id: "ai-metadata", label: "AI Metadata", content: aiContent }
|
|
390
382
|
];
|
|
391
383
|
return `
|
|
392
384
|
<div class="comp-tabs" id="${id}-tabs">
|
|
@@ -394,14 +386,13 @@ function buildComponentPage(input) {
|
|
|
394
386
|
${tabs.map(
|
|
395
387
|
(t, i) => `
|
|
396
388
|
<button
|
|
397
|
-
class="comp-tab${i === 0 ? " active" : ""}
|
|
389
|
+
class="comp-tab${i === 0 ? " active" : ""}"
|
|
398
390
|
id="${tabId(t.id)}"
|
|
399
391
|
role="tab"
|
|
400
392
|
aria-selected="${i === 0}"
|
|
401
393
|
aria-controls="${panelId(t.id)}"
|
|
402
|
-
onclick="
|
|
403
|
-
|
|
404
|
-
>${esc(t.label)}${t.locked ? " 🔒" : ""}</button>`
|
|
394
|
+
onclick="switchTab('${id}', '${t.id}', this)"
|
|
395
|
+
>${esc(t.label)}</button>`
|
|
405
396
|
).join("")}
|
|
406
397
|
</div>
|
|
407
398
|
${tabs.map(
|
|
@@ -2099,7 +2090,6 @@ function generateShowcase(config, resolution) {
|
|
|
2099
2090
|
];
|
|
2100
2091
|
const componentItems = SHOWCASE_COMPONENTS.map(({ id, label }) => ({ id, label }));
|
|
2101
2092
|
const allItems = [...foundationItems, ...componentItems];
|
|
2102
|
-
const isPro = isProUnlocked();
|
|
2103
2093
|
const flatTokens = Object.fromEntries(
|
|
2104
2094
|
Object.entries(tokens).map(([k, v]) => [
|
|
2105
2095
|
k.replace(/^(global|semantic|component)\./, ""),
|
|
@@ -2147,8 +2137,7 @@ function generateShowcase(config, resolution) {
|
|
|
2147
2137
|
showcaseExamples: defData.examples,
|
|
2148
2138
|
a11yContract: metaDef?.accessibilityContract ?? null,
|
|
2149
2139
|
a11yItems: defData.a11y,
|
|
2150
|
-
metadata:
|
|
2151
|
-
isPro
|
|
2140
|
+
metadata: metaDef
|
|
2152
2141
|
})
|
|
2153
2142
|
];
|
|
2154
2143
|
})
|
|
@@ -2160,13 +2149,12 @@ function generateShowcase(config, resolution) {
|
|
|
2160
2149
|
const showcaseData = {
|
|
2161
2150
|
systemName: name,
|
|
2162
2151
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2163
|
-
isPro,
|
|
2164
2152
|
components: SHOWCASE_COMPONENTS.map((entry) => {
|
|
2165
2153
|
const jsonDef = COMPONENT_JSON_DEFINITIONS[entry.label];
|
|
2166
2154
|
const metaDef = COMPONENT_METADATA_DEFINITIONS[entry.label] ?? null;
|
|
2167
2155
|
return {
|
|
2168
2156
|
json: jsonDef ? { ...jsonDef, cssVars: resolvedCssVars } : null,
|
|
2169
|
-
metadata:
|
|
2157
|
+
metadata: metaDef
|
|
2170
2158
|
};
|
|
2171
2159
|
})
|
|
2172
2160
|
};
|
|
@@ -2263,27 +2251,17 @@ ${densityCss}
|
|
|
2263
2251
|
border: 1px solid var(--color-border-default, #e2e8f0);
|
|
2264
2252
|
border-radius: 7px; padding: 3px;
|
|
2265
2253
|
}
|
|
2266
|
-
.density-toggle.locked { opacity: 0.5; cursor: not-allowed; }
|
|
2267
2254
|
.density-btn {
|
|
2268
2255
|
padding: 3px 10px; border-radius: 4px; border: none;
|
|
2269
2256
|
background: transparent; font-size: 12px; cursor: pointer;
|
|
2270
2257
|
color: var(--color-text-secondary, #64748b);
|
|
2271
2258
|
transition: background 120ms, color 120ms;
|
|
2272
2259
|
}
|
|
2273
|
-
.density-btn:disabled { cursor: not-allowed; }
|
|
2274
2260
|
.density-btn.active {
|
|
2275
2261
|
background: var(--color-bg-default, #fff);
|
|
2276
2262
|
color: var(--color-text-primary, #0f172a); font-weight: 500;
|
|
2277
2263
|
box-shadow: 0 1px 2px rgb(0 0 0 / 0.06);
|
|
2278
2264
|
}
|
|
2279
|
-
.density-lock {
|
|
2280
|
-
font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
|
|
2281
|
-
color: var(--color-text-secondary, #64748b);
|
|
2282
|
-
padding: 2px 6px; border-radius: 4px;
|
|
2283
|
-
background: var(--color-bg-overlay, #f1f5f9);
|
|
2284
|
-
border: 1px solid var(--color-border-default, #e2e8f0);
|
|
2285
|
-
white-space: nowrap;
|
|
2286
|
-
}
|
|
2287
2265
|
.content { padding: 36px 40px 80px; max-width: 860px; }
|
|
2288
2266
|
.page { display: none; }
|
|
2289
2267
|
.page.active { display: block; }
|
|
@@ -2485,21 +2463,6 @@ ${densityCss}
|
|
|
2485
2463
|
.ai-guidance-list { padding-left: 20px; display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
|
|
2486
2464
|
.ai-guidance-list li { font-size: 13px; color: var(--color-text-secondary, #64748b); line-height: 1.6; }
|
|
2487
2465
|
|
|
2488
|
-
/* \u2500\u2500 Locked tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2489
|
-
.comp-tab.locked { opacity: 0.45; cursor: default; }
|
|
2490
|
-
.comp-tab.locked:hover { color: var(--color-text-secondary, #64748b); }
|
|
2491
|
-
.locked-panel {
|
|
2492
|
-
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
2493
|
-
padding: 64px 32px; text-align: center;
|
|
2494
|
-
border: 1px dashed var(--color-border-default, #e2e8f0); border-radius: 10px;
|
|
2495
|
-
background: var(--color-bg-subtle, #f8fafc);
|
|
2496
|
-
}
|
|
2497
|
-
.locked-icon { font-size: 28px; color: var(--color-text-secondary, #64748b); margin-bottom: 12px; }
|
|
2498
|
-
.locked-title { font-size: 14px; font-weight: 600; color: var(--color-text-primary, #0f172a); margin-bottom: 8px; }
|
|
2499
|
-
.locked-desc { font-size: 13px; color: var(--color-text-secondary, #64748b); max-width: 360px; line-height: 1.6; margin-bottom: 6px; }
|
|
2500
|
-
.locked-hint { font-size: 12px; color: var(--color-text-secondary, #64748b); font-family: monospace; }
|
|
2501
|
-
.locked-hint code { background: var(--color-bg-overlay, #f1f5f9); padding: 1px 6px; border-radius: 4px; border: 1px solid var(--color-border-default, #e2e8f0); }
|
|
2502
|
-
|
|
2503
2466
|
/* \u2500\u2500 Component primitives \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
2504
2467
|
.ds-btn { border: none; cursor: pointer; font-size: 14px; font-weight: 500; padding: var(--component-padding-sm, 8px) var(--component-padding-md, 16px); border-radius: var(--radius-md, 4px); transition: filter 120ms; }
|
|
2505
2468
|
.ds-btn:hover:not(:disabled) { filter: brightness(0.92); }
|
|
@@ -2570,16 +2533,11 @@ ${densityCss}
|
|
|
2570
2533
|
${esc(name)} / <span id="topbar-current">Colors</span>
|
|
2571
2534
|
</div>
|
|
2572
2535
|
<div class="topbar-actions">
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
${PRESETS.map((p) => `
|
|
2579
|
-
<button class="density-btn${p === defaultDensity ? " active" : ""}" disabled>${p.charAt(0).toUpperCase() + p.slice(1)}</button>
|
|
2580
|
-
`).join("")}
|
|
2581
|
-
<span class="density-lock">\u2298 Pro</span>
|
|
2582
|
-
</div>`}
|
|
2536
|
+
<div class="density-toggle" id="density-toggle">
|
|
2537
|
+
${PRESETS.map((p) => `
|
|
2538
|
+
<button class="density-btn${p === defaultDensity ? " active" : ""}" onclick="setDensity('${p}', this)">${p.charAt(0).toUpperCase() + p.slice(1)}</button>
|
|
2539
|
+
`).join("")}
|
|
2540
|
+
</div>
|
|
2583
2541
|
${themes.length >= 2 ? `
|
|
2584
2542
|
<div class="theme-toggle">
|
|
2585
2543
|
${themes.map(
|
package/dist/index.js
CHANGED
|
@@ -1143,31 +1143,6 @@ var rl = readline.createInterface({
|
|
|
1143
1143
|
output: process.stdout
|
|
1144
1144
|
});
|
|
1145
1145
|
|
|
1146
|
-
// src/lib/license.ts
|
|
1147
|
-
import { readFileSync } from "fs";
|
|
1148
|
-
import { join } from "path";
|
|
1149
|
-
function readKeyFromDotEnv() {
|
|
1150
|
-
try {
|
|
1151
|
-
const content = readFileSync(join(process.cwd(), ".env"), "utf8");
|
|
1152
|
-
for (const raw of content.split("\n")) {
|
|
1153
|
-
const line = raw.trim();
|
|
1154
|
-
if (!line || line.startsWith("#")) continue;
|
|
1155
|
-
const eq = line.indexOf("=");
|
|
1156
|
-
if (eq === -1) continue;
|
|
1157
|
-
const key = line.slice(0, eq).trim();
|
|
1158
|
-
if (key !== "DSFORGE_KEY") continue;
|
|
1159
|
-
const val = line.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
|
|
1160
|
-
return val || void 0;
|
|
1161
|
-
}
|
|
1162
|
-
} catch {
|
|
1163
|
-
}
|
|
1164
|
-
return void 0;
|
|
1165
|
-
}
|
|
1166
|
-
function isProUnlocked() {
|
|
1167
|
-
const key = process.env["DSFORGE_KEY"] ?? readKeyFromDotEnv();
|
|
1168
|
-
return typeof key === "string" && key.length > 0;
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
1146
|
// src/presets/index.ts
|
|
1172
1147
|
var SPACING_PRESETS = {
|
|
1173
1148
|
compact: {
|
|
@@ -2612,62 +2587,7 @@ function generateThemeProvider(config) {
|
|
|
2612
2587
|
const themeNames = Object.keys(config.themes ?? { light: {}, dark: {} });
|
|
2613
2588
|
const defaultTheme = themeNames.includes("light") ? "light" : themeNames[0] ?? "light";
|
|
2614
2589
|
const themeType = themeNames.map((t) => `"${t}"`).join(" | ");
|
|
2615
|
-
const isPro = isProUnlocked();
|
|
2616
2590
|
const defaultDensity = config.meta.preset ?? "comfortable";
|
|
2617
|
-
const densityImport = isPro ? `
|
|
2618
|
-
import "../tokens/density.css";` : "";
|
|
2619
|
-
const densityTypes = isPro ? `
|
|
2620
|
-
export type DensityName = "compact" | "comfortable" | "spacious";
|
|
2621
|
-
` : "";
|
|
2622
|
-
const densityContextTypes = isPro ? `
|
|
2623
|
-
export interface DensityContextValue {
|
|
2624
|
-
density: DensityName;
|
|
2625
|
-
setDensity: (density: DensityName) => void;
|
|
2626
|
-
}
|
|
2627
|
-
` : "";
|
|
2628
|
-
const densityContext = isPro ? `
|
|
2629
|
-
export const DensityContext = React.createContext<DensityContextValue>({
|
|
2630
|
-
density: "${defaultDensity}",
|
|
2631
|
-
setDensity: () => undefined,
|
|
2632
|
-
});
|
|
2633
|
-
|
|
2634
|
-
/**
|
|
2635
|
-
* Hook to read and change the current density.
|
|
2636
|
-
* Must be used inside a <ThemeProvider>.
|
|
2637
|
-
*/
|
|
2638
|
-
export function useDensity(): DensityContextValue {
|
|
2639
|
-
return React.useContext(DensityContext);
|
|
2640
|
-
}
|
|
2641
|
-
` : "";
|
|
2642
|
-
const densityProp = isPro ? `
|
|
2643
|
-
/** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
|
|
2644
|
-
density?: DensityName;` : "";
|
|
2645
|
-
const densityOnChangeProp = isPro ? `
|
|
2646
|
-
/** Called when setDensity is invoked. */
|
|
2647
|
-
onDensityChange?: (density: DensityName) => void;` : "";
|
|
2648
|
-
const densityState = isPro ? `
|
|
2649
|
-
const [density, setDensityState] = React.useState<DensityName>(initialDensity);
|
|
2650
|
-
|
|
2651
|
-
React.useEffect(() => {
|
|
2652
|
-
setDensityState(initialDensity);
|
|
2653
|
-
}, [initialDensity]);
|
|
2654
|
-
|
|
2655
|
-
const setDensity = React.useCallback(
|
|
2656
|
-
(next: DensityName) => {
|
|
2657
|
-
setDensityState(next);
|
|
2658
|
-
onDensityChange?.(next);
|
|
2659
|
-
},
|
|
2660
|
-
[onDensityChange],
|
|
2661
|
-
);
|
|
2662
|
-
` : "";
|
|
2663
|
-
const densityDestructure = isPro ? `,
|
|
2664
|
-
density: initialDensity = "${defaultDensity}",
|
|
2665
|
-
onDensityChange,` : "";
|
|
2666
|
-
const densityProviderOpen = isPro ? `
|
|
2667
|
-
<DensityContext.Provider value={{ density, setDensity }}>` : "";
|
|
2668
|
-
const densityDataAttr = isPro ? ` data-density={density}` : "";
|
|
2669
|
-
const densityProviderClose = isPro ? `
|
|
2670
|
-
</DensityContext.Provider>` : "";
|
|
2671
2591
|
return `/**
|
|
2672
2592
|
* ThemeProvider \u2014 ${config.meta.name}
|
|
2673
2593
|
*
|
|
@@ -2679,27 +2599,39 @@ export function useDensity(): DensityContextValue {
|
|
|
2679
2599
|
* import "@${config.meta.name}/tokens/light.css"; // or dark.css
|
|
2680
2600
|
* import { ThemeProvider } from "@${config.meta.name}";
|
|
2681
2601
|
*
|
|
2682
|
-
* <ThemeProvider theme="light"
|
|
2602
|
+
* <ThemeProvider theme="light" density="${defaultDensity}">
|
|
2683
2603
|
* <App />
|
|
2684
2604
|
* </ThemeProvider>
|
|
2685
2605
|
*/
|
|
2686
2606
|
|
|
2687
|
-
import React from "react"
|
|
2607
|
+
import React from "react";
|
|
2608
|
+
import "../tokens/density.css";
|
|
2688
2609
|
|
|
2689
2610
|
// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2690
2611
|
|
|
2691
2612
|
export type ThemeName = ${themeType};
|
|
2692
|
-
|
|
2613
|
+
|
|
2614
|
+
export type DensityName = "compact" | "comfortable" | "spacious";
|
|
2615
|
+
|
|
2693
2616
|
export interface ThemeContextValue {
|
|
2694
2617
|
theme: ThemeName;
|
|
2695
2618
|
setTheme: (theme: ThemeName) => void;
|
|
2696
2619
|
}
|
|
2697
|
-
|
|
2620
|
+
|
|
2621
|
+
export interface DensityContextValue {
|
|
2622
|
+
density: DensityName;
|
|
2623
|
+
setDensity: (density: DensityName) => void;
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2698
2626
|
export interface ThemeProviderProps {
|
|
2699
2627
|
/** Initial theme. Defaults to "${defaultTheme}". */
|
|
2700
2628
|
theme?: ThemeName;
|
|
2701
2629
|
/** Called when setTheme is invoked \u2014 use to persist theme preference. */
|
|
2702
|
-
onThemeChange?: (theme: ThemeName) => void
|
|
2630
|
+
onThemeChange?: (theme: ThemeName) => void;
|
|
2631
|
+
/** Component density. Requires density.css to be imported. Defaults to "${defaultDensity}". */
|
|
2632
|
+
density?: DensityName;
|
|
2633
|
+
/** Called when setDensity is invoked. */
|
|
2634
|
+
onDensityChange?: (density: DensityName) => void;
|
|
2703
2635
|
children: React.ReactNode;
|
|
2704
2636
|
}
|
|
2705
2637
|
|
|
@@ -2717,12 +2649,27 @@ export const ThemeContext = React.createContext<ThemeContextValue>({
|
|
|
2717
2649
|
export function useTheme(): ThemeContextValue {
|
|
2718
2650
|
return React.useContext(ThemeContext);
|
|
2719
2651
|
}
|
|
2720
|
-
|
|
2652
|
+
|
|
2653
|
+
export const DensityContext = React.createContext<DensityContextValue>({
|
|
2654
|
+
density: "${defaultDensity}",
|
|
2655
|
+
setDensity: () => undefined,
|
|
2656
|
+
});
|
|
2657
|
+
|
|
2658
|
+
/**
|
|
2659
|
+
* Hook to read and change the current density.
|
|
2660
|
+
* Must be used inside a <ThemeProvider>.
|
|
2661
|
+
*/
|
|
2662
|
+
export function useDensity(): DensityContextValue {
|
|
2663
|
+
return React.useContext(DensityContext);
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2721
2666
|
// \u2500\u2500\u2500 Provider \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2722
2667
|
|
|
2723
2668
|
export function ThemeProvider({
|
|
2724
2669
|
theme: initialTheme = "${defaultTheme}",
|
|
2725
|
-
onThemeChange
|
|
2670
|
+
onThemeChange,
|
|
2671
|
+
density: initialDensity = "${defaultDensity}",
|
|
2672
|
+
onDensityChange,
|
|
2726
2673
|
children,
|
|
2727
2674
|
}: ThemeProviderProps) {
|
|
2728
2675
|
const [theme, setThemeState] = React.useState<ThemeName>(initialTheme);
|
|
@@ -2738,13 +2685,29 @@ export function ThemeProvider({
|
|
|
2738
2685
|
},
|
|
2739
2686
|
[onThemeChange],
|
|
2740
2687
|
);
|
|
2741
|
-
|
|
2742
|
-
|
|
2688
|
+
|
|
2689
|
+
const [density, setDensityState] = React.useState<DensityName>(initialDensity);
|
|
2690
|
+
|
|
2691
|
+
React.useEffect(() => {
|
|
2692
|
+
setDensityState(initialDensity);
|
|
2693
|
+
}, [initialDensity]);
|
|
2694
|
+
|
|
2695
|
+
const setDensity = React.useCallback(
|
|
2696
|
+
(next: DensityName) => {
|
|
2697
|
+
setDensityState(next);
|
|
2698
|
+
onDensityChange?.(next);
|
|
2699
|
+
},
|
|
2700
|
+
[onDensityChange],
|
|
2701
|
+
);
|
|
2702
|
+
|
|
2703
|
+
return (
|
|
2704
|
+
<DensityContext.Provider value={{ density, setDensity }}>
|
|
2743
2705
|
<ThemeContext.Provider value={{ theme, setTheme }}>
|
|
2744
|
-
<div data-theme={theme}
|
|
2706
|
+
<div data-theme={theme} data-density={density} style={{ display: "contents" }}>
|
|
2745
2707
|
{children}
|
|
2746
2708
|
</div>
|
|
2747
|
-
</ThemeContext.Provider
|
|
2709
|
+
</ThemeContext.Provider>
|
|
2710
|
+
</DensityContext.Provider>
|
|
2748
2711
|
);
|
|
2749
2712
|
}
|
|
2750
2713
|
`;
|
package/package.json
CHANGED
/package/{LICENSE → LICENSE.md}
RENAMED
|
File without changes
|