f39f1d818b
Replace hand-rolled LSP client (lspClient.ts, useLspClient.ts) with monaco-languageclient v10 extended mode using @typefox/monaco-editor-react. NWScript TextMate grammar from the LSP submodule provides syntax highlighting. Full LSP features: completion, hover, diagnostics, go-to-definition, signature help — all wired through WebSocket to the nwscript-language-server. LSP server patches: fix workspaceFolders null assertion crash, handle missing workspace/configuration gracefully, derive rootPath from rootUri when null, guard tokenizer getRawTokenContent against undefined tokens. Backend fixes: WebSocket routing changed to noServer mode so /ws, /ws/lsp, and /ws/terminal/* don't conflict. TLK index loaded at startup (41,927 entries from nwn-haks/layonara.tlk.json). Workspace routes get proper try/catch. writeConfig creates parent directories. setupClone ensures workspace structure. Frontend: GffEditor and AreaEditor rewritten with inline styles and TLK resolution for CExoLocString fields. EditorTabs rewritten with lucide icons. Tab content hydrates from API on refresh. Setup wizard gets friendly error messages. SimpleEditor/SimpleDiffEditor for non-LSP editor uses. Vite config updated for monaco-vscode-api compatibility.
485 lines
26 KiB
Markdown
485 lines
26 KiB
Markdown
# Layonara Forge — Agent Handoff Document
|
|
|
|
## What Is This
|
|
|
|
Layonara Forge is a purpose-built NWN (Neverwinter Nights) Development IDE that runs as a local web application in Docker. It lets contributors fork, clone, edit, build, and run a complete Layonara NWNX NWN server with zero native tooling — only Docker required. The project name was chosen during brainstorming: "Forge" evokes crafting/building in a fantasy context.
|
|
|
|
## Project Location
|
|
|
|
- **Forge codebase**: `/home/jmg/dev/layonara/layonara-forge/`
|
|
- **Design spec**: `/home/jmg/dev/docs/superpowers/specs/2026-04-20-layonara-forge-design.md`
|
|
- **Implementation plans**: `/home/jmg/dev/docs/superpowers/plans/2026-04-20-forge-plan-{1..6}-*.md`
|
|
- **Gitea integration plan**: See Cursor plan file `gitea_+_forge_integration_8e0df077.plan.md`
|
|
- **Design context**: `.impeccable.md` at project root (personality: arcane, precise, deep)
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Host Machine
|
|
├── Browser → localhost:3000 (Forge UI)
|
|
├── NWN Toolset → writes GFFs to modules/temp0/
|
|
├── ~/layonara-workspace/ (repos, server data, config)
|
|
└── Docker Socket
|
|
|
|
Docker Network
|
|
├── layonara-forge (Node.js + React, serves UI, manages everything via Docker socket)
|
|
├── layonara-builder (ephemeral: nasher, nwn_script_comp, layonara_nwn, cmake, gcc)
|
|
├── layonara-nwserver (ghcr.io/plenarius/unified — NWN:EE + NWNX)
|
|
└── layonara-mariadb (mariadb:10.11 — game database)
|
|
```
|
|
|
|
The Forge container manages sibling containers via the Docker socket (Portainer pattern). Contributors clone one repo, set two paths in `.env`, run `docker compose up`, and open a browser.
|
|
|
|
## Tech Stack
|
|
|
|
- **Backend**: Node.js 20 + Express 5 + TypeScript (ES modules)
|
|
- **Frontend**: React 19 + Vite 6 + Tailwind CSS 4 (utility classes unreliable — inline styles used for layout)
|
|
- **Icons**: lucide-react (SVG icons throughout)
|
|
- **Fonts**: Self-hosted variable fonts via @fontsource-variable (Manrope, Alegreya, JetBrains Mono)
|
|
- **Code editor**: Monaco Editor with NWScript Monarch tokenizer
|
|
- **NWScript LSP**: Forked `layonara/nwscript-ee-language-server` connected via `monaco-languageclient` WebSocket. Server patched for Forge compatibility (see LSP server patches below).
|
|
- **Terminal**: xterm.js with child_process.spawn shell sessions
|
|
- **Docker API**: dockerode
|
|
- **Git**: simple-git (named import: `import { simpleGit } from "simple-git"`)
|
|
- **Git provider**: Gitea at `https://gitea.layonara.com` (NOT GitHub — see Gitea section)
|
|
- **Real-time**: WebSocket via ws library
|
|
|
|
## What's Built (55 commits + UI overhaul session)
|
|
|
|
All 6 implementation plans are complete. The codebase compiles and both Docker images build successfully. A complete UI/UX overhaul was performed (see "UI/UX Overhaul" section below).
|
|
|
|
### Backend Services (`packages/backend/src/services/`)
|
|
|
|
|
|
| Service | File | Purpose |
|
|
| ------------ | ------------------------- | ------------------------------------------------------------------ |
|
|
| WebSocket | `ws.service.ts` | Event broadcasting to all connected clients |
|
|
| Workspace | `workspace.service.ts` | Directory structure management, forge.json config |
|
|
| Docker | `docker.service.ts` | Container CRUD, image pulls, ephemeral container runs |
|
|
| Build | `build.service.ts` | Module compile/pack, hot-reload, hak builds, NWNX builds |
|
|
| Server | `server.service.ts` | NWN server stack lifecycle (start/stop/restart MariaDB + nwserver) |
|
|
| Toolset | `toolset.service.ts` | temp0/ file watcher, GFF→JSON conversion, change management |
|
|
| Editor | `editor.service.ts` | File CRUD, directory trees, workspace search |
|
|
| Git | `git.service.ts` | Clone, pull, commit, push, diff, upstream polling |
|
|
| Git Provider | `git-provider.service.ts` | Gitea API (fork, PR, token validation) |
|
|
| Terminal | `terminal.service.ts` | Shell session management via child_process |
|
|
| LSP | `lsp.service.ts` | NWScript language server process management |
|
|
|
|
|
|
### Backend Routes (`packages/backend/src/routes/`)
|
|
|
|
|
|
| Route | Prefix | Endpoints |
|
|
| --------- | ---------------- | ----------------------------------------------------------------- |
|
|
| workspace | `/api/workspace` | GET /config, PUT /config, POST /init |
|
|
| docker | `/api/docker` | containers, pull, start/stop/restart, logs |
|
|
| build | `/api/build` | module/compile, module/pack, deploy, compile-single, haks, nwnx |
|
|
| server | `/api/server` | status, start, stop, restart, generate-config, seed-db, sql |
|
|
| toolset | `/api/toolset` | status, start, stop, changes, apply, apply-all, discard |
|
|
| editor | `/api/editor` | tree, file CRUD, search, resref, tlk, 2da, gff-schema |
|
|
| github | `/api/github` | validate-pat (actually Gitea token), fork, forks, pr, prs |
|
|
| repos | `/api/repos` | clone, list (/), status (/:repo/status), pull, commit, push, diff |
|
|
| terminal | `/api/terminal` | sessions CRUD |
|
|
|
|
|
|
### Frontend Pages (`packages/frontend/src/pages/`)
|
|
|
|
|
|
| Page | Route | Purpose |
|
|
| --------- | ----------- | ----------------------------------------------------------- |
|
|
| Dashboard | `/` | Server status, repo summary, quick actions (3-column cards) |
|
|
| Editor | `/editor` | Monaco editor with file explorer, tabs, GFF visual editors |
|
|
| Build | `/build` | Module/hak/NWNX build sections with streaming output |
|
|
| Server | `/server` | Controls, log viewer with filter, SQL console |
|
|
| Toolset | `/toolset` | temp0/ watcher status, change table, diff viewer |
|
|
| Repos | `/repos` | Git status cards, commit dialog, PR creation |
|
|
| Settings | `/settings` | PAT, theme, editable paths, Docker images, shortcuts, reset |
|
|
| Setup | `/setup` | 4-phase onboarding wizard with 10 steps |
|
|
|
|
|
|
### Special Features
|
|
|
|
- **NWScript syntax highlighting**: Monarch tokenizer with keyword/type/comment/string/preprocessor rules
|
|
- **SQL highlighting in NWScript strings**: Detects `NWNX_SQL_PrepareQuery()` calls, highlights SQL keywords in teal
|
|
- **Resref auto-lookup**: Backend indexes all GFF JSON files, hover on resref strings shows the item/creature/area
|
|
- **TLK preview**: Hover on integer literals shows the TLK string (handles 16777216 custom offset)
|
|
- **2DA intellisense**: Parses 2da files, provides completion for `Get2DAString` calls
|
|
- **Visual GFF editors**: Form-based editors for .uti, .utc, .are, .dlg, .utp, .utm JSON files
|
|
- **Conventional commit enforcement**: Type dropdown (feat/fix/refactor/etc), rejects malformed messages
|
|
- **Dark/light theme**: OKLCH CSS custom properties toggled via `light` class on root element
|
|
|
|
## UI/UX Overhaul (April 21, 2026 session)
|
|
|
|
A complete design overhaul was performed using the [Impeccable](https://impeccable.style/) design skill system. The design context is documented in `.impeccable.md`.
|
|
|
|
### Design System
|
|
|
|
**Personality**: Arcane, precise, deep — a craftsman's workbench.
|
|
|
|
**Fonts** (all self-hosted via `@fontsource-variable`, no Google Fonts):
|
|
|
|
- **Body/UI**: Manrope Variable — warm geometric sans
|
|
- **Headings**: Alegreya Variable — calligraphic serif with manuscript roots
|
|
- **Code/mono**: JetBrains Mono Variable
|
|
|
|
**Color palette** (full OKLCH, 30+ tokens in `globals.css`):
|
|
|
|
- Surfaces tinted toward amber (hue 65) — "warm darks, not cold ones"
|
|
- 3-level depth: `--forge-bg` → `--forge-surface` → `--forge-surface-raised`
|
|
- Accent: evolved gold `oklch(58% 0.155 65)` with hover and subtle variants
|
|
- Semantic colors: success (forest green, hue 150), danger (brick red, hue 25), warning (golden, hue 80), info (steel blue, hue 230)
|
|
- Each semantic color has base, bg, and border variants for both dark and light modes
|
|
- Log panels: dedicated `--forge-log-bg` / `--forge-log-text` tokens
|
|
|
|
**Icons**: lucide-react SVG icons throughout (Code2, Wrench, Hammer, Play, GitBranch, Settings, Sun/Moon, Terminal, etc.)
|
|
|
|
**Type scale** (fixed rem for IDE density):
|
|
|
|
- `--text-xs` (11px) through `--text-2xl` (28px), ~1.25 ratio
|
|
|
|
### What Changed
|
|
|
|
**Foundation**:
|
|
|
|
- Replaced Inter font with Manrope Variable, Baskerville with Alegreya Variable
|
|
- Removed Google Fonts `<link>` — all fonts bundled as npm deps
|
|
- Full OKLCH palette replacing all hex values (~60 hard-coded colors replaced)
|
|
- All Tailwind semantic color classes (`green-400`, `red-500/20`, etc.) replaced with forge tokens
|
|
- Global CSS: scrollbar theming, selection color, input/button base styles, `:focus-visible` ring, `prefers-reduced-motion`
|
|
|
|
**IDE Shell** (`IDELayout.tsx`):
|
|
|
|
- Lucide SVG icons replacing Unicode emoji in nav rail
|
|
- Removed 3px left border stripe (impeccable anti-pattern ban)
|
|
- Sidebar only shows on `/editor` route (was showing on all pages)
|
|
- All layout uses inline styles (Tailwind flex classes were not reliably applying)
|
|
- Terminal toggle bar with Terminal/Chevron icons
|
|
|
|
**All 8 pages rewritten** with consistent patterns:
|
|
|
|
- Card containers: `--forge-surface` bg, `--forge-border`, `0.75rem` radius
|
|
- Section headers: uppercase, `--text-xs`, icon + label
|
|
- Buttons: accent primary, outline secondary, danger for destructive
|
|
- Status badges: semantic colors with dots
|
|
- All inline styles (Tailwind utility classes unreliable for layout in this project)
|
|
|
|
**Setup wizard**:
|
|
|
|
- 4-phase indicator (Environment → Authentication → Repositories → Finalize) with numbered circles + connecting lines, matching James's work app wizard pattern
|
|
- Steps reordered: Workspace + NWN Home before Gitea Token
|
|
- PathInput component with folder icon for path fields
|
|
- StatusDot component replacing emoji (✅❌⏳) with styled HTML elements
|
|
- Navigation: ghost "← Back" left, accent "Next →" right, border-top separator
|
|
|
|
**Performance**:
|
|
|
|
- Routes code-split via `React.lazy()` — 10 chunks instead of 1 (760KB → initial 15KB app shell)
|
|
- Page chunks: Editor 98KB, Setup 16KB, Repos 13KB, others 5-8KB each
|
|
|
|
**Accessibility**:
|
|
|
|
- `:focus-visible` outline on all interactive elements
|
|
- `aria-label="Main navigation"` on nav
|
|
- Tab close button changed from `<span>` to `<button aria-label="Close tab">`
|
|
- Toast container has `aria-live="polite"` + `role="status"`
|
|
- `window.confirm()` guards on destructive actions (Discard All, Reset Setup)
|
|
- SetupGuard shows "Loading Forge…" instead of blank screen
|
|
|
|
### Important: Tailwind CSS 4 Quirk
|
|
|
|
Tailwind CSS 4 utility classes for layout (`flex`, `flex-1`, `items-center`, etc.) do NOT reliably apply in this project. All critical layout uses **inline styles** instead. This is a conscious decision, not laziness. The Tailwind `@import "tailwindcss"` is still loaded and works for some utilities (`rounded`, `overflow-hidden`, etc.) but **do not rely on Tailwind classes for flex/grid layout**. Use inline `style={{}}` props.
|
|
|
|
## Docker Images
|
|
|
|
### layonara-forge (563MB)
|
|
|
|
- Base: `node:20-slim`
|
|
- Multi-stage build: builder stage compiles TS + Vite, production stage has only runtime deps
|
|
- Serves React frontend as static files from Express
|
|
- The Dockerfile is at repo root: `Dockerfile`
|
|
|
|
### layonara-builder (577MB)
|
|
|
|
- Base: `ubuntu:24.04`
|
|
- All tools installed from **pre-built GitHub Release binaries** (no Nim compilation)
|
|
- The Dockerfile is at `builder/Dockerfile`
|
|
- Tools: nwn_gff, nwn_script_comp, nasher, layonara_nwn, cmake, gcc, git
|
|
|
|
**Critical**: The builder Dockerfile downloads pre-built binaries from:
|
|
|
|
- `layonara/neverwinter.nim` releases (nwn_gff, nwn_script_comp, etc.)
|
|
- `squattingmonk/nasher.nim` releases (nasher)
|
|
- `plenarius/layonara_nwn` releases (layonara_nwn)
|
|
|
|
If any of these release URLs break, the builder image won't build.
|
|
|
|
## Gitea Infrastructure
|
|
|
|
GitHub is no longer the primary git provider for contributors. Gitea is self-hosted on xandrial.
|
|
|
|
### Setup
|
|
|
|
- **Gitea URL**: `https://gitea.layonara.com`
|
|
- **Host**: xandrial (159.69.30.129, Hetzner CPX41)
|
|
- **Managed by**: Coolify on leanthar, service UUID `xo2yy8rml79lkzmf92cgeory`
|
|
- **Database**: PostgreSQL 16 sidecar in same Coolify service
|
|
- **Auth**: Authentik OIDC SSO (same login as Nextcloud and email)
|
|
- **SSH**: Port 2222 for git-over-SSH
|
|
- **Admin account**: `orth`
|
|
|
|
### Repos on Gitea
|
|
|
|
|
|
| Repo | Branch | Push Mirror → GitHub |
|
|
| --------------------- | ------- | -------------------- |
|
|
| `layonara/nwn-module` | `ee` | Yes, sync on commit |
|
|
| `layonara/nwn-haks` | `64bit` | Yes, sync on commit |
|
|
|
|
|
|
### NOT on Gitea
|
|
|
|
- `plenarius/unified` (NWNX) stays on GitHub — read-only, no contributions through Forge
|
|
|
|
### Branch Protection
|
|
|
|
- `ee` on nwn-module: only `orth` can push directly
|
|
- `64bit` on nwn-haks: only `orth` can push directly
|
|
- Contributors must fork within the layonara org and PR
|
|
|
|
### Push Mirrors
|
|
|
|
Each Gitea repo has a push mirror configured to sync to the corresponding GitHub repo. This triggers existing GitHub CI/CD and Discord bot webhooks. Commit attribution is preserved (it's in the git objects).
|
|
|
|
### How the Forge Connects
|
|
|
|
- `GIT_PROVIDER_URL` env var (default: `https://gitea.layonara.com`)
|
|
- Backend uses `git-provider.service.ts` which calls Gitea API at `$GIT_PROVIDER_URL/api/v1`
|
|
- Clone URLs: `https://<token>@gitea.layonara.com/<user>/<repo>.git`
|
|
- The `unified` repo clones from GitHub directly (no token needed, public repo)
|
|
|
|
## Forked Dependencies
|
|
|
|
### layonara/neverwinter.nim (fork of niv/neverwinter.nim)
|
|
|
|
- **Purpose**: Adds `-n` (no entry point) and `-E` (all errors) flags to `nwn_script_comp`
|
|
- **Cherry-picked**: PRs #152 and #153 from cgtudor's branches
|
|
- **Release workflow**: `.github/workflows/release.yml` — builds on tag push, creates GitHub Release with pre-built linux tarball
|
|
- **Current release**: `v2.1.2-layonara`
|
|
- **Upstream status**: Waiting for niv to merge the PRs. When merged, this fork can be retired.
|
|
- **Remote setup**: `origin` = layonara fork, `upstream` = niv/neverwinter.nim (push disabled: `no_push`)
|
|
|
|
### layonara/nwscript-ee-language-server (fork of PhilippeChab/nwscript-ee-language-server)
|
|
|
|
- **Purpose**: Migrates diagnostics from `nwnsc` to `nwn_script_comp`
|
|
- **Merged**: PR #77 (cgtudor's tdn-hack branch) with conflict resolution
|
|
- **Status**: Waiting for upstream compiler PRs to merge, then PhilippeChab can merge PR #77, then this fork can be retired.
|
|
- **Included in Forge**: As a git submodule at `lsp/nwscript-language-server/`
|
|
- **Remote setup**: `origin` = layonara fork, `upstream` = PhilippeChab (push disabled: `no_push`)
|
|
|
|
### plenarius/layonara_nwn
|
|
|
|
- **Not a fork**: James's own repo
|
|
- **Release workflow added**: `.github/workflows/release.yml` — builds on tag push
|
|
- **Current release**: `v0.1.1`
|
|
|
|
## Known Issues & Incomplete Work
|
|
|
|
### TypeScript
|
|
|
|
- All backend TS errors are resolved (Express 5 `*path` returns `string[]`, simple-git uses named import)
|
|
- Frontend uses `"moduleResolution": "bundler"` and `"noImplicitAny": false` in tsconfig to avoid strict-mode issues from subagent-generated code
|
|
- Frontend build script is `"build": "vite build"` (skips `tsc -b` which fights with bundler resolution)
|
|
|
|
### Tested End-to-End
|
|
|
|
- Setup wizard flow against live Gitea — fork detection, token validation, clone all 3 repos (including 5.5GB nwn-haks), workspace initialization
|
|
- NWScript LSP with real `.nss` files — completion, hover, syntax highlighting all working via `monaco-languageclient`
|
|
- TLK resolution in GFF visual editors — 41,927 entries from `layonara.tlk.json`
|
|
|
|
### Not Yet Tested End-to-End
|
|
|
|
- Module build → server start → connect with NWN client
|
|
- Toolset temp0/ watcher with real GFF files
|
|
- Hot-reload pipeline with a real nasher build
|
|
- Push mirrors actually triggering GitHub CI
|
|
|
|
### Remaining UI Work
|
|
|
|
- **GFF visual editors** (ItemEditor, CreatureEditor, AreaEditor, DialogEditor) still use Tailwind classes — may need inline style conversion
|
|
- **CommitDialog** component styling could be improved
|
|
- **FileExplorer** sidebar styling uses older patterns (Tailwind classes for layout)
|
|
- **EditorTabs** uses older patterns
|
|
- **SearchPanel** uses older patterns
|
|
- **ErrorBoundary** component not styled
|
|
- **Light mode** needs visual verification — all tokens have light variants but the overall look hasn't been tested
|
|
- The vendor bundle is still 598KB (Monaco dominates) — could be split further with `manualChunks`
|
|
|
|
### Database
|
|
|
|
- `db/schema.sql` contains the full schema from James's local dev DB (`nwn_dev`) + seed data for cnr_misc and pwdata
|
|
- DM row insertion happens during setup wizard (contributor provides CD key)
|
|
- Architecture is ready for richer seed data (production dump) but not implemented yet
|
|
|
|
### Infrastructure
|
|
|
|
- Gitea on xandrial needs monitoring/backup strategy (no backup service configured yet)
|
|
- The Gitea PostgreSQL database should be backed up regularly
|
|
|
|
## Key Conventions
|
|
|
|
- **NEVER close GitHub issues** without explicit permission — state changes fire Discord webhooks to the player community
|
|
- **NEVER push to `nwnxee/unified`** — James's fork is `plenarius/unified`
|
|
- **No pushing layonara-forge to any remote** until James says so — everything is local
|
|
- **Conventional commits**: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`
|
|
- **NWN resref limit**: 16 characters max for all filenames
|
|
- **Express 5**: Uses `*path` for catch-all/wildcard routes, NOT bare `*`
|
|
- **simple-git**: Use named import `import { simpleGit } from "simple-git"`, NOT default import
|
|
- **Inline styles for layout**: Do NOT use Tailwind classes for flex/grid layout — they don't reliably apply. Use inline `style={{}}` props.
|
|
- **CSS variables for all colors**: Never use hex values. Use `var(--forge-*)` tokens from `globals.css`.
|
|
- **lucide-react for icons**: Never use Unicode emoji for UI icons. Import from `lucide-react`.
|
|
|
|
## Environment Variables
|
|
|
|
### Forge Container
|
|
|
|
|
|
| Var | Default | Purpose |
|
|
| ------------------ | ---------------------------- | -------------------------------------------- |
|
|
| `WORKSPACE_PATH` | `/workspace` | Where repos, server data, config live |
|
|
| `NWN_HOME_PATH` | `/nwn-home` | NWN documents directory (for Toolset temp0/) |
|
|
| `GIT_PROVIDER_URL` | `https://gitea.layonara.com` | Gitea instance URL |
|
|
| `PORT` | `3000` | HTTP server port |
|
|
|
|
|
|
### Coolify
|
|
|
|
|
|
| Var | Location | Purpose |
|
|
| ------------------- | ---------------- | --------------------------------- |
|
|
| `COOLIFY_API_TOKEN` | `~/.env.coolify` | API access to Coolify on leanthar |
|
|
| `COOLIFY_URL` | `~/.env.coolify` | `https://leanthar.layonara.com` |
|
|
|
|
|
|
### Gitea
|
|
|
|
|
|
| Item | Value |
|
|
| ----------------------------- | ---------------------------------------------------------------------------- |
|
|
| API token | `eb79a92cea7dad657a0c81ddd2290a1be95057e2` (orth's token, name: forge-setup) |
|
|
| Gitea service UUID in Coolify | `xo2yy8rml79lkzmf92cgeory` |
|
|
|
|
|
|
## File Structure
|
|
|
|
```
|
|
layonara-forge/
|
|
├── Dockerfile # Forge image (multi-stage Node.js build)
|
|
├── docker-compose.yml # Forge container with socket + workspace mounts
|
|
├── .env.example # WORKSPACE_PATH, NWN_HOME_PATH, GIT_PROVIDER_URL
|
|
├── .impeccable.md # Design context (personality, palette, principles)
|
|
├── .agents/skills/ # Impeccable design skills (18 commands)
|
|
├── builder/
|
|
│ └── Dockerfile # Builder image (pre-built binaries, no Nim)
|
|
├── db/
|
|
│ └── schema.sql # MariaDB schema + seed data
|
|
├── lsp/
|
|
│ └── nwscript-language-server/ # Git submodule (forked LSP)
|
|
├── packages/
|
|
│ ├── backend/
|
|
│ │ └── src/
|
|
│ │ ├── index.ts # Express + WS + upgrade handlers
|
|
│ │ ├── config/ # repos.ts, env-template.ts
|
|
│ │ ├── gff/ # GFF schema definitions (6 types)
|
|
│ │ ├── nwscript/ # resref-index, tlk-index, twoda-index
|
|
│ │ ├── routes/ # All API routes
|
|
│ │ └── services/ # All backend services
|
|
│ └── frontend/
|
|
│ └── src/
|
|
│ ├── App.tsx # Router with SetupGuard + React.lazy routes
|
|
│ ├── components/ # editor/, gff/, terminal/, Toast, etc.
|
|
│ ├── hooks/ # useWebSocket, useEditorState, useTheme, useLspClient
|
|
│ ├── layouts/ # IDELayout (inline styles), SetupLayout
|
|
│ ├── lib/ # lspClient.ts (JSON-RPC bridge)
|
|
│ ├── pages/ # All page components (inline styles, lucide icons)
|
|
│ ├── services/ # api.ts
|
|
│ └── styles/ # globals.css (OKLCH tokens, font imports, global styles)
|
|
```
|
|
|
|
## Running Locally
|
|
|
|
### Native (dev mode, fastest iteration)
|
|
|
|
```bash
|
|
cd /home/jmg/dev/layonara/layonara-forge
|
|
WORKSPACE_PATH=/tmp/forge-test NWN_HOME_PATH=/home/jmg/dev/nwn/local-server/home GIT_PROVIDER_URL=https://gitea.layonara.com npm run dev
|
|
# Frontend: http://localhost:5173 (proxies to backend)
|
|
# Backend: http://localhost:3000
|
|
```
|
|
|
|
**Important**: If you run without `WORKSPACE_PATH`, it defaults to `/workspace` which doesn't exist natively. The File Explorer will show "Repository not cloned" and repos will show as uncloned.
|
|
|
|
### Docker (production mode)
|
|
|
|
```bash
|
|
cd /home/jmg/dev/layonara/layonara-forge
|
|
cp .env.example .env # edit paths
|
|
docker compose up -d
|
|
# Open http://localhost:3000
|
|
```
|
|
|
|
### Building Docker images
|
|
|
|
```bash
|
|
docker build -t layonara-builder builder/ # ~45 seconds
|
|
docker build -t layonara-forge . # ~2 minutes
|
|
```
|
|
|
|
## Current State (as of LSP integration session)
|
|
|
|
- All code is local — nothing has been pushed to any remote for the layonara-forge repo itself
|
|
- Setup wizard integration-tested against live Gitea (fork, clone all 3 repos verified)
|
|
- **monaco-languageclient v10** extended mode fully integrated — replaced hand-rolled LSP client
|
|
- **NWScript TextMate grammar** from submodule providing syntax highlighting (replaces Monarch tokenizer)
|
|
- **NWScript LSP** working end-to-end: completion, hover, diagnostics, go-to-definition, signature help
|
|
- **LSP server patched**: 4 crash bugs fixed (workspaceFolders null, workspace/configuration, rootPath null, tokenizer null guard)
|
|
- **TLK index** loaded at startup (41,927 entries), resolves localized string references in GFF editors
|
|
- GFF editors rewritten with inline styles, TLK lookup for CExoLocString fields
|
|
- EditorTabs rewritten with inline styles + lucide icons
|
|
- Tab content hydrates from API on refresh (localStorage persists tab list, API fetches content)
|
|
- Setup wizard error display improved with friendly error mapping
|
|
- Backend: WebSocket routing fixed (noServer mode), workspace routes have try/catch, writeConfig creates directories
|
|
- Docker: `git` added to production Dockerfile
|
|
|
|
### Editor Stack
|
|
|
|
| Component | Package | Purpose |
|
|
|-----------|---------|---------|
|
|
| Editor wrapper | `@typefox/monaco-editor-react` | React component wrapping MonacoEditorReactComp |
|
|
| LSP client | `monaco-languageclient` v10.7.0 | Extended mode with VS Code services |
|
|
| VS Code API | `@codingame/monaco-vscode-api` v25.1.2 | TextMate, themes, editor services |
|
|
| Grammar | `nwscript-extension/` | Local VS Code extension with `.tmLanguage.json` from submodule |
|
|
| Theme | Default Dark Modern | VS Code built-in (Forge Dark theme created but token colors need tuning) |
|
|
| LSP server | `lsp/nwscript-language-server/server/out/server.js` | Forked NWScript LSP, `--stdio` mode |
|
|
| LSP bridge | `packages/backend/src/services/lsp.service.ts` | WebSocket ↔ stdio pipe |
|
|
| Compiler | `lsp/.../server/resources/compiler/linux/nwn_script_comp` | Pre-built binary for diagnostics |
|
|
|
|
### Key Architecture Notes
|
|
|
|
- `monaco-editor` is overridden to `@codingame/monaco-vscode-editor-api` via npm overrides in root `package.json`
|
|
- The `MonacoVscodeApiWrapper` must be initialized once per app lifecycle — `MonacoEditorReactComp` handles this
|
|
- `SimpleEditor` and `SimpleDiffEditor` (for Server SQL console and Toolset diff viewer) also use `MonacoEditorReactComp` without a language client
|
|
- NWScript extension registered via `@codingame/monaco-vscode-api/extensions` `registerExtension` pattern
|
|
- Vite config requires `resolve.dedupe: ['vscode']`, `worker.format: 'es'`, and `@codingame/esbuild-import-meta-url-plugin`
|
|
- WebSocket routing: `/ws` (event broadcast), `/ws/lsp` (LSP bridge), `/ws/terminal/:id` (terminal) — all via `noServer` mode with manual upgrade handling
|
|
|
|
## Priority for Next Session
|
|
|
|
1. **Forge Dark theme** — created at `nwscript-extension/themes/forge-dark.json` but token colors don't apply (theme loads but syntax highlighting loses color). Needs investigation into how VS Code theme contributions interact with TextMate scopes in the `@codingame` stack. Consider starting from Default Dark Modern and customizing editor chrome colors via `userConfiguration` instead.
|
|
2. **Polish remaining components** — CommitDialog, FileExplorer, SearchPanel still use Tailwind classes for layout. ItemEditor, CreatureEditor, DialogEditor GFF editors also need inline style conversion.
|
|
3. **Test module build → server start → NWN client connection** — requires Docker socket access
|
|
4. **Test Toolset temp0/ sync** with real GFF files
|
|
5. **Test light mode** visually
|
|
6. **Set up Gitea backup** on xandrial
|
|
7. **Button nesting warning** — EditorTabs has `<button>` inside `<button>` (close button inside tab button). Change outer to `<div>` with click handler.
|
|
|