@codefluss/threejs-shared 0.0.1-alpha.1

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 ADDED
@@ -0,0 +1,124 @@
1
+ # @codefluss/threejs-shared
2
+
3
+ Shared ThreeJS components and utilities for multi-instance canvas-based plugins.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Instance Context** - Isolated store per plugin instance
8
+ - ✅ **Resource Manager** - Shared geometries/materials across instances
9
+ - ✅ **Performance Monitor** - Global FPS tracking with adaptive quality
10
+ - ✅ **POI System** - Points of Interest with 3D markers
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @codefluss/threejs-shared
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Instance Context
21
+
22
+ ```tsx
23
+ import { InstanceProvider, useInstanceState } from '@codefluss/threejs-shared';
24
+ import { createBackgroundStore } from './store';
25
+
26
+ function Background3D({ elementId, data }) {
27
+ return (
28
+ <InstanceProvider
29
+ instanceId={elementId}
30
+ createStore={createBackgroundStore}
31
+ >
32
+ <Scene data={data} />
33
+ </InstanceProvider>
34
+ );
35
+ }
36
+
37
+ function Scene({ data }) {
38
+ const isPlaying = useInstanceState(s => s.isPlaying);
39
+ // ...
40
+ }
41
+ ```
42
+
43
+ ### Resource Manager
44
+
45
+ ```tsx
46
+ import { resourceManager } from '@codefluss/threejs-shared';
47
+
48
+ // Register instance
49
+ resourceManager.registerInstance('bg-1');
50
+
51
+ // Get shared geometry (created once, reused by all instances)
52
+ const starGeo = resourceManager.getSharedGeometry(
53
+ 'star-sphere',
54
+ () => new THREE.SphereGeometry(0.05, 8, 8)
55
+ );
56
+
57
+ // Cleanup
58
+ resourceManager.unregisterInstance('bg-1');
59
+ ```
60
+
61
+ ### Performance Monitor
62
+
63
+ ```tsx
64
+ import { globalPerfMonitor } from '@codefluss/threejs-shared';
65
+ import { useFrame } from '@react-three/fiber';
66
+
67
+ // Register canvas
68
+ useEffect(() => {
69
+ globalPerfMonitor.registerCanvas(instanceId);
70
+ return () => globalPerfMonitor.unregisterCanvas(instanceId);
71
+ }, [instanceId]);
72
+
73
+ // Track FPS
74
+ useFrame((state, delta) => {
75
+ const fps = 1 / delta;
76
+ globalPerfMonitor.updateMetrics(instanceId, fps);
77
+ });
78
+
79
+ // Listen for quality changes
80
+ useEffect(() => {
81
+ const handler = (e: CustomEvent) => {
82
+ if (e.detail.instanceId === instanceId) {
83
+ setQuality(e.detail.quality);
84
+ }
85
+ };
86
+
87
+ window.addEventListener('performance:quality-change', handler);
88
+ return () => window.removeEventListener('performance:quality-change', handler);
89
+ }, [instanceId]);
90
+ ```
91
+
92
+ ### POI System
93
+
94
+ ```tsx
95
+ import { createPOIManager, POIMarker } from '@codefluss/threejs-shared';
96
+
97
+ // Create POI store
98
+ const poiStore = createPOIManager('conf-1');
99
+
100
+ // Add POI
101
+ const id = poiStore.getState().addPOI({
102
+ position: [1, 0.5, 0],
103
+ label: 'Feature A',
104
+ description: 'Main product feature',
105
+ color: '#4CAF50'
106
+ });
107
+
108
+ // Render POI markers
109
+ function POIMarkers() {
110
+ const pois = poiStore(s => s.pois);
111
+
112
+ return (
113
+ <>
114
+ {pois.map(poi => (
115
+ <POIMarker key={poi.id} {...poi} />
116
+ ))}
117
+ </>
118
+ );
119
+ }
120
+ ```
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,39 @@
1
+ import { StoreApi } from 'zustand';
2
+ import { InstanceProviderProps } from '../types';
3
+ /**
4
+ * Instance Provider - Creates isolated store per plugin instance
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * <InstanceProvider
9
+ * instanceId={elementId}
10
+ * createStore={createBackgroundInstance}
11
+ * >
12
+ * <InteractiveBackground />
13
+ * </InstanceProvider>
14
+ * ```
15
+ */
16
+ export declare function InstanceProvider<T>({ instanceId, createStore, children }: InstanceProviderProps<T>): import("react/jsx-runtime").JSX.Element;
17
+ /**
18
+ * Hook to access the instance store
19
+ *
20
+ * @throws Error if used outside InstanceProvider
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * const instance = useInstance();
25
+ * const isPlaying = instance((state) => state.isPlaying);
26
+ * ```
27
+ */
28
+ export declare function useInstance<T = any>(): StoreApi<T>;
29
+ /**
30
+ * Hook to select state from instance store
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const isPlaying = useInstanceState(s => s.isPlaying);
35
+ * const camera = useInstanceState(s => s.camera);
36
+ * ```
37
+ */
38
+ export declare function useInstanceState<T, R>(selector: (state: T) => R): R;
39
+ //# sourceMappingURL=instance-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance-context.d.ts","sourceRoot":"","sources":["../../src/context/instance-context.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAItD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,EAClC,UAAU,EACV,WAAW,EACX,QAAQ,EACT,EAAE,qBAAqB,CAAC,CAAC,CAAC,2CAuB1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAMlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GACxB,CAAC,CAKH"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Resource Manager - Singleton for sharing geometries, materials, and textures
3
+ * across multiple ThreeJS instances
4
+ *
5
+ * Benefits:
6
+ * - Reduces memory usage by sharing resources
7
+ * - Automatic cleanup when last instance is removed
8
+ * - Debug tracking of active instances
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { resourceManager } from '@codefluss/threejs-shared';
13
+ *
14
+ * // Register instance
15
+ * resourceManager.registerInstance('bg-1');
16
+ *
17
+ * // Get shared geometry
18
+ * const starGeo = resourceManager.getSharedGeometry(
19
+ * 'star-sphere',
20
+ * () => new THREE.SphereGeometry(0.05, 8, 8)
21
+ * );
22
+ *
23
+ * // Cleanup
24
+ * resourceManager.unregisterInstance('bg-1');
25
+ * ```
26
+ */
27
+ declare class ResourceManager {
28
+ private static instance;
29
+ private geometries;
30
+ private materials;
31
+ private textures;
32
+ private activeInstances;
33
+ private constructor();
34
+ static getInstance(): ResourceManager;
35
+ /**
36
+ * Get or create shared geometry
37
+ * Multiple instances can use the same geometry
38
+ */
39
+ getSharedGeometry(key: string, create: () => any): any;
40
+ /**
41
+ * Get or create shared material
42
+ * Multiple instances can use the same material
43
+ */
44
+ getSharedMaterial(key: string, create: () => any): any;
45
+ /**
46
+ * Get or create shared texture
47
+ * Multiple instances can use the same texture
48
+ */
49
+ getSharedTexture(key: string, create: () => any): any;
50
+ /**
51
+ * Register a new instance
52
+ * Tracks active instances for cleanup
53
+ */
54
+ registerInstance(instanceId: string): void;
55
+ /**
56
+ * Unregister instance
57
+ * Triggers cleanup if last instance
58
+ */
59
+ unregisterInstance(instanceId: string): void;
60
+ /**
61
+ * Get list of active instance IDs
62
+ */
63
+ getActiveInstances(): string[];
64
+ /**
65
+ * Get resource statistics
66
+ */
67
+ getStats(): {
68
+ geometries: number;
69
+ materials: number;
70
+ textures: number;
71
+ activeInstances: number;
72
+ };
73
+ /**
74
+ * Force cleanup all resources
75
+ * Called automatically when last instance is removed
76
+ */
77
+ private cleanup;
78
+ /**
79
+ * Manual cleanup (for testing)
80
+ */
81
+ forceCleanup(): void;
82
+ }
83
+ export declare const resourceManager: ResourceManager;
84
+ export {};
85
+ //# sourceMappingURL=ResourceManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceManager.d.ts","sourceRoot":"","sources":["../../src/core/ResourceManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,cAAM,eAAe;IACnB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkB;IAEzC,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,eAAe,CAAqB;IAE5C,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,eAAe;IAOrC;;;OAGG;IACH,iBAAiB,CACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,GAChB,GAAG;IASN;;;OAGG;IACH,iBAAiB,CACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,GAChB,GAAG;IASN;;;OAGG;IACH,gBAAgB,CACd,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,GAChB,GAAG;IASN;;;OAGG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAO1C;;;OAGG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAY5C;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,QAAQ;;;;;;IASR;;;OAGG;IACH,OAAO,CAAC,OAAO;IA4Bf;;OAEG;IACH,YAAY,IAAI,IAAI;CAIrB;AAGD,eAAO,MAAM,eAAe,iBAAgC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @codefluss/threejs-shared
3
+ *
4
+ * Shared ThreeJS components and utilities for canvas-based plugins
5
+ */
6
+ export { InstanceProvider, useInstance, useInstanceState } from './context/instance-context';
7
+ export { resourceManager } from './core/ResourceManager';
8
+ export { globalPerfMonitor } from './performance/PerformanceMonitor';
9
+ export { createPOIManager } from './poi/POIManager';
10
+ export { POIMarker } from './poi/POIMarker';
11
+ export type { QualityLevel, PerformanceMetrics, BaseThreeStore, POI, POIStore, SharedResource, PerformanceEvent, InstanceProviderProps } from './types';
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAG7F,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAGrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAG5C,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,GAAG,EACH,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,SAAS,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,10 @@
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{createContext as s,useRef as n,useEffect as a,useContext as i,useState as r}from"react";import{createStore as o}from"zustand";import{Html as c}from"@react-three/drei";import{useSpring as l}from"@react-spring/three";const u=s(null);function g({instanceId:t,createStore:s,children:i}){const r=n(void 0);return r.current||(r.current=s(t)),a(()=>()=>{const e=r.current?.getState();"function"==typeof e?.dispose&&e.dispose()},[]),/* @__PURE__ */e(u.Provider,{value:r.current,children:i})}function h(){const e=i(u);if(!e)throw new Error("useInstance must be used within InstanceProvider");return e}function d(e){return h()(e)}class p{static instance;geometries=/* @__PURE__ */new Map;materials=/* @__PURE__ */new Map;textures=/* @__PURE__ */new Map;activeInstances=/* @__PURE__ */new Set;constructor(){}static getInstance(){return p.instance||(p.instance=new p),p.instance}getSharedGeometry(e,t){if(!this.geometries.has(e)){const s=t();this.geometries.set(e,s),console.log(`[ResourceManager] Created geometry: ${e}`)}return this.geometries.get(e)}getSharedMaterial(e,t){if(!this.materials.has(e)){const s=t();this.materials.set(e,s),console.log(`[ResourceManager] Created material: ${e}`)}return this.materials.get(e)}getSharedTexture(e,t){if(!this.textures.has(e)){const s=t();this.textures.set(e,s),console.log(`[ResourceManager] Created texture: ${e}`)}return this.textures.get(e)}registerInstance(e){this.activeInstances.add(e),console.log(`[ResourceManager] Registered instance: ${e} (${this.activeInstances.size} total)`)}unregisterInstance(e){this.activeInstances.delete(e),console.log(`[ResourceManager] Unregistered instance: ${e} (${this.activeInstances.size} remaining)`),0===this.activeInstances.size&&this.cleanup()}getActiveInstances(){return Array.from(this.activeInstances)}getStats(){return{geometries:this.geometries.size,materials:this.materials.size,textures:this.textures.size,activeInstances:this.activeInstances.size}}cleanup(){console.log("[ResourceManager] Cleaning up all resources"),this.geometries.forEach((e,t)=>{e.dispose(),console.log(`[ResourceManager] Disposed geometry: ${t}`)}),this.materials.forEach((e,t)=>{e.dispose(),console.log(`[ResourceManager] Disposed material: ${t}`)}),this.textures.forEach((e,t)=>{e.dispose(),console.log(`[ResourceManager] Disposed texture: ${t}`)}),this.geometries.clear(),this.materials.clear(),this.textures.clear(),console.log("[ResourceManager] Cleanup complete")}forceCleanup(){this.cleanup(),this.activeInstances.clear()}}const m=p.getInstance();class I{static instance;canvasInstances=/* @__PURE__ */new Map;globalFPS=60;updateInterval=null;CHECK_INTERVAL=1e3;LOW_FPS_THRESHOLD=30;HIGH_FPS_THRESHOLD=50;constructor(){}static getInstance(){return I.instance||(I.instance=new I),I.instance}registerCanvas(e){this.canvasInstances.set(e,{fps:60,quality:"high",particleCount:0}),console.log(`[PerformanceMonitor] Registered canvas: ${e} (${this.canvasInstances.size} total)`),1===this.canvasInstances.size&&this.startMonitoring()}unregisterCanvas(e){this.canvasInstances.delete(e),console.log(`[PerformanceMonitor] Unregistered canvas: ${e} (${this.canvasInstances.size} remaining)`),0===this.canvasInstances.size&&this.stopMonitoring()}updateMetrics(e,t){const s=this.canvasInstances.get(e);s&&(s.fps=t)}updateParticleCount(e,t){const s=this.canvasInstances.get(e);s&&(s.particleCount=t)}getGlobalMetrics(){return{globalFPS:this.globalFPS,instanceCount:this.canvasInstances.size,instances:Array.from(this.canvasInstances.entries()).map(([e,t])=>({id:e,...t}))}}startMonitoring(){console.log("[PerformanceMonitor] Starting monitoring"),this.updateInterval=setInterval(()=>{this.globalFPS=this.calculateAverageFPS(),this.globalFPS<this.LOW_FPS_THRESHOLD?this.adjustQualityAcrossAll("reduce"):this.globalFPS>this.HIGH_FPS_THRESHOLD&&this.canAnyIncrease()&&this.adjustQualityAcrossAll("increase")},this.CHECK_INTERVAL)}stopMonitoring(){console.log("[PerformanceMonitor] Stopping monitoring"),this.updateInterval&&(clearInterval(this.updateInterval),this.updateInterval=null)}calculateAverageFPS(){if(0===this.canvasInstances.size)return 60;const e=Array.from(this.canvasInstances.values()).map(e=>e.fps),t=e.reduce((e,t)=>e+t,0)/e.length;return Math.round(t)}canAnyIncrease(){return Array.from(this.canvasInstances.values()).some(e=>"high"!==e.quality)}adjustQualityAcrossAll(e){let t=0;this.canvasInstances.forEach((s,n)=>{let a=s.quality;"reduce"===e?"high"===s.quality?a="medium":"medium"===s.quality&&(a="low"):"low"===s.quality?a="medium":"medium"===s.quality&&(a="high"),a!==s.quality&&(s.quality=a,this.notifyInstance(n,a),t++)}),t>0&&console.log(`[PerformanceMonitor] Adjusted quality for ${t} instances (${e})`)}notifyInstance(e,t){"undefined"!=typeof window&&window.dispatchEvent(new CustomEvent("performance:quality-change",{detail:{instanceId:e,quality:t}}))}forceCleanup(){this.stopMonitoring(),this.canvasInstances.clear(),this.globalFPS=60}}const v=I.getInstance();function f(e){return o()((t,s)=>({instanceId:e,pois:[],activePOI:null,addPOI:s=>{const n=`poi-${Date.now()}-${Math.random().toString(36).substr(2,9)}`,a={...s,id:n};return t(e=>({pois:[...e.pois,a]})),console.log(`[POIManager:${e}] Added POI: ${n}`),n},updatePOI:(s,n)=>{t(e=>({pois:e.pois.map(e=>e.id===s?{...e,...n}:e)})),console.log(`[POIManager:${e}] Updated POI: ${s}`)},removePOI:s=>{t(e=>({pois:e.pois.filter(e=>e.id!==s),activePOI:e.activePOI===s?null:e.activePOI})),console.log(`[POIManager:${e}] Removed POI: ${s}`)},setActivePOI:s=>{t({activePOI:s}),s&&console.log(`[POIManager:${e}] Set active POI: ${s}`)},getPOI:e=>s().pois.find(t=>t.id===e),clearAll:()=>{t({pois:[],activePOI:null}),console.log(`[POIManager:${e}] Cleared all POIs`)}}))}const P="mesh";function y({id:s,position:n,label:a,description:i,color:o="#4CAF50",onClick:u}){const[g,h]=r(!1),{scale:d}=l({scale:g?1.3:1,config:{tension:300,friction:20}});/* @__PURE__ */
2
+ return t("group",{position:n,children:[
3
+ /* @__PURE__ */t(P,{scale:d,onClick:()=>{u&&u(s)},onPointerOver:()=>h(!0),onPointerOut:()=>h(!1),children:[
4
+ /* @__PURE__ */e("sphereGeometry",{args:[.1,16,16]}),
5
+ /* @__PURE__ */e("meshStandardMaterial",{color:o,emissive:o,emissiveIntensity:g?.5:.2})]}),g&&/* @__PURE__ */t(P,{rotation:[Math.PI/2,0,0],children:[
6
+ /* @__PURE__ */e("ringGeometry",{args:[.12,.15,32]}),
7
+ /* @__PURE__ */e("meshBasicMaterial",{color:o,transparent:!0,opacity:.6})]}),
8
+ /* @__PURE__ */e(c,{position:[0,.3,0],center:!0,distanceFactor:10,style:{transition:"opacity 0.2s, transform 0.2s",opacity:g?1:.8,transform:g?"scale(1.05)":"scale(1)",pointerEvents:"none"},children:/* @__PURE__ */t("div",{style:{background:"rgba(0, 0, 0, 0.8)",color:"white",padding:"8px 12px",borderRadius:"4px",fontSize:"14px",whiteSpace:"nowrap",border:`2px solid ${o}`,boxShadow:"0 4px 12px rgba(0,0,0,0.3)"},children:[
9
+ /* @__PURE__ */e("div",{style:{fontWeight:"bold"},children:a}),g&&i&&/* @__PURE__ */e("div",{style:{fontSize:"12px",opacity:.8,marginTop:"4px",maxWidth:"200px",whiteSpace:"normal"},children:i})]})})]})}export{g as InstanceProvider,y as POIMarker,f as createPOIManager,v as globalPerfMonitor,m as resourceManager,h as useInstance,d as useInstanceState};
10
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/context/instance-context.tsx","../src/core/ResourceManager.ts","../src/performance/PerformanceMonitor.ts","../src/poi/POIManager.ts","../src/poi/POIMarker.tsx"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext, useRef, useEffect } from 'react';\nimport type { StoreApi } from 'zustand';\nimport type { InstanceProviderProps } from '../types';\n\nconst InstanceContext = createContext<StoreApi<any> | null>(null);\n\n/**\n * Instance Provider - Creates isolated store per plugin instance\n *\n * @example\n * ```tsx\n * <InstanceProvider\n * instanceId={elementId}\n * createStore={createBackgroundInstance}\n * >\n * <InteractiveBackground />\n * </InstanceProvider>\n * ```\n */\nexport function InstanceProvider<T>({\n instanceId,\n createStore,\n children\n}: InstanceProviderProps<T>) {\n const storeRef = useRef<StoreApi<T> | undefined>(undefined);\n\n // Create store only once\n if (!storeRef.current) {\n storeRef.current = createStore(instanceId);\n }\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n const state = storeRef.current?.getState() as any;\n if (typeof state?.dispose === 'function') {\n state.dispose();\n }\n };\n }, []);\n\n return (\n <InstanceContext.Provider value={storeRef.current}>\n {children}\n </InstanceContext.Provider>\n );\n}\n\n/**\n * Hook to access the instance store\n *\n * @throws Error if used outside InstanceProvider\n *\n * @example\n * ```tsx\n * const instance = useInstance();\n * const isPlaying = instance((state) => state.isPlaying);\n * ```\n */\nexport function useInstance<T = any>(): StoreApi<T> {\n const store = useContext(InstanceContext);\n if (!store) {\n throw new Error('useInstance must be used within InstanceProvider');\n }\n return store as StoreApi<T>;\n}\n\n/**\n * Hook to select state from instance store\n *\n * @example\n * ```tsx\n * const isPlaying = useInstanceState(s => s.isPlaying);\n * const camera = useInstanceState(s => s.camera);\n * ```\n */\nexport function useInstanceState<T, R>(\n selector: (state: T) => R\n): R {\n const store = useInstance<T>();\n\n // Use the store as a hook\n return (store as any)(selector);\n}\n","/**\n * Resource Manager - Singleton for sharing geometries, materials, and textures\n * across multiple ThreeJS instances\n *\n * Benefits:\n * - Reduces memory usage by sharing resources\n * - Automatic cleanup when last instance is removed\n * - Debug tracking of active instances\n *\n * @example\n * ```ts\n * import { resourceManager } from '@codefluss/threejs-shared';\n *\n * // Register instance\n * resourceManager.registerInstance('bg-1');\n *\n * // Get shared geometry\n * const starGeo = resourceManager.getSharedGeometry(\n * 'star-sphere',\n * () => new THREE.SphereGeometry(0.05, 8, 8)\n * );\n *\n * // Cleanup\n * resourceManager.unregisterInstance('bg-1');\n * ```\n */\nclass ResourceManager {\n private static instance: ResourceManager;\n\n private geometries = new Map<string, any>();\n private materials = new Map<string, any>();\n private textures = new Map<string, any>();\n private activeInstances = new Set<string>();\n\n private constructor() {\n // Singleton - private constructor\n }\n\n static getInstance(): ResourceManager {\n if (!ResourceManager.instance) {\n ResourceManager.instance = new ResourceManager();\n }\n return ResourceManager.instance;\n }\n\n /**\n * Get or create shared geometry\n * Multiple instances can use the same geometry\n */\n getSharedGeometry(\n key: string,\n create: () => any\n ): any {\n if (!this.geometries.has(key)) {\n const geometry = create();\n this.geometries.set(key, geometry);\n console.log(`[ResourceManager] Created geometry: ${key}`);\n }\n return this.geometries.get(key)!;\n }\n\n /**\n * Get or create shared material\n * Multiple instances can use the same material\n */\n getSharedMaterial(\n key: string,\n create: () => any\n ): any {\n if (!this.materials.has(key)) {\n const material = create();\n this.materials.set(key, material);\n console.log(`[ResourceManager] Created material: ${key}`);\n }\n return this.materials.get(key)!;\n }\n\n /**\n * Get or create shared texture\n * Multiple instances can use the same texture\n */\n getSharedTexture(\n key: string,\n create: () => any\n ): any {\n if (!this.textures.has(key)) {\n const texture = create();\n this.textures.set(key, texture);\n console.log(`[ResourceManager] Created texture: ${key}`);\n }\n return this.textures.get(key)!;\n }\n\n /**\n * Register a new instance\n * Tracks active instances for cleanup\n */\n registerInstance(instanceId: string): void {\n this.activeInstances.add(instanceId);\n console.log(\n `[ResourceManager] Registered instance: ${instanceId} (${this.activeInstances.size} total)`\n );\n }\n\n /**\n * Unregister instance\n * Triggers cleanup if last instance\n */\n unregisterInstance(instanceId: string): void {\n this.activeInstances.delete(instanceId);\n console.log(\n `[ResourceManager] Unregistered instance: ${instanceId} (${this.activeInstances.size} remaining)`\n );\n\n // Cleanup wenn keine Instanzen mehr\n if (this.activeInstances.size === 0) {\n this.cleanup();\n }\n }\n\n /**\n * Get list of active instance IDs\n */\n getActiveInstances(): string[] {\n return Array.from(this.activeInstances);\n }\n\n /**\n * Get resource statistics\n */\n getStats() {\n return {\n geometries: this.geometries.size,\n materials: this.materials.size,\n textures: this.textures.size,\n activeInstances: this.activeInstances.size\n };\n }\n\n /**\n * Force cleanup all resources\n * Called automatically when last instance is removed\n */\n private cleanup(): void {\n console.log('[ResourceManager] Cleaning up all resources');\n\n // Dispose geometries\n this.geometries.forEach((geo, key) => {\n geo.dispose();\n console.log(`[ResourceManager] Disposed geometry: ${key}`);\n });\n\n // Dispose materials\n this.materials.forEach((mat, key) => {\n mat.dispose();\n console.log(`[ResourceManager] Disposed material: ${key}`);\n });\n\n // Dispose textures\n this.textures.forEach((tex, key) => {\n tex.dispose();\n console.log(`[ResourceManager] Disposed texture: ${key}`);\n });\n\n this.geometries.clear();\n this.materials.clear();\n this.textures.clear();\n\n console.log('[ResourceManager] Cleanup complete');\n }\n\n /**\n * Manual cleanup (for testing)\n */\n forceCleanup(): void {\n this.cleanup();\n this.activeInstances.clear();\n }\n}\n\n// Export singleton instance\nexport const resourceManager = ResourceManager.getInstance();\n","import type { QualityLevel, PerformanceMetrics } from '../types';\n\n/**\n * Global Performance Monitor - Singleton for tracking FPS across all instances\n * and coordinating adaptive quality adjustments\n *\n * Features:\n * - Track FPS per instance\n * - Calculate global average FPS\n * - Automatic quality adjustment when performance drops\n * - Event-based quality change notifications\n *\n * @example\n * ```ts\n * import { globalPerfMonitor } from '@codefluss/threejs-shared';\n *\n * // Register canvas\n * globalPerfMonitor.registerCanvas('bg-1');\n *\n * // Update FPS in render loop\n * useFrame((state, delta) => {\n * const fps = 1 / delta;\n * globalPerfMonitor.updateMetrics('bg-1', fps);\n * });\n *\n * // Listen for quality changes\n * useEffect(() => {\n * const handler = (e: CustomEvent) => {\n * if (e.detail.instanceId === 'bg-1') {\n * setQuality(e.detail.quality);\n * }\n * };\n * window.addEventListener('performance:quality-change', handler);\n * return () => window.removeEventListener('performance:quality-change', handler);\n * }, []);\n * ```\n */\nclass GlobalPerformanceMonitor {\n private static instance: GlobalPerformanceMonitor;\n\n private canvasInstances = new Map<string, PerformanceMetrics>();\n private globalFPS = 60;\n private updateInterval: ReturnType<typeof setInterval> | null = null;\n private readonly CHECK_INTERVAL = 1000; // 1 second\n private readonly LOW_FPS_THRESHOLD = 30;\n private readonly HIGH_FPS_THRESHOLD = 50;\n\n private constructor() {\n // Singleton - private constructor\n }\n\n static getInstance(): GlobalPerformanceMonitor {\n if (!GlobalPerformanceMonitor.instance) {\n GlobalPerformanceMonitor.instance = new GlobalPerformanceMonitor();\n }\n return GlobalPerformanceMonitor.instance;\n }\n\n /**\n * Register a new canvas instance\n */\n registerCanvas(instanceId: string): void {\n this.canvasInstances.set(instanceId, {\n fps: 60,\n quality: 'high',\n particleCount: 0\n });\n\n console.log(\n `[PerformanceMonitor] Registered canvas: ${instanceId} (${this.canvasInstances.size} total)`\n );\n\n // Start monitoring if first instance\n if (this.canvasInstances.size === 1) {\n this.startMonitoring();\n }\n }\n\n /**\n * Unregister canvas instance\n */\n unregisterCanvas(instanceId: string): void {\n this.canvasInstances.delete(instanceId);\n\n console.log(\n `[PerformanceMonitor] Unregistered canvas: ${instanceId} (${this.canvasInstances.size} remaining)`\n );\n\n // Stop monitoring if no instances\n if (this.canvasInstances.size === 0) {\n this.stopMonitoring();\n }\n }\n\n /**\n * Update FPS for specific instance\n * Call this in your render loop (useFrame)\n */\n updateMetrics(instanceId: string, fps: number): void {\n const metrics = this.canvasInstances.get(instanceId);\n if (!metrics) return;\n\n metrics.fps = fps;\n }\n\n /**\n * Update particle count for specific instance\n */\n updateParticleCount(instanceId: string, count: number): void {\n const metrics = this.canvasInstances.get(instanceId);\n if (!metrics) return;\n\n metrics.particleCount = count;\n }\n\n /**\n * Get global performance metrics\n */\n getGlobalMetrics() {\n return {\n globalFPS: this.globalFPS,\n instanceCount: this.canvasInstances.size,\n instances: Array.from(this.canvasInstances.entries()).map(([id, metrics]) => ({\n id,\n ...metrics\n }))\n };\n }\n\n /**\n * Start monitoring performance\n */\n private startMonitoring(): void {\n console.log('[PerformanceMonitor] Starting monitoring');\n\n this.updateInterval = setInterval(() => {\n this.globalFPS = this.calculateAverageFPS();\n\n // Adaptive Quality basierend auf Global FPS\n if (this.globalFPS < this.LOW_FPS_THRESHOLD) {\n this.adjustQualityAcrossAll('reduce');\n } else if (this.globalFPS > this.HIGH_FPS_THRESHOLD && this.canAnyIncrease()) {\n this.adjustQualityAcrossAll('increase');\n }\n }, this.CHECK_INTERVAL);\n }\n\n /**\n * Stop monitoring\n */\n private stopMonitoring(): void {\n console.log('[PerformanceMonitor] Stopping monitoring');\n\n if (this.updateInterval) {\n clearInterval(this.updateInterval);\n this.updateInterval = null;\n }\n }\n\n /**\n * Calculate average FPS across all instances\n */\n private calculateAverageFPS(): number {\n if (this.canvasInstances.size === 0) return 60;\n\n const fpsList = Array.from(this.canvasInstances.values()).map(m => m.fps);\n const average = fpsList.reduce((a, b) => a + b, 0) / fpsList.length;\n\n return Math.round(average);\n }\n\n /**\n * Check if any instance can increase quality\n */\n private canAnyIncrease(): boolean {\n return Array.from(this.canvasInstances.values()).some(\n m => m.quality !== 'high'\n );\n }\n\n /**\n * Adjust quality across all instances\n */\n private adjustQualityAcrossAll(direction: 'reduce' | 'increase'): void {\n let changedCount = 0;\n\n this.canvasInstances.forEach((metrics, instanceId) => {\n let newQuality = metrics.quality;\n\n if (direction === 'reduce') {\n if (metrics.quality === 'high') {\n newQuality = 'medium';\n } else if (metrics.quality === 'medium') {\n newQuality = 'low';\n }\n } else {\n if (metrics.quality === 'low') {\n newQuality = 'medium';\n } else if (metrics.quality === 'medium') {\n newQuality = 'high';\n }\n }\n\n if (newQuality !== metrics.quality) {\n metrics.quality = newQuality;\n this.notifyInstance(instanceId, newQuality);\n changedCount++;\n }\n });\n\n if (changedCount > 0) {\n console.log(\n `[PerformanceMonitor] Adjusted quality for ${changedCount} instances (${direction})`\n );\n }\n }\n\n /**\n * Notify instance of quality change via CustomEvent\n */\n private notifyInstance(instanceId: string, quality: QualityLevel): void {\n if (typeof window === 'undefined') return;\n\n window.dispatchEvent(\n new CustomEvent('performance:quality-change', {\n detail: { instanceId, quality }\n })\n );\n }\n\n /**\n * Force cleanup (for testing)\n */\n forceCleanup(): void {\n this.stopMonitoring();\n this.canvasInstances.clear();\n this.globalFPS = 60;\n }\n}\n\n// Export singleton instance\nexport const globalPerfMonitor = GlobalPerformanceMonitor.getInstance();\n","import { createStore } from 'zustand';\nimport type { POI, POIStore } from '../types';\n\n/**\n * POI Manager Factory - Creates instance-based POI stores\n *\n * POI = Points of Interest (3D markers with labels)\n *\n * Features:\n * - Add/update/remove POI markers\n * - Track active POI\n * - Instance-based isolation\n *\n * @example\n * ```tsx\n * const poiStore = createPOIManager('conf-1');\n *\n * // Add POI\n * const id = poiStore.getState().addPOI({\n * position: [1, 0.5, 0],\n * label: 'Feature A',\n * description: 'Main product feature',\n * color: '#4CAF50'\n * });\n *\n * // Set active\n * poiStore.getState().setActivePOI(id);\n * ```\n */\nexport function createPOIManager(instanceId: string) {\n return createStore<POIStore>()((set, get) => ({\n instanceId,\n pois: [],\n activePOI: null,\n\n /**\n * Add new POI marker\n * @returns Generated POI ID\n */\n addPOI: (poi: Omit<POI, 'id'>) => {\n const id = `poi-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const newPOI: POI = { ...poi, id };\n\n set(state => ({\n pois: [...state.pois, newPOI]\n }));\n\n console.log(`[POIManager:${instanceId}] Added POI: ${id}`);\n return id;\n },\n\n /**\n * Update existing POI\n */\n updatePOI: (id: string, updates: Partial<POI>) => {\n set(state => ({\n pois: state.pois.map(p =>\n p.id === id ? { ...p, ...updates } : p\n )\n }));\n\n console.log(`[POIManager:${instanceId}] Updated POI: ${id}`);\n },\n\n /**\n * Remove POI by ID\n */\n removePOI: (id: string) => {\n set(state => ({\n pois: state.pois.filter(p => p.id !== id),\n activePOI: state.activePOI === id ? null : state.activePOI\n }));\n\n console.log(`[POIManager:${instanceId}] Removed POI: ${id}`);\n },\n\n /**\n * Set active POI (highlighted)\n */\n setActivePOI: (id: string | null) => {\n set({ activePOI: id });\n\n if (id) {\n console.log(`[POIManager:${instanceId}] Set active POI: ${id}`);\n }\n },\n\n /**\n * Get POI by ID\n */\n getPOI: (id: string) => {\n return get().pois.find(p => p.id === id);\n },\n\n /**\n * Clear all POIs\n */\n clearAll: () => {\n set({ pois: [], activePOI: null });\n console.log(`[POIManager:${instanceId}] Cleared all POIs`);\n }\n }));\n}\n","\"use client\";\n\nimport { Html } from '@react-three/drei';\nimport { useSpring } from '@react-spring/three';\nimport { useState } from 'react';\nimport type { POI } from '../types';\n\nconst Group: any = 'group';\nconst Mesh: any = 'mesh';\nconst SphereGeometry: any = 'sphereGeometry';\nconst RingGeometry: any = 'ringGeometry';\nconst MeshStandardMaterial: any = 'meshStandardMaterial';\nconst MeshBasicMaterial: any = 'meshBasicMaterial';\n\n/**\n * POI Marker Component - 3D sphere with HTML label overlay\n *\n * Features:\n * - Animated hover effect\n * - Clickable interaction\n * - HTML label with description\n * - Custom color\n *\n * @example\n * ```tsx\n * <POIMarker\n * id=\"poi-1\"\n * position={[1, 0.5, 0]}\n * label=\"Feature A\"\n * description=\"Main product feature\"\n * color=\"#4CAF50\"\n * onClick={(id) => console.log('Clicked:', id)}\n * />\n * ```\n */\nexport function POIMarker({\n id,\n position,\n label,\n description,\n color = '#4CAF50',\n onClick\n}: POI) {\n const [hovered, setHovered] = useState(false);\n\n // Animated scale on hover\n const { scale } = useSpring({\n scale: hovered ? 1.3 : 1,\n config: { tension: 300, friction: 20 }\n });\n\n const handleClick = () => {\n if (onClick) {\n onClick(id);\n }\n };\n\n return (\n <Group position={position}>\n {/* 3D Marker Sphere */}\n <Mesh\n // @ts-ignore - animated scale is valid but types are incompatible\n scale={scale}\n onClick={handleClick}\n onPointerOver={() => setHovered(true)}\n onPointerOut={() => setHovered(false)}\n >\n <SphereGeometry args={[0.1, 16, 16]} />\n <MeshStandardMaterial\n color={color}\n emissive={color}\n emissiveIntensity={hovered ? 0.5 : 0.2}\n />\n </Mesh>\n\n {/* Pulsing ring effect */}\n {hovered && (\n <Mesh rotation={[Math.PI / 2, 0, 0]}>\n <RingGeometry args={[0.12, 0.15, 32]} />\n <MeshBasicMaterial color={color} transparent opacity={0.6} />\n </Mesh>\n )}\n\n {/* HTML Label Overlay */}\n <Html\n position={[0, 0.3, 0]}\n center\n distanceFactor={10}\n style={{\n transition: 'opacity 0.2s, transform 0.2s',\n opacity: hovered ? 1 : 0.8,\n transform: hovered ? 'scale(1.05)' : 'scale(1)',\n pointerEvents: 'none'\n }}\n >\n <div\n style={{\n background: 'rgba(0, 0, 0, 0.8)',\n color: 'white',\n padding: '8px 12px',\n borderRadius: '4px',\n fontSize: '14px',\n whiteSpace: 'nowrap',\n border: `2px solid ${color}`,\n boxShadow: '0 4px 12px rgba(0,0,0,0.3)'\n }}\n >\n <div style={{ fontWeight: 'bold' }}>{label}</div>\n {hovered && description && (\n <div\n style={{\n fontSize: '12px',\n opacity: 0.8,\n marginTop: '4px',\n maxWidth: '200px',\n whiteSpace: 'normal'\n }}\n >\n {description}\n </div>\n )}\n </div>\n </Html>\n </Group>\n );\n}\n"],"names":["InstanceContext","createContext","InstanceProvider","instanceId","createStore","children","storeRef","useRef","current","useEffect","state","getState","dispose","Provider","value","useInstance","store","useContext","Error","useInstanceState","selector","ResourceManager","static","geometries","Map","materials","textures","activeInstances","Set","constructor","getInstance","instance","getSharedGeometry","key","create","this","has","geometry","set","console","log","get","getSharedMaterial","material","getSharedTexture","texture","registerInstance","add","size","unregisterInstance","delete","cleanup","getActiveInstances","Array","from","getStats","forEach","geo","mat","tex","clear","forceCleanup","resourceManager","GlobalPerformanceMonitor","canvasInstances","globalFPS","updateInterval","CHECK_INTERVAL","LOW_FPS_THRESHOLD","HIGH_FPS_THRESHOLD","registerCanvas","fps","quality","particleCount","startMonitoring","unregisterCanvas","stopMonitoring","updateMetrics","metrics","updateParticleCount","count","getGlobalMetrics","instanceCount","instances","entries","map","id","setInterval","calculateAverageFPS","adjustQualityAcrossAll","canAnyIncrease","clearInterval","fpsList","values","m","average","reduce","a","b","length","Math","round","some","direction","changedCount","newQuality","notifyInstance","window","dispatchEvent","CustomEvent","detail","globalPerfMonitor","createPOIManager","pois","activePOI","addPOI","poi","Date","now","random","toString","substr","newPOI","updatePOI","updates","p","removePOI","filter","setActivePOI","getPOI","find","clearAll","Mesh","POIMarker","position","label","description","color","onClick","hovered","setHovered","useState","scale","useSpring","config","tension","friction","jsxs","onPointerOver","onPointerOut","jsx","args","emissive","emissiveIntensity","rotation","PI","transparent","opacity","Html","center","distanceFactor","style","transition","transform","pointerEvents","background","padding","borderRadius","fontSize","whiteSpace","border","boxShadow","fontWeight","marginTop","maxWidth"],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ import { QualityLevel } from '../types';
2
+ /**
3
+ * Global Performance Monitor - Singleton for tracking FPS across all instances
4
+ * and coordinating adaptive quality adjustments
5
+ *
6
+ * Features:
7
+ * - Track FPS per instance
8
+ * - Calculate global average FPS
9
+ * - Automatic quality adjustment when performance drops
10
+ * - Event-based quality change notifications
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { globalPerfMonitor } from '@codefluss/threejs-shared';
15
+ *
16
+ * // Register canvas
17
+ * globalPerfMonitor.registerCanvas('bg-1');
18
+ *
19
+ * // Update FPS in render loop
20
+ * useFrame((state, delta) => {
21
+ * const fps = 1 / delta;
22
+ * globalPerfMonitor.updateMetrics('bg-1', fps);
23
+ * });
24
+ *
25
+ * // Listen for quality changes
26
+ * useEffect(() => {
27
+ * const handler = (e: CustomEvent) => {
28
+ * if (e.detail.instanceId === 'bg-1') {
29
+ * setQuality(e.detail.quality);
30
+ * }
31
+ * };
32
+ * window.addEventListener('performance:quality-change', handler);
33
+ * return () => window.removeEventListener('performance:quality-change', handler);
34
+ * }, []);
35
+ * ```
36
+ */
37
+ declare class GlobalPerformanceMonitor {
38
+ private static instance;
39
+ private canvasInstances;
40
+ private globalFPS;
41
+ private updateInterval;
42
+ private readonly CHECK_INTERVAL;
43
+ private readonly LOW_FPS_THRESHOLD;
44
+ private readonly HIGH_FPS_THRESHOLD;
45
+ private constructor();
46
+ static getInstance(): GlobalPerformanceMonitor;
47
+ /**
48
+ * Register a new canvas instance
49
+ */
50
+ registerCanvas(instanceId: string): void;
51
+ /**
52
+ * Unregister canvas instance
53
+ */
54
+ unregisterCanvas(instanceId: string): void;
55
+ /**
56
+ * Update FPS for specific instance
57
+ * Call this in your render loop (useFrame)
58
+ */
59
+ updateMetrics(instanceId: string, fps: number): void;
60
+ /**
61
+ * Update particle count for specific instance
62
+ */
63
+ updateParticleCount(instanceId: string, count: number): void;
64
+ /**
65
+ * Get global performance metrics
66
+ */
67
+ getGlobalMetrics(): {
68
+ globalFPS: number;
69
+ instanceCount: number;
70
+ instances: {
71
+ fps: number;
72
+ quality: QualityLevel;
73
+ particleCount: number;
74
+ drawCalls?: number;
75
+ triangles?: number;
76
+ id: string;
77
+ }[];
78
+ };
79
+ /**
80
+ * Start monitoring performance
81
+ */
82
+ private startMonitoring;
83
+ /**
84
+ * Stop monitoring
85
+ */
86
+ private stopMonitoring;
87
+ /**
88
+ * Calculate average FPS across all instances
89
+ */
90
+ private calculateAverageFPS;
91
+ /**
92
+ * Check if any instance can increase quality
93
+ */
94
+ private canAnyIncrease;
95
+ /**
96
+ * Adjust quality across all instances
97
+ */
98
+ private adjustQualityAcrossAll;
99
+ /**
100
+ * Notify instance of quality change via CustomEvent
101
+ */
102
+ private notifyInstance;
103
+ /**
104
+ * Force cleanup (for testing)
105
+ */
106
+ forceCleanup(): void;
107
+ }
108
+ export declare const globalPerfMonitor: GlobalPerformanceMonitor;
109
+ export {};
110
+ //# sourceMappingURL=PerformanceMonitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PerformanceMonitor.d.ts","sourceRoot":"","sources":["../../src/performance/PerformanceMonitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,UAAU,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,cAAM,wBAAwB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAElD,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAEzC,OAAO;IAIP,MAAM,CAAC,WAAW,IAAI,wBAAwB;IAO9C;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAiBxC;;OAEG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAa1C;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAOpD;;OAEG;IACH,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAO5D;;OAEG;IACH,gBAAgB;;;;;;;;;;;;IAWhB;;OAEG;IACH,OAAO,CAAC,eAAe;IAevB;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAS3B;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAkC9B;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;OAEG;IACH,YAAY,IAAI,IAAI;CAKrB;AAGD,eAAO,MAAM,iBAAiB,0BAAyC,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { POIStore } from '../types';
2
+ /**
3
+ * POI Manager Factory - Creates instance-based POI stores
4
+ *
5
+ * POI = Points of Interest (3D markers with labels)
6
+ *
7
+ * Features:
8
+ * - Add/update/remove POI markers
9
+ * - Track active POI
10
+ * - Instance-based isolation
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const poiStore = createPOIManager('conf-1');
15
+ *
16
+ * // Add POI
17
+ * const id = poiStore.getState().addPOI({
18
+ * position: [1, 0.5, 0],
19
+ * label: 'Feature A',
20
+ * description: 'Main product feature',
21
+ * color: '#4CAF50'
22
+ * });
23
+ *
24
+ * // Set active
25
+ * poiStore.getState().setActivePOI(id);
26
+ * ```
27
+ */
28
+ export declare function createPOIManager(instanceId: string): import('zustand').StoreApi<POIStore>;
29
+ //# sourceMappingURL=POIManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"POIManager.d.ts","sourceRoot":"","sources":["../../src/poi/POIManager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAO,QAAQ,EAAE,MAAM,UAAU,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,wCAyElD"}
@@ -0,0 +1,24 @@
1
+ import { POI } from '../types';
2
+ /**
3
+ * POI Marker Component - 3D sphere with HTML label overlay
4
+ *
5
+ * Features:
6
+ * - Animated hover effect
7
+ * - Clickable interaction
8
+ * - HTML label with description
9
+ * - Custom color
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <POIMarker
14
+ * id="poi-1"
15
+ * position={[1, 0.5, 0]}
16
+ * label="Feature A"
17
+ * description="Main product feature"
18
+ * color="#4CAF50"
19
+ * onClick={(id) => console.log('Clicked:', id)}
20
+ * />
21
+ * ```
22
+ */
23
+ export declare function POIMarker({ id, position, label, description, color, onClick }: POI): import("react/jsx-runtime").JSX.Element;
24
+ //# sourceMappingURL=POIMarker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"POIMarker.d.ts","sourceRoot":"","sources":["../../src/poi/POIMarker.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AASpC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,CAAC,EACxB,EAAE,EACF,QAAQ,EACR,KAAK,EACL,WAAW,EACX,KAAiB,EACjB,OAAO,EACR,EAAE,GAAG,2CAmFL"}
@@ -0,0 +1,55 @@
1
+ import { StoreApi } from 'zustand';
2
+ export type QualityLevel = 'high' | 'medium' | 'low';
3
+ export interface PerformanceMetrics {
4
+ fps: number;
5
+ quality: QualityLevel;
6
+ particleCount: number;
7
+ drawCalls?: number;
8
+ triangles?: number;
9
+ }
10
+ export interface BaseThreeStore {
11
+ instanceId: string;
12
+ scene: any;
13
+ camera: any;
14
+ renderer: any;
15
+ isReady: boolean;
16
+ init: (canvasElement: HTMLCanvasElement) => void;
17
+ dispose: () => void;
18
+ }
19
+ export interface POI {
20
+ id: string;
21
+ position: [number, number, number];
22
+ label: string;
23
+ description?: string;
24
+ icon?: string;
25
+ color?: string;
26
+ metadata?: Record<string, unknown>;
27
+ onClick?: (id: string) => void;
28
+ }
29
+ export interface POIStore {
30
+ instanceId: string;
31
+ pois: POI[];
32
+ activePOI: string | null;
33
+ addPOI: (poi: Omit<POI, 'id'>) => string;
34
+ updatePOI: (id: string, updates: Partial<POI>) => void;
35
+ removePOI: (id: string) => void;
36
+ setActivePOI: (id: string | null) => void;
37
+ getPOI: (id: string) => POI | undefined;
38
+ clearAll: () => void;
39
+ }
40
+ export interface SharedResource {
41
+ key: string;
42
+ refCount: number;
43
+ resource: any;
44
+ }
45
+ export interface PerformanceEvent {
46
+ instanceId: string;
47
+ quality: QualityLevel;
48
+ fps?: number;
49
+ }
50
+ export interface InstanceProviderProps<T> {
51
+ instanceId: string;
52
+ createStore: (id: string) => StoreApi<T>;
53
+ children: React.ReactNode;
54
+ }
55
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAGrD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,GAAG,CAAC;IACX,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IAEjB,IAAI,EAAE,CAAC,aAAa,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACjD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAGD,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAEzB,MAAM,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,MAAM,CAAC;IACzC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC;IACvD,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,GAAG,GAAG,SAAS,CAAC;IACxC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAGD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,GAAG,CAAC;CACf;AAGD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@codefluss/threejs-shared",
3
+ "version": "0.0.1-alpha.1",
4
+ "type": "module",
5
+ "description": "Shared ThreeJS components and utilities for canvas-based plugins",
6
+ "keywords": [
7
+ "codefluss",
8
+ "threejs",
9
+ "3d",
10
+ "canvas",
11
+ "shared"
12
+ ],
13
+ "author": "Codefluss",
14
+ "main": "./dist/index.mjs",
15
+ "module": "./dist/index.mjs",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.mjs"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "dependencies": {
27
+ "@react-spring/three": "^10.0.3",
28
+ "@react-three/drei": "^10.7.7",
29
+ "@react-three/fiber": "^9.4.2",
30
+ "@react-three/rapier": "^2.2.0",
31
+ "react": "^19.2.3",
32
+ "react-dom": "^19.2.3",
33
+ "three": "^0.181.2",
34
+ "zustand": "^5.0.9"
35
+ },
36
+ "devDependencies": {
37
+ "@types/react": "19.2.4",
38
+ "@types/react-dom": "^19.2.3",
39
+ "@types/three": "^0.181.0",
40
+ "@vitejs/plugin-react": "^5.1.2",
41
+ "typescript": "^5.9.3",
42
+ "vite": "^7.2.7",
43
+ "vite-plugin-dts": "^4.5.4",
44
+ "@codefluss/vite-config-lib": "0.0.1-alpha.1"
45
+ },
46
+ "peerDependencies": {
47
+ "react": "^19.2.0",
48
+ "react-dom": "^19.2.0"
49
+ },
50
+ "license": "MIT",
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "dev": "vite",
56
+ "build": "tsc && vite build",
57
+ "preview": "vite preview",
58
+ "type-check": "tsc --noEmit"
59
+ }
60
+ }