@launch77/cli 1.2.0 → 1.3.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/CHANGELOG.md +11 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/infrastructure/git.d.ts +37 -0
- package/dist/infrastructure/git.d.ts.map +1 -0
- package/dist/infrastructure/git.js +82 -0
- package/dist/infrastructure/git.js.map +1 -0
- package/dist/infrastructure/github.d.ts +43 -0
- package/dist/infrastructure/github.d.ts.map +1 -0
- package/dist/infrastructure/github.js +89 -0
- package/dist/infrastructure/github.js.map +1 -0
- package/dist/modules/catalog/schemas/catalog-ui-components.schema.json +2 -18
- package/dist/modules/git/commands/git-connect.d.ts +3 -0
- package/dist/modules/git/commands/git-connect.d.ts.map +1 -0
- package/dist/modules/git/commands/git-connect.js +156 -0
- package/dist/modules/git/commands/git-connect.js.map +1 -0
- package/dist/modules/git/commands/git-setup-releases.d.ts +3 -0
- package/dist/modules/git/commands/git-setup-releases.d.ts.map +1 -0
- package/dist/modules/git/commands/git-setup-releases.js +128 -0
- package/dist/modules/git/commands/git-setup-releases.js.map +1 -0
- package/dist/modules/git/errors/git-errors.d.ts +18 -0
- package/dist/modules/git/errors/git-errors.d.ts.map +1 -0
- package/dist/modules/git/errors/git-errors.js +35 -0
- package/dist/modules/git/errors/git-errors.js.map +1 -0
- package/dist/modules/git/index.d.ts +3 -0
- package/dist/modules/git/index.d.ts.map +1 -0
- package/dist/modules/git/index.js +3 -0
- package/dist/modules/git/index.js.map +1 -0
- package/dist/modules/git/services/git-service.d.ts +19 -0
- package/dist/modules/git/services/git-service.d.ts.map +1 -0
- package/dist/modules/git/services/git-service.js +46 -0
- package/dist/modules/git/services/git-service.js.map +1 -0
- package/dist/modules/git/services/github-service.d.ts +27 -0
- package/dist/modules/git/services/github-service.d.ts.map +1 -0
- package/dist/modules/git/services/github-service.js +45 -0
- package/dist/modules/git/services/github-service.js.map +1 -0
- package/dist/modules/workspace/commands/init-workspace.d.ts.map +1 -1
- package/dist/modules/workspace/commands/init-workspace.js +4 -5
- package/dist/modules/workspace/commands/init-workspace.js.map +1 -1
- package/dist/modules/workspace/services/workspace-service.d.ts +2 -1
- package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -1
- package/dist/modules/workspace/services/workspace-service.js +27 -1
- package/dist/modules/workspace/services/workspace-service.js.map +1 -1
- package/dist/templates/workspace/.github/workflows/ci.yml +99 -0
- package/dist/templates/workspace/package.json +15 -1
- package/package.json +2 -2
- package/src/cli.ts +5 -0
- package/src/infrastructure/git.ts +86 -0
- package/src/infrastructure/github.ts +111 -0
- package/src/modules/git/commands/git-connect.ts +183 -0
- package/src/modules/git/commands/git-setup-releases.ts +148 -0
- package/src/modules/git/errors/git-errors.ts +37 -0
- package/src/modules/git/index.ts +2 -0
- package/src/modules/git/services/git-service.ts +52 -0
- package/src/modules/git/services/github-service.ts +52 -0
- package/src/modules/workspace/commands/init-workspace.ts +4 -6
- package/src/modules/workspace/services/workspace-service.ts +30 -1
- package/templates/workspace/.github/workflows/ci.yml +99 -0
- package/templates/workspace/package.json +4 -0
- package/dist/app-templates/webapp/.env.ci +0 -6
- package/dist/app-templates/webapp/.env.example +0 -9
- package/dist/app-templates/webapp/.eslintrc.json +0 -6
- package/dist/app-templates/webapp/README.md.hbs +0 -80
- package/dist/app-templates/webapp/app/about/page.tsx.hbs +0 -41
- package/dist/app-templates/webapp/app/dashboard/page.tsx.hbs +0 -51
- package/dist/app-templates/webapp/app/globals.css +0 -31
- package/dist/app-templates/webapp/app/layout.tsx.hbs +0 -26
- package/dist/app-templates/webapp/app/page.tsx.hbs +0 -30
- package/dist/app-templates/webapp/next.config.js +0 -99
- package/dist/app-templates/webapp/package.json.hbs +0 -30
- package/dist/app-templates/webapp/postcss.config.js +0 -6
- package/dist/app-templates/webapp/tailwind.config.ts +0 -24
- package/dist/app-templates/webapp/tsconfig.json +0 -29
- package/dist/app-templates/webapp/vercel.json.hbs +0 -7
- package/dist/modules/catalog/schemas/schemas/catalog-ui-components.schema.json +0 -145
- package/dist/plugins/theme/package.json +0 -32
- package/dist/plugins/theme/plugin.json +0 -9
- package/dist/plugins/theme/src/generator.ts +0 -92
- package/dist/plugins/theme/src/utils/config-modifier.ts +0 -142
- package/dist/plugins/theme/src/utils/css-modifier.ts +0 -89
- package/dist/plugins/theme/templates/app/theme-test/page.tsx +0 -156
- package/dist/plugins/theme/templates/src/modules/theme/README.md +0 -209
- package/dist/plugins/theme/templates/src/modules/theme/config/brand.css +0 -23
- package/dist/plugins/theme/tsconfig.json +0 -14
- package/dist/plugins/theme/tsup.config.ts +0 -10
- package/dist/templates/templates/startup/apps/.gitkeep +0 -8
- package/dist/templates/templates/workspace/.launch77/workspace.json +0 -3
- package/dist/templates/templates/workspace/README.md +0 -62
- package/dist/templates/templates/workspace/app-templates/.gitkeep +0 -1
- package/dist/templates/templates/workspace/apps/.gitkeep +0 -1
- package/dist/templates/templates/workspace/libraries/.gitkeep +0 -1
- package/dist/templates/templates/workspace/package.json +0 -31
- package/dist/templates/templates/workspace/plugins/.gitkeep +0 -1
- package/dist/templates/templates/workspace/tsconfig.json +0 -22
- package/dist/templates/templates/workspace/turbo.json +0 -25
- /package/dist/templates/{templates/workspace → workspace}/.eslintignore +0 -0
- /package/dist/templates/{templates/workspace → workspace}/.eslintrc.js +0 -0
- /package/dist/templates/{templates/workspace → workspace}/.husky/pre-push +0 -0
- /package/dist/templates/{templates/workspace → workspace}/.lintstagedrc.json +0 -0
- /package/dist/templates/{templates/workspace → workspace}/.prettierrc +0 -0
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://launch77.io/schemas/catalog-ui-components.schema.json",
|
|
4
|
-
"title": "Launch77 UI Components Catalog",
|
|
5
|
-
"description": "Catalog metadata for UI component libraries. Generated during library build and consumed by Launch77 platform for component discovery.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["catalogType", "catalogVersion", "packageName", "packageVersion", "generatedAt", "components"],
|
|
8
|
-
"additionalProperties": false,
|
|
9
|
-
"properties": {
|
|
10
|
-
"catalogType": {
|
|
11
|
-
"type": "string",
|
|
12
|
-
"const": "ui-components",
|
|
13
|
-
"description": "Type of catalog - identifies this as a UI components catalog"
|
|
14
|
-
},
|
|
15
|
-
"catalogVersion": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
18
|
-
"description": "Semantic version of the catalog schema (e.g., 0.1.0)"
|
|
19
|
-
},
|
|
20
|
-
"packageName": {
|
|
21
|
-
"type": "string",
|
|
22
|
-
"pattern": "^@[a-z0-9-]+/[a-z0-9-]+$",
|
|
23
|
-
"description": "NPM package name (e.g., @launch77/ui)"
|
|
24
|
-
},
|
|
25
|
-
"packageVersion": {
|
|
26
|
-
"type": "string",
|
|
27
|
-
"description": "Version of the package containing these components"
|
|
28
|
-
},
|
|
29
|
-
"generatedAt": {
|
|
30
|
-
"type": "string",
|
|
31
|
-
"format": "date-time",
|
|
32
|
-
"description": "ISO 8601 timestamp when catalog was generated"
|
|
33
|
-
},
|
|
34
|
-
"components": {
|
|
35
|
-
"type": "array",
|
|
36
|
-
"description": "Array of UI component metadata",
|
|
37
|
-
"items": {
|
|
38
|
-
"$ref": "#/definitions/UiComponent"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"definitions": {
|
|
43
|
-
"UiComponent": {
|
|
44
|
-
"type": "object",
|
|
45
|
-
"description": "Metadata for a single UI component",
|
|
46
|
-
"required": ["id", "kind", "name", "packageName", "category", "description", "tags"],
|
|
47
|
-
"additionalProperties": false,
|
|
48
|
-
"properties": {
|
|
49
|
-
"id": {
|
|
50
|
-
"type": "string",
|
|
51
|
-
"pattern": "^@[a-z0-9-]+/[a-z0-9-]+#[A-Z][a-zA-Z0-9]*$",
|
|
52
|
-
"description": "Unique identifier: packageName#ComponentName (e.g., @launch77/ui#Button)"
|
|
53
|
-
},
|
|
54
|
-
"kind": {
|
|
55
|
-
"type": "string",
|
|
56
|
-
"const": "ui_component",
|
|
57
|
-
"description": "Component kind - always 'ui_component' for UI components"
|
|
58
|
-
},
|
|
59
|
-
"name": {
|
|
60
|
-
"type": "string",
|
|
61
|
-
"pattern": "^[A-Z][a-zA-Z0-9]*$",
|
|
62
|
-
"description": "Component name in PascalCase (e.g., Button, AlertDialog)"
|
|
63
|
-
},
|
|
64
|
-
"packageName": {
|
|
65
|
-
"type": "string",
|
|
66
|
-
"pattern": "^@[a-z0-9-]+/[a-z0-9-]+$",
|
|
67
|
-
"description": "NPM package containing this component"
|
|
68
|
-
},
|
|
69
|
-
"category": {
|
|
70
|
-
"type": "string",
|
|
71
|
-
"enum": ["actions", "forms", "feedback", "layout", "marketing", "typography", "infrastructure", "compliance"],
|
|
72
|
-
"description": "Component category for organization and discovery"
|
|
73
|
-
},
|
|
74
|
-
"description": {
|
|
75
|
-
"type": "string",
|
|
76
|
-
"minLength": 1,
|
|
77
|
-
"description": "Human-readable description of the component"
|
|
78
|
-
},
|
|
79
|
-
"tags": {
|
|
80
|
-
"type": "array",
|
|
81
|
-
"description": "Search tags for component discovery",
|
|
82
|
-
"items": {
|
|
83
|
-
"type": "string",
|
|
84
|
-
"minLength": 1
|
|
85
|
-
},
|
|
86
|
-
"minItems": 1
|
|
87
|
-
},
|
|
88
|
-
"props": {
|
|
89
|
-
"type": "array",
|
|
90
|
-
"description": "Component props (optional - extracted from TypeScript)",
|
|
91
|
-
"items": {
|
|
92
|
-
"type": "string"
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
"sourcePath": {
|
|
96
|
-
"type": "string",
|
|
97
|
-
"description": "Relative path to component source file from repository root (optional)"
|
|
98
|
-
},
|
|
99
|
-
"library": {
|
|
100
|
-
"type": "string",
|
|
101
|
-
"description": "Parent library package name (optional - typically same as packageName)"
|
|
102
|
-
},
|
|
103
|
-
"guidance": {
|
|
104
|
-
"type": "object",
|
|
105
|
-
"description": "Usage guidance for the component (optional)",
|
|
106
|
-
"additionalProperties": false,
|
|
107
|
-
"properties": {
|
|
108
|
-
"whenToUse": {
|
|
109
|
-
"type": "string",
|
|
110
|
-
"description": "When to use this component"
|
|
111
|
-
},
|
|
112
|
-
"whenNotToUse": {
|
|
113
|
-
"type": "string",
|
|
114
|
-
"description": "When NOT to use this component (anti-patterns)"
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
"example": {
|
|
119
|
-
"type": "string",
|
|
120
|
-
"description": "Code example showing component usage (optional)"
|
|
121
|
-
},
|
|
122
|
-
"examples": {
|
|
123
|
-
"type": "object",
|
|
124
|
-
"description": "Reference to examples module (optional)",
|
|
125
|
-
"required": ["module", "export", "sourcePath"],
|
|
126
|
-
"additionalProperties": false,
|
|
127
|
-
"properties": {
|
|
128
|
-
"module": {
|
|
129
|
-
"type": "string",
|
|
130
|
-
"description": "Module path to import examples (e.g., @launch77/ui/examples)"
|
|
131
|
-
},
|
|
132
|
-
"export": {
|
|
133
|
-
"type": "string",
|
|
134
|
-
"description": "Export name for examples (e.g., ButtonExamples)"
|
|
135
|
-
},
|
|
136
|
-
"sourcePath": {
|
|
137
|
-
"type": "string",
|
|
138
|
-
"description": "Relative path to examples source file from repository root"
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@launch77-shared/plugin-theme",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Launch77 theme plugin - Design system with Tailwind preset",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/generator.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"generate": "./dist/generator.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist/",
|
|
12
|
-
"templates/",
|
|
13
|
-
"plugin.json"
|
|
14
|
-
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "tsup",
|
|
17
|
-
"dev": "tsup --watch",
|
|
18
|
-
"typecheck": "tsc --noEmit"
|
|
19
|
-
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"@launch77/plugin-runtime": "file:../../../launch77-cli/libraries/plugin-runtime/launch77-plugin-runtime-0.1.0.tgz",
|
|
22
|
-
"chalk": "^5.3.0"
|
|
23
|
-
},
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@types/node": "^20.10.0",
|
|
26
|
-
"tsup": "^8.0.0",
|
|
27
|
-
"typescript": "^5.3.0"
|
|
28
|
-
},
|
|
29
|
-
"publishConfig": {
|
|
30
|
-
"access": "public"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import * as path from 'path'
|
|
3
|
-
import chalk from 'chalk'
|
|
4
|
-
import { StandardGenerator } from '@launch77/plugin-runtime'
|
|
5
|
-
import type { GeneratorContext } from '@launch77/plugin-runtime'
|
|
6
|
-
import { addTailwindPreset } from './utils/config-modifier.js'
|
|
7
|
-
import { insertCssImports } from './utils/css-modifier.js'
|
|
8
|
-
|
|
9
|
-
export class ThemeGenerator extends StandardGenerator {
|
|
10
|
-
constructor(context: GeneratorContext) {
|
|
11
|
-
super(context)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
protected async injectCode(): Promise<void> {
|
|
15
|
-
console.log(chalk.cyan('🔧 Configuring theme...\n'))
|
|
16
|
-
|
|
17
|
-
await this.updateGlobalsCss()
|
|
18
|
-
await this.updateTailwindConfig()
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
private async updateGlobalsCss(): Promise<void> {
|
|
22
|
-
const globalsCssPath = path.join(this.context.appPath, 'app/globals.css')
|
|
23
|
-
|
|
24
|
-
const result = await insertCssImports(globalsCssPath, [
|
|
25
|
-
"@import '@launch77-shared/lib-theme/tokens.css';",
|
|
26
|
-
"@import '../src/modules/theme/config/brand.css';",
|
|
27
|
-
])
|
|
28
|
-
|
|
29
|
-
if (result.success) {
|
|
30
|
-
if (result.alreadyExists) {
|
|
31
|
-
console.log(chalk.gray(' ✓ CSS imports already exist in app/globals.css'))
|
|
32
|
-
} else {
|
|
33
|
-
console.log(chalk.green(' ✓ Added CSS imports to app/globals.css'))
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
console.log(chalk.yellow(` ⚠️ Could not auto-configure app/globals.css: ${result.error}`))
|
|
37
|
-
console.log(chalk.gray(' You will need to add imports manually'))
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private async updateTailwindConfig(): Promise<void> {
|
|
42
|
-
const tailwindConfigPath = path.join(this.context.appPath, 'tailwind.config.ts')
|
|
43
|
-
|
|
44
|
-
const result = await addTailwindPreset(tailwindConfigPath, "require('@launch77-shared/lib-theme')")
|
|
45
|
-
|
|
46
|
-
if (result.success) {
|
|
47
|
-
if (result.alreadyExists) {
|
|
48
|
-
console.log(chalk.gray(' ✓ Theme preset already exists in tailwind.config.ts'))
|
|
49
|
-
} else {
|
|
50
|
-
console.log(chalk.green(' ✓ Added theme preset to tailwind.config.ts'))
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
console.log(chalk.yellow(` ⚠️ Could not auto-configure tailwind.config.ts: ${result.error}`))
|
|
54
|
-
console.log(chalk.gray(' You will need to add preset manually'))
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
protected showNextSteps(): void {
|
|
59
|
-
console.log(chalk.white('Next Steps:\n'))
|
|
60
|
-
console.log(chalk.gray('1. Test your theme:'))
|
|
61
|
-
console.log(chalk.gray(' Visit http://localhost:3000/theme-test\n'))
|
|
62
|
-
console.log(chalk.gray('2. Customize your brand:'))
|
|
63
|
-
console.log(chalk.gray(' Edit src/modules/theme/config/brand.css\n'))
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// CLI entry point
|
|
68
|
-
async function main() {
|
|
69
|
-
const args = process.argv.slice(2)
|
|
70
|
-
const appPath = args.find((arg) => arg.startsWith('--appPath='))?.split('=')[1]
|
|
71
|
-
const appName = args.find((arg) => arg.startsWith('--appName='))?.split('=')[1]
|
|
72
|
-
const workspaceName = args.find((arg) => arg.startsWith('--workspaceName='))?.split('=')[1]
|
|
73
|
-
const pluginPath = args.find((arg) => arg.startsWith('--pluginPath='))?.split('=')[1]
|
|
74
|
-
|
|
75
|
-
if (!appPath || !appName || !workspaceName || !pluginPath) {
|
|
76
|
-
console.error(chalk.red('Error: Missing required arguments'))
|
|
77
|
-
console.error('Usage: generate --appPath=<path> --appName=<name> --workspaceName=<workspace> --pluginPath=<path>')
|
|
78
|
-
process.exit(1)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const generator = new ThemeGenerator({ appPath, appName, workspaceName, pluginPath })
|
|
82
|
-
await generator.run()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Run if executed directly
|
|
86
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
87
|
-
main().catch((error) => {
|
|
88
|
-
console.error(chalk.red('\n❌ Error during theme setup:'))
|
|
89
|
-
console.error(error)
|
|
90
|
-
process.exit(1)
|
|
91
|
-
})
|
|
92
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises'
|
|
2
|
-
|
|
3
|
-
export interface AddTailwindPresetResult {
|
|
4
|
-
success: boolean
|
|
5
|
-
error?: string
|
|
6
|
-
alreadyExists?: boolean
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Add Launch77 theme preset to tailwind.config.ts
|
|
11
|
-
* Idempotent - won't duplicate if preset already exists
|
|
12
|
-
*/
|
|
13
|
-
export async function addTailwindPreset(
|
|
14
|
-
filePath: string,
|
|
15
|
-
presetRequire: string
|
|
16
|
-
): Promise<AddTailwindPresetResult> {
|
|
17
|
-
try {
|
|
18
|
-
// Check if file exists
|
|
19
|
-
const fileExists = await fs
|
|
20
|
-
.access(filePath)
|
|
21
|
-
.then(() => true)
|
|
22
|
-
.catch(() => false)
|
|
23
|
-
|
|
24
|
-
if (!fileExists) {
|
|
25
|
-
return {
|
|
26
|
-
success: false,
|
|
27
|
-
error: `File not found: ${filePath}`,
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Read file content
|
|
32
|
-
const content = await fs.readFile(filePath, 'utf-8')
|
|
33
|
-
|
|
34
|
-
// Check if preset already exists
|
|
35
|
-
if (content.includes(presetRequire)) {
|
|
36
|
-
return {
|
|
37
|
-
success: true,
|
|
38
|
-
alreadyExists: true,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Find the config object and check for existing presets property
|
|
43
|
-
const lines = content.split('\n')
|
|
44
|
-
let configStartIndex = -1
|
|
45
|
-
let presetsIndex = -1
|
|
46
|
-
let indentation = ' '
|
|
47
|
-
|
|
48
|
-
// Find where the config object starts
|
|
49
|
-
for (let i = 0; i < lines.length; i++) {
|
|
50
|
-
const line = lines[i]
|
|
51
|
-
|
|
52
|
-
// Look for the config object declaration
|
|
53
|
-
if (line.includes('satisfies Config') || line.includes(': Config =')) {
|
|
54
|
-
configStartIndex = i
|
|
55
|
-
// Detect indentation from first property
|
|
56
|
-
for (let j = i + 1; j < lines.length; j++) {
|
|
57
|
-
const propLine = lines[j]
|
|
58
|
-
const match = propLine.match(/^(\s+)\w+:/)
|
|
59
|
-
if (match) {
|
|
60
|
-
indentation = match[1]
|
|
61
|
-
break
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Check if presets property already exists
|
|
67
|
-
if (line.includes('presets:')) {
|
|
68
|
-
presetsIndex = i
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (configStartIndex === -1) {
|
|
73
|
-
return {
|
|
74
|
-
success: false,
|
|
75
|
-
error: 'Could not find Tailwind config object',
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
let newContent: string
|
|
80
|
-
|
|
81
|
-
if (presetsIndex !== -1) {
|
|
82
|
-
// Presets property exists - add to existing array
|
|
83
|
-
const presetsLine = lines[presetsIndex]
|
|
84
|
-
|
|
85
|
-
// Check if it's an empty array
|
|
86
|
-
if (presetsLine.includes('[]')) {
|
|
87
|
-
// Replace empty array with array containing our preset
|
|
88
|
-
lines[presetsIndex] = presetsLine.replace('[]', `[${presetRequire}]`)
|
|
89
|
-
} else {
|
|
90
|
-
// Add to existing array - find the opening bracket
|
|
91
|
-
const openBracketIndex = presetsLine.indexOf('[')
|
|
92
|
-
if (openBracketIndex !== -1) {
|
|
93
|
-
// Insert after the opening bracket
|
|
94
|
-
lines[presetsIndex] =
|
|
95
|
-
presetsLine.slice(0, openBracketIndex + 1) +
|
|
96
|
-
presetRequire +
|
|
97
|
-
', ' +
|
|
98
|
-
presetsLine.slice(openBracketIndex + 1)
|
|
99
|
-
} else {
|
|
100
|
-
return {
|
|
101
|
-
success: false,
|
|
102
|
-
error: 'Could not parse existing presets array',
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
newContent = lines.join('\n')
|
|
107
|
-
} else {
|
|
108
|
-
// No presets property - add it as first property in config
|
|
109
|
-
// Find the line after config object starts (skip opening brace)
|
|
110
|
-
let insertIndex = configStartIndex + 1
|
|
111
|
-
|
|
112
|
-
// Skip the opening brace line if it's on the same line
|
|
113
|
-
if (lines[configStartIndex].includes('{')) {
|
|
114
|
-
// Opening brace is on same line, insert after
|
|
115
|
-
} else {
|
|
116
|
-
// Find the opening brace
|
|
117
|
-
while (insertIndex < lines.length && !lines[insertIndex].includes('{')) {
|
|
118
|
-
insertIndex++
|
|
119
|
-
}
|
|
120
|
-
insertIndex++
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Insert presets property
|
|
124
|
-
const presetsLine = `${indentation}presets: [${presetRequire}],`
|
|
125
|
-
lines.splice(insertIndex, 0, presetsLine)
|
|
126
|
-
|
|
127
|
-
newContent = lines.join('\n')
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Write back to file
|
|
131
|
-
await fs.writeFile(filePath, newContent, 'utf-8')
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
success: true,
|
|
135
|
-
}
|
|
136
|
-
} catch (error) {
|
|
137
|
-
return {
|
|
138
|
-
success: false,
|
|
139
|
-
error: error instanceof Error ? error.message : String(error),
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises'
|
|
2
|
-
|
|
3
|
-
export interface InsertCssImportsResult {
|
|
4
|
-
success: boolean
|
|
5
|
-
error?: string
|
|
6
|
-
alreadyExists?: boolean
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Insert CSS imports after @tailwind directives
|
|
11
|
-
* Idempotent - won't duplicate if imports already exist
|
|
12
|
-
*/
|
|
13
|
-
export async function insertCssImports(
|
|
14
|
-
filePath: string,
|
|
15
|
-
imports: string[]
|
|
16
|
-
): Promise<InsertCssImportsResult> {
|
|
17
|
-
try {
|
|
18
|
-
// Check if file exists
|
|
19
|
-
const fileExists = await fs
|
|
20
|
-
.access(filePath)
|
|
21
|
-
.then(() => true)
|
|
22
|
-
.catch(() => false)
|
|
23
|
-
|
|
24
|
-
if (!fileExists) {
|
|
25
|
-
return {
|
|
26
|
-
success: false,
|
|
27
|
-
error: `File not found: ${filePath}`,
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Read file content
|
|
32
|
-
const content = await fs.readFile(filePath, 'utf-8')
|
|
33
|
-
|
|
34
|
-
// Check if imports already exist
|
|
35
|
-
const allImportsExist = imports.every((importLine) => content.includes(importLine))
|
|
36
|
-
|
|
37
|
-
if (allImportsExist) {
|
|
38
|
-
return {
|
|
39
|
-
success: true,
|
|
40
|
-
alreadyExists: true,
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Find the last @tailwind directive
|
|
45
|
-
const lines = content.split('\n')
|
|
46
|
-
let lastTailwindIndex = -1
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < lines.length; i++) {
|
|
49
|
-
if (lines[i].trim().startsWith('@tailwind')) {
|
|
50
|
-
lastTailwindIndex = i
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (lastTailwindIndex === -1) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: 'Could not find @tailwind directives',
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Insert imports after the last @tailwind directive
|
|
62
|
-
const importsToAdd = imports.filter((importLine) => !content.includes(importLine))
|
|
63
|
-
|
|
64
|
-
if (importsToAdd.length === 0) {
|
|
65
|
-
return {
|
|
66
|
-
success: true,
|
|
67
|
-
alreadyExists: true,
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Build the new content
|
|
72
|
-
const before = lines.slice(0, lastTailwindIndex + 1)
|
|
73
|
-
const after = lines.slice(lastTailwindIndex + 1)
|
|
74
|
-
|
|
75
|
-
const newContent = [...before, '', '/* Launch77 Theme */', ...importsToAdd, ...after].join('\n')
|
|
76
|
-
|
|
77
|
-
// Write back to file
|
|
78
|
-
await fs.writeFile(filePath, newContent, 'utf-8')
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
success: true,
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
return {
|
|
85
|
-
success: false,
|
|
86
|
-
error: error instanceof Error ? error.message : String(error),
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import Link from 'next/link'
|
|
2
|
-
|
|
3
|
-
export default function ThemeTestPage() {
|
|
4
|
-
return (
|
|
5
|
-
<main className="min-h-screen bg-background p-8">
|
|
6
|
-
<div className="max-w-4xl mx-auto space-y-8">
|
|
7
|
-
{/* Header */}
|
|
8
|
-
<div className="space-y-2">
|
|
9
|
-
<h1 className="text-4xl font-bold text-foreground">
|
|
10
|
-
Theme Test Page
|
|
11
|
-
</h1>
|
|
12
|
-
<p className="text-muted-foreground">
|
|
13
|
-
Test your theme colors and see changes in real-time
|
|
14
|
-
</p>
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
{/* Instructions */}
|
|
18
|
-
<div className="bg-card border border-border rounded-lg p-6 space-y-4">
|
|
19
|
-
<h2 className="text-2xl font-semibold text-card-foreground">
|
|
20
|
-
How to Test Your Theme
|
|
21
|
-
</h2>
|
|
22
|
-
<ol className="list-decimal list-inside space-y-2 text-card-foreground">
|
|
23
|
-
<li>Open <code className="bg-muted px-2 py-1 rounded text-sm">app/brand.css</code></li>
|
|
24
|
-
<li>Uncomment and change the color values (HSL format)</li>
|
|
25
|
-
<li>Save the file and watch this page update automatically</li>
|
|
26
|
-
</ol>
|
|
27
|
-
<p className="text-sm text-muted-foreground">
|
|
28
|
-
Example: Change <code className="bg-muted px-2 py-1 rounded">--color-primary</code> to <code className="bg-muted px-2 py-1 rounded">270 91% 65%</code> for purple
|
|
29
|
-
</p>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
{/* Color Swatches */}
|
|
33
|
-
<div className="space-y-4">
|
|
34
|
-
<h2 className="text-2xl font-semibold text-foreground">
|
|
35
|
-
Theme Colors
|
|
36
|
-
</h2>
|
|
37
|
-
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
|
38
|
-
<div className="space-y-2">
|
|
39
|
-
<div className="h-24 bg-primary rounded-lg flex items-center justify-center">
|
|
40
|
-
<span className="text-primary-foreground font-semibold">Primary</span>
|
|
41
|
-
</div>
|
|
42
|
-
<p className="text-sm text-muted-foreground">--color-primary</p>
|
|
43
|
-
</div>
|
|
44
|
-
|
|
45
|
-
<div className="space-y-2">
|
|
46
|
-
<div className="h-24 bg-secondary rounded-lg flex items-center justify-center">
|
|
47
|
-
<span className="text-secondary-foreground font-semibold">Secondary</span>
|
|
48
|
-
</div>
|
|
49
|
-
<p className="text-sm text-muted-foreground">--color-secondary</p>
|
|
50
|
-
</div>
|
|
51
|
-
|
|
52
|
-
<div className="space-y-2">
|
|
53
|
-
<div className="h-24 bg-accent rounded-lg flex items-center justify-center">
|
|
54
|
-
<span className="text-accent-foreground font-semibold">Accent</span>
|
|
55
|
-
</div>
|
|
56
|
-
<p className="text-sm text-muted-foreground">--color-accent</p>
|
|
57
|
-
</div>
|
|
58
|
-
|
|
59
|
-
<div className="space-y-2">
|
|
60
|
-
<div className="h-24 bg-muted rounded-lg flex items-center justify-center">
|
|
61
|
-
<span className="text-muted-foreground font-semibold">Muted</span>
|
|
62
|
-
</div>
|
|
63
|
-
<p className="text-sm text-muted-foreground">--color-muted</p>
|
|
64
|
-
</div>
|
|
65
|
-
|
|
66
|
-
<div className="space-y-2">
|
|
67
|
-
<div className="h-24 bg-card border border-border rounded-lg flex items-center justify-center">
|
|
68
|
-
<span className="text-card-foreground font-semibold">Card</span>
|
|
69
|
-
</div>
|
|
70
|
-
<p className="text-sm text-muted-foreground">--color-card</p>
|
|
71
|
-
</div>
|
|
72
|
-
|
|
73
|
-
<div className="space-y-2">
|
|
74
|
-
<div className="h-24 bg-destructive rounded-lg flex items-center justify-center">
|
|
75
|
-
<span className="text-destructive-foreground font-semibold">Destructive</span>
|
|
76
|
-
</div>
|
|
77
|
-
<p className="text-sm text-muted-foreground">--color-destructive</p>
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
|
|
82
|
-
{/* Interactive Components */}
|
|
83
|
-
<div className="space-y-4">
|
|
84
|
-
<h2 className="text-2xl font-semibold text-foreground">
|
|
85
|
-
Component Examples
|
|
86
|
-
</h2>
|
|
87
|
-
|
|
88
|
-
{/* Buttons */}
|
|
89
|
-
<div className="space-y-3">
|
|
90
|
-
<h3 className="text-lg font-medium text-foreground">Buttons</h3>
|
|
91
|
-
<div className="flex flex-wrap gap-3">
|
|
92
|
-
<button className="px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:opacity-90 transition-opacity font-medium">
|
|
93
|
-
Primary Button
|
|
94
|
-
</button>
|
|
95
|
-
<button className="px-6 py-3 bg-secondary text-secondary-foreground rounded-lg hover:opacity-90 transition-opacity font-medium">
|
|
96
|
-
Secondary Button
|
|
97
|
-
</button>
|
|
98
|
-
<button className="px-6 py-3 bg-accent text-accent-foreground rounded-lg hover:opacity-90 transition-opacity font-medium">
|
|
99
|
-
Accent Button
|
|
100
|
-
</button>
|
|
101
|
-
<button className="px-6 py-3 bg-destructive text-destructive-foreground rounded-lg hover:opacity-90 transition-opacity font-medium">
|
|
102
|
-
Destructive Button
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
</div>
|
|
106
|
-
|
|
107
|
-
{/* Cards */}
|
|
108
|
-
<div className="space-y-3">
|
|
109
|
-
<h3 className="text-lg font-medium text-foreground">Cards</h3>
|
|
110
|
-
<div className="grid md:grid-cols-2 gap-4">
|
|
111
|
-
<div className="bg-card border border-border rounded-lg p-6 space-y-2">
|
|
112
|
-
<h4 className="font-semibold text-card-foreground">Card Title</h4>
|
|
113
|
-
<p className="text-muted-foreground">
|
|
114
|
-
This card uses the card background and border colors from your theme.
|
|
115
|
-
</p>
|
|
116
|
-
</div>
|
|
117
|
-
<div className="bg-muted rounded-lg p-6 space-y-2">
|
|
118
|
-
<h4 className="font-semibold text-foreground">Muted Card</h4>
|
|
119
|
-
<p className="text-muted-foreground">
|
|
120
|
-
This card uses the muted background color for a subtle effect.
|
|
121
|
-
</p>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</div>
|
|
125
|
-
|
|
126
|
-
{/* Form Elements */}
|
|
127
|
-
<div className="space-y-3">
|
|
128
|
-
<h3 className="text-lg font-medium text-foreground">Form Elements</h3>
|
|
129
|
-
<div className="space-y-3 max-w-md">
|
|
130
|
-
<input
|
|
131
|
-
type="text"
|
|
132
|
-
placeholder="Input with border color"
|
|
133
|
-
className="w-full px-4 py-2 bg-background border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
|
|
134
|
-
/>
|
|
135
|
-
<textarea
|
|
136
|
-
placeholder="Textarea with theme colors"
|
|
137
|
-
rows={3}
|
|
138
|
-
className="w-full px-4 py-2 bg-background border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background"
|
|
139
|
-
/>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
</div>
|
|
143
|
-
|
|
144
|
-
{/* Back Link */}
|
|
145
|
-
<div className="pt-8 border-t border-border">
|
|
146
|
-
<Link
|
|
147
|
-
href="/"
|
|
148
|
-
className="inline-flex items-center text-primary hover:underline"
|
|
149
|
-
>
|
|
150
|
-
← Back to Home
|
|
151
|
-
</Link>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
</main>
|
|
155
|
-
)
|
|
156
|
-
}
|