feat: add Archipelago agent skills and app starter
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
name: archipelago-app
|
||||||
|
description: Build, package, test, and update a manifest-driven Archipelago app using the real app developer contract, rootless runtime, and lifecycle acceptance flow.
|
||||||
|
metadata:
|
||||||
|
short-description: Build a real Archipelago app
|
||||||
|
---
|
||||||
|
|
||||||
|
# Archipelago app development
|
||||||
|
|
||||||
|
Use this skill when creating or changing an Archipelago app, manifest, container
|
||||||
|
build, app integration, credentials, signer flow, or app preview. Always load
|
||||||
|
`archipelago-design` for newly authored UI.
|
||||||
|
|
||||||
|
Read [app-contract.md](references/app-contract.md),
|
||||||
|
`docs/app-developer-guide.md`, and `docs/app-manifest-spec.md` before coding.
|
||||||
|
|
||||||
|
## Required loop
|
||||||
|
|
||||||
|
1. Create a dedicated worktree or project directory and choose the smallest
|
||||||
|
useful app scope. Prefer the maintained starter and its pinned dependencies.
|
||||||
|
2. Implement the app as a manifest, rootless container, persistent data path,
|
||||||
|
truthful health/readiness check, declared interface, and tests. Never add a
|
||||||
|
per-app Rust installer or rootful/Docker-socket shortcut.
|
||||||
|
3. Use generated secrets or declared secret files; never put credentials in a
|
||||||
|
manifest, image, logs, URL, or frontend bundle. Keep production wallets and
|
||||||
|
app databases outside development fixtures.
|
||||||
|
4. Validate the manifest and build context, then run the app through install,
|
||||||
|
start, stop, restart, manager restart, uninstall with data preservation, and
|
||||||
|
reinstall. Verify the real My Apps/Services launch path, not only a direct
|
||||||
|
port.
|
||||||
|
5. For UI, exercise standalone and embedded modes at 320, 390, 768, and 1440px
|
||||||
|
plus phone landscape, keyboard navigation, loading/empty/error/retry/success,
|
||||||
|
safe areas, and reduced motion.
|
||||||
|
6. Report source, disposable-node, actual-node, and release-artifact evidence
|
||||||
|
separately. Catalog publication is a separate request.
|
||||||
|
|
||||||
|
Important runtime facts:
|
||||||
|
|
||||||
|
- `/opt/archipelago/apps` is rebuilt from the runtime payload at backend start;
|
||||||
|
staging only there will be lost. Follow the developer guide's payload path.
|
||||||
|
- Signed catalog entries take precedence over disk manifests for catalog apps.
|
||||||
|
- Installed does not mean ready or launchable; use health and readiness evidence.
|
||||||
|
- App interfaces describe the service behind the gate. Do not hard-code a node
|
||||||
|
IP, scheme, app path, or host frame URL into app code.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [app contract and acceptance](references/app-contract.md)
|
||||||
|
- [design routing](../archipelago-design/SKILL.md)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Archipelago app"
|
||||||
|
short_description: "Build a real Archipelago app"
|
||||||
|
brand_color: "#F7931A"
|
||||||
|
default_prompt: "Use $archipelago-app to build and validate this Archipelago app."
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# App contract
|
||||||
|
|
||||||
|
Start with `docs/app-developer-guide.md` and `docs/app-manifest-spec.md`; those
|
||||||
|
files are authoritative for fields and launch behavior. Validate with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/validate-app-manifest.sh apps/<id>/manifest.yml
|
||||||
|
python3 scripts/generate-app-catalog.py
|
||||||
|
python3 scripts/check-app-catalog-drift.py --release --strict
|
||||||
|
```
|
||||||
|
|
||||||
|
Use pinned image versions, read-only root, no-new-privileges, minimal
|
||||||
|
capabilities, rootless Podman, declared persistent data under
|
||||||
|
`/var/lib/archipelago/<id>`, health checks, generated/declared secrets, and
|
||||||
|
truthful interfaces. Test install/start/stop/restart/manager-restart/uninstall
|
||||||
|
with data preservation/reinstall on a disposable node. Do not replace the
|
||||||
|
signed catalog to make a local test app appear.
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
name: archipelago-design
|
||||||
|
description: Create or change Archipelago UI using the shared semantic tokens, components, responsive patterns, accessibility states, and embedded/standalone host contract.
|
||||||
|
metadata:
|
||||||
|
short-description: Keep Archipelago UI consistent
|
||||||
|
---
|
||||||
|
|
||||||
|
# Archipelago design system
|
||||||
|
|
||||||
|
Use this skill for any new or changed Archipelago UI, including app screens,
|
||||||
|
Terminal, setup flows, dialogs, dashboards, and app wrappers. Read
|
||||||
|
[design-contract.md](references/design-contract.md) before implementation.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Find and reuse the closest shared component and pattern before creating one.
|
||||||
|
- Use semantic `--archy-*` tokens and the pinned kit version. A bespoke color,
|
||||||
|
font, radius, shadow, or z-index needs a documented reason.
|
||||||
|
- Preserve the dark baseline, readable surfaces, 4px spacing rhythm, bottom
|
||||||
|
action placement, 44px touch targets, visible focus, and safe-area behavior.
|
||||||
|
- Keep terminal/editor/tmux keys inside the terminal focus boundary; generic
|
||||||
|
modal Escape/arrow handlers must not consume them.
|
||||||
|
- Implement loading, empty, offline, denied, validation-error, retry, disabled,
|
||||||
|
and confirmed-success states. Status color must have text or icon support.
|
||||||
|
- Test standalone and embedded modes. Embedded apps do not duplicate the host
|
||||||
|
wallpaper or navigation and must work without a host handshake.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Read the reference screen and source; choose the matching component/pattern.
|
||||||
|
2. Build with the shared kit and local assets, not runtime dashboard CSS or CDN
|
||||||
|
fonts. Keep host RPC, signer, and credential bridges behind typed adapters.
|
||||||
|
3. Render the gallery/preview at required viewports and inspect screenshots and
|
||||||
|
keyboard behavior. Check contrast on the real composited surface.
|
||||||
|
4. Record deliberate exceptions and update the kit only when a reusable need is
|
||||||
|
demonstrated. Do not silently accept visual-diff changes.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [design contract](references/design-contract.md)
|
||||||
|
- [existing component map](references/component-map.md)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Archipelago design"
|
||||||
|
short_description: "Keep Archipelago UI consistent"
|
||||||
|
brand_color: "#F7931A"
|
||||||
|
default_prompt: "Use $archipelago-design to implement this UI in Archipelago's design system."
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: true
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Existing component map
|
||||||
|
|
||||||
|
Reuse these first:
|
||||||
|
|
||||||
|
- Structure: `BaseModal.vue`, `BackButton.vue`, `EmptyState.vue`,
|
||||||
|
`SkeletonCard.vue`.
|
||||||
|
- Controls: `ToggleSwitch.vue`, `PasswordRevealInput.vue`,
|
||||||
|
`AppSearchField.vue`, `CopyButton.vue`.
|
||||||
|
- Feedback: `ToastStack.vue`, `ContainerStatus.vue`, existing upload/progress
|
||||||
|
components.
|
||||||
|
- Completion: `PaymentSuccessPane.vue`, `IdentitySuccessPane.vue`.
|
||||||
|
- App flows: `AppLauncherOverlay.vue`, `AppCredentialInterstitial.vue`.
|
||||||
|
|
||||||
|
The terminal must have an explicit keyboard ownership boundary and must not be
|
||||||
|
wrapped in the generic arrow-key modal behavior without adapting it.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Design contract
|
||||||
|
|
||||||
|
The current baseline is defined by `neode-ui/src/style.css`,
|
||||||
|
`neode-ui/tailwind.config.js`, `BaseModal.vue`, `AppSearchField.vue`,
|
||||||
|
`EmptyState.vue`, `SkeletonCard.vue`, and `PaymentSuccessPane.vue`. Preserve the
|
||||||
|
dark canvas, glass-card surface, 4px spacing scale, semantic orange accent,
|
||||||
|
local licensed fonts, bottom card actions, 40px desktop/52px mobile search,
|
||||||
|
44px touch targets, visible focus, dynamic viewport, safe-area and embedded
|
||||||
|
canvas rules while the shared kit is extracted.
|
||||||
|
|
||||||
|
Use semantic tokens, not raw values. New controls must document keyboard,
|
||||||
|
focus, disabled, loading, validation, error, retry, success, responsive, and
|
||||||
|
reduced-motion behavior. No essential action may be hover-only. Use the existing
|
||||||
|
dialog footer/content split and restore focus after close.
|
||||||
|
|
||||||
|
Visual acceptance covers 320/390/768/1440px, phone landscape, enlarged text,
|
||||||
|
standalone/embedded modes, dark native controls, no-blur fallback, and actual
|
||||||
|
Companion WebView behavior where applicable.
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
name: archipelago
|
||||||
|
description: Configure, troubleshoot, and safely modify an Archipelago node or its developer environment using the repository's typed operations, ownership rules, and recovery workflow.
|
||||||
|
metadata:
|
||||||
|
short-description: Work safely on Archipelago systems
|
||||||
|
---
|
||||||
|
|
||||||
|
# Archipelago system work
|
||||||
|
|
||||||
|
Use this skill for node configuration, terminal/developer setup, service
|
||||||
|
diagnosis, network and SSH work, supported app operations, and system changes.
|
||||||
|
For app implementation load `archipelago-app`; for any new or changed UI also
|
||||||
|
load `archipelago-design`.
|
||||||
|
|
||||||
|
Read [system-map.md](references/system-map.md) before acting. Read `AGENTS.md`
|
||||||
|
and the relevant project documentation in the current checkout.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Identify the node, repository/worktree, owner, and whether the request is
|
||||||
|
planning, source work, a disposable test, or a live-node operation.
|
||||||
|
2. Inspect current state before changing it. Prefer `archy`/Archipelago RPC
|
||||||
|
operations and existing scripts over direct edits or ad-hoc service commands.
|
||||||
|
3. Preserve wallets, app data, credentials, user uninstall decisions, and
|
||||||
|
unrelated services. Back up only the scoped state before a mutation.
|
||||||
|
4. Make the smallest reversible change. Keep generated state separate from
|
||||||
|
user overrides; never edit generated or packaged files when an owned source
|
||||||
|
or managed override exists.
|
||||||
|
5. Verify the actual result and report source tests, disposable integration,
|
||||||
|
live-node acceptance, and packaged-artifact checks separately.
|
||||||
|
|
||||||
|
For repository work, use a dedicated worktree and focused branch. For backend
|
||||||
|
unit tests use `scripts/test-backend-isolated.sh`; do not run unrestricted
|
||||||
|
`cargo test` on a node with installed apps. Do not publish OTA, ISO, catalog,
|
||||||
|
or Git mirrors from this skill unless that publication is explicitly requested
|
||||||
|
and every release gate is satisfied.
|
||||||
|
|
||||||
|
## Terminal and sessions
|
||||||
|
|
||||||
|
Treat terminal close as detach. Resume the existing named session; never create
|
||||||
|
a duplicate shell or replay a lost command. A reboot can restore workspace and
|
||||||
|
Codex conversation metadata but cannot restore the old process. Distinguish
|
||||||
|
running, detached, ended, and interrupted states in user-facing output.
|
||||||
|
|
||||||
|
Keep credentials, wallet material, terminal output, and command contents out of
|
||||||
|
diagnostics and support bundles. A terminal is a privileged capability: verify
|
||||||
|
the authenticated owner, origin, attachment grant, and account boundary before
|
||||||
|
any PTY bytes flow.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [system map and safe recipes](references/system-map.md)
|
||||||
|
- [app and design routing](references/routing.md)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "Archipelago system"
|
||||||
|
short_description: "Work safely on Archipelago systems"
|
||||||
|
brand_color: "#F7931A"
|
||||||
|
default_prompt: "Use $archipelago to inspect and safely change this Archipelago node."
|
||||||
|
policy:
|
||||||
|
allow_implicit_invocation: true
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Skill routing
|
||||||
|
|
||||||
|
Load `archipelago-app` for manifests, containers, app previews, lifecycle,
|
||||||
|
credentials, signer integration, or app packaging. Load `archipelago-design` for
|
||||||
|
any newly authored or changed UI. For backend-only work, use only the system
|
||||||
|
skill and the relevant project docs.
|
||||||
|
|
||||||
|
Planning stays planning. A source change, node mutation, deployment, or
|
||||||
|
publication requires that explicit scope from the user. A skill supplies
|
||||||
|
workflow knowledge; it does not grant additional privileges.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# System map and safe recipes
|
||||||
|
|
||||||
|
The native backend is `core/archipelago`; the Vue dashboard is `neode-ui`; ISO
|
||||||
|
and first-boot material lives under `image-recipe` and `scripts`; app manifests
|
||||||
|
are under `apps/<id>/manifest.yml`. Read `CLAUDE.md`, `AGENTS.md`, and the current
|
||||||
|
release checklist before release work.
|
||||||
|
|
||||||
|
Use the existing typed RPC and orchestration layers for app lifecycle, network,
|
||||||
|
wallet, identity, and service operations. Inspect a matching handler before
|
||||||
|
adding an endpoint. Preserve the existing session cookie, CSRF, Origin, and
|
||||||
|
app-gate protections.
|
||||||
|
|
||||||
|
For source tests, run frontend checks from `neode-ui`. Backend unit tests run
|
||||||
|
only through `scripts/test-backend-isolated.sh`; live host checks must be named
|
||||||
|
and explicit. Do not touch real wallet/payment/channel state for a test fixture.
|
||||||
|
|
||||||
|
Developer changes belong in a dedicated worktree. Keep generated catalog,
|
||||||
|
runtime payload, release artifacts, and local node state separate until the
|
||||||
|
request explicitly includes packaging or deployment.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FROM node:22-alpine AS build
|
||||||
|
WORKDIR /src
|
||||||
|
COPY package.json vite.config.ts index.html ./
|
||||||
|
COPY src ./src
|
||||||
|
RUN npm install --ignore-scripts && npm run build
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine
|
||||||
|
COPY --from=build /src/dist /usr/share/nginx/html
|
||||||
|
COPY <<'EOF' /etc/nginx/conf.d/default.conf
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
location / { try_files $uri $uri/ /index.html; }
|
||||||
|
}
|
||||||
|
EOF
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Archipelago app starter
|
||||||
|
|
||||||
|
This is a small standalone Vue/Vite app that demonstrates Archipelago's initial
|
||||||
|
design contract: semantic `--archy-*` tokens, dark native controls, responsive
|
||||||
|
cards, bottom actions, visible focus, 44px targets, and loading/empty/error/
|
||||||
|
success states. It intentionally has no dashboard dependency or host handshake.
|
||||||
|
|
||||||
|
Run `npm install && npm run dev` from this directory. Before packaging, copy the
|
||||||
|
source into a project, replace the demo state with a real API, pin dependencies,
|
||||||
|
and validate the manifest using the repository app developer guide. The Docker
|
||||||
|
file is a starter build example; it is not a substitute for lifecycle acceptance.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="theme-color" content="#0a0a0a" />
|
||||||
|
<title>Archipelago app</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
app:
|
||||||
|
id: archipelago-app-starter
|
||||||
|
name: Archipelago App Starter
|
||||||
|
version: 0.1.0
|
||||||
|
description: A minimal Archipelago design-system starter with responsive states.
|
||||||
|
container:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
tag: localhost/archipelago-app-starter:0.1.0
|
||||||
|
resources:
|
||||||
|
cpu_limit: 1
|
||||||
|
memory_limit: 128Mi
|
||||||
|
disk_limit: 256Mi
|
||||||
|
security:
|
||||||
|
capabilities: []
|
||||||
|
readonly_root: true
|
||||||
|
no_new_privileges: true
|
||||||
|
network_policy: isolated
|
||||||
|
ports:
|
||||||
|
- host: 8180
|
||||||
|
container: 8080
|
||||||
|
protocol: tcp
|
||||||
|
bind: 127.0.0.1
|
||||||
|
auth: gated
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: /var/lib/archipelago/archipelago-app-starter
|
||||||
|
target: /data
|
||||||
|
options: [rw]
|
||||||
|
health_check:
|
||||||
|
type: http
|
||||||
|
endpoint: http://127.0.0.1:8080
|
||||||
|
path: /
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
interfaces:
|
||||||
|
main:
|
||||||
|
name: Archipelago App Starter
|
||||||
|
description: Responsive design-system starter
|
||||||
|
type: ui
|
||||||
|
port: 8180
|
||||||
|
protocol: http
|
||||||
|
path: /
|
||||||
|
metadata:
|
||||||
|
category: development
|
||||||
|
tier: optional
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "archipelago-app-starter",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 127.0.0.1",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview --host 127.0.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
"vite": "^7.2.2",
|
||||||
|
"typescript": "~5.9.3",
|
||||||
|
"vue": "^3.5.24"
|
||||||
|
},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<main class="archy-shell" aria-labelledby="page-title">
|
||||||
|
<header class="archy-header">
|
||||||
|
<div>
|
||||||
|
<p class="archy-eyebrow">ARCHIPELAGO APP</p>
|
||||||
|
<h1 id="page-title">Project workspace</h1>
|
||||||
|
<p class="archy-muted">A small, useful starting point for a node app.</p>
|
||||||
|
</div>
|
||||||
|
<button class="archy-button archy-button-secondary" type="button" @click="reload">
|
||||||
|
Refresh
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="archy-card" aria-labelledby="items-title">
|
||||||
|
<div class="archy-section-heading">
|
||||||
|
<div>
|
||||||
|
<p class="archy-eyebrow">YOUR DATA</p>
|
||||||
|
<h2 id="items-title">Items</h2>
|
||||||
|
</div>
|
||||||
|
<button class="archy-button" type="button" @click="addItem">Add item</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="status === 'loading'" class="archy-state" role="status">Loading items…</div>
|
||||||
|
<div v-else-if="status === 'error'" class="archy-state archy-state-error" role="alert">
|
||||||
|
<strong>Items are unavailable.</strong>
|
||||||
|
<span>Check the app service and try again.</span>
|
||||||
|
<button class="archy-button archy-button-secondary" type="button" @click="reload">Try again</button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="items.length === 0" class="archy-state">
|
||||||
|
<strong>No items yet.</strong>
|
||||||
|
<span>Create one to see the app's normal success state.</span>
|
||||||
|
<button class="archy-button archy-button-secondary" type="button" @click="addItem">Create first item</button>
|
||||||
|
</div>
|
||||||
|
<ul v-else class="archy-list" aria-live="polite">
|
||||||
|
<li v-for="item in items" :key="item.id" class="archy-list-row">
|
||||||
|
<div>
|
||||||
|
<strong>{{ item.name }}</strong>
|
||||||
|
<span class="archy-muted">{{ item.detail }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="archy-status">Ready</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p class="archy-footnote">This starter is standalone-safe and does not require a host handshake.</p>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
type Item = { id: number; name: string; detail: string }
|
||||||
|
const status = ref<'ready' | 'loading' | 'error'>('ready')
|
||||||
|
const items = ref<Item[]>([])
|
||||||
|
let nextId = 1
|
||||||
|
|
||||||
|
function addItem() {
|
||||||
|
status.value = 'ready'
|
||||||
|
items.value.push({ id: nextId, name: `Item ${nextId}`, detail: 'Persist this through your app API.' })
|
||||||
|
nextId += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
status.value = 'loading'
|
||||||
|
window.setTimeout(() => { status.value = 'ready' }, 250)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import './styles.css'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
:root {
|
||||||
|
color-scheme: dark;
|
||||||
|
--archy-canvas: #0a0a0a;
|
||||||
|
--archy-surface: rgba(0, 0, 0, 0.65);
|
||||||
|
--archy-surface-muted: rgba(255, 255, 255, 0.06);
|
||||||
|
--archy-border: rgba(255, 255, 255, 0.18);
|
||||||
|
--archy-text: rgba(255, 255, 255, 0.92);
|
||||||
|
--archy-muted: rgba(255, 255, 255, 0.58);
|
||||||
|
--archy-accent: #fb923c;
|
||||||
|
--archy-success: #4ade80;
|
||||||
|
--archy-danger: #f87171;
|
||||||
|
--archy-radius-card: 16px;
|
||||||
|
--archy-radius-control: 12px;
|
||||||
|
--archy-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
|
||||||
|
--archy-space-1: 4px;
|
||||||
|
--archy-space-2: 8px;
|
||||||
|
--archy-space-3: 12px;
|
||||||
|
--archy-space-4: 16px;
|
||||||
|
--archy-space-6: 24px;
|
||||||
|
--archy-space-8: 32px;
|
||||||
|
font-family: Avenir Next, system-ui, sans-serif;
|
||||||
|
background: var(--archy-canvas);
|
||||||
|
color: var(--archy-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; min-width: 320px; background: var(--archy-canvas); }
|
||||||
|
button { font: inherit; }
|
||||||
|
button:focus-visible { outline: 2px solid var(--archy-accent); outline-offset: 3px; }
|
||||||
|
|
||||||
|
.archy-shell { width: min(100% - 32px, 960px); margin: 0 auto; padding: 40px 0 56px; }
|
||||||
|
.archy-header, .archy-section-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--archy-space-4); }
|
||||||
|
.archy-header { margin-bottom: var(--archy-space-8); }
|
||||||
|
.archy-eyebrow { margin: 0 0 var(--archy-space-2); color: var(--archy-accent); font-size: 0.72rem; font-weight: 800; letter-spacing: 0.12em; }
|
||||||
|
h1, h2 { margin: 0; line-height: 1.15; }
|
||||||
|
h1 { font-size: clamp(1.8rem, 5vw, 2.8rem); }
|
||||||
|
h2 { font-size: 1.25rem; }
|
||||||
|
.archy-muted, .archy-footnote { color: var(--archy-muted); }
|
||||||
|
.archy-card { padding: var(--archy-space-6); background: var(--archy-surface); border: 1px solid var(--archy-border); border-radius: var(--archy-radius-card); box-shadow: var(--archy-shadow); }
|
||||||
|
.archy-section-heading { align-items: center; margin-bottom: var(--archy-space-6); }
|
||||||
|
.archy-button { min-height: 44px; padding: 10px 18px; border: 1px solid rgba(251, 146, 60, 0.35); border-radius: var(--archy-radius-control); background: rgba(251, 146, 60, 0.2); color: #fed7aa; cursor: pointer; }
|
||||||
|
.archy-button:hover { background: rgba(251, 146, 60, 0.3); }
|
||||||
|
.archy-button-secondary { border-color: var(--archy-border); background: var(--archy-surface-muted); color: var(--archy-text); }
|
||||||
|
.archy-state { display: grid; gap: var(--archy-space-3); padding: var(--archy-space-6); border-radius: var(--archy-radius-control); background: var(--archy-surface-muted); color: var(--archy-muted); }
|
||||||
|
.archy-state strong { color: var(--archy-text); }
|
||||||
|
.archy-state-error { border: 1px solid rgba(248, 113, 113, 0.35); }
|
||||||
|
.archy-state-error strong { color: var(--archy-danger); }
|
||||||
|
.archy-list { display: grid; gap: var(--archy-space-2); padding: 0; margin: 0; list-style: none; }
|
||||||
|
.archy-list-row { display: flex; align-items: center; justify-content: space-between; gap: var(--archy-space-4); padding: var(--archy-space-4); border-radius: var(--archy-radius-control); background: var(--archy-surface-muted); }
|
||||||
|
.archy-list-row div { display: grid; gap: var(--archy-space-1); min-width: 0; }
|
||||||
|
.archy-status { color: var(--archy-success); font-size: 0.8rem; }
|
||||||
|
.archy-footnote { margin: var(--archy-space-4) 0 0; font-size: 0.8rem; }
|
||||||
|
@media (max-width: 560px) { .archy-shell { width: min(100% - 24px, 960px); padding-top: 24px; } .archy-header { flex-direction: column; } .archy-header .archy-button { width: 100%; } .archy-card { padding: var(--archy-space-4); } .archy-section-heading { align-items: stretch; flex-direction: column; } .archy-section-heading .archy-button { width: 100%; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { scroll-behavior: auto !important; transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; } }
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({ plugins: [vue()] })
|
||||||
Reference in New Issue
Block a user