ChoruYt commited on
Commit
dc2b30a
·
verified ·
1 Parent(s): beaf32f

Upload 151 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +30 -0
  2. Dockerfile +93 -0
  3. README.md +110 -10
  4. dist/assets/index-CYg-Es9k.css +10 -0
  5. dist/assets/index-Dv5XhPnV.js +0 -0
  6. dist/favicon.svg +5 -0
  7. dist/index.html +16 -0
  8. docs/README.md +81 -0
  9. docs/auth/README.md +117 -0
  10. docs/cloud/README.md +71 -0
  11. docs/dashboard/README.md +97 -0
  12. docs/editor/00-panel-gate.md +25 -0
  13. docs/editor/01-editor-default.md +26 -0
  14. docs/editor/02-editor-explorer-file.md +27 -0
  15. docs/editor/03-editor-terminal.md +25 -0
  16. docs/editor/04-editor-console.md +25 -0
  17. docs/editor/05-editor-webview.md +25 -0
  18. docs/editor/06-editor-problems.md +25 -0
  19. docs/editor/07-sidebar-npm.md +25 -0
  20. docs/editor/08-sidebar-github.md +25 -0
  21. docs/editor/09-sidebar-drive.md +26 -0
  22. docs/editor/10-sidebar-ai.md +27 -0
  23. docs/editor/11-history-modal.md +25 -0
  24. docs/editor/12-settings-modal.md +26 -0
  25. docs/editor/13-settings-theme-changed.md +25 -0
  26. docs/editor/14-sidebar-pylib.md +26 -0
  27. docs/editor/README.md +302 -0
  28. docs/github/README.md +69 -0
  29. docs/leaderboard/README.md +62 -0
  30. docs/panel/README.md +59 -0
  31. docs/profile/README.md +105 -0
  32. docs/projects/README.md +76 -0
  33. docs/screenshots/desktop/01-panel-gate.png +0 -0
  34. docs/screenshots/desktop/02-home-landing.png +0 -0
  35. docs/screenshots/desktop/03-login.png +0 -0
  36. docs/screenshots/desktop/04-register.png +0 -0
  37. docs/screenshots/desktop/05-forgot-password.png +0 -0
  38. docs/screenshots/desktop/06-verify-pending.png +0 -0
  39. docs/screenshots/desktop/07-dashboard.png +3 -0
  40. docs/screenshots/desktop/08-leaderboard.png +0 -0
  41. docs/screenshots/desktop/09-profile-self.png +0 -0
  42. docs/screenshots/desktop/10-profile-other.png +3 -0
  43. docs/screenshots/desktop/11-editor.png +3 -0
  44. docs/screenshots/editor/00-panel-gate.png +0 -0
  45. docs/screenshots/editor/01-editor-default.png +0 -0
  46. docs/screenshots/editor/02-editor-explorer-file.png +3 -0
  47. docs/screenshots/editor/03-editor-terminal.png +3 -0
  48. docs/screenshots/editor/04-editor-console.png +3 -0
  49. docs/screenshots/editor/05-editor-webview.png +3 -0
  50. 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
