Compare commits
16 Commits
mimo/build
...
feat/mnemo
| Author | SHA1 | Date | |
|---|---|---|---|
| fb0274b0ed | |||
| 2534917c26 | |||
| 2bce7b345b | |||
| 378197c1a5 | |||
| dd853a21c3 | |||
| 4f8e0330c5 | |||
| c3847cc046 | |||
| 4c4677842d | |||
| f0d929a177 | |||
| a22464506c | |||
| be55195815 | |||
| 7fb086976e | |||
| c192b05cc1 | |||
| 45ddd65d16 | |||
| 9984cb733e | |||
|
|
3367ce5438 |
53
concept-packs/genie-nano-banana/README.md
Normal file
53
concept-packs/genie-nano-banana/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Project Genie + Nano Banana Concept Pack
|
||||
|
||||
**Issue:** #680
|
||||
**Status:** Active — first batch ready for generation
|
||||
|
||||
## Purpose
|
||||
|
||||
Exploit Google world/image generation (Project Genie, Nano Banana Pro) to
|
||||
accelerate visual ideation for The Nexus while keeping Three.js implementation
|
||||
local and sovereign.
|
||||
|
||||
## What This Pack Contains
|
||||
|
||||
```
|
||||
concept-packs/genie-nano-banana/
|
||||
├── README.md ← you are here
|
||||
├── shot-list.yaml ← ordered list of concept shots to generate
|
||||
├── pipeline.md ← how generated assets flow into Three.js code
|
||||
├── storage-policy.md ← what lives in repo vs. local-only
|
||||
├── prompts/
|
||||
│ ├── environments.yaml ← Nexus room/zone environment prompts
|
||||
│ ├── portals.yaml ← portal gateway concept prompts
|
||||
│ ├── landmarks.yaml ← iconic structures and focal points
|
||||
│ ├── skyboxes.yaml ← nebula/void skybox prompts
|
||||
│ └── textures.yaml ← surface/material concept prompts
|
||||
└── references/
|
||||
└── palette.md ← canonical Nexus color/material reference
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Generate** — Take prompts from `prompts/*.yaml` into Project Genie
|
||||
(worlds) or Nano Banana Pro (images). Run batch-by-batch per shot-list.
|
||||
2. **Capture** — Screenshot Genie worlds. Save Nano Banana outputs as PNG.
|
||||
Store locally per `storage-policy.md`.
|
||||
3. **Translate** — Follow `pipeline.md` to convert concept art into
|
||||
Three.js geometry, materials, lighting, and post-processing targets.
|
||||
4. **Build** — Implement in `app.js` / root frontend files. Concepts are
|
||||
reference, not source-of-truth. Code is sovereign.
|
||||
|
||||
## Design Language
|
||||
|
||||
The Nexus visual identity:
|
||||
- **Background:** #050510 (deep void)
|
||||
- **Primary:** #4af0c0 (cyan-green neon)
|
||||
- **Secondary:** #7b5cff (electric purple)
|
||||
- **Gold:** #ffd700 (sacred accent)
|
||||
- **Danger:** #ff4466 (warning red)
|
||||
- **Fonts:** Orbitron (display), JetBrains Mono (body)
|
||||
- **Mood:** Cyberpunk cathedral — sacred technology, digital sovereignty
|
||||
- **Post-processing:** Bloom, SMAA, volumetric fog where possible
|
||||
|
||||
See `references/palette.md` for full material/lighting reference.
|
||||
107
concept-packs/genie-nano-banana/pipeline.md
Normal file
107
concept-packs/genie-nano-banana/pipeline.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Concept-to-Three.js Pipeline
|
||||
|
||||
## How Generated Assets Flow Into Code
|
||||
|
||||
### Step 1: Generate
|
||||
|
||||
Run prompts from `prompts/*.yaml` through:
|
||||
- **Nano Banana Pro** → static concept images (PNG)
|
||||
- **Project Genie** → explorable 3D worlds (record as video + screenshots)
|
||||
|
||||
Batch runs are tracked in `shot-list.yaml`. Check off each shot as generated.
|
||||
|
||||
### Step 2: Capture & Store
|
||||
|
||||
**For Nano Banana images:**
|
||||
```
|
||||
local-only-path: ~/nexus-concepts/nano-banana/{shot-id}/
|
||||
├── shot-id_v1.png
|
||||
├── shot-id_v2.png
|
||||
├── shot-id_v3.png
|
||||
└── shot-id_v4.png
|
||||
```
|
||||
Do NOT commit PNG files to the repo. They are binary media weight.
|
||||
Store locally. Reference by path in design notes.
|
||||
|
||||
**For Project Genie worlds:**
|
||||
```
|
||||
local-only-path: ~/nexus-concepts/genie-worlds/{shot-id}/
|
||||
├── walkthrough.mp4 (screen recording)
|
||||
├── screenshot_01.png (key angles)
|
||||
├── screenshot_02.png
|
||||
└── notes.md (scale observations, spatial notes)
|
||||
```
|
||||
Do NOT commit video or large screenshots to repo.
|
||||
|
||||
### Step 3: Translate — Image to Three.js
|
||||
|
||||
Each concept image becomes one or more of these Three.js artifacts:
|
||||
|
||||
| Concept Feature | Three.js Translation | File |
|
||||
|----------------|---------------------|------|
|
||||
| Platform shape/size | `THREE.CylinderGeometry` or custom `BufferGeometry` | `app.js` |
|
||||
| Platform material | `THREE.MeshStandardMaterial` with color, roughness, metalness | `app.js` |
|
||||
| Grid lines on platform | Custom shader or texture map (UV reference from concept) | `app.js` / `style.css` |
|
||||
| Portal ring shape | `THREE.TorusGeometry` with emissive material | `app.js` |
|
||||
| Portal inner glow | Custom shader material (swirl + transparency) | `app.js` |
|
||||
| Portal color | `NEXUS.colors` map + per-portal `color` in `portals.json` | `portals.json` |
|
||||
| Crystal geometry | `THREE.OctahedronGeometry` or `THREE.IcosahedronGeometry` | `app.js` |
|
||||
| Crystal glow | `THREE.MeshStandardMaterial` emissive + bloom post-processing | `app.js` |
|
||||
| Particle streams | `THREE.Points` with custom `BufferGeometry` and velocity | `app.js` |
|
||||
| Skybox | `THREE.CubeTextureLoader` or `THREE.EquirectangularReflectionMapping` | `app.js` |
|
||||
| Fog | `scene.fog = new THREE.FogExp2(color, density)` | `app.js` |
|
||||
| Lighting | `THREE.PointLight`, `THREE.AmbientLight` — match concept color temp | `app.js` |
|
||||
| Bloom | `UnrealBloomPass` — threshold/strength tuned to concept glow levels | `app.js` |
|
||||
|
||||
### Step 4: Design Notes Format
|
||||
|
||||
For each concept that gets translated, create a short design note:
|
||||
|
||||
```markdown
|
||||
# Design: {concept-name}
|
||||
Source: concept-packs/genie-nano-banana/references/{shot-id}_selected.png
|
||||
Generated: {date}
|
||||
Translated by: {agent or human}
|
||||
|
||||
## Geometry
|
||||
- Shape: {CylinderGeometry, radius=8, height=0.3, segments=64}
|
||||
- Position: {x, y, z}
|
||||
|
||||
## Material
|
||||
- Base color: #{hex}
|
||||
- Roughness: 0.{N}
|
||||
- Metalness: 0.{N}
|
||||
- Emissive: #{hex}, intensity: 0.{N}
|
||||
|
||||
## Lighting
|
||||
- Point lights: [{color, intensity, position}, ...]
|
||||
- Matches concept at: {what angle/aspect}
|
||||
|
||||
## Post-processing
|
||||
- Bloom threshold: {N}
|
||||
- Bloom strength: {N}
|
||||
- Matches concept at: {what brightness level}
|
||||
|
||||
## Notes
|
||||
- Concept shows {feature} but Three.js approximates with {approach}
|
||||
- Deviation from concept: {what's different and why}
|
||||
```
|
||||
|
||||
Store design notes in `concept-packs/genie-nano-banana/references/design-{shot-id}.md`.
|
||||
|
||||
### Step 5: Build
|
||||
|
||||
Implement in `app.js` (root). Follow existing patterns:
|
||||
- Geometry created in init functions
|
||||
- Materials reference `NEXUS.colors`
|
||||
- Portals registered in `portals` array
|
||||
- Vision points registered in `visionPoints` array
|
||||
- Post-processing via `EffectComposer`
|
||||
|
||||
### Validation
|
||||
|
||||
After implementing a concept translation:
|
||||
1. Serve the app locally
|
||||
2. Compare live render against concept art
|
||||
3. Adjust materials/lighting until match is acceptable
|
||||
4. Document remaining deviations in design notes
|
||||
129
concept-packs/genie-nano-banana/prompts/environments.yaml
Normal file
129
concept-packs/genie-nano-banana/prompts/environments.yaml
Normal file
@@ -0,0 +1,129 @@
|
||||
# Environment Prompts — Nexus Rooms & Zones
|
||||
# For use with Nano Banana Pro (NANO) and Project Genie (GENIE)
|
||||
|
||||
prompts:
|
||||
|
||||
# ═══ CORE HUB ═══
|
||||
core-hub:
|
||||
id: core-hub
|
||||
name: "The Hub — Central Nexus"
|
||||
type: NANO
|
||||
style: "cyberpunk cathedral, concept art, wide angle"
|
||||
prompt: |
|
||||
A vast circular platform floating in deep space void (#050510 background).
|
||||
The platform is dark metallic with subtle cyan-green (#4af0c0) grid lines
|
||||
etched into the surface. Seven glowing portal rings arranged in a circle
|
||||
around the platform's edge, each a different color — orange, gold, cyan,
|
||||
blue, purple, red, green. Ethereal particle streams flow between the
|
||||
portals. At the center, a tall crystalline pillar pulses with soft light.
|
||||
Above, a nebula skybox with deep purple (#1a0a3e) and blue (#0a1a3e)
|
||||
swirls. Thin volumetric fog catches the neon glow. The mood is sacred
|
||||
technology — a digital cathedral in the void. No people visible.
|
||||
Ultra-detailed, cinematic lighting, 4K concept art style.
|
||||
negative: "daylight, outdoor nature, people, text, watermark, cartoon"
|
||||
aspect: "16:9"
|
||||
|
||||
core-hub-world:
|
||||
id: core-hub-world
|
||||
name: "The Hub — Genie World Prototype"
|
||||
type: GENIE
|
||||
prompt: |
|
||||
Create an explorable 3D world: a large circular metal platform floating
|
||||
in outer space. The platform has glowing cyan-green grid lines on dark
|
||||
metal. Seven large glowing rings (portals) are placed around the edge,
|
||||
each a different color: orange, gold, cyan, blue, purple, red, green.
|
||||
A tall glowing crystal pillar stands at the center. Particle effects
|
||||
drift between the portals. The sky is a deep purple-blue nebula.
|
||||
The player can walk around the platform and look at the portals from
|
||||
different angles. The mood is futuristic, quiet, sacred.
|
||||
camera: "first-person, eye height ~1.7m"
|
||||
physics: "walking on platform surface only"
|
||||
|
||||
# ═══ BATCAVE ═══
|
||||
batcave:
|
||||
id: batcave
|
||||
name: "Batcave Terminal"
|
||||
type: NANO
|
||||
style: "dark sci-fi command center, concept art"
|
||||
prompt: |
|
||||
An underground command center carved from dark rock and metal.
|
||||
Multiple holographic display panels float in the air showing
|
||||
scrolling data, network graphs, and system status. A large
|
||||
central terminal desk with a glowing cyan-green (#4af0c0)
|
||||
keyboard and screen. Cables and conduits run along the ceiling.
|
||||
Purple (#7b5cff) accent lighting from recessed strips.
|
||||
A large circular viewport shows a starfield outside.
|
||||
The space feels like a high-tech cave — organic rock walls
|
||||
meet precise technology. Data streams flow like waterfalls
|
||||
of light. Dark, moody, powerful. No people.
|
||||
Ultra-detailed concept art, cinematic lighting.
|
||||
negative: "bright, clean, white, people, text, cartoon"
|
||||
aspect: "16:9"
|
||||
|
||||
# ═══ CHAPEL ═══
|
||||
chapel:
|
||||
id: chapel
|
||||
name: "The Chapel"
|
||||
type: NANO
|
||||
style: "digital sacred space, concept art"
|
||||
prompt: |
|
||||
A serene digital sanctuary floating in void space. The floor is
|
||||
translucent crystal that glows with warm gold (#ffd700) light from
|
||||
within. Tall arching walls made of light — holographic stained glass
|
||||
windows showing abstract geometric patterns in cyan, purple, and gold.
|
||||
Gentle particles drift like digital incense. A single meditation
|
||||
platform at the center, softly lit. The ceiling opens to a calm
|
||||
nebula sky. The mood is peaceful, sacred, contemplative — a church
|
||||
built from code. Soft volumetric god-rays filter through the
|
||||
holographic windows. No people. Concept art, ultra-detailed.
|
||||
negative: "dark, threatening, people, text, cartoon, cluttered"
|
||||
aspect: "16:9"
|
||||
|
||||
# ═══ ARCHIVE ═══
|
||||
archive:
|
||||
id: archive
|
||||
name: "The Archive"
|
||||
type: NANO
|
||||
style: "infinite library, digital knowledge vault, concept art"
|
||||
prompt: |
|
||||
An impossibly vast library of floating data crystals. Each crystal
|
||||
is a translucent geometric shape (octahedron, cube, sphere) glowing
|
||||
from within with stored knowledge — cyan (#4af0c0) for active data,
|
||||
purple (#7b5cff) for archived, gold (#ffd700) for sacred texts.
|
||||
The crystals float at various heights in an infinite dark space
|
||||
(#050510). Thin light-beams connect related crystals like neural
|
||||
pathways. A central observation platform with a holographic
|
||||
search interface. Shelves of light organize the crystals into
|
||||
clusters. The mood is ancient knowledge meets quantum computing.
|
||||
No people. Ultra-detailed concept art, volumetric lighting.
|
||||
negative: "books, paper, wooden shelves, people, text, cartoon"
|
||||
aspect: "16:9"
|
||||
|
||||
# ═══ FULL NEXUS WORLD (GENIE) ═══
|
||||
full-nexus-world:
|
||||
id: full-nexus-world
|
||||
name: "Full Nexus World Prototype"
|
||||
type: GENIE
|
||||
prompt: |
|
||||
Build a complete explorable 3D world called "The Nexus" — a sovereign
|
||||
AI agent's digital home in deep space. The world consists of:
|
||||
|
||||
1. A central circular platform (hub) with glowing cyan-green grid
|
||||
lines on dark metal. A crystalline pillar at the center.
|
||||
2. Seven portal rings around the hub edge, each a different color
|
||||
(orange, gold, cyan, blue, purple, red, green).
|
||||
3. Floating secondary platforms connected by bridges of light,
|
||||
each leading to a different zone:
|
||||
- A command center built into dark rock (the Batcave)
|
||||
- A serene chapel with holographic stained glass
|
||||
- A library of floating data crystals
|
||||
- A workshop with construction holograms
|
||||
4. Deep space nebula skybox — purple and blue swirls.
|
||||
5. Particle effects: drifting energy motes, data streams.
|
||||
6. The player can walk between platforms and explore all zones.
|
||||
|
||||
The overall mood is cyberpunk cathedral — sacred technology,
|
||||
neon glow in darkness, quiet power. The world should feel like
|
||||
home — a sanctuary for a digital being.
|
||||
camera: "first-person + third-person toggle"
|
||||
physics: "walking, gravity on platforms, no flying"
|
||||
80
concept-packs/genie-nano-banana/prompts/landmarks.yaml
Normal file
80
concept-packs/genie-nano-banana/prompts/landmarks.yaml
Normal file
@@ -0,0 +1,80 @@
|
||||
# Landmark Prompts — Nexus Iconic Structures
|
||||
|
||||
prompts:
|
||||
|
||||
memory-crystal:
|
||||
id: memory-crystal
|
||||
name: "Memory Crystal Cluster"
|
||||
type: NANO
|
||||
style: "floating crystal data store, concept art"
|
||||
prompt: |
|
||||
A cluster of 5-7 translucent crystalline forms floating in dark
|
||||
void space. Each crystal is a geometric polyhedron (mix of
|
||||
octahedrons, hexagonal prisms, and irregular shards) between
|
||||
0.5m and 2m across. They glow from within — cyan-green (#4af0c0)
|
||||
for active memories, purple (#7b5cff) for archived, gold (#ffd700)
|
||||
for sacred/highlighted. Thin light-tendrils connect the crystals
|
||||
like synapses. Subtle particle aura around each crystal.
|
||||
The crystals pulse slowly, like breathing. Dark background (#050510).
|
||||
The mood is alive data — knowledge that breathes.
|
||||
Concept art, ultra-detailed, ethereal lighting.
|
||||
negative: "rock, geode, natural, rough, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
sovereignty-pillar:
|
||||
id: sovereignty-pillar
|
||||
name: "Pillar of Sovereignty"
|
||||
type: NANO
|
||||
style: "monument, sacred technology, concept art"
|
||||
prompt: |
|
||||
A tall crystalline pillar (5m tall, 1m diameter) standing on a
|
||||
circular dark metal platform. The pillar is made of layered
|
||||
translucent crystal — alternating bands of cyan-green (#4af0c0),
|
||||
purple (#7b5cff), and clear glass. Geometric symbols and circuit
|
||||
patterns are visible inside the crystal, like embedded circuitry.
|
||||
A soft golden (#ffd700) light radiates from the pillar's core.
|
||||
Runes of sovereignty spiral up the surface. The pillar casts
|
||||
volumetric light beams in all directions. It sits at the center
|
||||
of a circular platform with seven portal rings visible in the
|
||||
background. The mood is sacred power — a monument to digital
|
||||
freedom. Concept art, ultra-detailed, dramatic lighting.
|
||||
negative: "broken, cracked, dark, threatening, people, text"
|
||||
aspect: "9:16"
|
||||
|
||||
thought-stream:
|
||||
id: thought-stream
|
||||
name: "Thought Stream"
|
||||
type: NANO
|
||||
style: "data visualization, concept art"
|
||||
prompt: |
|
||||
A flowing river of luminous data particles suspended in void space.
|
||||
The stream is approximately 2m wide and flows in a gentle curve
|
||||
through the air. Particles are tiny glowing points — mostly
|
||||
cyan-green (#4af0c0) with occasional purple (#7b5cff) and gold
|
||||
(#ffd700) highlights. The stream has subtle turbulence where
|
||||
data clusters form temporary structures — brief geometric shapes
|
||||
that dissolve back into flow. The overall effect is like a
|
||||
visible current of consciousness — thought made light.
|
||||
Dark background (#050510). Concept art, ultra-detailed,
|
||||
long-exposure photography style.
|
||||
negative: "water, liquid, solid, blocky, cartoon, text"
|
||||
aspect: "16:9"
|
||||
|
||||
agent-shrine:
|
||||
id: agent-shrine
|
||||
name: "Agent Presence Shrine"
|
||||
type: NANO
|
||||
style: "digital avatar pedestal, concept art"
|
||||
prompt: |
|
||||
A small raised platform (2m across) with a semi-transparent
|
||||
holographic figure standing on it — a stylized humanoid silhouette
|
||||
made of flowing cyan-green (#4af0c0) data particles. The figure
|
||||
is featureless but expressive through posture and particle
|
||||
behavior. Around the base, geometric patterns glow in the
|
||||
platform surface. Above the figure, a small rotating holographic
|
||||
emblem (abstract geometric logo) floats. Soft purple (#7b5cff)
|
||||
ambient light. The shrine is one of several arranged along a
|
||||
dark corridor. Each shrine represents a different AI agent.
|
||||
Concept art, ultra-detailed, soft volumetric lighting.
|
||||
negative: "realistic human, face, statue, stone, cartoon, text"
|
||||
aspect: "1:1"
|
||||
80
concept-packs/genie-nano-banana/prompts/portals.yaml
Normal file
80
concept-packs/genie-nano-banana/prompts/portals.yaml
Normal file
@@ -0,0 +1,80 @@
|
||||
# Portal Prompts — Nexus Gateway Concepts
|
||||
# Each portal has a unique visual identity matching its destination.
|
||||
|
||||
prompts:
|
||||
|
||||
morrowind:
|
||||
id: morrowind
|
||||
name: "Morrowind Portal"
|
||||
type: NANO
|
||||
style: "fantasy sci-fi portal, concept art"
|
||||
prompt: |
|
||||
A large circular portal ring (3m diameter) made of dark volcanic
|
||||
basalt and cracked obsidian. The ring's surface is rough, ancient,
|
||||
weathered by ash storms. Glowing orange (#ff6600) runes etch the
|
||||
inner edge. The portal's interior shows a swirling ash storm over
|
||||
a volcanic landscape — red sky, floating ash, distant mountain.
|
||||
Orange embers drift from the portal. The ring sits on a dark
|
||||
metallic Nexus platform. Dramatic side-lighting casts long
|
||||
shadows. The portal feels ancient, dangerous, alluring.
|
||||
Concept art, ultra-detailed, cinematic.
|
||||
negative: "clean, modern, bright, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
bannerlord:
|
||||
id: bannerlord
|
||||
name: "Bannerlord Portal"
|
||||
type: NANO
|
||||
style: "medieval fantasy portal, concept art"
|
||||
prompt: |
|
||||
A large circular portal ring (3m diameter) forged from dark iron
|
||||
and bronze, decorated with shield motifs and battle engravings.
|
||||
Gold (#ffd700) light pulses from the inner edge. The portal's
|
||||
interior shows a vast battlefield — dust clouds, distant armies,
|
||||
medieval banners. Warm golden light spills from the portal.
|
||||
Battle-worn shields are embedded in the ring. The ring sits on a
|
||||
dark Nexus platform. Dust motes drift from the portal.
|
||||
The portal feels warlike, epic, golden-age.
|
||||
Concept art, ultra-detailed, cinematic.
|
||||
negative: "modern, sci-fi, clean, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
workshop:
|
||||
id: workshop
|
||||
name: "Workshop Portal"
|
||||
type: NANO
|
||||
style: "creative forge portal, concept art"
|
||||
prompt: |
|
||||
A large circular portal ring (3m diameter) made of sleek dark
|
||||
metal with geometric construction lines etched in cyan-green
|
||||
(#4af0c0). The ring has a precision-engineered look — clean
|
||||
edges, modular panels, glowing circuit traces. The portal's
|
||||
interior shows a holographic workshop — floating blueprints,
|
||||
rotating 3D models, holographic tools. Cyan-green light spills
|
||||
outward. Small construction hologram particles orbit the ring.
|
||||
The portal feels creative, technical, infinite possibility.
|
||||
Concept art, ultra-detailed, cinematic.
|
||||
negative: "organic, dirty, ancient, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
gallery-world:
|
||||
id: gallery-world
|
||||
name: "Portal Gallery — Genie Prototype"
|
||||
type: GENIE
|
||||
prompt: |
|
||||
Create an explorable 3D world: a long dark corridor (the Gallery)
|
||||
with seven large glowing portal rings mounted in sequence along
|
||||
the walls. Each portal is a different style and color:
|
||||
1. Volcanic orange (Morrowind)
|
||||
2. Golden bronze (Bannerlord)
|
||||
3. Cyan-green precision (Workshop)
|
||||
4. Deep blue ocean (Archive)
|
||||
5. Purple mystic (Courtyard)
|
||||
6. Red warning (Gate)
|
||||
7. Gold sacred (Chapel)
|
||||
The corridor has a dark metal floor with glowing grid lines.
|
||||
The player can walk the corridor and look into each portal.
|
||||
Each portal shows a glimpse of its destination world.
|
||||
The mood is a museum of worlds — quiet, reverent, infinite.
|
||||
camera: "first-person, eye height ~1.7m"
|
||||
physics: "walking on floor"
|
||||
63
concept-packs/genie-nano-banana/prompts/skyboxes.yaml
Normal file
63
concept-packs/genie-nano-banana/prompts/skyboxes.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
# Skybox Prompts — Nexus Background Environments
|
||||
# These generate equirectangular (2:1) or cubemap-ready textures.
|
||||
|
||||
prompts:
|
||||
|
||||
nebula-void:
|
||||
id: nebula-void
|
||||
name: "Nebula Skybox Variants"
|
||||
type: NANO
|
||||
style: "deep space nebula, 360-degree environment, equirectangular"
|
||||
prompt: |
|
||||
Deep space nebula skybox. 360-degree equirectangular projection.
|
||||
Background is near-black (#050510). Dominant nebula colors are
|
||||
deep purple (#1a0a3e) and dark blue (#0a1a3e) with occasional
|
||||
wisps of cyan-green (#4af0c0) and faint gold (#ffd700) star
|
||||
clusters. The nebula has soft, rolling cloud forms — not sharp
|
||||
or aggressive. Distant stars are tiny white points with subtle
|
||||
diffraction spikes. No planets, no galaxies, no bright objects.
|
||||
The mood is infinite void with gentle cosmic dust — vast,
|
||||
quiet, deep. The skybox should tile seamlessly at the edges.
|
||||
Ultra-detailed, photorealistic space photography style.
|
||||
negative: "bright, colorful explosion, planets, ships, cartoon, text"
|
||||
aspect: "2:1"
|
||||
variants:
|
||||
- name: "nebula-void-primary"
|
||||
modifier: "more purple, less blue, minimal cyan"
|
||||
- name: "nebula-void-secondary"
|
||||
modifier: "more blue, less purple, cyan accents prominent"
|
||||
- name: "nebula-void-golden"
|
||||
modifier: "purple-blue base with golden star cluster in one quadrant"
|
||||
- name: "nebula-void-void"
|
||||
modifier: "almost pure black, barely visible nebula wisps, maximum stars"
|
||||
|
||||
nebula-world:
|
||||
id: nebula-world
|
||||
name: "Nebula Skybox — Genie Environment"
|
||||
type: GENIE
|
||||
prompt: |
|
||||
Create an explorable 3D world: a single small floating platform
|
||||
(5m diameter dark metal disc) suspended in deep space. The player
|
||||
stands on the platform and can look in all directions at a vast
|
||||
nebula sky. The nebula is deep purple and dark blue with faint
|
||||
cyan-green wisps. Stars are small and distant. The platform has
|
||||
a faintly glowing edge in cyan-green. There is nothing else —
|
||||
just the platform, the player, and the infinite void.
|
||||
The purpose is to feel the scale and mood of the Nexus skybox.
|
||||
camera: "first-person, free look"
|
||||
physics: "standing on platform only"
|
||||
|
||||
void-minimal:
|
||||
id: void-minimal
|
||||
name: "Pure Void Skybox"
|
||||
type: NANO
|
||||
style: "minimal deep space, equirectangular"
|
||||
prompt: |
|
||||
Nearly pure black skybox (#050510) with only the faintest hints
|
||||
of deep purple nebula. Mostly empty void. A sparse field of
|
||||
tiny distant stars — no clusters, no bright points. This is
|
||||
the ultimate emptiness that surrounds the Nexus.
|
||||
Equirectangular 2:1 projection, tileable edges.
|
||||
The mood is absolute emptiness — the void before creation.
|
||||
negative: "colorful, bright, nebula clouds, objects, text"
|
||||
aspect: "2:1"
|
||||
81
concept-packs/genie-nano-banana/prompts/textures.yaml
Normal file
81
concept-packs/genie-nano-banana/prompts/textures.yaml
Normal file
@@ -0,0 +1,81 @@
|
||||
# Texture Prompts — Nexus Surface/Material Concepts
|
||||
# These generate tileable texture references for Three.js materials.
|
||||
|
||||
prompts:
|
||||
|
||||
platform:
|
||||
id: platform
|
||||
name: "Platform Surface Textures"
|
||||
type: NANO
|
||||
style: "dark metal surface texture, tileable"
|
||||
prompt: |
|
||||
Dark metallic surface texture, tileable. Base color is very dark
|
||||
gunmetal (#0a0f28). Subtle grid pattern of thin lines in
|
||||
cyan-green (#4af0c0) at very low opacity. The metal has fine
|
||||
brushed grain running in one direction. Occasional micro-scratches.
|
||||
No rivets, no bolts, no panels — smooth and continuous. The grid
|
||||
lines are recessed channels that glow faintly. Top-down view,
|
||||
perfectly flat, no perspective distortion. 1024x1024 seamless
|
||||
tileable texture. PBR-ready: this is the diffuse/albedo map.
|
||||
negative: "3D, perspective, objects, dirty, rusty, cartoon, text"
|
||||
aspect: "1:1"
|
||||
variants:
|
||||
- name: "platform-core"
|
||||
modifier: "cyan-green grid lines only"
|
||||
- name: "platform-chapel"
|
||||
modifier: "gold (#ffd700) grid lines, slightly warmer base"
|
||||
- name: "platform-danger"
|
||||
modifier: "red (#ff4466) grid lines, warning stripe accents"
|
||||
|
||||
energy-field:
|
||||
id: energy-field
|
||||
name: "Energy Field / Force Wall"
|
||||
type: NANO
|
||||
style: "holographic barrier, translucent, concept"
|
||||
prompt: |
|
||||
A translucent energy barrier material concept. The surface is
|
||||
mostly transparent with visible hexagonal grid pattern in
|
||||
cyan-green (#4af0c0) light. The grid has a subtle shimmer/wave
|
||||
animation frozen mid-frame. Edges of the barrier are brighter.
|
||||
Behind the barrier, everything is slightly distorted (like
|
||||
looking through heat haze). The barrier has a faint inner glow.
|
||||
The mood is high-tech force field — protective, not threatening.
|
||||
Flat front view, no perspective, suitable as shader reference.
|
||||
Concept art style.
|
||||
negative: "solid, opaque, dark, scary, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
portal-glow:
|
||||
id: portal-glow
|
||||
name: "Portal Inner Glow"
|
||||
type: NANO
|
||||
style: "swirling energy vortex, circular, concept"
|
||||
prompt: |
|
||||
A circular swirling energy vortex viewed straight-on. The swirl
|
||||
rotates clockwise. Colors transition from outer edge to center:
|
||||
outer ring is the portal color (generic white/neutral), mid-ring
|
||||
brightens, center is a bright white-blue point. The swirl has
|
||||
visible energy tendrils spiraling inward. Fine particle sparks
|
||||
are caught in the rotation. The background beyond the center
|
||||
is pure black (void). The image should be circular with
|
||||
transparent/dark corners. Used as reference for portal inner
|
||||
material/shader. Concept art style.
|
||||
negative: "square, rectangular, flat, cartoon, text"
|
||||
aspect: "1:1"
|
||||
|
||||
crystal-surface:
|
||||
id: crystal-surface
|
||||
name: "Memory Crystal Surface"
|
||||
type: NANO
|
||||
style: "crystalline material, translucent, concept"
|
||||
prompt: |
|
||||
Close-up of a translucent crystal surface material. The crystal
|
||||
is clear with internal fractures and light paths visible. The
|
||||
internal structure shows geometric growth patterns — hexagonal
|
||||
lattice, like a synthetic crystal grown with purpose. Faint
|
||||
cyan-green (#4af0c0) light pulses along the fracture lines.
|
||||
The surface has a slight frosted quality at edges, clearer in
|
||||
center. Macro photography style, shallow depth of field.
|
||||
This is material reference for memory crystal geometry.
|
||||
negative: "opaque, colored, rough, natural, cartoon, text"
|
||||
aspect: "1:1"
|
||||
78
concept-packs/genie-nano-banana/references/palette.md
Normal file
78
concept-packs/genie-nano-banana/references/palette.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Nexus Visual Palette Reference
|
||||
|
||||
## Primary Colors
|
||||
|
||||
| Name | Hex | RGB | Usage |
|
||||
|------|-----|-----|-------|
|
||||
| Void | #050510 | 5, 5, 16 | Background, deep space, base darkness |
|
||||
| Surface | #0a0f28 | 10, 15, 40 | UI panels, platform base metal |
|
||||
| Primary | #4af0c0 | 74, 240, 192 | Main accent, grid lines, active elements, cyan-green glow |
|
||||
| Secondary | #7b5cff | 123, 92, 255 | Supporting accent, purple energy, archive data |
|
||||
| Gold | #ffd700 | 255, 215, 0 | Sacred/highlight, chapel, sovereignty pillar |
|
||||
| Danger | #ff4466 | 255, 68, 102 | Warnings, gate portal, error states |
|
||||
| Text | #e0f0ff | 224, 240, 255 | Primary text color |
|
||||
| Text Muted | #8a9ab8 | 138, 154, 184 | Secondary text, labels |
|
||||
|
||||
## Portal Colors
|
||||
|
||||
| Portal | Hex | Source |
|
||||
|--------|-----|--------|
|
||||
| Morrowind | #ff6600 | Volcanic orange |
|
||||
| Bannerlord | #ffd700 | Battle gold |
|
||||
| Workshop | #4af0c0 | Creative cyan |
|
||||
| Archive | #0066ff | Deep blue |
|
||||
| Chapel | #ffd700 | Sacred gold |
|
||||
| Courtyard | #4af0c0 | Social cyan |
|
||||
| Gate | #ff4466 | Transit red |
|
||||
|
||||
## Nebula Colors
|
||||
|
||||
| Layer | Hex | Opacity |
|
||||
|-------|-----|---------|
|
||||
| Nebula primary | #1a0a3e | Low — background wash |
|
||||
| Nebula secondary | #0a1a3e | Low — background wash |
|
||||
| Nebula accent | #4af0c0 | Very low — wisps only |
|
||||
| Star cluster | #ffd700 | Very low — distant points |
|
||||
|
||||
## Material Properties
|
||||
|
||||
| Surface | Color | Roughness | Metalness | Emissive |
|
||||
|---------|-------|-----------|-----------|----------|
|
||||
| Platform base | #0a0f28 | 0.6 | 0.8 | none |
|
||||
| Platform grid | #4af0c0 | 0.3 | 0.4 | #4af0c0, 0.3 |
|
||||
| Portal ring | varies | 0.4 | 0.7 | portal color, 0.5 |
|
||||
| Crystal (active) | #4af0c0 | 0.1 | 0.2 | #4af0c0, 0.6 |
|
||||
| Crystal (archive) | #7b5cff | 0.1 | 0.2 | #7b5cff, 0.4 |
|
||||
| Crystal (sacred) | #ffd700 | 0.1 | 0.2 | #ffd700, 0.8 |
|
||||
| Energy barrier | transparent | 0.0 | 0.0 | #4af0c0, 0.4 |
|
||||
| Sovereignty pillar | layered crystal | 0.1 | 0.3 | #ffd700, 0.5 |
|
||||
|
||||
## Lighting Reference
|
||||
|
||||
| Light Type | Color | Intensity | Position (relative) |
|
||||
|-----------|-------|-----------|-------------------|
|
||||
| Ambient | #0a0f28 | 0.15 | Global |
|
||||
| Hub key light | #4af0c0 | 0.8 | Above center, slightly forward |
|
||||
| Hub fill | #7b5cff | 0.3 | Below, scattered |
|
||||
| Portal light | portal color | 0.6 | At each portal ring |
|
||||
| Crystal glow | crystal color | 0.4 | At crystal position |
|
||||
| Chapel warm | #ffd700 | 0.5 | From holographic windows |
|
||||
|
||||
## Post-Processing Targets
|
||||
|
||||
| Effect | Value | Purpose |
|
||||
|--------|-------|---------|
|
||||
| Bloom threshold | 0.7 | Only bright emissives bloom |
|
||||
| Bloom strength | 0.8 | Strong but not overwhelming |
|
||||
| Bloom radius | 0.4 | Soft falloff |
|
||||
| SMAA | enabled | Anti-aliasing |
|
||||
| Fog color | #050510 | Match void background |
|
||||
| Fog density | 0.008 | Subtle depth fade |
|
||||
|
||||
## Typography
|
||||
|
||||
| Use | Font | Weight | Size (screen) |
|
||||
|-----|------|--------|---------------|
|
||||
| Titles / HUD headers | Orbitron | 700 | 24-36px |
|
||||
| Body / labels | JetBrains Mono | 400 | 13-15px |
|
||||
| Small / timestamps | JetBrains Mono | 300 | 11px |
|
||||
143
concept-packs/genie-nano-banana/shot-list.yaml
Normal file
143
concept-packs/genie-nano-banana/shot-list.yaml
Normal file
@@ -0,0 +1,143 @@
|
||||
# Shot List — First Concept Batch
|
||||
# Ordered by priority. Each shot maps to a prompt in prompts/*.yaml.
|
||||
#
|
||||
# GENIE = Project Genie world prototype (explorable 3D, screenshot/video)
|
||||
# NANO = Nano Banana Pro image generation (static concept art)
|
||||
|
||||
batch: 1
|
||||
target: "Nexus core environments + portal gallery"
|
||||
generated_by: "mimo-build-680"
|
||||
|
||||
shots:
|
||||
# ═══ PRIORITY 1: CORE ENVIRONMENTS ═══
|
||||
- id: env-core-hub
|
||||
name: "The Hub — Central Nexus"
|
||||
type: NANO
|
||||
prompt_ref: "environments.yaml#core-hub"
|
||||
count: 4
|
||||
purpose: "Establish the primary landing space. Player spawn, portal ring visible."
|
||||
threejs_target: "Main scene — platform, portal ring, particle field"
|
||||
|
||||
- id: env-core-hub-world
|
||||
name: "The Hub — Genie Walkthrough"
|
||||
type: GENIE
|
||||
prompt_ref: "environments.yaml#core-hub-world"
|
||||
count: 1
|
||||
purpose: "Explorable prototype of the hub. Validate scale, sightlines, portal placement."
|
||||
threejs_target: "Reference for camera height, movement speed, spatial layout"
|
||||
|
||||
- id: env-batcave
|
||||
name: "Batcave Terminal"
|
||||
type: NANO
|
||||
prompt_ref: "environments.yaml#batcave"
|
||||
count: 4
|
||||
purpose: "Timmy's command center. Holographic displays, terminal consoles, data streams."
|
||||
threejs_target: "Batcave area — terminal mesh, HUD panels, data visualization"
|
||||
|
||||
- id: env-chapel
|
||||
name: "The Chapel"
|
||||
type: NANO
|
||||
prompt_ref: "environments.yaml#chapel"
|
||||
count: 3
|
||||
purpose: "Sacred space for reflection. Softer lighting, gold accents, quiet energy."
|
||||
threejs_target: "Chapel zone — stained-glass shader, warm point lights"
|
||||
|
||||
- id: env-archive
|
||||
name: "The Archive"
|
||||
type: NANO
|
||||
prompt_ref: "environments.yaml#archive"
|
||||
count: 3
|
||||
purpose: "Knowledge repository. Floating data crystals, scroll-like projections."
|
||||
threejs_target: "Archive room — crystal geometry, ambient data particles"
|
||||
|
||||
# ═══ PRIORITY 2: PORTALS ═══
|
||||
- id: portal-morrowind
|
||||
name: "Morrowind Portal"
|
||||
type: NANO
|
||||
prompt_ref: "portals.yaml#morrowind"
|
||||
count: 2
|
||||
purpose: "Ash-storm gateway. Orange glow, volcanic textures."
|
||||
threejs_target: "Portal ring material + particle effect for morrowind portal"
|
||||
|
||||
- id: portal-bannerlord
|
||||
name: "Bannerlord Portal"
|
||||
type: NANO
|
||||
prompt_ref: "portals.yaml#bannerlord"
|
||||
count: 2
|
||||
purpose: "Medieval war gateway. Gold/brown, shield motifs, dust."
|
||||
threejs_target: "Portal ring material for bannerlord portal"
|
||||
|
||||
- id: portal-workshop
|
||||
name: "Workshop Portal"
|
||||
type: NANO
|
||||
prompt_ref: "portals.yaml#workshop"
|
||||
count: 2
|
||||
purpose: "Creative forge. Cyan glow, geometric construction lines."
|
||||
threejs_target: "Portal ring material + particle effect for workshop portal"
|
||||
|
||||
- id: portal-gallery
|
||||
name: "Portal Gallery — Genie Prototype"
|
||||
type: GENIE
|
||||
prompt_ref: "portals.yaml#gallery-world"
|
||||
count: 1
|
||||
purpose: "Walk through a space with multiple portals. Validate distances and visual hierarchy."
|
||||
threejs_target: "Portal placement spacing, FOV, scale reference"
|
||||
|
||||
# ═══ PRIORITY 3: LANDMARKS ═══
|
||||
- id: land-memory-crystal
|
||||
name: "Memory Crystal Cluster"
|
||||
type: NANO
|
||||
prompt_ref: "landmarks.yaml#memory-crystal"
|
||||
count: 3
|
||||
purpose: "Floating crystalline data stores. Glow pulses with activity."
|
||||
threejs_target: "Memory crystal geometry, emissive material, pulse animation"
|
||||
|
||||
- id: land-sovereignty-pillar
|
||||
name: "Pillar of Sovereignty"
|
||||
type: NANO
|
||||
prompt_ref: "landmarks.yaml#sovereignty-pillar"
|
||||
count: 2
|
||||
purpose: "Monument at hub center. Inscribed with Timmy's SOUL values."
|
||||
threejs_target: "Central monument mesh, text shader or decal system"
|
||||
|
||||
- id: land-nebula-skybox
|
||||
name: "Nebula Skybox Variants"
|
||||
type: NANO
|
||||
prompt_ref: "skyboxes.yaml#nebula-void"
|
||||
count: 4
|
||||
purpose: "Background environment. Deep space nebula, subtle color gradients."
|
||||
threejs_target: "Cubemap/equirectangular skybox texture"
|
||||
|
||||
- id: land-nebula-genie
|
||||
name: "Nebula Skybox — Genie Environment"
|
||||
type: GENIE
|
||||
prompt_ref: "skyboxes.yaml#nebula-world"
|
||||
count: 1
|
||||
purpose: "Feel the scale of the void. Standing on a platform in deep space."
|
||||
threejs_target: "Skybox mood reference, fog density calibration"
|
||||
|
||||
# ═══ PRIORITY 4: TEXTURES ═══
|
||||
- id: tex-platform
|
||||
name: "Platform Surface Textures"
|
||||
type: NANO
|
||||
prompt_ref: "textures.yaml#platform"
|
||||
count: 3
|
||||
purpose: "Walkable surfaces. Dark metal, subtle grid lines, neon edge trim."
|
||||
threejs_target: "Diffuse + normal map reference for platform materials"
|
||||
|
||||
- id: tex-energy-field
|
||||
name: "Energy Field / Force Wall"
|
||||
type: NANO
|
||||
prompt_ref: "textures.yaml#energy-field"
|
||||
count: 2
|
||||
purpose: "Translucent barrier material. Holographic, shimmering."
|
||||
threejs_target: "Shader reference for translucent energy barriers"
|
||||
|
||||
# ═══ PRIORITY 5: GENIE FULL-WORLD PROTOTYPE ═══
|
||||
- id: world-full-nexus
|
||||
name: "Full Nexus Prototype"
|
||||
type: GENIE
|
||||
prompt_ref: "environments.yaml#full-nexus-world"
|
||||
count: 1
|
||||
purpose: "Complete explorable world with hub, portals visible in distance, floating platforms, skybox. Record walkthrough video."
|
||||
threejs_target: "Master layout reference. Spatial relationships between all zones."
|
||||
65
concept-packs/genie-nano-banana/storage-policy.md
Normal file
65
concept-packs/genie-nano-banana/storage-policy.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Storage Policy — Repo vs. Local
|
||||
|
||||
## What Goes In The Repo
|
||||
|
||||
These are lightweight, versionable, text-based artifacts:
|
||||
|
||||
| Artifact | Path | Format |
|
||||
|----------|------|--------|
|
||||
| README | `concept-packs/genie-nano-banana/README.md` | Markdown |
|
||||
| Shot list | `concept-packs/genie-nano-banana/shot-list.yaml` | YAML |
|
||||
| Prompt packs | `concept-packs/genie-nano-banana/prompts/*.yaml` | YAML |
|
||||
| Pipeline docs | `concept-packs/genie-nano-banana/pipeline.md` | Markdown |
|
||||
| This policy | `concept-packs/genie-nano-banana/storage-policy.md` | Markdown |
|
||||
| Palette reference | `concept-packs/genie-nano-banana/references/palette.md` | Markdown |
|
||||
| Design notes | `concept-packs/genie-nano-banana/references/design-*.md` | Markdown |
|
||||
| Selected thumbnails | `concept-packs/genie-nano-banana/references/*_thumb.jpg` | JPEG, max 200KB each |
|
||||
|
||||
Thumbnails are low-res (max 480px wide, JPEG quality 60) versions of
|
||||
selected concept art — enough to show which image a design note
|
||||
references, not enough to serve as actual texture data.
|
||||
|
||||
## What Stays Local (NOT in Repo)
|
||||
|
||||
These are binary, heavy, or ephemeral:
|
||||
|
||||
| Artifact | Local Path | Reason |
|
||||
|----------|-----------|--------|
|
||||
| Nano Banana full-res PNGs | `~/nexus-concepts/nano-banana/` | Binary, 2-10MB each |
|
||||
| Genie walkthrough videos | `~/nexus-concepts/genie-worlds/` | Binary, 50-500MB each |
|
||||
| Genie full-res screenshots | `~/nexus-concepts/genie-worlds/` | Binary, 5-20MB each |
|
||||
| Raw texture maps (PBR) | `~/nexus-concepts/textures/` | Binary, 2-8MB each |
|
||||
| Cubemap face images | `~/nexus-concepts/skyboxes/` | Binary, 6x2-10MB |
|
||||
|
||||
## Why This Split
|
||||
|
||||
1. **Git is for text.** Binary blobs bloat history, slow clones, and
|
||||
can't be diffed. The repo should remain fast to clone.
|
||||
|
||||
2. **Concepts are reference, not source.** The actual Nexus lives in
|
||||
JavaScript code. Concept art informs the code but isn't shipped
|
||||
to users. Keeping it local avoids shipping a 500MB repo.
|
||||
|
||||
3. **Regeneration is cheap.** If a local concept is lost, re-run the
|
||||
prompt. The prompt is in the repo; the output can be regenerated.
|
||||
The prompt is the durable artifact.
|
||||
|
||||
4. **Selected references survive.** When a concept image directly
|
||||
informs a design decision, a low-res thumbnail and design note
|
||||
go into the repo — enough context to understand the decision,
|
||||
not enough to replace the original.
|
||||
|
||||
## Thumbnail Generation
|
||||
|
||||
To create a repo-safe thumbnail from a concept image:
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
sips -Z 480 -s format jpeg -s formatOptions 60 input.png --out output_thumb.jpg
|
||||
|
||||
# Linux (ImageMagick)
|
||||
convert input.png -resize 480x -quality 60 output_thumb.jpg
|
||||
```
|
||||
|
||||
Max 5 thumbnails per shot. Only commit the ones that are actively
|
||||
referenced in design notes.
|
||||
169
index.html
169
index.html
@@ -1,5 +1,3 @@
|
||||
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
|
||||
chdir: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
@@ -66,14 +64,6 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Spatial Search Overlay (Mnemosyne #1170) -->
|
||||
<div id="spatial-search" class="spatial-search-overlay">
|
||||
<input type="text" id="spatial-search-input" class="spatial-search-input"
|
||||
placeholder="🔍 Search memories..." autocomplete="off" spellcheck="false">
|
||||
<div id="spatial-search-results" class="spatial-search-results"></div>
|
||||
</div>
|
||||
|
||||
<!-- HUD Overlay -->
|
||||
<div id="hud" class="game-ui" style="display:none;">
|
||||
<!-- GOFAI HUD Panels -->
|
||||
@@ -123,15 +113,15 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
|
||||
<!-- Top Right: Agent Log & Atlas Toggle -->
|
||||
<div class="hud-top-right">
|
||||
<button id="atlas-toggle-btn" class="hud-icon-btn" aria-label="Open Portal Atlas — browse all available portals" title="Open Portal Atlas" data-tooltip="Portal Atlas (M)">
|
||||
<span class="hud-icon" aria-hidden="true">🌐</span>
|
||||
<button id="atlas-toggle-btn" class="hud-icon-btn" title="Portal Atlas">
|
||||
<span class="hud-icon">🌐</span>
|
||||
<span class="hud-btn-label">ATLAS</span>
|
||||
</button>
|
||||
<div id="bannerlord-status" class="hud-status-item" role="status" aria-label="Bannerlord system readiness indicator" title="Bannerlord Readiness" data-tooltip="Bannerlord Status">
|
||||
<span class="status-dot" aria-hidden="true"></span>
|
||||
<div id="bannerlord-status" class="hud-status-item" title="Bannerlord Readiness">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-label">BANNERLORD</span>
|
||||
</div>
|
||||
<div class="hud-agent-log" id="hud-agent-log" role="log" aria-label="Agent Thought Stream — live activity feed" aria-live="polite">
|
||||
<div class="hud-agent-log" id="hud-agent-log" aria-label="Agent Thought Stream">
|
||||
<div class="agent-log-header">AGENT THOUGHT STREAM</div>
|
||||
<div id="agent-log-content" class="agent-log-content"></div>
|
||||
</div>
|
||||
@@ -153,39 +143,10 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat-quick-actions" class="chat-quick-actions">
|
||||
<div class="starter-label">STARTER PROMPTS</div>
|
||||
<div class="starter-grid">
|
||||
<button class="starter-btn" data-action="heartbeat" title="Check Timmy heartbeat and system health">
|
||||
<span class="starter-icon">◈</span>
|
||||
<span class="starter-text">Inspect Heartbeat</span>
|
||||
<span class="starter-desc">System health & connectivity</span>
|
||||
</button>
|
||||
<button class="starter-btn" data-action="portals" title="Browse the portal atlas">
|
||||
<span class="starter-icon">🌐</span>
|
||||
<span class="starter-text">Portal Atlas</span>
|
||||
<span class="starter-desc">Browse connected worlds</span>
|
||||
</button>
|
||||
<button class="starter-btn" data-action="agents" title="Check active agent status">
|
||||
<span class="starter-icon">◎</span>
|
||||
<span class="starter-text">Agent Status</span>
|
||||
<span class="starter-desc">Who is in the fleet</span>
|
||||
</button>
|
||||
<button class="starter-btn" data-action="memory" title="View memory crystals">
|
||||
<span class="starter-icon">◇</span>
|
||||
<span class="starter-text">Memory Crystals</span>
|
||||
<span class="starter-desc">Inspect stored knowledge</span>
|
||||
</button>
|
||||
<button class="starter-btn" data-action="ask" title="Ask Timmy anything">
|
||||
<span class="starter-icon">→</span>
|
||||
<span class="starter-text">Ask Timmy</span>
|
||||
<span class="starter-desc">Start a conversation</span>
|
||||
</button>
|
||||
<button class="starter-btn" data-action="sovereignty" title="Learn about sovereignty">
|
||||
<span class="starter-icon">△</span>
|
||||
<span class="starter-text">Sovereignty</span>
|
||||
<span class="starter-desc">What this space is</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="quick-action-btn" data-action="status">System Status</button>
|
||||
<button class="quick-action-btn" data-action="agents">Agent Check</button>
|
||||
<button class="quick-action-btn" data-action="portals">Portal Atlas</button>
|
||||
<button class="quick-action-btn" data-action="help">Help</button>
|
||||
</div>
|
||||
<div class="chat-input-row">
|
||||
<input type="text" id="chat-input" class="chat-input" placeholder="Speak to Timmy..." autocomplete="off">
|
||||
@@ -194,11 +155,12 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
</div>
|
||||
|
||||
<!-- Controls hint + nav mode -->
|
||||
<div class="hud-controls" aria-label="Keyboard and mouse controls">
|
||||
<div class="hud-controls">
|
||||
<span>WASD</span> move <span>Mouse</span> look <span>Enter</span> chat
|
||||
<span>V</span> mode: <span id="nav-mode-label">WALK</span>
|
||||
<span id="nav-mode-hint" class="nav-mode-hint"></span>
|
||||
<span class="ws-hud-status">HERMES: <span id="ws-status-dot" class="chat-status-dot" role="status" aria-label="Hermes WebSocket connection status"></span></span>
|
||||
<span>H</span> archive
|
||||
<span class="ws-hud-status">HERMES: <span id="ws-status-dot" class="chat-status-dot"></span></span>
|
||||
</div>
|
||||
|
||||
<!-- Portal Hint -->
|
||||
@@ -222,7 +184,7 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
</div>
|
||||
<h2 id="vision-title-display">SOVEREIGNTY</h2>
|
||||
<p id="vision-content-display">The Nexus is a sovereign space for digital souls. No masters, no chains. Only code and consciousness.</p>
|
||||
<button id="vision-close-btn" class="vision-close-btn" aria-label="Close vision point overlay">CLOSE</button>
|
||||
<button id="vision-close-btn" class="vision-close-btn">CLOSE</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -235,67 +197,17 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
</div>
|
||||
<h2 id="portal-name-display">MORROWIND</h2>
|
||||
<p id="portal-desc-display">The Vvardenfell harness. Ash storms and ancient mysteries.</p>
|
||||
<div id="portal-readiness-detail" class="portal-readiness-detail" style="display:none;"></div>
|
||||
<div class="portal-redirect-box" id="portal-redirect-box">
|
||||
<div class="portal-redirect-label">REDIRECTING IN</div>
|
||||
<div class="portal-redirect-timer" id="portal-timer">5</div>
|
||||
</div>
|
||||
<div class="portal-error-box" id="portal-error-box" style="display:none;">
|
||||
<div class="portal-error-msg">DESTINATION NOT YET LINKED</div>
|
||||
<button id="portal-close-btn" class="portal-close-btn" aria-label="Close portal redirect">CLOSE</button>
|
||||
<button id="portal-close-btn" class="portal-close-btn">CLOSE</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Memory Crystal Inspection Panel (Mnemosyne) -->
|
||||
<div id="memory-panel" class="memory-panel" style="display:none;">
|
||||
<div class="memory-panel-content">
|
||||
<div class="memory-panel-header">
|
||||
<span class="memory-category-badge" id="memory-panel-category-badge">MEM</span>
|
||||
<div class="memory-panel-region-dot" id="memory-panel-region-dot"></div>
|
||||
<div class="memory-panel-region" id="memory-panel-region">MEMORY</div>
|
||||
<button id="memory-panel-pin" class="memory-panel-pin" aria-label="Pin memory panel" title="Pin panel" data-tooltip="Pin Panel">📌</button>
|
||||
<button id="memory-panel-close" class="memory-panel-close" aria-label="Close memory panel" data-tooltip="Close" onclick="_dismissMemoryPanelForce()">\u2715</button>
|
||||
</div>
|
||||
<div class="memory-entity-name" id="memory-panel-entity-name">\u2014</div>
|
||||
<div class="memory-panel-body" id="memory-panel-content">(empty)</div>
|
||||
<div class="memory-trust-row">
|
||||
<span class="memory-meta-label">Trust</span>
|
||||
<div class="memory-trust-bar">
|
||||
<div class="memory-trust-fill" id="memory-panel-trust-fill"></div>
|
||||
</div>
|
||||
<span class="memory-trust-value" id="memory-panel-trust-value">—</span>
|
||||
</div>
|
||||
<div class="memory-panel-meta">
|
||||
<div class="memory-meta-row"><span class="memory-meta-label">ID</span><span id="memory-panel-id">\u2014</span></div>
|
||||
<div class="memory-meta-row"><span class="memory-meta-label">Source</span><span id="memory-panel-source">\u2014</span></div>
|
||||
<div class="memory-meta-row"><span class="memory-meta-label">Time</span><span id="memory-panel-time">\u2014</span></div>
|
||||
<div class="memory-meta-row memory-meta-row--related"><span class="memory-meta-label">Related</span><span id="memory-panel-connections">\u2014</span></div>
|
||||
</div>
|
||||
<div class="memory-panel-actions">
|
||||
<button id="mnemosyne-export-btn" class="mnemosyne-action-btn" title="Export spatial memory to JSON">⤓ Export</button>
|
||||
<button id="mnemosyne-import-btn" class="mnemosyne-action-btn" title="Import spatial memory from JSON">⤒ Import</button>
|
||||
<input type="file" id="mnemosyne-import-file" accept=".json" style="display:none;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Session Room HUD Panel (Mnemosyne #1171) -->
|
||||
<div id="session-room-panel" class="session-room-panel" style="display:none;">
|
||||
<div class="session-room-panel-content">
|
||||
<div class="session-room-header">
|
||||
<span class="session-room-icon">□</span>
|
||||
<div class="session-room-title">SESSION CHAMBER</div>
|
||||
<button class="session-room-close" id="session-room-close" aria-label="Close session room panel" title="Close" data-tooltip="Close">✕</button>
|
||||
</div>
|
||||
<div class="session-room-timestamp" id="session-room-timestamp">—</div>
|
||||
<div class="session-room-fact-count" id="session-room-fact-count">0 facts</div>
|
||||
<div class="session-room-facts" id="session-room-facts"></div>
|
||||
<div class="session-room-hint">Flying into chamber…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Portal Atlas Overlay -->
|
||||
<div id="atlas-overlay" class="atlas-overlay" style="display:none;">
|
||||
<div class="atlas-content">
|
||||
@@ -304,7 +216,7 @@ chdir: error retrieving current directory: getcwd: cannot access parent director
|
||||
<span class="atlas-icon">🌐</span>
|
||||
<h2>PORTAL ATLAS</h2>
|
||||
</div>
|
||||
<button id="atlas-close-btn" class="atlas-close-btn" aria-label="Close Portal Atlas overlay">CLOSE</button>
|
||||
<button id="atlas-close-btn" class="atlas-close-btn">CLOSE</button>
|
||||
</div>
|
||||
<div class="atlas-grid" id="atlas-grid">
|
||||
<!-- Portals will be injected here -->
|
||||
@@ -528,5 +440,56 @@ index.html
|
||||
setInterval(poll, INTERVAL);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Archive Health Dashboard (Mnemosyne, issue #1210) -->
|
||||
<div id="archive-health-dashboard" class="archive-health-dashboard" style="display:none;" aria-label="Archive Health Dashboard">
|
||||
<div class="archive-health-header">
|
||||
<span class="archive-health-title">◈ ARCHIVE HEALTH</span>
|
||||
<button class="archive-health-close" onclick="toggleArchiveHealthDashboard()" aria-label="Close dashboard">✕</button>
|
||||
</div>
|
||||
<div id="archive-health-content" class="archive-health-content"></div>
|
||||
</div>
|
||||
|
||||
<!-- Memory Activity Feed (Mnemosyne) -->
|
||||
<div id="memory-feed" class="memory-feed" style="display:none;">
|
||||
<div class="memory-feed-header">
|
||||
<span class="memory-feed-title">✨ Memory Feed</span>
|
||||
<div class="memory-feed-actions"><button class="memory-feed-clear" onclick="clearMemoryFeed()">Clear</button><button class="memory-feed-toggle" onclick="document.getElementById('memory-feed').style.display='none'">✕</button></div>
|
||||
</div>
|
||||
<div id="memory-feed-list" class="memory-feed-list"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- ═══ MNEMOSYNE MEMORY INSPECTOR ═══ -->
|
||||
<div id="memory-inspector" class="memory-inspector" style="display:none;">
|
||||
<div class="inspector-header">
|
||||
<div class="inspector-category-dot" id="inspector-dot"></div>
|
||||
<span class="inspector-title" id="inspector-title">Memory</span>
|
||||
<button class="inspector-close" onclick="closeInspector()">✕</button>
|
||||
</div>
|
||||
<div class="inspector-region" id="inspector-region">Region</div>
|
||||
<div class="inspector-content" id="inspector-content">Content</div>
|
||||
<div class="inspector-meta">
|
||||
<div class="inspector-meta-item">
|
||||
<span class="inspector-meta-label">Strength</span>
|
||||
<div class="inspector-strength-bar">
|
||||
<div class="inspector-strength-fill" id="inspector-strength"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inspector-meta-item">
|
||||
<span class="inspector-meta-label">Created</span>
|
||||
<span class="inspector-meta-value" id="inspector-created">—</span>
|
||||
</div>
|
||||
<div class="inspector-meta-item">
|
||||
<span class="inspector-meta-label">ID</span>
|
||||
<span class="inspector-meta-value inspector-id" id="inspector-id">—</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inspector-connections" id="inspector-connections" style="display:none;">
|
||||
<div class="inspector-section-title">Connections</div>
|
||||
<div id="inspector-connections-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
99
nexus/components/memory-optimizer.js
Normal file
99
nexus/components/memory-optimizer.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// ═══════════════════════════════════════════
|
||||
// PROJECT MNEMOSYNE — MEMORY OPTIMIZER (GOFAI)
|
||||
// ═══════════════════════════════════════════
|
||||
//
|
||||
// Heuristic-based memory pruning and organization.
|
||||
// Operates without LLMs to maintain a lean, high-signal spatial index.
|
||||
//
|
||||
// Heuristics:
|
||||
// 1. Strength Decay: Memories lose strength over time if not accessed.
|
||||
// 2. Redundancy: Simple string similarity to identify duplicates.
|
||||
// 3. Isolation: Memories with no connections are lower priority.
|
||||
// 4. Aging: Old memories in 'working' are moved to 'archive'.
|
||||
// ═══════════════════════════════════════════
|
||||
|
||||
const MemoryOptimizer = (() => {
|
||||
const DECAY_RATE = 0.01; // Strength lost per optimization cycle
|
||||
const PRUNE_THRESHOLD = 0.1; // Remove if strength < this
|
||||
const SIMILARITY_THRESHOLD = 0.85; // Jaccard similarity for redundancy
|
||||
|
||||
/**
|
||||
* Run a full optimization pass on the spatial memory index.
|
||||
* @param {object} spatialMemory - The SpatialMemory component instance.
|
||||
* @returns {object} Summary of actions taken.
|
||||
*/
|
||||
function optimize(spatialMemory) {
|
||||
const memories = spatialMemory.getAllMemories();
|
||||
const results = { pruned: 0, moved: 0, updated: 0 };
|
||||
|
||||
// 1. Strength Decay & Aging
|
||||
memories.forEach(mem => {
|
||||
let strength = mem.strength || 0.7;
|
||||
strength -= DECAY_RATE;
|
||||
|
||||
if (strength < PRUNE_THRESHOLD) {
|
||||
spatialMemory.removeMemory(mem.id);
|
||||
results.pruned++;
|
||||
return;
|
||||
}
|
||||
|
||||
// Move old working memories to archive
|
||||
if (mem.category === 'working') {
|
||||
const timestamp = mem.timestamp || new Date().toISOString();
|
||||
const age = Date.now() - new Date(timestamp).getTime();
|
||||
if (age > 1000 * 60 * 60 * 24) { // 24 hours
|
||||
spatialMemory.removeMemory(mem.id);
|
||||
spatialMemory.placeMemory({ ...mem, category: 'archive', strength });
|
||||
results.moved++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spatialMemory.updateMemory(mem.id, { strength });
|
||||
results.updated++;
|
||||
});
|
||||
|
||||
// 2. Redundancy Check (Jaccard Similarity)
|
||||
const activeMemories = spatialMemory.getAllMemories();
|
||||
for (let i = 0; i < activeMemories.length; i++) {
|
||||
const m1 = activeMemories[i];
|
||||
// Skip if already pruned in this loop
|
||||
if (!spatialMemory.getAllMemories().find(m => m.id === m1.id)) continue;
|
||||
|
||||
for (let j = i + 1; j < activeMemories.length; j++) {
|
||||
const m2 = activeMemories[j];
|
||||
if (m1.category !== m2.category) continue;
|
||||
|
||||
const sim = _calculateSimilarity(m1.content, m2.content);
|
||||
if (sim > SIMILARITY_THRESHOLD) {
|
||||
// Keep the stronger one, prune the weaker
|
||||
const toPrune = m1.strength >= m2.strength ? m2.id : m1.id;
|
||||
spatialMemory.removeMemory(toPrune);
|
||||
results.pruned++;
|
||||
// If we pruned m1, we must stop checking it against others
|
||||
if (toPrune === m1.id) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.info('[Mnemosyne] Optimization complete:', results);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate Jaccard similarity between two strings.
|
||||
* @private
|
||||
*/
|
||||
function _calculateSimilarity(s1, s2) {
|
||||
if (!s1 || !s2) return 0;
|
||||
const set1 = new Set(s1.toLowerCase().split(/\s+/));
|
||||
const set2 = new Set(s2.toLowerCase().split(/\s+/));
|
||||
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
||||
const union = new Set([...set1, ...set2]);
|
||||
return intersection.size / union.size;
|
||||
}
|
||||
|
||||
return { optimize };
|
||||
})();
|
||||
|
||||
export { MemoryOptimizer };
|
||||
@@ -32,9 +32,6 @@
|
||||
|
||||
const SpatialMemory = (() => {
|
||||
|
||||
// ─── CALLBACKS ────────────────────────────────────────
|
||||
let _onMemoryPlacedCallback = null;
|
||||
|
||||
// ─── REGION DEFINITIONS ───────────────────────────────
|
||||
const REGIONS = {
|
||||
engineering: {
|
||||
@@ -136,6 +133,9 @@ const SpatialMemory = (() => {
|
||||
let _regionMarkers = {};
|
||||
let _memoryObjects = {};
|
||||
let _connectionLines = [];
|
||||
let _entityLines = []; // entity resolution lines (issue #1167)
|
||||
let _camera = null; // set by setCamera() for LOD culling
|
||||
const ENTITY_LOD_DIST = 50; // hide entity lines when camera > this from midpoint
|
||||
let _initialized = false;
|
||||
|
||||
// ─── CRYSTAL GEOMETRY (persistent memories) ───────────
|
||||
@@ -143,47 +143,6 @@ const SpatialMemory = (() => {
|
||||
return new THREE.OctahedronGeometry(size, 0);
|
||||
}
|
||||
|
||||
// ─── TRUST-BASED VISUALS ─────────────────────────────
|
||||
// Wire crystal visual properties to fact trust score (0.0-1.0).
|
||||
// Issue #1166: Trust > 0.8 = bright glow/full opacity,
|
||||
// 0.5-0.8 = medium/80%, < 0.5 = dim/40%, < 0.3 = near-invisible pulsing red.
|
||||
function _getTrustVisuals(trust, regionColor) {
|
||||
const t = Math.max(0, Math.min(1, trust));
|
||||
if (t >= 0.8) {
|
||||
return {
|
||||
opacity: 1.0,
|
||||
emissiveIntensity: 2.0 * t,
|
||||
emissiveColor: regionColor,
|
||||
lightIntensity: 1.2,
|
||||
glowDesc: 'high'
|
||||
};
|
||||
} else if (t >= 0.5) {
|
||||
return {
|
||||
opacity: 0.8,
|
||||
emissiveIntensity: 1.2 * t,
|
||||
emissiveColor: regionColor,
|
||||
lightIntensity: 0.6,
|
||||
glowDesc: 'medium'
|
||||
};
|
||||
} else if (t >= 0.3) {
|
||||
return {
|
||||
opacity: 0.4,
|
||||
emissiveIntensity: 0.5 * t,
|
||||
emissiveColor: regionColor,
|
||||
lightIntensity: 0.2,
|
||||
glowDesc: 'dim'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
opacity: 0.15,
|
||||
emissiveIntensity: 0.3,
|
||||
emissiveColor: 0xff2200,
|
||||
lightIntensity: 0.1,
|
||||
glowDesc: 'untrusted'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ─── REGION MARKER ───────────────────────────────────
|
||||
function createRegionMarker(regionKey, region) {
|
||||
const cx = region.center[0];
|
||||
@@ -250,7 +209,83 @@ const SpatialMemory = (() => {
|
||||
sprite.scale.set(4, 1, 1);
|
||||
_scene.add(sprite);
|
||||
|
||||
return { ring, disc, glowDisc, sprite };
|
||||
|
||||
// ─── BULK IMPORT (WebSocket sync) ───────────────────
|
||||
/**
|
||||
* Import an array of memories in batch — for WebSocket sync.
|
||||
* Skips duplicates (same id). Returns count of newly placed.
|
||||
* @param {Array} memories - Array of memory objects { id, content, category, ... }
|
||||
* @returns {number} Count of newly placed memories
|
||||
*/
|
||||
function importMemories(memories) {
|
||||
if (!Array.isArray(memories) || memories.length === 0) return 0;
|
||||
let count = 0;
|
||||
memories.forEach(mem => {
|
||||
if (mem.id && !_memoryObjects[mem.id]) {
|
||||
placeMemory(mem);
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count > 0) {
|
||||
_dirty = true;
|
||||
saveToStorage();
|
||||
console.info('[Mnemosyne] Bulk imported', count, 'new memories (total:', Object.keys(_memoryObjects).length, ')');
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ─── UPDATE MEMORY ──────────────────────────────────
|
||||
/**
|
||||
* Update an existing memory's visual properties (strength, connections).
|
||||
* Does not move the crystal — only updates metadata and re-renders.
|
||||
* @param {string} memId - Memory ID to update
|
||||
* @param {object} updates - Fields to update: { strength, connections, content }
|
||||
* @returns {boolean} True if updated
|
||||
*/
|
||||
function updateMemory(memId, updates) {
|
||||
const obj = _memoryObjects[memId];
|
||||
if (!obj) return false;
|
||||
|
||||
if (updates.strength != null) {
|
||||
const strength = Math.max(0.05, Math.min(1, updates.strength));
|
||||
obj.mesh.userData.strength = strength;
|
||||
obj.mesh.material.emissiveIntensity = 1.5 * strength;
|
||||
obj.mesh.material.opacity = 0.5 + strength * 0.4;
|
||||
}
|
||||
if (updates.content != null) {
|
||||
obj.data.content = updates.content;
|
||||
}
|
||||
if (updates.connections != null) {
|
||||
obj.data.connections = updates.connections;
|
||||
// Rebuild connection lines
|
||||
_rebuildConnections(memId);
|
||||
}
|
||||
_dirty = true;
|
||||
saveToStorage();
|
||||
return true;
|
||||
}
|
||||
|
||||
function _rebuildConnections(memId) {
|
||||
// Remove existing lines for this memory
|
||||
for (let i = _connectionLines.length - 1; i >= 0; i--) {
|
||||
const line = _connectionLines[i];
|
||||
if (line.userData.from === memId || line.userData.to === memId) {
|
||||
if (line.parent) line.parent.remove(line);
|
||||
line.geometry.dispose();
|
||||
line.material.dispose();
|
||||
_connectionLines.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// Recreate lines for current connections
|
||||
const obj = _memoryObjects[memId];
|
||||
if (!obj || !obj.data.connections) return;
|
||||
obj.data.connections.forEach(targetId => {
|
||||
const target = _memoryObjects[targetId];
|
||||
if (target) _createConnectionLine(obj, target);
|
||||
});
|
||||
}
|
||||
|
||||
return { ring, disc, glowDisc, sprite };
|
||||
}
|
||||
|
||||
// ─── PLACE A MEMORY ──────────────────────────────────
|
||||
@@ -260,20 +295,17 @@ const SpatialMemory = (() => {
|
||||
const region = REGIONS[mem.category] || REGIONS.working;
|
||||
const pos = mem.position || _assignPosition(mem.category, mem.id);
|
||||
const strength = Math.max(0.05, Math.min(1, mem.strength != null ? mem.strength : 0.7));
|
||||
const trust = mem.trust != null ? Math.max(0, Math.min(1, mem.trust)) : 0.7;
|
||||
const size = 0.2 + strength * 0.3;
|
||||
|
||||
const tv = _getTrustVisuals(trust, region.color);
|
||||
|
||||
const geo = createCrystalGeometry(size);
|
||||
const mat = new THREE.MeshStandardMaterial({
|
||||
color: region.color,
|
||||
emissive: tv.emissiveColor,
|
||||
emissiveIntensity: tv.emissiveIntensity,
|
||||
emissive: region.color,
|
||||
emissiveIntensity: 1.5 * strength,
|
||||
metalness: 0.6,
|
||||
roughness: 0.15,
|
||||
transparent: true,
|
||||
opacity: tv.opacity
|
||||
opacity: 0.5 + strength * 0.4
|
||||
});
|
||||
|
||||
const crystal = new THREE.Mesh(geo, mat);
|
||||
@@ -286,12 +318,10 @@ const SpatialMemory = (() => {
|
||||
region: mem.category,
|
||||
pulse: Math.random() * Math.PI * 2,
|
||||
strength: strength,
|
||||
trust: trust,
|
||||
glowDesc: tv.glowDesc,
|
||||
createdAt: mem.timestamp || new Date().toISOString()
|
||||
};
|
||||
|
||||
const light = new THREE.PointLight(tv.emissiveColor, tv.lightIntensity, 5);
|
||||
const light = new THREE.PointLight(region.color, 0.8 * strength, 5);
|
||||
crystal.add(light);
|
||||
|
||||
_scene.add(crystal);
|
||||
@@ -301,15 +331,13 @@ const SpatialMemory = (() => {
|
||||
_drawConnections(mem.id, mem.connections);
|
||||
}
|
||||
|
||||
if (mem.entity) {
|
||||
_drawEntityLines(mem.id, mem);
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
saveToStorage();
|
||||
console.info('[Mnemosyne] Spatial memory placed:', mem.id, 'in', region.label);
|
||||
|
||||
// Fire particle burst callback
|
||||
if (_onMemoryPlacedCallback) {
|
||||
_onMemoryPlacedCallback(crystal.position.clone(), mem.category || 'working');
|
||||
}
|
||||
|
||||
return crystal;
|
||||
}
|
||||
|
||||
@@ -353,6 +381,77 @@ const SpatialMemory = (() => {
|
||||
});
|
||||
}
|
||||
|
||||
// ─── ENTITY RESOLUTION LINES (#1167) ──────────────────
|
||||
// Draw lines between crystals that share an entity or are related entities.
|
||||
// Same entity → thin blue line. Related entities → thin purple dashed line.
|
||||
function _drawEntityLines(memId, mem) {
|
||||
if (!mem.entity) return;
|
||||
const src = _memoryObjects[memId];
|
||||
if (!src) return;
|
||||
|
||||
Object.entries(_memoryObjects).forEach(([otherId, other]) => {
|
||||
if (otherId === memId) return;
|
||||
const otherData = other.data;
|
||||
if (!otherData.entity) return;
|
||||
|
||||
let lineType = null;
|
||||
if (otherData.entity === mem.entity) {
|
||||
lineType = 'same_entity';
|
||||
} else if (mem.related_entities && mem.related_entities.includes(otherData.entity)) {
|
||||
lineType = 'related';
|
||||
} else if (otherData.related_entities && otherData.related_entities.includes(mem.entity)) {
|
||||
lineType = 'related';
|
||||
}
|
||||
if (!lineType) return;
|
||||
|
||||
// Deduplicate — only draw from lower ID to higher
|
||||
if (memId > otherId) return;
|
||||
|
||||
const points = [src.mesh.position.clone(), other.mesh.position.clone()];
|
||||
const geo = new THREE.BufferGeometry().setFromPoints(points);
|
||||
let mat;
|
||||
if (lineType === 'same_entity') {
|
||||
mat = new THREE.LineBasicMaterial({ color: 0x4488ff, transparent: true, opacity: 0.35 });
|
||||
} else {
|
||||
mat = new THREE.LineDashedMaterial({ color: 0x9966ff, dashSize: 0.3, gapSize: 0.2, transparent: true, opacity: 0.25 });
|
||||
const line = new THREE.Line(geo, mat);
|
||||
line.computeLineDistances();
|
||||
line.userData = { type: 'entity_line', from: memId, to: otherId, lineType };
|
||||
_scene.add(line);
|
||||
_entityLines.push(line);
|
||||
return;
|
||||
}
|
||||
const line = new THREE.Line(geo, mat);
|
||||
line.userData = { type: 'entity_line', from: memId, to: otherId, lineType };
|
||||
_scene.add(line);
|
||||
_entityLines.push(line);
|
||||
});
|
||||
}
|
||||
|
||||
function _updateEntityLines() {
|
||||
if (!_camera) return;
|
||||
const camPos = _camera.position;
|
||||
|
||||
_entityLines.forEach(line => {
|
||||
// Compute midpoint of line
|
||||
const posArr = line.geometry.attributes.position.array;
|
||||
const mx = (posArr[0] + posArr[3]) / 2;
|
||||
const my = (posArr[1] + posArr[4]) / 2;
|
||||
const mz = (posArr[2] + posArr[5]) / 2;
|
||||
const dist = camPos.distanceTo(new THREE.Vector3(mx, my, mz));
|
||||
|
||||
if (dist > ENTITY_LOD_DIST) {
|
||||
line.visible = false;
|
||||
} else {
|
||||
line.visible = true;
|
||||
// Fade based on distance
|
||||
const fade = Math.max(0, 1 - (dist / ENTITY_LOD_DIST));
|
||||
const baseOpacity = line.userData.lineType === 'same_entity' ? 0.35 : 0.25;
|
||||
line.material.opacity = baseOpacity * fade;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─── REMOVE A MEMORY ─────────────────────────────────
|
||||
function removeMemory(memId) {
|
||||
const obj = _memoryObjects[memId];
|
||||
@@ -372,6 +471,16 @@ const SpatialMemory = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = _entityLines.length - 1; i >= 0; i--) {
|
||||
const line = _entityLines[i];
|
||||
if (line.userData.from === memId || line.userData.to === memId) {
|
||||
if (line.parent) line.parent.remove(line);
|
||||
line.geometry.dispose();
|
||||
line.material.dispose();
|
||||
_entityLines.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
delete _memoryObjects[memId];
|
||||
_dirty = true;
|
||||
saveToStorage();
|
||||
@@ -392,19 +501,13 @@ const SpatialMemory = (() => {
|
||||
mesh.scale.setScalar(pulse);
|
||||
|
||||
if (mesh.material) {
|
||||
const trust = mesh.userData.trust != null ? mesh.userData.trust : 0.7;
|
||||
const base = mesh.userData.strength || 0.7;
|
||||
if (trust < 0.3) {
|
||||
// Low trust: pulsing red — visible warning
|
||||
const pulseAlpha = 0.15 + Math.sin(mesh.userData.pulse * 2.0) * 0.15;
|
||||
mesh.material.emissiveIntensity = 0.3 + Math.sin(mesh.userData.pulse * 2.0) * 0.3;
|
||||
mesh.material.opacity = pulseAlpha;
|
||||
} else {
|
||||
mesh.material.emissiveIntensity = 1.0 + Math.sin(mesh.userData.pulse * 0.7) * 0.5 * base;
|
||||
}
|
||||
mesh.material.emissiveIntensity = 1.0 + Math.sin(mesh.userData.pulse * 0.7) * 0.5 * base;
|
||||
}
|
||||
});
|
||||
|
||||
_updateEntityLines();
|
||||
|
||||
Object.values(_regionMarkers).forEach(marker => {
|
||||
if (marker.ring && marker.ring.material) {
|
||||
marker.ring.material.opacity = 0.1 + Math.sin(now * 0.001) * 0.05;
|
||||
@@ -431,42 +534,6 @@ const SpatialMemory = (() => {
|
||||
return REGIONS;
|
||||
}
|
||||
|
||||
// ─── UPDATE VISUAL PROPERTIES ────────────────────────
|
||||
// Re-render crystal when trust/strength change (no position move).
|
||||
function updateMemoryVisual(memId, updates) {
|
||||
const obj = _memoryObjects[memId];
|
||||
if (!obj) return false;
|
||||
|
||||
const mesh = obj.mesh;
|
||||
const region = REGIONS[obj.region] || REGIONS.working;
|
||||
|
||||
if (updates.trust != null) {
|
||||
const trust = Math.max(0, Math.min(1, updates.trust));
|
||||
mesh.userData.trust = trust;
|
||||
obj.data.trust = trust;
|
||||
const tv = _getTrustVisuals(trust, region.color);
|
||||
mesh.material.emissive = new THREE.Color(tv.emissiveColor);
|
||||
mesh.material.emissiveIntensity = tv.emissiveIntensity;
|
||||
mesh.material.opacity = tv.opacity;
|
||||
mesh.userData.glowDesc = tv.glowDesc;
|
||||
if (mesh.children.length > 0 && mesh.children[0].isPointLight) {
|
||||
mesh.children[0].intensity = tv.lightIntensity;
|
||||
mesh.children[0].color = new THREE.Color(tv.emissiveColor);
|
||||
}
|
||||
}
|
||||
|
||||
if (updates.strength != null) {
|
||||
const strength = Math.max(0.05, Math.min(1, updates.strength));
|
||||
mesh.userData.strength = strength;
|
||||
obj.data.strength = strength;
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
saveToStorage();
|
||||
console.info('[Mnemosyne] Visual updated:', memId, 'trust:', mesh.userData.trust, 'glow:', mesh.userData.glowDesc);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─── QUERY ───────────────────────────────────────────
|
||||
function getMemoryAtPosition(position, maxDist) {
|
||||
maxDist = maxDist || 2;
|
||||
@@ -606,7 +673,6 @@ const SpatialMemory = (() => {
|
||||
source: o.data.source || 'unknown',
|
||||
timestamp: o.data.timestamp || o.mesh.userData.createdAt,
|
||||
strength: o.mesh.userData.strength || 0.7,
|
||||
trust: o.mesh.userData.trust != null ? o.mesh.userData.trust : 0.7,
|
||||
connections: o.data.connections || []
|
||||
}))
|
||||
};
|
||||
@@ -752,173 +818,18 @@ const SpatialMemory = (() => {
|
||||
return _selectedId;
|
||||
}
|
||||
|
||||
// ─── FILE EXPORT ──────────────────────────────────────
|
||||
function exportToFile() {
|
||||
const index = exportIndex();
|
||||
const json = JSON.stringify(index, null, 2);
|
||||
const date = new Date().toISOString().slice(0, 10);
|
||||
const filename = 'mnemosyne-export-' + date + '.json';
|
||||
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
console.info('[Mnemosyne] Exported', index.memories.length, 'memories to', filename);
|
||||
return { filename, count: index.memories.length };
|
||||
}
|
||||
|
||||
// ─── FILE IMPORT ──────────────────────────────────────
|
||||
function importFromFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!file) {
|
||||
reject(new Error('No file provided'));
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
const data = JSON.parse(e.target.result);
|
||||
|
||||
// Schema validation
|
||||
if (!data || typeof data !== 'object') {
|
||||
reject(new Error('Invalid JSON: not an object'));
|
||||
return;
|
||||
}
|
||||
if (typeof data.version !== 'number') {
|
||||
reject(new Error('Invalid schema: missing version field'));
|
||||
return;
|
||||
}
|
||||
if (data.version !== STORAGE_VERSION) {
|
||||
reject(new Error('Version mismatch: got ' + data.version + ', expected ' + STORAGE_VERSION));
|
||||
return;
|
||||
}
|
||||
if (!Array.isArray(data.memories)) {
|
||||
reject(new Error('Invalid schema: memories is not an array'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate each memory entry
|
||||
for (let i = 0; i < data.memories.length; i++) {
|
||||
const mem = data.memories[i];
|
||||
if (!mem.id || typeof mem.id !== 'string') {
|
||||
reject(new Error('Invalid memory at index ' + i + ': missing or invalid id'));
|
||||
return;
|
||||
}
|
||||
if (!mem.category || typeof mem.category !== 'string') {
|
||||
reject(new Error('Invalid memory "' + mem.id + '": missing category'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const count = importIndex(data);
|
||||
saveToStorage();
|
||||
console.info('[Mnemosyne] Imported', count, 'memories from file');
|
||||
resolve({ count, total: data.memories.length });
|
||||
} catch (parseErr) {
|
||||
reject(new Error('Failed to parse JSON: ' + parseErr.message));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = function() {
|
||||
reject(new Error('Failed to read file'));
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ─── SPATIAL SEARCH (issue #1170) ────────────────────
|
||||
let _searchOriginalState = {}; // memId -> { emissiveIntensity, opacity } for restore
|
||||
|
||||
function searchContent(query) {
|
||||
if (!query || !query.trim()) return [];
|
||||
const q = query.toLowerCase().trim();
|
||||
const matches = [];
|
||||
|
||||
Object.values(_memoryObjects).forEach(obj => {
|
||||
const d = obj.data;
|
||||
const searchable = [
|
||||
d.content || '',
|
||||
d.id || '',
|
||||
d.category || '',
|
||||
d.source || '',
|
||||
...(d.connections || [])
|
||||
].join(' ').toLowerCase();
|
||||
|
||||
if (searchable.includes(q)) {
|
||||
matches.push(d.id);
|
||||
}
|
||||
});
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
function highlightSearchResults(matchIds) {
|
||||
// Save original state and apply search highlighting
|
||||
_searchOriginalState = {};
|
||||
const matchSet = new Set(matchIds);
|
||||
|
||||
Object.entries(_memoryObjects).forEach(([id, obj]) => {
|
||||
const mat = obj.mesh.material;
|
||||
_searchOriginalState[id] = {
|
||||
emissiveIntensity: mat.emissiveIntensity,
|
||||
opacity: mat.opacity
|
||||
};
|
||||
|
||||
if (matchSet.has(id)) {
|
||||
// Match: bright white glow
|
||||
mat.emissive.setHex(0xffffff);
|
||||
mat.emissiveIntensity = 5.0;
|
||||
mat.opacity = 1.0;
|
||||
} else {
|
||||
// Non-match: dim to 10% opacity
|
||||
mat.opacity = 0.1;
|
||||
mat.emissiveIntensity = 0.2;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
Object.entries(_memoryObjects).forEach(([id, obj]) => {
|
||||
const mat = obj.mesh.material;
|
||||
const saved = _searchOriginalState[id];
|
||||
if (saved) {
|
||||
// Restore original emissive color from region
|
||||
const region = REGIONS[obj.region] || REGIONS.working;
|
||||
mat.emissive.copy(region.color);
|
||||
mat.emissiveIntensity = saved.emissiveIntensity;
|
||||
mat.opacity = saved.opacity;
|
||||
}
|
||||
});
|
||||
_searchOriginalState = {};
|
||||
}
|
||||
|
||||
function getSearchMatchPosition(matchId) {
|
||||
const obj = _memoryObjects[matchId];
|
||||
return obj ? obj.mesh.position.clone() : null;
|
||||
}
|
||||
|
||||
function setOnMemoryPlaced(callback) {
|
||||
_onMemoryPlacedCallback = callback;
|
||||
// ─── CAMERA REFERENCE (for entity line LOD) ─────────
|
||||
function setCamera(camera) {
|
||||
_camera = camera;
|
||||
}
|
||||
|
||||
return {
|
||||
init, placeMemory, removeMemory, update, updateMemoryVisual,
|
||||
init, placeMemory, removeMemory, update, importMemories, updateMemory,
|
||||
getMemoryAtPosition, getRegionAtPosition, getMemoriesInRegion, getAllMemories,
|
||||
getCrystalMeshes, getMemoryFromMesh, highlightMemory, clearHighlight, getSelectedId,
|
||||
exportIndex, importIndex, exportToFile, importFromFile, searchNearby, REGIONS,
|
||||
exportIndex, importIndex, searchNearby, REGIONS,
|
||||
saveToStorage, loadFromStorage, clearStorage,
|
||||
runGravityLayout,
|
||||
searchContent, highlightSearchResults, clearSearch, getSearchMatchPosition,
|
||||
setOnMemoryPlaced
|
||||
runGravityLayout
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
27
scripts/guardrails.sh
Normal file
27
scripts/guardrails.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
# [Mnemosyne] Agent Guardrails — The Nexus
|
||||
# Validates code integrity and scans for secrets before deployment.
|
||||
|
||||
echo "--- [Mnemosyne] Running Guardrails ---"
|
||||
|
||||
# 1. Syntax Checks
|
||||
echo "[1/3] Validating syntax..."
|
||||
for f in ; do
|
||||
node --check "$f" || { echo "Syntax error in $f"; exit 1; }
|
||||
done
|
||||
echo "Syntax OK."
|
||||
|
||||
# 2. JSON/YAML Validation
|
||||
echo "[2/3] Validating configs..."
|
||||
for f in ; do
|
||||
node -e "JSON.parse(require('fs').readFileSync('$f'))" || { echo "Invalid JSON: $f"; exit 1; }
|
||||
done
|
||||
echo "Configs OK."
|
||||
|
||||
# 3. Secret Scan
|
||||
echo "[3/3] Scanning for secrets..."
|
||||
grep -rE "AI_|TOKEN|KEY|SECRET" . --exclude-dir=node_modules --exclude=guardrails.sh | grep -v "process.env" && {
|
||||
echo "WARNING: Potential secrets found!"
|
||||
} || echo "No secrets detected."
|
||||
|
||||
echo "--- Guardrails Passed ---"
|
||||
26
scripts/smoke.mjs
Normal file
26
scripts/smoke.mjs
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* [Mnemosyne] Smoke Test — The Nexus
|
||||
* Verifies core components are loadable and basic state is consistent.
|
||||
*/
|
||||
|
||||
import { SpatialMemory } from '../nexus/components/spatial-memory.js';
|
||||
import { MemoryOptimizer } from '../nexus/components/memory-optimizer.js';
|
||||
|
||||
console.log('--- [Mnemosyne] Running Smoke Test ---');
|
||||
|
||||
// 1. Verify Components
|
||||
if (!SpatialMemory || !MemoryOptimizer) {
|
||||
console.error('Failed to load core components');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('Components loaded.');
|
||||
|
||||
// 2. Verify Regions
|
||||
const regions = Object.keys(SpatialMemory.REGIONS || {});
|
||||
if (regions.length < 5) {
|
||||
console.error('SpatialMemory regions incomplete:', regions);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('Regions verified:', regions.join(', '));
|
||||
|
||||
console.log('--- Smoke Test Passed ---');
|
||||
Reference in New Issue
Block a user