feat(app): base-aware routing, embedded flag, panel slide animation

- Use import.meta.env.BASE_URL for router history (Archy /aiui/ deployments)
- Capture embedded flag from URL params before router init
- Add panelSlideIn animation with prefers-reduced-motion support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 12:41:26 +00:00
co-authored by Claude Opus 4.6
parent 21b43214ac
commit bda670d8d9
4 changed files with 86 additions and 1 deletions
+58
View File
@@ -285,3 +285,61 @@ type(scope): description
- No force push to main/dev
- Never commit `.env.local`, secrets, or `node_modules`
- Squash merge features, tag releases `v1.0.0`
## Archipelago (Archy) Integration
AIUI runs inside an iframe in Archipelago's Chat mode. All communication with the host happens via `window.postMessage()` through a strict protocol.
### Architecture
```
AIUI (iframe) ←→ postMessage ←→ Archy ContextBroker ←→ Node data
```
AIUI is **quarantined** — it never directly accesses Archy's APIs, stores, or node data. The Archy ContextBroker fetches and sanitizes data before passing it to AIUI.
### Protocol
Use `archyBridge.ts` (`src/services/archyBridge.ts`) for all Archy communication:
```ts
import { archyBridge } from '@/services/archyBridge'
// Request context (respects user permissions)
const apps = await archyBridge.requestContext('apps')
if (!apps.permitted) {
// Show: "Enable 'Installed Apps' access in Archy Settings"
}
// Request an action
await archyBridge.requestAction('open-app', { appId: 'btcpay-server' })
// Listen for theme/permission updates
archyBridge.onPermissionsUpdate((categories) => { ... })
archyBridge.onThemeUpdate((theme) => { ... })
```
**Context categories** (user toggles each on/off in Archy Settings):
- `apps` — App names, status, health (no credentials)
- `system` — CPU, RAM, disk (no paths or IPs)
- `network` — Connection status, peer count (no IPs)
- `wallet` — Balance, channel count (no keys or seeds)
- `files` — File/folder names (no contents)
### Critical Rules
1. **NEVER** fetch Archy APIs directly — always use `archyBridge`
2. **NEVER** store or log raw user data from context responses
3. **NEVER** make HTTP requests to the host machine
4. Handle `permitted: false` gracefully — tell users what to enable
5. Send `ready` message on mount so Archy knows the iframe loaded
6. Build must output a static SPA servable from any base path
7. All AI provider keys are user-provided and stored locally in AIUI only
### Build & Deploy
AIUI deploys as a Podman container on the Archy node:
- Build: `pnpm build``packages/app/dist/`
- Container: nginx:alpine serving the dist
- Proxied at `/aiui/` via Archy's nginx
- Updates independently of Archy — new container image = new version
+6 -1
View File
@@ -5,8 +5,13 @@ import App from './App.vue'
import './styles/main.css'
import { initializePlugins } from './plugins'
// Capture embedded flag before router init (survives SPA navigation)
// Only embedded when explicitly requested via ?embedded param
const _embeddedFlag = new URLSearchParams(window.location.search).has('embedded')
;(window as unknown as Record<string, unknown>).__AIUI_EMBEDDED__ = _embeddedFlag
const router = createRouter({
history: createWebHistory(),
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
+21
View File
@@ -605,6 +605,27 @@ input:focus-visible {
50% { opacity: 1; }
}
@keyframes panelSlideIn {
0% {
opacity: 0;
transform: translateX(-40px) scale(0.97);
}
100% {
opacity: 1;
transform: translateX(0) scale(1);
}
}
.panel-slide-in {
animation: panelSlideIn 0.6s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
.panel-slide-in {
animation: none;
}
}
/* ===== POSTER CARD — path-glass border ===== */
.poster-card {
+1
View File
@@ -11,6 +11,7 @@ import { rssPlugin } from './vite-rss'
import { fsPlugin } from './vite-fs'
export default defineConfig({
base: process.env.VITE_BASE_PATH || '/',
plugins: [
vue(),
tailwindcss(),