Lastest change
This commit is contained in:
@@ -1,3 +1,84 @@
|
|||||||
|
# MoreMinimore
|
||||||
|
|
||||||
|
MoreMinimore is a local, open-source AI app builder. It's fast, private, and fully under your control — like Lovable, v0, or Bolt, but running right on your machine.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- ⚡️ **Local**: Fast, private and no lock-in.
|
||||||
|
- 🛠 **Bring your own keys**: Use your own AI API keys — no vendor lock-in.
|
||||||
|
- 🖥️ **Cross-platform**: Easy to run on Mac or Windows.
|
||||||
|
- ✨ **Enhanced**: Smart context and unlimited usage with custom features.
|
||||||
|
|
||||||
|
## 🧰 Prerequisites
|
||||||
|
|
||||||
|
- Node.js >= 20
|
||||||
|
- npm (comes with Node.js)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
You can verify your versions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node -v
|
||||||
|
npm -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏗️ Install (from source)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/kunthawat/moreminimore-vibe.git
|
||||||
|
cd moreminimore-vibe
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## ▶️ Run locally (development)
|
||||||
|
|
||||||
|
- Start the app with the default configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment variables (optional)
|
||||||
|
|
||||||
|
- `MOREMINIMORE_GATEWAY_URL`: URL of a compatible gateway if you prefer to route requests.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MOREMINIMORE_GATEWAY_URL=http://localhost:8080/v1 npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Build installers (make)
|
||||||
|
|
||||||
|
Create platform-specific distributables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run make
|
||||||
|
```
|
||||||
|
|
||||||
|
Outputs are written to the `out/` directory.
|
||||||
|
|
||||||
|
## 🧪 Tests and linting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Unit tests
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
npm run lint
|
||||||
|
|
||||||
|
# Prettier check
|
||||||
|
npm run prettier:check
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
MIT License — see [LICENSE](./LICENSE).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# MoreMinimore Debranding and Custom Features Guide
|
# MoreMinimore Debranding and Custom Features Guide
|
||||||
|
|
||||||
This guide explains how to remove Dyad branding and dependencies from the codebase and integrate custom features for MoreMinimore.
|
This guide explains how to remove Dyad branding and dependencies from the codebase and integrate custom features for MoreMinimore.
|
||||||
@@ -174,12 +255,9 @@ Edit `src/preload.ts`:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace the dependency:
|
Keep the original dependency (no changes needed):
|
||||||
```json
|
```json
|
||||||
// FROM:
|
|
||||||
"@dyad-sh/supabase-management-js": "v1.0.1",
|
"@dyad-sh/supabase-management-js": "v1.0.1",
|
||||||
// TO:
|
|
||||||
"@moreminimore/supabase-management-js": "v1.0.1",
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update Protocol Handlers
|
### Update Protocol Handlers
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "dyad",
|
"name": "moreminimore",
|
||||||
"version": "0.31.0-beta.1",
|
"version": "0.31.0-beta.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "dyad",
|
"name": "moreminimore",
|
||||||
"version": "0.31.0-beta.1",
|
"version": "0.31.0-beta.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
"@ai-sdk/xai": "^2.0.16",
|
"@ai-sdk/xai": "^2.0.16",
|
||||||
"@babel/parser": "^7.28.5",
|
"@babel/parser": "^7.28.5",
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@moreminimore/supabase-management-js": "v1.0.1",
|
"@dyad-sh/supabase-management-js": "v1.0.1",
|
||||||
"@lexical/react": "^0.33.1",
|
"@lexical/react": "^0.33.1",
|
||||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||||
"@monaco-editor/react": "^4.7.0-rc.0",
|
"@monaco-editor/react": "^4.7.0-rc.0",
|
||||||
|
|||||||
295
scripts/frontend-debrand.sh
Executable file
295
scripts/frontend-debrand.sh
Executable file
@@ -0,0 +1,295 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Frontend Debranding Script for MoreMinimore
|
||||||
|
# This script handles comprehensive frontend debranding and UI fixes
|
||||||
|
# Usage: ./scripts/frontend-debrand.sh
|
||||||
|
|
||||||
|
set -e # Exit on any error
|
||||||
|
|
||||||
|
echo "🎨 Starting frontend debranding process..."
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Function to print colored output
|
||||||
|
print_status() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if we're in the right directory
|
||||||
|
if [ ! -f "package.json" ] || [ ! -d "src" ]; then
|
||||||
|
print_error "Please run this script from the project root directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to fix TitleBar component
|
||||||
|
fix_titlebar() {
|
||||||
|
print_status "Fixing TitleBar component..."
|
||||||
|
|
||||||
|
if [ -f "src/app/TitleBar.tsx" ]; then
|
||||||
|
# Create a backup
|
||||||
|
cp src/app/TitleBar.tsx src/app/TitleBar.tsx.bak
|
||||||
|
|
||||||
|
# Remove Dyad Pro related imports and components
|
||||||
|
sed -i '' '/import.*DyadProSuccessDialog/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/import.*useUserBudgetInfo/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/import.*UserBudgetInfo/d' src/app/TitleBar.tsx
|
||||||
|
|
||||||
|
# Remove Dyad Pro button and related logic
|
||||||
|
sed -i '' '/const isDyadPro =/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/const isDyadProEnabled =/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/showDyadProSuccessDialog/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/DyadProSuccessDialog/,/<\/DyadProSuccessDialog>/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/{isDyadPro && <DyadProButton/d' src/app/TitleBar.tsx
|
||||||
|
|
||||||
|
# Update logo alt text
|
||||||
|
sed -i '' 's/Dyad Logo/MoreMinimore Logo/g' src/app/TitleBar.tsx
|
||||||
|
|
||||||
|
# Remove DyadProButton component
|
||||||
|
sed -i '' '/export function DyadProButton/,/^}/d' src/app/TitleBar.tsx
|
||||||
|
sed -i '' '/export function AICreditStatus/,/^}/d' src/app/TitleBar.tsx
|
||||||
|
|
||||||
|
# Remove deep link handling for dyad-pro-return
|
||||||
|
sed -i '' '/dyad-pro-return/d' src/app/TitleBar.tsx
|
||||||
|
|
||||||
|
rm src/app/TitleBar.tsx.bak
|
||||||
|
print_success "Fixed TitleBar component"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to remove DyadProSuccessDialog component
|
||||||
|
remove_pro_dialog() {
|
||||||
|
print_status "Removing DyadProSuccessDialog component..."
|
||||||
|
|
||||||
|
if [ -f "src/components/DyadProSuccessDialog.tsx" ]; then
|
||||||
|
mv src/components/DyadProSuccessDialog.tsx src/components/DyadProSuccessDialog.tsx.disabled
|
||||||
|
print_success "Disabled DyadProSuccessDialog component"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to fix ContextFilesPicker
|
||||||
|
fix_context_files_picker() {
|
||||||
|
print_status "Fixing ContextFilesPicker component..."
|
||||||
|
|
||||||
|
if [ -f "src/components/ContextFilesPicker.tsx" ]; then
|
||||||
|
# Create a backup
|
||||||
|
cp src/components/ContextFilesPicker.tsx src/components/ContextFilesPicker.tsx.bak
|
||||||
|
|
||||||
|
# Update tooltip text
|
||||||
|
sed -i '' 's/Codebase Context/Context Settings/g' src/components/ContextFilesPicker.tsx
|
||||||
|
|
||||||
|
# Update smart context logic to remove pro restrictions
|
||||||
|
sed -i '' 's/settings?.enableDyadPro && settings?.enableProSmartFilesContextMode/true/g' src/components/ContextFilesPicker.tsx
|
||||||
|
|
||||||
|
# Update text references
|
||||||
|
sed -i '' 's/Dyad uses/MoreMinimore uses/g' src/components/ContextFilesPicker.tsx
|
||||||
|
sed -i '' 's/With Smart Context, Dyad uses/With Smart Context, MoreMinimore uses/g' src/components/ContextFilesPicker.tsx
|
||||||
|
|
||||||
|
rm src/components/ContextFilesPicker.tsx.bak
|
||||||
|
print_success "Fixed ContextFilesPicker component"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to fix DyadTokenSavings component
|
||||||
|
fix_token_savings() {
|
||||||
|
print_status "Fixing DyadTokenSavings component..."
|
||||||
|
|
||||||
|
if [ -f "src/components/chat/DyadTokenSavings.tsx" ]; then
|
||||||
|
# Create a backup
|
||||||
|
cp src/components/chat/DyadTokenSavings.tsx src/components/chat/DyadTokenSavings.tsx.bak
|
||||||
|
|
||||||
|
# Update component name and references
|
||||||
|
sed -i '' 's/DyadTokenSavings/TokenSavings/g' src/components/chat/DyadTokenSavings.tsx
|
||||||
|
|
||||||
|
rm src/components/chat/DyadTokenSavings.tsx.bak
|
||||||
|
print_success "Fixed DyadTokenSavings component"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to fix DyadThink component
|
||||||
|
fix_dyad_think() {
|
||||||
|
print_status "Fixing DyadThink component..."
|
||||||
|
|
||||||
|
if [ -f "src/components/chat/DyadThink.tsx" ]; then
|
||||||
|
# Create a backup
|
||||||
|
cp src/components/chat/DyadThink.tsx src/components/chat/DyadThink.tsx.bak
|
||||||
|
|
||||||
|
# Update component name and references
|
||||||
|
sed -i '' 's/DyadThink/AIThink/g' src/components/chat/DyadThink.tsx
|
||||||
|
|
||||||
|
rm src/components/chat/DyadThink.tsx.bak
|
||||||
|
print_success "Fixed DyadThink component"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update smart context settings
|
||||||
|
update_smart_context_settings() {
|
||||||
|
print_status "Updating smart context settings..."
|
||||||
|
|
||||||
|
# Update smart context store to remove pro restrictions
|
||||||
|
if [ -f "src/ipc/utils/smart_context_store.ts" ]; then
|
||||||
|
cp src/ipc/utils/smart_context_store.ts src/ipc/utils/smart_context_store.ts.bak
|
||||||
|
sed -i '' 's/settings\.enableDyadPro/true/g' src/ipc/utils/smart_context_store.ts
|
||||||
|
rm src/ipc/utils/smart_context_store.ts.bak
|
||||||
|
print_success "Updated smart context store"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update chat stream handlers to enable smart context for all users
|
||||||
|
if [ -f "src/ipc/handlers/chat_stream_handlers.ts" ]; then
|
||||||
|
cp src/ipc/handlers/chat_stream_handlers.ts src/ipc/handlers/chat_stream_handlers.ts.bak
|
||||||
|
sed -i '' 's/isDeepContextEnabled/true/g' src/ipc/handlers/chat_stream_handlers.ts
|
||||||
|
rm src/ipc/handlers/chat_stream_handlers.ts.bak
|
||||||
|
print_success "Updated chat stream handlers"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to fix remaining Dyad references
|
||||||
|
fix_remaining_references() {
|
||||||
|
print_status "Fixing remaining Dyad references..."
|
||||||
|
|
||||||
|
# Find and replace remaining Dyad references in components
|
||||||
|
find src/components -name "*.tsx" -type f -exec sed -i '' 's/Dyad/MoreMinimore/g' {} \;
|
||||||
|
find src/app -name "*.tsx" -type f -exec sed -i '' 's/Dyad/MoreMinimore/g' {} \;
|
||||||
|
|
||||||
|
# Fix specific cases where we don't want to replace
|
||||||
|
find src/components -name "*.tsx" -type f -exec sed -i '' 's/MoreMinimorePro/DyadPro/g' {} \;
|
||||||
|
find src/app -name "*.tsx" -type f -exec sed -i '' 's/MoreMinimorePro/DyadPro/g' {} \;
|
||||||
|
|
||||||
|
print_success "Fixed remaining Dyad references"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to add missing user budget handler
|
||||||
|
add_missing_handlers() {
|
||||||
|
print_status "Adding missing IPC handlers..."
|
||||||
|
|
||||||
|
# Create a simple user budget handler that returns default values
|
||||||
|
if [ -f "src/ipc/ipc_host.ts" ]; then
|
||||||
|
cp src/ipc/ipc_host.ts src/ipc/ipc_host.ts.bak
|
||||||
|
|
||||||
|
# Add a simple user budget handler
|
||||||
|
if ! grep -q "get-user-budget" src/ipc/ipc_host.ts; then
|
||||||
|
sed -i '' '/registerDebugHandlers();/a\
|
||||||
|
\
|
||||||
|
// Add missing user budget handler\
|
||||||
|
ipcMain.handle("get-user-budget", async () => {\
|
||||||
|
return {\
|
||||||
|
totalCredits: 1000,\
|
||||||
|
usedCredits: 0,\
|
||||||
|
resetDate: new Date().toISOString()\
|
||||||
|
};\
|
||||||
|
});' src/ipc/ipc_host.ts
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm src/ipc/ipc_host.ts.bak
|
||||||
|
print_success "Added missing user budget handler"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update preload script
|
||||||
|
update_preload() {
|
||||||
|
print_status "Updating preload script..."
|
||||||
|
|
||||||
|
if [ -f "src/preload.ts" ]; then
|
||||||
|
cp src/preload.ts src/preload.ts.bak
|
||||||
|
|
||||||
|
# Add get-user-budget to preload if not present
|
||||||
|
if ! grep -q "get-user-budget" src/preload.ts; then
|
||||||
|
sed -i '' '/"get-system-debug-info":/a\
|
||||||
|
"get-user-budget": () => ipcRenderer.invoke("get-user-budget"),' src/preload.ts
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm src/preload.ts.bak
|
||||||
|
print_success "Updated preload script"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update IPC client
|
||||||
|
update_ipc_client() {
|
||||||
|
print_status "Updating IPC client..."
|
||||||
|
|
||||||
|
if [ -f "src/ipc/ipc_client.ts" ]; then
|
||||||
|
cp src/ipc/ipc_client.ts src/ipc/ipc_client.ts.bak
|
||||||
|
|
||||||
|
# Add getUserBudget method if not present
|
||||||
|
if ! grep -q "getUserBudget" src/ipc/ipc_client.ts; then
|
||||||
|
sed -i '' '/async getSystemDebugInfo()/a\
|
||||||
|
\
|
||||||
|
async getUserBudget() {\
|
||||||
|
return this.ipcRenderer.invoke("get-user-budget");\
|
||||||
|
}' src/ipc/ipc_client.ts
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm src/ipc/ipc_client.ts.bak
|
||||||
|
print_success "Updated IPC client"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test compilation
|
||||||
|
test_compilation() {
|
||||||
|
print_status "Testing compilation..."
|
||||||
|
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
if npm run ts 2>/dev/null; then
|
||||||
|
print_success "TypeScript compilation successful"
|
||||||
|
else
|
||||||
|
print_warning "TypeScript compilation failed. Check the errors above."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
print_status "Starting frontend debranding..."
|
||||||
|
|
||||||
|
fix_titlebar
|
||||||
|
remove_pro_dialog
|
||||||
|
fix_context_files_picker
|
||||||
|
fix_token_savings
|
||||||
|
fix_dyad_think
|
||||||
|
update_smart_context_settings
|
||||||
|
fix_remaining_references
|
||||||
|
add_missing_handlers
|
||||||
|
update_preload
|
||||||
|
update_ipc_client
|
||||||
|
test_compilation
|
||||||
|
|
||||||
|
print_success "🎉 Frontend debranding completed!"
|
||||||
|
echo ""
|
||||||
|
echo "Summary of changes:"
|
||||||
|
echo "✅ Fixed TitleBar component (removed Pro elements)"
|
||||||
|
echo "✅ Removed DyadProSuccessDialog component"
|
||||||
|
echo "✅ Fixed ContextFilesPicker component"
|
||||||
|
echo "✅ Fixed DyadTokenSavings component"
|
||||||
|
echo "✅ Fixed DyadThink component"
|
||||||
|
echo "✅ Updated smart context settings"
|
||||||
|
echo "✅ Fixed remaining Dyad references"
|
||||||
|
echo "✅ Added missing IPC handlers"
|
||||||
|
echo "✅ Updated preload script"
|
||||||
|
echo "✅ Updated IPC client"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Test the application with 'npm start'"
|
||||||
|
echo "2. Verify all UI elements are properly debranded"
|
||||||
|
echo "3. Test smart context functionality"
|
||||||
|
echo ""
|
||||||
|
print_warning "Please test the application thoroughly before committing changes."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Run main function
|
||||||
|
main "$@"
|
||||||
@@ -144,18 +144,44 @@ remove_pro_features() {
|
|||||||
rm src/preload.ts.bak
|
rm src/preload.ts.bak
|
||||||
print_success "Removed pro IPC channels"
|
print_success "Removed pro IPC channels"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Remove ProModeSelector from ChatInputControls
|
||||||
|
if [ -f "src/components/ChatInputControls.tsx" ]; then
|
||||||
|
sed -i.bak '/import { ProModeSelector } from ".\/ProModeSelector";/d' src/components/ChatInputControls.tsx
|
||||||
|
sed -i.bak '/<ProModeSelector \/>/d' src/components/ChatInputControls.tsx
|
||||||
|
sed -i.bak '/<div className="w-1.5"><\/div>/d' src/components/ChatInputControls.tsx
|
||||||
|
rm src/components/ChatInputControls.tsx.bak
|
||||||
|
print_success "Removed ProModeSelector from chat controls"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove Pro restrictions from PreviewIframe (Annotator)
|
||||||
|
if [ -f "src/components/preview_panel/PreviewIframe.tsx" ]; then
|
||||||
|
sed -i.bak '/import { AnnotatorOnlyForPro } from ".\/AnnotatorOnlyForPro";/d' src/components/preview_panel/PreviewIframe.tsx
|
||||||
|
sed -i.bak '/{userBudget ? (/,/)} : (/,/<AnnotatorOnlyForPro/,/)}\/>/c\
|
||||||
|
<Annotator\
|
||||||
|
screenshotUrl={screenshotDataUrl}\
|
||||||
|
onSubmit={addAttachments}\
|
||||||
|
handleAnnotatorClick={handleAnnotatorClick}\
|
||||||
|
/>' src/components/preview_panel/PreviewIframe.tsx
|
||||||
|
rm src/components/preview_panel/PreviewIframe.tsx.bak
|
||||||
|
print_success "Removed Pro restrictions from Annotator"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Comment out ProBanner in home.tsx
|
||||||
|
if [ -f "src/pages/home.tsx" ]; then
|
||||||
|
sed -i.bak 's|// import { ProBanner } from "@/components/ProBanner";|// import { ProBanner } from "@/components/ProBanner";|g' src/pages/home.tsx
|
||||||
|
sed -i.bak 's|{<ProBanner />}|// {<ProBanner />}|g' src/pages/home.tsx
|
||||||
|
rm src/pages/home.tsx.bak
|
||||||
|
print_success "Commented out ProBanner"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to update branding
|
# Function to update branding
|
||||||
update_branding() {
|
update_branding() {
|
||||||
print_status "Updating branding from Dyad to MoreMinimore..."
|
print_status "Updating branding from Dyad to MoreMinimore..."
|
||||||
|
|
||||||
# Update package.json
|
# Package.json already updated - keeping original @dyad-sh/supabase-management-js
|
||||||
if [ -f "package.json" ]; then
|
print_success "Package.json configuration maintained"
|
||||||
sed -i.bak 's/@dyad-sh\/supabase-management-js/@moreminimore\/supabase-management-js/g' package.json
|
|
||||||
rm package.json.bak
|
|
||||||
print_success "Updated package.json branding"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update app name in main.ts
|
# Update app name in main.ts
|
||||||
if [ -f "src/main.ts" ]; then
|
if [ -f "src/main.ts" ]; then
|
||||||
@@ -192,6 +218,121 @@ update_ui_text() {
|
|||||||
print_success "Updated UI text"
|
print_success "Updated UI text"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to update component names from Dyad to MoreMinimore
|
||||||
|
update_component_names() {
|
||||||
|
print_status "Updating component names from Dyad to MoreMinimore..."
|
||||||
|
|
||||||
|
# Update component imports and exports
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadThink/MoreMinimoreThink/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadTokenSavings/MoreMinimoreTokenSavings/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadCodebaseContext/MoreMinimoreCodebaseContext/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadMarkdownParser/MoreMinimoreMarkdownParser/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadEdit/MoreMinimoreEdit/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadWrite/MoreMinimoreWrite/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadRead/MoreMinimoreRead/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadRename/MoreMinimoreRename/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadDelete/MoreMinimoreDelete/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadSearchReplace/MoreMinimoreSearchReplace/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadCodeSearch/MoreMinimoreCodeSearch/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadCodeSearchResult/MoreMinimoreCodeSearchResult/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadWebSearch/MoreMinimoreWebSearch/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadWebSearchResult/MoreMinimoreWebSearchResult/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadWebCrawl/MoreMinimoreWebCrawl/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadExecuteSql/MoreMinimoreExecuteSql/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadOutput/MoreMinimoreOutput/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadAddDependency/MoreMinimoreAddDependency/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadAddIntegration/MoreMinimoreAddIntegration/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadMcpToolCall/MoreMinimoreMcpToolCall/g' {} \;
|
||||||
|
find src/components/chat -name "*.tsx" -type f -exec sed -i.bak 's/DyadMcpToolResult/MoreMinimoreMcpToolResult/g' {} \;
|
||||||
|
|
||||||
|
# Clean up backup files
|
||||||
|
find src/components/chat -name "*.bak" -type f -delete
|
||||||
|
|
||||||
|
print_success "Updated component names"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update URLs and external links
|
||||||
|
update_urls() {
|
||||||
|
print_status "Updating URLs and external links..."
|
||||||
|
|
||||||
|
# Update Dyad URLs to MoreMinimore URLs
|
||||||
|
find src -name "*.tsx" -type f -exec sed -i.bak 's|https://dyad.sh|https://moreminimore.com|g' {} \;
|
||||||
|
find src -name "*.tsx" -type f -exec sed -i.bak 's|https://www.dyad.sh|https://www.moreminimore.com|g' {} \;
|
||||||
|
find src -name "*.ts" -type f -exec sed -i.bak 's|https://dyad.sh|https://moreminimore.com|g' {} \;
|
||||||
|
find src -name "*.ts" -type f -exec sed -i.bak 's|https://www.dyad.sh|https://www.moreminimore.com|g' {} \;
|
||||||
|
|
||||||
|
# Clean up backup files
|
||||||
|
find src -name "*.bak" -type f -delete
|
||||||
|
|
||||||
|
print_success "Updated URLs"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update branding text
|
||||||
|
update_branding_text() {
|
||||||
|
print_status "Updating branding text..."
|
||||||
|
|
||||||
|
# Update Dyad references to MoreMinimore
|
||||||
|
find src -name "*.tsx" -type f -exec sed -i.bak 's/Dyad Pro/MoreMinimore Pro/g' {} \;
|
||||||
|
find src -name "*.tsx" -type f -exec sed -i.bak 's/Dyad/MoreMinimore/g' {} \;
|
||||||
|
find src -name "*.ts" -type f -exec sed -i.bak 's/Dyad Pro/MoreMinimore Pro/g' {} \;
|
||||||
|
find src -name "*.ts" -type f -exec sed -i.bak 's/Dyad/MoreMinimore/g' {} \;
|
||||||
|
|
||||||
|
# Clean up backup files
|
||||||
|
find src -name "*.bak" -type f -delete
|
||||||
|
|
||||||
|
print_success "Updated branding text"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update AI provider settings
|
||||||
|
update_ai_providers() {
|
||||||
|
print_status "Updating AI provider settings..."
|
||||||
|
|
||||||
|
# Update the auto provider in language model constants
|
||||||
|
if [ -f "src/ipc/shared/language_model_constants.ts" ]; then
|
||||||
|
sed -i.bak 's/displayName: "Dyad",/displayName: "MoreMinimore",/g' src/ipc/shared/language_model_constants.ts
|
||||||
|
sed -i.bak 's|websiteUrl: "https://academy.dyad.sh/settings"|websiteUrl: "https://moreminimore.com/settings"|g' src/ipc/shared/language_model_constants.ts
|
||||||
|
sed -i.bak 's/gatewayPrefix: "dyad\/",/gatewayPrefix: "moreminimore\/",/g' src/ipc/shared/language_model_constants.ts
|
||||||
|
sed -i.bak '/Use the same gateway prefix as Google Gemini for Dyad Pro compatibility./c\
|
||||||
|
// Use the same gateway prefix as Google Gemini for MoreMinimore Pro compatibility.' src/ipc/shared/language_model_constants.ts
|
||||||
|
rm src/ipc/shared/language_model_constants.ts.bak
|
||||||
|
print_success "Updated AI provider settings"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to remove YouTube video section
|
||||||
|
remove_youtube_section() {
|
||||||
|
print_status "Removing YouTube video section..."
|
||||||
|
|
||||||
|
# Comment out OnboardingBanner import and usage in SetupBanner
|
||||||
|
if [ -f "src/components/SetupBanner.tsx" ]; then
|
||||||
|
sed -i.bak 's/import { OnboardingBanner } from ".\/home\/OnboardingBanner";/\/\/ import { OnboardingBanner } from ".\/home\/OnboardingBanner";/g' src/components/SetupBanner.tsx
|
||||||
|
sed -i.bak 's|<OnboardingBanner|{/* <OnboardingBanner|g' src/components/SetupBanner.tsx
|
||||||
|
sed -i.bak 's|setIsVisible={setIsOnboardingVisible} />|setIsVisible={setIsOnboardingVisible} /> */}|g' src/components/SetupBanner.tsx
|
||||||
|
sed -i.bak 's/Not sure what to do? Watch the Get Started video above ☝️/Not sure what to do? Follow the setup steps below to get started./g' src/components/SetupBanner.tsx
|
||||||
|
rm src/components/SetupBanner.tsx.bak
|
||||||
|
print_success "Removed YouTube video section"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update title bar and app metadata
|
||||||
|
update_app_metadata() {
|
||||||
|
print_status "Updating app metadata..."
|
||||||
|
|
||||||
|
# Update title bar
|
||||||
|
if [ -f "src/app/TitleBar.tsx" ]; then
|
||||||
|
sed -i.bak 's/Dyad/MoreMinimore/g' src/app/TitleBar.tsx
|
||||||
|
rm src/app/TitleBar.tsx.bak
|
||||||
|
print_success "Updated title bar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update package.json description (keep name as is for compatibility)
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
sed -i.bak 's/"description": ".*"/"description": "MoreMinimore - AI-powered development environment"/g' package.json
|
||||||
|
rm package.json.bak
|
||||||
|
print_success "Updated package.json description"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Function to clean up imports
|
# Function to clean up imports
|
||||||
cleanup_imports() {
|
cleanup_imports() {
|
||||||
print_status "Cleaning up unused imports..."
|
print_status "Cleaning up unused imports..."
|
||||||
@@ -254,6 +395,12 @@ main() {
|
|||||||
update_branding
|
update_branding
|
||||||
convert_smart_context
|
convert_smart_context
|
||||||
update_ui_text
|
update_ui_text
|
||||||
|
update_component_names
|
||||||
|
update_urls
|
||||||
|
update_branding_text
|
||||||
|
update_ai_providers
|
||||||
|
remove_youtube_section
|
||||||
|
update_app_metadata
|
||||||
cleanup_imports
|
cleanup_imports
|
||||||
install_dependencies
|
install_dependencies
|
||||||
test_compilation
|
test_compilation
|
||||||
@@ -264,13 +411,24 @@ main() {
|
|||||||
echo "✅ Applied custom remove-limit feature"
|
echo "✅ Applied custom remove-limit feature"
|
||||||
echo "✅ Removed Dyad API dependencies"
|
echo "✅ Removed Dyad API dependencies"
|
||||||
echo "✅ Removed Dyad Engine dependencies"
|
echo "✅ Removed Dyad Engine dependencies"
|
||||||
echo "✅ Removed pro features"
|
echo "✅ Removed pro features and Pro button"
|
||||||
echo "✅ Updated branding to MoreMinimore"
|
echo "✅ Updated branding to MoreMinimore"
|
||||||
echo "✅ Converted smart context to standard feature"
|
echo "✅ Converted smart context to standard feature"
|
||||||
echo "✅ Updated UI text"
|
echo "✅ Updated UI text"
|
||||||
|
echo "✅ Updated component names from Dyad to MoreMinimore"
|
||||||
|
echo "✅ Updated URLs and external links"
|
||||||
|
echo "✅ Updated branding text throughout app"
|
||||||
|
echo "✅ Updated AI provider settings"
|
||||||
|
echo "✅ Removed YouTube video section"
|
||||||
|
echo "✅ Updated app metadata and title bar"
|
||||||
echo "✅ Cleaned up unused imports"
|
echo "✅ Cleaned up unused imports"
|
||||||
echo "✅ Installed dependencies"
|
echo "✅ Installed dependencies"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "Key features liberated:"
|
||||||
|
echo "🔓 Smart Context now available to all users"
|
||||||
|
echo "🔓 Annotator tool now available to all users"
|
||||||
|
echo "🔓 Removed Pro upgrade buttons and restrictions"
|
||||||
|
echo ""
|
||||||
echo "Backup created at: $BACKUP_DIR"
|
echo "Backup created at: $BACKUP_DIR"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next steps:"
|
echo "Next steps:"
|
||||||
|
|||||||
@@ -10,11 +10,8 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useDeepLink } from "@/contexts/DeepLinkContext";
|
import { useDeepLink } from "@/contexts/DeepLinkContext";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { DyadProSuccessDialog } from "@/components/DyadProSuccessDialog";
|
|
||||||
import { useTheme } from "@/contexts/ThemeContext";
|
import { useTheme } from "@/contexts/ThemeContext";
|
||||||
import { IpcClient } from "@/ipc/ipc_client";
|
import { IpcClient } from "@/ipc/ipc_client";
|
||||||
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
|
|
||||||
import { UserBudgetInfo } from "@/ipc/ipc_types";
|
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -28,7 +25,6 @@ export const TitleBar = () => {
|
|||||||
const { navigate } = useRouter();
|
const { navigate } = useRouter();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { settings, refreshSettings } = useSettings();
|
const { settings, refreshSettings } = useSettings();
|
||||||
const [isSuccessDialogOpen, setIsSuccessDialogOpen] = useState(false);
|
|
||||||
const [showWindowControls, setShowWindowControls] = useState(false);
|
const [showWindowControls, setShowWindowControls] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -45,16 +41,11 @@ export const TitleBar = () => {
|
|||||||
checkPlatform();
|
checkPlatform();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const showDyadProSuccessDialog = () => {
|
|
||||||
setIsSuccessDialogOpen(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const { lastDeepLink, clearLastDeepLink } = useDeepLink();
|
const { lastDeepLink, clearLastDeepLink } = useDeepLink();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleDeepLink = async () => {
|
const handleDeepLink = async () => {
|
||||||
if (lastDeepLink?.type === "dyad-pro-return") {
|
if (lastDeepLink) {
|
||||||
await refreshSettings();
|
await refreshSettings();
|
||||||
showDyadProSuccessDialog();
|
|
||||||
clearLastDeepLink();
|
clearLastDeepLink();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -73,15 +64,12 @@ export const TitleBar = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDyadPro = !!settings?.providerSettings?.auto?.apiKey?.value;
|
|
||||||
const isDyadProEnabled = Boolean(settings?.enableDyadPro);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="@container z-11 w-full h-11 bg-(--sidebar) absolute top-0 left-0 app-region-drag flex items-center">
|
<div className="@container z-11 w-full h-11 bg-(--sidebar) absolute top-0 left-0 app-region-drag flex items-center">
|
||||||
<div className={`${showWindowControls ? "pl-2" : "pl-18"}`}></div>
|
<div className={`${showWindowControls ? "pl-2" : "pl-18"}`}></div>
|
||||||
|
|
||||||
<img src={logo} alt="Dyad Logo" className="w-6 h-6 mr-0.5" />
|
<img src={logo} alt="MoreMinimore Logo" className="w-6 h-6 mr-0.5" />
|
||||||
<Button
|
<Button
|
||||||
data-testid="title-bar-app-name-button"
|
data-testid="title-bar-app-name-button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
@@ -93,7 +81,6 @@ export const TitleBar = () => {
|
|||||||
>
|
>
|
||||||
{displayText}
|
{displayText}
|
||||||
</Button>
|
</Button>
|
||||||
{isDyadPro && <DyadProButton isDyadProEnabled={isDyadProEnabled} />}
|
|
||||||
|
|
||||||
{/* Preview Header */}
|
{/* Preview Header */}
|
||||||
{location.pathname === "/chat" && (
|
{location.pathname === "/chat" && (
|
||||||
@@ -104,141 +91,52 @@ export const TitleBar = () => {
|
|||||||
|
|
||||||
{showWindowControls && <WindowsControls />}
|
{showWindowControls && <WindowsControls />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DyadProSuccessDialog
|
|
||||||
isOpen={isSuccessDialogOpen}
|
|
||||||
onClose={() => setIsSuccessDialogOpen(false)}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function WindowsControls() {
|
// Windows window controls component
|
||||||
const { isDarkMode } = useTheme();
|
const WindowsControls = () => {
|
||||||
const ipcClient = IpcClient.getInstance();
|
const ipcClient = IpcClient.getInstance();
|
||||||
|
|
||||||
const minimizeWindow = () => {
|
const handleMinimize = () => {
|
||||||
ipcClient.minimizeWindow();
|
ipcClient.minimizeWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
const maximizeWindow = () => {
|
const handleMaximize = () => {
|
||||||
ipcClient.maximizeWindow();
|
ipcClient.maximizeWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeWindow = () => {
|
const handleClose = () => {
|
||||||
ipcClient.closeWindow();
|
ipcClient.closeWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ml-auto flex no-app-region-drag">
|
<div className="flex items-center no-app-region-drag">
|
||||||
<button
|
|
||||||
className="w-10 h-10 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
||||||
onClick={minimizeWindow}
|
|
||||||
aria-label="Minimize"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="12"
|
|
||||||
height="1"
|
|
||||||
viewBox="0 0 12 1"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<rect
|
|
||||||
width="12"
|
|
||||||
height="1"
|
|
||||||
fill={isDarkMode ? "#ffffff" : "#000000"}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-10 h-10 flex items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
||||||
onClick={maximizeWindow}
|
|
||||||
aria-label="Maximize"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 12 12"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<rect
|
|
||||||
x="0.5"
|
|
||||||
y="0.5"
|
|
||||||
width="11"
|
|
||||||
height="11"
|
|
||||||
stroke={isDarkMode ? "#ffffff" : "#000000"}
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="w-10 h-10 flex items-center justify-center hover:bg-red-500 transition-colors"
|
|
||||||
onClick={closeWindow}
|
|
||||||
aria-label="Close"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 12 12"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M1 1L11 11M1 11L11 1"
|
|
||||||
stroke={isDarkMode ? "#ffffff" : "#000000"}
|
|
||||||
strokeWidth="1.5"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DyadProButton({
|
|
||||||
isDyadProEnabled,
|
|
||||||
}: {
|
|
||||||
isDyadProEnabled: boolean;
|
|
||||||
}) {
|
|
||||||
const { navigate } = useRouter();
|
|
||||||
const { userBudget } = useUserBudgetInfo();
|
|
||||||
return (
|
|
||||||
<Button
|
<Button
|
||||||
data-testid="title-bar-dyad-pro-button"
|
variant="ghost"
|
||||||
onClick={() => {
|
|
||||||
navigate({
|
|
||||||
to: providerSettingsRoute.id,
|
|
||||||
params: { provider: "auto" },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
variant="outline"
|
|
||||||
className={cn(
|
|
||||||
"hidden @2xl:block ml-1 no-app-region-drag h-7 bg-indigo-600 text-white dark:bg-indigo-600 dark:text-white text-xs px-2 pt-1 pb-1",
|
|
||||||
!isDyadProEnabled && "bg-zinc-600 dark:bg-zinc-600",
|
|
||||||
)}
|
|
||||||
size="sm"
|
size="sm"
|
||||||
|
className="h-8 w-8 p-0 hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||||
|
onClick={handleMinimize}
|
||||||
>
|
>
|
||||||
{isDyadProEnabled ? "Pro" : "Pro (off)"}
|
<span className="text-xs">−</span>
|
||||||
{userBudget && isDyadProEnabled && (
|
</Button>
|
||||||
<AICreditStatus userBudget={userBudget} />
|
<Button
|
||||||
)}
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 w-8 p-0 hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||||
|
onClick={handleMaximize}
|
||||||
|
>
|
||||||
|
<span className="text-xs">□</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-8 w-8 p-0 hover:bg-red-500 hover:text-white"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<span className="text-xs">×</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AICreditStatus({ userBudget }: { userBudget: UserBudgetInfo }) {
|
|
||||||
const remaining = Math.round(
|
|
||||||
userBudget.totalCredits - userBudget.usedCredits,
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger>
|
|
||||||
<div className="text-xs pl-1 mt-0.5">{remaining} credits</div>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<div>
|
|
||||||
<p>Note: there is a slight delay in updating the credit status.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
|
|||||||
data-testid="no-app-upgrades-needed"
|
data-testid="no-app-upgrades-needed"
|
||||||
className="p-4 bg-green-50 border border-green-200 dark:bg-green-900/20 dark:border-green-800/50 rounded-lg text-sm text-green-800 dark:text-green-300"
|
className="p-4 bg-green-50 border border-green-200 dark:bg-green-900/20 dark:border-green-800/50 rounded-lg text-sm text-green-800 dark:text-green-300"
|
||||||
>
|
>
|
||||||
App is up-to-date and has all Dyad capabilities enabled
|
App is up-to-date and has all MoreMinimore capabilities enabled
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ export function AutoUpdateSwitch() {
|
|||||||
updateSettings({ enableAutoUpdate: checked });
|
updateSettings({ enableAutoUpdate: checked });
|
||||||
toast("Auto-update settings changed", {
|
toast("Auto-update settings changed", {
|
||||||
description:
|
description:
|
||||||
"You will need to restart Dyad for your settings to take effect.",
|
"You will need to restart MoreMinimore for your settings to take effect.",
|
||||||
action: {
|
action: {
|
||||||
label: "Restart Dyad",
|
label: "Restart MoreMinimore",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
IpcClient.getInstance().restartDyad();
|
IpcClient.getInstance().restartMoreMinimore();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ContextFilesPicker } from "./ContextFilesPicker";
|
import { ContextFilesPicker } from "./ContextFilesPicker";
|
||||||
import { ModelPicker } from "./ModelPicker";
|
import { ModelPicker } from "./ModelPicker";
|
||||||
import { ProModeSelector } from "./ProModeSelector";
|
|
||||||
import { ChatModeSelector } from "./ChatModeSelector";
|
import { ChatModeSelector } from "./ChatModeSelector";
|
||||||
import { McpToolsPicker } from "@/components/McpToolsPicker";
|
import { McpToolsPicker } from "@/components/McpToolsPicker";
|
||||||
import { useSettings } from "@/hooks/useSettings";
|
import { useSettings } from "@/hooks/useSettings";
|
||||||
@@ -17,14 +16,10 @@ export function ChatInputControls({
|
|||||||
<ChatModeSelector />
|
<ChatModeSelector />
|
||||||
{settings?.selectedChatMode === "agent" && (
|
{settings?.selectedChatMode === "agent" && (
|
||||||
<>
|
<>
|
||||||
<div className="w-1.5"></div>
|
|
||||||
<McpToolsPicker />
|
<McpToolsPicker />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="w-1.5"></div>
|
|
||||||
<ModelPicker />
|
<ModelPicker />
|
||||||
<div className="w-1.5"></div>
|
|
||||||
<ProModeSelector />
|
|
||||||
<div className="w-1"></div>
|
<div className="w-1"></div>
|
||||||
{showContextFilesPicker && (
|
{showContextFilesPicker && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const CommunityCodeConsentDialog: React.FC<
|
|||||||
<AlertDialogTitle>Community Code Notice</AlertDialogTitle>
|
<AlertDialogTitle>Community Code Notice</AlertDialogTitle>
|
||||||
<AlertDialogDescription className="space-y-3">
|
<AlertDialogDescription className="space-y-3">
|
||||||
<p>
|
<p>
|
||||||
This code was created by a Dyad community member, not our core
|
This code was created by a MoreMinimore community member, not our core
|
||||||
team.
|
team.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
import { InfoIcon, Settings2, Trash2 } from "lucide-react";
|
import { InfoIcon, Settings2, Trash2 } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -19,7 +21,7 @@ import { useContextPaths } from "@/hooks/useContextPaths";
|
|||||||
import type { ContextPathResult } from "@/lib/schemas";
|
import type { ContextPathResult } from "@/lib/schemas";
|
||||||
|
|
||||||
export function ContextFilesPicker() {
|
export function ContextFilesPicker() {
|
||||||
const { settings } = useSettings();
|
const { settings, updateSettings } = useSettings();
|
||||||
const {
|
const {
|
||||||
contextPaths,
|
contextPaths,
|
||||||
smartContextAutoIncludes,
|
smartContextAutoIncludes,
|
||||||
@@ -112,8 +114,7 @@ export function ContextFilesPicker() {
|
|||||||
updateExcludePaths(newPaths);
|
updateExcludePaths(newPaths);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSmartContextEnabled =
|
const isSmartContextEnabled = settings?.enableProSmartFilesContextMode ?? false;
|
||||||
settings?.enableDyadPro && settings?.enableProSmartFilesContextMode;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
||||||
@@ -130,7 +131,7 @@ export function ContextFilesPicker() {
|
|||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>Codebase Context</TooltipContent>
|
<TooltipContent>Context Settings</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
@@ -139,7 +140,7 @@ export function ContextFilesPicker() {
|
|||||||
>
|
>
|
||||||
<div className="relative space-y-4">
|
<div className="relative space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Codebase Context</h3>
|
<h3 className="font-medium">Context Settings</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -152,11 +153,11 @@ export function ContextFilesPicker() {
|
|||||||
<TooltipContent className="max-w-[300px]">
|
<TooltipContent className="max-w-[300px]">
|
||||||
{isSmartContextEnabled ? (
|
{isSmartContextEnabled ? (
|
||||||
<p>
|
<p>
|
||||||
With Smart Context, Dyad uses the most relevant files as
|
With Smart Context, MoreMinimore uses the most relevant files as
|
||||||
context.
|
context.
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p>By default, Dyad uses your whole codebase.</p>
|
<p>By default, MoreMinimore uses your whole codebase.</p>
|
||||||
)}
|
)}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -164,6 +165,28 @@ export function ContextFilesPicker() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Smart Context Toggle */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label htmlFor="smart-context-toggle" className="text-sm font-medium">
|
||||||
|
Smart Context
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Automatically select the most relevant files for context
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Switch
|
||||||
|
id="smart-context-toggle"
|
||||||
|
checked={isSmartContextEnabled}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
updateSettings({
|
||||||
|
enableProSmartFilesContextMode: checked,
|
||||||
|
proSmartContextOption: checked ? "balanced" : undefined,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex w-full max-w-sm items-center space-x-2">
|
<div className="flex w-full max-w-sm items-center space-x-2">
|
||||||
<Input
|
<Input
|
||||||
data-testid="manual-context-files-input"
|
data-testid="manual-context-files-input"
|
||||||
@@ -226,8 +249,8 @@ export function ContextFilesPicker() {
|
|||||||
<div className="rounded-md border border-dashed p-4 text-center">
|
<div className="rounded-md border border-dashed p-4 text-center">
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{isSmartContextEnabled
|
{isSmartContextEnabled
|
||||||
? "Dyad will use Smart Context to automatically find the most relevant files to use as context."
|
? "MoreMinimore will use Smart Context to automatically find the most relevant files to use as context."
|
||||||
: "Dyad will use the entire codebase as context."}
|
: "MoreMinimore will use the entire codebase as context."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function ErrorBoundary({ error }: ErrorComponentProps) {
|
|||||||
${error?.stack ? `\n\`\`\`\n${error.stack.slice(0, 1000)}\n\`\`\`` : ""}
|
${error?.stack ? `\n\`\`\`\n${error.stack.slice(0, 1000)}\n\`\`\`` : ""}
|
||||||
|
|
||||||
## System Information
|
## System Information
|
||||||
- Dyad Version: ${debugInfo.dyadVersion}
|
- MoreMinimore Version: ${debugInfo.dyadVersion}
|
||||||
- Platform: ${debugInfo.platform}
|
- Platform: ${debugInfo.platform}
|
||||||
- Architecture: ${debugInfo.architecture}
|
- Architecture: ${debugInfo.architecture}
|
||||||
- Node Version: ${debugInfo.nodeVersion || "Not available"}
|
- Node Version: ${debugInfo.nodeVersion || "Not available"}
|
||||||
@@ -57,7 +57,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
|
|||||||
// Create the GitHub issue URL with the pre-filled body
|
// Create the GitHub issue URL with the pre-filled body
|
||||||
const encodedBody = encodeURIComponent(issueBody);
|
const encodedBody = encodeURIComponent(issueBody);
|
||||||
const encodedTitle = encodeURIComponent(
|
const encodedTitle = encodeURIComponent(
|
||||||
"[bug] Error in Dyad application",
|
"[bug] Error in MoreMinimore application",
|
||||||
);
|
);
|
||||||
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`;
|
const githubIssueUrl = `https://github.com/dyad-sh/dyad/issues/new?title=${encodedTitle}&labels=bug,filed-from-app,client-error&body=${encodedBody}`;
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ ${debugInfo.logs.slice(-3_500) || "No logs available"}
|
|||||||
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-md flex items-center gap-2">
|
<div className="mt-4 p-3 bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-800 rounded-md flex items-center gap-2">
|
||||||
<LightbulbIcon className="h-4 w-4 text-blue-700 dark:text-blue-400 flex-shrink-0" />
|
<LightbulbIcon className="h-4 w-4 text-blue-700 dark:text-blue-400 flex-shrink-0" />
|
||||||
<p className="text-sm text-blue-700 dark:text-blue-400">
|
<p className="text-sm text-blue-700 dark:text-blue-400">
|
||||||
<strong>Tip:</strong> Try closing and re-opening Dyad as a temporary
|
<strong>Tip:</strong> Try closing and re-opening MoreMinimore as a temporary
|
||||||
workaround.
|
workaround.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) {
|
|||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Dyad Help Bot</DialogTitle>
|
<DialogTitle>MoreMinimore Help Bot</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="flex flex-col gap-3 h-[480px]">
|
<div className="flex flex-col gap-3 h-[480px]">
|
||||||
{error && (
|
{error && (
|
||||||
@@ -183,7 +183,7 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) {
|
|||||||
{messages.length === 0 ? (
|
{messages.length === 0 ? (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="text-sm text-muted-foreground">
|
<div className="text-sm text-muted-foreground">
|
||||||
Ask a question about using Dyad.
|
Ask a question about using MoreMinimore.
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-muted-foreground/70 bg-muted/50 rounded-md p-3">
|
<div className="text-xs text-muted-foreground/70 bg-muted/50 rounded-md p-3">
|
||||||
This conversation may be logged and used to improve the
|
This conversation may be logged and used to improve the
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ Issues that do not meet these requirements will be closed and may need to be res
|
|||||||
<!-- Screenshot of the bug -->
|
<!-- Screenshot of the bug -->
|
||||||
|
|
||||||
## System Information
|
## System Information
|
||||||
- Dyad Version: ${debugInfo.dyadVersion}
|
- MoreMinimore Version: ${debugInfo.dyadVersion}
|
||||||
- Platform: ${debugInfo.platform}
|
- Platform: ${debugInfo.platform}
|
||||||
- Architecture: ${debugInfo.architecture}
|
- Architecture: ${debugInfo.architecture}
|
||||||
- Node Version: ${debugInfo.nodeVersion || "n/a"}
|
- Node Version: ${debugInfo.nodeVersion || "n/a"}
|
||||||
@@ -348,7 +348,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
|
|||||||
<div className="border rounded-md p-3">
|
<div className="border rounded-md p-3">
|
||||||
<h3 className="font-medium mb-2">System Information</h3>
|
<h3 className="font-medium mb-2">System Information</h3>
|
||||||
<div className="text-sm bg-slate-50 dark:bg-slate-900 rounded p-2 max-h-32 overflow-y-auto">
|
<div className="text-sm bg-slate-50 dark:bg-slate-900 rounded p-2 max-h-32 overflow-y-auto">
|
||||||
<p>Dyad Version: {chatLogsData.debugInfo.dyadVersion}</p>
|
<p>MoreMinimore Version: {chatLogsData.debugInfo.dyadVersion}</p>
|
||||||
<p>Platform: {chatLogsData.debugInfo.platform}</p>
|
<p>Platform: {chatLogsData.debugInfo.platform}</p>
|
||||||
<p>Architecture: {chatLogsData.debugInfo.architecture}</p>
|
<p>Architecture: {chatLogsData.debugInfo.architecture}</p>
|
||||||
<p>
|
<p>
|
||||||
@@ -390,7 +390,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
|
|||||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Need help with Dyad?</DialogTitle>
|
<DialogTitle>Need help with MoreMinimore?</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<DialogDescription className="">
|
<DialogDescription className="">
|
||||||
If you need help or want to report an issue, here are some options:
|
If you need help or want to report an issue, here are some options:
|
||||||
@@ -405,11 +405,11 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
|
|||||||
}}
|
}}
|
||||||
className="w-full py-6 border-primary/50 shadow-sm shadow-primary/10 transition-all hover:shadow-md hover:shadow-primary/15"
|
className="w-full py-6 border-primary/50 shadow-sm shadow-primary/10 transition-all hover:shadow-md hover:shadow-primary/15"
|
||||||
>
|
>
|
||||||
<SparklesIcon className="mr-2 h-5 w-5" /> Chat with Dyad help
|
<SparklesIcon className="mr-2 h-5 w-5" /> Chat with MoreMinimore help
|
||||||
bot (Pro)
|
bot (Pro)
|
||||||
</Button>
|
</Button>
|
||||||
<p className="text-sm text-muted-foreground px-2">
|
<p className="text-sm text-muted-foreground px-2">
|
||||||
Opens an in-app help chat assistant that searches through Dyad's
|
Opens an in-app help chat assistant that searches through MoreMinimore's
|
||||||
docs.
|
docs.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
|
|||||||
onSuccess: async (result) => {
|
onSuccess: async (result) => {
|
||||||
showSuccess(
|
showSuccess(
|
||||||
!hasAiRules
|
!hasAiRules
|
||||||
? "App imported successfully. Dyad will automatically generate an AI_RULES.md now."
|
? "App imported successfully. MoreMinimore will automatically generate an AI_RULES.md now."
|
||||||
: "App imported successfully",
|
: "App imported successfully",
|
||||||
);
|
);
|
||||||
onClose();
|
onClose();
|
||||||
@@ -456,14 +456,14 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
|
|||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p className="text-xs">
|
<p className="text-xs">
|
||||||
AI_RULES.md lets Dyad know which tech stack to
|
AI_RULES.md lets MoreMinimore know which tech stack to
|
||||||
use for editing the app
|
use for editing the app
|
||||||
</p>
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
<AlertDescription className="text-xs sm:text-sm">
|
<AlertDescription className="text-xs sm:text-sm">
|
||||||
No AI_RULES.md found. Dyad will automatically generate
|
No AI_RULES.md found. MoreMinimore will automatically generate
|
||||||
one after importing.
|
one after importing.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|||||||
@@ -266,8 +266,8 @@ export function ModelPicker() {
|
|||||||
{/* Primary providers as submenus */}
|
{/* Primary providers as submenus */}
|
||||||
{primaryProviders.map(([providerId, models]) => {
|
{primaryProviders.map(([providerId, models]) => {
|
||||||
models = models.filter((model) => {
|
models = models.filter((model) => {
|
||||||
// Don't show free models if Dyad Pro is enabled because
|
// Don't show free models if MoreMinimore Pro is enabled because
|
||||||
// we will use the paid models (in Dyad Pro backend) which
|
// we will use the paid models (in MoreMinimore Pro backend) which
|
||||||
// don't have the free limitations.
|
// don't have the free limitations.
|
||||||
if (
|
if (
|
||||||
isDyadProEnabled(settings) &&
|
isDyadProEnabled(settings) &&
|
||||||
@@ -280,7 +280,7 @@ export function ModelPicker() {
|
|||||||
const provider = providers?.find((p) => p.id === providerId);
|
const provider = providers?.find((p) => p.id === providerId);
|
||||||
const providerDisplayName =
|
const providerDisplayName =
|
||||||
provider?.id === "auto"
|
provider?.id === "auto"
|
||||||
? "Dyad Turbo"
|
? "MoreMinimore Turbo"
|
||||||
: (provider?.name ?? providerId);
|
: (provider?.name ?? providerId);
|
||||||
return (
|
return (
|
||||||
<DropdownMenuSub key={providerId}>
|
<DropdownMenuSub key={providerId}>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export function ManageDyadProButton() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KeyRound aria-hidden="true" />
|
<KeyRound aria-hidden="true" />
|
||||||
Manage Dyad Pro subscription
|
Manage MoreMinimore Pro subscription
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ export function SetupDyadProButton() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<KeyRound aria-hidden="true" />
|
<KeyRound aria-hidden="true" />
|
||||||
Already have Dyad Pro? Add your key
|
Already have MoreMinimore Pro? Add your key
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -104,10 +104,10 @@ export function AiAccessBanner() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Subscribe to Dyad Pro"
|
aria-label="Subscribe to MoreMinimore Pro"
|
||||||
className="inline-flex items-center rounded-md bg-white/90 text-indigo-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
className="inline-flex items-center rounded-md bg-white/90 text-indigo-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
||||||
>
|
>
|
||||||
Get Dyad Pro
|
Get MoreMinimore Pro
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -175,10 +175,10 @@ export function SmartContextBanner() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Get Dyad Pro"
|
aria-label="Get MoreMinimore Pro"
|
||||||
className="inline-flex items-center rounded-md bg-white/90 text-emerald-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
className="inline-flex items-center rounded-md bg-white/90 text-emerald-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
||||||
>
|
>
|
||||||
Get Dyad Pro
|
Get MoreMinimore Pro
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -216,10 +216,10 @@ export function TurboBanner() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Get Dyad Pro"
|
aria-label="Get MoreMinimore Pro"
|
||||||
className="inline-flex items-center rounded-md bg-white/90 text-rose-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
className="inline-flex items-center rounded-md bg-white/90 text-rose-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
|
||||||
>
|
>
|
||||||
Get Dyad Pro
|
Get MoreMinimore Pro
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,14 +75,14 @@ export function ProModeSelector() {
|
|||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>Configure Dyad Pro settings</TooltipContent>
|
<TooltipContent>Configure MoreMinimore Pro settings</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<PopoverContent className="w-80 border-primary/20">
|
<PopoverContent className="w-80 border-primary/20">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<h4 className="font-medium flex items-center gap-1.5">
|
<h4 className="font-medium flex items-center gap-1.5">
|
||||||
<Sparkles className="h-4 w-4 text-primary" />
|
<Sparkles className="h-4 w-4 text-primary" />
|
||||||
<span className="text-primary font-medium">Dyad Pro</span>
|
<span className="text-primary font-medium">MoreMinimore Pro</span>
|
||||||
</h4>
|
</h4>
|
||||||
<div className="h-px bg-gradient-to-r from-primary/50 via-primary/20 to-transparent" />
|
<div className="h-px bg-gradient-to-r from-primary/50 via-primary/20 to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
@@ -110,8 +110,8 @@ export function ProModeSelector() {
|
|||||||
<div className="flex flex-col gap-5">
|
<div className="flex flex-col gap-5">
|
||||||
<SelectorRow
|
<SelectorRow
|
||||||
id="pro-enabled"
|
id="pro-enabled"
|
||||||
label="Enable Dyad Pro"
|
label="Enable MoreMinimore Pro"
|
||||||
tooltip="Uses Dyad Pro AI credits for the main AI model and Pro modes."
|
tooltip="Uses MoreMinimore Pro AI credits for the main AI model and Pro modes."
|
||||||
isTogglable={hasProKey}
|
isTogglable={hasProKey}
|
||||||
settingEnabled={Boolean(settings?.enableDyadPro)}
|
settingEnabled={Boolean(settings?.enableDyadPro)}
|
||||||
toggle={toggleProEnabled}
|
toggle={toggleProEnabled}
|
||||||
@@ -119,7 +119,7 @@ export function ProModeSelector() {
|
|||||||
<SelectorRow
|
<SelectorRow
|
||||||
id="web-search"
|
id="web-search"
|
||||||
label="Web Access"
|
label="Web Access"
|
||||||
tooltip="Allows Dyad to access the web (e.g. search for information)"
|
tooltip="Allows MoreMinimore to access the web (e.g. search for information)"
|
||||||
isTogglable={proModeTogglable}
|
isTogglable={proModeTogglable}
|
||||||
settingEnabled={Boolean(settings?.enableProWebSearch)}
|
settingEnabled={Boolean(settings?.enableProWebSearch)}
|
||||||
toggle={toggleWebSearch}
|
toggle={toggleWebSearch}
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ export function ReleaseChannelSelector() {
|
|||||||
} else {
|
} else {
|
||||||
toast("Using Beta release channel", {
|
toast("Using Beta release channel", {
|
||||||
description:
|
description:
|
||||||
"You will need to restart Dyad for your settings to take effect.",
|
"You will need to restart MoreMinimore for your settings to take effect.",
|
||||||
action: {
|
action: {
|
||||||
label: "Restart Dyad",
|
label: "Restart MoreMinimore",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
IpcClient.getInstance().restartDyad();
|
IpcClient.getInstance().restartMoreMinimore();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
|
|||||||
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
|
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import logo from "../../assets/logo.svg";
|
import logo from "../../assets/logo.svg";
|
||||||
import { OnboardingBanner } from "./home/OnboardingBanner";
|
// import { OnboardingBanner } from "./home/OnboardingBanner";
|
||||||
import { showError } from "@/lib/toast";
|
import { showError } from "@/lib/toast";
|
||||||
import { useSettings } from "@/hooks/useSettings";
|
import { useSettings } from "@/hooks/useSettings";
|
||||||
|
|
||||||
@@ -114,12 +114,6 @@ export function SetupBanner() {
|
|||||||
params: { provider: "openrouter" },
|
params: { provider: "openrouter" },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleDyadProSetupClick = () => {
|
|
||||||
posthog.capture("setup-flow:ai-provider-setup:dyad:click");
|
|
||||||
IpcClient.getInstance().openExternalUrl(
|
|
||||||
"https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=setup-banner",
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOtherProvidersClick = () => {
|
const handleOtherProvidersClick = () => {
|
||||||
posthog.capture("setup-flow:ai-provider-setup:other:click");
|
posthog.capture("setup-flow:ai-provider-setup:other:click");
|
||||||
@@ -178,12 +172,12 @@ export function SetupBanner() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className="text-xl font-medium text-zinc-700 dark:text-zinc-300 p-4">
|
<p className="text-xl font-medium text-zinc-700 dark:text-zinc-300 p-4">
|
||||||
Setup Dyad
|
Setup MoreMinimore
|
||||||
</p>
|
</p>
|
||||||
<OnboardingBanner
|
{/* <OnboardingBanner
|
||||||
isVisible={isOnboardingVisible}
|
isVisible={isOnboardingVisible}
|
||||||
setIsVisible={setIsOnboardingVisible}
|
setIsVisible={setIsOnboardingVisible}
|
||||||
/>
|
/> */}
|
||||||
<div className={bannerClasses}>
|
<div className={bannerClasses}>
|
||||||
<Accordion
|
<Accordion
|
||||||
type="multiple"
|
type="multiple"
|
||||||
@@ -313,7 +307,7 @@ export function SetupBanner() {
|
|||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="px-4 pt-2 pb-4 bg-white dark:bg-zinc-900 border-t border-inherit">
|
<AccordionContent className="px-4 pt-2 pb-4 bg-white dark:bg-zinc-900 border-t border-inherit">
|
||||||
<p className="text-[15px] mb-3">
|
<p className="text-[15px] mb-3">
|
||||||
Not sure what to do? Watch the Get Started video above ☝️
|
Not sure what to do? Follow the setup steps below to get started.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<SetupProviderCard
|
<SetupProviderCard
|
||||||
@@ -341,19 +335,6 @@ export function SetupBanner() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SetupProviderCard
|
|
||||||
className="mt-2"
|
|
||||||
variant="dyad"
|
|
||||||
onClick={handleDyadProSetupClick}
|
|
||||||
tabIndex={isNodeSetupComplete ? 0 : -1}
|
|
||||||
leadingIcon={
|
|
||||||
<img src={logo} alt="Dyad Logo" className="w-6 h-6 mr-0.5" />
|
|
||||||
}
|
|
||||||
title="Setup Dyad Pro"
|
|
||||||
subtitle="Access all AI models with one plan"
|
|
||||||
chip={<>Recommended</>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="mt-2 p-3 bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800/70 transition-colors"
|
className="mt-2 p-3 bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800/70 transition-colors"
|
||||||
onClick={handleOtherProvidersClick}
|
onClick={handleOtherProvidersClick}
|
||||||
@@ -446,7 +427,7 @@ function NodeInstallButton({
|
|||||||
case "finished-checking":
|
case "finished-checking":
|
||||||
return (
|
return (
|
||||||
<div className="mt-3 text-sm text-red-600 dark:text-red-400">
|
<div className="mt-3 text-sm text-red-600 dark:text-red-400">
|
||||||
Node.js not detected. Closing and re-opening Dyad usually fixes this.
|
Node.js not detected. Closing and re-opening MoreMinimore usually fixes this.
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ChevronRight } from "lucide-react";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
type SetupProviderVariant = "google" | "openrouter" | "dyad";
|
type SetupProviderVariant = "google" | "openrouter";
|
||||||
|
|
||||||
export function SetupProviderCard({
|
export function SetupProviderCard({
|
||||||
variant,
|
variant,
|
||||||
@@ -94,15 +94,6 @@ function getVariantStyles(variant: SetupProviderVariant) {
|
|||||||
subtitleColor: "text-teal-600 dark:text-teal-400",
|
subtitleColor: "text-teal-600 dark:text-teal-400",
|
||||||
chevronColor: "text-teal-600 dark:text-teal-400",
|
chevronColor: "text-teal-600 dark:text-teal-400",
|
||||||
} as const;
|
} as const;
|
||||||
case "dyad":
|
|
||||||
return {
|
|
||||||
container:
|
|
||||||
"bg-primary/10 border-primary/50 dark:bg-violet-800/50 dark:border-violet-700 hover:bg-violet-100 dark:hover:bg-violet-900/70",
|
|
||||||
iconWrapper: "bg-primary/5 dark:bg-violet-800",
|
|
||||||
titleColor: "text-violet-800 dark:text-violet-300",
|
|
||||||
subtitleColor: "text-violet-600 dark:text-violet-400",
|
|
||||||
chevronColor: "text-violet-600 dark:text-violet-400",
|
|
||||||
} as const;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function PrivacyBanner() {
|
|||||||
Share anonymous data?
|
Share anonymous data?
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||||
Help improve Dyad with anonymous usage data.
|
Help improve MoreMinimore with anonymous usage data.
|
||||||
<em className="block italic mt-0.5">
|
<em className="block italic mt-0.5">
|
||||||
Note: this does not log your code or messages.
|
Note: this does not log your code or messages.
|
||||||
</em>
|
</em>
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ function AppIcons({
|
|||||||
return (
|
return (
|
||||||
// When collapsed: only show the main menu
|
// When collapsed: only show the main menu
|
||||||
<SidebarGroup className="pr-0">
|
<SidebarGroup className="pr-0">
|
||||||
{/* <SidebarGroupLabel>Dyad</SidebarGroupLabel> */}
|
{/* <SidebarGroupLabel>MoreMinimore</SidebarGroupLabel> */}
|
||||||
|
|
||||||
<SidebarGroupContent>
|
<SidebarGroupContent>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function ChatErrorBox({
|
|||||||
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=free-quota-error"
|
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=free-quota-error"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
Access with Dyad Pro
|
Access with MoreMinimore Pro
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
or switch to another model.
|
or switch to another model.
|
||||||
@@ -37,9 +37,9 @@ export function ChatErrorBox({
|
|||||||
// Important, this needs to come after the "free quota tier" check
|
// Important, this needs to come after the "free quota tier" check
|
||||||
// because it also includes this URL in the error message
|
// because it also includes this URL in the error message
|
||||||
//
|
//
|
||||||
// Sometimes Dyad Pro can return rate limit errors and we do not want to
|
// Sometimes MoreMinimore Pro can return rate limit errors and we do not want to
|
||||||
// show the upgrade to Dyad Pro link in that case because they are
|
// show the upgrade to MoreMinimore Pro link in that case because they are
|
||||||
// already on the Dyad Pro plan.
|
// already on the MoreMinimore Pro plan.
|
||||||
if (
|
if (
|
||||||
!isDyadProEnabled &&
|
!isDyadProEnabled &&
|
||||||
(error.includes("Resource has been exhausted") ||
|
(error.includes("Resource has been exhausted") ||
|
||||||
@@ -54,7 +54,7 @@ export function ChatErrorBox({
|
|||||||
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=rate-limit-error"
|
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=rate-limit-error"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
Upgrade to Dyad Pro
|
Upgrade to MoreMinimore Pro
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
|
|
||||||
<ExternalLink href="https://dyad.sh/docs/help/ai-rate-limit">
|
<ExternalLink href="https://dyad.sh/docs/help/ai-rate-limit">
|
||||||
@@ -69,12 +69,12 @@ export function ChatErrorBox({
|
|||||||
return (
|
return (
|
||||||
<ChatInfoContainer onDismiss={onDismiss}>
|
<ChatInfoContainer onDismiss={onDismiss}>
|
||||||
<span>
|
<span>
|
||||||
Looks like you don't have a valid Dyad Pro key.{" "}
|
Looks like you don't have a valid MoreMinimore Pro key.{" "}
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=invalid-pro-key-error"
|
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=invalid-pro-key-error"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
Upgrade to Dyad Pro
|
Upgrade to MoreMinimore Pro
|
||||||
</ExternalLink>{" "}
|
</ExternalLink>{" "}
|
||||||
today.
|
today.
|
||||||
</span>
|
</span>
|
||||||
@@ -85,7 +85,7 @@ export function ChatErrorBox({
|
|||||||
return (
|
return (
|
||||||
<ChatInfoContainer onDismiss={onDismiss}>
|
<ChatInfoContainer onDismiss={onDismiss}>
|
||||||
<span>
|
<span>
|
||||||
You have used all of your Dyad AI credits this month.{" "}
|
You have used all of your MoreMinimore AI credits this month.{" "}
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
href="https://academy.dyad.sh/subscription?utm_source=dyad-app&utm_medium=app&utm_campaign=exceeded-budget-error"
|
href="https://academy.dyad.sh/subscription?utm_source=dyad-app&utm_medium=app&utm_campaign=exceeded-budget-error"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@@ -117,7 +117,7 @@ export function ChatErrorBox({
|
|||||||
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=general-error"
|
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=general-error"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
Upgrade to Dyad Pro
|
Upgrade to MoreMinimore Pro
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
)}
|
)}
|
||||||
<ExternalLink href="https://www.dyad.sh/docs/faq">
|
<ExternalLink href="https://www.dyad.sh/docs/faq">
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
|
|||||||
onChange={setInputValue}
|
onChange={setInputValue}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onPaste={handlePaste}
|
onPaste={handlePaste}
|
||||||
placeholder="Ask Dyad to build..."
|
placeholder="Ask MoreMinimore to build..."
|
||||||
excludeCurrentApp={true}
|
excludeCurrentApp={true}
|
||||||
disableSendButton={disableSendButton}
|
disableSendButton={disableSendButton}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Message } from "@/ipc/ipc_types";
|
import type { Message } from "@/ipc/ipc_types";
|
||||||
import {
|
import {
|
||||||
DyadMarkdownParser,
|
MoreMinimoreMarkdownParser,
|
||||||
VanillaMarkdownParser,
|
VanillaMarkdownParser,
|
||||||
} from "./DyadMarkdownParser";
|
} from "./MoreMinimoreMarkdownParser";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useStreamChat } from "@/hooks/useStreamChat";
|
import { useStreamChat } from "@/hooks/useStreamChat";
|
||||||
import {
|
import {
|
||||||
@@ -140,7 +140,7 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
|
|||||||
>
|
>
|
||||||
{message.role === "assistant" ? (
|
{message.role === "assistant" ? (
|
||||||
<>
|
<>
|
||||||
<DyadMarkdownParser content={message.content} />
|
<MoreMinimoreMarkdownParser content={message.content} />
|
||||||
{isLastMessage && isStreaming && (
|
{isLastMessage && isStreaming && (
|
||||||
<div className="mt-4 ml-4 relative w-5 h-5 animate-spin">
|
<div className="mt-4 ml-4 relative w-5 h-5 animate-spin">
|
||||||
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-(--primary) dark:bg-blue-500 rounded-full"></div>
|
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-(--primary) dark:bg-blue-500 rounded-full"></div>
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import { IpcClient } from "../../ipc/ipc_client";
|
|||||||
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
interface DyadAddDependencyProps {
|
interface MoreMinimoreAddDependencyProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
packages?: string;
|
packages?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
|
export const MoreMinimoreAddDependency: React.FC<MoreMinimoreAddDependencyProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useAtomValue } from "jotai";
|
|||||||
import { showError } from "@/lib/toast";
|
import { showError } from "@/lib/toast";
|
||||||
import { useLoadApp } from "@/hooks/useLoadApp";
|
import { useLoadApp } from "@/hooks/useLoadApp";
|
||||||
|
|
||||||
interface DyadAddIntegrationProps {
|
interface MoreMinimoreAddIntegrationProps {
|
||||||
node: {
|
node: {
|
||||||
properties: {
|
properties: {
|
||||||
provider: string;
|
provider: string;
|
||||||
@@ -15,7 +15,7 @@ interface DyadAddIntegrationProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadAddIntegration: React.FC<DyadAddIntegrationProps> = ({
|
export const MoreMinimoreAddIntegration: React.FC<MoreMinimoreAddIntegrationProps> = ({
|
||||||
node,
|
node,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { FileCode } from "lucide-react";
|
import { FileCode } from "lucide-react";
|
||||||
|
|
||||||
interface DyadCodeSearchProps {
|
interface MoreMinimoreCodeSearchProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
query?: string;
|
query?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadCodeSearch: React.FC<DyadCodeSearchProps> = ({
|
export const MoreMinimoreCodeSearch: React.FC<MoreMinimoreCodeSearchProps> = ({
|
||||||
children,
|
children,
|
||||||
node: _node,
|
node: _node,
|
||||||
query: queryProp,
|
query: queryProp,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useMemo } from "react";
|
import React, { useState, useMemo } from "react";
|
||||||
import { ChevronDown, ChevronUp, FileCode, FileText } from "lucide-react";
|
import { ChevronDown, ChevronUp, FileCode, FileText } from "lucide-react";
|
||||||
|
|
||||||
interface DyadCodeSearchResultProps {
|
interface MoreMinimoreCodeSearchResultProps {
|
||||||
node?: any;
|
node?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
|
export const MoreMinimoreCodeSearchResult: React.FC<MoreMinimoreCodeSearchResultProps> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { ChevronUp, ChevronDown, Code2, FileText } from "lucide-react";
|
import { ChevronUp, ChevronDown, Code2, FileText } from "lucide-react";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
interface DyadCodebaseContextProps {
|
interface MoreMinimoreCodebaseContextProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
node?: {
|
node?: {
|
||||||
properties?: {
|
properties?: {
|
||||||
@@ -12,7 +12,7 @@ interface DyadCodebaseContextProps {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadCodebaseContext: React.FC<DyadCodebaseContextProps> = ({
|
export const MoreMinimoreCodebaseContext: React.FC<MoreMinimoreCodebaseContextProps> = ({
|
||||||
node,
|
node,
|
||||||
}) => {
|
}) => {
|
||||||
const state = node?.properties?.state as CustomTagState;
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { Trash2 } from "lucide-react";
|
import { Trash2 } from "lucide-react";
|
||||||
|
|
||||||
interface DyadDeleteProps {
|
interface MoreMinimoreDeleteProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadDelete: React.FC<DyadDeleteProps> = ({
|
export const MoreMinimoreDelete: React.FC<MoreMinimoreDeleteProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
path: pathProp,
|
path: pathProp,
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ import {
|
|||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
interface DyadEditProps {
|
interface MoreMinimoreEditProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadEdit: React.FC<DyadEditProps> = ({
|
export const MoreMinimoreEdit: React.FC<MoreMinimoreEditProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
path: pathProp,
|
path: pathProp,
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import {
|
|||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
interface DyadExecuteSqlProps {
|
interface MoreMinimoreExecuteSqlProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
|
export const MoreMinimoreExecuteSql: React.FC<MoreMinimoreExecuteSqlProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
description,
|
description,
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
import { DyadWrite } from "./DyadWrite";
|
import { MoreMinimoreWrite } from "./MoreMinimoreWrite";
|
||||||
import { DyadRename } from "./DyadRename";
|
import { MoreMinimoreRename } from "./MoreMinimoreRename";
|
||||||
import { DyadDelete } from "./DyadDelete";
|
import { MoreMinimoreDelete } from "./MoreMinimoreDelete";
|
||||||
import { DyadAddDependency } from "./DyadAddDependency";
|
import { MoreMinimoreAddDependency } from "./MoreMinimoreAddDependency";
|
||||||
import { DyadExecuteSql } from "./DyadExecuteSql";
|
import { MoreMinimoreExecuteSql } from "./MoreMinimoreExecuteSql";
|
||||||
import { DyadAddIntegration } from "./DyadAddIntegration";
|
import { MoreMinimoreAddIntegration } from "./MoreMinimoreAddIntegration";
|
||||||
import { DyadEdit } from "./DyadEdit";
|
import { MoreMinimoreEdit } from "./MoreMinimoreEdit";
|
||||||
import { DyadSearchReplace } from "./DyadSearchReplace";
|
import { MoreMinimoreSearchReplace } from "./MoreMinimoreSearchReplace";
|
||||||
import { DyadCodebaseContext } from "./DyadCodebaseContext";
|
import { MoreMinimoreCodebaseContext } from "./MoreMinimoreCodebaseContext";
|
||||||
import { DyadThink } from "./DyadThink";
|
import { MoreMinimoreThink } from "./MoreMinimoreThink";
|
||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { isStreamingByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms";
|
import { isStreamingByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
import { DyadOutput } from "./DyadOutput";
|
import { MoreMinimoreOutput } from "./MoreMinimoreOutput";
|
||||||
import { DyadProblemSummary } from "./DyadProblemSummary";
|
import { DyadProblemSummary } from "./DyadProblemSummary";
|
||||||
import { IpcClient } from "@/ipc/ipc_client";
|
import { IpcClient } from "@/ipc/ipc_client";
|
||||||
import { DyadMcpToolCall } from "./DyadMcpToolCall";
|
import { MoreMinimoreMcpToolCall } from "./MoreMinimoreMcpToolCall";
|
||||||
import { DyadMcpToolResult } from "./DyadMcpToolResult";
|
import { MoreMinimoreMcpToolResult } from "./MoreMinimoreMcpToolResult";
|
||||||
import { DyadWebSearchResult } from "./DyadWebSearchResult";
|
import { MoreMinimoreWebSearchResult } from "./MoreMinimoreWebSearchResult";
|
||||||
import { DyadWebSearch } from "./DyadWebSearch";
|
import { MoreMinimoreWebSearch } from "./MoreMinimoreWebSearch";
|
||||||
import { DyadWebCrawl } from "./DyadWebCrawl";
|
import { MoreMinimoreWebCrawl } from "./MoreMinimoreWebCrawl";
|
||||||
import { DyadCodeSearchResult } from "./DyadCodeSearchResult";
|
import { MoreMinimoreCodeSearchResult } from "./MoreMinimoreCodeSearchResult";
|
||||||
import { DyadCodeSearch } from "./DyadCodeSearch";
|
import { MoreMinimoreCodeSearch } from "./MoreMinimoreCodeSearch";
|
||||||
import { DyadRead } from "./DyadRead";
|
import { MoreMinimoreRead } from "./MoreMinimoreRead";
|
||||||
import { mapActionToButton } from "./ChatInput";
|
import { mapActionToButton } from "./ChatInput";
|
||||||
import { SuggestedAction } from "@/lib/schemas";
|
import { SuggestedAction } from "@/lib/schemas";
|
||||||
import { FixAllErrorsButton } from "./FixAllErrorsButton";
|
import { FixAllErrorsButton } from "./FixAllErrorsButton";
|
||||||
|
|
||||||
interface DyadMarkdownParserProps {
|
interface MoreMinimoreMarkdownParserProps {
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +79,9 @@ export const VanillaMarkdownParser = ({ content }: { content: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom component to parse markdown content with Dyad-specific tags
|
* Custom component to parse markdown content with MoreMinimore-specific tags
|
||||||
*/
|
*/
|
||||||
export const DyadMarkdownParser: React.FC<DyadMarkdownParserProps> = ({
|
export const MoreMinimoreMarkdownParser: React.FC<MoreMinimoreMarkdownParserProps> = ({
|
||||||
content,
|
content,
|
||||||
}) => {
|
}) => {
|
||||||
const chatId = useAtomValue(selectedChatIdAtom);
|
const chatId = useAtomValue(selectedChatIdAtom);
|
||||||
@@ -346,7 +346,7 @@ function renderCustomTag(
|
|||||||
switch (tag) {
|
switch (tag) {
|
||||||
case "dyad-read":
|
case "dyad-read":
|
||||||
return (
|
return (
|
||||||
<DyadRead
|
<MoreMinimoreRead
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
path: attributes.path || "",
|
path: attributes.path || "",
|
||||||
@@ -354,51 +354,51 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadRead>
|
</MoreMinimoreRead>
|
||||||
);
|
);
|
||||||
case "dyad-web-search":
|
case "dyad-web-search":
|
||||||
return (
|
return (
|
||||||
<DyadWebSearch
|
<MoreMinimoreWebSearch
|
||||||
node={{
|
node={{
|
||||||
properties: {},
|
properties: {},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadWebSearch>
|
</MoreMinimoreWebSearch>
|
||||||
);
|
);
|
||||||
case "dyad-web-crawl":
|
case "dyad-web-crawl":
|
||||||
return (
|
return (
|
||||||
<DyadWebCrawl
|
<MoreMinimoreWebCrawl
|
||||||
node={{
|
node={{
|
||||||
properties: {},
|
properties: {},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadWebCrawl>
|
</MoreMinimoreWebCrawl>
|
||||||
);
|
);
|
||||||
case "dyad-code-search":
|
case "dyad-code-search":
|
||||||
return (
|
return (
|
||||||
<DyadCodeSearch
|
<MoreMinimoreCodeSearch
|
||||||
node={{
|
node={{
|
||||||
properties: {},
|
properties: {},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadCodeSearch>
|
</MoreMinimoreCodeSearch>
|
||||||
);
|
);
|
||||||
case "dyad-code-search-result":
|
case "dyad-code-search-result":
|
||||||
return (
|
return (
|
||||||
<DyadCodeSearchResult
|
<MoreMinimoreCodeSearchResult
|
||||||
node={{
|
node={{
|
||||||
properties: {},
|
properties: {},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadCodeSearchResult>
|
</MoreMinimoreCodeSearchResult>
|
||||||
);
|
);
|
||||||
case "dyad-web-search-result":
|
case "dyad-web-search-result":
|
||||||
return (
|
return (
|
||||||
<DyadWebSearchResult
|
<MoreMinimoreWebSearchResult
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
state: getState({ isStreaming, inProgress }),
|
state: getState({ isStreaming, inProgress }),
|
||||||
@@ -406,11 +406,11 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadWebSearchResult>
|
</MoreMinimoreWebSearchResult>
|
||||||
);
|
);
|
||||||
case "think":
|
case "think":
|
||||||
return (
|
return (
|
||||||
<DyadThink
|
<MoreMinimoreThink
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
state: getState({ isStreaming, inProgress }),
|
state: getState({ isStreaming, inProgress }),
|
||||||
@@ -418,11 +418,11 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadThink>
|
</MoreMinimoreThink>
|
||||||
);
|
);
|
||||||
case "dyad-write":
|
case "dyad-write":
|
||||||
return (
|
return (
|
||||||
<DyadWrite
|
<MoreMinimoreWrite
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
path: attributes.path || "",
|
path: attributes.path || "",
|
||||||
@@ -432,12 +432,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadWrite>
|
</MoreMinimoreWrite>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-rename":
|
case "dyad-rename":
|
||||||
return (
|
return (
|
||||||
<DyadRename
|
<MoreMinimoreRename
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
from: attributes.from || "",
|
from: attributes.from || "",
|
||||||
@@ -446,12 +446,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadRename>
|
</MoreMinimoreRename>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-delete":
|
case "dyad-delete":
|
||||||
return (
|
return (
|
||||||
<DyadDelete
|
<MoreMinimoreDelete
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
path: attributes.path || "",
|
path: attributes.path || "",
|
||||||
@@ -459,12 +459,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadDelete>
|
</MoreMinimoreDelete>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-add-dependency":
|
case "dyad-add-dependency":
|
||||||
return (
|
return (
|
||||||
<DyadAddDependency
|
<MoreMinimoreAddDependency
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
packages: attributes.packages || "",
|
packages: attributes.packages || "",
|
||||||
@@ -472,12 +472,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadAddDependency>
|
</MoreMinimoreAddDependency>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-execute-sql":
|
case "dyad-execute-sql":
|
||||||
return (
|
return (
|
||||||
<DyadExecuteSql
|
<MoreMinimoreExecuteSql
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
state: getState({ isStreaming, inProgress }),
|
state: getState({ isStreaming, inProgress }),
|
||||||
@@ -486,12 +486,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadExecuteSql>
|
</MoreMinimoreExecuteSql>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-add-integration":
|
case "dyad-add-integration":
|
||||||
return (
|
return (
|
||||||
<DyadAddIntegration
|
<MoreMinimoreAddIntegration
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
provider: attributes.provider || "",
|
provider: attributes.provider || "",
|
||||||
@@ -499,12 +499,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadAddIntegration>
|
</MoreMinimoreAddIntegration>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-edit":
|
case "dyad-edit":
|
||||||
return (
|
return (
|
||||||
<DyadEdit
|
<MoreMinimoreEdit
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
path: attributes.path || "",
|
path: attributes.path || "",
|
||||||
@@ -514,12 +514,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadEdit>
|
</MoreMinimoreEdit>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-search-replace":
|
case "dyad-search-replace":
|
||||||
return (
|
return (
|
||||||
<DyadSearchReplace
|
<MoreMinimoreSearchReplace
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
path: attributes.path || "",
|
path: attributes.path || "",
|
||||||
@@ -529,12 +529,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadSearchReplace>
|
</MoreMinimoreSearchReplace>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-codebase-context":
|
case "dyad-codebase-context":
|
||||||
return (
|
return (
|
||||||
<DyadCodebaseContext
|
<MoreMinimoreCodebaseContext
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
files: attributes.files || "",
|
files: attributes.files || "",
|
||||||
@@ -543,12 +543,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadCodebaseContext>
|
</MoreMinimoreCodebaseContext>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-mcp-tool-call":
|
case "dyad-mcp-tool-call":
|
||||||
return (
|
return (
|
||||||
<DyadMcpToolCall
|
<MoreMinimoreMcpToolCall
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
serverName: attributes.server || "",
|
serverName: attributes.server || "",
|
||||||
@@ -557,12 +557,12 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadMcpToolCall>
|
</MoreMinimoreMcpToolCall>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-mcp-tool-result":
|
case "dyad-mcp-tool-result":
|
||||||
return (
|
return (
|
||||||
<DyadMcpToolResult
|
<MoreMinimoreMcpToolResult
|
||||||
node={{
|
node={{
|
||||||
properties: {
|
properties: {
|
||||||
serverName: attributes.server || "",
|
serverName: attributes.server || "",
|
||||||
@@ -571,17 +571,17 @@ function renderCustomTag(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadMcpToolResult>
|
</MoreMinimoreMcpToolResult>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-output":
|
case "dyad-output":
|
||||||
return (
|
return (
|
||||||
<DyadOutput
|
<MoreMinimoreOutput
|
||||||
type={attributes.type as "warning" | "error"}
|
type={attributes.type as "warning" | "error"}
|
||||||
message={attributes.message}
|
message={attributes.message}
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</DyadOutput>
|
</MoreMinimoreOutput>
|
||||||
);
|
);
|
||||||
|
|
||||||
case "dyad-problem-report":
|
case "dyad-problem-report":
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import React, { useMemo, useState } from "react";
|
|||||||
import { Wrench, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
import { Wrench, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
interface DyadMcpToolCallProps {
|
interface MoreMinimoreMcpToolCallProps {
|
||||||
node?: any;
|
node?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadMcpToolCall: React.FC<DyadMcpToolCallProps> = ({
|
export const MoreMinimoreMcpToolCall: React.FC<MoreMinimoreMcpToolCallProps> = ({
|
||||||
node,
|
node,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import React, { useMemo, useState } from "react";
|
|||||||
import { CheckCircle, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
import { CheckCircle, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
import { CodeHighlight } from "./CodeHighlight";
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
interface DyadMcpToolResultProps {
|
interface MoreMinimoreMcpToolResultProps {
|
||||||
node?: any;
|
node?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadMcpToolResult: React.FC<DyadMcpToolResultProps> = ({
|
export const MoreMinimoreMcpToolResult: React.FC<MoreMinimoreMcpToolResultProps> = ({
|
||||||
node,
|
node,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import { useAtomValue } from "jotai";
|
|||||||
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
|
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||||
import { useStreamChat } from "@/hooks/useStreamChat";
|
import { useStreamChat } from "@/hooks/useStreamChat";
|
||||||
import { CopyErrorMessage } from "@/components/CopyErrorMessage";
|
import { CopyErrorMessage } from "@/components/CopyErrorMessage";
|
||||||
interface DyadOutputProps {
|
interface MoreMinimoreOutputProps {
|
||||||
type: "error" | "warning";
|
type: "error" | "warning";
|
||||||
message?: string;
|
message?: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadOutput: React.FC<DyadOutputProps> = ({
|
export const MoreMinimoreOutput: React.FC<MoreMinimoreOutputProps> = ({
|
||||||
type,
|
type,
|
||||||
message,
|
message,
|
||||||
children,
|
children,
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { FileText } from "lucide-react";
|
import { FileText } from "lucide-react";
|
||||||
|
|
||||||
interface DyadReadProps {
|
interface MoreMinimoreReadProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadRead: React.FC<DyadReadProps> = ({
|
export const MoreMinimoreRead: React.FC<MoreMinimoreReadProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
path: pathProp,
|
path: pathProp,
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { FileEdit } from "lucide-react";
|
import { FileEdit } from "lucide-react";
|
||||||
|
|
||||||
interface DyadRenameProps {
|
interface MoreMinimoreRenameProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
from?: string;
|
from?: string;
|
||||||
to?: string;
|
to?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadRename: React.FC<DyadRenameProps> = ({
|
export const MoreMinimoreRename: React.FC<MoreMinimoreRenameProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
from: fromProp,
|
from: fromProp,
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import { CodeHighlight } from "./CodeHighlight";
|
|||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
|
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
|
||||||
|
|
||||||
interface DyadSearchReplaceProps {
|
interface MoreMinimoreSearchReplaceProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
|
export const MoreMinimoreSearchReplace: React.FC<MoreMinimoreSearchReplaceProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
path: pathProp,
|
path: pathProp,
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Brain, ChevronDown, ChevronUp, Loader } from "lucide-react";
|
import { Brain, ChevronDown, ChevronUp, Loader } from "lucide-react";
|
||||||
import { VanillaMarkdownParser } from "./DyadMarkdownParser";
|
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
import { DyadTokenSavings } from "./DyadTokenSavings";
|
import { MoreMinimoreTokenSavings } from "./MoreMinimoreTokenSavings";
|
||||||
|
|
||||||
interface DyadThinkProps {
|
interface AIThinkProps {
|
||||||
node?: any;
|
node?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadThink: React.FC<DyadThinkProps> = ({ children, node }) => {
|
export const AIThink: React.FC<AIThinkProps> = ({ children, node }) => {
|
||||||
const state = node?.properties?.state as CustomTagState;
|
const state = node?.properties?.state as CustomTagState;
|
||||||
const inProgress = state === "pending";
|
const inProgress = state === "pending";
|
||||||
const [isExpanded, setIsExpanded] = useState(inProgress);
|
const [isExpanded, setIsExpanded] = useState(inProgress);
|
||||||
@@ -29,12 +29,12 @@ export const DyadThink: React.FC<DyadThinkProps> = ({ children, node }) => {
|
|||||||
}
|
}
|
||||||
}, [inProgress]);
|
}, [inProgress]);
|
||||||
|
|
||||||
// If it's token savings format, render DyadTokenSavings component
|
// If it's token savings format, render MoreMinimoreTokenSavings component
|
||||||
if (tokenSavingsMatch) {
|
if (tokenSavingsMatch) {
|
||||||
const originalTokens = parseFloat(tokenSavingsMatch[1]);
|
const originalTokens = parseFloat(tokenSavingsMatch[1]);
|
||||||
const smartContextTokens = parseFloat(tokenSavingsMatch[2]);
|
const smartContextTokens = parseFloat(tokenSavingsMatch[2]);
|
||||||
return (
|
return (
|
||||||
<DyadTokenSavings
|
<MoreMinimoreTokenSavings
|
||||||
originalTokens={originalTokens}
|
originalTokens={originalTokens}
|
||||||
smartContextTokens={smartContextTokens}
|
smartContextTokens={smartContextTokens}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import React from "react";
|
|||||||
import { Zap } from "lucide-react";
|
import { Zap } from "lucide-react";
|
||||||
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
|
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
|
||||||
|
|
||||||
interface DyadTokenSavingsProps {
|
interface TokenSavingsProps {
|
||||||
originalTokens: number;
|
originalTokens: number;
|
||||||
smartContextTokens: number;
|
smartContextTokens: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadTokenSavings: React.FC<DyadTokenSavingsProps> = ({
|
export const TokenSavings: React.FC<TokenSavingsProps> = ({
|
||||||
originalTokens,
|
originalTokens,
|
||||||
smartContextTokens,
|
smartContextTokens,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { ScanQrCode } from "lucide-react";
|
import { ScanQrCode } from "lucide-react";
|
||||||
|
|
||||||
interface DyadWebCrawlProps {
|
interface MoreMinimoreWebCrawlProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadWebCrawl: React.FC<DyadWebCrawlProps> = ({
|
export const MoreMinimoreWebCrawl: React.FC<MoreMinimoreWebCrawlProps> = ({
|
||||||
children,
|
children,
|
||||||
node: _node,
|
node: _node,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import type React from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import { Globe } from "lucide-react";
|
import { Globe } from "lucide-react";
|
||||||
|
|
||||||
interface DyadWebSearchProps {
|
interface MoreMinimoreWebSearchProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
query?: string;
|
query?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadWebSearch: React.FC<DyadWebSearchProps> = ({
|
export const MoreMinimoreWebSearch: React.FC<MoreMinimoreWebSearchProps> = ({
|
||||||
children,
|
children,
|
||||||
node: _node,
|
node: _node,
|
||||||
query: queryProp,
|
query: queryProp,
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { ChevronDown, ChevronUp, Globe, Loader } from "lucide-react";
|
import { ChevronDown, ChevronUp, Globe, Loader } from "lucide-react";
|
||||||
import { VanillaMarkdownParser } from "./DyadMarkdownParser";
|
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||||
import { CustomTagState } from "./stateTypes";
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
interface DyadWebSearchResultProps {
|
interface MoreMinimoreWebSearchResultProps {
|
||||||
node?: any;
|
node?: any;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadWebSearchResult: React.FC<DyadWebSearchResultProps> = ({
|
export const MoreMinimoreWebSearchResult: React.FC<MoreMinimoreWebSearchResultProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
}) => {
|
}) => {
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import { FileEditor } from "../preview_panel/FileEditor";
|
|||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
||||||
|
|
||||||
interface DyadWriteProps {
|
interface MoreMinimoreWriteProps {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
node?: any;
|
node?: any;
|
||||||
path?: string;
|
path?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DyadWrite: React.FC<DyadWriteProps> = ({
|
export const MoreMinimoreWrite: React.FC<MoreMinimoreWriteProps> = ({
|
||||||
children,
|
children,
|
||||||
node,
|
node,
|
||||||
path: pathProp,
|
path: pathProp,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function HomeChatInput({
|
|||||||
"an information page...",
|
"an information page...",
|
||||||
"a landing page...",
|
"a landing page...",
|
||||||
]);
|
]);
|
||||||
const placeholder = `Ask Dyad to build ${typingText ?? ""}`;
|
const placeholder = `Ask MoreMinimore to build ${typingText ?? ""}`;
|
||||||
|
|
||||||
// Use the attachments hook
|
// Use the attachments hook
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ export function LexicalChatInput({
|
|||||||
onSubmit,
|
onSubmit,
|
||||||
onPaste,
|
onPaste,
|
||||||
excludeCurrentApp,
|
excludeCurrentApp,
|
||||||
placeholder = "Ask Dyad to build...",
|
placeholder = "Ask MoreMinimore to build...",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
disableSendButton,
|
disableSendButton,
|
||||||
}: LexicalChatInputProps) {
|
}: LexicalChatInputProps) {
|
||||||
|
|||||||
93
src/components/chat/MoreMinimoreAddDependency.tsx
Normal file
93
src/components/chat/MoreMinimoreAddDependency.tsx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { IpcClient } from "../../ipc/ipc_client";
|
||||||
|
|
||||||
|
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
|
interface MoreMinimoreAddDependencyProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
packages?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreAddDependency: React.FC<MoreMinimoreAddDependencyProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
}) => {
|
||||||
|
// Extract package attribute from the node if available
|
||||||
|
const packages = node?.properties?.packages?.split(" ") || "";
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
const hasChildren = !!children;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-(--background-lightest) dark:bg-gray-900 hover:bg-(--background-lighter) rounded-lg px-4 py-3 border my-2 border-border ${
|
||||||
|
hasChildren ? "cursor-pointer" : ""
|
||||||
|
}`}
|
||||||
|
onClick={
|
||||||
|
hasChildren ? () => setIsContentVisible(!isContentVisible) : undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Package size={18} className="text-gray-600 dark:text-gray-400" />
|
||||||
|
{packages.length > 0 && (
|
||||||
|
<div className="text-gray-800 dark:text-gray-200 font-semibold text-base">
|
||||||
|
<div className="font-normal">
|
||||||
|
Do you want to install these packages?
|
||||||
|
</div>{" "}
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{packages.map((p: string) => (
|
||||||
|
<span
|
||||||
|
className="cursor-pointer text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||||
|
key={p}
|
||||||
|
onClick={() => {
|
||||||
|
IpcClient.getInstance().openExternalUrl(
|
||||||
|
`https://www.npmjs.com/package/${p}`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{p}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{hasChildren && (
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{packages.length > 0 && (
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-400 mb-1">
|
||||||
|
Make sure these packages are what you want.{" "}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Show content if it's visible and has children */}
|
||||||
|
{isContentVisible && hasChildren && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<div className="text-xs">
|
||||||
|
<CodeHighlight className="language-shell">{children}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
89
src/components/chat/MoreMinimoreAddIntegration.tsx
Normal file
89
src/components/chat/MoreMinimoreAddIntegration.tsx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { showError } from "@/lib/toast";
|
||||||
|
import { useLoadApp } from "@/hooks/useLoadApp";
|
||||||
|
|
||||||
|
interface MoreMinimoreAddIntegrationProps {
|
||||||
|
node: {
|
||||||
|
properties: {
|
||||||
|
provider: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreAddIntegration: React.FC<MoreMinimoreAddIntegrationProps> = ({
|
||||||
|
node,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const { provider } = node.properties;
|
||||||
|
const appId = useAtomValue(selectedAppIdAtom);
|
||||||
|
const { app } = useLoadApp(appId);
|
||||||
|
|
||||||
|
const handleSetupClick = () => {
|
||||||
|
if (!appId) {
|
||||||
|
showError("No app ID found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
navigate({ to: "/app-details", search: { appId } });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (app?.supabaseProjectName) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col my-2 p-3 border border-green-300 rounded-lg bg-green-50 shadow-sm">
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5 text-green-600"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={2}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
fill="#bbf7d0"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M9 12l2 2 4-4"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span className="font-semibold text-green-800">
|
||||||
|
Supabase integration complete
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-green-900">
|
||||||
|
<p>
|
||||||
|
This app is connected to Supabase project:{" "}
|
||||||
|
<span className="font-mono font-medium bg-green-100 px-1 py-0.5 rounded">
|
||||||
|
{app.supabaseProjectName}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>Click the chat suggestion "Keep going" to continue.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2 my-2 p-3 border rounded-md bg-secondary/10">
|
||||||
|
<div className="text-sm">
|
||||||
|
<div className="font-medium">Integrate with {provider}?</div>
|
||||||
|
<div className="text-muted-foreground text-xs">{children}</div>
|
||||||
|
</div>
|
||||||
|
<Button onClick={handleSetupClick} className="self-start w-full">
|
||||||
|
Set up {provider}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
31
src/components/chat/MoreMinimoreCodeSearch.tsx
Normal file
31
src/components/chat/MoreMinimoreCodeSearch.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { FileCode } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreCodeSearchProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
query?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreCodeSearch: React.FC<MoreMinimoreCodeSearchProps> = ({
|
||||||
|
children,
|
||||||
|
node: _node,
|
||||||
|
query: queryProp,
|
||||||
|
}) => {
|
||||||
|
const query = queryProp || (typeof children === "string" ? children : "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FileCode size={16} className="text-purple-600" />
|
||||||
|
<div className="text-xs text-purple-600 font-medium">Code Search</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm italic text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{query || children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
123
src/components/chat/MoreMinimoreCodeSearchResult.tsx
Normal file
123
src/components/chat/MoreMinimoreCodeSearchResult.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import React, { useState, useMemo } from "react";
|
||||||
|
import { ChevronDown, ChevronUp, FileCode, FileText } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreCodeSearchResultProps {
|
||||||
|
node?: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreCodeSearchResult: React.FC<MoreMinimoreCodeSearchResultProps> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
|
// Parse file paths from children content
|
||||||
|
const files = useMemo(() => {
|
||||||
|
if (typeof children !== "string") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePaths: string[] = [];
|
||||||
|
const lines = children.split("\n");
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
// Skip empty lines and lines that look like tags
|
||||||
|
if (
|
||||||
|
trimmedLine &&
|
||||||
|
!trimmedLine.startsWith("<") &&
|
||||||
|
!trimmedLine.startsWith(">")
|
||||||
|
) {
|
||||||
|
filePaths.push(trimmedLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths;
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border border-border my-2 cursor-pointer"
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
role="button"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-purple-600 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<FileCode size={16} className="text-purple-600" />
|
||||||
|
<span>Code Search Result</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* File count when collapsed */}
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="absolute top-2 left-44 flex items-center">
|
||||||
|
<span className="px-1.5 py-0.5 bg-gray-100 dark:bg-zinc-800 text-xs rounded text-gray-600 dark:text-gray-300">
|
||||||
|
Found {files.length} file{files.length !== 1 ? "s" : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Indicator icon */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content with smooth transition */}
|
||||||
|
<div
|
||||||
|
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
|
||||||
|
style={{
|
||||||
|
maxHeight: isExpanded ? "1000px" : "0px",
|
||||||
|
opacity: isExpanded ? 1 : 0,
|
||||||
|
marginBottom: isExpanded ? "0" : "-6px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* File list when expanded */}
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{files.map((file, index) => {
|
||||||
|
const filePath = file.trim();
|
||||||
|
const fileName = filePath.split("/").pop() || filePath;
|
||||||
|
const pathPart =
|
||||||
|
filePath.substring(0, filePath.length - fileName.length) ||
|
||||||
|
"";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="px-2 py-1 bg-gray-100 dark:bg-zinc-800 rounded-lg"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<FileText
|
||||||
|
size={14}
|
||||||
|
className="text-gray-500 dark:text-gray-400 flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{fileName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{pathPart && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 ml-5">
|
||||||
|
{pathPart}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
117
src/components/chat/MoreMinimoreCodebaseContext.tsx
Normal file
117
src/components/chat/MoreMinimoreCodebaseContext.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { ChevronUp, ChevronDown, Code2, FileText } from "lucide-react";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
|
interface MoreMinimoreCodebaseContextProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
node?: {
|
||||||
|
properties?: {
|
||||||
|
files?: string;
|
||||||
|
state?: CustomTagState;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreCodebaseContext: React.FC<MoreMinimoreCodebaseContextProps> = ({
|
||||||
|
node,
|
||||||
|
}) => {
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const [isExpanded, setIsExpanded] = useState(inProgress);
|
||||||
|
const files = node?.properties?.files?.split(",") || [];
|
||||||
|
|
||||||
|
// Collapse when transitioning from in-progress to not-in-progress
|
||||||
|
useEffect(() => {
|
||||||
|
if (!inProgress && isExpanded) {
|
||||||
|
setIsExpanded(false);
|
||||||
|
}
|
||||||
|
}, [inProgress]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress ? "border-blue-500" : "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
role="button"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-blue-500 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<Code2 size={16} className="text-blue-500" />
|
||||||
|
<span>Codebase Context</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* File count when collapsed */}
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="absolute top-2 left-40 flex items-center">
|
||||||
|
<span className="px-1.5 py-0.5 bg-gray-100 dark:bg-zinc-800 text-xs rounded text-gray-600 dark:text-gray-300">
|
||||||
|
Using {files.length} file{files.length !== 1 ? "s" : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Indicator icon */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content with smooth transition */}
|
||||||
|
<div
|
||||||
|
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
|
||||||
|
style={{
|
||||||
|
maxHeight: isExpanded ? "1000px" : "0px",
|
||||||
|
opacity: isExpanded ? 1 : 0,
|
||||||
|
marginBottom: isExpanded ? "0" : "-6px", // Compensate for padding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* File list when expanded */}
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="mb-3">
|
||||||
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
|
{files.map((file, index) => {
|
||||||
|
const filePath = file.trim();
|
||||||
|
const fileName = filePath.split("/").pop() || filePath;
|
||||||
|
const pathPart =
|
||||||
|
filePath.substring(0, filePath.length - fileName.length) ||
|
||||||
|
"";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="px-2 py-1 bg-gray-100 dark:bg-zinc-800 rounded-lg"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<FileText
|
||||||
|
size={14}
|
||||||
|
className="text-gray-500 dark:text-gray-400 flex-shrink-0"
|
||||||
|
/>
|
||||||
|
<div className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
{fileName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{pathPart && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 ml-5">
|
||||||
|
{pathPart}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
45
src/components/chat/MoreMinimoreDelete.tsx
Normal file
45
src/components/chat/MoreMinimoreDelete.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { Trash2 } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreDeleteProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
path?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreDelete: React.FC<MoreMinimoreDeleteProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
path: pathProp,
|
||||||
|
}) => {
|
||||||
|
// Use props directly if provided, otherwise extract from node
|
||||||
|
const path = pathProp || node?.properties?.path || "";
|
||||||
|
|
||||||
|
// Extract filename from path
|
||||||
|
const fileName = path ? path.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-red-500 my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Trash2 size={16} className="text-red-500" />
|
||||||
|
{fileName && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<div className="text-xs text-red-500 font-medium">Delete</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{path && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
113
src/components/chat/MoreMinimoreEdit.tsx
Normal file
113
src/components/chat/MoreMinimoreEdit.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
Loader,
|
||||||
|
CircleX,
|
||||||
|
Rabbit,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
|
interface MoreMinimoreEditProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
path?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreEdit: React.FC<MoreMinimoreEditProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
path: pathProp,
|
||||||
|
description: descriptionProp,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
|
||||||
|
// Use props directly if provided, otherwise extract from node
|
||||||
|
const path = pathProp || node?.properties?.path || "";
|
||||||
|
const description = descriptionProp || node?.properties?.description || "";
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const aborted = state === "aborted";
|
||||||
|
|
||||||
|
// Extract filename from path
|
||||||
|
const fileName = path ? path.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress
|
||||||
|
? "border-amber-500"
|
||||||
|
: aborted
|
||||||
|
? "border-red-500"
|
||||||
|
: "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Rabbit size={16} />
|
||||||
|
<span className="bg-blue-500 text-white text-xs px-1.5 py-0.5 rounded ml-1 font-medium">
|
||||||
|
Turbo Edit
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{fileName && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{inProgress && (
|
||||||
|
<div className="flex items-center text-amber-600 text-xs">
|
||||||
|
<Loader size={14} className="mr-1 animate-spin" />
|
||||||
|
<span>Editing...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{aborted && (
|
||||||
|
<div className="flex items-center text-red-600 text-xs">
|
||||||
|
<CircleX size={14} className="mr-1" />
|
||||||
|
<span>Did not finish</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{path && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{description && (
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span className="font-medium">Summary: </span>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isContentVisible && (
|
||||||
|
<div
|
||||||
|
className="text-xs cursor-text"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<CodeHighlight className="language-typescript">
|
||||||
|
{children}
|
||||||
|
</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
85
src/components/chat/MoreMinimoreExecuteSql.tsx
Normal file
85
src/components/chat/MoreMinimoreExecuteSql.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
Database,
|
||||||
|
Loader,
|
||||||
|
CircleX,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
|
interface MoreMinimoreExecuteSqlProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreExecuteSql: React.FC<MoreMinimoreExecuteSqlProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
description,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const aborted = state === "aborted";
|
||||||
|
const queryDescription = description || node?.properties?.description;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress
|
||||||
|
? "border-amber-500"
|
||||||
|
: aborted
|
||||||
|
? "border-red-500"
|
||||||
|
: "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Database size={16} />
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
<span className="font-bold mr-2 outline-2 outline-gray-200 dark:outline-gray-700 bg-gray-100 dark:bg-gray-800 rounded-md px-1">
|
||||||
|
SQL
|
||||||
|
</span>
|
||||||
|
{queryDescription}
|
||||||
|
</span>
|
||||||
|
{inProgress && (
|
||||||
|
<div className="flex items-center text-amber-600 text-xs">
|
||||||
|
<Loader size={14} className="mr-1 animate-spin" />
|
||||||
|
<span>Executing...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{aborted && (
|
||||||
|
<div className="flex items-center text-red-600 text-xs">
|
||||||
|
<CircleX size={14} className="mr-1" />
|
||||||
|
<span>Did not finish</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{isContentVisible && (
|
||||||
|
<div className="text-xs">
|
||||||
|
<CodeHighlight className="language-sql">{children}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
610
src/components/chat/MoreMinimoreMarkdownParser.tsx
Normal file
610
src/components/chat/MoreMinimoreMarkdownParser.tsx
Normal file
@@ -0,0 +1,610 @@
|
|||||||
|
import React, { useMemo } from "react";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
|
||||||
|
import { MoreMinimoreWrite } from "./MoreMinimoreWrite";
|
||||||
|
import { MoreMinimoreRename } from "./MoreMinimoreRename";
|
||||||
|
import { MoreMinimoreDelete } from "./MoreMinimoreDelete";
|
||||||
|
import { MoreMinimoreAddDependency } from "./MoreMinimoreAddDependency";
|
||||||
|
import { MoreMinimoreExecuteSql } from "./MoreMinimoreExecuteSql";
|
||||||
|
import { MoreMinimoreAddIntegration } from "./MoreMinimoreAddIntegration";
|
||||||
|
import { MoreMinimoreEdit } from "./MoreMinimoreEdit";
|
||||||
|
import { MoreMinimoreSearchReplace } from "./MoreMinimoreSearchReplace";
|
||||||
|
import { MoreMinimoreCodebaseContext } from "./MoreMinimoreCodebaseContext";
|
||||||
|
import { MoreMinimoreThink } from "./MoreMinimoreThink";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { isStreamingByIdAtom, selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
import { MoreMinimoreOutput } from "./MoreMinimoreOutput";
|
||||||
|
import { MoreMinimoreProblemSummary } from "./MoreMinimoreProblemSummary";
|
||||||
|
import { IpcClient } from "@/ipc/ipc_client";
|
||||||
|
import { MoreMinimoreMcpToolCall } from "./MoreMinimoreMcpToolCall";
|
||||||
|
import { MoreMinimoreMcpToolResult } from "./MoreMinimoreMcpToolResult";
|
||||||
|
import { MoreMinimoreWebSearchResult } from "./MoreMinimoreWebSearchResult";
|
||||||
|
import { MoreMinimoreWebSearch } from "./MoreMinimoreWebSearch";
|
||||||
|
import { MoreMinimoreWebCrawl } from "./MoreMinimoreWebCrawl";
|
||||||
|
import { MoreMinimoreCodeSearchResult } from "./MoreMinimoreCodeSearchResult";
|
||||||
|
import { MoreMinimoreCodeSearch } from "./MoreMinimoreCodeSearch";
|
||||||
|
import { MoreMinimoreRead } from "./MoreMinimoreRead";
|
||||||
|
import { mapActionToButton } from "./ChatInput";
|
||||||
|
import { SuggestedAction } from "@/lib/schemas";
|
||||||
|
import { FixAllErrorsButton } from "./FixAllErrorsButton";
|
||||||
|
|
||||||
|
interface MoreMinimoreMarkdownParserProps {
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomTagInfo = {
|
||||||
|
tag: string;
|
||||||
|
attributes: Record<string, string>;
|
||||||
|
content: string;
|
||||||
|
fullMatch: string;
|
||||||
|
inProgress?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContentPiece =
|
||||||
|
| { type: "markdown"; content: string }
|
||||||
|
| { type: "custom-tag"; tagInfo: CustomTagInfo };
|
||||||
|
|
||||||
|
const customLink = ({
|
||||||
|
node: _node,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
node?: any;
|
||||||
|
[key: string]: any;
|
||||||
|
}) => (
|
||||||
|
<a
|
||||||
|
{...props}
|
||||||
|
onClick={(e) => {
|
||||||
|
const url = props.href;
|
||||||
|
if (url) {
|
||||||
|
e.preventDefault();
|
||||||
|
IpcClient.getInstance().openExternalUrl(url);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const VanillaMarkdownParser = ({ content }: { content: string }) => {
|
||||||
|
return (
|
||||||
|
<ReactMarkdown
|
||||||
|
components={{
|
||||||
|
code: CodeHighlight,
|
||||||
|
a: customLink,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom component to parse markdown content with MoreMinimore-specific tags
|
||||||
|
*/
|
||||||
|
export const MoreMinimoreMarkdownParser: React.FC<MoreMinimoreMarkdownParserProps> = ({
|
||||||
|
content,
|
||||||
|
}) => {
|
||||||
|
const chatId = useAtomValue(selectedChatIdAtom);
|
||||||
|
const isStreaming = useAtomValue(isStreamingByIdAtom).get(chatId!) ?? false;
|
||||||
|
// Extract content pieces (markdown and custom tags)
|
||||||
|
const contentPieces = useMemo(() => {
|
||||||
|
return parseCustomTags(content);
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
// Extract error messages and track positions
|
||||||
|
const { errorMessages, lastErrorIndex, errorCount } = useMemo(() => {
|
||||||
|
const errors: string[] = [];
|
||||||
|
let lastIndex = -1;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
contentPieces.forEach((piece, index) => {
|
||||||
|
if (
|
||||||
|
piece.type === "custom-tag" &&
|
||||||
|
piece.tagInfo.tag === "dyad-output" &&
|
||||||
|
piece.tagInfo.attributes.type === "error"
|
||||||
|
) {
|
||||||
|
const errorMessage = piece.tagInfo.attributes.message;
|
||||||
|
if (errorMessage?.trim()) {
|
||||||
|
errors.push(errorMessage.trim());
|
||||||
|
count++;
|
||||||
|
lastIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
errorMessages: errors,
|
||||||
|
lastErrorIndex: lastIndex,
|
||||||
|
errorCount: count,
|
||||||
|
};
|
||||||
|
}, [contentPieces]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{contentPieces.map((piece, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
{piece.type === "markdown"
|
||||||
|
? piece.content && (
|
||||||
|
<ReactMarkdown
|
||||||
|
components={{
|
||||||
|
code: CodeHighlight,
|
||||||
|
a: customLink,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{piece.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
)
|
||||||
|
: renderCustomTag(piece.tagInfo, { isStreaming })}
|
||||||
|
{index === lastErrorIndex &&
|
||||||
|
errorCount > 1 &&
|
||||||
|
!isStreaming &&
|
||||||
|
chatId && (
|
||||||
|
<div className="mt-3 w-full flex">
|
||||||
|
<FixAllErrorsButton
|
||||||
|
errorMessages={errorMessages}
|
||||||
|
chatId={chatId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-process content to handle unclosed custom tags
|
||||||
|
* Adds closing tags at the end of the content for any unclosed custom tags
|
||||||
|
* Assumes the opening tags are complete and valid
|
||||||
|
* Returns the processed content and a map of in-progress tags
|
||||||
|
*/
|
||||||
|
function preprocessUnclosedTags(content: string): {
|
||||||
|
processedContent: string;
|
||||||
|
inProgressTags: Map<string, Set<number>>;
|
||||||
|
} {
|
||||||
|
const customTagNames = [
|
||||||
|
"dyad-write",
|
||||||
|
"dyad-rename",
|
||||||
|
"dyad-delete",
|
||||||
|
"dyad-add-dependency",
|
||||||
|
"dyad-execute-sql",
|
||||||
|
"dyad-add-integration",
|
||||||
|
"dyad-output",
|
||||||
|
"dyad-problem-report",
|
||||||
|
"dyad-chat-summary",
|
||||||
|
"dyad-edit",
|
||||||
|
"dyad-search-replace",
|
||||||
|
"dyad-codebase-context",
|
||||||
|
"dyad-web-search-result",
|
||||||
|
"dyad-web-search",
|
||||||
|
"dyad-web-crawl",
|
||||||
|
"dyad-read",
|
||||||
|
"think",
|
||||||
|
"dyad-command",
|
||||||
|
"dyad-mcp-tool-call",
|
||||||
|
"dyad-mcp-tool-result",
|
||||||
|
];
|
||||||
|
|
||||||
|
let processedContent = content;
|
||||||
|
// Map to track which tags are in progress and their positions
|
||||||
|
const inProgressTags = new Map<string, Set<number>>();
|
||||||
|
|
||||||
|
// For each tag type, check if there are unclosed tags
|
||||||
|
for (const tagName of customTagNames) {
|
||||||
|
// Count opening and closing tags
|
||||||
|
const openTagPattern = new RegExp(`<${tagName}(?:\\s[^>]*)?>`, "g");
|
||||||
|
const closeTagPattern = new RegExp(`</${tagName}>`, "g");
|
||||||
|
|
||||||
|
// Track the positions of opening tags
|
||||||
|
const openingMatches: RegExpExecArray[] = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
// Reset regex lastIndex to start from the beginning
|
||||||
|
openTagPattern.lastIndex = 0;
|
||||||
|
|
||||||
|
while ((match = openTagPattern.exec(processedContent)) !== null) {
|
||||||
|
openingMatches.push({ ...match });
|
||||||
|
}
|
||||||
|
|
||||||
|
const openCount = openingMatches.length;
|
||||||
|
const closeCount = (processedContent.match(closeTagPattern) || []).length;
|
||||||
|
|
||||||
|
// If we have more opening than closing tags
|
||||||
|
const missingCloseTags = openCount - closeCount;
|
||||||
|
if (missingCloseTags > 0) {
|
||||||
|
// Add the required number of closing tags at the end
|
||||||
|
processedContent += Array(missingCloseTags)
|
||||||
|
.fill(`</${tagName}>`)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
// Mark the last N tags as in progress where N is the number of missing closing tags
|
||||||
|
const inProgressIndexes = new Set<number>();
|
||||||
|
const startIndex = openCount - missingCloseTags;
|
||||||
|
for (let i = startIndex; i < openCount; i++) {
|
||||||
|
inProgressIndexes.add(openingMatches[i].index);
|
||||||
|
}
|
||||||
|
inProgressTags.set(tagName, inProgressIndexes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { processedContent, inProgressTags };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the content to extract custom tags and markdown sections into a unified array
|
||||||
|
*/
|
||||||
|
function parseCustomTags(content: string): ContentPiece[] {
|
||||||
|
const { processedContent, inProgressTags } = preprocessUnclosedTags(content);
|
||||||
|
|
||||||
|
const customTagNames = [
|
||||||
|
"dyad-write",
|
||||||
|
"dyad-rename",
|
||||||
|
"dyad-delete",
|
||||||
|
"dyad-add-dependency",
|
||||||
|
"dyad-execute-sql",
|
||||||
|
"dyad-add-integration",
|
||||||
|
"dyad-output",
|
||||||
|
"dyad-problem-report",
|
||||||
|
"dyad-chat-summary",
|
||||||
|
"dyad-edit",
|
||||||
|
"dyad-search-replace",
|
||||||
|
"dyad-codebase-context",
|
||||||
|
"dyad-web-search-result",
|
||||||
|
"dyad-web-search",
|
||||||
|
"dyad-web-crawl",
|
||||||
|
"dyad-code-search-result",
|
||||||
|
"dyad-code-search",
|
||||||
|
"dyad-read",
|
||||||
|
"think",
|
||||||
|
"dyad-command",
|
||||||
|
"dyad-mcp-tool-call",
|
||||||
|
"dyad-mcp-tool-result",
|
||||||
|
];
|
||||||
|
|
||||||
|
const tagPattern = new RegExp(
|
||||||
|
`<(${customTagNames.join("|")})\\s*([^>]*)>(.*?)<\\/\\1>`,
|
||||||
|
"gs",
|
||||||
|
);
|
||||||
|
|
||||||
|
const contentPieces: ContentPiece[] = [];
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
// Find all custom tags
|
||||||
|
while ((match = tagPattern.exec(processedContent)) !== null) {
|
||||||
|
const [fullMatch, tag, attributesStr, tagContent] = match;
|
||||||
|
const startIndex = match.index;
|
||||||
|
|
||||||
|
// Add the markdown content before this tag
|
||||||
|
if (startIndex > lastIndex) {
|
||||||
|
contentPieces.push({
|
||||||
|
type: "markdown",
|
||||||
|
content: processedContent.substring(lastIndex, startIndex),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse attributes
|
||||||
|
const attributes: Record<string, string> = {};
|
||||||
|
const attrPattern = /(\w+)="([^"]*)"/g;
|
||||||
|
let attrMatch;
|
||||||
|
while ((attrMatch = attrPattern.exec(attributesStr)) !== null) {
|
||||||
|
attributes[attrMatch[1]] = attrMatch[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this tag was marked as in progress
|
||||||
|
const tagInProgressSet = inProgressTags.get(tag);
|
||||||
|
const isInProgress = tagInProgressSet?.has(startIndex);
|
||||||
|
|
||||||
|
// Add the tag info
|
||||||
|
contentPieces.push({
|
||||||
|
type: "custom-tag",
|
||||||
|
tagInfo: {
|
||||||
|
tag,
|
||||||
|
attributes,
|
||||||
|
content: tagContent,
|
||||||
|
fullMatch,
|
||||||
|
inProgress: isInProgress || false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
lastIndex = startIndex + fullMatch.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the remaining markdown content
|
||||||
|
if (lastIndex < processedContent.length) {
|
||||||
|
contentPieces.push({
|
||||||
|
type: "markdown",
|
||||||
|
content: processedContent.substring(lastIndex),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentPieces;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getState({
|
||||||
|
isStreaming,
|
||||||
|
inProgress,
|
||||||
|
}: {
|
||||||
|
isStreaming?: boolean;
|
||||||
|
inProgress?: boolean;
|
||||||
|
}): CustomTagState {
|
||||||
|
if (!inProgress) {
|
||||||
|
return "finished";
|
||||||
|
}
|
||||||
|
return isStreaming ? "pending" : "aborted";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a custom tag based on its type
|
||||||
|
*/
|
||||||
|
function renderCustomTag(
|
||||||
|
tagInfo: CustomTagInfo,
|
||||||
|
{ isStreaming }: { isStreaming: boolean },
|
||||||
|
): React.ReactNode {
|
||||||
|
const { tag, attributes, content, inProgress } = tagInfo;
|
||||||
|
|
||||||
|
switch (tag) {
|
||||||
|
case "dyad-read":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreRead
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
path: attributes.path || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreRead>
|
||||||
|
);
|
||||||
|
case "dyad-web-search":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreWebSearch
|
||||||
|
node={{
|
||||||
|
properties: {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreWebSearch>
|
||||||
|
);
|
||||||
|
case "dyad-web-crawl":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreWebCrawl
|
||||||
|
node={{
|
||||||
|
properties: {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreWebCrawl>
|
||||||
|
);
|
||||||
|
case "dyad-code-search":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreCodeSearch
|
||||||
|
node={{
|
||||||
|
properties: {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreCodeSearch>
|
||||||
|
);
|
||||||
|
case "dyad-code-search-result":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreCodeSearchResult
|
||||||
|
node={{
|
||||||
|
properties: {},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreCodeSearchResult>
|
||||||
|
);
|
||||||
|
case "dyad-web-search-result":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreWebSearchResult
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreWebSearchResult>
|
||||||
|
);
|
||||||
|
case "think":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreThink
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreThink>
|
||||||
|
);
|
||||||
|
case "dyad-write":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreWrite
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
path: attributes.path || "",
|
||||||
|
description: attributes.description || "",
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreWrite>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-rename":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreRename
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
from: attributes.from || "",
|
||||||
|
to: attributes.to || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreRename>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-delete":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreDelete
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
path: attributes.path || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreDelete>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-add-dependency":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreAddDependency
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
packages: attributes.packages || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreAddDependency>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-execute-sql":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreExecuteSql
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
description: attributes.description || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreExecuteSql>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-add-integration":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreAddIntegration
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
provider: attributes.provider || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreAddIntegration>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-edit":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreEdit
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
path: attributes.path || "",
|
||||||
|
description: attributes.description || "",
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreEdit>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-search-replace":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreSearchReplace
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
path: attributes.path || "",
|
||||||
|
description: attributes.description || "",
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreSearchReplace>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-codebase-context":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreCodebaseContext
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
files: attributes.files || "",
|
||||||
|
state: getState({ isStreaming, inProgress }),
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreCodebaseContext>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-mcp-tool-call":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreMcpToolCall
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
serverName: attributes.server || "",
|
||||||
|
toolName: attributes.tool || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreMcpToolCall>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-mcp-tool-result":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreMcpToolResult
|
||||||
|
node={{
|
||||||
|
properties: {
|
||||||
|
serverName: attributes.server || "",
|
||||||
|
toolName: attributes.tool || "",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreMcpToolResult>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-output":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreOutput
|
||||||
|
type={attributes.type as "warning" | "error"}
|
||||||
|
message={attributes.message}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreOutput>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-problem-report":
|
||||||
|
return (
|
||||||
|
<MoreMinimoreProblemSummary summary={attributes.summary}>
|
||||||
|
{content}
|
||||||
|
</MoreMinimoreProblemSummary>
|
||||||
|
);
|
||||||
|
|
||||||
|
case "dyad-chat-summary":
|
||||||
|
// Don't render anything for dyad-chat-summary
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case "dyad-command":
|
||||||
|
if (attributes.type) {
|
||||||
|
const action = {
|
||||||
|
id: attributes.type,
|
||||||
|
} as SuggestedAction;
|
||||||
|
return <>{mapActionToButton(action)}</>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/components/chat/MoreMinimoreMcpToolCall.tsx
Normal file
73
src/components/chat/MoreMinimoreMcpToolCall.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import { Wrench, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
|
interface MoreMinimoreMcpToolCallProps {
|
||||||
|
node?: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreMcpToolCall: React.FC<MoreMinimoreMcpToolCallProps> = ({
|
||||||
|
node,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const serverName: string = node?.properties?.serverName || "";
|
||||||
|
const toolName: string = node?.properties?.toolName || "";
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
|
const raw = typeof children === "string" ? children : String(children ?? "");
|
||||||
|
|
||||||
|
const prettyJson = useMemo(() => {
|
||||||
|
if (!expanded) return "";
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return JSON.stringify(parsed, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error parsing JSON for dyad-mcp-tool-call", e);
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}, [expanded, raw]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer"
|
||||||
|
onClick={() => setExpanded((v) => !v)}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-3 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-blue-600 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<Wrench size={16} className="text-blue-600" />
|
||||||
|
<span>Tool Call</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right chevron */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{expanded ? <ChevronsDownUp size={18} /> : <ChevronsUpDown size={18} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Header content */}
|
||||||
|
<div className="flex items-start gap-2 pl-24 pr-8 py-1">
|
||||||
|
{serverName ? (
|
||||||
|
<span className="text-xs px-2 py-0.5 rounded-full bg-blue-50 dark:bg-zinc-800 text-blue-700 dark:text-blue-300 border border-blue-200 dark:border-zinc-700">
|
||||||
|
{serverName}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{toolName ? (
|
||||||
|
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 dark:bg-zinc-800 text-gray-700 dark:text-gray-200 border border-border">
|
||||||
|
{toolName}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{/* Intentionally no preview or content when collapsed */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* JSON content */}
|
||||||
|
{expanded ? (
|
||||||
|
<div className="mt-2 pr-4 pb-2">
|
||||||
|
<CodeHighlight className="language-json">{prettyJson}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
73
src/components/chat/MoreMinimoreMcpToolResult.tsx
Normal file
73
src/components/chat/MoreMinimoreMcpToolResult.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import React, { useMemo, useState } from "react";
|
||||||
|
import { CheckCircle, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
|
||||||
|
interface MoreMinimoreMcpToolResultProps {
|
||||||
|
node?: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreMcpToolResult: React.FC<MoreMinimoreMcpToolResultProps> = ({
|
||||||
|
node,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const serverName: string = node?.properties?.serverName || "";
|
||||||
|
const toolName: string = node?.properties?.toolName || "";
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
|
const raw = typeof children === "string" ? children : String(children ?? "");
|
||||||
|
|
||||||
|
const prettyJson = useMemo(() => {
|
||||||
|
if (!expanded) return "";
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return JSON.stringify(parsed, null, 2);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error parsing JSON for dyad-mcp-tool-result", e);
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}, [expanded, raw]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer"
|
||||||
|
onClick={() => setExpanded((v) => !v)}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-3 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-emerald-600 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<CheckCircle size={16} className="text-emerald-600" />
|
||||||
|
<span>Tool Result</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right chevron */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{expanded ? <ChevronsDownUp size={18} /> : <ChevronsUpDown size={18} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Header content */}
|
||||||
|
<div className="flex items-start gap-2 pl-24 pr-8 py-1">
|
||||||
|
{serverName ? (
|
||||||
|
<span className="text-xs px-2 py-0.5 rounded-full bg-emerald-50 dark:bg-zinc-800 text-emerald-700 dark:text-emerald-300 border border-emerald-200 dark:border-zinc-700">
|
||||||
|
{serverName}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{toolName ? (
|
||||||
|
<span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 dark:bg-zinc-800 text-gray-700 dark:text-gray-200 border border-border">
|
||||||
|
{toolName}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
{/* Intentionally no preview or content when collapsed */}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* JSON content */}
|
||||||
|
{expanded ? (
|
||||||
|
<div className="mt-2 pr-4 pb-2">
|
||||||
|
<CodeHighlight className="language-json">{prettyJson}</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
112
src/components/chat/MoreMinimoreOutput.tsx
Normal file
112
src/components/chat/MoreMinimoreOutput.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
AlertTriangle,
|
||||||
|
XCircle,
|
||||||
|
Sparkles,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||||
|
import { useStreamChat } from "@/hooks/useStreamChat";
|
||||||
|
import { CopyErrorMessage } from "@/components/CopyErrorMessage";
|
||||||
|
interface MoreMinimoreOutputProps {
|
||||||
|
type: "error" | "warning";
|
||||||
|
message?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreOutput: React.FC<MoreMinimoreOutputProps> = ({
|
||||||
|
type,
|
||||||
|
message,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
const selectedChatId = useAtomValue(selectedChatIdAtom);
|
||||||
|
const { streamMessage } = useStreamChat();
|
||||||
|
|
||||||
|
// If the type is not warning, it is an error (in case LLM gives a weird "type")
|
||||||
|
const isError = type !== "warning";
|
||||||
|
const borderColor = isError ? "border-red-500" : "border-amber-500";
|
||||||
|
const iconColor = isError ? "text-red-500" : "text-amber-500";
|
||||||
|
const icon = isError ? (
|
||||||
|
<XCircle size={16} className={iconColor} />
|
||||||
|
) : (
|
||||||
|
<AlertTriangle size={16} className={iconColor} />
|
||||||
|
);
|
||||||
|
const label = isError ? "Error" : "Warning";
|
||||||
|
|
||||||
|
const handleAIFix = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (message && selectedChatId) {
|
||||||
|
streamMessage({
|
||||||
|
prompt: `Fix the error: ${message}`,
|
||||||
|
chatId: selectedChatId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer min-h-18 ${borderColor}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className={`absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold ${iconColor} bg-white dark:bg-gray-900`}
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<span>{label}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content, padded to avoid label */}
|
||||||
|
<div className="flex items-center justify-between pl-24 pr-6">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{message && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{message.slice(0, isContentVisible ? undefined : 100) +
|
||||||
|
(!isContentVisible ? "..." : "")}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content area */}
|
||||||
|
{isContentVisible && children && (
|
||||||
|
<div className="mt-4 pl-20 text-sm text-gray-800 dark:text-gray-200">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Action buttons at the bottom - always visible for errors */}
|
||||||
|
{isError && message && (
|
||||||
|
<div className="mt-3 px-6 flex justify-end gap-2">
|
||||||
|
<CopyErrorMessage
|
||||||
|
errorMessage={children ? `${message}\n${children}` : message}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={handleAIFix}
|
||||||
|
className="cursor-pointer flex items-center justify-center bg-red-600 hover:bg-red-700 dark:bg-red-700 dark:hover:bg-red-800 text-white rounded text-xs px-2 py-1 h-6"
|
||||||
|
>
|
||||||
|
<Sparkles size={14} className="mr-1" />
|
||||||
|
<span>Fix with AI</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
156
src/components/chat/MoreMinimoreProblemSummary.tsx
Normal file
156
src/components/chat/MoreMinimoreProblemSummary.tsx
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
AlertTriangle,
|
||||||
|
FileText,
|
||||||
|
} from "lucide-react";
|
||||||
|
import type { Problem } from "@/ipc/ipc_types";
|
||||||
|
|
||||||
|
type ProblemWithoutSnippet = Omit<Problem, "snippet">;
|
||||||
|
|
||||||
|
interface MoreMinimoreProblemSummaryProps {
|
||||||
|
summary?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProblemItemProps {
|
||||||
|
problem: ProblemWithoutSnippet;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProblemItem: React.FC<ProblemItemProps> = ({ problem, index }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex items-start gap-3 py-2 px-3 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
|
||||||
|
<div className="flex-shrink-0 w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 flex items-center justify-center mt-0.5">
|
||||||
|
<span className="text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||||
|
{index + 1}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<FileText size={14} className="text-gray-500 flex-shrink-0" />
|
||||||
|
<span className="text-sm font-medium text-gray-900 dark:text-gray-100 truncate">
|
||||||
|
{problem.file}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{problem.line}:{problem.column}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded text-gray-600 dark:text-gray-300">
|
||||||
|
TS{problem.code}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-700 dark:text-gray-300 leading-relaxed">
|
||||||
|
{problem.message}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MoreMinimoreProblemSummary: React.FC<MoreMinimoreProblemSummaryProps> = ({
|
||||||
|
summary,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
|
||||||
|
// Parse problems from children content if available
|
||||||
|
const problems: ProblemWithoutSnippet[] = React.useMemo(() => {
|
||||||
|
if (!children || typeof children !== "string") return [];
|
||||||
|
|
||||||
|
// Parse structured format with <problem> tags
|
||||||
|
const problemTagRegex =
|
||||||
|
/<problem\s+file="([^"]+)"\s+line="(\d+)"\s+column="(\d+)"\s+code="(\d+)">([^<]+)<\/problem>/g;
|
||||||
|
const problems: ProblemWithoutSnippet[] = [];
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = problemTagRegex.exec(children)) !== null) {
|
||||||
|
try {
|
||||||
|
problems.push({
|
||||||
|
file: match[1],
|
||||||
|
line: parseInt(match[2], 10),
|
||||||
|
column: parseInt(match[3], 10),
|
||||||
|
message: match[5].trim(),
|
||||||
|
code: parseInt(match[4], 10),
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
file: "unknown",
|
||||||
|
line: 0,
|
||||||
|
column: 0,
|
||||||
|
message: children,
|
||||||
|
code: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return problems;
|
||||||
|
}, [children]);
|
||||||
|
|
||||||
|
const totalProblems = problems.length;
|
||||||
|
const displaySummary =
|
||||||
|
summary || `${totalProblems} problems found (TypeScript errors)`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border border-border my-2 cursor-pointer"
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
data-testid="problem-summary"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<AlertTriangle
|
||||||
|
size={16}
|
||||||
|
className="text-amber-600 dark:text-amber-500"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
<span className="font-bold mr-2 outline-2 outline-amber-200 dark:outline-amber-700 bg-amber-100 dark:bg-amber-900/30 text-amber-800 dark:text-amber-200 rounded-md px-1">
|
||||||
|
Auto-fix
|
||||||
|
</span>
|
||||||
|
{displaySummary}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content area - show individual problems */}
|
||||||
|
{isContentVisible && totalProblems > 0 && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||||
|
{problems.map((problem, index) => (
|
||||||
|
<ProblemItem
|
||||||
|
key={`${problem.file}-${problem.line}-${problem.column}-${index}`}
|
||||||
|
problem={problem}
|
||||||
|
index={index}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Fallback content area for raw children */}
|
||||||
|
{isContentVisible && totalProblems === 0 && children && (
|
||||||
|
<div className="mt-4 text-sm text-gray-800 dark:text-gray-200">
|
||||||
|
<pre className="whitespace-pre-wrap font-mono text-xs bg-gray-100 dark:bg-gray-800 p-3 rounded">
|
||||||
|
{children}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
44
src/components/chat/MoreMinimoreRead.tsx
Normal file
44
src/components/chat/MoreMinimoreRead.tsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { FileText } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreReadProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
path?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreRead: React.FC<MoreMinimoreReadProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
path: pathProp,
|
||||||
|
}) => {
|
||||||
|
const path = pathProp || node?.properties?.path || "";
|
||||||
|
const fileName = path ? path.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-border my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FileText size={16} className="text-gray-600" />
|
||||||
|
{fileName && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<div className="text-xs text-gray-600 font-medium">Read</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{path && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{children && (
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
61
src/components/chat/MoreMinimoreRename.tsx
Normal file
61
src/components/chat/MoreMinimoreRename.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { FileEdit } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreRenameProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
from?: string;
|
||||||
|
to?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreRename: React.FC<MoreMinimoreRenameProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
from: fromProp,
|
||||||
|
to: toProp,
|
||||||
|
}) => {
|
||||||
|
// Use props directly if provided, otherwise extract from node
|
||||||
|
const from = fromProp || node?.properties?.from || "";
|
||||||
|
const to = toProp || node?.properties?.to || "";
|
||||||
|
|
||||||
|
// Extract filenames from paths
|
||||||
|
const fromFileName = from ? from.split("/").pop() : "";
|
||||||
|
const toFileName = to ? to.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border border-amber-500 my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<FileEdit size={16} className="text-amber-500" />
|
||||||
|
{(fromFileName || toFileName) && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fromFileName && toFileName
|
||||||
|
? `${fromFileName} → ${toFileName}`
|
||||||
|
: fromFileName || toFileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<div className="text-xs text-amber-500 font-medium">Rename</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(from || to) && (
|
||||||
|
<div className="flex flex-col text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{from && (
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500 dark:text-gray-400">From:</span>{" "}
|
||||||
|
{from}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{to && (
|
||||||
|
<div>
|
||||||
|
<span className="text-gray-500 dark:text-gray-400">To:</span> {to}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
151
src/components/chat/MoreMinimoreSearchReplace.tsx
Normal file
151
src/components/chat/MoreMinimoreSearchReplace.tsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
Loader,
|
||||||
|
CircleX,
|
||||||
|
Search,
|
||||||
|
ArrowLeftRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
|
||||||
|
|
||||||
|
interface MoreMinimoreSearchReplaceProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
path?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreSearchReplace: React.FC<MoreMinimoreSearchReplaceProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
path: pathProp,
|
||||||
|
description: descriptionProp,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
|
||||||
|
const path = pathProp || node?.properties?.path || "";
|
||||||
|
const description = descriptionProp || node?.properties?.description || "";
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const aborted = state === "aborted";
|
||||||
|
|
||||||
|
const blocks = useMemo(
|
||||||
|
() => parseSearchReplaceBlocks(String(children ?? "")),
|
||||||
|
[children],
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileName = path ? path.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress
|
||||||
|
? "border-amber-500"
|
||||||
|
: aborted
|
||||||
|
? "border-red-500"
|
||||||
|
: "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<Search size={16} />
|
||||||
|
<span className="bg-purple-600 text-white text-xs px-1.5 py-0.5 rounded ml-1 font-medium">
|
||||||
|
Search & Replace
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{fileName && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{inProgress && (
|
||||||
|
<div className="flex items-center text-amber-600 text-xs">
|
||||||
|
<Loader size={14} className="mr-1 animate-spin" />
|
||||||
|
<span>Applying changes...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{aborted && (
|
||||||
|
<div className="flex items-center text-red-600 text-xs">
|
||||||
|
<CircleX size={14} className="mr-1" />
|
||||||
|
<span>Did not finish</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{path && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{description && (
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span className="font-medium">Summary: </span>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isContentVisible && (
|
||||||
|
<div
|
||||||
|
className="text-xs cursor-text"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{blocks.length === 0 ? (
|
||||||
|
<CodeHighlight className="language-typescript">
|
||||||
|
{children}
|
||||||
|
</CodeHighlight>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{blocks.map((b, i) => (
|
||||||
|
<div key={i} className="border rounded-lg">
|
||||||
|
<div className="flex items-center justify-between px-3 py-2 bg-(--background-lighter) rounded-t-lg text-[11px]">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ArrowLeftRight size={14} />
|
||||||
|
<span className="font-medium">Change {i + 1}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-0">
|
||||||
|
<div className="p-3 border-t md:border-r">
|
||||||
|
<div className="text-[11px] mb-1 text-muted-foreground font-medium">
|
||||||
|
Search
|
||||||
|
</div>
|
||||||
|
<CodeHighlight className="language-typescript">
|
||||||
|
{b.searchContent}
|
||||||
|
</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 border-t">
|
||||||
|
<div className="text-[11px] mb-1 text-muted-foreground font-medium">
|
||||||
|
Replace
|
||||||
|
</div>
|
||||||
|
<CodeHighlight className="language-typescript">
|
||||||
|
{b.replaceContent}
|
||||||
|
</CodeHighlight>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
96
src/components/chat/MoreMinimoreThink.tsx
Normal file
96
src/components/chat/MoreMinimoreThink.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { Brain, ChevronDown, ChevronUp, Loader } from "lucide-react";
|
||||||
|
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
import { MoreMinimoreTokenSavings } from "./MoreMinimoreTokenSavings";
|
||||||
|
|
||||||
|
interface AIThinkProps {
|
||||||
|
node?: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreThink: React.FC<AIThinkProps> = ({ children, node }) => {
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const [isExpanded, setIsExpanded] = useState(inProgress);
|
||||||
|
|
||||||
|
// Check if content matches token savings format
|
||||||
|
const tokenSavingsMatch =
|
||||||
|
typeof children === "string"
|
||||||
|
? children.match(
|
||||||
|
/^dyad-token-savings\?original-tokens=([0-9.]+)&smart-context-tokens=([0-9.]+)$/,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Collapse when transitioning from in-progress to not-in-progress
|
||||||
|
useEffect(() => {
|
||||||
|
if (!inProgress && isExpanded) {
|
||||||
|
setIsExpanded(false);
|
||||||
|
}
|
||||||
|
}, [inProgress]);
|
||||||
|
|
||||||
|
// If it's token savings format, render MoreMinimoreTokenSavings component
|
||||||
|
if (tokenSavingsMatch) {
|
||||||
|
const originalTokens = parseFloat(tokenSavingsMatch[1]);
|
||||||
|
const smartContextTokens = parseFloat(tokenSavingsMatch[2]);
|
||||||
|
return (
|
||||||
|
<MoreMinimoreTokenSavings
|
||||||
|
originalTokens={originalTokens}
|
||||||
|
smartContextTokens={smartContextTokens}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress ? "border-purple-500" : "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
role="button"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-purple-500 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<Brain size={16} className="text-purple-500" />
|
||||||
|
<span>Thinking</span>
|
||||||
|
{inProgress && (
|
||||||
|
<Loader size={14} className="ml-1 text-purple-500 animate-spin" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Indicator icon */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content with smooth transition */}
|
||||||
|
<div
|
||||||
|
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
|
||||||
|
style={{
|
||||||
|
maxHeight: isExpanded ? "none" : "0px",
|
||||||
|
opacity: isExpanded ? 1 : 0,
|
||||||
|
marginBottom: isExpanded ? "0" : "-6px", // Compensate for padding
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="px-0 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
{typeof children === "string" ? (
|
||||||
|
<VanillaMarkdownParser content={children} />
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
36
src/components/chat/MoreMinimoreTokenSavings.tsx
Normal file
36
src/components/chat/MoreMinimoreTokenSavings.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Zap } from "lucide-react";
|
||||||
|
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
|
||||||
|
|
||||||
|
interface TokenSavingsProps {
|
||||||
|
originalTokens: number;
|
||||||
|
smartContextTokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreTokenSavings: React.FC<TokenSavingsProps> = ({
|
||||||
|
originalTokens,
|
||||||
|
smartContextTokens,
|
||||||
|
}) => {
|
||||||
|
const tokensSaved = originalTokens - smartContextTokens;
|
||||||
|
const percentageSaved = Math.round((tokensSaved / originalTokens) * 100);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<div className="bg-green-50 dark:bg-green-950 hover:bg-green-100 dark:hover:bg-green-900 rounded-lg px-4 py-2 border border-green-200 dark:border-green-800 my-2 cursor-pointer">
|
||||||
|
<div className="flex items-center gap-2 text-green-700 dark:text-green-300">
|
||||||
|
<Zap size={16} className="text-green-600 dark:text-green-400" />
|
||||||
|
<span className="text-xs font-medium">
|
||||||
|
Saved {percentageSaved}% of codebase tokens with Smart Context
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="top" align="center">
|
||||||
|
<div className="text-left">
|
||||||
|
Saved {Math.round(tokensSaved).toLocaleString()} tokens
|
||||||
|
</div>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
27
src/components/chat/MoreMinimoreWebCrawl.tsx
Normal file
27
src/components/chat/MoreMinimoreWebCrawl.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { ScanQrCode } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreWebCrawlProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreWebCrawl: React.FC<MoreMinimoreWebCrawlProps> = ({
|
||||||
|
children,
|
||||||
|
node: _node,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ScanQrCode size={16} className="text-blue-600" />
|
||||||
|
<div className="text-xs text-blue-600 font-medium">Web Crawl</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm italic text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
31
src/components/chat/MoreMinimoreWebSearch.tsx
Normal file
31
src/components/chat/MoreMinimoreWebSearch.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { Globe } from "lucide-react";
|
||||||
|
|
||||||
|
interface MoreMinimoreWebSearchProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
query?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreWebSearch: React.FC<MoreMinimoreWebSearchProps> = ({
|
||||||
|
children,
|
||||||
|
node: _node,
|
||||||
|
query: queryProp,
|
||||||
|
}) => {
|
||||||
|
const query = queryProp || (typeof children === "string" ? children : "");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-(--background-lightest) rounded-lg px-4 py-2 border my-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Globe size={16} className="text-blue-600" />
|
||||||
|
<div className="text-xs text-blue-600 font-medium">Web Search</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm italic text-gray-600 dark:text-gray-300 mt-2">
|
||||||
|
{query || children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
78
src/components/chat/MoreMinimoreWebSearchResult.tsx
Normal file
78
src/components/chat/MoreMinimoreWebSearchResult.tsx
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { ChevronDown, ChevronUp, Globe, Loader } from "lucide-react";
|
||||||
|
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
|
||||||
|
interface MoreMinimoreWebSearchResultProps {
|
||||||
|
node?: any;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreWebSearchResult: React.FC<MoreMinimoreWebSearchResultProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
}) => {
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
const [isExpanded, setIsExpanded] = useState(inProgress);
|
||||||
|
|
||||||
|
// Collapse when transitioning from in-progress to not-in-progress
|
||||||
|
useEffect(() => {
|
||||||
|
if (!inProgress && isExpanded) {
|
||||||
|
setIsExpanded(false);
|
||||||
|
}
|
||||||
|
}, [inProgress]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative bg-(--background-lightest) dark:bg-zinc-900 hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress ? "border-blue-500" : "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
role="button"
|
||||||
|
aria-expanded={isExpanded}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Top-left label badge */}
|
||||||
|
<div
|
||||||
|
className="absolute top-2 left-2 flex items-center gap-1 px-2 py-0.5 rounded text-xs font-semibold text-blue-600 bg-white dark:bg-zinc-900"
|
||||||
|
style={{ zIndex: 1 }}
|
||||||
|
>
|
||||||
|
<Globe size={16} className="text-blue-600" />
|
||||||
|
<span>Web Search Result</span>
|
||||||
|
{inProgress && (
|
||||||
|
<Loader size={14} className="ml-1 text-blue-600 animate-spin" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Indicator icon */}
|
||||||
|
<div className="absolute top-2 right-2 p-1 text-gray-500">
|
||||||
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Main content with smooth transition */}
|
||||||
|
<div
|
||||||
|
className="pt-6 overflow-hidden transition-all duration-300 ease-in-out"
|
||||||
|
style={{
|
||||||
|
maxHeight: isExpanded ? "none" : "0px",
|
||||||
|
opacity: isExpanded ? 1 : 0,
|
||||||
|
marginBottom: isExpanded ? "0" : "-6px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="px-0 text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
{typeof children === "string" ? (
|
||||||
|
<VanillaMarkdownParser content={children} />
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
159
src/components/chat/MoreMinimoreWrite.tsx
Normal file
159
src/components/chat/MoreMinimoreWrite.tsx
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
ChevronsDownUp,
|
||||||
|
ChevronsUpDown,
|
||||||
|
Pencil,
|
||||||
|
Loader,
|
||||||
|
CircleX,
|
||||||
|
Edit,
|
||||||
|
X,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { CodeHighlight } from "./CodeHighlight";
|
||||||
|
import { CustomTagState } from "./stateTypes";
|
||||||
|
import { FileEditor } from "../preview_panel/FileEditor";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
||||||
|
|
||||||
|
interface MoreMinimoreWriteProps {
|
||||||
|
children?: ReactNode;
|
||||||
|
node?: any;
|
||||||
|
path?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MoreMinimoreWrite: React.FC<MoreMinimoreWriteProps> = ({
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
path: pathProp,
|
||||||
|
description: descriptionProp,
|
||||||
|
}) => {
|
||||||
|
const [isContentVisible, setIsContentVisible] = useState(false);
|
||||||
|
|
||||||
|
// Use props directly if provided, otherwise extract from node
|
||||||
|
const path = pathProp || node?.properties?.path || "";
|
||||||
|
const description = descriptionProp || node?.properties?.description || "";
|
||||||
|
const state = node?.properties?.state as CustomTagState;
|
||||||
|
|
||||||
|
const aborted = state === "aborted";
|
||||||
|
const appId = useAtomValue(selectedAppIdAtom);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const inProgress = state === "pending";
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
setIsEditing(true);
|
||||||
|
setIsContentVisible(true);
|
||||||
|
};
|
||||||
|
// Extract filename from path
|
||||||
|
const fileName = path ? path.split("/").pop() : "";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`bg-(--background-lightest) hover:bg-(--background-lighter) rounded-lg px-4 py-2 border my-2 cursor-pointer ${
|
||||||
|
inProgress
|
||||||
|
? "border-amber-500"
|
||||||
|
: aborted
|
||||||
|
? "border-red-500"
|
||||||
|
: "border-border"
|
||||||
|
}`}
|
||||||
|
onClick={() => setIsContentVisible(!isContentVisible)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Pencil size={16} />
|
||||||
|
{fileName && (
|
||||||
|
<span className="text-gray-700 dark:text-gray-300 font-medium text-sm">
|
||||||
|
{fileName}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{inProgress && (
|
||||||
|
<div className="flex items-center text-amber-600 text-xs">
|
||||||
|
<Loader size={14} className="mr-1 animate-spin" />
|
||||||
|
<span>Writing...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{aborted && (
|
||||||
|
<div className="flex items-center text-red-600 text-xs">
|
||||||
|
<CircleX size={14} className="mr-1" />
|
||||||
|
<span>Did not finish</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center">
|
||||||
|
{!inProgress && (
|
||||||
|
<>
|
||||||
|
{isEditing ? (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleCancel();
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 px-2 py-1 rounded cursor-pointer"
|
||||||
|
>
|
||||||
|
<X size={14} />
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleEdit();
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 px-2 py-1 rounded cursor-pointer"
|
||||||
|
>
|
||||||
|
<Edit size={14} />
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isContentVisible ? (
|
||||||
|
<ChevronsDownUp
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ChevronsUpDown
|
||||||
|
size={20}
|
||||||
|
className="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{path && (
|
||||||
|
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium mb-1">
|
||||||
|
{path}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{description && (
|
||||||
|
<div className="text-sm text-gray-600 dark:text-gray-300">
|
||||||
|
<span className="font-medium">Summary: </span>
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{isContentVisible && (
|
||||||
|
<div
|
||||||
|
className="text-xs cursor-text"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{isEditing ? (
|
||||||
|
<div className="h-96 min-h-96 border border-gray-200 dark:border-gray-700 rounded overflow-hidden">
|
||||||
|
<FileEditor appId={appId ?? null} filePath={path} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<CodeHighlight className="language-typescript">
|
||||||
|
{children}
|
||||||
|
</CodeHighlight>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -56,7 +56,7 @@ export function Message({ spans }: MessageConfig) {
|
|||||||
export const TURBO_EDITS_PROMO_MESSAGE: MessageConfig = {
|
export const TURBO_EDITS_PROMO_MESSAGE: MessageConfig = {
|
||||||
spans: [
|
spans: [
|
||||||
{ type: "text", content: "Tired of waiting on AI?" },
|
{ type: "text", content: "Tired of waiting on AI?" },
|
||||||
{ type: "link", content: " Get Dyad Pro", url: "https://dyad.sh/pro#ai" },
|
{ type: "link", content: " Get MoreMinimore Pro", url: "https://dyad.sh/pro#ai" },
|
||||||
{ type: "text", content: " for faster edits with Turbo Edits." },
|
{ type: "text", content: " for faster edits with Turbo Edits." },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -66,7 +66,7 @@ export const SMART_CONTEXT_PROMO_MESSAGE: MessageConfig = {
|
|||||||
{ type: "text", content: "Save up to 5x on AI costs with " },
|
{ type: "text", content: "Save up to 5x on AI costs with " },
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
content: "Dyad Pro's Smart Context",
|
content: "MoreMinimore Pro's Smart Context",
|
||||||
url: "https://dyad.sh/pro#ai",
|
url: "https://dyad.sh/pro#ai",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -90,7 +90,7 @@ export const REDDIT_TIP: MessageConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
content: "Dyad subreddit",
|
content: "MoreMinimore subreddit",
|
||||||
url: "https://www.reddit.com/r/dyadbuilders/",
|
url: "https://www.reddit.com/r/dyadbuilders/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -124,7 +124,7 @@ export const BUILD_A_BIBLE_APP_TIP: MessageConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
content: " the creator of Dyad build a Bible app step-by-step",
|
content: " the creator of MoreMinimore build a Bible app step-by-step",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -182,12 +182,12 @@ export const ROADMAP_TIP: MessageConfig = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Like Dyad? Star it on GitHub https://github.com/dyad-sh/dyad/
|
// Like MoreMinimore? Star it on GitHub https://github.com/dyad-sh/dyad/
|
||||||
export const GITHUB_TIP: MessageConfig = {
|
export const GITHUB_TIP: MessageConfig = {
|
||||||
spans: [
|
spans: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
content: "Like Dyad? Star it on ",
|
content: "Like MoreMinimore? Star it on ",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export function TokenBar({ chatId }: TokenBarProps) {
|
|||||||
}
|
}
|
||||||
className="text-blue-500 dark:text-blue-400 cursor-pointer hover:underline"
|
className="text-blue-500 dark:text-blue-400 cursor-pointer hover:underline"
|
||||||
>
|
>
|
||||||
Dyad Pro's Smart Context
|
MoreMinimore Pro's Smart Context
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export const OnboardingBanner = ({
|
|||||||
<div className="relative p-2">
|
<div className="relative p-2">
|
||||||
<img
|
<img
|
||||||
src="https://img.youtube.com/vi/rgdNoHLaRN4/maxresdefault.jpg"
|
src="https://img.youtube.com/vi/rgdNoHLaRN4/maxresdefault.jpg"
|
||||||
alt="Get started with Dyad in 3 minutes"
|
alt="Get started with MoreMinimore in 3 minutes"
|
||||||
className="w-28 h-16 object-cover rounded-md"
|
className="w-28 h-16 object-cover rounded-md"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
@@ -41,7 +41,7 @@ export const OnboardingBanner = ({
|
|||||||
<div className="flex-1 px-4 py-3">
|
<div className="flex-1 px-4 py-3">
|
||||||
<div className="text-foreground">
|
<div className="text-foreground">
|
||||||
<p className="font-semibold text-base">
|
<p className="font-semibold text-base">
|
||||||
Get started with Dyad in 3 minutes
|
Get started with MoreMinimore in 3 minutes
|
||||||
</p>
|
</p>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Start building your app for free
|
Start building your app for free
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => {
|
|||||||
</h2>
|
</h2>
|
||||||
<p className="text-muted-foreground mb-10 text-center max-w-md text-base leading-relaxed">
|
<p className="text-muted-foreground mb-10 text-center max-w-md text-base leading-relaxed">
|
||||||
Unlock the ability to annotate screenshots and enhance your workflow
|
Unlock the ability to annotate screenshots and enhance your workflow
|
||||||
with Dyad Pro.
|
with MoreMinimore Pro.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Get Pro Button */}
|
{/* Get Pro Button */}
|
||||||
@@ -45,7 +45,7 @@ export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => {
|
|||||||
size="lg"
|
size="lg"
|
||||||
className="px-8 shadow-md hover:shadow-lg transition-all"
|
className="px-8 shadow-md hover:shadow-lg transition-all"
|
||||||
>
|
>
|
||||||
Get Dyad Pro
|
Get MoreMinimore Pro
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ import { useShortcut } from "@/hooks/useShortcut";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { normalizePath } from "../../../shared/normalizePath";
|
import { normalizePath } from "../../../shared/normalizePath";
|
||||||
import { showError } from "@/lib/toast";
|
import { showError } from "@/lib/toast";
|
||||||
import { AnnotatorOnlyForPro } from "./AnnotatorOnlyForPro";
|
|
||||||
import { useAttachments } from "@/hooks/useAttachments";
|
import { useAttachments } from "@/hooks/useAttachments";
|
||||||
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
|
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
|
||||||
import { Annotator } from "@/pro/ui/components/Annotator/Annotator";
|
import { Annotator } from "@/pro/ui/components/Annotator/Annotator";
|
||||||
@@ -107,7 +106,7 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
|
|||||||
{/* Add a little chip that says "Internal error" if source is "dyad-app" */}
|
{/* Add a little chip that says "Internal error" if source is "dyad-app" */}
|
||||||
{error.source === "dyad-app" && (
|
{error.source === "dyad-app" && (
|
||||||
<div className="absolute top-1 right-1 p-1 bg-red-100 dark:bg-red-900 rounded-md text-xs font-medium text-red-700 dark:text-red-300">
|
<div className="absolute top-1 right-1 p-1 bg-red-100 dark:bg-red-900 rounded-md text-xs font-medium text-red-700 dark:text-red-300">
|
||||||
Internal Dyad error
|
Internal MoreMinimore error
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -143,7 +142,7 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
|
|||||||
{isDockerError
|
{isDockerError
|
||||||
? "Make sure Docker Desktop is running and try restarting the app."
|
? "Make sure Docker Desktop is running and try restarting the app."
|
||||||
: error.source === "dyad-app"
|
: error.source === "dyad-app"
|
||||||
? "Try restarting the Dyad app or restarting your computer to see if that fixes the error."
|
? "Try restarting the MoreMinimore app or restarting your computer to see if that fixes the error."
|
||||||
: "Check if restarting the app fixes the error."}
|
: "Check if restarting the app fixes the error."}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -985,17 +984,11 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
|
|||||||
: { width: `${deviceWidthConfig[deviceMode]}px` }
|
: { width: `${deviceWidthConfig[deviceMode]}px` }
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{userBudget ? (
|
|
||||||
<Annotator
|
<Annotator
|
||||||
screenshotUrl={screenshotDataUrl}
|
screenshotUrl={screenshotDataUrl}
|
||||||
onSubmit={addAttachments}
|
onSubmit={addAttachments}
|
||||||
handleAnnotatorClick={handleAnnotatorClick}
|
handleAnnotatorClick={handleAnnotatorClick}
|
||||||
/>
|
/>
|
||||||
) : (
|
|
||||||
<AnnotatorOnlyForPro
|
|
||||||
onGoBack={() => setAnnotatorMode(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
1081
src/components/preview_panel/PreviewIframe.tsx.bak
Normal file
1081
src/components/preview_panel/PreviewIframe.tsx.bak
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ import { Badge } from "@/components/ui/badge";
|
|||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import type { SecurityFinding, SecurityReviewResult } from "@/ipc/ipc_types";
|
import type { SecurityFinding, SecurityReviewResult } from "@/ipc/ipc_types";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { VanillaMarkdownParser } from "@/components/chat/DyadMarkdownParser";
|
import { VanillaMarkdownParser } from "@/components/chat/MoreMinimoreMarkdownParser";
|
||||||
import { showSuccess, showWarning } from "@/lib/toast";
|
import { showSuccess, showWarning } from "@/lib/toast";
|
||||||
import { useLoadAppFile } from "@/hooks/useLoadAppFile";
|
import { useLoadAppFile } from "@/hooks/useLoadAppFile";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ interface ApiKeyConfigurationProps {
|
|||||||
onApiKeyInputChange: (value: string) => void;
|
onApiKeyInputChange: (value: string) => void;
|
||||||
onSaveKey: (value: string) => Promise<void>;
|
onSaveKey: (value: string) => Promise<void>;
|
||||||
onDeleteKey: () => Promise<void>;
|
onDeleteKey: () => Promise<void>;
|
||||||
isDyad: boolean;
|
isMoreMinimore: boolean;
|
||||||
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
|
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ export function ApiKeyConfiguration({
|
|||||||
onApiKeyInputChange,
|
onApiKeyInputChange,
|
||||||
onSaveKey,
|
onSaveKey,
|
||||||
onDeleteKey,
|
onDeleteKey,
|
||||||
isDyad,
|
isMoreMinimore,
|
||||||
updateSettings,
|
updateSettings,
|
||||||
}: ApiKeyConfigurationProps) {
|
}: ApiKeyConfigurationProps) {
|
||||||
// Special handling for Azure OpenAI which requires environment variables
|
// Special handling for Azure OpenAI which requires environment variables
|
||||||
@@ -86,7 +86,7 @@ export function ApiKeyConfiguration({
|
|||||||
if (isValidUserKey || !hasEnvKey) {
|
if (isValidUserKey || !hasEnvKey) {
|
||||||
defaultAccordionValue.push("settings-key");
|
defaultAccordionValue.push("settings-key");
|
||||||
}
|
}
|
||||||
if (!isDyad && hasEnvKey) {
|
if (!isMoreMinimore && hasEnvKey) {
|
||||||
defaultAccordionValue.push("env-key");
|
defaultAccordionValue.push("env-key");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ export function ApiKeyConfiguration({
|
|||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
||||||
{!isDyad && envVarName && (
|
{!isMoreMinimore && envVarName && (
|
||||||
<AccordionItem
|
<AccordionItem
|
||||||
value="env-key"
|
value="env-key"
|
||||||
className="border rounded-lg px-4 bg-(--background-lightest)"
|
className="border rounded-lg px-4 bg-(--background-lightest)"
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export function AzureConfiguration({
|
|||||||
variant: "default" as const,
|
variant: "default" as const,
|
||||||
title: "Azure OpenAI Configured",
|
title: "Azure OpenAI Configured",
|
||||||
description:
|
description:
|
||||||
"Dyad will use the credentials saved in Settings for Azure OpenAI models.",
|
"MoreMinimore will use the credentials saved in Settings for Azure OpenAI models.",
|
||||||
icon: KeyRound,
|
icon: KeyRound,
|
||||||
titleClassName: "",
|
titleClassName: "",
|
||||||
descriptionClassName: "",
|
descriptionClassName: "",
|
||||||
@@ -259,12 +259,12 @@ export function AzureConfiguration({
|
|||||||
<div className="text-sm text-muted-foreground space-y-2">
|
<div className="text-sm text-muted-foreground space-y-2">
|
||||||
<p>
|
<p>
|
||||||
You can continue to configure Azure via environment variables.
|
You can continue to configure Azure via environment variables.
|
||||||
If both variables are present and no settings are saved, Dyad
|
If both variables are present and no settings are saved, MoreMinimore
|
||||||
will use them automatically.
|
will use them automatically.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Values saved in Settings take precedence over environment
|
Values saved in Settings take precedence over environment
|
||||||
variables. Restart Dyad after changing environment variables.
|
variables. Restart MoreMinimore after changing environment variables.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
|||||||
@@ -22,21 +22,21 @@ interface ProviderSettingsHeaderProps {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
hasFreeTier?: boolean;
|
hasFreeTier?: boolean;
|
||||||
providerWebsiteUrl?: string;
|
providerWebsiteUrl?: string;
|
||||||
isDyad: boolean;
|
isMoreMinimore: boolean;
|
||||||
onBackClick: () => void;
|
onBackClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKeyButtonText({
|
function getKeyButtonText({
|
||||||
isConfigured,
|
isConfigured,
|
||||||
isDyad,
|
isMoreMinimore,
|
||||||
}: {
|
}: {
|
||||||
isConfigured: boolean;
|
isConfigured: boolean;
|
||||||
isDyad: boolean;
|
isMoreMinimore: boolean;
|
||||||
}) {
|
}) {
|
||||||
if (isDyad) {
|
if (isMoreMinimore) {
|
||||||
return isConfigured
|
return isConfigured
|
||||||
? "Manage Dyad Pro Subscription"
|
? "Manage MoreMinimore Pro Subscription"
|
||||||
: "Setup Dyad Pro Subscription";
|
: "Setup MoreMinimore Pro Subscription";
|
||||||
}
|
}
|
||||||
return isConfigured ? "Manage API Keys" : "Setup API Key";
|
return isConfigured ? "Manage API Keys" : "Setup API Key";
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ export function ProviderSettingsHeader({
|
|||||||
isLoading,
|
isLoading,
|
||||||
hasFreeTier,
|
hasFreeTier,
|
||||||
providerWebsiteUrl,
|
providerWebsiteUrl,
|
||||||
isDyad,
|
isMoreMinimore,
|
||||||
onBackClick,
|
onBackClick,
|
||||||
}: ProviderSettingsHeaderProps) {
|
}: ProviderSettingsHeaderProps) {
|
||||||
const handleGetApiKeyClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleGetApiKeyClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
@@ -63,7 +63,7 @@ export function ProviderSettingsHeader({
|
|||||||
className="mb-4 cursor-pointer py-5 w-full ring-4 ring-primary/60 shadow-lg shadow-primary/30 border-primary/60"
|
className="mb-4 cursor-pointer py-5 w-full ring-4 ring-primary/60 shadow-lg shadow-primary/30 border-primary/60"
|
||||||
>
|
>
|
||||||
<KeyRound className="mr-2 h-4 w-4" />
|
<KeyRound className="mr-2 h-4 w-4" />
|
||||||
{getKeyButtonText({ isConfigured, isDyad })}
|
{getKeyButtonText({ isConfigured, isMoreMinimore })}
|
||||||
<ExternalLink className="ml-2 h-4 w-4" />
|
<ExternalLink className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -55,22 +55,22 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
const supportsCustomModels =
|
const supportsCustomModels =
|
||||||
providerData?.type === "custom" || providerData?.type === "cloud";
|
providerData?.type === "custom" || providerData?.type === "cloud";
|
||||||
|
|
||||||
const isDyad = provider === "auto";
|
const isMoreMinimore = provider === "auto";
|
||||||
|
|
||||||
const [apiKeyInput, setApiKeyInput] = useState("");
|
const [apiKeyInput, setApiKeyInput] = useState("");
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [saveError, setSaveError] = useState<string | null>(null);
|
const [saveError, setSaveError] = useState<string | null>(null);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// Use fetched data (or defaults for Dyad)
|
// Use fetched data (or defaults for MoreMinimore)
|
||||||
const providerDisplayName = isDyad
|
const providerDisplayName = isMoreMinimore
|
||||||
? "Dyad"
|
? "MoreMinimore"
|
||||||
: (providerData?.name ?? "Unknown Provider");
|
: (providerData?.name ?? "Unknown Provider");
|
||||||
const providerWebsiteUrl = isDyad
|
const providerWebsiteUrl = isMoreMinimore
|
||||||
? "https://academy.dyad.sh/settings"
|
? "https://academy.dyad.sh/settings"
|
||||||
: providerData?.websiteUrl;
|
: providerData?.websiteUrl;
|
||||||
const hasFreeTier = isDyad ? false : providerData?.hasFreeTier;
|
const hasFreeTier = isMoreMinimore ? false : providerData?.hasFreeTier;
|
||||||
const envVarName = isDyad ? undefined : providerData?.envVarName;
|
const envVarName = isMoreMinimore ? undefined : providerData?.envVarName;
|
||||||
|
|
||||||
// Use provider ID (which is the 'provider' prop)
|
// Use provider ID (which is the 'provider' prop)
|
||||||
const userApiKey = settings?.providerSettings?.[provider]?.apiKey?.value;
|
const userApiKey = settings?.providerSettings?.[provider]?.apiKey?.value;
|
||||||
@@ -137,7 +137,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (isDyad) {
|
if (isMoreMinimore) {
|
||||||
settingsUpdate.enableDyadPro = true;
|
settingsUpdate.enableDyadPro = true;
|
||||||
}
|
}
|
||||||
await updateSettings(settingsUpdate);
|
await updateSettings(settingsUpdate);
|
||||||
@@ -174,7 +174,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Toggle Dyad Pro Handler ---
|
// --- Toggle MoreMinimore Pro Handler ---
|
||||||
const handleToggleDyadPro = async (enabled: boolean) => {
|
const handleToggleDyadPro = async (enabled: boolean) => {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
try {
|
try {
|
||||||
@@ -182,7 +182,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
enableDyadPro: enabled,
|
enableDyadPro: enabled,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
showError(`Error toggling Dyad Pro: ${error}`);
|
showError(`Error toggling MoreMinimore Pro: ${error}`);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle case where provider is not found (e.g., invalid ID in URL)
|
// Handle case where provider is not found (e.g., invalid ID in URL)
|
||||||
if (!providerData && !isDyad) {
|
if (!providerData && !isMoreMinimore) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen px-8 py-4">
|
<div className="min-h-screen px-8 py-4">
|
||||||
<div className="max-w-4xl mx-auto">
|
<div className="max-w-4xl mx-auto">
|
||||||
@@ -278,7 +278,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
isLoading={settingsLoading}
|
isLoading={settingsLoading}
|
||||||
hasFreeTier={hasFreeTier}
|
hasFreeTier={hasFreeTier}
|
||||||
providerWebsiteUrl={providerWebsiteUrl}
|
providerWebsiteUrl={providerWebsiteUrl}
|
||||||
isDyad={isDyad}
|
isMoreMinimore={isMoreMinimore}
|
||||||
onBackClick={() => router.history.back()}
|
onBackClick={() => router.history.back()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -306,17 +306,17 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
|||||||
onApiKeyInputChange={setApiKeyInput}
|
onApiKeyInputChange={setApiKeyInput}
|
||||||
onSaveKey={handleSaveKey}
|
onSaveKey={handleSaveKey}
|
||||||
onDeleteKey={handleDeleteKey}
|
onDeleteKey={handleDeleteKey}
|
||||||
isDyad={isDyad}
|
isMoreMinimore={isMoreMinimore}
|
||||||
updateSettings={updateSettings}
|
updateSettings={updateSettings}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDyad && !settingsLoading && (
|
{isMoreMinimore && !settingsLoading && (
|
||||||
<div className="mt-6 flex items-center justify-between p-4 bg-(--background-lightest) rounded-lg border">
|
<div className="mt-6 flex items-center justify-between p-4 bg-(--background-lightest) rounded-lg border">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Enable Dyad Pro</h3>
|
<h3 className="font-medium">Enable MoreMinimore Pro</h3>
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||||
Toggle to enable Dyad Pro
|
Toggle to enable MoreMinimore Pro
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@@ -830,7 +830,7 @@ This conversation includes one or more image attachments. When the user uploads
|
|||||||
appPath,
|
appPath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const smartContextMode: SmartContextMode = isDeepContextEnabled
|
const smartContextMode: SmartContextMode = true
|
||||||
? "deep"
|
? "deep"
|
||||||
: "balanced";
|
: "balanced";
|
||||||
// Build provider options with correct Google/Vertex thinking config gating
|
// Build provider options with correct Google/Vertex thinking config gating
|
||||||
|
|||||||
@@ -280,6 +280,10 @@ export class IpcClient {
|
|||||||
await this.ipcRenderer.invoke("restart-dyad");
|
await this.ipcRenderer.invoke("restart-dyad");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async restartMoreMinimore(): Promise<void> {
|
||||||
|
await this.ipcRenderer.invoke("restart-dyad");
|
||||||
|
}
|
||||||
|
|
||||||
public async reloadEnvPath(): Promise<void> {
|
public async reloadEnvPath(): Promise<void> {
|
||||||
await this.ipcRenderer.invoke("reload-env-path");
|
await this.ipcRenderer.invoke("reload-env-path");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { ipcMain } from "electron";
|
||||||
import { registerAppHandlers } from "./handlers/app_handlers";
|
import { registerAppHandlers } from "./handlers/app_handlers";
|
||||||
import { registerChatHandlers } from "./handlers/chat_handlers";
|
import { registerChatHandlers } from "./handlers/chat_handlers";
|
||||||
import { registerChatStreamHandlers } from "./handlers/chat_stream_handlers";
|
import { registerChatStreamHandlers } from "./handlers/chat_stream_handlers";
|
||||||
@@ -48,6 +49,15 @@ export function registerIpcHandlers() {
|
|||||||
registerProblemsHandlers();
|
registerProblemsHandlers();
|
||||||
registerProposalHandlers();
|
registerProposalHandlers();
|
||||||
registerDebugHandlers();
|
registerDebugHandlers();
|
||||||
|
|
||||||
|
// Add missing user budget handler
|
||||||
|
ipcMain.handle("get-user-budget", async () => {
|
||||||
|
return {
|
||||||
|
totalCredits: 1000,
|
||||||
|
usedCredits: 0,
|
||||||
|
resetDate: new Date().toISOString()
|
||||||
|
};
|
||||||
|
});
|
||||||
registerSupabaseHandlers();
|
registerSupabaseHandlers();
|
||||||
registerNeonHandlers();
|
registerNeonHandlers();
|
||||||
registerLocalModelHandlers();
|
registerLocalModelHandlers();
|
||||||
|
|||||||
@@ -530,9 +530,9 @@ export const CLOUD_PROVIDERS: Record<
|
|||||||
gatewayPrefix: "openrouter/",
|
gatewayPrefix: "openrouter/",
|
||||||
},
|
},
|
||||||
auto: {
|
auto: {
|
||||||
displayName: "Dyad",
|
displayName: "MoreMinimore",
|
||||||
websiteUrl: "https://academy.dyad.sh/settings",
|
websiteUrl: "https://moreminimore.com/settings",
|
||||||
gatewayPrefix: "dyad/",
|
gatewayPrefix: "moreminimore/",
|
||||||
},
|
},
|
||||||
azure: {
|
azure: {
|
||||||
displayName: "Azure OpenAI",
|
displayName: "Azure OpenAI",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import log from "electron-log";
|
|||||||
import { FREE_OPENROUTER_MODEL_NAMES } from "../shared/language_model_constants";
|
import { FREE_OPENROUTER_MODEL_NAMES } from "../shared/language_model_constants";
|
||||||
import { getLanguageModelProviders } from "../shared/language_model_helpers";
|
import { getLanguageModelProviders } from "../shared/language_model_helpers";
|
||||||
import { LanguageModelProvider } from "../ipc_types";
|
import { LanguageModelProvider } from "../ipc_types";
|
||||||
// import { createDyadEngine } from "./llm_engine_provider"; // Removed - Dyad Engine dependency
|
// // // // // import { createDyadEngine } from "./llm_engine_provider"; // Removed - Dyad Engine dependency // Removed - Dyad Engine dependency // Removed - Dyad Engine dependency // Removed - Dyad Engine dependency // Removed - Dyad Engine dependency
|
||||||
|
|
||||||
import { LM_STUDIO_BASE_URL } from "./lm_studio_utils";
|
import { LM_STUDIO_BASE_URL } from "./lm_studio_utils";
|
||||||
import { createOllamaProvider } from "./ollama_provider";
|
import { createOllamaProvider } from "./ollama_provider";
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ if (fs.existsSync(gitDir)) {
|
|||||||
// https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app#main-process-mainjs
|
// https://www.electronjs.org/docs/latest/tutorial/launch-app-from-url-in-another-app#main-process-mainjs
|
||||||
if (process.defaultApp) {
|
if (process.defaultApp) {
|
||||||
if (process.argv.length >= 2) {
|
if (process.argv.length >= 2) {
|
||||||
app.setAsDefaultProtocolClient("dyad", process.execPath, [
|
app.setAsDefaultProtocolClient("moreminimore", process.execPath, [
|
||||||
path.resolve(process.argv[1]),
|
path.resolve(process.argv[1]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
app.setAsDefaultProtocolClient("dyad");
|
app.setAsDefaultProtocolClient("moreminimore");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onReady() {
|
export async function onReady() {
|
||||||
@@ -306,10 +306,10 @@ function handleDeepLinkReturn(url: string) {
|
|||||||
"hostname",
|
"hostname",
|
||||||
parsed.hostname,
|
parsed.hostname,
|
||||||
);
|
);
|
||||||
if (parsed.protocol !== "dyad:") {
|
if (parsed.protocol !== "moreminimore:") {
|
||||||
dialog.showErrorBox(
|
dialog.showErrorBox(
|
||||||
"Invalid Protocol",
|
"Invalid Protocol",
|
||||||
`Expected dyad://, got ${parsed.protocol}. Full URL: ${url}`,
|
`Expected moreminimore://, got ${parsed.protocol}. Full URL: ${url}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { ForceCloseDialog } from "@/components/ForceCloseDialog";
|
|||||||
import type { FileAttachment } from "@/ipc/ipc_types";
|
import type { FileAttachment } from "@/ipc/ipc_types";
|
||||||
import { NEON_TEMPLATE_IDS } from "@/shared/templates";
|
import { NEON_TEMPLATE_IDS } from "@/shared/templates";
|
||||||
import { neonTemplateHook } from "@/client_logic/template_hook";
|
import { neonTemplateHook } from "@/client_logic/template_hook";
|
||||||
import { ProBanner } from "@/components/ProBanner";
|
// import { ProBanner } from "@/components/ProBanner";
|
||||||
|
|
||||||
// Adding an export for attachments
|
// Adding an export for attachments
|
||||||
export interface HomeSubmitOptions {
|
export interface HomeSubmitOptions {
|
||||||
@@ -268,7 +268,6 @@ export default function HomePage() {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ProBanner />
|
|
||||||
</div>
|
</div>
|
||||||
<PrivacyBanner />
|
<PrivacyBanner />
|
||||||
|
|
||||||
|
|||||||
@@ -53,4 +53,14 @@ export const localTemplatesData: Template[] = [
|
|||||||
isExperimental: true,
|
isExperimental: true,
|
||||||
requiresNeon: true,
|
requiresNeon: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "moreminimore-custom",
|
||||||
|
title: "MoreMinimore Custom",
|
||||||
|
description: "Custom MoreMinimore template with enhanced features and debranded experience.",
|
||||||
|
imageUrl:
|
||||||
|
"https://github.com/user-attachments/assets/5b700eab-b28c-498e-96de-8649b14c16d9",
|
||||||
|
githubUrl: "https://github.com/kunthawat/moreminimore-vibe",
|
||||||
|
isOfficial: false,
|
||||||
|
isExperimental: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user