mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(skills): per-card loading state on skill enable (#454)
* feat(settings,skills): reorganize settings and skills pages for consistency and usability - Remove dead code: TauriCommandsPanel, useSettingsAnimation, SettingsPanelLayout, SettingsBackButton, ProfilePanel, AdvancedPanel, SkillsPanel, SkillsGrid (~1900 lines) - Standardize settings panel padding to p-4 space-y-4 across all panels - Add breadcrumb navigation to SettingsHeader with route-derived breadcrumbs - Decompose oversized panels: LocalModelPanel, AutocompletePanel, CronJobsPanel, ScreenIntelligencePanel into sub-components in dedicated subdirectories - Deduplicate skills management: move browser access toggle to Skills page, remove redundant /settings/skills route - Unify skill card layout: UnifiedSkillCard component with overflow menu for secondary actions, consistent status/CTA patterns across all skill types - Add skill search bar and category filter with grouped results Closes #396 * fix(settings,skills): address CodeRabbit review feedback - Use semantic nav/ol/li for breadcrumbs with aria-hidden separators - Fix duplicate text-xs class in CompletionStyleSection - Use interface instead of type for CronSkillConfig - Disable cron option inputs when parent skill is disabled - Deduplicate preset error rendering in DeviceCapabilitySection - Fix low-contrast labels: text-stone-300 → text-stone-700, text-stone-200 → text-stone-600 - Disable "Set Path" button when input is empty - Add aria-pressed to category filter buttons - Convert SkillCategoryFilter to arrow function - Use void operator for async onClick handler - Simplify redundant conditional in ThirdPartySkillCard - Use async/await instead of .then() in BrowserAccessToggle * fix(tests): update Skills page tests for unified card layout - Mock openhumanGetRuntimeFlags/openhumanSetBrowserAllowAll for BrowserAccessToggle - Open overflow menu before clicking sync/debug buttons (now in secondary actions) - Remove assertion for deleted "3rd Party Skills" heading * fix(skills): show per-card loading state instead of hiding entire skill list on enable When clicking Enable on a third-party skill (e.g. Gmail, Notion), the entire skill list was replaced with a loading message. Now only the clicked card's button shows "Enabling..." with a disabled state while the install RPC runs, keeping all other cards visible and interactive.
This commit is contained in:
@@ -39,6 +39,7 @@ export interface UnifiedSkillCardProps {
|
||||
metricsText?: string;
|
||||
};
|
||||
syncSummaryText?: string;
|
||||
ctaDisabled?: boolean;
|
||||
}
|
||||
|
||||
const CTA_STYLES: Record<string, string> = {
|
||||
@@ -60,6 +61,7 @@ export function UnifiedSkillCard({
|
||||
secondaryActions,
|
||||
syncProgress,
|
||||
syncSummaryText,
|
||||
ctaDisabled,
|
||||
}: UnifiedSkillCardProps) {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
@@ -164,11 +166,12 @@ export function UnifiedSkillCard({
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
disabled={ctaDisabled}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onCtaClick();
|
||||
}}
|
||||
className={`flex-shrink-0 rounded-lg border px-3 py-1.5 text-[11px] font-medium transition-colors ${ctaStyle}`}>
|
||||
className={`flex-shrink-0 rounded-lg border px-3 py-1.5 text-[11px] font-medium transition-colors ${ctaStyle} ${ctaDisabled ? 'opacity-50 cursor-not-allowed' : ''}`}>
|
||||
{ctaLabel}
|
||||
</button>
|
||||
</div>
|
||||
@@ -180,9 +183,10 @@ export function UnifiedSkillCard({
|
||||
interface ThirdPartySkillCardProps {
|
||||
skill: SkillListEntry;
|
||||
onSetup: () => void;
|
||||
isInstalling?: boolean;
|
||||
}
|
||||
|
||||
export function ThirdPartySkillCard({ skill, onSetup }: ThirdPartySkillCardProps) {
|
||||
export function ThirdPartySkillCard({ skill, onSetup, isInstalling }: ThirdPartySkillCardProps) {
|
||||
const connectionStatus = useSkillConnectionStatus(skill.id);
|
||||
const statusDisplay = STATUS_DISPLAY[connectionStatus] ?? STATUS_DISPLAY.offline;
|
||||
const skillState = useSkillState<SkillHostConnectionState & Record<string, unknown>>(skill.id);
|
||||
@@ -239,6 +243,7 @@ export function ThirdPartySkillCard({ skill, onSetup }: ThirdPartySkillCardProps
|
||||
}
|
||||
|
||||
function ctaLabel(): string {
|
||||
if (isInstalling) return 'Enabling...';
|
||||
switch (connectionStatus) {
|
||||
case 'offline': return 'Enable';
|
||||
case 'setup_required': return 'Setup';
|
||||
@@ -305,6 +310,7 @@ export function ThirdPartySkillCard({ skill, onSetup }: ThirdPartySkillCardProps
|
||||
statusColor={statusDisplay.color}
|
||||
ctaLabel={ctaLabel()}
|
||||
ctaVariant={ctaVariant()}
|
||||
ctaDisabled={isInstalling}
|
||||
onCtaClick={() => onSetup()}
|
||||
secondaryActions={secondaryActions}
|
||||
syncProgress={
|
||||
|
||||
@@ -353,11 +353,9 @@ export default function Skills() {
|
||||
onChange={setSelectedCategory}
|
||||
/>
|
||||
|
||||
{skillsLoading || installing ? (
|
||||
{skillsLoading ? (
|
||||
<div className="py-8 text-center">
|
||||
<p className="text-sm text-stone-400">
|
||||
{installing ? `Installing ${installing}...` : 'Loading skills...'}
|
||||
</p>
|
||||
<p className="text-sm text-stone-400">Loading skills...</p>
|
||||
</div>
|
||||
) : filteredItems.length === 0 ? (
|
||||
<div className="py-8 text-center">
|
||||
@@ -406,6 +404,7 @@ export default function Skills() {
|
||||
<ThirdPartySkillCard
|
||||
key={item.id}
|
||||
skill={item.skill!}
|
||||
isInstalling={installing === item.id}
|
||||
onSetup={() => openSkillSetup(item.skill!)}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user