From 49a15e1ac996c3941ba72817e380285cda25ba9c Mon Sep 17 00:00:00 2001 From: OG T Date: Thu, 9 Apr 2026 18:09:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20G1=20=E9=AA=A8=E6=9E=B6=E5=B1=8F?= =?UTF-8?q?=E5=8F=96=E4=BB=A3=E8=BC=89=E5=85=A5=E4=B8=AD=20+=20S8=20?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=8F=90=E4=BA=A4=20=E2=80=94=20Sprint=205R?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - G1: PulseSkeleton + CardSkeleton 元件 - 首頁所有 LobsterLoading 替換為 PulseSkeleton/CardSkeleton - Tab 2/4 載入狀態用 CardSkeleton - 活躍事件載入用 PulseSkeleton Sprint 5R Phase 1B+1C 全部完成: S1(KPI卡片) S2(FlowPipeline OpenClaw) S3(AI提案) S4(環形圖) S5(時間線) S6(Terminal) S7(待審批) S8(拓撲群組+主機) S9(AI模型) S10(監控3×2) S11(Tab修復) S12(頁面修復) G1(骨架屏) Co-Authored-By: Claude Sonnet 4.6 --- apps/web/src/app/[locale]/page.tsx | 8 +-- .../src/components/shared/pulse-skeleton.tsx | 50 +++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/components/shared/pulse-skeleton.tsx diff --git a/apps/web/src/app/[locale]/page.tsx b/apps/web/src/app/[locale]/page.tsx index a7f54e76..f443aab4 100644 --- a/apps/web/src/app/[locale]/page.tsx +++ b/apps/web/src/app/[locale]/page.tsx @@ -25,7 +25,7 @@ import { OpenClawPanel } from '@/components/ai/openclaw-panel' import { HostGrid, type HostInfo, type HostService } from '@/components/infra/host-grid' import { AppLayout } from '@/components/layout' import { PageTabs, type TabConfig } from '@/components/layout/page-tabs' -import { LobsterLoading } from '@/components/shared/lobster-loading' +import { PulseSkeleton, CardSkeleton } from '@/components/shared/pulse-skeleton' import { ServiceTopology } from '@/components/topology' import { BarChart3, Flame, Telescope, FlaskConical, Activity, GitBranch } from 'lucide-react' import { DispositionMini } from '@/components/shared/disposition-mini' @@ -56,7 +56,7 @@ function AlertsAndApprovalsTab() { }).finally(() => setLoading(false)) }, []) - if (loading) return + if (loading) return return (
@@ -172,7 +172,7 @@ function DispositionTab() { .finally(() => setLoading(false)) }, []) - if (loading) return + if (loading) return const s = data?.summary if (!s || s.total === 0) return ( @@ -730,7 +730,7 @@ export default function Home({ params }: { params: { locale: string } }) {
{isIncidentsLoading ? ( - + ) : incidentsError ? (
{incidentsError}
) : (incidents?.length ?? 0) === 0 ? ( diff --git a/apps/web/src/components/shared/pulse-skeleton.tsx b/apps/web/src/components/shared/pulse-skeleton.tsx new file mode 100644 index 00000000..a0cbdd85 --- /dev/null +++ b/apps/web/src/components/shared/pulse-skeleton.tsx @@ -0,0 +1,50 @@ +'use client' + +/** + * PulseSkeleton — 脈動骨架屏 (取代「載入中...」文字) + * Sprint 5R G1: Gemini 建議 — AI 呼吸感知 + * @created 2026-04-09 Claude Opus 4.6 Asia/Taipei + */ + +export function PulseSkeleton({ width = '100%', height = 16, borderRadius = 4, count = 1 }: { + width?: string | number + height?: number + borderRadius?: number + count?: number +}) { + return ( + <> + +
+ {Array.from({ length: count }).map((_, i) => ( +
+ ))} +
+ + ) +} + +/** 卡片級骨架屏 — 模擬 .card 結構 */ +export function CardSkeleton({ lines = 3 }: { lines?: number }) { + return ( +
+ +
+ +
+
+ ) +}