Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
711063c
add index
erseco Jan 8, 2026
4917694
Merge branch 'main' of github.com:exelearning/exelearning into 918-li…
erseco Jan 8, 2026
cb3a92e
Add files to allow merge
erseco Jan 9, 2026
d4e340e
Add files to allow merge
erseco Jan 9, 2026
e5c4539
Fix elp: links, user themes, themes css and idevice css issues
erseco Jan 9, 2026
98bd924
First approach for embeddable version
erseco Jan 10, 2026
619f7bc
First approach for embeddable version
erseco Jan 10, 2026
4e6f274
First approach for embeddable version
erseco Jan 10, 2026
634348b
First approach for embeddable version
erseco Jan 10, 2026
09dfd28
First approach for embeddable version
erseco Jan 10, 2026
fe5ffbf
First approach for embeddable version
erseco Jan 10, 2026
b574d13
First approach for embeddable version
erseco Jan 10, 2026
3c3fd69
First approach for embeddable version
erseco Jan 10, 2026
5796779
First approach for embeddable version
erseco Jan 10, 2026
6fe99ad
First approach for embeddable version
erseco Jan 10, 2026
f28daa3
First approach for embeddable version
erseco Jan 10, 2026
ece2fc4
First approach for embeddable version
erseco Jan 10, 2026
672a5ed
First approach for embeddable version
erseco Jan 10, 2026
fde702e
First approach for embeddable version
erseco Jan 10, 2026
99400b3
First approach for embeddable version
erseco Jan 10, 2026
d16d9d5
First approach for embeddable version
erseco Jan 10, 2026
606d74e
First approach for embeddable version
erseco Jan 10, 2026
c1af3fc
First approach for embeddable version
erseco Jan 10, 2026
f47cfb8
Merge branch '918-link-to-elpx-is-not-working' into release/3.1-embed…
erseco Jan 10, 2026
5bd4afe
Increase coverage
erseco Jan 10, 2026
29ca805
Merge remote-tracking branch 'origin/918-link-to-elpx-is-not-working'…
erseco Jan 10, 2026
4651f95
Increase coverage
erseco Jan 10, 2026
a0f56a9
Merge remote-tracking branch 'origin/918-link-to-elpx-is-not-working'…
erseco Jan 10, 2026
ec576e4
Unified embed and online version
erseco Jan 11, 2026
edfc7ea
Unified embed and online version
erseco Jan 11, 2026
61e530b
Unified embed and online version
erseco Jan 11, 2026
1cb41a6
Add static tests
erseco Jan 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions .github/workflows/docs-and-repos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ on:
workflow_dispatch: # Allows manually triggering

permissions:
contents: read
pages: write
id-token: write
contents: write

jobs:
build-and-deploy:
Expand All @@ -27,14 +25,12 @@ jobs:

# ---- Build MkDocs docs ----
- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v5
with:
python-version: '3.x'

- name: Install MkDocs
run: |
python -m pip install --upgrade pip
pip install mkdocs mkdocs-material
run: pip install --upgrade pip mkdocs mkdocs-material

- name: Build docs
run: |
Expand Down Expand Up @@ -275,15 +271,14 @@ jobs:
[ -d site/rpm ] && generate_index "site/rpm" "rpm" || true

# ---- Deploy to GitHub Pages ----
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v4
with:
path: site

- name: Deploy to GitHub Pages
if: github.repository == 'exelearning/exelearning'
id: deployment
uses: actions/deploy-pages@v4
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: site
branch: gh-pages
clean-exclude: pr-preview
force: false

publish-chocolatey:
if: github.event_name == 'release' && github.event.action == 'released'
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/pr-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Deploy PR Preview

on:
pull_request:
types:
- opened
- reopened
- synchronize
- closed

concurrency: preview-${{ github.ref }}

permissions:
contents: write
pull-requests: write

jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Bun
if: github.event.action != 'closed'
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
if: github.event.action != 'closed'
run: bun install

