Compare commits
32
Commits
+20
-1
@@ -1,7 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased
|
## v1.8.3-alpha (2026-08-14)
|
||||||
|
|
||||||
|
- **The network map on TVs: no more blank page, no more frozen page — and it moves again.** The map's entrance animation needed a smoothness that TV kiosk hardware can't always deliver, so the page could sit blank until a refresh; the previous fix cured the freeze by stopping the animation entirely, which went too far. Now the map appears instantly with everything already in place, then resumes its calm orbital motion at a gentler pace suited to TVs. Resizing or rotating any screen also redraws the map properly instead of leaving it tiny, stretched, or empty.
|
||||||
|
- **The dashboard's corner logo is back to normal.** The new glossy paint finish was meant for the big emblem on the screensaver, intro, and login screens — it had quietly spread to the small logo in the dashboard header, where it looked wrong. Each screen now gets exactly the treatment intended for it.
|
||||||
|
- **App icons no longer vanish in My Apps.** The freshly restyled Alby Hub and phoenixd icons could render as blank squares in some views — a subtlety in how the icon files declared their size. Fixed at the source, and the icon tool app developers use now produces immune files.
|
||||||
|
|
||||||
|
## v1.8.2-alpha (2026-08-13)
|
||||||
|
|
||||||
|
- **An app that can't be shown inside the dashboard now becomes a tab app by itself.** A few apps refuse to render inside another page no matter what — they break out with their own code or insist on owning the whole browser window. Opening one used to mean staring at a grey pane. Now the dashboard notices, offers the app in its own tab, and remembers: from then on that app's button opens a tab directly (with the little launch icon that tab apps carry), first click, every time. If a later update makes the app embeddable after all, the dashboard notices that too and goes back to embedding it.
|
||||||
|
- **The logo emblem got its glossy black paint finish — properly this time.** The circle behind the A on the screensaver, intro, and login now wears a deep wet-paint look: warm light blooming from the top edge, fine grain so the dark tones stay smooth instead of banding, and no more ring border. (An earlier rougher version of this experiment briefly shipped by accident and then vanished depending on which screen you were on — this is the finished, deliberate one, everywhere.)
|
||||||
|
- **New app icons now match the store's look, on every screen.** Alby Hub and phoenixd arrived with edge-to-edge logos that ignored the breathing room every other app icon has, and the app detail page skipped the icon backdrop entirely. Both icons are re-set on the standard canvas, the detail page now applies the same icon treatment as the store tiles, and app developers get a one-command tool that puts any logo onto the house canvas automatically.
|
||||||
|
|
||||||
|
## v1.8.1-alpha (2026-08-13)
|
||||||
|
|
||||||
|
- **Apps that refused to open inside the dashboard now embed like everything else.** Some apps ship browser headers that forbid being shown inside another page — correct hardening on the open web, but inside Archipelago it produced a dead grey pane when you opened them from My Apps (Alby Hub was the first to hit it). The app gate, which already checks your login on every request to an app, now removes just those framing headers on the way through; each app's own content-security rules pass through untouched. No more per-app proxy workarounds.
|
||||||
|
- **The network map no longer freezes kiosk TVs.** The animated federation map at 4K was too much for the deliberately conservative graphics settings the on-screen display used on every machine — settings chosen years back to stop audio crackle on much older hardware. Two fixes: on kiosk screens the map now opens in its flat 2D view (the 3D globe is one tap away, and remembered) and animates at half rate — invisible from the couch, half the work. And the display itself now recognizes what machine it runs on: older kiosk boxes keep the proven careful settings, modern ones finally get real GPU rendering.
|
||||||
|
- **New Settings → Display → Graphics choice for the on-screen display.** Auto (recommended) picks the right rendering mode for the machine by itself; Compatibility forces the most conservative mode if a screen ever stutters, tears, or crackles; Quality forces full GPU rendering on hardware the automatic detection doesn't recognize. Changing it restarts the on-screen display, like the size presets.
|
||||||
|
|
||||||
|
## v1.8.0-alpha (2026-08-12)
|
||||||
|
|
||||||
|
- **Archipelago is now open source.** The full source code of the node you are running — the orchestrator, the dashboard, the app platform, the mesh, the release tooling — is published for anyone to read, build and audit at source.archipelago-foundation.org/lfg2025/archy. A node that holds your money, your files and your communications should not ask to be taken on faith: from this release onward you, or anyone you trust, can see exactly what it does and follow every change we make in the open.
|
||||||
- **Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on "Install", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do ("download the update again"), and offers Download again instead of a dead "Install" button, rather than a generic "it failed".
|
- **Installing an update is reliable again, and tells you what happened when it isn't.** Some nodes could download an update but never apply it — the button stayed on "Install", and no amount of retrying worked. The cause: applying the update consumed the downloaded files as it went, so if any one step hit a snag partway through, the leftover files were incomplete and every later attempt failed the safety re-check forever, needing a technician to recover. Applying no longer consumes the download — a failed apply can always be retried from the same files — and the pieces are now applied in a fixed order with the program itself last, so a hiccup can't leave a half-swapped node. When an apply does fail, the screen now shows the real reason and what to do ("download the update again"), and offers Download again instead of a dead "Install" button, rather than a generic "it failed".
|
||||||
- **Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.
|
- **Video on the kiosk stops tearing.** The kiosk's display had no vertical sync at all, so fast motion — IndeedHub films especially — showed horizontal tearing lines. The display driver now syncs every frame to the panel (no extra hardware needed, existing kiosks pick it up with this update), and on machines with a GPU, video decoding moves off the CPU onto the video hardware — smoother playback that also leaves more headroom for audio, not less.
|
||||||
- **The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.
|
- **The Back button finally does what you expect.** Pressing Back — the mouse's side button on a kiosk, a swipe on a phone, the toolbar button in any browser — used to navigate the screen underneath an open window, or leave the dashboard entirely. Back now closes the topmost open window first, one per press, exactly like a native app; closing a window yourself never leaves a phantom entry that makes you press Back twice.
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -374,7 +374,7 @@
|
|||||||
"id": "pine",
|
"id": "pine",
|
||||||
"title": "Pine",
|
"title": "Pine",
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"description": "A private voice assistant for your home. Pine runs speech-to-text (Whisper), text-to-speech (Piper) and wake-word detection (openWakeWord) on your own node and pairs with a PineVoice satellite speaker, so Home Assistant Assist works locally with nothing sent to the cloud. Ask it about your node — block height, sync, peers, Lightning balance — and, when a Claude API key is set, anything else.",
|
"description": "A private voice assistant for your home. Pine runs speech-to-text (Whisper), text-to-speech (Piper) and wake-word detection (openWakeWord) on your own node and pairs with a PineVoice satellite speaker, so Home Assistant Assist works locally with nothing sent to the cloud. Ask it about your node \u2014 block height, sync, peers, Lightning balance \u2014 and, when a Claude API key is set, anything else.",
|
||||||
"icon": "/assets/img/app-icons/pine.svg",
|
"icon": "/assets/img/app-icons/pine.svg",
|
||||||
"author": "Archipelago",
|
"author": "Archipelago",
|
||||||
"category": "home",
|
"category": "home",
|
||||||
@@ -459,7 +459,7 @@
|
|||||||
"id": "netbird",
|
"id": "netbird",
|
||||||
"title": "NetBird",
|
"title": "NetBird",
|
||||||
"version": "2.38.0",
|
"version": "2.38.0",
|
||||||
"description": "Self-hosted WireGuard mesh VPN control plane with dashboard, embedded identity provider, management API, signal, relay, and STUN. The user-facing entry point — a TLS proxy in front of the dashboard + server.",
|
"description": "Self-hosted WireGuard mesh VPN control plane with dashboard, embedded identity provider, management API, signal, relay, and STUN. The user-facing entry point \u2014 a TLS proxy in front of the dashboard + server.",
|
||||||
"icon": "/assets/img/app-icons/netbird.svg",
|
"icon": "/assets/img/app-icons/netbird.svg",
|
||||||
"author": "NetBird",
|
"author": "NetBird",
|
||||||
"category": "networking",
|
"category": "networking",
|
||||||
@@ -547,6 +547,30 @@
|
|||||||
"/var/lib/archipelago/nextcloud:/var/www/html"
|
"/var/lib/archipelago/nextcloud:/var/www/html"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alby-hub",
|
||||||
|
"title": "Alby Hub",
|
||||||
|
"version": "1.23.0",
|
||||||
|
"description": "Self-custodial Lightning wallet hub. Runs its own Lightning node on your Archipelago and connects your apps to it over Nostr Wallet Connect \u2014 one hub, every app pays through it.",
|
||||||
|
"icon": "/assets/img/app-icons/alby-hub.svg",
|
||||||
|
"author": "Alby",
|
||||||
|
"category": "money",
|
||||||
|
"tier": "optional",
|
||||||
|
"dockerImage": "source.archipelago-foundation.org/lfg2025/alby-hub:v1.23.0",
|
||||||
|
"repoUrl": "https://github.com/getAlby/hub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "phoenixd",
|
||||||
|
"title": "phoenixd",
|
||||||
|
"version": "0.9.0",
|
||||||
|
"description": "Headless Lightning daemon by ACINQ (the Phoenix wallet team). No screen of its own \u2014 it exposes a small local API that other apps and tools use to send and receive Lightning payments. Channel liquidity is managed automatically for a fee.",
|
||||||
|
"icon": "/assets/img/app-icons/phoenixd.svg",
|
||||||
|
"author": "ACINQ",
|
||||||
|
"category": "money",
|
||||||
|
"tier": "optional",
|
||||||
|
"dockerImage": "source.archipelago-foundation.org/lfg2025/phoenixd:0.9.0",
|
||||||
|
"repoUrl": "https://github.com/ACINQ/phoenixd"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
app:
|
||||||
|
id: alby-hub
|
||||||
|
name: Alby Hub
|
||||||
|
version: 1.23.0
|
||||||
|
description: Self-custodial Lightning wallet hub. Runs its own Lightning node on your Archipelago and connects your apps to it over Nostr Wallet Connect — one hub, every app pays through it.
|
||||||
|
category: money
|
||||||
|
|
||||||
|
container:
|
||||||
|
image: source.archipelago-foundation.org/lfg2025/alby-hub:v1.23.0
|
||||||
|
pull_policy: if-not-present
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- storage: 1Gi
|
||||||
|
|
||||||
|
resources:
|
||||||
|
cpu_limit: 1
|
||||||
|
memory_limit: 512Mi
|
||||||
|
disk_limit: 2Gi
|
||||||
|
|
||||||
|
security:
|
||||||
|
capabilities: []
|
||||||
|
readonly_root: true
|
||||||
|
no_new_privileges: true
|
||||||
|
network_policy: bridge
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- host: 8187
|
||||||
|
container: 8080
|
||||||
|
protocol: tcp
|
||||||
|
bind: 127.0.0.1
|
||||||
|
auth: gated
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- type: bind
|
||||||
|
source: /var/lib/archipelago/alby-hub
|
||||||
|
target: /data
|
||||||
|
options: [rw]
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- WORK_DIR=/data
|
||||||
|
- PORT=8080
|
||||||
|
# LDK peers are dialed outbound-only in v1; no inbound p2p port is
|
||||||
|
# advertised, so no extra port mapping is needed for payments to work.
|
||||||
|
- LOG_LEVEL=info
|
||||||
|
|
||||||
|
health_check:
|
||||||
|
type: http
|
||||||
|
endpoint: http://localhost:8080
|
||||||
|
path: /
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
interfaces:
|
||||||
|
main:
|
||||||
|
name: Web UI
|
||||||
|
description: Alby Hub wallet interface
|
||||||
|
type: ui
|
||||||
|
port: 8187
|
||||||
|
protocol: http
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
icon: /assets/img/app-icons/alby-hub.svg
|
||||||
|
repo: https://github.com/getAlby/hub
|
||||||
|
tier: optional
|
||||||
|
launch:
|
||||||
|
# Embedded: the gate neutralizes Alby Hub's X-Frame-Options: DENY on
|
||||||
|
# proxied responses. Nodes older than the gate fix show a blocked
|
||||||
|
# frame — flip to true only if targeting such nodes.
|
||||||
|
open_in_new_tab: false
|
||||||
|
features:
|
||||||
|
- Self-custodial Lightning node (LDK) with a friendly wallet UI
|
||||||
|
- Connect wallets and apps via Nostr Wallet Connect (NWC)
|
||||||
|
- Per-app budgets and isolated sub-wallets
|
||||||
|
- Works with the Alby browser extension and mobile app
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
app:
|
||||||
|
id: phoenixd
|
||||||
|
name: phoenixd
|
||||||
|
version: 0.9.0
|
||||||
|
description: Headless Lightning daemon by ACINQ (the Phoenix wallet team). No screen of its own — it exposes a small local API that other apps and tools use to send and receive Lightning payments. Channel liquidity is managed automatically for a fee.
|
||||||
|
category: money
|
||||||
|
|
||||||
|
container:
|
||||||
|
# Image entrypoint already runs with --agree-to-terms-of-service and
|
||||||
|
# --http-bind-ip 0.0.0.0, as user "phoenix"; no custom args needed.
|
||||||
|
image: source.archipelago-foundation.org/lfg2025/phoenixd:0.9.0
|
||||||
|
pull_policy: if-not-present
|
||||||
|
# The image runs as user phoenix (1000:1000); the datadir bind source
|
||||||
|
# must be chowned to that identity or phoenixd dies on
|
||||||
|
# "Failed to open /data/phoenix.conf with Permission denied".
|
||||||
|
data_uid: "1000:1000"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- storage: 500Mi
|
||||||
|
|
||||||
|
resources:
|
||||||
|
cpu_limit: 1
|
||||||
|
memory_limit: 512Mi
|
||||||
|
disk_limit: 1Gi
|
||||||
|
|
||||||
|
security:
|
||||||
|
capabilities: []
|
||||||
|
readonly_root: true
|
||||||
|
no_new_privileges: true
|
||||||
|
network_policy: bridge
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- host: 9740
|
||||||
|
container: 9740
|
||||||
|
protocol: tcp
|
||||||
|
bind: 127.0.0.1
|
||||||
|
auth: none
|
||||||
|
auth_rationale: >-
|
||||||
|
Loopback-only JSON API, not a web page. Every request is
|
||||||
|
authenticated by the http password phoenixd generates in its own
|
||||||
|
data directory on first run; the app gate's browser login page
|
||||||
|
would break the API clients this port exists for.
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# The wallet seed (seed.dat) and phoenix.conf live here. This directory
|
||||||
|
# must survive reinstall/migration like any other app data dir —
|
||||||
|
# losing it means losing funds.
|
||||||
|
# Target is /data (via PHOENIX_DATADIR below), NOT the image's default
|
||||||
|
# /phoenix/.phoenix: the orchestrator treats any bind path containing a
|
||||||
|
# dot as a file mount and skips creating its source directory, so a
|
||||||
|
# hidden-dir target never gets its host dir and the unit crash-loops.
|
||||||
|
- type: bind
|
||||||
|
source: /var/lib/archipelago/phoenixd
|
||||||
|
target: /data
|
||||||
|
options: [rw]
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- PHOENIX_DATADIR=/data
|
||||||
|
|
||||||
|
health_check:
|
||||||
|
type: tcp
|
||||||
|
endpoint: localhost:9740
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
metadata:
|
||||||
|
icon: /assets/img/app-icons/phoenixd.svg
|
||||||
|
repo: https://github.com/ACINQ/phoenixd
|
||||||
|
tier: optional
|
||||||
|
features:
|
||||||
|
- Ultra-light Lightning node — no bitcoin node required
|
||||||
|
- Automated channel and liquidity management (fees apply)
|
||||||
|
- Simple HTTP API + websockets for payments
|
||||||
|
- Backed by the team behind the Phoenix mobile wallet
|
||||||
Generated
+1
-1
@@ -104,7 +104,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "archipelago"
|
name = "archipelago"
|
||||||
version = "1.7.129-alpha"
|
version = "1.8.3-alpha"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"archipelago-container",
|
"archipelago-container",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "archipelago"
|
name = "archipelago"
|
||||||
version = "1.7.129-alpha"
|
version = "1.8.3-alpha"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license.workspace = true
|
license.workspace = true
|
||||||
description = "Archipelago Bitcoin Node OS - Native backend"
|
description = "Archipelago Bitcoin Node OS - Native backend"
|
||||||
|
|||||||
@@ -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(
|
||||||
@@ -1268,7 +1292,14 @@ impl RpcHandler {
|
|||||||
} else {
|
} else {
|
||||||
"auto"
|
"auto"
|
||||||
};
|
};
|
||||||
Ok(serde_json::json!({ "has_kiosk": has_kiosk, "preset": preset }))
|
let graphics = if conf.contains("KIOSK_GRAPHICS=performance") {
|
||||||
|
"performance"
|
||||||
|
} else if conf.contains("KIOSK_GRAPHICS=quality") {
|
||||||
|
"quality"
|
||||||
|
} else {
|
||||||
|
"auto"
|
||||||
|
};
|
||||||
|
Ok(serde_json::json!({ "has_kiosk": has_kiosk, "preset": preset, "graphics": graphics }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// system.kiosk-display.set — Write the kiosk display preset and restart
|
/// system.kiosk-display.set — Write the kiosk display preset and restart
|
||||||
@@ -1279,24 +1310,56 @@ impl RpcHandler {
|
|||||||
params: Option<serde_json::Value>,
|
params: Option<serde_json::Value>,
|
||||||
) -> Result<serde_json::Value> {
|
) -> Result<serde_json::Value> {
|
||||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||||
let preset = params
|
let preset = params.get("preset").and_then(|v| v.as_str());
|
||||||
.get("preset")
|
let graphics = params.get("graphics").and_then(|v| v.as_str());
|
||||||
.and_then(|v| v.as_str())
|
if preset.is_none() && graphics.is_none() {
|
||||||
.ok_or_else(|| anyhow::anyhow!("Missing preset"))?;
|
anyhow::bail!("Missing preset or graphics");
|
||||||
|
}
|
||||||
|
|
||||||
let conf = match preset {
|
// The conf carries two independent settings (display scale preset +
|
||||||
|
// graphics tier). A set of one must not clobber the other, so the
|
||||||
|
// half not being changed is carried over from the file as-is.
|
||||||
|
let existing = tokio::fs::read_to_string(KIOSK_DISPLAY_CONF)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let display_part = match preset {
|
||||||
// Resolution-derived default: 4K -> 2.0 (1920-wide layout),
|
// Resolution-derived default: 4K -> 2.0 (1920-wide layout),
|
||||||
// 1080p TV -> 1.5, laptop panels -> 1.0.
|
// 1080p TV -> 1.5, laptop panels -> 1.0.
|
||||||
"auto" => String::new(),
|
Some("auto") => String::new(),
|
||||||
// Biggest UI: every panel targets a 1280-wide layout.
|
// Biggest UI: every panel targets a 1280-wide layout.
|
||||||
"large" => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1280\n".to_string(),
|
Some("large") => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1280\n".to_string(),
|
||||||
// Full-HD layout on any panel that can carry it.
|
// Full-HD layout on any panel that can carry it.
|
||||||
"balanced" => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1920\n".to_string(),
|
Some("balanced") => "ARCHIPELAGO_KIOSK_TARGET_CSS_WIDTH=1920\n".to_string(),
|
||||||
// No scaling: native CSS viewport, most content, smallest UI.
|
// No scaling: native CSS viewport, most content, smallest UI.
|
||||||
"native" => "ARCHIPELAGO_KIOSK_SCALE=1\n".to_string(),
|
Some("native") => "ARCHIPELAGO_KIOSK_SCALE=1\n".to_string(),
|
||||||
other => anyhow::bail!("Unknown display preset: {other}"),
|
Some(other) => anyhow::bail!("Unknown display preset: {other}"),
|
||||||
|
None => existing
|
||||||
|
.lines()
|
||||||
|
.filter(|l| l.starts_with("ARCHIPELAGO_KIOSK_"))
|
||||||
|
.map(|l| format!("{l}\n"))
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let graphics_part = match graphics {
|
||||||
|
// Auto: the launcher classifies the hardware itself (CPU/iGPU
|
||||||
|
// generation) — legacy boxes keep the choppy-audio-safe flags,
|
||||||
|
// modern iGPUs get GPU rasterization.
|
||||||
|
Some("auto") => String::new(),
|
||||||
|
// Force the conservative legacy flag set (troubleshooting).
|
||||||
|
Some("performance") => "KIOSK_GRAPHICS=performance\n".to_string(),
|
||||||
|
// Force the modern flag set even on unclassified hardware.
|
||||||
|
Some("quality") => "KIOSK_GRAPHICS=quality\n".to_string(),
|
||||||
|
Some(other) => anyhow::bail!("Unknown graphics mode: {other}"),
|
||||||
|
None => existing
|
||||||
|
.lines()
|
||||||
|
.find(|l| l.starts_with("KIOSK_GRAPHICS="))
|
||||||
|
.map(|l| format!("{l}\n"))
|
||||||
|
.unwrap_or_default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let conf = format!("{display_part}{graphics_part}");
|
||||||
|
|
||||||
host_sudo(&["/usr/bin/mkdir", "-p", "/etc/archipelago"]).await?;
|
host_sudo(&["/usr/bin/mkdir", "-p", "/etc/archipelago"]).await?;
|
||||||
if conf.is_empty() {
|
if conf.is_empty() {
|
||||||
let _ = host_sudo(&["/usr/bin/rm", "-f", KIOSK_DISPLAY_CONF]).await;
|
let _ = host_sudo(&["/usr/bin/rm", "-f", KIOSK_DISPLAY_CONF]).await;
|
||||||
@@ -1332,8 +1395,8 @@ impl RpcHandler {
|
|||||||
])
|
])
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
info!(preset, "Kiosk display preset applied");
|
info!(?preset, ?graphics, "Kiosk display settings applied");
|
||||||
Ok(serde_json::json!({ "preset": preset, "applied": true }))
|
Ok(serde_json::json!({ "preset": preset, "graphics": graphics, "applied": true }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -520,11 +520,63 @@ async fn proxy_to_app(
|
|||||||
|
|
||||||
let client = hyper::Client::new();
|
let client = hyper::Client::new();
|
||||||
match client.request(Request::from_parts(parts, body)).await {
|
match client.request(Request::from_parts(parts, body)).await {
|
||||||
Ok(resp) => resp,
|
Ok(mut resp) => {
|
||||||
|
neutralize_frame_blocking(resp.headers_mut());
|
||||||
|
resp
|
||||||
|
}
|
||||||
Err(_) => app_down_page(app),
|
Err(_) => app_down_page(app),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make gate-proxied app responses embeddable by the dashboard's My Apps
|
||||||
|
/// iframe. Apps that were never designed for framing ship
|
||||||
|
/// `X-Frame-Options: DENY` (Alby Hub) or a CSP `frame-ancestors` directive,
|
||||||
|
/// and either one makes the embedded app session a dead grey pane — the
|
||||||
|
/// historical workaround was a bespoke per-app nginx proxy (gitea), which is
|
||||||
|
/// exactly the per-app patching the manifest platform exists to delete.
|
||||||
|
///
|
||||||
|
/// Framing protection exists to stop a FOREIGN origin from framing an authed
|
||||||
|
/// page and clickjacking it. Behind the gate that threat model is already
|
||||||
|
/// handled the way the gate's own pages handle it: every proxied request is
|
||||||
|
/// authenticated by the gate first, and the gate's own responses declare
|
||||||
|
/// `frame-ancestors 'self' http://*:* https://*:*` (see `page()`) because the
|
||||||
|
/// dashboard is reached by LAN IP, mDNS name, and onion alike. Upstream
|
||||||
|
/// X-Frame-Options is dropped entirely; only the `frame-ancestors` directive
|
||||||
|
/// is removed from the app's CSP — the rest of the app's policy (script-src,
|
||||||
|
/// connect-src, …) is the app's business and passes through untouched.
|
||||||
|
fn neutralize_frame_blocking(headers: &mut hyper::HeaderMap) {
|
||||||
|
headers.remove("x-frame-options");
|
||||||
|
let Some(csp) = headers.get("content-security-policy") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(raw) = csp.to_str() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !raw.to_ascii_lowercase().contains("frame-ancestors") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let kept: Vec<&str> = raw
|
||||||
|
.split(';')
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|d| !d.to_ascii_lowercase().starts_with("frame-ancestors") && !d.is_empty())
|
||||||
|
.collect();
|
||||||
|
if kept.is_empty() {
|
||||||
|
headers.remove("content-security-policy");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match header::HeaderValue::from_str(&kept.join("; ")) {
|
||||||
|
Ok(v) => {
|
||||||
|
headers.insert("content-security-policy", v);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Unrepresentable after filtering — fail open for framing but
|
||||||
|
// closed for the policy: better to drop a mangled CSP than to
|
||||||
|
// serve one we rewrote incorrectly.
|
||||||
|
headers.remove("content-security-policy");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Cookie names owned by the gate/daemon, never the app's to see.
|
/// Cookie names owned by the gate/daemon, never the app's to see.
|
||||||
const GATE_COOKIE_NAMES: &[&str] = &["session", "csrf_token"];
|
const GATE_COOKIE_NAMES: &[&str] = &["session", "csrf_token"];
|
||||||
|
|
||||||
@@ -1072,6 +1124,50 @@ mod tests {
|
|||||||
/// 2026-08-05). It must still be uncacheable, and still refuse to be
|
/// 2026-08-05). It must still be uncacheable, and still refuse to be
|
||||||
/// framed by a foreign origin, which `frame-ancestors` expresses and
|
/// framed by a foreign origin, which `frame-ancestors` expresses and
|
||||||
/// `X-Frame-Options` cannot.
|
/// `X-Frame-Options` cannot.
|
||||||
|
/// Upstream frame-blocking must not survive the proxy: X-Frame-Options
|
||||||
|
/// goes away entirely, CSP loses ONLY its frame-ancestors directive —
|
||||||
|
/// the app's remaining policy must pass through byte-preserving in
|
||||||
|
/// content (Alby Hub's DENY + strict CSP was the real-world case,
|
||||||
|
/// archi-dev-box 2026-08-12).
|
||||||
|
#[test]
|
||||||
|
fn proxied_responses_lose_frame_blocking_but_keep_the_apps_csp() {
|
||||||
|
let mut headers = hyper::HeaderMap::new();
|
||||||
|
headers.insert("x-frame-options", "DENY".parse().unwrap());
|
||||||
|
headers.insert(
|
||||||
|
"content-security-policy",
|
||||||
|
"default-src 'self'; frame-ancestors 'none'; img-src 'self' https://cdn.example"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
neutralize_frame_blocking(&mut headers);
|
||||||
|
assert!(!headers.contains_key("x-frame-options"));
|
||||||
|
let csp = headers["content-security-policy"].to_str().unwrap();
|
||||||
|
assert!(!csp.contains("frame-ancestors"));
|
||||||
|
assert!(csp.contains("default-src 'self'"));
|
||||||
|
assert!(csp.contains("img-src 'self' https://cdn.example"));
|
||||||
|
|
||||||
|
// CSP that is ONLY a frame-ancestors directive disappears entirely.
|
||||||
|
let mut only = hyper::HeaderMap::new();
|
||||||
|
only.insert(
|
||||||
|
"content-security-policy",
|
||||||
|
"frame-ancestors 'self'".parse().unwrap(),
|
||||||
|
);
|
||||||
|
neutralize_frame_blocking(&mut only);
|
||||||
|
assert!(!only.contains_key("content-security-policy"));
|
||||||
|
|
||||||
|
// No frame directives at all → CSP untouched.
|
||||||
|
let mut plain = hyper::HeaderMap::new();
|
||||||
|
plain.insert(
|
||||||
|
"content-security-policy",
|
||||||
|
"default-src 'self'".parse().unwrap(),
|
||||||
|
);
|
||||||
|
neutralize_frame_blocking(&mut plain);
|
||||||
|
assert_eq!(
|
||||||
|
plain["content-security-policy"].to_str().unwrap(),
|
||||||
|
"default-src 'self'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn challenge_pages_are_uncacheable_and_framable_only_by_this_node() {
|
fn challenge_pages_are_uncacheable_and_framable_only_by_this_node() {
|
||||||
let resp = login_page(&app(), None, StatusCode::UNAUTHORIZED);
|
let resp = login_page(&app(), None, StatusCode::UNAUTHORIZED);
|
||||||
|
|||||||
@@ -1540,9 +1540,7 @@ pub async fn apply_update(data_dir: &Path) -> Result<()> {
|
|||||||
.await?
|
.await?
|
||||||
.available_update
|
.available_update
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
anyhow::anyhow!(
|
anyhow::anyhow!("Update manifest missing from state — re-download the update")
|
||||||
"Update manifest missing from state — re-download the update"
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
verify_staged_components(&staging_dir, &manifest).await?;
|
verify_staged_components(&staging_dir, &manifest).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
||||||
+40
-40
File diff suppressed because one or more lines are too long
+1
-1
@@ -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
-1
File diff suppressed because one or more lines are too long
@@ -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};
|
|
||||||
@@ -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
@@ -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
-1
File diff suppressed because one or more lines are too long
@@ -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 +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
@@ -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};
|
||||||
+1
-1
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};
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -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 +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
@@ -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
@@ -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};
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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};
|
|
||||||
@@ -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 +0,0 @@
|
|||||||
import{b as r}from"./_baseUniq-DAOs4kUj.js";var e=4;function a(o){return r(o,e)}export{a as c};
|
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -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
-1
@@ -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};
|
||||||
}
|
}
|
||||||
+1
-1
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
-1
@@ -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+`
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
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
-1
@@ -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
-1
@@ -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
-1
@@ -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
+4
-4
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -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};
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
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};
|
||||||
+1
-1
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
-1
@@ -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()+`
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -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
+1
-1
File diff suppressed because one or more lines are too long
@@ -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
File diff suppressed because one or more lines are too long
+120
-13
@@ -130,7 +130,46 @@ app:
|
|||||||
|
|
||||||
Additional extension keys may exist for current integrations, for example Bitcoin, Lightning, or app-specific launch/interface metadata. Treat extension keys as transitional unless they are documented as reusable platform primitives.
|
Additional extension keys may exist for current integrations, for example Bitcoin, Lightning, or app-specific launch/interface metadata. Treat extension keys as transitional unless they are documented as reusable platform primitives.
|
||||||
|
|
||||||
Use `metadata.launch.open_in_new_tab: true` when the app UI is known to reject iframe embedding with headers such as `X-Frame-Options` or restrictive CSP. The frontend app-session metadata is generated from this flag during release work.
|
### Iframe embedding — the rules
|
||||||
|
|
||||||
|
The dashboard opens apps in an **embedded frame** (My Apps → app session) by
|
||||||
|
default. Whether that works is decided by HTTP headers, not by wishes, so
|
||||||
|
know the mechanics:
|
||||||
|
|
||||||
|
- Browsers refuse to render a page in an iframe when the response carries
|
||||||
|
`X-Frame-Options: DENY`/`SAMEORIGIN` (the dashboard and the app are
|
||||||
|
different origins — different port at minimum) or a CSP `frame-ancestors`
|
||||||
|
directive that excludes the dashboard's origin.
|
||||||
|
- Many upstream apps ship exactly those headers (Alby Hub sends
|
||||||
|
`X-Frame-Options: DENY`). In a normal deployment that is correct hardening;
|
||||||
|
behind Archipelago's app gate the clickjacking threat those headers address
|
||||||
|
is already handled — every proxied request is authenticated by the gate
|
||||||
|
first.
|
||||||
|
- Therefore **the gate neutralizes frame blocking on proxied responses**: it
|
||||||
|
removes `X-Frame-Options` and strips only the `frame-ancestors` directive
|
||||||
|
from the app's CSP. The rest of the app's CSP (script-src, connect-src, …)
|
||||||
|
passes through untouched — the gate never weakens the app's own content
|
||||||
|
policy, only its framing policy. You do not need a bespoke reverse proxy,
|
||||||
|
header patches, or app config to be embeddable.
|
||||||
|
|
||||||
|
Set `metadata.launch.open_in_new_tab: true` only when embedding is broken by
|
||||||
|
things headers can't fix — the app frame-busts in JavaScript, requires being
|
||||||
|
the top-level origin (OAuth redirect flows, WebAuthn), or sets
|
||||||
|
`SameSite=Strict` session cookies that never accompany framed requests. Test
|
||||||
|
in the real embedded app session, **not** a plain browser tab: tabs don't
|
||||||
|
enforce framing headers, so a tab proves nothing about the iframe.
|
||||||
|
|
||||||
|
**The dashboard also self-heals**: if a running app's frame fails to load,
|
||||||
|
the session offers "open in tab" and *remembers the app as a tab app* — every
|
||||||
|
later launch opens a tab directly, with the tab-launch icon on its button.
|
||||||
|
The memory clears itself when the app later embeds successfully, and expires
|
||||||
|
weekly so fixes get re-probed. This safety net is not a licence to skip the
|
||||||
|
manifest flag: declaring `open_in_new_tab: true` up front spares your users
|
||||||
|
the one dead-pane encounter the detector needs.
|
||||||
|
|
||||||
|
(Historical note: before the gate handled this, embeddable-but-blocking apps
|
||||||
|
each carried a hand-built nginx strip proxy — gitea's port-3000 proxy is the
|
||||||
|
surviving example. Do not copy that pattern for new apps.)
|
||||||
|
|
||||||
### Launch Interfaces
|
### Launch Interfaces
|
||||||
|
|
||||||
@@ -406,19 +445,73 @@ curl http://localhost:8180/health
|
|||||||
podman logs my-app
|
podman logs my-app
|
||||||
```
|
```
|
||||||
|
|
||||||
### On an Archipelago Node
|
### On an Archipelago Node (before your app is in the catalog)
|
||||||
|
|
||||||
1. Install via the marketplace UI or RPC:
|
The App Store lists **signed-catalog apps and Nostr-discovered apps only** —
|
||||||
```bash
|
a manifest on the node's disk never appears in the store by itself. That is
|
||||||
curl -b cookies.txt -X POST http://archipelago.local/rpc/v1 \
|
deliberate: the store is a trust surface. But the orchestrator installs from
|
||||||
-d '{"method":"package.install","params":{"id":"my-app","dockerImage":"docker.io/myorg/my-app:1.0.0"}}'
|
disk manifests just fine, so you can test the complete install/run/uninstall
|
||||||
```
|
lifecycle on your own node before your app is published anywhere.
|
||||||
2. Verify the container is running:
|
|
||||||
```bash
|
**1. Stage the manifest where it survives reboots.**
|
||||||
curl -b cookies.txt -X POST http://archipelago.local/rpc/v1 \
|
|
||||||
-d '{"method":"container-list"}'
|
`/opt/archipelago/apps/` is *rebuilt on every backend start* from the runtime
|
||||||
```
|
payload that ships inside the frontend bundle
|
||||||
3. Check the UI. The app's detail page is `http://archipelago.local/dashboard/apps/my-app`; the embedded launch surface is `http://archipelago.local/dashboard/app-session/my-app`
|
(`/opt/archipelago/web-ui/archipelago-runtime/apps/`). If you copy your
|
||||||
|
manifest only into `/opt/archipelago/apps/`, the next restart silently deletes
|
||||||
|
it. Stage into the payload directory instead — the boot sync then promotes it
|
||||||
|
for you:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /opt/archipelago/web-ui/archipelago-runtime/apps/my-app
|
||||||
|
sudo cp apps/my-app/manifest.yml /opt/archipelago/web-ui/archipelago-runtime/apps/my-app/
|
||||||
|
sudo systemctl restart archipelago # manifests are loaded at startup
|
||||||
|
```
|
||||||
|
|
||||||
|
Watch `journalctl -u archipelago` after the restart — the orchestrator
|
||||||
|
validates every manifest on load and tells you about problems immediately
|
||||||
|
(for example a host-port collision with another installed app).
|
||||||
|
|
||||||
|
**2. Install over JSON-RPC.**
|
||||||
|
|
||||||
|
The repo ships the same session helper the release lifecycle gate uses.
|
||||||
|
Three things to know before using it: it needs `jq`; it reuses a cached
|
||||||
|
session from `/tmp/archy-rpc-session-<uid>` unless `ARCHY_FORCE_LOGIN=1` is
|
||||||
|
set (a stale cache fails every call quietly); and it sets `set -euo pipefail`,
|
||||||
|
so run it inside a script or subshell — sourcing it into your interactive
|
||||||
|
shell makes the first failed step kill the whole chain without printing
|
||||||
|
anything.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash <<'EOF'
|
||||||
|
export ARCHY_PASSWORD='<your dashboard password>' ARCHY_FORCE_LOGIN=1
|
||||||
|
# Stock nodes serve HTTPS on 443; dev boxes behind plain nginx use:
|
||||||
|
# export ARCHY_HOST=127.0.0.1 ARCHY_SCHEME=http
|
||||||
|
source tests/lifecycle/lib/rpc.bash
|
||||||
|
rpc_login && echo "login ok"
|
||||||
|
# Both fields are required: `dockerImage` is normally supplied by the App
|
||||||
|
# Store from the signed catalog — pre-catalog, you pass your manifest's
|
||||||
|
# image yourself (it must match, and must come from a trusted registry).
|
||||||
|
rpc_call package.install '{"id":"my-app","dockerImage":"docker.io/myorg/my-app:1.0.0"}'
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Verify the lifecycle, not just the install:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rpc_call package.status '{"id":"my-app"}' # state + health (run inside the same subshell pattern)
|
||||||
|
podman ps --filter name=my-app # container is up
|
||||||
|
rpc_call package.stop '{"id":"my-app"}' # …and start, restart
|
||||||
|
sudo systemctl restart archipelago # app must survive this
|
||||||
|
rpc_call package.uninstall '{"id":"my-app","preserve_data":true}'
|
||||||
|
rpc_call package.install '{"id":"my-app"}' # data still there?
|
||||||
|
```
|
||||||
|
|
||||||
|
The app's detail page is `https://<node>/dashboard/apps/my-app`; a gated web
|
||||||
|
UI is reachable through the app gate on its manifest port once running.
|
||||||
|
|
||||||
|
Only after this loop is green does the app belong in a catalog submission —
|
||||||
|
catalog inclusion is what makes it appear in the App Store.
|
||||||
|
|
||||||
### Validate Manifest
|
### Validate Manifest
|
||||||
|
|
||||||
@@ -438,6 +531,20 @@ The broader app update policy for `1.8-alpha` is still being finalized. Until th
|
|||||||
|
|
||||||
## App Icon
|
## App Icon
|
||||||
|
|
||||||
|
- **The tile plate (dark rounded background, border, sheen) is applied by
|
||||||
|
the system** — every surface renders your icon with the house
|
||||||
|
`archy-app-icon` treatment automatically. What the system deliberately
|
||||||
|
does NOT add is an inner margin (icons that already carry whitespace
|
||||||
|
would double-margin), so your file needs it baked in: **square canvas,
|
||||||
|
~12% margin per side**. Edge-to-edge marks look wrong next to every
|
||||||
|
other tile. Don't hand-tune it: run your mark through the normalizer
|
||||||
|
and commit its output —
|
||||||
|
```bash
|
||||||
|
scripts/normalize-app-icon.py your-mark.svg apps-icon-output.svg
|
||||||
|
```
|
||||||
|
It wraps any SVG (full-bleed upstream logos included) onto the house
|
||||||
|
canvas with the standard margin, preserving the original artwork
|
||||||
|
untouched inside.
|
||||||
- Provide a URL to your app icon (PNG, WebP, or SVG)
|
- Provide a URL to your app icon (PNG, WebP, or SVG)
|
||||||
- Recommended size: 256x256 pixels
|
- Recommended size: 256x256 pixels
|
||||||
- Square aspect ratio
|
- Square aspect ratio
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user