stellavault 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.
Files changed (294) hide show
  1. package/.env.example +12 -0
  2. package/CLAUDE.md +39 -0
  3. package/CONTRIBUTING.md +65 -0
  4. package/LICENSE +21 -0
  5. package/README.md +182 -0
  6. package/memory/MEMORY.md +25 -0
  7. package/package.json +33 -0
  8. package/packages/cli/bin/ekh.js +2 -0
  9. package/packages/cli/bin/stellavault.js +2 -0
  10. package/packages/cli/dist/commands/brief-cmd.d.ts +2 -0
  11. package/packages/cli/dist/commands/brief-cmd.d.ts.map +1 -0
  12. package/packages/cli/dist/commands/brief-cmd.js +82 -0
  13. package/packages/cli/dist/commands/brief-cmd.js.map +1 -0
  14. package/packages/cli/dist/commands/capture-cmd.d.ts +7 -0
  15. package/packages/cli/dist/commands/capture-cmd.d.ts.map +1 -0
  16. package/packages/cli/dist/commands/capture-cmd.js +31 -0
  17. package/packages/cli/dist/commands/capture-cmd.js.map +1 -0
  18. package/packages/cli/dist/commands/card-cmd.d.ts +4 -0
  19. package/packages/cli/dist/commands/card-cmd.d.ts.map +1 -0
  20. package/packages/cli/dist/commands/card-cmd.js +26 -0
  21. package/packages/cli/dist/commands/card-cmd.js.map +1 -0
  22. package/packages/cli/dist/commands/clip-cmd.d.ts +4 -0
  23. package/packages/cli/dist/commands/clip-cmd.d.ts.map +1 -0
  24. package/packages/cli/dist/commands/clip-cmd.js +151 -0
  25. package/packages/cli/dist/commands/clip-cmd.js.map +1 -0
  26. package/packages/cli/dist/commands/cloud-cmd.d.ts +4 -0
  27. package/packages/cli/dist/commands/cloud-cmd.d.ts.map +1 -0
  28. package/packages/cli/dist/commands/cloud-cmd.js +64 -0
  29. package/packages/cli/dist/commands/cloud-cmd.js.map +1 -0
  30. package/packages/cli/dist/commands/contradictions-cmd.d.ts +2 -0
  31. package/packages/cli/dist/commands/contradictions-cmd.d.ts.map +1 -0
  32. package/packages/cli/dist/commands/contradictions-cmd.js +34 -0
  33. package/packages/cli/dist/commands/contradictions-cmd.js.map +1 -0
  34. package/packages/cli/dist/commands/decay-cmd.d.ts +2 -0
  35. package/packages/cli/dist/commands/decay-cmd.d.ts.map +1 -0
  36. package/packages/cli/dist/commands/decay-cmd.js +48 -0
  37. package/packages/cli/dist/commands/decay-cmd.js.map +1 -0
  38. package/packages/cli/dist/commands/digest-cmd.d.ts +4 -0
  39. package/packages/cli/dist/commands/digest-cmd.d.ts.map +1 -0
  40. package/packages/cli/dist/commands/digest-cmd.js +79 -0
  41. package/packages/cli/dist/commands/digest-cmd.js.map +1 -0
  42. package/packages/cli/dist/commands/duplicates-cmd.d.ts +4 -0
  43. package/packages/cli/dist/commands/duplicates-cmd.d.ts.map +1 -0
  44. package/packages/cli/dist/commands/duplicates-cmd.js +30 -0
  45. package/packages/cli/dist/commands/duplicates-cmd.js.map +1 -0
  46. package/packages/cli/dist/commands/federate-cmd.d.ts +5 -0
  47. package/packages/cli/dist/commands/federate-cmd.d.ts.map +1 -0
  48. package/packages/cli/dist/commands/federate-cmd.js +217 -0
  49. package/packages/cli/dist/commands/federate-cmd.js.map +1 -0
  50. package/packages/cli/dist/commands/gaps-cmd.d.ts +2 -0
  51. package/packages/cli/dist/commands/gaps-cmd.d.ts.map +1 -0
  52. package/packages/cli/dist/commands/gaps-cmd.js +33 -0
  53. package/packages/cli/dist/commands/gaps-cmd.js.map +1 -0
  54. package/packages/cli/dist/commands/graph-cmd.d.ts +2 -0
  55. package/packages/cli/dist/commands/graph-cmd.d.ts.map +1 -0
  56. package/packages/cli/dist/commands/graph-cmd.js +77 -0
  57. package/packages/cli/dist/commands/graph-cmd.js.map +1 -0
  58. package/packages/cli/dist/commands/index-cmd.d.ts +2 -0
  59. package/packages/cli/dist/commands/index-cmd.d.ts.map +1 -0
  60. package/packages/cli/dist/commands/index-cmd.js +57 -0
  61. package/packages/cli/dist/commands/index-cmd.js.map +1 -0
  62. package/packages/cli/dist/commands/init-cmd.d.ts +2 -0
  63. package/packages/cli/dist/commands/init-cmd.d.ts.map +1 -0
  64. package/packages/cli/dist/commands/init-cmd.js +123 -0
  65. package/packages/cli/dist/commands/init-cmd.js.map +1 -0
  66. package/packages/cli/dist/commands/learn-cmd.d.ts +2 -0
  67. package/packages/cli/dist/commands/learn-cmd.d.ts.map +1 -0
  68. package/packages/cli/dist/commands/learn-cmd.js +48 -0
  69. package/packages/cli/dist/commands/learn-cmd.js.map +1 -0
  70. package/packages/cli/dist/commands/pack-cmd.d.ts +15 -0
  71. package/packages/cli/dist/commands/pack-cmd.d.ts.map +1 -0
  72. package/packages/cli/dist/commands/pack-cmd.js +93 -0
  73. package/packages/cli/dist/commands/pack-cmd.js.map +1 -0
  74. package/packages/cli/dist/commands/review-cmd.d.ts +4 -0
  75. package/packages/cli/dist/commands/review-cmd.d.ts.map +1 -0
  76. package/packages/cli/dist/commands/review-cmd.js +107 -0
  77. package/packages/cli/dist/commands/review-cmd.js.map +1 -0
  78. package/packages/cli/dist/commands/search-cmd.d.ts +4 -0
  79. package/packages/cli/dist/commands/search-cmd.d.ts.map +1 -0
  80. package/packages/cli/dist/commands/search-cmd.js +38 -0
  81. package/packages/cli/dist/commands/search-cmd.js.map +1 -0
  82. package/packages/cli/dist/commands/serve-cmd.d.ts +2 -0
  83. package/packages/cli/dist/commands/serve-cmd.d.ts.map +1 -0
  84. package/packages/cli/dist/commands/serve-cmd.js +14 -0
  85. package/packages/cli/dist/commands/serve-cmd.js.map +1 -0
  86. package/packages/cli/dist/commands/status-cmd.d.ts +2 -0
  87. package/packages/cli/dist/commands/status-cmd.d.ts.map +1 -0
  88. package/packages/cli/dist/commands/status-cmd.js +33 -0
  89. package/packages/cli/dist/commands/status-cmd.js.map +1 -0
  90. package/packages/cli/dist/commands/sync-cmd.d.ts +5 -0
  91. package/packages/cli/dist/commands/sync-cmd.d.ts.map +1 -0
  92. package/packages/cli/dist/commands/sync-cmd.js +62 -0
  93. package/packages/cli/dist/commands/sync-cmd.js.map +1 -0
  94. package/packages/cli/dist/commands/vault-cmd.d.ts +10 -0
  95. package/packages/cli/dist/commands/vault-cmd.d.ts.map +1 -0
  96. package/packages/cli/dist/commands/vault-cmd.js +54 -0
  97. package/packages/cli/dist/commands/vault-cmd.js.map +1 -0
  98. package/packages/cli/dist/index.d.ts +2 -0
  99. package/packages/cli/dist/index.d.ts.map +1 -0
  100. package/packages/cli/dist/index.js +156 -0
  101. package/packages/cli/dist/index.js.map +1 -0
  102. package/packages/cli/package.json +24 -0
  103. package/packages/cli/src/commands/brief-cmd.ts +87 -0
  104. package/packages/cli/src/commands/capture-cmd.ts +34 -0
  105. package/packages/cli/src/commands/card-cmd.ts +29 -0
  106. package/packages/cli/src/commands/clip-cmd.ts +172 -0
  107. package/packages/cli/src/commands/cloud-cmd.ts +75 -0
  108. package/packages/cli/src/commands/contradictions-cmd.ts +41 -0
  109. package/packages/cli/src/commands/decay-cmd.ts +57 -0
  110. package/packages/cli/src/commands/digest-cmd.ts +89 -0
  111. package/packages/cli/src/commands/duplicates-cmd.ts +38 -0
  112. package/packages/cli/src/commands/federate-cmd.ts +236 -0
  113. package/packages/cli/src/commands/gaps-cmd.ts +40 -0
  114. package/packages/cli/src/commands/graph-cmd.ts +88 -0
  115. package/packages/cli/src/commands/index-cmd.ts +65 -0
  116. package/packages/cli/src/commands/init-cmd.ts +145 -0
  117. package/packages/cli/src/commands/learn-cmd.ts +56 -0
  118. package/packages/cli/src/commands/pack-cmd.ts +121 -0
  119. package/packages/cli/src/commands/review-cmd.ts +125 -0
  120. package/packages/cli/src/commands/search-cmd.ts +45 -0
  121. package/packages/cli/src/commands/serve-cmd.ts +17 -0
  122. package/packages/cli/src/commands/status-cmd.ts +37 -0
  123. package/packages/cli/src/commands/sync-cmd.ts +68 -0
  124. package/packages/cli/src/commands/vault-cmd.ts +64 -0
  125. package/packages/cli/src/index.ts +187 -0
  126. package/packages/core/package.json +40 -0
  127. package/packages/core/src/api/dashboard.ts +138 -0
  128. package/packages/core/src/api/graph-data.ts +286 -0
  129. package/packages/core/src/api/pwa.ts +82 -0
  130. package/packages/core/src/api/server.ts +660 -0
  131. package/packages/core/src/capture/voice.ts +168 -0
  132. package/packages/core/src/cloud/index.ts +2 -0
  133. package/packages/core/src/cloud/sync.ts +167 -0
  134. package/packages/core/src/config.ts +82 -0
  135. package/packages/core/src/federation/credits.ts +80 -0
  136. package/packages/core/src/federation/hyperswarm.d.ts +19 -0
  137. package/packages/core/src/federation/identity.ts +90 -0
  138. package/packages/core/src/federation/index.ts +8 -0
  139. package/packages/core/src/federation/node.ts +235 -0
  140. package/packages/core/src/federation/privacy.ts +52 -0
  141. package/packages/core/src/federation/reputation.ts +202 -0
  142. package/packages/core/src/federation/search.ts +129 -0
  143. package/packages/core/src/federation/sharing.ts +165 -0
  144. package/packages/core/src/federation/trust.ts +76 -0
  145. package/packages/core/src/federation/types.ts +25 -0
  146. package/packages/core/src/i18n/index.ts +85 -0
  147. package/packages/core/src/index.ts +133 -0
  148. package/packages/core/src/indexer/chunker.ts +180 -0
  149. package/packages/core/src/indexer/embedder.ts +9 -0
  150. package/packages/core/src/indexer/index.ts +113 -0
  151. package/packages/core/src/indexer/local-embedder.ts +35 -0
  152. package/packages/core/src/indexer/scanner.ts +142 -0
  153. package/packages/core/src/indexer/watcher.ts +62 -0
  154. package/packages/core/src/intelligence/contradiction-detector.ts +134 -0
  155. package/packages/core/src/intelligence/decay-engine.ts +229 -0
  156. package/packages/core/src/intelligence/duplicate-detector.ts +71 -0
  157. package/packages/core/src/intelligence/fsrs.ts +79 -0
  158. package/packages/core/src/intelligence/gap-detector.ts +109 -0
  159. package/packages/core/src/intelligence/learning-path.ts +86 -0
  160. package/packages/core/src/intelligence/notifications.ts +106 -0
  161. package/packages/core/src/intelligence/predictive-gaps.ts +94 -0
  162. package/packages/core/src/intelligence/semantic-versioning.ts +97 -0
  163. package/packages/core/src/intelligence/types.ts +28 -0
  164. package/packages/core/src/mcp/custom-tools.ts +97 -0
  165. package/packages/core/src/mcp/index.ts +1 -0
  166. package/packages/core/src/mcp/server.ts +142 -0
  167. package/packages/core/src/mcp/tools/agentic-graph.ts +96 -0
  168. package/packages/core/src/mcp/tools/brief.ts +49 -0
  169. package/packages/core/src/mcp/tools/decay.ts +40 -0
  170. package/packages/core/src/mcp/tools/decision-journal.ts +95 -0
  171. package/packages/core/src/mcp/tools/export.ts +72 -0
  172. package/packages/core/src/mcp/tools/federated-search.ts +43 -0
  173. package/packages/core/src/mcp/tools/generate-claude-md.ts +130 -0
  174. package/packages/core/src/mcp/tools/get-document.ts +26 -0
  175. package/packages/core/src/mcp/tools/get-related.ts +41 -0
  176. package/packages/core/src/mcp/tools/learning-path.ts +52 -0
  177. package/packages/core/src/mcp/tools/list-topics.ts +20 -0
  178. package/packages/core/src/mcp/tools/search.ts +35 -0
  179. package/packages/core/src/mcp/tools/snapshot.ts +98 -0
  180. package/packages/core/src/multi-vault/index.ts +118 -0
  181. package/packages/core/src/pack/creator.ts +127 -0
  182. package/packages/core/src/pack/exporter.ts +21 -0
  183. package/packages/core/src/pack/importer.ts +82 -0
  184. package/packages/core/src/pack/index.ts +5 -0
  185. package/packages/core/src/pack/marketplace.ts +103 -0
  186. package/packages/core/src/pack/pii-masker.ts +38 -0
  187. package/packages/core/src/pack/types.ts +39 -0
  188. package/packages/core/src/plugins/index.ts +100 -0
  189. package/packages/core/src/plugins/webhooks.ts +110 -0
  190. package/packages/core/src/search/bm25.ts +16 -0
  191. package/packages/core/src/search/index.ts +83 -0
  192. package/packages/core/src/search/rrf.ts +31 -0
  193. package/packages/core/src/search/semantic.ts +15 -0
  194. package/packages/core/src/store/index.ts +2 -0
  195. package/packages/core/src/store/sqlite-vec.ts +290 -0
  196. package/packages/core/src/store/types.ts +22 -0
  197. package/packages/core/src/team/index.ts +126 -0
  198. package/packages/core/src/types/chunk.ts +25 -0
  199. package/packages/core/src/types/document.ts +24 -0
  200. package/packages/core/src/types/graph.ts +44 -0
  201. package/packages/core/src/types/index.ts +15 -0
  202. package/packages/core/src/types/search.ts +38 -0
  203. package/packages/core/src/utils/retry.ts +85 -0
  204. package/packages/core/tests/api-card.test.ts +60 -0
  205. package/packages/core/tests/api-routes.test.ts +98 -0
  206. package/packages/core/tests/bm25.test.ts +87 -0
  207. package/packages/core/tests/chunker.test.ts +48 -0
  208. package/packages/core/tests/cluster.test.ts +75 -0
  209. package/packages/core/tests/constellation.test.ts +77 -0
  210. package/packages/core/tests/export-utils.test.ts +97 -0
  211. package/packages/core/tests/fsrs.test.ts +96 -0
  212. package/packages/core/tests/gesture-detector.test.ts +45 -0
  213. package/packages/core/tests/graph-data.test.ts +87 -0
  214. package/packages/core/tests/layout.test.ts +83 -0
  215. package/packages/core/tests/mcp.test.ts +148 -0
  216. package/packages/core/tests/pack.test.ts +127 -0
  217. package/packages/core/tests/pii-masker.test.ts +42 -0
  218. package/packages/core/tests/profile-card.test.ts +62 -0
  219. package/packages/core/tests/rrf.test.ts +29 -0
  220. package/packages/core/tests/search-integration.test.ts +139 -0
  221. package/packages/core/tests/store.test.ts +80 -0
  222. package/packages/graph/click-result.png +0 -0
  223. package/packages/graph/index.html +17 -0
  224. package/packages/graph/package.json +32 -0
  225. package/packages/graph/src/App.tsx +7 -0
  226. package/packages/graph/src/api/client.ts +39 -0
  227. package/packages/graph/src/components/ClusterFilter.tsx +73 -0
  228. package/packages/graph/src/components/ConstellationView.tsx +232 -0
  229. package/packages/graph/src/components/ExportPanel.tsx +177 -0
  230. package/packages/graph/src/components/Graph3D.tsx +230 -0
  231. package/packages/graph/src/components/GraphEdges.tsx +100 -0
  232. package/packages/graph/src/components/GraphNodes.tsx +386 -0
  233. package/packages/graph/src/components/HealthDashboard.tsx +173 -0
  234. package/packages/graph/src/components/Layout.tsx +214 -0
  235. package/packages/graph/src/components/MotionOverlay.tsx +81 -0
  236. package/packages/graph/src/components/MotionToggle.tsx +33 -0
  237. package/packages/graph/src/components/MultiverseView.tsx +286 -0
  238. package/packages/graph/src/components/NodeDetail.tsx +232 -0
  239. package/packages/graph/src/components/PulseParticle.tsx +232 -0
  240. package/packages/graph/src/components/SearchBar.tsx +107 -0
  241. package/packages/graph/src/components/StarField.tsx +197 -0
  242. package/packages/graph/src/components/StatusBar.tsx +53 -0
  243. package/packages/graph/src/components/Timeline.tsx +148 -0
  244. package/packages/graph/src/components/ToolsPanel.tsx +512 -0
  245. package/packages/graph/src/components/Tooltip.tsx +100 -0
  246. package/packages/graph/src/components/TypeFilter.tsx +131 -0
  247. package/packages/graph/src/embed/EmbedGraph.tsx +144 -0
  248. package/packages/graph/src/hooks/useConstellationLOD.ts +76 -0
  249. package/packages/graph/src/hooks/useDecay.ts +37 -0
  250. package/packages/graph/src/hooks/useExport.ts +165 -0
  251. package/packages/graph/src/hooks/useGraph.ts +69 -0
  252. package/packages/graph/src/hooks/useKeyboardNav.ts +122 -0
  253. package/packages/graph/src/hooks/useLayout.ts +45 -0
  254. package/packages/graph/src/hooks/useMotion.ts +120 -0
  255. package/packages/graph/src/hooks/usePulse.ts +58 -0
  256. package/packages/graph/src/hooks/useSearch.ts +71 -0
  257. package/packages/graph/src/lib/constellation.ts +107 -0
  258. package/packages/graph/src/lib/export-utils.ts +48 -0
  259. package/packages/graph/src/lib/gesture-detector.ts +123 -0
  260. package/packages/graph/src/lib/layout.worker.ts +153 -0
  261. package/packages/graph/src/lib/motion-controller.ts +83 -0
  262. package/packages/graph/src/lib/profile-card.ts +122 -0
  263. package/packages/graph/src/main.tsx +4 -0
  264. package/packages/graph/src/stores/graph-store.ts +155 -0
  265. package/packages/graph/success.png +0 -0
  266. package/packages/graph/test-click.mjs +49 -0
  267. package/packages/graph/test-explore.mjs +102 -0
  268. package/packages/graph/test-final.mjs +61 -0
  269. package/packages/graph/test-graph.mjs +139 -0
  270. package/packages/graph/test-hover.mjs +48 -0
  271. package/packages/graph/test-pulse.mjs +68 -0
  272. package/packages/graph/test-screenshot.mjs +56 -0
  273. package/packages/graph/test-v2.mjs +97 -0
  274. package/packages/graph/vite.config.ts +15 -0
  275. package/packages/sync/.env.example +11 -0
  276. package/packages/sync/.sync-state.json +317 -0
  277. package/packages/sync/.upload-state.json +1009 -0
  278. package/packages/sync/create-stella-network-notion.mjs +151 -0
  279. package/packages/sync/create-stellavault-project-notion.mjs +322 -0
  280. package/packages/sync/logs/sync-2026-03-28.log +6 -0
  281. package/packages/sync/logs/sync-2026-03-29.log +12 -0
  282. package/packages/sync/logs/sync-2026-03-30.log +6 -0
  283. package/packages/sync/logs/sync-2026-03-31.log +6 -0
  284. package/packages/sync/logs/sync-2026-04-01.log +6 -0
  285. package/packages/sync/logs/sync-2026-04-02.log +6 -0
  286. package/packages/sync/package-lock.json +373 -0
  287. package/packages/sync/package.json +16 -0
  288. package/packages/sync/run-sync.bat +18 -0
  289. package/packages/sync/run-sync.mjs +46 -0
  290. package/packages/sync/setup-scheduler.mjs +119 -0
  291. package/packages/sync/structured-sync.mjs +187 -0
  292. package/packages/sync/sync-to-obsidian.mjs +264 -0
  293. package/packages/sync/upload-pdca-to-notion.mjs +495 -0
  294. package/tsconfig.base.json +18 -0