- name: Build static distribution
if: github.event.action != 'closed'
run: bun run build:static

- name: Deploy preview
id: deploy
uses: rossjrw/pr-preview-action@v1
with:
source-dir: ./dist/static/
preview-branch: gh-pages
umbrella-dir: pr-preview
action: auto
qr-code: true

- name: Add preview URL to summary
if: github.event.action != 'closed'
run: |
echo "## 🚀 PR Preview Deployed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Preview URL:** ${{ steps.deploy.outputs.deployment-url }}" >> $GITHUB_STEP_SUMMARY
43 changes: 38 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,33 @@ else
bun run dev:local
endif

# Start full app: Elysia backend + Electron
# Start full app: Static files + Electron (no server needed)
.PHONY: run-app
run-app: check-bun deps css bundle
@echo "Launching eXeLearning App (Electron + Elysia)..."
@bun run build:standalone
@bun run dev:app
@echo "Building static files..."
@bun scripts/build-static-bundle.ts
@echo "Launching eXeLearning App (Electron)..."
@bun run electron

# Build static distribution (PWA mode, no server required)
# Usage: make build-static
.PHONY: build-static
build-static: check-bun deps css bundle
@echo "Building static distribution..."
@bun run build:static
@echo "Static distribution built at dist/static/"

# Build static distribution and serve it
# Usage: make up-static [PORT=8080]
.PHONY: up-static
up-static: build-static
@echo ""
@echo "============================================================"
@echo " Serving static distribution at http://localhost:$${PORT:-8080}"
@echo " Press Ctrl+C to stop"
@echo "============================================================"
@echo ""
@bunx serve dist/static -p $${PORT:-8080}


# =============================================================================
Expand Down Expand Up @@ -532,6 +553,13 @@ test-e2e-ui: check-env ## Run Playwright E2E tests with UI
test-e2e-firefox: check-env ## Run Playwright E2E tests with Firefox
bunx playwright test --project=firefox

.PHONY: test-e2e-static
test-e2e-static: check-env ## Run Playwright E2E tests with static bundle (no server)
PLAYWRIGHT_PROJECT=chromium-static bunx playwright test --project=chromium-static

.PHONY: test-e2e-all
test-e2e-all: test-e2e test-e2e-static ## Run E2E tests for both server and static modes


# =============================================================================
# DATABASE-SPECIFIC E2E TESTS
Expand Down Expand Up @@ -807,7 +835,10 @@ help:
@echo "Local:"
@echo " make up-local Start locally (web only, dev mode)"
@echo " make up-local APP_ENV=prod Start locally (web only, prod mode)"
@echo " make run-app Start Electron + backend (desktop app)"
@echo " make build-static Build static distribution (PWA mode)"
@echo " make up-static Build and serve static distribution (PWA mode)"
@echo " make up-static PORT=3000 Same, but on custom port"
@echo " make run-app Start Electron app (static mode, no server)"
@echo " make bundle Build all assets (TS + CSS + JS bundle)"
@echo " make deps Install dependencies"
@echo ""
Expand Down Expand Up @@ -839,6 +870,8 @@ help:
@echo " make test-e2e Run Playwright E2E tests (Chromium)"
@echo " make test-e2e-chromium Run E2E tests with Chromium"
@echo " make test-e2e-firefox Run E2E tests with Firefox"
@echo " make test-e2e-static Run E2E tests with static bundle (no server)"
@echo " make test-e2e-all Run E2E tests for both server and static modes"
@echo ""
@echo "Legacy (Core2 Duo / No Bun):"
@echo " make up-legacy Start legacy server with Node.js (Docker)"
Expand Down
23 changes: 20 additions & 3 deletions assets/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,26 @@ body[mode="advanced"] .exe-simplified {
display: none !important;
}