- title: Exocore Docs
3
- emoji: 📚
4
- colorFrom: green
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ | ![Home — desktop](../screenshots/desktop/02-home-landing.png) | ![Home — mobile](../screenshots/mobile/02-home-landing.png) |
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
+ | ![Login — desktop](../screenshots/desktop/03-login.png) | ![Login — mobile](../screenshots/mobile/03-login.png) |
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
+ | ![Register — desktop](../screenshots/desktop/04-register.png) | ![Register — mobile](../screenshots/mobile/04-register.png) |
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
+ | ![Forgot — desktop](../screenshots/desktop/05-forgot-password.png) | ![Forgot — mobile](../screenshots/mobile/05-forgot-password.png) |
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
+ | ![Verify — desktop](../screenshots/desktop/06-verify-pending.png) | ![Verify — mobile](../screenshots/mobile/06-verify-pending.png) |
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
+ | ![Dashboard — desktop](../screenshots/desktop/07-dashboard.png) | ![Dashboard — mobile](../screenshots/mobile/07-dashboard.png) |
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
+ | ![Dashboard redirect — desktop](../screenshots/desktop/07-dashboard.png) | ![Dashboard redirect — mobile](../screenshots/mobile/07-dashboard.png) |
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
+ | ![00 — Panel gate — desktop](../screenshots/editor/00-panel-gate.png) | ![00 — Panel gate — mobile](../screenshots/editor/mobile/00-panel-gate.png) |
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
+ | ![01 — Default editor view — desktop](../screenshots/editor/01-editor-default.png) | ![01 — Default editor view — mobile](../screenshots/editor/mobile/01-editor-default.png) |
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
+ | ![02 — Explorer file open — desktop](../screenshots/editor/02-editor-explorer-file.png) | ![02 — Explorer file open — mobile](../screenshots/editor/mobile/02-editor-explorer-file.png) |
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
+ | ![03 — Integrated terminal — desktop](../screenshots/editor/03-editor-terminal.png) | ![03 — Integrated terminal — mobile](../screenshots/editor/mobile/03-editor-terminal.png) |
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
+ | ![04 — Console pane — desktop](../screenshots/editor/04-editor-console.png) | ![04 — Console pane — mobile](../screenshots/editor/mobile/04-editor-console.png) |
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
+ ![05 — Webview / preview (desktop only) — desktop](../screenshots/editor/05-editor-webview.png)
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
+ | ![06 — Problems pane — desktop](../screenshots/editor/06-editor-problems.png) | ![06 — Problems pane — mobile](../screenshots/editor/mobile/06-editor-problems.png) |
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
+ | ![07 — NPM sidebar pane — desktop](../screenshots/editor/07-sidebar-npm.png) | ![07 — NPM sidebar pane — mobile](../screenshots/editor/mobile/07-sidebar-npm.png) |
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
+ | ![08 — GitHub sidebar pane — desktop](../screenshots/editor/08-sidebar-github.png) | ![08 — GitHub sidebar pane — mobile](../screenshots/editor/mobile/08-sidebar-github.png) |
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
+ | ![09 — Drive sidebar pane — desktop](../screenshots/editor/09-sidebar-drive.png) | ![09 — Drive sidebar pane — mobile](../screenshots/editor/mobile/09-sidebar-drive.png) |
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 — AI sidebar pane — desktop](../screenshots/editor/10-sidebar-ai.png) | ![10 — AI sidebar pane — mobile](../screenshots/editor/mobile/10-sidebar-ai.png) |
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
+ | ![11 — Code history modal — desktop](../screenshots/editor/11-history-modal.png) | ![11 — Code history modal — mobile](../screenshots/editor/mobile/11-history-modal.png) |
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
+ | ![12 — Settings modal — desktop](../screenshots/editor/12-settings-modal.png) | ![12 — Settings modal — mobile](../screenshots/editor/mobile/12-settings-modal.png) |
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
+ | ![13 — Settings · theme switch — desktop](../screenshots/editor/13-settings-theme-changed.png) | ![13 — Settings · theme switch — mobile](../screenshots/editor/mobile/13-settings-theme-changed.png) |
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
+ | ![14 — PyLib pane (Python project) — desktop](../screenshots/editor/14-sidebar-pylib.png) | ![14 — PyLib pane (Python project) — mobile](../screenshots/editor/mobile/14-sidebar-pylib.png) |
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
+ | ![Panel gate — desktop](../screenshots/editor/00-panel-gate.png) | ![Panel gate — mobile](../screenshots/editor/mobile/00-panel-gate.png) |
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
+ | ![Editor default — desktop](../screenshots/editor/01-editor-default.png) | ![Editor default — mobile](../screenshots/editor/mobile/01-editor-default.png) |
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
+ | ![Explorer file — desktop](../screenshots/editor/02-editor-explorer-file.png) | ![Explorer file — mobile](../screenshots/editor/mobile/02-editor-explorer-file.png) |
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
+ | ![Terminal — desktop](../screenshots/editor/03-editor-terminal.png) | ![Terminal — mobile](../screenshots/editor/mobile/03-editor-terminal.png) |
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
+ | ![Console — desktop](../screenshots/editor/04-editor-console.png) | ![Console — mobile](../screenshots/editor/mobile/04-editor-console.png) |
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
+ ![Webview — desktop](../screenshots/editor/05-editor-webview.png)
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
+ | ![Problems — desktop](../screenshots/editor/06-editor-problems.png) | ![Problems — mobile](../screenshots/editor/mobile/06-editor-problems.png) |
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
+ | ![NPM — desktop](../screenshots/editor/07-sidebar-npm.png) | ![NPM — mobile](../screenshots/editor/mobile/07-sidebar-npm.png) |
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
+ | ![GitHub — desktop](../screenshots/editor/08-sidebar-github.png) | ![GitHub — mobile](../screenshots/editor/mobile/08-sidebar-github.png) |
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
+ | ![Drive — desktop](../screenshots/editor/09-sidebar-drive.png) | ![Drive — mobile](../screenshots/editor/mobile/09-sidebar-drive.png) |
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
+ | ![AI — desktop](../screenshots/editor/10-sidebar-ai.png) | ![AI — mobile](../screenshots/editor/mobile/10-sidebar-ai.png) |
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
+ | ![History — desktop](../screenshots/editor/11-history-modal.png) | ![History — mobile](../screenshots/editor/mobile/11-history-modal.png) |
173
+
174
+ ### 12 — Settings modal
175
+
176
+ Theme picker, font size, key bindings, indent rules, etc.
177
+
178
+ | Desktop | Mobile |
179
+ |---------|--------|
180
+ | ![Settings — desktop](../screenshots/editor/12-settings-modal.png) | ![Settings — mobile](../screenshots/editor/mobile/12-settings-modal.png) |
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
+ | ![Theme switch — desktop](../screenshots/editor/13-settings-theme-changed.png) | ![Theme switch — mobile](../screenshots/editor/mobile/13-settings-theme-changed.png) |
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
+ | ![PyLib — desktop](../screenshots/editor/14-sidebar-pylib.png) | ![PyLib — mobile](../screenshots/editor/mobile/14-sidebar-pylib.png) |
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
+ | ![Dashboard — desktop](../screenshots/desktop/07-dashboard.png) | ![Dashboard — mobile](../screenshots/mobile/07-dashboard.png) |
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
+ | ![Leaderboard — desktop](../screenshots/desktop/08-leaderboard.png) | ![Leaderboard — mobile](../screenshots/mobile/08-leaderboard.png) |
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
+ ![Panel setup gate — desktop](../screenshots/desktop/01-panel-gate.png)
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
+ ![Panel setup gate — mobile](../screenshots/mobile/01-panel-gate.png)
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
+ | ![@skibide profile — desktop](../screenshots/desktop/10-profile-other.png) | ![@skibide profile — mobile](../screenshots/mobile/10-profile-other.png) |
19
+
20
+ ## Top contributor — `@exocore` (the OWNER)
21
+
22
+ | Desktop | Mobile |
23
+ |---------|--------|
24
+ | ![@exocore profile — desktop](../screenshots/desktop/09-profile-self.png) | ![@exocore profile — mobile](../screenshots/mobile/09-profile-self.png) |
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
+ | ![Dashboard — desktop](../screenshots/desktop/07-dashboard.png) | ![Dashboard — mobile](../screenshots/mobile/07-dashboard.png) |
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

  • SHA256: ea3d48db0952d418275812e5198f9b2db9cd8086deab3a23d2b404fcf521c711
  • Pointer size: 131 Bytes
  • Size of remote file: 202 kB
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

  • SHA256: 5ab9d04c7db8877993fa9470b8dc1d5fd77fe79090619cf78b9c00aa1ee86b54
  • Pointer size: 131 Bytes
  • Size of remote file: 100 kB
