Compare commits
No commits in common. "d91dc1d5e2ac0973041d37990ea7d95130055538" and "a2a7eb6cd77278d3d342bc4e2887bac5c478136f" have entirely different histories.
d91dc1d5e2
...
a2a7eb6cd7
3 changed files with 2 additions and 453 deletions
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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)
|
||||
|
|
@ -10,8 +10,6 @@ import { getThemeFiles, getActiveTheme, applyTheme, cycleTheme, reloadTheme, ope
|
|||
|
||||
// internal tray state
|
||||
let tray: Tray = null;
|
||||
// Keep strong reference to prevent garbage collection on Linux
|
||||
let trayIcon: Electron.NativeImage;
|
||||
|
||||
// Create and resize tray icon for macOS
|
||||
function createTrayIcon() {
|
||||
|
|
@ -19,11 +17,10 @@ function createTrayIcon() {
|
|||
const image = nativeImage.createFromDataURL(macOsTrayIconAsset);
|
||||
const resized = image.resize({ width: 20, height: 20 });
|
||||
resized.setTemplateImage(true);
|
||||
trayIcon = resized;
|
||||
return resized;
|
||||
} else {
|
||||
trayIcon = nativeImage.createFromDataURL(trayIconAsset);
|
||||
return nativeImage.createFromDataURL(trayIconAsset);
|
||||
}
|
||||
return trayIcon;
|
||||
}
|
||||
|
||||
export function initTray() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue