Skip to content

App Panel, Installed Apps, App Details #1608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e909713
Apps panel base created
thughey Mar 12, 2025
1c47e45
Detail Panel base added with Open State
thughey Mar 12, 2025
7ea8b3c
Updated panels to handle appended side panels
thughey Mar 12, 2025
29af009
Updated minor styling
thughey Mar 13, 2025
0c2ded9
Updated styling & Organized components
thughey Mar 13, 2025
31b0d6e
Updated tools accordion styles
thughey Mar 13, 2025
9d4c69d
Updated Styling
thughey Mar 13, 2025
c2121eb
Styling updates
thughey Mar 13, 2025
e06a4e1
Updated spacing and fonts
thughey Mar 13, 2025
8c46ebd
Fixed panel width problems
thughey Mar 13, 2025
485b4e0
Consolidated the AppCard & FeaturedAppCard
thughey Mar 13, 2025
d469849
Installed tab initial base - needs styling
thughey Mar 13, 2025
cb382b1
AppCard Hover/Active effects
thughey Mar 14, 2025
890847c
Installed apps - base card styled
thughey Mar 14, 2025
dbc5686
Updated InstalledCard error states
thughey Mar 14, 2025
32eb82a
Updated settings style
thughey Mar 14, 2025
a6eb941
Updated InstallCard Error
thughey Mar 14, 2025
c2be8b6
Updated status indicators
thughey Mar 15, 2025
b69cf8e
Detail Panel installed error bar
thughey Mar 15, 2025
c303106
Authenticate by link added
thughey Mar 15, 2025
4232a13
Updated button styles
thughey Mar 15, 2025
812a2a9
Added various states
thughey Mar 15, 2025
dc27455
Updated hidden toolbar
thughey Mar 15, 2025
faf0c54
Merge from main
Kitenite Mar 25, 2025
f3f8ce9
Clean up
Kitenite Mar 25, 2025
5a0136e
Add missing translations
Kitenite Mar 25, 2025
52ab6a7
Clean up
Kitenite Mar 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/studio/src/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"name": "Windows",
"emptyState": "Select a window to edit its settings"
},
"brand": "Brand"
"brand": "Brand",
"apps": "Apps"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/locales/ja/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@
"name": "ウィンドウ",
"emptyState": "編集するウィンドウを選択してください"
},
"brand": "ブランド"
"brand": "ブランド",
"apps": "アプリ"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/locales/kr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"name": "윈도우",
"emptyState": "설정을 편집할 창(윈도우)을 선택하세요"
},
"brand": "브랜드"
"brand": "브랜드",
"apps": "앱"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@
"name": "窗口",
"emptyState": "选择一个窗口以编辑其设置"
},
"brand": "品牌"
"brand": "品牌",
"apps": "应用"
}
}
},
Expand Down
106 changes: 106 additions & 0 deletions apps/studio/src/routes/editor/LayersPanel/AppsTab/AppCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import type { AppData } from './index';
import { cn } from '@onlook/ui/utils';

// Company brand colors
const BRAND_COLORS: Record<string, string> = {
Stripe: '#635BFF',
MongoDB: '#47A248',
Figma: '#F24E1E',
GitHub: '#181717',
Slack: '#4A154B',
Notion: '#151515',
Salesforce: '#00A1E0',
Airtable: '#18BFFF',
Twilio: '#F22F46',
};

export interface AppCardProps {
app: AppData;
onClick: (app: AppData) => void;
className?: string;
isActive?: boolean;
anyAppActive?: boolean;
isHovered?: boolean;
anyCardHovered?: boolean;
listId?: string;
hideDivider?: boolean;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
}

