Skip to content

Commit 5531b1c

Browse files
author
Test User
committed
feat: Complete Phase 2 - Design System (DPR) Generation
- Add interactive DPR questionnaire TUI with 20+ questions - Support for text input, select lists, scale ratings, and multi-select - Progress bar, back navigation, and answer persistence - Questions cover audience, emotions, style, colors, typography, layout, components, animation, accessibility, and responsive design - Implement DPR.md generator with comprehensive sections: - Executive Summary, Audience Analysis, Design Principles - Visual Identity, Layout Guidelines, Component Library - Animation Guidelines, Accessibility, Responsive Design - Wireframes & Mockups, Implementation Guidelines - Add design-tokens.json generator: - Colors (primary, secondary, accent, neutral, semantic) - Typography (fonts, sizes, weights, line heights) - Spacing scale and values - Border radius, shadows, breakpoints - Create design_rules.mdc generator for AI agents: - Color usage rules, typography rules, spacing rules - Component guidelines, responsive rules - Accessibility requirements (WCAG compliance) - Tailwind CSS code style guidelines - Integrate design wizard with TUI menu - Wire up ApplyDesign command to menu executor Phase 2 of v0.0.19-beta complete.
1 parent 27f4b17 commit 5531b1c

File tree

9 files changed

+2157
-35
lines changed

9 files changed

+2157
-35
lines changed

internal/commands/design.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package commands
2+
3+
import (
4+
"github.com/DoPlan-dev/CLI/internal/wizard"
5+
)
6+
7+
// ApplyDesign launches the design system (DPR) wizard
8+
func ApplyDesign() error {
9+
return wizard.RunDesignWizard()
10+
}
11+

internal/commands/executor.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package commands
2+
3+
import (
4+
"github.com/DoPlan-dev/CLI/internal/tui"
5+
)
6+
7+
// TUICommandExecutor implements the CommandExecutor interface for the TUI
8+
type TUICommandExecutor struct{}
9+
10+
// NewTUICommandExecutor creates a new command executor for the TUI
11+
func NewTUICommandExecutor() tui.CommandExecutor {
12+
return &TUICommandExecutor{}
13+
}
14+
15+
func (e *TUICommandExecutor) RunDevServer() error {
16+
return RunDevServer()
17+
}
18+
19+
func (e *TUICommandExecutor) UndoLastAction() error {
20+
return UndoLastAction()
21+
}
22+
23+
func (e *TUICommandExecutor) CreateNewProject() error {
24+
return CreateNewProject()
25+
}
26+
27+
func (e *TUICommandExecutor) DeployProject() error {
28+
return DeployProject()
29+
}
30+
31+
func (e *TUICommandExecutor) PublishPackage() error {
32+
return PublishPackage()
33+
}
34+
35+
func (e *TUICommandExecutor) RunSecurityScan() error {
36+
return RunSecurityScan()
37+
}
38+
39+
func (e *TUICommandExecutor) AutoFix() error {
40+
return AutoFix()
41+
}
42+
43+
func (e *TUICommandExecutor) DiscussIdea() error {
44+
return DiscussIdea()
45+
}
46+
47+
func (e *TUICommandExecutor) GenerateDocuments() error {
48+
return GenerateDocuments()
49+
}
50+
51+
func (e *TUICommandExecutor) CreatePlan() error {
52+
return CreatePlan()
53+
}
54+
55+
func (e *TUICommandExecutor) UpdateProgress() error {
56+
return UpdateProgress()
57+
}
58+
59+
func (e *TUICommandExecutor) ManageAPIKeys() error {
60+
// TODO: Implement keys command
61+
return nil
62+
}
63+
64+
func (e *TUICommandExecutor) ApplyDesign() error {
65+
return ApplyDesign()
66+
}
67+
68+
func (e *TUICommandExecutor) SetupIntegration() error {
69+
// TODO: Implement integration command
70+
return nil
71+
}
72+

