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