const AppCard: React.FC<AppCardProps> = ({
app,
onClick,
className,
isActive = false,
anyAppActive = false,
isHovered = false,
anyCardHovered = false,
listId,
hideDivider = false,
onMouseEnter,
onMouseLeave,
}) => {
// Never dim active cards or hovered cards
// Only dim cards that are neither active nor hovered when either:
// - There's an active card in the list, or
// - There's a hovered card in the list
const isDimmed = !isActive && !isHovered && (anyAppActive || anyCardHovered);

return (
<button
className={cn(
'group w-full text-left flex flex-col cursor-pointer flex-grow relative overflow-hidden',
'transition-all duration-100',
isActive && 'bg-background-secondary/50',
className,
)}
style={{ opacity: isDimmed ? 0.65 : 1 }}
onClick={() => onClick(app)}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
{/* Animated background that scales up on hover */}
<div className="absolute inset-0 bg-background-secondary/50 opacity-0 transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] pointer-events-none transform scale-90 group-hover:scale-100 group-hover:opacity-100"></div>

<div className="w-full relative">
<div className="flex items-center w-full">
<div
className={cn(
'flex-shrink-0 w-9 h-9 flex items-center justify-center rounded-md overflow-hidden mr-3 border',
'transition-all duration-100',
isActive ? 'border-white/20' : 'border-white/[0.07]',
'group-hover:border-white/20',
)}
style={{ backgroundColor: BRAND_COLORS[app.name] || '#ffffff' }}
>
{app.icon ? (
<img
src={app.icon}
alt={`${app.name} logo`}
className="w-5 h-5 object-contain"
style={{ filter: 'brightness(0) invert(1)' }} // Make SVG white
/>
) : (
<div className="w-full h-full flex items-center justify-center text-white text-xl font-semibold">
{app.name.charAt(0)}
</div>
)}
</div>
<div className="flex-1 min-w-0 flex items-center">
<h3 className="text-base font-normal text-white">{app.name}</h3>
</div>
</div>
<p className="text-sm text-gray-400 mt-2 line-clamp-2">{app.description}</p>
</div>

{/* Bottom divider line - only shown if hideDivider is false */}
{!hideDivider && (
<div className="absolute bottom-0 left-4 right-4 h-[0.5px] bg-border"></div>
)}
</button>
);
};

export default AppCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState } from 'react';
import { Icons } from '@onlook/ui/icons';
import { cn } from '@onlook/ui/utils';

// Import the AppIcon component or create a placeholder
interface AppIconProps {
size?: 'sm' | 'md' | 'lg';
}

const AppIcon: React.FC<AppIconProps> = ({ size = 'md' }) => {
const sizeClasses = {
sm: 'w-6 h-6 text-xl',
md: 'w-8 h-8 text-2xl',
lg: 'w-[60px] h-[60px] text-[32px]',
};

return (
<div
className={`flex items-center justify-center rounded-md bg-background-secondary text-white font-semibold border border-white/[0.07] ${sizeClasses[size]}`}
></div>
);
};

export interface ToolInputProps {
label: string;
type: string;
description: string;
}

export interface ToolProps {
name: string;
description?: string;
inputs?: ToolInputProps[];
icon?: React.ReactNode;
}

const ToolCard: React.FC<ToolProps> = ({ name, description, inputs, icon }) => {
const [isExpanded, setIsExpanded] = useState(false);

return (
<div className="border-b border-border last:border-b-0">
<div
className="flex items-center py-3 px-3 cursor-pointer"
onClick={() => setIsExpanded(!isExpanded)}
>
<div className="mr-3">{icon || <AppIcon size="sm" />}</div>
<div className="flex-1">
<h3 className="text-base font-normal text-white">{name}</h3>
</div>
<div>
<Icons.ChevronDown
className={cn(
'h-5 w-5 text-gray-400 transition-transform',
isExpanded ? 'transform rotate-180' : '',
)}
/>
</div>
</div>

{isExpanded && (
<div className="px-3 pb-5">
{description && (
<p className="text-sm font-normal text-muted-foreground mb-4 ml-[42px]">
{description}
</p>
)}

{inputs && inputs.length > 0 && (
<div className="rounded-md overflow-hidden border border-border">
<div className="bg-background-secondary/60 px-3 py-2">
<div className="flex text-sm font-normal">
<div className="w-1/3 text-gray-400">Input</div>
<div className="w-2/3 text-gray-400">Description</div>
</div>
</div>

{inputs.map((input, index) => (
<div key={index} className="border-t border-border">
<div className="flex px-3 py-3">
<div className="w-1/3 flex flex-col gap-[2px]">
<div className="text-white text-sm">{input.label}</div>
<div className="text-muted-foreground text-xs">
{input.type}
</div>
</div>
<div className="w-2/3 text-sm text-white">
{input.description}
</div>
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
);
};

export default ToolCard;
Loading