@@ -0,0 +1,197 @@
1
+ // 우주 배경: 다층 별 + 성운 효과 + 은하수
2
+
3
+ import { useMemo, useRef } from 'react';
4
+ import { useFrame } from '@react-three/fiber';
5
+ import * as THREE from 'three';
6
+ import { useGraphStore } from '../stores/graph-store.js';
7
+
8
+ // 원형 별 텍스처
9
+ function createStarTexture(): THREE.Texture {
10
+ const size = 64;
11
+ const canvas = document.createElement('canvas');
12
+ canvas.width = size;
13
+ canvas.height = size;
14
+ const ctx = canvas.getContext('2d')!;
15
+ const gradient = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
16
+ gradient.addColorStop(0, 'rgba(255,255,255,1)');
17
+ gradient.addColorStop(0.15, 'rgba(255,255,255,0.8)');
18
+ gradient.addColorStop(0.4, 'rgba(200,220,255,0.2)');
19
+ gradient.addColorStop(1, 'rgba(200,220,255,0)');
20
+ ctx.fillStyle = gradient;
21
+ ctx.fillRect(0, 0, size, size);
22
+ const tex = new THREE.CanvasTexture(canvas);
23
+ tex.needsUpdate = true;
24
+ return tex;
25
+ }
26
+
27
+ // 성운 텍스처 (부드러운 구름)
28
+ function createNebulaTexture(r: number, g: number, b: number): THREE.Texture {
29
+ const size = 128;
30
+ const canvas = document.createElement('canvas');
31
+ canvas.width = size;
32
+ canvas.height = size;
33
+ const ctx = canvas.getContext('2d')!;
34
+ const gradient = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
35
+ gradient.addColorStop(0, `rgba(${r},${g},${b},0.7)`);
36
+ gradient.addColorStop(0.3, `rgba(${r},${g},${b},0.35)`);
37
+ gradient.addColorStop(0.6, `rgba(${r},${g},${b},0.12)`);
38
+ gradient.addColorStop(1, `rgba(${r},${g},${b},0)`);
39
+ ctx.fillStyle = gradient;
40
+ ctx.fillRect(0, 0, size, size);
41
+ const tex = new THREE.CanvasTexture(canvas);
42
+ tex.needsUpdate = true;
43
+ return tex;
44
+ }
45
+
46
+ const starTexture = createStarTexture();
47
+
48
+ // 성운 컬러 프리셋
49
+ const NEBULA_PRESETS = [
50
+ { r: 100, g: 60, b: 200 }, // 보라
51
+ { r: 30, g: 80, b: 220 }, // 딥블루
52
+ { r: 180, g: 50, b: 120 }, // 핑크
53
+ { r: 40, g: 120, b: 200 }, // 시안
54
+ { r: 130, g: 40, b: 180 }, // 인디고
55
+ { r: 60, g: 100, b: 180 }, // 스틸블루
56
+ ];
57
+
58
+ const nebulaTextures = NEBULA_PRESETS.map(c => createNebulaTexture(c.r, c.g, c.b));
59
+
60
+ // --- 별 레이어 ---
61
+ function Stars({ count, minR, maxR, size, opacity, speed }: {
62
+ count: number; minR: number; maxR: number; size: number; opacity: number; speed: number;
63
+ }) {
64
+ const ref = useRef<THREE.Points>(null);
65
+
66
+ const { positions, colors } = useMemo(() => {
67
+ const pos = new Float32Array(count * 3);
68
+ const col = new Float32Array(count * 3);
69
+ for (let i = 0; i < count; i++) {
70
+ const r = minR + Math.random() * (maxR - minR);
71
+ const theta = Math.acos(2 * Math.random() - 1);
72
+ const phi = Math.random() * 2 * Math.PI;
73
+ pos[i * 3] = r * Math.sin(theta) * Math.cos(phi);
74
+ pos[i * 3 + 1] = r * Math.cos(theta);
75
+ pos[i * 3 + 2] = r * Math.sin(theta) * Math.sin(phi);
76
+
77
+ // 별 색상 다양화 (화이트~블루~옐로우)
78
+ const temp = Math.random();
79
+ if (temp < 0.6) {
80
+ // 화이트~블루
81
+ col[i * 3] = 0.8 + Math.random() * 0.2;
82
+ col[i * 3 + 1] = 0.85 + Math.random() * 0.15;
83
+ col[i * 3 + 2] = 0.95 + Math.random() * 0.05;
84
+ } else if (temp < 0.8) {
85
+ // 옐로우
86
+ col[i * 3] = 1;
87
+ col[i * 3 + 1] = 0.9 + Math.random() * 0.1;
88
+ col[i * 3 + 2] = 0.6 + Math.random() * 0.2;
89
+ } else {
90
+ // 블루
91
+ col[i * 3] = 0.5 + Math.random() * 0.3;
92
+ col[i * 3 + 1] = 0.6 + Math.random() * 0.3;
93
+ col[i * 3 + 2] = 1;
94
+ }
95
+ }
96
+ return { positions: pos, colors: col };
97
+ }, [count, minR, maxR]);
98
+
99
+ useFrame(({ clock }) => {
100
+ if (ref.current) {
101
+ ref.current.rotation.y = clock.getElapsedTime() * speed;
102
+ }
103
+ });
104
+
105
+ return (
106
+ <points ref={ref}>
107
+ <bufferGeometry>
108
+ <bufferAttribute attach="attributes-position" args={[positions, 3]} />
109
+ <bufferAttribute attach="attributes-color" args={[colors, 3]} />
110
+ </bufferGeometry>
111
+ <pointsMaterial
112
+ vertexColors
113
+ transparent
114
+ opacity={opacity}
115
+ depthWrite={false}
116
+ blending={THREE.AdditiveBlending}
117
+ sizeAttenuation
118
+ size={size}
119
+ map={starTexture}
120
+ />
121
+ </points>
122
+ );
123
+ }
124
+
125
+ // --- 성운 레이어 ---
126
+ function Nebulae() {
127
+ const ref = useRef<THREE.Group>(null);
128
+
129
+ const clouds = useMemo(() => {
130
+ const items: Array<{
131
+ pos: [number, number, number];
132
+ scale: number;
133
+ texIdx: number;
134
+ opacity: number;
135
+ }> = [];
136
+
137
+ // 뇌 주변 + 배경에 성운 구름 대량 배치
138
+ for (let i = 0; i < 25; i++) {
139
+ const theta = Math.acos(2 * Math.random() - 1);
140
+ const phi = Math.random() * 2 * Math.PI;
141
+ const r = 300 + Math.random() * 900;
142
+ items.push({
143
+ pos: [
144
+ r * Math.sin(theta) * Math.cos(phi),
145
+ r * Math.cos(theta),
146
+ r * Math.sin(theta) * Math.sin(phi),
147
+ ],
148
+ scale: 300 + Math.random() * 600,
149
+ texIdx: Math.floor(Math.random() * nebulaTextures.length),
150
+ opacity: 0.06 + Math.random() * 0.12,
151
+ });
152
+ }
153
+ return items;
154
+ }, []);
155
+
156
+ useFrame(({ clock }) => {
157
+ if (ref.current) {
158
+ ref.current.rotation.y = clock.getElapsedTime() * 0.002;
159
+ }
160
+ });
161
+
162
+ return (
163
+ <group ref={ref}>
164
+ {clouds.map((cloud, i) => (
165
+ <sprite key={i} position={cloud.pos} scale={[cloud.scale, cloud.scale, 1]}>
166
+ <spriteMaterial
167
+ map={nebulaTextures[cloud.texIdx]}
168
+ transparent
169
+ opacity={cloud.opacity}
170
+ depthWrite={false}
171
+ blending={THREE.AdditiveBlending}
172
+ />
173
+ </sprite>
174
+ ))}
175
+ </group>
176
+ );
177
+ }
178
+
179
+ // --- 메인 ---
180
+ export function StarField() {
181
+ // Light mode에서는 별/성운 숨김 — 밝은 배경에 부적합
182
+ const theme = useGraphStore((s) => s.theme);
183
+ if (theme === 'light') return null;
184
+
185
+ return (
186
+ <group>
187
+ {/* 원경 별 (작고 많음) */}
188
+ <Stars count={3000} minR={1200} maxR={2500} size={1.2} opacity={0.5} speed={0.003} />
189
+ {/* 중경 별 (중간) */}
190
+ <Stars count={800} minR={800} maxR={1400} size={2} opacity={0.6} speed={0.005} />
191
+ {/* 근경 밝은 별 (크고 적음) */}
192
+ <Stars count={150} minR={600} maxR={1000} size={3.5} opacity={0.8} speed={0.007} />
193
+ {/* 성운 */}
194
+ <Nebulae />
195
+ </group>
196
+ );
197
+ }
@@ -0,0 +1,53 @@
1
+ import { useGraphStore } from '../stores/graph-store.js';
2
+ import { ExportPanel } from './ExportPanel.js';
3
+
4
+ export function StatusBar() {
5
+ const nodes = useGraphStore((s) => s.nodes);
6
+ const edges = useGraphStore((s) => s.edges);
7
+ const clusters = useGraphStore((s) => s.clusters);
8
+ const loading = useGraphStore((s) => s.loading);
9
+ const mode = useGraphStore((s) => s.mode);
10
+ const lodLevel = useGraphStore((s) => s.lodLevel);
11
+ const theme = useGraphStore((s) => s.theme);
12
+ const isDark = theme === 'dark';
13
+
14
+ return (
15
+ <div style={{
16
+ padding: '6px 16px',
17
+ background: isDark ? 'rgba(10, 10, 20, 0.8)' : 'rgba(240, 242, 248, 0.95)',
18
+ borderTop: `1px solid ${isDark ? 'rgba(100, 120, 255, 0.1)' : 'rgba(0, 0, 0, 0.08)'}`,
19
+ fontSize: '11px',
20
+ color: isDark ? '#667' : '#556',
21
+ display: 'flex',
22
+ gap: '20px',
23
+ alignItems: 'center',
24
+ }}>
25
+ {loading ? (
26
+ <span>Loading...</span>
27
+ ) : (
28
+ <>
29
+ <span>{nodes.length} docs</span>
30
+ <span>{edges.length} edges</span>
31
+ <span>{clusters.length} clusters</span>
32
+ <span style={{ color: '#556' }}>|</span>
33
+ <span style={{ color: mode === 'semantic' ? '#88aaff' : '#88cc88' }}>
34
+ {mode === 'semantic' ? 'AI Semantic' : 'Obsidian Folders'}
35
+ </span>
36
+ <span style={{ color: '#556' }}>|</span>
37
+ <span style={{ color: lodLevel === 'universe' ? '#c088ff' : lodLevel === 'note' ? '#88ffaa' : '#88aaff' }}>
38
+ {lodLevel}
39
+ </span>
40
+ <span style={{ color: '#445', maxWidth: '40%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
41
+ {clusters.map(c => c.label).join(' · ')}
42
+ </span>
43
+ <div style={{ marginLeft: 'auto', display: 'flex', gap: '8px', alignItems: 'center' }}>
44
+ <ExportPanel />
45
+ <span style={{ color: '#334', fontSize: '10px' }}>
46
+ ESC=reset · /=search
47
+ </span>
48
+ </div>
49
+ </>
50
+ )}
51
+ </div>
52
+ );
53
+ }
@@ -0,0 +1,148 @@
1
+ // 지식 타임라인 — 시간 축 슬라이더로 노트 생성/수정 이력 필터
2
+
3
+ import { useMemo, useCallback } from 'react';
4
+ import { useGraphStore } from '../stores/graph-store.js';
5
+
6
+ export function Timeline() {
7
+ const nodes = useGraphStore((s) => s.nodes);
8
+ const showTimeline = useGraphStore((s) => s.showTimeline);
9
+ const timelineRange = useGraphStore((s) => s.timelineRange);
10
+ const setTimelineRange = useGraphStore((s) => s.setTimelineRange);
11
+ const theme = useGraphStore((s) => s.theme);
12
+ const isDark = theme === 'dark';
13
+
14
+ // 전체 날짜 범위 계산
15
+ const { minMs, maxMs, histogram } = useMemo(() => {
16
+ const timestamps: number[] = [];
17
+ for (const n of nodes) {
18
+ if (n.lastModified) {
19
+ const ms = new Date(n.lastModified).getTime();
20
+ if (!isNaN(ms)) timestamps.push(ms);
21
+ }
22
+ }
23
+ if (timestamps.length === 0) return { minMs: 0, maxMs: 0, histogram: [] as number[] };
24
+
25
+ const min = Math.min(...timestamps);
26
+ const max = Math.max(...timestamps);
27
+ const range = max - min || 1;
28
+
29
+ // 50 bin 히스토그램
30
+ const bins = 50;
31
+ const hist = new Array(bins).fill(0);
32
+ for (const ts of timestamps) {
33
+ const idx = Math.min(Math.floor(((ts - min) / range) * bins), bins - 1);
34
+ hist[idx]++;
35
+ }
36
+ return { minMs: min, maxMs: max, histogram: hist };
37
+ }, [nodes]);
38
+
39
+ // 현재 범위 내 노드 수
40
+ const activeCount = useMemo(() => {
41
+ if (!timelineRange) return nodes.length;
42
+ const [start, end] = timelineRange;
43
+ return nodes.filter((n) => {
44
+ if (!n.lastModified) return true;
45
+ const ms = new Date(n.lastModified).getTime();
46
+ return ms >= start && ms <= end;
47
+ }).length;
48
+ }, [nodes, timelineRange]);
49
+
50
+ const handleStartChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
51
+ const val = Number(e.target.value);
52
+ const end = timelineRange ? timelineRange[1] : maxMs;
53
+ setTimelineRange([Math.min(val, end), end]);
54
+ }, [timelineRange, maxMs, setTimelineRange]);
55
+
56
+ const handleEndChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
57
+ const val = Number(e.target.value);
58
+ const start = timelineRange ? timelineRange[0] : minMs;
59
+ setTimelineRange([start, Math.max(val, start)]);
60
+ }, [timelineRange, minMs, setTimelineRange]);
61
+
62
+ const resetRange = useCallback(() => {
63
+ setTimelineRange(null);
64
+ }, [setTimelineRange]);
65
+
66
+ if (!showTimeline || minMs === 0) return null;
67
+
68
+ const startVal = timelineRange ? timelineRange[0] : minMs;
69
+ const endVal = timelineRange ? timelineRange[1] : maxMs;
70
+ const maxHist = Math.max(1, ...histogram);
71
+
72
+ const formatDate = (ms: number) => new Date(ms).toLocaleDateString('ko-KR', { year: 'numeric', month: 'short', day: 'numeric' });
73
+
74
+ return (
75
+ <div style={{
76
+ padding: '8px 16px',
77
+ background: isDark ? 'rgba(10,10,20,0.9)' : 'rgba(240,242,248,0.97)',
78
+ borderTop: `1px solid ${isDark ? 'rgba(100,120,255,0.1)' : 'rgba(0,0,0,0.08)'}`,
79
+ backdropFilter: 'blur(8px)',
80
+ }}>
81
+ {/* 히스토그램 */}
82
+ <div style={{ display: 'flex', alignItems: 'flex-end', gap: '1px', height: '30px', marginBottom: '4px' }}>
83
+ {histogram.map((count, i) => {
84
+ const binStart = minMs + (i / histogram.length) * (maxMs - minMs);
85
+ const binEnd = minMs + ((i + 1) / histogram.length) * (maxMs - minMs);
86
+ const inRange = binStart >= startVal && binEnd <= endVal;
87
+ return (
88
+ <div
89
+ key={i}
90
+ style={{
91
+ flex: 1,
92
+ height: `${(count / maxHist) * 100}%`,
93
+ minHeight: count > 0 ? '2px' : '0',
94
+ background: inRange
95
+ ? (isDark ? 'rgba(100,180,255,0.6)' : 'rgba(59,130,246,0.5)')
96
+ : (isDark ? 'rgba(100,120,255,0.15)' : 'rgba(0,0,0,0.08)'),
97
+ borderRadius: '1px 1px 0 0',
98
+ transition: 'background 0.15s',
99
+ }}
100
+ />
101
+ );
102
+ })}
103
+ </div>
104
+
105
+ {/* 슬라이더 */}
106
+ <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
107
+ <span style={{ fontSize: '10px', color: isDark ? '#667' : '#999', minWidth: '90px' }}>
108
+ {formatDate(startVal)}
109
+ </span>
110
+ <input
111
+ type="range"
112
+ min={minMs}
113
+ max={maxMs}
114
+ value={startVal}
115
+ onChange={handleStartChange}
116
+ style={{ flex: 1, accentColor: isDark ? '#6488ff' : '#3b82f6' }}
117
+ />
118
+ <input
119
+ type="range"
120
+ min={minMs}
121
+ max={maxMs}
122
+ value={endVal}
123
+ onChange={handleEndChange}
124
+ style={{ flex: 1, accentColor: isDark ? '#6488ff' : '#3b82f6' }}
125
+ />
126
+ <span style={{ fontSize: '10px', color: isDark ? '#667' : '#999', minWidth: '90px', textAlign: 'right' }}>
127
+ {formatDate(endVal)}
128
+ </span>
129
+ <button
130
+ onClick={resetRange}
131
+ style={{
132
+ fontSize: '10px', padding: '2px 6px', border: `1px solid ${isDark ? 'rgba(100,120,255,0.15)' : 'rgba(0,0,0,0.12)'}`,
133
+ borderRadius: '3px', cursor: 'pointer',
134
+ background: isDark ? 'rgba(100,120,255,0.06)' : 'rgba(0,0,0,0.03)',
135
+ color: isDark ? '#aab' : '#555',
136
+ }}
137
+ >
138
+ Reset
139
+ </button>
140
+ </div>
141
+
142
+ {/* 상태 */}
143
+ <div style={{ fontSize: '10px', color: isDark ? '#556' : '#888', marginTop: '2px', textAlign: 'center' }}>
144
+ {activeCount}/{nodes.length} docs in range
145
+ </div>
146
+ </div>
147
+ );
148
+ }