Skip to content

Commit 33a868b

Browse files
committed
loading skeleton dan lazy load
1 parent dcd5606 commit 33a868b

4 files changed

Lines changed: 495 additions & 47 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# GridDefense ADS Simulator
1+
# Mas Ari ADS Simulator
22

33
![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)
44
![Built with React](https://img.shields.io/badge/React-19-61dafb.svg)
@@ -10,12 +10,12 @@
1010
## Demo Preview
1111

1212
<p align="center">
13-
<img src="./public/demo/demo.gif" alt="GridDefense ADS Simulator animated demo" width="100%" />
13+
<img src="./public/demo/demo.gif" alt="Mas Ari ADS Simulator animated demo" width="100%" />
1414
</p>
1515

1616
## Overview
1717

18-
**GridDefense ADS Simulator** is a personal open-source coding exploration for learning and teaching smart defense scheme logic based on fast power-flow-aware load shedding.
18+
**Mas Ari ADS Simulator** is a personal open-source coding exploration for learning and teaching smart defense scheme logic based on fast power-flow-aware load shedding.
1919

2020
This demo explores a more explainable defense scheme concept using:
2121

@@ -93,7 +93,7 @@ https://masarray.github.io/ads/
9393
The Vite base path is configured as:
9494

9595
```ts
96-
base: "/ads/"
96+
base: "/ads/";
9797
```
9898

9999
If the repository is renamed again, update `base` in `vite.config.ts` and the public URLs in `index.html` and this README.

index.html

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,56 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66

7-
<title>GridDefense ADS Simulator | Smart Power Flow Defense Scheme</title>
7+
<title>
8+
Mas Ari ADS Simulator | Intelligent Defense Scheme with Power Flow
9+
Awareness
10+
</title>
811
<meta
912
name="description"
1013
content="Open-source GPL-3.0 smart Adaptive Defense Scheme simulator for learning power-flow-aware load shedding, OLS, OGS, islanding, generator runback, blackstart restoration, frequency injection, and explainable trip-matrix reasoning."
1114
/>
1215
<meta
1316
name="keywords"
14-
content="Adaptive Defense Scheme, ADS simulator, smart defense scheme, GridDefense ADS, load shedding simulator, fast power flow load shedding, Power Flow Lite, trip matrix, OLS, overload shedding, OGS, over generation shedding, generator runback, blackstart, restoration, islanding, frequency injection, under frequency load shedding, UFLS, over frequency relay, 81O, power system protection, grid automation, substation automation, SCADA, electrical engineering education"
17+
content="Adaptive Defense Scheme, ADS simulator, smart defense scheme, Mas Ari ADS, load shedding simulator, fast power flow load shedding, Power Flow Lite, trip matrix, OLS, overload shedding, OGS, over generation shedding, generator runback, blackstart, restoration, islanding, frequency injection, under frequency load shedding, UFLS, over frequency relay, 81O, power system protection, grid automation, substation automation, SCADA, electrical engineering education"
1518
/>
1619
<meta name="author" content="Ari Sulistiono" />
1720
<meta name="robots" content="index, follow" />
1821
<meta name="theme-color" content="#06100e" />
1922
<link rel="canonical" href="https://masarray.github.io/ads/" />
2023

2124
<meta property="og:type" content="website" />
22-
<meta property="og:title" content="GridDefense ADS Simulator" />
25+
<meta property="og:title" content="Mas Ari ADS Simulator" />
2326
<meta
2427
property="og:description"
2528
content="Learn smart power-flow-aware defense scheme logic with topology awareness, source-load balance, island detection, OLS, OGS, blackstart, and explainable trip-matrix reasoning."
2629
/>
27-
<meta property="og:site_name" content="GridDefense ADS Simulator" />
30+
<meta property="og:site_name" content="Mas Ari ADS Simulator" />
2831
<meta property="og:url" content="https://masarray.github.io/ads/" />
29-
<meta property="og:image" content="https://masarray.github.io/ads/demo/demo.gif" />
30-
<meta property="og:image:alt" content="GridDefense ADS Simulator animated demo" />
32+
<meta
33+
property="og:image"
34+
content="https://masarray.github.io/ads/demo/demo.gif"
35+
/>
36+
<meta
37+
property="og:image:alt"
38+
content="Mas Ari ADS Simulator animated demo"
39+
/>
3140

3241
<meta name="twitter:card" content="summary_large_image" />
33-
<meta name="twitter:title" content="GridDefense ADS Simulator" />
42+
<meta name="twitter:title" content="Mas Ari ADS Simulator" />
3443
<meta
3544
name="twitter:description"
3645
content="Open-source smart ADS simulator with Power Flow Lite, OLS, OGS, blackstart, and Trip Matrix reasoning."
3746
/>
38-
<meta name="twitter:image" content="https://masarray.github.io/ads/demo/demo.gif" />
47+
<meta
48+
name="twitter:image"
49+
content="https://masarray.github.io/ads/demo/demo.gif"
50+
/>
3951

4052
<script type="application/ld+json">
4153
{
4254
"@context": "https://schema.org",
4355
"@type": "SoftwareApplication",
44-
"name": "GridDefense ADS Simulator",
56+
"name": "Mas Ari ADS Simulator",
4557
"alternateName": "Adaptive Defense Scheme Simulator",
4658
"applicationCategory": "EducationalApplication",
4759
"operatingSystem": "Web Browser",

src/components/cockpit/AppShell.tsx

Lines changed: 123 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,4 @@
1-
/*
2-
* Adaptive Defense Scheme Simulator
3-
* Copyright (C) 2026 Ari Sulistiono
4-
*
5-
* This program is free software: you can redistribute it and/or modify
6-
* it under the terms of the GNU General Public License version 3 only,
7-
* as published by the Free Software Foundation.
8-
*
9-
* This program is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12-
*
13-
* See the LICENSE file for details.
14-
* SPDX-License-Identifier: GPL-3.0-only
15-
*/
16-
17-
import { useRef, useState } from "react";
1+
import { lazy, Suspense, useEffect, useRef, useState } from "react";
182
import { AnimatePresence, motion, useDragControls } from "framer-motion";
193
import {
204
AlertTriangle,
@@ -32,13 +16,25 @@ import {
3216
Zap,
3317
} from "lucide-react";
3418
import { ReasoningRail } from "./ReasoningRail";
35-
import { EngineerPanel } from "./EngineerPanel";
36-
import { EventLogView } from "./EventLogView";
37-
import { SettingsView } from "./SettingsView";
38-
import { TrippingMatrixView } from "./TrippingMatrixView";
39-
import { SldCanvas } from "../sld/SldCanvas";
4019
import { useAdsStore } from "../../lib/ads/store";
4120

21+
const SldCanvas = lazy(() =>
22+
import("../sld/SldCanvas").then((module) => ({ default: module.SldCanvas })),
23+
);
24+
const EngineerPanel = lazy(() =>
25+
import("./EngineerPanel").then((module) => ({ default: module.EngineerPanel })),
26+
);
27+
const EventLogView = lazy(() =>
28+
import("./EventLogView").then((module) => ({ default: module.EventLogView })),
29+
);
30+
const SettingsView = lazy(() =>
31+
import("./SettingsView").then((module) => ({ default: module.SettingsView })),
32+
);
33+
const TrippingMatrixView = lazy(() =>
34+
import("./TrippingMatrixView").then((module) => ({ default: module.TrippingMatrixView })),
35+
);
36+
37+
4238
type AppView = "cockpit" | "settings" | "events" | "matrix";
4339

4440
const navItems: Array<{ id: AppView; label: string; icon: typeof GitFork }> = [
@@ -48,6 +44,82 @@ const navItems: Array<{ id: AppView; label: string; icon: typeof GitFork }> = [
4844
{ id: "matrix", label: "Matrix", icon: Grid3X3 },
4945
];
5046

47+
48+
function StartupSplash() {
49+
return (
50+
<main className="startup-splash" aria-label="Loading GridDefense ADS cockpit">
51+
<div className="startup-orb" aria-hidden="true">
52+
<span />
53+
<span />
54+
<span />
55+
</div>
56+
<section className="startup-card">
57+
<div className="startup-brand-row">
58+
<span className="startup-brand-mark">ADS</span>
59+
<div>
60+
<h1>GridDefense ADS</h1>
61+
<p>Preparing smart defense scheme cockpit</p>
62+
</div>
63+
</div>
64+
<div className="startup-progress" aria-hidden="true">
65+
<span />
66+
</div>
67+
<div className="startup-steps">
68+
<span>Loading SLD model</span>
69+
<span>Preparing Power Flow Lite</span>
70+
<span>Building Trip Matrix</span>
71+
</div>
72+
</section>
73+
</main>
74+
);
75+
}
76+
77+
function ViewLoadingSkeleton({ title }: { title: string }) {
78+
return (
79+
<section className="view-loading-skeleton" aria-label={title}>
80+
<div className="view-loading-card">
81+
<span className="skeleton-dot" aria-hidden="true" />
82+
<div>
83+
<h3>{title}</h3>
84+
<p>Loading only when needed to keep the cockpit startup fast.</p>
85+
</div>
86+
</div>
87+
<div className="skeleton-lines" aria-hidden="true">
88+
<span />
89+
<span />
90+
<span />
91+
</div>
92+
</section>
93+
);
94+
}
95+
96+
function SldStartupSkeleton() {
97+
return (
98+
<section className="sld-stage sld-stage-skeleton" aria-label="Loading SLD">
99+
<div className="sld-startup-card">
100+
<span className="sld-startup-icon" aria-hidden="true" />
101+
<h3>Loading Single Line Diagram</h3>
102+
<p>Preparing topology, busbar state, Power Flow Lite and Trip Matrix overlays.</p>
103+
<div className="sld-skeleton-grid" aria-hidden="true">
104+
<span />
105+
<span />
106+
<span />
107+
</div>
108+
</div>
109+
</section>
110+
);
111+
}
112+
113+
function EngineerPanelSkeleton() {
114+
return (
115+
<section className="engineer-panel-skeleton" aria-label="Loading engineering panel">
116+
<span />
117+
<span />
118+
<span />
119+
</section>
120+
);
121+
}
122+
51123
const viewVariants = {
52124
initial: { opacity: 0, y: 16, scale: 0.985, filter: "blur(6px)" },
53125
animate: {
@@ -67,6 +139,7 @@ const viewVariants = {
67139
};
68140

69141
export function AppShell() {
142+
const [bootReady, setBootReady] = useState(false);
70143
const [activeView, setActiveView] = useState<AppView>("cockpit");
71144
const [draftFrequencyHz, setDraftFrequencyHz] = useState(48.25);
72145
const [frequencyOpen, setFrequencyOpen] = useState(false);
@@ -80,6 +153,13 @@ export function AppShell() {
80153
const runBlackstartSequence = useAdsStore((state) => state.runBlackstartSequence);
81154
const scenarioRun = useAdsStore((state) => state.scenarioRun);
82155

156+
useEffect(() => {
157+
const timer = window.setTimeout(() => setBootReady(true), 760);
158+
return () => window.clearTimeout(timer);
159+
}, []);
160+
161+
if (!bootReady) return <StartupSplash />;
162+
83163
return (
84164
<main className="app-shell">
85165
<header className="top-app-bar">
@@ -549,15 +629,31 @@ export function AppShell() {
549629
{activeView === "cockpit" ? (
550630
<div className="workspace">
551631
<div className="main-column">
552-
<SldCanvas />
553-
<EngineerPanel />
632+
<Suspense fallback={<SldStartupSkeleton />}>
633+
<SldCanvas />
634+
</Suspense>
635+
<Suspense fallback={<EngineerPanelSkeleton />}>
636+
<EngineerPanel />
637+
</Suspense>
554638
</div>
555639
<ReasoningRail />
556640
</div>
557641
) : null}
558-
{activeView === "settings" ? <SettingsView /> : null}
559-
{activeView === "events" ? <EventLogView /> : null}
560-
{activeView === "matrix" ? <TrippingMatrixView /> : null}
642+
{activeView === "settings" ? (
643+
<Suspense fallback={<ViewLoadingSkeleton title="Loading cockpit settings" />}>
644+
<SettingsView />
645+
</Suspense>
646+
) : null}
647+
{activeView === "events" ? (
648+
<Suspense fallback={<ViewLoadingSkeleton title="Loading event log" />}>
649+
<EventLogView />
650+
</Suspense>
651+
) : null}
652+
{activeView === "matrix" ? (
653+
<Suspense fallback={<ViewLoadingSkeleton title="Loading trip matrix" />}>
654+
<TrippingMatrixView />
655+
</Suspense>
656+
) : null}
561657
</motion.div>
562658
</AnimatePresence>
563659
</main>

0 commit comments

Comments
 (0)