flutter-pro-max-cli 1.0.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/LICENSE +21 -0
- package/README.md +106 -0
- package/assets/.agent/workflows/flutter-pro-max.md +167 -0
- package/assets/.agent/workflows/scripts/core.py +341 -0
- package/assets/.agent/workflows/scripts/search.py +106 -0
- package/assets/.claude/skills/flutter-pro-max/SKILL.md +182 -0
- package/assets/.claude/skills/flutter-pro-max/scripts/core.py +341 -0
- package/assets/.claude/skills/flutter-pro-max/scripts/search.py +106 -0
- package/assets/.codebuddy/commands/flutter-pro-max.md +58 -0
- package/assets/.codebuddy/commands/scripts/core.py +341 -0
- package/assets/.codebuddy/commands/scripts/search.py +106 -0
- package/assets/.codex/skills/flutter-pro-max/SKILL.md +100 -0
- package/assets/.codex/skills/flutter-pro-max/scripts/core.py +341 -0
- package/assets/.codex/skills/flutter-pro-max/scripts/search.py +106 -0
- package/assets/.cursor/commands/flutter-pro-max.md +154 -0
- package/assets/.cursor/commands/scripts/core.py +341 -0
- package/assets/.cursor/commands/scripts/search.py +106 -0
- package/assets/.gemini/skills/flutter-pro-max/SKILL.md +100 -0
- package/assets/.gemini/skills/flutter-pro-max/scripts/core.py +341 -0
- package/assets/.gemini/skills/flutter-pro-max/scripts/search.py +106 -0
- package/assets/.github/prompts/flutter-pro-max.prompt.md +61 -0
- package/assets/.github/prompts/scripts/core.py +341 -0
- package/assets/.github/prompts/scripts/search.py +106 -0
- package/assets/.kiro/steering/flutter-pro-max.md +60 -0
- package/assets/.kiro/steering/scripts/core.py +341 -0
- package/assets/.kiro/steering/scripts/search.py +106 -0
- package/assets/.qoder/rules/flutter-pro-max.md +58 -0
- package/assets/.qoder/rules/scripts/core.py +341 -0
- package/assets/.qoder/rules/scripts/search.py +106 -0
- package/assets/.roo/commands/flutter-pro-max.md +58 -0
- package/assets/.roo/commands/scripts/core.py +341 -0
- package/assets/.roo/commands/scripts/search.py +106 -0
- package/assets/.shared/data/architect.csv +18 -0
- package/assets/.shared/data/charts.csv +26 -0
- package/assets/.shared/data/colors.csv +97 -0
- package/assets/.shared/data/icons.csv +101 -0
- package/assets/.shared/data/landing.csv +31 -0
- package/assets/.shared/data/name_convention.csv +16 -0
- package/assets/.shared/data/package.csv +101 -0
- package/assets/.shared/data/patterns.csv +109 -0
- package/assets/.shared/data/products.csv +97 -0
- package/assets/.shared/data/prompts.csv +24 -0
- package/assets/.shared/data/styles.csv +59 -0
- package/assets/.shared/data/typography.csv +58 -0
- package/assets/.shared/data/ux-guidelines.csv +100 -0
- package/assets/.shared/data/widget.csv +65 -0
- package/assets/.shared/flutter-pro-max/SKILL.md +165 -0
- package/assets/.shared/flutter-pro-max/scripts/core.py +341 -0
- package/assets/.shared/flutter-pro-max/scripts/search.py +106 -0
- package/assets/.trae/skills/flutter-pro-max/SKILL.md +61 -0
- package/assets/.trae/skills/flutter-pro-max/scripts/core.py +341 -0
- package/assets/.trae/skills/flutter-pro-max/scripts/search.py +106 -0
- package/assets/.windsurf/workflows/flutter-pro-max.md +154 -0
- package/assets/.windsurf/workflows/scripts/core.py +341 -0
- package/assets/.windsurf/workflows/scripts/search.py +106 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +67 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +31 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.js +15 -0
- package/dist/utils/detect.d.ts +8 -0
- package/dist/utils/detect.js +80 -0
- package/dist/utils/extract.d.ts +4 -0
- package/dist/utils/extract.js +83 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +9 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bui Long
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# 🚀 Flutter Pro Max CLI
|
|
2
|
+
|
|
3
|
+
**The official command-line interface for deploying Flutter Pro Max technical intelligence to your favorite AI coding assistants.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/flutter-pro-max-cli)
|
|
6
|
+
[](https://www.npmjs.com/package/flutter-pro-max-cli)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 📦 Installation
|
|
13
|
+
|
|
14
|
+
### The Quick Way (Recommended)
|
|
15
|
+
Bootstrap any project in seconds. This command will guide you through selecting and installing the skill for your environment.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx flutter-pro-max-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Global Installation
|
|
22
|
+
For heavy users who want the `flutter-pro-max` command available everywhere:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Install globally
|
|
26
|
+
npm install -g flutter-pro-max-cli
|
|
27
|
+
|
|
28
|
+
# Initialize in your project
|
|
29
|
+
flutter-pro-max init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 🛠️ Usage
|
|
35
|
+
|
|
36
|
+
### Interactive Setup
|
|
37
|
+
Simply run the command and follow the prompts to detect and install the skill for your active AI assistants.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
flutter-pro-max init
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Scripted Installation
|
|
44
|
+
For CI/CD or automated setups, you can specify the assistant type directly:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Install for a specific assistant
|
|
48
|
+
flutter-pro-max init --ai claude
|
|
49
|
+
flutter-pro-max init --ai cursor
|
|
50
|
+
|
|
51
|
+
# Install for all supported assistants
|
|
52
|
+
flutter-pro-max init --ai all
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 🤖 Supported AI Assistants
|
|
58
|
+
|
|
59
|
+
This CLI bridges the gap between the Flutter Pro Max knowledge base and your development tools:
|
|
60
|
+
|
|
61
|
+
| Assistant | Type Flag | Installation Path |
|
|
62
|
+
|-----------|-----------|-------------------|
|
|
63
|
+
| **Claude Code** | `claude` | `.claude/skills/` |
|
|
64
|
+
| **Cursor** | `cursor` | `.cursor/commands/` |
|
|
65
|
+
| **Windsurf** | `windsurf` | `.windsurf/workflows/` |
|
|
66
|
+
| **Antigravity** | `antigravity` | `.agent/workflows/` |
|
|
67
|
+
| **Trae** | `trae` | `.trae/skills/` |
|
|
68
|
+
| **Gemini CLI** | `gemini` | `.gemini/skills/` |
|
|
69
|
+
| **GitHub Copilot** | `copilot` | `.github/prompts/` |
|
|
70
|
+
| **RooCode** | `roocode` | `.roo/commands/` |
|
|
71
|
+
| **Kiro** | `kiro` | `.kiro/steering/` |
|
|
72
|
+
| **Qoder** | `qoder` | `.qoder/rules/` |
|
|
73
|
+
| **CodeBuddy** | `codebuddy` | `.codebuddy/commands/` |
|
|
74
|
+
| **Codex** | `codex` | `.codex/skills/` |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 🔧 Development
|
|
79
|
+
|
|
80
|
+
If you want to contribute or modify the CLI:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Clone and install
|
|
84
|
+
git clone https://github.com/btLong402/flutter-skill.git
|
|
85
|
+
cd cli
|
|
86
|
+
npm install
|
|
87
|
+
|
|
88
|
+
# Build the project
|
|
89
|
+
npm run build
|
|
90
|
+
|
|
91
|
+
# Run locally in development mode
|
|
92
|
+
npm run dev
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 📄 License
|
|
98
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
<div align="center">
|
|
103
|
+
|
|
104
|
+
**Streamline your Flutter development with AI-powered architectural intelligence.**
|
|
105
|
+
|
|
106
|
+
</div>
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Flutter Pro Max - Chuyên gia Flutter với Clean Architecture và Performance
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# flutter-pro-max
|
|
6
|
+
|
|
7
|
+
Searchable database của Flutter widgets, packages, design patterns, architecture guidelines, colors, typography, và best practices.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python3 --version
|
|
13
|
+
pip install rank-bm25
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## How to Use This Workflow
|
|
19
|
+
|
|
20
|
+
Khi user yêu cầu Flutter work, follow workflow này:
|
|
21
|
+
|
|
22
|
+
### Step 1: Analyze User Requirements
|
|
23
|
+
|
|
24
|
+
Trích xuất thông tin từ request:
|
|
25
|
+
- **Architecture**: Clean Architecture, Feature-First, DDD
|
|
26
|
+
- **State Management**: Riverpod (default), Bloc, Provider
|
|
27
|
+
- **UI Components**: Widgets, Layouts, Animations
|
|
28
|
+
- **Design**: Colors, Typography, Styles
|
|
29
|
+
- **Package needs**: Networking, Database, Security, etc.
|
|
30
|
+
|
|
31
|
+
### Step 2: Search Relevant Data (14 Sources)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python3 .agent/workflows/scripts/search.py "<keyword>" --top 5
|
|
35
|
+
python3 .agent/workflows/scripts/search.py "<keyword>" --stack riverpod --top 5
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Available stacks:** `riverpod`, `bloc`, `provider`
|
|
39
|
+
|
|
40
|
+
**Search Examples by Domain:**
|
|
41
|
+
```bash
|
|
42
|
+
# Flutter Widgets
|
|
43
|
+
python3 .agent/workflows/scripts/search.py "ListView pagination" --top 5
|
|
44
|
+
|
|
45
|
+
# Design Patterns
|
|
46
|
+
python3 .agent/workflows/scripts/search.py "authentication login" --top 5
|
|
47
|
+
|
|
48
|
+
# Charts
|
|
49
|
+
python3 .agent/workflows/scripts/search.py "chart bar comparison" --top 5
|
|
50
|
+
|
|
51
|
+
# Typography
|
|
52
|
+
python3 .agent/workflows/scripts/search.py "font modern SaaS" --top 5
|
|
53
|
+
|
|
54
|
+
# Colors by Product
|
|
55
|
+
python3 .agent/workflows/scripts/search.py "fintech crypto dark" --top 5
|
|
56
|
+
|
|
57
|
+
# UX Guidelines
|
|
58
|
+
python3 .agent/workflows/scripts/search.py "touch target accessibility" --top 5
|
|
59
|
+
|
|
60
|
+
# UI Styles
|
|
61
|
+
python3 .agent/workflows/scripts/search.py "glassmorphism neumorphism" --top 5
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Step 3: Apply Technical Standards
|
|
65
|
+
|
|
66
|
+
#### Dart 3 Modern Syntax
|
|
67
|
+
```dart
|
|
68
|
+
// ✅ Records
|
|
69
|
+
(String name, int age) getUserInfo() => ('John', 25);
|
|
70
|
+
|
|
71
|
+
// ✅ Pattern Matching
|
|
72
|
+
String getMessage(UIState state) => switch (state) {
|
|
73
|
+
LoadingState() => 'Loading...',
|
|
74
|
+
DataState(data: var d) => 'Data: $d',
|
|
75
|
+
ErrorState(message: var m) => 'Error: $m',
|
|
76
|
+
};
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Performance Rules
|
|
80
|
+
- Luôn dùng `const` constructor khi có thể
|
|
81
|
+
- Ưu tiên `SizedBox` hơn `Container` cho spacing
|
|
82
|
+
- Dùng `ListView.builder` thay vì `ListView` + `children`
|
|
83
|
+
|
|
84
|
+
#### State Management
|
|
85
|
+
- **Default**: Riverpod với `riverpod_generator`
|
|
86
|
+
- **Alternative**: Bloc (khi user yêu cầu)
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Search Reference
|
|
91
|
+
|
|
92
|
+
### Available Data Sources (14 files)
|
|
93
|
+
|
|
94
|
+
| Type | File | Content |
|
|
95
|
+
|------|------|---------|
|
|
96
|
+
| Widget | `widget.csv` | 65+ Flutter widgets với pro-tips |
|
|
97
|
+
| Package | `package.csv` | 100+ packages với best practices |
|
|
98
|
+
| Pattern | `patterns.csv` | 100+ design patterns với code snippets |
|
|
99
|
+
| Architecture | `architect.csv` | Clean Architecture layer paths |
|
|
100
|
+
| Chart | `charts.csv` | Chart type recommendations by data |
|
|
101
|
+
| Color | `colors.csv` | Color palettes by product type |
|
|
102
|
+
| Typography | `typography.csv` | Font pairings với Google Fonts |
|
|
103
|
+
| Style | `styles.csv` | UI style guidelines (Glass, Neubrutalism...) |
|
|
104
|
+
| UX Guideline | `ux-guidelines.csv` | UX best practices (Do/Don't) |
|
|
105
|
+
| Icon | `icons.csv` | Icon recommendations |
|
|
106
|
+
| Landing | `landing.csv` | Landing page section patterns |
|
|
107
|
+
| Naming | `name_convention.csv` | Dart/Flutter naming conventions |
|
|
108
|
+
| Product | `products.csv` | Product type styling recommendations |
|
|
109
|
+
| Prompt | `prompts.csv` | AI prompt templates |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Example Workflow
|
|
114
|
+
|
|
115
|
+
**User Request:** "Tạo màn hình đăng nhập với Riverpod"
|
|
116
|
+
|
|
117
|
+
1. **Search widgets:**
|
|
118
|
+
```bash
|
|
119
|
+
python3 .agent/workflows/scripts/search.py "form input text field" --top 5
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
2. **Search patterns:**
|
|
123
|
+
```bash
|
|
124
|
+
python3 .agent/workflows/scripts/search.py "authentication login" --top 5
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
3. **Search packages:**
|
|
128
|
+
```bash
|
|
129
|
+
python3 .agent/workflows/scripts/search.py "validation form" --stack riverpod --top 5
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
4. **Search colors:**
|
|
133
|
+
```bash
|
|
134
|
+
python3 .agent/workflows/scripts/search.py "saas professional" --top 3
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
5. **Apply results** to generate code với Riverpod state management
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Pre-Delivery Checklist
|
|
142
|
+
|
|
143
|
+
### Code Quality
|
|
144
|
+
- [ ] Sử dụng `const` constructors
|
|
145
|
+
- [ ] Sound Null Safety (không dùng `!` bừa bãi)
|
|
146
|
+
- [ ] Dart 3 syntax (Records, Pattern Matching)
|
|
147
|
+
|
|
148
|
+
### Performance
|
|
149
|
+
- [ ] `ListView.builder` cho lists dài
|
|
150
|
+
- [ ] `SizedBox` thay vì `Container` cho spacing
|
|
151
|
+
- [ ] `const` widgets được đánh dấu
|
|
152
|
+
|
|
153
|
+
### Architecture
|
|
154
|
+
- [ ] Tuân thủ Clean Architecture layers
|
|
155
|
+
- [ ] Dependency Injection đúng cách
|
|
156
|
+
- [ ] Repository pattern cho data access
|
|
157
|
+
|
|
158
|
+
### State Management
|
|
159
|
+
- [ ] Riverpod providers được tổ chức hợp lý
|
|
160
|
+
- [ ] Không leak state giữa các features
|
|
161
|
+
- [ ] Error handling với AsyncValue
|
|
162
|
+
|
|
163
|
+
### UX/UI
|
|
164
|
+
- [ ] Touch targets tối thiểu 44x44px
|
|
165
|
+
- [ ] Colors đúng với product type
|
|
166
|
+
- [ ] Typography phù hợp với brand
|
|
167
|
+
- [ ] WCAG contrast requirements
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Flutter Pro Max Core - BM25 search engine for Flutter knowledge base
|
|
5
|
+
Zero dependencies - self-contained BM25 implementation
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import csv
|
|
9
|
+
import re
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from math import log
|
|
12
|
+
from collections import defaultdict
|
|
13
|
+
|
|
14
|
+
# ============ CONFIGURATION ============
|
|
15
|
+
def _get_data_dir():
|
|
16
|
+
"""Auto-detect data directory based on script location"""
|
|
17
|
+
script_dir = Path(__file__).parent
|
|
18
|
+
possible_paths = [
|
|
19
|
+
# When running from root/scripts/
|
|
20
|
+
script_dir.parent / ".shared" / "data",
|
|
21
|
+
# When running from .shared/flutter-pro-max/scripts/
|
|
22
|
+
script_dir.parent.parent / "data",
|
|
23
|
+
# Fallback: cwd
|
|
24
|
+
Path.cwd() / ".shared" / "data",
|
|
25
|
+
]
|
|
26
|
+
for p in possible_paths:
|
|
27
|
+
if p.exists():
|
|
28
|
+
return p.resolve()
|
|
29
|
+
return possible_paths[0] # Default to first option
|
|
30
|
+
|
|
31
|
+
DATA_DIR = _get_data_dir()
|
|
32
|
+
MAX_RESULTS = 5
|
|
33
|
+
|
|
34
|
+
# Domain configuration: file, search columns, output columns
|
|
35
|
+
CSV_CONFIG = {
|
|
36
|
+
"widget": {
|
|
37
|
+
"file": "widget.csv",
|
|
38
|
+
"search_cols": ["Widget Name", "Category", "Description", "Key Properties", "Usage Context & Pro-Tips"],
|
|
39
|
+
"output_cols": ["Widget Name", "Category", "Description", "Key Properties", "Usage Context & Pro-Tips"]
|
|
40
|
+
},
|
|
41
|
+
"package": {
|
|
42
|
+
"file": "package.csv",
|
|
43
|
+
"search_cols": ["pkg_name", "category", "description", "best_practice_snippet", "pro_tip", "alternatives"],
|
|
44
|
+
"output_cols": ["pkg_name", "category", "description", "best_practice_snippet", "pro_tip", "alternatives"]
|
|
45
|
+
},
|
|
46
|
+
"pattern": {
|
|
47
|
+
"file": "patterns.csv",
|
|
48
|
+
"search_cols": ["pattern_name", "category", "problem_tags", "description", "key_widgets", "code_snippet", "anti_pattern"],
|
|
49
|
+
"output_cols": ["pattern_name", "category", "problem_tags", "description", "key_widgets", "code_snippet", "anti_pattern"]
|
|
50
|
+
},
|
|
51
|
+
"architect": {
|
|
52
|
+
"file": "architect.csv",
|
|
53
|
+
"search_cols": ["path_pattern", "layer", "responsibility_description", "allowed_dependencies", "tech_stack_note"],
|
|
54
|
+
"output_cols": ["path_pattern", "layer", "responsibility_description", "allowed_dependencies", "tech_stack_note"]
|
|
55
|
+
},
|
|
56
|
+
"chart": {
|
|
57
|
+
"file": "charts.csv",
|
|
58
|
+
"search_cols": ["Data Type", "Keywords", "Best Chart Type", "Secondary Options", "Accessibility Notes"],
|
|
59
|
+
"output_cols": ["Data Type", "Keywords", "Best Chart Type", "Secondary Options", "Color Guidance", "Accessibility Notes", "Library Recommendation"]
|
|
60
|
+
},
|
|
61
|
+
"color": {
|
|
62
|
+
"file": "colors.csv",
|
|
63
|
+
"search_cols": ["Product Type", "Keywords", "Notes"],
|
|
64
|
+
"output_cols": ["Product Type", "Keywords", "Primary (Hex)", "Secondary (Hex)", "CTA (Hex)", "Notes"]
|
|
65
|
+
},
|
|
66
|
+
"typography": {
|
|
67
|
+
"file": "typography.csv",
|
|
68
|
+
"search_cols": ["Font Pairing Name", "Category", "Mood/Style Keywords", "Best For", "Heading Font", "Body Font"],
|
|
69
|
+
"output_cols": ["Font Pairing Name", "Category", "Heading Font", "Body Font", "Mood/Style Keywords", "Best For", "Google Fonts URL", "Notes"]
|
|
70
|
+
},
|
|
71
|
+
"style": {
|
|
72
|
+
"file": "styles.csv",
|
|
73
|
+
"search_cols": ["Style Category", "Type", "Keywords", "Best For"],
|
|
74
|
+
"output_cols": ["Style Category", "Type", "Keywords", "Primary Colors", "Effects & Animation", "Best For", "Do Not Use For"]
|
|
75
|
+
},
|
|
76
|
+
"ux": {
|
|
77
|
+
"file": "ux-guidelines.csv",
|
|
78
|
+
"search_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't"],
|
|
79
|
+
"output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't"]
|
|
80
|
+
},
|
|
81
|
+
"icon": {
|
|
82
|
+
"file": "icons.csv",
|
|
83
|
+
"search_cols": ["Category", "Icon Name", "Keywords", "Best For"],
|
|
84
|
+
"output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For"]
|
|
85
|
+
},
|
|
86
|
+
"landing": {
|
|
87
|
+
"file": "landing.csv",
|
|
88
|
+
"search_cols": ["Pattern Name", "Keywords", "Section Order", "Conversion Optimization"],
|
|
89
|
+
"output_cols": ["Pattern Name", "Keywords", "Section Order", "Primary CTA Placement", "Color Strategy", "Conversion Optimization"]
|
|
90
|
+
},
|
|
91
|
+
"naming": {
|
|
92
|
+
"file": "name_convention.csv",
|
|
93
|
+
"search_cols": ["Layer", "File Template", "Class Template", "Example File", "Example Class", "Notes"],
|
|
94
|
+
"output_cols": ["Layer", "File Template", "Class Template", "Example File", "Example Class", "Notes"]
|
|
95
|
+
},
|
|
96
|
+
"product": {
|
|
97
|
+
"file": "products.csv",
|
|
98
|
+
"search_cols": ["Product Type", "Keywords", "Primary Style Recommendation"],
|
|
99
|
+
"output_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Secondary Styles", "Color Palette Focus"]
|
|
100
|
+
},
|
|
101
|
+
"prompt": {
|
|
102
|
+
"file": "prompts.csv",
|
|
103
|
+
"search_cols": ["Style Category", "AI Prompt Keywords (Copy-Paste Ready)", "CSS/Technical Keywords"],
|
|
104
|
+
"output_cols": ["Style Category", "AI Prompt Keywords (Copy-Paste Ready)", "CSS/Technical Keywords", "Implementation Checklist"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Stack exclusion mapping for filtering conflicting packages
|
|
109
|
+
STACK_EXCLUSIONS = {
|
|
110
|
+
"riverpod": ["bloc", "flutter_bloc", "provider", "hydrated_bloc"],
|
|
111
|
+
"bloc": ["riverpod", "flutter_riverpod", "provider"],
|
|
112
|
+
"provider": ["riverpod", "flutter_riverpod", "bloc", "flutter_bloc"],
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
AVAILABLE_DOMAINS = list(CSV_CONFIG.keys())
|
|
116
|
+
AVAILABLE_STACKS = list(STACK_EXCLUSIONS.keys())
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ============ BM25 IMPLEMENTATION ============
|
|
120
|
+
class BM25:
|
|
121
|
+
"""BM25 ranking algorithm for text search - zero dependencies"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, k1=1.5, b=0.75):
|
|
124
|
+
self.k1 = k1
|
|
125
|
+
self.b = b
|
|
126
|
+
self.corpus = []
|
|
127
|
+
self.doc_lengths = []
|
|
128
|
+
self.avgdl = 0
|
|
129
|
+
self.idf = {}
|
|
130
|
+
self.doc_freqs = defaultdict(int)
|
|
131
|
+
self.N = 0
|
|
132
|
+
|
|
133
|
+
def tokenize(self, text):
|
|
134
|
+
"""Lowercase, split, remove punctuation, filter short words"""
|
|
135
|
+
text = re.sub(r'[^\w\s]', ' ', str(text).lower())
|
|
136
|
+
return [w for w in text.split() if len(w) > 1]
|
|
137
|
+
|
|
138
|
+
def fit(self, documents):
|
|
139
|
+
"""Build BM25 index from documents"""
|
|
140
|
+
self.corpus = [self.tokenize(doc) for doc in documents]
|
|
141
|
+
self.N = len(self.corpus)
|
|
142
|
+
if self.N == 0:
|
|
143
|
+
return
|
|
144
|
+
self.doc_lengths = [len(doc) for doc in self.corpus]
|
|
145
|
+
self.avgdl = sum(self.doc_lengths) / self.N
|
|
146
|
+
|
|
147
|
+
for doc in self.corpus:
|
|
148
|
+
seen = set()
|
|
149
|
+
for word in doc:
|
|
150
|
+
if word not in seen:
|
|
151
|
+
self.doc_freqs[word] += 1
|
|
152
|
+
seen.add(word)
|
|
153
|
+
|
|
154
|
+
for word, freq in self.doc_freqs.items():
|
|
155
|
+
self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1)
|
|
156
|
+
|
|
157
|
+
def score(self, query):
|
|
158
|
+
"""Score all documents against query"""
|
|
159
|
+
query_tokens = self.tokenize(query)
|
|
160
|
+
scores = []
|
|
161
|
+
|
|
162
|
+
for idx, doc in enumerate(self.corpus):
|
|
163
|
+
score = 0
|
|
164
|
+
doc_len = self.doc_lengths[idx]
|
|
165
|
+
term_freqs = defaultdict(int)
|
|
166
|
+
for word in doc:
|
|
167
|
+
term_freqs[word] += 1
|
|
168
|
+
|
|
169
|
+
for token in query_tokens:
|
|
170
|
+
if token in self.idf:
|
|
171
|
+
tf = term_freqs[token]
|
|
172
|
+
idf = self.idf[token]
|
|
173
|
+
numerator = tf * (self.k1 + 1)
|
|
174
|
+
denominator = tf + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl)
|
|
175
|
+
score += idf * numerator / denominator
|
|
176
|
+
|
|
177
|
+
scores.append((idx, score))
|
|
178
|
+
|
|
179
|
+
return sorted(scores, key=lambda x: x[1], reverse=True)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ============ HELPER FUNCTIONS ============
|
|
183
|
+
def _load_csv(filepath):
|
|
184
|
+
"""Load CSV and return list of dicts"""
|
|
185
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
186
|
+
return list(csv.DictReader(f))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _search_csv(filepath, search_cols, output_cols, query, max_results, boost_col=None, boost_query=None):
|
|
190
|
+
"""Core search function using BM25 with optional boosting"""
|
|
191
|
+
if not filepath.exists():
|
|
192
|
+
return []
|
|
193
|
+
|
|
194
|
+
data = _load_csv(filepath)
|
|
195
|
+
|
|
196
|
+
# Build documents from search columns
|
|
197
|
+
documents = [" ".join(str(row.get(col, "")) for col in search_cols) for row in data]
|
|
198
|
+
|
|
199
|
+
# BM25 search
|
|
200
|
+
bm25 = BM25()
|
|
201
|
+
bm25.fit(documents)
|
|
202
|
+
ranked = bm25.score(query)
|
|
203
|
+
|
|
204
|
+
# Apply boosting if specified (widget name match, etc.)
|
|
205
|
+
if boost_col and boost_query:
|
|
206
|
+
boost_query_lower = boost_query.lower()
|
|
207
|
+
boosted = []
|
|
208
|
+
for idx, score in ranked:
|
|
209
|
+
if score > 0:
|
|
210
|
+
boost_value = str(data[idx].get(boost_col, "")).lower()
|
|
211
|
+
if boost_value in boost_query_lower or boost_query_lower in boost_value:
|
|
212
|
+
score *= 2.0 # Double score for exact/partial match
|
|
213
|
+
boosted.append((idx, score))
|
|
214
|
+
ranked = sorted(boosted, key=lambda x: x[1], reverse=True)
|
|
215
|
+
|
|
216
|
+
# Get top results with score > 0
|
|
217
|
+
results = []
|
|
218
|
+
for idx, score in ranked[:max_results]:
|
|
219
|
+
if score > 0:
|
|
220
|
+
row = data[idx]
|
|
221
|
+
result = {col: row.get(col, "") for col in output_cols if col in row}
|
|
222
|
+
result["_score"] = round(score, 4)
|
|
223
|
+
results.append(result)
|
|
224
|
+
|
|
225
|
+
return results
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def detect_domain(query):
|
|
229
|
+
"""Auto-detect the most relevant domain from query keywords"""
|
|
230
|
+
query_lower = query.lower()
|
|
231
|
+
|
|
232
|
+
domain_keywords = {
|
|
233
|
+
"widget": ["widget", "listview", "column", "row", "container", "text", "button", "scaffold", "appbar", "sliver"],
|
|
234
|
+
"package": ["package", "pub", "dependency", "library", "dio", "http", "riverpod", "bloc", "hive", "isar"],
|
|
235
|
+
"pattern": ["pattern", "architecture", "repository", "usecase", "state", "async", "error handling", "offline"],
|
|
236
|
+
"architect": ["layer", "folder", "structure", "clean", "feature", "domain", "data", "presentation"],
|
|
237
|
+
"chart": ["chart", "graph", "visualization", "bar", "pie", "line", "scatter", "heatmap"],
|
|
238
|
+
"color": ["color", "palette", "hex", "theme", "dark mode", "light mode"],
|
|
239
|
+
"typography": ["font", "typography", "heading", "text style", "google fonts"],
|
|
240
|
+
"style": ["style", "design", "ui", "glassmorphism", "neumorphism", "minimal", "modern"],
|
|
241
|
+
"ux": ["ux", "usability", "accessibility", "touch", "scroll", "animation", "gesture"],
|
|
242
|
+
"icon": ["icon", "lucide", "material icons", "cupertino"],
|
|
243
|
+
"landing": ["landing", "page", "hero", "cta", "section"],
|
|
244
|
+
"naming": ["naming", "convention", "file name", "class name", "snake_case", "PascalCase"],
|
|
245
|
+
"product": ["saas", "ecommerce", "fintech", "healthcare", "education", "food", "travel"],
|
|
246
|
+
"prompt": ["prompt", "ai", "css", "tailwind", "implementation"],
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
scores = {domain: sum(1 for kw in keywords if kw in query_lower) for domain, keywords in domain_keywords.items()}
|
|
250
|
+
best = max(scores, key=scores.get)
|
|
251
|
+
return best if scores[best] > 0 else "widget"
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# ============ MAIN SEARCH FUNCTIONS ============
|
|
255
|
+
def search(query, domain=None, max_results=MAX_RESULTS):
|
|
256
|
+
"""
|
|
257
|
+
Main search function with auto-domain detection
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
query: Search query string
|
|
261
|
+
domain: Optional domain (widget, package, pattern, etc.)
|
|
262
|
+
max_results: Number of results to return
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
Dict with domain, query, file, count, and results
|
|
266
|
+
"""
|
|
267
|
+
if domain is None:
|
|
268
|
+
domain = detect_domain(query)
|
|
269
|
+
|
|
270
|
+
if domain not in CSV_CONFIG:
|
|
271
|
+
return {"error": f"Unknown domain: {domain}. Available: {', '.join(AVAILABLE_DOMAINS)}"}
|
|
272
|
+
|
|
273
|
+
config = CSV_CONFIG[domain]
|
|
274
|
+
filepath = DATA_DIR / config["file"]
|
|
275
|
+
|
|
276
|
+
if not filepath.exists():
|
|
277
|
+
return {"error": f"File not found: {filepath}", "domain": domain}
|
|
278
|
+
|
|
279
|
+
# Apply widget boosting for widget domain
|
|
280
|
+
boost_col = "Widget Name" if domain == "widget" else None
|
|
281
|
+
boost_query = query if domain == "widget" else None
|
|
282
|
+
|
|
283
|
+
results = _search_csv(
|
|
284
|
+
filepath,
|
|
285
|
+
config["search_cols"],
|
|
286
|
+
config["output_cols"],
|
|
287
|
+
query,
|
|
288
|
+
max_results,
|
|
289
|
+
boost_col=boost_col,
|
|
290
|
+
boost_query=boost_query
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
"domain": domain,
|
|
295
|
+
"query": query,
|
|
296
|
+
"file": config["file"],
|
|
297
|
+
"count": len(results),
|
|
298
|
+
"results": results
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def search_with_stack(query, stack, domain=None, max_results=MAX_RESULTS):
|
|
303
|
+
"""
|
|
304
|
+
Search with stack-specific filtering (excludes conflicting packages)
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
query: Search query string
|
|
308
|
+
stack: Stack preference (riverpod, bloc, provider)
|
|
309
|
+
domain: Optional domain filter
|
|
310
|
+
max_results: Number of results to return
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Dict with filtered results
|
|
314
|
+
"""
|
|
315
|
+
if stack not in STACK_EXCLUSIONS:
|
|
316
|
+
return {"error": f"Unknown stack: {stack}. Available: {', '.join(AVAILABLE_STACKS)}"}
|
|
317
|
+
|
|
318
|
+
result = search(query, domain, max_results * 2) # Get more to filter
|
|
319
|
+
|
|
320
|
+
if "error" in result:
|
|
321
|
+
return result
|
|
322
|
+
|
|
323
|
+
# Filter out conflicting packages
|
|
324
|
+
excluded = STACK_EXCLUSIONS[stack]
|
|
325
|
+
filtered_results = []
|
|
326
|
+
|
|
327
|
+
for item in result["results"]:
|
|
328
|
+
# Check package name field
|
|
329
|
+
pkg_name = item.get("pkg_name", "").lower()
|
|
330
|
+
if pkg_name not in excluded:
|
|
331
|
+
filtered_results.append(item)
|
|
332
|
+
|
|
333
|
+
if len(filtered_results) >= max_results:
|
|
334
|
+
break
|
|
335
|
+
|
|
336
|
+
result["results"] = filtered_results
|
|
337
|
+
result["count"] = len(filtered_results)
|
|
338
|
+
result["stack"] = stack
|
|
339
|
+
result["excluded"] = excluded
|
|
340
|
+
|
|
341
|
+
return result
|