@nospt/plugin-dev-ai-hub 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { useState, useMemo } from 'react';
3
3
  import { useSearchParams } from 'react-router-dom';
4
- import { Box, Text, Skeleton, Flex, TablePagination } from '@backstage/ui';
4
+ import { Flex, Text, Box, Skeleton, TablePagination } from '@backstage/ui';
5
5
  import { RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine } from '@remixicon/react';
6
+ import SmartToyIcon from '@mui/icons-material/SmartToy';
6
7
  import { AssetCard } from '../AssetCard/AssetCard.esm.js';
7
8
  import { AssetFilters } from '../AssetFilters/AssetFilters.esm.js';
8
9
  import { AssetDetailPanel } from '../AssetDetailPanel/AssetDetailPanel.esm.js';
@@ -101,6 +102,10 @@ function DevAiHubPage() {
101
102
  return Array.from(tagSet).sort();
102
103
  }, [result]);
103
104
  return /* @__PURE__ */ jsxs("div", { className: styles.pageRoot, children: [
105
+ /* @__PURE__ */ jsx("div", { className: styles.pageHeader, children: /* @__PURE__ */ jsxs(Flex, { gap: "3", align: "center", children: [
106
+ /* @__PURE__ */ jsx(SmartToyIcon, { className: styles.pageHeaderIcon }),
107
+ /* @__PURE__ */ jsx(Text, { variant: "title-large", weight: "bold", children: "Dev AI Hub" })
108
+ ] }) }),
104
109
  /* @__PURE__ */ jsxs("div", { className: styles.content, children: [
105
110
  /* @__PURE__ */ jsx("div", { className: styles.statsGrid, children: STATS_CONFIG.map(({ key, label, Icon, gradient, shadow }) => {
106
111
  const isActive = filters.types[0] === key;
@@ -1 +1 @@
1
- {"version":3,"file":"DevAiHubPage.esm.js","sources":["../../../src/components/DevAiHubPage/DevAiHubPage.tsx"],"sourcesContent":["import { useState, useMemo, type ElementType } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Box, Flex, Text, Skeleton, TablePagination } from '@backstage/ui';\nimport { RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine } from '@remixicon/react';\nimport type { AssetType, AiTool } from '@nospt/plugin-dev-ai-hub-common';\nimport { AssetCard } from '../AssetCard';\nimport { AssetFilters } from '../AssetFilters';\nimport type { AssetFiltersValue } from '../AssetFilters';\nimport { AssetDetailPanel } from '../AssetDetailPanel';\nimport { AssetInstallDialog } from '../AssetInstallDialog';\nimport { useAssets, useStats, useProviders } from '../../hooks';\nimport styles from './DevAiHubPage.module.css';\n\nconst DEFAULT_FILTERS: AssetFiltersValue = {\n types: [],\n tools: [],\n search: '',\n tags: [],\n providerId: undefined,\n};\n\nconst PAGE_SIZE = 24;\n\nconst STATS_CONFIG: { key: AssetType; label: string; Icon: ElementType; gradient: string; shadow: string }[] = [\n { key: 'instruction', label: 'Instructions', Icon: RiArticleLine,\n gradient: 'linear-gradient(135deg, #54A0FF 0%, #2980FF 100%)', shadow: '#54A0FF40' },\n { key: 'agent', label: 'Agents', Icon: RiRobot2Line,\n gradient: 'linear-gradient(135deg, #FF6B9D 0%, #E0507A 100%)', shadow: '#FF6B9D40' },\n { key: 'skill', label: 'Skills', Icon: RiToolsLine,\n gradient: 'linear-gradient(135deg, #6AB04C 0%, #4A8F2E 100%)', shadow: '#6AB04C40' },\n { key: 'workflow', label: 'Workflows', Icon: RiGitBranchLine,\n gradient: 'linear-gradient(135deg, #F9CA24 0%, #D4A800 100%)', shadow: '#F9CA2440' },\n];\n\nexport function DevAiHubPage() {\n const [filters, setFilters] = useState<AssetFiltersValue>(DEFAULT_FILTERS);\n const [page, setPage] = useState(1);\n\n const [searchParams, setSearchParams] = useSearchParams();\n const selectedAssetId = searchParams.get('assetId');\n const installAssetId = searchParams.get('installId');\n\n const handleViewAsset = (id: string) =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.set('assetId', id); return n; });\n\n const handleCloseDetail = () =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.delete('assetId'); return n; });\n\n const handleInstallAsset = (id: string) =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.set('installId', id); return n; });\n\n const handleCloseInstall = () =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.delete('installId'); return n; });\n\n const { stats } = useStats();\n const { providers } = useProviders();\n\n const apiFilter = useMemo(\n () => ({\n type: filters.types.length === 1 ? (filters.types[0] as AssetType) : undefined,\n tool: filters.tools.length === 1 ? (filters.tools[0] as AiTool) : undefined,\n search: filters.search || undefined,\n tags: filters.tags.length > 0 ? filters.tags : undefined,\n providerId: filters.providerId || undefined,\n page,\n pageSize: PAGE_SIZE,\n }),\n [filters, page],\n );\n\n const { result, loading } = useAssets(apiFilter);\n\n const handleFiltersChange = (next: AssetFiltersValue) => {\n setFilters(next);\n setPage(1);\n };\n\n const totalPages = result ? Math.ceil(result.totalCount / PAGE_SIZE) : 0;\n\n const availableTags = useMemo(() => {\n if (!result) return [];\n const tagSet = new Set<string>();\n result.items.forEach(a => a.tags.forEach(t => tagSet.add(t)));\n return Array.from(tagSet).sort();\n }, [result]);\n\n return (\n <div className={styles.pageRoot}>\n <div className={styles.content}>\n {/* Stats row */}\n <div className={styles.statsGrid}>\n {STATS_CONFIG.map(({ key, label, Icon, gradient, shadow }) => {\n const isActive = filters.types[0] === key;\n return (\n <div\n key={key}\n className={styles.statCard}\n onClick={() => handleFiltersChange({ ...filters, types: isActive ? [] : [key] })}\n style={{\n background: gradient,\n boxShadow: isActive ? `0 8px 24px ${shadow}` : `0 2px 8px ${shadow}`,\n transform: isActive ? 'translateY(-2px)' : 'none',\n outline: isActive ? '2px solid rgba(255,255,255,0.6)' : 'none',\n outlineOffset: 2,\n }}\n role=\"button\"\n tabIndex={0}\n onKeyDown={e => { if (e.key === 'Enter') handleFiltersChange({ ...filters, types: isActive ? [] : [key] }); }}\n >\n <div className={styles.statCardInner}>\n <Box>\n <Text variant=\"title-large\" weight=\"bold\" className={styles.statValue}>\n {stats ? (stats.byType[key] ?? 0) : <Skeleton style={{ width: 32 }} />}\n </Text>\n <Text variant=\"body-small\" className={styles.statLabel}>\n {label}\n </Text>\n </Box>\n <div className={styles.statIconBox}>\n <Icon size={22} style={{ color: '#fff' }} />\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Filters */}\n <AssetFilters\n value={filters}\n onChange={handleFiltersChange}\n availableTags={availableTags}\n providers={providers.length > 1 ? providers : undefined}\n />\n\n {/* Results summary */}\n {result && !loading && (\n <Text variant=\"body-x-small\" color=\"secondary\" style={{ marginBottom: 'var(--bui-space-4)', display: 'block' }}>\n {result.totalCount} asset{result.totalCount !== 1 ? 's' : ''} found\n </Text>\n )}\n\n {/* Asset grid — 4 columns on large screens */}\n <div className={styles.assetGrid}>\n {loading\n ? Array.from({ length: 8 }).map((_, i) => (\n <Skeleton key={i} style={{ height: 150, borderRadius: 'var(--bui-radius-2)' }} />\n ))\n : result?.items.map(asset => (\n <AssetCard\n key={asset.id}\n asset={asset}\n onView={handleViewAsset}\n onInstall={handleInstallAsset}\n />\n ))}\n </div>\n\n {/* Empty state */}\n {!loading && result?.items.length === 0 && (\n <div className={styles.emptyState}>\n <div className={styles.emptyEmoji}>🤖</div>\n <Text variant=\"title-small\" color=\"secondary\" weight=\"bold\">No assets found</Text>\n <Text variant=\"body-small\" color=\"secondary\" style={{ marginTop: 'var(--bui-space-1)' }}>\n Try adjusting your filters or search terms.\n </Text>\n </div>\n )}\n\n {/* Pagination */}\n {totalPages > 1 && (\n <Flex className={styles.paginationRow}>\n <TablePagination\n pageSize={PAGE_SIZE}\n offset={(page - 1) * PAGE_SIZE}\n totalCount={result?.totalCount ?? 0}\n hasNextPage={page < totalPages}\n hasPreviousPage={page > 1}\n onNextPage={() => setPage(p => p + 1)}\n onPreviousPage={() => setPage(p => p - 1)}\n />\n </Flex>\n )}\n </div>\n\n <AssetDetailPanel\n assetId={selectedAssetId}\n onClose={handleCloseDetail}\n />\n\n <AssetInstallDialog\n assetId={installAssetId}\n onClose={handleCloseInstall}\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DevAiHubPage.esm.js","sources":["../../../src/components/DevAiHubPage/DevAiHubPage.tsx"],"sourcesContent":["import { useState, useMemo, type ElementType } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { Box, Flex, Text, Skeleton, TablePagination } from '@backstage/ui';\nimport { RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine } from '@remixicon/react';\nimport SmartToyIcon from '@mui/icons-material/SmartToy';\nimport type { AssetType, AiTool } from '@nospt/plugin-dev-ai-hub-common';\nimport { AssetCard } from '../AssetCard';\nimport { AssetFilters } from '../AssetFilters';\nimport type { AssetFiltersValue } from '../AssetFilters';\nimport { AssetDetailPanel } from '../AssetDetailPanel';\nimport { AssetInstallDialog } from '../AssetInstallDialog';\nimport { useAssets, useStats, useProviders } from '../../hooks';\nimport styles from './DevAiHubPage.module.css';\n\nconst DEFAULT_FILTERS: AssetFiltersValue = {\n types: [],\n tools: [],\n search: '',\n tags: [],\n providerId: undefined,\n};\n\nconst PAGE_SIZE = 24;\n\nconst STATS_CONFIG: { key: AssetType; label: string; Icon: ElementType; gradient: string; shadow: string }[] = [\n { key: 'instruction', label: 'Instructions', Icon: RiArticleLine,\n gradient: 'linear-gradient(135deg, #54A0FF 0%, #2980FF 100%)', shadow: '#54A0FF40' },\n { key: 'agent', label: 'Agents', Icon: RiRobot2Line,\n gradient: 'linear-gradient(135deg, #FF6B9D 0%, #E0507A 100%)', shadow: '#FF6B9D40' },\n { key: 'skill', label: 'Skills', Icon: RiToolsLine,\n gradient: 'linear-gradient(135deg, #6AB04C 0%, #4A8F2E 100%)', shadow: '#6AB04C40' },\n { key: 'workflow', label: 'Workflows', Icon: RiGitBranchLine,\n gradient: 'linear-gradient(135deg, #F9CA24 0%, #D4A800 100%)', shadow: '#F9CA2440' },\n];\n\nexport function DevAiHubPage() {\n const [filters, setFilters] = useState<AssetFiltersValue>(DEFAULT_FILTERS);\n const [page, setPage] = useState(1);\n\n const [searchParams, setSearchParams] = useSearchParams();\n const selectedAssetId = searchParams.get('assetId');\n const installAssetId = searchParams.get('installId');\n\n const handleViewAsset = (id: string) =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.set('assetId', id); return n; });\n\n const handleCloseDetail = () =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.delete('assetId'); return n; });\n\n const handleInstallAsset = (id: string) =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.set('installId', id); return n; });\n\n const handleCloseInstall = () =>\n setSearchParams(p => { const n = new URLSearchParams(p); n.delete('installId'); return n; });\n\n const { stats } = useStats();\n const { providers } = useProviders();\n\n const apiFilter = useMemo(\n () => ({\n type: filters.types.length === 1 ? (filters.types[0] as AssetType) : undefined,\n tool: filters.tools.length === 1 ? (filters.tools[0] as AiTool) : undefined,\n search: filters.search || undefined,\n tags: filters.tags.length > 0 ? filters.tags : undefined,\n providerId: filters.providerId || undefined,\n page,\n pageSize: PAGE_SIZE,\n }),\n [filters, page],\n );\n\n const { result, loading } = useAssets(apiFilter);\n\n const handleFiltersChange = (next: AssetFiltersValue) => {\n setFilters(next);\n setPage(1);\n };\n\n const totalPages = result ? Math.ceil(result.totalCount / PAGE_SIZE) : 0;\n\n const availableTags = useMemo(() => {\n if (!result) return [];\n const tagSet = new Set<string>();\n result.items.forEach(a => a.tags.forEach(t => tagSet.add(t)));\n return Array.from(tagSet).sort();\n }, [result]);\n\n return (\n <div className={styles.pageRoot}>\n <div className={styles.pageHeader}>\n <Flex gap=\"3\" align=\"center\">\n <SmartToyIcon className={styles.pageHeaderIcon} />\n <Text variant=\"title-large\" weight=\"bold\">Dev AI Hub</Text>\n </Flex>\n </div>\n <div className={styles.content}>\n {/* Stats row */}\n <div className={styles.statsGrid}>\n {STATS_CONFIG.map(({ key, label, Icon, gradient, shadow }) => {\n const isActive = filters.types[0] === key;\n return (\n <div\n key={key}\n className={styles.statCard}\n onClick={() => handleFiltersChange({ ...filters, types: isActive ? [] : [key] })}\n style={{\n background: gradient,\n boxShadow: isActive ? `0 8px 24px ${shadow}` : `0 2px 8px ${shadow}`,\n transform: isActive ? 'translateY(-2px)' : 'none',\n outline: isActive ? '2px solid rgba(255,255,255,0.6)' : 'none',\n outlineOffset: 2,\n }}\n role=\"button\"\n tabIndex={0}\n onKeyDown={e => { if (e.key === 'Enter') handleFiltersChange({ ...filters, types: isActive ? [] : [key] }); }}\n >\n <div className={styles.statCardInner}>\n <Box>\n <Text variant=\"title-large\" weight=\"bold\" className={styles.statValue}>\n {stats ? (stats.byType[key] ?? 0) : <Skeleton style={{ width: 32 }} />}\n </Text>\n <Text variant=\"body-small\" className={styles.statLabel}>\n {label}\n </Text>\n </Box>\n <div className={styles.statIconBox}>\n <Icon size={22} style={{ color: '#fff' }} />\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Filters */}\n <AssetFilters\n value={filters}\n onChange={handleFiltersChange}\n availableTags={availableTags}\n providers={providers.length > 1 ? providers : undefined}\n />\n\n {/* Results summary */}\n {result && !loading && (\n <Text variant=\"body-x-small\" color=\"secondary\" style={{ marginBottom: 'var(--bui-space-4)', display: 'block' }}>\n {result.totalCount} asset{result.totalCount !== 1 ? 's' : ''} found\n </Text>\n )}\n\n {/* Asset grid — 4 columns on large screens */}\n <div className={styles.assetGrid}>\n {loading\n ? Array.from({ length: 8 }).map((_, i) => (\n <Skeleton key={i} style={{ height: 150, borderRadius: 'var(--bui-radius-2)' }} />\n ))\n : result?.items.map(asset => (\n <AssetCard\n key={asset.id}\n asset={asset}\n onView={handleViewAsset}\n onInstall={handleInstallAsset}\n />\n ))}\n </div>\n\n {/* Empty state */}\n {!loading && result?.items.length === 0 && (\n <div className={styles.emptyState}>\n <div className={styles.emptyEmoji}>🤖</div>\n <Text variant=\"title-small\" color=\"secondary\" weight=\"bold\">No assets found</Text>\n <Text variant=\"body-small\" color=\"secondary\" style={{ marginTop: 'var(--bui-space-1)' }}>\n Try adjusting your filters or search terms.\n </Text>\n </div>\n )}\n\n {/* Pagination */}\n {totalPages > 1 && (\n <Flex className={styles.paginationRow}>\n <TablePagination\n pageSize={PAGE_SIZE}\n offset={(page - 1) * PAGE_SIZE}\n totalCount={result?.totalCount ?? 0}\n hasNextPage={page < totalPages}\n hasPreviousPage={page > 1}\n onNextPage={() => setPage(p => p + 1)}\n onPreviousPage={() => setPage(p => p - 1)}\n />\n </Flex>\n )}\n </div>\n\n <AssetDetailPanel\n assetId={selectedAssetId}\n onClose={handleCloseDetail}\n />\n\n <AssetInstallDialog\n assetId={installAssetId}\n onClose={handleCloseInstall}\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import styleInject from '../../node_modules_dist/style-inject/dist/style-inject.es.esm.js';
2
2
 
3
- var css_248z = "@layer components {\n .DevAiHubPage_pageRoot__58eb18b1eb {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .DevAiHubPage_content__58eb18b1eb {\n flex: 1;\n padding: var(--bui-space-6);\n overflow-y: auto;\n }\n\n .DevAiHubPage_statsGrid__58eb18b1eb {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--bui-space-4);\n margin-bottom: var(--bui-space-6);\n }\n\n @media (max-width: 600px) {\n .DevAiHubPage_statsGrid__58eb18b1eb {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n .DevAiHubPage_statCard__58eb18b1eb {\n border-radius: var(--bui-radius-3);\n padding: var(--bui-space-4);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .DevAiHubPage_statCard__58eb18b1eb:hover {\n transform: translateY(-2px);\n }\n\n .DevAiHubPage_statCardInner__58eb18b1eb {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .DevAiHubPage_statValue__58eb18b1eb {\n color: #fff;\n line-height: 1;\n }\n\n .DevAiHubPage_statLabel__58eb18b1eb {\n color: rgba(255, 255, 255, 0.85);\n font-weight: 500;\n margin-top: var(--bui-space-1);\n }\n\n .DevAiHubPage_statIconBox__58eb18b1eb {\n width: 40px;\n height: 40px;\n border-radius: var(--bui-radius-2);\n background-color: rgba(255, 255, 255, 0.2);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .DevAiHubPage_assetGrid__58eb18b1eb {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--bui-space-3);\n }\n\n @media (max-width: 1200px) {\n .DevAiHubPage_assetGrid__58eb18b1eb {\n grid-template-columns: repeat(3, 1fr);\n }\n }\n\n @media (max-width: 900px) {\n .DevAiHubPage_assetGrid__58eb18b1eb {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 600px) {\n .DevAiHubPage_assetGrid__58eb18b1eb {\n grid-template-columns: 1fr;\n }\n }\n\n .DevAiHubPage_emptyState__58eb18b1eb {\n padding: 96px 0;\n text-align: center;\n }\n\n .DevAiHubPage_emptyEmoji__58eb18b1eb {\n margin-bottom: var(--bui-space-4);\n opacity: 0.15;\n font-size: 5rem;\n }\n\n .DevAiHubPage_paginationRow__58eb18b1eb {\n display: flex;\n justify-content: center;\n margin-top: var(--bui-space-8);\n }\n}\n";
4
- var styles = {"pageRoot":"DevAiHubPage_pageRoot__58eb18b1eb","content":"DevAiHubPage_content__58eb18b1eb","statsGrid":"DevAiHubPage_statsGrid__58eb18b1eb","statCard":"DevAiHubPage_statCard__58eb18b1eb","statCardInner":"DevAiHubPage_statCardInner__58eb18b1eb","statValue":"DevAiHubPage_statValue__58eb18b1eb","statLabel":"DevAiHubPage_statLabel__58eb18b1eb","statIconBox":"DevAiHubPage_statIconBox__58eb18b1eb","assetGrid":"DevAiHubPage_assetGrid__58eb18b1eb","emptyState":"DevAiHubPage_emptyState__58eb18b1eb","emptyEmoji":"DevAiHubPage_emptyEmoji__58eb18b1eb","paginationRow":"DevAiHubPage_paginationRow__58eb18b1eb"};
3
+ var css_248z = "@layer components {\n .DevAiHubPage_pageRoot__dbf26b481d {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .DevAiHubPage_pageHeader__dbf26b481d {\n padding: var(--bui-space-6) var(--bui-space-6) 0;\n flex-shrink: 0;\n }\n\n .DevAiHubPage_pageHeaderIcon__dbf26b481d {\n font-size: 28px;\n opacity: 0.85;\n }\n\n .DevAiHubPage_content__dbf26b481d {\n flex: 1;\n padding: var(--bui-space-6);\n overflow-y: auto;\n }\n\n .DevAiHubPage_statsGrid__dbf26b481d {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--bui-space-4);\n margin-bottom: var(--bui-space-6);\n }\n\n @media (max-width: 600px) {\n .DevAiHubPage_statsGrid__dbf26b481d {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n .DevAiHubPage_statCard__dbf26b481d {\n border-radius: var(--bui-radius-3);\n padding: var(--bui-space-4);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .DevAiHubPage_statCard__dbf26b481d:hover {\n transform: translateY(-2px);\n }\n\n .DevAiHubPage_statCardInner__dbf26b481d {\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n .DevAiHubPage_statValue__dbf26b481d {\n color: #fff;\n line-height: 1;\n }\n\n .DevAiHubPage_statLabel__dbf26b481d {\n color: rgba(255, 255, 255, 0.85);\n font-weight: 500;\n margin-top: var(--bui-space-1);\n }\n\n .DevAiHubPage_statIconBox__dbf26b481d {\n width: 40px;\n height: 40px;\n border-radius: var(--bui-radius-2);\n background-color: rgba(255, 255, 255, 0.2);\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .DevAiHubPage_assetGrid__dbf26b481d {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--bui-space-3);\n }\n\n @media (max-width: 1200px) {\n .DevAiHubPage_assetGrid__dbf26b481d {\n grid-template-columns: repeat(3, 1fr);\n }\n }\n\n @media (max-width: 900px) {\n .DevAiHubPage_assetGrid__dbf26b481d {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 600px) {\n .DevAiHubPage_assetGrid__dbf26b481d {\n grid-template-columns: 1fr;\n }\n }\n\n .DevAiHubPage_emptyState__dbf26b481d {\n padding: 96px 0;\n text-align: center;\n }\n\n .DevAiHubPage_emptyEmoji__dbf26b481d {\n margin-bottom: var(--bui-space-4);\n opacity: 0.15;\n font-size: 5rem;\n }\n\n .DevAiHubPage_paginationRow__dbf26b481d {\n display: flex;\n justify-content: center;\n margin-top: var(--bui-space-8);\n }\n}\n";
4
+ var styles = {"pageRoot":"DevAiHubPage_pageRoot__dbf26b481d","pageHeader":"DevAiHubPage_pageHeader__dbf26b481d","pageHeaderIcon":"DevAiHubPage_pageHeaderIcon__dbf26b481d","content":"DevAiHubPage_content__dbf26b481d","statsGrid":"DevAiHubPage_statsGrid__dbf26b481d","statCard":"DevAiHubPage_statCard__dbf26b481d","statCardInner":"DevAiHubPage_statCardInner__dbf26b481d","statValue":"DevAiHubPage_statValue__dbf26b481d","statLabel":"DevAiHubPage_statLabel__dbf26b481d","statIconBox":"DevAiHubPage_statIconBox__dbf26b481d","assetGrid":"DevAiHubPage_assetGrid__dbf26b481d","emptyState":"DevAiHubPage_emptyState__dbf26b481d","emptyEmoji":"DevAiHubPage_emptyEmoji__dbf26b481d","paginationRow":"DevAiHubPage_paginationRow__dbf26b481d"};
5
5
  styleInject(css_248z);
6
6
 
7
7
  export { styles as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nospt/plugin-dev-ai-hub",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Frontend plugin for Dev AI Hub",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.esm.js",
@@ -58,7 +58,7 @@
58
58
  "@backstage/ui": "^0.14.3",
59
59
  "@mui/icons-material": "^5.15.0",
60
60
  "@mui/material": "^5.15.0",
61
- "@nospt/plugin-dev-ai-hub-common": "^0.2.1",
61
+ "@nospt/plugin-dev-ai-hub-common": "^0.2.2",
62
62
  "@remixicon/react": "^4.6.0",
63
63
  "react-markdown": "^9.0.0",
64
64
  "react-syntax-highlighter": "^16.1.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nospt/plugin-dev-ai-hub",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Frontend plugin for Dev AI Hub",
5
5
  "license": "Apache-2.0",
6
6
  "main": "src/index.ts",
@@ -5,6 +5,16 @@
5
5
  height: 100%;
6
6
  }
7
7
 
8
+ .pageHeader {
9
+ padding: var(--bui-space-6) var(--bui-space-6) 0;
10
+ flex-shrink: 0;
11
+ }
12
+
13
+ .pageHeaderIcon {
14
+ font-size: 28px;
15
+ opacity: 0.85;
16
+ }
17
+
8
18
  .content {
9
19
  flex: 1;
10
20
  padding: var(--bui-space-6);
@@ -2,6 +2,7 @@ import { useState, useMemo, type ElementType } from 'react';
2
2
  import { useSearchParams } from 'react-router-dom';
3
3
  import { Box, Flex, Text, Skeleton, TablePagination } from '@backstage/ui';
4
4
  import { RiArticleLine, RiRobot2Line, RiToolsLine, RiGitBranchLine } from '@remixicon/react';
5
+ import SmartToyIcon from '@mui/icons-material/SmartToy';
5
6
  import type { AssetType, AiTool } from '@nospt/plugin-dev-ai-hub-common';
6
7
  import { AssetCard } from '../AssetCard';
7
8
  import { AssetFilters } from '../AssetFilters';
@@ -86,6 +87,12 @@ export function DevAiHubPage() {
86
87
 
87
88
  return (
88
89
  <div className={styles.pageRoot}>
90
+ <div className={styles.pageHeader}>
91
+ <Flex gap="3" align="center">
92
+ <SmartToyIcon className={styles.pageHeaderIcon} />
93
+ <Text variant="title-large" weight="bold">Dev AI Hub</Text>
94
+ </Flex>
95
+ </div>
89
96
  <div className={styles.content}>
90
97
  {/* Stats row */}
91
98
  <div className={styles.statsGrid}>