Compare commits

..

No commits in common. "d91dc1d5e2ac0973041d37990ea7d95130055538" and "a2a7eb6cd77278d3d342bc4e2887bac5c478136f" have entirely different histories.

3 changed files with 2 additions and 453 deletions

View file

@ -1,386 +0,0 @@
# sanctum-web Layout Redesign Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Fork stoatchat/for-web into sanctum-web, fix the root layout so the message area fills the screen properly, and deploy it to mithraic.space/app via Forgejo CI.
**Architecture:** The root layout lives in two files — `Interface.tsx` (the grid shell) and `Sidebar.tsx` (server list + channel list). Both use Panda CSS (`styled-system/jsx`). We replace the loose flex layout with an explicit CSS Grid, add `min-width: 0` guards on grid children, and wire up a Forgejo Actions pipeline that builds and rsyncs to the VPS on every push to main.
**Tech Stack:** Solid.js, Vite, TypeScript, Panda CSS (`styled-system/jsx`), pnpm monorepo, Forgejo Actions, rsync
---
## File Map
| Action | Path | Purpose |
|---|---|---|
| Clone | `git.mithraic.cloud/ad3laid3/sanctum-web` | The fork (already created) |
| Modify | `packages/client/src/Interface.tsx` | Root grid shell — Layout + Content styled components |
| Modify | `packages/client/src/interface/Sidebar.tsx` | Server list + channel list sizing |
| Create | `.forgejo/workflows/deploy.yml` | CI: build + rsync to VPS on push to main |
| Create | `packages/client/.env` | API URLs pointing at mithraic.space services |
---
## Task 1: Clone the fork and add upstream remote
**Files:**
- Working directory: wherever you keep projects locally
- [ ] **Step 1: Clone the fork**
```bash
git clone https://git.mithraic.cloud/ad3laid3/sanctum-web
cd sanctum-web
```
- [ ] **Step 2: Add the upstream so you can pull fixes from stoatchat later**
```bash
git remote add upstream https://github.com/stoatchat/for-web
git fetch upstream
```
- [ ] **Step 3: Install dependencies**
```bash
pnpm install
```
- [ ] **Step 4: Confirm dev server starts**
```bash
cd packages/client
pnpm dev
```
Expected: Vite dev server starts, browser opens the app (will show login screen since no backend is configured yet — that's fine).
- [ ] **Step 5: Commit the baseline (upstream content as-is)**
```bash
cd ../..
git add .
git commit -m "chore: initial fork from stoatchat/for-web"
git push origin main
```
---
## Task 2: Configure environment for mithraic.space
**Files:**
- Create: `packages/client/.env`
- [ ] **Step 1: Create the env file**
```bash
cat > packages/client/.env << 'EOF'
VITE_API_URL=https://mithraic.space/api
VITE_WS_URL=wss://mithraic.space/ws
VITE_MEDIA_URL=https://mithraic.space/media
VITE_PROXY_URL=https://mithraic.space/proxy
EOF
```
- [ ] **Step 2: Confirm build succeeds with these env vars**
```bash
cd packages/client
pnpm build
```
Expected: `dist/` folder is created with no errors.
- [ ] **Step 3: Commit**
```bash
cd ../..
git add packages/client/.env
git commit -m "chore: configure env for mithraic.space"
git push origin main
```
---
## Task 3: Fix the root layout in Interface.tsx
**Files:**
- Modify: `packages/client/src/Interface.tsx`
The current `Layout` uses `display: flex` with no explicit column widths, and `Content` uses `width: 100%` which doesn't fill remaining space properly in a flex context. The fix: switch Layout to CSS Grid with `auto 1fr` columns, and replace `width: 100%` on Content with `minWidth: 0` so it respects the grid.
- [ ] **Step 1: Open `packages/client/src/Interface.tsx` and find the two styled components at the bottom of the file**
They look like this:
```typescript
const Layout = styled("div", {
base: {
display: "flex",
height: "100%",
minWidth: 0,
},
// ...
});
const Content = styled("div", {
base: {
background: "var(--md-sys-color-surface-container-low)",
display: "flex",
width: "100%",
minWidth: 0,
},
// ...
});
```
- [ ] **Step 2: Replace `Layout` base styles — switch from flex to grid**
Change the `Layout` styled component's `base` to:
```typescript
const Layout = styled("div", {
base: {
display: "grid",
gridTemplateColumns: "auto 1fr",
height: "100%",
overflow: "hidden",
},
variants: {
disconnected: {
true: {
color: "var(--md-sys-color-on-primary-container)",
background: "var(--md-sys-color-primary-container)",
},
false: {
color: "var(--md-sys-color-outline)",
background: "var(--md-sys-color-surface-container-high)",
},
},
},
});
```
- [ ] **Step 3: Replace `Content` base styles — remove width:100%, add minWidth:0**
```typescript
const Content = styled("div", {
base: {
background: "var(--md-sys-color-surface-container-low)",
display: "flex",
minWidth: 0,
overflow: "hidden",
},
variants: {
sidebar: {
false: {
borderTopLeftRadius: "var(--borderRadius-lg)",
borderBottomLeftRadius: "var(--borderRadius-lg)",
overflow: "hidden",
},
},
},
});
```
- [ ] **Step 4: Start the dev server and verify the message area now fills the screen**
```bash
cd packages/client && pnpm dev
```
Expected: The message area takes up the majority of the window. Sidebar stays on the left without overflowing.
- [ ] **Step 5: Commit**
```bash
cd ../..
git add packages/client/src/Interface.tsx
git commit -m "fix: switch root layout to CSS Grid, message area fills remaining space"
git push origin main
```
---
## Task 4: Fix Sidebar panel widths
**Files:**
- Modify: `packages/client/src/interface/Sidebar.tsx`
The Sidebar wraps both the server icon list and the channel list in a single `display: flex` div. We need to give each child an explicit width so they never squeeze or overflow.
- [ ] **Step 1: Open `packages/client/src/interface/Sidebar.tsx` and find the root return statement**
It looks like:
```typescript
return (
<div style={{ display: "flex", "flex-shrink": 0 }}>
<ServerList ... />
<Show when={...}>
<Switch fallback={<Home />}>
<Match when={params.server}><Server /></Match>
</Switch>
</Show>
</div>
);
```
- [ ] **Step 2: Wrap ServerList in a fixed-width container (65px) and the channel sidebar in a 240px container**
```typescript
return (
<div style={{ display: "flex", height: "100%", "flex-shrink": 0 }}>
<div style={{ width: "65px", "flex-shrink": 0, overflow: "hidden" }}>
<ServerList
orderedServers={state.ordering.orderedServers(client())}
setServerOrder={state.ordering.setServerOrder}
unreadConversations={state.ordering
.orderedConversations(client())
.filter((channel) => channel.unread)}
user={user()!}
selectedServer={() => params.server}
onCreateOrJoinServer={() =>
openModal({ type: "create_or_join_server", client: client() })
}
menuGenerator={props.menuGenerator}
/>
</div>
<Show
when={
state.layout.getSectionState(LAYOUT_SECTIONS.PRIMARY_SIDEBAR, true) &&
!location.pathname.startsWith("/discover")
}
>
<div style={{ width: "240px", "flex-shrink": 0, overflow: "hidden" }}>
<Switch fallback={<Home />}>
<Match when={params.server}>
<Server />
</Match>
</Switch>
</div>
</Show>
</div>
);
```
- [ ] **Step 3: Check in dev server — server icon column should be narrow (65px), channel list should be 240px, message area fills the rest**
Expected: Three visually distinct columns. The message area is clearly the widest.
- [ ] **Step 4: Commit**
```bash
git add packages/client/src/interface/Sidebar.tsx
git commit -m "fix: explicit 65px server list and 240px channel sidebar widths"
git push origin main
```
---
## Task 5: Set up Forgejo CI — build and deploy
**Files:**
- Create: `.forgejo/workflows/deploy.yml`
This workflow runs on every push to `main`, builds the client, and rsyncs the output to the VPS directory that Caddy serves at `mithraic.space/app`.
- [ ] **Step 1: Add an SSH deploy key to Forgejo**
On the VPS, generate a deploy key (if one doesn't exist):
```bash
ssh-keygen -t ed25519 -C "sanctum-web-deploy" -f ~/.ssh/sanctum_web_deploy -N ""
cat ~/.ssh/sanctum_web_deploy.pub >> ~/.ssh/authorized_keys
cat ~/.ssh/sanctum_web_deploy # copy this — it's the private key for the secret
```
In Forgejo → `sanctum-web` repo → Settings → Secrets, add:
- `DEPLOY_KEY` = the private key content from above
- `DEPLOY_HOST` = your VPS IP or hostname
- `DEPLOY_USER` = the SSH user on the VPS (e.g. `root` or `deploy`)
- `DEPLOY_PATH` = the path Caddy serves for mithraic.space/app (e.g. `/var/www/sanctum-web`)
- [ ] **Step 2: Create the workflow file**
```bash
mkdir -p .forgejo/workflows
```
Create `.forgejo/workflows/deploy.yml`:
```yaml
name: Build & Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: docker
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm@10
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm --filter client build
- name: Deploy to VPS
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
run: |
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
rsync -avz --delete \
-e "ssh -i ~/.ssh/deploy_key" \
packages/client/dist/ \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/"
```
- [ ] **Step 3: Commit and push — watch the Actions tab on Forgejo**
```bash
git add .forgejo/workflows/deploy.yml
git commit -m "ci: build and deploy to VPS on push to main"
git push origin main
```
Expected: Forgejo Actions picks up the workflow, builds successfully, and rsyncs `dist/` to the VPS. Visit `mithraic.space/app` — the new layout should be live.
---
## Task 6: Verify the live deployment
- [ ] **Step 1: Open `mithraic.space/app` in a browser**
Check:
- Server icon column is narrow on the left
- Channel list is a fixed-width column next to it
- Message area fills the remaining screen width
- No horizontal overflow or clipped panels
- [ ] **Step 2: Open Sanctum desktop app**
Since it loads `mithraic.space/app`, it should automatically show the new layout. Verify the same proportions hold in the desktop window.
- [ ] **Step 3: Test on a narrow browser window (900px wide)**
Expected: The layout still shows the message area without horizontal scrollbar. If the member list appears (in a text channel), it can collapse or overlap at this width — that's acceptable for v1.
- [ ] **Step 4: Tag the first release**
```bash
git tag v1.0.0
git push origin v1.0.0
```

View file

@ -1,62 +0,0 @@
# sanctum-web Layout Redesign
**Date:** 2026-04-22
**Repo:** git.mithraic.cloud/ad3laid3/sanctum-web
**Base:** stoatchat/for-web (Solid.js + Vite + TypeScript)
---
## Goal
Fork the Stoat web frontend and fix its layout proportions so the app feels native on desktop and works properly as a PWA. The message area should dominate the screen. Nothing in the API, auth, or business logic layer is changed.
---
## Layout
The root shell is rebuilt as a four-column CSS Grid:
```
[server icons 65px] [channels 240px] [messages 1fr] [members 240px]
```
| Panel | Width | Behaviour |
|---|---|---|
| Server icon sidebar | 65px fixed | Icon-only, scrollable, always visible |
| Channel list | 240px default | User-resizable, collapsible |
| Message area | fills remaining space (1fr) | Message list + pinned input at bottom |
| Member list | 240px | Collapsible, hidden by default on narrow viewports |
---
## Scope
**Changed:**
- Root layout shell component — CSS replaced with proper grid
- Global CSS spacing/sizing tokens that cause proportion issues
**Not changed:**
- API client
- WebSocket layer
- Auth flow
- Message rendering components
- Channel/server/member components
- Any business logic
---
## Build & Deployment
- Repo: `git.mithraic.cloud/ad3laid3/sanctum-web`
- CI: Forgejo Actions — `pnpm build` on push to `main`, rsync `dist/` to VPS
- Serves at: `mithraic.space/app` (replaces current for-web output, no Caddy changes)
- Sanctum desktop app picks up the new frontend automatically on next launch
---
## Out of Scope
- Tauri desktop wrapper (separate project, builds on this frontend)
- Feature additions beyond layout fixes
- Mobile/responsive breakpoints beyond basic collapse behaviour
- Rebranding (keep existing Stoat/Revolt dark aesthetic)

View file

@ -10,8 +10,6 @@ import { getThemeFiles, getActiveTheme, applyTheme, cycleTheme, reloadTheme, ope
// internal tray state // internal tray state
let tray: Tray = null; let tray: Tray = null;
// Keep strong reference to prevent garbage collection on Linux
let trayIcon: Electron.NativeImage;
// Create and resize tray icon for macOS // Create and resize tray icon for macOS
function createTrayIcon() { function createTrayIcon() {
@ -19,11 +17,10 @@ function createTrayIcon() {
const image = nativeImage.createFromDataURL(macOsTrayIconAsset); const image = nativeImage.createFromDataURL(macOsTrayIconAsset);
const resized = image.resize({ width: 20, height: 20 }); const resized = image.resize({ width: 20, height: 20 });
resized.setTemplateImage(true); resized.setTemplateImage(true);
trayIcon = resized; return resized;
} else { } else {
trayIcon = nativeImage.createFromDataURL(trayIconAsset); return nativeImage.createFromDataURL(trayIconAsset);
} }
return trayIcon;
} }
export function initTray() { export function initTray() {