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:
co-authored by
Claude Opus 4.6
parent
21b43214ac
commit
bda670d8d9
@@ -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
|
||||
|
||||
@@ -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: '/',
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user