Spaces:
Runtime error
Runtime error
Upload 151 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +30 -0
- Dockerfile +93 -0
- README.md +110 -10
- dist/assets/index-CYg-Es9k.css +10 -0
- dist/assets/index-Dv5XhPnV.js +0 -0
- dist/favicon.svg +5 -0
- dist/index.html +16 -0
- docs/README.md +81 -0
- docs/auth/README.md +117 -0
- docs/cloud/README.md +71 -0
- docs/dashboard/README.md +97 -0
- docs/editor/00-panel-gate.md +25 -0
- docs/editor/01-editor-default.md +26 -0
- docs/editor/02-editor-explorer-file.md +27 -0
- docs/editor/03-editor-terminal.md +25 -0
- docs/editor/04-editor-console.md +25 -0
- docs/editor/05-editor-webview.md +25 -0
- docs/editor/06-editor-problems.md +25 -0
- docs/editor/07-sidebar-npm.md +25 -0
- docs/editor/08-sidebar-github.md +25 -0
- docs/editor/09-sidebar-drive.md +26 -0
- docs/editor/10-sidebar-ai.md +27 -0
- docs/editor/11-history-modal.md +25 -0
- docs/editor/12-settings-modal.md +26 -0
- docs/editor/13-settings-theme-changed.md +25 -0
- docs/editor/14-sidebar-pylib.md +26 -0
- docs/editor/README.md +302 -0
- docs/github/README.md +69 -0
- docs/leaderboard/README.md +62 -0
- docs/panel/README.md +59 -0
- docs/profile/README.md +105 -0
- docs/projects/README.md +76 -0
- docs/screenshots/desktop/01-panel-gate.png +0 -0
- docs/screenshots/desktop/02-home-landing.png +0 -0
- docs/screenshots/desktop/03-login.png +0 -0
- docs/screenshots/desktop/04-register.png +0 -0
- docs/screenshots/desktop/05-forgot-password.png +0 -0
- docs/screenshots/desktop/06-verify-pending.png +0 -0
- docs/screenshots/desktop/07-dashboard.png +3 -0
- docs/screenshots/desktop/08-leaderboard.png +0 -0
- docs/screenshots/desktop/09-profile-self.png +0 -0
- docs/screenshots/desktop/10-profile-other.png +3 -0
- docs/screenshots/desktop/11-editor.png +3 -0
- docs/screenshots/editor/00-panel-gate.png +0 -0
- docs/screenshots/editor/01-editor-default.png +0 -0
- docs/screenshots/editor/02-editor-explorer-file.png +3 -0
- docs/screenshots/editor/03-editor-terminal.png +3 -0
- docs/screenshots/editor/04-editor-console.png +3 -0
- docs/screenshots/editor/05-editor-webview.png +3 -0
- docs/screenshots/editor/06-editor-problems.png +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,33 @@ saved_model/**/* 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 36 |
+
docs/screenshots/desktop/07-dashboard.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
docs/screenshots/desktop/10-profile-other.png filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
docs/screenshots/desktop/11-editor.png filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
docs/screenshots/editor/02-editor-explorer-file.png filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
docs/screenshots/editor/03-editor-terminal.png filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
docs/screenshots/editor/04-editor-console.png filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
docs/screenshots/editor/05-editor-webview.png filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
docs/screenshots/editor/06-editor-problems.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
docs/screenshots/editor/07-sidebar-npm.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
docs/screenshots/editor/08-sidebar-github.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
docs/screenshots/editor/09-sidebar-drive.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
docs/screenshots/editor/10-sidebar-ai.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
docs/screenshots/editor/11-history-modal.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
docs/screenshots/mobile/07-dashboard.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
docs/screenshots/mobile/11-editor.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
public/screenshots/desktop/07-dashboard.png filter=lfs diff=lfs merge=lfs -text
|
| 52 |
+
public/screenshots/desktop/10-profile-other.png filter=lfs diff=lfs merge=lfs -text
|
| 53 |
+
public/screenshots/desktop/11-editor.png filter=lfs diff=lfs merge=lfs -text
|
| 54 |
+
public/screenshots/editor/02-editor-explorer-file.png filter=lfs diff=lfs merge=lfs -text
|
| 55 |
+
public/screenshots/editor/03-editor-terminal.png filter=lfs diff=lfs merge=lfs -text
|
| 56 |
+
public/screenshots/editor/04-editor-console.png filter=lfs diff=lfs merge=lfs -text
|
| 57 |
+
public/screenshots/editor/05-editor-webview.png filter=lfs diff=lfs merge=lfs -text
|
| 58 |
+
public/screenshots/editor/06-editor-problems.png filter=lfs diff=lfs merge=lfs -text
|
| 59 |
+
public/screenshots/editor/07-sidebar-npm.png filter=lfs diff=lfs merge=lfs -text
|
| 60 |
+
public/screenshots/editor/08-sidebar-github.png filter=lfs diff=lfs merge=lfs -text
|
| 61 |
+
public/screenshots/editor/09-sidebar-drive.png filter=lfs diff=lfs merge=lfs -text
|
| 62 |
+
public/screenshots/editor/10-sidebar-ai.png filter=lfs diff=lfs merge=lfs -text
|
| 63 |
+
public/screenshots/editor/11-history-modal.png filter=lfs diff=lfs merge=lfs -text
|
| 64 |
+
public/screenshots/mobile/07-dashboard.png filter=lfs diff=lfs merge=lfs -text
|
| 65 |
+
public/screenshots/mobile/11-editor.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ──────────────────────────────────────────────────────────────────────────────
|
| 2 |
+
# Exocore Docs — production Docker image
|
| 3 |
+
#
|
| 4 |
+
# Two-stage build:
|
| 5 |
+
# 1. Node 20 builds the Vite + React SPA into ./dist
|
| 6 |
+
# 2. nginx-alpine serves the static bundle (no Node runtime in the
|
| 7 |
+
# final image — keeps it tiny: ~25 MB compressed)
|
| 8 |
+
#
|
| 9 |
+
# Build:
|
| 10 |
+
# docker build -t exocore-docs:latest -f exocore-docs/Dockerfile exocore-docs
|
| 11 |
+
#
|
| 12 |
+
# Run (host port 8080 → container port 80):
|
| 13 |
+
# docker run --rm -p 8080:80 exocore-docs:latest
|
| 14 |
+
#
|
| 15 |
+
# Hugging Face Spaces: this image works as-is on a "Docker" Space — HF
|
| 16 |
+
# expects something on port 7860, so override at run-time:
|
| 17 |
+
# docker run -p 7860:7860 -e PORT=7860 exocore-docs:latest
|
| 18 |
+
# ──────────────────────────────────────────────────────────────────────────────
|
| 19 |
+
|
| 20 |
+
# ───── Stage 1: build the SPA ─────
|
| 21 |
+
FROM node:20-bookworm-slim AS builder
|
| 22 |
+
|
| 23 |
+
WORKDIR /build
|
| 24 |
+
|
| 25 |
+
# Cache npm install on the lockfile.
|
| 26 |
+
COPY package*.json ./
|
| 27 |
+
RUN npm install --legacy-peer-deps
|
| 28 |
+
|
| 29 |
+
# Pull in the rest of the project.
|
| 30 |
+
COPY tsconfig.json vite.config.ts index.html ./
|
| 31 |
+
COPY public ./public
|
| 32 |
+
COPY docs ./docs
|
| 33 |
+
COPY src ./src
|
| 34 |
+
|
| 35 |
+
RUN npm run build
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# ───── Stage 2: static serve via nginx ─────
|
| 39 |
+
FROM nginx:1.27-alpine AS runtime
|
| 40 |
+
|
| 41 |
+
# Drop the default nginx site and replace it with one that listens on
|
| 42 |
+
# $PORT (defaults to 80) and serves the SPA with hash-routing fallback.
|
| 43 |
+
RUN rm /etc/nginx/conf.d/default.conf
|
| 44 |
+
|
| 45 |
+
# Inline nginx config — kept here so the image is reproducible without
|
| 46 |
+
# any extra files outside the Docker build context.
|
| 47 |
+
RUN printf '%s\n' \
|
| 48 |
+
'server {' \
|
| 49 |
+
' listen ${NGINX_PORT};' \
|
| 50 |
+
' server_name _;' \
|
| 51 |
+
' root /usr/share/nginx/html;' \
|
| 52 |
+
' index index.html;' \
|
| 53 |
+
'' \
|
| 54 |
+
' # Long cache on hashed assets, short cache on the entry HTML.' \
|
| 55 |
+
' location /assets/ {' \
|
| 56 |
+
' add_header Cache-Control "public, max-age=31536000, immutable";' \
|
| 57 |
+
' try_files $uri =404;' \
|
| 58 |
+
' }' \
|
| 59 |
+
' location /screenshots/ {' \
|
| 60 |
+
' add_header Cache-Control "public, max-age=86400";' \
|
| 61 |
+
' try_files $uri =404;' \
|
| 62 |
+
' }' \
|
| 63 |
+
'' \
|
| 64 |
+
' # Hash-router SPA fallback — every unknown path returns index.html.' \
|
| 65 |
+
' location / {' \
|
| 66 |
+
' add_header Cache-Control "no-cache";' \
|
| 67 |
+
' try_files $uri $uri/ /index.html;' \
|
| 68 |
+
' }' \
|
| 69 |
+
'' \
|
| 70 |
+
' gzip on;' \
|
| 71 |
+
' gzip_types text/plain text/css application/javascript application/json image/svg+xml;' \
|
| 72 |
+
' gzip_min_length 1024;' \
|
| 73 |
+
'}' \
|
| 74 |
+
> /etc/nginx/templates/default.conf.template
|
| 75 |
+
|
| 76 |
+
COPY --from=builder /build/dist /usr/share/nginx/html
|
| 77 |
+
|
| 78 |
+
# Default to the conventional HTTP port. Override with -e PORT=7860 for
|
| 79 |
+
# Hugging Face Docker Spaces, or any other host that demands a specific
|
| 80 |
+
# listen port. nginx envsubst will fill `${NGINX_PORT}` at container start.
|
| 81 |
+
ENV PORT=80
|
| 82 |
+
ENV NGINX_PORT=80
|
| 83 |
+
|
| 84 |
+
# Cheap healthcheck — `wget --spider` is included in the alpine base image.
|
| 85 |
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
| 86 |
+
CMD wget --quiet --spider http://localhost:${NGINX_PORT}/ || exit 1
|
| 87 |
+
|
| 88 |
+
# Allow overriding the listen port via $PORT (HF Spaces convention).
|
| 89 |
+
# nginx automatically substitutes envs in /etc/nginx/templates/*.template
|
| 90 |
+
# when the official image starts up.
|
| 91 |
+
EXPOSE 80
|
| 92 |
+
|
| 93 |
+
CMD ["sh", "-c", "export NGINX_PORT=${PORT:-80}; exec nginx -g 'daemon off;'"]
|
README.md
CHANGED
|
@@ -1,10 +1,110 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Exocore Docs
|
| 2 |
+
|
| 3 |
+
A standalone Vite + React documentation site that renders every Markdown
|
| 4 |
+
file under [`./docs/`](./docs/) with full-text search, a sticky sidebar,
|
| 5 |
+
breadcrumbs, prev/next pagers, and dark-mode syntax highlighting. Built to
|
| 6 |
+
drop straight onto a Hugging Face **Static Space**, GitHub Pages, Netlify,
|
| 7 |
+
S3 — anywhere that serves a `dist/` folder.
|
| 8 |
+
|
| 9 |
+
> **Self-contained.** The full docs corpus (markdown + screenshots) lives
|
| 10 |
+
> inside this project (`./docs/` and `./public/screenshots/`). It used to
|
| 11 |
+
> read from `../exocore-web/docs/` via a vite alias, but the upstream copy
|
| 12 |
+
> in `exocore-web/` is now mirrored locally so this site can be split off
|
| 13 |
+
> into its own repo without breaking. To resync after editing the docs in
|
| 14 |
+
> the main project, just `cp -R ../exocore-web/docs/. ./docs/` and
|
| 15 |
+
> `cp -R ../exocore-web/docs/screenshots/. ./public/screenshots/`.
|
| 16 |
+
|
| 17 |
+
## Stack
|
| 18 |
+
|
| 19 |
+
| Piece | What it does |
|
| 20 |
+
| --- | --- |
|
| 21 |
+
| **Vite 6** | Bundles the SPA. Reads the local `./docs/` tree at build time via `import.meta.glob` so the entire docs corpus is shipped in one JS bundle (no runtime fetch). |
|
| 22 |
+
| **React 19** + **react-router-dom 7** | App shell, hash-routed pages (works on any dumb static host without rewrite rules). |
|
| 23 |
+
| **react-markdown** + **remark-gfm** + **rehype-slug** + **rehype-highlight** | Markdown → HTML, GitHub-Flavoured Markdown extensions, slug-based heading anchors, and `highlight.js` syntax colours (GitHub Dark theme). |
|
| 24 |
+
| **In-memory search** (`src/lib/docs.ts`) | Tokenised title+body indexing with snippet previews and `/` shortcut. |
|
| 25 |
+
|
| 26 |
+
## Quickstart
|
| 27 |
+
|
| 28 |
+
```bash
|
| 29 |
+
cd exocore-docs
|
| 30 |
+
npm install
|
| 31 |
+
npm run dev # vite on :5173
|
| 32 |
+
npm run build # outputs ./dist
|
| 33 |
+
npm run preview # serves ./dist on :4173
|
| 34 |
+
```
|
| 35 |
+
|
| 36 |
+
## Hugging Face Spaces deploy
|
| 37 |
+
|
| 38 |
+
1. `npm run build`
|
| 39 |
+
2. Push the **contents of `dist/`** (not the `dist/` folder itself) to a
|
| 40 |
+
Hugging Face *Static* Space repo. The space root must contain
|
| 41 |
+
`index.html`.
|
| 42 |
+
3. Done — every page is reachable under `https://<user>-<space>.hf.space/`.
|
| 43 |
+
|
| 44 |
+
Because the SPA uses `HashRouter`, a deep link like
|
| 45 |
+
`/#/docs/editor/` works without any nginx rewrite. Asset URLs are
|
| 46 |
+
`./assets/...` (relative), so the build also works under a sub-path
|
| 47 |
+
(`/myspace/...`) with no changes.
|
| 48 |
+
|
| 49 |
+
## How docs are picked up
|
| 50 |
+
|
| 51 |
+
```ts
|
| 52 |
+
import.meta.glob("../../docs/**/*.md", { query: "?raw", import: "default", eager: true });
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
That single line walks the entire local `./docs/` tree at build time and
|
| 56 |
+
inlines every `.md` file into the JS bundle. The pipeline in
|
| 57 |
+
`src/lib/docs.ts`:
|
| 58 |
+
|
| 59 |
+
1. Strips the path prefix down to a slug (`docs/editor/README.md → editor/`).
|
| 60 |
+
2. Pulls the first H1 as the title.
|
| 61 |
+
3. Pulls the first non-heading paragraph as the search-result excerpt.
|
| 62 |
+
4. Builds a lower-cased haystack for the in-memory search.
|
| 63 |
+
|
| 64 |
+
Add a new `.md` anywhere under `./docs/` and rebuild — it shows up in the
|
| 65 |
+
sidebar, in the home grid, and in search with no further wiring.
|
| 66 |
+
|
| 67 |
+
## Layout reference
|
| 68 |
+
|
| 69 |
+
```
|
| 70 |
+
exocore-docs/
|
| 71 |
+
├── index.html ← Vite entry HTML
|
| 72 |
+
├── public/
|
| 73 |
+
│ ├── favicon.svg ← inline-svg "E" favicon
|
| 74 |
+
│ └── screenshots/ ← desktop / mobile / editor PNGs (served as static assets)
|
| 75 |
+
├── docs/ ← full markdown corpus (mirrored from exocore-web/docs)
|
| 76 |
+
│ ├── README.md ← index page
|
| 77 |
+
│ ├── auth/ dashboard/ editor/ github/ cloud/ …
|
| 78 |
+
│ └── screenshots/ ← same screenshots, kept in-tree so the docs render
|
| 79 |
+
│ correctly when browsed on disk (e.g. on GitHub)
|
| 80 |
+
├── package.json ← React 19 + Vite 6 + react-markdown
|
| 81 |
+
├── vite.config.ts ← base: "./", allowedHosts, no external aliases
|
| 82 |
+
├── tsconfig.json
|
| 83 |
+
├── src/
|
| 84 |
+
│ ├── main.tsx ← HashRouter mount
|
| 85 |
+
│ ├── App.tsx ← <Layout/> shell + routes
|
| 86 |
+
│ ├── styles.css ← single-file palette + responsive shell
|
| 87 |
+
│ ├── lib/docs.ts ← glob ./docs/**/*.md → DOCS[] index + search()
|
| 88 |
+
│ ├── components/
|
| 89 |
+
│ │ ├── Layout.tsx ← topbar + sidebar shell + mobile drawer
|
| 90 |
+
│ │ ├── Sidebar.tsx ← per-section nav
|
| 91 |
+
│ │ ├── SearchBox.tsx ← / shortcut + dropdown results
|
| 92 |
+
│ │ └── MarkdownView.tsx ← react-markdown w/ rewritten links + real <img>
|
| 93 |
+
│ └── pages/
|
| 94 |
+
│ ├── Home.tsx ← hero + featured grid + all-pages list
|
| 95 |
+
│ ├── DocPage.tsx ← rendered markdown + breadcrumb + pager
|
| 96 |
+
│ └── NotFound.tsx
|
| 97 |
+
```
|
| 98 |
+
|
| 99 |
+
## Notes / gotchas
|
| 100 |
+
|
| 101 |
+
- **Images inside docs** (e.g. `../screenshots/editor/01.png`) are now
|
| 102 |
+
rendered as real `<img>` tags. The renderer (`MarkdownView.tsx`)
|
| 103 |
+
resolves the relative path against the doc's slug, detects the
|
| 104 |
+
`screenshots/` segment, and re-anchors onto `./screenshots/...` —
|
| 105 |
+
served verbatim by the host out of `public/screenshots/`.
|
| 106 |
+
- **Routing** is hash-based on purpose — keeps the site portable to every
|
| 107 |
+
static host without server-side URL rewriting.
|
| 108 |
+
- **Bundle size** is ~630 KB JS (200 KB gzip) for the full SPA + every
|
| 109 |
+
doc. Screenshots are *not* in the JS bundle — they sit in `public/` and
|
| 110 |
+
are loaded lazily as the user scrolls each page.
|
dist/assets/index-CYg-Es9k.css
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
| 2 |
+
Theme: GitHub Dark
|
| 3 |
+
Description: Dark theme as seen on github.com
|
| 4 |
+
Author: github.com
|
| 5 |
+
Maintainer: @Hirse
|
| 6 |
+
Updated: 2021-05-15
|
| 7 |
+
|
| 8 |
+
Outdated base version: https://github.com/primer/github-syntax-dark
|
| 9 |
+
Current colors taken from GitHub's CSS
|
| 10 |
+
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}:root{--bg: #0E0E10;--bg-soft: #16161A;--bg-card: #1C1C22;--bg-elev: #22232A;--border: #2A2B33;--text: #E6E6EA;--text-dim: #9A9AA6;--text-mute: #6F6F7A;--accent: #FFE500;--accent-2: #00FF94;--link: #7CC2FF;--code-bg: #11131A;--shadow: 0 6px 24px rgba(0, 0, 0, .45);--radius: 14px;--radius-sm: 8px;--topbar-h: 56px;--sidebar-w: 280px;--content-max: 880px}*{box-sizing:border-box}html,body,#root{height:100%}body{margin:0;background:var(--bg);color:var(--text);font-family:Inter,SF Pro Text,system-ui,-apple-system,sans-serif;font-size:15px;line-height:1.55;-webkit-font-smoothing:antialiased}a{color:var(--link);text-decoration:none;transition:color .12s ease}a:hover{color:var(--accent)}kbd{background:var(--bg-elev);border:1px solid var(--border);border-bottom-width:2px;border-radius:6px;padding:0 6px;font-family:JetBrains Mono,Fira Code,monospace;font-size:12px}.app-shell{min-height:100%;display:flex;flex-direction:column}.topbar{position:sticky;top:0;z-index:50;height:var(--topbar-h);background:#0e0e10d9;-webkit-backdrop-filter:saturate(180%) blur(8px);backdrop-filter:saturate(180%) blur(8px);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 18px;gap:14px}.brand{display:inline-flex;align-items:center;gap:10px;font-weight:700;color:var(--text);font-size:16px}.brand-mark{width:28px;height:28px;border-radius:8px;background:var(--accent);color:var(--bg);display:inline-flex;align-items:center;justify-content:center;font-weight:900}.brand-text{white-space:nowrap}.topbar-actions{margin-left:auto;display:flex;gap:4px}.topbar-link{color:var(--text-dim);padding:6px 10px;border-radius:8px;font-size:13px}.topbar-link:hover{color:var(--text);background:var(--bg-elev)}.hamburger{background:transparent;border:1px solid var(--border);border-radius:8px;width:36px;height:36px;display:none;flex-direction:column;justify-content:center;align-items:center;gap:4px;cursor:pointer}.hamburger span{display:block;width:16px;height:2px;background:var(--text);border-radius:2px}.shell-body{flex:1;display:grid;grid-template-columns:var(--sidebar-w) minmax(0,1fr);position:relative}.main-pane{padding:32px 36px 80px;min-width:0}.sidebar-backdrop{display:none;position:fixed;inset:var(--topbar-h) 0 0 0;background:#0000008c;z-index:30}.sidebar{border-right:1px solid var(--border);background:var(--bg-soft);padding:22px 16px 40px;overflow-y:auto;height:calc(100vh - var(--topbar-h));position:sticky;top:var(--topbar-h)}.sidebar-home{display:block;color:var(--text-dim);padding:6px 8px;border-radius:6px;font-size:13px;margin-bottom:14px}.sidebar-home.active,.sidebar-home:hover{color:var(--accent);background:var(--bg-elev)}.sidebar-section{margin-bottom:18px}.sidebar-section-label{text-transform:uppercase;letter-spacing:.08em;font-size:11px;color:var(--text-mute);padding:0 8px 6px}.sidebar-list{list-style:none;margin:0;padding:0}.sidebar-link{display:block;padding:6px 10px;border-radius:6px;color:var(--text-dim);font-size:14px;line-height:1.3;margin-bottom:1px;border-left:2px solid transparent}.sidebar-link:hover{color:var(--text);background:var(--bg-card)}.sidebar-link.active{color:var(--accent);background:var(--bg-elev);border-left-color:var(--accent);font-weight:600}.search-wrap{position:relative;width:100%;max-width:720px}.search-shell{display:flex;align-items:center;gap:10px;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:10px 14px;transition:border-color .15s ease,box-shadow .15s ease}.search-shell:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px #ffe5001f}.search-icon{font-size:14px;opacity:.7}.search-input{flex:1;background:transparent;border:0;outline:0;color:var(--text);font-size:15px;min-width:0}.search-clear{background:transparent;border:0;color:var(--text-mute);font-size:18px;cursor:pointer;padding:0 4px}.search-clear:hover{color:var(--text)}.search-results{position:absolute;top:calc(100% + 6px);left:0;right:0;background:var(--bg-card);border:1px solid var(--border);border-radius:12px;box-shadow:var(--shadow);max-height:60vh;overflow-y:auto;z-index:40}.search-empty{padding:16px;color:var(--text-dim);font-size:14px}.search-result{display:block;padding:12px 14px;border-bottom:1px solid var(--border);color:var(--text)}.search-result:last-child{border-bottom:0}.search-result.active,.search-result:hover{background:var(--bg-elev)}.search-result-head{display:flex;justify-content:space-between;gap:12px;align-items:baseline;margin-bottom:4px}.search-result-title{font-weight:600;font-size:14px;color:var(--text)}.search-result-section{color:var(--text-mute);font-size:11px;text-transform:uppercase;letter-spacing:.06em}.search-result-snippet{color:var(--text-dim);font-size:13px;line-height:1.4;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}.home{max-width:var(--content-max);margin:0 auto}.hero{padding:24px 0 28px;margin-bottom:22px;border-bottom:1px solid var(--border)}.hero-eyebrow{display:inline-block;font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:var(--accent);background:#ffe5001a;border:1px solid rgba(255,229,0,.35);border-radius:999px;padding:4px 12px;margin-bottom:16px}.hero-title{font-size:clamp(28px,4vw,40px);line-height:1.15;margin:0 0 14px;letter-spacing:-.02em}.hero-sub{color:var(--text-dim);font-size:16px;max-width:640px;margin:0 0 22px}.hero-cta{color:var(--text-dim);font-size:13px;margin-top:14px}.hero-link{background:transparent;border:0;color:var(--link);cursor:pointer;padding:0;font:inherit}.hero-link:hover{color:var(--accent)}.section{margin:36px 0}.section-title{font-size:18px;margin:0 0 14px;color:var(--text)}.card-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}.doc-card{display:block;padding:18px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);transition:transform .15s ease,border-color .15s ease,background .15s ease}.doc-card:hover{background:var(--bg-elev);border-color:var(--accent);transform:translateY(-2px)}.doc-card-tag{display:inline-block;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--accent);margin-bottom:8px}.doc-card-title{font-weight:700;font-size:16px;margin-bottom:8px}.doc-card-excerpt{color:var(--text-dim);font-size:13px;margin:0 0 12px;line-height:1.45;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.doc-card-cta{color:var(--link);font-size:13px}.all-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:22px}.all-section-label{text-transform:uppercase;letter-spacing:.08em;font-size:12px;color:var(--text-mute);margin-bottom:8px}.all-section-list{list-style:none;margin:0;padding:0}.all-section-list li{padding:4px 0}.all-section-list a{color:var(--text-dim);font-size:14px}.all-section-list a:hover{color:var(--accent)}.doc-page{max-width:var(--content-max);margin:0 auto}.breadcrumb{color:var(--text-mute);font-size:13px;margin-bottom:14px;display:flex;flex-wrap:wrap;align-items:center}.crumb{display:inline-flex;align-items:center}.crumb-sep{padding:0 8px;color:var(--text-mute)}.doc-page-search{margin:0 0 24px}.doc-pager{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:36px;padding-top:22px;border-top:1px solid var(--border)}.doc-pager-cell.right{justify-self:end;text-align:right}.doc-pager-link{display:block;padding:14px 16px;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);color:var(--text);min-width:220px}.doc-pager-link:hover{border-color:var(--accent)}.doc-pager-dir{display:block;color:var(--text-mute);font-size:12px;text-transform:uppercase;letter-spacing:.08em;margin-bottom:4px}.doc-pager-title{color:var(--text);font-weight:600;font-size:14px}.markdown-body{font-size:15px;line-height:1.7;color:var(--text);word-wrap:break-word}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5{line-height:1.25;margin:28px 0 14px;color:var(--text);font-weight:700}.markdown-body h1{font-size:clamp(26px,3.4vw,34px);border-bottom:1px solid var(--border);padding-bottom:12px;margin-top:8px}.markdown-body h2{font-size:22px}.markdown-body h3{font-size:18px}.markdown-body p{margin:0 0 14px;color:var(--text)}.markdown-body ul,.markdown-body ol{padding-left:22px;margin:0 0 14px}.markdown-body li{margin-bottom:4px}.markdown-body blockquote{border-left:3px solid var(--accent);background:var(--bg-card);color:var(--text-dim);padding:10px 14px;margin:14px 0;border-radius:0 var(--radius-sm) var(--radius-sm) 0}.markdown-body code{background:var(--code-bg);border:1px solid var(--border);padding:1px 6px;border-radius:5px;font-family:JetBrains Mono,Fira Code,monospace;font-size:13px}.markdown-body pre{background:var(--code-bg)!important;border:1px solid var(--border);border-radius:var(--radius-sm);padding:14px 16px;overflow-x:auto;margin:14px 0}.markdown-body pre code{background:transparent;border:0;padding:0;font-size:13px;line-height:1.55}.markdown-body hr{border:0;border-top:1px solid var(--border);margin:24px 0}.markdown-body table{border-collapse:collapse;width:100%;margin:14px 0;font-size:14px}.markdown-body th,.markdown-body td{border:1px solid var(--border);padding:8px 12px;text-align:left}.markdown-body th{background:var(--bg-card)}.markdown-body img{max-width:100%;border-radius:var(--radius-sm)}.md-img-stub{display:inline-flex;align-items:center;gap:6px;background:var(--bg-card);border:1px dashed var(--border);color:var(--text-mute);padding:4px 10px;border-radius:8px;font-size:13px}.footer{padding:18px 24px;color:var(--text-mute);font-size:12px;border-top:1px solid var(--border);background:var(--bg-soft);display:flex;justify-content:center;gap:8px}.footer-sep{opacity:.5}.not-found{padding:60px 0;text-align:center}.not-found h1{font-size:80px;margin:0;color:var(--accent)}@media(max-width:960px){.hamburger{display:inline-flex}.shell-body{grid-template-columns:1fr}.sidebar{position:fixed;top:var(--topbar-h);left:0;bottom:0;width:min(85vw,320px);z-index:35;transform:translate(-100%);transition:transform .22s cubic-bezier(.2,.7,.2,1);height:auto}.shell-body.sidebar-open .sidebar{transform:translate(0)}.shell-body.sidebar-open .sidebar-backdrop{display:block}.main-pane{padding:24px 18px 64px}}
|
dist/assets/index-Dv5XhPnV.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
dist/favicon.svg
ADDED
|
|
dist/index.html
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<meta name="description" content="Exocore — visual documentation. Browser-based IDE, social platform, and developer panel." />
|
| 8 |
+
<meta name="theme-color" content="#FFE500" />
|
| 9 |
+
<title>Exocore Docs</title>
|
| 10 |
+
<script type="module" crossorigin src="./assets/index-Dv5XhPnV.js"></script>
|
| 11 |
+
<link rel="stylesheet" crossorigin href="./assets/index-CYg-Es9k.css">
|
| 12 |
+
</head>
|
| 13 |
+
<body>
|
| 14 |
+
<div id="root"></div>
|
| 15 |
+
</body>
|
| 16 |
+
</html>
|
docs/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Exocore Web — Visual Documentation
|
| 2 |
+
|
| 3 |
+
> Generated on April 24, 2026 — captures every panel route in **desktop (1440×900)**
|
| 4 |
+
> and **mobile (390×844)** using bundled Chromium via Puppeteer.
|
| 5 |
+
> Re-run with `npx tsx exocore-web/scripts/capture-docs.ts`.
|
| 6 |
+
|
| 7 |
+
Exocore Web is a next-generation **browser-based IDE + developer panel** with built-in
|
| 8 |
+
social features (posts, DMs, friends), gamified XP / leaderboard, project nodes,
|
| 9 |
+
cloud storage (Google Drive), GitHub integration, NPM / pip / Cargo package
|
| 10 |
+
managers, and a multi-language editor backed by Monaco + CodeMirror.
|
| 11 |
+
|
| 12 |
+
The panel is mounted at the `/exocore` URL prefix. The whole SPA sits behind the
|
| 13 |
+
**panel-devs gate** (`devs.json` master account) which protects every route on the
|
| 14 |
+
server. Once unlocked, regular **user accounts** sign in via `/exocore/login`.
|
| 15 |
+
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
## 📚 Sections
|
| 19 |
+
|
| 20 |
+
| # | Folder | What it covers |
|
| 21 |
+
|---|--------|----------------|
|
| 22 |
+
| 1 | [`./panel/`](./panel/README.md) | Panel-devs gate (master `devs.json` lock screen) |
|
| 23 |
+
| 2 | [`./auth/`](./auth/README.md) | Home landing, Login, Register, Forgot, Verify, OAuth callback |
|
| 24 |
+
| 3 | [`./dashboard/`](./dashboard/README.md) | Main `/dashboard` workspace (after user login) |
|
| 25 |
+
| 4 | [`./profile/`](./profile/README.md) | `/u/:username` — profile, posts, friends, "stalk" mode |
|
| 26 |
+
| 5 | [`./social/`](./social/README.md) | SocialPanel — chat, DMs, online presence, posts |
|
| 27 |
+
| 6 | [`./leaderboard/`](./leaderboard/README.md) | Top members ranked by XP / level / achievements |
|
| 28 |
+
| 7 | [`./projects/`](./projects/README.md) | Project nodes, CreateProjectWizard, FileManager |
|
| 29 |
+
| 8 | [`./cloud/`](./cloud/README.md) | Google Drive cloud storage manager |
|
| 30 |
+
| 9 | [`./github/`](./github/README.md) | GitHub integration (org browse, clone, push) |
|
| 31 |
+
| 10 | [`./editor/`](./editor/README.md) | IDE (Monaco/CodeMirror), languages, terminal, AI, NPM |
|
| 32 |
+
|
| 33 |
+
Raw screenshot grids live in
|
| 34 |
+
[`./screenshots/desktop/`](./screenshots/desktop) and
|
| 35 |
+
[`./screenshots/mobile/`](./screenshots/mobile).
|
| 36 |
+
|
| 37 |
+
---
|
| 38 |
+
|
| 39 |
+
## 🗺️ Route map
|
| 40 |
+
|
| 41 |
+
| URL | Component | Auth required |
|
| 42 |
+
|------------------------------|----------------------------------------|---------------|
|
| 43 |
+
| `/exocore/` | `access/auth/Home.tsx` (landing) | Panel only |
|
| 44 |
+
| `/exocore/login` | `access/auth/Login.tsx` | Panel only |
|
| 45 |
+
| `/exocore/register` | `access/auth/Register.tsx` | Panel only |
|
| 46 |
+
| `/exocore/forgot` | `access/auth/Forgot.tsx` | Panel only |
|
| 47 |
+
| `/exocore/verify-pending` | `access/auth/VerifyPending.tsx` | Panel only |
|
| 48 |
+
| `/exocore/auth/callback` | `access/auth/AuthCallback.tsx` | Panel only |
|
| 49 |
+
| `/exocore/dashboard` | `home/Dashboard.tsx` | Panel + user |
|
| 50 |
+
| `/exocore/u/:username` | `profile/Profile.tsx` | Panel + user |
|
| 51 |
+
| `/exocore/leaderboard` | `leaderboard/Leaderboard.tsx` | Panel + user |
|
| 52 |
+
| `/exocore/editor` | `editor/coding.tsx` | Panel + user |
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## 🧰 Stack snapshot
|
| 57 |
+
|
| 58 |
+
- **Server**: Express 5 + TypeScript (`exocore-web/app.ts`, `index.ts`), with
|
| 59 |
+
WebSocket multiplexer (`server/wsMux`), social hub (`server/social/hub`),
|
| 60 |
+
RPC hub (`server/rpc/hub`).
|
| 61 |
+
- **Client**: React 19 + Vite + React Router (basename `/exocore`), Monaco / CodeMirror,
|
| 62 |
+
xterm.js, Framer Motion, SweetAlert2, Zustand.
|
| 63 |
+
- **Backend (separate)**: `Exocore-Backend/` — Express + Google Drive sync,
|
| 64 |
+
encrypted user store, mail, OAuth.
|
| 65 |
+
- **Auth model**:
|
| 66 |
+
- **Panel gate** → server-side `devs.json` (one master account per server).
|
| 67 |
+
- **User auth** → email-verified accounts handled by the Exocore-Backend.
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
## ▶️ How the screenshots were captured
|
| 72 |
+
|
| 73 |
+
Both viewports were rendered with a real **Chromium 138** binary
|
| 74 |
+
(`/nix/store/.../chromium`) driven by Puppeteer. The script:
|
| 75 |
+
|
| 76 |
+
1. Loads `/exocore/` to capture the panel-devs **setup gate**.
|
| 77 |
+
2. Submits the gate form to create the master account.
|
| 78 |
+
3. Re-visits every route and takes a `fullPage` PNG.
|
| 79 |
+
4. Repeats for the mobile viewport.
|
| 80 |
+
|
| 81 |
+
See [`exocore-web/scripts/capture-docs.ts`](../scripts/capture-docs.ts).
|
docs/auth/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Authentication Flow
|
| 2 |
+
|
| 3 |
+
Once the panel-devs gate is unlocked, the SPA exposes a complete user-account
|
| 4 |
+
flow backed by **`Exocore-Backend`** (email-verified accounts, Google Drive
|
| 5 |
+
hashed at rest, OAuth). All five auth screens render with the same dark
|
| 6 |
+
neon-yellow brand and share `access/components/AuthLayout.tsx`.
|
| 7 |
+
|
| 8 |
+
## Files
|
| 9 |
+
|
| 10 |
+
| Route | Component |
|
| 11 |
+
|-------|-----------|
|
| 12 |
+
| `/exocore/` | [`access/auth/Home.tsx`](../../client/access/auth/Home.tsx) |
|
| 13 |
+
| `/exocore/login` | [`access/auth/Login.tsx`](../../client/access/auth/Login.tsx) |
|
| 14 |
+
| `/exocore/register` | [`access/auth/Register.tsx`](../../client/access/auth/Register.tsx) |
|
| 15 |
+
| `/exocore/forgot` | [`access/auth/Forgot.tsx`](../../client/access/auth/Forgot.tsx) |
|
| 16 |
+
| `/exocore/verify-pending`| [`access/auth/VerifyPending.tsx`](../../client/access/auth/VerifyPending.tsx) |
|
| 17 |
+
| `/exocore/auth/callback` | [`access/auth/AuthCallback.tsx`](../../client/access/auth/AuthCallback.tsx) |
|
| 18 |
+
|
| 19 |
+
The user token (`exo_token`) is stored in `localStorage` and is rebroadcast on
|
| 20 |
+
every `/exocore/api/*` axios request via the global interceptor in
|
| 21 |
+
`access/panelAuth.ts`.
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
## 1. Home (landing) — `/exocore/`
|
| 26 |
+
|
| 27 |
+
The first screen a visitor sees after the panel-devs gate. Pure marketing:
|
| 28 |
+
brand, tagline, two CTAs (`Get Started` → `/login`, `Create Account` →
|
| 29 |
+
`/register`) and feature pills (Fast IDE, Live Terminal, AI Assistant,
|
| 30 |
+
NPM Manager, Git/GitHub, Web Preview).
|
| 31 |
+
|
| 32 |
+
| Desktop | Mobile |
|
| 33 |
+
|---------|--------|
|
| 34 |
+
|  |  |
|
| 35 |
+
|
| 36 |
+
If `localStorage.exo_token` is already present, `useEffect` redirects to
|
| 37 |
+
`/dashboard` — repeat visitors skip this page entirely.
|
| 38 |
+
|
| 39 |
+
---
|
| 40 |
+
|
| 41 |
+
## 2. Login — `/exocore/login`
|
| 42 |
+
|
| 43 |
+
Two-field sign in (username **or** email + password) with show-password eye
|
| 44 |
+
toggle, "Forgot password?" link, and an inline "Create an account" footer
|
| 45 |
+
link.
|
| 46 |
+
|
| 47 |
+
| Desktop | Mobile |
|
| 48 |
+
|---------|--------|
|
| 49 |
+
|  |  |
|
| 50 |
+
|
| 51 |
+
Submission calls `POST /exocore/api/auth/login` (proxied to the backend).
|
| 52 |
+
On success the token is stamped into `localStorage.exo_token` and the user is
|
| 53 |
+
forwarded to `/dashboard`. Failures show a red banner inside the card.
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
## 3. Register — `/exocore/register`
|
| 58 |
+
|
| 59 |
+
Multi-step account creation: username, email, password (with strength meter),
|
| 60 |
+
country, optional avatar upload. Validation is real-time (Zod-ish hand-rolled
|
| 61 |
+
checks) and the `Create account` button is disabled until every field passes.
|
| 62 |
+
|
| 63 |
+
| Desktop | Mobile |
|
| 64 |
+
|---------|--------|
|
| 65 |
+
|  |  |
|
| 66 |
+
|
| 67 |
+
After submit, the backend sends a verification e-mail and the SPA pushes the
|
| 68 |
+
user to `/verify-pending`.
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
## 4. Forgot password — `/exocore/forgot`
|
| 73 |
+
|
| 74 |
+
Single email input. Submitting calls
|
| 75 |
+
`POST /exocore/api/auth/forgot` which dispatches a one-time reset code to the
|
| 76 |
+
mail address. The page then asks for the code + new password inline (no
|
| 77 |
+
extra route — same component swaps state).
|
| 78 |
+
|
| 79 |
+
| Desktop | Mobile |
|
| 80 |
+
|---------|--------|
|
| 81 |
+
|  |  |
|
| 82 |
+
|
| 83 |
+
---
|
| 84 |
+
|
| 85 |
+
## 5. Verify pending — `/exocore/verify-pending`
|
| 86 |
+
|
| 87 |
+
Holding screen shown right after sign-up. Polls
|
| 88 |
+
`GET /exocore/api/auth/verify/status?email=…` every few seconds and
|
| 89 |
+
auto-forwards to `/login` once the user clicks the link inside the
|
| 90 |
+
verification mail. Buttons: **Resend e-mail**, **Change e-mail**, **Back**.
|
| 91 |
+
|
| 92 |
+
| Desktop | Mobile |
|
| 93 |
+
|---------|--------|
|
| 94 |
+
|  |  |
|
| 95 |
+
|
| 96 |
+
---
|
| 97 |
+
|
| 98 |
+
## 6. OAuth callback — `/exocore/auth/callback`
|
| 99 |
+
|
| 100 |
+
Tiny landing page used by Google OAuth (and any future provider). Reads the
|
| 101 |
+
`?token=` query string set by the backend redirect, persists it via
|
| 102 |
+
`setPanelToken`, and `navigate('/dashboard')`. No screenshot needed — it
|
| 103 |
+
flashes for less than a second.
|
| 104 |
+
|
| 105 |
+
---
|
| 106 |
+
|
| 107 |
+
## Backend touchpoints
|
| 108 |
+
|
| 109 |
+
| Endpoint | Used by |
|
| 110 |
+
|-------------------------------------------|---------|
|
| 111 |
+
| `POST /exocore/api/auth/register` | Register |
|
| 112 |
+
| `POST /exocore/api/auth/login` | Login |
|
| 113 |
+
| `POST /exocore/api/auth/forgot` | Forgot |
|
| 114 |
+
| `POST /exocore/api/auth/verify` | Verify-pending poll |
|
| 115 |
+
| `GET /exocore/api/auth/userinfo` | Bootstrapped by Dashboard |
|
| 116 |
+
| `GET /exocore/api/auth/audit` | Account / security tab |
|
| 117 |
+
| `DELETE /exocore/api/auth/delete` | Account → Delete account |
|
docs/cloud/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Cloud Backups — Google Drive integration
|
| 2 |
+
|
| 3 |
+
Exocore mirrors all panel state to a private folder on the **owner's Google
|
| 4 |
+
Drive** so that you can wipe / re-deploy the server and restore everything in
|
| 5 |
+
seconds. The client side lives in
|
| 6 |
+
[`home/GDriveManager.tsx`](../../client/home/GDriveManager.tsx) (modal opened
|
| 7 |
+
from the dashboard's `☁ Cloud Backups` quick action) plus
|
| 8 |
+
[`editor/GDrivePane.tsx`](../../client/editor/GDrivePane.tsx) (Drive sidebar
|
| 9 |
+
inside the IDE).
|
| 10 |
+
|
| 11 |
+
## What gets synced
|
| 12 |
+
|
| 13 |
+
| Backend file | What it stores |
|
| 14 |
+
|---------------------------------------|----------------|
|
| 15 |
+
| `Exocore-Backend/local-db/users.enc` | All user accounts (encrypted at rest) |
|
| 16 |
+
| `Exocore-Backend/local-db/posts.json` | Public posts feed |
|
| 17 |
+
| `Exocore-Backend/local-db/dms.*` | Direct messages |
|
| 18 |
+
| `Exocore-Backend/local-db/global-chat.json` | Global chat backlog |
|
| 19 |
+
| `Exocore-Backend/local-db/token.enc` | OAuth bootstrap |
|
| 20 |
+
| `client/access/devs.json` | Panel-devs master account |
|
| 21 |
+
|
| 22 |
+
A background loop in
|
| 23 |
+
[`Exocore-Backend/src/services/drive.ts`](../../Exocore-Backend/src/services/drive.ts)
|
| 24 |
+
checks every 60 s, uploads diffs, and emits cache-restore events on boot
|
| 25 |
+
(`[cache] restoring from Drive…`).
|
| 26 |
+
|
| 27 |
+
## Manager modal — desktop / mobile
|
| 28 |
+
|
| 29 |
+
Opened from `Dashboard → Quick Actions → Cloud Backups`. Sections:
|
| 30 |
+
|
| 31 |
+
```
|
| 32 |
+
┌─ Connected account ──────────────┐
|
| 33 |
+
│ 📷 Google avatar email · plan │
|
| 34 |
+
│ Disk used: 4.2 GB / 15 GB │
|
| 35 |
+
│ [ Re-authorize ] [ Disconnect ] │
|
| 36 |
+
├─ Sync status ────────────────────┤
|
| 37 |
+
│ ✅ users.enc 2 s ago │
|
| 38 |
+
│ ✅ posts.json 2 s ago │
|
| 39 |
+
│ 🔄 dms.* uploading… │
|
| 40 |
+
│ ⚠️ tokens.enc error: … │
|
| 41 |
+
├─ Manual actions ─────────────────┤
|
| 42 |
+
│ [ ⬆ Force upload ] [ ⬇ Pull now ]│
|
| 43 |
+
│ [ 🗑 Reset cloud cache ] │
|
| 44 |
+
└──────────────────────────────────┘
|
| 45 |
+
```
|
| 46 |
+
|
| 47 |
+
(Same dashboard capture applies — the modal opens on top of it once a user is
|
| 48 |
+
authenticated.)
|
| 49 |
+
|
| 50 |
+
| Desktop | Mobile |
|
| 51 |
+
|---------|--------|
|
| 52 |
+
|  |  |
|
| 53 |
+
|
| 54 |
+
## OAuth flow
|
| 55 |
+
|
| 56 |
+
1. User clicks **Connect Google Drive** → server redirects to Google's consent
|
| 57 |
+
screen with the `drive.file` scope.
|
| 58 |
+
2. Google redirects back to `/exocore/api/oauth/google/callback`.
|
| 59 |
+
3. Server exchanges the code, encrypts + writes the refresh token to
|
| 60 |
+
`Exocore-Backend/local-db/token.enc`.
|
| 61 |
+
4. Sync loop boots and walks the `EXOCORE_BACKUPS` parent folder.
|
| 62 |
+
|
| 63 |
+
## RPC + HTTP touchpoints
|
| 64 |
+
|
| 65 |
+
| Channel / route | Purpose |
|
| 66 |
+
|-------------------------------------------|---------|
|
| 67 |
+
| RPC `drive.status` | Connected account + per-file last-sync ts |
|
| 68 |
+
| RPC `drive.sync` | Force a full upload pass |
|
| 69 |
+
| RPC `drive.pull` | Replace local cache with Drive copy |
|
| 70 |
+
| RPC `drive.disconnect` | Revoke + clear local token |
|
| 71 |
+
| HTTP `POST /exocore/api/editor/gdrive/*` | IDE-side file picker (proxied to backend Drive scope) |
|
docs/dashboard/README.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dashboard — `/exocore/dashboard`
|
| 2 |
+
|
| 3 |
+
The Dashboard is the **command-centre** that opens right after a successful
|
| 4 |
+
user login. Implemented in
|
| 5 |
+
[`client/home/Dashboard.tsx`](../../client/home/Dashboard.tsx); ~770 lines
|
| 6 |
+
that orchestrate every other panel as either an inline view (`activeView`) or
|
| 7 |
+
a modal (`*Open` flags).
|
| 8 |
+
|
| 9 |
+
> The screenshots below show the **redirect to `/login`** because the docs
|
| 10 |
+
> capture run was unauthenticated. With a real `exo_token` in
|
| 11 |
+
> `localStorage`, the same route renders the layout described below.
|
| 12 |
+
|
| 13 |
+
| Desktop | Mobile |
|
| 14 |
+
|---------|--------|
|
| 15 |
+
|  |  |
|
| 16 |
+
|
| 17 |
+
---
|
| 18 |
+
|
| 19 |
+
## Layout
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
┌─ Header ──────────────────────────────────────────────┐
|
| 23 |
+
│ EXOCORE • CPU 4 % · RAM 42 % · 14:22:01 │
|
| 24 |
+
│ Avatar ⟶ Profile · Leaderboard · ⚙ Account · Logout │
|
| 25 |
+
├─ Welcome card ────────────────────────────────────────┤
|
| 26 |
+
│ Hi {nickname}, plan: FREE / PRO / OWNER │
|
| 27 |
+
│ Plan-upgrade button (opens PlansModal) │
|
| 28 |
+
├─ Stats row ───────────────────────────────────────────┤
|
| 29 |
+
│ Projects · Active · CPU · RAM (live ticking) │
|
| 30 |
+
├─ Project Nodes ───────────────────────────────────────┤
|
| 31 |
+
│ Grid of ProjectNodeCard (open · run · archive) │
|
| 32 |
+
│ + "Show all" toggle if > 6 │
|
| 33 |
+
├─ Quick Actions ───────────────────────────────────────┤
|
| 34 |
+
│ ▢ Project Nodes · ⚙ Account · ☁ Cloud Backups · … │
|
| 35 |
+
├─ Social grid ─────────────────────────────────────────┤
|
| 36 |
+
│ GitHub · Drive · Discord · X · Donations … │
|
| 37 |
+
└───────────────────────────────────────────────────────┘
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
## State machine
|
| 41 |
+
|
| 42 |
+
`activeView` only swaps two top-level views:
|
| 43 |
+
|
| 44 |
+
| Value | What renders |
|
| 45 |
+
|-------|--------------|
|
| 46 |
+
| `'home'` | The composite layout above |
|
| 47 |
+
| `'account'`| Inline `<Account />` settings sub-page |
|
| 48 |
+
|
| 49 |
+
Everything else is a modal driven by booleans:
|
| 50 |
+
|
| 51 |
+
| Flag | Modal component | What it does |
|
| 52 |
+
|------|----------------|--------------|
|
| 53 |
+
| `projectsOpen` | `FileManager` | Browse all projects + storage usage |
|
| 54 |
+
| `gDriveOpen` | `GDriveManager` | OAuth-connect Google Drive, sync state |
|
| 55 |
+
| `githubOpen` | `GithubManager` | OAuth-connect GitHub, browse repos |
|
| 56 |
+
| `plansOpen` | `PlansModal` | Upgrade plan (free → pro / owner) |
|
| 57 |
+
| `ownerPaymentsOpen`| `OwnerPaymentsPanel` | Owner-only revenue / pay-out view |
|
| 58 |
+
| `headerMenuOpen` | Inline dropdown | Profile · Leaderboard · Account |
|
| 59 |
+
| `dialog` | SweetAlert2 wrapper | Confirm / prompt / alert |
|
| 60 |
+
|
| 61 |
+
## Header chips
|
| 62 |
+
|
| 63 |
+
- **Avatar** → `navigate(/u/${userData.username})` (own profile)
|
| 64 |
+
- **Trophy** → `/leaderboard`
|
| 65 |
+
- **Gear** → switches `activeView` to `'account'`
|
| 66 |
+
- **Logout** → clears `exo_token`, navigates back to `/login`
|
| 67 |
+
|
| 68 |
+
## Stats card
|
| 69 |
+
|
| 70 |
+
Powered by an interval that polls `/exocore/api/auth/userinfo` and the local
|
| 71 |
+
`navigator.connection` API; falls back to mocked values (`stats = { cpu, ram }`)
|
| 72 |
+
when the live numbers aren't available yet.
|
| 73 |
+
|
| 74 |
+
## Social grid
|
| 75 |
+
|
| 76 |
+
A static array `SOCIAL_LINKS` with id `github`, `gdrive`, `discord`, `twitter`,
|
| 77 |
+
`donate`, `youtube`. Rendered as **outbound** cards — clicking opens the org's
|
| 78 |
+
public page (e.g. `https://github.com/Exocore-Organization`). Different from
|
| 79 |
+
the in-app `social/SocialPanel` (chat / DM / posts) which is documented
|
| 80 |
+
separately under [`../social/`](../social/README.md).
|
| 81 |
+
|
| 82 |
+
## Auth shape consumed
|
| 83 |
+
|
| 84 |
+
```ts
|
| 85 |
+
type UserData = {
|
| 86 |
+
username: string; // e.g. "@exocore"
|
| 87 |
+
nickname: string; // "Costanos"
|
| 88 |
+
email: string;
|
| 89 |
+
plan: "free" | "pro" | "owner";
|
| 90 |
+
role: "user" | "mod" | "admin" | "owner";
|
| 91 |
+
xp: number;
|
| 92 |
+
level: number;
|
| 93 |
+
avatarUrl?: string;
|
| 94 |
+
country?: string;
|
| 95 |
+
// … plus payments / drive / github linked-account fields
|
| 96 |
+
};
|
| 97 |
+
```
|
docs/editor/00-panel-gate.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 00 — Panel gate
|
| 2 |
+
|
| 3 |
+
First-touch screen at `/exocore/`. Until a panel-devs master account exists in `devs.json`, every other route is locked behind this gate. The form doubles as both the **register-master** flow (when the file is empty) and the **unlock** flow (when an account is already provisioned).
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- POST `/exocore/api/access/panel/register` (first run) → writes the salted master credential into `devs.json`.
|
| 14 |
+
- POST `/exocore/api/access/panel/unlock` (subsequent runs) → returns a short-lived panel cookie used by every other route.
|
| 15 |
+
- All other client routes redirect here while the cookie is missing.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/access/Panel.tsx`](../../client/access/Panel.tsx)
|
| 20 |
+
- [`routes/access/panel.ts`](../../routes/access/panel.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/01-editor-default.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 01 — Default editor view
|
| 2 |
+
|
| 3 |
+
Right after login + project pick, the editor opens onto the project root. Monaco shows a welcome banner, the file explorer lists the seeded files, and the bottom panel starts collapsed.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- `<LayoutHeader />` shows the project name + path, command palette button, and theme toggle.
|
| 14 |
+
- `<Sidebar />` defaults to the **Explorer** tab (`activeSidebarTab = 'explorer'`).
|
| 15 |
+
- Bottom panel is `'none'` unless the user navigated in with `?autoinstall=1` (in which case the terminal opens with the install command pre-loaded).
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/coding.tsx`](../../client/editor/coding.tsx)
|
| 20 |
+
- [`client/editor/Layout.tsx`](../../client/editor/Layout.tsx)
|
| 21 |
+
- [`client/editor/Sidebar.tsx`](../../client/editor/Sidebar.tsx)
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
← Back to the [editor index](./README.md).
|
| 26 |
+
|
docs/editor/02-editor-explorer-file.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 02 — Explorer file open
|
| 2 |
+
|
| 3 |
+
Clicking a file in the explorer (here `index.js`) opens it in a new tab. Monaco loads its contents, the LSP bridge spins up the matching language server, and diagnostics start streaming back into the gutter and the Problems pane.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Tabs are stored in `useFileStore` so a refresh restores the same set of open files.
|
| 14 |
+
- The active file's contents are persisted to IndexedDB on every keystroke (via the autosave timer in `coding.tsx`).
|
| 15 |
+
- `useLspClient(content, fileName, projectId)` opens a WebSocket to `/exocore/api/editor/lsp` and routes diagnostics back into the editor.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/Sidebar.tsx`](../../client/editor/Sidebar.tsx)
|
| 20 |
+
- [`client/editor/Tabs.tsx`](../../client/editor/Tabs.tsx)
|
| 21 |
+
- [`client/editor/SimpleCodeEditor.tsx`](../../client/editor/SimpleCodeEditor.tsx)
|
| 22 |
+
- [`client/editor/LspClient.tsx`](../../client/editor/LspClient.tsx)
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
← Back to the [editor index](./README.md).
|
| 27 |
+
|
docs/editor/03-editor-terminal.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 03 — Integrated terminal
|
| 2 |
+
|
| 3 |
+
`KittyTerminal.tsx` wraps `xterm.js` over a WebSocket pty bridge. Multiple tabs and split panes are supported; each tab is a separate node-pty process scoped to the project's working directory.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- WebSocket endpoint: `wss://<host>/exocore/api/editor/shell?projectId=…&tabId=…`.
|
| 14 |
+
- Server-side: `node-pty` spawns the user's preferred shell (`bash` on Linux containers, `pwsh` on Windows hosts) inside `projects/<projectId>/`.
|
| 15 |
+
- ANSI colors and unicode glyphs use the `Cascadia Code` / `JetBrainsMono Nerd Font` fallback chain.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/terminal/KittyTerminal.tsx`](../../client/terminal/KittyTerminal.tsx)
|
| 20 |
+
- [`routes/editor/shell.ts`](../../routes/editor/shell.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/04-editor-console.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 04 — Console pane
|
| 2 |
+
|
| 3 |
+
`ConsolePane.tsx` shows the project runtime's stdout / stderr stream. The Start / Stop button calls `routes/editor/runtime.ts` which spawns the configured `exocore.run` script and pipes the output back over Server-Sent Events.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Console is **runtime output** — it's separate from the terminal (which is an interactive pty).
|
| 14 |
+
- The desktop screenshot was captured mid-run; the mobile shot intentionally shows the empty pane (running the dev server inside the headless mobile profile detaches the page session).
|
| 15 |
+
- The Stop button sends `SIGTERM` first then `SIGKILL` after a 3s grace period.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/ConsolePane.tsx`](../../client/editor/ConsolePane.tsx)
|
| 20 |
+
- [`routes/editor/runtime.ts`](../../routes/editor/runtime.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/05-editor-webview.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 05 — Webview / preview (desktop only)
|
| 2 |
+
|
| 3 |
+
Whenever the runtime detects a freshly-bound HTTP server it surfaces the URL inside an embedded `<iframe>` (`Webview.tsx`). The mobile capture intentionally skips this frame — the embedded preview iframe consistently crashes the Chromium 138 mobile target during Puppeteer captures.
|
| 4 |
+
|
| 5 |
+
## Screenshot
|
| 6 |
+
|
| 7 |
+

|
| 8 |
+
|
| 9 |
+
> Mobile capture is intentionally skipped for this frame — the embedded preview iframe crashes the Chromium 138 mobile target during Puppeteer runs.
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Detection: a small line-scanner inside `runtime.ts` watches stdout for `Listening on http://…` / `Local: http://…` and feeds the URL to the webview pane.
|
| 14 |
+
- Reverse-proxy: `/exocore/api/editor/proxy/:projectId/*` rewrites HTML/JS to keep relative URLs pointing at the proxy.
|
| 15 |
+
- Tunnel button next to the URL spawns a Cloudflare quick-tunnel for sharing.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/Webview.tsx`](../../client/editor/Webview.tsx)
|
| 20 |
+
- [`routes/editor/runtime.ts`](../../routes/editor/runtime.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/06-editor-problems.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 06 — Problems pane
|
| 2 |
+
|
| 3 |
+
LSP diagnostics aggregated across every open file, grouped per-file with severity icons. Click a row to jump straight to the offending line.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Source: the same `useLspClient(...)` hook used by the editor gutter.
|
| 14 |
+
- Severity counts (errors / warnings) are also surfaced in the status bar.
|
| 15 |
+
- Click a diagnostic → `jumpToLine(line)` in `coding.tsx` selects the position in the active textarea and scrolls it into view.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/Layout.tsx`](../../client/editor/Layout.tsx)
|
| 20 |
+
- [`client/editor/LspClient.tsx`](../../client/editor/LspClient.tsx)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/07-sidebar-npm.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 07 — NPM sidebar pane
|
| 2 |
+
|
| 3 |
+
`NpmPane.tsx` searches the public registry, lists the project's installed dependencies, and (when an access token is present in localStorage) lets you publish a new version.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Search proxies through `/exocore/api/editor/npm/search?q=…` to avoid CORS and to allow private-registry overrides.
|
| 14 |
+
- Install / uninstall calls into `routes/editor/npm.ts` which runs `npm install --save` inside the project workspace.
|
| 15 |
+
- Publish flow uses the user's npm access token (stored client-side, never echoed back to the server logs).
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/NpmPane.tsx`](../../client/editor/NpmPane.tsx)
|
| 20 |
+
- [`routes/editor/npm.ts`](../../routes/editor/npm.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/08-sidebar-github.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 08 — GitHub sidebar pane
|
| 2 |
+
|
| 3 |
+
`GithubPane.tsx` covers the full editor-side Git workflow: OAuth login, clone, push / pull, branch switching, staged-files diff view, and per-file revert. See the dedicated [GitHub pane docs](../github/README.md) for the full feature matrix.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- OAuth happens through `/exocore/api/access/github/start` → `/callback`; the resulting token is stored encrypted on the backend, not in localStorage.
|
| 14 |
+
- All git operations are POSTed through `/exocore/api/editor/github/*` and shell out to `git` inside the project workspace.
|
| 15 |
+
- The diff view is a Monaco DiffEditor reused by the file-history modal (frame 11).
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/GithubPane.tsx`](../../client/editor/GithubPane.tsx)
|
| 20 |
+
- [`routes/editor/github.ts`](../../routes/editor/github.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/09-sidebar-drive.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 09 — Drive sidebar pane
|
| 2 |
+
|
| 3 |
+
`GDrivePane.tsx` picks files / folders from the connected Google Drive account and uploads project files back the other way. Drive auth is shared with the dashboard's [Cloud manager](../cloud/README.md).
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Picker uses the standard Google Picker API embedded inside an iframe modal — no extra OAuth round-trip if the user already linked Drive in the dashboard.
|
| 14 |
+
- Upload writes go through `/exocore/api/editor/gdrive/upload` (multipart) and inherit the user's per-folder ACL.
|
| 15 |
+
- Drive sessions are revoked from `Dashboard → Account → Connections`.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/GDrivePane.tsx`](../../client/editor/GDrivePane.tsx)
|
| 20 |
+
- [`routes/editor/gdrive.ts`](../../routes/editor/gdrive.ts)
|
| 21 |
+
- [`server/services/googleDrive`](../../server/services/googleDrive)
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
← Back to the [editor index](./README.md).
|
| 26 |
+
|
docs/editor/10-sidebar-ai.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 10 — AI sidebar pane
|
| 2 |
+
|
| 3 |
+
`ExocoreAI.tsx` is the in-editor chat assistant. It streams Server-Sent Events from `routes/editor/ai.ts`, supports file / terminal tool-use, and can target the built-in Exo provider or any user-configured OpenAI / Anthropic / OpenRouter key.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Chat tab — ChatGPT-style stream with code-blocks, file-attach, and an `Apply diff` button that pipes the suggested change through Monaco's diff merger.
|
| 14 |
+
- Setup tab — pick provider, paste API key (kept in localStorage), set system prompt, choose model.
|
| 15 |
+
- Tool-use is gated server-side: every read / write / shell call is logged in the project's `.exocore/ai-audit.log`.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/ExocoreAI.tsx`](../../client/editor/ExocoreAI.tsx)
|
| 20 |
+
- [`client/editor/ai/ExoSetupPanel.tsx`](../../client/editor/ai/ExoSetupPanel.tsx)
|
| 21 |
+
- [`client/editor/ai/RestSetupPanel.tsx`](../../client/editor/ai/RestSetupPanel.tsx)
|
| 22 |
+
- [`routes/editor/ai.ts`](../../routes/editor/ai.ts)
|
| 23 |
+
|
| 24 |
+
---
|
| 25 |
+
|
| 26 |
+
← Back to the [editor index](./README.md).
|
| 27 |
+
|
docs/editor/11-history-modal.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 11 — Code history modal
|
| 2 |
+
|
| 3 |
+
Local versioned snapshots of the active file (kept in IndexedDB). Each entry shows the timestamp, a unified diff against the previous snapshot, and a `Restore` button. Opened from the topbar `Code history` icon.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Snapshots are auto-captured on every successful autosave (debounced 800 ms after the last keystroke).
|
| 14 |
+
- Storage is per-file inside IndexedDB so it survives refresh, browser restart, and OAuth re-auth.
|
| 15 |
+
- Restore swaps the active editor buffer back to the snapshot's content but does **not** auto-save — the user has to commit the change explicitly.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/coding.tsx (HistoryCodePreview)`](../../client/editor/coding.tsx (HistoryCodePreview))
|
| 20 |
+
- [`client/editor/fileStore.ts`](../../client/editor/fileStore.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/12-settings-modal.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 12 — Settings modal
|
| 2 |
+
|
| 3 |
+
Theme picker, font family / size, key bindings, indent rules, word-wrap toggle, autosave delay, and the AI provider quick-link. Persisted to localStorage as well as synced to the user account when logged in.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Theme list is sourced from `editorThemes.ts` (60+ themes — One Dark Pro, Dracula, Tokyo Night, Replit, Catppuccin, Monokai, Synthwave 84, …).
|
| 14 |
+
- Settings are split into tabs — `General · Editor · AI · About`.
|
| 15 |
+
- Each numeric input is validated client-side before being written back to the store; out-of-range values fall back to the default.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/Settings.tsx`](../../client/editor/Settings.tsx)
|
| 20 |
+
- [`client/editor/editorThemes.ts`](../../client/editor/editorThemes.ts)
|
| 21 |
+
- [`client/editor/uiStore.ts`](../../client/editor/uiStore.ts)
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
← Back to the [editor index](./README.md).
|
| 26 |
+
|
docs/editor/13-settings-theme-changed.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 13 — Settings · theme switch
|
| 2 |
+
|
| 3 |
+
Same settings modal, captured immediately after switching to a different theme (Dracula on desktop). The swatches re-render live across the entire editor chrome — header, sidebar, status bar, even the modal itself — so users can preview without closing the dialog.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Theme application path: `setTheme(id)` → `useLegacyEditorStore` → CSS variables on `<body>` → re-render of every styled component.
|
| 14 |
+
- The Monaco / CodeMirror grammars are restyled in the same tick using the matching token-color map from `editorThemes.ts`.
|
| 15 |
+
- Closing the modal commits the change; pressing **Cancel** rolls back to the previous theme via the snapshot taken on open.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/Settings.tsx`](../../client/editor/Settings.tsx)
|
| 20 |
+
- [`client/editor/editorThemes.ts`](../../client/editor/editorThemes.ts)
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
← Back to the [editor index](./README.md).
|
| 25 |
+
|
docs/editor/14-sidebar-pylib.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 14 — PyLib pane (Python project)
|
| 2 |
+
|
| 3 |
+
Switching to the seeded `exorepo-py` project re-labels the package tab to **PyPI** and renders `PyLibrary.tsx` in place of `NpmPane.tsx`. The same UX patterns apply (search → install → list installed) but the backing store is the project's `requirements.txt` / `pyproject.toml`.
|
| 4 |
+
|
| 5 |
+
## Screenshots
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What it does
|
| 12 |
+
|
| 13 |
+
- Package-tab swap is driven by `getPackageManagerLabel(projectLanguage)` — the same dispatcher routes Rust → Cargo, Go → go modules, Java → Maven, etc.
|
| 14 |
+
- Installs go through `pip install --user` inside the project's virtualenv (created on first install).
|
| 15 |
+
- Search hits `https://pypi.org/pypi/<name>/json` server-side and is cached for 5 minutes per name.
|
| 16 |
+
|
| 17 |
+
## Source files
|
| 18 |
+
|
| 19 |
+
- [`client/editor/PyLibrary.tsx`](../../client/editor/PyLibrary.tsx)
|
| 20 |
+
- [`client/editor/PackagesPane.tsx`](../../client/editor/PackagesPane.tsx)
|
| 21 |
+
- [`routes/editor/pylib.ts`](../../routes/editor/pylib.ts)
|
| 22 |
+
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
← Back to the [editor index](./README.md).
|
| 26 |
+
|
docs/editor/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Editor / IDE — `/exocore/editor`
|
| 2 |
+
|
| 3 |
+
The Exocore IDE is a full Monaco-and-CodeMirror powered code editor with file
|
| 4 |
+
explorer, integrated terminal (xterm + node-pty), package managers, AI
|
| 5 |
+
assistant, GitHub pane, Drive pane, language servers, and a webview for live
|
| 6 |
+
previews.
|
| 7 |
+
|
| 8 |
+
Entry point: [`client/editor/coding.tsx`](../../client/editor/coding.tsx).
|
| 9 |
+
Layout shell: [`client/editor/Layout.tsx`](../../client/editor/Layout.tsx).
|
| 10 |
+
|
| 11 |
+
Every screenshot below was captured by
|
| 12 |
+
[`scripts/capture-editor.ts`](../../scripts/capture-editor.ts) against a freshly
|
| 13 |
+
seeded `exorepo-demo` (Node) project. Mobile shots come from a 414×896 iPhone
|
| 14 |
+
profile; desktop shots from a 1440×900 Chromium 138 window.
|
| 15 |
+
|
| 16 |
+
## Per-frame docs (one MD per screenshot)
|
| 17 |
+
|
| 18 |
+
Each frame in the gallery below has its own dedicated walkthrough — click
|
| 19 |
+
the title to jump straight to it on GitHub:
|
| 20 |
+
|
| 21 |
+
| # | Frame | Doc |
|
| 22 |
+
|---|-------|-----|
|
| 23 |
+
| 00 | Panel gate | [`00-panel-gate.md`](./00-panel-gate.md) |
|
| 24 |
+
| 01 | Default editor view | [`01-editor-default.md`](./01-editor-default.md) |
|
| 25 |
+
| 02 | Explorer · open file | [`02-editor-explorer-file.md`](./02-editor-explorer-file.md) |
|
| 26 |
+
| 03 | Integrated terminal | [`03-editor-terminal.md`](./03-editor-terminal.md) |
|
| 27 |
+
| 04 | Console pane | [`04-editor-console.md`](./04-editor-console.md) |
|
| 28 |
+
| 05 | Webview / preview *(desktop)* | [`05-editor-webview.md`](./05-editor-webview.md) |
|
| 29 |
+
| 06 | Problems pane | [`06-editor-problems.md`](./06-editor-problems.md) |
|
| 30 |
+
| 07 | NPM sidebar | [`07-sidebar-npm.md`](./07-sidebar-npm.md) |
|
| 31 |
+
| 08 | GitHub sidebar | [`08-sidebar-github.md`](./08-sidebar-github.md) |
|
| 32 |
+
| 09 | Drive sidebar | [`09-sidebar-drive.md`](./09-sidebar-drive.md) |
|
| 33 |
+
| 10 | AI sidebar | [`10-sidebar-ai.md`](./10-sidebar-ai.md) |
|
| 34 |
+
| 11 | Code-history modal | [`11-history-modal.md`](./11-history-modal.md) |
|
| 35 |
+
| 12 | Settings modal | [`12-settings-modal.md`](./12-settings-modal.md) |
|
| 36 |
+
| 13 | Settings · theme switch | [`13-settings-theme-changed.md`](./13-settings-theme-changed.md) |
|
| 37 |
+
| 14 | PyLib pane (Python project) | [`14-sidebar-pylib.md`](./14-sidebar-pylib.md) |
|
| 38 |
+
|
| 39 |
+
## Layout
|
| 40 |
+
|
| 41 |
+
```
|
| 42 |
+
┌─ Title bar ───────────────────────────────────────────────┐
|
| 43 |
+
│ Exocore · project / file path · 🔍 cmd-K · 🌗 theme │
|
| 44 |
+
├─ Sidebar tabs ─────┬─ Tabs (open files) ──────────────────┤
|
| 45 |
+
│ ▾ Explorer (📁) │ index.ts ✕ utils.ts ✕ README.md ✕ │
|
| 46 |
+
│ project tree ├─ Editor pane ────────────────────────┤
|
| 47 |
+
│ ▾ NPM (📦) │ Monaco (or CodeMirror per language) │
|
| 48 |
+
│ ▾ GitHub (🌿) │ with LSP completion / diagnostics │
|
| 49 |
+
│ ▾ Drive (☁) │ │
|
| 50 |
+
│ ▾ AI (🤖) │ │
|
| 51 |
+
├────────────────── ├─ Bottom panel ───────────────────────┤
|
| 52 |
+
│ │ [problems] [console] [terminal] [webview]
|
| 53 |
+
└────────────────────┴───────────────────────────────────────┘
|
| 54 |
+
Status bar — git branch · LSP · runtime · cursor
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
`SidebarTab = 'explorer' | 'npm' | 'github' | 'drive' | 'ai'`
|
| 58 |
+
`BottomPanel = 'problems' | 'console' | 'terminal' | 'webview' | 'none'`
|
| 59 |
+
|
| 60 |
+
## Walkthrough
|
| 61 |
+
|
| 62 |
+
### 00 — Panel gate
|
| 63 |
+
|
| 64 |
+
The first thing a fresh browser sees is the per-machine panel-devs gate
|
| 65 |
+
(`/exocore/`). Until a panel admin (e.g. `Choruyt`) is registered, the rest
|
| 66 |
+
of the site is locked behind this form.
|
| 67 |
+
|
| 68 |
+
| Desktop | Mobile |
|
| 69 |
+
|---------|--------|
|
| 70 |
+
|  |  |
|
| 71 |
+
|
| 72 |
+
### 01 — Default editor view
|
| 73 |
+
|
| 74 |
+
After login the editor opens onto the project root — Monaco shows a welcome
|
| 75 |
+
banner and the explorer lists the seeded files.
|
| 76 |
+
|
| 77 |
+
| Desktop | Mobile |
|
| 78 |
+
|---------|--------|
|
| 79 |
+
|  |  |
|
| 80 |
+
|
| 81 |
+
### 02 — Explorer file open
|
| 82 |
+
|
| 83 |
+
Clicking a file in the explorer (here `index.js`) opens it in a new tab and
|
| 84 |
+
streams diagnostics from the matching LSP.
|
| 85 |
+
|
| 86 |
+
| Desktop | Mobile |
|
| 87 |
+
|---------|--------|
|
| 88 |
+
|  |  |
|
| 89 |
+
|
| 90 |
+
### 03 — Integrated terminal
|
| 91 |
+
|
| 92 |
+
`KittyTerminal.tsx` wraps `xterm.js` over a WebSocket pty bridge
|
| 93 |
+
(`routes/editor/shell.ts`). Multiple tabs and split panes are supported.
|
| 94 |
+
|
| 95 |
+
| Desktop | Mobile |
|
| 96 |
+
|---------|--------|
|
| 97 |
+
|  |  |
|
| 98 |
+
|
| 99 |
+
### 04 — Console pane
|
| 100 |
+
|
| 101 |
+
`ConsolePane.tsx` shows the project's runtime stdout/stderr from
|
| 102 |
+
`routes/editor/runtime.ts`. The Start/Stop button spawns the configured
|
| 103 |
+
`exocore.run` script. The desktop screenshot was captured mid-run; mobile
|
| 104 |
+
shows the empty pane (running the dev server inside the headless mobile
|
| 105 |
+
profile detaches the page session).
|
| 106 |
+
|
| 107 |
+
| Desktop | Mobile |
|
| 108 |
+
|---------|--------|
|
| 109 |
+
|  |  |
|
| 110 |
+
|
| 111 |
+
### 05 — Webview / preview (desktop only)
|
| 112 |
+
|
| 113 |
+
When the runtime detects a bound HTTP server it surfaces the URL inside an
|
| 114 |
+
embedded `<iframe>` (`Webview.tsx`). The mobile capture intentionally skips
|
| 115 |
+
this — the embedded preview iframe consistently crashes the Chromium 138
|
| 116 |
+
mobile target.
|
| 117 |
+
|
| 118 |
+

|
| 119 |
+
|
| 120 |
+
### 06 — Problems pane
|
| 121 |
+
|
| 122 |
+
LSP diagnostics grouped per-file. Click a row to jump straight to the
|
| 123 |
+
offending line.
|
| 124 |
+
|
| 125 |
+
| Desktop | Mobile |
|
| 126 |
+
|---------|--------|
|
| 127 |
+
|  |  |
|
| 128 |
+
|
| 129 |
+
### 07 — NPM sidebar pane
|
| 130 |
+
|
| 131 |
+
`NpmPane.tsx` searches the public registry, lists installed dependencies, and
|
| 132 |
+
publishes packages using a browser-stored access token.
|
| 133 |
+
|
| 134 |
+
| Desktop | Mobile |
|
| 135 |
+
|---------|--------|
|
| 136 |
+
|  |  |
|
| 137 |
+
|
| 138 |
+
### 08 — GitHub sidebar pane
|
| 139 |
+
|
| 140 |
+
`GithubPane.tsx` covers OAuth auth, clone, push/pull, branch switching, and
|
| 141 |
+
the staged-files diff view. Dedicated docs: [GitHub pane](../github/README.md).
|
| 142 |
+
|
| 143 |
+
| Desktop | Mobile |
|
| 144 |
+
|---------|--------|
|
| 145 |
+
|  |  |
|
| 146 |
+
|
| 147 |
+
### 09 — Drive sidebar pane
|
| 148 |
+
|
| 149 |
+
`GDrivePane.tsx` picks files/folders from the connected Google Drive account
|
| 150 |
+
and uploads project files back the other way.
|
| 151 |
+
|
| 152 |
+
| Desktop | Mobile |
|
| 153 |
+
|---------|--------|
|
| 154 |
+
|  |  |
|
| 155 |
+
|
| 156 |
+
### 10 — AI sidebar pane
|
| 157 |
+
|
| 158 |
+
`ExocoreAI.tsx` is the chat assistant with file/terminal tool-use, streaming
|
| 159 |
+
SSE responses through `routes/editor/ai.ts`.
|
| 160 |
+
|
| 161 |
+
| Desktop | Mobile |
|
| 162 |
+
|---------|--------|
|
| 163 |
+
|  |  |
|
| 164 |
+
|
| 165 |
+
### 11 — Code history modal
|
| 166 |
+
|
| 167 |
+
Local versioned snapshots of the active file (kept in IndexedDB) — diff +
|
| 168 |
+
restore. Opened from the topbar `Code history` icon.
|
| 169 |
+
|
| 170 |
+
| Desktop | Mobile |
|
| 171 |
+
|---------|--------|
|
| 172 |
+
|  |  |
|
| 173 |
+
|
| 174 |
+
### 12 — Settings modal
|
| 175 |
+
|
| 176 |
+
Theme picker, font size, key bindings, indent rules, etc.
|
| 177 |
+
|
| 178 |
+
| Desktop | Mobile |
|
| 179 |
+
|---------|--------|
|
| 180 |
+
|  |  |
|
| 181 |
+
|
| 182 |
+
### 13 — Settings · theme switch
|
| 183 |
+
|
| 184 |
+
Same modal after switching to a different theme (Dracula on desktop). The
|
| 185 |
+
swatches re-render live across the editor and the surrounding chrome.
|
| 186 |
+
|
| 187 |
+
| Desktop | Mobile |
|
| 188 |
+
|---------|--------|
|
| 189 |
+
|  |  |
|
| 190 |
+
|
| 191 |
+
### 14 — PyLib pane (Python project)
|
| 192 |
+
|
| 193 |
+
Switching to the seeded `exorepo-py` project re-labels the package tab to
|
| 194 |
+
**PyPI** and renders `PyLibrary.tsx` in place of `NpmPane.tsx`.
|
| 195 |
+
|
| 196 |
+
| Desktop | Mobile |
|
| 197 |
+
|---------|--------|
|
| 198 |
+
|  |  |
|
| 199 |
+
|
| 200 |
+
## Sidebar tabs reference
|
| 201 |
+
|
| 202 |
+
| Tab | Component | What it does |
|
| 203 |
+
|-----|-----------|--------------|
|
| 204 |
+
| **Explorer** | `Sidebar.tsx` | Tree view, drag-and-drop, context menu, multi-select |
|
| 205 |
+
| **NPM** | `NpmPane.tsx` | Search npm, install / uninstall, see installed deps |
|
| 206 |
+
| **GitHub** | `GithubPane.tsx` | See [GitHub docs](../github/README.md) |
|
| 207 |
+
| **Drive** | `GDrivePane.tsx` | Pick / upload from Google Drive |
|
| 208 |
+
| **AI** | `ExocoreAI.tsx` | Chat assistant with file/terminal tool-use |
|
| 209 |
+
|
| 210 |
+
The package tab swaps based on project language via
|
| 211 |
+
`PackagesPane.tsx` — `NPM` for Node, `PyPI` for Python, `Cargo` for Rust,
|
| 212 |
+
`Go`, `Maven`, `Gems`, `Composer`, `NuGet`, `Hex`, `Cabal`, `SPM`, …
|
| 213 |
+
|
| 214 |
+
## Bottom panels reference
|
| 215 |
+
|
| 216 |
+
| Panel | Source | Notes |
|
| 217 |
+
|----------|--------------------|-------|
|
| 218 |
+
| Problems | LSP diagnostics | grouped per-file, click to jump |
|
| 219 |
+
| Console | `ConsolePane.tsx` | runtime stdout / stderr |
|
| 220 |
+
| Terminal | `KittyTerminal.tsx` + `xterm` | pty-backed, supports tabs |
|
| 221 |
+
| Webview | `Webview.tsx` | proxied iframe of the running project |
|
| 222 |
+
|
| 223 |
+
## Languages
|
| 224 |
+
|
| 225 |
+
`editor/language.tsx` registers Monaco grammars + CodeMirror extensions for:
|
| 226 |
+
|
| 227 |
+
- TypeScript / JavaScript (with JSX)
|
| 228 |
+
- Python · PHP · Rust · C · C++
|
| 229 |
+
- HTML · CSS · JSON · Markdown
|
| 230 |
+
- Plus syntax highlight via Prism for less common languages
|
| 231 |
+
(Go, Ruby, YAML, TOML, SQL, Shell, …) inside chat code blocks.
|
| 232 |
+
|
| 233 |
+
LSP bridge: WebSocket to `routes/editor/_lspBridge.ts`, which spawns the
|
| 234 |
+
appropriate language server (`pyright`, `tsserver`, `rust-analyzer`,
|
| 235 |
+
`clangd`, …) per project.
|
| 236 |
+
|
| 237 |
+
## Command palette
|
| 238 |
+
|
| 239 |
+
`Ctrl + K` opens [`CommandPalette.tsx`](../../client/editor/CommandPalette.tsx)
|
| 240 |
+
with fuzzy-search across files, commands, settings, and AI prompts.
|
| 241 |
+
|
| 242 |
+
## AI assistant
|
| 243 |
+
|
| 244 |
+
Two-pane component (`ExocoreAI.tsx`):
|
| 245 |
+
|
| 246 |
+
- **Chat** — ChatGPT-style stream with code-blocks, file-attach, and
|
| 247 |
+
"apply this diff" button.
|
| 248 |
+
- **Setup** — pick provider (Exo-built-in / OpenAI / Anthropic / OpenRouter)
|
| 249 |
+
via [`ai/ExoSetupPanel.tsx`](../../client/editor/ai/ExoSetupPanel.tsx) +
|
| 250 |
+
[`ai/RestSetupPanel.tsx`](../../client/editor/ai/RestSetupPanel.tsx).
|
| 251 |
+
|
| 252 |
+
Server bridge: [`routes/editor/ai.ts`](../../routes/editor/ai.ts) (proxies to
|
| 253 |
+
the configured provider, streams SSE back).
|
| 254 |
+
|
| 255 |
+
## Mobile layout
|
| 256 |
+
|
| 257 |
+
On viewports `≤ 768 px` the desktop sidebar collapses into an overlay drawer
|
| 258 |
+
that slides in over the editor, and the bottom panel becomes a full-screen
|
| 259 |
+
sheet pinned to a 5-button bottom nav (`.m-nav-btn`): **Files · Errors ·
|
| 260 |
+
Console · Terminal · Preview**. Tap the active button again to close the
|
| 261 |
+
drawer / sheet.
|
| 262 |
+
|
| 263 |
+
The Files button reveals the same five sidebar tabs from desktop
|
| 264 |
+
(Explorer · NPM · Git · Drive · AI) plus a close pill.
|
| 265 |
+
|
| 266 |
+
## Backend routes
|
| 267 |
+
|
| 268 |
+
| Route | Source |
|
| 269 |
+
|--------------------------------------|--------|
|
| 270 |
+
| `/exocore/api/editor/coding` | File CRUD |
|
| 271 |
+
| `/exocore/api/editor/runtime` | Spawn / stop project process |
|
| 272 |
+
| `/exocore/api/editor/shell` | WS pty bridge for the terminal |
|
| 273 |
+
| `/exocore/api/editor/npm` | Search / install npm |
|
| 274 |
+
| `/exocore/api/editor/pylib` | pip equivalent |
|
| 275 |
+
| `/exocore/api/editor/deps` | Cross-package summary |
|
| 276 |
+
| `/exocore/api/editor/templates` | Template catalog (used by `capture-editor.ts` to seed `exorepo-demo` + `exorepo-py`) |
|
| 277 |
+
| `/exocore/api/editor/github` | Git ops |
|
| 278 |
+
| `/exocore/api/editor/gdrive` | Drive picker |
|
| 279 |
+
| `/exocore/api/editor/ai` | AI proxy |
|
| 280 |
+
| `/exocore/api/editor/projects` | Project CRUD |
|
| 281 |
+
|
| 282 |
+
## Re-capturing the screenshots
|
| 283 |
+
|
| 284 |
+
```bash
|
| 285 |
+
# Both viewports (default).
|
| 286 |
+
EXOCORE_CAPTURE=1 npx tsx exocore-web/scripts/capture-editor.ts
|
| 287 |
+
|
| 288 |
+
# Desktop only.
|
| 289 |
+
VIEWPORT=desktop EXOCORE_CAPTURE=1 npx tsx exocore-web/scripts/capture-editor.ts
|
| 290 |
+
|
| 291 |
+
# Mobile only.
|
| 292 |
+
VIEWPORT=mobile EXOCORE_CAPTURE=1 npx tsx exocore-web/scripts/capture-editor.ts
|
| 293 |
+
|
| 294 |
+
# Top-up the two mobile frames the main pass occasionally drops
|
| 295 |
+
# (theme switch + python pylib). Each runs in its own browser context.
|
| 296 |
+
EXOCORE_CAPTURE=1 npx tsx exocore-web/scripts/capture-editor-mobile-fix.ts
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
`EXOCORE_CAPTURE=1` raises the global rate-limit so the capture script can
|
| 300 |
+
hit `/login`, the templates SSE endpoint, and a dozen editor routes in
|
| 301 |
+
quick succession. The shipped `Start application` workflow already sets
|
| 302 |
+
this; production deploys do not.
|
docs/github/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GitHub integration
|
| 2 |
+
|
| 3 |
+
Two surfaces:
|
| 4 |
+
|
| 5 |
+
1. **Dashboard card** — shows org connection status + a "Visit Org" link to
|
| 6 |
+
`https://github.com/Exocore-Organization`.
|
| 7 |
+
2. **`GithubManager` modal** ([`home/GithubManager.tsx`](../../client/home/GithubManager.tsx))
|
| 8 |
+
— full OAuth dance, repo browser, clone-into-project flow.
|
| 9 |
+
3. **IDE Github pane** ([`editor/GithubPane.tsx`](../../client/editor/GithubPane.tsx))
|
| 10 |
+
— sidebar inside the editor with branch / commit / push / pull buttons.
|
| 11 |
+
|
| 12 |
+
Backend route: [`routes/editor/github.ts`](../../routes/editor/github.ts).
|
| 13 |
+
|
| 14 |
+
## Visual
|
| 15 |
+
|
| 16 |
+
The Dashboard capture below shows the GitHub card on the social grid; the
|
| 17 |
+
modal opens on top with the same dark-yellow theme.
|
| 18 |
+
|
| 19 |
+
| Desktop | Mobile |
|
| 20 |
+
|---------|--------|
|
| 21 |
+
|  |  |
|
| 22 |
+
|
| 23 |
+
## Manager modal sections
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
┌─ Connected account ───────────────────────────────┐
|
| 27 |
+
│ @octocat · 142 repos · 8 orgs │
|
| 28 |
+
│ scopes: repo, read:org, workflow │
|
| 29 |
+
│ [ Disconnect ] │
|
| 30 |
+
├─ Browse repos ────────────────────────────────────┤
|
| 31 |
+
│ search · filter (orgs / forks / archived) │
|
| 32 |
+
│ ┌─ row ────────────────────────────────────────┐ │
|
| 33 |
+
│ │ ⭐ exocore-web · TypeScript · 1 day ago │ │
|
| 34 |
+
│ │ [ Open in IDE ] [ Clone to project ] [ … ] │ │
|
| 35 |
+
│ └──────────────────────────────────────────────┘ │
|
| 36 |
+
├─ Pending PRs / Issues ────────────────────────────┤
|
| 37 |
+
│ inline preview cards │
|
| 38 |
+
└───────────────────────────────────────────────────┘
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## OAuth flow
|
| 42 |
+
|
| 43 |
+
1. Click **Connect GitHub** → redirect to GitHub OAuth consent
|
| 44 |
+
(`scope=repo,read:org,workflow`).
|
| 45 |
+
2. GitHub redirects to `/exocore/api/editor/github/callback`.
|
| 46 |
+
3. Server stores access token in encrypted user record.
|
| 47 |
+
4. UI re-fetches `github.status` and unlocks browse / clone actions.
|
| 48 |
+
|
| 49 |
+
## IDE Github pane
|
| 50 |
+
|
| 51 |
+
Lives in the left sidebar (`tab="github"`). Provides:
|
| 52 |
+
|
| 53 |
+
- **Branch picker** — switch / create / delete branches.
|
| 54 |
+
- **Status** — staged / unstaged / untracked files with diff preview.
|
| 55 |
+
- **Commit composer** — message input + sign-off toggle.
|
| 56 |
+
- **Push / pull** with conflict UI.
|
| 57 |
+
- **Open repo on github.com** chip.
|
| 58 |
+
|
| 59 |
+
## RPC channels
|
| 60 |
+
|
| 61 |
+
| Channel | Purpose |
|
| 62 |
+
|---------|---------|
|
| 63 |
+
| `github.status` | Returns `{ connected, login, scopes, repoCount }` |
|
| 64 |
+
| `github.repos` | Paginated repo list (with filters) |
|
| 65 |
+
| `github.clone` | Clones repo into a new Project Node |
|
| 66 |
+
| `github.commit` | Stages + commits selected paths |
|
| 67 |
+
| `github.push` | Pushes current branch |
|
| 68 |
+
| `github.pull` | Fetch + merge upstream |
|
| 69 |
+
| `github.disconnect` | Revoke OAuth |
|
docs/leaderboard/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Leaderboard — `/exocore/leaderboard`
|
| 2 |
+
|
| 3 |
+
Top members across the Exocore server, ranked across three dimensions you can
|
| 4 |
+
toggle in the header. Component:
|
| 5 |
+
[`client/leaderboard/Leaderboard.tsx`](../../client/leaderboard/Leaderboard.tsx).
|
| 6 |
+
|
| 7 |
+
| Desktop | Mobile |
|
| 8 |
+
|---------|--------|
|
| 9 |
+
|  |  |
|
| 10 |
+
|
| 11 |
+
## What you see in the capture
|
| 12 |
+
|
| 13 |
+
The capture run shows the **live data** on this server right now (4 users):
|
| 14 |
+
|
| 15 |
+
| Rank | Avatar | Nickname | Username | XP | Lv |
|
| 16 |
+
|------|--------|----------|----------|----|----|
|
| 17 |
+
| 🥇 1 | KA | **kaith** | `@@skibide` | 37 XP | Lv 1 — Beginner |
|
| 18 |
+
| 🥈 2 | CU | **Cute** | `@@choruyt` | 28 XP | Lv 1 — Beginner |
|
| 19 |
+
| 🥉 3 | CO | **Costanos** | `@@exocore` | 6 XP | Lv 0 — Beginner |
|
| 20 |
+
| 4 | SA | **samir** | `@@samir` | 0 XP | Lv 0 — Beginner |
|
| 21 |
+
|
| 22 |
+
The top 3 are rendered as the **podium** (large cards with halo / glow), the
|
| 23 |
+
rest fall into a flat list underneath.
|
| 24 |
+
|
| 25 |
+
## Header controls
|
| 26 |
+
|
| 27 |
+
```
|
| 28 |
+
[ XP | Level | Achievements ] [ search… ] [ Top 50 ▾ ]
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
- **XP / Level / Achievements** — switches the sort dimension.
|
| 32 |
+
- **search** — fuzzy filter on `nickname` and `@username`.
|
| 33 |
+
- **Top N** — limit dropdown: `10`, `50`, `100`, `All`.
|
| 34 |
+
|
| 35 |
+
## RPC
|
| 36 |
+
|
| 37 |
+
```ts
|
| 38 |
+
rpc.call("xp.leaderboard", {
|
| 39 |
+
sort: "xp" | "level" | "achievements",
|
| 40 |
+
limit: 10 | 50 | 100 | -1,
|
| 41 |
+
query: string
|
| 42 |
+
})
|
| 43 |
+
↪︎ { entries: LeaderboardEntry[], total: number, generatedAt: number }
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Backend handler lives at
|
| 47 |
+
[`Exocore-Backend/src/auth/leaderboard.ts`](../../Exocore-Backend/src/auth/leaderboard.ts);
|
| 48 |
+
it pulls from the encrypted user store + computes XP-to-level using the
|
| 49 |
+
shared formula in `xpService.ts`.
|
| 50 |
+
|
| 51 |
+
## Click behaviour
|
| 52 |
+
|
| 53 |
+
Clicking any card or row calls `navigate(/u/${entry.username})` →
|
| 54 |
+
[Profile](../profile/README.md).
|
| 55 |
+
|
| 56 |
+
## Empty / loading
|
| 57 |
+
|
| 58 |
+
| State | Render |
|
| 59 |
+
|-------|--------|
|
| 60 |
+
| Loading | spinner + "Crunching ranks…" |
|
| 61 |
+
| No matches | "No members match `<query>`." |
|
| 62 |
+
| API down | red banner + retry button |
|
docs/panel/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Panel Devs Gate
|
| 2 |
+
|
| 3 |
+
Every request that reaches `/exocore/*` first hits the **panel-devs gate**
|
| 4 |
+
(see [`client/access/panel-devs.tsx`](../../client/access/panel-devs.tsx) +
|
| 5 |
+
[`server/lib/devGate.ts`](../../server/lib/devGate.ts)).
|
| 6 |
+
|
| 7 |
+
It guards the workspace with a **single master account per server**, persisted
|
| 8 |
+
to `client/access/devs.json` as a salted SHA-256 hash. No request is allowed
|
| 9 |
+
through to the backend hub until you hold a valid session token.
|
| 10 |
+
|
| 11 |
+
## States
|
| 12 |
+
|
| 13 |
+
| State | When it shows | Visual |
|
| 14 |
+
|-------|--------------|--------|
|
| 15 |
+
| `setup` | First time the panel is opened on this server (`devs.json` is missing) | Yellow `FIRST RUN` badge, three input form (user / pass / confirm) |
|
| 16 |
+
| `login` | `devs.json` exists, no valid session token in IndexedDB | Green `READY` badge, two input form (user / pass) |
|
| 17 |
+
| `authenticated` | Valid session token found | Forwards to the SPA, also paints the IP-change alarm banner |
|
| 18 |
+
|
| 19 |
+
## Desktop
|
| 20 |
+
|
| 21 |
+
### Setup (first run)
|
| 22 |
+
|
| 23 |
+

|
| 24 |
+
|
| 25 |
+
### After unlock (returns "Welcome back" on subsequent visits)
|
| 26 |
+
|
| 27 |
+
The same screen appears with a green **READY** badge and two inputs after the
|
| 28 |
+
master account is created.
|
| 29 |
+
|
| 30 |
+
## Mobile
|
| 31 |
+
|
| 32 |
+

|
| 33 |
+
|
| 34 |
+
## Security highlights
|
| 35 |
+
|
| 36 |
+
- One account per server — re-create by deleting `devs.json` on the host.
|
| 37 |
+
- Server APIs are sealed behind this gate; un-tokenised hits are rejected.
|
| 38 |
+
- Brute-force lockout: too many failed RPC `devAccess.login` calls returns
|
| 39 |
+
`429` with a `retryAfterSec` countdown that the UI displays under the form.
|
| 40 |
+
- IP-change alarm — when your session's IP differs from the last known one,
|
| 41 |
+
a banner is rendered across every panel page (`PanelIpAlarm` in the same
|
| 42 |
+
file).
|
| 43 |
+
- Session tokens are stored client-side in **idb-keyval** (IndexedDB) under
|
| 44 |
+
`exocore_panel_token`, so they survive reloads but never touch
|
| 45 |
+
`localStorage`.
|
| 46 |
+
|
| 47 |
+
## RPC channels used
|
| 48 |
+
|
| 49 |
+
| Channel | Purpose |
|
| 50 |
+
|---------|---------|
|
| 51 |
+
| `devAccess.status` | Returns `{ initialized: boolean }` to pick `setup` vs `login` |
|
| 52 |
+
| `devAccess.setup` | Creates `devs.json` and returns the first session token |
|
| 53 |
+
| `devAccess.login` | Validates credentials → returns a session token |
|
| 54 |
+
| `devAccess.me` | Quick "is this token still valid?" check |
|
| 55 |
+
| `devAccess.session`| Returns session metadata (last IP, UA, expiry) |
|
| 56 |
+
| `devAccess.logout` | Revokes the token server-side |
|
| 57 |
+
|
| 58 |
+
(Implemented in
|
| 59 |
+
[`server/rpc/devAccessHandlers.ts`](../../server/rpc/devAccessHandlers.ts).)
|
docs/profile/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Profile — `/exocore/u/:username`
|
| 2 |
+
|
| 3 |
+
The Profile view is half **Twitter-style timeline** and half **stalker page**.
|
| 4 |
+
Same component renders for visitors and the logged-in user; the action row
|
| 5 |
+
swaps between *post composer* (own page) and *Friend / Message* buttons
|
| 6 |
+
(stalk mode).
|
| 7 |
+
|
| 8 |
+
Source: [`client/profile/Profile.tsx`](../../client/profile/Profile.tsx)
|
| 9 |
+
(~600 LOC, also wires post creation, comments, reactions and friend
|
| 10 |
+
add/remove).
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## Stalking another user — `@skibide` (3 posts, ADMIN role)
|
| 15 |
+
|
| 16 |
+
| Desktop | Mobile |
|
| 17 |
+
|---------|--------|
|
| 18 |
+
|  |  |
|
| 19 |
+
|
| 20 |
+
## Top contributor — `@exocore` (the OWNER)
|
| 21 |
+
|
| 22 |
+
| Desktop | Mobile |
|
| 23 |
+
|---------|--------|
|
| 24 |
+
|  |  |
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Anatomy
|
| 29 |
+
|
| 30 |
+
```
|
| 31 |
+
┌─ ← Back to Dashboard ──────────────────────────────┐
|
| 32 |
+
│ Cover banner (gradient) ──────────────────────────│
|
| 33 |
+
│ ┌──────────┐ │
|
| 34 |
+
│ │ Avatar │ Nickname [ROLE chip] │
|
| 35 |
+
│ └──────────┘ @username │
|
| 36 |
+
│ Lv N · Title · Posts · Friends · 🏳 country │
|
| 37 |
+
│ ID xxx · PLAN xxx · STATUS verified │
|
| 38 |
+
│ [bio one-liner] │
|
| 39 |
+
│ [XP progress bar N XP · K to Lv N+1] │
|
| 40 |
+
│ [achievement chips] │
|
| 41 |
+
├─ Post composer (own only) ─────────────────────────│
|
| 42 |
+
│ textarea + 📎 image + [Post] │
|
| 43 |
+
├─ Feed (most-recent first) ─────────────────────────│
|
| 44 |
+
│ ┌── post card ──────────────────────────────────┐ │
|
| 45 |
+
│ │ avatar nickname │ │
|
| 46 |
+
│ │ N {timeAgo} │ │
|
| 47 |
+
│ │ markdown body (with code blocks) │ │
|
| 48 |
+
│ │ optional image │ │
|
| 49 |
+
│ │ ❤ 12 😂 3 💀 1 💬 N comments │ │
|
| 50 |
+
│ │ [reactions row · click to toggle] │ │
|
| 51 |
+
│ │ [comment composer + thread] │ │
|
| 52 |
+
│ └────────────────────────────────────────────────┘ │
|
| 53 |
+
│ … more posts … │
|
| 54 |
+
└────────────────────────────────────────────────────┘
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
## RPC channels
|
| 58 |
+
|
| 59 |
+
| Channel | Purpose |
|
| 60 |
+
|---------|---------|
|
| 61 |
+
| `posts.profile` | Fetch user + their posts |
|
| 62 |
+
| `posts.create` | New post (text + optional file) |
|
| 63 |
+
| `posts.delete` | Soft-delete own post |
|
| 64 |
+
| `posts.comment` | Append comment to a post |
|
| 65 |
+
| `posts.react` | Toggle reaction emoji |
|
| 66 |
+
| `social.friend` | Add / remove friend (`action`: `'add' \| 'remove'`) |
|
| 67 |
+
| `xp.catalog` | Achievement chip metadata (icon + label) |
|
| 68 |
+
|
| 69 |
+
## Stalk-mode buttons
|
| 70 |
+
|
| 71 |
+
When the URL `:username` ≠ the logged-in user's `@username`, the composer is
|
| 72 |
+
hidden and these chips appear under the header:
|
| 73 |
+
|
| 74 |
+
- **+ Friend** (or **✓ Friends · Remove** if already mutual)
|
| 75 |
+
- **💬 Chat** — opens a DM directly inside `SocialPanel` (sets `activeDM` and
|
| 76 |
+
switches the bottom-bar tab to `'dms'`)
|
| 77 |
+
- **🚫 Block** (only visible for moderators / owner role)
|
| 78 |
+
|
| 79 |
+
## Posts schema (server side)
|
| 80 |
+
|
| 81 |
+
```ts
|
| 82 |
+
interface Post {
|
| 83 |
+
id: string; // post_<ms>_<rand>
|
| 84 |
+
ts: number; // epoch ms
|
| 85 |
+
author: string; // "@exocore"
|
| 86 |
+
imageUrl?: string | null;
|
| 87 |
+
imageFileId?: string | null;
|
| 88 |
+
text: string; // markdown
|
| 89 |
+
comments: Comment[];
|
| 90 |
+
reactions: Record<string, string[]>; // emoji ⇒ usernames
|
| 91 |
+
deleted?: boolean;
|
| 92 |
+
}
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
(Stored on the backend at `Exocore-Backend/local-db/posts.json`, mirrored to
|
| 96 |
+
Google Drive.)
|
| 97 |
+
|
| 98 |
+
## Empty / error states
|
| 99 |
+
|
| 100 |
+
| State | Render |
|
| 101 |
+
|-------|--------|
|
| 102 |
+
| Loading | spinner + "Loading profile…" |
|
| 103 |
+
| 404 | "User not found." with **← Back to dashboard** link |
|
| 104 |
+
| Banned | "This account has been suspended." |
|
| 105 |
+
| No posts| "No posts yet." underneath the header |
|
docs/projects/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Project Nodes
|
| 2 |
+
|
| 3 |
+
A **Project Node** is one isolated workspace inside the panel. Every node has
|
| 4 |
+
its own filesystem, run command, env vars, package manager state, and live
|
| 5 |
+
runtime stats. The Dashboard renders them as cards
|
| 6 |
+
([`home/ProjectNodeCard.tsx`](../../client/home/ProjectNodeCard.tsx)) and the
|
| 7 |
+
manager modal ([`home/FileManager.tsx`](../../client/home/FileManager.tsx))
|
| 8 |
+
lets you create / archive / delete / browse them.
|
| 9 |
+
|
| 10 |
+
## Visual
|
| 11 |
+
|
| 12 |
+
The Dashboard capture below shows where the project grid lives (top of the
|
| 13 |
+
"Project Nodes" section). Without an active user account it redirects to
|
| 14 |
+
login — sign in to see live cards.
|
| 15 |
+
|
| 16 |
+
| Desktop | Mobile |
|
| 17 |
+
|---------|--------|
|
| 18 |
+
|  |  |
|
| 19 |
+
|
| 20 |
+
## Card anatomy
|
| 21 |
+
|
| 22 |
+
```
|
| 23 |
+
┌───────────────────────────────┐
|
| 24 |
+
│ ▦ project-name [⋮] │
|
| 25 |
+
│ language · template │
|
| 26 |
+
├───────────────────────────────┤
|
| 27 |
+
│ status: Running / Idle /… │
|
| 28 |
+
│ CPU 7 % · RAM 142 MB │
|
| 29 |
+
├───────────────────────────────┤
|
| 30 |
+
│ [ Open ] [ Run ] [ ⋮ ] │
|
| 31 |
+
└───────────────────────────────┘
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
Card actions:
|
| 35 |
+
|
| 36 |
+
- **Open** → `navigate(/editor?project=${id})` (deep-links into the IDE)
|
| 37 |
+
- **Run** → POST `editor/runtime/start`
|
| 38 |
+
- **⋮ menu** → Rename · Stop · Restart · Archive · Delete · Duplicate
|
| 39 |
+
|
| 40 |
+
## Create wizard
|
| 41 |
+
|
| 42 |
+
Triggered by the `+ New Project` quick action. Steps:
|
| 43 |
+
|
| 44 |
+
1. **Pick a template** —
|
| 45 |
+
`editor/templates` provides Node.js, Python, Static HTML, React/Vite,
|
| 46 |
+
Express, Flask, Next.js, Vanilla, "Empty".
|
| 47 |
+
2. **Name + slug** — auto-slugged with collision detection.
|
| 48 |
+
3. **Select runtime** — Node 20 / 22, Python 3.10 / 3.11 / 3.12, Bun, Deno.
|
| 49 |
+
4. **Initial files** — preview tree from the template; allow tweaks.
|
| 50 |
+
5. **Confirm + create** → spawns project under `exocore-web/uploads/<id>/`,
|
| 51 |
+
records metadata in `editor/projects` and navigates straight into it.
|
| 52 |
+
|
| 53 |
+
Source: [`home/CreateProjectWizard.tsx`](../../client/home/CreateProjectWizard.tsx).
|
| 54 |
+
|
| 55 |
+
## File manager modal
|
| 56 |
+
|
| 57 |
+
Opened from `Quick Actions → Project Nodes`. Shows:
|
| 58 |
+
|
| 59 |
+
- All nodes (active + archived) with sort by name / last opened / size.
|
| 60 |
+
- Storage quota bar (per plan: free 1 GB, pro 10 GB, owner unlimited).
|
| 61 |
+
- Bulk actions: archive, restore, delete, export ZIP.
|
| 62 |
+
|
| 63 |
+
## Backend routes used
|
| 64 |
+
|
| 65 |
+
| Route | Verb | Source |
|
| 66 |
+
|-------|------|--------|
|
| 67 |
+
| `/exocore/api/editor/projects` | GET | List projects |
|
| 68 |
+
| `/exocore/api/editor/projects` | POST | Create project |
|
| 69 |
+
| `/exocore/api/editor/projects/:id` | PATCH | Rename / archive |
|
| 70 |
+
| `/exocore/api/editor/projects/:id` | DELETE | Delete |
|
| 71 |
+
| `/exocore/api/editor/runtime/start` | POST | Spawn process |
|
| 72 |
+
| `/exocore/api/editor/runtime/stop` | POST | SIGTERM |
|
| 73 |
+
| `/exocore/api/editor/runtime/stats` | GET | CPU / RAM tick |
|
| 74 |
+
|
| 75 |
+
(Wired in [`routes/editor/projects.ts`](../../routes/editor/projects.ts) and
|
| 76 |
+
[`routes/editor/runtime.ts`](../../routes/editor/runtime.ts).)
|
docs/screenshots/desktop/01-panel-gate.png
ADDED
|
docs/screenshots/desktop/02-home-landing.png
ADDED
|
docs/screenshots/desktop/03-login.png
ADDED
|
docs/screenshots/desktop/04-register.png
ADDED
|
docs/screenshots/desktop/05-forgot-password.png
ADDED
|
docs/screenshots/desktop/06-verify-pending.png
ADDED
|
docs/screenshots/desktop/07-dashboard.png
ADDED
|
Git LFS Details
|
docs/screenshots/desktop/08-leaderboard.png
ADDED
|
docs/screenshots/desktop/09-profile-self.png
ADDED
|
docs/screenshots/desktop/10-profile-other.png
ADDED
|
Git LFS Details
|
docs/screenshots/desktop/11-editor.png
ADDED
|
Git LFS Details
|
docs/screenshots/editor/00-panel-gate.png
ADDED
|
docs/screenshots/editor/01-editor-default.png
ADDED
|
docs/screenshots/editor/02-editor-explorer-file.png
ADDED
|
Git LFS Details
|
docs/screenshots/editor/03-editor-terminal.png
ADDED
|
Git LFS Details
|
docs/screenshots/editor/04-editor-console.png
ADDED
|
Git LFS Details
|
docs/screenshots/editor/05-editor-webview.png
ADDED
|
Git LFS Details
|
docs/screenshots/editor/06-editor-problems.png
ADDED
|
Git LFS Details
|