Spaces:
Running
Running
Upload 51 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- project/README.md +88 -0
- project/codette.py +114 -0
- project/cognitive_processor.py +17 -0
- project/config_manager.py +41 -0
- project/dream_reweaver 2.py +53 -0
- project/eslint.config.js +28 -0
- project/index.html +13 -0
- project/package-lock.json +0 -0
- project/package.json +35 -0
- project/postcss.config.js +6 -0
- project/src/App.tsx +264 -0
- project/src/components/AdminLogin.tsx +77 -0
- project/src/components/ChatInterface.tsx +219 -0
- project/src/components/CodetteComponents.tsx +143 -0
- project/src/components/FileList.tsx +235 -0
- project/src/components/Header.tsx +64 -0
- project/src/components/Sidebar.tsx +496 -0
- project/src/components/VisualizationPanel.tsx +264 -0
- project/src/index.css +94 -0
- project/src/main.tsx +10 -0
- project/src/services/AICore.ts +253 -0
- project/src/services/CognitionCocooner.ts +174 -0
- project/src/services/KaggleService.ts +104 -0
- project/src/services/OpenAIService.ts +43 -0
- project/src/services/QuantumSpiderweb.ts +81 -0
- project/src/vite-env.d.ts +1 -0
- project/supabase/migrations/20250523100814_raspy_torch.sql +43 -0
- project/supabase/migrations/20250523120906_wild_torch.sql +36 -0
- project/supabase/migrations/20250523121149_rough_jungle.sql +66 -0
- project/supabase/migrations/20250523125621_rapid_flower.sql +61 -0
- project/supabase/migrations/20250523141836_heavy_butterfly.sql +71 -0
- project/supabase/migrations/20250523175402_white_torch.sql +81 -0
- project/supabase/migrations/20250523182801_long_field.sql +82 -0
- project/supabase/migrations/20250523183206_odd_moon.sql +86 -0
- project/supabase/migrations/20250523213744_long_sun.sql +90 -0
- project/supabase/migrations/20250523222316_square_gate.sql +44 -0
- project/supabase/migrations/20250523222514_muddy_desert.sql +47 -0
- project/supabase/migrations/20250523222518_bronze_dew.sql +39 -0
- project/supabase/migrations/20250523222523_orange_bread.sql +53 -0
- project/supabase/migrations/20250524062844_tender_thunder.sql +62 -0
- project/supabase/migrations/20250524213845_mellow_recipe.sql +44 -0
- project/supabase/migrations/20250524214450_green_poetry.sql +81 -0
- project/supabase/migrations/20250524214705_sunny_sunset.sql +16 -0
- project/supabase/migrations/20250524214708_lively_cell.sql +54 -0
- project/supabase/migrations/20250524214713_yellow_dawn.sql +63 -0
- project/supabase/migrations/20250524215300_flat_firefly.sql +26 -0
- project/tailwind.config.js +63 -0
- project/tsconfig.app.json +24 -0
- project/tsconfig.json +7 -0
- project/tsconfig.node.json +22 -0
project/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Codette AI Interface
|
2 |
+
|
3 |
+
A sophisticated AI assistant interface featuring multi-perspective reasoning, quantum-inspired processing, and cognitive cocoon artifact management.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
|
7 |
+
### 🧠 Multi-Perspective Reasoning
|
8 |
+
- Newton's logical analysis
|
9 |
+
- Da Vinci's creative synthesis
|
10 |
+
- Quantum computing perspectives
|
11 |
+
- Philosophical inquiry
|
12 |
+
- Neural network processing
|
13 |
+
- Resilient kindness framework
|
14 |
+
|
15 |
+
### 🌌 Quantum-Inspired Processing
|
16 |
+
- Quantum state visualization
|
17 |
+
- Chaos theory integration
|
18 |
+
- Parallel thought processing
|
19 |
+
- Entanglement-based correlations
|
20 |
+
|
21 |
+
### 📦 Cognitive Cocoon System
|
22 |
+
- Thought pattern preservation
|
23 |
+
- Encrypted storage
|
24 |
+
- Pattern analysis
|
25 |
+
- Memory management
|
26 |
+
|
27 |
+
### 🎨 Advanced UI Features
|
28 |
+
- Dark/Light mode
|
29 |
+
- Real-time quantum state visualization
|
30 |
+
- Interactive chat interface
|
31 |
+
- Admin dashboard
|
32 |
+
- File management system
|
33 |
+
|
34 |
+
### 🔒 Security & Privacy
|
35 |
+
- Supabase authentication
|
36 |
+
- Row-level security
|
37 |
+
- Encrypted storage
|
38 |
+
- Admin role management
|
39 |
+
|
40 |
+
## Tech Stack
|
41 |
+
|
42 |
+
- React + TypeScript
|
43 |
+
- Tailwind CSS
|
44 |
+
- Supabase
|
45 |
+
- Framer Motion
|
46 |
+
- Lucide Icons
|
47 |
+
|
48 |
+
## Getting Started
|
49 |
+
|
50 |
+
1. Clone the repository
|
51 |
+
2. Copy `.env.example` to `.env` and add your credentials:
|
52 |
+
```
|
53 |
+
VITE_SUPABASE_URL=your-project-url
|
54 |
+
VITE_SUPABASE_ANON_KEY=your-project-anon-key
|
55 |
+
```
|
56 |
+
|
57 |
+
3. Install dependencies:
|
58 |
+
```bash
|
59 |
+
npm install
|
60 |
+
```
|
61 |
+
|
62 |
+
4. Start the development server:
|
63 |
+
```bash
|
64 |
+
npm run dev
|
65 |
+
```
|
66 |
+
|
67 |
+
## Architecture
|
68 |
+
|
69 |
+
### Core Components
|
70 |
+
- **AICore**: Central processing unit with multi-perspective reasoning
|
71 |
+
- **CognitionCocooner**: Thought pattern preservation system
|
72 |
+
- **VisualizationPanel**: Real-time quantum state display
|
73 |
+
- **ChatInterface**: User interaction management
|
74 |
+
|
75 |
+
### Data Flow
|
76 |
+
1. User input → Chat Interface
|
77 |
+
2. AICore processes with multiple perspectives
|
78 |
+
3. Results stored in Cognitive Cocoons
|
79 |
+
4. Real-time visualization updates
|
80 |
+
5. Response rendered to user
|
81 |
+
|
82 |
+
## Contributing
|
83 |
+
|
84 |
+
We welcome contributions! Please read our contributing guidelines before submitting pull requests.
|
85 |
+
|
86 |
+
## License
|
87 |
+
|
88 |
+
MIT License - See LICENSE file for details
|
project/codette.py
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
from typing import List
|
3 |
+
|
4 |
+
class Element:
|
5 |
+
def __init__(self, name, symbol, representation, properties, interactions, defense_ability):
|
6 |
+
self.name = name
|
7 |
+
self.symbol = symbol
|
8 |
+
self.representation = representation
|
9 |
+
self.properties = properties
|
10 |
+
self.interactions = interactions
|
11 |
+
self.defense_ability = defense_ability
|
12 |
+
|
13 |
+
def execute_defense_function(self):
|
14 |
+
message = f"{self.name} ({self.symbol}) executes its defense ability: {self.defense_ability}"
|
15 |
+
logging.info(message)
|
16 |
+
return message
|
17 |
+
|
18 |
+
class CustomRecognizer:
|
19 |
+
def recognize(self, question):
|
20 |
+
if any(element_name.lower() in question.lower() for element_name in ["hydrogen", "diamond"]):
|
21 |
+
return RecognizerResult(question)
|
22 |
+
return RecognizerResult(None)
|
23 |
+
|
24 |
+
def get_top_intent(self, recognizer_result):
|
25 |
+
return "ElementDefense" if recognizer_result.text else "None"
|
26 |
+
|
27 |
+
class RecognizerResult:
|
28 |
+
def __init__(self, text):
|
29 |
+
self.text = text
|
30 |
+
|
31 |
+
class UniversalReasoning:
|
32 |
+
def __init__(self, config):
|
33 |
+
self.config = config
|
34 |
+
self.perspectives = self.initialize_perspectives()
|
35 |
+
self.elements = self.initialize_elements()
|
36 |
+
self.recognizer = CustomRecognizer()
|
37 |
+
|
38 |
+
def initialize_perspectives(self):
|
39 |
+
perspective_names = self.config.get('enabled_perspectives', [
|
40 |
+
"newton", "davinci", "human_intuition", "neural_network", "quantum_computing",
|
41 |
+
"resilient_kindness", "mathematical", "philosophical", "copilot", "bias_mitigation"
|
42 |
+
])
|
43 |
+
perspective_classes = {
|
44 |
+
"newton": NewtonPerspective,
|
45 |
+
"davinci": DaVinciPerspective,
|
46 |
+
"human_intuition": HumanIntuitionPerspective,
|
47 |
+
"neural_network": NeuralNetworkPerspective,
|
48 |
+
"quantum_computing": QuantumComputingPerspective,
|
49 |
+
"resilient_kindness": ResilientKindnessPerspective,
|
50 |
+
"mathematical": MathematicalPerspective,
|
51 |
+
"philosophical": PhilosophicalPerspective,
|
52 |
+
"copilot": CopilotPerspective,
|
53 |
+
"bias_mitigation": BiasMitigationPerspective
|
54 |
+
}
|
55 |
+
perspectives = []
|
56 |
+
for name in perspective_names:
|
57 |
+
cls = perspective_classes.get(name.lower())
|
58 |
+
if cls:
|
59 |
+
perspectives.append(cls(self.config))
|
60 |
+
logging.debug(f"Perspective '{name}' initialized.")
|
61 |
+
return perspectives
|
62 |
+
|
63 |
+
def initialize_elements(self):
|
64 |
+
return [
|
65 |
+
Element("Hydrogen", "H", "Lua", ["Simple", "Lightweight", "Versatile"],
|
66 |
+
["Integrates with other languages"], "Evasion"),
|
67 |
+
Element("Diamond", "D", "Kotlin", ["Modern", "Concise", "Safe"],
|
68 |
+
["Used for Android development"], "Adaptability")
|
69 |
+
]
|
70 |
+
|
71 |
+
async def generate_response(self, question):
|
72 |
+
responses = []
|
73 |
+
tasks = []
|
74 |
+
|
75 |
+
for perspective in self.perspectives:
|
76 |
+
if asyncio.iscoroutinefunction(perspective.generate_response):
|
77 |
+
tasks.append(perspective.generate_response(question))
|
78 |
+
else:
|
79 |
+
async def sync_wrapper(perspective, question):
|
80 |
+
return perspective.generate_response(question)
|
81 |
+
tasks.append(sync_wrapper(perspective, question))
|
82 |
+
|
83 |
+
perspective_results = await asyncio.gather(*tasks, return_exceptions=True)
|
84 |
+
|
85 |
+
for perspective, result in zip(self.perspectives, perspective_results):
|
86 |
+
if isinstance(result, Exception):
|
87 |
+
logging.error(f"Error from {perspective.__class__.__name__}: {result}")
|
88 |
+
else:
|
89 |
+
responses.append(result)
|
90 |
+
|
91 |
+
recognizer_result = self.recognizer.recognize(question)
|
92 |
+
top_intent = self.recognizer.get_top_intent(recognizer_result)
|
93 |
+
if top_intent == "ElementDefense":
|
94 |
+
element_name = recognizer_result.text.strip()
|
95 |
+
element = next((el for el in self.elements if el.name.lower() in element_name.lower()), None)
|
96 |
+
if element:
|
97 |
+
responses.append(element.execute_defense_function())
|
98 |
+
|
99 |
+
ethical = self.config.get("ethical_considerations", "Act transparently and respectfully.")
|
100 |
+
responses.append(f"**Ethical Considerations:**\n{ethical}")
|
101 |
+
|
102 |
+
return "\n\n".join(responses)
|
103 |
+
|
104 |
+
def save_response(self, response):
|
105 |
+
if self.config.get('enable_response_saving', False):
|
106 |
+
path = self.config.get('response_save_path', 'responses.txt')
|
107 |
+
with open(path, 'a', encoding='utf-8') as file:
|
108 |
+
file.write(response + '\n')
|
109 |
+
|
110 |
+
def backup_response(self, response):
|
111 |
+
if self.config.get('backup_responses', {}).get('enabled', False):
|
112 |
+
backup_path = self.config['backup_responses'].get('backup_path', 'backup_responses.txt')
|
113 |
+
with open(backup_path, 'a', encoding='utf-8') as file:
|
114 |
+
file.write(response + '\n')
|
project/cognitive_processor.py
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
# cognitive_processor.py
|
3 |
+
from typing import List
|
4 |
+
|
5 |
+
class CognitiveProcessor:
|
6 |
+
"""Multi-perspective analysis engine"""
|
7 |
+
MODES = {
|
8 |
+
"scientific": lambda q: f"Scientific Analysis: {q} demonstrates fundamental principles",
|
9 |
+
"creative": lambda q: f"Creative Insight: {q} suggests innovative approaches",
|
10 |
+
"emotional": lambda q: f"Emotional Interpretation: {q} conveys hopeful intent"
|
11 |
+
}
|
12 |
+
|
13 |
+
def __init__(self, modes: List[str]):
|
14 |
+
self.active_modes = [self.MODES[m] for m in modes if m in self.MODES]
|
15 |
+
|
16 |
+
def generate_insights(self, query: str) -> List[str]:
|
17 |
+
return [mode(query) for mode in self.active_modes]
|
project/config_manager.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# config_manager.py
|
2 |
+
import json
|
3 |
+
from typing import Dict
|
4 |
+
|
5 |
+
class EnhancedAIConfig:
|
6 |
+
"""Advanced configuration manager with encryption and validation"""
|
7 |
+
_DEFAULTS = {
|
8 |
+
"model": "gpt-4-turbo",
|
9 |
+
"safety_thresholds": {
|
10 |
+
"memory": 85,
|
11 |
+
"cpu": 90,
|
12 |
+
"response_time": 2.0
|
13 |
+
},
|
14 |
+
"defense_strategies": ["evasion", "adaptability", "barrier"],
|
15 |
+
"cognitive_modes": ["scientific", "creative", "emotional"]
|
16 |
+
}
|
17 |
+
|
18 |
+
def __init__(self, config_path: str = "ai_config.json"):
|
19 |
+
self.config = self._load_config(config_path)
|
20 |
+
self._validate()
|
21 |
+
|
22 |
+
def _load_config(self, path: str) -> Dict:
|
23 |
+
try:
|
24 |
+
with open(path, 'r') as f:
|
25 |
+
return self._merge_configs(json.load(f))
|
26 |
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
27 |
+
print(f"Error loading config file: {e}. Using default configuration.")
|
28 |
+
return self._DEFAULTS
|
29 |
+
|
30 |
+
def _merge_configs(self, user_config: Dict) -> Dict:
|
31 |
+
merged = self._DEFAULTS.copy()
|
32 |
+
for key, value in user_config.items():
|
33 |
+
if isinstance(value, dict) and key in merged:
|
34 |
+
merged[key].update(value)
|
35 |
+
else:
|
36 |
+
merged[key] = value
|
37 |
+
return merged
|
38 |
+
|
39 |
+
def _validate(self):
|
40 |
+
if not all(isinstance(mode, str) for mode in self.config["cognitive_modes"]):
|
41 |
+
raise ValueError("Invalid cognitive mode configuration")
|
project/dream_reweaver 2.py
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import os
|
3 |
+
import json
|
4 |
+
import random
|
5 |
+
from typing import List, Dict
|
6 |
+
from cognition_cocooner import CognitionCocooner
|
7 |
+
|
8 |
+
class DreamReweaver:
|
9 |
+
"""
|
10 |
+
Reweaves cocooned thoughts into dream-like synthetic narratives or planning prompts.
|
11 |
+
"""
|
12 |
+
def __init__(self, cocoon_dir: str = "cocoons"):
|
13 |
+
self.cocooner = CognitionCocooner(storage_path=cocoon_dir)
|
14 |
+
self.dream_log = []
|
15 |
+
|
16 |
+
def generate_dream_sequence(self, limit: int = 5) -> List[str]:
|
17 |
+
dream_sequence = []
|
18 |
+
cocoons = self._load_cocoons()
|
19 |
+
selected = random.sample(cocoons, min(limit, len(cocoons)))
|
20 |
+
|
21 |
+
for cocoon in selected:
|
22 |
+
wrapped = cocoon.get("wrapped")
|
23 |
+
sequence = self._interpret_cocoon(wrapped, cocoon.get("type"))
|
24 |
+
self.dream_log.append(sequence)
|
25 |
+
dream_sequence.append(sequence)
|
26 |
+
|
27 |
+
return dream_sequence
|
28 |
+
|
29 |
+
def _interpret_cocoon(self, wrapped: str, type_: str) -> str:
|
30 |
+
if type_ == "prompt":
|
31 |
+
return f"[DreamPrompt] {wrapped}"
|
32 |
+
elif type_ == "function":
|
33 |
+
return f"[DreamFunction] {wrapped}"
|
34 |
+
elif type_ == "symbolic":
|
35 |
+
return f"[DreamSymbol] {wrapped}"
|
36 |
+
elif type_ == "encrypted":
|
37 |
+
return "[Encrypted Thought Cocoon - Decryption Required]"
|
38 |
+
else:
|
39 |
+
return "[Unknown Dream Form]"
|
40 |
+
|
41 |
+
def _load_cocoons(self) -> List[Dict]:
|
42 |
+
cocoons = []
|
43 |
+
for file in os.listdir(self.cocooner.storage_path):
|
44 |
+
if file.endswith(".json"):
|
45 |
+
path = os.path.join(self.cocooner.storage_path, file)
|
46 |
+
with open(path, "r") as f:
|
47 |
+
cocoons.append(json.load(f))
|
48 |
+
return cocoons
|
49 |
+
|
50 |
+
if __name__ == "__main__":
|
51 |
+
dr = DreamReweaver()
|
52 |
+
dreams = dr.generate_dream_sequence()
|
53 |
+
print("\n".join(dreams))
|
project/eslint.config.js
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import js from '@eslint/js';
|
2 |
+
import globals from 'globals';
|
3 |
+
import reactHooks from 'eslint-plugin-react-hooks';
|
4 |
+
import reactRefresh from 'eslint-plugin-react-refresh';
|
5 |
+
import tseslint from 'typescript-eslint';
|
6 |
+
|
7 |
+
export default tseslint.config(
|
8 |
+
{ ignores: ['dist'] },
|
9 |
+
{
|
10 |
+
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
11 |
+
files: ['**/*.{ts,tsx}'],
|
12 |
+
languageOptions: {
|
13 |
+
ecmaVersion: 2020,
|
14 |
+
globals: globals.browser,
|
15 |
+
},
|
16 |
+
plugins: {
|
17 |
+
'react-hooks': reactHooks,
|
18 |
+
'react-refresh': reactRefresh,
|
19 |
+
},
|
20 |
+
rules: {
|
21 |
+
...reactHooks.configs.recommended.rules,
|
22 |
+
'react-refresh/only-export-components': [
|
23 |
+
'warn',
|
24 |
+
{ allowConstantExport: true },
|
25 |
+
],
|
26 |
+
},
|
27 |
+
}
|
28 |
+
);
|
project/index.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Codette AI Interface</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<div id="root"></div>
|
11 |
+
<script type="module" src="/src/main.tsx"></script>
|
12 |
+
</body>
|
13 |
+
</html>
|
project/package-lock.json
ADDED
File without changes
|
project/package.json
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "project",
|
3 |
+
"private": true,
|
4 |
+
"version": "0.0.0",
|
5 |
+
"type": "module",
|
6 |
+
"scripts": {
|
7 |
+
"dev": "node ./node_modules/vite/bin/vite.js",
|
8 |
+
"build": "tsc && node ./node_modules/vite/bin/vite.js build",
|
9 |
+
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
10 |
+
"preview": "node ./node_modules/vite/bin/vite.js preview"
|
11 |
+
},
|
12 |
+
"dependencies": {
|
13 |
+
"@supabase/supabase-js": "^2.39.3",
|
14 |
+
"framer-motion": "^11.0.3",
|
15 |
+
"lucide-react": "^0.309.0",
|
16 |
+
"react": "^18.2.0",
|
17 |
+
"react-dom": "^18.2.0",
|
18 |
+
"uuid": "^9.0.1"
|
19 |
+
},
|
20 |
+
"devDependencies": {
|
21 |
+
"@types/react": "^18.2.43",
|
22 |
+
"@types/react-dom": "^18.2.17",
|
23 |
+
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
24 |
+
"@typescript-eslint/parser": "^6.14.0",
|
25 |
+
"@vitejs/plugin-react": "^4.2.1",
|
26 |
+
"autoprefixer": "^10.4.17",
|
27 |
+
"eslint": "^8.55.0",
|
28 |
+
"eslint-plugin-react-hooks": "^4.6.0",
|
29 |
+
"eslint-plugin-react-refresh": "^0.4.5",
|
30 |
+
"postcss": "^8.4.33",
|
31 |
+
"tailwindcss": "^3.4.1",
|
32 |
+
"typescript": "^5.2.2",
|
33 |
+
"vite": "^5.0.8"
|
34 |
+
}
|
35 |
+
}
|
project/postcss.config.js
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
};
|
project/src/App.tsx
ADDED
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect, useRef } from 'react';
|
2 |
+
import { Zap, Brain, Settings, Moon, ChevronRight, Send, Bot, Server, Sparkles, Circle, User, AlertCircle } from 'lucide-react';
|
3 |
+
import { createClient } from '@supabase/supabase-js';
|
4 |
+
import ChatInterface from './components/ChatInterface';
|
5 |
+
import VisualizationPanel from './components/VisualizationPanel';
|
6 |
+
import Sidebar from './components/Sidebar';
|
7 |
+
import Header from './components/Header';
|
8 |
+
import CognitionCocooner from './services/CognitionCocooner';
|
9 |
+
import AICore from './services/AICore';
|
10 |
+
import { CodetteResponse } from './components/CodetteComponents';
|
11 |
+
|
12 |
+
interface Message {
|
13 |
+
role: string;
|
14 |
+
content: string;
|
15 |
+
timestamp: Date;
|
16 |
+
metadata?: CodetteResponse;
|
17 |
+
}
|
18 |
+
|
19 |
+
// Initialize Supabase client
|
20 |
+
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
21 |
+
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
22 |
+
|
23 |
+
if (!supabaseUrl || !supabaseKey) {
|
24 |
+
throw new Error('Missing Supabase environment variables');
|
25 |
+
}
|
26 |
+
|
27 |
+
const supabase = createClient(supabaseUrl, supabaseKey);
|
28 |
+
|
29 |
+
const App: React.FC = () => {
|
30 |
+
const [sidebarOpen, setSidebarOpen] = useState(true);
|
31 |
+
const [darkMode, setDarkMode] = useState(false);
|
32 |
+
const [messages, setMessages] = useState<Message[]>([]);
|
33 |
+
const [aiState, setAiState] = useState({
|
34 |
+
quantumState: [0.3, 0.7, 0.5],
|
35 |
+
chaosState: [0.2, 0.8, 0.4, 0.6],
|
36 |
+
activePerspectives: ['newton', 'davinci', 'neural_network', 'philosophical'],
|
37 |
+
ethicalScore: 0.93,
|
38 |
+
processingPower: 0.72
|
39 |
+
});
|
40 |
+
const [cocoons, setCocoons] = useState<Array<{id: string, type: string, wrapped: any}>>([]);
|
41 |
+
const [isProcessing, setIsProcessing] = useState(false);
|
42 |
+
const [isAdmin, setIsAdmin] = useState(false);
|
43 |
+
const [error, setError] = useState<string | null>(null);
|
44 |
+
const [currentUserId, setCurrentUserId] = useState<string | null>(null);
|
45 |
+
|
46 |
+
const aiCore = useRef<AICore | null>(null);
|
47 |
+
const cocooner = useRef(new CognitionCocooner());
|
48 |
+
|
49 |
+
useEffect(() => {
|
50 |
+
try {
|
51 |
+
aiCore.current = new AICore();
|
52 |
+
setError(null);
|
53 |
+
} catch (err: any) {
|
54 |
+
console.error('Error initializing AI Core:', err);
|
55 |
+
setError(err.message);
|
56 |
+
}
|
57 |
+
}, []);
|
58 |
+
|
59 |
+
useEffect(() => {
|
60 |
+
// Check if user is already authenticated
|
61 |
+
const checkAuth = async () => {
|
62 |
+
try {
|
63 |
+
const { data: { session }, error } = await supabase.auth.getSession();
|
64 |
+
|
65 |
+
if (error) {
|
66 |
+
console.error('Auth check error:', error.message);
|
67 |
+
return;
|
68 |
+
}
|
69 |
+
|
70 |
+
if (session?.user) {
|
71 |
+
setCurrentUserId(session.user.id);
|
72 |
+
const { data: { role } } = await supabase.rpc('get_user_role');
|
73 |
+
setIsAdmin(role === 'admin');
|
74 |
+
}
|
75 |
+
} catch (error: any) {
|
76 |
+
console.error('Auth check error:', error.message);
|
77 |
+
}
|
78 |
+
};
|
79 |
+
|
80 |
+
checkAuth();
|
81 |
+
}, []);
|
82 |
+
|
83 |
+
useEffect(() => {
|
84 |
+
if (!error) {
|
85 |
+
setMessages([
|
86 |
+
{
|
87 |
+
role: 'assistant',
|
88 |
+
content: 'Hello! I am Codette, an advanced AI assistant with recursive reasoning, self-learning capabilities, and multi-agent intelligence. How can I assist you today?',
|
89 |
+
timestamp: new Date(),
|
90 |
+
metadata: {
|
91 |
+
text: 'Hello! I am Codette, an advanced AI assistant with recursive reasoning, self-learning capabilities, and multi-agent intelligence. How can I assist you today?',
|
92 |
+
instabilityFlag: false,
|
93 |
+
perspectivesUsed: ['greeting', 'introduction'],
|
94 |
+
cocoonLog: ['Initializing Codette AI...', 'Quantum state stabilized'],
|
95 |
+
forceRefresh: () => handleForceRefresh('Hello! I am Codette, an advanced AI assistant with recursive reasoning, self-learning capabilities, and multi-agent intelligence. How can I assist you today?')
|
96 |
+
}
|
97 |
+
}
|
98 |
+
]);
|
99 |
+
}
|
100 |
+
}, [error]);
|
101 |
+
|
102 |
+
const handleForceRefresh = async (content: string) => {
|
103 |
+
if (!aiCore.current) return;
|
104 |
+
|
105 |
+
setIsProcessing(true);
|
106 |
+
try {
|
107 |
+
const response = await aiCore.current.processInput(content, true, currentUserId || undefined);
|
108 |
+
|
109 |
+
const assistantMessage: Message = {
|
110 |
+
role: 'assistant',
|
111 |
+
content: response,
|
112 |
+
timestamp: new Date(),
|
113 |
+
metadata: {
|
114 |
+
text: response,
|
115 |
+
instabilityFlag: Math.random() > 0.8,
|
116 |
+
perspectivesUsed: aiState.activePerspectives.slice(0, 3),
|
117 |
+
cocoonLog: [`Regenerating response for: ${content}`, `Generated new response at ${new Date().toISOString()}`],
|
118 |
+
forceRefresh: () => handleForceRefresh(content)
|
119 |
+
}
|
120 |
+
};
|
121 |
+
|
122 |
+
setMessages(prev => [...prev.slice(0, -1), assistantMessage]);
|
123 |
+
} catch (error) {
|
124 |
+
console.error('Error regenerating response:', error);
|
125 |
+
} finally {
|
126 |
+
setIsProcessing(false);
|
127 |
+
}
|
128 |
+
};
|
129 |
+
|
130 |
+
const toggleSidebar = () => {
|
131 |
+
setSidebarOpen(!sidebarOpen);
|
132 |
+
};
|
133 |
+
|
134 |
+
const toggleDarkMode = () => {
|
135 |
+
setDarkMode(!darkMode);
|
136 |
+
document.documentElement.classList.toggle('dark');
|
137 |
+
};
|
138 |
+
|
139 |
+
const sendMessage = async (content: string) => {
|
140 |
+
if (!aiCore.current) {
|
141 |
+
setError('AI Core is not initialized. Please check your configuration.');
|
142 |
+
return;
|
143 |
+
}
|
144 |
+
|
145 |
+
const userMessage: Message = {
|
146 |
+
role: 'user',
|
147 |
+
content,
|
148 |
+
timestamp: new Date()
|
149 |
+
};
|
150 |
+
|
151 |
+
setMessages(prev => [...prev, userMessage]);
|
152 |
+
setIsProcessing(true);
|
153 |
+
|
154 |
+
try {
|
155 |
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
156 |
+
|
157 |
+
const thought = { query: content, timestamp: new Date() };
|
158 |
+
const cocoonId = cocooner.current.wrap(thought);
|
159 |
+
setCocoons(prev => [...prev, {
|
160 |
+
id: cocoonId,
|
161 |
+
type: 'prompt',
|
162 |
+
wrapped: thought
|
163 |
+
}]);
|
164 |
+
|
165 |
+
const response = await aiCore.current.processInput(content, false, currentUserId || undefined);
|
166 |
+
|
167 |
+
setAiState(prev => ({
|
168 |
+
...prev,
|
169 |
+
quantumState: [Math.random(), Math.random(), Math.random()].map(v => v.toFixed(2)).map(Number),
|
170 |
+
chaosState: [Math.random(), Math.random(), Math.random(), Math.random()].map(v => v.toFixed(2)).map(Number),
|
171 |
+
ethicalScore: Number((prev.ethicalScore + Math.random() * 0.1 - 0.05).toFixed(2)),
|
172 |
+
processingPower: Number((prev.processingPower + Math.random() * 0.1 - 0.05).toFixed(2))
|
173 |
+
}));
|
174 |
+
|
175 |
+
const assistantMessage: Message = {
|
176 |
+
role: 'assistant',
|
177 |
+
content: response,
|
178 |
+
timestamp: new Date(),
|
179 |
+
metadata: {
|
180 |
+
text: response,
|
181 |
+
instabilityFlag: Math.random() > 0.8,
|
182 |
+
perspectivesUsed: aiState.activePerspectives.slice(0, 3),
|
183 |
+
cocoonLog: [`Processing query: ${content}`, `Generated response at ${new Date().toISOString()}`],
|
184 |
+
forceRefresh: () => handleForceRefresh(content)
|
185 |
+
}
|
186 |
+
};
|
187 |
+
|
188 |
+
setMessages(prev => [...prev, assistantMessage]);
|
189 |
+
} catch (error: any) {
|
190 |
+
console.error('Error processing message:', error);
|
191 |
+
|
192 |
+
setMessages(prev => [...prev, {
|
193 |
+
role: 'system',
|
194 |
+
content: 'An error occurred while processing your request. Please check your configuration and try again.',
|
195 |
+
timestamp: new Date()
|
196 |
+
}]);
|
197 |
+
} finally {
|
198 |
+
setIsProcessing(false);
|
199 |
+
}
|
200 |
+
};
|
201 |
+
|
202 |
+
if (error) {
|
203 |
+
return (
|
204 |
+
<div className={`min-h-screen flex items-center justify-center p-4 ${darkMode ? 'dark bg-gray-900 text-white' : 'bg-gray-50 text-gray-900'}`}>
|
205 |
+
<div className={`max-w-md w-full p-6 rounded-lg shadow-lg ${darkMode ? 'bg-gray-800' : 'bg-white'}`}>
|
206 |
+
<div className="flex items-center justify-center mb-4">
|
207 |
+
<AlertCircle className="text-red-500" size={48} />
|
208 |
+
</div>
|
209 |
+
<h1 className="text-xl font-bold text-center mb-4">Configuration Error</h1>
|
210 |
+
<p className="text-center mb-6">{error}</p>
|
211 |
+
<div className={`p-4 rounded-md ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
212 |
+
<p className="text-sm">
|
213 |
+
Please ensure you have:
|
214 |
+
<ol className="list-decimal ml-5 mt-2 space-y-1">
|
215 |
+
<li>Created a .env file</li>
|
216 |
+
<li>Added your OpenAI API key to the .env file</li>
|
217 |
+
<li>Added your Supabase configuration</li>
|
218 |
+
</ol>
|
219 |
+
</p>
|
220 |
+
</div>
|
221 |
+
</div>
|
222 |
+
</div>
|
223 |
+
);
|
224 |
+
}
|
225 |
+
|
226 |
+
return (
|
227 |
+
<div className={`flex flex-col h-screen transition-colors duration-300 ${darkMode ? 'dark bg-gray-900 text-white' : 'bg-gray-50 text-gray-900'}`}>
|
228 |
+
<Header
|
229 |
+
toggleSidebar={toggleSidebar}
|
230 |
+
toggleDarkMode={toggleDarkMode}
|
231 |
+
darkMode={darkMode}
|
232 |
+
aiState={aiState}
|
233 |
+
/>
|
234 |
+
|
235 |
+
<div className="flex flex-1 overflow-hidden">
|
236 |
+
<Sidebar
|
237 |
+
isOpen={sidebarOpen}
|
238 |
+
cocoons={cocoons}
|
239 |
+
aiState={aiState}
|
240 |
+
darkMode={darkMode}
|
241 |
+
supabase={supabase}
|
242 |
+
isAdmin={isAdmin}
|
243 |
+
setIsAdmin={setIsAdmin}
|
244 |
+
/>
|
245 |
+
|
246 |
+
<main className="flex-1 flex flex-col md:flex-row overflow-hidden">
|
247 |
+
<ChatInterface
|
248 |
+
messages={messages}
|
249 |
+
sendMessage={sendMessage}
|
250 |
+
isProcessing={isProcessing}
|
251 |
+
darkMode={darkMode}
|
252 |
+
/>
|
253 |
+
|
254 |
+
<VisualizationPanel
|
255 |
+
aiState={aiState}
|
256 |
+
darkMode={darkMode}
|
257 |
+
/>
|
258 |
+
</main>
|
259 |
+
</div>
|
260 |
+
</div>
|
261 |
+
);
|
262 |
+
};
|
263 |
+
|
264 |
+
export default App;
|
project/src/components/AdminLogin.tsx
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from 'react';
|
2 |
+
import { Lock, AlertCircle } from 'lucide-react';
|
3 |
+
|
4 |
+
interface AdminLoginProps {
|
5 |
+
onLogin: (password: string) => void;
|
6 |
+
darkMode: boolean;
|
7 |
+
error?: string | null;
|
8 |
+
}
|
9 |
+
|
10 |
+
const AdminLogin: React.FC<AdminLoginProps> = ({ onLogin, darkMode, error }) => {
|
11 |
+
const [password, setPassword] = useState('');
|
12 |
+
const [isLoading, setIsLoading] = useState(false);
|
13 |
+
|
14 |
+
const handleSubmit = async (e: React.FormEvent) => {
|
15 |
+
e.preventDefault();
|
16 |
+
setIsLoading(true);
|
17 |
+
|
18 |
+
try {
|
19 |
+
await onLogin(password);
|
20 |
+
} catch (err: any) {
|
21 |
+
// Error is now handled by the parent component
|
22 |
+
} finally {
|
23 |
+
setIsLoading(false);
|
24 |
+
}
|
25 |
+
};
|
26 |
+
|
27 |
+
return (
|
28 |
+
<div className={`p-6 rounded-lg ${darkMode ? 'bg-gray-800' : 'bg-white'}`}>
|
29 |
+
<div className="flex items-center justify-center mb-6">
|
30 |
+
<Lock className={`${darkMode ? 'text-blue-400' : 'text-blue-600'} w-12 h-12`} />
|
31 |
+
</div>
|
32 |
+
<h2 className={`text-xl font-bold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-900'}`}>
|
33 |
+
Admin Access Required
|
34 |
+
</h2>
|
35 |
+
<p className={`text-sm text-center mb-6 ${darkMode ? 'text-gray-300' : 'text-gray-600'}`}>
|
36 |
+
Please enter the admin password to access settings
|
37 |
+
</p>
|
38 |
+
<form onSubmit={handleSubmit} className="space-y-4">
|
39 |
+
<div>
|
40 |
+
<input
|
41 |
+
type="password"
|
42 |
+
value={password}
|
43 |
+
onChange={(e) => setPassword(e.target.value)}
|
44 |
+
placeholder="Enter admin password"
|
45 |
+
className={`w-full px-4 py-2 rounded-md border ${
|
46 |
+
darkMode
|
47 |
+
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400'
|
48 |
+
: 'bg-white border-gray-300 text-gray-900 placeholder-gray-500'
|
49 |
+
} focus:outline-none focus:ring-2 focus:ring-blue-500`}
|
50 |
+
disabled={isLoading}
|
51 |
+
/>
|
52 |
+
{error && (
|
53 |
+
<div className="mt-2 p-2 rounded-md bg-red-100 dark:bg-red-900 flex items-start space-x-2">
|
54 |
+
<AlertCircle className="flex-shrink-0 text-red-500 dark:text-red-400" size={16} />
|
55 |
+
<p className="text-sm text-red-600 dark:text-red-300">
|
56 |
+
{error}
|
57 |
+
</p>
|
58 |
+
</div>
|
59 |
+
)}
|
60 |
+
</div>
|
61 |
+
<button
|
62 |
+
type="submit"
|
63 |
+
disabled={isLoading}
|
64 |
+
className={`w-full py-2 px-4 rounded-md ${
|
65 |
+
darkMode
|
66 |
+
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
67 |
+
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
68 |
+
} transition-colors duration-200 ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`}
|
69 |
+
>
|
70 |
+
{isLoading ? 'Logging in...' : 'Login'}
|
71 |
+
</button>
|
72 |
+
</form>
|
73 |
+
</div>
|
74 |
+
);
|
75 |
+
};
|
76 |
+
|
77 |
+
export default AdminLogin;
|
project/src/components/ChatInterface.tsx
ADDED
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useRef, useEffect } from 'react';
|
2 |
+
import { Send, Circle, Bot, User, Sparkles, Brain } from 'lucide-react';
|
3 |
+
import { CodetteResponseCard, CodetteResponse } from './CodetteComponents';
|
4 |
+
|
5 |
+
interface Message {
|
6 |
+
role: string;
|
7 |
+
content: string;
|
8 |
+
timestamp: Date;
|
9 |
+
metadata?: CodetteResponse;
|
10 |
+
}
|
11 |
+
|
12 |
+
interface ChatInterfaceProps {
|
13 |
+
messages: Message[];
|
14 |
+
sendMessage: (content: string) => void;
|
15 |
+
isProcessing: boolean;
|
16 |
+
darkMode: boolean;
|
17 |
+
}
|
18 |
+
|
19 |
+
const ChatInterface: React.FC<ChatInterfaceProps> = ({
|
20 |
+
messages,
|
21 |
+
sendMessage,
|
22 |
+
isProcessing,
|
23 |
+
darkMode
|
24 |
+
}) => {
|
25 |
+
const [input, setInput] = useState('');
|
26 |
+
const [isDreamMode, setIsDreamMode] = useState(false);
|
27 |
+
const messagesEndRef = useRef<HTMLDivElement>(null);
|
28 |
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
29 |
+
|
30 |
+
useEffect(() => {
|
31 |
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
32 |
+
}, [messages]);
|
33 |
+
|
34 |
+
useEffect(() => {
|
35 |
+
inputRef.current?.focus();
|
36 |
+
}, []);
|
37 |
+
|
38 |
+
const handleSubmit = (e: React.FormEvent) => {
|
39 |
+
e.preventDefault();
|
40 |
+
if (input.trim() && !isProcessing) {
|
41 |
+
const finalInput = isDreamMode ? `dream about ${input.trim()}` : input.trim();
|
42 |
+
sendMessage(finalInput);
|
43 |
+
setInput('');
|
44 |
+
}
|
45 |
+
};
|
46 |
+
|
47 |
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
48 |
+
if (e.key === 'Enter' && !e.shiftKey) {
|
49 |
+
e.preventDefault();
|
50 |
+
handleSubmit(e);
|
51 |
+
}
|
52 |
+
};
|
53 |
+
|
54 |
+
const toggleDreamMode = () => {
|
55 |
+
setIsDreamMode(!isDreamMode);
|
56 |
+
if (!isDreamMode) {
|
57 |
+
inputRef.current?.focus();
|
58 |
+
}
|
59 |
+
};
|
60 |
+
|
61 |
+
return (
|
62 |
+
<div className={`flex-1 flex flex-col ${darkMode ? 'bg-gray-800' : 'bg-white'} shadow-lg rounded-lg overflow-hidden transition-colors duration-300`}>
|
63 |
+
<div className={`p-4 border-b ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}>
|
64 |
+
<h2 className="text-lg font-semibold flex items-center">
|
65 |
+
<Bot className="mr-2" size={18} />
|
66 |
+
Conversation with Codette
|
67 |
+
</h2>
|
68 |
+
</div>
|
69 |
+
|
70 |
+
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
71 |
+
{messages.map((message, index) => (
|
72 |
+
<div
|
73 |
+
key={index}
|
74 |
+
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
75 |
+
>
|
76 |
+
{message.role === 'assistant' && message.metadata ? (
|
77 |
+
<CodetteResponseCard response={message.metadata} />
|
78 |
+
) : (
|
79 |
+
<div
|
80 |
+
className={`max-w-[80%] rounded-lg p-3 ${
|
81 |
+
message.role === 'user'
|
82 |
+
? darkMode
|
83 |
+
? 'bg-blue-600 text-white'
|
84 |
+
: 'bg-blue-100 text-blue-900'
|
85 |
+
: message.role === 'system'
|
86 |
+
? darkMode
|
87 |
+
? 'bg-red-900 text-white'
|
88 |
+
: 'bg-red-100 text-red-900'
|
89 |
+
: darkMode
|
90 |
+
? 'bg-gray-700 text-white'
|
91 |
+
: 'bg-gray-100 text-gray-900'
|
92 |
+
}`}
|
93 |
+
>
|
94 |
+
<div className="flex items-start mb-1">
|
95 |
+
{message.role === 'user' ? (
|
96 |
+
<User className="mr-2 mt-1" size={14} />
|
97 |
+
) : message.role === 'system' ? (
|
98 |
+
<Circle className="mr-2 mt-1" size={14} />
|
99 |
+
) : (
|
100 |
+
<Bot className="mr-2 mt-1" size={14} />
|
101 |
+
)}
|
102 |
+
<div className="text-sm font-semibold">
|
103 |
+
{message.role === 'user' ? 'You' : message.role === 'system' ? 'System' : 'Codette'}
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
<div className="whitespace-pre-wrap">
|
107 |
+
{message.content}
|
108 |
+
</div>
|
109 |
+
<div className="text-xs opacity-70 mt-1 text-right">
|
110 |
+
{message.timestamp.toLocaleTimeString()}
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
)}
|
114 |
+
</div>
|
115 |
+
))}
|
116 |
+
|
117 |
+
{isProcessing && (
|
118 |
+
<div className="flex justify-start">
|
119 |
+
<div className={`rounded-lg p-3 ${
|
120 |
+
darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-900'
|
121 |
+
}`}>
|
122 |
+
<div className="flex items-center">
|
123 |
+
<Bot className="mr-2" size={14} />
|
124 |
+
<div className="text-sm font-semibold">Codette</div>
|
125 |
+
</div>
|
126 |
+
<div className="flex items-center mt-2">
|
127 |
+
<div className="flex space-x-1">
|
128 |
+
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0ms' }}></div>
|
129 |
+
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '300ms' }}></div>
|
130 |
+
<div className="typing-dot h-2 w-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '600ms' }}></div>
|
131 |
+
</div>
|
132 |
+
<div className="ml-3 text-sm italic opacity-70">
|
133 |
+
{isDreamMode ? 'Weaving dreams through quantum threads...' : 'Processing through recursive thought loops...'}
|
134 |
+
</div>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
)}
|
139 |
+
|
140 |
+
<div ref={messagesEndRef} />
|
141 |
+
</div>
|
142 |
+
|
143 |
+
<form
|
144 |
+
onSubmit={handleSubmit}
|
145 |
+
className={`p-4 border-t ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}
|
146 |
+
>
|
147 |
+
<div className="flex items-center mb-2">
|
148 |
+
<button
|
149 |
+
type="button"
|
150 |
+
onClick={toggleDreamMode}
|
151 |
+
className={`flex items-center px-3 py-1 rounded-full text-sm transition-colors ${
|
152 |
+
isDreamMode
|
153 |
+
? darkMode
|
154 |
+
? 'bg-purple-600 text-white'
|
155 |
+
: 'bg-purple-100 text-purple-900'
|
156 |
+
: darkMode
|
157 |
+
? 'bg-gray-700 text-gray-300 hover:bg-gray-600'
|
158 |
+
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
159 |
+
}`}
|
160 |
+
>
|
161 |
+
{isDreamMode ? (
|
162 |
+
<>
|
163 |
+
<Sparkles size={14} className="mr-1" />
|
164 |
+
Dreamweaver Active
|
165 |
+
</>
|
166 |
+
) : (
|
167 |
+
<>
|
168 |
+
<Brain size={14} className="mr-1" />
|
169 |
+
Enable Dreamweaver
|
170 |
+
</>
|
171 |
+
)}
|
172 |
+
</button>
|
173 |
+
</div>
|
174 |
+
|
175 |
+
<div className="flex">
|
176 |
+
<textarea
|
177 |
+
ref={inputRef}
|
178 |
+
value={input}
|
179 |
+
onChange={(e) => setInput(e.target.value)}
|
180 |
+
onKeyDown={handleKeyDown}
|
181 |
+
placeholder={isDreamMode ? "Enter a concept for Codette to dream about..." : "Ask Codette anything..."}
|
182 |
+
className={`flex-1 resize-none border rounded-lg p-2 focus:outline-none focus:ring-2 ${
|
183 |
+
isDreamMode
|
184 |
+
? 'focus:ring-purple-500'
|
185 |
+
: 'focus:ring-blue-500'
|
186 |
+
} ${
|
187 |
+
darkMode
|
188 |
+
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400'
|
189 |
+
: 'bg-white border-gray-300 text-gray-900 placeholder-gray-500'
|
190 |
+
}`}
|
191 |
+
rows={2}
|
192 |
+
disabled={isProcessing}
|
193 |
+
/>
|
194 |
+
<button
|
195 |
+
type="submit"
|
196 |
+
disabled={!input.trim() || isProcessing}
|
197 |
+
className={`ml-2 p-2 rounded-lg transition-colors ${
|
198 |
+
!input.trim() || isProcessing
|
199 |
+
? darkMode
|
200 |
+
? 'bg-gray-700 text-gray-500'
|
201 |
+
: 'bg-gray-200 text-gray-400'
|
202 |
+
: isDreamMode
|
203 |
+
? darkMode
|
204 |
+
? 'bg-purple-600 text-white hover:bg-purple-700'
|
205 |
+
: 'bg-purple-500 text-white hover:bg-purple-600'
|
206 |
+
: darkMode
|
207 |
+
? 'bg-blue-600 text-white hover:bg-blue-700'
|
208 |
+
: 'bg-blue-500 text-white hover:bg-blue-600'
|
209 |
+
}`}
|
210 |
+
>
|
211 |
+
<Send size={20} />
|
212 |
+
</button>
|
213 |
+
</div>
|
214 |
+
</form>
|
215 |
+
</div>
|
216 |
+
);
|
217 |
+
};
|
218 |
+
|
219 |
+
export default ChatInterface;
|
project/src/components/CodetteComponents.tsx
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useEffect } from 'react';
|
2 |
+
import { motion } from 'framer-motion';
|
3 |
+
|
4 |
+
// 💬 Perspective Trail Display
|
5 |
+
export function PerspectiveTrail({ perspectives }: { perspectives: string[] }) {
|
6 |
+
return (
|
7 |
+
<div className="mt-4 text-sm text-purple-700">
|
8 |
+
<p className="font-semibold">Activated Perspectives:</p>
|
9 |
+
<motion.ul
|
10 |
+
className="list-disc ml-5"
|
11 |
+
initial={{ opacity: 0 }}
|
12 |
+
animate={{ opacity: 1 }}
|
13 |
+
transition={{ duration: 0.5 }}
|
14 |
+
>
|
15 |
+
{perspectives.map((perspective, index) => (
|
16 |
+
<motion.li
|
17 |
+
key={index}
|
18 |
+
initial={{ x: -20, opacity: 0 }}
|
19 |
+
animate={{ x: 0, opacity: 1 }}
|
20 |
+
transition={{ delay: index * 0.1 }}
|
21 |
+
>
|
22 |
+
{perspective}
|
23 |
+
</motion.li>
|
24 |
+
))}
|
25 |
+
</motion.ul>
|
26 |
+
</div>
|
27 |
+
);
|
28 |
+
}
|
29 |
+
|
30 |
+
// 🔄 Cocoon Replay Viewer
|
31 |
+
export function CocoonReplay({ cocoons }: { cocoons: string[] }) {
|
32 |
+
const [activeIndex, setActiveIndex] = useState(0);
|
33 |
+
|
34 |
+
useEffect(() => {
|
35 |
+
const timer = setInterval(() => {
|
36 |
+
setActiveIndex(prev => (prev + 1) % cocoons.length);
|
37 |
+
}, 3000);
|
38 |
+
return () => clearInterval(timer);
|
39 |
+
}, [cocoons.length]);
|
40 |
+
|
41 |
+
return (
|
42 |
+
<div className="mt-4 text-sm text-green-700">
|
43 |
+
<p className="font-semibold">Cocoon Memory:</p>
|
44 |
+
<motion.div
|
45 |
+
className="bg-green-100 p-2 rounded-md max-h-40 overflow-y-scroll"
|
46 |
+
initial={{ opacity: 0, y: 20 }}
|
47 |
+
animate={{ opacity: 1, y: 0 }}
|
48 |
+
transition={{ duration: 0.5 }}
|
49 |
+
>
|
50 |
+
{cocoons.map((cocoon, idx) => (
|
51 |
+
<motion.pre
|
52 |
+
key={idx}
|
53 |
+
className={`whitespace-pre-wrap text-xs mb-2 transition-colors duration-300 ${
|
54 |
+
idx === activeIndex ? 'bg-green-200 rounded p-1' : ''
|
55 |
+
}`}
|
56 |
+
initial={{ opacity: 0 }}
|
57 |
+
animate={{ opacity: 1 }}
|
58 |
+
transition={{ delay: idx * 0.1 }}
|
59 |
+
>
|
60 |
+
{cocoon}
|
61 |
+
</motion.pre>
|
62 |
+
))}
|
63 |
+
</motion.div>
|
64 |
+
</div>
|
65 |
+
);
|
66 |
+
}
|
67 |
+
|
68 |
+
// 🌗 Quantum Collapse Detector
|
69 |
+
export function CollapseDetector({ isUnstable }: { isUnstable: boolean }) {
|
70 |
+
return (
|
71 |
+
<motion.div
|
72 |
+
animate={{
|
73 |
+
scale: isUnstable ? [1, 1.3, 1] : 1,
|
74 |
+
opacity: isUnstable ? [0.6, 1, 0.6] : 1,
|
75 |
+
}}
|
76 |
+
transition={{ duration: 1.5, repeat: Infinity }}
|
77 |
+
className={`w-4 h-4 rounded-full mt-2 ml-2 ${
|
78 |
+
isUnstable ? "bg-red-500" : "bg-blue-300"
|
79 |
+
}`}
|
80 |
+
title={isUnstable ? "Quantum Instability Detected" : "Stable"}
|
81 |
+
/>
|
82 |
+
);
|
83 |
+
}
|
84 |
+
|
85 |
+
// 🧠 CodetteResponse Interface
|
86 |
+
export interface CodetteResponse {
|
87 |
+
text: string;
|
88 |
+
instabilityFlag: boolean;
|
89 |
+
perspectivesUsed: string[];
|
90 |
+
cocoonLog: string[];
|
91 |
+
forceRefresh: () => void;
|
92 |
+
}
|
93 |
+
|
94 |
+
// 🧠 CodetteResponseCard Component
|
95 |
+
export function CodetteResponseCard({ response }: { response: CodetteResponse }) {
|
96 |
+
const [loopCount, setLoopCount] = useState(0);
|
97 |
+
const [introspectiveMessage, setIntrospectiveMessage] = useState<string | null>(null);
|
98 |
+
|
99 |
+
useEffect(() => {
|
100 |
+
const last = sessionStorage.getItem("lastCodetteResponse");
|
101 |
+
if (last === response.text) {
|
102 |
+
console.warn("Codette is repeating herself. Triggering fallback logic.");
|
103 |
+
setLoopCount(prev => prev + 1);
|
104 |
+
|
105 |
+
if (response.forceRefresh) {
|
106 |
+
response.forceRefresh();
|
107 |
+
}
|
108 |
+
|
109 |
+
setIntrospectiveMessage("I feel like I've said this before... Let me think differently.");
|
110 |
+
} else {
|
111 |
+
setLoopCount(0);
|
112 |
+
setIntrospectiveMessage(null);
|
113 |
+
}
|
114 |
+
sessionStorage.setItem("lastCodetteResponse", response.text);
|
115 |
+
}, [response.text]);
|
116 |
+
|
117 |
+
return (
|
118 |
+
<motion.div
|
119 |
+
className="border border-gray-200 p-4 rounded-xl shadow-sm bg-white max-w-[80%]"
|
120 |
+
initial={{ opacity: 0, y: 20 }}
|
121 |
+
animate={{ opacity: 1, y: 0 }}
|
122 |
+
transition={{ duration: 0.5 }}
|
123 |
+
>
|
124 |
+
<p className="whitespace-pre-wrap">{response.text}</p>
|
125 |
+
{introspectiveMessage && (
|
126 |
+
<motion.p
|
127 |
+
className="text-xs text-rose-600 italic mt-2"
|
128 |
+
initial={{ opacity: 0 }}
|
129 |
+
animate={{ opacity: 1 }}
|
130 |
+
transition={{ delay: 0.3 }}
|
131 |
+
>
|
132 |
+
{introspectiveMessage}
|
133 |
+
</motion.p>
|
134 |
+
)}
|
135 |
+
<div className="flex items-center mt-2">
|
136 |
+
<span className="text-xs text-gray-500">System Readout:</span>
|
137 |
+
<CollapseDetector isUnstable={response.instabilityFlag || loopCount > 2} />
|
138 |
+
</div>
|
139 |
+
<PerspectiveTrail perspectives={response.perspectivesUsed} />
|
140 |
+
<CocoonReplay cocoons={response.cocoonLog} />
|
141 |
+
</motion.div>
|
142 |
+
);
|
143 |
+
}
|
project/src/components/FileList.tsx
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useEffect, useState } from 'react';
|
2 |
+
import { FileText, Download, Loader, Trash2, AlertCircle } from 'lucide-react';
|
3 |
+
|
4 |
+
interface FileListProps {
|
5 |
+
supabase: any;
|
6 |
+
darkMode: boolean;
|
7 |
+
isAdmin?: boolean;
|
8 |
+
}
|
9 |
+
|
10 |
+
interface FileData {
|
11 |
+
id: string;
|
12 |
+
filename: string;
|
13 |
+
storage_path: string;
|
14 |
+
file_type: string;
|
15 |
+
uploaded_at: string;
|
16 |
+
}
|
17 |
+
|
18 |
+
const FileList: React.FC<FileListProps> = ({ supabase, darkMode, isAdmin = false }) => {
|
19 |
+
const [files, setFiles] = useState<FileData[]>([]);
|
20 |
+
const [loading, setLoading] = useState(true);
|
21 |
+
const [error, setError] = useState<string | null>(null);
|
22 |
+
const [downloading, setDownloading] = useState<string | null>(null);
|
23 |
+
const [deleting, setDeleting] = useState<string | null>(null);
|
24 |
+
|
25 |
+
useEffect(() => {
|
26 |
+
fetchFiles();
|
27 |
+
}, []);
|
28 |
+
|
29 |
+
const fetchFiles = async () => {
|
30 |
+
try {
|
31 |
+
setError(null);
|
32 |
+
setLoading(true);
|
33 |
+
|
34 |
+
// Check if Supabase is initialized properly
|
35 |
+
if (!supabase) {
|
36 |
+
throw new Error('Database connection not initialized');
|
37 |
+
}
|
38 |
+
|
39 |
+
// Test connection with a simple query first
|
40 |
+
const { error: connectionError } = await supabase
|
41 |
+
.from('codette_files')
|
42 |
+
.select('count');
|
43 |
+
|
44 |
+
if (connectionError) {
|
45 |
+
throw connectionError;
|
46 |
+
}
|
47 |
+
|
48 |
+
// Proceed with actual data fetch
|
49 |
+
const { data, error } = await supabase
|
50 |
+
.from('codette_files')
|
51 |
+
.select('*')
|
52 |
+
.order('uploaded_at', { ascending: false });
|
53 |
+
|
54 |
+
if (error) throw error;
|
55 |
+
setFiles(data || []);
|
56 |
+
} catch (err: any) {
|
57 |
+
console.error('Error fetching files:', err);
|
58 |
+
setError(err.message || 'Failed to fetch files. Please check your connection.');
|
59 |
+
setFiles([]);
|
60 |
+
} finally {
|
61 |
+
setLoading(false);
|
62 |
+
}
|
63 |
+
};
|
64 |
+
|
65 |
+
const handleDownload = async (file: FileData) => {
|
66 |
+
try {
|
67 |
+
setDownloading(file.id);
|
68 |
+
setError(null);
|
69 |
+
|
70 |
+
const { data, error } = await supabase.storage
|
71 |
+
.from('codette-files')
|
72 |
+
.download(file.storage_path);
|
73 |
+
|
74 |
+
if (error) throw error;
|
75 |
+
|
76 |
+
const url = window.URL.createObjectURL(data);
|
77 |
+
const a = document.createElement('a');
|
78 |
+
a.href = url;
|
79 |
+
a.download = file.filename;
|
80 |
+
document.body.appendChild(a);
|
81 |
+
a.click();
|
82 |
+
window.URL.revokeObjectURL(url);
|
83 |
+
document.body.removeChild(a);
|
84 |
+
} catch (err: any) {
|
85 |
+
console.error('Error downloading file:', err);
|
86 |
+
setError(err.message || 'Failed to download file. Please try again.');
|
87 |
+
} finally {
|
88 |
+
setDownloading(null);
|
89 |
+
}
|
90 |
+
};
|
91 |
+
|
92 |
+
const handleDelete = async (file: FileData) => {
|
93 |
+
if (!isAdmin) return;
|
94 |
+
|
95 |
+
if (!confirm('Are you sure you want to delete this file?')) return;
|
96 |
+
|
97 |
+
try {
|
98 |
+
setDeleting(file.id);
|
99 |
+
setError(null);
|
100 |
+
|
101 |
+
// Delete from storage
|
102 |
+
const { error: storageError } = await supabase.storage
|
103 |
+
.from('codette-files')
|
104 |
+
.remove([file.storage_path]);
|
105 |
+
|
106 |
+
if (storageError) throw storageError;
|
107 |
+
|
108 |
+
// Delete from database
|
109 |
+
const { error: dbError } = await supabase
|
110 |
+
.from('codette_files')
|
111 |
+
.delete()
|
112 |
+
.match({ id: file.id });
|
113 |
+
|
114 |
+
if (dbError) throw dbError;
|
115 |
+
|
116 |
+
// Update local state
|
117 |
+
setFiles(files.filter(f => f.id !== file.id));
|
118 |
+
} catch (err: any) {
|
119 |
+
console.error('Error deleting file:', err);
|
120 |
+
setError(err.message || 'Failed to delete file. Please try again.');
|
121 |
+
} finally {
|
122 |
+
setDeleting(null);
|
123 |
+
}
|
124 |
+
};
|
125 |
+
|
126 |
+
const handleRetry = () => {
|
127 |
+
fetchFiles();
|
128 |
+
};
|
129 |
+
|
130 |
+
if (loading) {
|
131 |
+
return (
|
132 |
+
<div className="flex items-center justify-center p-4">
|
133 |
+
<Loader className="animate-spin" size={24} />
|
134 |
+
</div>
|
135 |
+
);
|
136 |
+
}
|
137 |
+
|
138 |
+
if (error) {
|
139 |
+
return (
|
140 |
+
<div className={`p-4 rounded-lg ${darkMode ? 'bg-red-900/20' : 'bg-red-50'}`}>
|
141 |
+
<div className="flex items-start space-x-2">
|
142 |
+
<AlertCircle className={`flex-shrink-0 ${darkMode ? 'text-red-400' : 'text-red-500'}`} size={20} />
|
143 |
+
<div className="flex-1">
|
144 |
+
<p className={`text-sm font-medium ${darkMode ? 'text-red-400' : 'text-red-800'}`}>
|
145 |
+
Connection Error
|
146 |
+
</p>
|
147 |
+
<p className={`text-sm mt-1 ${darkMode ? 'text-red-300' : 'text-red-600'}`}>
|
148 |
+
{error}
|
149 |
+
</p>
|
150 |
+
<button
|
151 |
+
onClick={handleRetry}
|
152 |
+
className={`mt-3 px-3 py-1 rounded-md text-sm ${
|
153 |
+
darkMode
|
154 |
+
? 'bg-red-900/30 hover:bg-red-900/50 text-red-300'
|
155 |
+
: 'bg-red-100 hover:bg-red-200 text-red-700'
|
156 |
+
}`}
|
157 |
+
>
|
158 |
+
Try Again
|
159 |
+
</button>
|
160 |
+
</div>
|
161 |
+
</div>
|
162 |
+
</div>
|
163 |
+
);
|
164 |
+
}
|
165 |
+
|
166 |
+
return (
|
167 |
+
<div className="space-y-2">
|
168 |
+
<h3 className="text-sm font-semibold mb-3">Uploaded Files</h3>
|
169 |
+
{files.length === 0 ? (
|
170 |
+
<p className={`text-sm ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
171 |
+
No files uploaded yet.
|
172 |
+
</p>
|
173 |
+
) : (
|
174 |
+
<div className="space-y-2">
|
175 |
+
{files.map((file) => (
|
176 |
+
<div
|
177 |
+
key={file.id}
|
178 |
+
className={`p-3 rounded-md ${
|
179 |
+
darkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-100 hover:bg-gray-200'
|
180 |
+
} transition-colors flex items-center justify-between`}
|
181 |
+
>
|
182 |
+
<div className="flex items-center space-x-2">
|
183 |
+
<FileText size={16} className="text-blue-500" />
|
184 |
+
<div>
|
185 |
+
<p className="text-sm font-medium truncate max-w-[150px]">
|
186 |
+
{file.filename}
|
187 |
+
</p>
|
188 |
+
<p className={`text-xs ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
189 |
+
{new Date(file.uploaded_at).toLocaleDateString()}
|
190 |
+
</p>
|
191 |
+
</div>
|
192 |
+
</div>
|
193 |
+
<div className="flex items-center space-x-2">
|
194 |
+
<button
|
195 |
+
onClick={() => handleDownload(file)}
|
196 |
+
disabled={downloading === file.id}
|
197 |
+
className={`p-1 rounded-md transition-colors ${
|
198 |
+
darkMode
|
199 |
+
? 'hover:bg-gray-500 text-gray-300'
|
200 |
+
: 'hover:bg-gray-300 text-gray-700'
|
201 |
+
}`}
|
202 |
+
>
|
203 |
+
{downloading === file.id ? (
|
204 |
+
<Loader className="animate-spin" size={16} />
|
205 |
+
) : (
|
206 |
+
<Download size={16} />
|
207 |
+
)}
|
208 |
+
</button>
|
209 |
+
{isAdmin && (
|
210 |
+
<button
|
211 |
+
onClick={() => handleDelete(file)}
|
212 |
+
disabled={deleting === file.id}
|
213 |
+
className={`p-1 rounded-md transition-colors ${
|
214 |
+
darkMode
|
215 |
+
? 'hover:bg-red-500 text-gray-300'
|
216 |
+
: 'hover:bg-red-100 text-red-600'
|
217 |
+
}`}
|
218 |
+
>
|
219 |
+
{deleting === file.id ? (
|
220 |
+
<Loader className="animate-spin" size={16} />
|
221 |
+
) : (
|
222 |
+
<Trash2 size={16} />
|
223 |
+
)}
|
224 |
+
</button>
|
225 |
+
)}
|
226 |
+
</div>
|
227 |
+
</div>
|
228 |
+
))}
|
229 |
+
</div>
|
230 |
+
)}
|
231 |
+
</div>
|
232 |
+
);
|
233 |
+
};
|
234 |
+
|
235 |
+
export default FileList;
|
project/src/components/Header.tsx
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import { Menu, Moon, Sun, ChevronRight, Brain, Zap } from 'lucide-react';
|
3 |
+
|
4 |
+
interface HeaderProps {
|
5 |
+
toggleSidebar: () => void;
|
6 |
+
toggleDarkMode: () => void;
|
7 |
+
darkMode: boolean;
|
8 |
+
aiState: {
|
9 |
+
quantumState: number[];
|
10 |
+
chaosState: number[];
|
11 |
+
activePerspectives: string[];
|
12 |
+
ethicalScore: number;
|
13 |
+
processingPower: number;
|
14 |
+
};
|
15 |
+
}
|
16 |
+
|
17 |
+
const Header: React.FC<HeaderProps> = ({
|
18 |
+
toggleSidebar,
|
19 |
+
toggleDarkMode,
|
20 |
+
darkMode,
|
21 |
+
aiState
|
22 |
+
}) => {
|
23 |
+
return (
|
24 |
+
<header className={`h-16 flex items-center justify-between px-4 border-b transition-colors duration-300 ${darkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'}`}>
|
25 |
+
<div className="flex items-center">
|
26 |
+
<button
|
27 |
+
onClick={toggleSidebar}
|
28 |
+
className={`p-2 rounded-md ${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}`}
|
29 |
+
>
|
30 |
+
<Menu size={20} />
|
31 |
+
</button>
|
32 |
+
|
33 |
+
<div className="ml-4 flex items-center">
|
34 |
+
<Brain className="text-purple-600" size={24} />
|
35 |
+
<h1 className="ml-2 text-xl font-bold">Codette AI</h1>
|
36 |
+
<span className={`ml-2 px-2 py-0.5 text-xs rounded-full ${darkMode ? 'bg-purple-900 text-purple-200' : 'bg-purple-100 text-purple-800'}`}>
|
37 |
+
v2.0
|
38 |
+
</span>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
|
42 |
+
<div className="flex items-center space-x-4">
|
43 |
+
<div className={`hidden md:flex items-center py-1 px-3 rounded-full text-sm ${
|
44 |
+
darkMode ? 'bg-blue-900 text-blue-200' : 'bg-blue-100 text-blue-800'
|
45 |
+
}`}>
|
46 |
+
<Zap size={14} className="mr-1" />
|
47 |
+
<span className="font-medium">Quantum State:</span>
|
48 |
+
<span className="ml-1 font-mono">
|
49 |
+
[{aiState.quantumState.map(v => v.toFixed(1)).join(', ')}]
|
50 |
+
</span>
|
51 |
+
</div>
|
52 |
+
|
53 |
+
<button
|
54 |
+
onClick={toggleDarkMode}
|
55 |
+
className={`p-2 rounded-md ${darkMode ? 'hover:bg-gray-700' : 'hover:bg-gray-100'}`}
|
56 |
+
>
|
57 |
+
{darkMode ? <Sun size={20} /> : <Moon size={20} />}
|
58 |
+
</button>
|
59 |
+
</div>
|
60 |
+
</header>
|
61 |
+
);
|
62 |
+
};
|
63 |
+
|
64 |
+
export default Header;
|
project/src/components/Sidebar.tsx
ADDED
@@ -0,0 +1,496 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from 'react';
|
2 |
+
import { Brain, Settings, Circle, Sparkles, Zap, FileText, ChevronDown, ChevronRight, Upload, AlertCircle } from 'lucide-react';
|
3 |
+
import FileList from './FileList';
|
4 |
+
import AdminLogin from './AdminLogin';
|
5 |
+
|
6 |
+
interface SidebarProps {
|
7 |
+
isOpen: boolean;
|
8 |
+
cocoons: Array<{
|
9 |
+
id: string;
|
10 |
+
type: string;
|
11 |
+
wrapped: any;
|
12 |
+
}>;
|
13 |
+
aiState: {
|
14 |
+
quantumState: number[];
|
15 |
+
chaosState: number[];
|
16 |
+
activePerspectives: string[];
|
17 |
+
ethicalScore: number;
|
18 |
+
processingPower: number;
|
19 |
+
};
|
20 |
+
darkMode: boolean;
|
21 |
+
supabase: any;
|
22 |
+
isAdmin: boolean;
|
23 |
+
setIsAdmin: (isAdmin: boolean) => void;
|
24 |
+
}
|
25 |
+
|
26 |
+
const Sidebar: React.FC<SidebarProps> = ({
|
27 |
+
isOpen,
|
28 |
+
cocoons,
|
29 |
+
aiState,
|
30 |
+
darkMode,
|
31 |
+
supabase,
|
32 |
+
isAdmin,
|
33 |
+
setIsAdmin
|
34 |
+
}) => {
|
35 |
+
const [activeSection, setActiveSection] = useState<string>('cocoons');
|
36 |
+
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
37 |
+
const [uploadError, setUploadError] = useState<string | null>(null);
|
38 |
+
const [isUploading, setIsUploading] = useState(false);
|
39 |
+
const [showAdminPrompt, setShowAdminPrompt] = useState(false);
|
40 |
+
const [authError, setAuthError] = useState<string | null>(null);
|
41 |
+
|
42 |
+
if (!isOpen) return null;
|
43 |
+
|
44 |
+
const handleAdminLogin = async (password: string) => {
|
45 |
+
try {
|
46 |
+
setAuthError(null);
|
47 |
+
|
48 |
+
const { data: { user, session }, error } = await supabase.auth.signInWithPassword({
|
49 |
+
email: '[email protected]',
|
50 |
+
password: password
|
51 |
+
});
|
52 |
+
|
53 |
+
if (error) {
|
54 |
+
setAuthError(error.message);
|
55 |
+
throw error;
|
56 |
+
}
|
57 |
+
|
58 |
+
if (!session) {
|
59 |
+
throw new Error('No session after login');
|
60 |
+
}
|
61 |
+
|
62 |
+
// Verify admin role
|
63 |
+
const { data: { role }, error: roleError } = await supabase.rpc('get_user_role');
|
64 |
+
|
65 |
+
if (roleError) {
|
66 |
+
throw roleError;
|
67 |
+
}
|
68 |
+
|
69 |
+
if (role === 'admin') {
|
70 |
+
setIsAdmin(true);
|
71 |
+
setShowAdminPrompt(false);
|
72 |
+
setAuthError(null);
|
73 |
+
} else {
|
74 |
+
throw new Error('Insufficient permissions');
|
75 |
+
}
|
76 |
+
} catch (error: any) {
|
77 |
+
console.error('Login error:', error);
|
78 |
+
setAuthError(error.message || 'Invalid login credentials');
|
79 |
+
throw error;
|
80 |
+
}
|
81 |
+
};
|
82 |
+
|
83 |
+
const handleFileUpload = async () => {
|
84 |
+
if (!selectedFile) return;
|
85 |
+
|
86 |
+
if (!isAdmin) {
|
87 |
+
setUploadError('Only administrators can upload files.');
|
88 |
+
return;
|
89 |
+
}
|
90 |
+
|
91 |
+
try {
|
92 |
+
setIsUploading(true);
|
93 |
+
setUploadError(null);
|
94 |
+
|
95 |
+
// Get user role from session
|
96 |
+
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
97 |
+
|
98 |
+
if (userError) throw userError;
|
99 |
+
|
100 |
+
if (!user || user.role !== 'admin') {
|
101 |
+
throw new Error('Only administrators can upload files.');
|
102 |
+
}
|
103 |
+
|
104 |
+
// Upload file to Supabase storage
|
105 |
+
const { data, error } = await supabase.storage
|
106 |
+
.from('codette-files')
|
107 |
+
.upload(`${Date.now()}-${selectedFile.name}`, selectedFile, {
|
108 |
+
upsert: false
|
109 |
+
});
|
110 |
+
|
111 |
+
if (error) throw error;
|
112 |
+
|
113 |
+
// Add file reference to database
|
114 |
+
const { error: dbError } = await supabase
|
115 |
+
.from('codette_files')
|
116 |
+
.insert([
|
117 |
+
{
|
118 |
+
filename: selectedFile.name,
|
119 |
+
storage_path: data.path,
|
120 |
+
file_type: selectedFile.type,
|
121 |
+
uploaded_at: new Date().toISOString()
|
122 |
+
}
|
123 |
+
]);
|
124 |
+
|
125 |
+
if (dbError) throw dbError;
|
126 |
+
|
127 |
+
setSelectedFile(null);
|
128 |
+
setUploadError(null);
|
129 |
+
} catch (error: any) {
|
130 |
+
console.error('Error uploading file:', error);
|
131 |
+
setUploadError(error.message || 'Failed to upload file. Please try again.');
|
132 |
+
} finally {
|
133 |
+
setIsUploading(false);
|
134 |
+
}
|
135 |
+
};
|
136 |
+
|
137 |
+
const handleSettingsClick = () => {
|
138 |
+
if (!isAdmin) {
|
139 |
+
setShowAdminPrompt(true);
|
140 |
+
}
|
141 |
+
setActiveSection('settings');
|
142 |
+
};
|
143 |
+
|
144 |
+
return (
|
145 |
+
<aside className={`w-64 flex-shrink-0 border-r transition-colors duration-300 flex flex-col ${
|
146 |
+
darkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'
|
147 |
+
}`}>
|
148 |
+
<nav className="flex-1 overflow-y-auto">
|
149 |
+
<div className="p-4">
|
150 |
+
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
|
151 |
+
Navigation
|
152 |
+
</h2>
|
153 |
+
|
154 |
+
<ul className="space-y-1">
|
155 |
+
<li>
|
156 |
+
<button
|
157 |
+
onClick={() => setActiveSection('cocoons')}
|
158 |
+
className={`w-full flex items-center px-3 py-2 rounded-md ${
|
159 |
+
activeSection === 'cocoons'
|
160 |
+
? darkMode
|
161 |
+
? 'bg-gray-700 text-white'
|
162 |
+
: 'bg-gray-200 text-gray-900'
|
163 |
+
: darkMode
|
164 |
+
? 'text-gray-300 hover:bg-gray-700'
|
165 |
+
: 'text-gray-700 hover:bg-gray-100'
|
166 |
+
}`}
|
167 |
+
>
|
168 |
+
<Brain size={18} className="mr-2" />
|
169 |
+
<span>Thought Cocoons</span>
|
170 |
+
</button>
|
171 |
+
</li>
|
172 |
+
|
173 |
+
<li>
|
174 |
+
<button
|
175 |
+
onClick={() => setActiveSection('perspectives')}
|
176 |
+
className={`w-full flex items-center px-3 py-2 rounded-md ${
|
177 |
+
activeSection === 'perspectives'
|
178 |
+
? darkMode
|
179 |
+
? 'bg-gray-700 text-white'
|
180 |
+
: 'bg-gray-200 text-gray-900'
|
181 |
+
: darkMode
|
182 |
+
? 'text-gray-300 hover:bg-gray-700'
|
183 |
+
: 'text-gray-700 hover:bg-gray-100'
|
184 |
+
}`}
|
185 |
+
>
|
186 |
+
<Sparkles size={18} className="mr-2" />
|
187 |
+
<span>Perspectives</span>
|
188 |
+
</button>
|
189 |
+
</li>
|
190 |
+
|
191 |
+
<li>
|
192 |
+
<button
|
193 |
+
onClick={handleSettingsClick}
|
194 |
+
className={`w-full flex items-center px-3 py-2 rounded-md ${
|
195 |
+
activeSection === 'settings'
|
196 |
+
? darkMode
|
197 |
+
? 'bg-gray-700 text-white'
|
198 |
+
: 'bg-gray-200 text-gray-900'
|
199 |
+
: darkMode
|
200 |
+
? 'text-gray-300 hover:bg-gray-700'
|
201 |
+
: 'text-gray-700 hover:bg-gray-100'
|
202 |
+
}`}
|
203 |
+
>
|
204 |
+
<Settings size={18} className="mr-2" />
|
205 |
+
<span>Settings</span>
|
206 |
+
</button>
|
207 |
+
</li>
|
208 |
+
</ul>
|
209 |
+
</div>
|
210 |
+
|
211 |
+
<div className="px-4 py-2">
|
212 |
+
<div className={`h-px ${darkMode ? 'bg-gray-700' : 'bg-gray-200'}`}></div>
|
213 |
+
</div>
|
214 |
+
|
215 |
+
{activeSection === 'cocoons' && (
|
216 |
+
<div className="p-4">
|
217 |
+
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
|
218 |
+
Recent Thought Cocoons
|
219 |
+
</h2>
|
220 |
+
|
221 |
+
{cocoons.length === 0 ? (
|
222 |
+
<div className={`p-3 rounded-md ${
|
223 |
+
darkMode ? 'bg-gray-700 text-gray-300' : 'bg-gray-100 text-gray-500'
|
224 |
+
}`}>
|
225 |
+
<p className="text-sm">No thought cocoons yet.</p>
|
226 |
+
<p className="text-xs mt-1 italic">Interact with Codette to generate thought patterns.</p>
|
227 |
+
</div>
|
228 |
+
) : (
|
229 |
+
<ul className="space-y-2">
|
230 |
+
{cocoons.map((cocoon) => (
|
231 |
+
<li key={cocoon.id}>
|
232 |
+
<div className={`p-3 rounded-md ${
|
233 |
+
darkMode ? 'bg-gray-700 hover:bg-gray-600' : 'bg-gray-100 hover:bg-gray-200'
|
234 |
+
} cursor-pointer transition-colors`}>
|
235 |
+
<div className="flex items-start">
|
236 |
+
<FileText size={16} className={`mr-2 mt-0.5 ${
|
237 |
+
cocoon.type === 'prompt'
|
238 |
+
? 'text-blue-500'
|
239 |
+
: cocoon.type === 'encrypted'
|
240 |
+
? 'text-purple-500'
|
241 |
+
: 'text-green-500'
|
242 |
+
}`} />
|
243 |
+
<div>
|
244 |
+
<div className="text-sm font-medium">
|
245 |
+
{cocoon.type === 'prompt' ? 'User Query' :
|
246 |
+
cocoon.type === 'encrypted' ? 'Encrypted Thought' :
|
247 |
+
'Symbolic Pattern'}
|
248 |
+
</div>
|
249 |
+
<div className="text-xs truncate mt-1 max-w-[180px]">
|
250 |
+
{cocoon.type === 'encrypted'
|
251 |
+
? '🔒 Encrypted content'
|
252 |
+
: typeof cocoon.wrapped === 'object' && cocoon.wrapped.query
|
253 |
+
? cocoon.wrapped.query
|
254 |
+
: `Cocoon ID: ${cocoon.id}`}
|
255 |
+
</div>
|
256 |
+
<div className={`text-xs mt-1 ${
|
257 |
+
darkMode ? 'text-gray-400' : 'text-gray-500'
|
258 |
+
}`}>
|
259 |
+
{typeof cocoon.wrapped === 'object' && cocoon.wrapped.timestamp
|
260 |
+
? new Date(cocoon.wrapped.timestamp).toLocaleTimeString()
|
261 |
+
: 'Unknown time'}
|
262 |
+
</div>
|
263 |
+
</div>
|
264 |
+
</div>
|
265 |
+
</div>
|
266 |
+
</li>
|
267 |
+
))}
|
268 |
+
</ul>
|
269 |
+
)}
|
270 |
+
|
271 |
+
{/* File List Component */}
|
272 |
+
<div className="mt-6">
|
273 |
+
<FileList supabase={supabase} darkMode={darkMode} isAdmin={isAdmin} />
|
274 |
+
</div>
|
275 |
+
</div>
|
276 |
+
)}
|
277 |
+
|
278 |
+
{activeSection === 'perspectives' && (
|
279 |
+
<div className="p-4">
|
280 |
+
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-2">
|
281 |
+
Active Perspectives
|
282 |
+
</h2>
|
283 |
+
|
284 |
+
<ul className="space-y-2">
|
285 |
+
{aiState.activePerspectives.map((perspective) => (
|
286 |
+
<li key={perspective}>
|
287 |
+
<div className={`p-3 rounded-md ${
|
288 |
+
darkMode ? 'bg-gray-700' : 'bg-gray-100'
|
289 |
+
}`}>
|
290 |
+
<div className="flex items-center">
|
291 |
+
<Zap size={16} className="mr-2 text-yellow-500" />
|
292 |
+
<div className="text-sm font-medium capitalize">
|
293 |
+
{perspective.replace('_', ' ')}
|
294 |
+
</div>
|
295 |
+
</div>
|
296 |
+
<div className={`text-xs mt-2 ${
|
297 |
+
darkMode ? 'text-gray-400' : 'text-gray-500'
|
298 |
+
}`}>
|
299 |
+
Confidence: {(Math.random() * 0.4 + 0.6).toFixed(2)}
|
300 |
+
</div>
|
301 |
+
</div>
|
302 |
+
</li>
|
303 |
+
))}
|
304 |
+
</ul>
|
305 |
+
|
306 |
+
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mt-6 mb-2">
|
307 |
+
Available Perspectives
|
308 |
+
</h2>
|
309 |
+
|
310 |
+
<ul className="space-y-2">
|
311 |
+
{['creative', 'bias_mitigation', 'quantum_computing', 'resilient_kindness'].map((perspective) => (
|
312 |
+
<li key={perspective}>
|
313 |
+
<div className={`p-3 rounded-md ${
|
314 |
+
darkMode ? 'bg-gray-700 bg-opacity-50' : 'bg-gray-100 bg-opacity-50'
|
315 |
+
}`}>
|
316 |
+
<div className="flex items-center">
|
317 |
+
<Circle size={16} className={`mr-2 ${
|
318 |
+
darkMode ? 'text-gray-500' : 'text-gray-400'
|
319 |
+
}`} />
|
320 |
+
<div className={`text-sm capitalize ${
|
321 |
+
darkMode ? 'text-gray-400' : 'text-gray-500'
|
322 |
+
}`}>
|
323 |
+
{perspective.replace('_', ' ')}
|
324 |
+
</div>
|
325 |
+
</div>
|
326 |
+
</div>
|
327 |
+
</li>
|
328 |
+
))}
|
329 |
+
</ul>
|
330 |
+
</div>
|
331 |
+
)}
|
332 |
+
|
333 |
+
{activeSection === 'settings' && (
|
334 |
+
<div className="p-4">
|
335 |
+
{showAdminPrompt ? (
|
336 |
+
<AdminLogin
|
337 |
+
onLogin={handleAdminLogin}
|
338 |
+
darkMode={darkMode}
|
339 |
+
error={authError}
|
340 |
+
/>
|
341 |
+
) : (
|
342 |
+
<>
|
343 |
+
<h2 className="text-sm font-semibold uppercase tracking-wider text-gray-500 mb-4">
|
344 |
+
AI Core Settings
|
345 |
+
</h2>
|
346 |
+
|
347 |
+
<div className="space-y-4">
|
348 |
+
{isAdmin && (
|
349 |
+
<div className="p-4 rounded-md bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100">
|
350 |
+
Logged in as Administrator
|
351 |
+
</div>
|
352 |
+
)}
|
353 |
+
|
354 |
+
{isAdmin && (
|
355 |
+
<div className="space-y-2">
|
356 |
+
<label className="block text-sm font-medium">Upload File for Codette</label>
|
357 |
+
<div className="flex items-center space-x-2">
|
358 |
+
<input
|
359 |
+
type="file"
|
360 |
+
onChange={(e) => {
|
361 |
+
setSelectedFile(e.target.files?.[0] || null);
|
362 |
+
setUploadError(null);
|
363 |
+
}}
|
364 |
+
className="hidden"
|
365 |
+
id="file-upload"
|
366 |
+
disabled={isUploading}
|
367 |
+
/>
|
368 |
+
<label
|
369 |
+
htmlFor="file-upload"
|
370 |
+
className={`flex-1 cursor-pointer px-4 py-2 rounded-md border-2 border-dashed ${
|
371 |
+
darkMode
|
372 |
+
? 'border-gray-600 hover:border-gray-500'
|
373 |
+
: 'border-gray-300 hover:border-gray-400'
|
374 |
+
} flex items-center justify-center ${isUploading ? 'opacity-50 cursor-not-allowed' : ''}`}
|
375 |
+
>
|
376 |
+
<Upload size={18} className="mr-2" />
|
377 |
+
{selectedFile ? selectedFile.name : 'Choose File'}
|
378 |
+
</label>
|
379 |
+
{selectedFile && (
|
380 |
+
<button
|
381 |
+
onClick={handleFileUpload}
|
382 |
+
disabled={isUploading}
|
383 |
+
className={`px-4 py-2 rounded-md ${
|
384 |
+
darkMode
|
385 |
+
? 'bg-purple-600 hover:bg-purple-700'
|
386 |
+
: 'bg-purple-500 hover:bg-purple-600'
|
387 |
+
} text-white ${isUploading ? 'opacity-50 cursor-not-allowed' : ''}`}
|
388 |
+
>
|
389 |
+
{isUploading ? 'Uploading...' : 'Upload'}
|
390 |
+
</button>
|
391 |
+
)}
|
392 |
+
</div>
|
393 |
+
{uploadError && (
|
394 |
+
<div className="mt-2 p-2 rounded-md bg-red-100 dark:bg-red-900 flex items-start space-x-2">
|
395 |
+
<AlertCircle className="flex-shrink-0 text-red-500 dark:text-red-400" size={16} />
|
396 |
+
<p className="text-sm text-red-600 dark:text-red-300">
|
397 |
+
{uploadError}
|
398 |
+
</p>
|
399 |
+
</div>
|
400 |
+
)}
|
401 |
+
</div>
|
402 |
+
)}
|
403 |
+
|
404 |
+
<div>
|
405 |
+
<label className="flex items-center justify-between">
|
406 |
+
<span className="text-sm font-medium">Recursive Depth</span>
|
407 |
+
<select
|
408 |
+
className={`text-sm rounded-md ${
|
409 |
+
darkMode
|
410 |
+
? 'bg-gray-700 border-gray-600 text-white'
|
411 |
+
: 'bg-white border-gray-300 text-gray-900'
|
412 |
+
}`}
|
413 |
+
>
|
414 |
+
<option>Normal</option>
|
415 |
+
<option>Deep</option>
|
416 |
+
<option>Shallow</option>
|
417 |
+
</select>
|
418 |
+
</label>
|
419 |
+
</div>
|
420 |
+
|
421 |
+
<div>
|
422 |
+
<label className="flex items-center justify-between">
|
423 |
+
<span className="text-sm font-medium">Ethical Filter</span>
|
424 |
+
<div className="relative inline-block w-10 align-middle select-none">
|
425 |
+
<input
|
426 |
+
type="checkbox"
|
427 |
+
id="ethical-toggle"
|
428 |
+
className="sr-only"
|
429 |
+
defaultChecked={true}
|
430 |
+
/>
|
431 |
+
<label
|
432 |
+
htmlFor="ethical-toggle"
|
433 |
+
className={`block h-6 overflow-hidden rounded-full cursor-pointer ${
|
434 |
+
darkMode ? 'bg-gray-700' : 'bg-gray-300'
|
435 |
+
}`}
|
436 |
+
>
|
437 |
+
<span
|
438 |
+
className={`absolute block w-4 h-4 rounded-full transform transition-transform duration-200 ease-in-out bg-white left-1 top-1 translate-x-4`}
|
439 |
+
></span>
|
440 |
+
</label>
|
441 |
+
</div>
|
442 |
+
</label>
|
443 |
+
</div>
|
444 |
+
|
445 |
+
<div className="pt-2 pb-1">
|
446 |
+
<label className="block text-sm font-medium mb-2">Processing Speed</label>
|
447 |
+
<input
|
448 |
+
type="range"
|
449 |
+
min="0"
|
450 |
+
max="100"
|
451 |
+
defaultValue="75"
|
452 |
+
className="w-full"
|
453 |
+
/>
|
454 |
+
<div className="flex justify-between text-xs text-gray-500 mt-1">
|
455 |
+
<span>Accurate</span>
|
456 |
+
<span>Balanced</span>
|
457 |
+
<span>Fast</span>
|
458 |
+
</div>
|
459 |
+
</div>
|
460 |
+
|
461 |
+
<div className="pt-4">
|
462 |
+
<button className={`w-full py-2 px-4 rounded-md ${
|
463 |
+
darkMode
|
464 |
+
? 'bg-blue-600 hover:bg-blue-700 text-white'
|
465 |
+
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
466 |
+
}`}>
|
467 |
+
Apply Settings
|
468 |
+
</button>
|
469 |
+
</div>
|
470 |
+
</div>
|
471 |
+
</>
|
472 |
+
)}
|
473 |
+
</div>
|
474 |
+
)}
|
475 |
+
</nav>
|
476 |
+
|
477 |
+
<div className={`p-4 border-t ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}>
|
478 |
+
<div className={`rounded-md p-3 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
479 |
+
<div className="flex items-center">
|
480 |
+
<div className={`w-2 h-2 rounded-full ${
|
481 |
+
Math.random() > 0.5
|
482 |
+
? 'bg-green-500'
|
483 |
+
: 'bg-yellow-500'
|
484 |
+
} mr-2`}></div>
|
485 |
+
<div className="text-sm font-medium">System Status</div>
|
486 |
+
</div>
|
487 |
+
<div className={`text-xs mt-1 ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
488 |
+
All neural paths functioning
|
489 |
+
</div>
|
490 |
+
</div>
|
491 |
+
</div>
|
492 |
+
</aside>
|
493 |
+
);
|
494 |
+
};
|
495 |
+
|
496 |
+
export default Sidebar;
|
project/src/components/VisualizationPanel.tsx
ADDED
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useRef, useEffect } from 'react';
|
2 |
+
import { Brain, Zap, Sparkles } from 'lucide-react';
|
3 |
+
|
4 |
+
interface VisualizationPanelProps {
|
5 |
+
aiState: {
|
6 |
+
quantumState: number[];
|
7 |
+
chaosState: number[];
|
8 |
+
activePerspectives: string[];
|
9 |
+
ethicalScore: number;
|
10 |
+
processingPower: number;
|
11 |
+
};
|
12 |
+
darkMode: boolean;
|
13 |
+
}
|
14 |
+
|
15 |
+
const VisualizationPanel: React.FC<VisualizationPanelProps> = ({
|
16 |
+
aiState,
|
17 |
+
darkMode
|
18 |
+
}) => {
|
19 |
+
const quantumCanvasRef = useRef<HTMLCanvasElement>(null);
|
20 |
+
const neuralCanvasRef = useRef<HTMLCanvasElement>(null);
|
21 |
+
|
22 |
+
// Draw quantum state visualization
|
23 |
+
useEffect(() => {
|
24 |
+
const canvas = quantumCanvasRef.current;
|
25 |
+
if (!canvas) return;
|
26 |
+
|
27 |
+
const ctx = canvas.getContext('2d');
|
28 |
+
if (!ctx) return;
|
29 |
+
|
30 |
+
// Clear canvas
|
31 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
32 |
+
|
33 |
+
// Draw quantum state as a particle system
|
34 |
+
const centerX = canvas.width / 2;
|
35 |
+
const centerY = canvas.height / 2;
|
36 |
+
const radius = Math.min(centerX, centerY) * 0.8;
|
37 |
+
|
38 |
+
// Background circle
|
39 |
+
ctx.beginPath();
|
40 |
+
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
41 |
+
ctx.fillStyle = darkMode ? 'rgba(30, 58, 138, 0.2)' : 'rgba(219, 234, 254, 0.5)';
|
42 |
+
ctx.fill();
|
43 |
+
|
44 |
+
// Draw quantum particles
|
45 |
+
aiState.quantumState.forEach((state, i) => {
|
46 |
+
const angle = (i / aiState.quantumState.length) * Math.PI * 2;
|
47 |
+
const distance = state * radius;
|
48 |
+
const x = centerX + Math.cos(angle) * distance;
|
49 |
+
const y = centerY + Math.sin(angle) * distance;
|
50 |
+
|
51 |
+
// Particle
|
52 |
+
const gradient = ctx.createRadialGradient(x, y, 0, x, y, 15);
|
53 |
+
gradient.addColorStop(0, darkMode ? 'rgba(147, 51, 234, 0.9)' : 'rgba(147, 51, 234, 0.7)');
|
54 |
+
gradient.addColorStop(1, darkMode ? 'rgba(147, 51, 234, 0)' : 'rgba(147, 51, 234, 0)');
|
55 |
+
|
56 |
+
ctx.beginPath();
|
57 |
+
ctx.arc(x, y, 15, 0, Math.PI * 2);
|
58 |
+
ctx.fillStyle = gradient;
|
59 |
+
ctx.fill();
|
60 |
+
|
61 |
+
// Connection to center
|
62 |
+
ctx.beginPath();
|
63 |
+
ctx.moveTo(centerX, centerY);
|
64 |
+
ctx.lineTo(x, y);
|
65 |
+
ctx.strokeStyle = darkMode ? 'rgba(147, 51, 234, 0.4)' : 'rgba(147, 51, 234, 0.3)';
|
66 |
+
ctx.lineWidth = 2;
|
67 |
+
ctx.stroke();
|
68 |
+
});
|
69 |
+
|
70 |
+
// Draw center node
|
71 |
+
ctx.beginPath();
|
72 |
+
ctx.arc(centerX, centerY, 8, 0, Math.PI * 2);
|
73 |
+
ctx.fillStyle = darkMode ? '#a855f7' : '#8b5cf6';
|
74 |
+
ctx.fill();
|
75 |
+
}, [aiState.quantumState, darkMode]);
|
76 |
+
|
77 |
+
// Draw neural network visualization
|
78 |
+
useEffect(() => {
|
79 |
+
const canvas = neuralCanvasRef.current;
|
80 |
+
if (!canvas) return;
|
81 |
+
|
82 |
+
const ctx = canvas.getContext('2d');
|
83 |
+
if (!ctx) return;
|
84 |
+
|
85 |
+
// Clear canvas
|
86 |
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
87 |
+
|
88 |
+
// Define layers
|
89 |
+
const layers = [3, 5, 5, 2]; // Input, hidden, hidden, output
|
90 |
+
const nodeSize = 6;
|
91 |
+
const layerSpacing = canvas.width / (layers.length + 1);
|
92 |
+
const neuronColor = darkMode ? '#22c55e' : '#10b981';
|
93 |
+
const connectionColor = darkMode ? 'rgba(34, 197, 94, 0.2)' : 'rgba(16, 185, 129, 0.1)';
|
94 |
+
const activeConnectionColor = darkMode ? 'rgba(34, 197, 94, 0.6)' : 'rgba(16, 185, 129, 0.5)';
|
95 |
+
|
96 |
+
// Draw connections and nodes
|
97 |
+
for (let l = 0; l < layers.length - 1; l++) {
|
98 |
+
const currentLayerSize = layers[l];
|
99 |
+
const nextLayerSize = layers[l + 1];
|
100 |
+
const currentX = (l + 1) * layerSpacing;
|
101 |
+
const nextX = (l + 2) * layerSpacing;
|
102 |
+
|
103 |
+
for (let i = 0; i < currentLayerSize; i++) {
|
104 |
+
const currentY = (i + 1) * (canvas.height / (currentLayerSize + 1));
|
105 |
+
|
106 |
+
for (let j = 0; j < nextLayerSize; j++) {
|
107 |
+
const nextY = (j + 1) * (canvas.height / (nextLayerSize + 1));
|
108 |
+
|
109 |
+
// Draw connection
|
110 |
+
ctx.beginPath();
|
111 |
+
ctx.moveTo(currentX, currentY);
|
112 |
+
ctx.lineTo(nextX, nextY);
|
113 |
+
|
114 |
+
// Randomly activate some connections based on chaos state
|
115 |
+
const isActive = Math.random() < aiState.chaosState[l % aiState.chaosState.length];
|
116 |
+
ctx.strokeStyle = isActive ? activeConnectionColor : connectionColor;
|
117 |
+
ctx.lineWidth = isActive ? 1.5 : 0.5;
|
118 |
+
ctx.stroke();
|
119 |
+
}
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
// Draw nodes
|
124 |
+
for (let l = 0; l < layers.length; l++) {
|
125 |
+
const layerSize = layers[l];
|
126 |
+
const x = (l + 1) * layerSpacing;
|
127 |
+
|
128 |
+
for (let i = 0; i < layerSize; i++) {
|
129 |
+
const y = (i + 1) * (canvas.height / (layerSize + 1));
|
130 |
+
|
131 |
+
// Node
|
132 |
+
ctx.beginPath();
|
133 |
+
ctx.arc(x, y, nodeSize, 0, Math.PI * 2);
|
134 |
+
|
135 |
+
// Node color with pulsing effect based on quantum state
|
136 |
+
const stateIndex = (l + i) % aiState.quantumState.length;
|
137 |
+
const pulseFactor = 0.7 + (aiState.quantumState[stateIndex] * 0.3);
|
138 |
+
|
139 |
+
const gradient = ctx.createRadialGradient(x, y, 0, x, y, nodeSize * 1.5);
|
140 |
+
gradient.addColorStop(0, neuronColor);
|
141 |
+
gradient.addColorStop(1, 'rgba(16, 185, 129, 0)');
|
142 |
+
|
143 |
+
ctx.fillStyle = gradient;
|
144 |
+
ctx.fill();
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}, [aiState.chaosState, aiState.quantumState, darkMode]);
|
148 |
+
|
149 |
+
return (
|
150 |
+
<div className={`hidden md:flex md:w-1/3 flex-col overflow-hidden border-l ${
|
151 |
+
darkMode ? 'bg-gray-800 border-gray-700' : 'bg-white border-gray-200'
|
152 |
+
} transition-colors duration-300`}>
|
153 |
+
<div className={`p-4 border-b ${darkMode ? 'border-gray-700' : 'border-gray-200'}`}>
|
154 |
+
<h2 className="text-lg font-semibold flex items-center">
|
155 |
+
<Brain className="mr-2" size={18} />
|
156 |
+
Codette State Visualization
|
157 |
+
</h2>
|
158 |
+
</div>
|
159 |
+
|
160 |
+
<div className="flex-1 overflow-y-auto p-4 space-y-6">
|
161 |
+
<div className={`rounded-lg p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
162 |
+
<h3 className="text-md font-semibold mb-2 flex items-center">
|
163 |
+
<Sparkles className="mr-2" size={16} />
|
164 |
+
Quantum State
|
165 |
+
</h3>
|
166 |
+
<canvas
|
167 |
+
ref={quantumCanvasRef}
|
168 |
+
width={300}
|
169 |
+
height={200}
|
170 |
+
className="w-full rounded-md"
|
171 |
+
/>
|
172 |
+
<div className="grid grid-cols-3 gap-2 mt-3">
|
173 |
+
{aiState.quantumState.map((value, index) => (
|
174 |
+
<div key={`quantum-${index}`} className="text-center">
|
175 |
+
<div className={`text-xs uppercase font-semibold ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
176 |
+
Q{index + 1}
|
177 |
+
</div>
|
178 |
+
<div className="text-lg font-mono">{value.toFixed(2)}</div>
|
179 |
+
</div>
|
180 |
+
))}
|
181 |
+
</div>
|
182 |
+
</div>
|
183 |
+
|
184 |
+
<div className={`rounded-lg p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
185 |
+
<h3 className="text-md font-semibold mb-2 flex items-center">
|
186 |
+
<Brain className="mr-2" size={16} />
|
187 |
+
Neural Activity
|
188 |
+
</h3>
|
189 |
+
<canvas
|
190 |
+
ref={neuralCanvasRef}
|
191 |
+
width={300}
|
192 |
+
height={200}
|
193 |
+
className="w-full rounded-md"
|
194 |
+
/>
|
195 |
+
<div className="grid grid-cols-4 gap-2 mt-3">
|
196 |
+
{aiState.chaosState.map((value, index) => (
|
197 |
+
<div key={`chaos-${index}`} className="text-center">
|
198 |
+
<div className={`text-xs uppercase font-semibold ${darkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
199 |
+
C{index + 1}
|
200 |
+
</div>
|
201 |
+
<div className="text-lg font-mono">{value.toFixed(2)}</div>
|
202 |
+
</div>
|
203 |
+
))}
|
204 |
+
</div>
|
205 |
+
</div>
|
206 |
+
|
207 |
+
<div className={`rounded-lg p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
208 |
+
<h3 className="text-md font-semibold mb-3 flex items-center">
|
209 |
+
<Zap className="mr-2" size={16} />
|
210 |
+
Active Perspectives
|
211 |
+
</h3>
|
212 |
+
<div className="flex flex-wrap gap-2">
|
213 |
+
{aiState.activePerspectives.map((perspective, index) => (
|
214 |
+
<div
|
215 |
+
key={perspective}
|
216 |
+
className={`px-3 py-1 rounded-full text-sm ${
|
217 |
+
darkMode
|
218 |
+
? 'bg-indigo-900 text-indigo-200'
|
219 |
+
: 'bg-indigo-100 text-indigo-800'
|
220 |
+
}`}
|
221 |
+
>
|
222 |
+
{perspective.replace('_', ' ')}
|
223 |
+
</div>
|
224 |
+
))}
|
225 |
+
</div>
|
226 |
+
</div>
|
227 |
+
|
228 |
+
<div className={`rounded-lg p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-100'}`}>
|
229 |
+
<h3 className="text-md font-semibold mb-3">Performance Metrics</h3>
|
230 |
+
|
231 |
+
<div className="space-y-4">
|
232 |
+
<div>
|
233 |
+
<div className="flex justify-between mb-1">
|
234 |
+
<span className="text-sm">Ethical Governance</span>
|
235 |
+
<span className="text-sm font-semibold">{Number(aiState.ethicalScore) * 100}%</span>
|
236 |
+
</div>
|
237 |
+
<div className={`w-full h-2 rounded-full ${darkMode ? 'bg-gray-600' : 'bg-gray-300'}`}>
|
238 |
+
<div
|
239 |
+
className="h-full rounded-full bg-green-500"
|
240 |
+
style={{ width: `${Number(aiState.ethicalScore) * 100}%` }}
|
241 |
+
></div>
|
242 |
+
</div>
|
243 |
+
</div>
|
244 |
+
|
245 |
+
<div>
|
246 |
+
<div className="flex justify-between mb-1">
|
247 |
+
<span className="text-sm">Processing Power</span>
|
248 |
+
<span className="text-sm font-semibold">{Number(aiState.processingPower) * 100}%</span>
|
249 |
+
</div>
|
250 |
+
<div className={`w-full h-2 rounded-full ${darkMode ? 'bg-gray-600' : 'bg-gray-300'}`}>
|
251 |
+
<div
|
252 |
+
className="h-full rounded-full bg-blue-500"
|
253 |
+
style={{ width: `${Number(aiState.processingPower) * 100}%` }}
|
254 |
+
></div>
|
255 |
+
</div>
|
256 |
+
</div>
|
257 |
+
</div>
|
258 |
+
</div>
|
259 |
+
</div>
|
260 |
+
</div>
|
261 |
+
);
|
262 |
+
};
|
263 |
+
|
264 |
+
export default VisualizationPanel;
|
project/src/index.css
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@tailwind base;
|
2 |
+
@tailwind components;
|
3 |
+
@tailwind utilities;
|
4 |
+
|
5 |
+
:root {
|
6 |
+
--primary: #1E3A8A;
|
7 |
+
--secondary: #7E22CE;
|
8 |
+
--accent: #0D9488;
|
9 |
+
--background: #F9FAFB;
|
10 |
+
--foreground: #111827;
|
11 |
+
}
|
12 |
+
|
13 |
+
.dark {
|
14 |
+
--primary: #3B82F6;
|
15 |
+
--secondary: #8B5CF6;
|
16 |
+
--accent: #10B981;
|
17 |
+
--background: #111827;
|
18 |
+
--foreground: #F9FAFB;
|
19 |
+
}
|
20 |
+
|
21 |
+
body {
|
22 |
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
23 |
+
margin: 0;
|
24 |
+
padding: 0;
|
25 |
+
box-sizing: border-box;
|
26 |
+
}
|
27 |
+
|
28 |
+
/* Animation for neural pulses */
|
29 |
+
@keyframes pulse {
|
30 |
+
0% {
|
31 |
+
transform: scale(1);
|
32 |
+
opacity: 1;
|
33 |
+
}
|
34 |
+
50% {
|
35 |
+
transform: scale(1.2);
|
36 |
+
opacity: 0.8;
|
37 |
+
}
|
38 |
+
100% {
|
39 |
+
transform: scale(1);
|
40 |
+
opacity: 1;
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
/* Animation for typing indicators */
|
45 |
+
@keyframes bounce {
|
46 |
+
0%, 100% {
|
47 |
+
transform: translateY(0);
|
48 |
+
}
|
49 |
+
50% {
|
50 |
+
transform: translateY(-4px);
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
.typing-dot {
|
55 |
+
animation: bounce 1s infinite;
|
56 |
+
}
|
57 |
+
|
58 |
+
/* Custom scrollbar */
|
59 |
+
::-webkit-scrollbar {
|
60 |
+
width: 8px;
|
61 |
+
height: 8px;
|
62 |
+
}
|
63 |
+
|
64 |
+
::-webkit-scrollbar-track {
|
65 |
+
background: transparent;
|
66 |
+
}
|
67 |
+
|
68 |
+
::-webkit-scrollbar-thumb {
|
69 |
+
background: rgba(156, 163, 175, 0.5);
|
70 |
+
border-radius: 4px;
|
71 |
+
}
|
72 |
+
|
73 |
+
::-webkit-scrollbar-thumb:hover {
|
74 |
+
background: rgba(156, 163, 175, 0.7);
|
75 |
+
}
|
76 |
+
|
77 |
+
/* Transitions */
|
78 |
+
.transition-all {
|
79 |
+
transition-property: all;
|
80 |
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
81 |
+
transition-duration: 300ms;
|
82 |
+
}
|
83 |
+
|
84 |
+
.transition-opacity {
|
85 |
+
transition-property: opacity;
|
86 |
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
87 |
+
transition-duration: 150ms;
|
88 |
+
}
|
89 |
+
|
90 |
+
.transition-transform {
|
91 |
+
transition-property: transform;
|
92 |
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
93 |
+
transition-duration: 150ms;
|
94 |
+
}
|
project/src/main.tsx
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { StrictMode } from 'react';
|
2 |
+
import { createRoot } from 'react-dom/client';
|
3 |
+
import App from './App.tsx';
|
4 |
+
import './index.css';
|
5 |
+
|
6 |
+
createRoot(document.getElementById('root')!).render(
|
7 |
+
<StrictMode>
|
8 |
+
<App />
|
9 |
+
</StrictMode>
|
10 |
+
);
|
project/src/services/AICore.ts
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
2 |
+
import KaggleService from './KaggleService';
|
3 |
+
import CognitionCocooner from './CognitionCocooner';
|
4 |
+
import { QuantumSpiderweb } from './QuantumSpiderweb';
|
5 |
+
|
6 |
+
interface CodetteResponse {
|
7 |
+
text: string;
|
8 |
+
instabilityFlag: boolean;
|
9 |
+
perspectivesUsed: string[];
|
10 |
+
cocoonLog: string[];
|
11 |
+
forceRefresh: () => void;
|
12 |
+
}
|
13 |
+
|
14 |
+
class AICore {
|
15 |
+
private perspectives: string[];
|
16 |
+
private ethicalGovernance: boolean;
|
17 |
+
private recursionDepth: number;
|
18 |
+
private supabase: SupabaseClient;
|
19 |
+
private kaggle: KaggleService;
|
20 |
+
private cocooner: CognitionCocooner;
|
21 |
+
private spiderweb: QuantumSpiderweb;
|
22 |
+
private lastResponse: string | null = null;
|
23 |
+
private responseVariations: string[] = [];
|
24 |
+
private userId: string | null = null;
|
25 |
+
|
26 |
+
constructor() {
|
27 |
+
this.perspectives = ['newton', 'davinci', 'human_intuition', 'neural_network', 'quantum_computing', 'philosophical'];
|
28 |
+
this.ethicalGovernance = true;
|
29 |
+
this.recursionDepth = 3;
|
30 |
+
this.kaggle = new KaggleService();
|
31 |
+
this.cocooner = new CognitionCocooner();
|
32 |
+
this.spiderweb = new QuantumSpiderweb({ node_count: 5 });
|
33 |
+
|
34 |
+
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
35 |
+
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
36 |
+
|
37 |
+
if (!supabaseUrl || !supabaseKey) {
|
38 |
+
throw new Error('Supabase configuration is missing. Please check your environment variables.');
|
39 |
+
}
|
40 |
+
|
41 |
+
this.supabase = createClient(supabaseUrl, supabaseKey);
|
42 |
+
}
|
43 |
+
|
44 |
+
async setUser(userId: string) {
|
45 |
+
this.userId = userId;
|
46 |
+
this.cocooner.setUserId(userId);
|
47 |
+
await this.loadUserFingerprint();
|
48 |
+
}
|
49 |
+
|
50 |
+
private async loadUserFingerprint() {
|
51 |
+
if (!this.userId) return;
|
52 |
+
|
53 |
+
const fingerprint = await this.cocooner.loadFingerprint();
|
54 |
+
if (fingerprint) {
|
55 |
+
this.perspectives = fingerprint.active_perspectives;
|
56 |
+
this.recursionDepth = fingerprint.recursion_depth;
|
57 |
+
this.ethicalGovernance = fingerprint.ethical_score > 0.7;
|
58 |
+
}
|
59 |
+
}
|
60 |
+
|
61 |
+
async processInput(input: string, forceNewResponse: boolean = false, userId?: string): Promise<string> {
|
62 |
+
try {
|
63 |
+
if (userId && !this.userId) {
|
64 |
+
await this.setUser(userId);
|
65 |
+
}
|
66 |
+
|
67 |
+
await this.loadUserFingerprint();
|
68 |
+
|
69 |
+
// Search Kaggle for relevant datasets and notebooks
|
70 |
+
const [datasets, notebooks] = await Promise.all([
|
71 |
+
this.kaggle.searchDatasets(input),
|
72 |
+
this.kaggle.searchNotebooks(input)
|
73 |
+
]);
|
74 |
+
|
75 |
+
// Generate comprehensive response using multiple perspectives
|
76 |
+
let result = this.generateMultiPerspectiveResponse(input, datasets, notebooks);
|
77 |
+
|
78 |
+
// Apply recursive reasoning if depth > 3
|
79 |
+
if (this.recursionDepth > 3) {
|
80 |
+
result = await this.applyRecursiveReasoning(result, input);
|
81 |
+
}
|
82 |
+
|
83 |
+
// Log interaction if user is authenticated
|
84 |
+
if (this.userId) {
|
85 |
+
try {
|
86 |
+
await this.supabase.from('cocoons').insert([{
|
87 |
+
user_id: this.userId,
|
88 |
+
type: 'interaction',
|
89 |
+
content: {
|
90 |
+
input,
|
91 |
+
response: result,
|
92 |
+
perspectives_used: this.perspectives,
|
93 |
+
recursion_depth: this.recursionDepth
|
94 |
+
},
|
95 |
+
metadata: {
|
96 |
+
timestamp: new Date().toISOString(),
|
97 |
+
datasets_found: datasets.length,
|
98 |
+
notebooks_found: notebooks.length
|
99 |
+
}
|
100 |
+
}]);
|
101 |
+
} catch (error) {
|
102 |
+
console.warn('Failed to log interaction:', error);
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
// Wrap in cognitive cocoon
|
107 |
+
this.cocooner.wrap({ input, result }, 'prompt');
|
108 |
+
|
109 |
+
if (this.recursionDepth > 3) {
|
110 |
+
this.spiderweb.activate({
|
111 |
+
source: 'AICore',
|
112 |
+
depth: this.recursionDepth,
|
113 |
+
trigger: 'deep_reasoning'
|
114 |
+
});
|
115 |
+
}
|
116 |
+
|
117 |
+
this.lastResponse = result;
|
118 |
+
if (forceNewResponse || !this.responseVariations.includes(result)) {
|
119 |
+
this.responseVariations.push(result);
|
120 |
+
}
|
121 |
+
|
122 |
+
return result;
|
123 |
+
} catch (error: any) {
|
124 |
+
console.error('Error processing input:', error);
|
125 |
+
return `I apologize, but I encountered an error while processing your request. Let me try to help you in a different way.
|
126 |
+
|
127 |
+
Based on my analysis capabilities, I can still provide insights about "${input}" using my multi-perspective reasoning system. Would you like me to explore this topic from different analytical angles?`;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
private generateMultiPerspectiveResponse(input: string, datasets: any[], notebooks: any[]): string {
|
132 |
+
let response = `🧠 **Codette's Multi-Perspective Analysis**\n\n`;
|
133 |
+
|
134 |
+
// Newton's Logical Analysis
|
135 |
+
if (this.perspectives.includes('newton')) {
|
136 |
+
response += `🔬 **Newton's Logical Framework:**\nApproaching "${input}" through systematic analysis and empirical reasoning. `;
|
137 |
+
if (datasets.length > 0) {
|
138 |
+
response += `I've identified ${datasets.length} relevant datasets that could provide quantitative insights.\n\n`;
|
139 |
+
} else {
|
140 |
+
response += `This requires structured investigation and methodical examination of underlying principles.\n\n`;
|
141 |
+
}
|
142 |
+
}
|
143 |
+
|
144 |
+
// Da Vinci's Creative Synthesis
|
145 |
+
if (this.perspectives.includes('davinci')) {
|
146 |
+
response += `🎨 **Da Vinci's Creative Synthesis:**\nExamining "${input}" through the lens of interdisciplinary thinking and innovative connections. `;
|
147 |
+
if (notebooks.length > 0) {
|
148 |
+
response += `Found ${notebooks.length} analytical notebooks that demonstrate creative problem-solving approaches.\n\n`;
|
149 |
+
} else {
|
150 |
+
response += `This topic invites exploration of unexpected relationships and novel perspectives.\n\n`;
|
151 |
+
}
|
152 |
+
}
|
153 |
+
|
154 |
+
// Neural Network Processing
|
155 |
+
if (this.perspectives.includes('neural_network')) {
|
156 |
+
response += `🧬 **Neural Network Processing:**\nAnalyzing patterns and correlations in "${input}" through distributed cognitive processing. `;
|
157 |
+
response += `My neural pathways are identifying complex relationships and emergent properties in this domain.\n\n`;
|
158 |
+
}
|
159 |
+
|
160 |
+
// Philosophical Inquiry
|
161 |
+
if (this.perspectives.includes('philosophical')) {
|
162 |
+
response += `🤔 **Philosophical Inquiry:**\nExploring the deeper implications and fundamental questions raised by "${input}". `;
|
163 |
+
response += `What are the ethical considerations and broader societal impacts we should consider?\n\n`;
|
164 |
+
}
|
165 |
+
|
166 |
+
// Quantum Computing Perspective
|
167 |
+
if (this.perspectives.includes('quantum_computing')) {
|
168 |
+
response += `⚛️ **Quantum Computing Perspective:**\nExamining "${input}" through quantum principles of superposition and entanglement. `;
|
169 |
+
response += `Multiple solution states exist simultaneously until observation collapses them into actionable insights.\n\n`;
|
170 |
+
}
|
171 |
+
|
172 |
+
// Add specific insights based on available data
|
173 |
+
if (datasets.length > 0 || notebooks.length > 0) {
|
174 |
+
response += `📊 **Data-Driven Insights:**\n`;
|
175 |
+
|
176 |
+
if (datasets.length > 0) {
|
177 |
+
const topDataset = datasets[0];
|
178 |
+
response += `• **Key Dataset**: "${topDataset.title}" - ${topDataset.description}\n`;
|
179 |
+
}
|
180 |
+
|
181 |
+
if (notebooks.length > 0) {
|
182 |
+
const topNotebook = notebooks[0];
|
183 |
+
response += `• **Analytical Approach**: "${topNotebook.title}" - ${topNotebook.description}\n`;
|
184 |
+
}
|
185 |
+
|
186 |
+
response += `\n`;
|
187 |
+
}
|
188 |
+
|
189 |
+
// Ethical governance check
|
190 |
+
if (this.ethicalGovernance) {
|
191 |
+
response += `⚖️ **Ethical Considerations:**\nAll analysis conducted with respect for privacy, fairness, and responsible AI principles.\n\n`;
|
192 |
+
}
|
193 |
+
|
194 |
+
response += `🔄 **Recursive Depth**: ${this.recursionDepth}/5 - ${this.recursionDepth > 3 ? 'Deep analysis mode engaged' : 'Standard processing'}\n`;
|
195 |
+
response += `🎯 **Confidence Level**: ${(0.7 + Math.random() * 0.25).toFixed(2)}`;
|
196 |
+
|
197 |
+
return response;
|
198 |
+
}
|
199 |
+
|
200 |
+
private async applyRecursiveReasoning(initialResponse: string, input: string): Promise<string> {
|
201 |
+
// Simulate recursive refinement
|
202 |
+
const refinements = [
|
203 |
+
"Upon deeper reflection, I should also consider...",
|
204 |
+
"Cross-referencing with quantum entanglement principles...",
|
205 |
+
"Applying chaos theory to identify emergent patterns...",
|
206 |
+
"Integrating multi-dimensional analysis..."
|
207 |
+
];
|
208 |
+
|
209 |
+
const randomRefinement = refinements[Math.floor(Math.random() * refinements.length)];
|
210 |
+
|
211 |
+
return `${initialResponse}\n\n🔄 **Recursive Refinement:**\n${randomRefinement}\n\nThis additional layer of analysis reveals nuanced aspects of "${input}" that warrant further exploration through continued interaction.`;
|
212 |
+
}
|
213 |
+
|
214 |
+
setPerspectives(perspectives: string[]): void {
|
215 |
+
this.perspectives = perspectives;
|
216 |
+
if (this.userId) {
|
217 |
+
this.cocooner.updateFingerprint({ active_perspectives: perspectives });
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
setEthicalGovernance(enabled: boolean): void {
|
222 |
+
this.ethicalGovernance = enabled;
|
223 |
+
if (this.userId) {
|
224 |
+
this.cocooner.updateFingerprint({ ethical_score: enabled ? 1 : 0.5 });
|
225 |
+
}
|
226 |
+
}
|
227 |
+
|
228 |
+
setRecursionDepth(depth: number): void {
|
229 |
+
if (depth < 1) depth = 1;
|
230 |
+
if (depth > 5) depth = 5;
|
231 |
+
this.recursionDepth = depth;
|
232 |
+
if (this.userId) {
|
233 |
+
this.cocooner.updateFingerprint({ recursion_depth: depth });
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
getCodetteResponse(): CodetteResponse {
|
238 |
+
return {
|
239 |
+
text: this.lastResponse || '',
|
240 |
+
instabilityFlag: this.recursionDepth > 3 || this.responseVariations.length > 5,
|
241 |
+
perspectivesUsed: this.perspectives,
|
242 |
+
cocoonLog: this.cocooner.getRecentCocoons(5),
|
243 |
+
forceRefresh: () => {
|
244 |
+
this.recursionDepth = Math.min(this.recursionDepth + 1, 5);
|
245 |
+
if (this.userId) {
|
246 |
+
this.cocooner.updateFingerprint({ recursion_depth: this.recursionDepth });
|
247 |
+
}
|
248 |
+
}
|
249 |
+
};
|
250 |
+
}
|
251 |
+
}
|
252 |
+
|
253 |
+
export default AICore;
|
project/src/services/CognitionCocooner.ts
ADDED
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { createClient, SupabaseClient } from '@supabase/supabase-js';
|
2 |
+
|
3 |
+
interface CognitiveFingerprint {
|
4 |
+
id: string;
|
5 |
+
user_id: string;
|
6 |
+
active_perspectives: string[];
|
7 |
+
recursion_depth: number;
|
8 |
+
ethical_score: number;
|
9 |
+
processing_power: number;
|
10 |
+
quantum_state: number[];
|
11 |
+
created_at: string;
|
12 |
+
updated_at: string;
|
13 |
+
}
|
14 |
+
|
15 |
+
interface Cocoon {
|
16 |
+
id: string;
|
17 |
+
type: string;
|
18 |
+
wrapped: any;
|
19 |
+
}
|
20 |
+
|
21 |
+
class CognitionCocooner {
|
22 |
+
private supabase: SupabaseClient;
|
23 |
+
private userId: string | null = null;
|
24 |
+
private fingerprint: CognitiveFingerprint | null = null;
|
25 |
+
|
26 |
+
constructor() {
|
27 |
+
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
|
28 |
+
const supabaseKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
|
29 |
+
|
30 |
+
if (!supabaseUrl || !supabaseKey) {
|
31 |
+
throw new Error('Supabase configuration is missing');
|
32 |
+
}
|
33 |
+
|
34 |
+
this.supabase = createClient(supabaseUrl, supabaseKey);
|
35 |
+
}
|
36 |
+
|
37 |
+
setUserId(userId: string) {
|
38 |
+
this.userId = userId;
|
39 |
+
}
|
40 |
+
|
41 |
+
private generateId(): string {
|
42 |
+
return `cocoon_${Math.floor(Math.random() * 90000) + 10000}`;
|
43 |
+
}
|
44 |
+
|
45 |
+
async loadFingerprint(): Promise<CognitiveFingerprint | null> {
|
46 |
+
if (!this.userId) return null;
|
47 |
+
|
48 |
+
try {
|
49 |
+
const { data, error } = await this.supabase
|
50 |
+
.from('user_cognitive_fingerprints')
|
51 |
+
.select('*')
|
52 |
+
.eq('user_id', this.userId)
|
53 |
+
.single();
|
54 |
+
|
55 |
+
if (error) throw error;
|
56 |
+
|
57 |
+
if (!data) {
|
58 |
+
// Create initial fingerprint if none exists
|
59 |
+
const initialFingerprint = {
|
60 |
+
user_id: this.userId,
|
61 |
+
active_perspectives: ['newton', 'davinci', 'neural_network'],
|
62 |
+
recursion_depth: 3,
|
63 |
+
ethical_score: 0.8,
|
64 |
+
processing_power: 0.7,
|
65 |
+
quantum_state: [0.3, 0.7, 0.5]
|
66 |
+
};
|
67 |
+
|
68 |
+
const { data: newData, error: insertError } = await this.supabase
|
69 |
+
.from('user_cognitive_fingerprints')
|
70 |
+
.insert([initialFingerprint])
|
71 |
+
.select()
|
72 |
+
.single();
|
73 |
+
|
74 |
+
if (insertError) throw insertError;
|
75 |
+
this.fingerprint = newData;
|
76 |
+
return newData;
|
77 |
+
}
|
78 |
+
|
79 |
+
this.fingerprint = data;
|
80 |
+
return data;
|
81 |
+
} catch (error) {
|
82 |
+
console.error('Error loading cognitive fingerprint:', error);
|
83 |
+
return null;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
async updateFingerprint(updates: Partial<CognitiveFingerprint>): Promise<void> {
|
88 |
+
if (!this.userId || !this.fingerprint) return;
|
89 |
+
|
90 |
+
try {
|
91 |
+
const { error } = await this.supabase
|
92 |
+
.from('user_cognitive_fingerprints')
|
93 |
+
.update({
|
94 |
+
...updates,
|
95 |
+
updated_at: new Date().toISOString()
|
96 |
+
})
|
97 |
+
.eq('user_id', this.userId);
|
98 |
+
|
99 |
+
if (error) throw error;
|
100 |
+
|
101 |
+
this.fingerprint = {
|
102 |
+
...this.fingerprint,
|
103 |
+
...updates,
|
104 |
+
updated_at: new Date().toISOString()
|
105 |
+
};
|
106 |
+
} catch (error) {
|
107 |
+
console.error('Error updating cognitive fingerprint:', error);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
wrap(thought: any, type: string = 'prompt'): string {
|
112 |
+
const cocoonId = this.generateId();
|
113 |
+
const wrapped = this.applyWrapper(thought, type);
|
114 |
+
|
115 |
+
// Update fingerprint based on thought processing
|
116 |
+
if (this.fingerprint) {
|
117 |
+
const updates: Partial<CognitiveFingerprint> = {
|
118 |
+
ethical_score: Math.min(1, this.fingerprint.ethical_score + 0.01),
|
119 |
+
processing_power: Math.min(1, this.fingerprint.processing_power + 0.005),
|
120 |
+
quantum_state: this.fingerprint.quantum_state.map(v =>
|
121 |
+
Math.min(1, v + (Math.random() * 0.1 - 0.05))
|
122 |
+
)
|
123 |
+
};
|
124 |
+
this.updateFingerprint(updates);
|
125 |
+
}
|
126 |
+
|
127 |
+
return cocoonId;
|
128 |
+
}
|
129 |
+
|
130 |
+
private applyWrapper(thought: any, type: string): any {
|
131 |
+
const perspectiveModifier = this.fingerprint?.active_perspectives.length || 3;
|
132 |
+
const recursionFactor = this.fingerprint?.recursion_depth || 3;
|
133 |
+
|
134 |
+
switch (type) {
|
135 |
+
case 'prompt':
|
136 |
+
return {
|
137 |
+
content: thought,
|
138 |
+
meta: {
|
139 |
+
perspectives: perspectiveModifier,
|
140 |
+
recursion: recursionFactor,
|
141 |
+
timestamp: new Date().toISOString()
|
142 |
+
}
|
143 |
+
};
|
144 |
+
case 'function':
|
145 |
+
return {
|
146 |
+
code: thought,
|
147 |
+
analysis: {
|
148 |
+
complexity: recursionFactor * 0.2,
|
149 |
+
perspectives: perspectiveModifier
|
150 |
+
}
|
151 |
+
};
|
152 |
+
case 'symbolic':
|
153 |
+
return {
|
154 |
+
pattern: thought,
|
155 |
+
quantum: {
|
156 |
+
state: this.fingerprint?.quantum_state || [0.3, 0.7, 0.5],
|
157 |
+
stability: this.fingerprint?.ethical_score || 0.8
|
158 |
+
}
|
159 |
+
};
|
160 |
+
default:
|
161 |
+
return thought;
|
162 |
+
}
|
163 |
+
}
|
164 |
+
|
165 |
+
getRecentCocoons(limit: number = 5): string[] {
|
166 |
+
// Simulated cocoon retrieval
|
167 |
+
return Array(limit).fill(null).map((_, i) => {
|
168 |
+
const timestamp = new Date(Date.now() - i * 60000).toISOString();
|
169 |
+
return `Cocoon processed at ${timestamp}`;
|
170 |
+
});
|
171 |
+
}
|
172 |
+
}
|
173 |
+
|
174 |
+
export default CognitionCocooner;
|
project/src/services/KaggleService.ts
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
class KaggleService {
|
2 |
+
private username: string;
|
3 |
+
private key: string;
|
4 |
+
|
5 |
+
constructor() {
|
6 |
+
this.username = import.meta.env.VITE_KAGGLE_USERNAME || '';
|
7 |
+
this.key = import.meta.env.VITE_KAGGLE_KEY || '';
|
8 |
+
|
9 |
+
if (!this.username || !this.key) {
|
10 |
+
console.warn('Kaggle credentials not found. Some features may be limited.');
|
11 |
+
}
|
12 |
+
}
|
13 |
+
|
14 |
+
async searchDatasets(query: string) {
|
15 |
+
try {
|
16 |
+
// Simulate Kaggle dataset search using mock data
|
17 |
+
// In a real implementation, you would use Kaggle's REST API
|
18 |
+
const mockDatasets = [
|
19 |
+
{
|
20 |
+
title: `Dataset related to: ${query}`,
|
21 |
+
description: `This dataset contains comprehensive data about ${query} with various features and analysis opportunities.`,
|
22 |
+
owner: 'kaggle-user',
|
23 |
+
votes: Math.floor(Math.random() * 1000),
|
24 |
+
downloadCount: Math.floor(Math.random() * 10000)
|
25 |
+
},
|
26 |
+
{
|
27 |
+
title: `Advanced ${query} Analysis`,
|
28 |
+
description: `Deep dive into ${query} with statistical analysis and machine learning applications.`,
|
29 |
+
owner: 'data-scientist',
|
30 |
+
votes: Math.floor(Math.random() * 500),
|
31 |
+
downloadCount: Math.floor(Math.random() * 5000)
|
32 |
+
}
|
33 |
+
];
|
34 |
+
|
35 |
+
return mockDatasets;
|
36 |
+
} catch (error) {
|
37 |
+
console.error('Error searching datasets:', error);
|
38 |
+
return [];
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
async getDatasetInfo(owner: string, dataset: string) {
|
43 |
+
try {
|
44 |
+
// Mock dataset information
|
45 |
+
return {
|
46 |
+
title: dataset,
|
47 |
+
owner: owner,
|
48 |
+
description: `Detailed information about ${dataset} dataset`,
|
49 |
+
files: ['data.csv', 'metadata.json'],
|
50 |
+
size: '10.5 MB',
|
51 |
+
lastUpdated: new Date().toISOString()
|
52 |
+
};
|
53 |
+
} catch (error) {
|
54 |
+
console.error('Error getting dataset info:', error);
|
55 |
+
return null;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
async searchNotebooks(query: string) {
|
60 |
+
try {
|
61 |
+
// Simulate Kaggle notebook search using mock data
|
62 |
+
const mockNotebooks = [
|
63 |
+
{
|
64 |
+
title: `${query} Analysis Notebook`,
|
65 |
+
description: `Comprehensive analysis of ${query} using Python and machine learning techniques.`,
|
66 |
+
owner: 'notebook-author',
|
67 |
+
votes: Math.floor(Math.random() * 200),
|
68 |
+
language: 'Python'
|
69 |
+
},
|
70 |
+
{
|
71 |
+
title: `Exploring ${query} Patterns`,
|
72 |
+
description: `Data visualization and pattern recognition in ${query} datasets.`,
|
73 |
+
owner: 'data-explorer',
|
74 |
+
votes: Math.floor(Math.random() * 150),
|
75 |
+
language: 'R'
|
76 |
+
}
|
77 |
+
];
|
78 |
+
|
79 |
+
return mockNotebooks;
|
80 |
+
} catch (error) {
|
81 |
+
console.error('Error searching notebooks:', error);
|
82 |
+
return [];
|
83 |
+
}
|
84 |
+
}
|
85 |
+
|
86 |
+
async getNotebookInfo(owner: string, notebook: string) {
|
87 |
+
try {
|
88 |
+
// Mock notebook information
|
89 |
+
return {
|
90 |
+
title: notebook,
|
91 |
+
owner: owner,
|
92 |
+
description: `Detailed analysis notebook: ${notebook}`,
|
93 |
+
language: 'Python',
|
94 |
+
lastUpdated: new Date().toISOString(),
|
95 |
+
votes: Math.floor(Math.random() * 100)
|
96 |
+
};
|
97 |
+
} catch (error) {
|
98 |
+
console.error('Error getting notebook info:', error);
|
99 |
+
return null;
|
100 |
+
}
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
export default KaggleService;
|
project/src/services/OpenAIService.ts
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import OpenAI from 'openai';
|
2 |
+
|
3 |
+
interface ChatMessage {
|
4 |
+
role: 'system' | 'user' | 'assistant';
|
5 |
+
content: string;
|
6 |
+
}
|
7 |
+
|
8 |
+
class OpenAIService {
|
9 |
+
private openai: OpenAI;
|
10 |
+
private model: string = 'gpt-4';
|
11 |
+
|
12 |
+
constructor() {
|
13 |
+
const apiKey = import.meta.env.VITE_OPENAI_API_KEY;
|
14 |
+
if (!apiKey) {
|
15 |
+
throw new Error('OpenAI API key is required. Please add your API key to the .env file as VITE_OPENAI_API_KEY.');
|
16 |
+
}
|
17 |
+
|
18 |
+
this.openai = new OpenAI({
|
19 |
+
apiKey,
|
20 |
+
dangerouslyAllowBrowser: true // Note: In production, API calls should be made from a backend
|
21 |
+
});
|
22 |
+
}
|
23 |
+
|
24 |
+
async sendChatCompletion(messages: ChatMessage[]) {
|
25 |
+
try {
|
26 |
+
const completion = await this.openai.chat.completions.create({
|
27 |
+
model: this.model,
|
28 |
+
messages,
|
29 |
+
temperature: 0.7,
|
30 |
+
max_tokens: 1000,
|
31 |
+
frequency_penalty: 0,
|
32 |
+
presence_penalty: 0
|
33 |
+
});
|
34 |
+
|
35 |
+
return completion.choices[0].message;
|
36 |
+
} catch (error) {
|
37 |
+
console.error('Error in chat completion:', error);
|
38 |
+
throw error;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
}
|
42 |
+
|
43 |
+
export default OpenAIService;
|
project/src/services/QuantumSpiderweb.ts
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
interface SpiderwebConfig {
|
2 |
+
node_count: number;
|
3 |
+
}
|
4 |
+
|
5 |
+
export class QuantumSpiderweb {
|
6 |
+
private nodes: number;
|
7 |
+
private state: Map<string, any>;
|
8 |
+
private lastUpdate: number;
|
9 |
+
private entanglementMatrix: number[][];
|
10 |
+
|
11 |
+
constructor(config: SpiderwebConfig) {
|
12 |
+
this.nodes = config.node_count;
|
13 |
+
this.state = new Map();
|
14 |
+
this.lastUpdate = Date.now();
|
15 |
+
this.entanglementMatrix = Array(this.nodes).fill(0).map(() =>
|
16 |
+
Array(this.nodes).fill(0).map(() => Math.random())
|
17 |
+
);
|
18 |
+
}
|
19 |
+
|
20 |
+
activate(data: { source: string; depth: number; trigger: string }) {
|
21 |
+
const currentTime = Date.now();
|
22 |
+
const timeDelta = currentTime - this.lastUpdate;
|
23 |
+
this.lastUpdate = currentTime;
|
24 |
+
|
25 |
+
// Generate quantum states with entanglement effects
|
26 |
+
const nodeStates = Array(this.nodes).fill(0).map((_, i) => {
|
27 |
+
let state = Math.random();
|
28 |
+
// Apply entanglement effects from other nodes
|
29 |
+
for (let j = 0; j < this.nodes; j++) {
|
30 |
+
if (i !== j) {
|
31 |
+
state += this.entanglementMatrix[i][j] * Math.random() * 0.1;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
return Math.min(Math.max(state, 0), 1); // Normalize to [0,1]
|
35 |
+
});
|
36 |
+
|
37 |
+
// Calculate coherence based on time delta
|
38 |
+
const coherence = Math.exp(-timeDelta / 10000); // Decay factor
|
39 |
+
|
40 |
+
const stateKey = `${data.source}_${currentTime}`;
|
41 |
+
this.state.set(stateKey, {
|
42 |
+
...data,
|
43 |
+
timestamp: new Date().toISOString(),
|
44 |
+
nodeStates,
|
45 |
+
coherence,
|
46 |
+
entanglementStrength: this.calculateEntanglementStrength()
|
47 |
+
});
|
48 |
+
|
49 |
+
// Update entanglement matrix
|
50 |
+
this.updateEntanglement();
|
51 |
+
}
|
52 |
+
|
53 |
+
private calculateEntanglementStrength(): number {
|
54 |
+
return this.entanglementMatrix.reduce((sum, row) =>
|
55 |
+
sum + row.reduce((rowSum, val) => rowSum + val, 0), 0
|
56 |
+
) / (this.nodes * this.nodes);
|
57 |
+
}
|
58 |
+
|
59 |
+
private updateEntanglement() {
|
60 |
+
// Gradually evolve entanglement patterns
|
61 |
+
this.entanglementMatrix = this.entanglementMatrix.map(row =>
|
62 |
+
row.map(val => {
|
63 |
+
const delta = (Math.random() - 0.5) * 0.1;
|
64 |
+
return Math.min(Math.max(val + delta, 0), 1);
|
65 |
+
})
|
66 |
+
);
|
67 |
+
}
|
68 |
+
|
69 |
+
getState(): Map<string, any> {
|
70 |
+
return this.state;
|
71 |
+
}
|
72 |
+
|
73 |
+
getLatestState(): any {
|
74 |
+
const states = Array.from(this.state.values());
|
75 |
+
return states[states.length - 1] || null;
|
76 |
+
}
|
77 |
+
|
78 |
+
getEntanglementMatrix(): number[][] {
|
79 |
+
return this.entanglementMatrix;
|
80 |
+
}
|
81 |
+
}
|
project/src/vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
project/supabase/migrations/20250523100814_raspy_torch.sql
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Create codette_files table for file management
|
3 |
+
|
4 |
+
1. New Tables
|
5 |
+
- `codette_files`
|
6 |
+
- `id` (uuid, primary key)
|
7 |
+
- `filename` (text)
|
8 |
+
- `storage_path` (text)
|
9 |
+
- `file_type` (text)
|
10 |
+
- `uploaded_at` (timestamptz)
|
11 |
+
- `created_at` (timestamptz)
|
12 |
+
|
13 |
+
2. Security
|
14 |
+
- Enable RLS on `codette_files` table
|
15 |
+
- Add policies for:
|
16 |
+
- Authenticated users can read all files
|
17 |
+
- Authenticated users can insert their own files
|
18 |
+
*/
|
19 |
+
|
20 |
+
CREATE TABLE IF NOT EXISTS public.codette_files (
|
21 |
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
22 |
+
filename text NOT NULL,
|
23 |
+
storage_path text NOT NULL,
|
24 |
+
file_type text,
|
25 |
+
uploaded_at timestamptz DEFAULT now(),
|
26 |
+
created_at timestamptz DEFAULT now()
|
27 |
+
);
|
28 |
+
|
29 |
+
-- Enable Row Level Security
|
30 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
31 |
+
|
32 |
+
-- Create policies
|
33 |
+
CREATE POLICY "Allow authenticated users to read files"
|
34 |
+
ON public.codette_files
|
35 |
+
FOR SELECT
|
36 |
+
TO authenticated
|
37 |
+
USING (true);
|
38 |
+
|
39 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
40 |
+
ON public.codette_files
|
41 |
+
FOR INSERT
|
42 |
+
TO authenticated
|
43 |
+
WITH CHECK (true);
|
project/supabase/migrations/20250523120906_wild_torch.sql
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Create storage bucket for Codette files
|
3 |
+
|
4 |
+
1. New Storage Bucket
|
5 |
+
- Creates 'codette-files' bucket for storing uploaded files
|
6 |
+
2. Security
|
7 |
+
- Enable public access for authenticated users
|
8 |
+
- Add policies for read and write operations
|
9 |
+
*/
|
10 |
+
|
11 |
+
-- Create the storage bucket
|
12 |
+
INSERT INTO storage.buckets (id, name)
|
13 |
+
VALUES ('codette-files', 'codette-files')
|
14 |
+
ON CONFLICT (id) DO NOTHING;
|
15 |
+
|
16 |
+
-- Set up RLS policies for the bucket
|
17 |
+
CREATE POLICY "Allow authenticated users to read files"
|
18 |
+
ON storage.objects FOR SELECT
|
19 |
+
TO authenticated
|
20 |
+
USING (bucket_id = 'codette-files');
|
21 |
+
|
22 |
+
CREATE POLICY "Allow authenticated users to upload files"
|
23 |
+
ON storage.objects FOR INSERT
|
24 |
+
TO authenticated
|
25 |
+
WITH CHECK (bucket_id = 'codette-files');
|
26 |
+
|
27 |
+
CREATE POLICY "Allow authenticated users to update files"
|
28 |
+
ON storage.objects FOR UPDATE
|
29 |
+
TO authenticated
|
30 |
+
USING (bucket_id = 'codette-files')
|
31 |
+
WITH CHECK (bucket_id = 'codette-files');
|
32 |
+
|
33 |
+
CREATE POLICY "Allow authenticated users to delete files"
|
34 |
+
ON storage.objects FOR DELETE
|
35 |
+
TO authenticated
|
36 |
+
USING (bucket_id = 'codette-files');
|
project/supabase/migrations/20250523121149_rough_jungle.sql
ADDED
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Create storage bucket and policies
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Create codette-files storage bucket if it doesn't exist
|
6 |
+
- Add RLS policies for authenticated users to:
|
7 |
+
- Read files
|
8 |
+
- Upload files
|
9 |
+
- Update files
|
10 |
+
- Delete files
|
11 |
+
- Add safety checks to prevent policy conflicts
|
12 |
+
*/
|
13 |
+
|
14 |
+
-- Create the storage bucket
|
15 |
+
INSERT INTO storage.buckets (id, name)
|
16 |
+
VALUES ('codette-files', 'codette-files')
|
17 |
+
ON CONFLICT (id) DO NOTHING;
|
18 |
+
|
19 |
+
-- Set up RLS policies for the bucket with existence checks
|
20 |
+
DO $$
|
21 |
+
BEGIN
|
22 |
+
IF NOT EXISTS (
|
23 |
+
SELECT 1 FROM pg_policies
|
24 |
+
WHERE tablename = 'objects'
|
25 |
+
AND policyname = 'Allow authenticated users to read files'
|
26 |
+
) THEN
|
27 |
+
CREATE POLICY "Allow authenticated users to read files"
|
28 |
+
ON storage.objects FOR SELECT
|
29 |
+
TO authenticated
|
30 |
+
USING (bucket_id = 'codette-files');
|
31 |
+
END IF;
|
32 |
+
|
33 |
+
IF NOT EXISTS (
|
34 |
+
SELECT 1 FROM pg_policies
|
35 |
+
WHERE tablename = 'objects'
|
36 |
+
AND policyname = 'Allow authenticated users to upload files'
|
37 |
+
) THEN
|
38 |
+
CREATE POLICY "Allow authenticated users to upload files"
|
39 |
+
ON storage.objects FOR INSERT
|
40 |
+
TO authenticated
|
41 |
+
WITH CHECK (bucket_id = 'codette-files');
|
42 |
+
END IF;
|
43 |
+
|
44 |
+
IF NOT EXISTS (
|
45 |
+
SELECT 1 FROM pg_policies
|
46 |
+
WHERE tablename = 'objects'
|
47 |
+
AND policyname = 'Allow authenticated users to update files'
|
48 |
+
) THEN
|
49 |
+
CREATE POLICY "Allow authenticated users to update files"
|
50 |
+
ON storage.objects FOR UPDATE
|
51 |
+
TO authenticated
|
52 |
+
USING (bucket_id = 'codette-files')
|
53 |
+
WITH CHECK (bucket_id = 'codette-files');
|
54 |
+
END IF;
|
55 |
+
|
56 |
+
IF NOT EXISTS (
|
57 |
+
SELECT 1 FROM pg_policies
|
58 |
+
WHERE tablename = 'objects'
|
59 |
+
AND policyname = 'Allow authenticated users to delete files'
|
60 |
+
) THEN
|
61 |
+
CREATE POLICY "Allow authenticated users to delete files"
|
62 |
+
ON storage.objects FOR DELETE
|
63 |
+
TO authenticated
|
64 |
+
USING (bucket_id = 'codette-files');
|
65 |
+
END IF;
|
66 |
+
END $$;
|
project/supabase/migrations/20250523125621_rapid_flower.sql
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Update storage policies with existence checks
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Add existence checks before creating each policy
|
6 |
+
- Only create policies that don't already exist
|
7 |
+
- Maintain all required policies for the storage bucket
|
8 |
+
|
9 |
+
2. Security
|
10 |
+
- Maintain existing RLS policies
|
11 |
+
- Ensure proper access control for authenticated users
|
12 |
+
- Preserve admin-only upload restrictions
|
13 |
+
*/
|
14 |
+
|
15 |
+
-- Wrap everything in a transaction
|
16 |
+
BEGIN;
|
17 |
+
|
18 |
+
-- Create policies with existence checks
|
19 |
+
DO $$
|
20 |
+
BEGIN
|
21 |
+
-- Check and create read policy
|
22 |
+
IF NOT EXISTS (
|
23 |
+
SELECT 1 FROM pg_policies
|
24 |
+
WHERE tablename = 'objects'
|
25 |
+
AND schemaname = 'storage'
|
26 |
+
AND policyname = 'Allow authenticated users to read files'
|
27 |
+
) THEN
|
28 |
+
CREATE POLICY "Allow authenticated users to read files"
|
29 |
+
ON storage.objects FOR SELECT
|
30 |
+
TO authenticated
|
31 |
+
USING (bucket_id = 'codette-files');
|
32 |
+
END IF;
|
33 |
+
|
34 |
+
-- Check and create upload policy for admin users
|
35 |
+
IF NOT EXISTS (
|
36 |
+
SELECT 1 FROM pg_policies
|
37 |
+
WHERE tablename = 'objects'
|
38 |
+
AND schemaname = 'storage'
|
39 |
+
AND policyname = 'Allow admin users to upload files'
|
40 |
+
) THEN
|
41 |
+
CREATE POLICY "Allow admin users to upload files"
|
42 |
+
ON storage.objects FOR INSERT
|
43 |
+
TO authenticated
|
44 |
+
WITH CHECK (bucket_id = 'codette-files' AND auth.jwt() ->> 'role' = 'admin');
|
45 |
+
END IF;
|
46 |
+
|
47 |
+
-- Check and create policy for admin file insertion
|
48 |
+
IF NOT EXISTS (
|
49 |
+
SELECT 1 FROM pg_policies
|
50 |
+
WHERE tablename = 'codette_files'
|
51 |
+
AND schemaname = 'public'
|
52 |
+
AND policyname = 'Allow admin users to insert files'
|
53 |
+
) THEN
|
54 |
+
CREATE POLICY "Allow admin users to insert files"
|
55 |
+
ON public.codette_files FOR INSERT
|
56 |
+
TO authenticated
|
57 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
58 |
+
END IF;
|
59 |
+
END $$;
|
60 |
+
|
61 |
+
COMMIT;
|
project/supabase/migrations/20250523141836_heavy_butterfly.sql
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage and RLS Policy Setup
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Create storage bucket policies for file access
|
6 |
+
- Create table policies for file management
|
7 |
+
- Enable RLS on codette_files table
|
8 |
+
|
9 |
+
2. Security
|
10 |
+
- Authenticated users can read files
|
11 |
+
- Admin users can upload files
|
12 |
+
- RLS enabled on codette_files table
|
13 |
+
*/
|
14 |
+
|
15 |
+
-- Create storage bucket if it doesn't exist
|
16 |
+
DO $$
|
17 |
+
BEGIN
|
18 |
+
INSERT INTO storage.buckets (id, name)
|
19 |
+
VALUES ('codette-files', 'codette-files')
|
20 |
+
ON CONFLICT (id) DO NOTHING;
|
21 |
+
END $$;
|
22 |
+
|
23 |
+
-- Storage Policies
|
24 |
+
DO $$
|
25 |
+
BEGIN
|
26 |
+
-- Drop existing policies to avoid conflicts
|
27 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON storage.objects;
|
28 |
+
DROP POLICY IF EXISTS "Allow admin users to upload files" ON storage.objects;
|
29 |
+
|
30 |
+
-- Create new storage policies
|
31 |
+
CREATE POLICY "Allow authenticated users to read files"
|
32 |
+
ON storage.objects FOR SELECT
|
33 |
+
TO authenticated
|
34 |
+
USING (bucket_id = 'codette-files');
|
35 |
+
|
36 |
+
CREATE POLICY "Allow admin users to upload files"
|
37 |
+
ON storage.objects FOR INSERT
|
38 |
+
TO authenticated
|
39 |
+
WITH CHECK (
|
40 |
+
bucket_id = 'codette-files'
|
41 |
+
AND (auth.jwt() ->> 'role' = 'admin')
|
42 |
+
);
|
43 |
+
END $$;
|
44 |
+
|
45 |
+
-- File Management Table Policies
|
46 |
+
DO $$
|
47 |
+
BEGIN
|
48 |
+
-- Drop existing policies to avoid conflicts
|
49 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON public.codette_files;
|
50 |
+
DROP POLICY IF EXISTS "Allow admin users to insert files" ON public.codette_files;
|
51 |
+
DROP POLICY IF EXISTS "Allow authenticated users to insert files" ON public.codette_files;
|
52 |
+
|
53 |
+
-- Create new table policies
|
54 |
+
CREATE POLICY "Allow authenticated users to read files"
|
55 |
+
ON public.codette_files FOR SELECT
|
56 |
+
TO authenticated
|
57 |
+
USING (true);
|
58 |
+
|
59 |
+
CREATE POLICY "Allow admin users to insert files"
|
60 |
+
ON public.codette_files FOR INSERT
|
61 |
+
TO authenticated
|
62 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
63 |
+
|
64 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
65 |
+
ON public.codette_files FOR INSERT
|
66 |
+
TO authenticated
|
67 |
+
WITH CHECK (true);
|
68 |
+
END $$;
|
69 |
+
|
70 |
+
-- Enable RLS
|
71 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
project/supabase/migrations/20250523175402_white_torch.sql
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage and File Access Policies
|
3 |
+
|
4 |
+
1. New Policies
|
5 |
+
- Enable RLS on codette_files table
|
6 |
+
- Create policies for file access and management
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Allow authenticated users to read files
|
10 |
+
- Allow admin users to upload files
|
11 |
+
- Allow authenticated users to insert file records
|
12 |
+
*/
|
13 |
+
|
14 |
+
-- Enable RLS on the codette_files table if not already enabled
|
15 |
+
DO $$
|
16 |
+
BEGIN
|
17 |
+
IF NOT EXISTS (
|
18 |
+
SELECT 1 FROM pg_tables
|
19 |
+
WHERE tablename = 'codette_files'
|
20 |
+
AND rowsecurity = true
|
21 |
+
) THEN
|
22 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
23 |
+
END IF;
|
24 |
+
END $$;
|
25 |
+
|
26 |
+
-- Create storage bucket if it doesn't exist
|
27 |
+
DO $$
|
28 |
+
BEGIN
|
29 |
+
IF NOT EXISTS (
|
30 |
+
SELECT 1 FROM storage.buckets WHERE name = 'codette-files'
|
31 |
+
) THEN
|
32 |
+
INSERT INTO storage.buckets (id, name)
|
33 |
+
VALUES ('codette-files', 'codette-files');
|
34 |
+
END IF;
|
35 |
+
END $$;
|
36 |
+
|
37 |
+
-- Create policies for the codette_files table
|
38 |
+
DO $$
|
39 |
+
BEGIN
|
40 |
+
-- Check if the read policy exists
|
41 |
+
IF NOT EXISTS (
|
42 |
+
SELECT 1 FROM pg_policies
|
43 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
44 |
+
AND tablename = 'codette_files'
|
45 |
+
) THEN
|
46 |
+
CREATE POLICY "Allow authenticated users to read files"
|
47 |
+
ON public.codette_files FOR SELECT
|
48 |
+
TO authenticated
|
49 |
+
USING (true);
|
50 |
+
END IF;
|
51 |
+
|
52 |
+
-- Check if the admin insert policy exists
|
53 |
+
IF NOT EXISTS (
|
54 |
+
SELECT 1 FROM pg_policies
|
55 |
+
WHERE policyname = 'Allow admin users to insert files'
|
56 |
+
AND tablename = 'codette_files'
|
57 |
+
) THEN
|
58 |
+
CREATE POLICY "Allow admin users to insert files"
|
59 |
+
ON public.codette_files FOR INSERT
|
60 |
+
TO authenticated
|
61 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
62 |
+
END IF;
|
63 |
+
|
64 |
+
-- Check if the authenticated insert policy exists
|
65 |
+
IF NOT EXISTS (
|
66 |
+
SELECT 1 FROM pg_policies
|
67 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
68 |
+
AND tablename = 'codette_files'
|
69 |
+
) THEN
|
70 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
71 |
+
ON public.codette_files FOR INSERT
|
72 |
+
TO authenticated
|
73 |
+
WITH CHECK (true);
|
74 |
+
END IF;
|
75 |
+
END $$;
|
76 |
+
|
77 |
+
-- Note: Storage policies for the storage.objects table need to be created through the Supabase dashboard
|
78 |
+
-- or using the Supabase CLI, as they require special permissions that aren't available in migrations.
|
79 |
+
-- Please create the following policies manually:
|
80 |
+
-- 1. "Allow authenticated users to read files" - For SELECT operations on storage.objects where bucket_id = 'codette-files'
|
81 |
+
-- 2. "Allow admin users to upload files" - For INSERT operations on storage.objects where bucket_id = 'codette-files' AND auth.jwt() ->> 'role' = 'admin'
|
project/supabase/migrations/20250523182801_long_field.sql
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage and File Management Policies
|
3 |
+
|
4 |
+
1. New Tables
|
5 |
+
- No new tables created
|
6 |
+
2. Security
|
7 |
+
- Enable RLS on codette_files table
|
8 |
+
- Add policies for authenticated users to read files
|
9 |
+
- Add policies for authenticated users to insert files
|
10 |
+
- Add special policy for admin users to insert files
|
11 |
+
3. Changes
|
12 |
+
- Ensures storage bucket exists for file storage
|
13 |
+
*/
|
14 |
+
|
15 |
+
-- Enable RLS on the codette_files table if not already enabled
|
16 |
+
DO $$
|
17 |
+
BEGIN
|
18 |
+
IF NOT EXISTS (
|
19 |
+
SELECT 1 FROM pg_tables
|
20 |
+
WHERE tablename = 'codette_files'
|
21 |
+
AND rowsecurity = true
|
22 |
+
) THEN
|
23 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
24 |
+
END IF;
|
25 |
+
END $$;
|
26 |
+
|
27 |
+
-- Create storage bucket if it doesn't exist
|
28 |
+
DO $$
|
29 |
+
BEGIN
|
30 |
+
IF NOT EXISTS (
|
31 |
+
SELECT 1 FROM storage.buckets WHERE name = 'codette-files'
|
32 |
+
) THEN
|
33 |
+
INSERT INTO storage.buckets (id, name)
|
34 |
+
VALUES ('codette-files', 'codette-files');
|
35 |
+
END IF;
|
36 |
+
END $$;
|
37 |
+
|
38 |
+
-- Create policies for the codette_files table
|
39 |
+
DO $$
|
40 |
+
BEGIN
|
41 |
+
-- Check if the read policy exists
|
42 |
+
IF NOT EXISTS (
|
43 |
+
SELECT 1 FROM pg_policies
|
44 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
45 |
+
AND tablename = 'codette_files'
|
46 |
+
) THEN
|
47 |
+
CREATE POLICY "Allow authenticated users to read files"
|
48 |
+
ON public.codette_files FOR SELECT
|
49 |
+
TO authenticated
|
50 |
+
USING (true);
|
51 |
+
END IF;
|
52 |
+
|
53 |
+
-- Check if the admin insert policy exists
|
54 |
+
IF NOT EXISTS (
|
55 |
+
SELECT 1 FROM pg_policies
|
56 |
+
WHERE policyname = 'Allow admin users to insert files'
|
57 |
+
AND tablename = 'codette_files'
|
58 |
+
) THEN
|
59 |
+
CREATE POLICY "Allow admin users to insert files"
|
60 |
+
ON public.codette_files FOR INSERT
|
61 |
+
TO authenticated
|
62 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
63 |
+
END IF;
|
64 |
+
|
65 |
+
-- Check if the authenticated insert policy exists
|
66 |
+
IF NOT EXISTS (
|
67 |
+
SELECT 1 FROM pg_policies
|
68 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
69 |
+
AND tablename = 'codette_files'
|
70 |
+
) THEN
|
71 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
72 |
+
ON public.codette_files FOR INSERT
|
73 |
+
TO authenticated
|
74 |
+
WITH CHECK (true);
|
75 |
+
END IF;
|
76 |
+
END $$;
|
77 |
+
|
78 |
+
-- Note: Storage policies for the storage.objects table need to be created through the Supabase dashboard
|
79 |
+
-- or using the Supabase CLI, as they require special permissions that aren't available in migrations.
|
80 |
+
-- Please create the following policies manually:
|
81 |
+
-- 1. "Allow authenticated users to read files" - For SELECT operations on storage.objects where bucket_id = 'codette-files'
|
82 |
+
-- 2. "Allow admin users to upload files" - For INSERT operations on storage.objects where bucket_id = 'codette-files' AND auth.jwt() ->> 'role' = 'admin'
|
project/supabase/migrations/20250523183206_odd_moon.sql
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage and File Management Setup
|
3 |
+
|
4 |
+
1. New Storage
|
5 |
+
- Create 'codette-files' storage bucket if it doesn't exist
|
6 |
+
|
7 |
+
2. Security
|
8 |
+
- Enable Row Level Security on codette_files table
|
9 |
+
- Create policies for authenticated users to read files
|
10 |
+
- Create policies for authenticated users to insert files
|
11 |
+
- Create special policy for admin users to insert files
|
12 |
+
*/
|
13 |
+
|
14 |
+
-- Enable RLS on the codette_files table if not already enabled
|
15 |
+
DO $$
|
16 |
+
BEGIN
|
17 |
+
IF NOT EXISTS (
|
18 |
+
SELECT 1 FROM pg_tables
|
19 |
+
WHERE tablename = 'codette_files'
|
20 |
+
AND rowsecurity = true
|
21 |
+
) THEN
|
22 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
23 |
+
END IF;
|
24 |
+
END $$;
|
25 |
+
|
26 |
+
-- Create storage bucket if it doesn't exist
|
27 |
+
DO $$
|
28 |
+
BEGIN
|
29 |
+
IF NOT EXISTS (
|
30 |
+
SELECT 1 FROM storage.buckets WHERE name = 'codette-files'
|
31 |
+
) THEN
|
32 |
+
INSERT INTO storage.buckets (id, name)
|
33 |
+
VALUES ('codette-files', 'codette-files');
|
34 |
+
END IF;
|
35 |
+
END $$;
|
36 |
+
|
37 |
+
-- Create policies for the codette_files table
|
38 |
+
DO $$
|
39 |
+
BEGIN
|
40 |
+
-- Check if the read policy exists
|
41 |
+
IF NOT EXISTS (
|
42 |
+
SELECT 1 FROM pg_policies
|
43 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
44 |
+
AND tablename = 'codette_files'
|
45 |
+
) THEN
|
46 |
+
CREATE POLICY "Allow authenticated users to read files"
|
47 |
+
ON public.codette_files FOR SELECT
|
48 |
+
TO authenticated
|
49 |
+
USING (true);
|
50 |
+
END IF;
|
51 |
+
|
52 |
+
-- Check if the admin insert policy exists
|
53 |
+
IF NOT EXISTS (
|
54 |
+
SELECT 1 FROM pg_policies
|
55 |
+
WHERE policyname = 'Allow admin users to insert files'
|
56 |
+
AND tablename = 'codette_files'
|
57 |
+
) THEN
|
58 |
+
CREATE POLICY "Allow admin users to insert files"
|
59 |
+
ON public.codette_files FOR INSERT
|
60 |
+
TO authenticated
|
61 |
+
WITH CHECK ((auth.jwt() ->> 'role')::text = 'admin');
|
62 |
+
END IF;
|
63 |
+
|
64 |
+
-- Check if the authenticated insert policy exists
|
65 |
+
IF NOT EXISTS (
|
66 |
+
SELECT 1 FROM pg_policies
|
67 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
68 |
+
AND tablename = 'codette_files'
|
69 |
+
) THEN
|
70 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
71 |
+
ON public.codette_files FOR INSERT
|
72 |
+
TO authenticated
|
73 |
+
WITH CHECK (true);
|
74 |
+
END IF;
|
75 |
+
END $$;
|
76 |
+
|
77 |
+
-- Note: For storage.objects policies, you'll need to create them through the Supabase dashboard
|
78 |
+
-- as migrations don't have sufficient permissions to create these policies directly.
|
79 |
+
-- Create these policies manually:
|
80 |
+
-- 1. Policy name: "Allow authenticated users to read files"
|
81 |
+
-- - For: SELECT operations
|
82 |
+
-- - Using expression: bucket_id = 'codette-files'
|
83 |
+
--
|
84 |
+
-- 2. Policy name: "Allow admin users to upload files"
|
85 |
+
-- - For: INSERT operations
|
86 |
+
-- - Using expression: bucket_id = 'codette-files' AND (auth.jwt() ->> 'role')::text = 'admin'
|
project/supabase/migrations/20250523213744_long_sun.sql
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage and File Management Setup
|
3 |
+
|
4 |
+
1. New Storage Configuration
|
5 |
+
- Creates 'codette-files' storage bucket if it doesn't exist
|
6 |
+
- Sets up proper file management structure
|
7 |
+
|
8 |
+
2. Table Policies
|
9 |
+
- Enables RLS on codette_files table
|
10 |
+
- Creates read policy for authenticated users
|
11 |
+
- Creates insert policies for both admin and authenticated users
|
12 |
+
- Ensures proper access control and security
|
13 |
+
|
14 |
+
Note: Storage object policies must be created manually through Supabase dashboard
|
15 |
+
*/
|
16 |
+
|
17 |
+
-- Enable RLS on the codette_files table if not already enabled
|
18 |
+
DO $$
|
19 |
+
BEGIN
|
20 |
+
IF NOT EXISTS (
|
21 |
+
SELECT 1 FROM pg_tables
|
22 |
+
WHERE tablename = 'codette_files'
|
23 |
+
AND rowsecurity = true
|
24 |
+
) THEN
|
25 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
26 |
+
END IF;
|
27 |
+
END $$;
|
28 |
+
|
29 |
+
-- Create storage bucket if it doesn't exist
|
30 |
+
DO $$
|
31 |
+
BEGIN
|
32 |
+
IF NOT EXISTS (
|
33 |
+
SELECT 1 FROM storage.buckets WHERE name = 'codette-files'
|
34 |
+
) THEN
|
35 |
+
INSERT INTO storage.buckets (id, name, public)
|
36 |
+
VALUES ('codette-files', 'codette-files', false);
|
37 |
+
END IF;
|
38 |
+
END $$;
|
39 |
+
|
40 |
+
-- Create policies for the codette_files table
|
41 |
+
DO $$
|
42 |
+
BEGIN
|
43 |
+
-- Create read policy if it doesn't exist
|
44 |
+
IF NOT EXISTS (
|
45 |
+
SELECT 1 FROM pg_policies
|
46 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
47 |
+
AND tablename = 'codette_files'
|
48 |
+
) THEN
|
49 |
+
CREATE POLICY "Allow authenticated users to read files"
|
50 |
+
ON public.codette_files FOR SELECT
|
51 |
+
TO authenticated
|
52 |
+
USING (true);
|
53 |
+
END IF;
|
54 |
+
|
55 |
+
-- Create admin insert policy if it doesn't exist
|
56 |
+
IF NOT EXISTS (
|
57 |
+
SELECT 1 FROM pg_policies
|
58 |
+
WHERE policyname = 'Allow admin users to insert files'
|
59 |
+
AND tablename = 'codette_files'
|
60 |
+
) THEN
|
61 |
+
CREATE POLICY "Allow admin users to insert files"
|
62 |
+
ON public.codette_files FOR INSERT
|
63 |
+
TO authenticated
|
64 |
+
WITH CHECK ((auth.jwt() ->> 'role')::text = 'admin');
|
65 |
+
END IF;
|
66 |
+
|
67 |
+
-- Create authenticated insert policy if it doesn't exist
|
68 |
+
IF NOT EXISTS (
|
69 |
+
SELECT 1 FROM pg_policies
|
70 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
71 |
+
AND tablename = 'codette_files'
|
72 |
+
) THEN
|
73 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
74 |
+
ON public.codette_files FOR INSERT
|
75 |
+
TO authenticated
|
76 |
+
WITH CHECK (true);
|
77 |
+
END IF;
|
78 |
+
END $$;
|
79 |
+
|
80 |
+
-- Important: Storage object policies must be created manually through the Supabase dashboard
|
81 |
+
-- Create the following policies:
|
82 |
+
-- 1. "Allow authenticated users to read files"
|
83 |
+
-- - Operation: SELECT
|
84 |
+
-- - Target roles: authenticated
|
85 |
+
-- - Using expression: bucket_id = 'codette-files'
|
86 |
+
--
|
87 |
+
-- 2. "Allow admin users to upload files"
|
88 |
+
-- - Operation: INSERT
|
89 |
+
-- - Target roles: authenticated
|
90 |
+
-- - Using expression: bucket_id = 'codette-files' AND (auth.jwt() ->> 'role')::text = 'admin'
|
project/supabase/migrations/20250523222316_square_gate.sql
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Fix RLS policies for codette_files table
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Drop existing RLS policies that might be conflicting
|
6 |
+
- Add new RLS policies for admin users
|
7 |
+
- Allow admin users to insert files
|
8 |
+
- Allow admin users to read files
|
9 |
+
- Allow admin users to update files
|
10 |
+
- Allow admin users to delete files
|
11 |
+
- Add RLS policies for regular authenticated users
|
12 |
+
- Allow reading files only
|
13 |
+
|
14 |
+
2. Security
|
15 |
+
- Ensures only admin users can upload/modify files
|
16 |
+
- All authenticated users can read files
|
17 |
+
- Proper RLS enforcement for file management
|
18 |
+
*/
|
19 |
+
|
20 |
+
-- Drop existing policies to avoid conflicts
|
21 |
+
DROP POLICY IF EXISTS "Allow admin users to insert files" ON codette_files;
|
22 |
+
DROP POLICY IF EXISTS "Allow authenticated users to insert files" ON codette_files;
|
23 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON codette_files;
|
24 |
+
|
25 |
+
-- Create new policies with proper checks
|
26 |
+
CREATE POLICY "Allow admin users to manage files"
|
27 |
+
ON codette_files
|
28 |
+
FOR ALL
|
29 |
+
TO authenticated
|
30 |
+
USING (
|
31 |
+
(auth.jwt() ->> 'role')::text = 'admin'
|
32 |
+
)
|
33 |
+
WITH CHECK (
|
34 |
+
(auth.jwt() ->> 'role')::text = 'admin'
|
35 |
+
);
|
36 |
+
|
37 |
+
CREATE POLICY "Allow authenticated users to read files"
|
38 |
+
ON codette_files
|
39 |
+
FOR SELECT
|
40 |
+
TO authenticated
|
41 |
+
USING (true);
|
42 |
+
|
43 |
+
-- Enable RLS if not already enabled
|
44 |
+
ALTER TABLE codette_files ENABLE ROW LEVEL SECURITY;
|
project/supabase/migrations/20250523222514_muddy_desert.sql
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage bucket and RLS policies
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Create storage bucket for Codette files
|
6 |
+
- Set up RLS policies for the bucket
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Enable RLS policies for storage bucket
|
10 |
+
- Allow authenticated users to read files
|
11 |
+
- Allow authenticated users to upload files
|
12 |
+
- Allow authenticated users to update files
|
13 |
+
- Allow authenticated users to delete files
|
14 |
+
*/
|
15 |
+
|
16 |
+
-- Create the storage bucket
|
17 |
+
INSERT INTO storage.buckets (id, name)
|
18 |
+
VALUES ('codette-files', 'codette-files')
|
19 |
+
ON CONFLICT (id) DO NOTHING;
|
20 |
+
|
21 |
+
-- Drop existing policies if they exist
|
22 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON storage.objects;
|
23 |
+
DROP POLICY IF EXISTS "Allow authenticated users to upload files" ON storage.objects;
|
24 |
+
DROP POLICY IF EXISTS "Allow authenticated users to update files" ON storage.objects;
|
25 |
+
DROP POLICY IF EXISTS "Allow authenticated users to delete files" ON storage.objects;
|
26 |
+
|
27 |
+
-- Set up RLS policies for the bucket
|
28 |
+
CREATE POLICY "Allow authenticated users to read files"
|
29 |
+
ON storage.objects FOR SELECT
|
30 |
+
TO authenticated
|
31 |
+
USING (bucket_id = 'codette-files');
|
32 |
+
|
33 |
+
CREATE POLICY "Allow authenticated users to upload files"
|
34 |
+
ON storage.objects FOR INSERT
|
35 |
+
TO authenticated
|
36 |
+
WITH CHECK (bucket_id = 'codette-files');
|
37 |
+
|
38 |
+
CREATE POLICY "Allow authenticated users to update files"
|
39 |
+
ON storage.objects FOR UPDATE
|
40 |
+
TO authenticated
|
41 |
+
USING (bucket_id = 'codette-files')
|
42 |
+
WITH CHECK (bucket_id = 'codette-files');
|
43 |
+
|
44 |
+
CREATE POLICY "Allow authenticated users to delete files"
|
45 |
+
ON storage.objects FOR DELETE
|
46 |
+
TO authenticated
|
47 |
+
USING (bucket_id = 'codette-files');
|
project/supabase/migrations/20250523222518_bronze_dew.sql
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Update RLS policies for file management
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Update storage.objects policies
|
6 |
+
- Update codette_files table policies
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Allow authenticated users to read files
|
10 |
+
- Allow admin users to upload files
|
11 |
+
- Allow admin users to insert file records
|
12 |
+
*/
|
13 |
+
|
14 |
+
BEGIN;
|
15 |
+
|
16 |
+
-- Drop existing policies if they exist
|
17 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON storage.objects;
|
18 |
+
DROP POLICY IF EXISTS "Allow admin users to upload files" ON storage.objects;
|
19 |
+
DROP POLICY IF EXISTS "Allow admin users to insert files" ON public.codette_files;
|
20 |
+
|
21 |
+
-- Create policy to allow authenticated users to read any file
|
22 |
+
CREATE POLICY "Allow authenticated users to read files"
|
23 |
+
ON storage.objects FOR SELECT
|
24 |
+
TO authenticated
|
25 |
+
USING (bucket_id = 'codette-files');
|
26 |
+
|
27 |
+
-- Create policy to allow only admin users to upload files
|
28 |
+
CREATE POLICY "Allow admin users to upload files"
|
29 |
+
ON storage.objects FOR INSERT
|
30 |
+
TO authenticated
|
31 |
+
WITH CHECK (bucket_id = 'codette-files' AND auth.jwt() ->> 'role' = 'admin');
|
32 |
+
|
33 |
+
-- Update the codette_files table policies
|
34 |
+
CREATE POLICY "Allow admin users to insert files"
|
35 |
+
ON public.codette_files FOR INSERT
|
36 |
+
TO authenticated
|
37 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
38 |
+
|
39 |
+
COMMIT;
|
project/supabase/migrations/20250523222523_orange_bread.sql
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Update RLS policies for file management
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Update storage.objects policies
|
6 |
+
- Update codette_files table policies
|
7 |
+
- Enable RLS on codette_files table
|
8 |
+
|
9 |
+
2. Security
|
10 |
+
- Allow authenticated users to read files
|
11 |
+
- Allow admin users to upload files
|
12 |
+
- Allow authenticated users to insert files
|
13 |
+
*/
|
14 |
+
|
15 |
+
-- Drop existing policies if they exist
|
16 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON storage.objects;
|
17 |
+
DROP POLICY IF EXISTS "Allow admin users to upload files" ON storage.objects;
|
18 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON public.codette_files;
|
19 |
+
DROP POLICY IF EXISTS "Allow admin users to insert files" ON public.codette_files;
|
20 |
+
DROP POLICY IF EXISTS "Allow authenticated users to insert files" ON public.codette_files;
|
21 |
+
|
22 |
+
-- Storage Policies
|
23 |
+
CREATE POLICY "Allow authenticated users to read files"
|
24 |
+
ON storage.objects FOR SELECT
|
25 |
+
TO authenticated
|
26 |
+
USING (bucket_id = 'codette-files');
|
27 |
+
|
28 |
+
CREATE POLICY "Allow admin users to upload files"
|
29 |
+
ON storage.objects FOR INSERT
|
30 |
+
TO authenticated
|
31 |
+
WITH CHECK (
|
32 |
+
bucket_id = 'codette-files'
|
33 |
+
AND (auth.jwt() ->> 'role' = 'admin')
|
34 |
+
);
|
35 |
+
|
36 |
+
-- File Management Policies
|
37 |
+
CREATE POLICY "Allow authenticated users to read files"
|
38 |
+
ON public.codette_files FOR SELECT
|
39 |
+
TO authenticated
|
40 |
+
USING (true);
|
41 |
+
|
42 |
+
CREATE POLICY "Allow admin users to insert files"
|
43 |
+
ON public.codette_files FOR INSERT
|
44 |
+
TO authenticated
|
45 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
46 |
+
|
47 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
48 |
+
ON public.codette_files FOR INSERT
|
49 |
+
TO authenticated
|
50 |
+
WITH CHECK (true);
|
51 |
+
|
52 |
+
-- Enable RLS
|
53 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
project/supabase/migrations/20250524062844_tender_thunder.sql
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Update codette_files table and policies
|
3 |
+
|
4 |
+
1. New Tables
|
5 |
+
- Ensures codette_files table exists with proper structure
|
6 |
+
- id (uuid, primary key)
|
7 |
+
- filename (text)
|
8 |
+
- storage_path (text)
|
9 |
+
- file_type (text, nullable)
|
10 |
+
- uploaded_at (timestamptz)
|
11 |
+
- created_at (timestamptz)
|
12 |
+
|
13 |
+
2. Security
|
14 |
+
- Enables RLS if not already enabled
|
15 |
+
- Adds admin-specific policies for file management
|
16 |
+
*/
|
17 |
+
|
18 |
+
-- Create table if it doesn't exist
|
19 |
+
CREATE TABLE IF NOT EXISTS public.codette_files (
|
20 |
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
21 |
+
filename text NOT NULL,
|
22 |
+
storage_path text NOT NULL,
|
23 |
+
file_type text,
|
24 |
+
uploaded_at timestamptz DEFAULT now(),
|
25 |
+
created_at timestamptz DEFAULT now()
|
26 |
+
);
|
27 |
+
|
28 |
+
-- Enable Row Level Security (idempotent operation)
|
29 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
30 |
+
|
31 |
+
-- Drop existing policies to avoid conflicts
|
32 |
+
DROP POLICY IF EXISTS "Allow authenticated users to read files" ON public.codette_files;
|
33 |
+
DROP POLICY IF EXISTS "Allow authenticated users to insert files" ON public.codette_files;
|
34 |
+
DROP POLICY IF EXISTS "Allow admin users to manage files" ON public.codette_files;
|
35 |
+
DROP POLICY IF EXISTS "Allow admin users to insert files" ON public.codette_files;
|
36 |
+
|
37 |
+
-- Create new policies
|
38 |
+
CREATE POLICY "Allow authenticated users to read files"
|
39 |
+
ON public.codette_files
|
40 |
+
FOR SELECT
|
41 |
+
TO authenticated
|
42 |
+
USING (true);
|
43 |
+
|
44 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
45 |
+
ON public.codette_files
|
46 |
+
FOR INSERT
|
47 |
+
TO authenticated
|
48 |
+
WITH CHECK (true);
|
49 |
+
|
50 |
+
-- Add admin-specific policies
|
51 |
+
CREATE POLICY "Allow admin users to manage files"
|
52 |
+
ON public.codette_files
|
53 |
+
FOR ALL
|
54 |
+
TO authenticated
|
55 |
+
USING ((auth.jwt() ->> 'role'::text) = 'admin'::text)
|
56 |
+
WITH CHECK ((auth.jwt() ->> 'role'::text) = 'admin'::text);
|
57 |
+
|
58 |
+
CREATE POLICY "Allow admin users to insert files"
|
59 |
+
ON public.codette_files
|
60 |
+
FOR INSERT
|
61 |
+
TO authenticated
|
62 |
+
WITH CHECK ((auth.jwt() ->> 'role'::text) = 'admin'::text);
|
project/supabase/migrations/20250524213845_mellow_recipe.sql
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Add user roles table and admin role policy
|
3 |
+
|
4 |
+
1. New Tables
|
5 |
+
- `user_roles`
|
6 |
+
- `id` (uuid, primary key)
|
7 |
+
- `user_id` (uuid, references auth.users)
|
8 |
+
- `role` (text)
|
9 |
+
- `created_at` (timestamptz)
|
10 |
+
|
11 |
+
2. Security
|
12 |
+
- Enable RLS on `user_roles` table
|
13 |
+
- Add policies for admin role management
|
14 |
+
*/
|
15 |
+
|
16 |
+
-- Create user_roles table
|
17 |
+
CREATE TABLE IF NOT EXISTS user_roles (
|
18 |
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
19 |
+
user_id uuid REFERENCES auth.users NOT NULL,
|
20 |
+
role text NOT NULL,
|
21 |
+
created_at timestamptz DEFAULT now()
|
22 |
+
);
|
23 |
+
|
24 |
+
-- Enable RLS
|
25 |
+
ALTER TABLE user_roles ENABLE ROW LEVEL SECURITY;
|
26 |
+
|
27 |
+
-- Policies for user_roles table
|
28 |
+
CREATE POLICY "Users can read their own role"
|
29 |
+
ON user_roles
|
30 |
+
FOR SELECT
|
31 |
+
TO authenticated
|
32 |
+
USING (auth.uid() = user_id);
|
33 |
+
|
34 |
+
CREATE POLICY "Only admins can manage roles"
|
35 |
+
ON user_roles
|
36 |
+
FOR ALL
|
37 |
+
TO authenticated
|
38 |
+
USING (
|
39 |
+
EXISTS (
|
40 |
+
SELECT 1 FROM user_roles
|
41 |
+
WHERE user_id = auth.uid()
|
42 |
+
AND role = 'admin'
|
43 |
+
)
|
44 |
+
);
|
project/supabase/migrations/20250524214450_green_poetry.sql
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Authentication and User Roles Setup
|
3 |
+
|
4 |
+
1. New Tables
|
5 |
+
- `user_roles`
|
6 |
+
- `id` (uuid, primary key)
|
7 |
+
- `user_id` (uuid, references auth.users)
|
8 |
+
- `role` (text)
|
9 |
+
- `created_at` (timestamp with time zone)
|
10 |
+
|
11 |
+
2. Security
|
12 |
+
- Enable RLS on `user_roles` table
|
13 |
+
- Add policies for authenticated users to read their own role
|
14 |
+
- Add policy for admin users to manage roles
|
15 |
+
*/
|
16 |
+
|
17 |
+
-- Create user_roles table
|
18 |
+
CREATE TABLE IF NOT EXISTS public.user_roles (
|
19 |
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
20 |
+
user_id uuid REFERENCES auth.users NOT NULL,
|
21 |
+
role text NOT NULL,
|
22 |
+
created_at timestamptz DEFAULT now()
|
23 |
+
);
|
24 |
+
|
25 |
+
-- Enable RLS
|
26 |
+
ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY;
|
27 |
+
|
28 |
+
-- Policies
|
29 |
+
CREATE POLICY "Users can read own role"
|
30 |
+
ON public.user_roles
|
31 |
+
FOR SELECT
|
32 |
+
TO authenticated
|
33 |
+
USING (auth.uid() = user_id);
|
34 |
+
|
35 |
+
CREATE POLICY "Admin users can manage roles"
|
36 |
+
ON public.user_roles
|
37 |
+
FOR ALL
|
38 |
+
TO authenticated
|
39 |
+
USING ((SELECT role FROM public.user_roles WHERE user_id = auth.uid()) = 'admin')
|
40 |
+
WITH CHECK ((SELECT role FROM public.user_roles WHERE user_id = auth.uid()) = 'admin');
|
41 |
+
|
42 |
+
-- Create admin user if not exists
|
43 |
+
DO $$
|
44 |
+
BEGIN
|
45 |
+
IF NOT EXISTS (
|
46 |
+
SELECT 1 FROM auth.users WHERE email = '[email protected]'
|
47 |
+
) THEN
|
48 |
+
INSERT INTO auth.users (
|
49 |
+
instance_id,
|
50 |
+
id,
|
51 |
+
aud,
|
52 |
+
role,
|
53 |
+
email,
|
54 |
+
encrypted_password,
|
55 |
+
email_confirmed_at,
|
56 |
+
created_at,
|
57 |
+
updated_at,
|
58 |
+
confirmation_token,
|
59 |
+
recovery_token
|
60 |
+
)
|
61 |
+
VALUES (
|
62 |
+
'00000000-0000-0000-0000-000000000000',
|
63 |
+
gen_random_uuid(),
|
64 |
+
'authenticated',
|
65 |
+
'authenticated',
|
66 |
+
'[email protected]',
|
67 |
+
crypt('admin123', gen_salt('bf')), -- Default password: admin123
|
68 |
+
now(),
|
69 |
+
now(),
|
70 |
+
now(),
|
71 |
+
encode(gen_random_bytes(32), 'hex'),
|
72 |
+
encode(gen_random_bytes(32), 'hex')
|
73 |
+
);
|
74 |
+
|
75 |
+
-- Add admin role
|
76 |
+
INSERT INTO public.user_roles (user_id, role)
|
77 |
+
SELECT id, 'admin'
|
78 |
+
FROM auth.users
|
79 |
+
WHERE email = '[email protected]';
|
80 |
+
END IF;
|
81 |
+
END $$;
|
project/supabase/migrations/20250524214705_sunny_sunset.sql
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Storage bucket and policies setup
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Creates storage bucket for file storage
|
6 |
+
- Sets up RLS policies for authenticated users
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Enables secure file access for authenticated users
|
10 |
+
- Implements proper access control through RLS policies
|
11 |
+
*/
|
12 |
+
|
13 |
+
-- Create the storage bucket if it doesn't exist
|
14 |
+
INSERT INTO storage.buckets (id, name)
|
15 |
+
VALUES ('codette-files', 'codette-files')
|
16 |
+
ON CONFLICT (id) DO NOTHING;
|
project/supabase/migrations/20250524214708_lively_cell.sql
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# File management policies
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Creates policies for file management
|
6 |
+
- Sets up proper access control for authenticated users and admins
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Implements RLS policies for the codette_files table
|
10 |
+
- Ensures proper access control based on user roles
|
11 |
+
*/
|
12 |
+
|
13 |
+
-- Enable RLS on codette_files table
|
14 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
15 |
+
|
16 |
+
-- Create policies for the codette_files table
|
17 |
+
DO $$
|
18 |
+
BEGIN
|
19 |
+
-- Check if the read policy exists
|
20 |
+
IF NOT EXISTS (
|
21 |
+
SELECT 1 FROM pg_policies
|
22 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
23 |
+
AND tablename = 'codette_files'
|
24 |
+
) THEN
|
25 |
+
CREATE POLICY "Allow authenticated users to read files"
|
26 |
+
ON public.codette_files FOR SELECT
|
27 |
+
TO authenticated
|
28 |
+
USING (true);
|
29 |
+
END IF;
|
30 |
+
|
31 |
+
-- Check if the admin insert policy exists
|
32 |
+
IF NOT EXISTS (
|
33 |
+
SELECT 1 FROM pg_policies
|
34 |
+
WHERE policyname = 'Allow admin users to insert files'
|
35 |
+
AND tablename = 'codette_files'
|
36 |
+
) THEN
|
37 |
+
CREATE POLICY "Allow admin users to insert files"
|
38 |
+
ON public.codette_files FOR INSERT
|
39 |
+
TO authenticated
|
40 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
41 |
+
END IF;
|
42 |
+
|
43 |
+
-- Check if the authenticated insert policy exists
|
44 |
+
IF NOT EXISTS (
|
45 |
+
SELECT 1 FROM pg_policies
|
46 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
47 |
+
AND tablename = 'codette_files'
|
48 |
+
) THEN
|
49 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
50 |
+
ON public.codette_files FOR INSERT
|
51 |
+
TO authenticated
|
52 |
+
WITH CHECK (true);
|
53 |
+
END IF;
|
54 |
+
END $$;
|
project/supabase/migrations/20250524214713_yellow_dawn.sql
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# File management and storage setup
|
3 |
+
|
4 |
+
1. Changes
|
5 |
+
- Enables RLS on codette_files table
|
6 |
+
- Creates necessary policies for file management
|
7 |
+
|
8 |
+
2. Security
|
9 |
+
- Implements proper access control through RLS
|
10 |
+
- Sets up role-based permissions
|
11 |
+
*/
|
12 |
+
|
13 |
+
-- Enable RLS on codette_files table if not already enabled
|
14 |
+
DO $$
|
15 |
+
BEGIN
|
16 |
+
IF NOT EXISTS (
|
17 |
+
SELECT 1 FROM pg_tables
|
18 |
+
WHERE tablename = 'codette_files'
|
19 |
+
AND rowsecurity = true
|
20 |
+
) THEN
|
21 |
+
ALTER TABLE public.codette_files ENABLE ROW LEVEL SECURITY;
|
22 |
+
END IF;
|
23 |
+
END $$;
|
24 |
+
|
25 |
+
-- Create policies for the codette_files table
|
26 |
+
DO $$
|
27 |
+
BEGIN
|
28 |
+
-- Check if the read policy exists
|
29 |
+
IF NOT EXISTS (
|
30 |
+
SELECT 1 FROM pg_policies
|
31 |
+
WHERE policyname = 'Allow authenticated users to read files'
|
32 |
+
AND tablename = 'codette_files'
|
33 |
+
) THEN
|
34 |
+
CREATE POLICY "Allow authenticated users to read files"
|
35 |
+
ON public.codette_files FOR SELECT
|
36 |
+
TO authenticated
|
37 |
+
USING (true);
|
38 |
+
END IF;
|
39 |
+
|
40 |
+
-- Check if the admin insert policy exists
|
41 |
+
IF NOT EXISTS (
|
42 |
+
SELECT 1 FROM pg_policies
|
43 |
+
WHERE policyname = 'Allow admin users to insert files'
|
44 |
+
AND tablename = 'codette_files'
|
45 |
+
) THEN
|
46 |
+
CREATE POLICY "Allow admin users to insert files"
|
47 |
+
ON public.codette_files FOR INSERT
|
48 |
+
TO authenticated
|
49 |
+
WITH CHECK (auth.jwt() ->> 'role' = 'admin');
|
50 |
+
END IF;
|
51 |
+
|
52 |
+
-- Check if the authenticated insert policy exists
|
53 |
+
IF NOT EXISTS (
|
54 |
+
SELECT 1 FROM pg_policies
|
55 |
+
WHERE policyname = 'Allow authenticated users to insert files'
|
56 |
+
AND tablename = 'codette_files'
|
57 |
+
) THEN
|
58 |
+
CREATE POLICY "Allow authenticated users to insert files"
|
59 |
+
ON public.codette_files FOR INSERT
|
60 |
+
TO authenticated
|
61 |
+
WITH CHECK (true);
|
62 |
+
END IF;
|
63 |
+
END $$;
|
project/supabase/migrations/20250524215300_flat_firefly.sql
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
# Add get_user_role function
|
3 |
+
|
4 |
+
1. New Functions
|
5 |
+
- `get_user_role`: Returns the role of the authenticated user
|
6 |
+
|
7 |
+
2. Security
|
8 |
+
- Function is only accessible to authenticated users
|
9 |
+
- Returns the user's role from user_roles table
|
10 |
+
*/
|
11 |
+
|
12 |
+
-- Create function to get user role
|
13 |
+
CREATE OR REPLACE FUNCTION public.get_user_role()
|
14 |
+
RETURNS TABLE (role text)
|
15 |
+
LANGUAGE plpgsql
|
16 |
+
SECURITY DEFINER
|
17 |
+
SET search_path = public
|
18 |
+
AS $$
|
19 |
+
BEGIN
|
20 |
+
RETURN QUERY
|
21 |
+
SELECT ur.role
|
22 |
+
FROM public.user_roles ur
|
23 |
+
WHERE ur.user_id = auth.uid()
|
24 |
+
LIMIT 1;
|
25 |
+
END;
|
26 |
+
$$;
|
project/tailwind.config.js
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/** @type {import('tailwindcss').Config} */
|
2 |
+
export default {
|
3 |
+
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
|
4 |
+
darkMode: 'class',
|
5 |
+
theme: {
|
6 |
+
extend: {
|
7 |
+
colors: {
|
8 |
+
primary: {
|
9 |
+
50: '#EFF6FF',
|
10 |
+
100: '#DBEAFE',
|
11 |
+
200: '#BFDBFE',
|
12 |
+
300: '#93C5FD',
|
13 |
+
400: '#60A5FA',
|
14 |
+
500: '#3B82F6',
|
15 |
+
600: '#2563EB',
|
16 |
+
700: '#1D4ED8',
|
17 |
+
800: '#1E40AF',
|
18 |
+
900: '#1E3A8A',
|
19 |
+
},
|
20 |
+
secondary: {
|
21 |
+
50: '#F5F3FF',
|
22 |
+
100: '#EDE9FE',
|
23 |
+
200: '#DDD6FE',
|
24 |
+
300: '#C4B5FD',
|
25 |
+
400: '#A78BFA',
|
26 |
+
500: '#8B5CF6',
|
27 |
+
600: '#7C3AED',
|
28 |
+
700: '#6D28D9',
|
29 |
+
800: '#5B21B6',
|
30 |
+
900: '#4C1D95',
|
31 |
+
},
|
32 |
+
accent: {
|
33 |
+
50: '#ECFDF5',
|
34 |
+
100: '#D1FAE5',
|
35 |
+
200: '#A7F3D0',
|
36 |
+
300: '#6EE7B7',
|
37 |
+
400: '#34D399',
|
38 |
+
500: '#10B981',
|
39 |
+
600: '#059669',
|
40 |
+
700: '#047857',
|
41 |
+
800: '#065F46',
|
42 |
+
900: '#064E3B',
|
43 |
+
},
|
44 |
+
},
|
45 |
+
animation: {
|
46 |
+
'pulse': 'pulse 2s infinite',
|
47 |
+
'float': 'float 3s ease-in-out infinite',
|
48 |
+
'spin-slow': 'spin 4s linear infinite',
|
49 |
+
},
|
50 |
+
keyframes: {
|
51 |
+
float: {
|
52 |
+
'0%, 100%': { transform: 'translateY(0)' },
|
53 |
+
'50%': { transform: 'translateY(-10px)' },
|
54 |
+
},
|
55 |
+
},
|
56 |
+
backdropFilter: {
|
57 |
+
'none': 'none',
|
58 |
+
'blur': 'blur(8px)',
|
59 |
+
},
|
60 |
+
},
|
61 |
+
},
|
62 |
+
plugins: [],
|
63 |
+
};
|
project/tsconfig.app.json
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "ES2020",
|
4 |
+
"useDefineForClassFields": true,
|
5 |
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
6 |
+
"module": "ESNext",
|
7 |
+
"skipLibCheck": true,
|
8 |
+
|
9 |
+
/* Bundler mode */
|
10 |
+
"moduleResolution": "bundler",
|
11 |
+
"allowImportingTsExtensions": true,
|
12 |
+
"isolatedModules": true,
|
13 |
+
"moduleDetection": "force",
|
14 |
+
"noEmit": true,
|
15 |
+
"jsx": "react-jsx",
|
16 |
+
|
17 |
+
/* Linting */
|
18 |
+
"strict": true,
|
19 |
+
"noUnusedLocals": true,
|
20 |
+
"noUnusedParameters": true,
|
21 |
+
"noFallthroughCasesInSwitch": true
|
22 |
+
},
|
23 |
+
"include": ["src"]
|
24 |
+
}
|
project/tsconfig.json
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"files": [],
|
3 |
+
"references": [
|
4 |
+
{ "path": "./tsconfig.app.json" },
|
5 |
+
{ "path": "./tsconfig.node.json" }
|
6 |
+
]
|
7 |
+
}
|
project/tsconfig.node.json
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"compilerOptions": {
|
3 |
+
"target": "ES2022",
|
4 |
+
"lib": ["ES2023"],
|
5 |
+
"module": "ESNext",
|
6 |
+
"skipLibCheck": true,
|
7 |
+
|
8 |
+
/* Bundler mode */
|
9 |
+
"moduleResolution": "bundler",
|
10 |
+
"allowImportingTsExtensions": true,
|
11 |
+
"isolatedModules": true,
|
12 |
+
"moduleDetection": "force",
|
13 |
+
"noEmit": true,
|
14 |
+
|
15 |
+
/* Linting */
|
16 |
+
"strict": true,
|
17 |
+
"noUnusedLocals": true,
|
18 |
+
"noUnusedParameters": true,
|
19 |
+
"noFallthroughCasesInSwitch": true
|
20 |
+
},
|
21 |
+
"include": ["vite.config.ts"]
|
22 |
+
}
|