optimize: skills page structure (#905)
This commit is contained in:
@@ -4,7 +4,9 @@ import type {
|
|||||||
NavItemDef,
|
NavItemDef,
|
||||||
RouteDef,
|
RouteDef,
|
||||||
SettingsSectionDef,
|
SettingsSectionDef,
|
||||||
|
SkillDetailMetaProps,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import type { ComponentType } from 'react';
|
||||||
|
|
||||||
class RendererExtensionRegistry {
|
class RendererExtensionRegistry {
|
||||||
private extensions: RendererExtension[] = [];
|
private extensions: RendererExtension[] = [];
|
||||||
@@ -56,6 +58,10 @@ class RendererExtensionRegistry {
|
|||||||
.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSkillDetailMetaComponents(): ComponentType<SkillDetailMetaProps>[] {
|
||||||
|
return this.extensions.flatMap((ext) => ext.skills?.detailMetaComponents ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
async initializeAll(): Promise<void> {
|
async initializeAll(): Promise<void> {
|
||||||
for (const ext of this.extensions) {
|
for (const ext of this.extensions) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { ComponentType } from 'react';
|
import type { ComponentType } from 'react';
|
||||||
|
import type { Skill } from '../types/skill';
|
||||||
|
|
||||||
export interface NavItemDef {
|
export interface NavItemDef {
|
||||||
to: string;
|
to: string;
|
||||||
@@ -37,11 +38,21 @@ export interface SettingsSectionExtension {
|
|||||||
sections: SettingsSectionDef[];
|
sections: SettingsSectionDef[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SkillDetailMetaProps {
|
||||||
|
skill: Skill;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SkillsExtension {
|
||||||
|
id: string;
|
||||||
|
detailMetaComponents?: ComponentType<SkillDetailMetaProps>[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface RendererExtension {
|
export interface RendererExtension {
|
||||||
id: string;
|
id: string;
|
||||||
sidebar?: SidebarExtension;
|
sidebar?: SidebarExtension;
|
||||||
routes?: RouteExtension;
|
routes?: RouteExtension;
|
||||||
settings?: SettingsSectionExtension;
|
settings?: SettingsSectionExtension;
|
||||||
|
skills?: SkillsExtension;
|
||||||
i18nResources?: I18nResources;
|
i18nResources?: I18nResources;
|
||||||
setup?(): void | Promise<void>;
|
setup?(): void | Promise<void>;
|
||||||
teardown?(): void;
|
teardown?(): void;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { hostApiFetch } from '@/lib/host-api';
|
|||||||
import { trackUiEvent } from '@/lib/telemetry';
|
import { trackUiEvent } from '@/lib/telemetry';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import type { Skill } from '@/types/skill';
|
import type { Skill } from '@/types/skill';
|
||||||
|
import { rendererExtensionRegistry } from '@/extensions/registry';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
|
|
||||||
@@ -73,6 +74,7 @@ function SkillDetailDialog({ skill, isOpen, onClose, onToggle, onUninstall, onOp
|
|||||||
const [envVars, setEnvVars] = useState<Array<{ key: string; value: string }>>([]);
|
const [envVars, setEnvVars] = useState<Array<{ key: string; value: string }>>([]);
|
||||||
const [apiKey, setApiKey] = useState('');
|
const [apiKey, setApiKey] = useState('');
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
const detailMetaComponents = rendererExtensionRegistry.getSkillDetailMetaComponents();
|
||||||
|
|
||||||
// Initialize config from skill
|
// Initialize config from skill
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -206,13 +208,16 @@ function SkillDetailDialog({ skill, isOpen, onClose, onToggle, onUninstall, onOp
|
|||||||
<h2 className="text-[28px] font-serif text-foreground font-normal mb-3 text-center tracking-tight">
|
<h2 className="text-[28px] font-serif text-foreground font-normal mb-3 text-center tracking-tight">
|
||||||
{skill.name}
|
{skill.name}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="flex items-center justify-center gap-2.5 mb-6 opacity-80">
|
<div data-skill-detail-meta-row="1" className="flex items-center justify-center gap-2.5 mb-6 opacity-80">
|
||||||
<Badge variant="secondary" className="font-mono text-[11px] font-medium px-3 py-0.5 rounded-full bg-black/[0.04] dark:bg-white/[0.08] hover:bg-black/[0.08] dark:hover:bg-white/[0.12] border-0 shadow-none text-foreground/70 transition-colors">
|
<Badge variant="secondary" className="font-mono text-[11px] font-medium px-3 py-0.5 rounded-full bg-black/[0.04] dark:bg-white/[0.08] hover:bg-black/[0.08] dark:hover:bg-white/[0.12] border-0 shadow-none text-foreground/70 transition-colors">
|
||||||
v{skill.version}
|
v{skill.version}
|
||||||
</Badge>
|
</Badge>
|
||||||
<Badge variant="secondary" className="font-mono text-[11px] font-medium px-3 py-0.5 rounded-full bg-black/[0.04] dark:bg-white/[0.08] hover:bg-black/[0.08] dark:hover:bg-white/[0.12] border-0 shadow-none text-foreground/70 transition-colors">
|
<Badge variant="secondary" className="font-mono text-[11px] font-medium px-3 py-0.5 rounded-full bg-black/[0.04] dark:bg-white/[0.08] hover:bg-black/[0.08] dark:hover:bg-white/[0.12] border-0 shadow-none text-foreground/70 transition-colors">
|
||||||
{skill.isCore ? t('detail.coreSystem') : skill.isBundled ? t('detail.bundled') : t('detail.userInstalled')}
|
{skill.isCore ? t('detail.coreSystem') : skill.isBundled ? t('detail.bundled') : t('detail.userInstalled')}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
{detailMetaComponents.map((DetailMetaComponent, index) => (
|
||||||
|
<DetailMetaComponent key={`skill-detail-meta-${index}`} skill={skill} />
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{skill.description && (
|
{skill.description && (
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export default defineConfig({
|
|||||||
'@': resolve(__dirname, 'src'),
|
'@': resolve(__dirname, 'src'),
|
||||||
'@electron': resolve(__dirname, 'electron'),
|
'@electron': resolve(__dirname, 'electron'),
|
||||||
},
|
},
|
||||||
|
dedupe: ['react', 'react-dom', 'react-i18next', 'zustand', 'sonner', 'lucide-react'],
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user