Spaces:
Running
Running
Commit
·
794cf6c
0
Parent(s):
initial commit
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .claude/commands/nourish.md +51 -0
- .claude/commands/peel.md +35 -0
- .claude/commands/plant.md +40 -0
- .gitattributes +35 -0
- .gitignore +90 -0
- CLAUDE.md +36 -0
- Dockerfile +38 -0
- PLAN.md +870 -0
- README.md +19 -0
- auth-callback.html +62 -0
- bun.lock +769 -0
- eslint.config.js +62 -0
- index.html +33 -0
- layers/context-template.md +34 -0
- layers/structure.md +106 -0
- llms.txt +1107 -0
- package.json +46 -0
- src/App.svelte +65 -0
- src/app.css +16 -0
- src/context.md +18 -0
- src/lib/components/Editor.svelte +128 -0
- src/lib/components/auth/LoginButton.svelte +167 -0
- src/lib/components/auth/context.md +28 -0
- src/lib/components/chat/ChatPanel.svelte +430 -0
- src/lib/components/chat/context.md +14 -0
- src/lib/components/console/ConsoleMessage.svelte +109 -0
- src/lib/components/console/ConsolePanel.svelte +131 -0
- src/lib/components/console/context.md +32 -0
- src/lib/components/context.md +45 -0
- src/lib/components/editor/CodeEditor.svelte +28 -0
- src/lib/components/editor/context.md +31 -0
- src/lib/components/game/GameCanvas.svelte +81 -0
- src/lib/components/game/GameError.svelte +30 -0
- src/lib/components/game/context.md +33 -0
- src/lib/components/layout/AppHeader.svelte +318 -0
- src/lib/components/layout/LoadingScreen.svelte +141 -0
- src/lib/components/layout/SplitView.svelte +355 -0
- src/lib/components/layout/context.md +33 -0
- src/lib/config/animations.ts +43 -0
- src/lib/config/context.md +34 -0
- src/lib/config/shortcuts.ts +57 -0
- src/lib/server/agent-runner.ts +128 -0
- src/lib/server/api.ts +165 -0
- src/lib/server/context.md +20 -0
- src/lib/services/auth.ts +172 -0
- src/lib/services/console-capture.ts +90 -0
- src/lib/services/context.md +43 -0
- src/lib/services/game-engine.ts +77 -0
- src/lib/services/html-parser.ts +18 -0
- src/lib/stores/agent.ts +271 -0
.claude/commands/nourish.md
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🍃 /nourish
|
2 |
+
|
3 |
+
Complete conversation by updating context and applying cleanup.
|
4 |
+
|
5 |
+
## Auto-Loaded Context:
|
6 |
+
|
7 |
+
@CLAUDE.md
|
8 |
+
@layers/structure.md
|
9 |
+
@llms.txt
|
10 |
+
|
11 |
+
User arguments: "$ARGUMENTS"
|
12 |
+
|
13 |
+
## Steps
|
14 |
+
|
15 |
+
### 1. Identify Changes
|
16 |
+
|
17 |
+
- Detect modified, added, or deleted files in conversation
|
18 |
+
- Map changes to their parent folders and components
|
19 |
+
|
20 |
+
### 2. Update Context Chain
|
21 |
+
|
22 |
+
Traverse upward through context tiers (see CLAUDE.md):
|
23 |
+
|
24 |
+
- **Tier 2**: Update relevant `context.md` files to reflect current code state
|
25 |
+
- **Tier 1**: Update `layers/structure.md` if structure/commands/stack changed
|
26 |
+
- Follow all rules from CLAUDE.md, especially "No History" principle
|
27 |
+
|
28 |
+
### 3. Apply Cleanup
|
29 |
+
|
30 |
+
Fix obvious issues encountered:
|
31 |
+
|
32 |
+
- Remove comments; code should be self-explanatory without comments
|
33 |
+
- Remove dead code and unused files
|
34 |
+
- Consolidate duplicate patterns
|
35 |
+
- Apply CLAUDE.md principles (simplicity, reuse, single responsibility)
|
36 |
+
|
37 |
+
### 4. Verify
|
38 |
+
|
39 |
+
- Context accurately reflects current state
|
40 |
+
- Project is leaner or same size as before
|
41 |
+
- No history references in code or context
|
42 |
+
|
43 |
+
## Output
|
44 |
+
|
45 |
+
Report conversation completion: updated context files and improvements made.
|
46 |
+
|
47 |
+
## Guidelines
|
48 |
+
|
49 |
+
- When updating context, don't over-specify implementation details
|
50 |
+
- If changes were internal (e.g. business logic), it may not be necessary to update context
|
51 |
+
- Context should be even shorter after updates, avoiding context rot
|
.claude/commands/peel.md
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🧄 /peel
|
2 |
+
|
3 |
+
Load relevant context for the current conversation.
|
4 |
+
|
5 |
+
## Auto-Loaded Context:
|
6 |
+
|
7 |
+
@CLAUDE.md
|
8 |
+
@layers/structure.md
|
9 |
+
@llms.txt
|
10 |
+
|
11 |
+
User arguments: "$ARGUMENTS"
|
12 |
+
|
13 |
+
## Steps
|
14 |
+
|
15 |
+
### 1. Parse Work Area
|
16 |
+
|
17 |
+
- Analyze user request or arguments
|
18 |
+
- Determine relevant components or features
|
19 |
+
- Assess required context depth
|
20 |
+
|
21 |
+
### 2. Load Targeted Context
|
22 |
+
|
23 |
+
- Read relevant `context.md` files for identified areas
|
24 |
+
- Skip unrelated component contexts to minimize tokens
|
25 |
+
|
26 |
+
### 3. Confirm Scope
|
27 |
+
|
28 |
+
- Brief summary of loaded context
|
29 |
+
- State understanding of work focus
|
30 |
+
- Note any assumptions made
|
31 |
+
|
32 |
+
## Guidelines
|
33 |
+
|
34 |
+
- Load only what's needed for the current task
|
35 |
+
- Defer code reading until necessary
|
.claude/commands/plant.md
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 🌱 /plant
|
2 |
+
|
3 |
+
Initialize context management structure for the project.
|
4 |
+
|
5 |
+
## Auto-Loaded Context:
|
6 |
+
|
7 |
+
@CLAUDE.md
|
8 |
+
@layers/structure.md
|
9 |
+
|
10 |
+
User arguments: "$ARGUMENTS"
|
11 |
+
|
12 |
+
## Steps
|
13 |
+
|
14 |
+
### 1. Analyze Project
|
15 |
+
|
16 |
+
- Scan technology stack, build tools, directory structure
|
17 |
+
- Identify major components and entry points
|
18 |
+
- Understand existing patterns and conventions
|
19 |
+
|
20 |
+
### 2. Fill Core Templates
|
21 |
+
|
22 |
+
- Update `CLAUDE.md` with project-specific details
|
23 |
+
- Complete `layers/structure.md` with actual stack, commands, layout
|
24 |
+
- Remove template placeholders
|
25 |
+
|
26 |
+
### 3. Create Component Context
|
27 |
+
|
28 |
+
- Generate `context.md` files for major folders using templates
|
29 |
+
- Document purpose, scope, dependencies for each component
|
30 |
+
- Place in appropriate directories
|
31 |
+
|
32 |
+
### 4. Validate
|
33 |
+
|
34 |
+
- Ensure coverage of main components
|
35 |
+
- Check that context hierarchy makes sense
|
36 |
+
- Verify no placeholder text remains
|
37 |
+
|
38 |
+
## Output
|
39 |
+
|
40 |
+
List created/updated files by tier and any areas needing attention.
|
.gitattributes
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Dependencies
|
2 |
+
node_modules/
|
3 |
+
.pnp
|
4 |
+
.pnp.js
|
5 |
+
|
6 |
+
# Build outputs
|
7 |
+
dist/
|
8 |
+
dist-ssr/
|
9 |
+
build/
|
10 |
+
*.local
|
11 |
+
|
12 |
+
# Vite
|
13 |
+
.vite/
|
14 |
+
|
15 |
+
# Logs
|
16 |
+
logs/
|
17 |
+
*.log
|
18 |
+
npm-debug.log*
|
19 |
+
yarn-debug.log*
|
20 |
+
yarn-error.log*
|
21 |
+
pnpm-debug.log*
|
22 |
+
lerna-debug.log*
|
23 |
+
|
24 |
+
# Environment files
|
25 |
+
.env
|
26 |
+
.env.local
|
27 |
+
.env.development.local
|
28 |
+
.env.test.local
|
29 |
+
.env.production.local
|
30 |
+
*.env
|
31 |
+
|
32 |
+
# IDE
|
33 |
+
.vscode/*
|
34 |
+
!.vscode/extensions.json
|
35 |
+
!.vscode/settings.json
|
36 |
+
.idea/
|
37 |
+
*.swp
|
38 |
+
*.swo
|
39 |
+
*~
|
40 |
+
.DS_Store
|
41 |
+
|
42 |
+
# OS
|
43 |
+
Thumbs.db
|
44 |
+
.DS_Store
|
45 |
+
|
46 |
+
# Testing
|
47 |
+
coverage/
|
48 |
+
*.lcov
|
49 |
+
.nyc_output/
|
50 |
+
|
51 |
+
# TypeScript
|
52 |
+
*.tsbuildinfo
|
53 |
+
|
54 |
+
# Package managers
|
55 |
+
.npm
|
56 |
+
.yarn/
|
57 |
+
.pnpm-store/
|
58 |
+
|
59 |
+
# Temporary files
|
60 |
+
*.tmp
|
61 |
+
.temp/
|
62 |
+
tmp/
|
63 |
+
|
64 |
+
# HuggingFace Spaces
|
65 |
+
.huggingface/
|
66 |
+
flagged/
|
67 |
+
|
68 |
+
# Python (for future AI integration)
|
69 |
+
__pycache__/
|
70 |
+
*.py[cod]
|
71 |
+
*$py.class
|
72 |
+
*.so
|
73 |
+
.Python
|
74 |
+
venv/
|
75 |
+
ENV/
|
76 |
+
env/
|
77 |
+
.venv
|
78 |
+
|
79 |
+
# Jupyter Notebooks
|
80 |
+
.ipynb_checkpoints/
|
81 |
+
|
82 |
+
# Bun
|
83 |
+
bun.lockb
|
84 |
+
|
85 |
+
# LLMs
|
86 |
+
.vscode/
|
87 |
+
.claude/settings.local.json
|
88 |
+
|
89 |
+
# Dev Scripts
|
90 |
+
scripts/
|
CLAUDE.md
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# AI Context - Working Agreement
|
2 |
+
|
3 |
+
<project-description>
|
4 |
+
AI-assisted game development environment using the VibeGame engine. Iterative development with Svelte UI, Monaco editor, and Smolagents for AI-driven game modifications with console feedback loops.
|
5 |
+
</project-description>
|
6 |
+
|
7 |
+
**Required**: Read [layers/structure.md](layers/structure.md) before proceeding with any task
|
8 |
+
|
9 |
+
## Context Management System
|
10 |
+
|
11 |
+
- **Tier 0 — global**: `CLAUDE.md` (root). Global standards and system overview
|
12 |
+
- **Tier 1 — project**: `layers/structure.md`. Project map (stack, commands, layout, entry points)
|
13 |
+
- **Tier 2 — folder context**: `context.md` in any folder; one per folder; explains purpose/structure of that folder
|
14 |
+
- **Tier 3 — implementation**: Code files (scripts)
|
15 |
+
|
16 |
+
## Rules
|
17 |
+
|
18 |
+
- **Priority**: Your number one priority is to manage your own context; always load appropriate context before doing anything else
|
19 |
+
- **No History**: CRITICAL - Code and context must NEVER reference their own history. Write everything as the current, final state. Never include comments like "changed from X to Y" or "previously was Z". This is a severe form of context rot
|
20 |
+
- **Simplicity**: Keep code simple, elegant, concise, and readable
|
21 |
+
- **Structure**: Keep files small and single-responsibility; separate concerns (MVC/ECS as appropriate)
|
22 |
+
- **Reuse**: Reuse before adding new code; avoid repetition
|
23 |
+
- **Comments**: Code should be self-explanatory without comments; use concise comments only when necessary
|
24 |
+
- **State**: Single source of truth; caches/derivations only
|
25 |
+
- **Data**: Favor data-driven/declarative design
|
26 |
+
- **Fail Fast**: Make bugs immediately visible rather than hiding them; favor simplicity over defensive patterns
|
27 |
+
- **Backwards Compatibility**: Unless stated otherwise, favor simplicity over backwards compatibility; the design rules above should make breaking changes easy to trace and fix
|
28 |
+
|
29 |
+
## Security
|
30 |
+
|
31 |
+
- **Inputs & secrets**: Validate inputs; secrets only in env; never log sensitive data
|
32 |
+
- **Auth**: Gateway auth; server-side token validation; sanitize inputs
|
33 |
+
|
34 |
+
## Tools
|
35 |
+
|
36 |
+
- **Context7**: Use as needed to fetch documentation
|
Dockerfile
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM node:20-slim
|
2 |
+
|
3 |
+
# Install dependencies as root
|
4 |
+
RUN apt-get update && apt-get install -y \
|
5 |
+
curl unzip \
|
6 |
+
&& rm -rf /var/lib/apt/lists/*
|
7 |
+
|
8 |
+
# Use existing node user (UID 1000) for Hugging Face Spaces compatibility
|
9 |
+
# The node:20-slim image already has a 'node' user with UID 1000
|
10 |
+
|
11 |
+
# Install bun globally
|
12 |
+
RUN curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash
|
13 |
+
ENV PATH="/usr/local/bin:${PATH}"
|
14 |
+
|
15 |
+
# Set working directory with proper permissions
|
16 |
+
WORKDIR /app
|
17 |
+
RUN chown -R node:node /app
|
18 |
+
|
19 |
+
# Switch to non-root user
|
20 |
+
USER node
|
21 |
+
|
22 |
+
# Copy package files and install dependencies
|
23 |
+
COPY --chown=node:node package.json bun.lock* ./
|
24 |
+
RUN bun install
|
25 |
+
|
26 |
+
# Copy application files
|
27 |
+
COPY --chown=node:node . .
|
28 |
+
|
29 |
+
# Create writable directory for Vite temp files
|
30 |
+
|
31 |
+
RUN mkdir -p /app/.vite && chmod 755 /app/.vite
|
32 |
+
|
33 |
+
EXPOSE 7860
|
34 |
+
ENV NODE_ENV=production
|
35 |
+
ENV HOME=/home/node
|
36 |
+
|
37 |
+
ENTRYPOINT []
|
38 |
+
CMD ["bun", "run", "dev", "--host", "0.0.0.0", "--port", "7860"]
|
PLAN.md
ADDED
@@ -0,0 +1,870 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# AI Agent Development Plan - Refined Strategy
|
2 |
+
|
3 |
+
## Overview
|
4 |
+
|
5 |
+
Iterative AI-driven game development system using **browser-native TypeScript architecture** with streaming AI agents for real-time VibeGame game modification and self-correcting feedback loops.
|
6 |
+
|
7 |
+
## Current Status
|
8 |
+
|
9 |
+
**Phase 1 - Foundation COMPLETED** ✅
|
10 |
+
|
11 |
+
- ✅ WebSocket server integrated with Vite
|
12 |
+
- ✅ HuggingFace Inference API connected
|
13 |
+
- ✅ Real-time streaming chat interface
|
14 |
+
- ✅ Agent state management with Svelte stores
|
15 |
+
- ✅ Bidirectional communication working
|
16 |
+
|
17 |
+
**Next: Tool Implementation** 🚀
|
18 |
+
|
19 |
+
## Core Strategy: Small, Testable Iterations
|
20 |
+
|
21 |
+
### Guiding Principles
|
22 |
+
|
23 |
+
1. **Start Ultra-Simple** - First tool in ~10 lines of code
|
24 |
+
2. **Test Continuously** - Every iteration delivers testable value
|
25 |
+
3. **Fail Visibly** - Errors shown immediately in UI
|
26 |
+
4. **Incremental Complexity** - Add features one at a time
|
27 |
+
5. **User-Centric Progress** - Each day delivers visible improvement
|
28 |
+
|
29 |
+
### Development Phases
|
30 |
+
|
31 |
+
- **Week 1**: Make it Work (basic tools)
|
32 |
+
- **Week 2**: Make it Good (error handling)
|
33 |
+
- **Week 3**: Make it Fast (optimization)
|
34 |
+
|
35 |
+
## Architecture Evolution
|
36 |
+
|
37 |
+
### Current Architecture (Simple)
|
38 |
+
|
39 |
+
```
|
40 |
+
Browser → WebSocket → Agent → Tools → Stores → UI
|
41 |
+
```
|
42 |
+
|
43 |
+
### Target Architecture (Advanced)
|
44 |
+
|
45 |
+
```
|
46 |
+
Browser → SSE/WebSocket → Composite Agent System → MCP Tools → Stores → UI
|
47 |
+
├── Base Model (Complex reasoning)
|
48 |
+
├── Quick Edit Model (Fast edits)
|
49 |
+
└── AutoFix Model (Error correction)
|
50 |
+
```
|
51 |
+
|
52 |
+
## Implementation Phases
|
53 |
+
|
54 |
+
### Phase 1: Minimal Viable Tools (Days 1-7)
|
55 |
+
|
56 |
+
**Goal:** Agent can read and edit game code
|
57 |
+
|
58 |
+
#### Day 1: First Tool - Read Game Code
|
59 |
+
|
60 |
+
- [ ] Create `src/lib/tools/registry.ts` with simple tool interface
|
61 |
+
- [ ] Implement `read-game-code.ts` (returns editor content)
|
62 |
+
- [ ] Update agent-runner to detect tool calls
|
63 |
+
- **Test:** "What's in the game?" → Agent describes scene
|
64 |
+
|
65 |
+
#### Day 2: Tool Execution
|
66 |
+
|
67 |
+
- [ ] Parse tool requests from agent responses
|
68 |
+
- [ ] Execute tools and return results
|
69 |
+
- [ ] Add tool context to agent
|
70 |
+
- **Test:** Agent accurately describes game elements
|
71 |
+
|
72 |
+
#### Day 3: Edit Game Code
|
73 |
+
|
74 |
+
- [ ] Implement `edit-game-code.ts` with string replacement
|
75 |
+
- [ ] Connect to editor store
|
76 |
+
- [ ] Basic XML validation
|
77 |
+
- **Test:** "Add a red box" → Box appears
|
78 |
+
|
79 |
+
#### Day 4: Console Reading
|
80 |
+
|
81 |
+
- [ ] Implement `read-console.ts`
|
82 |
+
- [ ] Filter and format messages
|
83 |
+
- **Test:** Agent reports console errors
|
84 |
+
|
85 |
+
#### Day 5-6: Tool Chaining
|
86 |
+
|
87 |
+
- [ ] Multiple tools per response
|
88 |
+
- [ ] Simple sequencing
|
89 |
+
- [ ] Context preservation
|
90 |
+
- **Test:** Multi-step operations work
|
91 |
+
|
92 |
+
#### Day 7: Testing & Stabilization
|
93 |
+
|
94 |
+
- [ ] Fix discovered bugs
|
95 |
+
- [ ] Improve error messages
|
96 |
+
- [ ] Add logging
|
97 |
+
- **Success Metrics:**
|
98 |
+
- ✅ Agent can describe game content
|
99 |
+
- ✅ Agent can add/modify elements
|
100 |
+
- ✅ Agent can read console
|
101 |
+
- ✅ 5 basic operations work reliably
|
102 |
+
|
103 |
+
### Phase 2: Error Detection & Correction (Days 8-14)
|
104 |
+
|
105 |
+
**Goal:** Agent detects and fixes simple errors
|
106 |
+
|
107 |
+
#### Day 8: Error Pattern Recognition
|
108 |
+
|
109 |
+
- [ ] Parse common VibeGame errors
|
110 |
+
- [ ] Categorize: syntax, physics, missing components
|
111 |
+
- **Test:** Agent identifies "no ground" error
|
112 |
+
|
113 |
+
#### Day 9: Simple Self-Correction
|
114 |
+
|
115 |
+
- [ ] Single retry on error
|
116 |
+
- [ ] Generate fix attempts
|
117 |
+
- **Test:** Agent adds missing ground
|
118 |
+
|
119 |
+
#### Day 10: Undo/Redo
|
120 |
+
|
121 |
+
- [ ] Edit history in editor store
|
122 |
+
- [ ] Rollback on failure
|
123 |
+
- **Test:** Failed edits can be undone
|
124 |
+
|
125 |
+
#### Day 11: Validation Tool
|
126 |
+
|
127 |
+
- [ ] Check required components
|
128 |
+
- [ ] Pre-validate changes
|
129 |
+
- **Test:** Prevents invalid states
|
130 |
+
|
131 |
+
#### Day 12-13: Better Prompting
|
132 |
+
|
133 |
+
- [ ] VibeGame-specific examples
|
134 |
+
- [ ] Few-shot learning
|
135 |
+
- **Test:** Higher first-attempt success
|
136 |
+
|
137 |
+
#### Day 14: Polish
|
138 |
+
|
139 |
+
- [ ] Clear error messages
|
140 |
+
- [ ] Better explanations
|
141 |
+
- **Success Metrics:**
|
142 |
+
- ✅ Agent detects common errors
|
143 |
+
- ✅ Agent fixes simple mistakes
|
144 |
+
- ✅ Edit history prevents data loss
|
145 |
+
- ✅ 80% success rate on common tasks
|
146 |
+
|
147 |
+
### Phase 3: Advanced Features (Days 15-21)
|
148 |
+
|
149 |
+
**Goal:** Faster, smarter, more capable
|
150 |
+
|
151 |
+
#### Day 15: Code-Based Tools
|
152 |
+
|
153 |
+
- [ ] Agent generates TypeScript for tools
|
154 |
+
- [ ] Controlled execution environment
|
155 |
+
- **Test:** More flexible tool usage
|
156 |
+
|
157 |
+
#### Day 16: Streaming Improvements
|
158 |
+
|
159 |
+
- [ ] Migrate to Server-Sent Events (SSE)
|
160 |
+
- [ ] Real-time tool visualization
|
161 |
+
- **Test:** Better feedback
|
162 |
+
|
163 |
+
#### Day 17: Quick Edit Model
|
164 |
+
|
165 |
+
- [ ] Route simple edits to Qwen3-Coder-7B
|
166 |
+
- [ ] Instant text/color changes
|
167 |
+
- **Test:** Sub-second simple edits
|
168 |
+
|
169 |
+
#### Day 18: Context Management
|
170 |
+
|
171 |
+
- [ ] Sliding window for long chats
|
172 |
+
- [ ] Iteration summarization
|
173 |
+
- **Test:** 50+ message sessions
|
174 |
+
|
175 |
+
#### Day 19: Template System
|
176 |
+
|
177 |
+
- [ ] Pre-built game templates
|
178 |
+
- [ ] Quick start patterns
|
179 |
+
- **Test:** "Make a platformer" works
|
180 |
+
|
181 |
+
#### Day 20: Thinking Visualization
|
182 |
+
|
183 |
+
- [ ] Show reasoning process
|
184 |
+
- [ ] Chain-of-thought UI
|
185 |
+
- **Test:** Users understand logic
|
186 |
+
|
187 |
+
#### Day 21: Final Polish
|
188 |
+
|
189 |
+
- [ ] Performance optimization
|
190 |
+
- [ ] Documentation
|
191 |
+
- **Success Metrics:**
|
192 |
+
- ✅ Sub-second simple edits
|
193 |
+
- ✅ Complex multi-step tasks work
|
194 |
+
- ✅ Users understand process
|
195 |
+
- ✅ Production-ready quality
|
196 |
+
|
197 |
+
## Tool Specifications
|
198 |
+
|
199 |
+
### Core Tool Interface (MCP-Compatible)
|
200 |
+
|
201 |
+
```typescript
|
202 |
+
interface Tool {
|
203 |
+
name: string;
|
204 |
+
description: string;
|
205 |
+
execute: (params: any) => Promise<ToolResult>;
|
206 |
+
}
|
207 |
+
|
208 |
+
interface ToolResult {
|
209 |
+
success: boolean;
|
210 |
+
data?: any;
|
211 |
+
error?: string;
|
212 |
+
}
|
213 |
+
```
|
214 |
+
|
215 |
+
### Initial Tool Set
|
216 |
+
|
217 |
+
1. **read_game_code** - Get current editor content
|
218 |
+
2. **edit_game_code** - Modify via search/replace
|
219 |
+
3. **read_console** - Get console messages
|
220 |
+
4. **validate_game** - Check VibeGame rules
|
221 |
+
|
222 |
+
### Future Tools
|
223 |
+
|
224 |
+
5. **list_entities** - Get all scene entities
|
225 |
+
6. **add_entity** - Add new entity
|
226 |
+
7. **remove_entity** - Delete entity
|
227 |
+
8. **test_physics** - Simulate and check
|
228 |
+
9. **reset_game** - Restart scene
|
229 |
+
|
230 |
+
## Model Strategy
|
231 |
+
|
232 |
+
### Phase 1: Single Model
|
233 |
+
|
234 |
+
- **Primary:** DeepSeek-R1-Distill-Qwen-1.5B (current)
|
235 |
+
- Simple prompting, basic tool use
|
236 |
+
|
237 |
+
### Phase 2: Fallback Chain
|
238 |
+
|
239 |
+
- **Primary:** DeepSeek-R1-Distill
|
240 |
+
- **Fallback:** Qwen3-Coder-7B
|
241 |
+
- Error recovery and retry logic
|
242 |
+
|
243 |
+
### Phase 3: Composite System
|
244 |
+
|
245 |
+
- **Complex Tasks:** ERNIE-4.5-21B-A3B-Thinking (128K context)
|
246 |
+
- **Quick Edits:** Qwen3-Coder-7B-Instruct
|
247 |
+
- **Error Fix:** Dedicated correction model
|
248 |
+
- **Streaming Post-Processor:** Real-time validation
|
249 |
+
|
250 |
+
## Key Improvements from Research
|
251 |
+
|
252 |
+
### From v0/Vercel
|
253 |
+
|
254 |
+
- Composite model architecture
|
255 |
+
- Streaming post-processing
|
256 |
+
- Quick edit optimization
|
257 |
+
- Real-time error correction
|
258 |
+
|
259 |
+
### From Smolagents
|
260 |
+
|
261 |
+
- Code-based tools (TypeScript instead of JSON)
|
262 |
+
- Direct execution without translation
|
263 |
+
- Simpler debugging
|
264 |
+
|
265 |
+
### From Modern Thinking Models
|
266 |
+
|
267 |
+
- 128K context windows
|
268 |
+
- Chain-of-thought reasoning
|
269 |
+
- Sparse MoE activation (3B active params)
|
270 |
+
|
271 |
+
### From MCP/Agentic Web
|
272 |
+
|
273 |
+
- Standardized tool protocols
|
274 |
+
- Browser-native execution
|
275 |
+
- SSE for streaming
|
276 |
+
- Direct store integration
|
277 |
+
|
278 |
+
## Testing Strategy
|
279 |
+
|
280 |
+
### Unit Tests
|
281 |
+
|
282 |
+
- Individual tool validation
|
283 |
+
- Mock stores for isolation
|
284 |
+
|
285 |
+
### Integration Tests
|
286 |
+
|
287 |
+
- Full flow: request → tools → response
|
288 |
+
- Multi-tool sequences
|
289 |
+
- Error recovery
|
290 |
+
|
291 |
+
### User Tests
|
292 |
+
|
293 |
+
- "Add jumping platforms"
|
294 |
+
- "Fix the bouncing balls"
|
295 |
+
- "Create a coin collector"
|
296 |
+
|
297 |
+
## Deployment
|
298 |
+
|
299 |
+
### Environment Variables
|
300 |
+
|
301 |
+
```env
|
302 |
+
HF_TOKEN=<user_token>
|
303 |
+
MODEL_PRIMARY=deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
|
304 |
+
MODEL_QUICK=Qwen/Qwen3-Coder-7B-Instruct
|
305 |
+
MAX_ITERATIONS=10
|
306 |
+
ITERATION_TIMEOUT=30000
|
307 |
+
PORT=7860
|
308 |
+
```
|
309 |
+
|
310 |
+
### Single Container (HuggingFace Spaces)
|
311 |
+
|
312 |
+
```dockerfile
|
313 |
+
FROM oven/bun:1-alpine
|
314 |
+
WORKDIR /app
|
315 |
+
COPY package.json bun.lockb ./
|
316 |
+
RUN bun install --frozen-lockfile
|
317 |
+
COPY . .
|
318 |
+
RUN bun run build
|
319 |
+
EXPOSE 7860
|
320 |
+
CMD ["bun", "run", "preview", "--host", "0.0.0.0", "--port", "7860"]
|
321 |
+
```
|
322 |
+
|
323 |
+
## Implementation Details
|
324 |
+
|
325 |
+
### Tool System Architecture
|
326 |
+
|
327 |
+
#### Pattern Recognition in Agent Responses
|
328 |
+
|
329 |
+
The agent will naturally include tool calls in its responses using a simple pattern:
|
330 |
+
|
331 |
+
```
|
332 |
+
[TOOL: tool_name {"param": "value"}]
|
333 |
+
```
|
334 |
+
|
335 |
+
Example agent response:
|
336 |
+
|
337 |
+
```
|
338 |
+
I'll check what's currently in your game.
|
339 |
+
|
340 |
+
[TOOL: read_game_code]
|
341 |
+
|
342 |
+
Based on the code, I can see you have a ground platform and a red ball...
|
343 |
+
```
|
344 |
+
|
345 |
+
#### Complete Tool Registry Implementation
|
346 |
+
|
347 |
+
```typescript
|
348 |
+
// src/lib/tools/registry.ts - FULL CODE TO CREATE
|
349 |
+
|
350 |
+
export interface Tool {
|
351 |
+
name: string;
|
352 |
+
description: string;
|
353 |
+
execute: (params: any) => Promise<ToolResult>;
|
354 |
+
}
|
355 |
+
|
356 |
+
export interface ToolResult {
|
357 |
+
success: boolean;
|
358 |
+
data?: any;
|
359 |
+
error?: string;
|
360 |
+
}
|
361 |
+
|
362 |
+
export interface ToolCall {
|
363 |
+
tool: string;
|
364 |
+
parameters?: any;
|
365 |
+
}
|
366 |
+
|
367 |
+
class ToolRegistry {
|
368 |
+
private tools: Map<string, Tool> = new Map();
|
369 |
+
|
370 |
+
register(tool: Tool) {
|
371 |
+
if (this.tools.has(tool.name)) {
|
372 |
+
console.warn(`Tool ${tool.name} already registered, overwriting`);
|
373 |
+
}
|
374 |
+
this.tools.set(tool.name, tool);
|
375 |
+
console.log(`Registered tool: ${tool.name}`);
|
376 |
+
}
|
377 |
+
|
378 |
+
async execute(toolName: string, params?: any): Promise<ToolResult> {
|
379 |
+
const tool = this.tools.get(toolName);
|
380 |
+
|
381 |
+
if (!tool) {
|
382 |
+
return {
|
383 |
+
success: false,
|
384 |
+
error: `Tool '${toolName}' not found. Available: ${Array.from(this.tools.keys()).join(", ")}`,
|
385 |
+
};
|
386 |
+
}
|
387 |
+
|
388 |
+
try {
|
389 |
+
console.log(`Executing tool: ${toolName}`, params);
|
390 |
+
const result = await tool.execute(params);
|
391 |
+
console.log(`Tool ${toolName} result:`, result);
|
392 |
+
return result;
|
393 |
+
} catch (error) {
|
394 |
+
console.error(`Tool ${toolName} error:`, error);
|
395 |
+
return {
|
396 |
+
success: false,
|
397 |
+
error: error instanceof Error ? error.message : "Unknown error",
|
398 |
+
};
|
399 |
+
}
|
400 |
+
}
|
401 |
+
|
402 |
+
getTools(): Tool[] {
|
403 |
+
return Array.from(this.tools.values());
|
404 |
+
}
|
405 |
+
|
406 |
+
getToolNames(): string[] {
|
407 |
+
return Array.from(this.tools.keys());
|
408 |
+
}
|
409 |
+
|
410 |
+
getToolDescriptions(): string {
|
411 |
+
return Array.from(this.tools.values())
|
412 |
+
.map((tool) => `- ${tool.name}: ${tool.description}`)
|
413 |
+
.join("\n");
|
414 |
+
}
|
415 |
+
}
|
416 |
+
|
417 |
+
// Global registry instance
|
418 |
+
export const toolRegistry = new ToolRegistry();
|
419 |
+
|
420 |
+
// Helper function to parse tool calls from agent response
|
421 |
+
export function parseToolCalls(response: string): ToolCall[] {
|
422 |
+
const toolCalls: ToolCall[] = [];
|
423 |
+
|
424 |
+
// Pattern: [TOOL: tool_name] or [TOOL: tool_name {"param": "value"}]
|
425 |
+
const toolPattern = /\[TOOL:\s*(\w+)(?:\s+({[^}]+}))?\]/g;
|
426 |
+
|
427 |
+
let match;
|
428 |
+
while ((match = toolPattern.exec(response)) !== null) {
|
429 |
+
const toolName = match[1];
|
430 |
+
const paramsStr = match[2];
|
431 |
+
|
432 |
+
let parameters = undefined;
|
433 |
+
if (paramsStr) {
|
434 |
+
try {
|
435 |
+
parameters = JSON.parse(paramsStr);
|
436 |
+
} catch (e) {
|
437 |
+
console.warn(`Failed to parse tool parameters: ${paramsStr}`);
|
438 |
+
}
|
439 |
+
}
|
440 |
+
|
441 |
+
toolCalls.push({ tool: toolName, parameters });
|
442 |
+
}
|
443 |
+
|
444 |
+
return toolCalls;
|
445 |
+
}
|
446 |
+
|
447 |
+
// Helper to format tool results for agent context
|
448 |
+
export function formatToolResult(toolName: string, result: ToolResult): string {
|
449 |
+
if (result.success) {
|
450 |
+
return `[TOOL_RESULT: ${toolName}]\n${JSON.stringify(result.data, null, 2)}\n[/TOOL_RESULT]`;
|
451 |
+
} else {
|
452 |
+
return `[TOOL_ERROR: ${toolName}]\n${result.error}\n[/TOOL_ERROR]`;
|
453 |
+
}
|
454 |
+
}
|
455 |
+
```
|
456 |
+
|
457 |
+
#### Tool Implementation Examples
|
458 |
+
|
459 |
+
**read-game-code.ts** (Ultra-simple first tool):
|
460 |
+
|
461 |
+
```typescript
|
462 |
+
import { editorStore } from "$lib/stores/editor";
|
463 |
+
import { get } from "svelte/store";
|
464 |
+
|
465 |
+
export const readGameCodeTool: Tool = {
|
466 |
+
name: "read_game_code",
|
467 |
+
description: "Get the current game code from the editor",
|
468 |
+
execute: async () => {
|
469 |
+
const state = get(editorStore);
|
470 |
+
return {
|
471 |
+
success: true,
|
472 |
+
data: {
|
473 |
+
content: state.content,
|
474 |
+
language: state.language,
|
475 |
+
},
|
476 |
+
};
|
477 |
+
},
|
478 |
+
};
|
479 |
+
```
|
480 |
+
|
481 |
+
**edit-game-code.ts** (Simple string replacement):
|
482 |
+
|
483 |
+
```typescript
|
484 |
+
export const editGameCodeTool: Tool = {
|
485 |
+
name: "edit_game_code",
|
486 |
+
description: "Edit game code using search and replace",
|
487 |
+
execute: async (params: { search: string; replace: string }) => {
|
488 |
+
const state = get(editorStore);
|
489 |
+
const newContent = state.content.replace(params.search, params.replace);
|
490 |
+
editorStore.setContent(newContent);
|
491 |
+
return {
|
492 |
+
success: true,
|
493 |
+
data: { modified: true, newContent },
|
494 |
+
};
|
495 |
+
},
|
496 |
+
};
|
497 |
+
```
|
498 |
+
|
499 |
+
#### Agent Runner Updates
|
500 |
+
|
501 |
+
Add to `src/lib/server/agent-runner.ts`:
|
502 |
+
|
503 |
+
```typescript
|
504 |
+
import { parseToolCalls, toolRegistry, formatToolResult } from '../tools/registry';
|
505 |
+
|
506 |
+
async processMessage(message: string, onStream?: (chunk: string) => void) {
|
507 |
+
// ... existing code ...
|
508 |
+
|
509 |
+
// After getting response, check for tool calls
|
510 |
+
const toolCalls = parseToolCalls(fullResponse);
|
511 |
+
|
512 |
+
if (toolCalls.length > 0) {
|
513 |
+
// Execute tools and add results to context
|
514 |
+
for (const call of toolCalls) {
|
515 |
+
const result = await toolRegistry.execute(call.tool, call.parameters);
|
516 |
+
const formatted = formatToolResult(call.tool, result);
|
517 |
+
|
518 |
+
// Add to message history for context
|
519 |
+
this.messageHistory.push({
|
520 |
+
id: this.generateId(),
|
521 |
+
role: 'system',
|
522 |
+
content: formatted,
|
523 |
+
timestamp: Date.now()
|
524 |
+
});
|
525 |
+
}
|
526 |
+
|
527 |
+
// Continue conversation with tool results
|
528 |
+
// (In phase 2, we'll make this recursive for self-correction)
|
529 |
+
}
|
530 |
+
}
|
531 |
+
```
|
532 |
+
|
533 |
+
#### System Prompt Enhancement
|
534 |
+
|
535 |
+
```typescript
|
536 |
+
const systemPrompt = `You are an AI assistant helping to build games with VibeGame.
|
537 |
+
|
538 |
+
Available tools:
|
539 |
+
${toolRegistry.getToolDescriptions()}
|
540 |
+
|
541 |
+
To use a tool, include in your response: [TOOL: tool_name {"param": "value"}]
|
542 |
+
|
543 |
+
Example:
|
544 |
+
- To read game code: [TOOL: read_game_code]
|
545 |
+
- To edit: [TOOL: edit_game_code {"search": "old text", "replace": "new text"}]
|
546 |
+
|
547 |
+
Always validate changes and ensure the game remains playable.
|
548 |
+
Use small, incremental changes rather than large rewrites.`;
|
549 |
+
```
|
550 |
+
|
551 |
+
### Testing Scenarios
|
552 |
+
|
553 |
+
#### Phase 1 Test Cases
|
554 |
+
|
555 |
+
1. **"What's in the game?"**
|
556 |
+
- Expected: Agent uses `read_game_code`, describes scene elements
|
557 |
+
|
558 |
+
2. **"Add a blue box at position 5,2,0"**
|
559 |
+
- Expected: Agent uses `edit_game_code` to insert `<dynamic-part>`
|
560 |
+
|
561 |
+
3. **"Change the ball color to green"**
|
562 |
+
- Expected: Agent finds and replaces color attribute
|
563 |
+
|
564 |
+
4. **"What errors are in the console?"**
|
565 |
+
- Expected: Agent uses `read_console` tool
|
566 |
+
|
567 |
+
5. **"Check the code and fix any issues"**
|
568 |
+
- Expected: Agent chains multiple tools
|
569 |
+
|
570 |
+
### Current File Structure
|
571 |
+
|
572 |
+
```
|
573 |
+
src/lib/
|
574 |
+
├── server/
|
575 |
+
│ ├── agent-runner.ts (✅ exists - needs tool integration)
|
576 |
+
│ └── api.ts (✅ exists - WebSocket handler)
|
577 |
+
├── tools/
|
578 |
+
│ └── (empty directory - needs implementation)
|
579 |
+
└── stores/
|
580 |
+
├── agent.ts (✅ exists)
|
581 |
+
├── editor.ts (✅ exists)
|
582 |
+
├── console.ts (✅ exists)
|
583 |
+
├── game.ts (✅ exists)
|
584 |
+
└── ui.ts (✅ exists)
|
585 |
+
```
|
586 |
+
|
587 |
+
### Target File Structure After Implementation
|
588 |
+
|
589 |
+
```
|
590 |
+
src/lib/
|
591 |
+
├── server/
|
592 |
+
│ ├── agent-runner.ts (updated with tool execution)
|
593 |
+
│ └── api.ts (existing WebSocket handler)
|
594 |
+
├── tools/
|
595 |
+
│ ├── registry.ts (to create)
|
596 |
+
│ ├── read-game-code.ts (to create)
|
597 |
+
│ ├── edit-game-code.ts (to create)
|
598 |
+
│ ├── read-console.ts (to create)
|
599 |
+
│ └── index.ts (to create - exports all tools)
|
600 |
+
└── stores/
|
601 |
+
└── (all existing)
|
602 |
+
```
|
603 |
+
|
604 |
+
## Next Immediate Steps
|
605 |
+
|
606 |
+
1. ✅ Update PLAN.md with implementation details
|
607 |
+
2. ⏳ Create tool registry (`src/lib/tools/registry.ts`)
|
608 |
+
3. ⏳ Implement read-game-code tool
|
609 |
+
4. ⏳ Update agent-runner for tool execution
|
610 |
+
5. ⏳ Test in UI with "What's in the game?"
|
611 |
+
|
612 |
+
## Success Criteria
|
613 |
+
|
614 |
+
### Week 1
|
615 |
+
|
616 |
+
- [ ] Basic tools working
|
617 |
+
- [ ] Agent can modify games
|
618 |
+
- [ ] Console feedback visible
|
619 |
+
|
620 |
+
### Week 2
|
621 |
+
|
622 |
+
- [ ] Error detection working
|
623 |
+
- [ ] Self-correction attempts
|
624 |
+
- [ ] Higher success rate
|
625 |
+
|
626 |
+
### Week 3
|
627 |
+
|
628 |
+
- [ ] Fast response times
|
629 |
+
- [ ] Advanced features
|
630 |
+
- [ ] Production ready
|
631 |
+
|
632 |
+
## Model Configuration Details
|
633 |
+
|
634 |
+
### HuggingFace Inference API Setup
|
635 |
+
|
636 |
+
#### Available Models (via Serverless API)
|
637 |
+
|
638 |
+
```typescript
|
639 |
+
// Phase 1: Simple model
|
640 |
+
const MODELS = {
|
641 |
+
primary: "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
|
642 |
+
|
643 |
+
// Phase 2: Fallback chain
|
644 |
+
fallback: "Qwen/Qwen2.5-Coder-7B-Instruct",
|
645 |
+
|
646 |
+
// Phase 3: Thinking models (require special handling)
|
647 |
+
thinking: {
|
648 |
+
ernie: "baidu/ERNIE-4.5-21B-A3B-Thinking", // Apache 2.0, 128K context
|
649 |
+
qwen: "Qwen/Qwen3-Next-80B-A3B-Thinking", // Longer thinking chains
|
650 |
+
},
|
651 |
+
};
|
652 |
+
```
|
653 |
+
|
654 |
+
#### Thinking Model Integration
|
655 |
+
|
656 |
+
For thinking models, parse and display reasoning:
|
657 |
+
|
658 |
+
```typescript
|
659 |
+
// Response may contain thinking process
|
660 |
+
const thinkingPattern = /<thinking>(.*?)<\/thinking>/s;
|
661 |
+
const match = response.match(thinkingPattern);
|
662 |
+
if (match) {
|
663 |
+
// Store thinking for UI display
|
664 |
+
const thinking = match[1];
|
665 |
+
// Extract actual response
|
666 |
+
const answer = response.replace(thinkingPattern, "");
|
667 |
+
}
|
668 |
+
```
|
669 |
+
|
670 |
+
### Advanced Features Implementation
|
671 |
+
|
672 |
+
#### Server-Sent Events (SSE) Migration
|
673 |
+
|
674 |
+
Replace WebSocket with SSE for simpler streaming:
|
675 |
+
|
676 |
+
```typescript
|
677 |
+
// src/lib/server/sse.ts
|
678 |
+
export function createSSEStream(req: Request) {
|
679 |
+
const stream = new ReadableStream({
|
680 |
+
start(controller) {
|
681 |
+
controller.enqueue('data: {"type":"connected"}\n\n');
|
682 |
+
},
|
683 |
+
async pull(controller) {
|
684 |
+
// Stream agent responses
|
685 |
+
const chunk = await getNextChunk();
|
686 |
+
controller.enqueue(`data: ${JSON.stringify(chunk)}\n\n`);
|
687 |
+
},
|
688 |
+
});
|
689 |
+
|
690 |
+
return new Response(stream, {
|
691 |
+
headers: {
|
692 |
+
"Content-Type": "text/event-stream",
|
693 |
+
"Cache-Control": "no-cache",
|
694 |
+
Connection: "keep-alive",
|
695 |
+
},
|
696 |
+
});
|
697 |
+
}
|
698 |
+
```
|
699 |
+
|
700 |
+
#### Code-Based Tool Execution (Smolagents-inspired)
|
701 |
+
|
702 |
+
Allow agent to write TypeScript code for tools:
|
703 |
+
|
704 |
+
```typescript
|
705 |
+
// Agent generates code like:
|
706 |
+
const code = `
|
707 |
+
const editor = await tools.readGameCode();
|
708 |
+
const content = editor.data.content;
|
709 |
+
const modified = content.replace('color="#ff0000"', 'color="#00ff00"');
|
710 |
+
await tools.editGameCode({ search: content, replace: modified });
|
711 |
+
return "Changed red to green";
|
712 |
+
`;
|
713 |
+
|
714 |
+
// Execute in sandboxed context
|
715 |
+
const sandbox = {
|
716 |
+
tools: toolRegistry,
|
717 |
+
console: { log: (...args) => consoleStore.addMessage("log", args.join(" ")) },
|
718 |
+
};
|
719 |
+
const result = await executeInSandbox(code, sandbox);
|
720 |
+
```
|
721 |
+
|
722 |
+
#### Composite Model Router
|
723 |
+
|
724 |
+
Route requests based on complexity:
|
725 |
+
|
726 |
+
```typescript
|
727 |
+
class ModelRouter {
|
728 |
+
async route(message: string, context: AgentMessage[]): Promise<string> {
|
729 |
+
const complexity = this.assessComplexity(message);
|
730 |
+
|
731 |
+
if (complexity === "simple") {
|
732 |
+
// Text changes, color modifications
|
733 |
+
return this.quickEditModel(message);
|
734 |
+
} else if (complexity === "complex") {
|
735 |
+
// Multi-step reasoning, debugging
|
736 |
+
return this.thinkingModel(message);
|
737 |
+
} else {
|
738 |
+
// Default
|
739 |
+
return this.primaryModel(message);
|
740 |
+
}
|
741 |
+
}
|
742 |
+
|
743 |
+
private assessComplexity(message: string): "simple" | "medium" | "complex" {
|
744 |
+
// Simple heuristics
|
745 |
+
if (message.match(/change|color|text|rename/i)) return "simple";
|
746 |
+
if (message.match(/debug|fix|error|why/i)) return "complex";
|
747 |
+
return "medium";
|
748 |
+
}
|
749 |
+
}
|
750 |
+
```
|
751 |
+
|
752 |
+
## Common Patterns & Solutions
|
753 |
+
|
754 |
+
### Pattern: Adding Game Elements
|
755 |
+
|
756 |
+
```typescript
|
757 |
+
// User: "Add a platform at 0,3,0"
|
758 |
+
// Agent response with tool:
|
759 |
+
`I'll add a platform at position 0,3,0 for you.
|
760 |
+
|
761 |
+
[TOOL: edit_game_code {"search": "</world>", "replace": " <static-part pos=\"0 3 0\" shape=\"box\" size=\"3 0.5 3\" color=\"#808080\"></static-part>\n</world>"}]
|
762 |
+
|
763 |
+
I've added a gray platform at position (0, 3, 0).`;
|
764 |
+
```
|
765 |
+
|
766 |
+
### Pattern: Debugging Errors
|
767 |
+
|
768 |
+
```typescript
|
769 |
+
// User: "The player keeps falling"
|
770 |
+
// Agent workflow:
|
771 |
+
1. [TOOL: read_game_code] - Check for ground
|
772 |
+
2. [TOOL: read_console] - Check for physics errors
|
773 |
+
3. Identify: No ground platform
|
774 |
+
4. [TOOL: edit_game_code] - Add ground
|
775 |
+
5. [TOOL: validate_game] - Verify fix
|
776 |
+
```
|
777 |
+
|
778 |
+
### Pattern: Complex Modifications
|
779 |
+
|
780 |
+
```typescript
|
781 |
+
// User: "Make this into a platformer"
|
782 |
+
// Agent uses template:
|
783 |
+
const platformerTemplate = `
|
784 |
+
<!-- Platforms -->
|
785 |
+
<static-part pos="-5 2 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
786 |
+
<static-part pos="0 4 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
787 |
+
<static-part pos="5 6 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
788 |
+
|
789 |
+
<!-- Moving platform -->
|
790 |
+
<kinematic-part pos="0 3 5" shape="box" size="4 0.5 4" color="#4169e1">
|
791 |
+
<tween target="body.pos-x" from="-10" to="10" duration="5" loop="ping-pong"></tween>
|
792 |
+
</kinematic-part>
|
793 |
+
`;
|
794 |
+
```
|
795 |
+
|
796 |
+
## Troubleshooting Guide
|
797 |
+
|
798 |
+
### Issue: Agent doesn't use tools
|
799 |
+
|
800 |
+
**Solution**: Enhance system prompt with examples
|
801 |
+
|
802 |
+
```typescript
|
803 |
+
const systemPrompt = `
|
804 |
+
IMPORTANT: You have tools available. Use them!
|
805 |
+
|
806 |
+
Examples of when to use tools:
|
807 |
+
- User asks "what's in the game?" → Use [TOOL: read_game_code]
|
808 |
+
- User says "add a box" → Use [TOOL: edit_game_code]
|
809 |
+
- User mentions "error" → Use [TOOL: read_console]
|
810 |
+
`;
|
811 |
+
```
|
812 |
+
|
813 |
+
### Issue: Tool execution fails
|
814 |
+
|
815 |
+
**Solution**: Add error recovery
|
816 |
+
|
817 |
+
```typescript
|
818 |
+
try {
|
819 |
+
const result = await tool.execute(params);
|
820 |
+
if (!result.success) {
|
821 |
+
// Retry with adjusted parameters
|
822 |
+
const adjusted = this.adjustParameters(params, result.error);
|
823 |
+
return await tool.execute(adjusted);
|
824 |
+
}
|
825 |
+
} catch (error) {
|
826 |
+
// Fallback to manual instruction
|
827 |
+
return {
|
828 |
+
success: false,
|
829 |
+
error: `Tool failed. Manual fix: ${this.getManualInstructions(tool.name)}`,
|
830 |
+
};
|
831 |
+
}
|
832 |
+
```
|
833 |
+
|
834 |
+
### Issue: Context overflow
|
835 |
+
|
836 |
+
**Solution**: Implement sliding window
|
837 |
+
|
838 |
+
```typescript
|
839 |
+
class ContextManager {
|
840 |
+
private maxTokens = 4000;
|
841 |
+
|
842 |
+
summarize(messages: AgentMessage[]): AgentMessage[] {
|
843 |
+
if (this.countTokens(messages) < this.maxTokens) {
|
844 |
+
return messages;
|
845 |
+
}
|
846 |
+
|
847 |
+
// Keep system, first user, and last N messages
|
848 |
+
const summary = this.createSummary(messages.slice(1, -5));
|
849 |
+
return [
|
850 |
+
messages[0], // system
|
851 |
+
{ role: "system", content: `Previous conversation summary: ${summary}` },
|
852 |
+
...messages.slice(-5), // recent context
|
853 |
+
];
|
854 |
+
}
|
855 |
+
}
|
856 |
+
```
|
857 |
+
|
858 |
+
## Resources
|
859 |
+
|
860 |
+
- [HuggingFace Inference API](https://huggingface.co/docs/api-inference/index)
|
861 |
+
- [MCP Specification](https://modelcontextprotocol.io/specification)
|
862 |
+
- [VibeGame Docs](llms.txt)
|
863 |
+
- [Smolagents](https://github.com/huggingface/smolagents)
|
864 |
+
- [AI SDK](https://ai-sdk.dev)
|
865 |
+
- [ERNIE-4.5 Model](https://huggingface.co/baidu/ERNIE-4.5-21B-A3B-Thinking)
|
866 |
+
- [Qwen3-Next Model](https://huggingface.co/Qwen/Qwen3-Next-80B-A3B-Thinking)
|
867 |
+
|
868 |
+
---
|
869 |
+
|
870 |
+
_This plan provides a complete roadmap for implementing an AI agent system for VibeGame game development. Start with Phase 1 for immediate value, then progressively add sophistication. Each iteration is designed to be completed in hours, not days, with immediate testing in the UI._
|
README.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: VibeGame
|
3 |
+
emoji: 🥕
|
4 |
+
colorFrom: purple
|
5 |
+
colorTo: green
|
6 |
+
sdk: docker
|
7 |
+
app_port: 7860
|
8 |
+
pinned: false
|
9 |
+
license: mit
|
10 |
+
short_description: Vibe code 3D games
|
11 |
+
hf_oauth: true
|
12 |
+
hf_oauth_expiration_minutes: 43200
|
13 |
+
hf_oauth_scopes:
|
14 |
+
- inference-api
|
15 |
+
---
|
16 |
+
|
17 |
+
# 🥕 VibeGame
|
18 |
+
|
19 |
+
Vibe code 3D games with [VibeGame](https://github.com/dylanebert/vibegame).
|
auth-callback.html
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>Authentication - VibeGame</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
margin: 0;
|
10 |
+
padding: 0;
|
11 |
+
background: linear-gradient(135deg, #0b0a09 0%, #0f0e0c 100%);
|
12 |
+
color: #ffffff;
|
13 |
+
font-family:
|
14 |
+
-apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", system-ui,
|
15 |
+
sans-serif;
|
16 |
+
display: flex;
|
17 |
+
justify-content: center;
|
18 |
+
align-items: center;
|
19 |
+
height: 100vh;
|
20 |
+
}
|
21 |
+
.container {
|
22 |
+
text-align: center;
|
23 |
+
padding: 2rem;
|
24 |
+
}
|
25 |
+
.spinner {
|
26 |
+
width: 40px;
|
27 |
+
height: 40px;
|
28 |
+
border: 4px solid rgba(255, 255, 255, 0.1);
|
29 |
+
border-top-color: #667eea;
|
30 |
+
border-radius: 50%;
|
31 |
+
animation: spin 1s linear infinite;
|
32 |
+
margin: 0 auto 1rem;
|
33 |
+
}
|
34 |
+
@keyframes spin {
|
35 |
+
to {
|
36 |
+
transform: rotate(360deg);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
h1 {
|
40 |
+
font-size: 1.5rem;
|
41 |
+
font-weight: 500;
|
42 |
+
margin: 0 0 0.5rem;
|
43 |
+
}
|
44 |
+
p {
|
45 |
+
color: rgba(255, 255, 255, 0.6);
|
46 |
+
margin: 0;
|
47 |
+
}
|
48 |
+
</style>
|
49 |
+
</head>
|
50 |
+
<body>
|
51 |
+
<div class="container">
|
52 |
+
<div class="spinner"></div>
|
53 |
+
<h1>Authenticating...</h1>
|
54 |
+
<p>Please wait while we complete your login.</p>
|
55 |
+
</div>
|
56 |
+
<script>
|
57 |
+
setTimeout(() => {
|
58 |
+
window.location.href = "/";
|
59 |
+
}, 5000);
|
60 |
+
</script>
|
61 |
+
</body>
|
62 |
+
</html>
|
bun.lock
ADDED
@@ -0,0 +1,769 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"lockfileVersion": 1,
|
3 |
+
"workspaces": {
|
4 |
+
"": {
|
5 |
+
"name": "vibegame",
|
6 |
+
"dependencies": {
|
7 |
+
"@huggingface/hub": "^2.6.3",
|
8 |
+
"@huggingface/inference": "^4.8.0",
|
9 |
+
"@types/node": "^24.3.3",
|
10 |
+
"gsap": "^3.13.0",
|
11 |
+
"monaco-editor": "^0.50.0",
|
12 |
+
"svelte-splitpanes": "^8.0.5",
|
13 |
+
"vibegame": "^0.1.1",
|
14 |
+
},
|
15 |
+
"devDependencies": {
|
16 |
+
"@eslint/js": "^9.33.0",
|
17 |
+
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
18 |
+
"@types/ws": "^8.18.1",
|
19 |
+
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
20 |
+
"@typescript-eslint/parser": "^8.40.0",
|
21 |
+
"eslint": "^9.33.0",
|
22 |
+
"eslint-config-prettier": "^10.1.8",
|
23 |
+
"eslint-plugin-import": "^2.32.0",
|
24 |
+
"eslint-plugin-prettier": "^5.5.4",
|
25 |
+
"prettier": "^3.6.2",
|
26 |
+
"svelte": "^4.2.18",
|
27 |
+
"svelte-check": "^3.8.4",
|
28 |
+
"typescript": "^5.6.0",
|
29 |
+
"vite": "^5.4.10",
|
30 |
+
"ws": "^8.18.3",
|
31 |
+
},
|
32 |
+
},
|
33 |
+
},
|
34 |
+
"packages": {
|
35 |
+
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
36 |
+
|
37 |
+
"@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.18.2", "", {}, "sha512-DTXrOsn3ra9ZonB+VyqJc16xnRXWsHVa5FK230Z+R1PJ7q8oSRmWQ+AU6e+IJYBHxkM0a5QVDqd729DyeEhHPA=="],
|
38 |
+
|
39 |
+
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="],
|
40 |
+
|
41 |
+
"@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="],
|
42 |
+
|
43 |
+
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="],
|
44 |
+
|
45 |
+
"@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="],
|
46 |
+
|
47 |
+
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="],
|
48 |
+
|
49 |
+
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="],
|
50 |
+
|
51 |
+
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="],
|
52 |
+
|
53 |
+
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="],
|
54 |
+
|
55 |
+
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="],
|
56 |
+
|
57 |
+
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="],
|
58 |
+
|
59 |
+
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="],
|
60 |
+
|
61 |
+
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="],
|
62 |
+
|
63 |
+
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="],
|
64 |
+
|
65 |
+
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="],
|
66 |
+
|
67 |
+
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="],
|
68 |
+
|
69 |
+
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="],
|
70 |
+
|
71 |
+
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="],
|
72 |
+
|
73 |
+
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="],
|
74 |
+
|
75 |
+
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="],
|
76 |
+
|
77 |
+
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="],
|
78 |
+
|
79 |
+
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="],
|
80 |
+
|
81 |
+
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="],
|
82 |
+
|
83 |
+
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="],
|
84 |
+
|
85 |
+
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
|
86 |
+
|
87 |
+
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
88 |
+
|
89 |
+
"@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
|
90 |
+
|
91 |
+
"@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
|
92 |
+
|
93 |
+
"@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
|
94 |
+
|
95 |
+
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
96 |
+
|
97 |
+
"@eslint/js": ["@eslint/js@9.35.0", "", {}, "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw=="],
|
98 |
+
|
99 |
+
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
100 |
+
|
101 |
+
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
|
102 |
+
|
103 |
+
"@huggingface/hub": ["@huggingface/hub@2.6.3", "", { "dependencies": { "@huggingface/tasks": "^0.19.45" }, "optionalDependencies": { "cli-progress": "^3.12.0" }, "bin": { "hfjs": "dist/cli.js" } }, "sha512-IEZ67adV+gWqg98A//mU0Ed+Q6xGPQxMfK+aV36b0Ww7R4EXG1O0zyiCcbLE/cvryfCD8+PNEwQgiPU+v63tsQ=="],
|
104 |
+
|
105 |
+
"@huggingface/inference": ["@huggingface/inference@4.8.0", "", { "dependencies": { "@huggingface/jinja": "^0.5.1", "@huggingface/tasks": "^0.19.45" } }, "sha512-Eq98EAXqYn4rKMfrbEXuhc3IjKfaeIO6eXNOZk9xk6v5akrIWRtd6d1h0fjAWyX4zRbdUpXRh6MvsqXnzGvXCA=="],
|
106 |
+
|
107 |
+
"@huggingface/jinja": ["@huggingface/jinja@0.5.1", "", {}, "sha512-yUZLld4lrM9iFxHCwFQ7D1HW2MWMwSbeB7WzWqFYDWK+rEb+WldkLdAJxUPOmgICMHZLzZGVcVjFh3w/YGubng=="],
|
108 |
+
|
109 |
+
"@huggingface/tasks": ["@huggingface/tasks@0.19.45", "", {}, "sha512-lM3QOgbfkGZ5gAZOYWOmzMM6BbKcXOIHjgnUAoymTdZEcEcGSr0vy/LWGEiK+vBXC4vU+sCT+WNoA/JZ8TEWdA=="],
|
110 |
+
|
111 |
+
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
112 |
+
|
113 |
+
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
|
114 |
+
|
115 |
+
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
116 |
+
|
117 |
+
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
118 |
+
|
119 |
+
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
120 |
+
|
121 |
+
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
122 |
+
|
123 |
+
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
124 |
+
|
125 |
+
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
126 |
+
|
127 |
+
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
128 |
+
|
129 |
+
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
130 |
+
|
131 |
+
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
132 |
+
|
133 |
+
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
|
134 |
+
|
135 |
+
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.50.1", "", { "os": "android", "cpu": "arm" }, "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag=="],
|
136 |
+
|
137 |
+
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.50.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw=="],
|
138 |
+
|
139 |
+
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.50.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw=="],
|
140 |
+
|
141 |
+
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.50.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw=="],
|
142 |
+
|
143 |
+
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.50.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA=="],
|
144 |
+
|
145 |
+
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.50.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ=="],
|
146 |
+
|
147 |
+
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg=="],
|
148 |
+
|
149 |
+
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.50.1", "", { "os": "linux", "cpu": "arm" }, "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw=="],
|
150 |
+
|
151 |
+
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw=="],
|
152 |
+
|
153 |
+
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.50.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w=="],
|
154 |
+
|
155 |
+
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q=="],
|
156 |
+
|
157 |
+
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.50.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q=="],
|
158 |
+
|
159 |
+
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ=="],
|
160 |
+
|
161 |
+
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.50.1", "", { "os": "linux", "cpu": "none" }, "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg=="],
|
162 |
+
|
163 |
+
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.50.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg=="],
|
164 |
+
|
165 |
+
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA=="],
|
166 |
+
|
167 |
+
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.50.1", "", { "os": "linux", "cpu": "x64" }, "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg=="],
|
168 |
+
|
169 |
+
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.50.1", "", { "os": "none", "cpu": "arm64" }, "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA=="],
|
170 |
+
|
171 |
+
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.50.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ=="],
|
172 |
+
|
173 |
+
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.50.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A=="],
|
174 |
+
|
175 |
+
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.50.1", "", { "os": "win32", "cpu": "x64" }, "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA=="],
|
176 |
+
|
177 |
+
"@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
|
178 |
+
|
179 |
+
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@3.1.2", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.10", "svelte-hmr": "^0.16.0", "vitefu": "^0.2.5" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA=="],
|
180 |
+
|
181 |
+
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@2.1.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.0" } }, "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg=="],
|
182 |
+
|
183 |
+
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
184 |
+
|
185 |
+
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
186 |
+
|
187 |
+
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
|
188 |
+
|
189 |
+
"@types/node": ["@types/node@24.3.3", "", { "dependencies": { "undici-types": "~7.10.0" } }, "sha512-GKBNHjoNw3Kra1Qg5UXttsY5kiWMEfoHq2TmXb+b1rcm6N7B3wTrFYIf/oSZ1xNQ+hVVijgLkiDZh7jRRsh+Gw=="],
|
190 |
+
|
191 |
+
"@types/pug": ["@types/pug@2.0.10", "", {}, "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA=="],
|
192 |
+
|
193 |
+
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
194 |
+
|
195 |
+
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="],
|
196 |
+
|
197 |
+
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.43.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw=="],
|
198 |
+
|
199 |
+
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.43.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.43.0", "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw=="],
|
200 |
+
|
201 |
+
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0" } }, "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg=="],
|
202 |
+
|
203 |
+
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.43.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA=="],
|
204 |
+
|
205 |
+
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg=="],
|
206 |
+
|
207 |
+
"@typescript-eslint/types": ["@typescript-eslint/types@8.43.0", "", {}, "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw=="],
|
208 |
+
|
209 |
+
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.43.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.43.0", "@typescript-eslint/tsconfig-utils": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw=="],
|
210 |
+
|
211 |
+
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.43.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g=="],
|
212 |
+
|
213 |
+
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw=="],
|
214 |
+
|
215 |
+
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
216 |
+
|
217 |
+
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
218 |
+
|
219 |
+
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
220 |
+
|
221 |
+
"ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
222 |
+
|
223 |
+
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
224 |
+
|
225 |
+
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
226 |
+
|
227 |
+
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
228 |
+
|
229 |
+
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
230 |
+
|
231 |
+
"array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
|
232 |
+
|
233 |
+
"array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
|
234 |
+
|
235 |
+
"array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="],
|
236 |
+
|
237 |
+
"array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
|
238 |
+
|
239 |
+
"array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
|
240 |
+
|
241 |
+
"arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
|
242 |
+
|
243 |
+
"async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
|
244 |
+
|
245 |
+
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
246 |
+
|
247 |
+
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
248 |
+
|
249 |
+
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
250 |
+
|
251 |
+
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
252 |
+
|
253 |
+
"bitecs": ["bitecs@0.3.40", "", {}, "sha512-wAylY4pNfX8IeIH5phtwt1lUNtHKrkoSNrArI7Ris2Y4nEQWFIVvXdgAuqprEg9bq8Wolmlj0gVfeG6MFmtI2Q=="],
|
254 |
+
|
255 |
+
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
256 |
+
|
257 |
+
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
258 |
+
|
259 |
+
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
260 |
+
|
261 |
+
"call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
|
262 |
+
|
263 |
+
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
264 |
+
|
265 |
+
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
|
266 |
+
|
267 |
+
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
268 |
+
|
269 |
+
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
270 |
+
|
271 |
+
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
272 |
+
|
273 |
+
"cli-progress": ["cli-progress@3.12.0", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="],
|
274 |
+
|
275 |
+
"code-red": ["code-red@1.0.4", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1", "acorn": "^8.10.0", "estree-walker": "^3.0.3", "periscopic": "^3.1.0" } }, "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw=="],
|
276 |
+
|
277 |
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
278 |
+
|
279 |
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
280 |
+
|
281 |
+
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
282 |
+
|
283 |
+
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
284 |
+
|
285 |
+
"css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="],
|
286 |
+
|
287 |
+
"data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
|
288 |
+
|
289 |
+
"data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
|
290 |
+
|
291 |
+
"data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
|
292 |
+
|
293 |
+
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
294 |
+
|
295 |
+
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
296 |
+
|
297 |
+
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
298 |
+
|
299 |
+
"define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
|
300 |
+
|
301 |
+
"define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
|
302 |
+
|
303 |
+
"detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="],
|
304 |
+
|
305 |
+
"doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
|
306 |
+
|
307 |
+
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
308 |
+
|
309 |
+
"emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
310 |
+
|
311 |
+
"es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="],
|
312 |
+
|
313 |
+
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
314 |
+
|
315 |
+
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
316 |
+
|
317 |
+
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
|
318 |
+
|
319 |
+
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
320 |
+
|
321 |
+
"es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
|
322 |
+
|
323 |
+
"es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
|
324 |
+
|
325 |
+
"es6-promise": ["es6-promise@3.3.1", "", {}, "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg=="],
|
326 |
+
|
327 |
+
"esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="],
|
328 |
+
|
329 |
+
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
330 |
+
|
331 |
+
"eslint": ["eslint@9.35.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg=="],
|
332 |
+
|
333 |
+
"eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="],
|
334 |
+
|
335 |
+
"eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
|
336 |
+
|
337 |
+
"eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="],
|
338 |
+
|
339 |
+
"eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="],
|
340 |
+
|
341 |
+
"eslint-plugin-prettier": ["eslint-plugin-prettier@5.5.4", "", { "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.11.7" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "optionalPeers": ["@types/eslint", "eslint-config-prettier"] }, "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg=="],
|
342 |
+
|
343 |
+
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
|
344 |
+
|
345 |
+
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
|
346 |
+
|
347 |
+
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
348 |
+
|
349 |
+
"esm-env-robust": ["esm-env-robust@0.0.3", "", { "dependencies": { "esm-env": "^1.0.0" } }, "sha512-90Gnuw2DALOqlL1581VxP3GHPUNHX9U+fQ+8FNcTTFClhY5gEggAAnJ3q1b2Oq23knRsjv8YpNeMRPaMLUymOA=="],
|
350 |
+
|
351 |
+
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
|
352 |
+
|
353 |
+
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
354 |
+
|
355 |
+
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
356 |
+
|
357 |
+
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
358 |
+
|
359 |
+
"estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
|
360 |
+
|
361 |
+
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
362 |
+
|
363 |
+
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
364 |
+
|
365 |
+
"fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
|
366 |
+
|
367 |
+
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
368 |
+
|
369 |
+
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
370 |
+
|
371 |
+
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
372 |
+
|
373 |
+
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
374 |
+
|
375 |
+
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
376 |
+
|
377 |
+
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
378 |
+
|
379 |
+
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
380 |
+
|
381 |
+
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
382 |
+
|
383 |
+
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
384 |
+
|
385 |
+
"for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
|
386 |
+
|
387 |
+
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
|
388 |
+
|
389 |
+
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
390 |
+
|
391 |
+
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
392 |
+
|
393 |
+
"function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
|
394 |
+
|
395 |
+
"functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
|
396 |
+
|
397 |
+
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
398 |
+
|
399 |
+
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
400 |
+
|
401 |
+
"get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
|
402 |
+
|
403 |
+
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
404 |
+
|
405 |
+
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
406 |
+
|
407 |
+
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
408 |
+
|
409 |
+
"globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
|
410 |
+
|
411 |
+
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
412 |
+
|
413 |
+
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
414 |
+
|
415 |
+
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
416 |
+
|
417 |
+
"gsap": ["gsap@3.13.0", "", {}, "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw=="],
|
418 |
+
|
419 |
+
"has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
|
420 |
+
|
421 |
+
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
422 |
+
|
423 |
+
"has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
|
424 |
+
|
425 |
+
"has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
|
426 |
+
|
427 |
+
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
428 |
+
|
429 |
+
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
430 |
+
|
431 |
+
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
432 |
+
|
433 |
+
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
434 |
+
|
435 |
+
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
436 |
+
|
437 |
+
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
438 |
+
|
439 |
+
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
|
440 |
+
|
441 |
+
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
442 |
+
|
443 |
+
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
|
444 |
+
|
445 |
+
"is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
|
446 |
+
|
447 |
+
"is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
|
448 |
+
|
449 |
+
"is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
|
450 |
+
|
451 |
+
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
452 |
+
|
453 |
+
"is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
|
454 |
+
|
455 |
+
"is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
|
456 |
+
|
457 |
+
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
|
458 |
+
|
459 |
+
"is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
|
460 |
+
|
461 |
+
"is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
|
462 |
+
|
463 |
+
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
464 |
+
|
465 |
+
"is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
|
466 |
+
|
467 |
+
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
468 |
+
|
469 |
+
"is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
|
470 |
+
|
471 |
+
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
472 |
+
|
473 |
+
"is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
|
474 |
+
|
475 |
+
"is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
|
476 |
+
|
477 |
+
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
478 |
+
|
479 |
+
"is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
|
480 |
+
|
481 |
+
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
482 |
+
|
483 |
+
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
484 |
+
|
485 |
+
"is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
|
486 |
+
|
487 |
+
"is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
|
488 |
+
|
489 |
+
"is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
|
490 |
+
|
491 |
+
"is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
|
492 |
+
|
493 |
+
"is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
|
494 |
+
|
495 |
+
"is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
|
496 |
+
|
497 |
+
"is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
|
498 |
+
|
499 |
+
"is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
|
500 |
+
|
501 |
+
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
502 |
+
|
503 |
+
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
504 |
+
|
505 |
+
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
506 |
+
|
507 |
+
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
508 |
+
|
509 |
+
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
510 |
+
|
511 |
+
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
512 |
+
|
513 |
+
"json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
|
514 |
+
|
515 |
+
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
516 |
+
|
517 |
+
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
518 |
+
|
519 |
+
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
520 |
+
|
521 |
+
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
522 |
+
|
523 |
+
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
524 |
+
|
525 |
+
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
526 |
+
|
527 |
+
"magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
|
528 |
+
|
529 |
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
530 |
+
|
531 |
+
"mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="],
|
532 |
+
|
533 |
+
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
534 |
+
|
535 |
+
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
536 |
+
|
537 |
+
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
|
538 |
+
|
539 |
+
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
540 |
+
|
541 |
+
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
542 |
+
|
543 |
+
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
544 |
+
|
545 |
+
"monaco-editor": ["monaco-editor@0.50.0", "", {}, "sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA=="],
|
546 |
+
|
547 |
+
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
548 |
+
|
549 |
+
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
550 |
+
|
551 |
+
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
552 |
+
|
553 |
+
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
554 |
+
|
555 |
+
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
556 |
+
|
557 |
+
"object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
|
558 |
+
|
559 |
+
"object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
|
560 |
+
|
561 |
+
"object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
|
562 |
+
|
563 |
+
"object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
|
564 |
+
|
565 |
+
"object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="],
|
566 |
+
|
567 |
+
"object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
|
568 |
+
|
569 |
+
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
570 |
+
|
571 |
+
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
572 |
+
|
573 |
+
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
|
574 |
+
|
575 |
+
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
576 |
+
|
577 |
+
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
578 |
+
|
579 |
+
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
580 |
+
|
581 |
+
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
582 |
+
|
583 |
+
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
584 |
+
|
585 |
+
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
586 |
+
|
587 |
+
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
588 |
+
|
589 |
+
"periscopic": ["periscopic@3.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^3.0.0", "is-reference": "^3.0.0" } }, "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw=="],
|
590 |
+
|
591 |
+
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
592 |
+
|
593 |
+
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
594 |
+
|
595 |
+
"possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
|
596 |
+
|
597 |
+
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
598 |
+
|
599 |
+
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
600 |
+
|
601 |
+
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
|
602 |
+
|
603 |
+
"prettier-linter-helpers": ["prettier-linter-helpers@1.0.0", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="],
|
604 |
+
|
605 |
+
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
606 |
+
|
607 |
+
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
608 |
+
|
609 |
+
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
610 |
+
|
611 |
+
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
612 |
+
|
613 |
+
"regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
|
614 |
+
|
615 |
+
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
616 |
+
|
617 |
+
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
618 |
+
|
619 |
+
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
620 |
+
|
621 |
+
"rimraf": ["rimraf@2.7.1", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w=="],
|
622 |
+
|
623 |
+
"rollup": ["rollup@4.50.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.50.1", "@rollup/rollup-android-arm64": "4.50.1", "@rollup/rollup-darwin-arm64": "4.50.1", "@rollup/rollup-darwin-x64": "4.50.1", "@rollup/rollup-freebsd-arm64": "4.50.1", "@rollup/rollup-freebsd-x64": "4.50.1", "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", "@rollup/rollup-linux-arm-musleabihf": "4.50.1", "@rollup/rollup-linux-arm64-gnu": "4.50.1", "@rollup/rollup-linux-arm64-musl": "4.50.1", "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", "@rollup/rollup-linux-ppc64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-gnu": "4.50.1", "@rollup/rollup-linux-riscv64-musl": "4.50.1", "@rollup/rollup-linux-s390x-gnu": "4.50.1", "@rollup/rollup-linux-x64-gnu": "4.50.1", "@rollup/rollup-linux-x64-musl": "4.50.1", "@rollup/rollup-openharmony-arm64": "4.50.1", "@rollup/rollup-win32-arm64-msvc": "4.50.1", "@rollup/rollup-win32-ia32-msvc": "4.50.1", "@rollup/rollup-win32-x64-msvc": "4.50.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA=="],
|
624 |
+
|
625 |
+
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
626 |
+
|
627 |
+
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
628 |
+
|
629 |
+
"safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
|
630 |
+
|
631 |
+
"safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
|
632 |
+
|
633 |
+
"safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
|
634 |
+
|
635 |
+
"sander": ["sander@0.5.1", "", { "dependencies": { "es6-promise": "^3.1.2", "graceful-fs": "^4.1.3", "mkdirp": "^0.5.1", "rimraf": "^2.5.2" } }, "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA=="],
|
636 |
+
|
637 |
+
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
638 |
+
|
639 |
+
"set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
|
640 |
+
|
641 |
+
"set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
|
642 |
+
|
643 |
+
"set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
|
644 |
+
|
645 |
+
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
646 |
+
|
647 |
+
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
648 |
+
|
649 |
+
"side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
|
650 |
+
|
651 |
+
"side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
|
652 |
+
|
653 |
+
"side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
|
654 |
+
|
655 |
+
"side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
|
656 |
+
|
657 |
+
"sorcery": ["sorcery@0.11.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", "buffer-crc32": "^1.0.0", "minimist": "^1.2.0", "sander": "^0.5.0" }, "bin": { "sorcery": "bin/sorcery" } }, "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ=="],
|
658 |
+
|
659 |
+
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
660 |
+
|
661 |
+
"stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
|
662 |
+
|
663 |
+
"string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
664 |
+
|
665 |
+
"string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
|
666 |
+
|
667 |
+
"string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
|
668 |
+
|
669 |
+
"string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
|
670 |
+
|
671 |
+
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
672 |
+
|
673 |
+
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
674 |
+
|
675 |
+
"strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="],
|
676 |
+
|
677 |
+
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
678 |
+
|
679 |
+
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
680 |
+
|
681 |
+
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
682 |
+
|
683 |
+
"svelte": ["svelte@4.2.20", "", { "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", "@jridgewell/trace-mapping": "^0.3.18", "@types/estree": "^1.0.1", "acorn": "^8.9.0", "aria-query": "^5.3.0", "axobject-query": "^4.0.0", "code-red": "^1.0.3", "css-tree": "^2.3.1", "estree-walker": "^3.0.3", "is-reference": "^3.0.1", "locate-character": "^3.0.0", "magic-string": "^0.30.4", "periscopic": "^3.1.0" } }, "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q=="],
|
684 |
+
|
685 |
+
"svelte-check": ["svelte-check@3.8.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.17", "chokidar": "^3.4.1", "picocolors": "^1.0.0", "sade": "^1.7.4", "svelte-preprocess": "^5.1.3", "typescript": "^5.0.3" }, "peerDependencies": { "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q=="],
|
686 |
+
|
687 |
+
"svelte-hmr": ["svelte-hmr@0.16.0", "", { "peerDependencies": { "svelte": "^3.19.0 || ^4.0.0" } }, "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA=="],
|
688 |
+
|
689 |
+
"svelte-preprocess": ["svelte-preprocess@5.1.4", "", { "dependencies": { "@types/pug": "^2.0.6", "detect-indent": "^6.1.0", "magic-string": "^0.30.5", "sorcery": "^0.11.0", "strip-indent": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.10.2", "coffeescript": "^2.5.1", "less": "^3.11.3 || ^4.0.0", "postcss": "^7 || ^8", "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", "pug": "^3.0.0", "sass": "^1.26.8", "stylus": "^0.55.0", "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["@babel/core", "coffeescript", "less", "postcss", "postcss-load-config", "pug", "sass", "stylus", "sugarss", "typescript"] }, "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA=="],
|
690 |
+
|
691 |
+
"svelte-splitpanes": ["svelte-splitpanes@8.0.9", "", { "dependencies": { "esm-env-robust": "0.0.3" }, "peerDependencies": { "svelte": "^4.2.19 || ^5.1.0" } }, "sha512-L3oLXTC99M191FInTXJ/f/2i0welRql1QuVbPaU8iy6nvCR6X9VyjHCsCpLqKGWHwqkWo/AM9CQ1c0nzlb+MkA=="],
|
692 |
+
|
693 |
+
"synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="],
|
694 |
+
|
695 |
+
"three": ["three@0.180.0", "", {}, "sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w=="],
|
696 |
+
|
697 |
+
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
698 |
+
|
699 |
+
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
700 |
+
|
701 |
+
"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
|
702 |
+
|
703 |
+
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
704 |
+
|
705 |
+
"typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
|
706 |
+
|
707 |
+
"typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
|
708 |
+
|
709 |
+
"typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
|
710 |
+
|
711 |
+
"typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
|
712 |
+
|
713 |
+
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
714 |
+
|
715 |
+
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
|
716 |
+
|
717 |
+
"undici-types": ["undici-types@7.10.0", "", {}, "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag=="],
|
718 |
+
|
719 |
+
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
720 |
+
|
721 |
+
"vibegame": ["vibegame@0.1.1", "", { "dependencies": { "@dimforge/rapier3d-compat": "^0.18.2", "gsap": "^3.13.0", "zod": "^4.1.5" }, "peerDependencies": { "bitecs": ">=0.3.40", "three": ">=0.170.0" } }, "sha512-HjWMlO4qUus9ChEBsD+abS9UGle/qiDZWukIqkve811N2oliKjWE7WYCDhqaDhWS18FaaYaY1a/Vc8yF0HaS1Q=="],
|
722 |
+
|
723 |
+
"vite": ["vite@5.4.20", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g=="],
|
724 |
+
|
725 |
+
"vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
|
726 |
+
|
727 |
+
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
728 |
+
|
729 |
+
"which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
|
730 |
+
|
731 |
+
"which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
|
732 |
+
|
733 |
+
"which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
|
734 |
+
|
735 |
+
"which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
|
736 |
+
|
737 |
+
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
738 |
+
|
739 |
+
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
740 |
+
|
741 |
+
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
742 |
+
|
743 |
+
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
744 |
+
|
745 |
+
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
746 |
+
|
747 |
+
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
748 |
+
|
749 |
+
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
750 |
+
|
751 |
+
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
752 |
+
|
753 |
+
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
754 |
+
|
755 |
+
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
756 |
+
|
757 |
+
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
758 |
+
|
759 |
+
"eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
760 |
+
|
761 |
+
"eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
762 |
+
|
763 |
+
"eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
|
764 |
+
|
765 |
+
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
766 |
+
|
767 |
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
768 |
+
}
|
769 |
+
}
|
eslint.config.js
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import js from "@eslint/js";
|
2 |
+
import tseslint from "@typescript-eslint/eslint-plugin";
|
3 |
+
import tsparser from "@typescript-eslint/parser";
|
4 |
+
import prettierConfig from "eslint-config-prettier";
|
5 |
+
import importPlugin from "eslint-plugin-import";
|
6 |
+
import prettier from "eslint-plugin-prettier";
|
7 |
+
|
8 |
+
export default [
|
9 |
+
js.configs.recommended,
|
10 |
+
{
|
11 |
+
files: ["**/*.ts", "**/*.tsx"],
|
12 |
+
languageOptions: {
|
13 |
+
parser: tsparser,
|
14 |
+
parserOptions: {
|
15 |
+
ecmaVersion: "latest",
|
16 |
+
sourceType: "module",
|
17 |
+
},
|
18 |
+
globals: {
|
19 |
+
console: "readonly",
|
20 |
+
window: "readonly",
|
21 |
+
document: "readonly",
|
22 |
+
HTMLElement: "readonly",
|
23 |
+
HTMLCanvasElement: "readonly",
|
24 |
+
KeyboardEvent: "readonly",
|
25 |
+
MouseEvent: "readonly",
|
26 |
+
WheelEvent: "readonly",
|
27 |
+
requestAnimationFrame: "readonly",
|
28 |
+
cancelAnimationFrame: "readonly",
|
29 |
+
performance: "readonly",
|
30 |
+
process: "readonly",
|
31 |
+
WebSocket: "readonly",
|
32 |
+
setTimeout: "readonly",
|
33 |
+
clearTimeout: "readonly",
|
34 |
+
setInterval: "readonly",
|
35 |
+
clearInterval: "readonly",
|
36 |
+
sessionStorage: "readonly",
|
37 |
+
localStorage: "readonly",
|
38 |
+
},
|
39 |
+
},
|
40 |
+
plugins: {
|
41 |
+
"@typescript-eslint": tseslint,
|
42 |
+
prettier: prettier,
|
43 |
+
import: importPlugin,
|
44 |
+
},
|
45 |
+
rules: {
|
46 |
+
...tseslint.configs.recommended.rules,
|
47 |
+
...prettierConfig.rules,
|
48 |
+
"prettier/prettier": "error",
|
49 |
+
"@typescript-eslint/no-unused-vars": [
|
50 |
+
"error",
|
51 |
+
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
52 |
+
],
|
53 |
+
"@typescript-eslint/explicit-function-return-type": "off",
|
54 |
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
55 |
+
"@typescript-eslint/no-explicit-any": "warn",
|
56 |
+
"no-console": "off",
|
57 |
+
},
|
58 |
+
},
|
59 |
+
{
|
60 |
+
ignores: ["dist/**", "node_modules/**"],
|
61 |
+
},
|
62 |
+
];
|
index.html
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>🥕 VibeGame</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
margin: 0;
|
10 |
+
padding: 0;
|
11 |
+
background: linear-gradient(135deg, #0b0a09 0%, #0f0e0c 100%);
|
12 |
+
color: #ffffff;
|
13 |
+
overflow: hidden;
|
14 |
+
min-height: 100vh;
|
15 |
+
}
|
16 |
+
|
17 |
+
#app {
|
18 |
+
opacity: 0;
|
19 |
+
animation: fadeIn 0.3s ease-out 0.1s forwards;
|
20 |
+
}
|
21 |
+
|
22 |
+
@keyframes fadeIn {
|
23 |
+
to {
|
24 |
+
opacity: 1;
|
25 |
+
}
|
26 |
+
}
|
27 |
+
</style>
|
28 |
+
</head>
|
29 |
+
<body>
|
30 |
+
<div id="app"></div>
|
31 |
+
<script type="module" src="/src/main.ts"></script>
|
32 |
+
</body>
|
33 |
+
</html>
|
layers/context-template.md
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Context Template
|
2 |
+
|
3 |
+
Concise context for any folder; link to code or subfolders for deeper context
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- [What this folder/module does]
|
8 |
+
|
9 |
+
## Layout
|
10 |
+
|
11 |
+
```
|
12 |
+
[FOLDER]/
|
13 |
+
context.md # This file, folder context (Tier 2)
|
14 |
+
├── [FILE1].[ext] # File
|
15 |
+
├── [FILE2].[ext] # File
|
16 |
+
├── [subfolder]/ # Subfolder
|
17 |
+
│ ├── context.md # Subfolder context
|
18 |
+
│ ├── [FILE3].[ext] # File
|
19 |
+
│ └── [FILE4].[ext] # File
|
20 |
+
```
|
21 |
+
|
22 |
+
## Scope
|
23 |
+
|
24 |
+
- [In-scope]
|
25 |
+
- [Out-of-scope]
|
26 |
+
|
27 |
+
## Entrypoints
|
28 |
+
|
29 |
+
- [Primary entry files or functions and when they are called]
|
30 |
+
|
31 |
+
## Dependencies
|
32 |
+
|
33 |
+
- [Internal modules]
|
34 |
+
- [External libraries/services]
|
layers/structure.md
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Project Structure
|
2 |
+
|
3 |
+
AI-assisted iterative game development environment with real-time feedback
|
4 |
+
|
5 |
+
## Stack
|
6 |
+
|
7 |
+
- Runtime: Bun
|
8 |
+
- Language: TypeScript
|
9 |
+
- UI Framework: Svelte
|
10 |
+
- Game Engine: VibeGame (3D physics, ECS)
|
11 |
+
- Code Editor: Monaco Editor
|
12 |
+
- AI Agent: Hugging Face Inference API
|
13 |
+
- WebSocket: ws (for real-time communication)
|
14 |
+
- Build: Vite
|
15 |
+
- Animation: GSAP (bundled with VibeGame)
|
16 |
+
- Physics: Rapier (via VibeGame)
|
17 |
+
- Rendering: Three.js (via VibeGame)
|
18 |
+
|
19 |
+
## Commands
|
20 |
+
|
21 |
+
- `bun dev` - Development server with hot reload on port 7860
|
22 |
+
- `bun run build` - Build for production
|
23 |
+
- `bun run preview` - Preview production build
|
24 |
+
- `bun run check` - TypeScript + Svelte type checking
|
25 |
+
- `bun run lint` - ESLint code quality check
|
26 |
+
- `bun run format` - Auto-format with Prettier
|
27 |
+
- `bun run validate` - Complete validation (format, lint, type check)
|
28 |
+
- `bun run update` - Update llms.txt from VibeGame
|
29 |
+
|
30 |
+
## Layout
|
31 |
+
|
32 |
+
```
|
33 |
+
vibegame/
|
34 |
+
├── CLAUDE.md # Global context (Tier 0)
|
35 |
+
├── PLAN.md # Development roadmap
|
36 |
+
├── layers/
|
37 |
+
│ ├── structure.md # Project structure (Tier 1)
|
38 |
+
│ └── context-template.md
|
39 |
+
├── src/
|
40 |
+
│ ├── main.ts # Svelte app initialization
|
41 |
+
│ ├── App.svelte # Root UI component (declarative)
|
42 |
+
│ └── lib/
|
43 |
+
│ ├── stores/ # State management
|
44 |
+
│ ├── services/ # Business logic
|
45 |
+
│ ├── server/ # WebSocket server & agent
|
46 |
+
│ ├── components/ # UI components
|
47 |
+
│ │ ├── chat/ # AI chat interface
|
48 |
+
│ │ ├── console/
|
49 |
+
│ │ ├── editor/
|
50 |
+
│ │ ├── game/
|
51 |
+
│ │ └── layout/
|
52 |
+
│ └── config/ # Configuration
|
53 |
+
├── index.html # Entry point
|
54 |
+
├── package.json # Dependencies and scripts
|
55 |
+
├── tsconfig.json # TypeScript configuration
|
56 |
+
├── vite.config.ts # Vite + Svelte + VibeGame config
|
57 |
+
├── bun.lock # Dependency lock file
|
58 |
+
├── llms.txt # VibeGame documentation
|
59 |
+
└── README.md
|
60 |
+
```
|
61 |
+
|
62 |
+
## Architecture
|
63 |
+
|
64 |
+
Three-layer architecture: UI (Svelte) → Agent (Hugging Face) → Game (VibeGame)
|
65 |
+
|
66 |
+
- **UI Layer**: Svelte components for chat, code editing, console display
|
67 |
+
- **Agent Layer**: Hugging Face models via WebSocket for game assistance
|
68 |
+
- **Game Layer**: VibeGame ECS with declarative XML scene definition
|
69 |
+
- **Feedback Loop**: Console output → Agent parsing → Self-correction (planned)
|
70 |
+
|
71 |
+
## Entry points
|
72 |
+
|
73 |
+
- Main entry: `index.html` (HTML shell)
|
74 |
+
- UI entry: `src/main.ts` (Svelte app mount)
|
75 |
+
- App root: `src/App.svelte` (Component tree root)
|
76 |
+
- Game definition: `<world>` tag in editor (Live-editable XML)
|
77 |
+
|
78 |
+
## Naming Conventions
|
79 |
+
|
80 |
+
TypeScript/Web standards with Svelte and ECS conventions
|
81 |
+
|
82 |
+
- Files: kebab-case for utilities, PascalCase for components (e.g., `Chat.svelte`, `mcp-tools.js`)
|
83 |
+
- Directories: lowercase (e.g., `src/`, `components/`, `stores/`)
|
84 |
+
- Svelte components: PascalCase (e.g., `Editor.svelte`, `Preview.svelte`)
|
85 |
+
- Stores: camelCase (e.g., `gameState`, `consoleMessages`)
|
86 |
+
- ECS Components: PascalCase (e.g., `Health`, `Transform`)
|
87 |
+
- Systems: PascalCase with "System" suffix (e.g., `HealthSystem`)
|
88 |
+
|
89 |
+
## Configuration
|
90 |
+
|
91 |
+
- TypeScript: `tsconfig.json` (Strict mode, ESNext target)
|
92 |
+
- Build: `vite.config.ts` (Vite with Svelte, VibeGame, WebSocket plugins)
|
93 |
+
- Dependencies: `package.json` (Svelte, VibeGame, Monaco, Hugging Face, ws)
|
94 |
+
- Game Scene: Live-editable in Monaco editor
|
95 |
+
|
96 |
+
## Where to add code
|
97 |
+
|
98 |
+
- UI components → `src/lib/components/[feature]/[Component].svelte`
|
99 |
+
- State management → `src/lib/stores/[store].ts`
|
100 |
+
- Business logic → `src/lib/services/[service].ts`
|
101 |
+
- Server/Agent logic → `src/lib/server/[module].ts`
|
102 |
+
- MCP tools → `src/lib/tools/[tool].ts`
|
103 |
+
- Configuration → `src/lib/config/[config].ts`
|
104 |
+
- Game entities → XML in Monaco editor
|
105 |
+
- Custom ECS components → `src/lib/game/components/[Component].ts`
|
106 |
+
- Custom systems → `src/lib/game/systems/[System].ts`
|
llms.txt
ADDED
@@ -0,0 +1,1107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# VibeGame
|
2 |
+
|
3 |
+
A 3D game engine with declarative XML syntax and ECS architecture. Start playing immediately with automatic player, camera, and lighting - just add a ground to prevent falling.
|
4 |
+
|
5 |
+
## Instant Playable Game
|
6 |
+
|
7 |
+
```html
|
8 |
+
<script src="https://cdn.jsdelivr.net/npm/vibegame@latest/dist/cdn/vibegame.standalone.iife.js"></script>
|
9 |
+
|
10 |
+
<world canvas="#game-canvas" sky="#87ceeb">
|
11 |
+
<!-- Ground (REQUIRED to prevent player falling) -->
|
12 |
+
<static-part pos="0 -0.5 0" shape="box" size="20 1 20" color="#90ee90"></static-part>
|
13 |
+
</world>
|
14 |
+
|
15 |
+
<canvas id="game-canvas"></canvas>
|
16 |
+
<script>
|
17 |
+
GAME.run();
|
18 |
+
</script>
|
19 |
+
```
|
20 |
+
|
21 |
+
This creates a complete game with:
|
22 |
+
- ✅ Player character (auto-created)
|
23 |
+
- ✅ Orbital camera (auto-created)
|
24 |
+
- ✅ Directional + ambient lighting (auto-created)
|
25 |
+
- ✅ Ground platform (you provide this)
|
26 |
+
- ✅ WASD movement, mouse camera, space to jump
|
27 |
+
|
28 |
+
## Development Setup
|
29 |
+
|
30 |
+
After installation with `npm create vibegame@latest my-game`:
|
31 |
+
|
32 |
+
```bash
|
33 |
+
cd my-game
|
34 |
+
bun dev # Start dev server with hot reload
|
35 |
+
```
|
36 |
+
|
37 |
+
### Project Structure
|
38 |
+
- **TypeScript** - Full TypeScript support with strict type checking
|
39 |
+
- **src/main.ts** - Entry point for your game
|
40 |
+
- **index.html** - HTML template with canvas element
|
41 |
+
- **vite.config.ts** - Build configuration
|
42 |
+
|
43 |
+
### Commands
|
44 |
+
- `bun dev` - Development server with hot reload
|
45 |
+
- `bun run build` - Production build
|
46 |
+
- `bun run preview` - Preview production build
|
47 |
+
- `bun run check` - TypeScript type checking
|
48 |
+
- `bun run lint` - Lint code with ESLint
|
49 |
+
- `bun run format` - Format code with Prettier
|
50 |
+
- `bun run update` - Update llms.txt after upgrading vibegame
|
51 |
+
|
52 |
+
## Physics Objects
|
53 |
+
|
54 |
+
```xml
|
55 |
+
<world canvas="#game-canvas">
|
56 |
+
<!-- 1. Static: Never moves (grounds, walls, platforms) -->
|
57 |
+
<static-part pos="0 -0.5 0" shape="box" size="20 1 20" color="#808080"></static-part>
|
58 |
+
|
59 |
+
<!-- 2. Dynamic: Falls with gravity (balls, crates, debris) -->
|
60 |
+
<dynamic-part pos="0 5 0" shape="sphere" size="1" color="#ff0000"></dynamic-part>
|
61 |
+
|
62 |
+
<!-- 3. Kinematic: Script-controlled movement (moving platforms, doors) -->
|
63 |
+
<kinematic-part pos="5 2 0" shape="box" size="3 0.5 3" color="#0000ff">
|
64 |
+
<!-- Animate the platform up and down -->
|
65 |
+
<tween target="body.pos-y" from="2" to="5" duration="3" loop="ping-pong"></tween>
|
66 |
+
</kinematic-part>
|
67 |
+
</world>
|
68 |
+
```
|
69 |
+
|
70 |
+
## CRITICAL: Physics Position vs Transform Position
|
71 |
+
|
72 |
+
<warning>
|
73 |
+
⚠️ **Physics bodies override transform positions!**
|
74 |
+
Always set position on the body, not the transform, for physics entities.
|
75 |
+
</warning>
|
76 |
+
|
77 |
+
```xml
|
78 |
+
<!-- ✅ BEST: Use recipe with pos shorthand -->
|
79 |
+
<dynamic-part pos="0 5 0" shape="sphere" size="1"></dynamic-part>
|
80 |
+
|
81 |
+
<!-- ❌ WRONG: Transform position ignored if body exists -->
|
82 |
+
<entity transform="pos: 0 5 0" body collider></entity> <!-- Falls to 0,0,0! -->
|
83 |
+
|
84 |
+
<!-- ✅ CORRECT: Set body position explicitly (if using raw entity) -->
|
85 |
+
<entity transform body="pos: 0 5 0" collider></entity>
|
86 |
+
```
|
87 |
+
|
88 |
+
## ECS Architecture Explained
|
89 |
+
|
90 |
+
Unlike traditional game engines with GameObjects, VibeGame uses Entity-Component-System:
|
91 |
+
|
92 |
+
- **Entities**: Just numbers (IDs), no data or behavior
|
93 |
+
- **Components**: Pure data containers (position, health, color)
|
94 |
+
- **Systems**: Functions that process entities with specific components
|
95 |
+
|
96 |
+
```typescript
|
97 |
+
// Component = Data only
|
98 |
+
const Health = GAME.defineComponent({
|
99 |
+
current: GAME.Types.f32,
|
100 |
+
max: GAME.Types.f32
|
101 |
+
});
|
102 |
+
|
103 |
+
// System = Logic only
|
104 |
+
const DamageSystem: GAME.System = {
|
105 |
+
update: (state) => {
|
106 |
+
const entities = GAME.defineQuery([Health])(state.world);
|
107 |
+
for (const entity of entities) {
|
108 |
+
Health.current[entity] -= 1 * state.time.delta;
|
109 |
+
if (Health.current[entity] <= 0) {
|
110 |
+
state.destroyEntity(entity);
|
111 |
+
}
|
112 |
+
}
|
113 |
+
}
|
114 |
+
};
|
115 |
+
```
|
116 |
+
|
117 |
+
## What's Auto-Created (Game Engine Defaults)
|
118 |
+
|
119 |
+
The engine automatically creates these if missing:
|
120 |
+
1. **Player** - Character with physics, controls, and respawn (at 0, 1, 0)
|
121 |
+
2. **Camera** - Orbital camera following the player
|
122 |
+
3. **Lighting** - Ambient + directional light with shadows
|
123 |
+
|
124 |
+
You only need to provide:
|
125 |
+
- **Ground/platforms** - Or the player falls forever
|
126 |
+
- **Game objects** - Whatever makes your game unique
|
127 |
+
|
128 |
+
### Override Auto-Creation (When Needed)
|
129 |
+
|
130 |
+
While auto-creation is recommended, you can manually create these for customization:
|
131 |
+
|
132 |
+
```xml
|
133 |
+
<world canvas="#game-canvas">
|
134 |
+
<static-part pos="0 -0.5 0" shape="box" size="20 1 20" color="#90ee90"></static-part>
|
135 |
+
|
136 |
+
<!-- Custom player spawn position and properties -->
|
137 |
+
<player pos="0 10 0" speed="8" jump-height="3"></player>
|
138 |
+
|
139 |
+
<!-- Custom camera settings -->
|
140 |
+
<camera orbit-camera="distance: 10; target-pitch: 0.5"></camera>
|
141 |
+
|
142 |
+
<!-- Custom lighting (or use <light> for both ambient + directional) -->
|
143 |
+
<ambient-light sky-color="#ff6b6b" ground-color="#4ecdc4" intensity="0.8"></ambient-light>
|
144 |
+
<directional-light color="#ffffff" intensity="0.5" direction="-1 -2 -1"></directional-light>
|
145 |
+
</world>
|
146 |
+
```
|
147 |
+
|
148 |
+
**Best Practice**: Use auto-creation unless you specifically need custom positions, properties, or multiple instances. The defaults are well-tuned for most games.
|
149 |
+
|
150 |
+
## Common Game Patterns
|
151 |
+
|
152 |
+
### Basic Platformer
|
153 |
+
```xml
|
154 |
+
<world canvas="#game-canvas">
|
155 |
+
<!-- Ground -->
|
156 |
+
<static-part pos="0 -0.5 0" shape="box" size="50 1 50" color="#90ee90"></static-part>
|
157 |
+
|
158 |
+
<!-- Platforms at different heights -->
|
159 |
+
<static-part pos="-5 2 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
160 |
+
<static-part pos="0 4 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
161 |
+
<static-part pos="5 6 0" shape="box" size="3 0.5 3" color="#808080"></static-part>
|
162 |
+
|
163 |
+
<!-- Moving platform -->
|
164 |
+
<kinematic-part pos="0 3 5" shape="box" size="4 0.5 4" color="#4169e1">
|
165 |
+
<tween target="body.pos-x" from="-10" to="10" duration="5" loop="ping-pong"></tween>
|
166 |
+
</kinematic-part>
|
167 |
+
|
168 |
+
<!-- Goal area -->
|
169 |
+
<static-part pos="10 8 0" shape="box" size="5 0.5 5" color="#ffd700"></static-part>
|
170 |
+
</world>
|
171 |
+
```
|
172 |
+
|
173 |
+
### Collectible Coins (Collision-based)
|
174 |
+
```xml
|
175 |
+
<world canvas="#game-canvas">
|
176 |
+
<static-part pos="0 -0.5 0" shape="box" size="20 1 20" color="#90ee90"></static-part>
|
177 |
+
|
178 |
+
<!-- Spinning coins -->
|
179 |
+
<kinematic-part pos="2 1 0" shape="cylinder" size="0.5 0.1 0.5" color="#ffd700">
|
180 |
+
<tween target="body.euler-y" from="0" to="360" duration="2" loop="loop"></tween>
|
181 |
+
</kinematic-part>
|
182 |
+
|
183 |
+
<kinematic-part pos="-2 1 0" shape="cylinder" size="0.5 0.1 0.5" color="#ffd700">
|
184 |
+
<tween target="body.euler-y" from="0" to="360" duration="2" loop="loop"></tween>
|
185 |
+
</kinematic-part>
|
186 |
+
</world>
|
187 |
+
```
|
188 |
+
|
189 |
+
### Physics Playground
|
190 |
+
```xml
|
191 |
+
<world canvas="#game-canvas">
|
192 |
+
<!-- Ground -->
|
193 |
+
<static-part pos="0 -0.5 0" shape="box" size="30 1 30" color="#90ee90"></static-part>
|
194 |
+
|
195 |
+
<!-- Walls -->
|
196 |
+
<static-part pos="15 5 0" shape="box" size="1 10 30" color="#808080"></static-part>
|
197 |
+
<static-part pos="-15 5 0" shape="box" size="1 10 30" color="#808080"></static-part>
|
198 |
+
<static-part pos="0 5 15" shape="box" size="30 10 1" color="#808080"></static-part>
|
199 |
+
<static-part pos="0 5 -15" shape="box" size="30 10 1" color="#808080"></static-part>
|
200 |
+
|
201 |
+
<!-- Spawn balls at different positions -->
|
202 |
+
<dynamic-part pos="-5 10 0" shape="sphere" size="1" color="#ff0000"></dynamic-part>
|
203 |
+
<dynamic-part pos="0 12 0" shape="sphere" size="1.5" color="#00ff00"></dynamic-part>
|
204 |
+
<dynamic-part pos="5 8 0" shape="sphere" size="0.8" color="#0000ff"></dynamic-part>
|
205 |
+
|
206 |
+
<!-- Bouncy ball (high restitution) -->
|
207 |
+
<dynamic-part pos="0 15 5" shape="sphere" size="1" color="#ff00ff"
|
208 |
+
collider="restitution: 0.9"></dynamic-part>
|
209 |
+
</world>
|
210 |
+
```
|
211 |
+
|
212 |
+
## Recipe Reference
|
213 |
+
|
214 |
+
| Recipe | Purpose | Key Attributes | Common Use |
|
215 |
+
|--------|---------|---------------|------------|
|
216 |
+
| `<static-part>` | Immovable objects | `pos`, `shape`, `size`, `color` | Grounds, walls, platforms |
|
217 |
+
| `<dynamic-part>` | Gravity-affected objects | `pos`, `shape`, `size`, `color`, `mass` | Balls, crates, falling objects |
|
218 |
+
| `<kinematic-part>` | Script-controlled physics | `pos`, `shape`, `size`, `color` | Moving platforms, doors |
|
219 |
+
| `<player>` | Player character | `pos`, `speed`, `jump-height` | Main character (auto-created) |
|
220 |
+
| `<entity>` | Base entity | Any components via attributes | Custom entities |
|
221 |
+
|
222 |
+
### Shape Options
|
223 |
+
- `box` - Rectangular solid (default)
|
224 |
+
- `sphere` - Ball shape
|
225 |
+
- `cylinder` - Cylindrical shape
|
226 |
+
- `capsule` - Pill shape (good for characters)
|
227 |
+
|
228 |
+
### Size Attribute
|
229 |
+
- Box: `size="width height depth"` or `size="2 1 2"`
|
230 |
+
- Sphere: `size="diameter"` or `size="1"`
|
231 |
+
- Cylinder: `size="diameter height"` or `size="1 2"`
|
232 |
+
- Broadcast: `size="2"` becomes `size="2 2 2"`
|
233 |
+
|
234 |
+
## How Recipes and Shorthands Work
|
235 |
+
|
236 |
+
### Everything is an Entity
|
237 |
+
Every XML tag creates an entity. Recipes like `<static-part>` are just shortcuts for `<entity>` with preset components.
|
238 |
+
|
239 |
+
```xml
|
240 |
+
<!-- These are equivalent: -->
|
241 |
+
<static-part pos="0 0 0" color="#ff0000"></static-part>
|
242 |
+
|
243 |
+
<entity
|
244 |
+
transform
|
245 |
+
body="type: fixed"
|
246 |
+
collider
|
247 |
+
renderer="color: 0xff0000"
|
248 |
+
pos="0 0 0"></entity>
|
249 |
+
```
|
250 |
+
|
251 |
+
### Component Attributes
|
252 |
+
Components are declared using bare attributes (no value means "use defaults"):
|
253 |
+
|
254 |
+
```xml
|
255 |
+
<!-- Bare attributes declare components with default values -->
|
256 |
+
<entity transform body collider renderer></entity>
|
257 |
+
|
258 |
+
<!-- Add properties to override defaults -->
|
259 |
+
<entity transform="pos-x: 5; pos-y: 2; pos-z: -3; scale: 2"></entity>
|
260 |
+
|
261 |
+
<!-- Mix bare and valued attributes -->
|
262 |
+
<entity transform="pos: 0 5 0" body="type: dynamic; mass: 10" collider renderer></entity>
|
263 |
+
|
264 |
+
<!-- Property groups -->
|
265 |
+
<entity transform="pos: 5 2 -3; scale: 2 2 2"></entity>
|
266 |
+
```
|
267 |
+
|
268 |
+
**Important**: Bare attributes like `transform` mean "include this component with default values", NOT "empty" or "disabled".
|
269 |
+
|
270 |
+
### Automatic Shorthand Expansion
|
271 |
+
Shorthands expand to ANY component with matching properties:
|
272 |
+
|
273 |
+
```xml
|
274 |
+
<!-- "pos" shorthand applies to components with posX, posY, posZ -->
|
275 |
+
<entity transform body pos="0 5 0"></entity>
|
276 |
+
<!-- Both transform AND body get pos values -->
|
277 |
+
|
278 |
+
<!-- "color" shorthand applies to renderer.color -->
|
279 |
+
<entity renderer color="#ff0000"></entity>
|
280 |
+
|
281 |
+
<!-- "size" shorthand (broadcasts single value) -->
|
282 |
+
<entity collider size="2"></entity>
|
283 |
+
<!-- Expands to: sizeX: 2, sizeY: 2, sizeZ: 2 -->
|
284 |
+
|
285 |
+
<!-- Multiple shorthands together -->
|
286 |
+
<entity transform body collider renderer pos="0 5 0" size="1" color="#ff0000"></entity>
|
287 |
+
```
|
288 |
+
|
289 |
+
### Recipe Internals
|
290 |
+
Recipes are registered component bundles with defaults:
|
291 |
+
|
292 |
+
```xml
|
293 |
+
<!-- What <dynamic-part> actually is: -->
|
294 |
+
<entity
|
295 |
+
transform
|
296 |
+
body="type: dynamic" <!-- Override -->
|
297 |
+
collider
|
298 |
+
renderer
|
299 |
+
respawn
|
300 |
+
></entity>
|
301 |
+
|
302 |
+
<!-- So this: -->
|
303 |
+
<dynamic-part pos="0 5 0" color="#ff0000"></dynamic-part>
|
304 |
+
|
305 |
+
<!-- Is really: -->
|
306 |
+
<entity
|
307 |
+
transform="pos: 0 5 0"
|
308 |
+
body="type: dynamic; pos: 0 5 0" <!-- pos applies to body too! -->
|
309 |
+
collider
|
310 |
+
renderer="color: 0xff0000"
|
311 |
+
respawn
|
312 |
+
></entity>
|
313 |
+
```
|
314 |
+
|
315 |
+
### Common Pitfall: Component Requirements
|
316 |
+
```xml
|
317 |
+
<!-- ❌ BAD: Missing required components -->
|
318 |
+
<entity pos="0 5 0"></entity> <!-- No transform component! -->
|
319 |
+
|
320 |
+
<!-- ✅ GOOD: Explicit components -->
|
321 |
+
<entity transform="pos: 0 5 0"></entity>
|
322 |
+
|
323 |
+
<!-- ✅ BEST: Use recipe with built-in components -->
|
324 |
+
<static-part pos="0 5 0"></static-part>
|
325 |
+
```
|
326 |
+
|
327 |
+
### Best Practices Summary
|
328 |
+
1. **Use recipes** (`<static-part>`, `<dynamic-part>`, etc.) instead of raw `<entity>` tags
|
329 |
+
2. **Use shorthands** (`pos`, `size`, `color`) for cleaner code
|
330 |
+
3. **Override only what you need** - recipes have good defaults
|
331 |
+
4. **Mix recipes with custom components** - e.g., `<dynamic-part health="max: 100">`
|
332 |
+
|
333 |
+
## Currently Supported Features
|
334 |
+
|
335 |
+
### ✅ What Works Well
|
336 |
+
- **Basic platforming** - Jump puzzles, obstacle courses
|
337 |
+
- **Physics interactions** - Balls, dominoes, stacking
|
338 |
+
- **Moving platforms** - Via kinematic bodies + tweening
|
339 |
+
- **Collectibles** - Using collision detection in systems
|
340 |
+
- **Third-person character control** - WASD + mouse camera
|
341 |
+
- **Gamepad support** - Xbox/PlayStation controllers
|
342 |
+
- **Visual effects** - Tweening colors, positions, rotations
|
343 |
+
|
344 |
+
### Example Prompts That Work
|
345 |
+
- "Create a platformer with moving platforms and collectible coins"
|
346 |
+
- "Make bouncing balls that collide with walls"
|
347 |
+
- "Build an obstacle course with rotating platforms"
|
348 |
+
- "Add falling crates that stack up"
|
349 |
+
- "Create a simple parkour level"
|
350 |
+
|
351 |
+
## Features Not Yet Built-In
|
352 |
+
|
353 |
+
### ❌ Engine Features Not Available
|
354 |
+
- **Multiplayer/Networking** - No server sync
|
355 |
+
- **Sound/Audio** - No audio system yet
|
356 |
+
- **Save/Load** - No persistence system
|
357 |
+
- **Inventory** - No item management
|
358 |
+
- **Dialog/NPCs** - No conversation system
|
359 |
+
- **AI/Pathfinding** - No enemy AI
|
360 |
+
- **Particles** - No particle effects
|
361 |
+
- **Custom shaders** - Fixed rendering pipeline
|
362 |
+
- **Terrain** - Use box platforms instead
|
363 |
+
|
364 |
+
### ✅ Available Through Web Platform
|
365 |
+
- **UI/HUD** - Use standard HTML/CSS overlays on the canvas
|
366 |
+
- **Animations** - GSAP is included for advanced UI animations
|
367 |
+
- **Score display** → HTML elements positioned over canvas
|
368 |
+
- **Menus** → Standard web UI (divs, buttons, etc.)
|
369 |
+
|
370 |
+
### Recommended Approaches
|
371 |
+
- **UI** → Position HTML elements over the canvas with CSS
|
372 |
+
- **Animations** → Use GSAP for smooth UI transitions
|
373 |
+
- **Level progression** → Reload with different XML or hide/show worlds
|
374 |
+
- **Enemy behavior** → Tweened movement patterns
|
375 |
+
- **Interactions** → Collision detection in custom systems
|
376 |
+
|
377 |
+
## Common Mistakes to Avoid
|
378 |
+
|
379 |
+
### ❌ Forgetting the Ground
|
380 |
+
```xml
|
381 |
+
<!-- BAD: No ground, player falls forever -->
|
382 |
+
<world canvas="#game-canvas">
|
383 |
+
<dynamic-part pos="0 5 0" shape="sphere"></dynamic-part>
|
384 |
+
</world>
|
385 |
+
```
|
386 |
+
|
387 |
+
### ❌ Setting Transform Position on Physics Objects
|
388 |
+
```xml
|
389 |
+
<!-- BAD: Transform position ignored -->
|
390 |
+
<entity transform="pos: 0 5 0" body collider></entity>
|
391 |
+
|
392 |
+
<!-- GOOD: Set body position (raw entity) -->
|
393 |
+
<entity transform body="pos: 0 5 0" collider></entity>
|
394 |
+
|
395 |
+
<!-- BEST: Use recipes with pos shorthand -->
|
396 |
+
<dynamic-part pos="0 5 0" shape="sphere"></dynamic-part>
|
397 |
+
```
|
398 |
+
|
399 |
+
### ❌ Missing World Tag
|
400 |
+
```xml
|
401 |
+
<!-- BAD: Entities outside world tag -->
|
402 |
+
<static-part pos="0 0 0" shape="box"></static-part>
|
403 |
+
|
404 |
+
<!-- GOOD: Everything inside world -->
|
405 |
+
<world canvas="#game-canvas">
|
406 |
+
<static-part pos="0 0 0" shape="box"></static-part>
|
407 |
+
</world>
|
408 |
+
```
|
409 |
+
|
410 |
+
### ❌ Wrong Physics Type
|
411 |
+
```xml
|
412 |
+
<!-- BAD: Dynamic platform (falls with gravity) -->
|
413 |
+
<dynamic-part pos="0 3 0" shape="box">
|
414 |
+
<tween target="body.pos-x" from="-5" to="5"></tween>
|
415 |
+
</dynamic-part>
|
416 |
+
|
417 |
+
<!-- GOOD: Kinematic for controlled movement -->
|
418 |
+
<kinematic-part pos="0 3 0" shape="box">
|
419 |
+
<tween target="body.pos-x" from="-5" to="5"></tween>
|
420 |
+
</kinematic-part>
|
421 |
+
```
|
422 |
+
|
423 |
+
## Custom Components and Systems
|
424 |
+
|
425 |
+
### Creating a Health System
|
426 |
+
```typescript
|
427 |
+
import * as GAME from 'vibegame';
|
428 |
+
|
429 |
+
// Define the component
|
430 |
+
const Health = GAME.defineComponent({
|
431 |
+
current: GAME.Types.f32,
|
432 |
+
max: GAME.Types.f32
|
433 |
+
});
|
434 |
+
|
435 |
+
// Create the system
|
436 |
+
const HealthSystem: GAME.System = {
|
437 |
+
update: (state) => {
|
438 |
+
const entities = GAME.defineQuery([Health])(state.world);
|
439 |
+
for (const entity of entities) {
|
440 |
+
// Regenerate health over time
|
441 |
+
if (Health.current[entity] < Health.max[entity]) {
|
442 |
+
Health.current[entity] += 5 * state.time.delta;
|
443 |
+
}
|
444 |
+
}
|
445 |
+
}
|
446 |
+
};
|
447 |
+
|
448 |
+
// Bundle as plugin
|
449 |
+
const HealthPlugin: GAME.Plugin = {
|
450 |
+
components: { Health },
|
451 |
+
systems: [HealthSystem],
|
452 |
+
config: {
|
453 |
+
defaults: {
|
454 |
+
"health": { current: 100, max: 100 }
|
455 |
+
}
|
456 |
+
}
|
457 |
+
};
|
458 |
+
|
459 |
+
// Use in game
|
460 |
+
GAME.withPlugin(HealthPlugin).run();
|
461 |
+
```
|
462 |
+
|
463 |
+
### Using in XML
|
464 |
+
```xml
|
465 |
+
<world canvas="#game-canvas">
|
466 |
+
<!-- Add health to a dynamic entity (best practice: use recipes) -->
|
467 |
+
<dynamic-part pos="0 2 0" shape="sphere" color="#ff0000"
|
468 |
+
health="current: 50; max: 100"></dynamic-part>
|
469 |
+
</world>
|
470 |
+
```
|
471 |
+
|
472 |
+
## State API Reference
|
473 |
+
|
474 |
+
Available in all systems via the `state` parameter:
|
475 |
+
|
476 |
+
### Entity Management
|
477 |
+
- `createEntity(): number` - Create new entity
|
478 |
+
- `destroyEntity(entity: number)` - Remove entity
|
479 |
+
- `query(...Components): number[]` - Find entities with components
|
480 |
+
|
481 |
+
### Component Operations
|
482 |
+
- `addComponent(entity, Component, data?)` - Add component
|
483 |
+
- `removeComponent(entity, Component)` - Remove component
|
484 |
+
- `hasComponent(entity, Component): boolean` - Check component
|
485 |
+
- `getComponent(name: string): Component | null` - Get by name
|
486 |
+
|
487 |
+
### Time
|
488 |
+
- `time.delta: number` - Frame time in seconds
|
489 |
+
- `time.elapsed: number` - Total time in seconds
|
490 |
+
- `time.fixed: number` - Fixed timestep (1/60)
|
491 |
+
|
492 |
+
### Physics Helpers
|
493 |
+
- `addComponent(entity, ApplyImpulse, {x, y, z})` - One-time push
|
494 |
+
- `addComponent(entity, ApplyForce, {x, y, z})` - Continuous force
|
495 |
+
- `addComponent(entity, KinematicMove, {x, y, z})` - Move kinematic
|
496 |
+
|
497 |
+
## Plugin System
|
498 |
+
|
499 |
+
### Using Specific Plugins
|
500 |
+
```typescript
|
501 |
+
import * as GAME from 'vibegame';
|
502 |
+
|
503 |
+
// Start with no plugins
|
504 |
+
GAME
|
505 |
+
.withoutDefaultPlugins()
|
506 |
+
.withPlugin(TransformsPlugin) // Just transforms
|
507 |
+
.withPlugin(RenderingPlugin) // Add rendering
|
508 |
+
.withPlugin(PhysicsPlugin) // Add physics
|
509 |
+
.run();
|
510 |
+
```
|
511 |
+
|
512 |
+
### Default Plugin Bundle
|
513 |
+
- **RecipesPlugin** - XML parsing and entity creation
|
514 |
+
- **TransformsPlugin** - Position, rotation, scale, hierarchy
|
515 |
+
- **RenderingPlugin** - Three.js meshes, lights, camera
|
516 |
+
- **PhysicsPlugin** - Rapier physics simulation
|
517 |
+
- **InputPlugin** - Keyboard, mouse, gamepad input
|
518 |
+
- **OrbitCameraPlugin** - Third-person camera
|
519 |
+
- **PlayerPlugin** - Character controller
|
520 |
+
- **TweenPlugin** - Animation system
|
521 |
+
- **RespawnPlugin** - Fall detection and reset
|
522 |
+
- **StartupPlugin** - Auto-create player/camera/lights
|
523 |
+
|
524 |
+
## Module Documentation
|
525 |
+
|
526 |
+
### Core
|
527 |
+
Math utilities for interpolation and 3D transformations.
|
528 |
+
|
529 |
+
### Animation
|
530 |
+
Procedural character animation with body parts that respond to movement states.
|
531 |
+
|
532 |
+
### Input
|
533 |
+
Focus-aware input handling for mouse, keyboard, and gamepad with buffered actions. Keyboard input only responds when canvas has focus.
|
534 |
+
|
535 |
+
### Orbit Camera
|
536 |
+
Orbital camera controller for third-person views and smooth target following.
|
537 |
+
|
538 |
+
### Physics
|
539 |
+
3D physics simulation with Rapier including rigid bodies, collisions, and character controllers.
|
540 |
+
|
541 |
+
### Player
|
542 |
+
Complete player character controller with physics movement and jumping.
|
543 |
+
|
544 |
+
### Recipes
|
545 |
+
Foundation for declarative XML entity creation with parent-child hierarchies and attribute shorthands.
|
546 |
+
|
547 |
+
### Rendering
|
548 |
+
Three.js rendering pipeline with meshes, lights, and cameras.
|
549 |
+
|
550 |
+
### Respawn
|
551 |
+
Automatic respawn system that resets entities when falling below Y=-100.
|
552 |
+
|
553 |
+
### Startup
|
554 |
+
Auto-creates player, camera, and lighting entities at startup if missing.
|
555 |
+
|
556 |
+
### Transforms
|
557 |
+
3D transforms with position, rotation, scale, and parent-child hierarchies.
|
558 |
+
|
559 |
+
### Tweening
|
560 |
+
Animates component properties with easing functions and loop modes.
|
561 |
+
|
562 |
+
## Plugin Reference
|
563 |
+
|
564 |
+
### Core
|
565 |
+
|
566 |
+
### Functions
|
567 |
+
|
568 |
+
#### lerp(a, b, t): number
|
569 |
+
Linear interpolation
|
570 |
+
|
571 |
+
#### slerp(fromX, fromY, fromZ, fromW, toX, toY, toZ, toW, t): Quaternion
|
572 |
+
Quaternion spherical interpolation
|
573 |
+
|
574 |
+
### Animation
|
575 |
+
|
576 |
+
### Components
|
577 |
+
|
578 |
+
#### AnimatedCharacter
|
579 |
+
- headEntity: eid
|
580 |
+
- torsoEntity: eid
|
581 |
+
- leftArmEntity: eid
|
582 |
+
- rightArmEntity: eid
|
583 |
+
- leftLegEntity: eid
|
584 |
+
- rightLegEntity: eid
|
585 |
+
- phase: f32 - Walk cycle phase (0-1)
|
586 |
+
- jumpTime: f32
|
587 |
+
- fallTime: f32
|
588 |
+
- animationState: ui8 - 0=IDLE, 1=WALKING, 2=JUMPING, 3=FALLING, 4=LANDING
|
589 |
+
- stateTransition: f32
|
590 |
+
|
591 |
+
#### HasAnimator
|
592 |
+
Tag component (no properties)
|
593 |
+
|
594 |
+
### Systems
|
595 |
+
|
596 |
+
#### AnimatedCharacterInitializationSystem
|
597 |
+
- Group: setup
|
598 |
+
- Creates body part entities for AnimatedCharacter components
|
599 |
+
|
600 |
+
#### AnimatedCharacterUpdateSystem
|
601 |
+
- Group: simulation
|
602 |
+
- Updates character animation based on movement and physics state
|
603 |
+
|
604 |
+
### Input
|
605 |
+
|
606 |
+
### Components
|
607 |
+
|
608 |
+
#### InputState
|
609 |
+
- moveX: f32 - Horizontal axis (-1 left, 1 right)
|
610 |
+
- moveY: f32 - Forward/backward (-1 back, 1 forward)
|
611 |
+
- moveZ: f32 - Vertical axis (-1 down, 1 up)
|
612 |
+
- lookX: f32 - Mouse delta X
|
613 |
+
- lookY: f32 - Mouse delta Y
|
614 |
+
- scrollDelta: f32 - Mouse wheel delta
|
615 |
+
- jump: ui8 - Jump available (0/1)
|
616 |
+
- primaryAction: ui8 - Primary action (0/1)
|
617 |
+
- secondaryAction: ui8 - Secondary action (0/1)
|
618 |
+
- leftMouse: ui8 - Left button (0/1)
|
619 |
+
- rightMouse: ui8 - Right button (0/1)
|
620 |
+
- middleMouse: ui8 - Middle button (0/1)
|
621 |
+
- jumpBufferTime: f32
|
622 |
+
- primaryBufferTime: f32
|
623 |
+
- secondaryBufferTime: f32
|
624 |
+
|
625 |
+
### Systems
|
626 |
+
|
627 |
+
#### InputSystem
|
628 |
+
- Group: simulation
|
629 |
+
- Updates InputState components with current input data
|
630 |
+
|
631 |
+
### Functions
|
632 |
+
|
633 |
+
#### setTargetCanvas(canvas: HTMLCanvasElement | null): void
|
634 |
+
Registers canvas for focus-based keyboard input
|
635 |
+
|
636 |
+
#### consumeJump(): boolean
|
637 |
+
Consumes buffered jump input
|
638 |
+
|
639 |
+
#### consumePrimary(): boolean
|
640 |
+
Consumes buffered primary action
|
641 |
+
|
642 |
+
#### consumeSecondary(): boolean
|
643 |
+
Consumes buffered secondary action
|
644 |
+
|
645 |
+
#### handleMouseMove(event: MouseEvent): void
|
646 |
+
Processes mouse movement
|
647 |
+
|
648 |
+
#### handleMouseDown(event: MouseEvent): void
|
649 |
+
Processes mouse button press
|
650 |
+
|
651 |
+
#### handleMouseUp(event: MouseEvent): void
|
652 |
+
Processes mouse button release
|
653 |
+
|
654 |
+
#### handleWheel(event: WheelEvent): void
|
655 |
+
Processes mouse wheel
|
656 |
+
|
657 |
+
### Constants
|
658 |
+
|
659 |
+
#### INPUT_CONFIG
|
660 |
+
Default input mappings and sensitivity settings
|
661 |
+
|
662 |
+
### Orbit Camera
|
663 |
+
|
664 |
+
### Components
|
665 |
+
|
666 |
+
#### OrbitCamera
|
667 |
+
- target: eid (0) - Target entity ID
|
668 |
+
- current-yaw: f32 (π) - Current horizontal angle
|
669 |
+
- current-pitch: f32 (π/6) - Current vertical angle
|
670 |
+
- current-distance: f32 (4) - Current distance
|
671 |
+
- target-yaw: f32 (π) - Target horizontal angle
|
672 |
+
- target-pitch: f32 (π/6) - Target vertical angle
|
673 |
+
- target-distance: f32 (4) - Target distance
|
674 |
+
- min-distance: f32 (1)
|
675 |
+
- max-distance: f32 (25)
|
676 |
+
- min-pitch: f32 (0)
|
677 |
+
- max-pitch: f32 (π/2)
|
678 |
+
- smoothness: f32 (0.5) - Interpolation speed
|
679 |
+
- offset-x: f32 (0)
|
680 |
+
- offset-y: f32 (1.25)
|
681 |
+
- offset-z: f32 (0)
|
682 |
+
|
683 |
+
### Systems
|
684 |
+
|
685 |
+
#### OrbitCameraSystem
|
686 |
+
- Group: draw
|
687 |
+
- Updates camera position and rotation around target
|
688 |
+
|
689 |
+
### Recipes
|
690 |
+
|
691 |
+
#### camera
|
692 |
+
- Creates orbital camera with default settings
|
693 |
+
- Components: orbit-camera, transform, world-transform, main-camera
|
694 |
+
|
695 |
+
### Physics
|
696 |
+
|
697 |
+
### Constants
|
698 |
+
|
699 |
+
- DEFAULT_GRAVITY: -60
|
700 |
+
|
701 |
+
### Enums
|
702 |
+
|
703 |
+
#### BodyType
|
704 |
+
- Dynamic = 0 - Affected by forces
|
705 |
+
- Fixed = 1 - Immovable static
|
706 |
+
- KinematicPositionBased = 2 - Script position
|
707 |
+
- KinematicVelocityBased = 3 - Script velocity
|
708 |
+
|
709 |
+
#### ColliderShape
|
710 |
+
- Box = 0
|
711 |
+
- Sphere = 1
|
712 |
+
- Capsule = 2
|
713 |
+
|
714 |
+
### Components
|
715 |
+
|
716 |
+
#### PhysicsWorld
|
717 |
+
- gravityX: f32 (0)
|
718 |
+
- gravityY: f32 (-60)
|
719 |
+
- gravityZ: f32 (0)
|
720 |
+
|
721 |
+
#### Body
|
722 |
+
- type: ui8 - BodyType enum (Fixed)
|
723 |
+
- mass: f32 (1)
|
724 |
+
- linearDamping: f32 (0)
|
725 |
+
- angularDamping: f32 (0)
|
726 |
+
- gravityScale: f32 (1)
|
727 |
+
- ccd: ui8 (0)
|
728 |
+
- lockRotX: ui8 (0)
|
729 |
+
- lockRotY: ui8 (0)
|
730 |
+
- lockRotZ: ui8 (0)
|
731 |
+
- posX, posY, posZ: f32
|
732 |
+
- rotX, rotY, rotZ, rotW: f32 (rotW=1)
|
733 |
+
- eulerX, eulerY, eulerZ: f32
|
734 |
+
- velX, velY, velZ: f32
|
735 |
+
- rotVelX, rotVelY, rotVelZ: f32
|
736 |
+
|
737 |
+
#### Collider
|
738 |
+
- shape: ui8 - ColliderShape enum (Box)
|
739 |
+
- sizeX, sizeY, sizeZ: f32 (1)
|
740 |
+
- radius: f32 (0.5)
|
741 |
+
- height: f32 (1)
|
742 |
+
- friction: f32 (0.5)
|
743 |
+
- restitution: f32 (0)
|
744 |
+
- density: f32 (1)
|
745 |
+
- isSensor: ui8 (0)
|
746 |
+
- membershipGroups: ui16 (0xffff)
|
747 |
+
- filterGroups: ui16 (0xffff)
|
748 |
+
- posOffsetX, posOffsetY, posOffsetZ: f32
|
749 |
+
- rotOffsetX, rotOffsetY, rotOffsetZ, rotOffsetW: f32 (rotOffsetW=1)
|
750 |
+
|
751 |
+
#### CharacterController
|
752 |
+
- offset: f32 (0.08)
|
753 |
+
- maxSlope: f32 (45°)
|
754 |
+
- maxSlide: f32 (30°)
|
755 |
+
- snapDist: f32 (0.5)
|
756 |
+
- autoStep: ui8 (1)
|
757 |
+
- maxStepHeight: f32 (0.3)
|
758 |
+
- minStepWidth: f32 (0.05)
|
759 |
+
- upX, upY, upZ: f32 (upY=1)
|
760 |
+
- moveX, moveY, moveZ: f32
|
761 |
+
- grounded: ui8
|
762 |
+
|
763 |
+
#### CharacterMovement
|
764 |
+
- desiredVelX, desiredVelY, desiredVelZ: f32
|
765 |
+
- velocityY: f32
|
766 |
+
- actualMoveX, actualMoveY, actualMoveZ: f32
|
767 |
+
|
768 |
+
#### InterpolatedTransform
|
769 |
+
- prevPosX, prevPosY, prevPosZ: f32
|
770 |
+
- prevRotX, prevRotY, prevRotZ, prevRotW: f32
|
771 |
+
- posX, posY, posZ: f32
|
772 |
+
- rotX, rotY, rotZ, rotW: f32
|
773 |
+
|
774 |
+
#### Force/Impulse Components
|
775 |
+
- ApplyForce: x, y, z (f32)
|
776 |
+
- ApplyTorque: x, y, z (f32)
|
777 |
+
- ApplyImpulse: x, y, z (f32)
|
778 |
+
- ApplyAngularImpulse: x, y, z (f32)
|
779 |
+
- SetLinearVelocity: x, y, z (f32)
|
780 |
+
- SetAngularVelocity: x, y, z (f32)
|
781 |
+
- KinematicMove: x, y, z (f32)
|
782 |
+
- KinematicRotate: x, y, z, w (f32)
|
783 |
+
|
784 |
+
#### Collision Events
|
785 |
+
- CollisionEvents: activeEvents (ui8)
|
786 |
+
- TouchedEvent: other, handle1, handle2 (ui32)
|
787 |
+
- TouchEndedEvent: other, handle1, handle2 (ui32)
|
788 |
+
|
789 |
+
### Systems
|
790 |
+
|
791 |
+
- PhysicsWorldSystem - Initializes physics world
|
792 |
+
- PhysicsInitializationSystem - Creates bodies and colliders
|
793 |
+
- CharacterMovementSystem - Character controller movement
|
794 |
+
- PhysicsCleanupSystem - Removes physics on entity destroy
|
795 |
+
- CollisionEventCleanupSystem - Clears collision events
|
796 |
+
- ApplyForcesSystem - Applies forces
|
797 |
+
- ApplyTorquesSystem - Applies torques
|
798 |
+
- ApplyImpulsesSystem - Applies impulses
|
799 |
+
- ApplyAngularImpulsesSystem - Applies angular impulses
|
800 |
+
- SetVelocitySystem - Sets velocities
|
801 |
+
- TeleportationSystem - Instant position changes
|
802 |
+
- KinematicMovementSystem - Kinematic movement
|
803 |
+
- PhysicsStepSystem - Steps simulation
|
804 |
+
- PhysicsRapierSyncSystem - Syncs Rapier to ECS
|
805 |
+
- PhysicsInterpolationSystem - Interpolates for rendering
|
806 |
+
|
807 |
+
### Functions
|
808 |
+
|
809 |
+
#### initializePhysics(): Promise<void>
|
810 |
+
Initializes Rapier WASM physics engine
|
811 |
+
|
812 |
+
### Recipes
|
813 |
+
|
814 |
+
- static-part - Immovable physics objects
|
815 |
+
- dynamic-part - Gravity-affected objects
|
816 |
+
- kinematic-part - Script-controlled objects
|
817 |
+
|
818 |
+
### Player
|
819 |
+
|
820 |
+
### Components
|
821 |
+
|
822 |
+
#### Player
|
823 |
+
- speed: f32 (5.3)
|
824 |
+
- jumpHeight: f32 (2.3)
|
825 |
+
- rotationSpeed: f32 (10)
|
826 |
+
- canJump: ui8 (1)
|
827 |
+
- isJumping: ui8 (0)
|
828 |
+
- jumpCooldown: f32 (0)
|
829 |
+
- lastGroundedTime: f32 (0)
|
830 |
+
- jumpBufferTime: f32 (-10000)
|
831 |
+
- cameraSensitivity: f32 (0.007)
|
832 |
+
- cameraZoomSensitivity: f32 (1.5)
|
833 |
+
- cameraEntity: eid (0)
|
834 |
+
|
835 |
+
### Systems
|
836 |
+
|
837 |
+
#### PlayerMovementSystem
|
838 |
+
- Group: fixed
|
839 |
+
- Handles movement, rotation, and jumping from input
|
840 |
+
|
841 |
+
#### PlayerGroundedSystem
|
842 |
+
- Group: fixed
|
843 |
+
- Tracks grounded state and jump availability
|
844 |
+
|
845 |
+
#### PlayerCameraLinkingSystem
|
846 |
+
- Group: simulation
|
847 |
+
- Links player to orbit camera
|
848 |
+
|
849 |
+
#### PlayerCameraControlSystem
|
850 |
+
- Group: simulation
|
851 |
+
- Camera control via mouse input
|
852 |
+
|
853 |
+
### Recipes
|
854 |
+
|
855 |
+
#### player
|
856 |
+
- Complete player setup with physics
|
857 |
+
- Components: player, character-movement, transform, world-transform, body, collider, character-controller, input-state, respawn
|
858 |
+
|
859 |
+
### Functions
|
860 |
+
|
861 |
+
#### processInput(moveForward, moveRight, cameraYaw): Vector3
|
862 |
+
Converts input to world-space movement
|
863 |
+
|
864 |
+
#### handleJump(entity, jumpPressed, currentTime): number
|
865 |
+
Processes jump with buffering
|
866 |
+
|
867 |
+
#### updateRotation(entity, inputVector, deltaTime, rotationData): Quaternion
|
868 |
+
Smooth rotation towards movement
|
869 |
+
|
870 |
+
### Recipes
|
871 |
+
|
872 |
+
### Components
|
873 |
+
|
874 |
+
#### Parent
|
875 |
+
- entity: i32 - Parent entity ID
|
876 |
+
|
877 |
+
### Functions
|
878 |
+
|
879 |
+
#### parseXMLToEntities(state, xmlContent): EntityCreationResult[]
|
880 |
+
Converts XML elements to ECS entities with hierarchy
|
881 |
+
|
882 |
+
#### createEntityFromRecipe(state, recipeName, attributes?): number
|
883 |
+
Creates entity from recipe with attributes
|
884 |
+
|
885 |
+
#### fromEuler(x, y, z): Quaternion
|
886 |
+
Converts Euler angles (radians) to quaternion
|
887 |
+
|
888 |
+
### Types
|
889 |
+
|
890 |
+
#### EntityCreationResult
|
891 |
+
- entity: number - Entity ID
|
892 |
+
- tagName: string - Recipe name
|
893 |
+
- children: EntityCreationResult[]
|
894 |
+
|
895 |
+
### Recipes
|
896 |
+
|
897 |
+
#### entity
|
898 |
+
- Base recipe with no default components
|
899 |
+
|
900 |
+
### Property Formats
|
901 |
+
|
902 |
+
- Single value: `transform="scale: 2"`
|
903 |
+
- Vector3: `transform="pos: 0 5 -3"`
|
904 |
+
- Broadcast: `transform="scale: 2"` → scale: 2 2 2
|
905 |
+
- Euler angles: `transform="euler: 0 45 0"` (degrees)
|
906 |
+
- Multiple: `transform="pos: 0 5 0; euler: 0 45 0"`
|
907 |
+
- Shorthands: `pos="0 5 0"` → transform component
|
908 |
+
|
909 |
+
### Rendering
|
910 |
+
|
911 |
+
### Components
|
912 |
+
|
913 |
+
#### Renderer
|
914 |
+
- shape: ui8 - 0=box, 1=sphere, 2=cylinder, 3=plane
|
915 |
+
- sizeX, sizeY, sizeZ: f32 (1)
|
916 |
+
- color: ui32 (0xffffff)
|
917 |
+
- visible: ui8 (1)
|
918 |
+
|
919 |
+
#### RenderContext
|
920 |
+
- clearColor: ui32 (0x000000)
|
921 |
+
- hasCanvas: ui8
|
922 |
+
|
923 |
+
#### MainCamera
|
924 |
+
Tag component (no properties)
|
925 |
+
|
926 |
+
#### Ambient
|
927 |
+
- skyColor: ui32 (0x87ceeb)
|
928 |
+
- groundColor: ui32 (0x4a4a4a)
|
929 |
+
- intensity: f32 (0.6)
|
930 |
+
|
931 |
+
#### Directional
|
932 |
+
- color: ui32 (0xffffff)
|
933 |
+
- intensity: f32 (1)
|
934 |
+
- castShadow: ui8 (1)
|
935 |
+
- shadowMapSize: ui32 (4096)
|
936 |
+
- directionX: f32 (-1)
|
937 |
+
- directionY: f32 (2)
|
938 |
+
- directionZ: f32 (-1)
|
939 |
+
- distance: f32 (30)
|
940 |
+
|
941 |
+
### Systems
|
942 |
+
|
943 |
+
#### MeshInstanceSystem
|
944 |
+
- Group: draw
|
945 |
+
- Synchronizes transforms with Three.js meshes
|
946 |
+
|
947 |
+
#### LightSyncSystem
|
948 |
+
- Group: draw
|
949 |
+
- Updates Three.js lights
|
950 |
+
|
951 |
+
#### CameraSyncSystem
|
952 |
+
- Group: draw
|
953 |
+
- Updates Three.js camera
|
954 |
+
|
955 |
+
#### WebGLRenderSystem
|
956 |
+
- Group: draw (last)
|
957 |
+
- Renders scene to canvas
|
958 |
+
|
959 |
+
### Functions
|
960 |
+
|
961 |
+
#### setCanvasElement(entity, canvas): void
|
962 |
+
Associates canvas with RenderContext
|
963 |
+
|
964 |
+
### Recipes
|
965 |
+
|
966 |
+
- ambient-light - Ambient hemisphere lighting
|
967 |
+
- directional-light - Directional light with shadows
|
968 |
+
- light - Both ambient and directional
|
969 |
+
|
970 |
+
### Respawn
|
971 |
+
|
972 |
+
### Components
|
973 |
+
|
974 |
+
#### Respawn
|
975 |
+
- posX, posY, posZ: f32 - Spawn position
|
976 |
+
- eulerX, eulerY, eulerZ: f32 - Spawn rotation (degrees)
|
977 |
+
|
978 |
+
### Systems
|
979 |
+
|
980 |
+
#### RespawnSystem
|
981 |
+
- Group: simulation
|
982 |
+
- Resets entities when Y < -100
|
983 |
+
|
984 |
+
### Startup
|
985 |
+
|
986 |
+
### Systems
|
987 |
+
|
988 |
+
#### LightingStartupSystem
|
989 |
+
- Group: setup
|
990 |
+
- Creates default lighting if none exists
|
991 |
+
|
992 |
+
#### CameraStartupSystem
|
993 |
+
- Group: setup
|
994 |
+
- Creates main camera if none exists
|
995 |
+
|
996 |
+
#### PlayerStartupSystem
|
997 |
+
- Group: setup
|
998 |
+
- Creates player entity if none exists
|
999 |
+
|
1000 |
+
#### PlayerCharacterSystem
|
1001 |
+
- Group: setup
|
1002 |
+
- Adds animated character to players
|
1003 |
+
|
1004 |
+
### Transforms
|
1005 |
+
|
1006 |
+
### Components
|
1007 |
+
|
1008 |
+
#### Transform
|
1009 |
+
- posX, posY, posZ: f32 (0)
|
1010 |
+
- rotX, rotY, rotZ, rotW: f32 (rotW=1) - Quaternion
|
1011 |
+
- eulerX, eulerY, eulerZ: f32 (0) - Degrees
|
1012 |
+
- scaleX, scaleY, scaleZ: f32 (1)
|
1013 |
+
|
1014 |
+
#### WorldTransform
|
1015 |
+
- Same properties as Transform
|
1016 |
+
- Auto-computed from hierarchy (read-only)
|
1017 |
+
|
1018 |
+
### Systems
|
1019 |
+
|
1020 |
+
#### TransformHierarchySystem
|
1021 |
+
- Group: simulation (last)
|
1022 |
+
- Syncs euler/quaternion and computes world transforms
|
1023 |
+
|
1024 |
+
### Tweening
|
1025 |
+
|
1026 |
+
### Components
|
1027 |
+
|
1028 |
+
#### Tween
|
1029 |
+
- duration: f32 (1) - Seconds
|
1030 |
+
- elapsed: f32
|
1031 |
+
- easingIndex: ui8
|
1032 |
+
- loopMode: ui8 - 0=Once, 1=Loop, 2=PingPong
|
1033 |
+
|
1034 |
+
#### TweenValue
|
1035 |
+
- source: ui32 - Tween entity
|
1036 |
+
- target: ui32 - Target entity
|
1037 |
+
- componentId: ui32
|
1038 |
+
- fieldIndex: ui32
|
1039 |
+
- from: f32
|
1040 |
+
- to: f32
|
1041 |
+
- value: f32 - Current value
|
1042 |
+
|
1043 |
+
### Systems
|
1044 |
+
|
1045 |
+
#### TweenSystem
|
1046 |
+
- Group: simulation
|
1047 |
+
- Interpolates values with easing and auto-cleanup
|
1048 |
+
|
1049 |
+
### Functions
|
1050 |
+
|
1051 |
+
#### createTween(state, entity, target, options): number | null
|
1052 |
+
Animates component property
|
1053 |
+
|
1054 |
+
### Easing Functions
|
1055 |
+
|
1056 |
+
- linear
|
1057 |
+
- sine-in, sine-out, sine-in-out
|
1058 |
+
- quad-in, quad-out, quad-in-out
|
1059 |
+
- cubic-in, cubic-out, cubic-in-out
|
1060 |
+
- quart-in, quart-out, quart-in-out
|
1061 |
+
- expo-in, expo-out, expo-in-out
|
1062 |
+
- circ-in, circ-out, circ-in-out
|
1063 |
+
- back-in, back-out, back-in-out
|
1064 |
+
- elastic-in, elastic-out, elastic-in-out
|
1065 |
+
- bounce-in, bounce-out, bounce-in-out
|
1066 |
+
|
1067 |
+
### Loop Modes
|
1068 |
+
|
1069 |
+
- once - Play once and destroy
|
1070 |
+
- loop - Repeat indefinitely
|
1071 |
+
- ping-pong - Alternate directions
|
1072 |
+
|
1073 |
+
### Shorthand Targets
|
1074 |
+
|
1075 |
+
- rotation - body.eulerX/Y/Z
|
1076 |
+
- at - body.posX/Y/Z
|
1077 |
+
- scale - transform.scaleX/Y/Z
|
1078 |
+
|
1079 |
+
## API Reference (External Links)
|
1080 |
+
|
1081 |
+
- [Core](https://dylanebert.github.io/vibegame/reference/core)
|
1082 |
+
- [Animation](https://dylanebert.github.io/vibegame/reference/animation)
|
1083 |
+
- [Input](https://dylanebert.github.io/vibegame/reference/input)
|
1084 |
+
- [Orbit Camera](https://dylanebert.github.io/vibegame/reference/orbit-camera)
|
1085 |
+
- [Physics](https://dylanebert.github.io/vibegame/reference/physics)
|
1086 |
+
- [Player](https://dylanebert.github.io/vibegame/reference/player)
|
1087 |
+
- [Recipes](https://dylanebert.github.io/vibegame/reference/recipes)
|
1088 |
+
- [Rendering](https://dylanebert.github.io/vibegame/reference/rendering)
|
1089 |
+
- [Respawn](https://dylanebert.github.io/vibegame/reference/respawn)
|
1090 |
+
- [Startup](https://dylanebert.github.io/vibegame/reference/startup)
|
1091 |
+
- [Transforms](https://dylanebert.github.io/vibegame/reference/transforms)
|
1092 |
+
- [Tweening](https://dylanebert.github.io/vibegame/reference/tweening)
|
1093 |
+
|
1094 |
+
## Examples (External Links)
|
1095 |
+
|
1096 |
+
- [Core](https://dylanebert.github.io/vibegame/examples/core)
|
1097 |
+
- [Animation](https://dylanebert.github.io/vibegame/examples/animation)
|
1098 |
+
- [Input](https://dylanebert.github.io/vibegame/examples/input)
|
1099 |
+
- [Orbit Camera](https://dylanebert.github.io/vibegame/examples/orbit-camera)
|
1100 |
+
- [Physics](https://dylanebert.github.io/vibegame/examples/physics)
|
1101 |
+
- [Player](https://dylanebert.github.io/vibegame/examples/player)
|
1102 |
+
- [Recipes](https://dylanebert.github.io/vibegame/examples/recipes)
|
1103 |
+
- [Rendering](https://dylanebert.github.io/vibegame/examples/rendering)
|
1104 |
+
- [Respawn](https://dylanebert.github.io/vibegame/examples/respawn)
|
1105 |
+
- [Startup](https://dylanebert.github.io/vibegame/examples/startup)
|
1106 |
+
- [Transforms](https://dylanebert.github.io/vibegame/examples/transforms)
|
1107 |
+
- [Tweening](https://dylanebert.github.io/vibegame/examples/tweening)
|
package.json
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "vibegame",
|
3 |
+
"version": "1.0.0",
|
4 |
+
"type": "module",
|
5 |
+
"scripts": {
|
6 |
+
"dev": "vite",
|
7 |
+
"build": "vite build",
|
8 |
+
"preview": "vite preview",
|
9 |
+
"check": "tsc --noEmit && svelte-check",
|
10 |
+
"check:ts": "tsc --noEmit",
|
11 |
+
"check:svelte": "svelte-check",
|
12 |
+
"update": "cp node_modules/vibegame/llms.txt ./llms.txt && echo '✓ Updated llms.txt from VibeGame'",
|
13 |
+
"postinstall": "test -f node_modules/vibegame/llms.txt && cp node_modules/vibegame/llms.txt ./llms.txt || true",
|
14 |
+
"format": "prettier --write .",
|
15 |
+
"format:check": "prettier --check .",
|
16 |
+
"lint": "eslint .",
|
17 |
+
"lint:fix": "eslint . --fix",
|
18 |
+
"validate": "bun run format:check && bun run lint && bun run check"
|
19 |
+
},
|
20 |
+
"devDependencies": {
|
21 |
+
"@eslint/js": "^9.33.0",
|
22 |
+
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
23 |
+
"@types/ws": "^8.18.1",
|
24 |
+
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
25 |
+
"@typescript-eslint/parser": "^8.40.0",
|
26 |
+
"eslint": "^9.33.0",
|
27 |
+
"eslint-config-prettier": "^10.1.8",
|
28 |
+
"eslint-plugin-import": "^2.32.0",
|
29 |
+
"eslint-plugin-prettier": "^5.5.4",
|
30 |
+
"prettier": "^3.6.2",
|
31 |
+
"svelte": "^4.2.18",
|
32 |
+
"svelte-check": "^3.8.4",
|
33 |
+
"typescript": "^5.6.0",
|
34 |
+
"vite": "^5.4.10",
|
35 |
+
"ws": "^8.18.3"
|
36 |
+
},
|
37 |
+
"dependencies": {
|
38 |
+
"@huggingface/hub": "^2.6.3",
|
39 |
+
"@huggingface/inference": "^4.8.0",
|
40 |
+
"@types/node": "^24.3.3",
|
41 |
+
"gsap": "^3.13.0",
|
42 |
+
"monaco-editor": "^0.50.0",
|
43 |
+
"svelte-splitpanes": "^8.0.5",
|
44 |
+
"vibegame": "^0.1.1"
|
45 |
+
}
|
46 |
+
}
|
src/App.svelte
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount, onDestroy } from 'svelte';
|
3 |
+
import { consoleCapture } from './lib/services/console-capture';
|
4 |
+
import { registerShortcuts, shortcuts } from './lib/config/shortcuts';
|
5 |
+
import { loadingStore } from './lib/stores/loading';
|
6 |
+
import AppHeader from './lib/components/layout/AppHeader.svelte';
|
7 |
+
import SplitView from './lib/components/layout/SplitView.svelte';
|
8 |
+
import LoadingScreen from './lib/components/layout/LoadingScreen.svelte';
|
9 |
+
|
10 |
+
let unregisterShortcuts: () => void;
|
11 |
+
|
12 |
+
onMount(() => {
|
13 |
+
loadingStore.startLoading();
|
14 |
+
|
15 |
+
consoleCapture.setup();
|
16 |
+
unregisterShortcuts = registerShortcuts(shortcuts);
|
17 |
+
|
18 |
+
setTimeout(() => {
|
19 |
+
loadingStore.setProgress(60);
|
20 |
+
}, 100);
|
21 |
+
|
22 |
+
requestAnimationFrame(() => {
|
23 |
+
requestAnimationFrame(() => {
|
24 |
+
loadingStore.finishLoading();
|
25 |
+
});
|
26 |
+
});
|
27 |
+
});
|
28 |
+
|
29 |
+
onDestroy(() => {
|
30 |
+
consoleCapture.teardown();
|
31 |
+
if (unregisterShortcuts) unregisterShortcuts();
|
32 |
+
});
|
33 |
+
</script>
|
34 |
+
|
35 |
+
<LoadingScreen
|
36 |
+
loading={$loadingStore.isLoading}
|
37 |
+
/>
|
38 |
+
|
39 |
+
<main>
|
40 |
+
<AppHeader />
|
41 |
+
<SplitView />
|
42 |
+
</main>
|
43 |
+
|
44 |
+
<style>
|
45 |
+
:global(body) {
|
46 |
+
margin: 0;
|
47 |
+
padding: 0;
|
48 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", system-ui, sans-serif;
|
49 |
+
background: linear-gradient(135deg, #0B0A09 0%, #0F0E0C 100%);
|
50 |
+
color: #ffffff;
|
51 |
+
overflow: hidden;
|
52 |
+
}
|
53 |
+
|
54 |
+
:global(*) {
|
55 |
+
box-sizing: border-box;
|
56 |
+
}
|
57 |
+
|
58 |
+
main {
|
59 |
+
width: 100vw;
|
60 |
+
height: 100vh;
|
61 |
+
overflow: hidden;
|
62 |
+
display: flex;
|
63 |
+
flex-direction: column;
|
64 |
+
}
|
65 |
+
</style>
|
src/app.css
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
* {
|
2 |
+
box-sizing: border-box;
|
3 |
+
}
|
4 |
+
|
5 |
+
html,
|
6 |
+
body {
|
7 |
+
margin: 0;
|
8 |
+
padding: 0;
|
9 |
+
height: 100%;
|
10 |
+
overflow: hidden;
|
11 |
+
}
|
12 |
+
|
13 |
+
#app {
|
14 |
+
height: 100vh;
|
15 |
+
width: 100vw;
|
16 |
+
}
|
src/context.md
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# src/
|
2 |
+
|
3 |
+
Main application source code.
|
4 |
+
|
5 |
+
## Structure
|
6 |
+
|
7 |
+
- `main.ts` - Svelte app mount point
|
8 |
+
- `App.svelte` - Game development environment with view modes
|
9 |
+
- `app.css` - Global styles
|
10 |
+
|
11 |
+
## Interface
|
12 |
+
|
13 |
+
- **Header**: View toggle (Code/Preview), status indicator, restart button
|
14 |
+
- **Code mode**: Split view with editor (40%) and game/console (60%)
|
15 |
+
- **Preview mode**: Full-screen game view
|
16 |
+
- **Animations**: GSAP transitions (0.2s) for smooth mode switching
|
17 |
+
- **Auto-reload**: 800ms debounce on code changes
|
18 |
+
- **Console**: Auto-scroll, message animations, hidden in preview mode
|
src/lib/components/Editor.svelte
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount, onDestroy, createEventDispatcher } from "svelte";
|
3 |
+
import * as monaco from "monaco-editor";
|
4 |
+
import type { editor } from "monaco-editor";
|
5 |
+
|
6 |
+
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
|
7 |
+
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
|
8 |
+
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
|
9 |
+
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
|
10 |
+
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
|
11 |
+
|
12 |
+
export let value: string = "";
|
13 |
+
export let language: string = "html";
|
14 |
+
export let theme: string = "vs-dark";
|
15 |
+
export let readOnly: boolean = false;
|
16 |
+
|
17 |
+
const dispatch = createEventDispatcher();
|
18 |
+
|
19 |
+
let editorContainer: HTMLDivElement;
|
20 |
+
let editorInstance: editor.IStandaloneCodeEditor | null = null;
|
21 |
+
let isInternalUpdate = false;
|
22 |
+
|
23 |
+
(self as any).MonacoEnvironment = {
|
24 |
+
getWorker: function (_: any, label: string) {
|
25 |
+
switch (label) {
|
26 |
+
case "json":
|
27 |
+
return new jsonWorker();
|
28 |
+
case "css":
|
29 |
+
case "scss":
|
30 |
+
case "less":
|
31 |
+
return new cssWorker();
|
32 |
+
case "html":
|
33 |
+
case "handlebars":
|
34 |
+
case "razor":
|
35 |
+
case "xml":
|
36 |
+
return new htmlWorker();
|
37 |
+
case "typescript":
|
38 |
+
case "javascript":
|
39 |
+
return new tsWorker();
|
40 |
+
default:
|
41 |
+
return new editorWorker();
|
42 |
+
}
|
43 |
+
},
|
44 |
+
};
|
45 |
+
|
46 |
+
onMount(() => {
|
47 |
+
editorInstance = monaco.editor.create(editorContainer, {
|
48 |
+
value: value,
|
49 |
+
language: language,
|
50 |
+
theme: theme,
|
51 |
+
minimap: {
|
52 |
+
enabled: false,
|
53 |
+
},
|
54 |
+
fontSize: 13,
|
55 |
+
fontFamily: '"Monaco", "Menlo", "Ubuntu Mono", monospace',
|
56 |
+
lineHeight: 20,
|
57 |
+
wordWrap: "on",
|
58 |
+
scrollBeyondLastLine: false,
|
59 |
+
renderWhitespace: "selection",
|
60 |
+
automaticLayout: true,
|
61 |
+
tabSize: 2,
|
62 |
+
insertSpaces: true,
|
63 |
+
formatOnPaste: true,
|
64 |
+
formatOnType: true,
|
65 |
+
suggestOnTriggerCharacters: true,
|
66 |
+
quickSuggestions: {
|
67 |
+
other: true,
|
68 |
+
comments: false,
|
69 |
+
strings: true,
|
70 |
+
},
|
71 |
+
scrollbar: {
|
72 |
+
verticalScrollbarSize: 10,
|
73 |
+
horizontalScrollbarSize: 10,
|
74 |
+
},
|
75 |
+
});
|
76 |
+
|
77 |
+
editorInstance.onDidChangeModelContent(() => {
|
78 |
+
if (!isInternalUpdate) {
|
79 |
+
const newValue = editorInstance?.getValue() || "";
|
80 |
+
dispatch("change", newValue);
|
81 |
+
}
|
82 |
+
});
|
83 |
+
|
84 |
+
const resizeObserver = new ResizeObserver(() => {
|
85 |
+
editorInstance?.layout();
|
86 |
+
});
|
87 |
+
resizeObserver.observe(editorContainer);
|
88 |
+
|
89 |
+
return () => {
|
90 |
+
resizeObserver.disconnect();
|
91 |
+
};
|
92 |
+
});
|
93 |
+
|
94 |
+
onDestroy(() => {
|
95 |
+
editorInstance?.dispose();
|
96 |
+
});
|
97 |
+
|
98 |
+
$: if (editorInstance && value !== editorInstance.getValue()) {
|
99 |
+
isInternalUpdate = true;
|
100 |
+
editorInstance.setValue(value);
|
101 |
+
isInternalUpdate = false;
|
102 |
+
}
|
103 |
+
|
104 |
+
$: if (editorInstance) {
|
105 |
+
const model = editorInstance.getModel();
|
106 |
+
if (model) {
|
107 |
+
monaco.editor.setModelLanguage(model, language);
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
$: if (editorInstance && theme) {
|
112 |
+
monaco.editor.setTheme(theme);
|
113 |
+
}
|
114 |
+
|
115 |
+
$: if (editorInstance) {
|
116 |
+
editorInstance.updateOptions({ readOnly });
|
117 |
+
}
|
118 |
+
</script>
|
119 |
+
|
120 |
+
<div bind:this={editorContainer} class="monaco-editor-container"></div>
|
121 |
+
|
122 |
+
<style>
|
123 |
+
.monaco-editor-container {
|
124 |
+
width: 100%;
|
125 |
+
height: 100%;
|
126 |
+
min-height: 200px;
|
127 |
+
}
|
128 |
+
</style>
|
src/lib/components/auth/LoginButton.svelte
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount } from 'svelte';
|
3 |
+
import { authStore } from '../../services/auth';
|
4 |
+
import gsap from 'gsap';
|
5 |
+
|
6 |
+
let authState = {
|
7 |
+
isAuthenticated: false,
|
8 |
+
user: null as any,
|
9 |
+
loading: true,
|
10 |
+
error: null as string | null
|
11 |
+
};
|
12 |
+
|
13 |
+
let loginBtn: HTMLButtonElement;
|
14 |
+
|
15 |
+
authStore.subscribe(state => {
|
16 |
+
authState = state;
|
17 |
+
});
|
18 |
+
|
19 |
+
onMount(async () => {
|
20 |
+
await authStore.init();
|
21 |
+
|
22 |
+
if (loginBtn && !authState.isAuthenticated) {
|
23 |
+
gsap.to(loginBtn, {
|
24 |
+
boxShadow: '0 0 20px rgba(255, 255, 255, 0.15)',
|
25 |
+
duration: 2,
|
26 |
+
repeat: -1,
|
27 |
+
yoyo: true,
|
28 |
+
ease: 'sine.inOut'
|
29 |
+
});
|
30 |
+
}
|
31 |
+
});
|
32 |
+
|
33 |
+
$: if (loginBtn && !authState.isAuthenticated && !authState.loading) {
|
34 |
+
gsap.to(loginBtn, {
|
35 |
+
boxShadow: '0 0 20px rgba(255, 255, 255, 0.15)',
|
36 |
+
duration: 2,
|
37 |
+
repeat: -1,
|
38 |
+
yoyo: true,
|
39 |
+
ease: 'sine.inOut'
|
40 |
+
});
|
41 |
+
}
|
42 |
+
|
43 |
+
function handleLogin() {
|
44 |
+
authStore.login();
|
45 |
+
}
|
46 |
+
|
47 |
+
function handleLogout() {
|
48 |
+
authStore.logout();
|
49 |
+
}
|
50 |
+
</script>
|
51 |
+
|
52 |
+
<div class="auth-container">
|
53 |
+
{#if authState.loading}
|
54 |
+
<div class="loading">Loading...</div>
|
55 |
+
{:else if authState.error}
|
56 |
+
<div class="error">
|
57 |
+
<span>{authState.error}</span>
|
58 |
+
<button on:click={handleLogin} class="retry-btn">Try Again</button>
|
59 |
+
</div>
|
60 |
+
{:else if authState.isAuthenticated}
|
61 |
+
<div class="user-info">
|
62 |
+
{#if authState.user?.avatarUrl}
|
63 |
+
<img src={authState.user.avatarUrl} alt={authState.user.name} class="avatar" />
|
64 |
+
{/if}
|
65 |
+
<span class="username">{authState.user?.name || 'User'}</span>
|
66 |
+
<button on:click={handleLogout} class="logout-btn">Logout</button>
|
67 |
+
</div>
|
68 |
+
{:else}
|
69 |
+
<button bind:this={loginBtn} on:click={handleLogin} class="login-btn">
|
70 |
+
Sign in
|
71 |
+
</button>
|
72 |
+
{/if}
|
73 |
+
</div>
|
74 |
+
|
75 |
+
<style>
|
76 |
+
.auth-container {
|
77 |
+
display: flex;
|
78 |
+
align-items: center;
|
79 |
+
gap: 0.5rem;
|
80 |
+
}
|
81 |
+
|
82 |
+
.loading {
|
83 |
+
color: #888;
|
84 |
+
font-size: 0.875rem;
|
85 |
+
}
|
86 |
+
|
87 |
+
.error {
|
88 |
+
display: flex;
|
89 |
+
align-items: center;
|
90 |
+
gap: 0.5rem;
|
91 |
+
color: #ff6b6b;
|
92 |
+
font-size: 0.875rem;
|
93 |
+
}
|
94 |
+
|
95 |
+
.retry-btn {
|
96 |
+
padding: 0.25rem 0.5rem;
|
97 |
+
background: #ff6b6b;
|
98 |
+
color: white;
|
99 |
+
border: none;
|
100 |
+
border-radius: 0.25rem;
|
101 |
+
cursor: pointer;
|
102 |
+
font-size: 0.75rem;
|
103 |
+
}
|
104 |
+
|
105 |
+
.retry-btn:hover {
|
106 |
+
background: #ff5252;
|
107 |
+
}
|
108 |
+
|
109 |
+
.user-info {
|
110 |
+
display: flex;
|
111 |
+
align-items: center;
|
112 |
+
gap: 0.5rem;
|
113 |
+
}
|
114 |
+
|
115 |
+
.avatar {
|
116 |
+
width: 24px;
|
117 |
+
height: 24px;
|
118 |
+
border-radius: 50%;
|
119 |
+
}
|
120 |
+
|
121 |
+
.username {
|
122 |
+
color: #ccc;
|
123 |
+
font-size: 0.875rem;
|
124 |
+
}
|
125 |
+
|
126 |
+
.logout-btn {
|
127 |
+
padding: 0.25rem 0.5rem;
|
128 |
+
background: transparent;
|
129 |
+
color: #888;
|
130 |
+
border: 1px solid #333;
|
131 |
+
border-radius: 0.25rem;
|
132 |
+
cursor: pointer;
|
133 |
+
font-size: 0.75rem;
|
134 |
+
transition: all 0.2s;
|
135 |
+
}
|
136 |
+
|
137 |
+
.logout-btn:hover {
|
138 |
+
background: #333;
|
139 |
+
color: #fff;
|
140 |
+
}
|
141 |
+
|
142 |
+
.login-btn {
|
143 |
+
padding: 0.35rem 0.85rem;
|
144 |
+
background: rgba(255, 255, 255, 0.03);
|
145 |
+
color: rgba(255, 255, 255, 0.7);
|
146 |
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
147 |
+
border-radius: 0.35rem;
|
148 |
+
cursor: pointer;
|
149 |
+
font-size: 0.75rem;
|
150 |
+
font-weight: 500;
|
151 |
+
transition: all 0.3s ease;
|
152 |
+
backdrop-filter: blur(10px);
|
153 |
+
position: relative;
|
154 |
+
overflow: hidden;
|
155 |
+
}
|
156 |
+
|
157 |
+
.login-btn:hover {
|
158 |
+
background: rgba(255, 255, 255, 0.08);
|
159 |
+
color: rgba(255, 255, 255, 0.95);
|
160 |
+
border-color: rgba(255, 255, 255, 0.15);
|
161 |
+
transform: translateY(-1px);
|
162 |
+
}
|
163 |
+
|
164 |
+
.login-btn:active {
|
165 |
+
transform: translateY(0);
|
166 |
+
}
|
167 |
+
</style>
|
src/lib/components/auth/context.md
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Auth Components
|
2 |
+
|
3 |
+
Authentication UI for Hugging Face OAuth
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
Handle user authentication flow and state display
|
8 |
+
|
9 |
+
## Layout
|
10 |
+
|
11 |
+
```
|
12 |
+
auth/
|
13 |
+
├── context.md # This file
|
14 |
+
└── LoginButton.svelte # OAuth login button
|
15 |
+
```
|
16 |
+
|
17 |
+
## Components
|
18 |
+
|
19 |
+
### LoginButton.svelte
|
20 |
+
|
21 |
+
- Subtle glassmorphic sign-in button with pulse animation
|
22 |
+
- User info display when authenticated
|
23 |
+
- Located in header top right
|
24 |
+
|
25 |
+
## Dependencies
|
26 |
+
|
27 |
+
- authStore for authentication state
|
28 |
+
- GSAP for subtle animations
|
src/lib/components/chat/ChatPanel.svelte
ADDED
@@ -0,0 +1,430 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount, onDestroy, afterUpdate } from "svelte";
|
3 |
+
import { agentStore, isConnected, isProcessing } from "../../stores/agent";
|
4 |
+
import { authStore } from "../../services/auth";
|
5 |
+
import gsap from "gsap";
|
6 |
+
|
7 |
+
let inputValue = "";
|
8 |
+
let messagesContainer: HTMLDivElement;
|
9 |
+
let sendButton: HTMLButtonElement;
|
10 |
+
let inputTextarea: HTMLTextAreaElement;
|
11 |
+
let authPromptBtn: HTMLButtonElement;
|
12 |
+
|
13 |
+
let hasConnected = false;
|
14 |
+
|
15 |
+
onMount(() => {
|
16 |
+
if ($authStore.isAuthenticated && !$authStore.loading) {
|
17 |
+
agentStore.connect();
|
18 |
+
hasConnected = true;
|
19 |
+
}
|
20 |
+
|
21 |
+
if (authPromptBtn) {
|
22 |
+
gsap.to(authPromptBtn, {
|
23 |
+
boxShadow: '0 0 25px rgba(255, 210, 30, 0.3)',
|
24 |
+
duration: 2.5,
|
25 |
+
repeat: -1,
|
26 |
+
yoyo: true,
|
27 |
+
ease: 'sine.inOut'
|
28 |
+
});
|
29 |
+
}
|
30 |
+
});
|
31 |
+
|
32 |
+
onDestroy(() => {
|
33 |
+
agentStore.disconnect();
|
34 |
+
});
|
35 |
+
|
36 |
+
$: if ($authStore.isAuthenticated && !$authStore.loading && !hasConnected) {
|
37 |
+
agentStore.connect();
|
38 |
+
hasConnected = true;
|
39 |
+
}
|
40 |
+
|
41 |
+
$: if (inputTextarea && $isConnected && !$isProcessing && $agentStore.messages.length === 0) {
|
42 |
+
gsap.to(inputTextarea, {
|
43 |
+
borderColor: 'rgba(65, 105, 225, 0.4)',
|
44 |
+
duration: 2,
|
45 |
+
repeat: -1,
|
46 |
+
yoyo: true,
|
47 |
+
ease: 'sine.inOut'
|
48 |
+
});
|
49 |
+
} else if (inputTextarea) {
|
50 |
+
gsap.killTweensOf(inputTextarea);
|
51 |
+
gsap.set(inputTextarea, {
|
52 |
+
borderColor: 'rgba(255, 255, 255, 0.1)'
|
53 |
+
});
|
54 |
+
}
|
55 |
+
|
56 |
+
afterUpdate(() => {
|
57 |
+
if (messagesContainer) {
|
58 |
+
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
59 |
+
}
|
60 |
+
});
|
61 |
+
|
62 |
+
function handleSubmit() {
|
63 |
+
if (inputValue.trim() && $authStore.isAuthenticated && $isConnected && !$isProcessing) {
|
64 |
+
if (sendButton) {
|
65 |
+
gsap.to(sendButton, {
|
66 |
+
scale: 0.9,
|
67 |
+
duration: 0.1,
|
68 |
+
ease: "power2.in",
|
69 |
+
onComplete: () => {
|
70 |
+
gsap.to(sendButton, {
|
71 |
+
scale: 1,
|
72 |
+
duration: 0.2,
|
73 |
+
ease: "elastic.out(1, 0.5)",
|
74 |
+
});
|
75 |
+
},
|
76 |
+
});
|
77 |
+
}
|
78 |
+
|
79 |
+
agentStore.sendMessage(inputValue.trim());
|
80 |
+
inputValue = "";
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
function handleKeydown(event: KeyboardEvent) {
|
85 |
+
if (event.key === "Enter" && !event.shiftKey) {
|
86 |
+
event.preventDefault();
|
87 |
+
handleSubmit();
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
function handleButtonMouseEnter() {
|
92 |
+
if (sendButton && !sendButton.disabled) {
|
93 |
+
gsap.to(sendButton, {
|
94 |
+
scale: 1.05,
|
95 |
+
duration: 0.2,
|
96 |
+
ease: "power2.out",
|
97 |
+
});
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
function handleButtonMouseLeave() {
|
102 |
+
if (sendButton) {
|
103 |
+
gsap.to(sendButton, {
|
104 |
+
scale: 1,
|
105 |
+
duration: 0.2,
|
106 |
+
ease: "power2.out",
|
107 |
+
});
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
function handleButtonMouseDown() {
|
112 |
+
if (sendButton && !sendButton.disabled) {
|
113 |
+
gsap.to(sendButton, {
|
114 |
+
scale: 0.95,
|
115 |
+
duration: 0.1,
|
116 |
+
ease: "power2.in",
|
117 |
+
});
|
118 |
+
}
|
119 |
+
}
|
120 |
+
|
121 |
+
function handleButtonMouseUp() {
|
122 |
+
if (sendButton && !sendButton.disabled) {
|
123 |
+
gsap.to(sendButton, {
|
124 |
+
scale: 1.05,
|
125 |
+
duration: 0.1,
|
126 |
+
ease: "power2.out",
|
127 |
+
});
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
$: {
|
132 |
+
if (sendButton) {
|
133 |
+
if (!$authStore.isAuthenticated || !$isConnected || $isProcessing || !inputValue.trim()) {
|
134 |
+
gsap.to(sendButton, {
|
135 |
+
opacity: 0.3,
|
136 |
+
duration: 0.2,
|
137 |
+
ease: "power2.out",
|
138 |
+
});
|
139 |
+
} else {
|
140 |
+
gsap.to(sendButton, {
|
141 |
+
opacity: 1,
|
142 |
+
duration: 0.2,
|
143 |
+
ease: "power2.out",
|
144 |
+
});
|
145 |
+
}
|
146 |
+
}
|
147 |
+
}
|
148 |
+
</script>
|
149 |
+
|
150 |
+
<div class="chat-panel">
|
151 |
+
<div class="messages" bind:this={messagesContainer}>
|
152 |
+
{#if !$authStore.isAuthenticated && !$authStore.loading}
|
153 |
+
<div class="auth-prompt">
|
154 |
+
<p>Sign in to chat.</p>
|
155 |
+
<button bind:this={authPromptBtn} on:click={() => authStore.login()} class="auth-prompt-btn">
|
156 |
+
Sign in with 🤗 Hugging Face
|
157 |
+
</button>
|
158 |
+
</div>
|
159 |
+
{:else}
|
160 |
+
{#if $agentStore.messages.length === 0 && $isConnected}
|
161 |
+
<div class="ready-message">Ready to Chat!</div>
|
162 |
+
{/if}
|
163 |
+
{#each $agentStore.messages as message}
|
164 |
+
<div class="message {message.role}">
|
165 |
+
<div class="message-content">
|
166 |
+
{message.content.trim()}
|
167 |
+
{#if message.streaming}
|
168 |
+
<span class="cursor">▊</span>
|
169 |
+
{/if}
|
170 |
+
</div>
|
171 |
+
</div>
|
172 |
+
{/each}
|
173 |
+
{#if $agentStore.error}
|
174 |
+
<div class="error-message">
|
175 |
+
Error: {$agentStore.error}
|
176 |
+
</div>
|
177 |
+
{/if}
|
178 |
+
{/if}
|
179 |
+
</div>
|
180 |
+
|
181 |
+
<div class="input-area">
|
182 |
+
<textarea
|
183 |
+
bind:this={inputTextarea}
|
184 |
+
bind:value={inputValue}
|
185 |
+
on:keydown={handleKeydown}
|
186 |
+
placeholder={!$authStore.isAuthenticated
|
187 |
+
? "Sign in to chat..."
|
188 |
+
: $isConnected
|
189 |
+
? $isProcessing
|
190 |
+
? "Processing..."
|
191 |
+
: "Type a message..."
|
192 |
+
: "Connecting..."}
|
193 |
+
disabled={!$authStore.isAuthenticated || !$isConnected || $isProcessing}
|
194 |
+
rows="1"
|
195 |
+
/>
|
196 |
+
<button
|
197 |
+
bind:this={sendButton}
|
198 |
+
on:click={handleSubmit}
|
199 |
+
on:mouseenter={handleButtonMouseEnter}
|
200 |
+
on:mouseleave={handleButtonMouseLeave}
|
201 |
+
on:mousedown={handleButtonMouseDown}
|
202 |
+
on:mouseup={handleButtonMouseUp}
|
203 |
+
disabled={!$authStore.isAuthenticated || !$isConnected || $isProcessing || !inputValue.trim()}
|
204 |
+
class="send-btn"
|
205 |
+
title="Send message"
|
206 |
+
>
|
207 |
+
→
|
208 |
+
</button>
|
209 |
+
</div>
|
210 |
+
</div>
|
211 |
+
|
212 |
+
<style>
|
213 |
+
.chat-panel {
|
214 |
+
display: flex;
|
215 |
+
flex-direction: column;
|
216 |
+
height: 100%;
|
217 |
+
width: 100%;
|
218 |
+
background: rgba(20, 20, 20, 0.5);
|
219 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
220 |
+
}
|
221 |
+
|
222 |
+
.messages {
|
223 |
+
flex: 1;
|
224 |
+
overflow-y: auto;
|
225 |
+
padding: 0.5rem;
|
226 |
+
display: flex;
|
227 |
+
flex-direction: column;
|
228 |
+
gap: 0.4rem;
|
229 |
+
min-height: 0;
|
230 |
+
}
|
231 |
+
|
232 |
+
.message {
|
233 |
+
padding: 0.4rem 0.6rem;
|
234 |
+
border-radius: 3px;
|
235 |
+
font-size: 0.75rem;
|
236 |
+
animation: fadeIn 0.2s ease-out;
|
237 |
+
}
|
238 |
+
|
239 |
+
@keyframes fadeIn {
|
240 |
+
from {
|
241 |
+
opacity: 0;
|
242 |
+
transform: translateY(5px);
|
243 |
+
}
|
244 |
+
to {
|
245 |
+
opacity: 1;
|
246 |
+
transform: translateY(0);
|
247 |
+
}
|
248 |
+
}
|
249 |
+
|
250 |
+
.message.user {
|
251 |
+
background: rgba(65, 105, 225, 0.08);
|
252 |
+
border-left: 2px solid rgba(65, 105, 225, 0.6);
|
253 |
+
margin-left: 1rem;
|
254 |
+
}
|
255 |
+
|
256 |
+
.message.assistant {
|
257 |
+
background: rgba(255, 255, 255, 0.02);
|
258 |
+
border-left: 2px solid rgba(255, 255, 255, 0.15);
|
259 |
+
margin-right: 1rem;
|
260 |
+
}
|
261 |
+
|
262 |
+
.message-content {
|
263 |
+
color: rgba(255, 255, 255, 0.9);
|
264 |
+
line-height: 1.5;
|
265 |
+
white-space: pre-line;
|
266 |
+
word-wrap: break-word;
|
267 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Monaco", "Menlo", monospace;
|
268 |
+
margin: 0;
|
269 |
+
display: block;
|
270 |
+
}
|
271 |
+
|
272 |
+
.cursor {
|
273 |
+
animation: blink 1s infinite;
|
274 |
+
}
|
275 |
+
|
276 |
+
@keyframes blink {
|
277 |
+
0%,
|
278 |
+
50% {
|
279 |
+
opacity: 1;
|
280 |
+
}
|
281 |
+
51%,
|
282 |
+
100% {
|
283 |
+
opacity: 0;
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
.error-message {
|
288 |
+
background: rgba(244, 67, 54, 0.1);
|
289 |
+
border-left: 2px solid #f44336;
|
290 |
+
border-radius: 3px;
|
291 |
+
padding: 0.4rem 0.6rem;
|
292 |
+
color: #ff9999;
|
293 |
+
font-size: 0.75rem;
|
294 |
+
animation: fadeIn 0.2s ease-out;
|
295 |
+
}
|
296 |
+
|
297 |
+
.input-area {
|
298 |
+
display: flex;
|
299 |
+
gap: 0.5rem;
|
300 |
+
padding: 0.5rem;
|
301 |
+
background: rgba(0, 0, 0, 0.2);
|
302 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
303 |
+
}
|
304 |
+
|
305 |
+
textarea {
|
306 |
+
flex: 1;
|
307 |
+
background: rgba(0, 0, 0, 0.3);
|
308 |
+
color: rgba(255, 255, 255, 0.9);
|
309 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
310 |
+
border-radius: 4px;
|
311 |
+
padding: 0.4rem 0.6rem;
|
312 |
+
resize: none;
|
313 |
+
font-family: "Monaco", "Menlo", monospace;
|
314 |
+
font-size: 0.8rem;
|
315 |
+
line-height: 1.2;
|
316 |
+
}
|
317 |
+
|
318 |
+
textarea:focus {
|
319 |
+
outline: none;
|
320 |
+
border-color: rgba(65, 105, 225, 0.5);
|
321 |
+
background: rgba(0, 0, 0, 0.5);
|
322 |
+
}
|
323 |
+
|
324 |
+
textarea:disabled {
|
325 |
+
opacity: 0.5;
|
326 |
+
cursor: not-allowed;
|
327 |
+
}
|
328 |
+
|
329 |
+
.send-btn {
|
330 |
+
padding: 0.4rem 0.8rem;
|
331 |
+
background: rgba(65, 105, 225, 0.2);
|
332 |
+
color: #4169e1;
|
333 |
+
border: 1px solid rgba(65, 105, 225, 0.3);
|
334 |
+
border-radius: 4px;
|
335 |
+
cursor: pointer;
|
336 |
+
font-size: 1rem;
|
337 |
+
transform-origin: center;
|
338 |
+
will-change: transform, opacity;
|
339 |
+
}
|
340 |
+
|
341 |
+
.send-btn:hover:not(:disabled) {
|
342 |
+
background: rgba(65, 105, 225, 0.3);
|
343 |
+
border-color: rgba(65, 105, 225, 0.5);
|
344 |
+
}
|
345 |
+
|
346 |
+
.send-btn:disabled {
|
347 |
+
cursor: not-allowed;
|
348 |
+
}
|
349 |
+
|
350 |
+
::-webkit-scrollbar {
|
351 |
+
width: 6px;
|
352 |
+
}
|
353 |
+
|
354 |
+
::-webkit-scrollbar-track {
|
355 |
+
background: transparent;
|
356 |
+
}
|
357 |
+
|
358 |
+
::-webkit-scrollbar-thumb {
|
359 |
+
background: rgba(255, 255, 255, 0.1);
|
360 |
+
border-radius: 3px;
|
361 |
+
}
|
362 |
+
|
363 |
+
::-webkit-scrollbar-thumb:hover {
|
364 |
+
background: rgba(255, 255, 255, 0.2);
|
365 |
+
}
|
366 |
+
|
367 |
+
.auth-prompt {
|
368 |
+
display: flex;
|
369 |
+
flex-direction: column;
|
370 |
+
align-items: center;
|
371 |
+
justify-content: center;
|
372 |
+
padding: 2rem;
|
373 |
+
height: 100%;
|
374 |
+
text-align: center;
|
375 |
+
}
|
376 |
+
|
377 |
+
.auth-prompt p {
|
378 |
+
color: rgba(255, 255, 255, 0.6);
|
379 |
+
margin-bottom: 1.5rem;
|
380 |
+
font-size: 0.875rem;
|
381 |
+
}
|
382 |
+
|
383 |
+
.auth-prompt-btn {
|
384 |
+
padding: 0.6rem 1.8rem;
|
385 |
+
background: rgba(255, 210, 30, 0.08);
|
386 |
+
color: rgba(255, 210, 30, 0.9);
|
387 |
+
border: 1px solid rgba(255, 210, 30, 0.2);
|
388 |
+
border-radius: 0.5rem;
|
389 |
+
cursor: pointer;
|
390 |
+
font-size: 0.875rem;
|
391 |
+
font-weight: 600;
|
392 |
+
transition: all 0.3s ease;
|
393 |
+
backdrop-filter: blur(10px);
|
394 |
+
position: relative;
|
395 |
+
overflow: hidden;
|
396 |
+
}
|
397 |
+
|
398 |
+
.auth-prompt-btn:hover {
|
399 |
+
transform: translateY(-2px);
|
400 |
+
background: rgba(255, 210, 30, 0.12);
|
401 |
+
color: rgba(255, 210, 30, 1);
|
402 |
+
border-color: rgba(255, 210, 30, 0.3);
|
403 |
+
}
|
404 |
+
|
405 |
+
.auth-prompt-btn:active {
|
406 |
+
transform: translateY(0);
|
407 |
+
}
|
408 |
+
|
409 |
+
.ready-message {
|
410 |
+
color: rgba(255, 255, 255, 0.2);
|
411 |
+
font-size: 0.75rem;
|
412 |
+
text-align: center;
|
413 |
+
font-style: italic;
|
414 |
+
display: flex;
|
415 |
+
align-items: center;
|
416 |
+
justify-content: center;
|
417 |
+
height: 100%;
|
418 |
+
min-height: 200px;
|
419 |
+
animation: gentle-fade 3s ease-in-out infinite;
|
420 |
+
}
|
421 |
+
|
422 |
+
@keyframes gentle-fade {
|
423 |
+
0%, 100% {
|
424 |
+
opacity: 0.7;
|
425 |
+
}
|
426 |
+
50% {
|
427 |
+
opacity: 1;
|
428 |
+
}
|
429 |
+
}
|
430 |
+
</style>
|
src/lib/components/chat/context.md
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Chat Context
|
2 |
+
|
3 |
+
AI assistant interface with authentication-gated input
|
4 |
+
|
5 |
+
## Components
|
6 |
+
|
7 |
+
- `ChatPanel.svelte` - Chat UI with auth-aware input controls
|
8 |
+
|
9 |
+
## Features
|
10 |
+
|
11 |
+
- Authentication-required input (visible but disabled when not logged in)
|
12 |
+
- Real-time message streaming when connected
|
13 |
+
- Breathing input animation when ready
|
14 |
+
- GSAP-animated button interactions
|
src/lib/components/console/ConsoleMessage.svelte
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { ConsoleMessage } from '../../stores/console';
|
3 |
+
import { onMount } from 'svelte';
|
4 |
+
import gsap from 'gsap';
|
5 |
+
|
6 |
+
export let message: ConsoleMessage;
|
7 |
+
|
8 |
+
let messageElement: HTMLDivElement;
|
9 |
+
|
10 |
+
onMount(() => {
|
11 |
+
gsap.fromTo(messageElement,
|
12 |
+
{ opacity: 0, y: -5 },
|
13 |
+
{ opacity: 1, y: 0, duration: 0.3, ease: "power2.out" }
|
14 |
+
);
|
15 |
+
});
|
16 |
+
</script>
|
17 |
+
|
18 |
+
<div
|
19 |
+
bind:this={messageElement}
|
20 |
+
class="console-line console-{message.type}"
|
21 |
+
>
|
22 |
+
<span class="console-type">{message.type}</span>
|
23 |
+
<span class="console-msg">{message.message}</span>
|
24 |
+
</div>
|
25 |
+
|
26 |
+
<style>
|
27 |
+
.console-line {
|
28 |
+
margin-bottom: 6px;
|
29 |
+
display: flex;
|
30 |
+
gap: 10px;
|
31 |
+
align-items: flex-start;
|
32 |
+
padding: 4px 6px;
|
33 |
+
border-radius: 4px;
|
34 |
+
transition: background 0.2s;
|
35 |
+
}
|
36 |
+
|
37 |
+
.console-line:hover {
|
38 |
+
background: rgba(139, 115, 85, 0.02);
|
39 |
+
}
|
40 |
+
|
41 |
+
.console-type {
|
42 |
+
display: flex;
|
43 |
+
align-items: center;
|
44 |
+
gap: 6px;
|
45 |
+
font-size: 9px;
|
46 |
+
text-transform: uppercase;
|
47 |
+
letter-spacing: 0.3px;
|
48 |
+
opacity: 0.5;
|
49 |
+
}
|
50 |
+
|
51 |
+
.console-type::before {
|
52 |
+
content: '';
|
53 |
+
width: 6px;
|
54 |
+
height: 6px;
|
55 |
+
border-radius: 50%;
|
56 |
+
display: inline-block;
|
57 |
+
}
|
58 |
+
|
59 |
+
.console-log .console-type::before {
|
60 |
+
background: rgba(251, 248, 244, 0.5);
|
61 |
+
}
|
62 |
+
|
63 |
+
.console-warn .console-type::before {
|
64 |
+
background: #D4A574;
|
65 |
+
}
|
66 |
+
|
67 |
+
.console-error .console-type::before {
|
68 |
+
background: #B85450;
|
69 |
+
}
|
70 |
+
|
71 |
+
.console-info .console-type::before {
|
72 |
+
background: #7C9885;
|
73 |
+
}
|
74 |
+
|
75 |
+
.console-msg {
|
76 |
+
flex: 1;
|
77 |
+
word-break: break-word;
|
78 |
+
white-space: pre-wrap;
|
79 |
+
color: rgba(251, 248, 244, 0.8);
|
80 |
+
}
|
81 |
+
|
82 |
+
.console-log {
|
83 |
+
color: rgba(251, 248, 244, 0.7);
|
84 |
+
}
|
85 |
+
|
86 |
+
.console-warn {
|
87 |
+
background: rgba(212, 165, 116, 0.05);
|
88 |
+
}
|
89 |
+
|
90 |
+
.console-warn .console-msg {
|
91 |
+
color: #D4A574;
|
92 |
+
}
|
93 |
+
|
94 |
+
.console-error {
|
95 |
+
background: rgba(184, 84, 80, 0.05);
|
96 |
+
}
|
97 |
+
|
98 |
+
.console-error .console-msg {
|
99 |
+
color: #B85450;
|
100 |
+
}
|
101 |
+
|
102 |
+
.console-info {
|
103 |
+
background: rgba(124, 152, 133, 0.03);
|
104 |
+
}
|
105 |
+
|
106 |
+
.console-info .console-msg {
|
107 |
+
color: #7C9885;
|
108 |
+
}
|
109 |
+
</style>
|
src/lib/components/console/ConsolePanel.svelte
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { consoleStore } from '../../stores/console';
|
3 |
+
import ConsoleMessage from './ConsoleMessage.svelte';
|
4 |
+
import { afterUpdate } from 'svelte';
|
5 |
+
|
6 |
+
let consoleOutput: HTMLDivElement;
|
7 |
+
let wasAtBottom = true;
|
8 |
+
|
9 |
+
function clearConsole() {
|
10 |
+
consoleStore.clear();
|
11 |
+
}
|
12 |
+
|
13 |
+
function checkScrollPosition() {
|
14 |
+
if (consoleOutput) {
|
15 |
+
const threshold = 5;
|
16 |
+
wasAtBottom = consoleOutput.scrollHeight - consoleOutput.scrollTop - consoleOutput.clientHeight < threshold;
|
17 |
+
}
|
18 |
+
}
|
19 |
+
|
20 |
+
afterUpdate(() => {
|
21 |
+
if (wasAtBottom && consoleOutput) {
|
22 |
+
consoleOutput.scrollTop = consoleOutput.scrollHeight;
|
23 |
+
}
|
24 |
+
});
|
25 |
+
</script>
|
26 |
+
|
27 |
+
<div class="console-section">
|
28 |
+
<div class="panel-header">
|
29 |
+
<h2>Console</h2>
|
30 |
+
<button on:click={clearConsole} class="clear-button">Clear</button>
|
31 |
+
</div>
|
32 |
+
<div
|
33 |
+
bind:this={consoleOutput}
|
34 |
+
on:scroll={checkScrollPosition}
|
35 |
+
class="console-output"
|
36 |
+
>
|
37 |
+
{#each $consoleStore.messages as message (message.id)}
|
38 |
+
<ConsoleMessage {message} />
|
39 |
+
{/each}
|
40 |
+
{#if $consoleStore.messages.length === 0}
|
41 |
+
<div class="console-empty">Awaiting console output...</div>
|
42 |
+
{/if}
|
43 |
+
</div>
|
44 |
+
</div>
|
45 |
+
|
46 |
+
<style>
|
47 |
+
.console-section {
|
48 |
+
width: 100%;
|
49 |
+
height: 100%;
|
50 |
+
display: flex;
|
51 |
+
flex-direction: column;
|
52 |
+
background: rgba(8, 7, 6, 0.4);
|
53 |
+
}
|
54 |
+
|
55 |
+
.panel-header {
|
56 |
+
display: flex;
|
57 |
+
justify-content: space-between;
|
58 |
+
align-items: center;
|
59 |
+
padding: 10px 16px;
|
60 |
+
background: rgba(15, 14, 12, 0.3);
|
61 |
+
border-bottom: 1px solid rgba(139, 115, 85, 0.06);
|
62 |
+
flex-shrink: 0;
|
63 |
+
}
|
64 |
+
|
65 |
+
.panel-header h2 {
|
66 |
+
margin: 0;
|
67 |
+
font-size: 11px;
|
68 |
+
font-weight: 500;
|
69 |
+
color: rgba(251, 248, 244, 0.5);
|
70 |
+
text-transform: uppercase;
|
71 |
+
letter-spacing: 0.5px;
|
72 |
+
}
|
73 |
+
|
74 |
+
.console-output {
|
75 |
+
flex: 1;
|
76 |
+
padding: 12px 16px;
|
77 |
+
background: transparent;
|
78 |
+
overflow-y: auto;
|
79 |
+
font-family: "JetBrains Mono", "Monaco", "Menlo", monospace;
|
80 |
+
font-size: 11px;
|
81 |
+
line-height: 1.6;
|
82 |
+
}
|
83 |
+
|
84 |
+
.console-output::-webkit-scrollbar {
|
85 |
+
width: 6px;
|
86 |
+
}
|
87 |
+
|
88 |
+
.console-output::-webkit-scrollbar-track {
|
89 |
+
background: rgba(139, 115, 85, 0.02);
|
90 |
+
}
|
91 |
+
|
92 |
+
.console-output::-webkit-scrollbar-thumb {
|
93 |
+
background: rgba(139, 115, 85, 0.1);
|
94 |
+
border-radius: 3px;
|
95 |
+
}
|
96 |
+
|
97 |
+
.console-output::-webkit-scrollbar-thumb:hover {
|
98 |
+
background: rgba(139, 115, 85, 0.15);
|
99 |
+
}
|
100 |
+
|
101 |
+
.console-empty {
|
102 |
+
color: rgba(251, 248, 244, 0.3);
|
103 |
+
font-style: italic;
|
104 |
+
font-size: 11px;
|
105 |
+
padding: 4px 6px;
|
106 |
+
}
|
107 |
+
|
108 |
+
.clear-button {
|
109 |
+
padding: 4px 10px;
|
110 |
+
background: rgba(139, 115, 85, 0.03);
|
111 |
+
color: rgba(251, 248, 244, 0.4);
|
112 |
+
border: 1px solid rgba(139, 115, 85, 0.08);
|
113 |
+
border-radius: 4px;
|
114 |
+
font-size: 10px;
|
115 |
+
font-weight: 500;
|
116 |
+
text-transform: uppercase;
|
117 |
+
letter-spacing: 0.3px;
|
118 |
+
cursor: pointer;
|
119 |
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
120 |
+
}
|
121 |
+
|
122 |
+
.clear-button:hover {
|
123 |
+
background: rgba(139, 115, 85, 0.06);
|
124 |
+
color: rgba(251, 248, 244, 0.6);
|
125 |
+
border-color: rgba(139, 115, 85, 0.12);
|
126 |
+
}
|
127 |
+
|
128 |
+
.clear-button:active {
|
129 |
+
transform: scale(0.95);
|
130 |
+
}
|
131 |
+
</style>
|
src/lib/components/console/context.md
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Console Components
|
2 |
+
|
3 |
+
Console output display and management
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Display console messages with formatting
|
8 |
+
- Auto-scroll and message buffering
|
9 |
+
- Clear and filter capabilities
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
console/
|
15 |
+
├── context.md # This file
|
16 |
+
├── ConsolePanel.svelte # Main console container
|
17 |
+
└── ConsoleMessage.svelte # Individual message display
|
18 |
+
```
|
19 |
+
|
20 |
+
## Scope
|
21 |
+
|
22 |
+
- In-scope: Console UI, message rendering
|
23 |
+
- Out-of-scope: Console capture logic
|
24 |
+
|
25 |
+
## Entrypoints
|
26 |
+
|
27 |
+
- `ConsolePanel.svelte` - Main component to embed console
|
28 |
+
|
29 |
+
## Dependencies
|
30 |
+
|
31 |
+
- consoleStore for messages
|
32 |
+
- GSAP for message animations
|
src/lib/components/context.md
ADDED
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Components
|
2 |
+
|
3 |
+
UI components organized by feature/responsibility
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Declarative UI components
|
8 |
+
- Single responsibility per component
|
9 |
+
- Clean props/events interface
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
components/
|
15 |
+
├── context.md # This file
|
16 |
+
├── auth/ # Authentication UI components
|
17 |
+
├── chat/ # AI chat interface
|
18 |
+
├── console/ # Console display components
|
19 |
+
├── editor/ # Code editor wrapper
|
20 |
+
├── game/ # Game canvas and errors
|
21 |
+
├── layout/ # App layout components
|
22 |
+
└── Editor.svelte # Monaco editor (legacy)
|
23 |
+
```
|
24 |
+
|
25 |
+
## Scope
|
26 |
+
|
27 |
+
- In-scope: UI rendering, user interactions
|
28 |
+
- Out-of-scope: Business logic, direct state mutations
|
29 |
+
|
30 |
+
## Entrypoints
|
31 |
+
|
32 |
+
- `AppHeader.svelte` - Top navigation bar with auth
|
33 |
+
- `LoginButton.svelte` - OAuth authentication button
|
34 |
+
- `ChatPanel.svelte` - AI chat interface
|
35 |
+
- `SplitView.svelte` - Main layout container
|
36 |
+
- `ConsolePanel.svelte` - Console output display
|
37 |
+
- `GameCanvas.svelte` - Game rendering area
|
38 |
+
- `CodeEditor.svelte` - Code editing interface
|
39 |
+
|
40 |
+
## Dependencies
|
41 |
+
|
42 |
+
- Stores for reactive state
|
43 |
+
- Services for operations
|
44 |
+
- GSAP for animations
|
45 |
+
- svelte-splitpanes for layout
|
src/lib/components/editor/CodeEditor.svelte
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { editorStore } from '../../stores/editor';
|
3 |
+
import Editor from '../Editor.svelte';
|
4 |
+
|
5 |
+
function handleChange(event: CustomEvent<string>) {
|
6 |
+
editorStore.setContent(event.detail);
|
7 |
+
}
|
8 |
+
</script>
|
9 |
+
|
10 |
+
<div class="editor-panel">
|
11 |
+
<Editor
|
12 |
+
value={$editorStore.content}
|
13 |
+
language={$editorStore.language}
|
14 |
+
theme={$editorStore.theme}
|
15 |
+
on:change={handleChange}
|
16 |
+
/>
|
17 |
+
</div>
|
18 |
+
|
19 |
+
<style>
|
20 |
+
.editor-panel {
|
21 |
+
width: 100%;
|
22 |
+
height: 100%;
|
23 |
+
display: flex;
|
24 |
+
flex-direction: column;
|
25 |
+
background: rgba(11, 10, 9, 0.4);
|
26 |
+
min-width: 0;
|
27 |
+
}
|
28 |
+
</style>
|
src/lib/components/editor/context.md
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Editor Components
|
2 |
+
|
3 |
+
Code editing interface
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Wrap Monaco editor
|
8 |
+
- Connect to editor store
|
9 |
+
- Handle code changes
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
editor/
|
15 |
+
├── context.md # This file
|
16 |
+
└── CodeEditor.svelte # Monaco editor wrapper
|
17 |
+
```
|
18 |
+
|
19 |
+
## Scope
|
20 |
+
|
21 |
+
- In-scope: Code editing UI
|
22 |
+
- Out-of-scope: Game parsing, validation
|
23 |
+
|
24 |
+
## Entrypoints
|
25 |
+
|
26 |
+
- `CodeEditor.svelte` - Main editor component
|
27 |
+
|
28 |
+
## Dependencies
|
29 |
+
|
30 |
+
- editorStore for content state
|
31 |
+
- Editor.svelte (Monaco wrapper)
|
src/lib/components/game/GameCanvas.svelte
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount, onDestroy } from 'svelte';
|
3 |
+
import { uiStore } from '../../stores/ui';
|
4 |
+
import { gameStore } from '../../stores/game';
|
5 |
+
import { editorStore } from '../../stores/editor';
|
6 |
+
import { gameEngine } from '../../services/game-engine';
|
7 |
+
import { HTMLParser } from '../../services/html-parser';
|
8 |
+
import GameError from './GameError.svelte';
|
9 |
+
|
10 |
+
let reloadTimer: any;
|
11 |
+
let previousContent = '';
|
12 |
+
let isInitialized = false;
|
13 |
+
|
14 |
+
$: if ($editorStore.content !== previousContent && $gameStore.isAutoRunning && isInitialized) {
|
15 |
+
previousContent = $editorStore.content;
|
16 |
+
clearTimeout(reloadTimer);
|
17 |
+
reloadTimer = setTimeout(async () => {
|
18 |
+
if (!$gameStore.isStarting) {
|
19 |
+
const worldContent = HTMLParser.extractGameContent($editorStore.content);
|
20 |
+
await gameEngine.start(worldContent);
|
21 |
+
}
|
22 |
+
}, 800);
|
23 |
+
}
|
24 |
+
|
25 |
+
onMount(async () => {
|
26 |
+
previousContent = $editorStore.content;
|
27 |
+
setTimeout(async () => {
|
28 |
+
if (!$gameStore.instance && $gameStore.isAutoRunning) {
|
29 |
+
const worldContent = HTMLParser.extractGameContent($editorStore.content);
|
30 |
+
await gameEngine.start(worldContent);
|
31 |
+
}
|
32 |
+
isInitialized = true;
|
33 |
+
}, 400);
|
34 |
+
});
|
35 |
+
|
36 |
+
onDestroy(() => {
|
37 |
+
clearTimeout(reloadTimer);
|
38 |
+
gameEngine.stop();
|
39 |
+
});
|
40 |
+
</script>
|
41 |
+
|
42 |
+
<div class="game-section">
|
43 |
+
{#if $uiStore.error}
|
44 |
+
<GameError error={$uiStore.error} />
|
45 |
+
{/if}
|
46 |
+
|
47 |
+
<div id="world-container" style="display: none;"></div>
|
48 |
+
|
49 |
+
<div class="canvas-container">
|
50 |
+
<canvas id="game-canvas"></canvas>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
|
54 |
+
<style>
|
55 |
+
.game-section {
|
56 |
+
width: 100%;
|
57 |
+
height: 100%;
|
58 |
+
display: flex;
|
59 |
+
flex-direction: column;
|
60 |
+
min-height: 0;
|
61 |
+
}
|
62 |
+
|
63 |
+
.canvas-container {
|
64 |
+
flex: 1;
|
65 |
+
position: relative;
|
66 |
+
background: radial-gradient(ellipse at center, rgba(124, 152, 133, 0.02) 0%, transparent 70%);
|
67 |
+
overflow: hidden;
|
68 |
+
display: flex;
|
69 |
+
align-items: center;
|
70 |
+
justify-content: center;
|
71 |
+
}
|
72 |
+
|
73 |
+
#game-canvas {
|
74 |
+
width: 100%;
|
75 |
+
height: 100%;
|
76 |
+
display: block;
|
77 |
+
object-fit: contain;
|
78 |
+
pointer-events: auto;
|
79 |
+
user-select: none;
|
80 |
+
}
|
81 |
+
</style>
|
src/lib/components/game/GameError.svelte
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let error: string;
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<div class="error-message">
|
6 |
+
❌ {error}
|
7 |
+
</div>
|
8 |
+
|
9 |
+
<style>
|
10 |
+
.error-message {
|
11 |
+
padding: 12px 16px;
|
12 |
+
background: rgba(184, 84, 80, 0.08);
|
13 |
+
border-left: 2px solid #B85450;
|
14 |
+
color: #B85450;
|
15 |
+
font-size: 12px;
|
16 |
+
font-family: "JetBrains Mono", "Monaco", "Menlo", monospace;
|
17 |
+
animation: slideDown 0.3s ease;
|
18 |
+
}
|
19 |
+
|
20 |
+
@keyframes slideDown {
|
21 |
+
from {
|
22 |
+
opacity: 0;
|
23 |
+
transform: translateY(-10px);
|
24 |
+
}
|
25 |
+
to {
|
26 |
+
opacity: 1;
|
27 |
+
transform: translateY(0);
|
28 |
+
}
|
29 |
+
}
|
30 |
+
</style>
|
src/lib/components/game/context.md
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Game Components
|
2 |
+
|
3 |
+
Game rendering and error display
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Render game canvas
|
8 |
+
- Display game errors
|
9 |
+
- Manage game lifecycle
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
game/
|
15 |
+
├── context.md # This file
|
16 |
+
├── GameCanvas.svelte # Game rendering canvas
|
17 |
+
└── GameError.svelte # Error message display
|
18 |
+
```
|
19 |
+
|
20 |
+
## Scope
|
21 |
+
|
22 |
+
- In-scope: Game display, error UI
|
23 |
+
- Out-of-scope: Game logic, physics
|
24 |
+
|
25 |
+
## Entrypoints
|
26 |
+
|
27 |
+
- `GameCanvas.svelte` - Main game rendering component
|
28 |
+
|
29 |
+
## Dependencies
|
30 |
+
|
31 |
+
- gameStore for game state
|
32 |
+
- gameEngine service for lifecycle
|
33 |
+
- uiStore for error state
|
src/lib/components/layout/AppHeader.svelte
ADDED
@@ -0,0 +1,318 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount } from 'svelte';
|
3 |
+
import { uiStore } from '../../stores/ui';
|
4 |
+
import { gameStore } from '../../stores/game';
|
5 |
+
import { gameEngine } from '../../services/game-engine';
|
6 |
+
import { editorStore } from '../../stores/editor';
|
7 |
+
import { HTMLParser } from '../../services/html-parser';
|
8 |
+
import LoginButton from '../auth/LoginButton.svelte';
|
9 |
+
import gsap from 'gsap';
|
10 |
+
|
11 |
+
async function restartGame() {
|
12 |
+
const worldContent = HTMLParser.extractGameContent($editorStore.content);
|
13 |
+
await gameEngine.start(worldContent);
|
14 |
+
}
|
15 |
+
|
16 |
+
function handleViewModeChange(mode: 'code' | 'preview') {
|
17 |
+
uiStore.setViewMode(mode);
|
18 |
+
}
|
19 |
+
|
20 |
+
onMount(() => {
|
21 |
+
gsap.fromTo(".app-header",
|
22 |
+
{ opacity: 0, y: -20 },
|
23 |
+
{ opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }
|
24 |
+
);
|
25 |
+
|
26 |
+
const handleKeyPress = (e: KeyboardEvent) => {
|
27 |
+
if ((e.ctrlKey || e.metaKey) && e.key === 'e') {
|
28 |
+
e.preventDefault();
|
29 |
+
uiStore.toggleViewMode();
|
30 |
+
}
|
31 |
+
};
|
32 |
+
window.addEventListener('keydown', handleKeyPress);
|
33 |
+
|
34 |
+
const toggleButtons = document.querySelectorAll('.toggle-btn');
|
35 |
+
toggleButtons.forEach(btn => {
|
36 |
+
btn.addEventListener('mouseenter', () => {
|
37 |
+
if (!btn.classList.contains('active')) {
|
38 |
+
gsap.to(btn, {
|
39 |
+
scale: 1.05,
|
40 |
+
duration: 0.2,
|
41 |
+
ease: "power2.out"
|
42 |
+
});
|
43 |
+
}
|
44 |
+
});
|
45 |
+
|
46 |
+
btn.addEventListener('mouseleave', () => {
|
47 |
+
if (!btn.classList.contains('active')) {
|
48 |
+
gsap.to(btn, {
|
49 |
+
scale: 1,
|
50 |
+
duration: 0.2,
|
51 |
+
ease: "power2.out"
|
52 |
+
});
|
53 |
+
}
|
54 |
+
});
|
55 |
+
});
|
56 |
+
|
57 |
+
return () => {
|
58 |
+
window.removeEventListener('keydown', handleKeyPress);
|
59 |
+
};
|
60 |
+
});
|
61 |
+
</script>
|
62 |
+
|
63 |
+
<div class="app-header">
|
64 |
+
<div class="header-left">
|
65 |
+
<div class="app-title">
|
66 |
+
<span class="app-icon">🥕</span>
|
67 |
+
<span class="app-name">VibeGame</span>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
<div class="header-center">
|
72 |
+
<div class="view-toggle">
|
73 |
+
<button
|
74 |
+
class="toggle-btn"
|
75 |
+
class:active={$uiStore.viewMode === 'code'}
|
76 |
+
on:click={() => handleViewModeChange('code')}
|
77 |
+
>
|
78 |
+
Code
|
79 |
+
</button>
|
80 |
+
<button
|
81 |
+
class="toggle-btn"
|
82 |
+
class:active={$uiStore.viewMode === 'preview'}
|
83 |
+
on:click={() => handleViewModeChange('preview')}
|
84 |
+
>
|
85 |
+
Preview
|
86 |
+
</button>
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
|
90 |
+
<div class="header-right">
|
91 |
+
<LoginButton />
|
92 |
+
<div class="status-indicator">
|
93 |
+
{#if $uiStore.error}
|
94 |
+
<span class="status-dot error"></span>
|
95 |
+
{:else if $gameStore.isRunning}
|
96 |
+
<span class="status-dot running"></span>
|
97 |
+
{:else}
|
98 |
+
<span class="status-dot"></span>
|
99 |
+
{/if}
|
100 |
+
</div>
|
101 |
+
<button
|
102 |
+
on:click={restartGame}
|
103 |
+
class="restart-button"
|
104 |
+
aria-label="Restart"
|
105 |
+
>
|
106 |
+
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
|
107 |
+
<path d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
108 |
+
<path d="M8 1a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h1.293L5.646 1.354a.5.5 0 1 1 .708-.708L8 2.293V1.5A.5.5 0 0 1 8 1z"/>
|
109 |
+
</svg>
|
110 |
+
<span>Restart</span>
|
111 |
+
</button>
|
112 |
+
</div>
|
113 |
+
</div>
|
114 |
+
|
115 |
+
<style>
|
116 |
+
.app-header {
|
117 |
+
height: 40px;
|
118 |
+
background: rgba(15, 14, 12, 0.6);
|
119 |
+
backdrop-filter: blur(10px);
|
120 |
+
border-bottom: 1px solid rgba(139, 115, 85, 0.06);
|
121 |
+
display: flex;
|
122 |
+
align-items: center;
|
123 |
+
justify-content: space-between;
|
124 |
+
padding: 0 16px;
|
125 |
+
flex-shrink: 0;
|
126 |
+
position: relative;
|
127 |
+
z-index: 10;
|
128 |
+
}
|
129 |
+
|
130 |
+
.header-left,
|
131 |
+
.header-center,
|
132 |
+
.header-right {
|
133 |
+
display: flex;
|
134 |
+
align-items: center;
|
135 |
+
flex: 1;
|
136 |
+
}
|
137 |
+
|
138 |
+
.header-left {
|
139 |
+
justify-content: flex-start;
|
140 |
+
}
|
141 |
+
|
142 |
+
.app-title {
|
143 |
+
display: flex;
|
144 |
+
align-items: center;
|
145 |
+
gap: 0.5rem;
|
146 |
+
user-select: none;
|
147 |
+
}
|
148 |
+
|
149 |
+
.app-icon {
|
150 |
+
font-size: 1.25rem;
|
151 |
+
line-height: 1;
|
152 |
+
}
|
153 |
+
|
154 |
+
.app-name {
|
155 |
+
font-size: 0.875rem;
|
156 |
+
font-weight: 600;
|
157 |
+
color: rgba(251, 248, 244, 0.9);
|
158 |
+
letter-spacing: 0.025em;
|
159 |
+
}
|
160 |
+
|
161 |
+
.header-center {
|
162 |
+
justify-content: center;
|
163 |
+
}
|
164 |
+
|
165 |
+
.header-right {
|
166 |
+
justify-content: flex-end;
|
167 |
+
gap: 12px;
|
168 |
+
}
|
169 |
+
|
170 |
+
.view-toggle {
|
171 |
+
display: flex;
|
172 |
+
background: rgba(139, 115, 85, 0.05);
|
173 |
+
border-radius: 6px;
|
174 |
+
padding: 2px;
|
175 |
+
border: 1px solid rgba(139, 115, 85, 0.08);
|
176 |
+
}
|
177 |
+
|
178 |
+
.toggle-btn {
|
179 |
+
padding: 6px 14px;
|
180 |
+
background: transparent;
|
181 |
+
border: none;
|
182 |
+
color: rgba(251, 248, 244, 0.5);
|
183 |
+
font-size: 11px;
|
184 |
+
font-weight: 500;
|
185 |
+
text-transform: uppercase;
|
186 |
+
letter-spacing: 0.5px;
|
187 |
+
cursor: pointer;
|
188 |
+
border-radius: 4px;
|
189 |
+
position: relative;
|
190 |
+
transform-origin: center;
|
191 |
+
transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
192 |
+
background 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
193 |
+
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
194 |
+
}
|
195 |
+
|
196 |
+
.toggle-btn::before {
|
197 |
+
content: '';
|
198 |
+
position: absolute;
|
199 |
+
inset: 0;
|
200 |
+
border-radius: 4px;
|
201 |
+
background: rgba(124, 152, 133, 0.08);
|
202 |
+
opacity: 0;
|
203 |
+
transition: opacity 0.2s ease-out;
|
204 |
+
}
|
205 |
+
|
206 |
+
.toggle-btn:hover:not(.active)::before {
|
207 |
+
opacity: 1;
|
208 |
+
}
|
209 |
+
|
210 |
+
.toggle-btn:hover:not(.active) {
|
211 |
+
color: rgba(251, 248, 244, 0.7);
|
212 |
+
}
|
213 |
+
|
214 |
+
.toggle-btn.active {
|
215 |
+
background: rgba(124, 152, 133, 0.15);
|
216 |
+
color: rgba(251, 248, 244, 0.9);
|
217 |
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1),
|
218 |
+
inset 0 1px 0 rgba(124, 152, 133, 0.2);
|
219 |
+
}
|
220 |
+
|
221 |
+
.toggle-btn.active::after {
|
222 |
+
content: '';
|
223 |
+
position: absolute;
|
224 |
+
bottom: -1px;
|
225 |
+
left: 50%;
|
226 |
+
transform: translateX(-50%);
|
227 |
+
width: 20px;
|
228 |
+
height: 2px;
|
229 |
+
background: #7C9885;
|
230 |
+
border-radius: 1px;
|
231 |
+
animation: slideIn 0.2s ease-out;
|
232 |
+
}
|
233 |
+
|
234 |
+
@keyframes slideIn {
|
235 |
+
from {
|
236 |
+
width: 0;
|
237 |
+
opacity: 0;
|
238 |
+
}
|
239 |
+
to {
|
240 |
+
width: 20px;
|
241 |
+
opacity: 1;
|
242 |
+
}
|
243 |
+
}
|
244 |
+
|
245 |
+
.restart-button {
|
246 |
+
display: flex;
|
247 |
+
align-items: center;
|
248 |
+
gap: 4px;
|
249 |
+
padding: 0.25rem 0.75rem;
|
250 |
+
background: transparent;
|
251 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
252 |
+
border-radius: 0.25rem;
|
253 |
+
color: rgba(255, 255, 255, 0.6);
|
254 |
+
font-size: 0.75rem;
|
255 |
+
font-weight: 500;
|
256 |
+
cursor: pointer;
|
257 |
+
transition: all 0.2s;
|
258 |
+
position: relative;
|
259 |
+
}
|
260 |
+
|
261 |
+
.restart-button:hover {
|
262 |
+
background: rgba(255, 255, 255, 0.05);
|
263 |
+
border-color: rgba(255, 255, 255, 0.2);
|
264 |
+
color: rgba(255, 255, 255, 0.9);
|
265 |
+
}
|
266 |
+
|
267 |
+
.restart-button span {
|
268 |
+
text-transform: none;
|
269 |
+
letter-spacing: normal;
|
270 |
+
}
|
271 |
+
|
272 |
+
.restart-button:active {
|
273 |
+
transform: scale(0.98);
|
274 |
+
}
|
275 |
+
|
276 |
+
.restart-button svg {
|
277 |
+
transition: transform 0.2s ease-out;
|
278 |
+
}
|
279 |
+
|
280 |
+
.restart-button:hover svg {
|
281 |
+
transform: rotate(180deg);
|
282 |
+
}
|
283 |
+
|
284 |
+
.status-indicator {
|
285 |
+
display: flex;
|
286 |
+
align-items: center;
|
287 |
+
}
|
288 |
+
|
289 |
+
.status-dot {
|
290 |
+
width: 8px;
|
291 |
+
height: 8px;
|
292 |
+
border-radius: 50%;
|
293 |
+
background: rgba(139, 115, 85, 0.3);
|
294 |
+
transition: all 0.3s ease;
|
295 |
+
}
|
296 |
+
|
297 |
+
.status-dot.running {
|
298 |
+
background: #7C9885;
|
299 |
+
box-shadow: 0 0 12px rgba(124, 152, 133, 0.4);
|
300 |
+
animation: breathe 2s ease-in-out infinite;
|
301 |
+
}
|
302 |
+
|
303 |
+
.status-dot.error {
|
304 |
+
background: #B85450;
|
305 |
+
box-shadow: 0 0 12px rgba(184, 84, 80, 0.4);
|
306 |
+
}
|
307 |
+
|
308 |
+
@keyframes breathe {
|
309 |
+
0%, 100% {
|
310 |
+
opacity: 0.8;
|
311 |
+
transform: scale(1);
|
312 |
+
}
|
313 |
+
50% {
|
314 |
+
opacity: 1;
|
315 |
+
transform: scale(1.1);
|
316 |
+
}
|
317 |
+
}
|
318 |
+
</style>
|
src/lib/components/layout/LoadingScreen.svelte
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount } from 'svelte';
|
3 |
+
import { gsap } from 'gsap';
|
4 |
+
|
5 |
+
export let loading = true;
|
6 |
+
|
7 |
+
let container: HTMLDivElement;
|
8 |
+
let spinner: HTMLDivElement;
|
9 |
+
let fadeOut = false;
|
10 |
+
|
11 |
+
onMount(() => {
|
12 |
+
const tl = gsap.timeline();
|
13 |
+
|
14 |
+
tl.from('.loading-title', {
|
15 |
+
opacity: 0,
|
16 |
+
y: 10,
|
17 |
+
duration: 0.3,
|
18 |
+
ease: 'power3.out'
|
19 |
+
})
|
20 |
+
.from('.spinner', {
|
21 |
+
opacity: 0,
|
22 |
+
scale: 0.8,
|
23 |
+
duration: 0.2,
|
24 |
+
ease: 'power3.out'
|
25 |
+
}, '-=0.1');
|
26 |
+
|
27 |
+
gsap.to(spinner, {
|
28 |
+
rotation: 360,
|
29 |
+
duration: 1,
|
30 |
+
repeat: -1,
|
31 |
+
ease: 'none'
|
32 |
+
});
|
33 |
+
|
34 |
+
return () => {
|
35 |
+
tl.kill();
|
36 |
+
gsap.killTweensOf(spinner);
|
37 |
+
};
|
38 |
+
});
|
39 |
+
|
40 |
+
$: if (!loading && container && !fadeOut) {
|
41 |
+
fadeOut = true;
|
42 |
+
gsap.to(container, {
|
43 |
+
opacity: 0,
|
44 |
+
duration: 0.25,
|
45 |
+
ease: 'power2.inOut',
|
46 |
+
onComplete: () => {
|
47 |
+
container.style.display = 'none';
|
48 |
+
}
|
49 |
+
});
|
50 |
+
}
|
51 |
+
</script>
|
52 |
+
|
53 |
+
{#if loading || fadeOut}
|
54 |
+
<div bind:this={container} class="loading-screen">
|
55 |
+
<div class="loading-content">
|
56 |
+
<h1 class="loading-title">VibeGame</h1>
|
57 |
+
<div bind:this={spinner} class="spinner">
|
58 |
+
<div class="spinner-ring"></div>
|
59 |
+
<div class="spinner-ring"></div>
|
60 |
+
<div class="spinner-ring"></div>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
</div>
|
64 |
+
{/if}
|
65 |
+
|
66 |
+
<style>
|
67 |
+
.loading-screen {
|
68 |
+
position: fixed;
|
69 |
+
top: 0;
|
70 |
+
left: 0;
|
71 |
+
width: 100vw;
|
72 |
+
height: 100vh;
|
73 |
+
background: #0B0A09;
|
74 |
+
display: flex;
|
75 |
+
align-items: center;
|
76 |
+
justify-content: center;
|
77 |
+
z-index: 10000;
|
78 |
+
}
|
79 |
+
|
80 |
+
.loading-content {
|
81 |
+
text-align: center;
|
82 |
+
display: flex;
|
83 |
+
flex-direction: column;
|
84 |
+
align-items: center;
|
85 |
+
gap: 2.5rem;
|
86 |
+
}
|
87 |
+
|
88 |
+
.loading-title {
|
89 |
+
font-size: 2.5rem;
|
90 |
+
font-weight: 200;
|
91 |
+
margin: 0;
|
92 |
+
color: #ffffff;
|
93 |
+
letter-spacing: -0.02em;
|
94 |
+
}
|
95 |
+
|
96 |
+
.spinner {
|
97 |
+
position: relative;
|
98 |
+
width: 40px;
|
99 |
+
height: 40px;
|
100 |
+
}
|
101 |
+
|
102 |
+
.spinner-ring {
|
103 |
+
position: absolute;
|
104 |
+
width: 100%;
|
105 |
+
height: 100%;
|
106 |
+
border: 2px solid transparent;
|
107 |
+
border-radius: 50%;
|
108 |
+
border-top-color: #ffffff;
|
109 |
+
opacity: 0.3;
|
110 |
+
}
|
111 |
+
|
112 |
+
.spinner-ring:nth-child(1) {
|
113 |
+
animation: spin 1.5s linear infinite;
|
114 |
+
}
|
115 |
+
|
116 |
+
.spinner-ring:nth-child(2) {
|
117 |
+
width: 80%;
|
118 |
+
height: 80%;
|
119 |
+
top: 10%;
|
120 |
+
left: 10%;
|
121 |
+
border-top-color: #ffffff;
|
122 |
+
opacity: 0.5;
|
123 |
+
animation: spin 1.2s linear infinite reverse;
|
124 |
+
}
|
125 |
+
|
126 |
+
.spinner-ring:nth-child(3) {
|
127 |
+
width: 60%;
|
128 |
+
height: 60%;
|
129 |
+
top: 20%;
|
130 |
+
left: 20%;
|
131 |
+
border-top-color: #ffffff;
|
132 |
+
opacity: 0.8;
|
133 |
+
animation: spin 0.9s linear infinite;
|
134 |
+
}
|
135 |
+
|
136 |
+
@keyframes spin {
|
137 |
+
to {
|
138 |
+
transform: rotate(360deg);
|
139 |
+
}
|
140 |
+
}
|
141 |
+
</style>
|
src/lib/components/layout/SplitView.svelte
ADDED
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { onMount } from 'svelte';
|
3 |
+
import { Splitpanes, Pane } from 'svelte-splitpanes';
|
4 |
+
import { uiStore } from '../../stores/ui';
|
5 |
+
import CodeEditor from '../editor/CodeEditor.svelte';
|
6 |
+
import GameCanvas from '../game/GameCanvas.svelte';
|
7 |
+
import ConsolePanel from '../console/ConsolePanel.svelte';
|
8 |
+
import ChatPanel from '../chat/ChatPanel.svelte';
|
9 |
+
import gsap from 'gsap';
|
10 |
+
|
11 |
+
$: viewMode = $uiStore.viewMode;
|
12 |
+
|
13 |
+
let userMainPaneSizes = { editor: 0.4, preview: 0.6 };
|
14 |
+
let userEditorPaneSizes = { code: 0.7, chat: 0.3 };
|
15 |
+
let userPreviewPaneSizes = { game: 0.75, console: 0.25 };
|
16 |
+
|
17 |
+
onMount(() => {
|
18 |
+
gsap.fromTo(".editor-pane",
|
19 |
+
{ opacity: 0, x: -20 },
|
20 |
+
{ opacity: 1, x: 0, duration: 0.6, delay: 0.1, ease: "power3.out" }
|
21 |
+
);
|
22 |
+
|
23 |
+
gsap.fromTo(".preview-pane",
|
24 |
+
{ opacity: 0, x: 20 },
|
25 |
+
{ opacity: 1, x: 0, duration: 0.6, delay: 0.2, ease: "power3.out" }
|
26 |
+
);
|
27 |
+
|
28 |
+
const resetCursor = () => {
|
29 |
+
document.body.style.cursor = '';
|
30 |
+
};
|
31 |
+
document.addEventListener('mouseup', resetCursor);
|
32 |
+
|
33 |
+
return () => {
|
34 |
+
document.removeEventListener('mouseup', resetCursor);
|
35 |
+
};
|
36 |
+
});
|
37 |
+
|
38 |
+
$: if (viewMode) {
|
39 |
+
handleViewModeAnimation(viewMode);
|
40 |
+
}
|
41 |
+
|
42 |
+
function handleViewModeAnimation(mode: 'code' | 'preview') {
|
43 |
+
const mainPanes = document.querySelector('.main-splitpanes')?.querySelectorAll(':scope > .splitpanes__pane');
|
44 |
+
if (!mainPanes || mainPanes.length < 2) return;
|
45 |
+
|
46 |
+
const editorPaneEl = mainPanes[0] as HTMLElement;
|
47 |
+
const previewPaneEl = mainPanes[1] as HTMLElement;
|
48 |
+
const mainSplitter = document.querySelector('.main-splitpanes > .splitpanes__splitter') as HTMLElement;
|
49 |
+
|
50 |
+
if (!editorPaneEl || !previewPaneEl) return;
|
51 |
+
|
52 |
+
const editorSplitpanes = editorPaneEl.querySelectorAll('.splitpanes__pane');
|
53 |
+
const codePane = editorSplitpanes[0] as HTMLElement;
|
54 |
+
const chatPane = editorSplitpanes[1] as HTMLElement;
|
55 |
+
|
56 |
+
const previewSplitpanes = previewPaneEl.querySelectorAll('.splitpanes__pane');
|
57 |
+
const gamePane = previewSplitpanes[0] as HTMLElement;
|
58 |
+
const consolePane = previewSplitpanes[1] as HTMLElement;
|
59 |
+
const consoleSplitter = previewPaneEl.querySelector('.splitpanes__splitter') as HTMLElement;
|
60 |
+
|
61 |
+
if (mode === 'preview') {
|
62 |
+
const editorWidth = editorPaneEl.offsetWidth;
|
63 |
+
const previewWidth = previewPaneEl.offsetWidth;
|
64 |
+
const totalWidth = editorWidth + previewWidth;
|
65 |
+
|
66 |
+
if (totalWidth > 0) {
|
67 |
+
userMainPaneSizes.editor = editorWidth / totalWidth;
|
68 |
+
userMainPaneSizes.preview = previewWidth / totalWidth;
|
69 |
+
}
|
70 |
+
|
71 |
+
if (codePane && chatPane) {
|
72 |
+
const codeHeight = codePane.offsetHeight;
|
73 |
+
const chatHeight = chatPane.offsetHeight;
|
74 |
+
const totalEditorHeight = codeHeight + chatHeight;
|
75 |
+
|
76 |
+
if (totalEditorHeight > 0) {
|
77 |
+
userEditorPaneSizes.code = codeHeight / totalEditorHeight;
|
78 |
+
userEditorPaneSizes.chat = chatHeight / totalEditorHeight;
|
79 |
+
}
|
80 |
+
}
|
81 |
+
|
82 |
+
if (gamePane && consolePane) {
|
83 |
+
const gameHeight = gamePane.offsetHeight;
|
84 |
+
const consoleHeight = consolePane.offsetHeight;
|
85 |
+
const totalHeight = gameHeight + consoleHeight;
|
86 |
+
|
87 |
+
if (totalHeight > 0) {
|
88 |
+
userPreviewPaneSizes.game = gameHeight / totalHeight;
|
89 |
+
userPreviewPaneSizes.console = consoleHeight / totalHeight;
|
90 |
+
}
|
91 |
+
}
|
92 |
+
|
93 |
+
gsap.set(editorPaneEl, { flexGrow: userMainPaneSizes.editor, flexBasis: `${userMainPaneSizes.editor * 100}%` });
|
94 |
+
gsap.set(previewPaneEl, { flexGrow: userMainPaneSizes.preview, flexBasis: `${userMainPaneSizes.preview * 100}%` });
|
95 |
+
|
96 |
+
if (codePane && chatPane) {
|
97 |
+
gsap.set(codePane, { flexGrow: userEditorPaneSizes.code, flexBasis: `${userEditorPaneSizes.code * 100}%` });
|
98 |
+
gsap.set(chatPane, { flexGrow: userEditorPaneSizes.chat, flexBasis: `${userEditorPaneSizes.chat * 100}%` });
|
99 |
+
}
|
100 |
+
|
101 |
+
if (gamePane && consolePane) {
|
102 |
+
gsap.set(gamePane, { flexGrow: userPreviewPaneSizes.game, flexBasis: `${userPreviewPaneSizes.game * 100}%` });
|
103 |
+
gsap.set(consolePane, { flexGrow: userPreviewPaneSizes.console, flexBasis: `${userPreviewPaneSizes.console * 100}%` });
|
104 |
+
}
|
105 |
+
}
|
106 |
+
|
107 |
+
if (mode === 'preview') {
|
108 |
+
gsap.timeline({ ease: "power2.out" })
|
109 |
+
.to(editorPaneEl, {
|
110 |
+
opacity: 0,
|
111 |
+
duration: 0.15,
|
112 |
+
})
|
113 |
+
.to(consolePane, {
|
114 |
+
opacity: 0,
|
115 |
+
duration: 0.15,
|
116 |
+
}, "-=0.15")
|
117 |
+
.to(editorPaneEl, {
|
118 |
+
flexGrow: 0,
|
119 |
+
flexBasis: "0%",
|
120 |
+
duration: 0.2,
|
121 |
+
ease: "power2.inOut",
|
122 |
+
onComplete: () => {
|
123 |
+
editorPaneEl.style.display = 'none';
|
124 |
+
if (mainSplitter) mainSplitter.style.display = 'none';
|
125 |
+
}
|
126 |
+
}, "-=0.1")
|
127 |
+
.to(consolePane, {
|
128 |
+
flexGrow: 0,
|
129 |
+
flexBasis: "0%",
|
130 |
+
duration: 0.2,
|
131 |
+
ease: "power2.inOut",
|
132 |
+
onComplete: () => {
|
133 |
+
consolePane.style.display = 'none';
|
134 |
+
if (consoleSplitter) consoleSplitter.style.display = 'none';
|
135 |
+
}
|
136 |
+
}, "-=0.2")
|
137 |
+
.to(gamePane, {
|
138 |
+
flexGrow: 1,
|
139 |
+
flexBasis: "100%",
|
140 |
+
duration: 0.2,
|
141 |
+
ease: "power2.inOut",
|
142 |
+
}, "-=0.2")
|
143 |
+
.to(previewPaneEl, {
|
144 |
+
flexGrow: 1,
|
145 |
+
flexBasis: "100%",
|
146 |
+
duration: 0.2,
|
147 |
+
ease: "power2.inOut",
|
148 |
+
}, "-=0.2");
|
149 |
+
} else {
|
150 |
+
editorPaneEl.style.display = '';
|
151 |
+
if (mainSplitter) mainSplitter.style.display = '';
|
152 |
+
consolePane.style.display = '';
|
153 |
+
if (consoleSplitter) consoleSplitter.style.display = '';
|
154 |
+
|
155 |
+
gsap.timeline({ ease: "power2.out" })
|
156 |
+
.set(editorPaneEl, {
|
157 |
+
opacity: 0,
|
158 |
+
flexGrow: 0,
|
159 |
+
flexBasis: "0%"
|
160 |
+
})
|
161 |
+
.set(consolePane, {
|
162 |
+
opacity: 0,
|
163 |
+
flexGrow: 0,
|
164 |
+
flexBasis: "0%"
|
165 |
+
})
|
166 |
+
.to([editorPaneEl, previewPaneEl], {
|
167 |
+
flexGrow: (i) => i === 0 ? userMainPaneSizes.editor : userMainPaneSizes.preview,
|
168 |
+
flexBasis: (i) => i === 0 ? `${userMainPaneSizes.editor * 100}%` : `${userMainPaneSizes.preview * 100}%`,
|
169 |
+
duration: 0.2,
|
170 |
+
ease: "power2.inOut",
|
171 |
+
})
|
172 |
+
.to([codePane, chatPane], {
|
173 |
+
flexGrow: (i) => i === 0 ? userEditorPaneSizes.code : userEditorPaneSizes.chat,
|
174 |
+
flexBasis: (i) => i === 0 ? `${userEditorPaneSizes.code * 100}%` : `${userEditorPaneSizes.chat * 100}%`,
|
175 |
+
duration: 0.2,
|
176 |
+
ease: "power2.inOut",
|
177 |
+
}, "-=0.2")
|
178 |
+
.to([gamePane, consolePane], {
|
179 |
+
flexGrow: (i) => i === 0 ? userPreviewPaneSizes.game : userPreviewPaneSizes.console,
|
180 |
+
flexBasis: (i) => i === 0 ? `${userPreviewPaneSizes.game * 100}%` : `${userPreviewPaneSizes.console * 100}%`,
|
181 |
+
duration: 0.2,
|
182 |
+
ease: "power2.inOut",
|
183 |
+
}, "-=0.2")
|
184 |
+
.to(consolePane, {
|
185 |
+
opacity: 1,
|
186 |
+
duration: 0.15,
|
187 |
+
}, "-=0.15")
|
188 |
+
.to(editorPaneEl, {
|
189 |
+
opacity: 1,
|
190 |
+
duration: 0.15,
|
191 |
+
onComplete: () => {
|
192 |
+
window.dispatchEvent(new Event('resize'));
|
193 |
+
}
|
194 |
+
}, "-=0.15");
|
195 |
+
}
|
196 |
+
}
|
197 |
+
</script>
|
198 |
+
|
199 |
+
<div class="editor-layout">
|
200 |
+
<Splitpanes class="main-splitpanes" theme="modern-theme">
|
201 |
+
<Pane minSize={20} size={40} class="editor-pane">
|
202 |
+
<div class="editor-panel">
|
203 |
+
<Splitpanes horizontal class="editor-splitpanes" theme="modern-theme">
|
204 |
+
<Pane minSize={30} size={70} class="code-pane">
|
205 |
+
<CodeEditor />
|
206 |
+
</Pane>
|
207 |
+
<Pane minSize={15} size={30} class="chat-pane">
|
208 |
+
<ChatPanel />
|
209 |
+
</Pane>
|
210 |
+
</Splitpanes>
|
211 |
+
</div>
|
212 |
+
</Pane>
|
213 |
+
<Pane minSize={25} size={60} class="preview-pane">
|
214 |
+
<div class="preview-panel">
|
215 |
+
<Splitpanes horizontal class="preview-splitpanes" theme="modern-theme">
|
216 |
+
<Pane minSize={30} class="game-pane">
|
217 |
+
<GameCanvas />
|
218 |
+
</Pane>
|
219 |
+
<Pane minSize={15} size={25} class="console-pane">
|
220 |
+
<ConsolePanel />
|
221 |
+
</Pane>
|
222 |
+
</Splitpanes>
|
223 |
+
</div>
|
224 |
+
</Pane>
|
225 |
+
</Splitpanes>
|
226 |
+
</div>
|
227 |
+
|
228 |
+
<style>
|
229 |
+
.editor-layout {
|
230 |
+
flex: 1;
|
231 |
+
display: flex;
|
232 |
+
min-height: 0;
|
233 |
+
background: rgba(139, 115, 85, 0.02);
|
234 |
+
}
|
235 |
+
|
236 |
+
:global(.main-splitpanes) {
|
237 |
+
width: 100%;
|
238 |
+
height: 100%;
|
239 |
+
}
|
240 |
+
|
241 |
+
:global(.editor-splitpanes) {
|
242 |
+
width: 100%;
|
243 |
+
height: 100%;
|
244 |
+
}
|
245 |
+
|
246 |
+
:global(.preview-splitpanes) {
|
247 |
+
width: 100%;
|
248 |
+
height: 100%;
|
249 |
+
}
|
250 |
+
|
251 |
+
:global(.editor-pane) {
|
252 |
+
display: flex;
|
253 |
+
overflow: hidden;
|
254 |
+
}
|
255 |
+
|
256 |
+
:global(.preview-pane) {
|
257 |
+
display: flex;
|
258 |
+
overflow: hidden;
|
259 |
+
}
|
260 |
+
|
261 |
+
:global(.code-pane) {
|
262 |
+
display: flex;
|
263 |
+
overflow: hidden;
|
264 |
+
}
|
265 |
+
|
266 |
+
:global(.game-pane) {
|
267 |
+
display: flex;
|
268 |
+
overflow: hidden;
|
269 |
+
}
|
270 |
+
|
271 |
+
:global(.console-pane) {
|
272 |
+
display: flex;
|
273 |
+
overflow: hidden;
|
274 |
+
}
|
275 |
+
|
276 |
+
:global(.chat-pane) {
|
277 |
+
display: flex;
|
278 |
+
overflow: hidden;
|
279 |
+
}
|
280 |
+
|
281 |
+
.editor-panel {
|
282 |
+
width: 100%;
|
283 |
+
height: 100%;
|
284 |
+
display: flex;
|
285 |
+
flex-direction: column;
|
286 |
+
background: rgba(11, 10, 9, 0.3);
|
287 |
+
min-width: 0;
|
288 |
+
overflow: hidden;
|
289 |
+
}
|
290 |
+
|
291 |
+
.preview-panel {
|
292 |
+
width: 100%;
|
293 |
+
height: 100%;
|
294 |
+
display: flex;
|
295 |
+
flex-direction: column;
|
296 |
+
background: rgba(11, 10, 9, 0.3);
|
297 |
+
min-width: 0;
|
298 |
+
overflow: hidden;
|
299 |
+
}
|
300 |
+
|
301 |
+
:global(.splitpanes.modern-theme .splitpanes__splitter) {
|
302 |
+
background: rgba(139, 115, 85, 0.06);
|
303 |
+
position: relative;
|
304 |
+
transition: background 0.2s;
|
305 |
+
}
|
306 |
+
|
307 |
+
:global(.splitpanes.modern-theme .splitpanes__splitter:hover) {
|
308 |
+
background: rgba(139, 115, 85, 0.12);
|
309 |
+
}
|
310 |
+
|
311 |
+
:global(.splitpanes.modern-theme .splitpanes__splitter:before) {
|
312 |
+
content: '';
|
313 |
+
position: absolute;
|
314 |
+
z-index: 1;
|
315 |
+
transition: opacity 0.2s;
|
316 |
+
background: rgba(124, 152, 133, 0.1);
|
317 |
+
opacity: 0;
|
318 |
+
}
|
319 |
+
|
320 |
+
:global(.splitpanes.modern-theme .splitpanes__splitter:hover:before) {
|
321 |
+
opacity: 1;
|
322 |
+
}
|
323 |
+
|
324 |
+
:global(.splitpanes--vertical.modern-theme > .splitpanes__splitter) {
|
325 |
+
width: 1px;
|
326 |
+
cursor: col-resize;
|
327 |
+
}
|
328 |
+
|
329 |
+
:global(.splitpanes--vertical.modern-theme > .splitpanes__splitter:before) {
|
330 |
+
left: -3px;
|
331 |
+
right: -3px;
|
332 |
+
height: 100%;
|
333 |
+
cursor: col-resize;
|
334 |
+
}
|
335 |
+
|
336 |
+
:global(.splitpanes--horizontal.modern-theme > .splitpanes__splitter) {
|
337 |
+
height: 1px;
|
338 |
+
cursor: row-resize;
|
339 |
+
}
|
340 |
+
|
341 |
+
:global(.splitpanes--horizontal.modern-theme > .splitpanes__splitter:before) {
|
342 |
+
top: -3px;
|
343 |
+
bottom: -3px;
|
344 |
+
width: 100%;
|
345 |
+
cursor: row-resize;
|
346 |
+
}
|
347 |
+
|
348 |
+
:global(body.splitpanes--dragging) {
|
349 |
+
cursor: inherit;
|
350 |
+
}
|
351 |
+
|
352 |
+
:global(body:not(.splitpanes--dragging) .splitpanes__splitter:not(:hover)) {
|
353 |
+
cursor: default;
|
354 |
+
}
|
355 |
+
</style>
|
src/lib/components/layout/context.md
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Layout Components
|
2 |
+
|
3 |
+
Application layout structure
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Loading state management
|
8 |
+
- Header navigation
|
9 |
+
- Two-pane layout with nested splits
|
10 |
+
- View mode transitions
|
11 |
+
|
12 |
+
## Structure
|
13 |
+
|
14 |
+
```
|
15 |
+
layout/
|
16 |
+
├── context.md # This file
|
17 |
+
├── LoadingScreen.svelte # Initial loading with spinner
|
18 |
+
├── AppHeader.svelte # Top navigation bar
|
19 |
+
└── SplitView.svelte # Two-pane layout with nested splits
|
20 |
+
```
|
21 |
+
|
22 |
+
## Layout Organization
|
23 |
+
|
24 |
+
- Main split: Editor pane (left) | Preview pane (right)
|
25 |
+
- Editor pane: Code editor (top) | Chat panel (bottom)
|
26 |
+
- Preview pane: Game canvas (top) | Console (bottom)
|
27 |
+
|
28 |
+
## Dependencies
|
29 |
+
|
30 |
+
- loadingStore for loading state
|
31 |
+
- uiStore for view mode
|
32 |
+
- svelte-splitpanes for resizable panes
|
33 |
+
- GSAP for animations
|
src/lib/config/animations.ts
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const animationConfig = {
|
2 |
+
header: {
|
3 |
+
initial: { opacity: 0, y: -20 },
|
4 |
+
animate: { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" },
|
5 |
+
},
|
6 |
+
|
7 |
+
editor: {
|
8 |
+
initial: { opacity: 0, x: -20 },
|
9 |
+
animate: {
|
10 |
+
opacity: 1,
|
11 |
+
x: 0,
|
12 |
+
duration: 0.6,
|
13 |
+
delay: 0.1,
|
14 |
+
ease: "power3.out",
|
15 |
+
},
|
16 |
+
},
|
17 |
+
|
18 |
+
preview: {
|
19 |
+
initial: { opacity: 0, x: 20 },
|
20 |
+
animate: {
|
21 |
+
opacity: 1,
|
22 |
+
x: 0,
|
23 |
+
duration: 0.6,
|
24 |
+
delay: 0.2,
|
25 |
+
ease: "power3.out",
|
26 |
+
},
|
27 |
+
},
|
28 |
+
|
29 |
+
consoleMessage: {
|
30 |
+
initial: { opacity: 0, y: -5 },
|
31 |
+
animate: { opacity: 1, y: 0, duration: 0.3, ease: "power2.out" },
|
32 |
+
},
|
33 |
+
|
34 |
+
error: {
|
35 |
+
initial: { opacity: 0, transform: "translateY(-10px)" },
|
36 |
+
animate: { opacity: 1, transform: "translateY(0)", duration: 0.3 },
|
37 |
+
},
|
38 |
+
|
39 |
+
viewTransition: {
|
40 |
+
duration: 0.2,
|
41 |
+
ease: "power2.inOut",
|
42 |
+
},
|
43 |
+
};
|
src/lib/config/context.md
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Configuration
|
2 |
+
|
3 |
+
Application configuration and constants
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Centralize configuration values
|
8 |
+
- Define animation settings
|
9 |
+
- Keyboard shortcuts registry
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
config/
|
15 |
+
├── context.md # This file
|
16 |
+
├── animations.ts # GSAP animation configs
|
17 |
+
└── shortcuts.ts # Keyboard shortcut definitions
|
18 |
+
```
|
19 |
+
|
20 |
+
## Scope
|
21 |
+
|
22 |
+
- In-scope: Static configuration, constants
|
23 |
+
- Out-of-scope: Runtime state, business logic
|
24 |
+
|
25 |
+
## Entrypoints
|
26 |
+
|
27 |
+
- `animationConfig` - Animation presets
|
28 |
+
- `shortcuts` - Keyboard shortcut definitions
|
29 |
+
- `registerShortcuts()` - Setup keyboard handlers
|
30 |
+
|
31 |
+
## Dependencies
|
32 |
+
|
33 |
+
- No external dependencies
|
34 |
+
- Imported by components and services
|
src/lib/config/shortcuts.ts
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface Shortcut {
|
2 |
+
key: string;
|
3 |
+
ctrl?: boolean;
|
4 |
+
meta?: boolean;
|
5 |
+
shift?: boolean;
|
6 |
+
alt?: boolean;
|
7 |
+
action: () => void;
|
8 |
+
description: string;
|
9 |
+
}
|
10 |
+
|
11 |
+
export const shortcuts: Shortcut[] = [
|
12 |
+
{
|
13 |
+
key: "e",
|
14 |
+
ctrl: true,
|
15 |
+
meta: true,
|
16 |
+
action: () => {
|
17 |
+
import("../stores/ui").then(({ uiStore }) => {
|
18 |
+
uiStore.toggleViewMode();
|
19 |
+
});
|
20 |
+
},
|
21 |
+
description: "Toggle between code and preview mode",
|
22 |
+
},
|
23 |
+
];
|
24 |
+
|
25 |
+
export function registerShortcuts(shortcuts: Shortcut[]): () => void {
|
26 |
+
const handler = (e: KeyboardEvent) => {
|
27 |
+
for (const shortcut of shortcuts) {
|
28 |
+
const ctrlMatch = shortcut.ctrl
|
29 |
+
? e.ctrlKey
|
30 |
+
: !shortcut.ctrl || !e.ctrlKey;
|
31 |
+
const metaMatch = shortcut.meta
|
32 |
+
? e.metaKey
|
33 |
+
: !shortcut.meta || !e.metaKey;
|
34 |
+
const shiftMatch = shortcut.shift
|
35 |
+
? e.shiftKey
|
36 |
+
: !shortcut.shift || !e.shiftKey;
|
37 |
+
const altMatch = shortcut.alt ? e.altKey : !shortcut.alt || !e.altKey;
|
38 |
+
|
39 |
+
if (
|
40 |
+
e.key === shortcut.key &&
|
41 |
+
(ctrlMatch || metaMatch) &&
|
42 |
+
shiftMatch &&
|
43 |
+
altMatch
|
44 |
+
) {
|
45 |
+
e.preventDefault();
|
46 |
+
shortcut.action();
|
47 |
+
break;
|
48 |
+
}
|
49 |
+
}
|
50 |
+
};
|
51 |
+
|
52 |
+
window.addEventListener("keydown", handler);
|
53 |
+
|
54 |
+
return () => {
|
55 |
+
window.removeEventListener("keydown", handler);
|
56 |
+
};
|
57 |
+
}
|
src/lib/server/agent-runner.ts
ADDED
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { HfInference } from "@huggingface/inference";
|
2 |
+
|
3 |
+
export interface AgentMessage {
|
4 |
+
id: string;
|
5 |
+
role: "user" | "assistant" | "system";
|
6 |
+
content: string;
|
7 |
+
timestamp: number;
|
8 |
+
}
|
9 |
+
|
10 |
+
export interface AgentConfig {
|
11 |
+
model?: string;
|
12 |
+
maxIterations?: number;
|
13 |
+
temperature?: number;
|
14 |
+
maxTokens?: number;
|
15 |
+
}
|
16 |
+
|
17 |
+
export class AgentRunner {
|
18 |
+
private client: HfInference | null = null;
|
19 |
+
private config: AgentConfig;
|
20 |
+
private messageHistory: AgentMessage[] = [];
|
21 |
+
|
22 |
+
constructor(config: AgentConfig = {}) {
|
23 |
+
this.config = {
|
24 |
+
model: "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
|
25 |
+
maxIterations: 10,
|
26 |
+
temperature: 0.7,
|
27 |
+
maxTokens: 1000,
|
28 |
+
...config,
|
29 |
+
};
|
30 |
+
}
|
31 |
+
|
32 |
+
async initialize(hfToken?: string) {
|
33 |
+
if (!hfToken) {
|
34 |
+
throw new Error(
|
35 |
+
"Hugging Face authentication required. Please sign in to continue.",
|
36 |
+
);
|
37 |
+
}
|
38 |
+
|
39 |
+
this.client = new HfInference(hfToken);
|
40 |
+
}
|
41 |
+
|
42 |
+
async processMessage(
|
43 |
+
message: string,
|
44 |
+
onStream?: (chunk: string) => void,
|
45 |
+
): Promise<string> {
|
46 |
+
if (!this.client) {
|
47 |
+
throw new Error("AgentRunner not initialized. Call initialize() first.");
|
48 |
+
}
|
49 |
+
|
50 |
+
const userMessage: AgentMessage = {
|
51 |
+
id: this.generateId(),
|
52 |
+
role: "user",
|
53 |
+
content: message,
|
54 |
+
timestamp: Date.now(),
|
55 |
+
};
|
56 |
+
|
57 |
+
this.messageHistory.push(userMessage);
|
58 |
+
|
59 |
+
try {
|
60 |
+
const systemPrompt = `You are an AI assistant helping to build games with VibeGame.
|
61 |
+
You can read and modify game code using XML syntax.
|
62 |
+
Always validate changes and ensure the game remains playable.
|
63 |
+
Use small, incremental changes rather than large rewrites.`;
|
64 |
+
|
65 |
+
const messages = [
|
66 |
+
{ role: "system", content: systemPrompt },
|
67 |
+
...this.messageHistory.map((msg) => ({
|
68 |
+
role: msg.role,
|
69 |
+
content: msg.content,
|
70 |
+
})),
|
71 |
+
];
|
72 |
+
|
73 |
+
let fullResponse = "";
|
74 |
+
|
75 |
+
const stream = await this.client.chatCompletionStream({
|
76 |
+
model: this.config.model!,
|
77 |
+
messages: messages as Array<{ role: string; content: string }>,
|
78 |
+
temperature: this.config.temperature,
|
79 |
+
max_tokens: this.config.maxTokens,
|
80 |
+
});
|
81 |
+
|
82 |
+
for await (const chunk of stream) {
|
83 |
+
if (chunk.choices && chunk.choices.length > 0) {
|
84 |
+
const delta = chunk.choices[0].delta?.content;
|
85 |
+
if (delta) {
|
86 |
+
fullResponse += delta;
|
87 |
+
if (onStream) {
|
88 |
+
onStream(delta);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
+
const assistantMessage: AgentMessage = {
|
95 |
+
id: this.generateId(),
|
96 |
+
role: "assistant",
|
97 |
+
content: fullResponse,
|
98 |
+
timestamp: Date.now(),
|
99 |
+
};
|
100 |
+
|
101 |
+
this.messageHistory.push(assistantMessage);
|
102 |
+
|
103 |
+
return fullResponse;
|
104 |
+
} catch (error) {
|
105 |
+
console.error("Error processing message:", error);
|
106 |
+
|
107 |
+
if (error instanceof Error && error.message.includes("rate limit")) {
|
108 |
+
throw new Error(
|
109 |
+
"Hugging Face API rate limit exceeded. Please provide an API token or wait before retrying.",
|
110 |
+
);
|
111 |
+
}
|
112 |
+
|
113 |
+
throw error;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
getHistory(): AgentMessage[] {
|
118 |
+
return [...this.messageHistory];
|
119 |
+
}
|
120 |
+
|
121 |
+
clearHistory() {
|
122 |
+
this.messageHistory = [];
|
123 |
+
}
|
124 |
+
|
125 |
+
private generateId(): string {
|
126 |
+
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
127 |
+
}
|
128 |
+
}
|
src/lib/server/api.ts
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { IncomingMessage } from "http";
|
2 |
+
import type { WebSocket } from "ws";
|
3 |
+
import type { AgentRunner } from "./agent-runner";
|
4 |
+
|
5 |
+
export interface WebSocketMessage {
|
6 |
+
type: "chat" | "error" | "status" | "stream" | "auth";
|
7 |
+
payload: {
|
8 |
+
content?: string;
|
9 |
+
role?: string;
|
10 |
+
chunk?: string;
|
11 |
+
error?: string;
|
12 |
+
processing?: boolean;
|
13 |
+
connected?: boolean;
|
14 |
+
message?: string;
|
15 |
+
token?: string;
|
16 |
+
};
|
17 |
+
timestamp: number;
|
18 |
+
}
|
19 |
+
|
20 |
+
class WebSocketManager {
|
21 |
+
private connections: Map<WebSocket, { token?: string; agent?: AgentRunner }> =
|
22 |
+
new Map();
|
23 |
+
|
24 |
+
handleConnection(ws: WebSocket, _request: IncomingMessage) {
|
25 |
+
this.connections.set(ws, {});
|
26 |
+
|
27 |
+
this.sendMessage(ws, {
|
28 |
+
type: "status",
|
29 |
+
payload: { connected: true, message: "Connected to agent server" },
|
30 |
+
timestamp: Date.now(),
|
31 |
+
});
|
32 |
+
|
33 |
+
ws.on("message", async (data) => {
|
34 |
+
try {
|
35 |
+
const message = JSON.parse(data.toString()) as WebSocketMessage;
|
36 |
+
await this.handleMessage(ws, message);
|
37 |
+
} catch (error) {
|
38 |
+
console.error("Error handling WebSocket message:", error);
|
39 |
+
this.sendError(ws, "Failed to process message");
|
40 |
+
}
|
41 |
+
});
|
42 |
+
|
43 |
+
ws.on("close", () => {
|
44 |
+
this.connections.delete(ws);
|
45 |
+
});
|
46 |
+
|
47 |
+
ws.on("error", (error) => {
|
48 |
+
console.error("WebSocket error:", error);
|
49 |
+
this.connections.delete(ws);
|
50 |
+
});
|
51 |
+
}
|
52 |
+
|
53 |
+
private async handleMessage(ws: WebSocket, message: WebSocketMessage) {
|
54 |
+
const connectionData = this.connections.get(ws);
|
55 |
+
|
56 |
+
switch (message.type) {
|
57 |
+
case "auth":
|
58 |
+
if (message.payload.token && connectionData) {
|
59 |
+
connectionData.token = message.payload.token;
|
60 |
+
|
61 |
+
try {
|
62 |
+
// Create a new agent instance for this connection
|
63 |
+
const { AgentRunner } = await import("./agent-runner");
|
64 |
+
connectionData.agent = new AgentRunner();
|
65 |
+
await connectionData.agent.initialize(message.payload.token);
|
66 |
+
|
67 |
+
this.sendMessage(ws, {
|
68 |
+
type: "status",
|
69 |
+
payload: { message: "Authenticated successfully" },
|
70 |
+
timestamp: Date.now(),
|
71 |
+
});
|
72 |
+
} catch (error) {
|
73 |
+
console.error("Authentication failed:", error);
|
74 |
+
this.sendError(
|
75 |
+
ws,
|
76 |
+
"Authentication failed. Please sign in with Hugging Face.",
|
77 |
+
);
|
78 |
+
}
|
79 |
+
} else {
|
80 |
+
this.sendError(ws, "No authentication token provided");
|
81 |
+
}
|
82 |
+
break;
|
83 |
+
|
84 |
+
case "chat":
|
85 |
+
try {
|
86 |
+
if (!connectionData?.agent) {
|
87 |
+
throw new Error(
|
88 |
+
"Authentication required. Please sign in with Hugging Face.",
|
89 |
+
);
|
90 |
+
}
|
91 |
+
|
92 |
+
const userMessage = message.payload.content;
|
93 |
+
if (!userMessage) {
|
94 |
+
throw new Error("No message content provided");
|
95 |
+
}
|
96 |
+
|
97 |
+
this.sendMessage(ws, {
|
98 |
+
type: "status",
|
99 |
+
payload: { processing: true },
|
100 |
+
timestamp: Date.now(),
|
101 |
+
});
|
102 |
+
|
103 |
+
const response = await connectionData.agent.processMessage(
|
104 |
+
userMessage,
|
105 |
+
(chunk: string) => {
|
106 |
+
this.sendMessage(ws, {
|
107 |
+
type: "stream",
|
108 |
+
payload: { chunk },
|
109 |
+
timestamp: Date.now(),
|
110 |
+
});
|
111 |
+
},
|
112 |
+
);
|
113 |
+
|
114 |
+
this.sendMessage(ws, {
|
115 |
+
type: "chat",
|
116 |
+
payload: {
|
117 |
+
role: "assistant",
|
118 |
+
content: response,
|
119 |
+
},
|
120 |
+
timestamp: Date.now(),
|
121 |
+
});
|
122 |
+
|
123 |
+
this.sendMessage(ws, {
|
124 |
+
type: "status",
|
125 |
+
payload: { processing: false },
|
126 |
+
timestamp: Date.now(),
|
127 |
+
});
|
128 |
+
} catch (error) {
|
129 |
+
console.error("Error processing chat message:", error);
|
130 |
+
this.sendError(
|
131 |
+
ws,
|
132 |
+
error instanceof Error ? error.message : "Unknown error",
|
133 |
+
);
|
134 |
+
}
|
135 |
+
break;
|
136 |
+
|
137 |
+
default:
|
138 |
+
this.sendError(ws, `Unknown message type: ${message.type}`);
|
139 |
+
}
|
140 |
+
}
|
141 |
+
|
142 |
+
private sendMessage(ws: WebSocket, message: WebSocketMessage) {
|
143 |
+
if (ws.readyState === ws.OPEN) {
|
144 |
+
ws.send(JSON.stringify(message));
|
145 |
+
}
|
146 |
+
}
|
147 |
+
|
148 |
+
private sendError(ws: WebSocket, error: string) {
|
149 |
+
this.sendMessage(ws, {
|
150 |
+
type: "error",
|
151 |
+
payload: { error },
|
152 |
+
timestamp: Date.now(),
|
153 |
+
});
|
154 |
+
}
|
155 |
+
|
156 |
+
broadcast(message: WebSocketMessage) {
|
157 |
+
for (const [ws] of this.connections) {
|
158 |
+
this.sendMessage(ws, message);
|
159 |
+
}
|
160 |
+
}
|
161 |
+
}
|
162 |
+
|
163 |
+
export const wsManager = new WebSocketManager();
|
164 |
+
|
165 |
+
export async function initializeAgent() {}
|
src/lib/server/context.md
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Server Context
|
2 |
+
|
3 |
+
WebSocket server for AI agent communication.
|
4 |
+
|
5 |
+
## Components
|
6 |
+
|
7 |
+
- `agent-runner.ts` - HuggingFace inference client with streaming support
|
8 |
+
- `api.ts` - WebSocket manager with per-connection agent instances
|
9 |
+
|
10 |
+
## Architecture
|
11 |
+
|
12 |
+
Vite plugin providing:
|
13 |
+
|
14 |
+
- WebSocket endpoint at `/ws`
|
15 |
+
- Per-connection authentication via HuggingFace tokens
|
16 |
+
- Message streaming for chat responses
|
17 |
+
|
18 |
+
## Integration
|
19 |
+
|
20 |
+
Frontend connection via `src/lib/stores/agent.ts` store.
|
src/lib/services/auth.ts
ADDED
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { writable, get } from "svelte/store";
|
2 |
+
import {
|
3 |
+
oauthLoginUrl,
|
4 |
+
oauthHandleRedirectIfPresent,
|
5 |
+
type UserInfo,
|
6 |
+
} from "@huggingface/hub";
|
7 |
+
|
8 |
+
export interface AuthState {
|
9 |
+
isAuthenticated: boolean;
|
10 |
+
user: UserInfo | null;
|
11 |
+
accessToken: string | null;
|
12 |
+
expiresAt: number | null;
|
13 |
+
loading: boolean;
|
14 |
+
error: string | null;
|
15 |
+
}
|
16 |
+
|
17 |
+
function createAuthStore() {
|
18 |
+
const { subscribe, set, update } = writable<AuthState>({
|
19 |
+
isAuthenticated: false,
|
20 |
+
user: null,
|
21 |
+
accessToken: null,
|
22 |
+
expiresAt: null,
|
23 |
+
loading: true,
|
24 |
+
error: null,
|
25 |
+
});
|
26 |
+
|
27 |
+
const getOAuthConfig = () => {
|
28 |
+
const isProduction = window.location.hostname.includes("hf.space");
|
29 |
+
const clientId = "87f5f1d1-6e9e-4962-98f0-e5f3831ec988";
|
30 |
+
|
31 |
+
let redirectUrl: string;
|
32 |
+
if (isProduction) {
|
33 |
+
redirectUrl = `https://${window.location.hostname}/auth/callback`;
|
34 |
+
} else {
|
35 |
+
redirectUrl = `http://localhost:7860/auth/callback`;
|
36 |
+
}
|
37 |
+
|
38 |
+
return {
|
39 |
+
clientId,
|
40 |
+
redirectUrl,
|
41 |
+
scopes: "openid profile inference-api",
|
42 |
+
};
|
43 |
+
};
|
44 |
+
|
45 |
+
return {
|
46 |
+
subscribe,
|
47 |
+
|
48 |
+
async init() {
|
49 |
+
update((state) => ({ ...state, loading: true }));
|
50 |
+
|
51 |
+
try {
|
52 |
+
const oauthResult = await oauthHandleRedirectIfPresent();
|
53 |
+
|
54 |
+
if (oauthResult) {
|
55 |
+
const { accessToken, accessTokenExpiresAt, userInfo } = oauthResult;
|
56 |
+
|
57 |
+
set({
|
58 |
+
isAuthenticated: true,
|
59 |
+
user: userInfo,
|
60 |
+
accessToken,
|
61 |
+
expiresAt: accessTokenExpiresAt
|
62 |
+
? accessTokenExpiresAt.getTime()
|
63 |
+
: null,
|
64 |
+
loading: false,
|
65 |
+
error: null,
|
66 |
+
});
|
67 |
+
|
68 |
+
sessionStorage.setItem(
|
69 |
+
"hf_auth",
|
70 |
+
JSON.stringify({
|
71 |
+
accessToken,
|
72 |
+
expiresAt: accessTokenExpiresAt
|
73 |
+
? accessTokenExpiresAt.getTime()
|
74 |
+
: null,
|
75 |
+
user: userInfo,
|
76 |
+
}),
|
77 |
+
);
|
78 |
+
|
79 |
+
return true;
|
80 |
+
}
|
81 |
+
|
82 |
+
const stored = sessionStorage.getItem("hf_auth");
|
83 |
+
if (stored) {
|
84 |
+
const authData = JSON.parse(stored);
|
85 |
+
|
86 |
+
if (!authData.expiresAt || authData.expiresAt > Date.now()) {
|
87 |
+
set({
|
88 |
+
isAuthenticated: true,
|
89 |
+
user: authData.user,
|
90 |
+
accessToken: authData.accessToken,
|
91 |
+
expiresAt: authData.expiresAt,
|
92 |
+
loading: false,
|
93 |
+
error: null,
|
94 |
+
});
|
95 |
+
return true;
|
96 |
+
} else {
|
97 |
+
sessionStorage.removeItem("hf_auth");
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
set({
|
102 |
+
isAuthenticated: false,
|
103 |
+
user: null,
|
104 |
+
accessToken: null,
|
105 |
+
expiresAt: null,
|
106 |
+
loading: false,
|
107 |
+
error: null,
|
108 |
+
});
|
109 |
+
|
110 |
+
return false;
|
111 |
+
} catch (error) {
|
112 |
+
console.error("Auth initialization error:", error);
|
113 |
+
set({
|
114 |
+
isAuthenticated: false,
|
115 |
+
user: null,
|
116 |
+
accessToken: null,
|
117 |
+
expiresAt: null,
|
118 |
+
loading: false,
|
119 |
+
error:
|
120 |
+
error instanceof Error ? error.message : "Authentication failed",
|
121 |
+
});
|
122 |
+
return false;
|
123 |
+
}
|
124 |
+
},
|
125 |
+
|
126 |
+
async login() {
|
127 |
+
const config = getOAuthConfig();
|
128 |
+
|
129 |
+
try {
|
130 |
+
const url = await oauthLoginUrl({
|
131 |
+
clientId: config.clientId,
|
132 |
+
redirectUrl: config.redirectUrl,
|
133 |
+
scopes: config.scopes,
|
134 |
+
});
|
135 |
+
|
136 |
+
window.location.href = url;
|
137 |
+
} catch (error) {
|
138 |
+
console.error("Login error:", error);
|
139 |
+
update((state) => ({
|
140 |
+
...state,
|
141 |
+
error: error instanceof Error ? error.message : "Login failed",
|
142 |
+
}));
|
143 |
+
}
|
144 |
+
},
|
145 |
+
|
146 |
+
logout() {
|
147 |
+
sessionStorage.removeItem("hf_auth");
|
148 |
+
set({
|
149 |
+
isAuthenticated: false,
|
150 |
+
user: null,
|
151 |
+
accessToken: null,
|
152 |
+
expiresAt: null,
|
153 |
+
loading: false,
|
154 |
+
error: null,
|
155 |
+
});
|
156 |
+
},
|
157 |
+
|
158 |
+
getToken(): string | null {
|
159 |
+
const state = get({ subscribe });
|
160 |
+
return state.accessToken;
|
161 |
+
},
|
162 |
+
|
163 |
+
isTokenValid(): boolean {
|
164 |
+
const state = get({ subscribe });
|
165 |
+
if (!state.accessToken) return false;
|
166 |
+
if (!state.expiresAt) return true;
|
167 |
+
return state.expiresAt > Date.now();
|
168 |
+
},
|
169 |
+
};
|
170 |
+
}
|
171 |
+
|
172 |
+
export const authStore = createAuthStore();
|
src/lib/services/console-capture.ts
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { consoleStore } from "../stores/console";
|
2 |
+
|
3 |
+
type ConsoleMethod = "log" | "warn" | "error" | "info";
|
4 |
+
type ConsoleMethodFunc = (...args: unknown[]) => void;
|
5 |
+
type OriginalConsole = Record<ConsoleMethod, ConsoleMethodFunc>;
|
6 |
+
|
7 |
+
export class ConsoleCapture {
|
8 |
+
private static instance: ConsoleCapture | null = null;
|
9 |
+
private originalConsole: OriginalConsole = {} as OriginalConsole;
|
10 |
+
private isCapturing = false;
|
11 |
+
|
12 |
+
private constructor() {}
|
13 |
+
|
14 |
+
static getInstance(): ConsoleCapture {
|
15 |
+
if (!ConsoleCapture.instance) {
|
16 |
+
ConsoleCapture.instance = new ConsoleCapture();
|
17 |
+
}
|
18 |
+
return ConsoleCapture.instance;
|
19 |
+
}
|
20 |
+
|
21 |
+
setup(): void {
|
22 |
+
if (this.isCapturing) return;
|
23 |
+
|
24 |
+
this.originalConsole = {
|
25 |
+
log: console.log,
|
26 |
+
warn: console.warn,
|
27 |
+
error: console.error,
|
28 |
+
info: console.info,
|
29 |
+
};
|
30 |
+
|
31 |
+
const methods: Array<"log" | "warn" | "error" | "info"> = [
|
32 |
+
"log",
|
33 |
+
"warn",
|
34 |
+
"error",
|
35 |
+
"info",
|
36 |
+
];
|
37 |
+
|
38 |
+
methods.forEach((method) => {
|
39 |
+
console[method] = (...args: unknown[]) => {
|
40 |
+
this.originalConsole[method]?.apply(console, args);
|
41 |
+
|
42 |
+
const firstArg = args[0];
|
43 |
+
if (typeof firstArg === "string") {
|
44 |
+
if (
|
45 |
+
firstArg.includes("[vite]") ||
|
46 |
+
firstArg.includes("[VibeGame] Console forwarding") ||
|
47 |
+
firstArg.includes("[DEBUG]")
|
48 |
+
) {
|
49 |
+
return;
|
50 |
+
}
|
51 |
+
}
|
52 |
+
|
53 |
+
const message = args
|
54 |
+
.map((arg) => {
|
55 |
+
if (arg instanceof Error) {
|
56 |
+
return arg.message;
|
57 |
+
} else if (typeof arg === "object") {
|
58 |
+
try {
|
59 |
+
return JSON.stringify(arg, null, 2);
|
60 |
+
} catch {
|
61 |
+
return String(arg);
|
62 |
+
}
|
63 |
+
}
|
64 |
+
return String(arg);
|
65 |
+
})
|
66 |
+
.join(" ");
|
67 |
+
|
68 |
+
consoleStore.addMessage(method, message);
|
69 |
+
};
|
70 |
+
});
|
71 |
+
|
72 |
+
this.isCapturing = true;
|
73 |
+
}
|
74 |
+
|
75 |
+
teardown(): void {
|
76 |
+
if (!this.isCapturing) return;
|
77 |
+
|
78 |
+
if (this.originalConsole.log) {
|
79 |
+
console.log = this.originalConsole.log;
|
80 |
+
console.warn = this.originalConsole.warn;
|
81 |
+
console.error = this.originalConsole.error;
|
82 |
+
console.info = this.originalConsole.info;
|
83 |
+
}
|
84 |
+
|
85 |
+
this.originalConsole = {} as OriginalConsole;
|
86 |
+
this.isCapturing = false;
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
export const consoleCapture = ConsoleCapture.getInstance();
|
src/lib/services/context.md
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Services
|
2 |
+
|
3 |
+
Business logic layer with pure functions and singleton services
|
4 |
+
|
5 |
+
## Purpose
|
6 |
+
|
7 |
+
- Encapsulate complex business logic
|
8 |
+
- Provide clean APIs for operations
|
9 |
+
- Keep components simple and declarative
|
10 |
+
|
11 |
+
## Layout
|
12 |
+
|
13 |
+
```
|
14 |
+
services/
|
15 |
+
├── context.md # This file
|
16 |
+
├── auth.ts # Hugging Face OAuth authentication
|
17 |
+
├── game-engine.ts # Game lifecycle management
|
18 |
+
├── console-capture.ts # Console interception
|
19 |
+
└── html-parser.ts # Game HTML parsing
|
20 |
+
```
|
21 |
+
|
22 |
+
## Scope
|
23 |
+
|
24 |
+
- In-scope: Business logic, side effects, API calls, authentication
|
25 |
+
- Out-of-scope: UI rendering, state storage
|
26 |
+
|
27 |
+
## Entrypoints
|
28 |
+
|
29 |
+
- `authStore.login()` - Start OAuth flow
|
30 |
+
- `authStore.logout()` - Clear authentication
|
31 |
+
- `authStore.getToken()` - Get current token
|
32 |
+
- `authStore.isTokenValid()` - Check token validity
|
33 |
+
- `gameEngine.start()` - Start game with world content
|
34 |
+
- `gameEngine.stop()` - Clean up game instance
|
35 |
+
- `consoleCapture.setup()` - Begin console interception
|
36 |
+
- `HTMLParser.extractGameContent()` - Parse world from HTML
|
37 |
+
|
38 |
+
## Dependencies
|
39 |
+
|
40 |
+
- @huggingface/hub for OAuth
|
41 |
+
- VibeGame for game engine
|
42 |
+
- Stores for state updates
|
43 |
+
- No UI dependencies
|
src/lib/services/game-engine.ts
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as GAME from "vibegame";
|
2 |
+
import { gameStore } from "../stores/game";
|
3 |
+
import { consoleStore } from "../stores/console";
|
4 |
+
import { uiStore } from "../stores/ui";
|
5 |
+
|
6 |
+
type GameInstance = Awaited<ReturnType<typeof GAME.run>>;
|
7 |
+
|
8 |
+
export class GameEngine {
|
9 |
+
private static instance: GameEngine | null = null;
|
10 |
+
private gameInstance: GameInstance | null = null;
|
11 |
+
|
12 |
+
private constructor() {}
|
13 |
+
|
14 |
+
static getInstance(): GameEngine {
|
15 |
+
if (!GameEngine.instance) {
|
16 |
+
GameEngine.instance = new GameEngine();
|
17 |
+
}
|
18 |
+
return GameEngine.instance;
|
19 |
+
}
|
20 |
+
|
21 |
+
async start(worldContent: string): Promise<void> {
|
22 |
+
gameStore.setStarting(true);
|
23 |
+
consoleStore.addMessage("info", "🎮 Starting game...");
|
24 |
+
|
25 |
+
this.stop();
|
26 |
+
uiStore.setError(null);
|
27 |
+
|
28 |
+
try {
|
29 |
+
const container = document.getElementById("world-container");
|
30 |
+
if (!container) {
|
31 |
+
throw new Error("World container not found");
|
32 |
+
}
|
33 |
+
|
34 |
+
container.innerHTML = worldContent;
|
35 |
+
|
36 |
+
this.gameInstance = await GAME.run();
|
37 |
+
gameStore.setInstance(this.gameInstance);
|
38 |
+
consoleStore.addMessage("info", "✅ Game started!");
|
39 |
+
} catch (error: unknown) {
|
40 |
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
41 |
+
uiStore.setError(errorMsg);
|
42 |
+
consoleStore.addMessage("error", `❌ Error: ${errorMsg}`);
|
43 |
+
gameStore.setInstance(null);
|
44 |
+
} finally {
|
45 |
+
gameStore.setStarting(false);
|
46 |
+
}
|
47 |
+
}
|
48 |
+
|
49 |
+
stop(): void {
|
50 |
+
if (this.gameInstance) {
|
51 |
+
try {
|
52 |
+
if (typeof this.gameInstance.destroy === "function") {
|
53 |
+
this.gameInstance.destroy();
|
54 |
+
} else if (typeof this.gameInstance.stop === "function") {
|
55 |
+
this.gameInstance.stop();
|
56 |
+
}
|
57 |
+
} catch (error) {
|
58 |
+
console.error("Error stopping game:", error);
|
59 |
+
}
|
60 |
+
this.gameInstance = null;
|
61 |
+
gameStore.setInstance(null);
|
62 |
+
}
|
63 |
+
|
64 |
+
const container = document.getElementById("world-container");
|
65 |
+
if (container) {
|
66 |
+
while (container.firstChild) {
|
67 |
+
container.removeChild(container.firstChild);
|
68 |
+
}
|
69 |
+
}
|
70 |
+
}
|
71 |
+
|
72 |
+
isRunning(): boolean {
|
73 |
+
return this.gameInstance !== null;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
export const gameEngine = GameEngine.getInstance();
|
src/lib/services/html-parser.ts
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export class HTMLParser {
|
2 |
+
static extractGameContent(html: string): string {
|
3 |
+
const worldMatch = html.match(/<world[^>]*>[\s\S]*?<\/world>/);
|
4 |
+
return worldMatch ? worldMatch[0] : '<world canvas="#game-canvas"></world>';
|
5 |
+
}
|
6 |
+
|
7 |
+
static validateGameHTML(html: string): { valid: boolean; error?: string } {
|
8 |
+
if (!html.includes("<world")) {
|
9 |
+
return { valid: false, error: "Missing <world> tag" };
|
10 |
+
}
|
11 |
+
|
12 |
+
if (!html.includes("canvas=")) {
|
13 |
+
return { valid: false, error: "Missing canvas attribute in <world> tag" };
|
14 |
+
}
|
15 |
+
|
16 |
+
return { valid: true };
|
17 |
+
}
|
18 |
+
}
|
src/lib/stores/agent.ts
ADDED
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { writable, derived } from "svelte/store";
|
2 |
+
import { authStore } from "../services/auth";
|
3 |
+
|
4 |
+
export interface ChatMessage {
|
5 |
+
id: string;
|
6 |
+
role: "user" | "assistant" | "system";
|
7 |
+
content: string;
|
8 |
+
timestamp: number;
|
9 |
+
streaming?: boolean;
|
10 |
+
}
|
11 |
+
|
12 |
+
export interface AgentState {
|
13 |
+
connected: boolean;
|
14 |
+
processing: boolean;
|
15 |
+
messages: ChatMessage[];
|
16 |
+
error: string | null;
|
17 |
+
streamingContent: string;
|
18 |
+
}
|
19 |
+
|
20 |
+
function createAgentStore() {
|
21 |
+
const { subscribe, update } = writable<AgentState>({
|
22 |
+
connected: false,
|
23 |
+
processing: false,
|
24 |
+
messages: [],
|
25 |
+
error: null,
|
26 |
+
streamingContent: "",
|
27 |
+
});
|
28 |
+
|
29 |
+
let ws: WebSocket | null = null;
|
30 |
+
let currentStreamId: string | null = null;
|
31 |
+
|
32 |
+
function connect() {
|
33 |
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
34 |
+
return;
|
35 |
+
}
|
36 |
+
|
37 |
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
38 |
+
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
39 |
+
|
40 |
+
ws = new WebSocket(wsUrl);
|
41 |
+
|
42 |
+
ws.onopen = () => {
|
43 |
+
update((state) => ({ ...state, connected: true, error: null }));
|
44 |
+
|
45 |
+
const token = authStore.getToken();
|
46 |
+
if (token && ws) {
|
47 |
+
ws.send(
|
48 |
+
JSON.stringify({
|
49 |
+
type: "auth",
|
50 |
+
payload: { token },
|
51 |
+
timestamp: Date.now(),
|
52 |
+
}),
|
53 |
+
);
|
54 |
+
} else {
|
55 |
+
update((state) => ({
|
56 |
+
...state,
|
57 |
+
error: "Authentication required. Please sign in with Hugging Face.",
|
58 |
+
connected: false,
|
59 |
+
}));
|
60 |
+
if (ws) {
|
61 |
+
ws.close();
|
62 |
+
}
|
63 |
+
}
|
64 |
+
};
|
65 |
+
|
66 |
+
ws.onmessage = (event) => {
|
67 |
+
try {
|
68 |
+
const message = JSON.parse(event.data);
|
69 |
+
handleWebSocketMessage(message);
|
70 |
+
} catch (error) {
|
71 |
+
console.error("Error parsing WebSocket message:", error);
|
72 |
+
}
|
73 |
+
};
|
74 |
+
|
75 |
+
ws.onerror = (error) => {
|
76 |
+
console.error("WebSocket error:", error);
|
77 |
+
update((state) => ({ ...state, error: "Connection error" }));
|
78 |
+
};
|
79 |
+
|
80 |
+
ws.onclose = () => {
|
81 |
+
update((state) => ({ ...state, connected: false }));
|
82 |
+
|
83 |
+
const token = authStore.getToken();
|
84 |
+
if (token) {
|
85 |
+
setTimeout(() => {
|
86 |
+
connect();
|
87 |
+
}, 3000);
|
88 |
+
}
|
89 |
+
};
|
90 |
+
}
|
91 |
+
|
92 |
+
function handleWebSocketMessage(message: {
|
93 |
+
type: string;
|
94 |
+
payload: {
|
95 |
+
processing?: boolean;
|
96 |
+
connected?: boolean;
|
97 |
+
chunk?: string;
|
98 |
+
role?: string;
|
99 |
+
content?: string;
|
100 |
+
error?: string;
|
101 |
+
};
|
102 |
+
}) {
|
103 |
+
switch (message.type) {
|
104 |
+
case "status":
|
105 |
+
if (message.payload.processing !== undefined) {
|
106 |
+
update((state) => ({
|
107 |
+
...state,
|
108 |
+
processing: message.payload.processing as boolean,
|
109 |
+
}));
|
110 |
+
}
|
111 |
+
if (message.payload.connected !== undefined) {
|
112 |
+
update((state) => ({
|
113 |
+
...state,
|
114 |
+
connected: message.payload.connected as boolean,
|
115 |
+
}));
|
116 |
+
}
|
117 |
+
break;
|
118 |
+
|
119 |
+
case "stream":
|
120 |
+
update((state) => {
|
121 |
+
const newContent =
|
122 |
+
state.streamingContent + (message.payload.chunk || "");
|
123 |
+
|
124 |
+
if (!currentStreamId) {
|
125 |
+
currentStreamId = `msg_${Date.now()}`;
|
126 |
+
const streamMessage: ChatMessage = {
|
127 |
+
id: currentStreamId,
|
128 |
+
role: "assistant",
|
129 |
+
content: newContent,
|
130 |
+
timestamp: Date.now(),
|
131 |
+
streaming: true,
|
132 |
+
};
|
133 |
+
return {
|
134 |
+
...state,
|
135 |
+
streamingContent: newContent,
|
136 |
+
messages: [...state.messages, streamMessage],
|
137 |
+
};
|
138 |
+
} else {
|
139 |
+
const messages = state.messages.map((msg) => {
|
140 |
+
if (msg.id === currentStreamId) {
|
141 |
+
return { ...msg, content: newContent };
|
142 |
+
}
|
143 |
+
return msg;
|
144 |
+
});
|
145 |
+
return {
|
146 |
+
...state,
|
147 |
+
streamingContent: newContent,
|
148 |
+
messages,
|
149 |
+
};
|
150 |
+
}
|
151 |
+
});
|
152 |
+
break;
|
153 |
+
|
154 |
+
case "chat":
|
155 |
+
update((state) => {
|
156 |
+
if (currentStreamId) {
|
157 |
+
const messages = state.messages.map((msg) => {
|
158 |
+
if (msg.id === currentStreamId) {
|
159 |
+
return { ...msg, streaming: false };
|
160 |
+
}
|
161 |
+
return msg;
|
162 |
+
});
|
163 |
+
currentStreamId = null;
|
164 |
+
return {
|
165 |
+
...state,
|
166 |
+
streamingContent: "",
|
167 |
+
messages,
|
168 |
+
};
|
169 |
+
} else {
|
170 |
+
if (message.payload.role && message.payload.content) {
|
171 |
+
const newMessage: ChatMessage = {
|
172 |
+
id: `msg_${Date.now()}`,
|
173 |
+
role: message.payload.role as "user" | "assistant" | "system",
|
174 |
+
content: message.payload.content,
|
175 |
+
timestamp: Date.now(),
|
176 |
+
};
|
177 |
+
return {
|
178 |
+
...state,
|
179 |
+
messages: [...state.messages, newMessage],
|
180 |
+
};
|
181 |
+
}
|
182 |
+
return state;
|
183 |
+
}
|
184 |
+
});
|
185 |
+
break;
|
186 |
+
|
187 |
+
case "error":
|
188 |
+
update((state) => ({
|
189 |
+
...state,
|
190 |
+
error: message.payload.error || null,
|
191 |
+
processing: false,
|
192 |
+
}));
|
193 |
+
break;
|
194 |
+
}
|
195 |
+
}
|
196 |
+
|
197 |
+
function sendMessage(content: string) {
|
198 |
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
199 |
+
update((state) => ({ ...state, error: "Not connected to server" }));
|
200 |
+
return;
|
201 |
+
}
|
202 |
+
|
203 |
+
const userMessage: ChatMessage = {
|
204 |
+
id: `msg_${Date.now()}`,
|
205 |
+
role: "user",
|
206 |
+
content,
|
207 |
+
timestamp: Date.now(),
|
208 |
+
};
|
209 |
+
|
210 |
+
update((state) => ({
|
211 |
+
...state,
|
212 |
+
messages: [...state.messages, userMessage],
|
213 |
+
error: null,
|
214 |
+
}));
|
215 |
+
|
216 |
+
ws.send(
|
217 |
+
JSON.stringify({
|
218 |
+
type: "chat",
|
219 |
+
payload: { content },
|
220 |
+
timestamp: Date.now(),
|
221 |
+
}),
|
222 |
+
);
|
223 |
+
}
|
224 |
+
|
225 |
+
function clearMessages() {
|
226 |
+
update((state) => ({
|
227 |
+
...state,
|
228 |
+
messages: [],
|
229 |
+
streamingContent: "",
|
230 |
+
error: null,
|
231 |
+
}));
|
232 |
+
currentStreamId = null;
|
233 |
+
}
|
234 |
+
|
235 |
+
function disconnect() {
|
236 |
+
if (ws) {
|
237 |
+
ws.close();
|
238 |
+
ws = null;
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
function reauthenticate() {
|
243 |
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
244 |
+
const token = authStore.getToken();
|
245 |
+
if (token) {
|
246 |
+
ws.send(
|
247 |
+
JSON.stringify({
|
248 |
+
type: "auth",
|
249 |
+
payload: { token },
|
250 |
+
timestamp: Date.now(),
|
251 |
+
}),
|
252 |
+
);
|
253 |
+
}
|
254 |
+
}
|
255 |
+
}
|
256 |
+
|
257 |
+
return {
|
258 |
+
subscribe,
|
259 |
+
connect,
|
260 |
+
disconnect,
|
261 |
+
sendMessage,
|
262 |
+
clearMessages,
|
263 |
+
reauthenticate,
|
264 |
+
};
|
265 |
+
}
|
266 |
+
|
267 |
+
export const agentStore = createAgentStore();
|
268 |
+
|
269 |
+
export const isProcessing = derived(agentStore, ($agent) => $agent.processing);
|
270 |
+
|
271 |
+
export const isConnected = derived(agentStore, ($agent) => $agent.connected);
|