/* eXe Mode */
body[installation-type="offline"] .exe-online,
body[installation-type="online"] .exe-offline {
/* eXe Mode - installation type visibility */
/* Online mode: hide offline and electron elements, show online elements */
body[installation-type="online"] .exe-offline,
body[installation-type="online"] .exe-electron {
display: none !important;
}

/* Static mode: hide online and electron elements, show offline elements (exe logo) */
body[installation-type="static"] .exe-online,
body[installation-type="static"] .exe-electron {
display: none !important;
}

/* Electron mode: hide online elements, show offline and electron elements */
body[installation-type="electron"] .exe-online {
display: none !important;
}

/* Legacy support: "offline" value maps to electron behavior */
body[installation-type="offline"] .exe-online {
display: none !important;
}

Expand Down
94 changes: 94 additions & 0 deletions doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,11 +532,105 @@ ws.on('open', async () => {
});
```

## 13. Theme Architecture

### 13.1 Theme Types

eXeLearning supports three types of themes:

| Type | Source | Storage | Served By |
|------|--------|---------|-----------|
| **Base** | Built-in with eXeLearning | Server `/perm/themes/base/` | Server |
| **Site** | Admin-installed for all users | Server `/perm/themes/site/` | Server |
| **User** | Imported by user or from .elpx | Client IndexedDB + Yjs | **Never server** |

### 13.2 Server Themes (Base & Site)

**Base themes** are included with eXeLearning and synchronized at startup:
- Located in `/public/files/perm/themes/base/`
- Cannot be modified by users
- Served directly by the server

**Site themes** are installed by administrators for all users:
- Located in `/perm/themes/site/`
- Admin can activate/deactivate themes
- Admin can set a default theme for new projects
- Served directly by the server

### 13.3 User Themes (Client-Side Only)

> **Important**: User themes are NEVER stored or served by the server.

User themes are stored entirely on the client side:

```
┌─────────────────────────────────────────────────────────────────────┐
│ USER THEME STORAGE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ IndexedDB (per-user isolation) │
│ └── user-themes store: key = "userId:themeName" │
│ └── Each user's themes isolated by userId prefix │
│ └── User "alice" cannot see user "bob"'s themes │
│ │
│ Yjs themeFiles (project document) │
│ └── Currently selected user theme (for collaboration/export) │
│ │
│ .elpx export │
│ └── Embedded theme files (for portability) │
│ │
└─────────────────────────────────────────────────────────────────────┘
```

### 13.4 User Theme Flow

```
1. IMPORT THEME
User uploads ZIP → Stored in IndexedDB (local storage)

2. SELECT THEME
User selects theme → Copied to Yjs themeFiles
(enables collaboration and export)

3. CHANGE TO ANOTHER THEME
User selects different theme → Removed from Yjs
(but remains in IndexedDB for future use)

4. EXPORT PROJECT (.elpx)
If user theme selected → Embedded in ZIP

5. OPEN PROJECT WITH EMBEDDED THEME
Another user opens .elpx → Theme extracted to their IndexedDB
(if ONLINE_THEMES_INSTALL is enabled)
```

### 13.5 Admin Configuration

```bash
# Allow users to import/install styles
ONLINE_THEMES_INSTALL=1 # 1 = enabled (default), 0 = disabled
```

When disabled (`ONLINE_THEMES_INSTALL=0`):
- Users cannot import external themes via the interface
- Users cannot open .elpx files with embedded themes

### 13.6 Why User Themes Are Client-Side

This design follows the same pattern as other user-specific data (like favorite iDevices):

1. **Per-user storage**: Each user's themes are private to them
2. **No server storage**: Themes don't consume server disk space
3. **Collaboration via Yjs**: Selected theme is shared with collaborators in real-time
4. **Portability**: Themes embedded in .elpx can be opened anywhere
5. **Offline capability**: Themes work without server connectivity

---

## Further Reading

- [Real-Time Collaboration](development/real-time.md) - WebSocket and Yjs details
- [REST API](development/rest-api.md) - API endpoints
- [Testing](development/testing.md) - Test patterns and coverage
- [Creating Styles](development/styles.md) - How to create custom themes

Loading
Loading