@roboticela/devkit 1.0.2

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.
@@ -0,0 +1,221 @@
1
+ // ── Colour math ───────────────────────────────────────────────────────────────
2
+ function hexToRgb(hex) {
3
+ const n = parseInt(hex.replace("#", ""), 16);
4
+ return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
5
+ }
6
+ function rgbToHex(r, g, b) {
7
+ return "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")).join("");
8
+ }
9
+ function lighten(hex, amount) {
10
+ const [r, g, b] = hexToRgb(hex);
11
+ return rgbToHex(r + (255 - r) * amount, g + (255 - g) * amount, b + (255 - b) * amount);
12
+ }
13
+ function darken(hex, amount) {
14
+ const [r, g, b] = hexToRgb(hex);
15
+ return rgbToHex(r * (1 - amount), g * (1 - amount), b * (1 - amount));
16
+ }
17
+ function relativeLuminance(hex) {
18
+ const [r, g, b] = hexToRgb(hex).map((c) => {
19
+ const s = c / 255;
20
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
21
+ });
22
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
23
+ }
24
+ function contrastRatio(hex1, hex2) {
25
+ const l1 = relativeLuminance(hex1);
26
+ const l2 = relativeLuminance(hex2);
27
+ const lighter = Math.max(l1, l2);
28
+ const darker = Math.min(l1, l2);
29
+ return (lighter + 0.05) / (darker + 0.05);
30
+ }
31
+ function accessibleTextColor(bg) {
32
+ return contrastRatio(bg, "#ffffff") >= 4.5 ? "#ffffff" : "#111827";
33
+ }
34
+ // ── Presets ───────────────────────────────────────────────────────────────────
35
+ const PRESETS = {
36
+ default: { primary: "#6366f1", secondary: "#f59e0b", radius: "md" },
37
+ minimal: { primary: "#475569", secondary: "#0ea5e9", radius: "sm" },
38
+ bold: { primary: "#7c3aed", secondary: "#ec4899", radius: "lg" },
39
+ playful: { primary: "#10b981", secondary: "#f97316", radius: "full" },
40
+ corporate: { primary: "#1d4ed8", secondary: "#374151", radius: "none" },
41
+ };
42
+ const RADIUS_MAP = {
43
+ none: "0",
44
+ sm: "0.25rem",
45
+ md: "0.5rem",
46
+ lg: "0.75rem",
47
+ xl: "1rem",
48
+ full: "9999px",
49
+ };
50
+ // ── Token generator ───────────────────────────────────────────────────────────
51
+ export function generateThemeCSS(config) {
52
+ const theme = config.theme ?? {};
53
+ const preset = PRESETS[theme.preset ?? "default"] ?? PRESETS.default;
54
+ const primary = theme.colors?.primary ?? preset.primary;
55
+ const secondary = theme.colors?.secondary ?? preset.secondary;
56
+ const radiusKey = theme.radius ?? preset.radius;
57
+ const baseRadius = RADIUS_MAP[radiusKey] ?? RADIUS_MAP.md;
58
+ const radii = {
59
+ sm: radiusKey === "none" ? "0" : `calc(${baseRadius} * 0.5)`,
60
+ md: baseRadius,
61
+ lg: radiusKey === "none" ? "0" : `calc(${baseRadius} * 1.5)`,
62
+ xl: radiusKey === "none" ? "0" : `calc(${baseRadius} * 2)`,
63
+ "2xl": radiusKey === "none" ? "0" : `calc(${baseRadius} * 3)`,
64
+ full: "9999px",
65
+ };
66
+ const fontSans = theme.fonts?.sans ?? "Inter";
67
+ const fontMono = theme.fonts?.mono ?? "JetBrains Mono";
68
+ const fontDisplay = theme.fonts?.display ?? fontSans;
69
+ const googleFonts = [];
70
+ if (fontSans !== "system-ui")
71
+ googleFonts.push(fontSans);
72
+ if (fontMono !== fontSans)
73
+ googleFonts.push(fontMono);
74
+ if (fontDisplay !== fontSans)
75
+ googleFonts.push(fontDisplay);
76
+ const imports = [...new Set(googleFonts)]
77
+ .map((f) => `@import url('https://fonts.googleapis.com/css2?family=${f.replace(/ /g, "+")}:wght@400;500;600;700;800&display=swap');`)
78
+ .join("\n");
79
+ return `/* ── DevKit Managed Theme Block — do not edit below this line ── */
80
+ /* ── Generated by @roboticela/devkit on ${new Date().toISOString().split("T")[0]} ── */
81
+
82
+ ${imports}
83
+
84
+ :root {
85
+ /* ── Brand: Primary ── */
86
+ --color-primary: ${primary};
87
+ --color-primary-hover: ${darken(primary, 0.1)};
88
+ --color-primary-active: ${darken(primary, 0.2)};
89
+ --color-primary-subtle: ${lighten(primary, 0.9)};
90
+ --color-primary-text: ${accessibleTextColor(primary)};
91
+
92
+ /* ── Brand: Secondary ── */
93
+ --color-secondary: ${secondary};
94
+ --color-secondary-hover: ${darken(secondary, 0.1)};
95
+ --color-secondary-subtle:${lighten(secondary, 0.9)};
96
+ --color-secondary-text: ${accessibleTextColor(secondary)};
97
+
98
+ /* ── Neutral / Surface ── */
99
+ --color-bg: #ffffff;
100
+ --color-bg-subtle: #f9fafb;
101
+ --color-bg-muted: #f3f4f6;
102
+ --color-border: #e5e7eb;
103
+ --color-border-strong: #d1d5db;
104
+
105
+ /* ── Text ── */
106
+ --color-text: #111827;
107
+ --color-text-muted: #6b7280;
108
+ --color-text-subtle: #9ca3af;
109
+ --color-text-inverse: #ffffff;
110
+
111
+ /* ── Semantic ── */
112
+ --color-success: #10b981;
113
+ --color-success-subtle: #ecfdf5;
114
+ --color-success-text: #065f46;
115
+ --color-warning: #f59e0b;
116
+ --color-warning-subtle: #fffbeb;
117
+ --color-warning-text: #92400e;
118
+ --color-error: #ef4444;
119
+ --color-error-subtle: #fef2f2;
120
+ --color-error-text: #991b1b;
121
+ --color-info: #3b82f6;
122
+ --color-info-subtle: #eff6ff;
123
+ --color-info-text: #1e40af;
124
+
125
+ /* ── Overlay ── */
126
+ --color-overlay: rgba(0,0,0,0.5);
127
+ --color-shadow: rgba(0,0,0,0.08);
128
+
129
+ /* ── Typography ── */
130
+ --font-sans: '${fontSans}', system-ui, -apple-system, sans-serif;
131
+ --font-mono: '${fontMono}', 'Fira Code', monospace;
132
+ --font-display: '${fontDisplay}', sans-serif;
133
+
134
+ --text-xs: 0.75rem;
135
+ --text-sm: 0.875rem;
136
+ --text-base: 1rem;
137
+ --text-lg: 1.125rem;
138
+ --text-xl: 1.25rem;
139
+ --text-2xl: 1.5rem;
140
+ --text-3xl: 1.875rem;
141
+ --text-4xl: 2.25rem;
142
+ --text-5xl: 3rem;
143
+ --text-6xl: 3.75rem;
144
+
145
+ --weight-normal: 400;
146
+ --weight-medium: 500;
147
+ --weight-semibold: 600;
148
+ --weight-bold: 700;
149
+ --weight-extrabold: 800;
150
+
151
+ --leading-tight: 1.25;
152
+ --leading-snug: 1.375;
153
+ --leading-normal: 1.5;
154
+ --leading-relaxed: 1.625;
155
+
156
+ /* ── Border Radius ── */
157
+ --radius-sm: ${radii.sm};
158
+ --radius-md: ${radii.md};
159
+ --radius-lg: ${radii.lg};
160
+ --radius-xl: ${radii.xl};
161
+ --radius-2xl: ${radii["2xl"]};
162
+ --radius-full: ${radii.full};
163
+
164
+ /* ── Spacing ── */
165
+ --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem;
166
+ --space-4: 1rem; --space-6: 1.5rem; --space-8: 2rem;
167
+ --space-10: 2.5rem; --space-12: 3rem; --space-16: 4rem;
168
+ --space-20: 5rem; --space-24: 6rem;
169
+
170
+ /* ── Shadows ── */
171
+ --shadow-sm: 0 1px 2px var(--color-shadow);
172
+ --shadow-md: 0 4px 6px -1px var(--color-shadow);
173
+ --shadow-lg: 0 10px 15px -3px var(--color-shadow);
174
+ --shadow-xl: 0 20px 25px -5px var(--color-shadow);
175
+
176
+ /* ── Transitions ── */
177
+ --transition-fast: 150ms ease;
178
+ --transition-normal: 200ms ease;
179
+ --transition-slow: 300ms ease;
180
+
181
+ /* ── Z-Index ── */
182
+ --z-dropdown: 1000; --z-sticky: 1100; --z-overlay: 1200;
183
+ --z-modal: 1300; --z-toast: 1400; --z-tooltip: 1500;
184
+ }
185
+ ${theme.darkMode !== false
186
+ ? `
187
+ .dark {
188
+ --color-primary: ${lighten(primary, 0.2)};
189
+ --color-primary-hover: ${primary};
190
+ --color-primary-subtle: ${darken(primary, 0.7)};
191
+ --color-primary-text: #ffffff;
192
+
193
+ --color-bg: #0f172a;
194
+ --color-bg-subtle: #1e293b;
195
+ --color-bg-muted: #334155;
196
+ --color-border: #334155;
197
+ --color-border-strong: #475569;
198
+
199
+ --color-text: #f1f5f9;
200
+ --color-text-muted: #94a3b8;
201
+ --color-text-subtle: #64748b;
202
+ --color-text-inverse: #0f172a;
203
+
204
+ --color-success: #34d399;
205
+ --color-success-subtle: #064e3b;
206
+ --color-warning: #fbbf24;
207
+ --color-warning-subtle: #451a03;
208
+ --color-error: #f87171;
209
+ --color-error-subtle: #450a0a;
210
+ --color-info: #60a5fa;
211
+ --color-info-subtle: #1e3a5f;
212
+ --color-overlay: rgba(0,0,0,0.7);
213
+ --color-shadow: rgba(0,0,0,0.3);
214
+ }
215
+ `
216
+ : ""}
217
+ /* ── End DevKit Managed Theme Block ── */
218
+
219
+ /* Your custom styles go here — DevKit will never modify below this line */
220
+ `;
221
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@roboticela/devkit",
3
+ "version": "1.0.2",
4
+ "description": "Roboticela DevKit CLI — scaffold, extend, and theme full-stack projects with one command",
5
+ "type": "module",
6
+ "bin": {
7
+ "devkit": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "dev": "tsx src/index.ts",
11
+ "build": "tsc && node scripts/post-build.js",
12
+ "start": "node dist/index.js",
13
+ "lint": "eslint src",
14
+ "prepublishOnly": "npm run build",
15
+ "release:patch": "npm version patch && npm publish --access public",
16
+ "release:minor": "npm version minor && npm publish --access public",
17
+ "release:major": "npm version major && npm publish --access public"
18
+ },
19
+ "keywords": [
20
+ "devkit",
21
+ "cli",
22
+ "scaffold",
23
+ "components",
24
+ "roboticela"
25
+ ],
26
+ "author": "Roboticela",
27
+ "license": "MIT",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/Roboticela/devkit-cli"
31
+ },
32
+ "engines": {
33
+ "node": ">=20"
34
+ },
35
+ "dependencies": {
36
+ "commander": "^12.1.0",
37
+ "@clack/prompts": "^0.9.0",
38
+ "chalk": "^5.3.0",
39
+ "ora": "^8.1.0",
40
+ "tar": "^7.4.0",
41
+ "semver": "^7.6.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^22.0.0",
45
+ "@types/tar": "^6.1.13",
46
+ "@types/semver": "^7.5.8",
47
+ "tsx": "^4.19.0",
48
+ "typescript": "^5.6.0",
49
+ "eslint": "^9.0.0",
50
+ "typescript-eslint": "^8.0.0",
51
+ "@eslint/js": "^9.0.0",
52
+ "globals": "^15.0.0"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "README.md"
57
+ ]
58
+ }