docs/screenshots/desktop/11-editor.png ADDED

Git LFS Details

  • SHA256: 95d031ea89060cefb46a46964f69889f4ec48b907ba10522707ee8a768d32bb9
  • Pointer size: 131 Bytes
  • Size of remote file: 202 kB
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

  • SHA256: c4a57795465bd13d37b69cbd2e6251e548fe29cf294711002d4c3b2b320c5343
  • Pointer size: 131 Bytes
  • Size of remote file: 117 kB
docs/screenshots/editor/03-editor-terminal.png ADDED

Git LFS Details

  • SHA256: c4a57795465bd13d37b69cbd2e6251e548fe29cf294711002d4c3b2b320c5343
  • Pointer size: 131 Bytes
  • Size of remote file: 117 kB
docs/screenshots/editor/04-editor-console.png ADDED

Git LFS Details

  • SHA256: 8b8441509726ed9e147e13da8e2d4b3186cbd3e1162098d416be73b621f654d2
  • Pointer size: 131 Bytes
  • Size of remote file: 127 kB
docs/screenshots/editor/05-editor-webview.png ADDED

Git LFS Details

  • SHA256: 90332a116543b0e0816506a06b3e99e1ccf09a7a43e5c5dc81c8389afb813974
  • Pointer size: 131 Bytes
  • Size of remote file: 145 kB
docs/screenshots/editor/06-editor-problems.png ADDED

Git LFS Details

  • SHA256: 42b73c1024bad726ca890e6b083c3d262985acf7fc2ea58b77ee0d86292d0672
  • Pointer size: 131 Bytes
  • Size of remote file: 127 kB