@servantcdh/ez-planet-cosmos 0.1.0
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.
- package/README.md +232 -0
- package/dist/index.d.ts +175 -0
- package/dist/index.js +946 -0
- package/dist/style.css +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# @servantcdh/ez-planet-cosmos
|
|
2
|
+
|
|
3
|
+
ReactFlow + Three.js 기반의 코스모스(우주) 스타일 플랫폼 시각화 라이브러리.
|
|
4
|
+
|
|
5
|
+
행성(Planet), 궤도(Orbit), 위성(Satellite) 메타포로 MLOps 파이프라인을 시각화합니다.
|
|
6
|
+
|
|
7
|
+
[](https://stackblitz.com/github/servantcdh/ez-planet/tree/master/examples/cosmos-demo?file=src/App.tsx)
|
|
8
|
+
|
|
9
|
+
## 설치
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @servantcdh/ez-planet-cosmos
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Dependencies
|
|
16
|
+
|
|
17
|
+
### Peer Dependencies (호스트 앱에서 설치)
|
|
18
|
+
|
|
19
|
+
| Package | Version | 용도 |
|
|
20
|
+
|---------|---------|------|
|
|
21
|
+
| `react` | `^18.0.0` | UI 런타임 |
|
|
22
|
+
| `react-dom` | `^18.0.0` | DOM 렌더링 |
|
|
23
|
+
| `@xyflow/react` | `^12.0.0` | 그래프 시각화 |
|
|
24
|
+
| `@react-three/fiber` | `^8.0.0` | Three.js React 바인딩 |
|
|
25
|
+
| `@react-three/drei` | `^9.0.0` | Three.js 유틸리티 |
|
|
26
|
+
| `three` | `>=0.160.0` | 3D 렌더링 (StarField) |
|
|
27
|
+
| `framer-motion` | `^11.0.0` | 애니메이션 |
|
|
28
|
+
|
|
29
|
+
### Bundled Dependencies (라이브러리에 포함)
|
|
30
|
+
|
|
31
|
+
| Package | 용도 |
|
|
32
|
+
|---------|------|
|
|
33
|
+
| `zustand` | 내부 상태 관리 |
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { CosmosCanvas } from '@servantcdh/ez-planet-cosmos'
|
|
39
|
+
import type { PlanetConfig, OrbitConfig } from '@servantcdh/ez-planet-cosmos'
|
|
40
|
+
import '@servantcdh/ez-planet-cosmos/dist/style.css'
|
|
41
|
+
import '@xyflow/react/dist/style.css'
|
|
42
|
+
|
|
43
|
+
const planets: PlanetConfig[] = [
|
|
44
|
+
{
|
|
45
|
+
id: 'terra',
|
|
46
|
+
label: 'Terra',
|
|
47
|
+
subtitle: 'Dataset',
|
|
48
|
+
color: '#3498db',
|
|
49
|
+
x: 200, y: 300,
|
|
50
|
+
satellites: [
|
|
51
|
+
{ id: 'upload', label: 'Upload', icon: '📤', route: '/data/upload' },
|
|
52
|
+
{ id: 'browse', label: 'Browse', icon: '🔍', route: '/data/browse' },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'nova',
|
|
57
|
+
label: 'Nova',
|
|
58
|
+
subtitle: 'Model',
|
|
59
|
+
color: '#e74c3c',
|
|
60
|
+
x: 650, y: 310,
|
|
61
|
+
satellites: [
|
|
62
|
+
{ id: 'train', label: 'Training', icon: '🧠', route: '/model/train' },
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
const orbits: OrbitConfig[] = [
|
|
68
|
+
{ source: 'terra', target: 'nova', label: 'Dataset → Model' },
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
return (
|
|
73
|
+
<CosmosCanvas
|
|
74
|
+
planets={planets}
|
|
75
|
+
orbits={orbits}
|
|
76
|
+
topBar={<MyTopBar />}
|
|
77
|
+
onPlanetClick={(planet) => console.log('Click:', planet.id)}
|
|
78
|
+
onPlanetEnter={(planet) => console.log('Enter:', planet.id)}
|
|
79
|
+
onSatelliteClick={({ satellite }) => {
|
|
80
|
+
if (satellite?.route) router.push(satellite.route)
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Props Reference
|
|
88
|
+
|
|
89
|
+
### CosmosCanvasProps
|
|
90
|
+
|
|
91
|
+
| Prop | Type | Required | Description |
|
|
92
|
+
|------|------|----------|-------------|
|
|
93
|
+
| `planets` | `PlanetConfig[]` | Yes | 행성 목록 |
|
|
94
|
+
| `orbits` | `OrbitConfig[]` | Yes | 궤도(연결선) 목록 |
|
|
95
|
+
| `onPlanetClick` | `(planet: PlanetConfig) => void` | No | 행성 클릭 (사이드패널 열기) |
|
|
96
|
+
| `onPlanetEnter` | `(planet: PlanetConfig) => void` | No | 행성 더블클릭 (내부 진입) |
|
|
97
|
+
| `onPlanetExit` | `() => void` | No | 행성 내부에서 나가기 |
|
|
98
|
+
| `onSatelliteClick` | `(event: CosmosNavigateEvent) => void` | No | 위성 클릭 (라우팅) |
|
|
99
|
+
| `topBar` | `ReactNode` | No | 상단 슬롯 (호스트 앱 네비게이션 바) |
|
|
100
|
+
| `starField` | `boolean \| StarFieldConfig` | No | Three.js 별 배경 (기본: `true`) |
|
|
101
|
+
| `theme` | `Partial<CosmosTheme>` | No | 테마 커스터마이징 |
|
|
102
|
+
| `tutorialSteps` | `TutorialStep[]` | No | 온보딩 튜토리얼 단계 |
|
|
103
|
+
| `showTutorial` | `boolean` | No | 튜토리얼 표시 여부 |
|
|
104
|
+
| `onTutorialComplete` | `() => void` | No | 튜토리얼 완료 콜백 |
|
|
105
|
+
| `initialView` | `CosmosView` | No | 초기 뷰 상태 |
|
|
106
|
+
| `onViewChange` | `(view: CosmosView) => void` | No | 뷰 상태 변경 콜백 |
|
|
107
|
+
| `renderPlanetInterior` | `(planet, onExit) => ReactNode` | No | 행성 내부 커스텀 렌더 |
|
|
108
|
+
| `nodeTypes` | `Record<string, ComponentType>` | No | 커스텀 ReactFlow 노드 타입 |
|
|
109
|
+
| `edgeTypes` | `Record<string, ComponentType>` | No | 커스텀 ReactFlow 엣지 타입 |
|
|
110
|
+
|
|
111
|
+
### PlanetConfig
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
interface PlanetConfig {
|
|
115
|
+
id: string
|
|
116
|
+
label: string
|
|
117
|
+
subtitle: string
|
|
118
|
+
color: string // 행성 색상 (radial gradient, glow)
|
|
119
|
+
x: number // ReactFlow 위치
|
|
120
|
+
y: number
|
|
121
|
+
satellites: Satellite[] // 위성 (서브메뉴)
|
|
122
|
+
status?: PlanetStatus // 'idle' | 'running' | 'error' | 'success' | 'disabled'
|
|
123
|
+
icon?: ReactNode
|
|
124
|
+
meta?: Record<string, unknown>
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Satellite
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
interface Satellite {
|
|
132
|
+
id: string
|
|
133
|
+
label: string
|
|
134
|
+
route?: string // 클릭 시 라우팅 경로
|
|
135
|
+
icon?: string | ReactNode
|
|
136
|
+
summary?: { total?: number; running?: number; failed?: number }
|
|
137
|
+
quickActions?: Array<{ label: string; action: string }>
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 주요 기능
|
|
142
|
+
|
|
143
|
+
### StarField
|
|
144
|
+
|
|
145
|
+
Three.js 기반 우주 배경. 2000개 별 + 500개 성운 파티클.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<CosmosCanvas
|
|
149
|
+
starField={{ starCount: 3000, nebulaCount: 800, rotationSpeed: 0.0005 }}
|
|
150
|
+
...
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### PlanetNode
|
|
155
|
+
|
|
156
|
+
상태별 시각 효과:
|
|
157
|
+
- `idle` — 기본 글로우
|
|
158
|
+
- `running` — 펄스 애니메이션
|
|
159
|
+
- `error` — 빨간 글로우
|
|
160
|
+
- `success` — 초록 글로우
|
|
161
|
+
- `disabled` — 회색 디밍
|
|
162
|
+
|
|
163
|
+
### SidePanel
|
|
164
|
+
|
|
165
|
+
행성 클릭 시 슬라이드-인 패널. 위성 목록, 요약 통계, Enter 버튼.
|
|
166
|
+
|
|
167
|
+
### PlanetInterior
|
|
168
|
+
|
|
169
|
+
행성 더블클릭 시 줌인 + 내부 뷰. 원형 위성 배치. `renderPlanetInterior`로 커스터마이징 가능.
|
|
170
|
+
|
|
171
|
+
### Tutorial System
|
|
172
|
+
|
|
173
|
+
3단계 온보딩: Welcome → Stepping (키보드/클릭) → Complete.
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
const tutorialSteps: TutorialStep[] = [
|
|
177
|
+
{ planetId: 'terra', title: 'Dataset', description: '데이터를 관리합니다.' },
|
|
178
|
+
{ planetId: 'nova', title: 'Model', description: '모델을 학습합니다.' },
|
|
179
|
+
]
|
|
180
|
+
|
|
181
|
+
<CosmosCanvas
|
|
182
|
+
tutorialSteps={tutorialSteps}
|
|
183
|
+
showTutorial={isFirstVisit}
|
|
184
|
+
onTutorialComplete={() => markTutorialDone()}
|
|
185
|
+
...
|
|
186
|
+
/>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### TopBar Slot
|
|
190
|
+
|
|
191
|
+
호스트 앱의 네비게이션 바를 주입합니다.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
<CosmosCanvas topBar={<MyAppTopBar user={user} onLogout={logout} />} ... />
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Theming
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
<CosmosCanvas
|
|
201
|
+
theme={{
|
|
202
|
+
background: '#0a0a1a',
|
|
203
|
+
textColor: '#e2e8f0',
|
|
204
|
+
panelBackground: 'rgba(20, 20, 40, 0.9)',
|
|
205
|
+
panelBorder: 'rgba(255, 255, 255, 0.1)',
|
|
206
|
+
controlsBackground: 'rgba(20, 20, 40, 0.8)',
|
|
207
|
+
fontFamily: 'Pretendard, sans-serif',
|
|
208
|
+
}}
|
|
209
|
+
...
|
|
210
|
+
/>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## 개별 컴포넌트 사용
|
|
214
|
+
|
|
215
|
+
모든 하위 컴포넌트를 개별적으로 import할 수 있습니다.
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
import {
|
|
219
|
+
CosmosCanvas,
|
|
220
|
+
PlanetNode,
|
|
221
|
+
OrbitEdge,
|
|
222
|
+
StarField,
|
|
223
|
+
SidePanel,
|
|
224
|
+
ZoomTransition,
|
|
225
|
+
PlanetInterior,
|
|
226
|
+
CosmosTutorial,
|
|
227
|
+
} from '@servantcdh/ez-planet-cosmos'
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { EdgeProps } from '@xyflow/react';
|
|
2
|
+
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
|
3
|
+
import { MemoExoticComponent } from 'react';
|
|
4
|
+
import { NodeProps } from '@xyflow/react';
|
|
5
|
+
import { ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* CosmosCanvas — ReactFlowProvider wrapper for the cosmic visualization.
|
|
9
|
+
*
|
|
10
|
+
* Renders: StarField + ReactFlow graph + SidePanel + PlanetInterior + Tutorial
|
|
11
|
+
* All data via props, no API dependencies.
|
|
12
|
+
*/
|
|
13
|
+
export declare function CosmosCanvas(props: CosmosCanvasProps): JSX_2.Element;
|
|
14
|
+
|
|
15
|
+
export declare interface CosmosCanvasProps {
|
|
16
|
+
planets: PlanetConfig[];
|
|
17
|
+
orbits: OrbitConfig[];
|
|
18
|
+
onPlanetClick?: (planet: PlanetConfig) => void;
|
|
19
|
+
onPlanetEnter?: (planet: PlanetConfig) => void;
|
|
20
|
+
onPlanetExit?: () => void;
|
|
21
|
+
onSatelliteClick?: (event: CosmosNavigateEvent) => void;
|
|
22
|
+
tutorialSteps?: TutorialStep[];
|
|
23
|
+
showTutorial?: boolean;
|
|
24
|
+
onTutorialComplete?: () => void;
|
|
25
|
+
initialView?: CosmosView;
|
|
26
|
+
onViewChange?: (view: CosmosView) => void;
|
|
27
|
+
renderPlanetInterior?: (planet: PlanetConfig, onExit: () => void) => ReactNode;
|
|
28
|
+
topBar?: ReactNode;
|
|
29
|
+
theme?: Partial<CosmosTheme>;
|
|
30
|
+
starField?: boolean | StarFieldConfig;
|
|
31
|
+
nodeTypes?: Record<string, React.ComponentType<any>>;
|
|
32
|
+
edgeTypes?: Record<string, React.ComponentType<any>>;
|
|
33
|
+
className?: string;
|
|
34
|
+
style?: React.CSSProperties;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export declare interface CosmosNavigateEvent {
|
|
38
|
+
planetId: string;
|
|
39
|
+
satelliteId?: string;
|
|
40
|
+
satellite?: Satellite;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export declare interface CosmosTheme {
|
|
44
|
+
background: string;
|
|
45
|
+
starColor: string;
|
|
46
|
+
nebulaColor: string;
|
|
47
|
+
textColor: string;
|
|
48
|
+
panelBackground: string;
|
|
49
|
+
panelBorder: string;
|
|
50
|
+
controlsBackground: string;
|
|
51
|
+
fontFamily: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare function CosmosTutorial({ steps, onHighlightPlanet, onStepChange, onFinish, flyTo: _flyTo, }: CosmosTutorialProps): JSX_2.Element;
|
|
55
|
+
|
|
56
|
+
declare interface CosmosTutorialProps {
|
|
57
|
+
steps: TutorialStep[];
|
|
58
|
+
onHighlightPlanet: (planetId: string | null) => void;
|
|
59
|
+
onStepChange: (stepIdx: number) => void;
|
|
60
|
+
onFinish: () => void;
|
|
61
|
+
/** Camera flyover function — called with (x, y, zoom, duration) */
|
|
62
|
+
flyTo?: (x: number, y: number, zoom: number, duration: number) => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export declare type CosmosView = {
|
|
66
|
+
mode: 'universe';
|
|
67
|
+
} | {
|
|
68
|
+
mode: 'inspecting';
|
|
69
|
+
planetId: string;
|
|
70
|
+
} | {
|
|
71
|
+
mode: 'entering';
|
|
72
|
+
planetId: string;
|
|
73
|
+
} | {
|
|
74
|
+
mode: 'planet';
|
|
75
|
+
planetId: string;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export declare type CosmosWorkspaceProps = CosmosCanvasProps;
|
|
79
|
+
|
|
80
|
+
export declare interface OrbitConfig {
|
|
81
|
+
source: string;
|
|
82
|
+
target: string;
|
|
83
|
+
label?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export declare const OrbitEdge: MemoExoticComponent<typeof OrbitEdgeComponent>;
|
|
87
|
+
|
|
88
|
+
declare function OrbitEdgeComponent({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, style, }: EdgeProps): JSX_2.Element;
|
|
89
|
+
|
|
90
|
+
export declare interface PlanetConfig {
|
|
91
|
+
id: string;
|
|
92
|
+
label: string;
|
|
93
|
+
subtitle: string;
|
|
94
|
+
color: string;
|
|
95
|
+
x: number;
|
|
96
|
+
y: number;
|
|
97
|
+
satellites: Satellite[];
|
|
98
|
+
status?: PlanetStatus;
|
|
99
|
+
icon?: ReactNode;
|
|
100
|
+
meta?: Record<string, unknown>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export declare function PlanetInterior({ planet, onExit, onSatelliteClick, renderCustom, }: PlanetInteriorProps): JSX_2.Element;
|
|
104
|
+
|
|
105
|
+
declare interface PlanetInteriorProps {
|
|
106
|
+
planet: PlanetConfig;
|
|
107
|
+
onExit: () => void;
|
|
108
|
+
onSatelliteClick?: (event: CosmosNavigateEvent) => void;
|
|
109
|
+
renderCustom?: (planet: PlanetConfig, onExit: () => void) => ReactNode;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export declare const PlanetNode: MemoExoticComponent<typeof PlanetNodeComponent>;
|
|
113
|
+
|
|
114
|
+
declare function PlanetNodeComponent({ data }: NodeProps): JSX_2.Element;
|
|
115
|
+
|
|
116
|
+
export declare type PlanetStatus = 'idle' | 'running' | 'error' | 'success' | 'disabled';
|
|
117
|
+
|
|
118
|
+
export declare interface Satellite {
|
|
119
|
+
id: string;
|
|
120
|
+
label: string;
|
|
121
|
+
route?: string;
|
|
122
|
+
icon?: string | ReactNode;
|
|
123
|
+
summary?: {
|
|
124
|
+
total?: number;
|
|
125
|
+
running?: number;
|
|
126
|
+
failed?: number;
|
|
127
|
+
};
|
|
128
|
+
quickActions?: Array<{
|
|
129
|
+
label: string;
|
|
130
|
+
action: string;
|
|
131
|
+
}>;
|
|
132
|
+
meta?: Record<string, unknown>;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export declare function SidePanel({ planet, onClose, onEnterPlanet, onSatelliteClick, }: SidePanelProps): JSX_2.Element;
|
|
136
|
+
|
|
137
|
+
declare interface SidePanelProps {
|
|
138
|
+
planet: PlanetConfig;
|
|
139
|
+
onClose: () => void;
|
|
140
|
+
onEnterPlanet: () => void;
|
|
141
|
+
onSatelliteClick?: (event: CosmosNavigateEvent) => void;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export declare function StarField({ config, background }: StarFieldProps): JSX_2.Element;
|
|
145
|
+
|
|
146
|
+
export declare interface StarFieldConfig {
|
|
147
|
+
starCount?: number;
|
|
148
|
+
nebulaCount?: number;
|
|
149
|
+
starColor?: string;
|
|
150
|
+
nebulaColor?: string;
|
|
151
|
+
starSize?: number;
|
|
152
|
+
nebulaSize?: number;
|
|
153
|
+
rotationSpeed?: number;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare interface StarFieldProps {
|
|
157
|
+
config?: StarFieldConfig;
|
|
158
|
+
background?: string;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export declare interface TutorialStep {
|
|
162
|
+
planetId: string;
|
|
163
|
+
title: string;
|
|
164
|
+
description: string;
|
|
165
|
+
chipText?: string;
|
|
166
|
+
chipColor?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export declare function ZoomTransition({ planet }: ZoomTransitionProps): JSX_2.Element;
|
|
170
|
+
|
|
171
|
+
declare interface ZoomTransitionProps {
|
|
172
|
+
planet: PlanetConfig;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export { }
|