feat: update openclaw and polish desktop flows
This commit is contained in:
@@ -32,9 +32,7 @@ export function AppCenter() {
|
||||
const init = useAppCenterStore((state) => state.init);
|
||||
const items = useAppCenterStore((state) => state.items);
|
||||
const selectedTagKey = useAppCenterStore((state) => state.selectedTagKey);
|
||||
const selectedItemId = useAppCenterStore((state) => state.selectedItemId);
|
||||
const selectTag = useAppCenterStore((state) => state.selectTag);
|
||||
const selectItem = useAppCenterStore((state) => state.selectItem);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
@@ -127,13 +125,12 @@ export function AppCenter() {
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(220px,260px))] justify-center gap-4 pb-2 sm:justify-start">
|
||||
{filteredItems.map((item) => {
|
||||
const Icon = getAppIcon(item.icon);
|
||||
const isSelected = selectedItemId === item.id;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
selectItem(item.id);
|
||||
onClick={(event) => {
|
||||
event.currentTarget.blur();
|
||||
openItem(item);
|
||||
}}
|
||||
data-testid={`app-center-item-${item.id}`}
|
||||
@@ -142,9 +139,7 @@ export function AppCenter() {
|
||||
'hover:border-[#7DBADB] hover:bg-white hover:shadow-[0_18px_42px_rgba(15,23,42,0.09)]',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1E3A8A]/30',
|
||||
'dark:border-white/10 dark:bg-slate-950/75 dark:hover:shadow-[0_10px_24px_rgba(0,0,0,0.24)]',
|
||||
isSelected
|
||||
? 'border-[#0369A1] bg-[#F6FBFE] shadow-[0_14px_30px_rgba(3,105,161,0.10)]'
|
||||
: 'border-slate-200/80',
|
||||
'border-slate-200/80',
|
||||
)}
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-20 bg-[linear-gradient(180deg,rgba(229,244,250,0.85),rgba(255,255,255,0))] dark:bg-[linear-gradient(180deg,rgba(30,58,138,0.18),rgba(15,23,42,0))]" />
|
||||
|
||||
@@ -32,6 +32,7 @@ import { useSettingsStore } from '@/stores/settings';
|
||||
import { useGatewayStore } from '@/stores/gateway';
|
||||
import { useUpdateStore } from '@/stores/update';
|
||||
import { UpdateSettings } from '@/components/settings/UpdateSettings';
|
||||
import { ProvidersSettings } from '@/components/settings/ProvidersSettings';
|
||||
import {
|
||||
getGatewayWsDiagnosticEnabled,
|
||||
invokeIpc,
|
||||
@@ -1109,6 +1110,12 @@ export function Settings() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{devModeUnlocked && (
|
||||
<div className="yinian-panel p-4" data-testid="settings-model-config-section">
|
||||
<ProvidersSettings />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{devModeUnlocked && (
|
||||
<div className="yinian-panel p-4" data-testid="settings-model-diagnostics">
|
||||
<div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
Plus,
|
||||
Play,
|
||||
RefreshCw,
|
||||
ShieldCheck,
|
||||
Trash2,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
@@ -27,12 +26,10 @@ import {
|
||||
YinianEmptyState,
|
||||
YinianHeaderActions,
|
||||
YinianInfoRow,
|
||||
YinianMetricCard,
|
||||
YinianNotice,
|
||||
YinianPageHeader,
|
||||
YinianPageShell,
|
||||
YinianPanel,
|
||||
yinianAccent,
|
||||
yinianPrimaryButton,
|
||||
} from '@/components/yinian/ui';
|
||||
import type { YinianLocalSkill, YinianSkillEntitlement } from '../../../shared/yinian';
|
||||
@@ -147,12 +144,6 @@ export function YinianSkills() {
|
||||
const visibleOpenClawSkills = openClawSkills
|
||||
.filter((skill) => !isOpenClawBuiltInSkill(skill))
|
||||
.sort((a, b) => a.name.localeCompare(b.name, i18n.language === 'zh' ? 'zh-CN' : 'en-US'));
|
||||
const states = config.entitlements.map((skill) => getManagerState(skill, localById.get(skill.skillId)));
|
||||
const runnableCount = states.filter(canRun).length;
|
||||
const notSyncedCount = states.filter((state) => state === 'not-synced').length;
|
||||
const updateCount = states.filter((state) => state === 'update-available').length;
|
||||
const failedCount = states.filter((state) => state === 'failed').length;
|
||||
const disabledCount = states.filter((state) => state === 'disabled').length;
|
||||
const selectedServerSkill = config.entitlements.find((skill) => skill.skillId === selectedServerSkillId);
|
||||
const selectedServerLocal = selectedServerSkill ? localById.get(selectedServerSkill.skillId) : undefined;
|
||||
const selectedServerState = selectedServerSkill ? getManagerState(selectedServerSkill, selectedServerLocal) : null;
|
||||
@@ -255,18 +246,6 @@ export function YinianSkills() {
|
||||
</YinianNotice>
|
||||
)}
|
||||
|
||||
<section className="grid gap-3 md:grid-cols-2 xl:grid-cols-5">
|
||||
{[
|
||||
{ label: t('business.metrics.enabled'), value: String(config.entitlements.length), icon: ShieldCheck, tone: yinianAccent },
|
||||
{ label: t('business.metrics.runnable'), value: String(runnableCount), icon: CheckCircle2, tone: 'text-emerald-600' },
|
||||
{ label: t('business.metrics.notSynced'), value: String(notSyncedCount), icon: CloudDownload, tone: 'text-slate-600 dark:text-slate-300' },
|
||||
{ label: t('business.metrics.updates'), value: String(updateCount), icon: RefreshCw, tone: 'text-amber-600' },
|
||||
{ label: t('business.metrics.issues'), value: String(failedCount + disabledCount), icon: AlertCircle, tone: failedCount ? 'text-red-600' : 'text-slate-500' },
|
||||
].map((item) => (
|
||||
<YinianMetricCard key={item.label} {...item} />
|
||||
))}
|
||||
</section>
|
||||
|
||||
<div className="flex flex-wrap gap-2 rounded-lg border border-slate-200/70 bg-white/60 p-1 dark:border-white/10 dark:bg-white/5">
|
||||
{[
|
||||
{ key: 'quickTasks' as const, label: t('business.quickTasks.title'), count: quickTasks.length },
|
||||
|
||||
Reference in New Issue
Block a user