@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cli.js +4 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/infrastructure/git.d.ts +37 -0
  5. package/dist/infrastructure/git.d.ts.map +1 -0
  6. package/dist/infrastructure/git.js +82 -0
  7. package/dist/infrastructure/git.js.map +1 -0
  8. package/dist/infrastructure/github.d.ts +43 -0
  9. package/dist/infrastructure/github.d.ts.map +1 -0
  10. package/dist/infrastructure/github.js +89 -0
  11. package/dist/infrastructure/github.js.map +1 -0
  12. package/dist/modules/catalog/schemas/catalog-ui-components.schema.json +2 -18
  13. package/dist/modules/git/commands/git-connect.d.ts +3 -0
  14. package/dist/modules/git/commands/git-connect.d.ts.map +1 -0
  15. package/dist/modules/git/commands/git-connect.js +156 -0
  16. package/dist/modules/git/commands/git-connect.js.map +1 -0
  17. package/dist/modules/git/commands/git-setup-releases.d.ts +3 -0
  18. package/dist/modules/git/commands/git-setup-releases.d.ts.map +1 -0
  19. package/dist/modules/git/commands/git-setup-releases.js +128 -0
  20. package/dist/modules/git/commands/git-setup-releases.js.map +1 -0
  21. package/dist/modules/git/errors/git-errors.d.ts +18 -0
  22. package/dist/modules/git/errors/git-errors.d.ts.map +1 -0
  23. package/dist/modules/git/errors/git-errors.js +35 -0
  24. package/dist/modules/git/errors/git-errors.js.map +1 -0
  25. package/dist/modules/git/index.d.ts +3 -0
  26. package/dist/modules/git/index.d.ts.map +1 -0
  27. package/dist/modules/git/index.js +3 -0
  28. package/dist/modules/git/index.js.map +1 -0
  29. package/dist/modules/git/services/git-service.d.ts +19 -0
  30. package/dist/modules/git/services/git-service.d.ts.map +1 -0
  31. package/dist/modules/git/services/git-service.js +46 -0
  32. package/dist/modules/git/services/git-service.js.map +1 -0
  33. package/dist/modules/git/services/github-service.d.ts +27 -0
  34. package/dist/modules/git/services/github-service.d.ts.map +1 -0
  35. package/dist/modules/git/services/github-service.js +45 -0
  36. package/dist/modules/git/services/github-service.js.map +1 -0
  37. package/dist/modules/workspace/commands/init-workspace.d.ts.map +1 -1
  38. package/dist/modules/workspace/commands/init-workspace.js +4 -5
  39. package/dist/modules/workspace/commands/init-workspace.js.map +1 -1
  40. package/dist/modules/workspace/services/workspace-service.d.ts +2 -1
  41. package/dist/modules/workspace/services/workspace-service.d.ts.map +1 -1
  42. package/dist/modules/workspace/services/workspace-service.js +27 -1
  43. package/dist/modules/workspace/services/workspace-service.js.map +1 -1
  44. package/dist/templates/workspace/.github/workflows/ci.yml +99 -0
  45. package/dist/templates/workspace/package.json +15 -1
  46. package/package.json +2 -2
  47. package/src/cli.ts +5 -0
  48. package/src/infrastructure/git.ts +86 -0
  49. package/src/infrastructure/github.ts +111 -0
  50. package/src/modules/git/commands/git-connect.ts +183 -0
  51. package/src/modules/git/commands/git-setup-releases.ts +148 -0
  52. package/src/modules/git/errors/git-errors.ts +37 -0
  53. package/src/modules/git/index.ts +2 -0
  54. package/src/modules/git/services/git-service.ts +52 -0
  55. package/src/modules/git/services/github-service.ts +52 -0
  56. package/src/modules/workspace/commands/init-workspace.ts +4 -6
  57. package/src/modules/workspace/services/workspace-service.ts +30 -1
  58. package/templates/workspace/.github/workflows/ci.yml +99 -0
  59. package/templates/workspace/package.json +4 -0
  60. package/dist/app-templates/webapp/.env.ci +0 -6
  61. package/dist/app-templates/webapp/.env.example +0 -9
  62. package/dist/app-templates/webapp/.eslintrc.json +0 -6
  63. package/dist/app-templates/webapp/README.md.hbs +0 -80
  64. package/dist/app-templates/webapp/app/about/page.tsx.hbs +0 -41
  65. package/dist/app-templates/webapp/app/dashboard/page.tsx.hbs +0 -51
  66. package/dist/app-templates/webapp/app/globals.css +0 -31
  67. package/dist/app-templates/webapp/app/layout.tsx.hbs +0 -26
  68. package/dist/app-templates/webapp/app/page.tsx.hbs +0 -30
  69. package/dist/app-templates/webapp/next.config.js +0 -99
  70. package/dist/app-templates/webapp/package.json.hbs +0 -30
  71. package/dist/app-templates/webapp/postcss.config.js +0 -6
  72. package/dist/app-templates/webapp/tailwind.config.ts +0 -24
  73. package/dist/app-templates/webapp/tsconfig.json +0 -29
  74. package/dist/app-templates/webapp/vercel.json.hbs +0 -7
  75. package/dist/modules/catalog/schemas/schemas/catalog-ui-components.schema.json +0 -145
  76. package/dist/plugins/theme/package.json +0 -32
  77. package/dist/plugins/theme/plugin.json +0 -9
  78. package/dist/plugins/theme/src/generator.ts +0 -92
  79. package/dist/plugins/theme/src/utils/config-modifier.ts +0 -142
  80. package/dist/plugins/theme/src/utils/css-modifier.ts +0 -89
  81. package/dist/plugins/theme/templates/app/theme-test/page.tsx +0 -156
  82. package/dist/plugins/theme/templates/src/modules/theme/README.md +0 -209
  83. package/dist/plugins/theme/templates/src/modules/theme/config/brand.css +0 -23
  84. package/dist/plugins/theme/tsconfig.json +0 -14
  85. package/dist/plugins/theme/tsup.config.ts +0 -10
  86. package/dist/templates/templates/startup/apps/.gitkeep +0 -8
  87. package/dist/templates/templates/workspace/.launch77/workspace.json +0 -3
  88. package/dist/templates/templates/workspace/README.md +0 -62
  89. package/dist/templates/templates/workspace/app-templates/.gitkeep +0 -1
  90. package/dist/templates/templates/workspace/apps/.gitkeep +0 -1
  91. package/dist/templates/templates/workspace/libraries/.gitkeep +0 -1
  92. package/dist/templates/templates/workspace/package.json +0 -31
  93. package/dist/templates/templates/workspace/plugins/.gitkeep +0 -1
  94. package/dist/templates/templates/workspace/tsconfig.json +0 -22
  95. package/dist/templates/templates/workspace/turbo.json +0 -25
  96. /package/dist/templates/{templates/workspace → workspace}/.eslintignore +0 -0
  97. /package/dist/templates/{templates/workspace → workspace}/.eslintrc.js +0 -0
  98. /package/dist/templates/{templates/workspace → workspace}/.husky/pre-push +0 -0
  99. /package/dist/templates/{templates/workspace → workspace}/.lintstagedrc.json +0 -0
  100. /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,9 +0,0 @@
1
- {
2
- "name": "theme",
3
- "version": "1.0.0",
4
- "description": "Launch77 design system with Tailwind preset and design tokens",
5
- "pluginDependencies": {},
6
- "libraryDependencies": {
7
- "@launch77-shared/lib-theme": "^0.1.0"
8
- }
9
- }
@@ -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
- }