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
|
||||
|
||||
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
|
||||
// FROM:
|
||||
"@dyad-sh/supabase-management-js": "v1.0.1",
|
||||
// TO:
|
||||
"@moreminimore/supabase-management-js": "v1.0.1",
|
||||
```
|
||||
|
||||
### 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",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dyad",
|
||||
"name": "moreminimore",
|
||||
"version": "0.31.0-beta.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
"@ai-sdk/xai": "^2.0.16",
|
||||
"@babel/parser": "^7.28.5",
|
||||
"@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",
|
||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||
"@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
|
||||
print_success "Removed pro IPC channels"
|
||||
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
|
||||
update_branding() {
|
||||
print_status "Updating branding from Dyad to MoreMinimore..."
|
||||
|
||||
# Update package.json
|
||||
if [ -f "package.json" ]; then
|
||||
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
|
||||
# Package.json already updated - keeping original @dyad-sh/supabase-management-js
|
||||
print_success "Package.json configuration maintained"
|
||||
|
||||
# Update app name in main.ts
|
||||
if [ -f "src/main.ts" ]; then
|
||||
@@ -192,6 +218,121 @@ update_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
|
||||
cleanup_imports() {
|
||||
print_status "Cleaning up unused imports..."
|
||||
@@ -254,6 +395,12 @@ main() {
|
||||
update_branding
|
||||
convert_smart_context
|
||||
update_ui_text
|
||||
update_component_names
|
||||
update_urls
|
||||
update_branding_text
|
||||
update_ai_providers
|
||||
remove_youtube_section
|
||||
update_app_metadata
|
||||
cleanup_imports
|
||||
install_dependencies
|
||||
test_compilation
|
||||
@@ -264,13 +411,24 @@ main() {
|
||||
echo "✅ Applied custom remove-limit feature"
|
||||
echo "✅ Removed Dyad API dependencies"
|
||||
echo "✅ Removed Dyad Engine dependencies"
|
||||
echo "✅ Removed pro features"
|
||||
echo "✅ Removed pro features and Pro button"
|
||||
echo "✅ Updated branding to MoreMinimore"
|
||||
echo "✅ Converted smart context to standard feature"
|
||||
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 "✅ Installed dependencies"
|
||||
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 ""
|
||||
echo "Next steps:"
|
||||
|
||||
@@ -10,11 +10,8 @@ import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useDeepLink } from "@/contexts/DeepLinkContext";
|
||||
import { useEffect, useState } from "react";
|
||||
import { DyadProSuccessDialog } from "@/components/DyadProSuccessDialog";
|
||||
import { useTheme } from "@/contexts/ThemeContext";
|
||||
import { IpcClient } from "@/ipc/ipc_client";
|
||||
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
|
||||
import { UserBudgetInfo } from "@/ipc/ipc_types";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
@@ -28,7 +25,6 @@ export const TitleBar = () => {
|
||||
const { navigate } = useRouter();
|
||||
const location = useLocation();
|
||||
const { settings, refreshSettings } = useSettings();
|
||||
const [isSuccessDialogOpen, setIsSuccessDialogOpen] = useState(false);
|
||||
const [showWindowControls, setShowWindowControls] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -45,16 +41,11 @@ export const TitleBar = () => {
|
||||
checkPlatform();
|
||||
}, []);
|
||||
|
||||
const showDyadProSuccessDialog = () => {
|
||||
setIsSuccessDialogOpen(true);
|
||||
};
|
||||
|
||||
const { lastDeepLink, clearLastDeepLink } = useDeepLink();
|
||||
useEffect(() => {
|
||||
const handleDeepLink = async () => {
|
||||
if (lastDeepLink?.type === "dyad-pro-return") {
|
||||
if (lastDeepLink) {
|
||||
await refreshSettings();
|
||||
showDyadProSuccessDialog();
|
||||
clearLastDeepLink();
|
||||
}
|
||||
};
|
||||
@@ -73,15 +64,12 @@ export const TitleBar = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const isDyadPro = !!settings?.providerSettings?.auto?.apiKey?.value;
|
||||
const isDyadProEnabled = Boolean(settings?.enableDyadPro);
|
||||
|
||||
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={`${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
|
||||
data-testid="title-bar-app-name-button"
|
||||
variant="outline"
|
||||
@@ -93,7 +81,6 @@ export const TitleBar = () => {
|
||||
>
|
||||
{displayText}
|
||||
</Button>
|
||||
{isDyadPro && <DyadProButton isDyadProEnabled={isDyadProEnabled} />}
|
||||
|
||||
{/* Preview Header */}
|
||||
{location.pathname === "/chat" && (
|
||||
@@ -104,141 +91,52 @@ export const TitleBar = () => {
|
||||
|
||||
{showWindowControls && <WindowsControls />}
|
||||
</div>
|
||||
|
||||
<DyadProSuccessDialog
|
||||
isOpen={isSuccessDialogOpen}
|
||||
onClose={() => setIsSuccessDialogOpen(false)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function WindowsControls() {
|
||||
const { isDarkMode } = useTheme();
|
||||
// Windows window controls component
|
||||
const WindowsControls = () => {
|
||||
const ipcClient = IpcClient.getInstance();
|
||||
|
||||
const minimizeWindow = () => {
|
||||
const handleMinimize = () => {
|
||||
ipcClient.minimizeWindow();
|
||||
};
|
||||
|
||||
const maximizeWindow = () => {
|
||||
const handleMaximize = () => {
|
||||
ipcClient.maximizeWindow();
|
||||
};
|
||||
|
||||
const closeWindow = () => {
|
||||
const handleClose = () => {
|
||||
ipcClient.closeWindow();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ml-auto flex 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"
|
||||
<div className="flex items-center no-app-region-drag">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||
onClick={handleMinimize}
|
||||
>
|
||||
<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"
|
||||
<span className="text-xs">−</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 w-8 p-0 hover:bg-zinc-200 dark:hover:bg-zinc-700"
|
||||
onClick={handleMaximize}
|
||||
>
|
||||
<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"
|
||||
<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}
|
||||
>
|
||||
<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>
|
||||
<span className="text-xs">×</span>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DyadProButton({
|
||||
isDyadProEnabled,
|
||||
}: {
|
||||
isDyadProEnabled: boolean;
|
||||
}) {
|
||||
const { navigate } = useRouter();
|
||||
const { userBudget } = useUserBudgetInfo();
|
||||
return (
|
||||
<Button
|
||||
data-testid="title-bar-dyad-pro-button"
|
||||
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"
|
||||
>
|
||||
{isDyadProEnabled ? "Pro" : "Pro (off)"}
|
||||
{userBudget && isDyadProEnabled && (
|
||||
<AICreditStatus userBudget={userBudget} />
|
||||
)}
|
||||
</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>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -94,7 +94,7 @@ export function AppUpgrades({ appId }: { appId: number | null }) {
|
||||
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"
|
||||
>
|
||||
App is up-to-date and has all Dyad capabilities enabled
|
||||
App is up-to-date and has all MoreMinimore capabilities enabled
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
|
||||
@@ -20,11 +20,11 @@ export function AutoUpdateSwitch() {
|
||||
updateSettings({ enableAutoUpdate: checked });
|
||||
toast("Auto-update settings changed", {
|
||||
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: {
|
||||
label: "Restart Dyad",
|
||||
label: "Restart MoreMinimore",
|
||||
onClick: () => {
|
||||
IpcClient.getInstance().restartDyad();
|
||||
IpcClient.getInstance().restartMoreMinimore();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ContextFilesPicker } from "./ContextFilesPicker";
|
||||
import { ModelPicker } from "./ModelPicker";
|
||||
import { ProModeSelector } from "./ProModeSelector";
|
||||
import { ChatModeSelector } from "./ChatModeSelector";
|
||||
import { McpToolsPicker } from "@/components/McpToolsPicker";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
@@ -17,14 +16,10 @@ export function ChatInputControls({
|
||||
<ChatModeSelector />
|
||||
{settings?.selectedChatMode === "agent" && (
|
||||
<>
|
||||
<div className="w-1.5"></div>
|
||||
<McpToolsPicker />
|
||||
</>
|
||||
)}
|
||||
<div className="w-1.5"></div>
|
||||
<ModelPicker />
|
||||
<div className="w-1.5"></div>
|
||||
<ProModeSelector />
|
||||
<div className="w-1"></div>
|
||||
{showContextFilesPicker && (
|
||||
<>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const CommunityCodeConsentDialog: React.FC<
|
||||
<AlertDialogTitle>Community Code Notice</AlertDialogTitle>
|
||||
<AlertDialogDescription className="space-y-3">
|
||||
<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.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
|
||||
import { InfoIcon, Settings2, Trash2 } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
@@ -19,7 +21,7 @@ import { useContextPaths } from "@/hooks/useContextPaths";
|
||||
import type { ContextPathResult } from "@/lib/schemas";
|
||||
|
||||
export function ContextFilesPicker() {
|
||||
const { settings } = useSettings();
|
||||
const { settings, updateSettings } = useSettings();
|
||||
const {
|
||||
contextPaths,
|
||||
smartContextAutoIncludes,
|
||||
@@ -112,8 +114,7 @@ export function ContextFilesPicker() {
|
||||
updateExcludePaths(newPaths);
|
||||
};
|
||||
|
||||
const isSmartContextEnabled =
|
||||
settings?.enableDyadPro && settings?.enableProSmartFilesContextMode;
|
||||
const isSmartContextEnabled = settings?.enableProSmartFilesContextMode ?? false;
|
||||
|
||||
return (
|
||||
<Popover open={isOpen} onOpenChange={setIsOpen}>
|
||||
@@ -130,7 +131,7 @@ export function ContextFilesPicker() {
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Codebase Context</TooltipContent>
|
||||
<TooltipContent>Context Settings</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<PopoverContent
|
||||
@@ -139,7 +140,7 @@ export function ContextFilesPicker() {
|
||||
>
|
||||
<div className="relative space-y-4">
|
||||
<div>
|
||||
<h3 className="font-medium">Codebase Context</h3>
|
||||
<h3 className="font-medium">Context Settings</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
@@ -152,11 +153,11 @@ export function ContextFilesPicker() {
|
||||
<TooltipContent className="max-w-[300px]">
|
||||
{isSmartContextEnabled ? (
|
||||
<p>
|
||||
With Smart Context, Dyad uses the most relevant files as
|
||||
With Smart Context, MoreMinimore uses the most relevant files as
|
||||
context.
|
||||
</p>
|
||||
) : (
|
||||
<p>By default, Dyad uses your whole codebase.</p>
|
||||
<p>By default, MoreMinimore uses your whole codebase.</p>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -164,6 +165,28 @@ export function ContextFilesPicker() {
|
||||
</p>
|
||||
</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">
|
||||
<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">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{isSmartContextEnabled
|
||||
? "Dyad 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 Smart Context to automatically find the most relevant files to use as context."
|
||||
: "MoreMinimore will use the entire codebase as context."}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -40,7 +40,7 @@ export function ErrorBoundary({ error }: ErrorComponentProps) {
|
||||
${error?.stack ? `\n\`\`\`\n${error.stack.slice(0, 1000)}\n\`\`\`` : ""}
|
||||
|
||||
## System Information
|
||||
- Dyad Version: ${debugInfo.dyadVersion}
|
||||
- MoreMinimore Version: ${debugInfo.dyadVersion}
|
||||
- Platform: ${debugInfo.platform}
|
||||
- Architecture: ${debugInfo.architecture}
|
||||
- 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
|
||||
const encodedBody = encodeURIComponent(issueBody);
|
||||
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}`;
|
||||
|
||||
@@ -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">
|
||||
<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">
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -160,7 +160,7 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) {
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Dyad Help Bot</DialogTitle>
|
||||
<DialogTitle>MoreMinimore Help Bot</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-3 h-[480px]">
|
||||
{error && (
|
||||
@@ -183,7 +183,7 @@ export function HelpBotDialog({ isOpen, onClose }: HelpBotDialogProps) {
|
||||
{messages.length === 0 ? (
|
||||
<div className="space-y-3">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Ask a question about using Dyad.
|
||||
Ask a question about using MoreMinimore.
|
||||
</div>
|
||||
<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
|
||||
|
||||
@@ -98,7 +98,7 @@ Issues that do not meet these requirements will be closed and may need to be res
|
||||
<!-- Screenshot of the bug -->
|
||||
|
||||
## System Information
|
||||
- Dyad Version: ${debugInfo.dyadVersion}
|
||||
- MoreMinimore Version: ${debugInfo.dyadVersion}
|
||||
- Platform: ${debugInfo.platform}
|
||||
- Architecture: ${debugInfo.architecture}
|
||||
- Node Version: ${debugInfo.nodeVersion || "n/a"}
|
||||
@@ -348,7 +348,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
|
||||
<div className="border rounded-md p-3">
|
||||
<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">
|
||||
<p>Dyad Version: {chatLogsData.debugInfo.dyadVersion}</p>
|
||||
<p>MoreMinimore Version: {chatLogsData.debugInfo.dyadVersion}</p>
|
||||
<p>Platform: {chatLogsData.debugInfo.platform}</p>
|
||||
<p>Architecture: {chatLogsData.debugInfo.architecture}</p>
|
||||
<p>
|
||||
@@ -390,7 +390,7 @@ Pro User ID: ${userBudget?.redactedUserId || "n/a"}
|
||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Need help with Dyad?</DialogTitle>
|
||||
<DialogTitle>Need help with MoreMinimore?</DialogTitle>
|
||||
</DialogHeader>
|
||||
<DialogDescription className="">
|
||||
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"
|
||||
>
|
||||
<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)
|
||||
</Button>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -248,7 +248,7 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
|
||||
onSuccess: async (result) => {
|
||||
showSuccess(
|
||||
!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",
|
||||
);
|
||||
onClose();
|
||||
@@ -456,14 +456,14 @@ export function ImportAppDialog({ isOpen, onClose }: ImportAppDialogProps) {
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<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
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<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.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -266,8 +266,8 @@ export function ModelPicker() {
|
||||
{/* Primary providers as submenus */}
|
||||
{primaryProviders.map(([providerId, models]) => {
|
||||
models = models.filter((model) => {
|
||||
// Don't show free models if Dyad Pro is enabled because
|
||||
// we will use the paid models (in Dyad Pro backend) which
|
||||
// Don't show free models if MoreMinimore Pro is enabled because
|
||||
// we will use the paid models (in MoreMinimore Pro backend) which
|
||||
// don't have the free limitations.
|
||||
if (
|
||||
isDyadProEnabled(settings) &&
|
||||
@@ -280,7 +280,7 @@ export function ModelPicker() {
|
||||
const provider = providers?.find((p) => p.id === providerId);
|
||||
const providerDisplayName =
|
||||
provider?.id === "auto"
|
||||
? "Dyad Turbo"
|
||||
? "MoreMinimore Turbo"
|
||||
: (provider?.name ?? providerId);
|
||||
return (
|
||||
<DropdownMenuSub key={providerId}>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function ManageDyadProButton() {
|
||||
}}
|
||||
>
|
||||
<KeyRound aria-hidden="true" />
|
||||
Manage Dyad Pro subscription
|
||||
Manage MoreMinimore Pro subscription
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export function SetupDyadProButton() {
|
||||
}}
|
||||
>
|
||||
<KeyRound aria-hidden="true" />
|
||||
Already have Dyad Pro? Add your key
|
||||
Already have MoreMinimore Pro? Add your key
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -104,10 +104,10 @@ export function AiAccessBanner() {
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
Get Dyad Pro
|
||||
Get MoreMinimore Pro
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -175,10 +175,10 @@ export function SmartContextBanner() {
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
Get Dyad Pro
|
||||
Get MoreMinimore Pro
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -216,10 +216,10 @@ export function TurboBanner() {
|
||||
</div>
|
||||
<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"
|
||||
>
|
||||
Get Dyad Pro
|
||||
Get MoreMinimore Pro
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -75,14 +75,14 @@ export function ProModeSelector() {
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Configure Dyad Pro settings</TooltipContent>
|
||||
<TooltipContent>Configure MoreMinimore Pro settings</TooltipContent>
|
||||
</Tooltip>
|
||||
<PopoverContent className="w-80 border-primary/20">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1">
|
||||
<h4 className="font-medium flex items-center gap-1.5">
|
||||
<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>
|
||||
<div className="h-px bg-gradient-to-r from-primary/50 via-primary/20 to-transparent" />
|
||||
</div>
|
||||
@@ -110,8 +110,8 @@ export function ProModeSelector() {
|
||||
<div className="flex flex-col gap-5">
|
||||
<SelectorRow
|
||||
id="pro-enabled"
|
||||
label="Enable Dyad Pro"
|
||||
tooltip="Uses Dyad Pro AI credits for the main AI model and Pro modes."
|
||||
label="Enable MoreMinimore Pro"
|
||||
tooltip="Uses MoreMinimore Pro AI credits for the main AI model and Pro modes."
|
||||
isTogglable={hasProKey}
|
||||
settingEnabled={Boolean(settings?.enableDyadPro)}
|
||||
toggle={toggleProEnabled}
|
||||
@@ -119,7 +119,7 @@ export function ProModeSelector() {
|
||||
<SelectorRow
|
||||
id="web-search"
|
||||
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}
|
||||
settingEnabled={Boolean(settings?.enableProWebSearch)}
|
||||
toggle={toggleWebSearch}
|
||||
|
||||
@@ -34,11 +34,11 @@ export function ReleaseChannelSelector() {
|
||||
} else {
|
||||
toast("Using Beta release channel", {
|
||||
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: {
|
||||
label: "Restart Dyad",
|
||||
label: "Restart MoreMinimore",
|
||||
onClick: () => {
|
||||
IpcClient.getInstance().restartDyad();
|
||||
IpcClient.getInstance().restartMoreMinimore();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders";
|
||||
import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo";
|
||||
// @ts-ignore
|
||||
import logo from "../../assets/logo.svg";
|
||||
import { OnboardingBanner } from "./home/OnboardingBanner";
|
||||
// import { OnboardingBanner } from "./home/OnboardingBanner";
|
||||
import { showError } from "@/lib/toast";
|
||||
import { useSettings } from "@/hooks/useSettings";
|
||||
|
||||
@@ -114,12 +114,6 @@ export function SetupBanner() {
|
||||
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 = () => {
|
||||
posthog.capture("setup-flow:ai-provider-setup:other:click");
|
||||
@@ -178,12 +172,12 @@ export function SetupBanner() {
|
||||
return (
|
||||
<>
|
||||
<p className="text-xl font-medium text-zinc-700 dark:text-zinc-300 p-4">
|
||||
Setup Dyad
|
||||
Setup MoreMinimore
|
||||
</p>
|
||||
<OnboardingBanner
|
||||
{/* <OnboardingBanner
|
||||
isVisible={isOnboardingVisible}
|
||||
setIsVisible={setIsOnboardingVisible}
|
||||
/>
|
||||
/> */}
|
||||
<div className={bannerClasses}>
|
||||
<Accordion
|
||||
type="multiple"
|
||||
@@ -313,7 +307,7 @@ export function SetupBanner() {
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="px-4 pt-2 pb-4 bg-white dark:bg-zinc-900 border-t border-inherit">
|
||||
<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>
|
||||
<div className="flex gap-2">
|
||||
<SetupProviderCard
|
||||
@@ -341,19 +335,6 @@ export function SetupBanner() {
|
||||
/>
|
||||
</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
|
||||
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}
|
||||
@@ -446,7 +427,7 @@ function NodeInstallButton({
|
||||
case "finished-checking":
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
default:
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type SetupProviderVariant = "google" | "openrouter" | "dyad";
|
||||
type SetupProviderVariant = "google" | "openrouter";
|
||||
|
||||
export function SetupProviderCard({
|
||||
variant,
|
||||
@@ -94,15 +94,6 @@ function getVariantStyles(variant: SetupProviderVariant) {
|
||||
subtitleColor: "text-teal-600 dark:text-teal-400",
|
||||
chevronColor: "text-teal-600 dark:text-teal-400",
|
||||
} 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?
|
||||
</h4>
|
||||
<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">
|
||||
Note: this does not log your code or messages.
|
||||
</em>
|
||||
|
||||
@@ -182,7 +182,7 @@ function AppIcons({
|
||||
return (
|
||||
// When collapsed: only show the main menu
|
||||
<SidebarGroup className="pr-0">
|
||||
{/* <SidebarGroupLabel>Dyad</SidebarGroupLabel> */}
|
||||
{/* <SidebarGroupLabel>MoreMinimore</SidebarGroupLabel> */}
|
||||
|
||||
<SidebarGroupContent>
|
||||
<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"
|
||||
variant="primary"
|
||||
>
|
||||
Access with Dyad Pro
|
||||
Access with MoreMinimore Pro
|
||||
</ExternalLink>
|
||||
</span>{" "}
|
||||
or switch to another model.
|
||||
@@ -37,9 +37,9 @@ export function ChatErrorBox({
|
||||
// Important, this needs to come after the "free quota tier" check
|
||||
// because it also includes this URL in the error message
|
||||
//
|
||||
// Sometimes Dyad 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
|
||||
// already on the Dyad Pro plan.
|
||||
// Sometimes MoreMinimore Pro can return rate limit errors and we do not want to
|
||||
// show the upgrade to MoreMinimore Pro link in that case because they are
|
||||
// already on the MoreMinimore Pro plan.
|
||||
if (
|
||||
!isDyadProEnabled &&
|
||||
(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"
|
||||
variant="primary"
|
||||
>
|
||||
Upgrade to Dyad Pro
|
||||
Upgrade to MoreMinimore Pro
|
||||
</ExternalLink>
|
||||
|
||||
<ExternalLink href="https://dyad.sh/docs/help/ai-rate-limit">
|
||||
@@ -69,12 +69,12 @@ export function ChatErrorBox({
|
||||
return (
|
||||
<ChatInfoContainer onDismiss={onDismiss}>
|
||||
<span>
|
||||
Looks like you don't have a valid Dyad Pro key.{" "}
|
||||
Looks like you don't have a valid MoreMinimore Pro key.{" "}
|
||||
<ExternalLink
|
||||
href="https://dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=invalid-pro-key-error"
|
||||
variant="primary"
|
||||
>
|
||||
Upgrade to Dyad Pro
|
||||
Upgrade to MoreMinimore Pro
|
||||
</ExternalLink>{" "}
|
||||
today.
|
||||
</span>
|
||||
@@ -85,7 +85,7 @@ export function ChatErrorBox({
|
||||
return (
|
||||
<ChatInfoContainer onDismiss={onDismiss}>
|
||||
<span>
|
||||
You have used all of your Dyad AI credits this month.{" "}
|
||||
You have used all of your MoreMinimore AI credits this month.{" "}
|
||||
<ExternalLink
|
||||
href="https://academy.dyad.sh/subscription?utm_source=dyad-app&utm_medium=app&utm_campaign=exceeded-budget-error"
|
||||
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"
|
||||
variant="primary"
|
||||
>
|
||||
Upgrade to Dyad Pro
|
||||
Upgrade to MoreMinimore Pro
|
||||
</ExternalLink>
|
||||
)}
|
||||
<ExternalLink href="https://www.dyad.sh/docs/faq">
|
||||
|
||||
@@ -392,7 +392,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
|
||||
onChange={setInputValue}
|
||||
onSubmit={handleSubmit}
|
||||
onPaste={handlePaste}
|
||||
placeholder="Ask Dyad to build..."
|
||||
placeholder="Ask MoreMinimore to build..."
|
||||
excludeCurrentApp={true}
|
||||
disableSendButton={disableSendButton}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Message } from "@/ipc/ipc_types";
|
||||
import {
|
||||
DyadMarkdownParser,
|
||||
MoreMinimoreMarkdownParser,
|
||||
VanillaMarkdownParser,
|
||||
} from "./DyadMarkdownParser";
|
||||
} from "./MoreMinimoreMarkdownParser";
|
||||
import { motion } from "framer-motion";
|
||||
import { useStreamChat } from "@/hooks/useStreamChat";
|
||||
import {
|
||||
@@ -140,7 +140,7 @@ const ChatMessage = ({ message, isLastMessage }: ChatMessageProps) => {
|
||||
>
|
||||
{message.role === "assistant" ? (
|
||||
<>
|
||||
<DyadMarkdownParser content={message.content} />
|
||||
<MoreMinimoreMarkdownParser content={message.content} />
|
||||
{isLastMessage && isStreaming && (
|
||||
<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>
|
||||
|
||||
@@ -7,13 +7,13 @@ import { IpcClient } from "../../ipc/ipc_client";
|
||||
import { Package, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||
import { CodeHighlight } from "./CodeHighlight";
|
||||
|
||||
interface DyadAddDependencyProps {
|
||||
interface MoreMinimoreAddDependencyProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
packages?: string;
|
||||
}
|
||||
|
||||
export const DyadAddDependency: React.FC<DyadAddDependencyProps> = ({
|
||||
export const MoreMinimoreAddDependency: React.FC<MoreMinimoreAddDependencyProps> = ({
|
||||
children,
|
||||
node,
|
||||
}) => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useAtomValue } from "jotai";
|
||||
import { showError } from "@/lib/toast";
|
||||
import { useLoadApp } from "@/hooks/useLoadApp";
|
||||
|
||||
interface DyadAddIntegrationProps {
|
||||
interface MoreMinimoreAddIntegrationProps {
|
||||
node: {
|
||||
properties: {
|
||||
provider: string;
|
||||
@@ -15,7 +15,7 @@ interface DyadAddIntegrationProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadAddIntegration: React.FC<DyadAddIntegrationProps> = ({
|
||||
export const MoreMinimoreAddIntegration: React.FC<MoreMinimoreAddIntegrationProps> = ({
|
||||
node,
|
||||
children,
|
||||
}) => {
|
||||
|
||||
@@ -2,13 +2,13 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { FileCode } from "lucide-react";
|
||||
|
||||
interface DyadCodeSearchProps {
|
||||
interface MoreMinimoreCodeSearchProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
query?: string;
|
||||
}
|
||||
|
||||
export const DyadCodeSearch: React.FC<DyadCodeSearchProps> = ({
|
||||
export const MoreMinimoreCodeSearch: React.FC<MoreMinimoreCodeSearchProps> = ({
|
||||
children,
|
||||
node: _node,
|
||||
query: queryProp,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { ChevronDown, ChevronUp, FileCode, FileText } from "lucide-react";
|
||||
|
||||
interface DyadCodeSearchResultProps {
|
||||
interface MoreMinimoreCodeSearchResultProps {
|
||||
node?: any;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadCodeSearchResult: React.FC<DyadCodeSearchResultProps> = ({
|
||||
export const MoreMinimoreCodeSearchResult: React.FC<MoreMinimoreCodeSearchResultProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
|
||||
import { ChevronUp, ChevronDown, Code2, FileText } from "lucide-react";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
|
||||
interface DyadCodebaseContextProps {
|
||||
interface MoreMinimoreCodebaseContextProps {
|
||||
children: React.ReactNode;
|
||||
node?: {
|
||||
properties?: {
|
||||
@@ -12,7 +12,7 @@ interface DyadCodebaseContextProps {
|
||||
};
|
||||
}
|
||||
|
||||
export const DyadCodebaseContext: React.FC<DyadCodebaseContextProps> = ({
|
||||
export const MoreMinimoreCodebaseContext: React.FC<MoreMinimoreCodebaseContextProps> = ({
|
||||
node,
|
||||
}) => {
|
||||
const state = node?.properties?.state as CustomTagState;
|
||||
|
||||
@@ -2,13 +2,13 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
|
||||
interface DyadDeleteProps {
|
||||
interface MoreMinimoreDeleteProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export const DyadDelete: React.FC<DyadDeleteProps> = ({
|
||||
export const MoreMinimoreDelete: React.FC<MoreMinimoreDeleteProps> = ({
|
||||
children,
|
||||
node,
|
||||
path: pathProp,
|
||||
|
||||
@@ -11,14 +11,14 @@ import {
|
||||
import { CodeHighlight } from "./CodeHighlight";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
|
||||
interface DyadEditProps {
|
||||
interface MoreMinimoreEditProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
path?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const DyadEdit: React.FC<DyadEditProps> = ({
|
||||
export const MoreMinimoreEdit: React.FC<MoreMinimoreEditProps> = ({
|
||||
children,
|
||||
node,
|
||||
path: pathProp,
|
||||
|
||||
@@ -11,13 +11,13 @@ import {
|
||||
import { CodeHighlight } from "./CodeHighlight";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
|
||||
interface DyadExecuteSqlProps {
|
||||
interface MoreMinimoreExecuteSqlProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const DyadExecuteSql: React.FC<DyadExecuteSqlProps> = ({
|
||||
export const MoreMinimoreExecuteSql: React.FC<MoreMinimoreExecuteSqlProps> = ({
|
||||
children,
|
||||
node,
|
||||
description,
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import React, { useMemo } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
||||
import { DyadWrite } from "./DyadWrite";
|
||||
import { DyadRename } from "./DyadRename";
|
||||
import { DyadDelete } from "./DyadDelete";
|
||||
import { DyadAddDependency } from "./DyadAddDependency";
|
||||
import { DyadExecuteSql } from "./DyadExecuteSql";
|
||||
import { DyadAddIntegration } from "./DyadAddIntegration";
|
||||
import { DyadEdit } from "./DyadEdit";
|
||||
import { DyadSearchReplace } from "./DyadSearchReplace";
|
||||
import { DyadCodebaseContext } from "./DyadCodebaseContext";
|
||||
import { DyadThink } from "./DyadThink";
|
||||
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 { DyadOutput } from "./DyadOutput";
|
||||
import { MoreMinimoreOutput } from "./MoreMinimoreOutput";
|
||||
import { DyadProblemSummary } from "./DyadProblemSummary";
|
||||
import { IpcClient } from "@/ipc/ipc_client";
|
||||
import { DyadMcpToolCall } from "./DyadMcpToolCall";
|
||||
import { DyadMcpToolResult } from "./DyadMcpToolResult";
|
||||
import { DyadWebSearchResult } from "./DyadWebSearchResult";
|
||||
import { DyadWebSearch } from "./DyadWebSearch";
|
||||
import { DyadWebCrawl } from "./DyadWebCrawl";
|
||||
import { DyadCodeSearchResult } from "./DyadCodeSearchResult";
|
||||
import { DyadCodeSearch } from "./DyadCodeSearch";
|
||||
import { DyadRead } from "./DyadRead";
|
||||
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 DyadMarkdownParserProps {
|
||||
interface MoreMinimoreMarkdownParserProps {
|
||||
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,
|
||||
}) => {
|
||||
const chatId = useAtomValue(selectedChatIdAtom);
|
||||
@@ -346,7 +346,7 @@ function renderCustomTag(
|
||||
switch (tag) {
|
||||
case "dyad-read":
|
||||
return (
|
||||
<DyadRead
|
||||
<MoreMinimoreRead
|
||||
node={{
|
||||
properties: {
|
||||
path: attributes.path || "",
|
||||
@@ -354,51 +354,51 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadRead>
|
||||
</MoreMinimoreRead>
|
||||
);
|
||||
case "dyad-web-search":
|
||||
return (
|
||||
<DyadWebSearch
|
||||
<MoreMinimoreWebSearch
|
||||
node={{
|
||||
properties: {},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadWebSearch>
|
||||
</MoreMinimoreWebSearch>
|
||||
);
|
||||
case "dyad-web-crawl":
|
||||
return (
|
||||
<DyadWebCrawl
|
||||
<MoreMinimoreWebCrawl
|
||||
node={{
|
||||
properties: {},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadWebCrawl>
|
||||
</MoreMinimoreWebCrawl>
|
||||
);
|
||||
case "dyad-code-search":
|
||||
return (
|
||||
<DyadCodeSearch
|
||||
<MoreMinimoreCodeSearch
|
||||
node={{
|
||||
properties: {},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadCodeSearch>
|
||||
</MoreMinimoreCodeSearch>
|
||||
);
|
||||
case "dyad-code-search-result":
|
||||
return (
|
||||
<DyadCodeSearchResult
|
||||
<MoreMinimoreCodeSearchResult
|
||||
node={{
|
||||
properties: {},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadCodeSearchResult>
|
||||
</MoreMinimoreCodeSearchResult>
|
||||
);
|
||||
case "dyad-web-search-result":
|
||||
return (
|
||||
<DyadWebSearchResult
|
||||
<MoreMinimoreWebSearchResult
|
||||
node={{
|
||||
properties: {
|
||||
state: getState({ isStreaming, inProgress }),
|
||||
@@ -406,11 +406,11 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadWebSearchResult>
|
||||
</MoreMinimoreWebSearchResult>
|
||||
);
|
||||
case "think":
|
||||
return (
|
||||
<DyadThink
|
||||
<MoreMinimoreThink
|
||||
node={{
|
||||
properties: {
|
||||
state: getState({ isStreaming, inProgress }),
|
||||
@@ -418,11 +418,11 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadThink>
|
||||
</MoreMinimoreThink>
|
||||
);
|
||||
case "dyad-write":
|
||||
return (
|
||||
<DyadWrite
|
||||
<MoreMinimoreWrite
|
||||
node={{
|
||||
properties: {
|
||||
path: attributes.path || "",
|
||||
@@ -432,12 +432,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadWrite>
|
||||
</MoreMinimoreWrite>
|
||||
);
|
||||
|
||||
case "dyad-rename":
|
||||
return (
|
||||
<DyadRename
|
||||
<MoreMinimoreRename
|
||||
node={{
|
||||
properties: {
|
||||
from: attributes.from || "",
|
||||
@@ -446,12 +446,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadRename>
|
||||
</MoreMinimoreRename>
|
||||
);
|
||||
|
||||
case "dyad-delete":
|
||||
return (
|
||||
<DyadDelete
|
||||
<MoreMinimoreDelete
|
||||
node={{
|
||||
properties: {
|
||||
path: attributes.path || "",
|
||||
@@ -459,12 +459,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadDelete>
|
||||
</MoreMinimoreDelete>
|
||||
);
|
||||
|
||||
case "dyad-add-dependency":
|
||||
return (
|
||||
<DyadAddDependency
|
||||
<MoreMinimoreAddDependency
|
||||
node={{
|
||||
properties: {
|
||||
packages: attributes.packages || "",
|
||||
@@ -472,12 +472,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadAddDependency>
|
||||
</MoreMinimoreAddDependency>
|
||||
);
|
||||
|
||||
case "dyad-execute-sql":
|
||||
return (
|
||||
<DyadExecuteSql
|
||||
<MoreMinimoreExecuteSql
|
||||
node={{
|
||||
properties: {
|
||||
state: getState({ isStreaming, inProgress }),
|
||||
@@ -486,12 +486,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadExecuteSql>
|
||||
</MoreMinimoreExecuteSql>
|
||||
);
|
||||
|
||||
case "dyad-add-integration":
|
||||
return (
|
||||
<DyadAddIntegration
|
||||
<MoreMinimoreAddIntegration
|
||||
node={{
|
||||
properties: {
|
||||
provider: attributes.provider || "",
|
||||
@@ -499,12 +499,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadAddIntegration>
|
||||
</MoreMinimoreAddIntegration>
|
||||
);
|
||||
|
||||
case "dyad-edit":
|
||||
return (
|
||||
<DyadEdit
|
||||
<MoreMinimoreEdit
|
||||
node={{
|
||||
properties: {
|
||||
path: attributes.path || "",
|
||||
@@ -514,12 +514,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadEdit>
|
||||
</MoreMinimoreEdit>
|
||||
);
|
||||
|
||||
case "dyad-search-replace":
|
||||
return (
|
||||
<DyadSearchReplace
|
||||
<MoreMinimoreSearchReplace
|
||||
node={{
|
||||
properties: {
|
||||
path: attributes.path || "",
|
||||
@@ -529,12 +529,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadSearchReplace>
|
||||
</MoreMinimoreSearchReplace>
|
||||
);
|
||||
|
||||
case "dyad-codebase-context":
|
||||
return (
|
||||
<DyadCodebaseContext
|
||||
<MoreMinimoreCodebaseContext
|
||||
node={{
|
||||
properties: {
|
||||
files: attributes.files || "",
|
||||
@@ -543,12 +543,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadCodebaseContext>
|
||||
</MoreMinimoreCodebaseContext>
|
||||
);
|
||||
|
||||
case "dyad-mcp-tool-call":
|
||||
return (
|
||||
<DyadMcpToolCall
|
||||
<MoreMinimoreMcpToolCall
|
||||
node={{
|
||||
properties: {
|
||||
serverName: attributes.server || "",
|
||||
@@ -557,12 +557,12 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadMcpToolCall>
|
||||
</MoreMinimoreMcpToolCall>
|
||||
);
|
||||
|
||||
case "dyad-mcp-tool-result":
|
||||
return (
|
||||
<DyadMcpToolResult
|
||||
<MoreMinimoreMcpToolResult
|
||||
node={{
|
||||
properties: {
|
||||
serverName: attributes.server || "",
|
||||
@@ -571,17 +571,17 @@ function renderCustomTag(
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</DyadMcpToolResult>
|
||||
</MoreMinimoreMcpToolResult>
|
||||
);
|
||||
|
||||
case "dyad-output":
|
||||
return (
|
||||
<DyadOutput
|
||||
<MoreMinimoreOutput
|
||||
type={attributes.type as "warning" | "error"}
|
||||
message={attributes.message}
|
||||
>
|
||||
{content}
|
||||
</DyadOutput>
|
||||
</MoreMinimoreOutput>
|
||||
);
|
||||
|
||||
case "dyad-problem-report":
|
||||
|
||||
@@ -2,12 +2,12 @@ import React, { useMemo, useState } from "react";
|
||||
import { Wrench, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||
import { CodeHighlight } from "./CodeHighlight";
|
||||
|
||||
interface DyadMcpToolCallProps {
|
||||
interface MoreMinimoreMcpToolCallProps {
|
||||
node?: any;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadMcpToolCall: React.FC<DyadMcpToolCallProps> = ({
|
||||
export const MoreMinimoreMcpToolCall: React.FC<MoreMinimoreMcpToolCallProps> = ({
|
||||
node,
|
||||
children,
|
||||
}) => {
|
||||
|
||||
@@ -2,12 +2,12 @@ import React, { useMemo, useState } from "react";
|
||||
import { CheckCircle, ChevronsUpDown, ChevronsDownUp } from "lucide-react";
|
||||
import { CodeHighlight } from "./CodeHighlight";
|
||||
|
||||
interface DyadMcpToolResultProps {
|
||||
interface MoreMinimoreMcpToolResultProps {
|
||||
node?: any;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadMcpToolResult: React.FC<DyadMcpToolResultProps> = ({
|
||||
export const MoreMinimoreMcpToolResult: React.FC<MoreMinimoreMcpToolResultProps> = ({
|
||||
node,
|
||||
children,
|
||||
}) => {
|
||||
|
||||
@@ -10,13 +10,13 @@ import { useAtomValue } from "jotai";
|
||||
import { selectedChatIdAtom } from "@/atoms/chatAtoms";
|
||||
import { useStreamChat } from "@/hooks/useStreamChat";
|
||||
import { CopyErrorMessage } from "@/components/CopyErrorMessage";
|
||||
interface DyadOutputProps {
|
||||
interface MoreMinimoreOutputProps {
|
||||
type: "error" | "warning";
|
||||
message?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadOutput: React.FC<DyadOutputProps> = ({
|
||||
export const MoreMinimoreOutput: React.FC<MoreMinimoreOutputProps> = ({
|
||||
type,
|
||||
message,
|
||||
children,
|
||||
|
||||
@@ -2,13 +2,13 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { FileText } from "lucide-react";
|
||||
|
||||
interface DyadReadProps {
|
||||
interface MoreMinimoreReadProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export const DyadRead: React.FC<DyadReadProps> = ({
|
||||
export const MoreMinimoreRead: React.FC<MoreMinimoreReadProps> = ({
|
||||
children,
|
||||
node,
|
||||
path: pathProp,
|
||||
|
||||
@@ -2,14 +2,14 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { FileEdit } from "lucide-react";
|
||||
|
||||
interface DyadRenameProps {
|
||||
interface MoreMinimoreRenameProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
from?: string;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
export const DyadRename: React.FC<DyadRenameProps> = ({
|
||||
export const MoreMinimoreRename: React.FC<MoreMinimoreRenameProps> = ({
|
||||
children,
|
||||
node,
|
||||
from: fromProp,
|
||||
|
||||
@@ -13,14 +13,14 @@ import { CodeHighlight } from "./CodeHighlight";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
import { parseSearchReplaceBlocks } from "@/pro/shared/search_replace_parser";
|
||||
|
||||
interface DyadSearchReplaceProps {
|
||||
interface MoreMinimoreSearchReplaceProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
path?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const DyadSearchReplace: React.FC<DyadSearchReplaceProps> = ({
|
||||
export const MoreMinimoreSearchReplace: React.FC<MoreMinimoreSearchReplaceProps> = ({
|
||||
children,
|
||||
node,
|
||||
path: pathProp,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Brain, ChevronDown, ChevronUp, Loader } from "lucide-react";
|
||||
import { VanillaMarkdownParser } from "./DyadMarkdownParser";
|
||||
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
import { DyadTokenSavings } from "./DyadTokenSavings";
|
||||
import { MoreMinimoreTokenSavings } from "./MoreMinimoreTokenSavings";
|
||||
|
||||
interface DyadThinkProps {
|
||||
interface AIThinkProps {
|
||||
node?: any;
|
||||
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 inProgress = state === "pending";
|
||||
const [isExpanded, setIsExpanded] = useState(inProgress);
|
||||
@@ -29,12 +29,12 @@ export const DyadThink: React.FC<DyadThinkProps> = ({ children, node }) => {
|
||||
}
|
||||
}, [inProgress]);
|
||||
|
||||
// If it's token savings format, render DyadTokenSavings component
|
||||
// If it's token savings format, render MoreMinimoreTokenSavings component
|
||||
if (tokenSavingsMatch) {
|
||||
const originalTokens = parseFloat(tokenSavingsMatch[1]);
|
||||
const smartContextTokens = parseFloat(tokenSavingsMatch[2]);
|
||||
return (
|
||||
<DyadTokenSavings
|
||||
<MoreMinimoreTokenSavings
|
||||
originalTokens={originalTokens}
|
||||
smartContextTokens={smartContextTokens}
|
||||
/>
|
||||
|
||||
@@ -2,12 +2,12 @@ import React from "react";
|
||||
import { Zap } from "lucide-react";
|
||||
import { Tooltip, TooltipTrigger, TooltipContent } from "../ui/tooltip";
|
||||
|
||||
interface DyadTokenSavingsProps {
|
||||
interface TokenSavingsProps {
|
||||
originalTokens: number;
|
||||
smartContextTokens: number;
|
||||
}
|
||||
|
||||
export const DyadTokenSavings: React.FC<DyadTokenSavingsProps> = ({
|
||||
export const TokenSavings: React.FC<TokenSavingsProps> = ({
|
||||
originalTokens,
|
||||
smartContextTokens,
|
||||
}) => {
|
||||
|
||||
@@ -2,12 +2,12 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { ScanQrCode } from "lucide-react";
|
||||
|
||||
interface DyadWebCrawlProps {
|
||||
interface MoreMinimoreWebCrawlProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
}
|
||||
|
||||
export const DyadWebCrawl: React.FC<DyadWebCrawlProps> = ({
|
||||
export const MoreMinimoreWebCrawl: React.FC<MoreMinimoreWebCrawlProps> = ({
|
||||
children,
|
||||
node: _node,
|
||||
}) => {
|
||||
|
||||
@@ -2,13 +2,13 @@ import type React from "react";
|
||||
import type { ReactNode } from "react";
|
||||
import { Globe } from "lucide-react";
|
||||
|
||||
interface DyadWebSearchProps {
|
||||
interface MoreMinimoreWebSearchProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
query?: string;
|
||||
}
|
||||
|
||||
export const DyadWebSearch: React.FC<DyadWebSearchProps> = ({
|
||||
export const MoreMinimoreWebSearch: React.FC<MoreMinimoreWebSearchProps> = ({
|
||||
children,
|
||||
node: _node,
|
||||
query: queryProp,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ChevronDown, ChevronUp, Globe, Loader } from "lucide-react";
|
||||
import { VanillaMarkdownParser } from "./DyadMarkdownParser";
|
||||
import { VanillaMarkdownParser } from "./MoreMinimoreMarkdownParser";
|
||||
import { CustomTagState } from "./stateTypes";
|
||||
|
||||
interface DyadWebSearchResultProps {
|
||||
interface MoreMinimoreWebSearchResultProps {
|
||||
node?: any;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DyadWebSearchResult: React.FC<DyadWebSearchResultProps> = ({
|
||||
export const MoreMinimoreWebSearchResult: React.FC<MoreMinimoreWebSearchResultProps> = ({
|
||||
children,
|
||||
node,
|
||||
}) => {
|
||||
|
||||
@@ -16,14 +16,14 @@ import { FileEditor } from "../preview_panel/FileEditor";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { selectedAppIdAtom } from "@/atoms/appAtoms";
|
||||
|
||||
interface DyadWriteProps {
|
||||
interface MoreMinimoreWriteProps {
|
||||
children?: ReactNode;
|
||||
node?: any;
|
||||
path?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const DyadWrite: React.FC<DyadWriteProps> = ({
|
||||
export const MoreMinimoreWrite: React.FC<MoreMinimoreWriteProps> = ({
|
||||
children,
|
||||
node,
|
||||
path: pathProp,
|
||||
|
||||
@@ -32,7 +32,7 @@ export function HomeChatInput({
|
||||
"an information page...",
|
||||
"a landing page...",
|
||||
]);
|
||||
const placeholder = `Ask Dyad to build ${typingText ?? ""}`;
|
||||
const placeholder = `Ask MoreMinimore to build ${typingText ?? ""}`;
|
||||
|
||||
// Use the attachments hook
|
||||
const {
|
||||
|
||||
@@ -250,7 +250,7 @@ export function LexicalChatInput({
|
||||
onSubmit,
|
||||
onPaste,
|
||||
excludeCurrentApp,
|
||||
placeholder = "Ask Dyad to build...",
|
||||
placeholder = "Ask MoreMinimore to build...",
|
||||
disabled = false,
|
||||
disableSendButton,
|
||||
}: 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 = {
|
||||
spans: [
|
||||
{ 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." },
|
||||
],
|
||||
};
|
||||
@@ -66,7 +66,7 @@ export const SMART_CONTEXT_PROMO_MESSAGE: MessageConfig = {
|
||||
{ type: "text", content: "Save up to 5x on AI costs with " },
|
||||
{
|
||||
type: "link",
|
||||
content: "Dyad Pro's Smart Context",
|
||||
content: "MoreMinimore Pro's Smart Context",
|
||||
url: "https://dyad.sh/pro#ai",
|
||||
},
|
||||
],
|
||||
@@ -90,7 +90,7 @@ export const REDDIT_TIP: MessageConfig = {
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
content: "Dyad subreddit",
|
||||
content: "MoreMinimore subreddit",
|
||||
url: "https://www.reddit.com/r/dyadbuilders/",
|
||||
},
|
||||
],
|
||||
@@ -124,7 +124,7 @@ export const BUILD_A_BIBLE_APP_TIP: MessageConfig = {
|
||||
},
|
||||
{
|
||||
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 = {
|
||||
spans: [
|
||||
{
|
||||
type: "text",
|
||||
content: "Like Dyad? Star it on ",
|
||||
content: "Like MoreMinimore? Star it on ",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
|
||||
@@ -145,7 +145,7 @@ export function TokenBar({ chatId }: TokenBarProps) {
|
||||
}
|
||||
className="text-blue-500 dark:text-blue-400 cursor-pointer hover:underline"
|
||||
>
|
||||
Dyad Pro's Smart Context
|
||||
MoreMinimore Pro's Smart Context
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -29,7 +29,7 @@ export const OnboardingBanner = ({
|
||||
<div className="relative p-2">
|
||||
<img
|
||||
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"
|
||||
/>
|
||||
<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="text-foreground">
|
||||
<p className="font-semibold text-base">
|
||||
Get started with Dyad in 3 minutes
|
||||
Get started with MoreMinimore in 3 minutes
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Start building your app for free
|
||||
|
||||
@@ -36,7 +36,7 @@ export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => {
|
||||
</h2>
|
||||
<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
|
||||
with Dyad Pro.
|
||||
with MoreMinimore Pro.
|
||||
</p>
|
||||
|
||||
{/* Get Pro Button */}
|
||||
@@ -45,7 +45,7 @@ export const AnnotatorOnlyForPro = ({ onGoBack }: AnnotatorOnlyForProProps) => {
|
||||
size="lg"
|
||||
className="px-8 shadow-md hover:shadow-lg transition-all"
|
||||
>
|
||||
Get Dyad Pro
|
||||
Get MoreMinimore Pro
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,6 @@ import { useShortcut } from "@/hooks/useShortcut";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { normalizePath } from "../../../shared/normalizePath";
|
||||
import { showError } from "@/lib/toast";
|
||||
import { AnnotatorOnlyForPro } from "./AnnotatorOnlyForPro";
|
||||
import { useAttachments } from "@/hooks/useAttachments";
|
||||
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
|
||||
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" */}
|
||||
{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">
|
||||
Internal Dyad error
|
||||
Internal MoreMinimore error
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -143,7 +142,7 @@ const ErrorBanner = ({ error, onDismiss, onAIFix }: ErrorBannerProps) => {
|
||||
{isDockerError
|
||||
? "Make sure Docker Desktop is running and try restarting the 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."}
|
||||
</span>
|
||||
</div>
|
||||
@@ -985,17 +984,11 @@ export const PreviewIframe = ({ loading }: { loading: boolean }) => {
|
||||
: { width: `${deviceWidthConfig[deviceMode]}px` }
|
||||
}
|
||||
>
|
||||
{userBudget ? (
|
||||
<Annotator
|
||||
screenshotUrl={screenshotDataUrl}
|
||||
onSubmit={addAttachments}
|
||||
handleAnnotatorClick={handleAnnotatorClick}
|
||||
/>
|
||||
) : (
|
||||
<AnnotatorOnlyForPro
|
||||
onGoBack={() => setAnnotatorMode(false)}
|
||||
/>
|
||||
)}
|
||||
<Annotator
|
||||
screenshotUrl={screenshotDataUrl}
|
||||
onSubmit={addAttachments}
|
||||
handleAnnotatorClick={handleAnnotatorClick}
|
||||
/>
|
||||
</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 type { SecurityFinding, SecurityReviewResult } from "@/ipc/ipc_types";
|
||||
import { useState, useEffect } from "react";
|
||||
import { VanillaMarkdownParser } from "@/components/chat/DyadMarkdownParser";
|
||||
import { VanillaMarkdownParser } from "@/components/chat/MoreMinimoreMarkdownParser";
|
||||
import { showSuccess, showWarning } from "@/lib/toast";
|
||||
import { useLoadAppFile } from "@/hooks/useLoadAppFile";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
@@ -33,7 +33,7 @@ interface ApiKeyConfigurationProps {
|
||||
onApiKeyInputChange: (value: string) => void;
|
||||
onSaveKey: (value: string) => Promise<void>;
|
||||
onDeleteKey: () => Promise<void>;
|
||||
isDyad: boolean;
|
||||
isMoreMinimore: boolean;
|
||||
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export function ApiKeyConfiguration({
|
||||
onApiKeyInputChange,
|
||||
onSaveKey,
|
||||
onDeleteKey,
|
||||
isDyad,
|
||||
isMoreMinimore,
|
||||
updateSettings,
|
||||
}: ApiKeyConfigurationProps) {
|
||||
// Special handling for Azure OpenAI which requires environment variables
|
||||
@@ -86,7 +86,7 @@ export function ApiKeyConfiguration({
|
||||
if (isValidUserKey || !hasEnvKey) {
|
||||
defaultAccordionValue.push("settings-key");
|
||||
}
|
||||
if (!isDyad && hasEnvKey) {
|
||||
if (!isMoreMinimore && hasEnvKey) {
|
||||
defaultAccordionValue.push("env-key");
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ export function ApiKeyConfiguration({
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
|
||||
{!isDyad && envVarName && (
|
||||
{!isMoreMinimore && envVarName && (
|
||||
<AccordionItem
|
||||
value="env-key"
|
||||
className="border rounded-lg px-4 bg-(--background-lightest)"
|
||||
|
||||
@@ -101,7 +101,7 @@ export function AzureConfiguration({
|
||||
variant: "default" as const,
|
||||
title: "Azure OpenAI Configured",
|
||||
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,
|
||||
titleClassName: "",
|
||||
descriptionClassName: "",
|
||||
@@ -259,12 +259,12 @@ export function AzureConfiguration({
|
||||
<div className="text-sm text-muted-foreground space-y-2">
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Values saved in Settings take precedence over environment
|
||||
variables. Restart Dyad after changing environment variables.
|
||||
variables. Restart MoreMinimore after changing environment variables.
|
||||
</p>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
|
||||
@@ -22,21 +22,21 @@ interface ProviderSettingsHeaderProps {
|
||||
isLoading: boolean;
|
||||
hasFreeTier?: boolean;
|
||||
providerWebsiteUrl?: string;
|
||||
isDyad: boolean;
|
||||
isMoreMinimore: boolean;
|
||||
onBackClick: () => void;
|
||||
}
|
||||
|
||||
function getKeyButtonText({
|
||||
isConfigured,
|
||||
isDyad,
|
||||
isMoreMinimore,
|
||||
}: {
|
||||
isConfigured: boolean;
|
||||
isDyad: boolean;
|
||||
isMoreMinimore: boolean;
|
||||
}) {
|
||||
if (isDyad) {
|
||||
if (isMoreMinimore) {
|
||||
return isConfigured
|
||||
? "Manage Dyad Pro Subscription"
|
||||
: "Setup Dyad Pro Subscription";
|
||||
? "Manage MoreMinimore Pro Subscription"
|
||||
: "Setup MoreMinimore Pro Subscription";
|
||||
}
|
||||
return isConfigured ? "Manage API Keys" : "Setup API Key";
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export function ProviderSettingsHeader({
|
||||
isLoading,
|
||||
hasFreeTier,
|
||||
providerWebsiteUrl,
|
||||
isDyad,
|
||||
isMoreMinimore,
|
||||
onBackClick,
|
||||
}: ProviderSettingsHeaderProps) {
|
||||
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"
|
||||
>
|
||||
<KeyRound className="mr-2 h-4 w-4" />
|
||||
{getKeyButtonText({ isConfigured, isDyad })}
|
||||
{getKeyButtonText({ isConfigured, isMoreMinimore })}
|
||||
<ExternalLink className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -55,22 +55,22 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
const supportsCustomModels =
|
||||
providerData?.type === "custom" || providerData?.type === "cloud";
|
||||
|
||||
const isDyad = provider === "auto";
|
||||
const isMoreMinimore = provider === "auto";
|
||||
|
||||
const [apiKeyInput, setApiKeyInput] = useState("");
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
// Use fetched data (or defaults for Dyad)
|
||||
const providerDisplayName = isDyad
|
||||
? "Dyad"
|
||||
// Use fetched data (or defaults for MoreMinimore)
|
||||
const providerDisplayName = isMoreMinimore
|
||||
? "MoreMinimore"
|
||||
: (providerData?.name ?? "Unknown Provider");
|
||||
const providerWebsiteUrl = isDyad
|
||||
const providerWebsiteUrl = isMoreMinimore
|
||||
? "https://academy.dyad.sh/settings"
|
||||
: providerData?.websiteUrl;
|
||||
const hasFreeTier = isDyad ? false : providerData?.hasFreeTier;
|
||||
const envVarName = isDyad ? undefined : providerData?.envVarName;
|
||||
const hasFreeTier = isMoreMinimore ? false : providerData?.hasFreeTier;
|
||||
const envVarName = isMoreMinimore ? undefined : providerData?.envVarName;
|
||||
|
||||
// Use provider ID (which is the 'provider' prop)
|
||||
const userApiKey = settings?.providerSettings?.[provider]?.apiKey?.value;
|
||||
@@ -137,7 +137,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
},
|
||||
},
|
||||
};
|
||||
if (isDyad) {
|
||||
if (isMoreMinimore) {
|
||||
settingsUpdate.enableDyadPro = true;
|
||||
}
|
||||
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) => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
@@ -182,7 +182,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
enableDyadPro: enabled,
|
||||
});
|
||||
} catch (error: any) {
|
||||
showError(`Error toggling Dyad Pro: ${error}`);
|
||||
showError(`Error toggling MoreMinimore Pro: ${error}`);
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -241,7 +241,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
}
|
||||
|
||||
// Handle case where provider is not found (e.g., invalid ID in URL)
|
||||
if (!providerData && !isDyad) {
|
||||
if (!providerData && !isMoreMinimore) {
|
||||
return (
|
||||
<div className="min-h-screen px-8 py-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
@@ -278,7 +278,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
isLoading={settingsLoading}
|
||||
hasFreeTier={hasFreeTier}
|
||||
providerWebsiteUrl={providerWebsiteUrl}
|
||||
isDyad={isDyad}
|
||||
isMoreMinimore={isMoreMinimore}
|
||||
onBackClick={() => router.history.back()}
|
||||
/>
|
||||
|
||||
@@ -306,17 +306,17 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
onApiKeyInputChange={setApiKeyInput}
|
||||
onSaveKey={handleSaveKey}
|
||||
onDeleteKey={handleDeleteKey}
|
||||
isDyad={isDyad}
|
||||
isMoreMinimore={isMoreMinimore}
|
||||
updateSettings={updateSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isDyad && !settingsLoading && (
|
||||
{isMoreMinimore && !settingsLoading && (
|
||||
<div className="mt-6 flex items-center justify-between p-4 bg-(--background-lightest) rounded-lg border">
|
||||
<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">
|
||||
Toggle to enable Dyad Pro
|
||||
Toggle to enable MoreMinimore Pro
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
|
||||
@@ -830,7 +830,7 @@ This conversation includes one or more image attachments. When the user uploads
|
||||
appPath,
|
||||
});
|
||||
}
|
||||
const smartContextMode: SmartContextMode = isDeepContextEnabled
|
||||
const smartContextMode: SmartContextMode = true
|
||||
? "deep"
|
||||
: "balanced";
|
||||
// Build provider options with correct Google/Vertex thinking config gating
|
||||
|
||||
@@ -280,6 +280,10 @@ export class IpcClient {
|
||||
await this.ipcRenderer.invoke("restart-dyad");
|
||||
}
|
||||
|
||||
public async restartMoreMinimore(): Promise<void> {
|
||||
await this.ipcRenderer.invoke("restart-dyad");
|
||||
}
|
||||
|
||||
public async reloadEnvPath(): Promise<void> {
|
||||
await this.ipcRenderer.invoke("reload-env-path");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ipcMain } from "electron";
|
||||
import { registerAppHandlers } from "./handlers/app_handlers";
|
||||
import { registerChatHandlers } from "./handlers/chat_handlers";
|
||||
import { registerChatStreamHandlers } from "./handlers/chat_stream_handlers";
|
||||
@@ -48,6 +49,15 @@ export function registerIpcHandlers() {
|
||||
registerProblemsHandlers();
|
||||
registerProposalHandlers();
|
||||
registerDebugHandlers();
|
||||
|
||||
// Add missing user budget handler
|
||||
ipcMain.handle("get-user-budget", async () => {
|
||||
return {
|
||||
totalCredits: 1000,
|
||||
usedCredits: 0,
|
||||
resetDate: new Date().toISOString()
|
||||
};
|
||||
});
|
||||
registerSupabaseHandlers();
|
||||
registerNeonHandlers();
|
||||
registerLocalModelHandlers();
|
||||
|
||||
@@ -530,9 +530,9 @@ export const CLOUD_PROVIDERS: Record<
|
||||
gatewayPrefix: "openrouter/",
|
||||
},
|
||||
auto: {
|
||||
displayName: "Dyad",
|
||||
websiteUrl: "https://academy.dyad.sh/settings",
|
||||
gatewayPrefix: "dyad/",
|
||||
displayName: "MoreMinimore",
|
||||
websiteUrl: "https://moreminimore.com/settings",
|
||||
gatewayPrefix: "moreminimore/",
|
||||
},
|
||||
azure: {
|
||||
displayName: "Azure OpenAI",
|
||||
|
||||
@@ -19,7 +19,7 @@ import log from "electron-log";
|
||||
import { FREE_OPENROUTER_MODEL_NAMES } from "../shared/language_model_constants";
|
||||
import { getLanguageModelProviders } from "../shared/language_model_helpers";
|
||||
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 { 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
|
||||
if (process.defaultApp) {
|
||||
if (process.argv.length >= 2) {
|
||||
app.setAsDefaultProtocolClient("dyad", process.execPath, [
|
||||
app.setAsDefaultProtocolClient("moreminimore", process.execPath, [
|
||||
path.resolve(process.argv[1]),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
app.setAsDefaultProtocolClient("dyad");
|
||||
app.setAsDefaultProtocolClient("moreminimore");
|
||||
}
|
||||
|
||||
export async function onReady() {
|
||||
@@ -306,10 +306,10 @@ function handleDeepLinkReturn(url: string) {
|
||||
"hostname",
|
||||
parsed.hostname,
|
||||
);
|
||||
if (parsed.protocol !== "dyad:") {
|
||||
if (parsed.protocol !== "moreminimore:") {
|
||||
dialog.showErrorBox(
|
||||
"Invalid Protocol",
|
||||
`Expected dyad://, got ${parsed.protocol}. Full URL: ${url}`,
|
||||
`Expected moreminimore://, got ${parsed.protocol}. Full URL: ${url}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import { ForceCloseDialog } from "@/components/ForceCloseDialog";
|
||||
import type { FileAttachment } from "@/ipc/ipc_types";
|
||||
import { NEON_TEMPLATE_IDS } from "@/shared/templates";
|
||||
import { neonTemplateHook } from "@/client_logic/template_hook";
|
||||
import { ProBanner } from "@/components/ProBanner";
|
||||
// import { ProBanner } from "@/components/ProBanner";
|
||||
|
||||
// Adding an export for attachments
|
||||
export interface HomeSubmitOptions {
|
||||
@@ -268,7 +268,6 @@ export default function HomePage() {
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<ProBanner />
|
||||
</div>
|
||||
<PrivacyBanner />
|
||||
|
||||
|
||||
@@ -53,4 +53,14 @@ export const localTemplatesData: Template[] = [
|
||||
isExperimental: 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