internal/dpr/cursor_rules.go

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
package dpr
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
// CursorRulesGenerator generates design_rules.mdc for AI agents
12+
type CursorRulesGenerator struct {
13+
projectRoot string
14+
data *DPRData
15+
}
16+
17+
// NewCursorRulesGenerator creates a new cursor rules generator
18+
func NewCursorRulesGenerator(projectRoot string, data *DPRData) *CursorRulesGenerator {
19+
return &CursorRulesGenerator{
20+
projectRoot: projectRoot,
21+
data: data,
22+
}
23+
}
24+
25+
// Generate creates the design_rules.mdc file
26+
func (crg *CursorRulesGenerator) Generate() error {
27+
rulesPath := filepath.Join(crg.projectRoot, ".doplan", "ai", "rules", "design_rules.mdc")
28+
29+
// Ensure directory exists
30+
if err := os.MkdirAll(filepath.Dir(rulesPath), 0755); err != nil {
31+
return fmt.Errorf("failed to create ai/rules directory: %w", err)
32+
}
33+
34+
content := crg.generateRulesContent()
35+
36+
return os.WriteFile(rulesPath, []byte(content), 0644)
37+
}
38+
39+
func (crg *CursorRulesGenerator) generateRulesContent() string {
40+
var content strings.Builder
41+
42+
// Header
43+
content.WriteString("# Design Rules\n\n")
44+
content.WriteString("This file defines the design system rules that all AI agents MUST follow when working on this project.\n\n")
45+
content.WriteString("**Source:** Generated from DPR (Design Preferences & Requirements) questionnaire\n\n")
46+
content.WriteString("---\n\n")
47+
48+
// Color Usage Rules
49+
content.WriteString("## Color Usage Rules\n\n")
50+
content.WriteString(crg.generateColorRules())
51+
content.WriteString("\n\n")
52+
53+
// Typography Rules
54+
content.WriteString("## Typography Rules\n\n")
55+
content.WriteString(crg.generateTypographyRules())
56+
content.WriteString("\n\n")
57+
58+
// Spacing Rules
59+
content.WriteString("## Spacing Rules\n\n")
60+
content.WriteString(crg.generateSpacingRules())
61+
content.WriteString("\n\n")
62+
63+
// Component Guidelines
64+
content.WriteString("## Component Guidelines\n\n")
65+
content.WriteString(crg.generateComponentGuidelines())
66+
content.WriteString("\n\n")
67+
68+
// Responsive Rules
69+
content.WriteString("## Responsive Rules\n\n")
70+
content.WriteString(crg.generateResponsiveRules())
71+
content.WriteString("\n\n")
72+
73+
// Accessibility Requirements
74+
content.WriteString("## Accessibility Requirements\n\n")
75+
content.WriteString(crg.generateAccessibilityRules())
76+
content.WriteString("\n\n")
77+
78+
// Code Style
79+
content.WriteString("## Code Style\n\n")
80+
content.WriteString(crg.generateCodeStyleRules())
81+
content.WriteString("\n\n")
82+
83+
return content.String()
84+
}
85+
86+
func (crg *CursorRulesGenerator) generateColorRules() string {
87+
primary := crg.getAnswerString("color_primary", "#667eea")
88+
secondary := crg.getAnswerString("color_secondary", "#764ba2")
89+
90+
return fmt.Sprintf(`### Primary Colors
91+
- **Primary Color:** %s - Use for primary actions, links, and brand elements
92+
- **Secondary Color:** %s - Use for secondary actions and accents
93+
- **DO NOT** use colors outside the design system
94+
- **DO NOT** create new colors without updating the design tokens
95+
96+
### Color Usage
97+
- Use semantic colors (success, warning, error, info) from design tokens
98+
- Ensure sufficient contrast ratios (WCAG AA minimum)
99+
- Test colors in both light and dark modes if applicable
100+
- Use neutral colors for backgrounds and text`, primary, secondary)
101+
}
102+
103+
func (crg *CursorRulesGenerator) generateTypographyRules() string {
104+
style := crg.getAnswerString("typography_style", "Sans-serif")
105+
importance := crg.getAnswerInt("typography_importance", 3)
106+
107+
rules := fmt.Sprintf(`### Typography System
108+
- **Font Style:** %s
109+
- **Importance Level:** %d/5
110+
111+
### Type Scale
112+
Use the following font sizes from design tokens:
113+
- xs: 0.75rem (12px)
114+
- sm: 0.875rem (14px)
115+
- base: 1rem (16px)
116+
- lg: 1.125rem (18px)
117+
- xl: 1.25rem (20px)
118+
- 2xl: 1.5rem (24px)
119+
- 3xl: 1.875rem (30px)
120+
- 4xl: 2.25rem (36px)
121+
- 5xl: 3rem (48px)
122+
123+
### Line Heights
124+
- Use line-height values from design tokens
125+
- Headings: tight (1.25)
126+
- Body text: normal (1.5)
127+
- Long-form content: relaxed (1.625)
128+
129+
### Font Weights
130+
- Use font weights from design tokens
131+
- Headings: semibold (600) or bold (700)
132+
- Body text: normal (400) or medium (500)`, style, importance)
133+
134+
return rules
135+
}
136+
137+
func (crg *CursorRulesGenerator) generateSpacingRules() string {
138+
spacing := crg.getAnswerString("layout_spacing", "Moderate")
139+
140+
return fmt.Sprintf(`### Spacing System
141+
- **Spacing Preference:** %s
142+
- **Base Unit:** Use spacing values from design-tokens.json
143+
- **DO NOT** use arbitrary spacing values
144+
- **DO NOT** use magic numbers (e.g., margin: 17px)
145+
146+
### Spacing Guidelines
147+
- Use spacing scale: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64
148+
- Maintain consistent spacing rhythm
149+
- Use larger spacing for section separation
150+
- Use smaller spacing for related elements
151+
152+
### Tailwind Utilities
153+
When using Tailwind CSS, use spacing utilities:
154+
- p-4, m-4, gap-4, space-y-4, etc.
155+
- DO NOT use arbitrary values like p-[17px]`, spacing)
156+
}
157+
158+
func (crg *CursorRulesGenerator) generateComponentGuidelines() string {
159+
style := crg.getAnswerString("components_style", "Elevated")
160+
interactivity := crg.getAnswerInt("components_interactivity", 3)
161+
162+
return fmt.Sprintf(`### Component Style
163+
- **Style:** %s
164+
- **Interactivity Level:** %d/5
165+
166+
### Component Rules
167+
- All components MUST follow the design system
168+
- Use design tokens for colors, spacing, typography
169+
- Maintain consistent styling across components
170+
- Ensure all components have proper states:
171+
- Default
172+
- Hover
173+
- Active/Focus
174+
- Disabled
175+
- Error (if applicable)
176+
177+
### Component Structure
178+
- Use consistent border radius from tokens
179+
- Apply shadows according to component style (%s)
180+
- Ensure proper focus indicators for accessibility
181+
- Use semantic HTML elements`, style, interactivity, style)
182+
}
183+
184+
func (crg *CursorRulesGenerator) generateResponsiveRules() string {
185+
priority := crg.getAnswerString("responsive_priority", "Mobile, Desktop")
186+
approach := crg.getAnswerString("responsive_approach", "Mobile-first")
187+
188+
return fmt.Sprintf(`### Responsive Strategy
189+
- **Device Priority:** %s
190+
- **Approach:** %s
191+
192+
### Breakpoints
193+
Use breakpoints from design tokens:
194+
- Mobile: 320px
195+
- Tablet: 768px
196+
- Desktop: 1024px
197+
- Large: 1440px
198+
199+
### Responsive Guidelines
200+
- Design %s
201+
- Test on all priority devices
202+
- Use fluid layouts where possible
203+
- Ensure touch-friendly interactions on mobile
204+
- Maintain usability across all screen sizes
205+
206+
### Tailwind Breakpoints
207+
When using Tailwind CSS:
208+
- sm: 640px (use sparingly)
209+
- md: 768px (tablet)
210+
- lg: 1024px (desktop)
211+
- xl: 1280px
212+
- 2xl: 1536px (large)`, priority, approach, approach)
213+
}
214+
215+
func (crg *CursorRulesGenerator) generateAccessibilityRules() string {
216+
importance := crg.getAnswerInt("accessibility_importance", 4)
217+
requirements := crg.getAnswerString("accessibility_requirements", "")
218+
219+
rules := fmt.Sprintf(`### Accessibility Priority
220+
- **Importance Level:** %d/5
221+
222+
### WCAG Compliance
223+
- **Minimum:** WCAG 2.1 AA compliance
224+
- **Target:** WCAG 2.1 AAA where possible
225+
226+
### Accessibility Requirements
227+
- **Keyboard Navigation:** All interactive elements must be keyboard accessible
228+
- **Screen Readers:** Use semantic HTML and ARIA attributes appropriately
229+
- **Color Contrast:** Minimum 4.5:1 for normal text, 3:1 for large text
230+
- **Focus Indicators:** Visible focus indicators on all interactive elements
231+
- **Alt Text:** Provide meaningful alt text for images
232+
- **Form Labels:** All form inputs must have associated labels
233+
- **Error Messages:** Clear, accessible error messages
234+
- **Skip Links:** Provide skip navigation links for keyboard users
235+
- **ARIA Labels:** Use ARIA labels for complex interactions
236+
- **Reduced Motion:** Respect prefers-reduced-motion media query
237+
238+
### Semantic HTML
239+
- Use proper heading hierarchy (h1, h2, h3, etc.)
240+
- Use semantic elements (nav, main, article, section, etc.)
241+
- Use form elements correctly (input, label, fieldset, legend)
242+
- Use button for actions, not div or span
243+
244+
### Testing
245+
- Test with keyboard navigation (Tab, Enter, Space, Arrow keys)
246+
- Test with screen readers (NVDA, JAWS, VoiceOver)
247+
- Verify color contrast ratios using tools
248+
- Test focus indicators visibility
249+
- Test with zoom up to 200%%
250+
- Verify all content is accessible without mouse`, importance)
251+
252+
if requirements != "" && requirements != "nil" {
253+
rules += fmt.Sprintf("\n\n### Specific Requirements\n%s", requirements)
254+
}
255+
256+
return rules
257+
}
258+
259+
func (crg *CursorRulesGenerator) generateCodeStyleRules() string {
260+
return `### Tailwind CSS Utilities
261+
When using Tailwind CSS, follow these patterns:
262+
263+
#### Colors
264+
- Use color utilities from design tokens: text-primary, bg-secondary
265+
- Use semantic colors: text-success, bg-error, etc.
266+
- DO NOT use arbitrary colors: text-[#ff0000]
267+
268+
#### Spacing
269+
- Use spacing scale: p-4, m-6, gap-8
270+
- DO NOT use arbitrary spacing: p-[17px]
271+
272+
#### Typography
273+
- Use font size utilities: text-sm, text-lg, text-2xl
274+
- Use font weight utilities: font-medium, font-bold
275+
- Use line height utilities: leading-tight, leading-normal
276+
277+
#### Components
278+
- Use border radius from tokens: rounded-md, rounded-lg
279+
- Use shadows: shadow-sm, shadow-md, shadow-lg
280+
- Use consistent component patterns
281+
282+
### Code Organization
283+
- Group related styles together
284+
- Use consistent naming conventions
285+
- Comment complex styling decisions
286+
- Reference design tokens in comments when helpful`
287+
}
288+
289+
func (crg *CursorRulesGenerator) getAnswerString(key string, defaultValue string) string {
290+
if val, ok := crg.data.Answers[key]; ok {
291+
if str, ok := val.(string); ok {
292+
return str
293+
}
294+
return fmt.Sprintf("%v", val)
295+
}
296+
return defaultValue
297+
}
298+
299+
func (crg *CursorRulesGenerator) getAnswerInt(key string, defaultValue int) int {
300+
if val, ok := crg.data.Answers[key]; ok {
301+
if num, ok := val.(int); ok {
302+
return num
303+
}
304+
if str, ok := val.(string); ok {
305+
if num, err := strconv.Atoi(str); err == nil {
306+
return num
307+
}
308+
}
309+
}
310+
return defaultValue
311+
}
312+

0 commit comments

Comments
 (0)