Compare commits

...
7 Commits
Author SHA1 Message Date
archipelagoandClaude Fable 5 d422218c6b chore(demo): rebuild bundled AIUI with the Routstr provider picker
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:41:17 -04:00
archipelagoandClaude Fable 5 a4be1b4b7d feat(aiui): Routstr tops the model picker with the full live catalog
New 'Routstr (sats)' category sits FIRST in the model dropdown
(operator request 2026-08-14), listing every model the node's
/aiui/api/routstr/models proxy returns (432 live today) — the picker
panel now scrolls (max-h 70vh) instead of overflowing. Selecting a
Routstr model routes the turn through the node's paid completions
proxy, and the explicit choice wins even when AIUI runs embedded in
Archy — a selection, not a fallback. Node refusals (no budget armed,
budget spent, wallet can't fund) surface verbatim in chat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:41:17 -04:00
archipelagoandClaude Fable 5 e3bd340725 feat(aiui): Routstr as an explicit, session-gated AI provider path
The D-04 Routstr leg was fallback-only — never user-selectable, and its
Nostr discovery parses a docs-shaped event content ({endpoints, models,
pricing}) that live kind-38421 announcements don't actually carry
({name, about}), so it could never match a real provider. This adds the
explicit path AIUI's model picker needs: /aiui/api/routstr/models
passes through the live aggregator catalog (the instance routstr.com's
own frontend queries; the canonical api.routstr.com 404s), and
/aiui/api/routstr/chat/completions makes one paid, non-streaming,
OpenAI-shaped call — session-gated, egress-screened (S3), refused
without an armed operator budget (D-05), paid via auto_pay_token,
change and refused-request tokens redeemed back into the wallet so a
failed attempt nets zero (verified live: quoted=1 reclaimed=1 net=0).
nginx template gains the location in both server blocks (T-13-15).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 13:41:07 -04:00
archipelagoandClaude Fable 5 ced95a60d1 feat(wallet): Lightning gets the arrival screen; copy buttons unified
Demo images / Build & push demo images (push) Successful in 3m27s
- lnd.createinvoice now returns r_hash_hex; new lnd.invoicestatus RPC
  looks the invoice up (SETTLED + amt_paid_sat). E2E-verified on this
  box: real invoice minted, status polls settled:false until paid.
- Receive modal: Lightning polls settlement every 3s and flips to the
  on-chain-style success view — straight to the green check + amount
  (no broadcast step; settlement is final). Raw bolt11 text removed:
  QR + CopyButton only. State fully reset per open/close.
- CopyButton is now the wallet's only copy affordance: the ark-address
  and ecash-token holdouts swapped in, their ad-hoc handlers deleted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 09:43:30 -04:00
archipelagoandClaude Fable 5 a0bd9e53f8 feat(settings): CA generation from the UI; Routstr panel beside the API key
Demo images / Build & push demo images (push) Successful in 3m36s
WebUI RULE (operator, 2026-08-14): never point users at a terminal. The
certificate section told users to run setup-node-ca.sh by hand — it now
has a Generate button backed by system.node-ca.generate, which runs the
idempotent script server-side (live-tested: generated and /ca.crt serves).
Routstr budget panel moves directly under the Claude API key card.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 09:19:05 -04:00
archipelagoandClaude Fable 5 6137762786 feat(settings): Routstr AI budget panel — the integration's missing switch
Demo images / Build & push demo images (push) Successful in 3m29s
The Routstr backend (Cashu-paid inference fallback, shipped 1.7.127) was
fully wired but permanently dormant: its D-05 gate requires an
operator-set sats allowance and nothing in the UI ever called
assistant.budget-get/set — default 0 meant never selected. New Settings
panel (below AI Data Access): allowance/spent/remaining, set-allowance
with 0-disables semantics, enabled/off badge. Backend untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 08:57:53 -04:00
archipelagoandClaude Fable 5 8d1fda29fa feat(federation): peer requests take the stage — clustered and faced
Demo images / Build & push demo images (push) Successful in 3m10s
Requests were spread evenly around the orbit, so they could sit BEHIND
the globe: the blinking call-to-action was invisible and the chart read
as mis-scaled until the user hand-rotated. Now requests cluster tightly
at one stage angle (spacing shrinks as count grows) and, when a NEW
request arrives, the camera steers to face the cluster front-and-center
(depth ∝ sin(angle−rotY); front = angle+π/2) — arrival only, so a user
who rotates away isn't fought. Static/reduced-motion paths snap+render.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 07:36:38 -04:00
99 changed files with 1162 additions and 168 deletions
@@ -93,10 +93,11 @@ describe('useAI', () => {
expect(activeModel.value).toBe('echo') expect(activeModel.value).toBe('echo')
}) })
it('lists available providers with models', () => { it('lists available providers with models, Routstr first', () => {
const { availableProviders } = useAI() const { availableProviders } = useAI()
expect(availableProviders.value.length).toBe(3) expect(availableProviders.value.length).toBe(4)
const ids = availableProviders.value.map(p => p.id) const ids = availableProviders.value.map(p => p.id)
expect(ids[0]).toBe('routstr')
expect(ids).toContain('claude') expect(ids).toContain('claude')
expect(ids).toContain('openrouter') expect(ids).toContain('openrouter')
expect(ids).toContain('mock') expect(ids).toContain('mock')
@@ -119,7 +119,7 @@
<Transition name="picker"> <Transition name="picker">
<div <div
v-if="showModelPicker" v-if="showModelPicker"
class="fixed z-[9999] path-glass-card header-overlay-panel p-3 space-y-3 animate-fade-up-fast shadow-2xl min-w-[220px]" class="fixed z-[9999] path-glass-card header-overlay-panel p-3 space-y-3 animate-fade-up-fast shadow-2xl min-w-[220px] max-h-[70vh] overflow-y-auto"
:style="modelPickerDropdownStyle" :style="modelPickerDropdownStyle"
@click.stop @click.stop
> >
@@ -332,7 +332,7 @@ const modelDisplayName = computed(() => {
}) })
function selectModel(providerId: string, modelId: string) { function selectModel(providerId: string, modelId: string) {
setProvider(providerId as 'claude' | 'openrouter' | 'mock') setProvider(providerId as 'routstr' | 'claude' | 'openrouter' | 'mock')
setModel(modelId) setModel(modelId)
showModelPicker.value = false showModelPicker.value = false
} }
+118 -5
View File
@@ -13,12 +13,14 @@ import { useCodeContext } from '@/composables/useCodeContext'
import { apiFetch } from '@/utils/api-fetch' import { apiFetch } from '@/utils/api-fetch'
import { useSettingsStore } from '@/stores/settings' import { useSettingsStore } from '@/stores/settings'
type Provider = 'claude' | 'openrouter' | 'mock' type Provider = 'routstr' | 'claude' | 'openrouter' | 'mock'
// API paths are relative to the base URL so they work both in dev (/) and Archy (/aiui/) // API paths are relative to the base URL so they work both in dev (/) and Archy (/aiui/)
const BASE = import.meta.env.BASE_URL || '/' const BASE = import.meta.env.BASE_URL || '/'
const CLAUDE_PATH = `${BASE}api/claude/v1/messages` const CLAUDE_PATH = `${BASE}api/claude/v1/messages`
const OPENROUTER_PATH = `${BASE}api/openrouter` const OPENROUTER_PATH = `${BASE}api/openrouter`
const ROUTSTR_MODELS_PATH = `${BASE}api/routstr/models`
const ROUTSTR_CHAT_PATH = `${BASE}api/routstr/chat/completions`
import { mockFilms } from '@/mocks/films' import { mockFilms } from '@/mocks/films'
import { mockSongs } from '@/mocks/songs' import { mockSongs } from '@/mocks/songs'
@@ -148,8 +150,41 @@ function looksLikeMissingApiKey(err: string): boolean {
) )
} }
// ─── Routstr model catalog (fetched from the node's session-gated proxy) ───
// The node forwards the live Routstr aggregator's /v1/models; entries carry
// sats_pricing so completions are Cashu-paid against the operator's budget.
const routstrModels = ref<{ id: string; name: string }[]>([])
let routstrModelsFetched = false
async function refreshRoutstrModels() {
if (routstrModelsFetched) return
routstrModelsFetched = true
try {
const res = await apiFetch(ROUTSTR_MODELS_PATH)
if (!res.ok) return
const data = await res.json()
if (Array.isArray(data?.data)) {
routstrModels.value = data.data
.filter((m: Record<string, unknown>) => typeof m.id === 'string')
.map((m: Record<string, unknown>) => ({
id: m.id as string,
name: (m.name as string) || (m.id as string),
}))
}
} catch {
routstrModelsFetched = false // allow a retry on the next send/open
}
}
const availableProviders = computed(() => { const availableProviders = computed(() => {
const providers: { id: Provider; name: string; models: { id: string; name: string }[] }[] = [ const providers: { id: Provider; name: string; models: { id: string; name: string }[] }[] = [
{
id: 'routstr',
name: 'Routstr (sats)',
models: routstrModels.value.length > 0
? routstrModels.value
: [{ id: 'routstr-unavailable', name: 'No models — node offline?' }],
},
{ {
id: 'claude', id: 'claude',
name: 'Claude (Max)', name: 'Claude (Max)',
@@ -381,6 +416,71 @@ async function streamOpenRouter(
}, onError, signal) }, onError, signal)
} }
/**
* Routstr: one paid, NON-streaming, OpenAI-shaped completion through the
* node's session-gated `/aiui/api/routstr/` forwarder. The node quotes a
* price from the live catalog, pays with a Cashu token against the
* operator's budget (Settings → System → Routstr AI budget), redeems the
* change, and passes the provider's JSON back. The full answer is emitted
* as a single token — streaming across a paid hop is the planned follow-up.
*/
async function streamRoutstr(
messages: ChatMessage[],
onToken: (text: string) => void,
onError: (err: string) => void,
systemPrompt: string,
signal?: AbortSignal,
): Promise<void> {
const wireMessages = [
{ role: 'system' as const, content: systemPrompt },
...messages.map((m) => ({ role: m.role as 'user' | 'assistant', content: m.content })),
]
const res = await apiFetch(ROUTSTR_CHAT_PATH, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: activeModel.value,
messages: wireMessages,
stream: false,
}),
signal,
})
const bodyText = await res.text().catch(() => '')
if (!res.ok) {
// The node's refusals carry a plain-language error.message (budget not
// set, budget spent, wallet can't fund) — surface it verbatim.
let msg = `Routstr error ${res.status}`
try {
const parsed = JSON.parse(bodyText)
// Node refusals use {error:{message}}; the upstream provider nests
// its own as {detail:{error:{message}}} or a plain {detail:"..."}.
const detail = parsed?.detail
msg =
parsed?.error?.message ??
detail?.error?.message ??
(typeof detail === 'string' ? detail : undefined) ??
msg
} catch { /* keep the status-only message */ }
onError(msg)
return
}
if (signal?.aborted) return
try {
const parsed = JSON.parse(bodyText)
const text = parsed?.choices?.[0]?.message?.content
if (typeof text === 'string' && text.length > 0) {
onToken(text)
} else {
onError('Routstr returned an empty response')
}
} catch {
onError('Routstr returned a malformed response')
}
}
/** /**
* Embedded-mode chat delegation (D-01/D-17): when AIUI is running inside * Embedded-mode chat delegation (D-01/D-17): when AIUI is running inside
* Archy, the model call, the tool-calling loop, and the model key all live * Archy, the model call, the tool-calling loop, and the model key all live
@@ -619,7 +719,11 @@ export async function streamWithModel(
activeModel.value = model activeModel.value = model
try { try {
if (useArchy().isEmbedded.value) { if (provider === 'routstr') {
// Explicitly chosen Routstr wins even embedded in Archy — the whole
// point of the picker entry is that it is a selection, not a fallback.
await streamRoutstr(history, onToken, onError, 'You are a helpful assistant.', signal)
} else if (useArchy().isEmbedded.value) {
// D-17: embedded mode delegates the loop, the tools and the key to // D-17: embedded mode delegates the loop, the tools and the key to
// Archy — provider/model selection here doesn't apply node-side. // Archy — provider/model selection here doesn't apply node-side.
await streamViaArchy(history, onToken, onError, signal) await streamViaArchy(history, onToken, onError, signal)
@@ -638,8 +742,9 @@ export async function streamWithModel(
export function useAI() { export function useAI() {
const chatStore = useChatStore() const chatStore = useChatStore()
// Fetch Wavlake catalog on first use (non-blocking) // Fetch Wavlake + Routstr catalogs on first use (non-blocking)
refreshWavlakeCatalog() refreshWavlakeCatalog()
refreshRoutstrModels()
function stopGeneration() { function stopGeneration() {
if (currentAbort) { if (currentAbort) {
@@ -712,7 +817,11 @@ export function useAI() {
const genParams = getConversationParams(chatStore) const genParams = getConversationParams(chatStore)
try { try {
if (useArchy().isEmbedded.value) { if (provider === 'routstr') {
// Explicitly chosen Routstr wins even embedded in Archy — a
// selection, not a fallback.
await streamRoutstr(history, onToken, onError, systemPrompt, signal)
} else if (useArchy().isEmbedded.value) {
// D-17: embedded mode delegates the loop, the tools and the key to // D-17: embedded mode delegates the loop, the tools and the key to
// Archy — provider/model selection here doesn't apply node-side. // Archy — provider/model selection here doesn't apply node-side.
await streamViaArchy(history, onToken, onError, signal) await streamViaArchy(history, onToken, onError, signal)
@@ -828,7 +937,11 @@ export function useAI() {
const genParams = getConversationParams(chatStore) const genParams = getConversationParams(chatStore)
try { try {
if (useArchy().isEmbedded.value) { if (provider === 'routstr') {
// Explicitly chosen Routstr wins even embedded in Archy — a
// selection, not a fallback.
await streamRoutstr(history, onToken, onError, systemPrompt, signal)
} else if (useArchy().isEmbedded.value) {
// D-17: embedded mode delegates the loop, the tools and the key to // D-17: embedded mode delegates the loop, the tools and the key to
// Archy — provider/model selection here doesn't apply node-side. // Archy — provider/model selection here doesn't apply node-side.
await streamViaArchy(history, onToken, onError, signal) await streamViaArchy(history, onToken, onError, signal)
+9
View File
@@ -6,6 +6,7 @@ mod node_message;
mod proxy; mod proxy;
mod remote_input; mod remote_input;
mod remote_relay; mod remote_relay;
mod routstr_proxy;
mod websocket; mod websocket;
use crate::api::rpc::RpcHandler; use crate::api::rpc::RpcHandler;
@@ -449,6 +450,14 @@ impl ApiHandler {
self.handle_model_proxy(req_with_bytes, p).await self.handle_model_proxy(req_with_bytes, p).await
} }
// AIUI Routstr proxy — the explicit, user-selected Routstr
// provider (model catalog + Cashu-paid completions), same
// session-gate discipline as the model proxy above. D-05: paid
// requests are refused unless the operator has armed a budget.
(_, p) if p.starts_with("/aiui/api/routstr/") => {
self.handle_routstr_proxy(req_with_bytes, p).await
}
// Health — unauthenticated, returns JSON with service status // Health — unauthenticated, returns JSON with service status
(Method::GET, "/health") => { (Method::GET, "/health") => {
let recovery_complete = crate::crash_recovery::is_recovery_complete(); let recovery_complete = crate::crash_recovery::is_recovery_complete();
@@ -84,14 +84,14 @@ async fn route_model_proxy(
/// call back into `ApiHandler::is_authenticated` — keeping this small and /// call back into `ApiHandler::is_authenticated` — keeping this small and
/// dependency-free is what makes the 401 behaviour unit-testable without /// dependency-free is what makes the 401 behaviour unit-testable without
/// paying for a full `ApiHandler` in every test. /// paying for a full `ApiHandler` in every test.
async fn is_authenticated(session_store: &SessionStore, headers: &HeaderMap) -> bool { pub(super) async fn is_authenticated(session_store: &SessionStore, headers: &HeaderMap) -> bool {
match session::extract_session_cookie(headers) { match session::extract_session_cookie(headers) {
Some(token) => session_store.validate(&token).await, Some(token) => session_store.validate(&token).await,
None => false, None => false,
} }
} }
fn unauthorized() -> Response<Body> { pub(super) fn unauthorized() -> Response<Body> {
let body = serde_json::json!({ "error": "Unauthorized" }); let body = serde_json::json!({ "error": "Unauthorized" });
Response::builder() Response::builder()
.status(StatusCode::UNAUTHORIZED) .status(StatusCode::UNAUTHORIZED)
@@ -119,7 +119,7 @@ fn key_not_configured() -> Response<Body> {
/// backends' egress screen never sees the forwarder path — the standalone /// backends' egress screen never sees the forwarder path — the standalone
/// frontend posts FULL history and images straight here — so the forwarder /// frontend posts FULL history and images straight here — so the forwarder
/// screens for itself. 400, plain-language, never naming what matched. /// screens for itself. 400, plain-language, never naming what matched.
fn blocked_secret_shaped() -> Response<Body> { pub(super) fn blocked_secret_shaped() -> Response<Body> {
let body = serde_json::json!({ let body = serde_json::json!({
"error": "Blocked: this request contained secret-shaped content (e.g. a seed phrase, key, or token). It was not sent anywhere." "error": "Blocked: this request contained secret-shaped content (e.g. a seed phrase, key, or token). It was not sent anywhere."
}); });
@@ -134,12 +134,12 @@ fn blocked_secret_shaped() -> Response<Body> {
/// the assistant's secret-shape rules (G-B1) with this node's own secrets /// the assistant's secret-shape rules (G-B1) with this node's own secrets
/// as the deny corpus. Returns Some(kind) — kind only, never the value — /// as the deny corpus. Returns Some(kind) — kind only, never the value —
/// when the content must not leave. /// when the content must not leave.
async fn forward_screen(text: &str, data_dir: &Path) -> Option<&'static str> { pub(super) async fn forward_screen(text: &str, data_dir: &Path) -> Option<&'static str> {
let secrets = crate::assistant::egress::load_known_secrets(&data_dir.join("secrets")).await; let secrets = crate::assistant::egress::load_known_secrets(&data_dir.join("secrets")).await;
crate::assistant::egress::scan_secret_shapes(text, &secrets) crate::assistant::egress::scan_secret_shapes(text, &secrets)
} }
fn bad_gateway(msg: &str) -> Response<Body> { pub(super) fn bad_gateway(msg: &str) -> Response<Body> {
let body = serde_json::json!({ "error": msg }); let body = serde_json::json!({ "error": msg });
Response::builder() Response::builder()
.status(StatusCode::BAD_GATEWAY) .status(StatusCode::BAD_GATEWAY)
@@ -331,7 +331,7 @@ async fn forward(
/// same shape as `proxy.rs`'s peer-content Range streamer — so a /// same shape as `proxy.rs`'s peer-content Range streamer — so a
/// token-by-token reply doesn't wait for the full response before the first /// token-by-token reply doesn't wait for the full response before the first
/// byte reaches the browser. /// byte reaches the browser.
fn stream_response(resp: reqwest::Response) -> Result<Response<Body>> { pub(super) fn stream_response(resp: reqwest::Response) -> Result<Response<Body>> {
let status = resp.status().as_u16(); let status = resp.status().as_u16();
let headers = resp.headers().clone(); let headers = resp.headers().clone();
let mut builder = Response::builder().status(status); let mut builder = Response::builder().status(status);
@@ -0,0 +1,535 @@
//! Session-gated forwarder for `/aiui/api/routstr/*` — the explicit,
//! user-selected Routstr path (as opposed to `assistant/backends/routstr.rs`,
//! which is the D-04 fallback leg the operator never chooses directly).
//!
//! AIUI's model picker lists Routstr as a first-class provider; selecting one
//! of its models routes chat completions through here. Same discipline as
//! `model_proxy.rs`: auth is re-derived from the request's own session cookie
//! (never trusted to nginx), inbound `authorization`/`cookie` headers are
//! never forwarded, and every outbound body is egress-screened (S3) before it
//! leaves the node.
//!
//! Payment is Cashu, D-05-gated end to end: a request is refused unless the
//! operator has set a non-zero Routstr allowance (Settings → System), the
//! quoted price fits the remaining allowance, and `auto_pay_token` (the ONE
//! budget-capped payment primitive, T-13-89) agrees to build the token. The
//! provider's change (`X-Cashu` / `X-Cashu-Refund` response headers, per
//! docs.routstr.com) is redeemed back into the node wallet and only the net
//! is recorded against the allowance.
//!
//! Upstream is the public Routstr aggregator instance routstr.com itself
//! ships against (verified live 2026-08-14: `/v1/models` serves the full
//! catalog with `sats_pricing`; the canonical `api.routstr.com` host 404s).
//! Making the instance operator-configurable — or sourcing it from the Nostr
//! provider announcements once those carry real endpoint/pricing content —
//! is the planned follow-up, not this file's job.
use super::ApiHandler;
use crate::session::SessionStore;
use anyhow::Result;
use hyper::{Body, Method, Request, Response, StatusCode};
use serde_json::{json, Value};
use std::path::Path;
use std::sync::Mutex as StdMutex;
use std::sync::OnceLock;
use std::time::{Duration, Instant};
use super::model_proxy::{
bad_gateway, blocked_secret_shaped, forward_screen, is_authenticated, unauthorized,
};
/// The live public Routstr aggregator (the same instance routstr.com's own
/// frontend queries for `/v1/providers` and `/v1/models`).
const ROUTSTR_INSTANCE: &str = "https://routstr.otrta.me";
/// Generation cap forced onto every forwarded completion — never unbounded
/// (T-13-88), and the completion half of the price quote is arithmetic over
/// exactly this figure.
const MAX_COMPLETION_TOKENS: u64 = 1024;
/// Same round-trip ceiling as `model_proxy.rs`'s Claude/Ollama forwarders.
const FORWARD_TIMEOUT_SECS: u64 = 180;
/// Models-catalog cache TTL — mirrors `backends/routstr.rs`'s provider
/// discovery TTL. The catalog prices every chat request, so it cannot be
/// fetched per-message without doubling latency.
const MODELS_CACHE_TTL: Duration = Duration::from_secs(300);
/// One model's sats-denominated pricing, parsed from the aggregator's
/// `/v1/models` entries (`sats_pricing`). Rates are sats PER TOKEN (fractional
/// floats); `request` is a flat per-request fee in sats. Untrusted input — a
/// missing/garbled field parses as 0.0 and simply prices low, which the
/// remaining-allowance ceiling still caps.
#[derive(Debug, Clone, Default, serde::Deserialize)]
struct SatsPricing {
#[serde(default)]
prompt: f64,
#[serde(default)]
completion: f64,
#[serde(default)]
request: f64,
}
/// Quote a price in whole sats for one completion call: flat request fee +
/// prompt rate × (payload chars / 4, the usual chars-per-token rule of thumb)
/// + completion rate × the forced `MAX_COMPLETION_TOKENS` cap, +20% margin,
/// rounded up, never below 1. Deliberately a pure function so the arithmetic
/// is unit-testable; deliberately conservative because the provider's change
/// comes back as a Cashu refund and is redeemed — overquoting costs nothing
/// but float, underquoting gets the request rejected upstream.
fn estimate_price_sats(pricing: &SatsPricing, prompt_chars: usize, completion_tokens: u64) -> u64 {
let prompt_tokens = (prompt_chars as f64) / 4.0;
let raw = pricing.request
+ pricing.prompt * prompt_tokens
+ pricing.completion * (completion_tokens as f64);
let with_margin = raw * 1.2;
(with_margin.ceil() as u64).max(1)
}
/// Process-lifetime cache of the aggregator's models catalog (same pattern as
/// `backends/routstr.rs`'s `PROVIDER_CACHE`).
static MODELS_CACHE: OnceLock<StdMutex<Option<(Instant, Value)>>> = OnceLock::new();
fn cached_models() -> Option<Value> {
let cache = MODELS_CACHE.get_or_init(|| StdMutex::new(None));
let guard = cache.lock().expect("routstr models cache poisoned");
guard.as_ref().and_then(|(at, models)| {
if at.elapsed() < MODELS_CACHE_TTL {
Some(models.clone())
} else {
None
}
})
}
fn set_cached_models(models: Value) {
let cache = MODELS_CACHE.get_or_init(|| StdMutex::new(None));
*cache.lock().expect("routstr models cache poisoned") = Some((Instant::now(), models));
}
/// Fetch (or serve cached) the aggregator's `/v1/models` catalog.
async fn fetch_models() -> Result<Value> {
if let Some(cached) = cached_models() {
return Ok(cached);
}
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(20))
.build()?;
let url = format!("{ROUTSTR_INSTANCE}/v1/models");
let resp = client.get(&url).send().await?;
if !resp.status().is_success() {
anyhow::bail!("routstr models upstream returned HTTP {}", resp.status());
}
let models: Value = resp.json().await?;
set_cached_models(models.clone());
Ok(models)
}
/// Find one model's `sats_pricing` in the catalog by exact id.
fn pricing_for_model(models: &Value, model_id: &str) -> Option<SatsPricing> {
models
.get("data")?
.as_array()?
.iter()
.find(|m| m.get("id").and_then(|v| v.as_str()) == Some(model_id))
.and_then(|m| m.get("sats_pricing"))
.and_then(|sp| serde_json::from_value(sp.clone()).ok())
}
fn json_response(status: StatusCode, body: Value) -> Response<Body> {
Response::builder()
.status(status)
.header("Content-Type", "application/json")
.body(Body::from(serde_json::to_vec(&body).unwrap_or_default()))
.unwrap_or_else(|_| Response::new(Body::from("{}")))
}
/// Plain-language refusal naming the UI path that fixes it — never a bare
/// status (RULE: every action needs a UI path).
fn budget_refusal(msg: String) -> Response<Body> {
json_response(
StatusCode::SERVICE_UNAVAILABLE,
json!({ "error": { "message": msg } }),
)
}
impl ApiHandler {
/// Entry point wired into the `/aiui/api/routstr/` arm in `mod.rs` —
/// thin, like `handle_model_proxy`, so the routing/budget logic below is
/// testable without a full `ApiHandler`.
pub(super) async fn handle_routstr_proxy(
&self,
req: Request<Body>,
path: &str,
) -> Result<Response<Body>> {
route_routstr_proxy(&self.session_store, &self.config.data_dir, req, path).await
}
}
async fn route_routstr_proxy(
session_store: &SessionStore,
data_dir: &Path,
req: Request<Body>,
path: &str,
) -> Result<Response<Body>> {
if !is_authenticated(session_store, req.headers()).await {
tracing::warn!("401 routstr proxy {} — session invalid or missing", path);
return Ok(unauthorized());
}
match path.strip_prefix("/aiui/api/routstr/") {
Some("models") if req.method() == Method::GET => forward_models().await,
Some("chat/completions") if req.method() == Method::POST => {
forward_chat(req, data_dir).await
}
_ => Ok(unauthorized()),
}
}
/// GET /aiui/api/routstr/models — the full catalog, passed through so AIUI
/// can render ids/names and show sats pricing. Read-only and unpaid.
async fn forward_models() -> Result<Response<Body>> {
match fetch_models().await {
Ok(models) => Ok(json_response(StatusCode::OK, models)),
Err(e) => {
tracing::warn!("routstr proxy: models upstream failed: {}", e);
Ok(bad_gateway("Routstr model catalog is unreachable"))
}
}
}
/// POST /aiui/api/routstr/chat/completions — one paid, non-streaming,
/// OpenAI-shaped completion. Order matters: screen (S3) → budget gate (D-05,
/// offline) → price quote → pay → forward → redeem change → record net.
async fn forward_chat(req: Request<Body>, data_dir: &Path) -> Result<Response<Body>> {
let payload = hyper::body::to_bytes(req.into_body())
.await
.map_err(|e| anyhow::anyhow!("read request payload: {e}"))?;
let payload_str = String::from_utf8_lossy(&payload).to_string();
// S3: the standalone frontend posts full history straight here with no
// assistant loop (and no egress screen) behind it.
if let Some(kind) = forward_screen(&payload_str, data_dir).await {
tracing::error!(
kind,
"routstr proxy: blocked chat forward — secret-shaped content"
);
return Ok(blocked_secret_shaped());
}
let mut body: Value = match serde_json::from_str(&payload_str) {
Ok(v) => v,
Err(_) => {
return Ok(json_response(
StatusCode::BAD_REQUEST,
json!({ "error": { "message": "request body is not valid JSON" } }),
));
}
};
let Some(model_id) = body.get("model").and_then(|v| v.as_str()).map(String::from) else {
return Ok(json_response(
StatusCode::BAD_REQUEST,
json!({ "error": { "message": "request is missing a model id" } }),
));
};
// D-05 gate, checked before any network I/O: a zero allowance means
// Routstr is refused outright, with the UI path that arms it.
let mut budget = crate::assistant::AssistantBudget::load(data_dir).await;
if budget.allowance_sats == 0 {
return Ok(budget_refusal(
"Routstr is disabled on this node — set a sats budget in Settings → System → \
Routstr AI budget to enable it."
.to_string(),
));
}
let remaining = budget.remaining_sats();
if remaining == 0 {
return Ok(budget_refusal(format!(
"This period's Routstr budget is spent ({} of {} sats). Raise the allowance in \
Settings → System → Routstr AI budget to continue.",
budget.spent_sats, budget.allowance_sats
)));
}
// Price the request from the catalog. An unknown model is a caller bug
// (the dropdown only offers catalog models), not a reason to guess a
// price.
let models = match fetch_models().await {
Ok(m) => m,
Err(e) => {
tracing::warn!("routstr proxy: cannot price request, models fetch failed: {e}");
return Ok(bad_gateway(
"Routstr model catalog is unreachable — cannot price this request",
));
}
};
let Some(pricing) = pricing_for_model(&models, &model_id) else {
return Ok(json_response(
StatusCode::BAD_REQUEST,
json!({ "error": { "message": format!("unknown Routstr model: {model_id}") } }),
));
};
// Force the shape this forwarder actually supports: non-streaming, with
// an explicit, capped generation limit (T-13-88).
let max_tokens = body
.get("max_tokens")
.and_then(|v| v.as_u64())
.unwrap_or(MAX_COMPLETION_TOKENS)
.min(MAX_COMPLETION_TOKENS);
body["stream"] = json!(false);
body["max_tokens"] = json!(max_tokens);
let price_sats = estimate_price_sats(&pricing, payload_str.len(), max_tokens);
if price_sats > remaining {
return Ok(budget_refusal(format!(
"This request quotes ~{price_sats} sats but only {remaining} sats remain in this \
period's Routstr budget (Settings → System → Routstr AI budget)."
)));
}
// Pay via the ONE budget-capped primitive (T-13-89) — same call, same
// mint list as the fallback leg in backends/routstr.rs.
let accepted_mints = crate::wallet::ecash::load_accepted_mints(data_dir)
.await
.map(|m| m.mints)
.unwrap_or_default();
let token = match crate::swarm::payment::auto_pay_token(
data_dir,
&budget.payment_policy(),
&accepted_mints,
price_sats,
)
.await?
{
Some(t) => t,
None => {
return Ok(budget_refusal(format!(
"The node wallet could not fund this request (~{price_sats} sats) — check the \
ecash balance and accepted mints in Settings → Wallet."
)));
}
};
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(FORWARD_TIMEOUT_SECS))
.build()?;
let url = format!("{ROUTSTR_INSTANCE}/v1/chat/completions");
let resp = match client
.post(&url)
.header("Authorization", format!("Bearer {token}"))
.header("Content-Type", "application/json")
.json(&body)
.send()
.await
{
Ok(r) => r,
Err(e) => {
// The token never reached the provider — reclaim it into our own
// wallet so the sats aren't stranded, and record nothing.
match crate::wallet::ecash::receive_token(data_dir, &token).await {
Ok(_) => tracing::info!(
"routstr proxy: upstream send failed ({e}); unsent payment token reclaimed"
),
Err(re) => tracing::warn!(
"routstr proxy: upstream send failed ({e}) AND reclaiming the unsent token \
failed ({re}) — {price_sats} sats may be stranded in the token"
),
}
return Ok(bad_gateway("Routstr provider is unreachable"));
}
};
let status = resp.status();
// Change comes back as a Cashu token header (docs.routstr.com names both
// spellings across versions); redeem it so only the net leaves the
// allowance.
let refund_token = ["x-cashu-refund", "x-cashu"]
.iter()
.find_map(|h| resp.headers().get(*h))
.and_then(|v| v.to_str().ok())
.map(String::from);
let resp_body = resp.bytes().await.unwrap_or_default();
let mut reclaimed = 0u64;
if let Some(refund) = refund_token {
match crate::wallet::ecash::receive_token(data_dir, &refund).await {
Ok(sats) => reclaimed = sats,
Err(e) => tracing::warn!("routstr proxy: redeeming the change token failed: {e}"),
}
} else if !status.is_success() {
// The provider refused the request (e.g. "mint unreachable") and
// sent no change — if it never actually redeemed our token, the
// proofs are still ours to take back. If it DID redeem and then
// failed, this reclaim fails harmlessly and the spend stands.
match crate::wallet::ecash::receive_token(data_dir, &token).await {
Ok(sats) => {
reclaimed = sats;
tracing::info!(
"routstr proxy: upstream refused (HTTP {status}); unredeemed payment token \
reclaimed ({sats} sats)"
);
}
Err(e) => tracing::warn!(
"routstr proxy: upstream refused (HTTP {status}) and the payment token could \
not be reclaimed ({e}) — treating the {price_sats} sats as spent"
),
}
}
let net_sats = price_sats.saturating_sub(reclaimed);
if net_sats > 0 {
if let Err(e) = budget.record_spend(data_dir, net_sats).await {
tracing::warn!(
error = %e,
"routstr proxy: failed to persist the budget spend (the payment itself already happened)"
);
}
}
tracing::info!(
model = %model_id,
quoted = price_sats,
reclaimed,
net = net_sats,
status = %status,
"routstr proxy: forwarded paid chat completion"
);
Ok(Response::builder()
.status(status.as_u16())
.header("Content-Type", "application/json")
.body(Body::from(resp_body))
.unwrap_or_else(|_| Response::new(Body::from("{}"))))
}
#[cfg(test)]
mod tests {
use super::*;
async fn test_store() -> SessionStore {
let path = std::env::temp_dir().join(format!(
"archy-routstr-proxy-test-sessions-{}.json",
rand::RngCore::next_u64(&mut rand::rngs::OsRng)
));
SessionStore::new_for_tests(path)
}
fn req(method: &str, path: &str, cookie: Option<&str>, body: &'static str) -> Request<Body> {
let mut builder = Request::builder().method(method).uri(path);
if let Some(c) = cookie {
builder = builder.header("cookie", format!("session={c}"));
}
builder.body(Body::from(body)).unwrap()
}
#[tokio::test]
async fn models_without_session_is_401() {
let store = test_store().await;
let data_dir = tempfile::tempdir().unwrap();
let r = req("GET", "/aiui/api/routstr/models", None, "");
let resp = route_routstr_proxy(&store, data_dir.path(), r, "/aiui/api/routstr/models")
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
#[tokio::test]
async fn chat_without_session_is_401() {
let store = test_store().await;
let data_dir = tempfile::tempdir().unwrap();
let r = req("POST", "/aiui/api/routstr/chat/completions", None, "{}");
let resp = route_routstr_proxy(
&store,
data_dir.path(),
r,
"/aiui/api/routstr/chat/completions",
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
}
/// D-05: a fresh node (no budget file → zero allowance) refuses the paid
/// path BEFORE any pricing/network I/O — this test runs fully offline.
#[tokio::test]
async fn chat_with_zero_allowance_is_refused_offline() {
let store = test_store().await;
let token = store.create().await;
let data_dir = tempfile::tempdir().unwrap();
let r = req(
"POST",
"/aiui/api/routstr/chat/completions",
Some(&token),
r#"{"model":"some-model","messages":[{"role":"user","content":"hi"}]}"#,
);
let resp = route_routstr_proxy(
&store,
data_dir.path(),
r,
"/aiui/api/routstr/chat/completions",
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::SERVICE_UNAVAILABLE);
let body = hyper::body::to_bytes(resp.into_body()).await.unwrap();
let v: Value = serde_json::from_slice(&body).unwrap();
let msg = v["error"]["message"].as_str().unwrap();
assert!(msg.contains("Settings"), "refusal must name the UI path");
}
#[tokio::test]
async fn chat_body_carrying_bip39_is_blocked() {
let store = test_store().await;
let token = store.create().await;
let data_dir = tempfile::tempdir().unwrap();
std::fs::create_dir_all(data_dir.path().join("secrets")).unwrap();
let r = req(
"POST",
"/aiui/api/routstr/chat/completions",
Some(&token),
r#"{"model":"m","messages":[{"role":"user","content":"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"}]}"#,
);
let resp = route_routstr_proxy(
&store,
data_dir.path(),
r,
"/aiui/api/routstr/chat/completions",
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
#[test]
fn price_estimate_is_conservative_and_never_zero() {
// A free/garbled pricing entry still quotes at least 1 sat.
assert_eq!(estimate_price_sats(&SatsPricing::default(), 100, 1024), 1);
// Live-catalog-shaped numbers (deepseek-v4-flash, 2026-08-14):
// request 0.001, prompt ~0.000178/tok, completion ~0.000267/tok.
let p = SatsPricing {
prompt: 0.000178,
completion: 0.000267,
request: 0.001,
};
let quote = estimate_price_sats(&p, 4000, 1024);
// ~0.18 + ~0.27 + flat, with margin → rounds up to 1 sat.
assert_eq!(quote, 1);
// A pricier model scales with the prompt.
let expensive = SatsPricing {
prompt: 0.05,
completion: 0.1,
request: 1.0,
};
let quote = estimate_price_sats(&expensive, 40_000, 1024);
assert!(quote >= 600, "quote {quote} should reflect real rates");
}
#[test]
fn pricing_lookup_finds_exact_model_id() {
let models = json!({ "data": [
{ "id": "a-model", "sats_pricing": { "prompt": 0.1, "completion": 0.2, "request": 1.0 } },
{ "id": "other", "sats_pricing": { "prompt": 0.3 } }
]});
let p = pricing_for_model(&models, "a-model").unwrap();
assert_eq!(p.request, 1.0);
assert!(pricing_for_model(&models, "missing").is_none());
}
}
@@ -344,6 +344,7 @@ impl RpcHandler {
"content.indeehub-projects" => self.handle_content_indeehub_projects().await, "content.indeehub-projects" => self.handle_content_indeehub_projects().await,
"system.settings.get" => self.handle_system_settings_get(params).await, "system.settings.get" => self.handle_system_settings_get(params).await,
"system.settings.set" => self.handle_system_settings_set(params).await, "system.settings.set" => self.handle_system_settings_set(params).await,
"system.node-ca.generate" => self.handle_system_node_ca_generate().await,
"system.kiosk-display.get" => self.handle_system_kiosk_display_get().await, "system.kiosk-display.get" => self.handle_system_kiosk_display_get().await,
"system.kiosk-display.set" => self.handle_system_kiosk_display_set(params).await, "system.kiosk-display.set" => self.handle_system_kiosk_display_set(params).await,
"bitcoin.relay-update-settings" => { "bitcoin.relay-update-settings" => {
@@ -133,6 +133,7 @@ impl RpcHandler {
"lnd.sendcoins" => self.handle_lnd_sendcoins(params).await, "lnd.sendcoins" => self.handle_lnd_sendcoins(params).await,
"lnd.estimatefee" => self.handle_lnd_estimatefee(params).await, "lnd.estimatefee" => self.handle_lnd_estimatefee(params).await,
"lnd.createinvoice" => self.handle_lnd_createinvoice(params).await, "lnd.createinvoice" => self.handle_lnd_createinvoice(params).await,
"lnd.invoicestatus" => self.handle_lnd_invoicestatus(params).await,
"lnd.payinvoice" => self.handle_lnd_payinvoice(params).await, "lnd.payinvoice" => self.handle_lnd_payinvoice(params).await,
"lnd.paymentstatus" => self.handle_lnd_paymentstatus(params).await, "lnd.paymentstatus" => self.handle_lnd_paymentstatus(params).await,
"lnd.create-psbt" => self.handle_lnd_create_psbt(params).await, "lnd.create-psbt" => self.handle_lnd_create_psbt(params).await,
@@ -503,6 +504,7 @@ impl RpcHandler {
"ai.permissions.set" => self.handle_ai_permissions_set(params).await, "ai.permissions.set" => self.handle_ai_permissions_set(params).await,
"system.settings.get" => self.handle_system_settings_get(params).await, "system.settings.get" => self.handle_system_settings_get(params).await,
"system.settings.set" => self.handle_system_settings_set(params).await, "system.settings.set" => self.handle_system_settings_set(params).await,
"system.node-ca.generate" => self.handle_system_node_ca_generate().await,
"system.kiosk-display.get" => self.handle_system_kiosk_display_get().await, "system.kiosk-display.get" => self.handle_system_kiosk_display_get().await,
"system.kiosk-display.set" => self.handle_system_kiosk_display_set(params).await, "system.kiosk-display.set" => self.handle_system_kiosk_display_set(params).await,
@@ -607,9 +607,77 @@ impl RpcHandler {
.unwrap_or("") .unwrap_or("")
.to_string(); .to_string();
// LND returns r_hash base64-encoded; the lookup endpoint the Receive
// flow polls (`lnd.invoicestatus`) wants it hex — hand the UI the
// ready-to-use form.
let r_hash_hex = {
use base64::Engine as _;
body.get("r_hash")
.and_then(|v| v.as_str())
.and_then(|b64| base64::engine::general_purpose::STANDARD.decode(b64).ok())
.map(hex::encode)
.unwrap_or_default()
};
Ok(serde_json::json!({ Ok(serde_json::json!({
"payment_request": payment_request, "payment_request": payment_request,
"amount_sats": amount_sats, "amount_sats": amount_sats,
"r_hash_hex": r_hash_hex,
}))
}
/// lnd.invoicestatus — is this invoice settled yet? Polled by the wallet's
/// Receive flow so a Lightning payment gets the same "money has arrived"
/// success screen as on-chain (minus the broadcast step: settlement is
/// final). Params: `{ "r_hash_hex": string }`.
pub(in crate::api::rpc) async fn handle_lnd_invoicestatus(
&self,
params: Option<serde_json::Value>,
) -> Result<serde_json::Value> {
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
let r_hash_hex = params
.get("r_hash_hex")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("Missing 'r_hash_hex' parameter"))?;
if r_hash_hex.len() != 64 || !r_hash_hex.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(anyhow::anyhow!("r_hash_hex must be 64 hex characters"));
}
let (client, macaroon_hex) = self.lnd_client().await?;
let resp = client
.get(format!("{LND_REST_BASE_URL}/v1/invoice/{r_hash_hex}"))
.header("Grpc-Metadata-macaroon", &macaroon_hex)
.send()
.await
.context("Failed to query invoice")?;
let status = resp.status();
let body: serde_json::Value = resp
.json()
.await
.context("Failed to parse invoice lookup response")?;
if !status.is_success() {
let msg = body
.get("message")
.and_then(|v| v.as_str())
.unwrap_or("Unknown error");
return Err(anyhow::anyhow!("Invoice lookup failed: {}", msg));
}
let settled = body
.get("state")
.and_then(|v| v.as_str())
.map(|s| s == "SETTLED")
.unwrap_or_else(|| body.get("settled").and_then(|v| v.as_bool()).unwrap_or(false));
let amt_paid_sat = body
.get("amt_paid_sat")
.and_then(|v| v.as_str())
.and_then(|s| s.parse::<i64>().ok())
.or_else(|| body.get("amt_paid_sat").and_then(|v| v.as_i64()))
.unwrap_or(0);
Ok(serde_json::json!({
"settled": settled,
"amt_paid_sat": amt_paid_sat,
})) }))
} }
@@ -1248,6 +1248,30 @@ impl RpcHandler {
} }
} }
/// system.node-ca.generate — run the node's (idempotent) CA setup so the
/// dashboard can offer certificate generation as a button. WebUI rule:
/// users must never be pointed at a terminal; the script reuses an
/// existing CA and only reissues the leaf, so re-running is safe.
pub(in crate::api::rpc) async fn handle_system_node_ca_generate(
&self,
) -> Result<serde_json::Value> {
let script = "/opt/archipelago/scripts/setup-node-ca.sh";
if tokio::fs::metadata(script).await.is_err() {
anyhow::bail!(
"The certificate setup script is not on this node yet — it arrives with the next update."
);
}
let status = host_sudo(&["/usr/bin/bash", script]).await?;
if !status.success() {
anyhow::bail!(
"Certificate generation failed (exit {:?}) — see the node log for detail",
status.code()
);
}
info!("Node CA generated/reissued via dashboard");
Ok(serde_json::json!({ "generated": true }))
}
/// system.kiosk-display.get — Current kiosk display preset + whether this /// system.kiosk-display.get — Current kiosk display preset + whether this
/// node has a kiosk at all (no kiosk unit -> the Settings section hides). /// node has a kiosk at all (no kiosk unit -> the Settings section hides).
pub(in crate::api::rpc) async fn handle_system_kiosk_display_get( pub(in crate::api::rpc) async fn handle_system_kiosk_display_get(
+2 -2
View File
@@ -1,3 +1,3 @@
commit=7b82dfc779f94e068ed4d7b2ada39d6fd8f77dce commit=a4be1b4b7d8e50d16ed602d5f9b3f905a048f9a9
built_at=2026-08-09T19:43:11Z built_at=2026-08-14T17:41:17Z
base_path=/aiui/ base_path=/aiui/
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
.header-overlay-panel[data-v-cce6627c]{background:#000000e0}.picker-enter-active[data-v-cce6627c]{transition:all .2s cubic-bezier(.22,1,.36,1)}.picker-leave-active[data-v-cce6627c]{transition:all .15s ease-in}.picker-enter-from[data-v-cce6627c],.picker-leave-to[data-v-cce6627c]{opacity:0;transform:translateY(-8px)}.context-menu-enter-active[data-v-13d6c372]{transition:all .15s cubic-bezier(.22,1,.36,1)}.context-menu-leave-active[data-v-13d6c372]{transition:all .1s ease-in}.context-menu-enter-from[data-v-13d6c372],.context-menu-leave-to[data-v-13d6c372]{opacity:0;transform:scale(.95)}.settings-modal-enter-active[data-v-c97db749]{transition:opacity .2s ease-out}.settings-modal-enter-active .glass-card[data-v-c97db749]{transition:all .25s cubic-bezier(.22,1,.36,1)}.settings-modal-leave-active[data-v-c97db749]{transition:opacity .15s ease-in}.settings-modal-enter-from[data-v-c97db749],.settings-modal-leave-to[data-v-c97db749]{opacity:0} .header-overlay-panel[data-v-49f33a03]{background:#000000e0}.picker-enter-active[data-v-49f33a03]{transition:all .2s cubic-bezier(.22,1,.36,1)}.picker-leave-active[data-v-49f33a03]{transition:all .15s ease-in}.picker-enter-from[data-v-49f33a03],.picker-leave-to[data-v-49f33a03]{opacity:0;transform:translateY(-8px)}.context-menu-enter-active[data-v-13d6c372]{transition:all .15s cubic-bezier(.22,1,.36,1)}.context-menu-leave-active[data-v-13d6c372]{transition:all .1s ease-in}.context-menu-enter-from[data-v-13d6c372],.context-menu-leave-to[data-v-13d6c372]{opacity:0;transform:scale(.95)}.settings-modal-enter-active[data-v-c97db749]{transition:opacity .2s ease-out}.settings-modal-enter-active .glass-card[data-v-c97db749]{transition:all .25s cubic-bezier(.22,1,.36,1)}.settings-modal-leave-active[data-v-c97db749]{transition:opacity .15s ease-in}.settings-modal-enter-from[data-v-c97db749],.settings-modal-leave-to[data-v-c97db749]{opacity:0}
@@ -1,4 +1,4 @@
import{a as S,D as V,c as r,e as s,E as y,G as g,t as c,F as p,H as h,i as b,g as j,I as B,r as u,k as T,J as U,b as l,n as k}from"./index-8cIrvc8q.js";import{useNostr as E}from"./useNostr-XONW-p_l.js";const F={class:"min-h-screen bg-[#0a0a0a] text-white"},H={class:"sticky top-0 z-10 glass border-b border-white/5"},L={class:"max-w-3xl mx-auto px-4 py-3 flex items-center gap-3"},R={class:"flex-1 min-w-0"},z={class:"text-sm font-semibold text-white/90 truncate"},G={class:"text-xs text-white/40"},P={key:0,class:"flex items-center justify-center h-64"},J={key:1,class:"max-w-3xl mx-auto px-4 py-12 text-center"},Y={class:"text-white/40 text-sm"},q={key:2,class:"max-w-3xl mx-auto px-4 py-6 space-y-4"},K={class:"flex items-center gap-2 mb-2"},O=["textContent"],Z=S({__name:"ConversationViewerPage",setup(Q){const C=B(),{connect:N,fetchNote:A}=E(),v=u(!0),i=u(null),f=u("Shared Conversation"),x=u(null),d=u(null),w=u([]),I=T(()=>d.value?new Date(d.value*1e3).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"}):"");function D(n){const t=[],o=n.split(` import{a as S,D as V,c as r,e as s,E as y,G as g,t as c,F as p,H as h,i as b,g as j,I as B,r as u,k as T,J as U,b as l,n as k}from"./index-DNCGxUDM.js";import{useNostr as E}from"./useNostr-CNDx2L4S.js";const F={class:"min-h-screen bg-[#0a0a0a] text-white"},H={class:"sticky top-0 z-10 glass border-b border-white/5"},L={class:"max-w-3xl mx-auto px-4 py-3 flex items-center gap-3"},R={class:"flex-1 min-w-0"},z={class:"text-sm font-semibold text-white/90 truncate"},G={class:"text-xs text-white/40"},P={key:0,class:"flex items-center justify-center h-64"},J={key:1,class:"max-w-3xl mx-auto px-4 py-12 text-center"},Y={class:"text-white/40 text-sm"},q={key:2,class:"max-w-3xl mx-auto px-4 py-6 space-y-4"},K={class:"flex items-center gap-2 mb-2"},O=["textContent"],Z=S({__name:"ConversationViewerPage",setup(Q){const C=B(),{connect:N,fetchNote:A}=E(),v=u(!0),i=u(null),f=u("Shared Conversation"),x=u(null),d=u(null),w=u([]),I=T(()=>d.value?new Date(d.value*1e3).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"}):"");function D(n){const t=[],o=n.split(`
`);let e="",a=[];for(const m of o){const _=m.match(/^##?\s*(?:Human|User|You)/),M=m.match(/^##?\s*(?:Assistant|AI|Claude)/);_||M?(e&&a.length>0&&t.push({role:e,content:a.join(` `);let e="",a=[];for(const m of o){const _=m.match(/^##?\s*(?:Human|User|You)/),M=m.match(/^##?\s*(?:Assistant|AI|Claude)/);_||M?(e&&a.length>0&&t.push({role:e,content:a.join(`
`).trim()}),e=_?"user":"assistant",a=[]):a.push(m)}return e&&a.length>0&&t.push({role:e,content:a.join(` `).trim()}),e=_?"user":"assistant",a=[]):a.push(m)}return e&&a.length>0&&t.push({role:e,content:a.join(`
`).trim()}),t.length===0&&n.trim()&&t.push({role:"assistant",content:n.trim()}),t}return V(async()=>{try{const n=C.params.nostrAddr;if(!n){i.value="No Nostr address provided.";return}await N();let t=null;try{const e=atob(n).split(":");e.length>=2&&(t={dTag:e[0],pubkey:e[1]})}catch{}if(t){const o=await A(t.dTag);if(o){const e=o.tags.find(a=>a[0]==="title");e&&(f.value=e[1]),x.value=o.authorName??null,d.value=o.created_at,w.value=D(o.content)}else i.value="Conversation not found on relays."}else i.value="Invalid Nostr address format."}catch(n){i.value=n instanceof Error?n.message:"Failed to load conversation."}finally{v.value=!1}}),(n,t)=>{const o=U("router-link");return l(),r("div",F,[s("header",H,[s("div",L,[y(o,{to:"/",class:"text-white/40 hover:text-white/70 transition-colors"},{default:g(()=>[...t[0]||(t[0]=[s("svg",{class:"w-5 h-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor","stroke-width":"2"},[s("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"})],-1)])]),_:1}),s("div",R,[s("h1",z,c(f.value),1),s("p",G,[x.value?(l(),r(p,{key:0},[h("by "+c(x.value),1)],64)):b("",!0),d.value?(l(),r(p,{key:1},[h(" · "+c(I.value),1)],64)):b("",!0)])]),t[1]||(t[1]=s("span",{class:"text-xs px-2 py-1 rounded-full bg-white/5 text-white/40"},"Read-only",-1))])]),v.value?(l(),r("div",P,[...t[2]||(t[2]=[s("div",{class:"w-6 h-6 rounded-full border-2 border-accent/30 border-t-accent animate-spin"},null,-1)])])):i.value?(l(),r("div",J,[s("p",Y,c(i.value),1),y(o,{to:"/",class:"mt-4 inline-block text-accent text-sm hover:underline"},{default:g(()=>[...t[3]||(t[3]=[h(" Go to AIUI ",-1)])]),_:1})])):(l(),r("main",q,[(l(!0),r(p,null,j(w.value,(e,a)=>(l(),r("div",{key:a,class:k(["rounded-xl p-4",e.role==="user"?"bg-white/[0.03] border border-white/5 ml-8":"mr-8"])},[s("div",K,[s("span",{class:k(["text-xs font-bold uppercase tracking-wider",e.role==="user"?"text-accent/70":"text-white/30"])},c(e.role==="user"?"Human":"Assistant"),3)]),s("div",{class:"text-sm text-white/80 leading-relaxed whitespace-pre-wrap break-words",textContent:c(e.content)},null,8,O)],2))),128))])),t[4]||(t[4]=s("footer",{class:"max-w-3xl mx-auto px-4 py-8 text-center"},[s("p",{class:"text-xs text-white/20"}," Shared via AIUI · Powered by Nostr ")],-1))])}}});export{Z as default}; `).trim()}),t.length===0&&n.trim()&&t.push({role:"assistant",content:n.trim()}),t}return V(async()=>{try{const n=C.params.nostrAddr;if(!n){i.value="No Nostr address provided.";return}await N();let t=null;try{const e=atob(n).split(":");e.length>=2&&(t={dTag:e[0],pubkey:e[1]})}catch{}if(t){const o=await A(t.dTag);if(o){const e=o.tags.find(a=>a[0]==="title");e&&(f.value=e[1]),x.value=o.authorName??null,d.value=o.created_at,w.value=D(o.content)}else i.value="Conversation not found on relays."}else i.value="Invalid Nostr address format."}catch(n){i.value=n instanceof Error?n.message:"Failed to load conversation."}finally{v.value=!1}}),(n,t)=>{const o=U("router-link");return l(),r("div",F,[s("header",H,[s("div",L,[y(o,{to:"/",class:"text-white/40 hover:text-white/70 transition-colors"},{default:g(()=>[...t[0]||(t[0]=[s("svg",{class:"w-5 h-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor","stroke-width":"2"},[s("path",{"stroke-linecap":"round","stroke-linejoin":"round",d:"M10 19l-7-7m0 0l7-7m-7 7h18"})],-1)])]),_:1}),s("div",R,[s("h1",z,c(f.value),1),s("p",G,[x.value?(l(),r(p,{key:0},[h("by "+c(x.value),1)],64)):b("",!0),d.value?(l(),r(p,{key:1},[h(" · "+c(I.value),1)],64)):b("",!0)])]),t[1]||(t[1]=s("span",{class:"text-xs px-2 py-1 rounded-full bg-white/5 text-white/40"},"Read-only",-1))])]),v.value?(l(),r("div",P,[...t[2]||(t[2]=[s("div",{class:"w-6 h-6 rounded-full border-2 border-accent/30 border-t-accent animate-spin"},null,-1)])])):i.value?(l(),r("div",J,[s("p",Y,c(i.value),1),y(o,{to:"/",class:"mt-4 inline-block text-accent text-sm hover:underline"},{default:g(()=>[...t[3]||(t[3]=[h(" Go to AIUI ",-1)])]),_:1})])):(l(),r("main",q,[(l(!0),r(p,null,j(w.value,(e,a)=>(l(),r("div",{key:a,class:k(["rounded-xl p-4",e.role==="user"?"bg-white/[0.03] border border-white/5 ml-8":"mr-8"])},[s("div",K,[s("span",{class:k(["text-xs font-bold uppercase tracking-wider",e.role==="user"?"text-accent/70":"text-white/30"])},c(e.role==="user"?"Human":"Assistant"),3)]),s("div",{class:"text-sm text-white/80 leading-relaxed whitespace-pre-wrap break-words",textContent:c(e.content)},null,8,O)],2))),128))])),t[4]||(t[4]=s("footer",{class:"max-w-3xl mx-auto px-4 py-8 text-center"},[s("p",{class:"text-xs text-white/20"}," Shared via AIUI · Powered by Nostr ")],-1))])}}});export{Z as default};
@@ -1 +1 @@
import{_ as m}from"./FilmDetail.vue_vue_type_script_setup_true_lang-BhKlPG3Y.js";import"./index-8cIrvc8q.js";export{m as default}; import{_ as m}from"./FilmDetail.vue_vue_type_script_setup_true_lang-Cgf4f-Ng.js";import"./index-DNCGxUDM.js";export{m as default};
-1
View File
@@ -1 +0,0 @@
import{_ as o}from"./FilmGrid.vue_vue_type_script_setup_true_lang-Dj0SEfcW.js";import"./index-8cIrvc8q.js";import"./useContentImages-7wLVntsF.js";export{o as default};
+1
View File
@@ -0,0 +1 @@
import{_ as o}from"./FilmGrid.vue_vue_type_script_setup_true_lang-CkIQ4bRp.js";import"./index-DNCGxUDM.js";import"./useContentImages-DdjyABL9.js";export{o as default};
@@ -1 +1 @@
import{a as F,b as l,c as r,e as o,n as d,u as a,t as c,f as L,w as S,v as j,F as v,g as m,h as U,i as u,j as E,r as y,k as w,l as z,m as B,p as D}from"./index-8cIrvc8q.js";import{u as G}from"./useContentImages-7wLVntsF.js";const N={class:"h-full flex flex-col"},V={class:"flex items-center justify-between gap-2"},I={class:"flex items-center gap-2 shrink-0"},M={class:"flex flex-wrap gap-1.5"},R=["onClick"],T={class:"flex-1 overflow-y-auto custom-scrollbar px-4 pt-4 pb-16"},q={class:"grid grid-cols-2 sm:grid-cols-3 gap-4"},P=["aria-label","onClick"],A={class:"poster-card flex-1 min-h-0"},H={key:0,class:"absolute inset-0 animate-shimmer"},J=["src","alt","onError"],K=["src","alt"],O={key:3,class:"absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none"},Q={class:"absolute bottom-0 left-0 right-0 p-2"},W={class:"text-xs font-semibold text-white/90 leading-tight truncate"},X={class:"flex items-center gap-1 mt-0.5"},Y={key:0,class:"text-xs text-accent font-bold"},Z={key:1,class:"text-xs text-white/40"},ee={class:"absolute top-1.5 right-1.5 flex gap-0.5"},te={key:0,class:"flex items-center justify-center py-12"},ae=F({__name:"FilmGrid",props:{films:{},title:{default:"Recommended Films"}},emits:["selectFilm"],setup(_){const b=_,{isDark:n}=E(),p=y(""),h=y(null),{coverSrc:x,fallbackSrc:k,onError:C,isLoading:f}=G({items:D(b,"films"),id:t=>t.id,existingUrl:t=>t.posterUrl||t.backdropUrl,fetch:t=>B(t.title,t.year).then(s=>s.posterUrl),fallback:t=>z(t.title,t.year)}),$=w(()=>{const t=new Map;for(const s of b.films)for(const e of s.genres)t.set(e,(t.get(e)??0)+1);return[...t.entries()].sort((s,e)=>e[1]-s[1]).slice(0,8).map(([s])=>s)}),g=w(()=>{let t=b.films;if(p.value){const s=p.value.toLowerCase();t=t.filter(e=>e.title.toLowerCase().includes(s)||e.director.toLowerCase().includes(s)||e.cast.some(i=>i.toLowerCase().includes(s)))}return h.value&&(t=t.filter(s=>s.genres.includes(h.value))),t});return(t,s)=>(l(),r("div",N,[o("div",{class:"p-4 space-y-3",style:U(a(n)?"border-bottom: 1px solid rgba(255, 255, 255, 0.08)":"border-bottom: 1px solid rgba(0, 0, 0, 0.06)")},[o("div",V,[o("h3",{class:d(["text-sm font-bold",a(n)?"text-white/90":"text-gray-900"])},c(_.title),3),o("div",I,[o("span",{class:d(["text-xs font-mono",a(n)?"text-white/30":"text-gray-400"])},c(g.value.length)+" films ",3),L(t.$slots,"header-actions")])]),S(o("input",{"onUpdate:modelValue":s[0]||(s[0]=e=>p.value=e),type:"text",placeholder:"Search films...",class:d(["w-full px-3 py-2 rounded-lg text-base outline-none transition-colors",a(n)?"bg-white/5 text-white/80 placeholder:text-white/25 focus:bg-white/10":"bg-black/3 text-gray-800 placeholder:text-gray-400 focus:bg-black/5"])},null,2),[[j,p.value]]),o("div",M,[(l(!0),r(v,null,m($.value,e=>(l(),r("button",{key:e,class:d(["text-xs px-2 py-1 rounded-md transition-all duration-150",h.value===e?"nav-tab-active":a(n)?"text-white/40 hover:text-white/70 hover:bg-white/5":"text-gray-500 hover:text-gray-800 hover:bg-black/5"]),onClick:i=>h.value=h.value===e?null:e},c(e),11,R))),128))])],4),o("div",T,[o("div",q,[(l(!0),r(v,null,m(g.value,e=>(l(),r("button",{key:e.id,class:"group flex flex-col items-stretch text-left w-full path-glass-bubble rounded-2xl overflow-hidden transition-all duration-200 hover:brightness-105","aria-label":`${e.title} (${e.year})`,onClick:i=>t.$emit("selectFilm",e)},[o("div",A,[o("div",{class:d(["aspect-[2/3] relative w-full overflow-hidden rounded-[10px]",a(x)(e)?"":a(n)?"bg-white/[0.06]":"bg-black/[0.04]"])},[a(f)(e)?(l(),r("div",H)):u("",!0),a(x)(e)?(l(),r("img",{key:1,src:a(x)(e),alt:`${e.title} (${e.year}) directed by ${e.director}`,class:"w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",loading:"lazy",onError:i=>a(C)(e)},null,40,J)):a(f)(e)?u("",!0):(l(),r("img",{key:2,src:a(k)(e),alt:e.title,class:"w-full h-full object-cover"},null,8,K)),a(x)(e)?(l(),r("div",O)):u("",!0),o("div",Q,[o("p",W,c(e.title),1),o("div",X,[e.rating>0?(l(),r("span",Y,"★ "+c(e.rating),1)):u("",!0),e.year>0?(l(),r("span",Z,c(e.year),1)):u("",!0)])]),o("div",ee,[(l(!0),r(v,null,m(e.sources.slice(0,2),i=>(l(),r("span",{key:i.type,class:"text-xs px-1 py-0.5 rounded bg-black/60 text-white/70 backdrop-blur-sm"},c(i.type),1))),128))])],2)])],8,P))),128))]),g.value.length===0?(l(),r("div",te,[o("p",{class:d(["text-sm",a(n)?"text-white/30":"text-gray-400"])}," No films match your search ",2)])):u("",!0)])]))}});export{ae as _}; import{a as F,b as l,c as r,e as o,n as d,u as a,t as c,f as L,w as S,v as j,F as v,g as m,h as U,i as u,j as E,r as y,k as w,l as z,m as B,p as D}from"./index-DNCGxUDM.js";import{u as G}from"./useContentImages-DdjyABL9.js";const N={class:"h-full flex flex-col"},V={class:"flex items-center justify-between gap-2"},I={class:"flex items-center gap-2 shrink-0"},M={class:"flex flex-wrap gap-1.5"},R=["onClick"],T={class:"flex-1 overflow-y-auto custom-scrollbar px-4 pt-4 pb-16"},q={class:"grid grid-cols-2 sm:grid-cols-3 gap-4"},P=["aria-label","onClick"],A={class:"poster-card flex-1 min-h-0"},H={key:0,class:"absolute inset-0 animate-shimmer"},J=["src","alt","onError"],K=["src","alt"],O={key:3,class:"absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none"},Q={class:"absolute bottom-0 left-0 right-0 p-2"},W={class:"text-xs font-semibold text-white/90 leading-tight truncate"},X={class:"flex items-center gap-1 mt-0.5"},Y={key:0,class:"text-xs text-accent font-bold"},Z={key:1,class:"text-xs text-white/40"},ee={class:"absolute top-1.5 right-1.5 flex gap-0.5"},te={key:0,class:"flex items-center justify-center py-12"},ae=F({__name:"FilmGrid",props:{films:{},title:{default:"Recommended Films"}},emits:["selectFilm"],setup(_){const b=_,{isDark:n}=E(),p=y(""),h=y(null),{coverSrc:x,fallbackSrc:k,onError:C,isLoading:f}=G({items:D(b,"films"),id:t=>t.id,existingUrl:t=>t.posterUrl||t.backdropUrl,fetch:t=>B(t.title,t.year).then(s=>s.posterUrl),fallback:t=>z(t.title,t.year)}),$=w(()=>{const t=new Map;for(const s of b.films)for(const e of s.genres)t.set(e,(t.get(e)??0)+1);return[...t.entries()].sort((s,e)=>e[1]-s[1]).slice(0,8).map(([s])=>s)}),g=w(()=>{let t=b.films;if(p.value){const s=p.value.toLowerCase();t=t.filter(e=>e.title.toLowerCase().includes(s)||e.director.toLowerCase().includes(s)||e.cast.some(i=>i.toLowerCase().includes(s)))}return h.value&&(t=t.filter(s=>s.genres.includes(h.value))),t});return(t,s)=>(l(),r("div",N,[o("div",{class:"p-4 space-y-3",style:U(a(n)?"border-bottom: 1px solid rgba(255, 255, 255, 0.08)":"border-bottom: 1px solid rgba(0, 0, 0, 0.06)")},[o("div",V,[o("h3",{class:d(["text-sm font-bold",a(n)?"text-white/90":"text-gray-900"])},c(_.title),3),o("div",I,[o("span",{class:d(["text-xs font-mono",a(n)?"text-white/30":"text-gray-400"])},c(g.value.length)+" films ",3),L(t.$slots,"header-actions")])]),S(o("input",{"onUpdate:modelValue":s[0]||(s[0]=e=>p.value=e),type:"text",placeholder:"Search films...",class:d(["w-full px-3 py-2 rounded-lg text-base outline-none transition-colors",a(n)?"bg-white/5 text-white/80 placeholder:text-white/25 focus:bg-white/10":"bg-black/3 text-gray-800 placeholder:text-gray-400 focus:bg-black/5"])},null,2),[[j,p.value]]),o("div",M,[(l(!0),r(v,null,m($.value,e=>(l(),r("button",{key:e,class:d(["text-xs px-2 py-1 rounded-md transition-all duration-150",h.value===e?"nav-tab-active":a(n)?"text-white/40 hover:text-white/70 hover:bg-white/5":"text-gray-500 hover:text-gray-800 hover:bg-black/5"]),onClick:i=>h.value=h.value===e?null:e},c(e),11,R))),128))])],4),o("div",T,[o("div",q,[(l(!0),r(v,null,m(g.value,e=>(l(),r("button",{key:e.id,class:"group flex flex-col items-stretch text-left w-full path-glass-bubble rounded-2xl overflow-hidden transition-all duration-200 hover:brightness-105","aria-label":`${e.title} (${e.year})`,onClick:i=>t.$emit("selectFilm",e)},[o("div",A,[o("div",{class:d(["aspect-[2/3] relative w-full overflow-hidden rounded-[10px]",a(x)(e)?"":a(n)?"bg-white/[0.06]":"bg-black/[0.04]"])},[a(f)(e)?(l(),r("div",H)):u("",!0),a(x)(e)?(l(),r("img",{key:1,src:a(x)(e),alt:`${e.title} (${e.year}) directed by ${e.director}`,class:"w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",loading:"lazy",onError:i=>a(C)(e)},null,40,J)):a(f)(e)?u("",!0):(l(),r("img",{key:2,src:a(k)(e),alt:e.title,class:"w-full h-full object-cover"},null,8,K)),a(x)(e)?(l(),r("div",O)):u("",!0),o("div",Q,[o("p",W,c(e.title),1),o("div",X,[e.rating>0?(l(),r("span",Y,"★ "+c(e.rating),1)):u("",!0),e.year>0?(l(),r("span",Z,c(e.year),1)):u("",!0)])]),o("div",ee,[(l(!0),r(v,null,m(e.sources.slice(0,2),i=>(l(),r("span",{key:i.type,class:"text-xs px-1 py-0.5 rounded bg-black/60 text-white/70 backdrop-blur-sm"},c(i.type),1))),128))])],2)])],8,P))),128))]),g.value.length===0?(l(),r("div",te,[o("p",{class:d(["text-sm",a(n)?"text-white/30":"text-gray-400"])}," No films match your search ",2)])):u("",!0)])]))}});export{ae as _};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{_ as m}from"./SongDetail.vue_vue_type_script_setup_true_lang-B3om3Z8v.js";import"./index-8cIrvc8q.js";export{m as default}; import{_ as m}from"./SongDetail.vue_vue_type_script_setup_true_lang-0mQhUBE8.js";import"./index-DNCGxUDM.js";export{m as default};
+1
View File
@@ -0,0 +1 @@
import{_ as o}from"./SongGrid.vue_vue_type_script_setup_true_lang-70SttNTZ.js";import"./index-DNCGxUDM.js";import"./useContentImages-DdjyABL9.js";export{o as default};
-1
View File
@@ -1 +0,0 @@
import{_ as o}from"./SongGrid.vue_vue_type_script_setup_true_lang-IvAOIQYW.js";import"./index-8cIrvc8q.js";import"./useContentImages-7wLVntsF.js";export{o as default};
@@ -1 +1 @@
import{a as z,q as M,x as B,y as E,p as q,b as o,c as r,e as s,n as c,u as a,t as u,f as D,w as F,v as G,F as v,g as m,h as N,i as h,z as U,j as V,r as _,k}from"./index-8cIrvc8q.js";import{u as P}from"./useContentImages-7wLVntsF.js";const R={class:"h-full flex flex-col"},T={class:"flex items-center justify-between gap-2"},I={class:"flex items-center gap-2 shrink-0"},A={class:"flex flex-wrap gap-1.5"},H=["onClick"],J={class:"flex-1 overflow-y-auto custom-scrollbar px-4 pt-4 pb-16"},K={class:"grid grid-cols-2 sm:grid-cols-3 gap-4"},O=["aria-label","onClick"],Q={class:"cover-card flex-1 min-h-0 relative flex items-center justify-center"},W={key:0,class:"absolute inset-0 animate-shimmer"},X=["onClick"],Y=["src","alt","onError"],Z=["src","alt"],tt={key:3,class:"absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none"},et={class:"absolute bottom-0 left-0 right-0 p-2"},st={class:"text-xs font-semibold text-white/90 leading-tight truncate"},lt={key:0,class:"text-xs text-white/40 truncate mt-0.5"},at={class:"absolute top-1.5 right-1.5 flex gap-0.5 flex-wrap justify-end max-w-[60%]"},ot={key:0,class:"flex items-center justify-center py-12"},nt=z({__name:"SongGrid",props:{songs:{},title:{default:"Recommended Songs"}},emits:["selectSong"],setup(g,{emit:C}){const x=g,y=C,{isDark:i}=V(),{play:S}=M(),p=_(""),d=_(null),{coverSrc:f,fallbackSrc:j,onError:$,isLoading:w}=P({items:q(x,"songs"),id:e=>e.id,existingUrl:e=>e.coverUrl,fetch:e=>E(e.title,e.artist,e.album),fallback:e=>B(e.title,e.artist)}),L=k(()=>{const e=new Map;for(const l of x.songs)for(const t of l.genres??[])e.set(t,(e.get(t)??0)+1);return[...e.entries()].sort((l,t)=>t[1]-l[1]).slice(0,8).map(([l])=>l)}),b=k(()=>{let e=x.songs;if(p.value){const l=p.value.toLowerCase();e=e.filter(t=>t.title.toLowerCase().includes(l)||t.artist.toLowerCase().includes(l)||(t.album??"").toLowerCase().includes(l))}return d.value&&(e=e.filter(l=>(l.genres??[]).includes(d.value))),e});return(e,l)=>(o(),r("div",R,[s("div",{class:"p-4 space-y-3",style:N(a(i)?"border-bottom: 1px solid rgba(255, 255, 255, 0.08)":"border-bottom: 1px solid rgba(0, 0, 0, 0.06)")},[s("div",T,[s("h3",{class:c(["text-sm font-bold",a(i)?"text-white/90":"text-gray-900"])},u(g.title),3),s("div",I,[s("span",{class:c(["text-xs font-mono",a(i)?"text-white/30":"text-gray-400"])},u(b.value.length)+" songs ",3),D(e.$slots,"header-actions")])]),F(s("input",{"onUpdate:modelValue":l[0]||(l[0]=t=>p.value=t),type:"text",placeholder:"Search songs...",class:c(["w-full px-3 py-2 rounded-lg text-base outline-none transition-colors",a(i)?"bg-white/5 text-white/80 placeholder:text-white/25 focus:bg-white/10":"bg-black/3 text-gray-800 placeholder:text-gray-400 focus:bg-black/5"])},null,2),[[G,p.value]]),s("div",A,[(o(!0),r(v,null,m(L.value,t=>(o(),r("button",{key:t,class:c(["text-xs px-2 py-1 rounded-md transition-all duration-150",d.value===t?"nav-tab-active":a(i)?"text-white/40 hover:text-white/70 hover:bg-white/5":"text-gray-500 hover:text-gray-800 hover:bg-black/5"]),onClick:n=>d.value=d.value===t?null:t},u(t),11,H))),128))])],4),s("div",J,[s("div",K,[(o(!0),r(v,null,m(b.value,t=>(o(),r("button",{key:t.id,class:"group flex flex-col items-stretch text-left w-full path-glass-bubble rounded-2xl overflow-hidden transition-all duration-200 hover:brightness-105","aria-label":`${t.title} by ${t.artist}`,onClick:n=>y("selectSong",t)},[s("div",Q,[s("div",{class:c(["aspect-square relative w-full overflow-hidden rounded-[10px]",a(f)(t)?"":a(i)?"bg-white/[0.06]":"bg-black/[0.04]"])},[a(w)(t)?(o(),r("div",W)):h("",!0),s("button",{class:"absolute inset-0 flex items-center justify-center z-10 backdrop-blur-sm bg-black/30 opacity-0 group-hover:opacity-100 transition-all duration-200","aria-label":"Play",onClick:U(n=>{a(S)(t),y("selectSong",t)},["stop"])},[...l[1]||(l[1]=[s("span",{class:"w-16 h-16 rounded-full flex items-center justify-center path-glass-icon"},[s("svg",{class:"w-8 h-8 text-white",fill:"currentColor",viewBox:"0 0 24 24"},[s("path",{d:"M8 5v14l11-7L8 5z"})])],-1)])],8,X),a(f)(t)?(o(),r("img",{key:1,src:a(f)(t),alt:`${t.title} by ${t.artist}`,class:"w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",loading:"lazy",onError:n=>a($)(t)},null,40,Y)):a(w)(t)?h("",!0):(o(),r("img",{key:2,src:a(j)(t),alt:t.title,class:"w-full h-full object-cover"},null,8,Z)),a(f)(t)?(o(),r("div",tt)):h("",!0),s("div",et,[s("p",st,u(t.title),1),t.artist?(o(),r("p",lt,u(t.artist),1)):h("",!0)]),s("div",at,[(o(!0),r(v,null,m((t.sources??[]).slice(0,2),n=>(o(),r("span",{key:n.type,class:"text-xs px-1 py-0.5 rounded bg-black/60 text-white/70 backdrop-blur-sm"},u(n.type),1))),128))])],2)])],8,O))),128))]),b.value.length===0?(o(),r("div",ot,[s("p",{class:c(["text-sm",a(i)?"text-white/30":"text-gray-400"])}," No songs match your search ",2)])):h("",!0)])]))}});export{nt as _}; import{a as z,q as M,x as B,y as E,p as q,b as o,c as r,e as s,n as c,u as a,t as u,f as D,w as F,v as G,F as v,g as m,h as N,i as h,z as U,j as V,r as _,k}from"./index-DNCGxUDM.js";import{u as P}from"./useContentImages-DdjyABL9.js";const R={class:"h-full flex flex-col"},T={class:"flex items-center justify-between gap-2"},I={class:"flex items-center gap-2 shrink-0"},A={class:"flex flex-wrap gap-1.5"},H=["onClick"],J={class:"flex-1 overflow-y-auto custom-scrollbar px-4 pt-4 pb-16"},K={class:"grid grid-cols-2 sm:grid-cols-3 gap-4"},O=["aria-label","onClick"],Q={class:"cover-card flex-1 min-h-0 relative flex items-center justify-center"},W={key:0,class:"absolute inset-0 animate-shimmer"},X=["onClick"],Y=["src","alt","onError"],Z=["src","alt"],tt={key:3,class:"absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none"},et={class:"absolute bottom-0 left-0 right-0 p-2"},st={class:"text-xs font-semibold text-white/90 leading-tight truncate"},lt={key:0,class:"text-xs text-white/40 truncate mt-0.5"},at={class:"absolute top-1.5 right-1.5 flex gap-0.5 flex-wrap justify-end max-w-[60%]"},ot={key:0,class:"flex items-center justify-center py-12"},nt=z({__name:"SongGrid",props:{songs:{},title:{default:"Recommended Songs"}},emits:["selectSong"],setup(g,{emit:C}){const x=g,y=C,{isDark:i}=V(),{play:S}=M(),p=_(""),d=_(null),{coverSrc:f,fallbackSrc:j,onError:$,isLoading:w}=P({items:q(x,"songs"),id:e=>e.id,existingUrl:e=>e.coverUrl,fetch:e=>E(e.title,e.artist,e.album),fallback:e=>B(e.title,e.artist)}),L=k(()=>{const e=new Map;for(const l of x.songs)for(const t of l.genres??[])e.set(t,(e.get(t)??0)+1);return[...e.entries()].sort((l,t)=>t[1]-l[1]).slice(0,8).map(([l])=>l)}),b=k(()=>{let e=x.songs;if(p.value){const l=p.value.toLowerCase();e=e.filter(t=>t.title.toLowerCase().includes(l)||t.artist.toLowerCase().includes(l)||(t.album??"").toLowerCase().includes(l))}return d.value&&(e=e.filter(l=>(l.genres??[]).includes(d.value))),e});return(e,l)=>(o(),r("div",R,[s("div",{class:"p-4 space-y-3",style:N(a(i)?"border-bottom: 1px solid rgba(255, 255, 255, 0.08)":"border-bottom: 1px solid rgba(0, 0, 0, 0.06)")},[s("div",T,[s("h3",{class:c(["text-sm font-bold",a(i)?"text-white/90":"text-gray-900"])},u(g.title),3),s("div",I,[s("span",{class:c(["text-xs font-mono",a(i)?"text-white/30":"text-gray-400"])},u(b.value.length)+" songs ",3),D(e.$slots,"header-actions")])]),F(s("input",{"onUpdate:modelValue":l[0]||(l[0]=t=>p.value=t),type:"text",placeholder:"Search songs...",class:c(["w-full px-3 py-2 rounded-lg text-base outline-none transition-colors",a(i)?"bg-white/5 text-white/80 placeholder:text-white/25 focus:bg-white/10":"bg-black/3 text-gray-800 placeholder:text-gray-400 focus:bg-black/5"])},null,2),[[G,p.value]]),s("div",A,[(o(!0),r(v,null,m(L.value,t=>(o(),r("button",{key:t,class:c(["text-xs px-2 py-1 rounded-md transition-all duration-150",d.value===t?"nav-tab-active":a(i)?"text-white/40 hover:text-white/70 hover:bg-white/5":"text-gray-500 hover:text-gray-800 hover:bg-black/5"]),onClick:n=>d.value=d.value===t?null:t},u(t),11,H))),128))])],4),s("div",J,[s("div",K,[(o(!0),r(v,null,m(b.value,t=>(o(),r("button",{key:t.id,class:"group flex flex-col items-stretch text-left w-full path-glass-bubble rounded-2xl overflow-hidden transition-all duration-200 hover:brightness-105","aria-label":`${t.title} by ${t.artist}`,onClick:n=>y("selectSong",t)},[s("div",Q,[s("div",{class:c(["aspect-square relative w-full overflow-hidden rounded-[10px]",a(f)(t)?"":a(i)?"bg-white/[0.06]":"bg-black/[0.04]"])},[a(w)(t)?(o(),r("div",W)):h("",!0),s("button",{class:"absolute inset-0 flex items-center justify-center z-10 backdrop-blur-sm bg-black/30 opacity-0 group-hover:opacity-100 transition-all duration-200","aria-label":"Play",onClick:U(n=>{a(S)(t),y("selectSong",t)},["stop"])},[...l[1]||(l[1]=[s("span",{class:"w-16 h-16 rounded-full flex items-center justify-center path-glass-icon"},[s("svg",{class:"w-8 h-8 text-white",fill:"currentColor",viewBox:"0 0 24 24"},[s("path",{d:"M8 5v14l11-7L8 5z"})])],-1)])],8,X),a(f)(t)?(o(),r("img",{key:1,src:a(f)(t),alt:`${t.title} by ${t.artist}`,class:"w-full h-full object-cover transition-transform duration-300 group-hover:scale-110",loading:"lazy",onError:n=>a($)(t)},null,40,Y)):a(w)(t)?h("",!0):(o(),r("img",{key:2,src:a(j)(t),alt:t.title,class:"w-full h-full object-cover"},null,8,Z)),a(f)(t)?(o(),r("div",tt)):h("",!0),s("div",et,[s("p",st,u(t.title),1),t.artist?(o(),r("p",lt,u(t.artist),1)):h("",!0)]),s("div",at,[(o(!0),r(v,null,m((t.sources??[]).slice(0,2),n=>(o(),r("span",{key:n.type,class:"text-xs px-1 py-0.5 rounded bg-black/60 text-white/70 backdrop-blur-sm"},u(n.type),1))),128))])],2)])],8,O))),128))]),b.value.length===0?(o(),r("div",ot,[s("p",{class:c(["text-sm",a(i)?"text-white/30":"text-gray-400"])}," No songs match your search ",2)])):h("",!0)])]))}});export{nt as _};
@@ -1 +1 @@
import{a as h,J as m,b as d,c as r,e as t,t as s,F as u,g as p,K as x,h as f}from"./index-8cIrvc8q.js";const g={class:"rounded-lg bg-white/[0.03] border border-white/5 p-2.5 mb-1"},b={class:"flex items-center gap-1.5 mb-1"},w={class:"w-5 h-5 rounded-full shrink-0 flex items-center justify-center text-xs font-bold bg-purple-500/20 text-purple-400"},y={class:"text-xs font-semibold text-white/70"},v={class:"text-xs ml-auto text-white/20"},k={class:"text-xs text-white/60 leading-relaxed whitespace-pre-wrap"},T=h({__name:"ThreadNode",props:{node:{},depth:{}},emits:["reply"],setup(e){function i(o){return new Date(o*1e3).toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit"})}return(o,n)=>{const l=m("ThreadNode",!0);return d(),r("div",{style:f({paddingLeft:`${Math.min(e.depth,4)*16}px`})},[t("div",g,[t("div",b,[t("div",w,s(e.node.note.authorName?.charAt(0)?.toUpperCase()??"?"),1),t("span",y,s(e.node.note.authorName??"anon"),1),t("span",v,s(i(e.node.note.created_at)),1)]),t("p",k,s(e.node.note.content),1),t("button",{class:"text-xs text-white/25 hover:text-accent/60 mt-1 transition-colors",onClick:n[0]||(n[0]=a=>o.$emit("reply",e.node.note))}," Reply ")]),(d(!0),r(u,null,p(e.node.children,a=>(d(),x(l,{key:a.note.id,node:a,depth:e.depth+1,onReply:n[1]||(n[1]=c=>o.$emit("reply",c))},null,8,["node","depth"]))),128))],4)}}});export{T as default}; import{a as h,J as m,b as d,c as r,e as t,t as s,F as u,g as p,K as x,h as f}from"./index-DNCGxUDM.js";const g={class:"rounded-lg bg-white/[0.03] border border-white/5 p-2.5 mb-1"},b={class:"flex items-center gap-1.5 mb-1"},w={class:"w-5 h-5 rounded-full shrink-0 flex items-center justify-center text-xs font-bold bg-purple-500/20 text-purple-400"},y={class:"text-xs font-semibold text-white/70"},v={class:"text-xs ml-auto text-white/20"},k={class:"text-xs text-white/60 leading-relaxed whitespace-pre-wrap"},T=h({__name:"ThreadNode",props:{node:{},depth:{}},emits:["reply"],setup(e){function i(o){return new Date(o*1e3).toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit"})}return(o,n)=>{const l=m("ThreadNode",!0);return d(),r("div",{style:f({paddingLeft:`${Math.min(e.depth,4)*16}px`})},[t("div",g,[t("div",b,[t("div",w,s(e.node.note.authorName?.charAt(0)?.toUpperCase()??"?"),1),t("span",y,s(e.node.note.authorName??"anon"),1),t("span",v,s(i(e.node.note.created_at)),1)]),t("p",k,s(e.node.note.content),1),t("button",{class:"text-xs text-white/25 hover:text-accent/60 mt-1 transition-colors",onClick:n[0]||(n[0]=a=>o.$emit("reply",e.node.note))}," Reply ")]),(d(!0),r(u,null,p(e.node.children,a=>(d(),x(l,{key:a.note.id,node:a,depth:e.depth+1,onReply:n[1]||(n[1]=c=>o.$emit("reply",c))},null,8,["node","depth"]))),128))],4)}}});export{T as default};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as E}from"./_baseUniq-DAOs4kUj.js";import{aR as g,ar as F,aS as M,aT as T,aU as _,aV as l,aW as $,aX as B,aY as S,aZ as y}from"./mermaid.core-v0oo9NRr.js";var R=/\s/;function G(n){for(var r=n.length;r--&&R.test(n.charAt(r)););return r}var H=/^\s+/;function L(n){return n&&n.slice(0,G(n)+1).replace(H,"")}var o=NaN,W=/^[-+]0x[0-9a-f]+$/i,X=/^0b[01]+$/i,Y=/^0o[0-7]+$/i,q=parseInt;function z(n){if(typeof n=="number")return n;if(x(n))return o;if(g(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=g(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=L(n);var t=X.test(n);return t||Y.test(n)?q(n.slice(2),t?2:8):W.test(n)?o:+n}var v=1/0,C=17976931348623157e292;function K(n){if(!n)return n===0?n:0;if(n=z(n),n===v||n===-v){var r=n<0?-1:1;return r*C}return n===n?n:0}function U(n){var r=K(n),t=r%1;return r===r?t?r-t:r:0}function fn(n){var r=n==null?0:n.length;return r?O(n):[]}var b=Object.prototype,Z=b.hasOwnProperty,dn=F(function(n,r){n=Object(n);var t=-1,i=r.length,a=i>2?r[2]:void 0;for(a&&M(r[0],r[1],a)&&(i=1);++t<i;)for(var f=r[t],e=T(f),s=-1,d=e.length;++s<d;){var u=e[s],h=n[u];(h===void 0||_(h,b[u])&&!Z.call(n,u))&&(n[u]=f[u])}return n});function un(n){var r=n==null?0:n.length;return r?n[r-1]:void 0}function D(n){return function(r,t,i){var a=Object(r);if(!l(r)){var f=m(t);r=P(r),t=function(s){return f(a[s],s,a)}}var e=n(r,t,i);return e>-1?a[f?r[e]:e]:void 0}}var J=Math.max;function Q(n,r,t){var i=n==null?0:n.length;if(!i)return-1;var a=t==null?0:U(t);return a<0&&(a=J(i+a,0)),p(n,m(r),a)}var hn=D(Q);function V(n,r){var t=-1,i=l(n)?Array(n.length):[];return w(n,function(a,f,e){i[++t]=r(a,f,e)}),i}function gn(n,r){var t=$(n)?c:V;return t(n,m(r))}var j=Object.prototype,k=j.hasOwnProperty;function nn(n,r){return n!=null&&k.call(n,r)}function mn(n,r){return n!=null&&A(n,r,nn)}function rn(n,r){return n<r}function tn(n,r,t){for(var i=-1,a=n.length;++i<a;){var f=n[i],e=r(f);if(e!=null&&(s===void 0?e===e&&!x(e):t(e,s)))var s=e,d=f}return d}function on(n){return n&&n.length?tn(n,B,rn):void 0}function an(n,r,t,i){if(!g(n))return n;r=I(r,n);for(var a=-1,f=r.length,e=f-1,s=n;s!=null&&++a<f;){var d=N(r[a]),u=t;if(d==="__proto__"||d==="constructor"||d==="prototype")return n;if(a!=e){var h=s[d];u=void 0,u===void 0&&(u=g(h)?h:S(r[a+1])?[]:{})}y(s,d,u),s=s[d]}return n}function vn(n,r,t){for(var i=-1,a=r.length,f={};++i<a;){var e=r[i],s=E(n,e);t(s,e)&&an(f,I(e,n),s)}return f}export{rn as a,tn as b,V as c,vn as d,on as e,fn as f,hn as g,mn as h,dn as i,U as j,un as l,gn as m,K as t}; import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as E}from"./_baseUniq-T1y-Xwdr.js";import{aR as g,ar as F,aS as M,aT as T,aU as _,aV as l,aW as $,aX as B,aY as S,aZ as y}from"./mermaid.core-CFUktQ8s.js";var R=/\s/;function G(n){for(var r=n.length;r--&&R.test(n.charAt(r)););return r}var H=/^\s+/;function L(n){return n&&n.slice(0,G(n)+1).replace(H,"")}var o=NaN,W=/^[-+]0x[0-9a-f]+$/i,X=/^0b[01]+$/i,Y=/^0o[0-7]+$/i,q=parseInt;function z(n){if(typeof n=="number")return n;if(x(n))return o;if(g(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=g(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=L(n);var t=X.test(n);return t||Y.test(n)?q(n.slice(2),t?2:8):W.test(n)?o:+n}var v=1/0,C=17976931348623157e292;function K(n){if(!n)return n===0?n:0;if(n=z(n),n===v||n===-v){var r=n<0?-1:1;return r*C}return n===n?n:0}function U(n){var r=K(n),t=r%1;return r===r?t?r-t:r:0}function fn(n){var r=n==null?0:n.length;return r?O(n):[]}var b=Object.prototype,Z=b.hasOwnProperty,dn=F(function(n,r){n=Object(n);var t=-1,i=r.length,a=i>2?r[2]:void 0;for(a&&M(r[0],r[1],a)&&(i=1);++t<i;)for(var f=r[t],e=T(f),s=-1,d=e.length;++s<d;){var u=e[s],h=n[u];(h===void 0||_(h,b[u])&&!Z.call(n,u))&&(n[u]=f[u])}return n});function un(n){var r=n==null?0:n.length;return r?n[r-1]:void 0}function D(n){return function(r,t,i){var a=Object(r);if(!l(r)){var f=m(t);r=P(r),t=function(s){return f(a[s],s,a)}}var e=n(r,t,i);return e>-1?a[f?r[e]:e]:void 0}}var J=Math.max;function Q(n,r,t){var i=n==null?0:n.length;if(!i)return-1;var a=t==null?0:U(t);return a<0&&(a=J(i+a,0)),p(n,m(r),a)}var hn=D(Q);function V(n,r){var t=-1,i=l(n)?Array(n.length):[];return w(n,function(a,f,e){i[++t]=r(a,f,e)}),i}function gn(n,r){var t=$(n)?c:V;return t(n,m(r))}var j=Object.prototype,k=j.hasOwnProperty;function nn(n,r){return n!=null&&k.call(n,r)}function mn(n,r){return n!=null&&A(n,r,nn)}function rn(n,r){return n<r}function tn(n,r,t){for(var i=-1,a=n.length;++i<a;){var f=n[i],e=r(f);if(e!=null&&(s===void 0?e===e&&!x(e):t(e,s)))var s=e,d=f}return d}function on(n){return n&&n.length?tn(n,B,rn):void 0}function an(n,r,t,i){if(!g(n))return n;r=I(r,n);for(var a=-1,f=r.length,e=f-1,s=n;s!=null&&++a<f;){var d=N(r[a]),u=t;if(d==="__proto__"||d==="constructor"||d==="prototype")return n;if(a!=e){var h=s[d];u=void 0,u===void 0&&(u=g(h)?h:S(r[a+1])?[]:{})}y(s,d,u),s=s[d]}return n}function vn(n,r,t){for(var i=-1,a=r.length,f={};++i<a;){var e=r[i],s=E(n,e);t(s,e)&&an(f,I(e,n),s)}return f}export{rn as a,tn as b,V as c,vn as d,on as e,fn as f,hn as g,mn as h,dn as i,U as j,un as l,gn as m,K as t};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{N as ln,O as an,P as Z,Q as O,R as V,S as un,T as y,V as tn,W as z,X as _,Y as rn,Z as o,$ as on,a0 as sn,a1 as fn}from"./mermaid.core-v0oo9NRr.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,D,S,v,R,W,a){var E=D-l,i=S-h,n=W-v,d=a-R,u=d*E-n*i;if(!(u*u<y))return u=(n*(h-R)-d*(l-v))/u,[l+u*E,h+u*i]}function J(l,h,D,S,v,R,W){var a=l-D,E=h-S,i=(W?R:-R)/z(a*a+E*E),n=i*E,d=-i*a,u=l+n,s=h+d,f=D+n,c=S+d,X=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,A=m*m+g*g,T=v-R,P=u*c-f*s,I=(g<0?-1:1)*z(on(0,T*T*A-P*P)),N=(P*g-m*I)/A,Q=(-P*m-g*I)/A,w=(P*g+m*I)/A,p=(-P*m+g*I)/A,x=N-X,e=Q-t,r=w-X,Y=p-t;return x*x+e*e>r*r+Y*Y&&(N=w,Q=p),{cx:N,cy:Q,x01:-n,y01:-d,x11:N*(v/T-1),y11:Q*(v/T-1)}}function hn(){var l=cn,h=yn,D=V(0),S=null,v=gn,R=dn,W=mn,a=null,E=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=R.apply(this,arguments)-un,X=rn(c-f),t=c>f;if(a||(a=n=E()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(X>tn-y)a.moveTo(s*Z(f),s*O(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*Z(c),u*O(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,A=f,T=c,P=X,I=X,N=W.apply(this,arguments)/2,Q=N>y&&(S?+S.apply(this,arguments):z(u*u+s*s)),w=_(rn(s-u)/2,+D.apply(this,arguments)),p=w,x=w,e,r;if(Q>y){var Y=sn(Q/u*O(N)),B=sn(Q/s*O(N));(P-=Y*2)>y?(Y*=t?1:-1,A+=Y,T-=Y):(P=0,A=T=(f+c)/2),(I-=B*2)>y?(B*=t?1:-1,m+=B,g-=B):(I=0,m=g=(f+c)/2)}var $=s*Z(m),j=s*O(m),C=u*Z(T),F=u*O(T);if(w>y){var G=s*Z(g),H=s*O(g),K=u*Z(A),L=u*O(A),q;if(X<an)if(q=pn($,j,K,L,G,H,C,F)){var M=$-q[0],U=j-q[1],k=G-q[0],b=H-q[1],nn=1/O(fn((M*k+U*b)/(z(M*M+U*U)*z(k*k+b*b)))/2),en=z(q[0]*q[0]+q[1]*q[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}I>y?x>y?(e=J(K,L,$,j,s,x,t),r=J(G,H,C,F,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo($,j),a.arc(0,0,s,m,g,!t)):a.moveTo($,j),!(u>y)||!(P>y)?a.lineTo(C,F):p>y?(e=J(C,F,G,H,u,-p,t),r=J($,j,K,L,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,A,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +R.apply(this,arguments))/2-an/2;return[Z(d)*n,O(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:V(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:V(+n),i):h},i.cornerRadius=function(n){return arguments.length?(D=typeof n=="function"?n:V(+n),i):D},i.padRadius=function(n){return arguments.length?(S=n==null?null:typeof n=="function"?n:V(+n),i):S},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:V(+n),i):v},i.endAngle=function(n){return arguments.length?(R=typeof n=="function"?n:V(+n),i):R},i.padAngle=function(n){return arguments.length?(W=typeof n=="function"?n:V(+n),i):W},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d}; import{N as ln,O as an,P as Z,Q as O,R as V,S as un,T as y,V as tn,W as z,X as _,Y as rn,Z as o,$ as on,a0 as sn,a1 as fn}from"./mermaid.core-CFUktQ8s.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,D,S,v,R,W,a){var E=D-l,i=S-h,n=W-v,d=a-R,u=d*E-n*i;if(!(u*u<y))return u=(n*(h-R)-d*(l-v))/u,[l+u*E,h+u*i]}function J(l,h,D,S,v,R,W){var a=l-D,E=h-S,i=(W?R:-R)/z(a*a+E*E),n=i*E,d=-i*a,u=l+n,s=h+d,f=D+n,c=S+d,X=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,A=m*m+g*g,T=v-R,P=u*c-f*s,I=(g<0?-1:1)*z(on(0,T*T*A-P*P)),N=(P*g-m*I)/A,Q=(-P*m-g*I)/A,w=(P*g+m*I)/A,p=(-P*m+g*I)/A,x=N-X,e=Q-t,r=w-X,Y=p-t;return x*x+e*e>r*r+Y*Y&&(N=w,Q=p),{cx:N,cy:Q,x01:-n,y01:-d,x11:N*(v/T-1),y11:Q*(v/T-1)}}function hn(){var l=cn,h=yn,D=V(0),S=null,v=gn,R=dn,W=mn,a=null,E=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=R.apply(this,arguments)-un,X=rn(c-f),t=c>f;if(a||(a=n=E()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(X>tn-y)a.moveTo(s*Z(f),s*O(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*Z(c),u*O(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,A=f,T=c,P=X,I=X,N=W.apply(this,arguments)/2,Q=N>y&&(S?+S.apply(this,arguments):z(u*u+s*s)),w=_(rn(s-u)/2,+D.apply(this,arguments)),p=w,x=w,e,r;if(Q>y){var Y=sn(Q/u*O(N)),B=sn(Q/s*O(N));(P-=Y*2)>y?(Y*=t?1:-1,A+=Y,T-=Y):(P=0,A=T=(f+c)/2),(I-=B*2)>y?(B*=t?1:-1,m+=B,g-=B):(I=0,m=g=(f+c)/2)}var $=s*Z(m),j=s*O(m),C=u*Z(T),F=u*O(T);if(w>y){var G=s*Z(g),H=s*O(g),K=u*Z(A),L=u*O(A),q;if(X<an)if(q=pn($,j,K,L,G,H,C,F)){var M=$-q[0],U=j-q[1],k=G-q[0],b=H-q[1],nn=1/O(fn((M*k+U*b)/(z(M*M+U*U)*z(k*k+b*b)))/2),en=z(q[0]*q[0]+q[1]*q[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}I>y?x>y?(e=J(K,L,$,j,s,x,t),r=J(G,H,C,F,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo($,j),a.arc(0,0,s,m,g,!t)):a.moveTo($,j),!(u>y)||!(P>y)?a.lineTo(C,F):p>y?(e=J(C,F,G,H,u,-p,t),r=J($,j,K,L,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,A,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +R.apply(this,arguments))/2-an/2;return[Z(d)*n,O(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:V(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:V(+n),i):h},i.cornerRadius=function(n){return arguments.length?(D=typeof n=="function"?n:V(+n),i):D},i.padRadius=function(n){return arguments.length?(S=n==null?null:typeof n=="function"?n:V(+n),i):S},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:V(+n),i):v},i.endAngle=function(n){return arguments.length?(R=typeof n=="function"?n:V(+n),i):R},i.padAngle=function(n){return arguments.length?(W=typeof n=="function"?n:V(+n),i):W},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
import{U as a,M as n}from"./mermaid.core-CFUktQ8s.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
-1
View File
@@ -1 +0,0 @@
import{U as a,M as n}from"./mermaid.core-v0oo9NRr.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{_ as i}from"./mermaid.core-v0oo9NRr.js";function t(c,e){c.accDescr&&e.setAccDescription?.(c.accDescr),c.accTitle&&e.setAccTitle?.(c.accTitle),c.title&&e.setDiagramTitle?.(c.title)}i(t,"populateCommonDb");export{t as p}; import{_ as i}from"./mermaid.core-CFUktQ8s.js";function t(c,e){c.accDescr&&e.setAccDescription?.(c.accDescr),c.accTitle&&e.setAccTitle?.(c.accTitle),c.title&&e.setDiagramTitle?.(c.title)}i(t,"populateCommonDb");export{t as p};
@@ -1 +1 @@
import{_ as a,d as o}from"./mermaid.core-v0oo9NRr.js";var d=a((t,e)=>{let n;return e==="sandbox"&&(n=o("#i"+t)),(e==="sandbox"?o(n.nodes()[0].contentDocument.body):o("body")).select(`[id="${t}"]`)},"getDiagramElement");export{d as g}; import{_ as a,d as o}from"./mermaid.core-CFUktQ8s.js";var d=a((t,e)=>{let n;return e==="sandbox"&&(n=o("#i"+t)),(e==="sandbox"?o(n.nodes()[0].contentDocument.body):o("body")).select(`[id="${t}"]`)},"getDiagramElement");export{d as g};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{_ as e}from"./mermaid.core-v0oo9NRr.js";var l=e(()=>` import{_ as e}from"./mermaid.core-CFUktQ8s.js";var l=e(()=>`
/* Font Awesome icon styling - consolidated */ /* Font Awesome icon styling - consolidated */
.label-icon { .label-icon {
display: inline-block; display: inline-block;
@@ -1 +1 @@
import{_ as a,e as w,l as x}from"./mermaid.core-v0oo9NRr.js";var d=a((e,t,i,r)=>{e.attr("class",i);const{width:o,height:h,x:n,y:c}=u(e,t);w(e,h,o,r);const s=l(n,c,o,h,t);e.attr("viewBox",s),x.debug(`viewBox configured: ${s} with padding: ${t}`)},"setupViewPortForSVG"),u=a((e,t)=>{const i=e.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:i.width+t*2,height:i.height+t*2,x:i.x,y:i.y}},"calculateDimensionsWithPadding"),l=a((e,t,i,r,o)=>`${e-o} ${t-o} ${i} ${r}`,"createViewBox");export{d as s}; import{_ as a,e as w,l as x}from"./mermaid.core-CFUktQ8s.js";var d=a((e,t,i,r)=>{e.attr("class",i);const{width:o,height:h,x:n,y:c}=u(e,t);w(e,h,o,r);const s=l(n,c,o,h,t);e.attr("viewBox",s),x.debug(`viewBox configured: ${s} with padding: ${t}`)},"setupViewPortForSVG"),u=a((e,t)=>{const i=e.node()?.getBBox()||{width:0,height:0,x:0,y:0};return{width:i.width+t*2,height:i.height+t*2,x:i.x,y:i.y}},"calculateDimensionsWithPadding"),l=a((e,t,i,r,o)=>`${e-o} ${t-o} ${i} ${r}`,"createViewBox");export{d as s};
@@ -1 +1 @@
import{_ as s}from"./mermaid.core-v0oo9NRr.js";var t,e=(t=class{constructor(i){this.init=i,this.records=this.init()}reset(){this.records=this.init()}},s(t,"ImperativeState"),t);export{e as I}; import{_ as s}from"./mermaid.core-CFUktQ8s.js";var t,e=(t=class{constructor(i){this.init=i,this.records=this.init()}reset(){this.records=this.init()}},s(t,"ImperativeState"),t);export{e as I};
@@ -1 +1 @@
import{_ as n,L as o,j as l}from"./mermaid.core-v0oo9NRr.js";var x=n((s,t)=>{const e=s.append("rect");if(e.attr("x",t.x),e.attr("y",t.y),e.attr("fill",t.fill),e.attr("stroke",t.stroke),e.attr("width",t.width),e.attr("height",t.height),t.name&&e.attr("name",t.name),t.rx&&e.attr("rx",t.rx),t.ry&&e.attr("ry",t.ry),t.attrs!==void 0)for(const r in t.attrs)e.attr(r,t.attrs[r]);return t.class&&e.attr("class",t.class),e},"drawRect"),d=n((s,t)=>{const e={x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,stroke:t.stroke,class:"rect"};x(s,e).lower()},"drawBackgroundRect"),g=n((s,t)=>{const e=t.text.replace(o," "),r=s.append("text");r.attr("x",t.x),r.attr("y",t.y),r.attr("class","legend"),r.style("text-anchor",t.anchor),t.class&&r.attr("class",t.class);const a=r.append("tspan");return a.attr("x",t.x+t.textMargin*2),a.text(e),r},"drawText"),h=n((s,t,e,r)=>{const a=s.append("image");a.attr("x",t),a.attr("y",e);const i=l.sanitizeUrl(r);a.attr("xlink:href",i)},"drawImage"),m=n((s,t,e,r)=>{const a=s.append("use");a.attr("x",t),a.attr("y",e);const i=l.sanitizeUrl(r);a.attr("xlink:href",`#${i}`)},"drawEmbeddedImage"),y=n(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),p=n(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj");export{d as a,p as b,m as c,x as d,h as e,g as f,y as g}; import{_ as n,L as o,j as l}from"./mermaid.core-CFUktQ8s.js";var x=n((s,t)=>{const e=s.append("rect");if(e.attr("x",t.x),e.attr("y",t.y),e.attr("fill",t.fill),e.attr("stroke",t.stroke),e.attr("width",t.width),e.attr("height",t.height),t.name&&e.attr("name",t.name),t.rx&&e.attr("rx",t.rx),t.ry&&e.attr("ry",t.ry),t.attrs!==void 0)for(const r in t.attrs)e.attr(r,t.attrs[r]);return t.class&&e.attr("class",t.class),e},"drawRect"),d=n((s,t)=>{const e={x:t.startx,y:t.starty,width:t.stopx-t.startx,height:t.stopy-t.starty,fill:t.fill,stroke:t.stroke,class:"rect"};x(s,e).lower()},"drawBackgroundRect"),g=n((s,t)=>{const e=t.text.replace(o," "),r=s.append("text");r.attr("x",t.x),r.attr("y",t.y),r.attr("class","legend"),r.style("text-anchor",t.anchor),t.class&&r.attr("class",t.class);const a=r.append("tspan");return a.attr("x",t.x+t.textMargin*2),a.text(e),r},"drawText"),h=n((s,t,e,r)=>{const a=s.append("image");a.attr("x",t),a.attr("y",e);const i=l.sanitizeUrl(r);a.attr("xlink:href",i)},"drawImage"),m=n((s,t,e,r)=>{const a=s.append("use");a.attr("x",t),a.attr("y",e);const i=l.sanitizeUrl(r);a.attr("xlink:href",`#${i}`)},"drawEmbeddedImage"),y=n(()=>({x:0,y:0,width:100,height:100,fill:"#EDF2AE",stroke:"#666",anchor:"start",rx:0,ry:0}),"getNoteRect"),p=n(()=>({x:0,y:0,width:100,height:100,"text-anchor":"start",style:"#666",textMargin:0,rx:0,ry:0,tspan:!0}),"getTextObj");export{d as a,p as b,m as c,x as d,h as e,g as f,y as g};
@@ -0,0 +1 @@
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-Cq2XT3QN.js";import{_ as i}from"./mermaid.core-CFUktQ8s.js";import"./chunk-FMBD7UC4-BALYpKmy.js";import"./chunk-55IACEB6-fbeP3Dtn.js";import"./chunk-QN33PNHL-B0_3_NGo.js";import"./index-DNCGxUDM.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-eem5VR5l.js";import{_ as i}from"./mermaid.core-v0oo9NRr.js";import"./chunk-FMBD7UC4-HblipWIM.js";import"./chunk-55IACEB6-CtULfmDo.js";import"./chunk-QN33PNHL-DSThOC6-.js";import"./index-8cIrvc8q.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -0,0 +1 @@
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-Cq2XT3QN.js";import{_ as i}from"./mermaid.core-CFUktQ8s.js";import"./chunk-FMBD7UC4-BALYpKmy.js";import"./chunk-55IACEB6-fbeP3Dtn.js";import"./chunk-QN33PNHL-B0_3_NGo.js";import"./index-DNCGxUDM.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
@@ -1 +0,0 @@
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-eem5VR5l.js";import{_ as i}from"./mermaid.core-v0oo9NRr.js";import"./chunk-FMBD7UC4-HblipWIM.js";import"./chunk-55IACEB6-CtULfmDo.js";import"./chunk-QN33PNHL-DSThOC6-.js";import"./index-8cIrvc8q.js";var u={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{u as diagram};
+1
View File
@@ -0,0 +1 @@
import{b as r}from"./_baseUniq-T1y-Xwdr.js";var e=4;function a(o){return r(o,e)}export{a as c};
-1
View File
@@ -1 +0,0 @@
import{b as r}from"./_baseUniq-DAOs4kUj.js";var e=4;function a(o){return r(o,e)}export{a as c};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{s as k,g as R,q as E,p as F,a as I,b as _,_ as l,H as D,y as G,D as f,E as P,F as C,l as z,K as H}from"./mermaid.core-v0oo9NRr.js";import{p as V}from"./chunk-4BX2VUAB-DWDvTYfd.js";import{p as W}from"./treemap-GDKQZRPO-DJjQsbt8.js";import"./index-8cIrvc8q.js";import"./_baseUniq-DAOs4kUj.js";import"./_basePickBy-CL4iQUG-.js";import"./clone-C1u3K6Fy.js";var h={showLegend:!0,ticks:5,max:null,min:0,graticule:"circle"},w={axes:[],curves:[],options:h},m=structuredClone(w),B=P.radar,j=l(()=>f({...B,...C().radar}),"getConfig"),b=l(()=>m.axes,"getAxes"),q=l(()=>m.curves,"getCurves"),K=l(()=>m.options,"getOptions"),N=l(a=>{m.axes=a.map(t=>({name:t.name,label:t.label??t.name}))},"setAxes"),U=l(a=>{m.curves=a.map(t=>({name:t.name,label:t.label??t.name,entries:X(t.entries)}))},"setCurves"),X=l(a=>{if(a[0].axis==null)return a.map(e=>e.value);const t=b();if(t.length===0)throw new Error("Axes must be populated before curves for reference entries");return t.map(e=>{const r=a.find(s=>s.axis?.$refText===e.name);if(r===void 0)throw new Error("Missing entry for axis "+e.label);return r.value})},"computeCurveEntries"),Y=l(a=>{const t=a.reduce((e,r)=>(e[r.name]=r,e),{});m.options={showLegend:t.showLegend?.value??h.showLegend,ticks:t.ticks?.value??h.ticks,max:t.max?.value??h.max,min:t.min?.value??h.min,graticule:t.graticule?.value??h.graticule}},"setOptions"),Z=l(()=>{G(),m=structuredClone(w)},"clear"),$={getAxes:b,getCurves:q,getOptions:K,setAxes:N,setCurves:U,setOptions:Y,getConfig:j,clear:Z,setAccTitle:_,getAccTitle:I,setDiagramTitle:F,getDiagramTitle:E,getAccDescription:R,setAccDescription:k},J=l(a=>{V(a,$);const{axes:t,curves:e,options:r}=a;$.setAxes(t),$.setCurves(e),$.setOptions(r)},"populate"),Q={parse:l(async a=>{const t=await W("radar",a);z.debug(t),J(t)},"parse")},tt=l((a,t,e,r)=>{const s=r.db,o=s.getAxes(),i=s.getCurves(),n=s.getOptions(),c=s.getConfig(),d=s.getDiagramTitle(),u=D(t),p=et(u,c),g=n.max??Math.max(...i.map(y=>Math.max(...y.entries))),x=n.min,v=Math.min(c.width,c.height)/2;at(p,o,v,n.ticks,n.graticule),rt(p,o,v,c),M(p,o,i,x,g,n.graticule,c),T(p,i,n.showLegend,c),p.append("text").attr("class","radarTitle").text(d).attr("x",0).attr("y",-c.height/2-c.marginTop)},"draw"),et=l((a,t)=>{const e=t.width+t.marginLeft+t.marginRight,r=t.height+t.marginTop+t.marginBottom,s={x:t.marginLeft+t.width/2,y:t.marginTop+t.height/2};return a.attr("viewbox",`0 0 ${e} ${r}`).attr("width",e).attr("height",r),a.append("g").attr("transform",`translate(${s.x}, ${s.y})`)},"drawFrame"),at=l((a,t,e,r,s)=>{if(s==="circle")for(let o=0;o<r;o++){const i=e*(o+1)/r;a.append("circle").attr("r",i).attr("class","radarGraticule")}else if(s==="polygon"){const o=t.length;for(let i=0;i<r;i++){const n=e*(i+1)/r,c=t.map((d,u)=>{const p=2*u*Math.PI/o-Math.PI/2,g=n*Math.cos(p),x=n*Math.sin(p);return`${g},${x}`}).join(" ");a.append("polygon").attr("points",c).attr("class","radarGraticule")}}},"drawGraticule"),rt=l((a,t,e,r)=>{const s=t.length;for(let o=0;o<s;o++){const i=t[o].label,n=2*o*Math.PI/s-Math.PI/2;a.append("line").attr("x1",0).attr("y1",0).attr("x2",e*r.axisScaleFactor*Math.cos(n)).attr("y2",e*r.axisScaleFactor*Math.sin(n)).attr("class","radarAxisLine"),a.append("text").text(i).attr("x",e*r.axisLabelFactor*Math.cos(n)).attr("y",e*r.axisLabelFactor*Math.sin(n)).attr("class","radarAxisLabel")}},"drawAxes");function M(a,t,e,r,s,o,i){const n=t.length,c=Math.min(i.width,i.height)/2;e.forEach((d,u)=>{if(d.entries.length!==n)return;const p=d.entries.map((g,x)=>{const v=2*Math.PI*x/n-Math.PI/2,y=A(g,r,s,c),O=y*Math.cos(v),S=y*Math.sin(v);return{x:O,y:S}});o==="circle"?a.append("path").attr("d",L(p,i.curveTension)).attr("class",`radarCurve-${u}`):o==="polygon"&&a.append("polygon").attr("points",p.map(g=>`${g.x},${g.y}`).join(" ")).attr("class",`radarCurve-${u}`)})}l(M,"drawCurves");function A(a,t,e,r){const s=Math.min(Math.max(a,t),e);return r*(s-t)/(e-t)}l(A,"relativeRadius");function L(a,t){const e=a.length;let r=`M${a[0].x},${a[0].y}`;for(let s=0;s<e;s++){const o=a[(s-1+e)%e],i=a[s],n=a[(s+1)%e],c=a[(s+2)%e],d={x:i.x+(n.x-o.x)*t,y:i.y+(n.y-o.y)*t},u={x:n.x-(c.x-i.x)*t,y:n.y-(c.y-i.y)*t};r+=` C${d.x},${d.y} ${u.x},${u.y} ${n.x},${n.y}`}return`${r} Z`}l(L,"closedRoundCurve");function T(a,t,e,r){if(!e)return;const s=(r.width/2+r.marginRight)*3/4,o=-(r.height/2+r.marginTop)*3/4,i=20;t.forEach((n,c)=>{const d=a.append("g").attr("transform",`translate(${s}, ${o+c*i})`);d.append("rect").attr("width",12).attr("height",12).attr("class",`radarLegendBox-${c}`),d.append("text").attr("x",16).attr("y",0).attr("class","radarLegendText").text(n.label)})}l(T,"drawLegend");var st={draw:tt},nt=l((a,t)=>{let e="";for(let r=0;r<a.THEME_COLOR_LIMIT;r++){const s=a[`cScale${r}`];e+=` import{s as k,g as R,q as E,p as F,a as I,b as _,_ as l,H as D,y as G,D as f,E as P,F as C,l as z,K as H}from"./mermaid.core-CFUktQ8s.js";import{p as V}from"./chunk-4BX2VUAB-DHPPu6Xd.js";import{p as W}from"./treemap-GDKQZRPO-DWvWdchV.js";import"./index-DNCGxUDM.js";import"./_baseUniq-T1y-Xwdr.js";import"./_basePickBy-8KTOl_Ov.js";import"./clone-4l7SFgdX.js";var h={showLegend:!0,ticks:5,max:null,min:0,graticule:"circle"},w={axes:[],curves:[],options:h},m=structuredClone(w),B=P.radar,j=l(()=>f({...B,...C().radar}),"getConfig"),b=l(()=>m.axes,"getAxes"),q=l(()=>m.curves,"getCurves"),K=l(()=>m.options,"getOptions"),N=l(a=>{m.axes=a.map(t=>({name:t.name,label:t.label??t.name}))},"setAxes"),U=l(a=>{m.curves=a.map(t=>({name:t.name,label:t.label??t.name,entries:X(t.entries)}))},"setCurves"),X=l(a=>{if(a[0].axis==null)return a.map(e=>e.value);const t=b();if(t.length===0)throw new Error("Axes must be populated before curves for reference entries");return t.map(e=>{const r=a.find(s=>s.axis?.$refText===e.name);if(r===void 0)throw new Error("Missing entry for axis "+e.label);return r.value})},"computeCurveEntries"),Y=l(a=>{const t=a.reduce((e,r)=>(e[r.name]=r,e),{});m.options={showLegend:t.showLegend?.value??h.showLegend,ticks:t.ticks?.value??h.ticks,max:t.max?.value??h.max,min:t.min?.value??h.min,graticule:t.graticule?.value??h.graticule}},"setOptions"),Z=l(()=>{G(),m=structuredClone(w)},"clear"),$={getAxes:b,getCurves:q,getOptions:K,setAxes:N,setCurves:U,setOptions:Y,getConfig:j,clear:Z,setAccTitle:_,getAccTitle:I,setDiagramTitle:F,getDiagramTitle:E,getAccDescription:R,setAccDescription:k},J=l(a=>{V(a,$);const{axes:t,curves:e,options:r}=a;$.setAxes(t),$.setCurves(e),$.setOptions(r)},"populate"),Q={parse:l(async a=>{const t=await W("radar",a);z.debug(t),J(t)},"parse")},tt=l((a,t,e,r)=>{const s=r.db,o=s.getAxes(),i=s.getCurves(),n=s.getOptions(),c=s.getConfig(),d=s.getDiagramTitle(),u=D(t),p=et(u,c),g=n.max??Math.max(...i.map(y=>Math.max(...y.entries))),x=n.min,v=Math.min(c.width,c.height)/2;at(p,o,v,n.ticks,n.graticule),rt(p,o,v,c),M(p,o,i,x,g,n.graticule,c),T(p,i,n.showLegend,c),p.append("text").attr("class","radarTitle").text(d).attr("x",0).attr("y",-c.height/2-c.marginTop)},"draw"),et=l((a,t)=>{const e=t.width+t.marginLeft+t.marginRight,r=t.height+t.marginTop+t.marginBottom,s={x:t.marginLeft+t.width/2,y:t.marginTop+t.height/2};return a.attr("viewbox",`0 0 ${e} ${r}`).attr("width",e).attr("height",r),a.append("g").attr("transform",`translate(${s.x}, ${s.y})`)},"drawFrame"),at=l((a,t,e,r,s)=>{if(s==="circle")for(let o=0;o<r;o++){const i=e*(o+1)/r;a.append("circle").attr("r",i).attr("class","radarGraticule")}else if(s==="polygon"){const o=t.length;for(let i=0;i<r;i++){const n=e*(i+1)/r,c=t.map((d,u)=>{const p=2*u*Math.PI/o-Math.PI/2,g=n*Math.cos(p),x=n*Math.sin(p);return`${g},${x}`}).join(" ");a.append("polygon").attr("points",c).attr("class","radarGraticule")}}},"drawGraticule"),rt=l((a,t,e,r)=>{const s=t.length;for(let o=0;o<s;o++){const i=t[o].label,n=2*o*Math.PI/s-Math.PI/2;a.append("line").attr("x1",0).attr("y1",0).attr("x2",e*r.axisScaleFactor*Math.cos(n)).attr("y2",e*r.axisScaleFactor*Math.sin(n)).attr("class","radarAxisLine"),a.append("text").text(i).attr("x",e*r.axisLabelFactor*Math.cos(n)).attr("y",e*r.axisLabelFactor*Math.sin(n)).attr("class","radarAxisLabel")}},"drawAxes");function M(a,t,e,r,s,o,i){const n=t.length,c=Math.min(i.width,i.height)/2;e.forEach((d,u)=>{if(d.entries.length!==n)return;const p=d.entries.map((g,x)=>{const v=2*Math.PI*x/n-Math.PI/2,y=A(g,r,s,c),O=y*Math.cos(v),S=y*Math.sin(v);return{x:O,y:S}});o==="circle"?a.append("path").attr("d",L(p,i.curveTension)).attr("class",`radarCurve-${u}`):o==="polygon"&&a.append("polygon").attr("points",p.map(g=>`${g.x},${g.y}`).join(" ")).attr("class",`radarCurve-${u}`)})}l(M,"drawCurves");function A(a,t,e,r){const s=Math.min(Math.max(a,t),e);return r*(s-t)/(e-t)}l(A,"relativeRadius");function L(a,t){const e=a.length;let r=`M${a[0].x},${a[0].y}`;for(let s=0;s<e;s++){const o=a[(s-1+e)%e],i=a[s],n=a[(s+1)%e],c=a[(s+2)%e],d={x:i.x+(n.x-o.x)*t,y:i.y+(n.y-o.y)*t},u={x:n.x-(c.x-i.x)*t,y:n.y-(c.y-i.y)*t};r+=` C${d.x},${d.y} ${u.x},${u.y} ${n.x},${n.y}`}return`${r} Z`}l(L,"closedRoundCurve");function T(a,t,e,r){if(!e)return;const s=(r.width/2+r.marginRight)*3/4,o=-(r.height/2+r.marginTop)*3/4,i=20;t.forEach((n,c)=>{const d=a.append("g").attr("transform",`translate(${s}, ${o+c*i})`);d.append("rect").attr("width",12).attr("height",12).attr("class",`radarLegendBox-${c}`),d.append("text").attr("x",16).attr("y",0).attr("class","radarLegendText").text(n.label)})}l(T,"drawLegend");var st={draw:tt},nt=l((a,t)=>{let e="";for(let r=0;r<a.THEME_COLOR_LIMIT;r++){const s=a[`cScale${r}`];e+=`
.radarCurve-${r} { .radarCurve-${r} {
color: ${s}; color: ${s};
fill: ${s}; fill: ${s};
@@ -1,4 +1,4 @@
import{_ as b,D as m,H as B,e as C,l as w,b as S,a as D,p as T,q as E,g as F,s as P,E as z,F as A,y as W}from"./mermaid.core-v0oo9NRr.js";import{p as _}from"./chunk-4BX2VUAB-DWDvTYfd.js";import{p as N}from"./treemap-GDKQZRPO-DJjQsbt8.js";import"./index-8cIrvc8q.js";import"./_baseUniq-DAOs4kUj.js";import"./_basePickBy-CL4iQUG-.js";import"./clone-C1u3K6Fy.js";var L=z.packet,u,v=(u=class{constructor(){this.packet=[],this.setAccTitle=S,this.getAccTitle=D,this.setDiagramTitle=T,this.getDiagramTitle=E,this.getAccDescription=F,this.setAccDescription=P}getConfig(){const t=m({...L,...A().packet});return t.showBits&&(t.paddingY+=10),t}getPacket(){return this.packet}pushWord(t){t.length>0&&this.packet.push(t)}clear(){W(),this.packet=[]}},b(u,"PacketDB"),u),M=1e4,Y=b((e,t)=>{_(e,t);let r=-1,o=[],n=1;const{bitsPerRow:l}=t.getConfig();for(let{start:a,end:i,bits:d,label:c}of e.blocks){if(a!==void 0&&i!==void 0&&i<a)throw new Error(`Packet block ${a} - ${i} is invalid. End must be greater than start.`);if(a??=r+1,a!==r+1)throw new Error(`Packet block ${a} - ${i??a} is not contiguous. It should start from ${r+1}.`);if(d===0)throw new Error(`Packet block ${a} is invalid. Cannot have a zero bit field.`);for(i??=a+(d??1)-1,d??=i-a+1,r=i,w.debug(`Packet block ${a} - ${r} with label ${c}`);o.length<=l+1&&t.getPacket().length<M;){const[p,s]=H({start:a,end:i,bits:d,label:c},n,l);if(o.push(p),p.end+1===n*l&&(t.pushWord(o),o=[],n++),!s)break;({start:a,end:i,bits:d,label:c}=s)}}t.pushWord(o)},"populate"),H=b((e,t,r)=>{if(e.start===void 0)throw new Error("start should have been set during first phase");if(e.end===void 0)throw new Error("end should have been set during first phase");if(e.start>e.end)throw new Error(`Block start ${e.start} is greater than block end ${e.end}.`);if(e.end+1<=t*r)return[e,void 0];const o=t*r-1,n=t*r;return[{start:e.start,end:o,label:e.label,bits:o-e.start},{start:n,end:e.end,label:e.label,bits:e.end-n}]},"getNextFittingBlock"),x={parser:{yy:void 0},parse:b(async e=>{const t=await N("packet",e),r=x.parser?.yy;if(!(r instanceof v))throw new Error("parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");w.debug(t),Y(t,r)},"parse")},I=b((e,t,r,o)=>{const n=o.db,l=n.getConfig(),{rowHeight:a,paddingY:i,bitWidth:d,bitsPerRow:c}=l,p=n.getPacket(),s=n.getDiagramTitle(),h=a+i,g=h*(p.length+1)-(s?0:a),k=d*c+2,f=B(t);f.attr("viewbox",`0 0 ${k} ${g}`),C(f,g,k,l.useMaxWidth);for(const[y,$]of p.entries())O(f,$,y,l);f.append("text").text(s).attr("x",k/2).attr("y",g-h/2).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("class","packetTitle")},"draw"),O=b((e,t,r,{rowHeight:o,paddingX:n,paddingY:l,bitWidth:a,bitsPerRow:i,showBits:d})=>{const c=e.append("g"),p=r*(o+l)+l;for(const s of t){const h=s.start%i*a+1,g=(s.end-s.start+1)*a-n;if(c.append("rect").attr("x",h).attr("y",p).attr("width",g).attr("height",o).attr("class","packetBlock"),c.append("text").attr("x",h+g/2).attr("y",p+o/2).attr("class","packetLabel").attr("dominant-baseline","middle").attr("text-anchor","middle").text(s.label),!d)continue;const k=s.end===s.start,f=p-2;c.append("text").attr("x",h+(k?g/2:0)).attr("y",f).attr("class","packetByte start").attr("dominant-baseline","auto").attr("text-anchor",k?"middle":"start").text(s.start),k||c.append("text").attr("x",h+g).attr("y",f).attr("class","packetByte end").attr("dominant-baseline","auto").attr("text-anchor","end").text(s.end)}},"drawWord"),j={draw:I},q={byteFontSize:"10px",startByteColor:"black",endByteColor:"black",labelColor:"black",labelFontSize:"12px",titleColor:"black",titleFontSize:"14px",blockStrokeColor:"black",blockStrokeWidth:"1",blockFillColor:"#efefef"},G=b(({packet:e}={})=>{const t=m(q,e);return` import{_ as b,D as m,H as B,e as C,l as w,b as S,a as D,p as T,q as E,g as F,s as P,E as z,F as A,y as W}from"./mermaid.core-CFUktQ8s.js";import{p as _}from"./chunk-4BX2VUAB-DHPPu6Xd.js";import{p as N}from"./treemap-GDKQZRPO-DWvWdchV.js";import"./index-DNCGxUDM.js";import"./_baseUniq-T1y-Xwdr.js";import"./_basePickBy-8KTOl_Ov.js";import"./clone-4l7SFgdX.js";var L=z.packet,u,v=(u=class{constructor(){this.packet=[],this.setAccTitle=S,this.getAccTitle=D,this.setDiagramTitle=T,this.getDiagramTitle=E,this.getAccDescription=F,this.setAccDescription=P}getConfig(){const t=m({...L,...A().packet});return t.showBits&&(t.paddingY+=10),t}getPacket(){return this.packet}pushWord(t){t.length>0&&this.packet.push(t)}clear(){W(),this.packet=[]}},b(u,"PacketDB"),u),M=1e4,Y=b((e,t)=>{_(e,t);let r=-1,o=[],n=1;const{bitsPerRow:l}=t.getConfig();for(let{start:a,end:i,bits:d,label:c}of e.blocks){if(a!==void 0&&i!==void 0&&i<a)throw new Error(`Packet block ${a} - ${i} is invalid. End must be greater than start.`);if(a??=r+1,a!==r+1)throw new Error(`Packet block ${a} - ${i??a} is not contiguous. It should start from ${r+1}.`);if(d===0)throw new Error(`Packet block ${a} is invalid. Cannot have a zero bit field.`);for(i??=a+(d??1)-1,d??=i-a+1,r=i,w.debug(`Packet block ${a} - ${r} with label ${c}`);o.length<=l+1&&t.getPacket().length<M;){const[p,s]=H({start:a,end:i,bits:d,label:c},n,l);if(o.push(p),p.end+1===n*l&&(t.pushWord(o),o=[],n++),!s)break;({start:a,end:i,bits:d,label:c}=s)}}t.pushWord(o)},"populate"),H=b((e,t,r)=>{if(e.start===void 0)throw new Error("start should have been set during first phase");if(e.end===void 0)throw new Error("end should have been set during first phase");if(e.start>e.end)throw new Error(`Block start ${e.start} is greater than block end ${e.end}.`);if(e.end+1<=t*r)return[e,void 0];const o=t*r-1,n=t*r;return[{start:e.start,end:o,label:e.label,bits:o-e.start},{start:n,end:e.end,label:e.label,bits:e.end-n}]},"getNextFittingBlock"),x={parser:{yy:void 0},parse:b(async e=>{const t=await N("packet",e),r=x.parser?.yy;if(!(r instanceof v))throw new Error("parser.parser?.yy was not a PacketDB. This is due to a bug within Mermaid, please report this issue at https://github.com/mermaid-js/mermaid/issues.");w.debug(t),Y(t,r)},"parse")},I=b((e,t,r,o)=>{const n=o.db,l=n.getConfig(),{rowHeight:a,paddingY:i,bitWidth:d,bitsPerRow:c}=l,p=n.getPacket(),s=n.getDiagramTitle(),h=a+i,g=h*(p.length+1)-(s?0:a),k=d*c+2,f=B(t);f.attr("viewbox",`0 0 ${k} ${g}`),C(f,g,k,l.useMaxWidth);for(const[y,$]of p.entries())O(f,$,y,l);f.append("text").text(s).attr("x",k/2).attr("y",g-h/2).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("class","packetTitle")},"draw"),O=b((e,t,r,{rowHeight:o,paddingX:n,paddingY:l,bitWidth:a,bitsPerRow:i,showBits:d})=>{const c=e.append("g"),p=r*(o+l)+l;for(const s of t){const h=s.start%i*a+1,g=(s.end-s.start+1)*a-n;if(c.append("rect").attr("x",h).attr("y",p).attr("width",g).attr("height",o).attr("class","packetBlock"),c.append("text").attr("x",h+g/2).attr("y",p+o/2).attr("class","packetLabel").attr("dominant-baseline","middle").attr("text-anchor","middle").text(s.label),!d)continue;const k=s.end===s.start,f=p-2;c.append("text").attr("x",h+(k?g/2:0)).attr("y",f).attr("class","packetByte start").attr("dominant-baseline","auto").attr("text-anchor",k?"middle":"start").text(s.start),k||c.append("text").attr("x",h+g).attr("y",f).attr("class","packetByte end").attr("dominant-baseline","auto").attr("text-anchor","end").text(s.end)}},"drawWord"),j={draw:I},q={byteFontSize:"10px",startByteColor:"black",endByteColor:"black",labelColor:"black",labelFontSize:"12px",titleColor:"black",titleFontSize:"14px",blockStrokeColor:"black",blockStrokeWidth:"1",blockFillColor:"#efefef"},G=b(({packet:e}={})=>{const t=m(q,e);return`
.packetByte { .packetByte {
font-size: ${t.byteFontSize}; font-size: ${t.byteFontSize};
} }
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/FilmGrid-BM-3a1vS.js","assets/FilmGrid.vue_vue_type_script_setup_true_lang-Dj0SEfcW.js","assets/index-8cIrvc8q.js","assets/index-BJkaQ2c4.css","assets/useContentImages-7wLVntsF.js","assets/FilmDetail-0aNPT6Ze.js","assets/FilmDetail.vue_vue_type_script_setup_true_lang-BhKlPG3Y.js"])))=>i.map(i=>d[i]);
import{d as e,_ as r}from"./index-8cIrvc8q.js";const _={id:"film",name:"Film Renderer",contentType:"film",surfaces:["chat-preview","panel-preview","panel-play"],chatPreview:e(()=>r(()=>import("./FilmGrid-BM-3a1vS.js"),__vite__mapDeps([0,1,2,3,4]))),panelPreview:e(()=>r(()=>import("./FilmGrid-BM-3a1vS.js"),__vite__mapDeps([0,1,2,3,4]))),panelPlay:e(()=>r(()=>import("./FilmDetail-0aNPT6Ze.js"),__vite__mapDeps([5,6,2,3])))};export{_ as filmRenderer};
@@ -0,0 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/FilmGrid-Dz4qYSg3.js","assets/FilmGrid.vue_vue_type_script_setup_true_lang-CkIQ4bRp.js","assets/index-DNCGxUDM.js","assets/index-BJh-vGUe.css","assets/useContentImages-DdjyABL9.js","assets/FilmDetail-D9AyPH42.js","assets/FilmDetail.vue_vue_type_script_setup_true_lang-Cgf4f-Ng.js"])))=>i.map(i=>d[i]);
import{d as e,_ as r}from"./index-DNCGxUDM.js";const _={id:"film",name:"Film Renderer",contentType:"film",surfaces:["chat-preview","panel-preview","panel-play"],chatPreview:e(()=>r(()=>import("./FilmGrid-Dz4qYSg3.js"),__vite__mapDeps([0,1,2,3,4]))),panelPreview:e(()=>r(()=>import("./FilmGrid-Dz4qYSg3.js"),__vite__mapDeps([0,1,2,3,4]))),panelPlay:e(()=>r(()=>import("./FilmDetail-D9AyPH42.js"),__vite__mapDeps([5,6,2,3])))};export{_ as filmRenderer};
@@ -1,4 +1,4 @@
import{g as qt}from"./chunk-FMBD7UC4-HblipWIM.js";import{_ as m,n as Ot,l as $,c as b1,d as E1,o as Ht,r as Xt,u as it,b as Qt,s as Jt,p as Zt,a as $t,g as te,q as ee,k as se,t as ie,J as re,v as ae,x as st,y as ne,z as ue,A as oe}from"./mermaid.core-v0oo9NRr.js";import{g as le}from"./chunk-55IACEB6-CtULfmDo.js";import{s as ce}from"./chunk-QN33PNHL-DSThOC6-.js";import{c as he}from"./channel-Dg2Em7BA.js";import"./index-8cIrvc8q.js";var de="flowchart-",G1,pe=(G1=class{constructor(){this.vertexCounter=0,this.config=b1(),this.vertices=new Map,this.edges=[],this.classes=new Map,this.subGraphs=[],this.subGraphLookup=new Map,this.tooltips=new Map,this.subCount=0,this.firstGraphFlag=!0,this.secCount=-1,this.posCrossRef=[],this.funs=[],this.setAccTitle=Qt,this.setAccDescription=Jt,this.setDiagramTitle=Zt,this.getAccTitle=$t,this.getAccDescription=te,this.getDiagramTitle=ee,this.funs.push(this.setupToolTips.bind(this)),this.addVertex=this.addVertex.bind(this),this.firstGraph=this.firstGraph.bind(this),this.setDirection=this.setDirection.bind(this),this.addSubGraph=this.addSubGraph.bind(this),this.addLink=this.addLink.bind(this),this.setLink=this.setLink.bind(this),this.updateLink=this.updateLink.bind(this),this.addClass=this.addClass.bind(this),this.setClass=this.setClass.bind(this),this.destructLink=this.destructLink.bind(this),this.setClickEvent=this.setClickEvent.bind(this),this.setTooltip=this.setTooltip.bind(this),this.updateLinkInterpolate=this.updateLinkInterpolate.bind(this),this.setClickFun=this.setClickFun.bind(this),this.bindFunctions=this.bindFunctions.bind(this),this.lex={firstGraph:this.firstGraph.bind(this)},this.clear(),this.setGen("gen-2")}sanitizeText(i){return se.sanitizeText(i,this.config)}lookUpDomId(i){for(const r of this.vertices.values())if(r.id===i)return r.domId;return i}addVertex(i,r,a,n,l,g,c={},b){if(!i||i.trim().length===0)return;let u;if(b!==void 0){let k;b.includes(` import{g as qt}from"./chunk-FMBD7UC4-BALYpKmy.js";import{_ as m,n as Ot,l as $,c as b1,d as E1,o as Ht,r as Xt,u as it,b as Qt,s as Jt,p as Zt,a as $t,g as te,q as ee,k as se,t as ie,J as re,v as ae,x as st,y as ne,z as ue,A as oe}from"./mermaid.core-CFUktQ8s.js";import{g as le}from"./chunk-55IACEB6-fbeP3Dtn.js";import{s as ce}from"./chunk-QN33PNHL-B0_3_NGo.js";import{c as he}from"./channel-BXJ3-Z4Z.js";import"./index-DNCGxUDM.js";var de="flowchart-",G1,pe=(G1=class{constructor(){this.vertexCounter=0,this.config=b1(),this.vertices=new Map,this.edges=[],this.classes=new Map,this.subGraphs=[],this.subGraphLookup=new Map,this.tooltips=new Map,this.subCount=0,this.firstGraphFlag=!0,this.secCount=-1,this.posCrossRef=[],this.funs=[],this.setAccTitle=Qt,this.setAccDescription=Jt,this.setDiagramTitle=Zt,this.getAccTitle=$t,this.getAccDescription=te,this.getDiagramTitle=ee,this.funs.push(this.setupToolTips.bind(this)),this.addVertex=this.addVertex.bind(this),this.firstGraph=this.firstGraph.bind(this),this.setDirection=this.setDirection.bind(this),this.addSubGraph=this.addSubGraph.bind(this),this.addLink=this.addLink.bind(this),this.setLink=this.setLink.bind(this),this.updateLink=this.updateLink.bind(this),this.addClass=this.addClass.bind(this),this.setClass=this.setClass.bind(this),this.destructLink=this.destructLink.bind(this),this.setClickEvent=this.setClickEvent.bind(this),this.setTooltip=this.setTooltip.bind(this),this.updateLinkInterpolate=this.updateLinkInterpolate.bind(this),this.setClickFun=this.setClickFun.bind(this),this.bindFunctions=this.bindFunctions.bind(this),this.lex={firstGraph:this.firstGraph.bind(this)},this.clear(),this.setGen("gen-2")}sanitizeText(i){return se.sanitizeText(i,this.config)}lookUpDomId(i){for(const r of this.vertices.values())if(r.id===i)return r.domId;return i}addVertex(i,r,a,n,l,g,c={},b){if(!i||i.trim().length===0)return;let u;if(b!==void 0){let k;b.includes(`
`)?k=b+` `)?k=b+`
`:k=`{ `:k=`{
`+b+` `+b+`
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
import{_ as e,l as s,H as n,e as i,I as p}from"./mermaid.core-v0oo9NRr.js";import{p as g}from"./treemap-GDKQZRPO-DJjQsbt8.js";import"./index-8cIrvc8q.js";import"./_baseUniq-DAOs4kUj.js";import"./_basePickBy-CL4iQUG-.js";import"./clone-C1u3K6Fy.js";var v={parse:e(async r=>{const a=await g("info",r);s.debug(a)},"parse")},d={version:p.version+""},m=e(()=>d.version,"getVersion"),c={getVersion:m},l=e((r,a,o)=>{s.debug(`rendering info diagram import{_ as e,l as s,H as n,e as i,I as p}from"./mermaid.core-CFUktQ8s.js";import{p as g}from"./treemap-GDKQZRPO-DWvWdchV.js";import"./index-DNCGxUDM.js";import"./_baseUniq-T1y-Xwdr.js";import"./_basePickBy-8KTOl_Ov.js";import"./clone-4l7SFgdX.js";var v={parse:e(async r=>{const a=await g("info",r);s.debug(a)},"parse")},d={version:p.version+""},m=e(()=>d.version,"getVersion"),c={getVersion:m},l=e((r,a,o)=>{s.debug(`rendering info diagram
`+r);const t=n(a);i(t,100,400,!0),t.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${o}`)},"draw"),f={draw:l},z={parser:v,db:c,renderer:f};export{z as diagram}; `+r);const t=n(a);i(t,100,400,!0),t.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size",32).style("text-anchor","middle").text(`v${o}`)},"draw"),f={draw:l},z={parser:v,db:c,renderer:f};export{z as diagram};
@@ -1,4 +1,4 @@
import{a as gt,g as lt,f as mt,d as xt}from"./chunk-TZMSLE5B-93PKdbpb.js";import{g as kt}from"./chunk-FMBD7UC4-HblipWIM.js";import{g as _t,s as vt,a as bt,b as wt,q as Tt,p as St,_ as n,c as R,d as X,e as $t,y as Mt}from"./mermaid.core-v0oo9NRr.js";import{d as et}from"./arc-UjuE1bPP.js";import"./index-8cIrvc8q.js";var U=(function(){var t=n(function(h,i,a,l){for(a=a||{},l=h.length;l--;a[h[l]]=i);return a},"o"),e=[6,8,10,11,12,14,16,17,18],s=[1,9],c=[1,10],r=[1,11],f=[1,12],u=[1,13],y=[1,14],g={trace:n(function(){},"trace"),yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,taskName:18,taskData:19,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",18:"taskName",19:"taskData"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,2]],performAction:n(function(i,a,l,d,p,o,b){var k=o.length-1;switch(p){case 1:return o[k-1];case 2:this.$=[];break;case 3:o[k-1].push(o[k]),this.$=o[k-1];break;case 4:case 5:this.$=o[k];break;case 6:case 7:this.$=[];break;case 8:d.setDiagramTitle(o[k].substr(6)),this.$=o[k].substr(6);break;case 9:this.$=o[k].trim(),d.setAccTitle(this.$);break;case 10:case 11:this.$=o[k].trim(),d.setAccDescription(this.$);break;case 12:d.addSection(o[k].substr(8)),this.$=o[k].substr(8);break;case 13:d.addTask(o[k-1],o[k]),this.$="task";break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:s,12:c,14:r,16:f,17:u,18:y},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:15,11:s,12:c,14:r,16:f,17:u,18:y},t(e,[2,5]),t(e,[2,6]),t(e,[2,8]),{13:[1,16]},{15:[1,17]},t(e,[2,11]),t(e,[2,12]),{19:[1,18]},t(e,[2,4]),t(e,[2,9]),t(e,[2,10]),t(e,[2,13])],defaultActions:{},parseError:n(function(i,a){if(a.recoverable)this.trace(i);else{var l=new Error(i);throw l.hash=a,l}},"parseError"),parse:n(function(i){var a=this,l=[0],d=[],p=[null],o=[],b=this.table,k="",C=0,K=0,dt=2,Q=1,yt=o.slice.call(arguments,1),_=Object.create(this.lexer),I={yy:{}};for(var O in this.yy)Object.prototype.hasOwnProperty.call(this.yy,O)&&(I.yy[O]=this.yy[O]);_.setInput(i,I.yy),I.yy.lexer=_,I.yy.parser=this,typeof _.yylloc>"u"&&(_.yylloc={});var Y=_.yylloc;o.push(Y);var ft=_.options&&_.options.ranges;typeof I.yy.parseError=="function"?this.parseError=I.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function pt(w){l.length=l.length-2*w,p.length=p.length-w,o.length=o.length-w}n(pt,"popStack");function D(){var w;return w=d.pop()||_.lex()||Q,typeof w!="number"&&(w instanceof Array&&(d=w,w=d.pop()),w=a.symbols_[w]||w),w}n(D,"lex");for(var v,A,T,q,F={},N,M,tt,z;;){if(A=l[l.length-1],this.defaultActions[A]?T=this.defaultActions[A]:((v===null||typeof v>"u")&&(v=D()),T=b[A]&&b[A][v]),typeof T>"u"||!T.length||!T[0]){var H="";z=[];for(N in b[A])this.terminals_[N]&&N>dt&&z.push("'"+this.terminals_[N]+"'");_.showPosition?H="Parse error on line "+(C+1)+`: import{a as gt,g as lt,f as mt,d as xt}from"./chunk-TZMSLE5B-Cajwq1yU.js";import{g as kt}from"./chunk-FMBD7UC4-BALYpKmy.js";import{g as _t,s as vt,a as bt,b as wt,q as Tt,p as St,_ as n,c as R,d as X,e as $t,y as Mt}from"./mermaid.core-CFUktQ8s.js";import{d as et}from"./arc-P9DEh39j.js";import"./index-DNCGxUDM.js";var U=(function(){var t=n(function(h,i,a,l){for(a=a||{},l=h.length;l--;a[h[l]]=i);return a},"o"),e=[6,8,10,11,12,14,16,17,18],s=[1,9],c=[1,10],r=[1,11],f=[1,12],u=[1,13],y=[1,14],g={trace:n(function(){},"trace"),yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,taskName:18,taskData:19,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",18:"taskName",19:"taskData"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,2]],performAction:n(function(i,a,l,d,p,o,b){var k=o.length-1;switch(p){case 1:return o[k-1];case 2:this.$=[];break;case 3:o[k-1].push(o[k]),this.$=o[k-1];break;case 4:case 5:this.$=o[k];break;case 6:case 7:this.$=[];break;case 8:d.setDiagramTitle(o[k].substr(6)),this.$=o[k].substr(6);break;case 9:this.$=o[k].trim(),d.setAccTitle(this.$);break;case 10:case 11:this.$=o[k].trim(),d.setAccDescription(this.$);break;case 12:d.addSection(o[k].substr(8)),this.$=o[k].substr(8);break;case 13:d.addTask(o[k-1],o[k]),this.$="task";break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:s,12:c,14:r,16:f,17:u,18:y},t(e,[2,7],{1:[2,1]}),t(e,[2,3]),{9:15,11:s,12:c,14:r,16:f,17:u,18:y},t(e,[2,5]),t(e,[2,6]),t(e,[2,8]),{13:[1,16]},{15:[1,17]},t(e,[2,11]),t(e,[2,12]),{19:[1,18]},t(e,[2,4]),t(e,[2,9]),t(e,[2,10]),t(e,[2,13])],defaultActions:{},parseError:n(function(i,a){if(a.recoverable)this.trace(i);else{var l=new Error(i);throw l.hash=a,l}},"parseError"),parse:n(function(i){var a=this,l=[0],d=[],p=[null],o=[],b=this.table,k="",C=0,K=0,dt=2,Q=1,yt=o.slice.call(arguments,1),_=Object.create(this.lexer),I={yy:{}};for(var O in this.yy)Object.prototype.hasOwnProperty.call(this.yy,O)&&(I.yy[O]=this.yy[O]);_.setInput(i,I.yy),I.yy.lexer=_,I.yy.parser=this,typeof _.yylloc>"u"&&(_.yylloc={});var Y=_.yylloc;o.push(Y);var ft=_.options&&_.options.ranges;typeof I.yy.parseError=="function"?this.parseError=I.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function pt(w){l.length=l.length-2*w,p.length=p.length-w,o.length=o.length-w}n(pt,"popStack");function D(){var w;return w=d.pop()||_.lex()||Q,typeof w!="number"&&(w instanceof Array&&(d=w,w=d.pop()),w=a.symbols_[w]||w),w}n(D,"lex");for(var v,A,T,q,F={},N,M,tt,z;;){if(A=l[l.length-1],this.defaultActions[A]?T=this.defaultActions[A]:((v===null||typeof v>"u")&&(v=D()),T=b[A]&&b[A][v]),typeof T>"u"||!T.length||!T[0]){var H="";z=[];for(N in b[A])this.terminals_[N]&&N>dt&&z.push("'"+this.terminals_[N]+"'");_.showPosition?H="Parse error on line "+(C+1)+`:
`+_.showPosition()+` `+_.showPosition()+`
Expecting `+z.join(", ")+", got '"+(this.terminals_[v]||v)+"'":H="Parse error on line "+(C+1)+": Unexpected "+(v==Q?"end of input":"'"+(this.terminals_[v]||v)+"'"),this.parseError(H,{text:_.match,token:this.terminals_[v]||v,line:_.yylineno,loc:Y,expected:z})}if(T[0]instanceof Array&&T.length>1)throw new Error("Parse Error: multiple actions possible at state: "+A+", token: "+v);switch(T[0]){case 1:l.push(v),p.push(_.yytext),o.push(_.yylloc),l.push(T[1]),v=null,K=_.yyleng,k=_.yytext,C=_.yylineno,Y=_.yylloc;break;case 2:if(M=this.productions_[T[1]][1],F.$=p[p.length-M],F._$={first_line:o[o.length-(M||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(M||1)].first_column,last_column:o[o.length-1].last_column},ft&&(F._$.range=[o[o.length-(M||1)].range[0],o[o.length-1].range[1]]),q=this.performAction.apply(F,[k,K,C,I.yy,T[1],p,o].concat(yt)),typeof q<"u")return q;M&&(l=l.slice(0,-1*M*2),p=p.slice(0,-1*M),o=o.slice(0,-1*M)),l.push(this.productions_[T[1]][0]),p.push(F.$),o.push(F._$),tt=b[l[l.length-2]][l[l.length-1]],l.push(tt);break;case 3:return!0}}return!0},"parse")},m=(function(){var h={EOF:1,parseError:n(function(a,l){if(this.yy.parser)this.yy.parser.parseError(a,l);else throw new Error(a)},"parseError"),setInput:n(function(i,a){return this.yy=a||this.yy||{},this._input=i,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:n(function(){var i=this._input[0];this.yytext+=i,this.yyleng++,this.offset++,this.match+=i,this.matched+=i;var a=i.match(/(?:\r\n?|\n).*/g);return a?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),i},"input"),unput:n(function(i){var a=i.length,l=i.split(/(?:\r\n?|\n)/g);this._input=i+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-a),this.offset-=a;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),l.length-1&&(this.yylineno-=l.length-1);var p=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:l?(l.length===d.length?this.yylloc.first_column:0)+d[d.length-l.length].length-l[0].length:this.yylloc.first_column-a},this.options.ranges&&(this.yylloc.range=[p[0],p[0]+this.yyleng-a]),this.yyleng=this.yytext.length,this},"unput"),more:n(function(){return this._more=!0,this},"more"),reject:n(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). Expecting `+z.join(", ")+", got '"+(this.terminals_[v]||v)+"'":H="Parse error on line "+(C+1)+": Unexpected "+(v==Q?"end of input":"'"+(this.terminals_[v]||v)+"'"),this.parseError(H,{text:_.match,token:this.terminals_[v]||v,line:_.yylineno,loc:Y,expected:z})}if(T[0]instanceof Array&&T.length>1)throw new Error("Parse Error: multiple actions possible at state: "+A+", token: "+v);switch(T[0]){case 1:l.push(v),p.push(_.yytext),o.push(_.yylloc),l.push(T[1]),v=null,K=_.yyleng,k=_.yytext,C=_.yylineno,Y=_.yylloc;break;case 2:if(M=this.productions_[T[1]][1],F.$=p[p.length-M],F._$={first_line:o[o.length-(M||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-(M||1)].first_column,last_column:o[o.length-1].last_column},ft&&(F._$.range=[o[o.length-(M||1)].range[0],o[o.length-1].range[1]]),q=this.performAction.apply(F,[k,K,C,I.yy,T[1],p,o].concat(yt)),typeof q<"u")return q;M&&(l=l.slice(0,-1*M*2),p=p.slice(0,-1*M),o=o.slice(0,-1*M)),l.push(this.productions_[T[1]][0]),p.push(F.$),o.push(F._$),tt=b[l[l.length-2]][l[l.length-1]],l.push(tt);break;case 3:return!0}}return!0},"parse")},m=(function(){var h={EOF:1,parseError:n(function(a,l){if(this.yy.parser)this.yy.parser.parseError(a,l);else throw new Error(a)},"parseError"),setInput:n(function(i,a){return this.yy=a||this.yy||{},this._input=i,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:n(function(){var i=this._input[0];this.yytext+=i,this.yyleng++,this.offset++,this.match+=i,this.matched+=i;var a=i.match(/(?:\r\n?|\n).*/g);return a?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),i},"input"),unput:n(function(i){var a=i.length,l=i.split(/(?:\r\n?|\n)/g);this._input=i+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-a),this.offset-=a;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),l.length-1&&(this.yylineno-=l.length-1);var p=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:l?(l.length===d.length?this.yylloc.first_column:0)+d[d.length-l.length].length-l[0].length:this.yylloc.first_column-a},this.options.ranges&&(this.yylloc.range=[p[0],p[0]+this.yyleng-a]),this.yyleng=this.yytext.length,this},"unput"),more:n(function(){return this._more=!0,this},"more"),reject:n(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:n(function(i){this.unput(this.match.slice(i))},"less"),pastInput:n(function(){var i=this.matched.substr(0,this.matched.length-this.match.length);return(i.length>20?"...":"")+i.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:n(function(){var i=this.match;return i.length<20&&(i+=this._input.substr(0,20-i.length)),(i.substr(0,20)+(i.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:n(function(){var i=this.pastInput(),a=new Array(i.length+1).join("-");return i+this.upcomingInput()+` `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:n(function(i){this.unput(this.match.slice(i))},"less"),pastInput:n(function(){var i=this.matched.substr(0,this.matched.length-this.match.length);return(i.length>20?"...":"")+i.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:n(function(){var i=this.match;return i.length<20&&(i+=this._input.substr(0,20-i.length)),(i.substr(0,20)+(i.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:n(function(){var i=this.pastInput(),a=new Array(i.length+1).join("-");return i+this.upcomingInput()+`
@@ -1,4 +1,4 @@
import{_ as o,l as te,c as U,H as fe,ah as ye,ai as be,aj as me,ac as Ee,E as K,i as F,t as _e,J as ke,ad as Se,ae as ce,af as le}from"./mermaid.core-v0oo9NRr.js";import{g as Ne}from"./chunk-FMBD7UC4-HblipWIM.js";import"./index-8cIrvc8q.js";var $=(function(){var e=o(function(O,i,n,r){for(n=n||{},r=O.length;r--;n[O[r]]=i);return n},"o"),u=[1,4],p=[1,13],s=[1,12],d=[1,15],_=[1,16],b=[1,20],l=[1,19],D=[6,7,8],I=[1,26],g=[1,24],w=[1,25],E=[6,7,11],G=[1,31],N=[6,7,11,24],V=[1,6,13,16,17,20,23],m=[1,35],A=[1,36],L=[1,6,7,11,13,16,17,20,23],H=[1,38],T={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,KANBAN:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14,shapeData:15,ICON:16,CLASS:17,nodeWithId:18,nodeWithoutId:19,NODE_DSTART:20,NODE_DESCR:21,NODE_DEND:22,NODE_ID:23,SHAPE_DATA:24,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"KANBAN",11:"EOF",13:"SPACELIST",16:"ICON",17:"CLASS",20:"NODE_DSTART",21:"NODE_DESCR",22:"NODE_DEND",23:"NODE_ID",24:"SHAPE_DATA"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,3],[12,2],[12,2],[12,2],[12,1],[12,2],[12,1],[12,1],[12,1],[12,1],[14,1],[14,1],[19,3],[18,1],[18,4],[15,2],[15,1]],performAction:o(function(i,n,r,a,h,t,M){var c=t.length-1;switch(h){case 6:case 7:return a;case 8:a.getLogger().trace("Stop NL ");break;case 9:a.getLogger().trace("Stop EOF ");break;case 11:a.getLogger().trace("Stop NL2 ");break;case 12:a.getLogger().trace("Stop EOF2 ");break;case 15:a.getLogger().info("Node: ",t[c-1].id),a.addNode(t[c-2].length,t[c-1].id,t[c-1].descr,t[c-1].type,t[c]);break;case 16:a.getLogger().info("Node: ",t[c].id),a.addNode(t[c-1].length,t[c].id,t[c].descr,t[c].type);break;case 17:a.getLogger().trace("Icon: ",t[c]),a.decorateNode({icon:t[c]});break;case 18:case 23:a.decorateNode({class:t[c]});break;case 19:a.getLogger().trace("SPACELIST");break;case 20:a.getLogger().trace("Node: ",t[c-1].id),a.addNode(0,t[c-1].id,t[c-1].descr,t[c-1].type,t[c]);break;case 21:a.getLogger().trace("Node: ",t[c].id),a.addNode(0,t[c].id,t[c].descr,t[c].type);break;case 22:a.decorateNode({icon:t[c]});break;case 27:a.getLogger().trace("node found ..",t[c-2]),this.$={id:t[c-1],descr:t[c-1],type:a.getType(t[c-2],t[c])};break;case 28:this.$={id:t[c],descr:t[c],type:0};break;case 29:a.getLogger().trace("node found ..",t[c-3]),this.$={id:t[c-3],descr:t[c-1],type:a.getType(t[c-2],t[c])};break;case 30:this.$=t[c-1]+t[c];break;case 31:this.$=t[c];break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:u},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:u},{6:p,7:[1,10],9:9,12:11,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},e(D,[2,3]),{1:[2,2]},e(D,[2,4]),e(D,[2,5]),{1:[2,6],6:p,12:21,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},{6:p,9:22,12:11,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},{6:I,7:g,10:23,11:w},e(E,[2,24],{18:17,19:18,14:27,16:[1,28],17:[1,29],20:b,23:l}),e(E,[2,19]),e(E,[2,21],{15:30,24:G}),e(E,[2,22]),e(E,[2,23]),e(N,[2,25]),e(N,[2,26]),e(N,[2,28],{20:[1,32]}),{21:[1,33]},{6:I,7:g,10:34,11:w},{1:[2,7],6:p,12:21,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},e(V,[2,14],{7:m,11:A}),e(L,[2,8]),e(L,[2,9]),e(L,[2,10]),e(E,[2,16],{15:37,24:G}),e(E,[2,17]),e(E,[2,18]),e(E,[2,20],{24:H}),e(N,[2,31]),{21:[1,39]},{22:[1,40]},e(V,[2,13],{7:m,11:A}),e(L,[2,11]),e(L,[2,12]),e(E,[2,15],{24:H}),e(N,[2,30]),{22:[1,41]},e(N,[2,27]),e(N,[2,29])],defaultActions:{2:[2,1],6:[2,2]},parseError:o(function(i,n){if(n.recoverable)this.trace(i);else{var r=new Error(i);throw r.hash=n,r}},"parseError"),parse:o(function(i){var n=this,r=[0],a=[],h=[null],t=[],M=this.table,c="",W=0,se=0,ue=2,re=1,ge=t.slice.call(arguments,1),y=Object.create(this.lexer),R={yy:{}};for(var J in this.yy)Object.prototype.hasOwnProperty.call(this.yy,J)&&(R.yy[J]=this.yy[J]);y.setInput(i,R.yy),R.yy.lexer=y,R.yy.parser=this,typeof y.yylloc>"u"&&(y.yylloc={});var q=y.yylloc;t.push(q);var de=y.options&&y.options.ranges;typeof R.yy.parseError=="function"?this.parseError=R.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function pe(S){r.length=r.length-2*S,h.length=h.length-S,t.length=t.length-S}o(pe,"popStack");function ae(){var S;return S=a.pop()||y.lex()||re,typeof S!="number"&&(S instanceof Array&&(a=S,S=a.pop()),S=n.symbols_[S]||S),S}o(ae,"lex");for(var k,P,x,Q,j={},z,C,oe,X;;){if(P=r[r.length-1],this.defaultActions[P]?x=this.defaultActions[P]:((k===null||typeof k>"u")&&(k=ae()),x=M[P]&&M[P][k]),typeof x>"u"||!x.length||!x[0]){var Z="";X=[];for(z in M[P])this.terminals_[z]&&z>ue&&X.push("'"+this.terminals_[z]+"'");y.showPosition?Z="Parse error on line "+(W+1)+`: import{_ as o,l as te,c as U,H as fe,ah as ye,ai as be,aj as me,ac as Ee,E as K,i as F,t as _e,J as ke,ad as Se,ae as ce,af as le}from"./mermaid.core-CFUktQ8s.js";import{g as Ne}from"./chunk-FMBD7UC4-BALYpKmy.js";import"./index-DNCGxUDM.js";var $=(function(){var e=o(function(O,i,n,r){for(n=n||{},r=O.length;r--;n[O[r]]=i);return n},"o"),u=[1,4],p=[1,13],s=[1,12],d=[1,15],_=[1,16],b=[1,20],l=[1,19],D=[6,7,8],I=[1,26],g=[1,24],w=[1,25],E=[6,7,11],G=[1,31],N=[6,7,11,24],V=[1,6,13,16,17,20,23],m=[1,35],A=[1,36],L=[1,6,7,11,13,16,17,20,23],H=[1,38],T={trace:o(function(){},"trace"),yy:{},symbols_:{error:2,start:3,mindMap:4,spaceLines:5,SPACELINE:6,NL:7,KANBAN:8,document:9,stop:10,EOF:11,statement:12,SPACELIST:13,node:14,shapeData:15,ICON:16,CLASS:17,nodeWithId:18,nodeWithoutId:19,NODE_DSTART:20,NODE_DESCR:21,NODE_DEND:22,NODE_ID:23,SHAPE_DATA:24,$accept:0,$end:1},terminals_:{2:"error",6:"SPACELINE",7:"NL",8:"KANBAN",11:"EOF",13:"SPACELIST",16:"ICON",17:"CLASS",20:"NODE_DSTART",21:"NODE_DESCR",22:"NODE_DEND",23:"NODE_ID",24:"SHAPE_DATA"},productions_:[0,[3,1],[3,2],[5,1],[5,2],[5,2],[4,2],[4,3],[10,1],[10,1],[10,1],[10,2],[10,2],[9,3],[9,2],[12,3],[12,2],[12,2],[12,2],[12,1],[12,2],[12,1],[12,1],[12,1],[12,1],[14,1],[14,1],[19,3],[18,1],[18,4],[15,2],[15,1]],performAction:o(function(i,n,r,a,h,t,M){var c=t.length-1;switch(h){case 6:case 7:return a;case 8:a.getLogger().trace("Stop NL ");break;case 9:a.getLogger().trace("Stop EOF ");break;case 11:a.getLogger().trace("Stop NL2 ");break;case 12:a.getLogger().trace("Stop EOF2 ");break;case 15:a.getLogger().info("Node: ",t[c-1].id),a.addNode(t[c-2].length,t[c-1].id,t[c-1].descr,t[c-1].type,t[c]);break;case 16:a.getLogger().info("Node: ",t[c].id),a.addNode(t[c-1].length,t[c].id,t[c].descr,t[c].type);break;case 17:a.getLogger().trace("Icon: ",t[c]),a.decorateNode({icon:t[c]});break;case 18:case 23:a.decorateNode({class:t[c]});break;case 19:a.getLogger().trace("SPACELIST");break;case 20:a.getLogger().trace("Node: ",t[c-1].id),a.addNode(0,t[c-1].id,t[c-1].descr,t[c-1].type,t[c]);break;case 21:a.getLogger().trace("Node: ",t[c].id),a.addNode(0,t[c].id,t[c].descr,t[c].type);break;case 22:a.decorateNode({icon:t[c]});break;case 27:a.getLogger().trace("node found ..",t[c-2]),this.$={id:t[c-1],descr:t[c-1],type:a.getType(t[c-2],t[c])};break;case 28:this.$={id:t[c],descr:t[c],type:0};break;case 29:a.getLogger().trace("node found ..",t[c-3]),this.$={id:t[c-3],descr:t[c-1],type:a.getType(t[c-2],t[c])};break;case 30:this.$=t[c-1]+t[c];break;case 31:this.$=t[c];break}},"anonymous"),table:[{3:1,4:2,5:3,6:[1,5],8:u},{1:[3]},{1:[2,1]},{4:6,6:[1,7],7:[1,8],8:u},{6:p,7:[1,10],9:9,12:11,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},e(D,[2,3]),{1:[2,2]},e(D,[2,4]),e(D,[2,5]),{1:[2,6],6:p,12:21,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},{6:p,9:22,12:11,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},{6:I,7:g,10:23,11:w},e(E,[2,24],{18:17,19:18,14:27,16:[1,28],17:[1,29],20:b,23:l}),e(E,[2,19]),e(E,[2,21],{15:30,24:G}),e(E,[2,22]),e(E,[2,23]),e(N,[2,25]),e(N,[2,26]),e(N,[2,28],{20:[1,32]}),{21:[1,33]},{6:I,7:g,10:34,11:w},{1:[2,7],6:p,12:21,13:s,14:14,16:d,17:_,18:17,19:18,20:b,23:l},e(V,[2,14],{7:m,11:A}),e(L,[2,8]),e(L,[2,9]),e(L,[2,10]),e(E,[2,16],{15:37,24:G}),e(E,[2,17]),e(E,[2,18]),e(E,[2,20],{24:H}),e(N,[2,31]),{21:[1,39]},{22:[1,40]},e(V,[2,13],{7:m,11:A}),e(L,[2,11]),e(L,[2,12]),e(E,[2,15],{24:H}),e(N,[2,30]),{22:[1,41]},e(N,[2,27]),e(N,[2,29])],defaultActions:{2:[2,1],6:[2,2]},parseError:o(function(i,n){if(n.recoverable)this.trace(i);else{var r=new Error(i);throw r.hash=n,r}},"parseError"),parse:o(function(i){var n=this,r=[0],a=[],h=[null],t=[],M=this.table,c="",W=0,se=0,ue=2,re=1,ge=t.slice.call(arguments,1),y=Object.create(this.lexer),R={yy:{}};for(var J in this.yy)Object.prototype.hasOwnProperty.call(this.yy,J)&&(R.yy[J]=this.yy[J]);y.setInput(i,R.yy),R.yy.lexer=y,R.yy.parser=this,typeof y.yylloc>"u"&&(y.yylloc={});var q=y.yylloc;t.push(q);var de=y.options&&y.options.ranges;typeof R.yy.parseError=="function"?this.parseError=R.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function pe(S){r.length=r.length-2*S,h.length=h.length-S,t.length=t.length-S}o(pe,"popStack");function ae(){var S;return S=a.pop()||y.lex()||re,typeof S!="number"&&(S instanceof Array&&(a=S,S=a.pop()),S=n.symbols_[S]||S),S}o(ae,"lex");for(var k,P,x,Q,j={},z,C,oe,X;;){if(P=r[r.length-1],this.defaultActions[P]?x=this.defaultActions[P]:((k===null||typeof k>"u")&&(k=ae()),x=M[P]&&M[P][k]),typeof x>"u"||!x.length||!x[0]){var Z="";X=[];for(z in M[P])this.terminals_[z]&&z>ue&&X.push("'"+this.terminals_[z]+"'");y.showPosition?Z="Parse error on line "+(W+1)+`:
`+y.showPosition()+` `+y.showPosition()+`
Expecting `+X.join(", ")+", got '"+(this.terminals_[k]||k)+"'":Z="Parse error on line "+(W+1)+": Unexpected "+(k==re?"end of input":"'"+(this.terminals_[k]||k)+"'"),this.parseError(Z,{text:y.match,token:this.terminals_[k]||k,line:y.yylineno,loc:q,expected:X})}if(x[0]instanceof Array&&x.length>1)throw new Error("Parse Error: multiple actions possible at state: "+P+", token: "+k);switch(x[0]){case 1:r.push(k),h.push(y.yytext),t.push(y.yylloc),r.push(x[1]),k=null,se=y.yyleng,c=y.yytext,W=y.yylineno,q=y.yylloc;break;case 2:if(C=this.productions_[x[1]][1],j.$=h[h.length-C],j._$={first_line:t[t.length-(C||1)].first_line,last_line:t[t.length-1].last_line,first_column:t[t.length-(C||1)].first_column,last_column:t[t.length-1].last_column},de&&(j._$.range=[t[t.length-(C||1)].range[0],t[t.length-1].range[1]]),Q=this.performAction.apply(j,[c,se,W,R.yy,x[1],h,t].concat(ge)),typeof Q<"u")return Q;C&&(r=r.slice(0,-1*C*2),h=h.slice(0,-1*C),t=t.slice(0,-1*C)),r.push(this.productions_[x[1]][0]),h.push(j.$),t.push(j._$),oe=M[r[r.length-2]][r[r.length-1]],r.push(oe);break;case 3:return!0}}return!0},"parse")},Y=(function(){var O={EOF:1,parseError:o(function(n,r){if(this.yy.parser)this.yy.parser.parseError(n,r);else throw new Error(n)},"parseError"),setInput:o(function(i,n){return this.yy=n||this.yy||{},this._input=i,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var i=this._input[0];this.yytext+=i,this.yyleng++,this.offset++,this.match+=i,this.matched+=i;var n=i.match(/(?:\r\n?|\n).*/g);return n?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),i},"input"),unput:o(function(i){var n=i.length,r=i.split(/(?:\r\n?|\n)/g);this._input=i+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-n),this.offset-=n;var a=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),r.length-1&&(this.yylineno-=r.length-1);var h=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:r?(r.length===a.length?this.yylloc.first_column:0)+a[a.length-r.length].length-r[0].length:this.yylloc.first_column-n},this.options.ranges&&(this.yylloc.range=[h[0],h[0]+this.yyleng-n]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). Expecting `+X.join(", ")+", got '"+(this.terminals_[k]||k)+"'":Z="Parse error on line "+(W+1)+": Unexpected "+(k==re?"end of input":"'"+(this.terminals_[k]||k)+"'"),this.parseError(Z,{text:y.match,token:this.terminals_[k]||k,line:y.yylineno,loc:q,expected:X})}if(x[0]instanceof Array&&x.length>1)throw new Error("Parse Error: multiple actions possible at state: "+P+", token: "+k);switch(x[0]){case 1:r.push(k),h.push(y.yytext),t.push(y.yylloc),r.push(x[1]),k=null,se=y.yyleng,c=y.yytext,W=y.yylineno,q=y.yylloc;break;case 2:if(C=this.productions_[x[1]][1],j.$=h[h.length-C],j._$={first_line:t[t.length-(C||1)].first_line,last_line:t[t.length-1].last_line,first_column:t[t.length-(C||1)].first_column,last_column:t[t.length-1].last_column},de&&(j._$.range=[t[t.length-(C||1)].range[0],t[t.length-1].range[1]]),Q=this.performAction.apply(j,[c,se,W,R.yy,x[1],h,t].concat(ge)),typeof Q<"u")return Q;C&&(r=r.slice(0,-1*C*2),h=h.slice(0,-1*C),t=t.slice(0,-1*C)),r.push(this.productions_[x[1]][0]),h.push(j.$),t.push(j._$),oe=M[r[r.length-2]][r[r.length-1]],r.push(oe);break;case 3:return!0}}return!0},"parse")},Y=(function(){var O={EOF:1,parseError:o(function(n,r){if(this.yy.parser)this.yy.parser.parseError(n,r);else throw new Error(n)},"parseError"),setInput:o(function(i,n){return this.yy=n||this.yy||{},this._input=i,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:o(function(){var i=this._input[0];this.yytext+=i,this.yyleng++,this.offset++,this.match+=i,this.matched+=i;var n=i.match(/(?:\r\n?|\n).*/g);return n?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),i},"input"),unput:o(function(i){var n=i.length,r=i.split(/(?:\r\n?|\n)/g);this._input=i+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-n),this.offset-=n;var a=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),r.length-1&&(this.yylineno-=r.length-1);var h=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:r?(r.length===a.length?this.yylloc.first_column:0)+a[a.length-r.length].length-r[0].length:this.yylloc.first_column-n},this.options.ranges&&(this.yylloc.range=[h[0],h[0]+this.yyleng-n]),this.yyleng=this.yytext.length,this},"unput"),more:o(function(){return this._more=!0,this},"more"),reject:o(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(i){this.unput(this.match.slice(i))},"less"),pastInput:o(function(){var i=this.matched.substr(0,this.matched.length-this.match.length);return(i.length>20?"...":"")+i.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var i=this.match;return i.length<20&&(i+=this._input.substr(0,20-i.length)),(i.substr(0,20)+(i.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var i=this.pastInput(),n=new Array(i.length+1).join("-");return i+this.upcomingInput()+` `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:o(function(i){this.unput(this.match.slice(i))},"less"),pastInput:o(function(){var i=this.matched.substr(0,this.matched.length-this.match.length);return(i.length>20?"...":"")+i.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:o(function(){var i=this.match;return i.length<20&&(i+=this._input.substr(0,20-i.length)),(i.substr(0,20)+(i.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:o(function(){var i=this.pastInput(),n=new Array(i.length+1).join("-");return i+this.upcomingInput()+`
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{R as S,V as z,aG as j,g as q,s as H,a as Z,b as J,q as K,p as Q,_ as p,l as F,c as X,D as Y,H as ee,a4 as te,e as ae,y as re,E as ne}from"./mermaid.core-v0oo9NRr.js";import{p as ie}from"./chunk-4BX2VUAB-DWDvTYfd.js";import{p as se}from"./treemap-GDKQZRPO-DJjQsbt8.js";import{d as I}from"./arc-UjuE1bPP.js";import{o as le}from"./ordinal-Cboi1Yqb.js";import"./index-8cIrvc8q.js";import"./_baseUniq-DAOs4kUj.js";import"./_basePickBy-CL4iQUG-.js";import"./clone-C1u3K6Fy.js";import"./init-Gi6I4Gst.js";function oe(e,a){return a<e?-1:a>e?1:a>=e?0:NaN}function ce(e){return e}function ue(){var e=ce,a=oe,f=null,y=S(0),s=S(z),o=S(0);function l(t){var n,c=(t=j(t)).length,d,x,h=0,u=new Array(c),i=new Array(c),v=+y.apply(this,arguments),w=Math.min(z,Math.max(-z,s.apply(this,arguments)-v)),m,C=Math.min(Math.abs(w)/c,o.apply(this,arguments)),$=C*(w<0?-1:1),g;for(n=0;n<c;++n)(g=i[u[n]=n]=+e(t[n],n,t))>0&&(h+=g);for(a!=null?u.sort(function(A,D){return a(i[A],i[D])}):f!=null&&u.sort(function(A,D){return f(t[A],t[D])}),n=0,x=h?(w-c*$)/h:0;n<c;++n,v=m)d=u[n],g=i[d],m=v+(g>0?g*x:0)+$,i[d]={data:t[d],index:n,value:g,startAngle:v,endAngle:m,padAngle:C};return i}return l.value=function(t){return arguments.length?(e=typeof t=="function"?t:S(+t),l):e},l.sortValues=function(t){return arguments.length?(a=t,f=null,l):a},l.sort=function(t){return arguments.length?(f=t,a=null,l):f},l.startAngle=function(t){return arguments.length?(y=typeof t=="function"?t:S(+t),l):y},l.endAngle=function(t){return arguments.length?(s=typeof t=="function"?t:S(+t),l):s},l.padAngle=function(t){return arguments.length?(o=typeof t=="function"?t:S(+t),l):o},l}var pe=ne.pie,G={sections:new Map,showData:!1},T=G.sections,N=G.showData,de=structuredClone(pe),ge=p(()=>structuredClone(de),"getConfig"),fe=p(()=>{T=new Map,N=G.showData,re()},"clear"),me=p(({label:e,value:a})=>{if(a<0)throw new Error(`"${e}" has invalid value: ${a}. Negative values are not allowed in pie charts. All slice values must be >= 0.`);T.has(e)||(T.set(e,a),F.debug(`added new section: ${e}, with value: ${a}`))},"addSection"),he=p(()=>T,"getSections"),ve=p(e=>{N=e},"setShowData"),Se=p(()=>N,"getShowData"),L={getConfig:ge,clear:fe,setDiagramTitle:Q,getDiagramTitle:K,setAccTitle:J,getAccTitle:Z,setAccDescription:H,getAccDescription:q,addSection:me,getSections:he,setShowData:ve,getShowData:Se},ye=p((e,a)=>{ie(e,a),a.setShowData(e.showData),e.sections.map(a.addSection)},"populateDb"),xe={parse:p(async e=>{const a=await se("pie",e);F.debug(a),ye(a,L)},"parse")},we=p(e=>` import{R as S,V as z,aG as j,g as q,s as H,a as Z,b as J,q as K,p as Q,_ as p,l as F,c as X,D as Y,H as ee,a4 as te,e as ae,y as re,E as ne}from"./mermaid.core-CFUktQ8s.js";import{p as ie}from"./chunk-4BX2VUAB-DHPPu6Xd.js";import{p as se}from"./treemap-GDKQZRPO-DWvWdchV.js";import{d as I}from"./arc-P9DEh39j.js";import{o as le}from"./ordinal-Cboi1Yqb.js";import"./index-DNCGxUDM.js";import"./_baseUniq-T1y-Xwdr.js";import"./_basePickBy-8KTOl_Ov.js";import"./clone-4l7SFgdX.js";import"./init-Gi6I4Gst.js";function oe(e,a){return a<e?-1:a>e?1:a>=e?0:NaN}function ce(e){return e}function ue(){var e=ce,a=oe,f=null,y=S(0),s=S(z),o=S(0);function l(t){var n,c=(t=j(t)).length,d,x,h=0,u=new Array(c),i=new Array(c),v=+y.apply(this,arguments),w=Math.min(z,Math.max(-z,s.apply(this,arguments)-v)),m,C=Math.min(Math.abs(w)/c,o.apply(this,arguments)),$=C*(w<0?-1:1),g;for(n=0;n<c;++n)(g=i[u[n]=n]=+e(t[n],n,t))>0&&(h+=g);for(a!=null?u.sort(function(A,D){return a(i[A],i[D])}):f!=null&&u.sort(function(A,D){return f(t[A],t[D])}),n=0,x=h?(w-c*$)/h:0;n<c;++n,v=m)d=u[n],g=i[d],m=v+(g>0?g*x:0)+$,i[d]={data:t[d],index:n,value:g,startAngle:v,endAngle:m,padAngle:C};return i}return l.value=function(t){return arguments.length?(e=typeof t=="function"?t:S(+t),l):e},l.sortValues=function(t){return arguments.length?(a=t,f=null,l):a},l.sort=function(t){return arguments.length?(f=t,a=null,l):f},l.startAngle=function(t){return arguments.length?(y=typeof t=="function"?t:S(+t),l):y},l.endAngle=function(t){return arguments.length?(s=typeof t=="function"?t:S(+t),l):s},l.padAngle=function(t){return arguments.length?(o=typeof t=="function"?t:S(+t),l):o},l}var pe=ne.pie,G={sections:new Map,showData:!1},T=G.sections,N=G.showData,de=structuredClone(pe),ge=p(()=>structuredClone(de),"getConfig"),fe=p(()=>{T=new Map,N=G.showData,re()},"clear"),me=p(({label:e,value:a})=>{if(a<0)throw new Error(`"${e}" has invalid value: ${a}. Negative values are not allowed in pie charts. All slice values must be >= 0.`);T.has(e)||(T.set(e,a),F.debug(`added new section: ${e}, with value: ${a}`))},"addSection"),he=p(()=>T,"getSections"),ve=p(e=>{N=e},"setShowData"),Se=p(()=>N,"getShowData"),L={getConfig:ge,clear:fe,setDiagramTitle:Q,getDiagramTitle:K,setAccTitle:J,getAccTitle:Z,setAccDescription:H,getAccDescription:q,addSection:me,getSections:he,setShowData:ve,getShowData:Se},ye=p((e,a)=>{ie(e,a),a.setShowData(e.showData),e.sections.map(a.addSection)},"populateDb"),xe={parse:p(async e=>{const a=await se("pie",e);F.debug(a),ye(a,L)},"parse")},we=p(e=>`
.pieCircle{ .pieCircle{
stroke: ${e.pieStrokeColor}; stroke: ${e.pieStrokeColor};
stroke-width : ${e.pieStrokeWidth}; stroke-width : ${e.pieStrokeWidth};
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/SongGrid-C568K-c_.js","assets/SongGrid.vue_vue_type_script_setup_true_lang-IvAOIQYW.js","assets/index-8cIrvc8q.js","assets/index-BJkaQ2c4.css","assets/useContentImages-7wLVntsF.js","assets/SongDetail-BiVK4Yzb.js","assets/SongDetail.vue_vue_type_script_setup_true_lang-B3om3Z8v.js"])))=>i.map(i=>d[i]);
import{d as e,_ as r}from"./index-8cIrvc8q.js";const o={id:"song",name:"Song Renderer",contentType:"song",surfaces:["chat-preview","panel-preview","panel-play"],chatPreview:e(()=>r(()=>import("./SongGrid-C568K-c_.js"),__vite__mapDeps([0,1,2,3,4]))),panelPreview:e(()=>r(()=>import("./SongGrid-C568K-c_.js"),__vite__mapDeps([0,1,2,3,4]))),panelPlay:e(()=>r(()=>import("./SongDetail-BiVK4Yzb.js"),__vite__mapDeps([5,6,2,3])))};export{o as songRenderer};
@@ -0,0 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/SongGrid-BdJzZ_PP.js","assets/SongGrid.vue_vue_type_script_setup_true_lang-70SttNTZ.js","assets/index-DNCGxUDM.js","assets/index-BJh-vGUe.css","assets/useContentImages-DdjyABL9.js","assets/SongDetail-Cdt_oumO.js","assets/SongDetail.vue_vue_type_script_setup_true_lang-0mQhUBE8.js"])))=>i.map(i=>d[i]);
import{d as e,_ as r}from"./index-DNCGxUDM.js";const o={id:"song",name:"Song Renderer",contentType:"song",surfaces:["chat-preview","panel-preview","panel-play"],chatPreview:e(()=>r(()=>import("./SongGrid-BdJzZ_PP.js"),__vite__mapDeps([0,1,2,3,4]))),panelPreview:e(()=>r(()=>import("./SongGrid-BdJzZ_PP.js"),__vite__mapDeps([0,1,2,3,4]))),panelPlay:e(()=>r(()=>import("./SongDetail-Cdt_oumO.js"),__vite__mapDeps([5,6,2,3])))};export{o as songRenderer};
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
import{s as e,b as r,a,S as s}from"./chunk-DI55MBZ5-C35X8NbN.js";import{_ as i}from"./mermaid.core-CFUktQ8s.js";import"./chunk-55IACEB6-fbeP3Dtn.js";import"./chunk-QN33PNHL-B0_3_NGo.js";import"./index-DNCGxUDM.js";var p={parser:a,get db(){return new s(2)},renderer:r,styles:e,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{p as diagram};
@@ -1 +0,0 @@
import{s as e,b as r,a,S as s}from"./chunk-DI55MBZ5-BzH7fNN2.js";import{_ as i}from"./mermaid.core-v0oo9NRr.js";import"./chunk-55IACEB6-CtULfmDo.js";import"./chunk-QN33PNHL-DSThOC6-.js";import"./index-8cIrvc8q.js";var p={parser:a,get db(){return new s(2)},renderer:r,styles:e,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{p as diagram};
@@ -1,4 +1,4 @@
import{_ as s,c as xt,l as E,d as j,ac as kt,ad as vt,ae as _t,af as bt,B as wt,ag as St,y as Et}from"./mermaid.core-v0oo9NRr.js";import{d as nt}from"./arc-UjuE1bPP.js";import"./index-8cIrvc8q.js";var Q=(function(){var n=s(function(x,r,a,c){for(a=a||{},c=x.length;c--;a[x[c]]=r);return a},"o"),t=[6,8,10,11,12,14,16,17,20,21],e=[1,9],l=[1,10],i=[1,11],d=[1,12],h=[1,13],f=[1,16],m=[1,17],p={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,timeline:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,period_statement:18,event_statement:19,period:20,event:21,$accept:0,$end:1},terminals_:{2:"error",4:"timeline",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",20:"period",21:"event"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,1],[9,1],[18,1],[19,1]],performAction:s(function(r,a,c,u,y,o,w){var v=o.length-1;switch(y){case 1:return o[v-1];case 2:this.$=[];break;case 3:o[v-1].push(o[v]),this.$=o[v-1];break;case 4:case 5:this.$=o[v];break;case 6:case 7:this.$=[];break;case 8:u.getCommonDb().setDiagramTitle(o[v].substr(6)),this.$=o[v].substr(6);break;case 9:this.$=o[v].trim(),u.getCommonDb().setAccTitle(this.$);break;case 10:case 11:this.$=o[v].trim(),u.getCommonDb().setAccDescription(this.$);break;case 12:u.addSection(o[v].substr(8)),this.$=o[v].substr(8);break;case 15:u.addTask(o[v],0,""),this.$=o[v];break;case 16:u.addEvent(o[v].substr(2)),this.$=o[v];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},n(t,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:e,12:l,14:i,16:d,17:h,18:14,19:15,20:f,21:m},n(t,[2,7],{1:[2,1]}),n(t,[2,3]),{9:18,11:e,12:l,14:i,16:d,17:h,18:14,19:15,20:f,21:m},n(t,[2,5]),n(t,[2,6]),n(t,[2,8]),{13:[1,19]},{15:[1,20]},n(t,[2,11]),n(t,[2,12]),n(t,[2,13]),n(t,[2,14]),n(t,[2,15]),n(t,[2,16]),n(t,[2,4]),n(t,[2,9]),n(t,[2,10])],defaultActions:{},parseError:s(function(r,a){if(a.recoverable)this.trace(r);else{var c=new Error(r);throw c.hash=a,c}},"parseError"),parse:s(function(r){var a=this,c=[0],u=[],y=[null],o=[],w=this.table,v="",N=0,P=0,V=2,U=1,H=o.slice.call(arguments,1),g=Object.create(this.lexer),b={yy:{}};for(var L in this.yy)Object.prototype.hasOwnProperty.call(this.yy,L)&&(b.yy[L]=this.yy[L]);g.setInput(r,b.yy),b.yy.lexer=g,b.yy.parser=this,typeof g.yylloc>"u"&&(g.yylloc={});var M=g.yylloc;o.push(M);var W=g.options&&g.options.ranges;typeof b.yy.parseError=="function"?this.parseError=b.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Z(T){c.length=c.length-2*T,y.length=y.length-T,o.length=o.length-T}s(Z,"popStack");function tt(){var T;return T=u.pop()||g.lex()||U,typeof T!="number"&&(T instanceof Array&&(u=T,T=u.pop()),T=a.symbols_[T]||T),T}s(tt,"lex");for(var S,A,I,J,R={},B,$,et,O;;){if(A=c[c.length-1],this.defaultActions[A]?I=this.defaultActions[A]:((S===null||typeof S>"u")&&(S=tt()),I=w[A]&&w[A][S]),typeof I>"u"||!I.length||!I[0]){var K="";O=[];for(B in w[A])this.terminals_[B]&&B>V&&O.push("'"+this.terminals_[B]+"'");g.showPosition?K="Parse error on line "+(N+1)+`: import{_ as s,c as xt,l as E,d as j,ac as kt,ad as vt,ae as _t,af as bt,B as wt,ag as St,y as Et}from"./mermaid.core-CFUktQ8s.js";import{d as nt}from"./arc-P9DEh39j.js";import"./index-DNCGxUDM.js";var Q=(function(){var n=s(function(x,r,a,c){for(a=a||{},c=x.length;c--;a[x[c]]=r);return a},"o"),t=[6,8,10,11,12,14,16,17,20,21],e=[1,9],l=[1,10],i=[1,11],d=[1,12],h=[1,13],f=[1,16],m=[1,17],p={trace:s(function(){},"trace"),yy:{},symbols_:{error:2,start:3,timeline:4,document:5,EOF:6,line:7,SPACE:8,statement:9,NEWLINE:10,title:11,acc_title:12,acc_title_value:13,acc_descr:14,acc_descr_value:15,acc_descr_multiline_value:16,section:17,period_statement:18,event_statement:19,period:20,event:21,$accept:0,$end:1},terminals_:{2:"error",4:"timeline",6:"EOF",8:"SPACE",10:"NEWLINE",11:"title",12:"acc_title",13:"acc_title_value",14:"acc_descr",15:"acc_descr_value",16:"acc_descr_multiline_value",17:"section",20:"period",21:"event"},productions_:[0,[3,3],[5,0],[5,2],[7,2],[7,1],[7,1],[7,1],[9,1],[9,2],[9,2],[9,1],[9,1],[9,1],[9,1],[18,1],[19,1]],performAction:s(function(r,a,c,u,y,o,w){var v=o.length-1;switch(y){case 1:return o[v-1];case 2:this.$=[];break;case 3:o[v-1].push(o[v]),this.$=o[v-1];break;case 4:case 5:this.$=o[v];break;case 6:case 7:this.$=[];break;case 8:u.getCommonDb().setDiagramTitle(o[v].substr(6)),this.$=o[v].substr(6);break;case 9:this.$=o[v].trim(),u.getCommonDb().setAccTitle(this.$);break;case 10:case 11:this.$=o[v].trim(),u.getCommonDb().setAccDescription(this.$);break;case 12:u.addSection(o[v].substr(8)),this.$=o[v].substr(8);break;case 15:u.addTask(o[v],0,""),this.$=o[v];break;case 16:u.addEvent(o[v].substr(2)),this.$=o[v];break}},"anonymous"),table:[{3:1,4:[1,2]},{1:[3]},n(t,[2,2],{5:3}),{6:[1,4],7:5,8:[1,6],9:7,10:[1,8],11:e,12:l,14:i,16:d,17:h,18:14,19:15,20:f,21:m},n(t,[2,7],{1:[2,1]}),n(t,[2,3]),{9:18,11:e,12:l,14:i,16:d,17:h,18:14,19:15,20:f,21:m},n(t,[2,5]),n(t,[2,6]),n(t,[2,8]),{13:[1,19]},{15:[1,20]},n(t,[2,11]),n(t,[2,12]),n(t,[2,13]),n(t,[2,14]),n(t,[2,15]),n(t,[2,16]),n(t,[2,4]),n(t,[2,9]),n(t,[2,10])],defaultActions:{},parseError:s(function(r,a){if(a.recoverable)this.trace(r);else{var c=new Error(r);throw c.hash=a,c}},"parseError"),parse:s(function(r){var a=this,c=[0],u=[],y=[null],o=[],w=this.table,v="",N=0,P=0,V=2,U=1,H=o.slice.call(arguments,1),g=Object.create(this.lexer),b={yy:{}};for(var L in this.yy)Object.prototype.hasOwnProperty.call(this.yy,L)&&(b.yy[L]=this.yy[L]);g.setInput(r,b.yy),b.yy.lexer=g,b.yy.parser=this,typeof g.yylloc>"u"&&(g.yylloc={});var M=g.yylloc;o.push(M);var W=g.options&&g.options.ranges;typeof b.yy.parseError=="function"?this.parseError=b.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function Z(T){c.length=c.length-2*T,y.length=y.length-T,o.length=o.length-T}s(Z,"popStack");function tt(){var T;return T=u.pop()||g.lex()||U,typeof T!="number"&&(T instanceof Array&&(u=T,T=u.pop()),T=a.symbols_[T]||T),T}s(tt,"lex");for(var S,A,I,J,R={},B,$,et,O;;){if(A=c[c.length-1],this.defaultActions[A]?I=this.defaultActions[A]:((S===null||typeof S>"u")&&(S=tt()),I=w[A]&&w[A][S]),typeof I>"u"||!I.length||!I[0]){var K="";O=[];for(B in w[A])this.terminals_[B]&&B>V&&O.push("'"+this.terminals_[B]+"'");g.showPosition?K="Parse error on line "+(N+1)+`:
`+g.showPosition()+` `+g.showPosition()+`
Expecting `+O.join(", ")+", got '"+(this.terminals_[S]||S)+"'":K="Parse error on line "+(N+1)+": Unexpected "+(S==U?"end of input":"'"+(this.terminals_[S]||S)+"'"),this.parseError(K,{text:g.match,token:this.terminals_[S]||S,line:g.yylineno,loc:M,expected:O})}if(I[0]instanceof Array&&I.length>1)throw new Error("Parse Error: multiple actions possible at state: "+A+", token: "+S);switch(I[0]){case 1:c.push(S),y.push(g.yytext),o.push(g.yylloc),c.push(I[1]),S=null,P=g.yyleng,v=g.yytext,N=g.yylineno,M=g.yylloc;break;case 2:if($=this.productions_[I[1]][1],R.$=y[y.length-$],R._$={first_line:o[o.length-($||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-($||1)].first_column,last_column:o[o.length-1].last_column},W&&(R._$.range=[o[o.length-($||1)].range[0],o[o.length-1].range[1]]),J=this.performAction.apply(R,[v,P,N,b.yy,I[1],y,o].concat(H)),typeof J<"u")return J;$&&(c=c.slice(0,-1*$*2),y=y.slice(0,-1*$),o=o.slice(0,-1*$)),c.push(this.productions_[I[1]][0]),y.push(R.$),o.push(R._$),et=w[c[c.length-2]][c[c.length-1]],c.push(et);break;case 3:return!0}}return!0},"parse")},k=(function(){var x={EOF:1,parseError:s(function(a,c){if(this.yy.parser)this.yy.parser.parseError(a,c);else throw new Error(a)},"parseError"),setInput:s(function(r,a){return this.yy=a||this.yy||{},this._input=r,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var r=this._input[0];this.yytext+=r,this.yyleng++,this.offset++,this.match+=r,this.matched+=r;var a=r.match(/(?:\r\n?|\n).*/g);return a?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),r},"input"),unput:s(function(r){var a=r.length,c=r.split(/(?:\r\n?|\n)/g);this._input=r+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-a),this.offset-=a;var u=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var y=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===u.length?this.yylloc.first_column:0)+u[u.length-c.length].length-c[0].length:this.yylloc.first_column-a},this.options.ranges&&(this.yylloc.range=[y[0],y[0]+this.yyleng-a]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true). Expecting `+O.join(", ")+", got '"+(this.terminals_[S]||S)+"'":K="Parse error on line "+(N+1)+": Unexpected "+(S==U?"end of input":"'"+(this.terminals_[S]||S)+"'"),this.parseError(K,{text:g.match,token:this.terminals_[S]||S,line:g.yylineno,loc:M,expected:O})}if(I[0]instanceof Array&&I.length>1)throw new Error("Parse Error: multiple actions possible at state: "+A+", token: "+S);switch(I[0]){case 1:c.push(S),y.push(g.yytext),o.push(g.yylloc),c.push(I[1]),S=null,P=g.yyleng,v=g.yytext,N=g.yylineno,M=g.yylloc;break;case 2:if($=this.productions_[I[1]][1],R.$=y[y.length-$],R._$={first_line:o[o.length-($||1)].first_line,last_line:o[o.length-1].last_line,first_column:o[o.length-($||1)].first_column,last_column:o[o.length-1].last_column},W&&(R._$.range=[o[o.length-($||1)].range[0],o[o.length-1].range[1]]),J=this.performAction.apply(R,[v,P,N,b.yy,I[1],y,o].concat(H)),typeof J<"u")return J;$&&(c=c.slice(0,-1*$*2),y=y.slice(0,-1*$),o=o.slice(0,-1*$)),c.push(this.productions_[I[1]][0]),y.push(R.$),o.push(R._$),et=w[c[c.length-2]][c[c.length-1]],c.push(et);break;case 3:return!0}}return!0},"parse")},k=(function(){var x={EOF:1,parseError:s(function(a,c){if(this.yy.parser)this.yy.parser.parseError(a,c);else throw new Error(a)},"parseError"),setInput:s(function(r,a){return this.yy=a||this.yy||{},this._input=r,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:s(function(){var r=this._input[0];this.yytext+=r,this.yyleng++,this.offset++,this.match+=r,this.matched+=r;var a=r.match(/(?:\r\n?|\n).*/g);return a?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),r},"input"),unput:s(function(r){var a=r.length,c=r.split(/(?:\r\n?|\n)/g);this._input=r+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-a),this.offset-=a;var u=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),c.length-1&&(this.yylineno-=c.length-1);var y=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:c?(c.length===u.length?this.yylloc.first_column:0)+u[u.length-c.length].length-c[0].length:this.yylloc.first_column-a},this.options.ranges&&(this.yylloc.range=[y[0],y[0]+this.yyleng-a]),this.yyleng=this.yytext.length,this},"unput"),more:s(function(){return this._more=!0,this},"more"),reject:s(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
`+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(r){this.unput(this.match.slice(r))},"less"),pastInput:s(function(){var r=this.matched.substr(0,this.matched.length-this.match.length);return(r.length>20?"...":"")+r.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var r=this.match;return r.length<20&&(r+=this._input.substr(0,20-r.length)),(r.substr(0,20)+(r.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var r=this.pastInput(),a=new Array(r.length+1).join("-");return r+this.upcomingInput()+` `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:s(function(r){this.unput(this.match.slice(r))},"less"),pastInput:s(function(){var r=this.matched.substr(0,this.matched.length-this.match.length);return(r.length>20?"...":"")+r.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:s(function(){var r=this.match;return r.length<20&&(r+=this._input.substr(0,20-r.length)),(r.substr(0,20)+(r.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:s(function(){var r=this.pastInput(),a=new Array(r.length+1).join("-");return r+this.upcomingInput()+`
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as v,r as g,B as w,C as I}from"./index-8cIrvc8q.js";function k(t){const a=g(new Set),r=w(new Map);function i(e){a.value=new Set([...a.value,e])}function f(e){const n=t.id(e);return a.value.has(n)?null:t.existingUrl(e)||r.get(n)||null}function l(e){return t.fallback(e)}function d(e){i(t.id(e))}function o(e){const n=t.id(e);return!f(e)&&!a.value.has(n)}function h(e){for(const n of e){const c=t.id(n);t.existingUrl(n)||r.has(c)||a.value.has(c)||t.fetch(n).then(s=>{s?r.set(c,s):i(c)}).catch(()=>{i(c)})}}const u=t.items,m=I(u)?()=>u.value:u;return v(m,e=>h(e),{immediate:!0}),{coverSrc:f,fallbackSrc:l,onError:d,isLoading:o}}export{k as u}; import{A as v,r as g,B as w,C as I}from"./index-DNCGxUDM.js";function k(t){const a=g(new Set),r=w(new Map);function i(e){a.value=new Set([...a.value,e])}function f(e){const n=t.id(e);return a.value.has(n)?null:t.existingUrl(e)||r.get(n)||null}function l(e){return t.fallback(e)}function d(e){i(t.id(e))}function o(e){const n=t.id(e);return!f(e)&&!a.value.has(n)}function h(e){for(const n of e){const c=t.id(n);t.existingUrl(n)||r.has(c)||a.value.has(c)||t.fetch(n).then(s=>{s?r.set(c,s):i(c)}).catch(()=>{i(c)})}}const u=t.items,m=I(u)?()=>u.value:u;return v(m,e=>h(e),{immediate:!0}),{coverSrc:f,fallbackSrc:l,onError:d,isLoading:o}}export{k as u};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -22,8 +22,8 @@
<link rel="icon" href="/aiui/favicon.svg" type="image/svg+xml" /> <link rel="icon" href="/aiui/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/aiui/apple-touch-icon-180x180.png" /> <link rel="apple-touch-icon" href="/aiui/apple-touch-icon-180x180.png" />
<title>AIUI</title> <title>AIUI</title>
<script type="module" crossorigin src="/aiui/assets/index-8cIrvc8q.js"></script> <script type="module" crossorigin src="/aiui/assets/index-DNCGxUDM.js"></script>
<link rel="stylesheet" crossorigin href="/aiui/assets/index-BJkaQ2c4.css"> <link rel="stylesheet" crossorigin href="/aiui/assets/index-BJh-vGUe.css">
<link rel="manifest" href="/aiui/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/aiui/registerSW.js"></script></head> <link rel="manifest" href="/aiui/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/aiui/registerSW.js"></script></head>
<body class="antialiased h-full overflow-hidden fixed w-full"> <body class="antialiased h-full overflow-hidden fixed w-full">
<div id="app"></div> <div id="app"></div>
+1 -1
View File
File diff suppressed because one or more lines are too long
@@ -107,6 +107,24 @@ server {
proxy_send_timeout 120s; proxy_send_timeout 120s;
} }
# AIUI Routstr proxy — the explicit, user-selected Routstr provider in
# AIUI's model picker. Same daemon, same session-gate discipline as the
# Claude block above; the daemon additionally refuses paid requests
# unless the operator has armed a Routstr budget (D-05), so this is
# never an open relay even though completions cost sats.
location /aiui/api/routstr/ {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
proxy_read_timeout 300s;
proxy_send_timeout 120s;
}
# AIUI Ollama (local AI) proxy — same daemon, same session gate as above. # AIUI Ollama (local AI) proxy — same daemon, same session gate as above.
# The standalone AIUI OpenRouter relay that used to live here is deleted # The standalone AIUI OpenRouter relay that used to live here is deleted
# outright: the node holds no key for that backend, it is not in the # outright: the node holds no key for that backend, it is not in the
@@ -1041,6 +1059,20 @@ server {
proxy_read_timeout 300s; proxy_read_timeout 300s;
proxy_send_timeout 120s; proxy_send_timeout 120s;
} }
# Session-gated Routstr provider path — same rationale and shape as the
# HTTP server block above; both blocks must carry it (T-13-15).
location /aiui/api/routstr/ {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Cookie $http_cookie;
proxy_buffering off;
proxy_cache off;
proxy_connect_timeout 120s;
proxy_read_timeout 300s;
proxy_send_timeout 120s;
}
location /aiui/api/ollama/ { location /aiui/api/ollama/ {
proxy_pass http://127.0.0.1:5678; proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1; proxy_http_version 1.1;
@@ -21,11 +21,25 @@
<label class="text-white/60 text-sm block mb-1">{{ t('receiveBitcoin.memoOptional') }}</label> <label class="text-white/60 text-sm block mb-1">{{ t('receiveBitcoin.memoOptional') }}</label>
<input v-model="invoiceMemo" type="text" :placeholder="t('receiveBitcoin.memoPlaceholder')" class="w-full input-glass" /> <input v-model="invoiceMemo" type="text" :placeholder="t('receiveBitcoin.memoPlaceholder')" class="w-full input-glass" />
</div> </div>
<div v-if="invoiceResult" class="mb-3 p-3 bg-white/5 rounded-lg text-center"> <!-- Paid: the invoice did its job straight to the green check
(no broadcast step: Lightning settlement is final) -->
<div v-if="invoicePaid" class="mb-3 p-6 bg-white/5 rounded-lg text-center">
<div class="flex justify-center mb-4">
<div class="w-16 h-16 rounded-full flex items-center justify-center bg-green-500/15">
<svg class="w-8 h-8 text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
</svg>
</div>
</div>
<p class="text-lg font-semibold text-white mb-1">{{ t('receiveBitcoin.paymentConfirmed') }}</p>
<p v-if="invoicePaid.amountSats > 0" class="text-2xl font-semibold text-white/95 mb-2">
{{ invoicePaid.amountSats.toLocaleString() }} sats
</p>
</div>
<div v-else-if="invoiceResult" class="mb-3 p-3 bg-white/5 rounded-lg text-center">
<canvas ref="lightningQrCanvas" class="mx-auto mb-3 rounded-lg" style="image-rendering: pixelated;"></canvas> <canvas ref="lightningQrCanvas" class="mx-auto mb-3 rounded-lg" style="image-rendering: pixelated;"></canvas>
<p class="text-white/50 text-xs mb-1">{{ t('receiveBitcoin.invoiceShareLabel') }}</p> <p class="text-white/50 text-xs mb-2">{{ t('receiveBitcoin.invoiceShareLabel') }}</p>
<p class="text-xs font-mono text-white/80 break-all">{{ invoiceResult }}</p> <CopyButton :value="invoiceResult" :label="t('common.copy')" />
<CopyButton :value="invoiceResult" :label="t('common.copy')" class="mt-2" />
</div> </div>
</div> </div>
@@ -147,6 +161,9 @@ watch(() => props.show, (open) => {
return return
} }
paymentSeen.value = null paymentSeen.value = null
invoicePaid.value = null
invoiceRHash.value = ''
stopWatchingInvoice()
// Blank slate on every open: a leftover amount/memo/token or a previous // Blank slate on every open: a leftover amount/memo/token or a previous
// invoice quietly carrying into a new receive flow is exactly the stale- // invoice quietly carrying into a new receive flow is exactly the stale-
// state class the operator flagged on the send modal (2026-08-05). // state class the operator flagged on the send modal (2026-08-05).
@@ -238,6 +255,46 @@ async function checkForPayment() {
onUnmounted(stopWatchingPayment) onUnmounted(stopWatchingPayment)
// Lightning settlement watch the on-chain pattern minus the broadcast
// step: an invoice is either open or SETTLED (final), so the success view
// goes straight to the green check.
const invoicePaid = ref<null | { amountSats: number }>(null)
const invoiceRHash = ref('')
let invoiceTimer: ReturnType<typeof setInterval> | null = null
function stopWatchingInvoice() {
if (invoiceTimer) {
clearInterval(invoiceTimer)
invoiceTimer = null
}
}
function startWatchingInvoice() {
stopWatchingInvoice()
invoiceTimer = setInterval(() => void checkInvoice(), 3000)
}
async function checkInvoice() {
if (!props.show || !invoiceRHash.value) {
stopWatchingInvoice()
return
}
try {
const res = await rpcClient.call<{ settled: boolean; amt_paid_sat: number }>({
method: 'lnd.invoicestatus',
params: { r_hash_hex: invoiceRHash.value },
})
if (!res.settled) return
invoicePaid.value = { amountSats: res.amt_paid_sat || invoiceAmount.value }
stopWatchingInvoice()
emit('received')
} catch {
// Transient poll failure keep watching.
}
}
onUnmounted(stopWatchingInvoice)
async function renderQr(data: string, canvas: HTMLCanvasElement | null, prefix = '') { async function renderQr(data: string, canvas: HTMLCanvasElement | null, prefix = '') {
if (!canvas || !data) return if (!canvas || !data) return
try { try {
@@ -272,11 +329,14 @@ async function receive() {
// lnd.createinvoice fail with connection-refused (FED-08 follow-up). // lnd.createinvoice fail with connection-refused (FED-08 follow-up).
if (!(await lightning.requireLightningReady('receive'))) return if (!(await lightning.requireLightningReady('receive'))) return
if (!invoiceAmount.value) { error.value = t('receiveBitcoin.enterAnAmount'); return } if (!invoiceAmount.value) { error.value = t('receiveBitcoin.enterAnAmount'); return }
const res = await rpcClient.call<{ payment_request: string }>({ const res = await rpcClient.call<{ payment_request: string; r_hash_hex?: string }>({
method: 'lnd.createinvoice', method: 'lnd.createinvoice',
params: { amount_sats: invoiceAmount.value, memo: invoiceMemo.value || undefined }, params: { amount_sats: invoiceAmount.value, memo: invoiceMemo.value || undefined },
}) })
invoiceResult.value = res.payment_request invoiceResult.value = res.payment_request
invoiceRHash.value = res.r_hash_hex || ''
invoicePaid.value = null
if (invoiceRHash.value) startWatchingInvoice()
nextTick(() => renderQr(res.payment_request, lightningQrCanvas.value, 'lightning:')) nextTick(() => renderQr(res.payment_request, lightningQrCanvas.value, 'lightning:'))
} else if (receiveMethod.value === 'onchain') { } else if (receiveMethod.value === 'onchain') {
const res = await rpcClient.call<{ address: string }>({ method: 'lnd.newaddress' }) const res = await rpcClient.call<{ address: string }>({ method: 'lnd.newaddress' })
+1 -5
View File
@@ -227,7 +227,7 @@
<canvas ref="tokenQrCanvas" class="rounded-lg bg-white p-2"></canvas> <canvas ref="tokenQrCanvas" class="rounded-lg bg-white p-2"></canvas>
</div> </div>
<p class="text-xs font-mono text-white/80 break-all">{{ ecashToken }}</p> <p class="text-xs font-mono text-white/80 break-all">{{ ecashToken }}</p>
<button @click="copyText(ecashToken)" class="mt-2 text-xs text-orange-400 hover:text-orange-300">{{ t('common.copy') }}</button> <CopyButton :value="ecashToken" :label="t('common.copy')" size="sm" class="mt-2" />
</div> </div>
<div v-if="error" class="mb-3 alert-error">{{ error }}</div> <div v-if="error" class="mb-3 alert-error">{{ error }}</div>
@@ -604,10 +604,6 @@ function sendAnother() {
error.value = '' error.value = ''
} }
function copyText(text: string) {
navigator.clipboard.writeText(text).catch(() => {})
}
const tokenQrCanvas = ref<HTMLCanvasElement | null>(null) const tokenQrCanvas = ref<HTMLCanvasElement | null>(null)
watch(ecashToken, async (token) => { watch(ecashToken, async (token) => {
if (!token) return if (!token) return
@@ -202,11 +202,7 @@
</div> </div>
<div v-if="arkAddress" class="mt-2 flex items-center gap-2 p-3 bg-white/5 rounded-lg"> <div v-if="arkAddress" class="mt-2 flex items-center gap-2 p-3 bg-white/5 rounded-lg">
<span class="text-xs font-mono text-white/90 break-all flex-1">{{ arkAddress }}</span> <span class="text-xs font-mono text-white/90 break-all flex-1">{{ arkAddress }}</span>
<button @click="copyArkAddress" class="p-2 rounded-lg hover:bg-white/10 text-white/50 hover:text-white shrink-0" :title="arkCopied ? 'Copied' : 'Copy'"> <CopyButton :value="arkAddress" icon-only class="shrink-0" />
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button>
</div> </div>
</div> </div>
@@ -277,6 +273,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import CopyButton from '@/components/CopyButton.vue'
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { rpcClient } from '@/api/rpc-client' import { rpcClient } from '@/api/rpc-client'
@@ -349,7 +346,6 @@ const arkStatus = ref<ArkStatus | null>(null)
const arkBalance = ref<ArkBalance | null>(null) const arkBalance = ref<ArkBalance | null>(null)
const arkConfig = ref({ network: 'signet', ark_server: '', esplora: '' }) const arkConfig = ref({ network: 'signet', ark_server: '', esplora: '' })
const arkAddress = ref('') const arkAddress = ref('')
const arkCopied = ref(false)
const loadingArk = ref(false) const loadingArk = ref(false)
const savingArk = ref(false) const savingArk = ref(false)
const arkBoarding = ref(false) const arkBoarding = ref(false)
@@ -505,22 +501,11 @@ async function fetchArkAddress(onchain: boolean) {
params: { onchain }, params: { onchain },
}) })
arkAddress.value = res.address arkAddress.value = res.address
arkCopied.value = false
} catch (err: unknown) { } catch (err: unknown) {
arkError.value = err instanceof Error ? err.message : 'Failed to get address' arkError.value = err instanceof Error ? err.message : 'Failed to get address'
} }
} }
async function copyArkAddress() {
if (!arkAddress.value) return
try {
await navigator.clipboard.writeText(arkAddress.value)
arkCopied.value = true
} catch {
/* clipboard unavailable (http) — the address is selectable */
}
}
async function boardArk() { async function boardArk() {
arkBoarding.value = true arkBoarding.value = true
arkError.value = '' arkError.value = ''
@@ -702,11 +702,22 @@ function applyGraph() {
const reqs = [...(props.requests ?? [])].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)) const reqs = [...(props.requests ?? [])].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
const reqRing = requestRingRadius(peerNodes.length) const reqRing = requestRingRadius(peerNodes.length)
const seenReqs = new Set<string>() const seenReqs = new Set<string>()
// Requests are a call to action: cluster them tightly at one stage angle
// (instead of spread around the orbit, where they could sit BEHIND the
// globe reading as a mis-scaled chart the user had to rotate by hand)
// and steer the camera to face them when a new one arrives.
const REQUEST_STAGE_ANGLE = 0.35
const reqSpacing = Math.min(0.5, (Math.PI * 0.9) / Math.max(reqs.length, 1))
let newRequestArrived = false
reqs.forEach((req, i) => { reqs.forEach((req, i) => {
seenReqs.add(req.id) seenReqs.add(req.id)
const slot = { angle: (i / reqs.length) * Math.PI * 2 + 0.35, ringRadius: reqRing } const slot = {
angle: REQUEST_STAGE_ANGLE + (i - (reqs.length - 1) / 2) * reqSpacing,
ringRadius: reqRing,
}
const existing = reqMap.get(req.id) const existing = reqMap.get(req.id)
if (!existing) { if (!existing) {
newRequestArrived = true
const vis = createRequest(req, slot) const vis = createRequest(req, slot)
if (introPlayed) { if (introPlayed) {
if (staticMode) vis.p = 1 if (staticMode) vis.p = 1
@@ -738,6 +749,20 @@ function applyGraph() {
for (const [id, vis] of reqMap) { for (const [id, vis] of reqMap) {
if (!seenReqs.has(id)) killRequest(vis) if (!seenReqs.has(id)) killRequest(vis)
} }
// Face the request cluster when a new one arrives. Depth for a node at
// world angle a is proportional to sin(a rotY): front-center (nearest
// to the viewer, horizontally centred) is a rotY = π/2. Only on
// ARRIVAL, so a user who rotates away afterwards isn't fought.
if (newRequestArrived && introPlayed) {
const target = nearestAngle(cam.rotY, REQUEST_STAGE_ANGLE + Math.PI / 2)
for (const t of spinTween) t.kill()
if (staticMode && !tickerAttached) {
cam.rotY = target
render()
} else {
gsap.to(cam, { rotY: target, duration: 1.1, ease: motionTokens.ease.inOut, overwrite: 'auto' })
}
}
// --- orbit guide rings --- // --- orbit guide rings ---
const desiredRings: { radius: number; kind: 'peer' | 'request' }[] = [] const desiredRings: { radius: number; kind: 'peer' | 'request' }[] = []
@@ -26,7 +26,30 @@ async function computeFingerprint(pem: string): Promise<string> {
.join(':') .join(':')
} }
onMounted(async () => { const generating = ref(false)
const generateError = ref('')
// WebUI rule: never point a user at a terminal the backend runs the
// (idempotent) CA setup script for us.
async function generateCa() {
generating.value = true
generateError.value = ''
try {
const { rpcClient } = await import('@/api/rpc-client')
await rpcClient.call({ method: 'system.node-ca.generate', timeout: 60000 })
loading.value = true
await probe()
if (!caAvailable.value) {
generateError.value = 'Generation reported success but the certificate is not being served yet — try reloading in a few seconds.'
}
} catch (e: unknown) {
generateError.value = e instanceof Error ? e.message : 'Certificate generation failed'
} finally {
generating.value = false
}
}
async function probe() {
try { try {
const res = await fetch('/ca.crt', { cache: 'no-store' }) const res = await fetch('/ca.crt', { cache: 'no-store' })
if (!res.ok) throw new Error(`HTTP ${res.status}`) if (!res.ok) throw new Error(`HTTP ${res.status}`)
@@ -48,7 +71,9 @@ onMounted(async () => {
} finally { } finally {
loading.value = false loading.value = false
} }
}) }
onMounted(probe)
</script> </script>
<template> <template>
@@ -70,9 +95,13 @@ onMounted(async () => {
v-else-if="!caAvailable" v-else-if="!caAvailable"
class="p-3 bg-white/5 border border-white/10 rounded-lg text-sm text-white/70" class="p-3 bg-white/5 border border-white/10 rounded-lg text-sm text-white/70"
> >
This node has not generated a certificate authority yet. Run <p class="mb-3">This node has not generated its certificate yet.</p>
<code class="px-1 py-0.5 bg-black/30 rounded text-xs">scripts/setup-node-ca.sh</code> <button
on the node, then reload this page. :disabled="generating"
@click="generateCa"
class="px-4 py-2 glass-button rounded-lg text-sm font-semibold disabled:opacity-60"
>{{ generating ? 'Generating…' : 'Generate certificate' }}</button>
<p v-if="generateError" class="mt-2 text-xs text-orange-300/90">{{ generateError }}</p>
</div> </div>
<div v-else class="space-y-4"> <div v-else class="space-y-4">
@@ -0,0 +1,112 @@
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { rpcClient } from '@/api/rpc-client'
// Routstr prepaid budget (D-05): the assistant may only spend against an
// operator-set sats ceiling. Zero allowance = Routstr never selected. This
// panel is the ONLY UI for that ceiling; it fronts assistant.budget-get/set.
const loaded = ref(false)
const allowance = ref(0)
const spent = ref(0)
const remaining = ref(0)
const draft = ref('')
const applying = ref(false)
const error = ref('')
const savedTick = ref(false)
const active = computed(() => allowance.value > 0)
async function refresh() {
const res = await rpcClient.call<{ allowance_sats: number; spent_sats: number; remaining_sats: number }>({
method: 'assistant.budget-get',
})
allowance.value = res.allowance_sats
spent.value = res.spent_sats
remaining.value = res.remaining_sats
draft.value = String(res.allowance_sats)
}
onMounted(async () => {
try {
await refresh()
loaded.value = true
} catch { /* backend without the RPC — section hides */ }
})
async function apply() {
const parsed = Number(draft.value)
if (!Number.isInteger(parsed) || parsed < 0) {
error.value = 'Enter a whole number of sats (0 disables Routstr).'
return
}
applying.value = true
error.value = ''
try {
await rpcClient.call({ method: 'assistant.budget-set', params: { allowance_sats: parsed } })
await refresh()
savedTick.value = true
setTimeout(() => { savedTick.value = false }, 2500)
} catch (e: unknown) {
error.value = e instanceof Error ? e.message : 'Failed to save budget'
} finally {
applying.value = false
}
}
</script>
<template>
<div v-if="loaded" class="glass-card px-6 py-6 mb-6">
<div class="flex items-center gap-3 mb-2">
<h2 class="text-xl font-semibold text-white/96">Routstr AI Budget</h2>
<span
class="text-xs font-mono px-2 py-0.5 rounded"
:class="active ? 'bg-emerald-500/20 text-emerald-300' : 'bg-white/10 text-white/50'"
>{{ active ? 'enabled' : 'off' }}</span>
</div>
<p class="text-sm text-white/60 mb-5">
Routstr is pay-per-use AI inference, paid in sats over Cashu, used when your local
model can't take a request. It never spends without a prepaid ceiling you set here
at <span class="font-mono">0</span> it is completely disabled. The assistant stops
when the ceiling is reached; raising it widens the remainder without erasing the
spend history.
</p>
<div class="grid grid-cols-3 gap-4 mb-5 text-center">
<div class="rounded-lg bg-white/5 py-3">
<div class="text-lg font-mono text-white/90">{{ allowance.toLocaleString() }}</div>
<div class="text-xs text-white/50 mt-1">Allowance (sats)</div>
</div>
<div class="rounded-lg bg-white/5 py-3">
<div class="text-lg font-mono text-white/90">{{ spent.toLocaleString() }}</div>
<div class="text-xs text-white/50 mt-1">Spent</div>
</div>
<div class="rounded-lg bg-white/5 py-3">
<div class="text-lg font-mono" :class="remaining > 0 ? 'text-emerald-300' : 'text-white/60'">{{ remaining.toLocaleString() }}</div>
<div class="text-xs text-white/50 mt-1">Remaining</div>
</div>
</div>
<div class="flex items-end gap-3">
<div class="flex-1 max-w-xs">
<label class="block text-sm text-white/60 mb-1" for="routstr-allowance">Set allowance (sats)</label>
<input
id="routstr-allowance"
v-model="draft"
type="number"
min="0"
step="1"
inputmode="numeric"
class="w-full bg-white/5 border border-white/10 rounded-lg px-4 py-2.5 text-sm text-white font-mono placeholder-white/30 focus:outline-none focus:border-white/30 transition-colors"
@keydown.enter="apply"
/>
</div>
<button
:disabled="applying || draft === String(allowance)"
@click="apply"
class="px-4 py-2 glass-button rounded-lg text-sm font-medium disabled:opacity-50"
>{{ applying ? 'Saving…' : 'Save' }}</button>
<span v-if="savedTick" class="text-sm text-emerald-300 pb-2">Saved</span>
</div>
<div v-if="error" class="mt-3 alert-error text-sm">{{ error }}</div>
</div>
</template>
@@ -3,6 +3,7 @@ import InterfaceModeSection from '@/views/settings/InterfaceModeSection.vue'
import KioskDisplaySection from '@/views/settings/KioskDisplaySection.vue' import KioskDisplaySection from '@/views/settings/KioskDisplaySection.vue'
import ClaudeAuthSection from '@/views/settings/ClaudeAuthSection.vue' import ClaudeAuthSection from '@/views/settings/ClaudeAuthSection.vue'
import AIDataAccessSection from '@/views/settings/AIDataAccessSection.vue' import AIDataAccessSection from '@/views/settings/AIDataAccessSection.vue'
import RoutstrBudgetSection from '@/views/settings/RoutstrBudgetSection.vue'
import WebhookSection from '@/views/settings/WebhookSection.vue' import WebhookSection from '@/views/settings/WebhookSection.vue'
import TelemetrySection from '@/views/settings/TelemetrySection.vue' import TelemetrySection from '@/views/settings/TelemetrySection.vue'
import NodeCertificateSection from '@/views/settings/NodeCertificateSection.vue' import NodeCertificateSection from '@/views/settings/NodeCertificateSection.vue'
@@ -15,6 +16,7 @@ import SystemDangerZone from '@/views/settings/SystemDangerZone.vue'
<InterfaceModeSection /> <InterfaceModeSection />
<KioskDisplaySection /> <KioskDisplaySection />
<ClaudeAuthSection /> <ClaudeAuthSection />
<RoutstrBudgetSection />
<AIDataAccessSection /> <AIDataAccessSection />
<WebhookSection /> <WebhookSection />
<TelemetrySection /> <TelemetrySection />