mirror of
https://github.com/MervinPraison/PraisonAIUI.git
synced 2026-11-07 05:56:17 -10:00
Compare commits
55
Commits
No files matched your search
@@ -32,4 +32,13 @@ const comment = ciFix.buildCiFixComment({
|
||||
});
|
||||
assert('comment mentions product guardrails', comment.includes('Product guardrails'));
|
||||
|
||||
const skip = ciFix.shouldSkipCiFix({
|
||||
comments: [],
|
||||
headSha: 'abc1234567890abcdef1234567890abcdef12',
|
||||
labels: [],
|
||||
claudeInProgress: false,
|
||||
failedChecks: [{ name: 'python', workflow: 'CI' }],
|
||||
});
|
||||
assert('ci fix without FINAL review allowed', !skip.skip);
|
||||
|
||||
process.exit(failed ? 1 : 0);
|
||||
@@ -46,10 +46,9 @@ function hasRecentCiFixComment(comments, headSha) {
|
||||
});
|
||||
}
|
||||
|
||||
function shouldSkipCiFix({ comments, headSha, labels, hasFinal, claudeInProgress, failedChecks }) {
|
||||
function shouldSkipCiFix({ comments, headSha, labels, claudeInProgress, failedChecks }) {
|
||||
if (!failedChecks.length) return { skip: true, reason: 'no failed checks' };
|
||||
if (labels.includes(CI_FIX_LABEL)) return { skip: true, reason: 'ci fix pending' };
|
||||
if (!hasFinal) return { skip: true, reason: 'awaiting final claude review trigger' };
|
||||
if (claudeInProgress) return { skip: true, reason: 'claude in progress' };
|
||||
if (hasCiFixCommentForSha(comments, headSha)) {
|
||||
return { skip: true, reason: 'already commented for this sha' };
|
||||
@@ -159,12 +158,14 @@ function buildCiFixComment({ headSha, failedChecks, failureSummaries }) {
|
||||
'',
|
||||
'**Product guardrails (AGENTS.md):**',
|
||||
'- Do not disturb PraisonAIUI contracts or frontend/backend layering to accommodate scope creep.',
|
||||
'- Core agent runtime belongs in praisonaiagents; agent-callable tools belong in PraisonAI-Tools — not here.',
|
||||
'- Prefer existing YAML config, plugins, and feature modules over new server.py/create_app() params or knobs with no live consumer.',
|
||||
'- Tests must continue to guard backward compatibility and UX behaviour — passing CI by lowering test standards is not acceptable.',
|
||||
'- If the feature does not genuinely add product value, recommend reverting or narrowing scope instead of patching around failures.',
|
||||
'',
|
||||
'## What to do',
|
||||
'1. State your verdict: **legitimate fix**, **regression fix**, or **needs human review** — and why (1–3 sentences).',
|
||||
'2. Fix root cause with **minimal changes**; never bloat the Agent class with extra params.',
|
||||
'2. Fix root cause with **minimal changes**; never bloat server.py or create_app() with extra params.',
|
||||
exampleTest
|
||||
? `3. Run failing tests locally, e.g. \`pytest ${exampleTest} -q\`, plus any related tests touched by the PR.`
|
||||
: '3. Run failing tests locally with targeted pytest, plus any related tests touched by the PR.',
|
||||
@@ -221,7 +222,6 @@ async function maybeTriggerCiFixClaude(github, owner, repo, prNumber, core, opts
|
||||
return { skipped: true, reason: 'no failed checks yet (may be pending)' };
|
||||
}
|
||||
|
||||
const hasFinal = mergeGate.hasFinalClaudeReviewTrigger(ctx.comments);
|
||||
const claudeInProgress = await mergeGate.hasInProgressClaudeAssistant(
|
||||
github, owner, repo, prNumber
|
||||
);
|
||||
@@ -229,7 +229,6 @@ async function maybeTriggerCiFixClaude(github, owner, repo, prNumber, core, opts
|
||||
comments: ctx.comments,
|
||||
headSha,
|
||||
labels,
|
||||
hasFinal,
|
||||
claudeInProgress,
|
||||
failedChecks: failedRuns,
|
||||
});
|
||||
|
||||
@@ -21,8 +21,27 @@ module.exports = {
|
||||
'SCOPE: Focus ONLY on PraisonAIUI (src/praisonaiui, src/frontend, tests, docs). '
|
||||
+ 'Do NOT expand into praisonaiagents or the monorepo unless the PR explicitly requires it.',
|
||||
finalClaudeProductValue:
|
||||
'4. Product value: review whether the change genuinely adds PraisonAIUI UX value and correct '
|
||||
+ 'layering (UI/frontend here; core agent logic belongs in praisonaiagents). Reject scope creep.',
|
||||
'4. Product value: review in depth whether the change genuinely adds PraisonAIUI UX value — '
|
||||
+ 'never add features for the sake of adding them. It must strengthen the UI (simpler, '
|
||||
+ 'more user-friendly, robust). If it does not clearly add value, request changes or recommend '
|
||||
+ 'rejecting/closing rather than merging scope creep.\n'
|
||||
+ '5. Layering: UI/frontend/server features here; core agent runtime belongs in praisonaiagents '
|
||||
+ '(PraisonAI monorepo). Agent-callable tools → PraisonAI-Tools. Do not pull monorepo or SDK '
|
||||
+ 'scope into this repo unless the PR explicitly requires it.\n'
|
||||
+ '6. Do not bloat server.py or create_app() with new params — prefer existing YAML config, '
|
||||
+ 'plugins, and feature modules; only add params if absolutely required. Reject knobs/exports '
|
||||
+ 'with no live consumer.',
|
||||
mergeGateProductValue:
|
||||
'Confirm product value gate: change strengthens PraisonAIUI UX (no scope creep). The aim is a '
|
||||
+ 'LIGHTWEIGHT AND POWERFUL package — BLOCK changes that add params/modules/exports duplicating '
|
||||
+ 'existing UI capabilities (YAML config, plugins, feature modules, existing server hooks), or '
|
||||
+ 'add knobs/fields with no live consumer, i.e. scope creep for the sake of it.',
|
||||
mergeGateLayering:
|
||||
'Layering routing: BLOCK if core agent logic was added here instead of praisonaiagents; BLOCK if '
|
||||
+ 'agent-callable tools were added here instead of PraisonAI-Tools.',
|
||||
mergeGateServerBloat:
|
||||
'Do not bloat server.py or create_app() with additional params — only if absolutely required; '
|
||||
+ 'prefer existing config/plugin patterns.',
|
||||
agentPyChecks: false,
|
||||
reviewBotLogins: [
|
||||
'coderabbitai[bot]',
|
||||
|
||||
@@ -205,7 +205,7 @@ const headAfterRebase = conflictIso(-21 * 60 * 1000);
|
||||
const rebaseComments = [conflictTrigger, rebaseDone, finalAfterRebase];
|
||||
assert('conflict blocks before rebase done', mg.hasRecentConflictComment([conflictTrigger], headAfterRebase));
|
||||
assert('conflict clears after rebase + FINAL on HEAD', !mg.hasRecentConflictComment(rebaseComments, headAfterRebase));
|
||||
assert('conflict still blocks without FINAL on HEAD', mg.hasRecentConflictComment(
|
||||
assert('conflict clears after rebase without FINAL on HEAD', !mg.hasRecentConflictComment(
|
||||
[conflictTrigger, rebaseDone],
|
||||
headAfterRebase
|
||||
));
|
||||
|
||||
@@ -67,7 +67,11 @@ function isClaudeTriggerNoise(c) {
|
||||
|
||||
function isClaudeFinalReplyComment(c) {
|
||||
const login = (c.user?.login || '').toLowerCase();
|
||||
if (!login.includes('praisonai-triage')) return false;
|
||||
const fromAutomation =
|
||||
login.includes('praisonai-triage') ||
|
||||
login.includes('github-actions') ||
|
||||
AUTO_ACTORS.some((a) => a.toLowerCase() === login);
|
||||
if (!fromAutomation) return false;
|
||||
return (c.body || '').includes('Claude finished');
|
||||
}
|
||||
|
||||
@@ -89,15 +93,47 @@ function isConflictRebaseTriggerComment(c) {
|
||||
|
||||
function isConflictRebaseCompletionComment(c) {
|
||||
const login = (c.user?.login || '').toLowerCase();
|
||||
if (!login.includes('praisonai-triage') && !login.includes('github-actions')) return false;
|
||||
const fromAutomation =
|
||||
login.includes('praisonai-triage') ||
|
||||
login.includes('github-actions') ||
|
||||
AUTO_ACTORS.some((a) => a.toLowerCase() === login);
|
||||
if (!fromAutomation) return false;
|
||||
const body = (c.body || '').toLowerCase();
|
||||
return (
|
||||
body.includes('rebase complete') ||
|
||||
body.includes('rebase onto') ||
|
||||
body.includes('force-pushed') ||
|
||||
(body.includes('conflict') && body.includes('resolved'))
|
||||
);
|
||||
}
|
||||
|
||||
/** HEAD landed after conflict rebase but no FINAL @claude since that push. */
|
||||
function needsFinalReReviewAfterConflictRebase(comments, headPushedAt) {
|
||||
if (!headPushedAt) return false;
|
||||
const headTime = new Date(headPushedAt).getTime();
|
||||
const conflictTriggers = comments.filter(isConflictRebaseTriggerComment);
|
||||
if (conflictTriggers.length === 0) return false;
|
||||
|
||||
const latestConflict = conflictTriggers.reduce((a, b) =>
|
||||
new Date(a.created_at) > new Date(b.created_at) ? a : b
|
||||
);
|
||||
const conflictTime = new Date(latestConflict.created_at).getTime();
|
||||
if (headTime <= conflictTime) return false;
|
||||
|
||||
const rebaseDone = comments.some(
|
||||
(c) =>
|
||||
new Date(c.created_at).getTime() > conflictTime && isConflictRebaseCompletionComment(c)
|
||||
);
|
||||
if (!rebaseDone) return false;
|
||||
|
||||
const finalsAfterHead = comments.filter(
|
||||
(c) =>
|
||||
isFinalClaudeTriggerComment(c) &&
|
||||
new Date(c.created_at).getTime() >= headTime - 60000
|
||||
);
|
||||
return finalsAfterHead.length === 0;
|
||||
}
|
||||
|
||||
function conflictRebaseQuiescent(comments, headPushedAt) {
|
||||
const conflictTriggers = comments.filter(isConflictRebaseTriggerComment);
|
||||
if (conflictTriggers.length === 0) return true;
|
||||
@@ -113,7 +149,7 @@ function conflictRebaseQuiescent(comments, headPushedAt) {
|
||||
);
|
||||
if (!rebaseDone) return false;
|
||||
|
||||
return finalClaudeCompletedOnSha(comments, headPushedAt);
|
||||
return true;
|
||||
}
|
||||
|
||||
function hasRecentConflictComment(comments, headPushedAt = null) {
|
||||
@@ -238,6 +274,9 @@ function shouldSkipStaleFinalRecovery(comments, headPushedAt, headPusherLogin =
|
||||
if (headPusherLogin && isClaudeAutomationLogin(headPusherLogin)) {
|
||||
return { skip: true, reason: 'head pushed by Claude automation' };
|
||||
}
|
||||
if (needsFinalReReviewAfterConflictRebase(comments, headPushedAt)) {
|
||||
return { skip: false, reason: '' };
|
||||
}
|
||||
if (isPushSoonAfterLatestFinal(comments, headPushedAt)) {
|
||||
return {
|
||||
skip: true,
|
||||
@@ -846,6 +885,7 @@ module.exports = {
|
||||
hasRecentConflictComment,
|
||||
isConflictRebaseTriggerComment,
|
||||
isConflictRebaseCompletionComment,
|
||||
needsFinalReReviewAfterConflictRebase,
|
||||
conflictRebaseQuiescent,
|
||||
hasHumanChangesRequested,
|
||||
hasAnyChangesRequested,
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
const chain = require('./pr-review-chain.js');
|
||||
|
||||
let failed = 0;
|
||||
function assert(name, cond) {
|
||||
if (!cond) {
|
||||
console.error('FAIL:', name);
|
||||
failed++;
|
||||
} else {
|
||||
console.log('ok:', name);
|
||||
}
|
||||
}
|
||||
|
||||
const coderabbit = {
|
||||
user: { login: 'coderabbitai[bot]' },
|
||||
body: '<!-- summarize by coderabbit.ai --> summary',
|
||||
created_at: '2026-07-02T07:16:00Z',
|
||||
};
|
||||
const greptile = {
|
||||
user: { login: 'greptile-apps[bot]' },
|
||||
body: '<h3>Greptile Summary</h3><p>Looks good</p>',
|
||||
created_at: '2026-07-02T07:19:00Z',
|
||||
};
|
||||
const copilotKick = {
|
||||
user: { login: 'MervinPraison' },
|
||||
body: '@copilot Do a thorough review',
|
||||
created_at: '2026-07-02T07:20:00Z',
|
||||
};
|
||||
const copilotReply = {
|
||||
user: { login: 'copilot-swe-agent' },
|
||||
body: 'Copilot review here',
|
||||
created_at: '2026-07-02T07:22:00Z',
|
||||
};
|
||||
|
||||
assert('prior not ready without greptile', !chain.priorReviewersReady([coderabbit]).ready);
|
||||
assert('prior ready with coderabbit + greptile', chain.priorReviewersReady([coderabbit, greptile]).ready);
|
||||
assert('copilot not ready before trigger', !chain.copilotReviewReady([coderabbit, greptile]).ready);
|
||||
assert(
|
||||
'copilot ready after reply',
|
||||
chain.copilotReviewReady([coderabbit, greptile, copilotKick, copilotReply]).ready
|
||||
);
|
||||
assert(
|
||||
'claude not ready before copilot',
|
||||
!chain.claudeFinalReady([coderabbit, greptile]).ready
|
||||
);
|
||||
assert(
|
||||
'claude ready after full chain',
|
||||
chain.claudeFinalReady([coderabbit, greptile, copilotKick, copilotReply]).ready
|
||||
);
|
||||
assert(
|
||||
'claude timeout fallback when copilot triggered but silent',
|
||||
chain.claudeFinalReady([coderabbit, greptile, copilotKick], [], { allowCopilotTimeout: true }).ready
|
||||
);
|
||||
assert(
|
||||
'no timeout fallback without copilot trigger',
|
||||
!chain.claudeFinalReady([coderabbit, greptile], [], { allowCopilotTimeout: true }).ready
|
||||
);
|
||||
assert(
|
||||
'skipCopilot proceeds after prior reviewers',
|
||||
chain.claudeFinalReady([coderabbit, greptile], [], { skipCopilot: true }).ready
|
||||
);
|
||||
|
||||
const oldPr = new Date(Date.now() - 31 * 60 * 1000).toISOString();
|
||||
assert(
|
||||
'prior not ready without coderabbit before timeout',
|
||||
!chain.priorReviewersReady([greptile], [], { prCreatedAt: new Date().toISOString(), requiredWaitMs: 30 * 60 * 1000 }).ready
|
||||
);
|
||||
assert(
|
||||
'prior timeout when coderabbit silent after 30m',
|
||||
chain.priorReviewersReady([greptile], [], { prCreatedAt: oldPr, requiredWaitMs: 30 * 60 * 1000 }).ready
|
||||
);
|
||||
assert(
|
||||
'claude FINAL timeout when required reviewer silent and no prior FINAL',
|
||||
chain.claudeFinalReady([greptile], [], { prCreatedAt: oldPr, requiredWaitMs: 30 * 60 * 1000 }).ready
|
||||
);
|
||||
assert(
|
||||
'claude FINAL timeout auto-skips copilot without explicit skipCopilot',
|
||||
chain.claudeFinalReady([greptile], [], { prCreatedAt: oldPr, requiredWaitMs: 30 * 60 * 1000 }).copilotSkipped
|
||||
);
|
||||
const finalClaude = {
|
||||
user: { login: 'github-actions[bot]' },
|
||||
body: '@claude You are the FINAL architecture reviewer for this PR.',
|
||||
created_at: '2026-07-02T07:25:00Z',
|
||||
};
|
||||
assert(
|
||||
'claude FINAL not re-posted when already triggered',
|
||||
!chain.claudeFinalReady([greptile, finalClaude], [], { prCreatedAt: oldPr, requiredWaitMs: 0 }).ready
|
||||
);
|
||||
|
||||
process.exit(failed ? 1 : 0);
|
||||
@@ -16,6 +16,7 @@ const CLAUDE_TRIGGER_LOGINS = new Set(['MervinPraison', 'github-actions[bot]']);
|
||||
const REQUIRED_PRIOR = ['coderabbit', 'greptile'];
|
||||
const OPTIONAL_PRIOR = ['qodo', 'gemini'];
|
||||
const OPTIONAL_PRIOR_WAIT_MS = 20 * 60 * 1000;
|
||||
const REQUIRED_PRIOR_WAIT_MS = 30 * 60 * 1000;
|
||||
|
||||
function loginOf(item) {
|
||||
return (item?.user?.login || '').toLowerCase();
|
||||
@@ -75,11 +76,23 @@ function priorReviewerStatus(comments, reviews = []) {
|
||||
|
||||
function priorReviewersReady(comments, reviews = [], options = {}) {
|
||||
const waitMs = options.optionalWaitMs ?? OPTIONAL_PRIOR_WAIT_MS;
|
||||
const requiredWaitMs = options.requiredWaitMs ?? REQUIRED_PRIOR_WAIT_MS;
|
||||
const prCreatedAt = options.prCreatedAt || null;
|
||||
const status = priorReviewerStatus(comments, reviews);
|
||||
|
||||
const missingRequired = REQUIRED_PRIOR.filter((id) => !status[id]);
|
||||
if (missingRequired.length) {
|
||||
if (prCreatedAt) {
|
||||
const age = Date.now() - new Date(prCreatedAt).getTime();
|
||||
if (age >= requiredWaitMs) {
|
||||
return {
|
||||
ready: true,
|
||||
reason: `required reviewer timeout (${missingRequired.join(', ')} silent after ${Math.round(requiredWaitMs / 60000)}m)`,
|
||||
status,
|
||||
requiredTimeout: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return { ready: false, reason: `waiting for ${missingRequired.join(', ')}`, status };
|
||||
}
|
||||
|
||||
@@ -157,8 +170,13 @@ function claudeFinalReady(comments, reviews = [], options = {}) {
|
||||
if (!prior.ready) {
|
||||
return { ready: false, reason: prior.reason };
|
||||
}
|
||||
if (isSkipCopilot(options)) {
|
||||
return { ready: true, reason: 'copilot skipped', copilotSkipped: true };
|
||||
if (isSkipCopilot(options) || prior.requiredTimeout) {
|
||||
return {
|
||||
ready: true,
|
||||
reason: prior.requiredTimeout ? prior.reason : 'copilot skipped',
|
||||
copilotSkipped: true,
|
||||
requiredTimeout: prior.requiredTimeout,
|
||||
};
|
||||
}
|
||||
const copilot = copilotReviewReady(comments, reviews);
|
||||
if (copilot.ready) {
|
||||
@@ -283,6 +301,7 @@ module.exports = {
|
||||
isSkipCopilot,
|
||||
REQUIRED_PRIOR,
|
||||
OPTIONAL_PRIOR,
|
||||
REQUIRED_PRIOR_WAIT_MS,
|
||||
COPILOT_REVIEW_BODY,
|
||||
priorReviewerStatus,
|
||||
priorReviewersReady,
|
||||
|
||||
@@ -40,7 +40,7 @@ env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
CLAUDE_UI_SCOPE: 'SCOPE: Focus ONLY on PraisonAIUI (src/praisonaiui, src/frontend, tests, docs). Do not expand into praisonaiagents or the monorepo unless the issue explicitly requires it.'
|
||||
CLAUDE_FINAL_PHASES_COMPACT: "**Phase 1: Review per AGENTS.md** (lazy imports, backward compat, import-time <200ms; SDK/product value gate — genuine UI value, correct UI vs praisonaiagents layering, no scope creep)\n**Phase 2: FIX valid issues** found by prior reviewers — push to THIS branch\n**Phase 3: Final verdict** — approve PR if ready, else request changes"
|
||||
CLAUDE_FINAL_PHASES_DETAILED: "**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions (lazy imports, import-time <200ms)\n4. SDK/product value: review whether the change genuinely adds value — stronger PraisonAIUI UX and correct layering (UI/frontend here; core agent logic belongs in praisonaiagents). Reject scope creep; request changes or recommend closing if value is unclear\n\n**Phase 2: FIX Valid Issues**\n5. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n6. Push all code fixes directly to THIS branch (do NOT create a new PR)\n7. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n8. If all issues are resolved, approve the PR / close the Issue\n9. If blocking issues remain, request changes / leave clear action items"
|
||||
CLAUDE_FINAL_PHASES_DETAILED: "**Phase 1: Review per AGENTS.md**\n1. Protocol-driven: check heavy implementations vs core SDK\n2. Backward compatible: ensure zero feature regressions\n3. Performance: no hot-path regressions (lazy imports, import-time <200ms)\n4. Product value: review in depth whether the change genuinely adds PraisonAIUI UX value — never add features for the sake of adding them. It must strengthen the UI (simpler, more user-friendly, robust). Reject scope creep; request changes or recommend closing if value is unclear\n5. Layering: UI/frontend/server features here; core agent runtime belongs in praisonaiagents. Agent-callable tools → PraisonAI-Tools\n6. Do not bloat server.py or create_app() with new params — prefer existing YAML config, plugins, and feature modules; reject knobs/exports with no live consumer\n\n**Phase 2: FIX Valid Issues**\n7. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix\n8. Push all code fixes directly to THIS branch (do NOT create a new PR)\n9. Comment a summary of exact files modified and what you skipped\n\n**Phase 3: Final Verdict**\n10. If all issues are resolved, approve the PR / close the Issue\n11. If blocking issues remain, request changes / leave clear action items"
|
||||
|
||||
jobs:
|
||||
# ================================================================
|
||||
@@ -565,7 +565,7 @@ jobs:
|
||||
|
||||
const finalBody = mergeGate.FINAL_CLAUDE_REVIEW_BODY;
|
||||
const staleOpts = { allowStaleRepost: true, skipCopilot: true, optionalWaitMs: 0 };
|
||||
const missingOpts = { allowCopilotTimeout: true, optionalWaitMs: 0 };
|
||||
const missingOpts = { skipCopilot: true, optionalWaitMs: 0 };
|
||||
|
||||
if (hasFinal && isStale) {
|
||||
const gate = mergeGate.shouldSkipStaleFinalRecovery(comments, headPushedAt, headPusher);
|
||||
@@ -654,7 +654,7 @@ jobs:
|
||||
|
||||
const opts = hasFinal && isStale
|
||||
? { allowStaleRepost: true, skipCopilot: true, optionalWaitMs: 0 }
|
||||
: { allowCopilotTimeout: true, optionalWaitMs: 0 };
|
||||
: { skipCopilot: true, optionalWaitMs: 0 };
|
||||
const result = await chain.maybeTriggerClaudeFinal(
|
||||
github, owner, repo, pr.number,
|
||||
mergeGate.FINAL_CLAUDE_REVIEW_BODY, core, opts
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Bot PR Recovery
|
||||
|
||||
# Belt-and-braces: kick CodeRabbit/Qodo when pull_request:opened never fired for bot PRs.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/15 * * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'Specific PR number to kick (optional)'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
recover:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Find and kick stalled bot PRs
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
PR_NUMBER_INPUT: ${{ github.event.inputs.pr_number }}
|
||||
with:
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const path = require('path');
|
||||
const chain = require(path.join(process.env.GITHUB_WORKSPACE, '.github/scripts/bot-pr-review-chain.js'));
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const manual = (process.env.PR_NUMBER_INPUT || '').trim();
|
||||
const prNumber = manual ? parseInt(manual, 10) : null;
|
||||
if (manual && Number.isNaN(prNumber)) {
|
||||
throw new Error(`Invalid pr_number: ${manual}`);
|
||||
}
|
||||
await chain.recoverStalledBotPrs(github, owner, repo, { prNumber }, core);
|
||||
@@ -280,7 +280,7 @@ jobs:
|
||||
model: claude-opus-4-8
|
||||
trigger_phrase: '@claude-merge-gate-internal'
|
||||
direct_prompt: |
|
||||
You are the MERGE GATE reviewer for PraisonAI PR #${{ matrix.pr_number }}.
|
||||
You are the MERGE GATE reviewer for PraisonAIUI PR #${{ matrix.pr_number }}.
|
||||
READ-ONLY: do not modify code, commit, push, rebase, or create branches.
|
||||
|
||||
CRITICAL OUTPUT RULE:
|
||||
@@ -295,12 +295,13 @@ jobs:
|
||||
Review checklist:
|
||||
1. Read AGENTS.md and the PR diff on the current branch.
|
||||
2. Read all PR comments and reviews (FINAL architecture reviewer / Lead Engineer must have run).
|
||||
3. Confirm SDK value gate: change strengthens praisonaiagents (no scope creep).
|
||||
4. Do not bloat the Agent class with additional params — only if absolutely required; we already support many params.
|
||||
5. If agent.py adds >100 lines or new Agent __init__ params, respond MERGE_GATE_VERDICT: BLOCK (manual review required).
|
||||
6. BLOCK if labels security/breaking-change/needs-manual-review/release, sensitive paths (.github/workflows, auth, pyproject.toml), secrets in diff, PR >800 lines or >30 files, or SDK changes without tests. Label `merge-gate-ci-only` exempts CI-only changes under `.github/workflows/`, `.github/actions/`, or merge-gate scripts.
|
||||
7. Confirm no CHANGES_REQUESTED reviews and CI green on HEAD ${{ matrix.head_sha }}.
|
||||
8. If ready to merge: MERGE_GATE_VERDICT: APPROVE. Otherwise: MERGE_GATE_VERDICT: BLOCK with reasons.
|
||||
3. Confirm product value gate: change strengthens PraisonAIUI UX (no scope creep). The aim is a LIGHTWEIGHT AND POWERFUL package — BLOCK changes that add params/modules/exports duplicating existing UI capabilities (YAML config, plugins, feature modules, existing server hooks), or add knobs/fields with no live consumer, i.e. scope creep for the sake of it.
|
||||
4. Layering routing: BLOCK if core agent logic was added here instead of praisonaiagents; BLOCK if agent-callable tools were added here instead of PraisonAI-Tools.
|
||||
5. Do not bloat server.py or create_app() with additional params — only if absolutely required; prefer existing config/plugin patterns.
|
||||
6. If server.py or create_app() adds >100 lines or new top-level params without a clear live consumer, respond MERGE_GATE_VERDICT: BLOCK (manual review required).
|
||||
7. BLOCK if labels security/breaking-change/needs-manual-review/release, sensitive paths (.github/workflows, auth, pyproject.toml), secrets in diff, PR >800 lines or >30 files, or product code changes without tests. Label `merge-gate-ci-only` exempts CI-only changes under `.github/workflows/`, `.github/actions/`, or merge-gate scripts.
|
||||
8. Confirm no CHANGES_REQUESTED reviews and CI green on HEAD ${{ matrix.head_sha }}.
|
||||
9. If ready to merge: MERGE_GATE_VERDICT: APPROVE. Otherwise: MERGE_GATE_VERDICT: BLOCK with reasons.
|
||||
|
||||
Do not approve or merge via GitHub UI/API — only post the verdict comment.
|
||||
disallowed_tools: |
|
||||
|
||||
@@ -349,6 +349,9 @@ jobs:
|
||||
SCOPE: src/praisonaiui, src/frontend, tests, docs only.
|
||||
|
||||
**Phase 1 — Review** architecture, security, tests, backward compat, import-time budget.
|
||||
Product value gate: change must strengthen PraisonAIUI UX (no scope creep). BLOCK params/modules/exports
|
||||
duplicating existing UI capabilities or knobs with no live consumer. Core agent logic belongs in
|
||||
praisonaiagents; agent-callable tools in PraisonAI-Tools — not here.
|
||||
**Phase 2 — FIX (mandatory)** implement every VALID issue on THIS branch. Push commits before Phase 3.
|
||||
Do NOT ask the author to fix. Do NOT open a new PR. Do NOT claim fork restrictions unless is_fork is true.
|
||||
**Phase 3 — Verdict** approve only after fixes are pushed and tests pass; else request changes with specifics.
|
||||
@@ -403,6 +406,11 @@ jobs:
|
||||
STEP 1 — READ GUIDELINES
|
||||
═══════════════════════════════════════════════════════════════
|
||||
Read `AGENTS.md` and any `docs/principles.md`. Key invariants:
|
||||
• AIM: This package must stay LIGHTWEIGHT AND POWERFUL. Do a CRITICAL REVIEW at each stage
|
||||
(issue triage, fix planning, PR review/implementation) and reject scope creep for the sake
|
||||
of adding features. Prefer existing YAML config, plugins, feature modules, and server hooks
|
||||
over new server.py/create_app() params or exports. Even when fixing a raised issue, keep the
|
||||
fix minimal — do NOT expand surface area or add knobs with no live consumer.
|
||||
• Cold import of `praisonaiui` must stay under 200 ms; module count must not grow.
|
||||
• Heavy / optional deps (`mcp`, `langchain`, `llama_index`, `semantic_kernel`,
|
||||
`slack_sdk`, `discord.py`, `botbuilder.core`, `openai`, `anthropic`, `mistralai`,
|
||||
@@ -438,6 +446,12 @@ jobs:
|
||||
|
||||
CONCEPTUAL CHECKS (if ANY fails — add a comment, do NOT open a PR):
|
||||
• Does it duplicate an existing public symbol in `src/praisonaiui/__init__.py`?
|
||||
• Does it duplicate existing UI capability via YAML config, plugins, or feature modules
|
||||
instead of adding new server params? → Reject as scope creep; prefer existing patterns.
|
||||
• Does it add core agent runtime logic here instead of praisonaiagents, or agent-callable
|
||||
tools here instead of PraisonAI-Tools? → Route externally; do not implement here.
|
||||
• Does it bloat server.py or create_app() with new params without a clear live consumer?
|
||||
→ Prefer existing config/plugin hooks; only add params if absolutely required.
|
||||
• Does it add new class-first message types (`class Ask*Message`, `class *Message`
|
||||
with a verb in the name)? → Use a verb-first helper instead.
|
||||
• Does it add a new hard dependency to `pyproject.toml` `[project] dependencies`?
|
||||
|
||||
@@ -12,7 +12,7 @@ on:
|
||||
- cron: '0 */6 * * *'
|
||||
|
||||
concurrency:
|
||||
group: merge-conflict-scan-${{ github.repository }}
|
||||
group: merge-conflict-scan-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
@@ -42,7 +42,9 @@ jobs:
|
||||
'@claude this PR has merge conflicts with `main`.',
|
||||
'Please rebase onto latest `main`, resolve conflicts (keep this PR\'s intent, merge in newer main logic),',
|
||||
'run targeted tests, and force-push with `--force-with-lease`.',
|
||||
'Comment which files you resolved.'
|
||||
'Comment which files you resolved.',
|
||||
'Do not bloat server.py or create_app() with new params — prefer existing YAML config, plugins, and feature modules unless absolutely required.',
|
||||
'Keep the resolution minimal — the aim is a lightweight and powerful UI package; do not introduce scope creep while resolving conflicts.',
|
||||
].join(' ');
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
@@ -83,16 +85,6 @@ jobs:
|
||||
};
|
||||
}
|
||||
|
||||
function hasFinalClaudeReviewTrigger(comments) {
|
||||
return comments.some((c) => {
|
||||
const body = (c.body || '').toLowerCase();
|
||||
if (!AUTO_ACTORS.includes(c.user.login)) return false;
|
||||
if (!body.includes('@claude')) return false;
|
||||
if (body.includes('merge conflict')) return false;
|
||||
return body.includes('final architecture reviewer') || body.includes('lead engineer');
|
||||
});
|
||||
}
|
||||
|
||||
function hasRecentAutoComment(comments) {
|
||||
const cutoff = Date.now() - COOLDOWN_MS;
|
||||
return comments.some((c) => {
|
||||
@@ -143,10 +135,6 @@ jobs:
|
||||
return { skipped: true, reason: 'recent auto comment' };
|
||||
}
|
||||
|
||||
if (!hasFinalClaudeReviewTrigger(comments)) {
|
||||
return { skipped: true, reason: 'awaiting final claude review trigger' };
|
||||
}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Knowledge Embedding Model Configuration
|
||||
|
||||
Knowledge / RAG search uses vector **embeddings**. The default embedding model is
|
||||
`text-embedding-3-small`. Many OpenAI keys or projects do not have access to that
|
||||
model (organization tier, model allowlist, or regional restrictions), which caused
|
||||
knowledge search to silently degrade to local substring matching.
|
||||
|
||||
PraisonAIUI now makes this failure mode configurable and observable.
|
||||
|
||||
## Configure the embedding model
|
||||
|
||||
Set an accessible model via environment variable:
|
||||
|
||||
```bash
|
||||
export AIUI_EMBEDDING_MODEL=text-embedding-ada-002
|
||||
```
|
||||
|
||||
`OPENAI_EMBEDDING_MODEL` is also honoured as a fallback if `AIUI_EMBEDDING_MODEL`
|
||||
is not set.
|
||||
|
||||
## Fallback chain
|
||||
|
||||
The OpenAI-compatible `/v1/embeddings` endpoint tries the requested (or default)
|
||||
model first, then each model in the fallback chain if the primary returns a
|
||||
`model_not_found` / 403 access error:
|
||||
|
||||
```bash
|
||||
export AIUI_EMBEDDING_FALLBACK_MODELS="text-embedding-ada-002,text-embedding-3-small"
|
||||
```
|
||||
|
||||
If every candidate is inaccessible, the endpoint returns `503` with an actionable
|
||||
`model_not_found` error instead of a generic `500`.
|
||||
|
||||
## Observing degraded search
|
||||
|
||||
`GET /api/knowledge/status` reports embedding availability:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 12,
|
||||
"backend": "SDKKnowledgeManager",
|
||||
"status": "degraded",
|
||||
"embedding": {"available": false, "error": "Project does not have access to model `text-embedding-3-small`"},
|
||||
"warnings": ["Embedding model unavailable; knowledge search degraded to local text matching. Set AIUI_EMBEDDING_MODEL to an accessible model."]
|
||||
}
|
||||
```
|
||||
|
||||
`POST /api/knowledge/search` indicates whether results came from vector search or a
|
||||
degraded local fallback:
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [],
|
||||
"count": 0,
|
||||
"search_mode": "fallback",
|
||||
"warning": "Vector search unavailable: Project does not have access to model text-embedding-3-small. Set AIUI_EMBEDDING_MODEL=text-embedding-ada-002 or enable embedding models in your provider project."
|
||||
}
|
||||
```
|
||||
|
||||
A dashboard can show a warning banner whenever `search_mode == "fallback"` or
|
||||
`embedding.available == false`.
|
||||
|
||||
## Model reference
|
||||
|
||||
| Model | Notes |
|
||||
|-------|-------|
|
||||
| `text-embedding-3-small` | Default — best cost/quality when available |
|
||||
| `text-embedding-3-large` | Higher quality — may also be restricted |
|
||||
| `text-embedding-ada-002` | Legacy — widely available on older projects |
|
||||
@@ -0,0 +1,69 @@
|
||||
# Application Design Protocol (ADP)
|
||||
|
||||
Declarative UI design standard for describing application layout, navigation, components, and theming from YAML or JSON manifests.
|
||||
|
||||
**Status:** v1alpha1 research draft — see [implementation-status.md](./implementation-status.md) for what is implemented today.
|
||||
|
||||
## Documents
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [Research report](../application-design-protocol-report.md) | 46 standards compared; YAML vs JSON debate; design principles |
|
||||
| [spec.md](./spec.md) | Normative specification (draft) |
|
||||
| [validation-rules.md](./validation-rules.md) | Cross-reference rules R1–R30 |
|
||||
| [aiui-mapping.md](./aiui-mapping.md) | PraisonAIUI field mapping |
|
||||
| [implementation-status.md](./implementation-status.md) | Feature implementation tracker |
|
||||
|
||||
## Schemas
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| [schema/application.schema.json](./schema/application.schema.json) | Root Application kind |
|
||||
| [schema/component-catalog.schema.json](./schema/component-catalog.schema.json) | Catalog meta-schema |
|
||||
| [schema/theme.schema.json](./schema/theme.schema.json) | Theme preset or DTCG tokens |
|
||||
| [catalogs/default.catalog.json](./catalogs/default.catalog.json) | Default component and layout catalog |
|
||||
|
||||
## Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [examples/minimal.application.yaml](./examples/minimal.application.yaml) | Smallest valid app |
|
||||
| [examples/docs-site.application.yaml](./examples/docs-site.application.yaml) | Full docs site |
|
||||
| [examples/landing-page.application.yaml](./examples/landing-page.application.yaml) | Zone-based layout |
|
||||
|
||||
## Validate locally
|
||||
|
||||
```bash
|
||||
pip install jsonschema pyyaml
|
||||
pytest tests/unit/test_adp_schemas.py -v
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```yaml
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: my-app
|
||||
version: "1.0.0"
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "My App"
|
||||
theme:
|
||||
preset: zinc
|
||||
darkMode: true
|
||||
templates:
|
||||
docs:
|
||||
layout: TwoColumnLayout
|
||||
slots:
|
||||
main: { type: DocContent }
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
```
|
||||
@@ -0,0 +1,138 @@
|
||||
# PraisonAIUI → ADP Field Mapping
|
||||
|
||||
Field-by-field mapping from current `aiui.template.yaml` (`Config` model in `src/praisonaiui/schema/models.py`) to ADP `Application` spec.
|
||||
|
||||
**Compatibility:** Flat `aiui.template.yaml` files work with the PraisonAIUI compiler today. ADP envelope format is **planned** — see [implementation-status.md](./implementation-status.md).
|
||||
|
||||
---
|
||||
|
||||
## Envelope (new in ADP)
|
||||
|
||||
| ADP field | PraisonAIUI today | Status | Change |
|
||||
|-----------|-------------------|--------|--------|
|
||||
| `schemaVersion: 1` | `schemaVersion: 1` | Implemented | Compatible |
|
||||
| `apiVersion: adp.io/v1alpha1` | Not present | Planned | Add when envelope parser lands |
|
||||
| `kind: Application` | Not present | Planned | Add when envelope parser lands |
|
||||
| `metadata.name` | Not present | Planned | Derive from `site.title` slug |
|
||||
| `metadata.version` | Not present | Planned | Recommend SemVer |
|
||||
|
||||
## Top-level → spec wrapper
|
||||
|
||||
| PraisonAIUI (flat) | ADP (envelope) | Status | Change |
|
||||
|--------------------|----------------|--------|--------|
|
||||
| `site:` | `spec.site:` | Planned | Wrap under `spec` |
|
||||
| `site.theme` | `spec.theme:` | Planned | Promote theme to spec level |
|
||||
| `navigation:` | `spec.navigation:` | Partial | Tabs compiled; not rendered |
|
||||
| `navbar:` | `spec.navbar:` | Partial | Compiled; not rendered |
|
||||
| `footer:` | `spec.footer:` | Partial | Compiled; not rendered |
|
||||
| `search:` | `spec.search:` | Partial | Compiled; not rendered |
|
||||
| `content:` | `spec.content:` | Implemented | Same |
|
||||
| `components:` | `spec.components:` | Implemented | Same |
|
||||
| `templates:` | `spec.templates:` | Implemented | Same |
|
||||
| `routes:` | `spec.routes:` | Partial | Compiled; routing not honoured |
|
||||
| `dependencies:` | `spec.dependencies:` | Implemented | Same |
|
||||
| `logo:` (top-level) | Future ADP field | Implemented | Top-level on `Config`, not under `site` |
|
||||
| `style:`, `chat:`, `auth:`, `dashboard:` | Not in ADP v1alpha1 | N/A | Separate kinds in future |
|
||||
|
||||
## Navigation fields
|
||||
|
||||
| Field | PraisonAIUI | ADP | Status | Notes |
|
||||
|-------|-------------|-----|--------|-------|
|
||||
| `navigation.tabs[]` | Mintlify-style tabs | `spec.navigation.tabs` | Partial | Compiled, not rendered |
|
||||
| `content.docs.nav.mode` | `NavConfig.mode` | Same | Gap | Defined but unused by compiler |
|
||||
| Top-level `navigation.mode` | Not present | Removed from ADP | N/A | Use `content.docs.nav.mode` instead |
|
||||
|
||||
## Theme fields
|
||||
|
||||
| Field | PraisonAIUI | ADP | Status |
|
||||
|-------|-------------|-----|--------|
|
||||
| `site.theme.preset` | Implemented | `spec.theme.preset` | Planned (envelope) |
|
||||
| `site.theme.radius` | Implemented | Same | Same |
|
||||
| `site.theme.darkMode` | Implemented | Same | Same |
|
||||
| `theme.tokens` | Not present | DTCG file ref | Planned |
|
||||
|
||||
## Component fields
|
||||
|
||||
| Field | PraisonAIUI | ADP | Status |
|
||||
|-------|-------------|-----|--------|
|
||||
| `components.{id}.type` | String literal | Catalog allowlist | Planned (R6) |
|
||||
| `components.{id}.props` | `dict[str, Any]` | Catalog schema | Planned (R7) |
|
||||
|
||||
## Template fields
|
||||
|
||||
| Field | PraisonAIUI | ADP | Status |
|
||||
|-------|-------------|-----|--------|
|
||||
| `templates.{id}.layout` | String | Catalog enum | Same values |
|
||||
| `templates.{id}.slots` | `{ ref \| type }` | Same | Same |
|
||||
| `templates.{id}.zones` | Widget arrays | Same | Same |
|
||||
| Slots + zones on same template | Allowed | R13: one or the other | Stricter in ADP |
|
||||
|
||||
## Route fields
|
||||
|
||||
| Field | PraisonAIUI | ADP | Status |
|
||||
|-------|-------------|-----|--------|
|
||||
| `routes[].match` | Glob string | Same | Same |
|
||||
| `routes[].template` | Template key | Same | Same |
|
||||
| Runtime route resolution | Not implemented | R28 required | Gap |
|
||||
|
||||
## Migration example
|
||||
|
||||
### Today (PraisonAIUI)
|
||||
|
||||
```yaml
|
||||
schemaVersion: 1
|
||||
|
||||
site:
|
||||
title: "My Docs"
|
||||
theme:
|
||||
preset: zinc
|
||||
|
||||
templates:
|
||||
docs:
|
||||
layout: TwoColumnLayout
|
||||
slots:
|
||||
main: { type: DocContent }
|
||||
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
```
|
||||
|
||||
### Target (ADP)
|
||||
|
||||
```yaml
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: my-docs
|
||||
version: "1.0.0"
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "My Docs"
|
||||
theme:
|
||||
preset: zinc
|
||||
templates:
|
||||
docs:
|
||||
layout: TwoColumnLayout
|
||||
slots:
|
||||
main: { type: DocContent }
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
```
|
||||
|
||||
## Breaking vs compatible changes
|
||||
|
||||
| Change | Breaking? | Mitigation |
|
||||
|--------|-----------|------------|
|
||||
| Add envelope fields | No | Envelope parser normalises to flat `Config` |
|
||||
| Catalog validation | Soft break | Warn first, error in strict mode |
|
||||
| Route runtime resolution | No (fix) | Frontend change |
|
||||
| R13 slots/zones exclusivity | Soft break | Warn on violation |
|
||||
| Remove chat/dashboard from ADP | No | Separate kinds in future |
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"name": "adp-default-catalog",
|
||||
"version": "1.0.0",
|
||||
"description": "Default ADP component and layout catalog for PraisonAIUI",
|
||||
"components": {
|
||||
"Header": {
|
||||
"description": "Site header with logo and navigation",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"logoText": { "type": "string" },
|
||||
"logoHref": { "type": "string", "default": "/" }
|
||||
},
|
||||
"required": ["logoText"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"aria": { "role": "banner" },
|
||||
"slots": ["header"]
|
||||
},
|
||||
"Footer": {
|
||||
"description": "Site footer",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"aria": { "role": "contentinfo" },
|
||||
"slots": ["footer"]
|
||||
},
|
||||
"DocsSidebar": {
|
||||
"description": "Documentation sidebar navigation",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": { "type": "string", "enum": ["docs-nav", "manual"], "default": "docs-nav" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"aria": { "role": "navigation" },
|
||||
"slots": ["left", "leftSidebar"]
|
||||
},
|
||||
"DocContent": {
|
||||
"description": "Main documentation content area",
|
||||
"props": { "type": "object", "additionalProperties": false },
|
||||
"aria": { "role": "main" },
|
||||
"slots": ["main"]
|
||||
},
|
||||
"Toc": {
|
||||
"description": "Table of contents for current page",
|
||||
"props": { "type": "object", "additionalProperties": false },
|
||||
"aria": { "role": "navigation" },
|
||||
"slots": ["right", "rightSidebar"]
|
||||
},
|
||||
"HeroBanner": {
|
||||
"description": "Hero banner with title and CTA",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": { "type": "string" },
|
||||
"subtitle": { "type": "string" },
|
||||
"ctaLabel": { "type": "string" },
|
||||
"ctaHref": { "type": "string" }
|
||||
},
|
||||
"required": ["title"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"slots": ["hero"]
|
||||
},
|
||||
"SocialLinks": {
|
||||
"description": "Social media link row",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"platform": { "type": "string" },
|
||||
"href": { "type": "string" }
|
||||
},
|
||||
"required": ["platform", "href"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"slots": ["footer"]
|
||||
},
|
||||
"Copyright": {
|
||||
"description": "Copyright notice",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"text": { "type": "string" }
|
||||
},
|
||||
"required": ["text"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"slots": ["footer"]
|
||||
}
|
||||
},
|
||||
"layouts": {
|
||||
"ThreeColumnLayout": {
|
||||
"description": "Header + left sidebar + main + right TOC + footer",
|
||||
"slots": ["header", "left", "main", "right", "footer"]
|
||||
},
|
||||
"TwoColumnLayout": {
|
||||
"description": "Sidebar + main content",
|
||||
"slots": ["left", "main"]
|
||||
},
|
||||
"CenteredLayout": {
|
||||
"description": "Centred single column",
|
||||
"slots": ["main"]
|
||||
},
|
||||
"FullWidthLayout": {
|
||||
"description": "Full-width content",
|
||||
"slots": ["header", "main", "footer"]
|
||||
},
|
||||
"FlexibleLayout": {
|
||||
"description": "WordPress-style zone grid",
|
||||
"zones": ["header", "topNav", "hero", "leftSidebar", "main", "rightSidebar", "bottomNav", "footer"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: praisonaiui-docs
|
||||
version: "1.0.0"
|
||||
labels:
|
||||
generator: praisonaiui
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "PraisonAIUI Docs"
|
||||
description: "Documentation for PraisonAIUI - YAML-driven website generator"
|
||||
ui: shadcn
|
||||
|
||||
theme:
|
||||
preset: zinc
|
||||
radius: md
|
||||
darkMode: true
|
||||
|
||||
navigation:
|
||||
tabs:
|
||||
- tab: "Documentation"
|
||||
groups:
|
||||
- group: "Getting Started"
|
||||
icon: rocket
|
||||
prefix: getting-started
|
||||
pages:
|
||||
- index
|
||||
- getting-started/installation
|
||||
- getting-started/quickstart
|
||||
- group: "Core Concepts"
|
||||
icon: lightbulb
|
||||
prefix: concepts
|
||||
pages:
|
||||
- concepts/architecture
|
||||
- concepts/configuration
|
||||
- concepts/templates
|
||||
- tab: "API Reference"
|
||||
groups:
|
||||
- group: "API Reference"
|
||||
icon: code
|
||||
prefix: api
|
||||
pages:
|
||||
- api/cli
|
||||
- api/python
|
||||
|
||||
navbar:
|
||||
primary:
|
||||
type: github
|
||||
href: "https://github.com/MervinPraison/PraisonAIUI"
|
||||
links:
|
||||
- label: "Submit Issues"
|
||||
href: "https://github.com/MervinPraison/PraisonAIUI/issues"
|
||||
|
||||
footer:
|
||||
socials:
|
||||
github: "https://github.com/MervinPraison/PraisonAI"
|
||||
links:
|
||||
- header: "Resources"
|
||||
items:
|
||||
- label: "GitHub"
|
||||
href: "https://github.com/MervinPraison/PraisonAI"
|
||||
|
||||
search:
|
||||
enabled: true
|
||||
provider: fusejs
|
||||
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
include:
|
||||
- "**/*.md"
|
||||
- "**/*.mdx"
|
||||
nav:
|
||||
mode: manual
|
||||
|
||||
components:
|
||||
header_main:
|
||||
type: Header
|
||||
props:
|
||||
logoText: "PraisonAIUI"
|
||||
footer_main:
|
||||
type: Footer
|
||||
props:
|
||||
text: "© 2026 PraisonAI"
|
||||
sidebar_docs:
|
||||
type: DocsSidebar
|
||||
props:
|
||||
source: docs-nav
|
||||
|
||||
templates:
|
||||
docs:
|
||||
layout: ThreeColumnLayout
|
||||
slots:
|
||||
header: { ref: header_main }
|
||||
left: { ref: sidebar_docs }
|
||||
main: { type: DocContent }
|
||||
footer: { ref: footer_main }
|
||||
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
@@ -0,0 +1,58 @@
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: colorful-landing
|
||||
version: "1.0.0"
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "Colorful Docs"
|
||||
description: "Zone-based landing page with hero and footer widgets"
|
||||
ui: shadcn
|
||||
|
||||
theme:
|
||||
preset: rose
|
||||
radius: xl
|
||||
darkMode: true
|
||||
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
nav:
|
||||
mode: auto
|
||||
collapsible: true
|
||||
|
||||
templates:
|
||||
docs:
|
||||
layout: FlexibleLayout
|
||||
zones:
|
||||
hero:
|
||||
- type: HeroBanner
|
||||
props:
|
||||
title: "Colorful Documentation"
|
||||
subtitle: "Vibrant, playful, and beautifully designed"
|
||||
ctaLabel: "Get Started"
|
||||
ctaHref: "/docs/getting-started/quickstart"
|
||||
rightSidebar:
|
||||
- type: Toc
|
||||
footer:
|
||||
- type: SocialLinks
|
||||
props:
|
||||
links:
|
||||
- platform: Twitter
|
||||
href: "https://twitter.com"
|
||||
- platform: GitHub
|
||||
href: "https://github.com"
|
||||
- type: Copyright
|
||||
props:
|
||||
text: "Made with love by Colorful Team"
|
||||
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
|
||||
a11y:
|
||||
skipToContent: true
|
||||
reduceMotion: false
|
||||
@@ -0,0 +1,32 @@
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: minimal-docs
|
||||
version: "1.0.0"
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "Minimal Docs"
|
||||
description: "Smallest valid ADP Application manifest"
|
||||
ui: shadcn
|
||||
|
||||
theme:
|
||||
preset: zinc
|
||||
radius: md
|
||||
darkMode: true
|
||||
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
|
||||
templates:
|
||||
docs:
|
||||
layout: TwoColumnLayout
|
||||
slots:
|
||||
main: { type: DocContent }
|
||||
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
@@ -0,0 +1,36 @@
|
||||
# ADP Implementation Status
|
||||
|
||||
Honest status of ADP v1alpha1 features against the PraisonAIUI reference implementation.
|
||||
|
||||
**Last updated:** August 2026
|
||||
|
||||
| Feature | Spec reference | Status | Notes |
|
||||
|---------|----------------|--------|-------|
|
||||
| JSON Schema validation in CLI | spec §6 | Planned | Schemas published; no `aiui validate --adp` yet |
|
||||
| ADP envelope parsing (`apiVersion/kind/spec`) | spec §3 | Planned | Flat `aiui.template.yaml` works today |
|
||||
| Component catalog validation (R6–R7) | spec §5 | Planned | Catalog split to `catalogs/default.catalog.json` |
|
||||
| `$yaml:` / `$json:` file includes | spec §7 | Planned | Not implemented in compiler |
|
||||
| Strict mode unknown-field rejection (R4) | validation-rules R4 | Planned | Pydantic allows extra fields on some models |
|
||||
| Route-driven template resolution (R28) | validation-rules R28 | Gap | Frontend hardcodes `templates.docs` |
|
||||
| Sidebar nav mode (R29) | validation-rules R29 | Gap | Filesystem-only sidebar today |
|
||||
| Catalog ARIA roles at render (R30) | validation-rules R30 | Gap | No catalog-driven a11y in renderer |
|
||||
| `navigation.tabs` rendering | spec §4.3 | Gap | Compiled to manifest but not consumed by React |
|
||||
| DTCG token file validation (R26) | validation-rules R26 | Planned | Preset shorthand works via Tailwind |
|
||||
| Published `$schema` URLs | README | Partial | Local schemas only; `https://adp.io/` is placeholder |
|
||||
|
||||
## What works today
|
||||
|
||||
- Flat `aiui.template.yaml` → Pydantic `Config` → compiler → JSON manifests → React SPA
|
||||
- Components, templates, routes, theme presets, content scanning
|
||||
- ADP example manifests validate against `application.schema.json` (standalone)
|
||||
- Unit tests in `tests/unit/test_adp_schemas.py`
|
||||
|
||||
## Migration path
|
||||
|
||||
1. Add envelope normalisation layer before `Config.model_validate`
|
||||
2. Wire `route-manifest.json` into frontend routing (R28)
|
||||
3. Honour `navigation.tabs` and `content.docs.nav.mode` (R29)
|
||||
4. Add `aiui validate --schema adp` using published JSON Schema
|
||||
5. Apply catalog ARIA hints at render time (R30)
|
||||
|
||||
See [aiui-mapping.md](./aiui-mapping.md) for field-level compatibility.
|
||||
@@ -0,0 +1,449 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "application.schema.json",
|
||||
"title": "ADP Application",
|
||||
"description": "Application Design Protocol — Application kind manifest",
|
||||
"type": "object",
|
||||
"required": ["schemaVersion", "apiVersion", "kind", "metadata", "spec"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^x-adp-": {}
|
||||
},
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"type": "integer",
|
||||
"const": 1,
|
||||
"description": "ADP envelope version"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
"const": "adp.io/v1alpha1"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"const": "Application"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^x-adp-": {}
|
||||
},
|
||||
"properties": {
|
||||
"name": { "type": "string", "minLength": 1 },
|
||||
"version": { "type": "string" },
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"type": "object",
|
||||
"required": ["site", "templates", "routes"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^x-adp-": {}
|
||||
},
|
||||
"properties": {
|
||||
"site": { "$ref": "#/$defs/site" },
|
||||
"theme": { "$ref": "#/$defs/theme" },
|
||||
"navigation": { "$ref": "#/$defs/navigation" },
|
||||
"navbar": { "$ref": "#/$defs/navbar" },
|
||||
"footer": { "$ref": "#/$defs/footer" },
|
||||
"search": { "$ref": "#/$defs/search" },
|
||||
"components": { "$ref": "#/$defs/components" },
|
||||
"templates": { "$ref": "#/$defs/templates" },
|
||||
"routes": { "$ref": "#/$defs/routes" },
|
||||
"content": { "$ref": "#/$defs/content" },
|
||||
"dependencies": { "$ref": "#/$defs/dependencies" },
|
||||
"seo": { "$ref": "#/$defs/seo" },
|
||||
"i18n": { "$ref": "#/$defs/i18n" },
|
||||
"a11y": { "$ref": "#/$defs/a11y" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"site": {
|
||||
"type": "object",
|
||||
"required": ["title"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^x-adp-": {}
|
||||
},
|
||||
"properties": {
|
||||
"title": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"ui": {
|
||||
"type": "string",
|
||||
"enum": ["shadcn", "mui", "chakra"],
|
||||
"default": "shadcn"
|
||||
},
|
||||
"routeBaseDocs": { "type": "string", "default": "/docs" },
|
||||
"customCss": { "type": "string" },
|
||||
"plugins": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"theme": {
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Preset theme",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"preset": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"zinc", "slate", "stone", "gray", "neutral",
|
||||
"red", "orange", "amber", "yellow", "lime", "green",
|
||||
"emerald", "teal", "cyan", "sky", "blue", "indigo",
|
||||
"violet", "purple", "fuchsia", "pink", "rose"
|
||||
]
|
||||
},
|
||||
"radius": {
|
||||
"type": "string",
|
||||
"enum": ["none", "sm", "md", "lg", "xl"]
|
||||
},
|
||||
"darkMode": { "type": "boolean" }
|
||||
},
|
||||
"required": ["preset"]
|
||||
},
|
||||
{
|
||||
"title": "DTCG token reference",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tokens": {
|
||||
"type": "string",
|
||||
"description": "Path or URL to DTCG 2025.10 compliant tokens.json"
|
||||
},
|
||||
"darkMode": { "type": "boolean" }
|
||||
},
|
||||
"required": ["tokens"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"slotRef": {
|
||||
"oneOf": [
|
||||
{ "type": "null" },
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["ref"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"ref": { "type": "string" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string" },
|
||||
"props": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"widget": {
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string" },
|
||||
"props": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": ["type"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string" },
|
||||
"props": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"type": "object",
|
||||
"required": ["layout"],
|
||||
"allOf": [
|
||||
{
|
||||
"oneOf": [
|
||||
{ "required": ["slots"] },
|
||||
{ "required": ["zones"] }
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"layout": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"ThreeColumnLayout",
|
||||
"TwoColumnLayout",
|
||||
"CenteredLayout",
|
||||
"FullWidthLayout",
|
||||
"FlexibleLayout"
|
||||
]
|
||||
},
|
||||
"slots": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/slotRef" }
|
||||
},
|
||||
"zones": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/widget" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": { "$ref": "#/$defs/template" }
|
||||
},
|
||||
"route": {
|
||||
"type": "object",
|
||||
"required": ["match", "template"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"match": { "type": "string" },
|
||||
"template": { "type": "string" },
|
||||
"slots": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/slotRef" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"routes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": { "$ref": "#/$defs/route" }
|
||||
},
|
||||
"navGroup": {
|
||||
"type": "object",
|
||||
"required": ["group"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"group": { "type": "string" },
|
||||
"icon": { "type": "string" },
|
||||
"prefix": { "type": "string" },
|
||||
"pages": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"navTab": {
|
||||
"type": "object",
|
||||
"required": ["tab"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tab": { "type": "string" },
|
||||
"url": { "type": "string" },
|
||||
"groups": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/navGroup" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tabs": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/navTab" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"navbar": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"primary": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": { "type": "string", "enum": ["button", "github"] },
|
||||
"label": { "type": "string" },
|
||||
"href": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["label", "href"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": { "type": "string" },
|
||||
"href": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"socials": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["header"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"header": { "type": "string" },
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["label", "href"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"label": { "type": "string" },
|
||||
"href": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": { "type": "boolean" },
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"enum": ["fusejs", "lunrjs", "pagefind"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"shadcn": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentSource": {
|
||||
"type": "object",
|
||||
"required": ["dir"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"dir": { "type": "string" },
|
||||
"include": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"indexFiles": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"nav": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"mode": { "type": "string", "enum": ["auto", "manual"] },
|
||||
"sort": { "type": "string", "enum": ["filesystem", "alpha", "date"] },
|
||||
"collapsible": { "type": "boolean" },
|
||||
"maxDepth": { "type": "integer", "minimum": 1, "maximum": 10 }
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": { "mode": { "const": "manual" } },
|
||||
"required": ["mode"]
|
||||
},
|
||||
"then": {
|
||||
"description": "Manual sidebar mode requires navigation.tabs at spec level"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"docs": { "$ref": "#/$defs/contentSource" },
|
||||
"blog": { "$ref": "#/$defs/contentSource" }
|
||||
}
|
||||
},
|
||||
"seo": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"titleTemplate": { "type": "string" },
|
||||
"defaultImage": { "type": "string" },
|
||||
"twitter": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"i18n": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"defaultLocale": { "type": "string" },
|
||||
"locales": { "type": "array", "items": { "type": "string" } },
|
||||
"rtlLocales": { "type": "array", "items": { "type": "string" } },
|
||||
"fallbackLocale": { "type": "string" },
|
||||
"translationsDir": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"a11y": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"skipToContent": { "type": "boolean" },
|
||||
"focusVisible": { "type": "boolean" },
|
||||
"reduceMotion": { "type": "boolean" },
|
||||
"ariaLabels": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "component-catalog.schema.json",
|
||||
"title": "ADP Component Catalog",
|
||||
"description": "Allowlisted component types for ADP Application manifests",
|
||||
"type": "object",
|
||||
"required": ["name", "version", "components"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^x-adp-": {}
|
||||
},
|
||||
"properties": {
|
||||
"name": { "type": "string" },
|
||||
"version": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"components": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/componentType" }
|
||||
},
|
||||
"layouts": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/layoutType" }
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"componentType": {
|
||||
"type": "object",
|
||||
"required": ["description", "props"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": { "type": "string" },
|
||||
"props": { "type": "object" },
|
||||
"aria": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"role": { "type": "string" },
|
||||
"label": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"slots": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"deprecated": { "type": "boolean" },
|
||||
"x-sunset": { "type": "string", "format": "date" }
|
||||
}
|
||||
},
|
||||
"layoutType": {
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Slot-based layout",
|
||||
"type": "object",
|
||||
"required": ["description", "slots"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": { "type": "string" },
|
||||
"slots": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"title": "Zone-based layout",
|
||||
"type": "object",
|
||||
"required": ["description", "zones"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"description": { "type": "string" },
|
||||
"zones": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "theme.schema.json",
|
||||
"title": "ADP Theme",
|
||||
"description": "Theme configuration — preset shorthand or DTCG 2025.10 token reference",
|
||||
"oneOf": [
|
||||
{
|
||||
"title": "Preset theme",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"preset": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"zinc", "slate", "stone", "gray", "neutral",
|
||||
"red", "orange", "amber", "yellow", "lime", "green",
|
||||
"emerald", "teal", "cyan", "sky", "blue", "indigo",
|
||||
"violet", "purple", "fuchsia", "pink", "rose"
|
||||
]
|
||||
},
|
||||
"radius": {
|
||||
"type": "string",
|
||||
"enum": ["none", "sm", "md", "lg", "xl"],
|
||||
"default": "md"
|
||||
},
|
||||
"darkMode": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": ["preset"]
|
||||
},
|
||||
{
|
||||
"title": "DTCG token reference",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"tokens": {
|
||||
"type": "string",
|
||||
"description": "Path or URL to DTCG 2025.10 compliant tokens.json"
|
||||
},
|
||||
"darkMode": { "type": "boolean" }
|
||||
},
|
||||
"required": ["tokens"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
# Application Design Protocol (ADP) — Normative Specification
|
||||
|
||||
**Version:** v1alpha1 (draft)
|
||||
**Status:** Draft — not yet normative
|
||||
**Date:** August 2026
|
||||
|
||||
This document uses [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) keywords: MUST, MUST NOT, SHOULD, MAY.
|
||||
|
||||
---
|
||||
|
||||
## 1. Scope
|
||||
|
||||
ADP defines a declarative format for describing **application UI design**: site metadata, theming, navigation, reusable components, page templates, and URL routing. It does NOT define:
|
||||
|
||||
- Server-side APIs or data models
|
||||
- Agent orchestration or workflow graphs
|
||||
- Infrastructure or deployment configuration
|
||||
- Executable code or templating logic in manifests
|
||||
|
||||
Implementations MUST treat ADP manifests as **data**, not code.
|
||||
|
||||
---
|
||||
|
||||
## 2. Terminology
|
||||
|
||||
| Term | Definition |
|
||||
|------|------------|
|
||||
| **Manifest** | A YAML or JSON document conforming to this spec |
|
||||
| **Application** | The root `kind: Application` manifest describing a complete UI |
|
||||
| **Component** | A named, reusable UI block with a `type` and `props` |
|
||||
| **Template** | A layout definition assigning components to slots or zones |
|
||||
| **Slot** | A named region within a layout (e.g. `header`, `main`) |
|
||||
| **Zone** | An ordered list of widgets in a slot (WordPress-style) |
|
||||
| **Route** | A URL pattern mapping to a template |
|
||||
| **Catalog** | Published JSON Schema defining allowed component types |
|
||||
| **Compiler** | Tool that validates and emits runtime JSON manifests |
|
||||
| **Renderer** | Runtime that consumes compiled manifests and renders UI |
|
||||
|
||||
---
|
||||
|
||||
## 3. Document envelope
|
||||
|
||||
Every ADP manifest MUST include:
|
||||
|
||||
```yaml
|
||||
schemaVersion: 1 # Integer; ADP envelope version
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application # Application | Theme | ComponentCatalog (future)
|
||||
|
||||
metadata:
|
||||
name: string # Required; unique identifier
|
||||
version: string # SHOULD use SemVer
|
||||
labels: object # MAY contain key-value pairs
|
||||
annotations: object # MAY contain tooling metadata
|
||||
|
||||
spec: object # Required; kind-specific payload
|
||||
```
|
||||
|
||||
### 3.1 Versioning policy
|
||||
|
||||
- `schemaVersion` — integer bumped on breaking envelope changes
|
||||
- `apiVersion` — string group/version (e.g. `adp.io/v1alpha1`, `adp.io/v1`)
|
||||
- `metadata.version` — application SemVer (independent of protocol version)
|
||||
- Breaking changes MUST increment `apiVersion` (e.g. v1alpha1 → v1)
|
||||
- Deprecated fields MUST include `deprecated: true` and SHOULD include `x-sunset` date
|
||||
|
||||
### 3.2 Authoring formats
|
||||
|
||||
Manifests MAY be authored as YAML (`.application.yaml`) or JSON (`.application.json`). Semantics MUST be identical. Runtimes MUST NOT consume YAML directly; compilers MUST normalise to JSON before validation.
|
||||
|
||||
---
|
||||
|
||||
## 4. Application spec
|
||||
|
||||
An `Application` spec MUST contain at minimum:
|
||||
|
||||
| Field | Required | Description |
|
||||
|-------|----------|-------------|
|
||||
| `site.title` | YES | Application display name |
|
||||
| `templates` | YES | At least one template |
|
||||
| `routes` | YES | At least one route |
|
||||
|
||||
All other fields are OPTIONAL.
|
||||
|
||||
### 4.1 Site (`spec.site`)
|
||||
|
||||
```yaml
|
||||
site:
|
||||
title: string # Required
|
||||
description: string # Optional
|
||||
ui: shadcn # UI framework hint; default shadcn
|
||||
routeBaseDocs: /docs # Base path for documentation routes
|
||||
```
|
||||
|
||||
### 4.2 Theme (`spec.theme`)
|
||||
|
||||
Theme MAY use preset shorthand or reference a DTCG token file:
|
||||
|
||||
```yaml
|
||||
theme:
|
||||
preset: zinc # Tailwind-style preset name
|
||||
radius: md # none | sm | md | lg | xl
|
||||
darkMode: true
|
||||
# OR
|
||||
tokens: $yaml: ./theme.tokens.json # DTCG 2025.10 file
|
||||
```
|
||||
|
||||
### 4.3 Navigation
|
||||
|
||||
ADP separates **top tab bar** from **sidebar source**:
|
||||
|
||||
```yaml
|
||||
navigation:
|
||||
tabs: # Top tab bar (Mintlify-style)
|
||||
- tab: Documentation
|
||||
groups:
|
||||
- group: Getting Started
|
||||
pages: [index, getting-started/installation]
|
||||
|
||||
content:
|
||||
docs:
|
||||
dir: ./docs
|
||||
nav:
|
||||
mode: auto # auto | manual — sidebar source
|
||||
sort: filesystem
|
||||
collapsible: true
|
||||
maxDepth: 4
|
||||
```
|
||||
|
||||
- **`navigation.tabs`** — top-level tab bar with grouped pages
|
||||
- **`content.docs.nav.mode: auto`** — sidebar generated from filesystem scan
|
||||
- **`content.docs.nav.mode: manual`** — sidebar driven by `navigation.tabs` groups
|
||||
- Renderers MUST honour `content.docs.nav.mode` when building sidebar (R29)
|
||||
|
||||
### 4.4 Components (`spec.components`)
|
||||
|
||||
```yaml
|
||||
components:
|
||||
header_main:
|
||||
type: Header # MUST exist in component catalog
|
||||
props:
|
||||
logoText: PraisonAIUI
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Component IDs MUST be unique within the manifest
|
||||
- `type` MUST match an entry in the published component catalog
|
||||
- `props` MUST validate against the catalog JSON Schema for that type
|
||||
|
||||
### 4.5 Templates (`spec.templates`)
|
||||
|
||||
```yaml
|
||||
templates:
|
||||
docs:
|
||||
layout: ThreeColumnLayout
|
||||
slots:
|
||||
header: { ref: header_main }
|
||||
left: { ref: sidebar_docs }
|
||||
main: { type: DocContent }
|
||||
right: null # Explicitly hide slot
|
||||
|
||||
landing:
|
||||
layout: FlexibleLayout
|
||||
zones:
|
||||
hero:
|
||||
- type: HeroBanner
|
||||
props: { title: "Welcome" }
|
||||
```
|
||||
|
||||
Rules:
|
||||
- `layout` MUST be a known layout type in the catalog
|
||||
- Each slot value MUST be `{ ref: id }`, `{ type: ComponentType }`, or `null`
|
||||
- Zones MUST use zone names valid for `FlexibleLayout`
|
||||
- A template MUST declare either `slots` or `zones`, not both (R13)
|
||||
|
||||
### 4.6 Routes (`spec.routes`)
|
||||
|
||||
```yaml
|
||||
routes:
|
||||
- match: /docs/changelog
|
||||
template: changelog
|
||||
slots: # Optional per-route slot overrides
|
||||
right: null
|
||||
- match: /docs/**
|
||||
template: docs
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Routes MUST be ordered most-specific-first
|
||||
- `match` MUST be a glob pattern
|
||||
- `template` MUST reference a key in `spec.templates`
|
||||
- Renderers MUST resolve routes at runtime using compiled `route-manifest.json`
|
||||
|
||||
### 4.7 Content (`spec.content`)
|
||||
|
||||
```yaml
|
||||
content:
|
||||
docs:
|
||||
dir: ./docs
|
||||
include: ["**/*.md", "**/*.mdx"]
|
||||
exclude: []
|
||||
nav:
|
||||
mode: auto
|
||||
sort: filesystem
|
||||
collapsible: true
|
||||
maxDepth: 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Component catalog
|
||||
|
||||
Implementations MUST publish a `component-catalog.schema.json` (meta-schema) and a catalog instance such as [`catalogs/default.catalog.json`](./catalogs/default.catalog.json):
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "adp-default-catalog",
|
||||
"version": "1.0.0",
|
||||
"components": {
|
||||
"Header": {
|
||||
"description": "Site header with logo and navigation",
|
||||
"props": {
|
||||
"type": "object",
|
||||
"properties": { "logoText": { "type": "string" } },
|
||||
"required": ["logoText"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"aria": { "role": "banner" },
|
||||
"slots": ["header"]
|
||||
}
|
||||
},
|
||||
"layouts": {
|
||||
"ThreeColumnLayout": {
|
||||
"description": "Header + sidebar + main + TOC + footer",
|
||||
"slots": ["header", "left", "main", "right", "footer"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Custom component types MUST be registered via plugin schema layers. Unknown types MUST cause validation failure in strict mode.
|
||||
|
||||
---
|
||||
|
||||
## 6. Compilation pipeline
|
||||
|
||||
A conforming compiler MUST:
|
||||
|
||||
1. Parse YAML or JSON manifest
|
||||
2. Normalise to JSON
|
||||
3. Validate against `application.schema.json` (JSON Schema 2020-12)
|
||||
4. Validate cross-reference rules (see [validation-rules.md](./validation-rules.md))
|
||||
5. Validate component props against catalog schema
|
||||
6. Emit runtime manifests:
|
||||
- `ui-config.json` — site, components, templates, theme, navigation
|
||||
- `route-manifest.json` — ordered routes with template refs
|
||||
- `docs-nav.json` — filesystem-derived navigation (if mode auto)
|
||||
|
||||
---
|
||||
|
||||
## 7. File composition
|
||||
|
||||
Manifests MAY include external files using Backstage-style placeholders:
|
||||
|
||||
```yaml
|
||||
theme:
|
||||
$yaml: ./theme.yaml
|
||||
|
||||
navigation:
|
||||
$yaml: ./navigation.yaml
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Paths MUST be relative to the containing manifest
|
||||
- Path traversal (`../../`) MUST be rejected
|
||||
- Supported placeholders: `$yaml:`, `$json:`, `$text:`
|
||||
|
||||
---
|
||||
|
||||
## 8. Extensions
|
||||
|
||||
Vendor extensions MUST use the `x-adp-` prefix:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
site:
|
||||
x-adp-customAnalytics: true
|
||||
```
|
||||
|
||||
Implementations MUST preserve unknown `x-adp-*` fields. Non-prefixed unknown fields MUST be rejected in strict mode.
|
||||
|
||||
---
|
||||
|
||||
## 9. Conformance
|
||||
|
||||
An implementation is **ADP-conformant** if and only if it:
|
||||
|
||||
1. Accepts manifests with `apiVersion: adp.io/v1alpha1` and `kind: Application`
|
||||
2. Validates all MUST-level requirements in this spec and validation-rules.md
|
||||
3. Rejects manifests with unknown fields in strict mode
|
||||
4. Emits runtime JSON manifests from valid Application specs
|
||||
5. Resolves routes to templates at render time
|
||||
|
||||
---
|
||||
|
||||
## 10. Related documents
|
||||
|
||||
- [Research report](../application-design-protocol-report.md)
|
||||
- [Implementation status](./implementation-status.md)
|
||||
- [PraisonAIUI mapping](./aiui-mapping.md)
|
||||
- [JSON Schema: application](./schema/application.schema.json)
|
||||
- [JSON Schema: component catalog](./schema/component-catalog.schema.json)
|
||||
- [JSON Schema: theme](./schema/theme.schema.json)
|
||||
- [Examples](./examples/)
|
||||
@@ -0,0 +1,115 @@
|
||||
# ADP Validation Rules
|
||||
|
||||
Cross-reference validation rules for Application Design Protocol manifests. These constraints cannot be expressed in JSON Schema alone and MUST be enforced by conforming compilers.
|
||||
|
||||
Rules use RFC 2119 keywords: MUST, MUST NOT, SHOULD, MAY.
|
||||
|
||||
Inspired by AWP validation rules R1–R32 pattern.
|
||||
|
||||
---
|
||||
|
||||
## Envelope rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R1** | MUST | `schemaVersion` MUST equal `1` for `apiVersion: adp.io/v1alpha1` |
|
||||
| **R2** | MUST | `kind` MUST be `Application` for root manifests |
|
||||
| **R3** | MUST | `metadata.name` MUST be unique within a deployment scope |
|
||||
| **R4** | MUST NOT | Unknown top-level fields outside `x-adp-*` namespace in strict mode |
|
||||
|
||||
## Component rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R5** | MUST | Every `spec.components` entry MUST have a unique ID |
|
||||
| **R6** | MUST | Every component `type` MUST exist in the published component catalog |
|
||||
| **R7** | MUST | Component `props` MUST validate against catalog JSON Schema for that type |
|
||||
| **R8** | MUST NOT | A component ID be referenced by `ref:` unless it exists in `spec.components` |
|
||||
| **R9** | SHOULD | Unused components (defined but never referenced) SHOULD emit a warning |
|
||||
|
||||
## Template rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R10** | MUST | Every `spec.templates` entry MUST declare a valid `layout` from the catalog |
|
||||
| **R11** | MUST | Slot names in `templates.*.slots` MUST be valid for the declared layout |
|
||||
| **R12** | MUST | Zone names in `templates.*.zones` MUST be valid for `FlexibleLayout` |
|
||||
| **R13** | MUST | Each template MUST declare either `slots` or `zones`, not both |
|
||||
| **R14** | MUST | Inline slot `{ type: X }` types MUST exist in the component catalog |
|
||||
| **R15** | MUST | Slot `{ ref: id }` MUST resolve to a component in `spec.components` |
|
||||
|
||||
## Route rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R16** | MUST | Every route `template` MUST reference a key in `spec.templates` |
|
||||
| **R17** | MUST | Routes MUST be ordered most-specific-first (longer/more precise patterns before wildcards) |
|
||||
| **R18** | MUST | Route slot overrides MUST use valid slot names for the target template's layout |
|
||||
| **R19** | SHOULD | At least one route SHOULD match the site root or docs base path |
|
||||
|
||||
## Content and navigation rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R20** | MUST | If `content.docs.dir` is declared, the directory MUST exist at compile time |
|
||||
| **R21** | MUST | If `content.docs.nav.mode` is `manual`, `navigation.tabs` MUST be non-empty |
|
||||
| **R22** | SHOULD | If `content.docs.nav.mode` is `auto`, a content source MUST be declared |
|
||||
|
||||
Navigation model:
|
||||
|
||||
- **`navigation.tabs`** — top tab bar (Mintlify-style)
|
||||
- **`content.docs.nav.mode`** — sidebar source: `auto` (filesystem scan) or `manual` (use `navigation.tabs` groups)
|
||||
|
||||
## File include rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R23** | MUST NOT | `$yaml:`, `$json:`, or `$text:` paths traverse outside the project root |
|
||||
| **R24** | MUST | Included files MUST parse as valid YAML/JSON/text respectively |
|
||||
| **R25** | MUST NOT | Circular file includes (A includes B includes A) |
|
||||
|
||||
## Theme rules
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R26** | MUST | If `theme.tokens` is declared, the referenced file MUST exist and validate against DTCG 2025.10 |
|
||||
| **R27** | MUST NOT | Both `theme.preset` and `theme.tokens` be declared simultaneously |
|
||||
|
||||
## Renderer conformance (target — not yet implemented in PraisonAIUI)
|
||||
|
||||
These rules define the target behaviour for ADP-conformant renderers. See [implementation-status.md](./implementation-status.md).
|
||||
|
||||
| Rule | Level | Description |
|
||||
|------|-------|-------------|
|
||||
| **R28** | MUST | Renderers MUST resolve the active template from `route-manifest.json` at runtime |
|
||||
| **R29** | MUST | Renderers MUST honour `content.docs.nav.mode` when building sidebar |
|
||||
| **R30** | MUST | Renderers MUST apply ARIA roles from catalog for each rendered component |
|
||||
|
||||
---
|
||||
|
||||
## Validation pipeline order
|
||||
|
||||
```
|
||||
1. Parse YAML/JSON
|
||||
2. Resolve $yaml/$json/$text includes (R23–R25)
|
||||
3. JSON Schema validate (application.schema.json)
|
||||
4. Cross-ref rules R1–R27
|
||||
5. Component props validate against catalog (R6–R7)
|
||||
6. Emit runtime manifests
|
||||
7. Runtime conformance R28–R30 (renderer responsibility)
|
||||
```
|
||||
|
||||
## Error format
|
||||
|
||||
Validation failures MUST report:
|
||||
|
||||
```json
|
||||
{
|
||||
"rule": "R8",
|
||||
"path": "spec.templates.docs.slots.header.ref",
|
||||
"message": "Component ref 'missing_header' not found in spec.components",
|
||||
"severity": "error"
|
||||
}
|
||||
```
|
||||
|
||||
Warnings (R9) use `"severity": "warning"`.
|
||||
@@ -0,0 +1,427 @@
|
||||
# Application Design Protocol (ADP) — Research Report
|
||||
|
||||
**Version:** 1.0 (draft)
|
||||
**Date:** August 2026
|
||||
**Scope:** UI, layout, navigation, theming — declarative application design from text manifests
|
||||
**Status:** Research and recommendation; not yet a published standard
|
||||
|
||||
---
|
||||
|
||||
## 1. Executive summary
|
||||
|
||||
There is no single existing standard for **designing full application UI from a declarative text file**. Instead, the problem is addressed in fragments:
|
||||
|
||||
- **Docs-site generators** (Mintlify, PraisonAIUI, Hugo) declare navigation and layout in config files.
|
||||
- **Form libraries** (JSON Forms, RJSF) declare layout trees from JSON Schema.
|
||||
- **Agent UI protocols** (A2UI, AG-UI) declare streaming component surfaces for LLM-generated UI.
|
||||
- **Catalog standards** (OpenUI, Backstage) declare component metadata without runtime layout.
|
||||
- **Design token standards** (DTCG 2025.10) declare theming without structure.
|
||||
|
||||
**Recommendation:** Define an **Application Design Protocol (ADP)** that synthesises proven patterns from 46 surveyed standards into a UI-only manifest format.
|
||||
|
||||
PraisonAIUI already implements approximately 60% of this model. ADP formalises, extends, and publishes what exists today.
|
||||
|
||||
---
|
||||
|
||||
## 2. Problem statement
|
||||
|
||||
### 2.1 What we need
|
||||
|
||||
A protocol that lets a developer (or LLM) describe an application's visual design — pages, navigation, components, themes, routes — in **one or more text files**, without writing application code.
|
||||
|
||||
### 2.2 What exists today (fragmented)
|
||||
|
||||
| Need | Who solves it | Gap |
|
||||
|------|---------------|-----|
|
||||
| Docs site from config | Mintlify, PraisonAIUI, Docusaurus | No shared spec; Mintlify uses JSON, others use YAML or JS |
|
||||
| Component catalog | OpenUI, A2UI | No layout or routing |
|
||||
| Layout trees | JSON Forms uiSchema | Forms only, not app shells |
|
||||
| Theming | DTCG, Material 3 | Tokens only, no pages |
|
||||
| Agent-generated UI | A2UI | Runtime streaming, not human-authored static sites |
|
||||
| Software metadata | Backstage | Catalog entities, not UI composition |
|
||||
|
||||
### 2.3 Design goals for ADP
|
||||
|
||||
1. **Human-readable** — editable without IDE plugins
|
||||
2. **LLM-safe** — structural schema rejects unknown fields
|
||||
3. **Portable** — same manifest renders on any ADP-compliant runtime
|
||||
4. **Validatable** — fail fast at build time, not in the browser
|
||||
5. **Extensible** — plugins add catalog entries, not inline code
|
||||
6. **Accessible** — ARIA hints required in component catalog
|
||||
|
||||
---
|
||||
|
||||
## 3. Standards landscape — 46 protocols in 10 categories
|
||||
|
||||
Each entry describes **how UI/layout/navigation is declared** — the primary comparison dimension.
|
||||
|
||||
### Category A: Web platform baseline (5)
|
||||
|
||||
| Standard | Format | How UI/layout/navigation is declared | Schema | Versioning |
|
||||
|----------|--------|--------------------------------------|--------|------------|
|
||||
| **HTML + CSS** | `.html` + `.css` | Semantic elements; CSS Flexbox/Grid; `<nav>` + links | Living spec grammar | Browser Baseline |
|
||||
| **Web Components + DSD** | HTML + JS | Custom element tree; slots; Declarative Shadow DOM | DOM IDL | Platform flags |
|
||||
| **WAI-ARIA + APG** | HTML attributes | Landmarks, widget roles, keyboard patterns | WAI-ARIA 1.2 | Rec track |
|
||||
| **DTCG Design Tokens** | JSON | Token groups: `$value`, `$type`, `{alias}` refs — **no layout** | DTCG 2025.10 | Date editions |
|
||||
| **OpenUI** | YAML/JSON | Component catalog: `components{id: props}` — **no layout tree** | Prop definitions | Per-library version |
|
||||
|
||||
### Category B: Schema-driven forms and layout (3)
|
||||
|
||||
| Standard | Format | How UI/layout/navigation is declared | Schema | Versioning |
|
||||
|----------|--------|--------------------------------------|--------|------------|
|
||||
| **JSON Forms** | JSON Schema + UI Schema | `VerticalLayout`, `Control`, `Categorization` with `scope` pointers | JSON Schema + uiSchema | Package SemVer |
|
||||
| **RJSF** | JSON Schema + uiSchema | Schema-tree mirroring; `ui:widget`, `ui:options` | React convention | Package SemVer |
|
||||
| **Google A2UI** | JSONL stream | Flat adjacency-list; `createSurface`; catalog-constrained types | JSON Schema catalog | v0.9.1 / v1.0 RC |
|
||||
|
||||
### Category C: Docs-site generators (6)
|
||||
|
||||
| Standard | Format | How UI/layout/navigation is declared | Schema | Versioning |
|
||||
|----------|--------|--------------------------------------|--------|------------|
|
||||
| **Mintlify** | JSON (`docs.json`) | `navigation.groups[].pages[]`; `$schema` URL; `$ref` splitting | Published JSON Schema URL | Platform |
|
||||
| **PraisonAIUI** | YAML | `components` → `templates{layout, slots, zones}` → `routes` | Pydantic (internal) | `schemaVersion: 1` |
|
||||
| **Docusaurus** | JS config | `themeConfig.navbar`, plugin sidebar config | TypeScript types | Package SemVer |
|
||||
| **Hugo** | TOML/YAML | `menu.main[]`, theme selection | Hugo schema | Release train |
|
||||
| **Jekyll** | YAML | Frontmatter per page; `_data/` nav | Conventions | SemVer |
|
||||
| **Gatsby** | JS/TS | `siteMetadata` + `plugins[]` | JS object | Major versions |
|
||||
|
||||
### Category D: Native declarative UI — reference only (5)
|
||||
|
||||
| Standard | Format | Serialised wire format? | Navigation model |
|
||||
|----------|--------|-------------------------|------------------|
|
||||
| **Jetpack Compose** | Kotlin | No | `NavHost` + routes |
|
||||
| **SwiftUI** | Swift | No | `NavigationStack` |
|
||||
| **Flutter** | Dart | No | `Router` / `Navigator` |
|
||||
| **Qt QML** | `.qml` | Yes | `StackView`, bindings |
|
||||
| **XAML** | XML | Yes (dialect-fragmented) | `Frame`, `NavigationView` |
|
||||
|
||||
### Category E: Design systems (2)
|
||||
|
||||
| Standard | Format | Theming model |
|
||||
|----------|--------|---------------|
|
||||
| **Material Design 3** | DTCG JSON | reference → system → component token tiers |
|
||||
| **Bootstrap 5.3** | SCSS → CSS vars | Sass maps → `--bs-*` custom properties |
|
||||
|
||||
### Category F: Design-to-code bridges — not source-of-truth (3)
|
||||
|
||||
| Standard | Output | Why not ADP source |
|
||||
|----------|--------|-------------------|
|
||||
| **Figma MCP** | React+Tailwind context | Proprietary; bridge only |
|
||||
| **Webflow export** | HTML/CSS/JS ZIP | No schema; paid export |
|
||||
| **Framer export** | React bundles | Platform lock-in |
|
||||
|
||||
### Category G: Component documentation (1)
|
||||
|
||||
| Standard | Format | Navigation |
|
||||
|----------|--------|------------|
|
||||
| **Storybook CSF 3** | TS modules | Sidebar via `title` path hierarchy |
|
||||
|
||||
### Category H: Configuration and packaging (12)
|
||||
|
||||
| Standard | Format | Composition mechanism | ADP borrow |
|
||||
|----------|--------|----------------------|------------|
|
||||
| **Kubernetes CRD** | YAML | `apiVersion/kind/spec`; structural OpenAPI | Structural validation |
|
||||
| **Helm** | YAML + templates | Subcharts; values merge; `values.schema.json` | Defaults + overrides |
|
||||
| **Backstage catalog** | YAML | `$yaml:`/`$json:` includes; relation graph | Envelope + includes |
|
||||
| **OpenAPI 3.1** | YAML/JSON | `$ref`, `components` registry | JSON Schema foundation |
|
||||
| **AsyncAPI 3.x** | YAML/JSON | Channels → messages | Future event UI |
|
||||
| **JSON Schema 2020-12** | JSON | `$ref`, `$defs`, conditionals | Validation dialect |
|
||||
| **CloudEvents** | JSON envelope | Fixed envelope + opaque payload | Future event wrapper |
|
||||
| **Docker Compose** | YAML | Multi-file merge; profiles | Override stacking |
|
||||
| **CUE** | CUE | Unify schema + data | Power-user compile layer |
|
||||
| **Jsonnet** | Jsonnet → JSON | `import` + merge | Theme variants |
|
||||
| **Pkl** | `.pkl` | `amends`, mixins | Typed config model |
|
||||
| **OCI Artifact** | JSON manifest | Multi-layer blobs | Bundle packaging |
|
||||
|
||||
### Category I: Agent and workflow protocols (7)
|
||||
|
||||
| Standard | Format | UI/state declaration | ADP borrow |
|
||||
|----------|--------|---------------------|------------|
|
||||
| **MCP** | JSON-RPC | Tool schemas — no UI layout | Future widget actions |
|
||||
| **AG-UI** | Streaming JSON | EventType enum; snapshots + deltas | Streaming model |
|
||||
| **A2A** | Proto → JSON-RPC | Agent Cards | Transport |
|
||||
| **AWP** | Multi-file YAML | 7 layers; rules R1–R32 | Layer + validation model |
|
||||
| **LangGraph** | workflow.yaml | Graph nodes + edges | Layout graph |
|
||||
| **AutoGen** | ComponentModel JSON | `{provider, type, version, config}` | Pluggable components |
|
||||
| **CrewAI** | JSONC/YAML | Agent arrays — weak schema | Anti-pattern |
|
||||
|
||||
### Category J: Application packaging (2)
|
||||
|
||||
| Standard | Format | Application declaration |
|
||||
|----------|--------|------------------------|
|
||||
| **OAM** | YAML | `Application` with `components[]`, `traits[]` |
|
||||
| **CNAB** | bundle.json | Parameters, credentials, actions |
|
||||
|
||||
---
|
||||
|
||||
## 4. How they do it — mechanism comparison matrix
|
||||
|
||||
| Mechanism | Standards | How it works | ADP |
|
||||
|-----------|-----------|--------------|-----|
|
||||
| **Envelope document** | K8s, Backstage, OAM, AWP | `apiVersion` + `kind` + `metadata` + `spec` | Adopt |
|
||||
| **Component registry + ref** | PraisonAIUI, OpenUI | Named map; templates use `ref:` | Adopt |
|
||||
| **Layout + slots** | PraisonAIUI, JSON Forms | Layout type + named regions | Adopt (unified) |
|
||||
| **Route → template** | PraisonAIUI, Flutter | URL glob → template | Adopt |
|
||||
| **Navigation tree** | Mintlify, Storybook | Hierarchical groups → pages | Adopt |
|
||||
| **Design token file** | DTCG, Material 3 | Token JSON with aliases | Adopt |
|
||||
| **Catalog allowlist** | A2UI, OpenUI | Closed component vocabulary | Adopt |
|
||||
| **Schema + uiSchema split** | JSON Forms, RJSF | Data vs presentation | Partial |
|
||||
| **Streaming surfaces** | A2UI, AG-UI | Incremental JSON updates | Future |
|
||||
| **Layered spec** | AWP, Helm | Separate files merged at compile | Adopt |
|
||||
| **File includes** | Backstage, Mintlify | `$yaml:` / `$ref` external files | Adopt |
|
||||
| **Structural pruning** | K8s CRD | Reject unknown fields | Adopt |
|
||||
| **Validation rules R1–Rn** | AWP | Cross-ref beyond JSON Schema | Adopt |
|
||||
| **JS executable config** | Docusaurus, Gatsby | Code config | Reject |
|
||||
| **Visual canvas** | Figma, Webflow | Proprietary designer | Reject |
|
||||
| **Code-only UI** | Compose, SwiftUI | No wire format | Out of scope |
|
||||
|
||||
---
|
||||
|
||||
## 5. Side-by-side: four closest analogues
|
||||
|
||||
| Dimension | PraisonAIUI | Mintlify | A2UI | Backstage |
|
||||
|-----------|-------------|----------|------|-----------|
|
||||
| **Authoring format** | YAML | JSON | JSON (stream) | YAML |
|
||||
| **Envelope** | `schemaVersion` only | Flat JSON | Message `version` | `apiVersion/kind/metadata/spec` |
|
||||
| **Navigation** | `navigation.tabs[]` + filesystem | `navigation.groups[]` | N/A | N/A (metadata catalog) |
|
||||
| **Components** | `components{id: type, props}` | N/A | Catalog + adjacency list | Entity kinds |
|
||||
| **Layout** | `templates{layout, slots, zones}` | Theme only | Surfaces | N/A |
|
||||
| **Routes** | `routes{match, template}` | Page paths in nav | N/A | N/A |
|
||||
| **Schema published** | Pydantic only | `$schema` URL | JSON Schema catalog | JSON Schema per kind |
|
||||
| **Runtime** | React SPA | Hosted platform | Any renderer | Catalog API |
|
||||
| **Primary use** | Docs sites | Docs sites | Agent-generated UI | Software inventory |
|
||||
|
||||
**Synthesis:** ADP combines PraisonAIUI's composition model, Mintlify's schema publishing, A2UI's catalog allowlist, and Backstage's envelope and file includes.
|
||||
|
||||
---
|
||||
|
||||
## 6. YAML vs alternatives — structured debate
|
||||
|
||||
### 6.1 The assumption
|
||||
|
||||
YAML is the best authoring format for application design manifests.
|
||||
|
||||
### 6.2 Counter-evidence
|
||||
|
||||
**Mintlify** — the closest commercial docs-site analogue — uses **JSON** (`docs.json`) with a published `$schema` URL for IDE autocomplete. This works well for flat-to-moderately-nested config but becomes verbose for deep layout trees.
|
||||
|
||||
**A2UI** uses **JSON** as both authoring and wire format because LLMs emit structured JSON reliably and ambiguity is unacceptable at runtime.
|
||||
|
||||
**Docusaurus and Gatsby** use **JavaScript/TypeScript** config for programmatic power — but this sacrifices portability and LLM generation safety.
|
||||
|
||||
### 6.3 Format comparison
|
||||
|
||||
| Format | Strengths | Weaknesses for ADP |
|
||||
|--------|-----------|-------------------|
|
||||
| **YAML** | Comments; readable nesting; LLM-friendly; PraisonAIUI continuity | Ambiguous typing; merge key footguns; no native DRY |
|
||||
| **JSON** | Unambiguous; `$schema` IDE support; Mintlify-proven | No comments; verbose for layout trees |
|
||||
| **TOML** | Clear flat tables (Hugo) | Poor for nested templates/zones |
|
||||
| **CUE / Jsonnet / Pkl** | Typed composition without templating | Learning curve; not LLM-default |
|
||||
| **HCL** | Modules, expressions | Ecosystem-bound; overkill for UI-only |
|
||||
| **JS/TS** | Full programmatic power | Not portable; not LLM-safe |
|
||||
|
||||
### 6.4 Recommendation
|
||||
|
||||
```
|
||||
Author (human/LLM) → YAML (default) or JSON (alternate)
|
||||
↓
|
||||
ADP compiler/validator (JSON Schema 2020-12)
|
||||
↓
|
||||
Normalised JSON manifest (runtime)
|
||||
↓
|
||||
UI renderer (catalog-constrained)
|
||||
```
|
||||
|
||||
- **Primary authoring:** YAML — best for nested `templates` / `zones` trees
|
||||
- **Alternate authoring:** JSON — for teams preferring Mintlify-style `$schema` tooling
|
||||
- **Never at runtime:** YAML — always compile to JSON before serve
|
||||
- **Composition:** `$yaml:` file includes (Backstage), not Helm Go templates
|
||||
- **Power users:** Optional CUE/Jsonnet compiles to YAML before validation
|
||||
|
||||
**Verdict:** YAML remains the recommended default, but ADP MUST accept JSON as an equivalent authoring format with identical semantics.
|
||||
|
||||
---
|
||||
|
||||
## 7. ADP design principles
|
||||
|
||||
1. **Declarative, not executable** — manifests describe intent; runtimes render; no inline code or templating logic in core spec.
|
||||
2. **Schema-first** — JSON Schema 2020-12 is canonical; YAML is authoring convenience.
|
||||
3. **Structural strictness** — unknown fields rejected in strict mode (K8s CRD pattern).
|
||||
4. **Catalog allowlist** — component `type` must exist in published catalog (A2UI pattern).
|
||||
5. **Envelope consistency** — every document uses `apiVersion`, `kind`, `metadata`, `spec`.
|
||||
6. **Separation of concerns** — theme, navigation, components, templates, routes are distinct spec sections.
|
||||
7. **Compile, don't interpret** — YAML → validated JSON manifest → static assets at build time.
|
||||
8. **Accessibility by default** — catalog entries include ARIA role hints (WAI-ARIA pattern).
|
||||
9. **Versioned protocol** — `schemaVersion` integer + `apiVersion` string; breaking changes bump version.
|
||||
10. **Extension namespace** — `x-adp-*` prefix reserved; plugins register catalog layers.
|
||||
11. **File composition** — `$yaml:` includes for modular configs (Backstage pattern).
|
||||
12. **Route-driven rendering** — URL resolution selects template; no hardcoded layout in renderer.
|
||||
|
||||
---
|
||||
|
||||
## 8. Proposed ADP document model
|
||||
|
||||
```yaml
|
||||
schemaVersion: 1
|
||||
apiVersion: adp.io/v1alpha1
|
||||
kind: Application
|
||||
|
||||
metadata:
|
||||
name: my-docs-site
|
||||
version: "1.0.0"
|
||||
labels: {}
|
||||
|
||||
spec:
|
||||
site:
|
||||
title: "My Site"
|
||||
description: "..."
|
||||
theme:
|
||||
preset: zinc # or DTCG token ref
|
||||
radius: md
|
||||
darkMode: true
|
||||
navigation:
|
||||
mode: auto # auto | manual
|
||||
tabs: [...]
|
||||
components:
|
||||
header_main:
|
||||
type: Header
|
||||
props: { logoText: "..." }
|
||||
templates:
|
||||
docs:
|
||||
layout: ThreeColumnLayout
|
||||
slots:
|
||||
header: { ref: header_main }
|
||||
main: { type: DocContent }
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
content:
|
||||
docs:
|
||||
dir: "./docs"
|
||||
```
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
application.yaml → Parse → JSON Schema validate → Cross-ref rules (R1–R30)
|
||||
→ Compile → ui-config.json + route-manifest.json + docs-nav.json
|
||||
→ React / static renderer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. PraisonAIUI alignment and gaps
|
||||
|
||||
### Already aligned
|
||||
|
||||
| PraisonAIUI today | ADP equivalent |
|
||||
|-------------------|----------------|
|
||||
| `schemaVersion: 1` | `schemaVersion` + `apiVersion` |
|
||||
| `components` + `ref:` | `spec.components` + slot refs |
|
||||
| `templates.layout` + `slots` | `spec.templates` |
|
||||
| `routes` | `spec.routes` |
|
||||
| `site.theme` | `spec.theme` |
|
||||
| Compiler → JSON manifests | ADP compile pipeline |
|
||||
| Pydantic validation | JSON Schema + cross-ref rules |
|
||||
|
||||
### Gaps to close
|
||||
|
||||
| Gap | ADP fix |
|
||||
|-----|---------|
|
||||
| No published JSON Schema | Publish `application.schema.json` |
|
||||
| `navigation.tabs` compiled but not rendered | Mandate runtime consumption in spec |
|
||||
| `route-manifest.json` not used for routing | Mandate route-driven template resolution |
|
||||
| Dual slots + zones models | Unified: zones = ordered slot arrays |
|
||||
| Closed hardcoded component types | Published `component-catalog.schema.json` |
|
||||
| No envelope (`kind`, `metadata`) | Add Backstage-style envelope |
|
||||
|
||||
---
|
||||
|
||||
## 10. Migration path
|
||||
|
||||
Existing `aiui.template.yaml` files remain valid. Migration steps:
|
||||
|
||||
1. Add `apiVersion: adp.io/v1alpha1` and `kind: Application` (optional during transition).
|
||||
2. Wrap top-level keys under `spec:` (compiler accepts both shapes during v1alpha1).
|
||||
3. Rename `site` fields unchanged; map `navigation`, `templates`, `routes` as-is.
|
||||
4. Publish catalog; register custom component types via plugin schema layers.
|
||||
|
||||
See [adp/aiui-mapping.md](./adp/aiui-mapping.md) for field-by-field mapping.
|
||||
|
||||
---
|
||||
|
||||
## 11. Open questions
|
||||
|
||||
1. **Should ADP standardise layout type names** (`ThreeColumnLayout`) or allow runtime-defined layouts?
|
||||
2. **Should navigation support both filesystem-auto and manual tabs** in one spec, or require a mode switch?
|
||||
3. **Should agent-driven surfaces (A2UI) be a separate `kind: Surface`** or embedded in Application?
|
||||
4. **Should theme reference DTCG token files directly** or use simplified preset shorthand?
|
||||
|
||||
---
|
||||
|
||||
## 12. Appendix A — Standards index
|
||||
|
||||
| # | Standard | URL |
|
||||
|---|----------|-----|
|
||||
| 1 | A2UI | https://a2ui.org/ |
|
||||
| 2 | AG-UI | https://docs.ag-ui.com/ |
|
||||
| 3 | AWP | https://github.com/veegee82/agent-workflow-protocol |
|
||||
| 4 | AsyncAPI | https://www.asyncapi.com/ |
|
||||
| 5 | Backstage catalog | https://backstage.io/docs/features/software-catalog/descriptor-format |
|
||||
| 6 | Bootstrap | https://getbootstrap.com/ |
|
||||
| 7 | CloudEvents | https://cloudevents.io/ |
|
||||
| 8 | CNAB | https://cnab.io/ |
|
||||
| 9 | CrewAI | https://docs.crewai.com/ |
|
||||
| 10 | CUE | https://cuelang.org/ |
|
||||
| 11 | Docusaurus | https://docusaurus.io/ |
|
||||
| 12 | DTCG Design Tokens | https://www.designtokens.org/ |
|
||||
| 13 | Figma Code Connect | https://developers.figma.com/docs/code-connect/ |
|
||||
| 14 | Framer | https://www.framer.com/ |
|
||||
| 15 | Gatsby | https://www.gatsbyjs.com/ |
|
||||
| 16 | Helm | https://helm.sh/ |
|
||||
| 17 | Hugo | https://gohugo.io/ |
|
||||
| 18 | Jekyll | https://jekyllrb.com/ |
|
||||
| 19 | JSON Forms | https://jsonforms.io/ |
|
||||
| 20 | JSON Schema | https://json-schema.org/ |
|
||||
| 21 | Kubernetes CRD | https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/ |
|
||||
| 22 | LangGraph | https://langchain-ai.github.io/langgraph/ |
|
||||
| 23 | Material Design 3 | https://m3.material.io/ |
|
||||
| 24 | MCP | https://modelcontextprotocol.io/ |
|
||||
| 25 | Mintlify | https://mintlify.com/docs |
|
||||
| 26 | OAM | https://oam.dev/ |
|
||||
| 27 | OCI Artifact | https://opencontainers.org/ |
|
||||
| 28 | OpenAPI | https://www.openapis.org/ |
|
||||
| 29 | OpenUI | https://openuispec.org/ |
|
||||
| 30 | Pkl | https://pkl-lang.org/ |
|
||||
| 31 | PraisonAIUI | https://github.com/MervinPraison/PraisonAIUI |
|
||||
| 32 | RJSF | https://rjsf-team.github.io/react-jsonschema-form/ |
|
||||
| 33 | Storybook CSF | https://storybook.js.org/docs/api/csf |
|
||||
| 34 | W3C ARIA APG | https://www.w3.org/WAI/ARIA/apg/ |
|
||||
| 35 | Web Components | https://web.dev/articles/declarative-shadow-dom |
|
||||
| 36 | Webflow | https://webflow.com/ |
|
||||
| 37 | AutoGen | https://microsoft.github.io/autogen/ |
|
||||
| 38 | A2A | https://google.github.io/A2A/ |
|
||||
| 39 | Jsonnet | https://jsonnet.org/ |
|
||||
| 40 | Docker Compose | https://docs.docker.com/compose/ |
|
||||
| 41 | Qt QML | https://doc.qt.io/qt-6/qmlapplications.html |
|
||||
| 42 | XAML | https://learn.microsoft.com/en-us/windows/apps/develop/platform/xaml/xaml-overview |
|
||||
| 43 | Jetpack Compose | https://developer.android.com/develop/ui/compose |
|
||||
| 44 | SwiftUI | https://developer.apple.com/xcode/swiftui/ |
|
||||
| 45 | HTML + CSS | https://html.spec.whatwg.org/ |
|
||||
| 46 | Flutter | https://flutter.dev/ |
|
||||
|
||||
---
|
||||
|
||||
## 13. Conclusion
|
||||
|
||||
ADP is feasible and largely prefigured by PraisonAIUI's existing architecture. The research across 46 standards yields a clear synthesis:
|
||||
|
||||
- **Envelope** from Backstage/K8s
|
||||
- **Composition** from PraisonAIUI (components → templates → routes)
|
||||
- **Catalog** from A2UI/OpenUI
|
||||
- **Theming** from DTCG
|
||||
- **Validation** from AWP (structural schema + R-rules)
|
||||
- **Authoring** in YAML (default) with JSON alternate (Mintlify pattern)
|
||||
|
||||
**Status:** Research draft — see [adp/implementation-status.md](./adp/implementation-status.md) for implementation gaps.
|
||||
|
||||
The draft spec, JSON Schema files, and example manifests are published alongside this report in [`docs/protocols/adp/`](./adp/).
|
||||
@@ -25,6 +25,7 @@ from praisonaiui.server import create_app
|
||||
|
||||
# Use shared seed data helper
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import safe_print as print
|
||||
from _shared.seed_data import seed_demo_data
|
||||
|
||||
seed_demo_data()
|
||||
|
||||
@@ -20,6 +20,7 @@ import sys
|
||||
|
||||
# Use shared seed data helper
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import safe_print as print
|
||||
from _shared.seed_data import seed_demo_data
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import time
|
||||
import praisonaiui as aiui
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '_shared'))
|
||||
from console import safe_print as print
|
||||
from stream_bridge import StreamBridge
|
||||
|
||||
try:
|
||||
|
||||
@@ -4,9 +4,15 @@ Demonstrates ALL 16 PraisonAIUI features with real API calls.
|
||||
Run: aiui run app.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from starlette.testclient import TestClient
|
||||
from praisonaiui.server import create_app
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import safe_print as print
|
||||
|
||||
|
||||
def test_all_features():
|
||||
"""Test all 16 feature APIs with real data."""
|
||||
|
||||
@@ -44,15 +44,18 @@ import sys
|
||||
# ── Import praisonaiui ──────────────────────────────────────
|
||||
import praisonaiui as aiui
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import icon, safe_print
|
||||
|
||||
# ── Check gateway availability ──────────────────────────────
|
||||
try:
|
||||
from praisonaiui.integration import AIUIGateway
|
||||
from praisonaiagents import Agent
|
||||
GATEWAY_OK = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Gateway dependencies missing: {e}")
|
||||
print(" Install with: pip install praisonai praisonaiagents")
|
||||
print(" Falling back to standalone mode.")
|
||||
safe_print(f"{icon('⚠️', '[WARN]')} Gateway dependencies missing: {e}")
|
||||
safe_print(" Install with: pip install praisonai praisonaiagents")
|
||||
safe_print(" Falling back to standalone mode.")
|
||||
GATEWAY_OK = False
|
||||
|
||||
# ── Set dashboard style ─────────────────────────────────────
|
||||
@@ -149,15 +152,15 @@ def _register_agents_in_dashboard():
|
||||
"model": agent_def["model"],
|
||||
"icon": agent_def["icon"],
|
||||
})
|
||||
print(f" ✓ Dashboard: {agent_def['icon']} {agent_def['name']}")
|
||||
safe_print(f" {icon('✓', '[OK]')} Dashboard: {agent_def['name']}")
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────
|
||||
async def main():
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
if GATEWAY_OK:
|
||||
print("🚀 Starting with Gateway mode (full execution + streaming)")
|
||||
print()
|
||||
safe_print(f"{icon('🚀', '[START]')} Starting with Gateway mode (full execution + streaming)")
|
||||
safe_print()
|
||||
|
||||
# Create the gateway
|
||||
gateway = AIUIGateway(
|
||||
@@ -174,22 +177,22 @@ async def main():
|
||||
self_reflect=False,
|
||||
)
|
||||
gateway.register_agent(agent, agent_id=agent_def["agent_id"])
|
||||
print(f" ✓ Gateway: {agent_def['icon']} {agent_def['name']} ({agent_def['model']})")
|
||||
safe_print(f" {icon('✓', '[OK]')} Gateway: {agent_def['name']} ({agent_def['model']})")
|
||||
|
||||
# ── Register agents in dashboard CRUD storage ───────
|
||||
_register_agents_in_dashboard()
|
||||
|
||||
print()
|
||||
print(f" Dashboard: http://localhost:8083")
|
||||
print(f" WebSocket: ws://localhost:8083/ws")
|
||||
print(f" Inspector: http://localhost:8083 → 🔍 Feature Inspector")
|
||||
print()
|
||||
safe_print()
|
||||
safe_print(" Dashboard: http://localhost:8083")
|
||||
safe_print(" WebSocket: ws://localhost:8083/ws")
|
||||
safe_print(f" Inspector: http://localhost:8083 {icon('→', '->')} {icon('🔍', '')} Feature Inspector")
|
||||
safe_print()
|
||||
|
||||
# Start the gateway — this runs the full server
|
||||
await gateway.start()
|
||||
else:
|
||||
# Fallback to standalone mode (same as example 15)
|
||||
print("📦 Starting in standalone mode (CRUD only, no agent execution)")
|
||||
safe_print(f"{icon('📦', '[INFO]')} Starting in standalone mode (CRUD only, no agent execution)")
|
||||
from praisonaiui.server import create_app
|
||||
import uvicorn
|
||||
|
||||
|
||||
@@ -26,15 +26,18 @@ import sys
|
||||
|
||||
import praisonaiui as aiui
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import icon, safe_print
|
||||
|
||||
# ── Check gateway availability ──────────────────────────────
|
||||
try:
|
||||
from praisonaiui.integration import AIUIGateway
|
||||
from praisonaiagents import Agent
|
||||
GATEWAY_OK = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Gateway dependencies not found: {e}")
|
||||
print(" Install with: pip install praisonai praisonaiagents")
|
||||
print(" Falling back to standalone mode (REST APIs only).\n")
|
||||
safe_print(f"{icon('⚠️', '[WARN]')} Gateway dependencies not found: {e}")
|
||||
safe_print(" Install with: pip install praisonai praisonaiagents")
|
||||
safe_print(" Falling back to standalone mode (REST APIs only).\n")
|
||||
GATEWAY_OK = False
|
||||
|
||||
aiui.set_style("dashboard")
|
||||
@@ -115,7 +118,7 @@ def _register_agents_in_dashboard():
|
||||
"model": agent_def["model"],
|
||||
"icon": agent_def["icon"],
|
||||
})
|
||||
print(f" ✓ Agent: {agent_def['icon']} {agent_def['name']}")
|
||||
safe_print(f" {icon('✓', '[OK]')} Agent: {agent_def['name']}")
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────
|
||||
@@ -125,8 +128,8 @@ async def main():
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
|
||||
if GATEWAY_OK:
|
||||
print("🚀 Starting with Gateway mode (full execution + streaming)")
|
||||
print()
|
||||
safe_print(f"{icon('🚀', '[START]')} Starting with Gateway mode (full execution + streaming)")
|
||||
safe_print()
|
||||
|
||||
gateway = AIUIGateway(host=host, port=port)
|
||||
|
||||
@@ -139,32 +142,32 @@ async def main():
|
||||
reflection=False,
|
||||
)
|
||||
gateway.register_agent(agent, agent_id=agent_def["agent_id"])
|
||||
print(f" ✓ Gateway: {agent_def['icon']} {agent_def['name']} ({agent_def['model']})")
|
||||
safe_print(f" {icon('✓', '[OK]')} Gateway: {agent_def['name']} ({agent_def['model']})")
|
||||
|
||||
# Also register in dashboard CRUD
|
||||
_register_agents_in_dashboard()
|
||||
|
||||
print()
|
||||
print(f" Dashboard: http://localhost:{port}")
|
||||
print(f" Explorer: http://localhost:{port} → 🔬 Feature Explorer")
|
||||
print(f" WebSocket: ws://localhost:{port}/ws")
|
||||
print()
|
||||
safe_print()
|
||||
safe_print(f" Dashboard: http://localhost:{port}")
|
||||
safe_print(f" Explorer: http://localhost:{port} {icon('→', '->')} {icon('🔬', '')} Feature Explorer")
|
||||
safe_print(f" WebSocket: ws://localhost:{port}/ws")
|
||||
safe_print()
|
||||
|
||||
await gateway.start()
|
||||
else:
|
||||
# Standalone: REST APIs only, no agent execution
|
||||
print("📦 Starting in standalone mode (REST APIs only, no agent execution)")
|
||||
safe_print(f"{icon('📦', '[INFO]')} Starting in standalone mode (REST APIs only, no agent execution)")
|
||||
from praisonaiui.server import create_app
|
||||
import uvicorn
|
||||
|
||||
_register_agents_in_dashboard()
|
||||
|
||||
app = create_app()
|
||||
print()
|
||||
print(f" Dashboard: http://localhost:{port}")
|
||||
print(f" Explorer: http://localhost:{port} → 🔬 Feature Explorer")
|
||||
print(f" Note: POST endpoints may fail without gateway")
|
||||
print()
|
||||
safe_print()
|
||||
safe_print(f" Dashboard: http://localhost:{port}")
|
||||
safe_print(f" Explorer: http://localhost:{port} {icon('→', '->')} {icon('🔬', '')} Feature Explorer")
|
||||
safe_print(" Note: POST endpoints may fail without gateway")
|
||||
safe_print()
|
||||
|
||||
config = uvicorn.Config(app, host=host, port=port, log_level="info")
|
||||
server = uvicorn.Server(config)
|
||||
|
||||
@@ -25,9 +25,13 @@ Usage:
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
import praisonaiui as aiui
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import icon, safe_print
|
||||
|
||||
# ── Check gateway availability ──────────────────────────────
|
||||
try:
|
||||
from praisonaiagents import Agent
|
||||
@@ -35,9 +39,9 @@ try:
|
||||
from praisonaiui.integration import AIUIGateway
|
||||
GATEWAY_OK = True
|
||||
except ImportError as e:
|
||||
print(f"⚠️ Gateway dependencies not found: {e}")
|
||||
print(" Install with: pip install praisonai praisonaiagents")
|
||||
print(" Falling back to standalone mode (REST APIs only).\n")
|
||||
safe_print(f"{icon('⚠️', '[WARN]')} Gateway dependencies not found: {e}")
|
||||
safe_print(" Install with: pip install praisonai praisonaiagents")
|
||||
safe_print(" Falling back to standalone mode (REST APIs only).\n")
|
||||
GATEWAY_OK = False
|
||||
|
||||
aiui.set_style("dashboard")
|
||||
@@ -127,7 +131,7 @@ def _register_agents_in_dashboard():
|
||||
"model": agent_def["model"],
|
||||
"icon": agent_def["icon"],
|
||||
})
|
||||
print(f" ✓ Agent: {agent_def['icon']} {agent_def['name']}")
|
||||
safe_print(f" {icon('✓', '[OK]')} Agent: {agent_def['name']}")
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────
|
||||
@@ -137,8 +141,8 @@ async def main():
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
|
||||
if GATEWAY_OK:
|
||||
print("🚀 Starting with Gateway mode (full execution + streaming)")
|
||||
print()
|
||||
safe_print(f"{icon('🚀', '[START]')} Starting with Gateway mode (full execution + streaming)")
|
||||
safe_print()
|
||||
|
||||
gateway = AIUIGateway(host=host, port=port)
|
||||
|
||||
@@ -151,21 +155,21 @@ async def main():
|
||||
reflection=False,
|
||||
)
|
||||
gateway.register_agent(agent, agent_id=agent_def["agent_id"])
|
||||
print(f" ✓ Gateway: {agent_def['icon']} {agent_def['name']} ({agent_def['model']})")
|
||||
safe_print(f" {icon('✓', '[OK]')} Gateway: {agent_def['name']} ({agent_def['model']})")
|
||||
|
||||
# Also register in dashboard CRUD
|
||||
_register_agents_in_dashboard()
|
||||
|
||||
print()
|
||||
print(f" Dashboard: http://localhost:{port}")
|
||||
print(f" Explorer: http://localhost:{port} → 🔬 Feature Explorer")
|
||||
print(f" WebSocket: ws://localhost:{port}/ws")
|
||||
print()
|
||||
safe_print()
|
||||
safe_print(f" Dashboard: http://localhost:{port}")
|
||||
safe_print(f" Explorer: http://localhost:{port} {icon('→', '->')} {icon('🔬', '')} Feature Explorer")
|
||||
safe_print(f" WebSocket: ws://localhost:{port}/ws")
|
||||
safe_print()
|
||||
|
||||
await gateway.start()
|
||||
else:
|
||||
# Standalone: REST APIs only, no agent execution
|
||||
print("📦 Starting in standalone mode (REST APIs only, no agent execution)")
|
||||
safe_print(f"{icon('📦', '[INFO]')} Starting in standalone mode (REST APIs only, no agent execution)")
|
||||
import uvicorn
|
||||
|
||||
from praisonaiui.server import create_app
|
||||
@@ -173,11 +177,11 @@ async def main():
|
||||
_register_agents_in_dashboard()
|
||||
|
||||
app = create_app()
|
||||
print()
|
||||
print(f" Dashboard: http://localhost:{port}")
|
||||
print(f" Explorer: http://localhost:{port} → 🔬 Feature Explorer")
|
||||
print(" Note: POST endpoints may fail without gateway")
|
||||
print()
|
||||
safe_print()
|
||||
safe_print(f" Dashboard: http://localhost:{port}")
|
||||
safe_print(f" Explorer: http://localhost:{port} {icon('→', '->')} {icon('🔬', '')} Feature Explorer")
|
||||
safe_print(" Note: POST endpoints may fail without gateway")
|
||||
safe_print()
|
||||
|
||||
config = uvicorn.Config(app, host=host, port=port, log_level="info")
|
||||
server = uvicorn.Server(config)
|
||||
|
||||
@@ -32,6 +32,9 @@ import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import icon, safe_print
|
||||
|
||||
# ── Check prerequisites ─────────────────────────────────────────────
|
||||
|
||||
try:
|
||||
@@ -40,8 +43,8 @@ try:
|
||||
SDK_OK = True
|
||||
except ImportError:
|
||||
SDK_OK = False
|
||||
print("⚠️ Missing dependencies. Install with:")
|
||||
print(" pip install praisonai[email] praisonaiagents")
|
||||
safe_print(f"{icon('⚠️', '[WARN]')} Missing dependencies. Install with:")
|
||||
safe_print(" pip install praisonai[email] praisonaiagents")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -68,8 +71,8 @@ agent = Agent(
|
||||
async def run_simple():
|
||||
"""Start email bot with automatic env var resolution."""
|
||||
bot = Bot("email", agent=agent)
|
||||
print(f"📧 Starting email bot for: {os.getenv('EMAIL_ADDRESS', '(not set)')}")
|
||||
print(" Listening for new emails... (Ctrl+C to stop)")
|
||||
safe_print(f"{icon('📧', '[EMAIL]')} Starting email bot for: {os.getenv('EMAIL_ADDRESS', '(not set)')}")
|
||||
safe_print(" Listening for new emails... (Ctrl+C to stop)")
|
||||
await bot.start()
|
||||
|
||||
|
||||
@@ -96,9 +99,9 @@ async def run_with_handlers():
|
||||
@bot.on_message
|
||||
async def log_email(message):
|
||||
subject = message.metadata.get("subject", "(no subject)")
|
||||
print(f"📨 From: {message.sender.username}")
|
||||
print(f" Subject: {subject}")
|
||||
print(f" Preview: {message.text[:100]}...")
|
||||
safe_print(f"{icon('📨', '[MSG]')} From: {message.sender.username}")
|
||||
safe_print(f" Subject: {subject}")
|
||||
safe_print(f" Preview: {message.text[:100]}...")
|
||||
|
||||
# Custom command: emails with subject starting with "/status"
|
||||
@bot.on_command("status")
|
||||
@@ -118,11 +121,11 @@ async def run_with_handlers():
|
||||
reply_to=message.message_id,
|
||||
)
|
||||
|
||||
print(f"📧 Starting email bot with custom handlers")
|
||||
print(f" Email: {bot._email_address}")
|
||||
print(f" Poll interval: {config.polling_interval}s")
|
||||
print(f" Allowed senders: {'all' if not config.allowed_users else config.allowed_users}")
|
||||
print(" Listening... (Ctrl+C to stop)")
|
||||
safe_print(f"{icon('📧', '[EMAIL]')} Starting email bot with custom handlers")
|
||||
safe_print(f" Email: {bot._email_address}")
|
||||
safe_print(f" Poll interval: {config.polling_interval}s")
|
||||
safe_print(f" Allowed senders: {'all' if not config.allowed_users else config.allowed_users}")
|
||||
safe_print(" Listening... (Ctrl+C to stop)")
|
||||
await bot.start()
|
||||
|
||||
|
||||
@@ -131,17 +134,17 @@ async def run_with_handlers():
|
||||
async def run_probe():
|
||||
"""Test email server connectivity."""
|
||||
bot = Bot("email", agent=agent)
|
||||
print("🔍 Testing email server connectivity...")
|
||||
safe_print(f"{icon('🔍', '[PROBE]')} Testing email server connectivity...")
|
||||
result = await bot.probe()
|
||||
|
||||
print(f"\n Connection: {'✅ OK' if result.ok else '❌ FAILED'}")
|
||||
print(f" Platform: {result.platform}")
|
||||
safe_print(f"\n Connection: {icon('✅', '[OK]') + ' OK' if result.ok else icon('❌', '[FAIL]') + ' FAILED'}")
|
||||
safe_print(f" Platform: {result.platform}")
|
||||
if result.bot_username:
|
||||
print(f" Email: {result.bot_username}")
|
||||
safe_print(f" Email: {result.bot_username}")
|
||||
if result.elapsed_ms:
|
||||
print(f" Latency: {result.elapsed_ms:.0f}ms")
|
||||
safe_print(f" Latency: {result.elapsed_ms:.0f}ms")
|
||||
if result.error:
|
||||
print(f" Error: {result.error}")
|
||||
safe_print(f" Error: {result.error}")
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -42,7 +42,9 @@ import sys
|
||||
# ── Imports ─────────────────────────────────────────────────────────
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "src"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from _shared.console import icon, safe_print
|
||||
from praisonaiui.server import create_app
|
||||
import uvicorn
|
||||
|
||||
@@ -91,11 +93,11 @@ def seed_email_channel():
|
||||
})
|
||||
if r.status_code in (200, 201):
|
||||
channel_id = r.json().get("id", "unknown")
|
||||
print(f" ✅ Email channel registered: {channel_id}")
|
||||
safe_print(f" {icon('✅', '[OK]')} Email channel registered: {channel_id}")
|
||||
else:
|
||||
print(f" ⚠️ Channel registration: {r.status_code} — {r.json()}")
|
||||
safe_print(f" {icon('⚠️', '[WARN]')} Channel registration: {r.status_code} - {r.json()}")
|
||||
else:
|
||||
print(" ✓ Email channel already exists")
|
||||
safe_print(f" {icon('✓', '[OK]')} Email channel already exists")
|
||||
|
||||
# Also seed other channels for comparison in dashboard
|
||||
for name, platform in [("Discord #general", "discord"), ("Telegram Support", "telegram")]:
|
||||
@@ -106,7 +108,7 @@ def seed_email_channel():
|
||||
"platform": platform,
|
||||
"config": {},
|
||||
})
|
||||
print(f" ✓ Seeded {platform} channel")
|
||||
safe_print(f" {icon('✓', '[OK]')} Seeded {platform} channel")
|
||||
|
||||
|
||||
# ── Verify email platform support ──────────────────────────────────
|
||||
@@ -122,8 +124,8 @@ def verify_email_support():
|
||||
if r.status_code == 200:
|
||||
platforms = r.json().get("platforms", [])
|
||||
email_supported = "email" in platforms
|
||||
print(f" Supported platforms: {platforms}")
|
||||
print(f" Email supported: {'✅ Yes' if email_supported else '❌ No — add email to SUPPORTED_PLATFORMS'}")
|
||||
safe_print(f" Supported platforms: {platforms}")
|
||||
safe_print(f" Email supported: {icon('✅', '[OK]') + ' Yes' if email_supported else icon('❌', '[X]') + ' No - add email to SUPPORTED_PLATFORMS'}")
|
||||
return email_supported
|
||||
return False
|
||||
|
||||
@@ -137,51 +139,51 @@ def test_channel_api():
|
||||
app = create_app()
|
||||
client = TestClient(app)
|
||||
|
||||
print("\n📋 Channel API Test")
|
||||
print("=" * 40)
|
||||
safe_print(f"\n{icon('📋', '[TEST]')} Channel API Test")
|
||||
safe_print("=" * 40)
|
||||
|
||||
# List channels
|
||||
r = client.get("/api/channels")
|
||||
channels = r.json().get("channels", [])
|
||||
print(f" GET /api/channels: {len(channels)} channels")
|
||||
safe_print(f" GET /api/channels: {len(channels)} channels")
|
||||
for ch in channels:
|
||||
icon = "📧" if ch.get("platform") == "email" else "💬"
|
||||
print(f" {icon} {ch.get('name', '?')} ({ch.get('platform', '?')})")
|
||||
ch_icon = icon("📧", "[email]") if ch.get("platform") == "email" else icon("💬", "[chat]")
|
||||
safe_print(f" {ch_icon} {ch.get('name', '?')} ({ch.get('platform', '?')})")
|
||||
|
||||
# Get email channel status
|
||||
email_ch = next((ch for ch in channels if ch.get("platform") == "email"), None)
|
||||
if email_ch:
|
||||
ch_id = email_ch.get("id")
|
||||
r = client.get(f"/api/channels/{ch_id}/status")
|
||||
print(f" GET /api/channels/{ch_id}/status: {r.json()}")
|
||||
safe_print(f" GET /api/channels/{ch_id}/status: {r.json()}")
|
||||
|
||||
print()
|
||||
safe_print()
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("📧 PraisonAIUI — Email Channel Integration")
|
||||
print("=" * 50)
|
||||
print()
|
||||
safe_print(f"{icon('📧', '[EMAIL]')} PraisonAIUI - Email Channel Integration")
|
||||
safe_print("=" * 50)
|
||||
safe_print()
|
||||
|
||||
if "--test" in sys.argv:
|
||||
# Test mode: verify APIs + seed data
|
||||
print("🔍 Verifying email platform support...")
|
||||
safe_print(f"{icon('🔍', '[VERIFY]')} Verifying email platform support...")
|
||||
verify_email_support()
|
||||
print()
|
||||
print("🌱 Seeding email channel...")
|
||||
safe_print()
|
||||
safe_print(f"{icon('🌱', '[SEED]')} Seeding email channel...")
|
||||
seed_email_channel()
|
||||
test_channel_api()
|
||||
else:
|
||||
# Run the dashboard
|
||||
print("🌱 Seeding channels...")
|
||||
safe_print(f"{icon('🌱', '[SEED]')} Seeding channels...")
|
||||
seed_email_channel()
|
||||
print()
|
||||
safe_print()
|
||||
|
||||
app = create_app()
|
||||
print(f"✅ Dashboard at http://localhost:8084")
|
||||
print(f" Channels: http://localhost:8084/api/channels")
|
||||
print(f" Platforms: http://localhost:8084/api/channels/platforms")
|
||||
safe_print(f"{icon('✅', '[OK]')} Dashboard at http://localhost:8084")
|
||||
safe_print(" Channels: http://localhost:8084/api/channels")
|
||||
safe_print(" Platforms: http://localhost:8084/api/channels/platforms")
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
uvicorn.run(app, host=host, port=8084, log_level="info")
|
||||
@@ -33,6 +33,9 @@ import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
from _shared.console import icon, safe_print
|
||||
|
||||
# ── Check prerequisites ─────────────────────────────────────────────
|
||||
|
||||
try:
|
||||
@@ -41,8 +44,8 @@ try:
|
||||
SDK_OK = True
|
||||
except ImportError:
|
||||
SDK_OK = False
|
||||
print("⚠️ Missing dependencies. Install with:")
|
||||
print(" pip install praisonai agentmail praisonaiagents")
|
||||
safe_print(f"{icon('⚠️', '[WARN]')} Missing dependencies. Install with:")
|
||||
safe_print(" pip install praisonai agentmail praisonaiagents")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -69,9 +72,9 @@ agent = Agent(
|
||||
async def run_simple():
|
||||
"""Start agentmail bot with automatic env var resolution."""
|
||||
bot = Bot("agentmail", agent=agent)
|
||||
print(f"📩 Starting AgentMail bot")
|
||||
print(f" API key: {'✅ set' if os.getenv('AGENTMAIL_API_KEY') else '❌ not set'}")
|
||||
print(" Listening for new emails... (Ctrl+C to stop)")
|
||||
safe_print(f"{icon('📩', '[MAIL]')} Starting AgentMail bot")
|
||||
safe_print(f" API key: {icon('✅', '[OK]') + ' set' if os.getenv('AGENTMAIL_API_KEY') else icon('❌', '[X]') + ' not set'}")
|
||||
safe_print(" Listening for new emails... (Ctrl+C to stop)")
|
||||
await bot.start()
|
||||
|
||||
|
||||
@@ -99,9 +102,9 @@ async def run_with_handlers():
|
||||
@bot.on_message
|
||||
async def log_email(message):
|
||||
subject = message.metadata.get("subject", "(no subject)")
|
||||
print(f"📨 From: {message.sender.username}")
|
||||
print(f" Subject: {subject}")
|
||||
print(f" Preview: {message.text[:100]}...")
|
||||
safe_print(f"{icon('📨', '[MSG]')} From: {message.sender.username}")
|
||||
safe_print(f" Subject: {subject}")
|
||||
safe_print(f" Preview: {message.text[:100]}...")
|
||||
|
||||
# Custom command: emails with subject starting with "/status"
|
||||
@bot.on_command("status")
|
||||
@@ -121,10 +124,10 @@ async def run_with_handlers():
|
||||
reply_to=message.message_id,
|
||||
)
|
||||
|
||||
print(f"📩 Starting AgentMail bot with custom handlers")
|
||||
print(f" Email: {bot.email_address or '(will be assigned on start)'}")
|
||||
print(f" Poll interval: {config.polling_interval}s")
|
||||
print(" Listening... (Ctrl+C to stop)")
|
||||
safe_print(f"{icon('📩', '[MAIL]')} Starting AgentMail bot with custom handlers")
|
||||
safe_print(f" Email: {bot.email_address or '(will be assigned on start)'}")
|
||||
safe_print(f" Poll interval: {config.polling_interval}s")
|
||||
safe_print(" Listening... (Ctrl+C to stop)")
|
||||
await bot.start()
|
||||
|
||||
|
||||
@@ -136,30 +139,30 @@ async def run_inbox_demo():
|
||||
token=os.environ.get("AGENTMAIL_API_KEY", ""),
|
||||
)
|
||||
|
||||
print("📩 AgentMail Inbox Lifecycle Demo")
|
||||
print("=" * 50)
|
||||
safe_print(f"{icon('📩', '[MAIL]')} AgentMail Inbox Lifecycle Demo")
|
||||
safe_print("=" * 50)
|
||||
|
||||
# Create inbox
|
||||
print("\n1. Creating inbox...")
|
||||
safe_print("\n1. Creating inbox...")
|
||||
inbox = await bot.create_inbox(domain=os.getenv("AGENTMAIL_DOMAIN"))
|
||||
print(f" ✅ Created: {inbox.get('email_address', 'unknown')}")
|
||||
print(f" ID: {inbox.get('id', 'unknown')}")
|
||||
safe_print(f" {icon('✅', '[OK]')} Created: {inbox.get('email_address', 'unknown')}")
|
||||
safe_print(f" ID: {inbox.get('id', 'unknown')}")
|
||||
|
||||
# List inboxes
|
||||
print("\n2. Listing inboxes...")
|
||||
safe_print("\n2. Listing inboxes...")
|
||||
inboxes = await bot.list_inboxes()
|
||||
print(f" Found: {len(inboxes)} inbox(es)")
|
||||
safe_print(f" Found: {len(inboxes)} inbox(es)")
|
||||
for ib in inboxes:
|
||||
print(f" 📧 {ib.get('email_address', '?')} (id: {ib.get('id', '?')})")
|
||||
safe_print(f" {icon('📧', '[EMAIL]')} {ib.get('email_address', '?')} (id: {ib.get('id', '?')})")
|
||||
|
||||
# Delete inbox
|
||||
inbox_id = inbox.get("id", "")
|
||||
if inbox_id:
|
||||
print(f"\n3. Deleting inbox {inbox_id}...")
|
||||
safe_print(f"\n3. Deleting inbox {inbox_id}...")
|
||||
deleted = await bot.delete_inbox(inbox_id)
|
||||
print(f" {'✅ Deleted' if deleted else '❌ Failed to delete'}")
|
||||
safe_print(f" {icon('✅', '[OK]') + ' Deleted' if deleted else icon('❌', '[X]') + ' Failed to delete'}")
|
||||
|
||||
print("\n✅ Inbox lifecycle complete")
|
||||
safe_print(f"\n{icon('✅', '[OK]')} Inbox lifecycle complete")
|
||||
|
||||
|
||||
# ── Probe: Test connectivity ────────────────────────────────────────
|
||||
@@ -167,17 +170,17 @@ async def run_inbox_demo():
|
||||
async def run_probe():
|
||||
"""Test AgentMail API connectivity."""
|
||||
bot = Bot("agentmail", agent=agent)
|
||||
print("🔍 Testing AgentMail API connectivity...")
|
||||
safe_print(f"{icon('🔍', '[PROBE]')} Testing AgentMail API connectivity...")
|
||||
result = await bot.probe()
|
||||
|
||||
print(f"\n Connection: {'✅ OK' if result.ok else '❌ FAILED'}")
|
||||
print(f" Platform: {result.platform}")
|
||||
safe_print(f"\n Connection: {icon('✅', '[OK]') + ' OK' if result.ok else icon('❌', '[FAIL]') + ' FAILED'}")
|
||||
safe_print(f" Platform: {result.platform}")
|
||||
if result.bot_username:
|
||||
print(f" Email: {result.bot_username}")
|
||||
safe_print(f" Email: {result.bot_username}")
|
||||
if result.elapsed_ms:
|
||||
print(f" Latency: {result.elapsed_ms:.0f}ms")
|
||||
safe_print(f" Latency: {result.elapsed_ms:.0f}ms")
|
||||
if result.error:
|
||||
print(f" Error: {result.error}")
|
||||
safe_print(f" Error: {result.error}")
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -40,7 +40,9 @@ import sys
|
||||
# ── Imports ─────────────────────────────────────────────────────────
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..", "src"))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||
|
||||
from _shared.console import icon, safe_print
|
||||
from praisonaiui.server import create_app
|
||||
import uvicorn
|
||||
|
||||
@@ -88,11 +90,11 @@ def seed_agentmail_channel():
|
||||
})
|
||||
if r.status_code in (200, 201):
|
||||
channel_id = r.json().get("id", "unknown")
|
||||
print(f" ✅ AgentMail channel registered: {channel_id}")
|
||||
safe_print(f" {icon('✅', '[OK]')} AgentMail channel registered: {channel_id}")
|
||||
else:
|
||||
print(f" ⚠️ Channel registration: {r.status_code} — {r.json()}")
|
||||
safe_print(f" {icon('⚠️', '[WARN]')} Channel registration: {r.status_code} - {r.json()}")
|
||||
else:
|
||||
print(" ✓ AgentMail channel already exists")
|
||||
safe_print(f" {icon('✓', '[OK]')} AgentMail channel already exists")
|
||||
|
||||
# Also seed email + telegram for comparison in dashboard
|
||||
for name, platform in [("Support Email", "email"), ("Telegram Support", "telegram")]:
|
||||
@@ -103,7 +105,7 @@ def seed_agentmail_channel():
|
||||
"platform": platform,
|
||||
"config": {},
|
||||
})
|
||||
print(f" ✓ Seeded {platform} channel")
|
||||
safe_print(f" {icon('✓', '[OK]')} Seeded {platform} channel")
|
||||
|
||||
|
||||
# ── Verify agentmail platform support ──────────────────────────────
|
||||
@@ -120,9 +122,9 @@ def verify_agentmail_support():
|
||||
platforms = r.json().get("platforms", [])
|
||||
am_supported = "agentmail" in platforms
|
||||
email_supported = "email" in platforms
|
||||
print(f" Supported platforms: {platforms}")
|
||||
print(f" email supported: {'✅ Yes' if email_supported else '❌ No'}")
|
||||
print(f" agentmail supported: {'✅ Yes' if am_supported else '❌ No'}")
|
||||
safe_print(f" Supported platforms: {platforms}")
|
||||
safe_print(f" email supported: {icon('✅', '[OK]') + ' Yes' if email_supported else icon('❌', '[X]') + ' No'}")
|
||||
safe_print(f" agentmail supported: {icon('✅', '[OK]') + ' Yes' if am_supported else icon('❌', '[X]') + ' No'}")
|
||||
return am_supported
|
||||
return False
|
||||
|
||||
@@ -136,51 +138,51 @@ def test_channel_api():
|
||||
app = create_app()
|
||||
client = TestClient(app)
|
||||
|
||||
print("\n📋 Channel API Test")
|
||||
print("=" * 40)
|
||||
safe_print(f"\n{icon('📋', '[TEST]')} Channel API Test")
|
||||
safe_print("=" * 40)
|
||||
|
||||
# List channels
|
||||
r = client.get("/api/channels")
|
||||
channels = r.json().get("channels", [])
|
||||
print(f" GET /api/channels: {len(channels)} channels")
|
||||
safe_print(f" GET /api/channels: {len(channels)} channels")
|
||||
for ch in channels:
|
||||
icon = {"email": "📧", "agentmail": "📩"}.get(ch.get("platform", ""), "💬")
|
||||
print(f" {icon} {ch.get('name', '?')} ({ch.get('platform', '?')})")
|
||||
ch_icon = {"email": icon("📧", "[email]"), "agentmail": icon("📩", "[mail]")}.get(ch.get("platform", ""), icon("💬", "[chat]"))
|
||||
safe_print(f" {ch_icon} {ch.get('name', '?')} ({ch.get('platform', '?')})")
|
||||
|
||||
# Get agentmail channel status
|
||||
am_ch = next((ch for ch in channels if ch.get("platform") == "agentmail"), None)
|
||||
if am_ch:
|
||||
ch_id = am_ch.get("id")
|
||||
r = client.get(f"/api/channels/{ch_id}/status")
|
||||
print(f" GET /api/channels/{ch_id}/status: {r.json()}")
|
||||
safe_print(f" GET /api/channels/{ch_id}/status: {r.json()}")
|
||||
|
||||
print()
|
||||
safe_print()
|
||||
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("📩 PraisonAIUI — AgentMail Channel Integration")
|
||||
print("=" * 50)
|
||||
print()
|
||||
safe_print(f"{icon('📩', '[MAIL]')} PraisonAIUI - AgentMail Channel Integration")
|
||||
safe_print("=" * 50)
|
||||
safe_print()
|
||||
|
||||
if "--test" in sys.argv:
|
||||
# Test mode: verify APIs + seed data
|
||||
print("🔍 Verifying agentmail platform support...")
|
||||
safe_print(f"{icon('🔍', '[VERIFY]')} Verifying agentmail platform support...")
|
||||
verify_agentmail_support()
|
||||
print()
|
||||
print("🌱 Seeding agentmail channel...")
|
||||
safe_print()
|
||||
safe_print(f"{icon('🌱', '[SEED]')} Seeding agentmail channel...")
|
||||
seed_agentmail_channel()
|
||||
test_channel_api()
|
||||
else:
|
||||
# Run the dashboard
|
||||
print("🌱 Seeding channels...")
|
||||
safe_print(f"{icon('🌱', '[SEED]')} Seeding channels...")
|
||||
seed_agentmail_channel()
|
||||
print()
|
||||
safe_print()
|
||||
|
||||
app = create_app()
|
||||
print(f"✅ Dashboard at http://localhost:8084")
|
||||
print(f" Channels: http://localhost:8084/api/channels")
|
||||
print(f" Platforms: http://localhost:8084/api/channels/platforms")
|
||||
safe_print(f"{icon('✅', '[OK]')} Dashboard at http://localhost:8084")
|
||||
safe_print(" Channels: http://localhost:8084/api/channels")
|
||||
safe_print(" Platforms: http://localhost:8084/api/channels/platforms")
|
||||
host = os.getenv("HOST", "127.0.0.1")
|
||||
uvicorn.run(app, host=host, port=8084, log_level="info")
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Cross-platform console output for examples.
|
||||
|
||||
Legacy Windows consoles default stdout to a non-UTF code page (cp1252/cp437)
|
||||
that cannot encode emoji such as U+1F680. Calling ``print`` with those
|
||||
characters raises ``UnicodeEncodeError`` and crashes the example before the
|
||||
server starts. These helpers mirror the ``_supports_unicode``/``_icon`` pattern
|
||||
in ``praisonaiui.cli`` so examples degrade to ASCII instead of crashing while
|
||||
preserving emoji on UTF-aware terminals.
|
||||
|
||||
Usage:
|
||||
from _shared.console import safe_print, icon
|
||||
|
||||
safe_print(f"{icon('🚀', '[START]')} Starting gateway...")
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
def supports_unicode() -> bool:
|
||||
try:
|
||||
encoding = sys.stdout.encoding or ""
|
||||
except Exception:
|
||||
return False
|
||||
return "utf" in encoding.lower()
|
||||
|
||||
|
||||
def icon(symbol: str, fallback: str) -> str:
|
||||
return symbol if supports_unicode() else fallback
|
||||
|
||||
|
||||
def safe_print(*args: Any, **kwargs: Any) -> None:
|
||||
sep = kwargs.pop("sep", " ")
|
||||
text = sep.join(str(a) for a in args)
|
||||
try:
|
||||
print(text, **kwargs)
|
||||
except UnicodeEncodeError:
|
||||
print(text.encode("ascii", errors="replace").decode("ascii"), **kwargs)
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "aiui"
|
||||
version = "0.3.134"
|
||||
version = "0.3.142"
|
||||
description = "YAML-driven website generator - CLI and compiler"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
|
||||
+89
-58
@@ -3,13 +3,14 @@ import { applyTheme } from './themes'
|
||||
import './index.css'
|
||||
import type { UIConfig, DocsNav, RouteManifest, NavItem } from './types'
|
||||
import { Header } from './Header'
|
||||
import { Sidebar } from './Sidebar'
|
||||
import { Sidebar, MobileNavSheet } from './Sidebar'
|
||||
import { Content } from './Content'
|
||||
import { ZoneWidgets } from './Widgets'
|
||||
import { Toc } from './Toc'
|
||||
import { Footer } from './Footer'
|
||||
import { ChatLayout, AgentUILayout, CopilotWidget, PlaygroundLayout } from './layouts'
|
||||
import { LocaleProvider } from './i18n'
|
||||
import { resolveTemplate, shouldShowToc } from './resolver'
|
||||
|
||||
function SkipLink({ enabled }: { enabled?: boolean }) {
|
||||
if (!enabled) return null
|
||||
@@ -31,12 +32,17 @@ export default function App() {
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [selectedItem, setSelectedItem] = useState<NavItem | null>(null)
|
||||
const [activeItemPath, setActiveItemPath] = useState<string>('')
|
||||
const [currentPath, setCurrentPath] = useState(() => window.location.pathname.replace(/\/$/, '') || '/')
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState(false)
|
||||
|
||||
// Helper function to find nav item by path
|
||||
// Helper function to find nav item by path (searches top-level items and nested children)
|
||||
const findNavItemByPath = (navData: DocsNav, path: string): NavItem | null => {
|
||||
const normalizedPath = path.replace(/\/$/, '') || '/'
|
||||
|
||||
const findItem = (items: NavItem[]): NavItem | null => {
|
||||
for (const item of items) {
|
||||
if (item.path === path || item.path === path.replace(/^\//, '')) {
|
||||
const itemPath = (item.path ?? '').replace(/\/$/, '')
|
||||
if (itemPath === normalizedPath || itemPath === normalizedPath.replace(/^\//, '')) {
|
||||
return item
|
||||
}
|
||||
if (item.children) {
|
||||
@@ -47,23 +53,14 @@ export default function App() {
|
||||
return null
|
||||
}
|
||||
|
||||
// Search all nav groups
|
||||
if (navData.items) {
|
||||
for (const group of navData.items) {
|
||||
if (group.children) {
|
||||
const found = findItem(group.children)
|
||||
if (found) return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
return navData.items ? findItem(navData.items) : null
|
||||
}
|
||||
|
||||
// Update SEO meta tags dynamically
|
||||
const updateSEO = (title: string, path: string, description?: string) => {
|
||||
const updateSEO = (title: string, path: string, description?: string, configOverride?: UIConfig) => {
|
||||
const cfg = configOverride ?? config
|
||||
// Update title using SEO titleTemplate if available
|
||||
const titleTemplate = config.seo?.titleTemplate || '%s | %s'
|
||||
const siteName = config.site?.title || 'Documentation'
|
||||
const titleTemplate = cfg.seo?.titleTemplate || '%s | %s'
|
||||
const siteName = cfg.site?.title || 'Documentation'
|
||||
if (titleTemplate.includes('%s')) {
|
||||
// Replace first %s with page title, second %s (if exists) with site name
|
||||
let formattedTitle = titleTemplate.replace('%s', title)
|
||||
@@ -91,7 +88,7 @@ export default function App() {
|
||||
metaDesc.name = 'description'
|
||||
document.head.appendChild(metaDesc)
|
||||
}
|
||||
metaDesc.content = description || `${title} - ${config.site?.description || ''}`
|
||||
metaDesc.content = description || `${title} - ${cfg.site?.description || ''}`
|
||||
|
||||
// Update Open Graph tags
|
||||
let ogTitle = document.querySelector('meta[property="og:title"]') as HTMLMetaElement
|
||||
@@ -111,8 +108,8 @@ export default function App() {
|
||||
ogUrl.content = window.location.origin + path
|
||||
|
||||
// Update Twitter meta tags if configured
|
||||
if (config.seo?.twitter) {
|
||||
Object.entries(config.seo.twitter).forEach(([key, value]) => {
|
||||
if (cfg.seo?.twitter) {
|
||||
Object.entries(cfg.seo.twitter).forEach(([key, value]) => {
|
||||
let twitterMeta = document.querySelector(`meta[name="twitter:${key}"]`) as HTMLMetaElement
|
||||
if (!twitterMeta) {
|
||||
twitterMeta = document.createElement('meta')
|
||||
@@ -124,17 +121,23 @@ export default function App() {
|
||||
}
|
||||
|
||||
// Set default image if available
|
||||
if (config.seo?.defaultImage) {
|
||||
if (cfg.seo?.defaultImage) {
|
||||
let ogImage = document.querySelector('meta[property="og:image"]') as HTMLMetaElement
|
||||
if (!ogImage) {
|
||||
ogImage = document.createElement('meta')
|
||||
ogImage.setAttribute('property', 'og:image')
|
||||
document.head.appendChild(ogImage)
|
||||
}
|
||||
ogImage.content = config.seo.defaultImage
|
||||
ogImage.content = cfg.seo.defaultImage
|
||||
}
|
||||
}
|
||||
|
||||
const selectNavItem = (item: NavItem) => {
|
||||
setSelectedItem(item)
|
||||
setActiveItemPath(item.path || item.title)
|
||||
updateSEO(item.title, item.path || '/')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
async function loadManifests() {
|
||||
try {
|
||||
@@ -163,12 +166,21 @@ export default function App() {
|
||||
)
|
||||
|
||||
// Handle initial URL path for SPA routing
|
||||
const currentPath = window.location.pathname
|
||||
if (currentPath && currentPath !== '/') {
|
||||
const currentPath = window.location.pathname.replace(/\/$/, '') || '/'
|
||||
setCurrentPath(currentPath)
|
||||
if (currentPath !== '/') {
|
||||
const found = findNavItemByPath(navData, currentPath)
|
||||
if (found) {
|
||||
setSelectedItem(found)
|
||||
setActiveItemPath(found.path || found.title)
|
||||
updateSEO(found.title, found.path || currentPath, undefined, configData)
|
||||
}
|
||||
} else {
|
||||
const docsHome = findNavItemByPath(navData, '/docs/index')
|
||||
if (docsHome) {
|
||||
setSelectedItem(docsHome)
|
||||
setActiveItemPath(docsHome.path || docsHome.title)
|
||||
updateSEO(docsHome.title, docsHome.path || '/docs/index', undefined, configData)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -184,19 +196,21 @@ export default function App() {
|
||||
// Handle browser back/forward navigation in a separate effect
|
||||
useEffect(() => {
|
||||
const handlePopState = () => {
|
||||
const path = window.location.pathname
|
||||
const path = window.location.pathname.replace(/\/$/, '') || '/'
|
||||
setCurrentPath(path)
|
||||
const found = findNavItemByPath(nav, path)
|
||||
|
||||
if (found) {
|
||||
setSelectedItem(found)
|
||||
setActiveItemPath(found.path || found.title)
|
||||
// Use updateSEO function for consistency
|
||||
updateSEO(found.title, path)
|
||||
selectNavItem(found)
|
||||
} else if (path === '/') {
|
||||
setSelectedItem(null)
|
||||
setActiveItemPath('')
|
||||
// Reset title to site title
|
||||
document.title = config.site?.title || 'Documentation'
|
||||
const docsHome = findNavItemByPath(nav, '/docs/index')
|
||||
if (docsHome) {
|
||||
selectNavItem(docsHome)
|
||||
} else {
|
||||
setSelectedItem(null)
|
||||
setActiveItemPath('')
|
||||
document.title = config.site?.title || 'Documentation'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,13 +221,42 @@ export default function App() {
|
||||
const handleItemClick = (item: NavItem) => {
|
||||
setSelectedItem(item)
|
||||
setActiveItemPath(item.path || item.title)
|
||||
// Use History API for SEO-friendly URLs
|
||||
const path = item.path || `/${item.title.toLowerCase().replace(/\s+/g, '-')}`
|
||||
setCurrentPath(path.replace(/\/$/, '') || '/')
|
||||
window.history.pushState({ path }, item.title, path)
|
||||
// Update SEO meta tags
|
||||
updateSEO(item.title, path)
|
||||
}
|
||||
|
||||
const templateMatch = resolveTemplate(
|
||||
currentPath.replace(/^\//, ''),
|
||||
routes,
|
||||
config.templates ?? {},
|
||||
)
|
||||
const activeTemplateKey = templateMatch?.template ?? 'docs'
|
||||
const activeTemplate = config.templates?.[activeTemplateKey] ?? config.templates?.docs
|
||||
const layout = templateMatch?.layout ?? activeTemplate?.layout ?? 'ThreeColumnLayout'
|
||||
const zones = activeTemplate?.zones
|
||||
const showToc = shouldShowToc(templateMatch)
|
||||
const showGlobalFooter = !(layout === 'FlexibleLayout' && (zones?.footer?.length ?? 0) > 0)
|
||||
|
||||
const docsChrome = (
|
||||
<>
|
||||
<MobileNavSheet
|
||||
open={mobileNavOpen}
|
||||
onOpenChange={setMobileNavOpen}
|
||||
nav={nav}
|
||||
activeItem={activeItemPath}
|
||||
onItemClick={handleItemClick}
|
||||
title={config.site?.title || 'Navigation'}
|
||||
/>
|
||||
<Header
|
||||
config={config}
|
||||
templateKey={activeTemplateKey}
|
||||
onMenuClick={() => setMobileNavOpen(true)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="dark flex items-center justify-center min-h-screen bg-background">
|
||||
@@ -231,15 +274,10 @@ export default function App() {
|
||||
)
|
||||
}
|
||||
|
||||
// Determine layout from templates config
|
||||
const layout = config.templates?.docs?.layout || 'ThreeColumnLayout'
|
||||
const zones = config.templates?.docs?.zones
|
||||
|
||||
// Render based on layout type
|
||||
const renderLayout = () => {
|
||||
switch (layout) {
|
||||
case 'TwoColumnLayout':
|
||||
// Sidebar + Content (no TOC)
|
||||
return (
|
||||
<div className="flex">
|
||||
<Sidebar nav={nav} activeItem={activeItemPath} onItemClick={handleItemClick} />
|
||||
@@ -247,7 +285,6 @@ export default function App() {
|
||||
</div>
|
||||
)
|
||||
case 'CenteredLayout':
|
||||
// Centered content, no sidebar
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full max-w-4xl px-6">
|
||||
@@ -257,7 +294,6 @@ export default function App() {
|
||||
</div>
|
||||
)
|
||||
case 'FullWidthLayout':
|
||||
// Full width content
|
||||
return (
|
||||
<div className="px-6">
|
||||
{zones?.hero && zones.hero.length > 0 && <ZoneWidgets widgets={zones.hero} />}
|
||||
@@ -265,12 +301,10 @@ export default function App() {
|
||||
</div>
|
||||
)
|
||||
case 'FlexibleLayout': {
|
||||
// WordPress-style zones layout with navigation invariant
|
||||
// When docs nav exists but no leftSidebar zone configured, render default nav
|
||||
const hasExplicitLeftSidebar = (zones?.leftSidebar?.length ?? 0) > 0
|
||||
const hasNavigation = (nav?.items?.length ?? 0) > 0
|
||||
const shouldRenderNavigation = hasExplicitLeftSidebar || hasNavigation
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{zones?.hero && zones.hero.length > 0 && <ZoneWidgets widgets={zones.hero} />}
|
||||
@@ -287,7 +321,7 @@ export default function App() {
|
||||
<div className="flex-1">
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
</div>
|
||||
<Toc selectedItem={selectedItem} zones={zones} />
|
||||
<Toc selectedItem={selectedItem} zones={zones} showToc={showToc} />
|
||||
</div>
|
||||
{zones?.bottomNav && zones.bottomNav.length > 0 && (
|
||||
<div className="border-t p-4 bg-muted/30">
|
||||
@@ -304,12 +338,11 @@ export default function App() {
|
||||
}
|
||||
case 'ThreeColumnLayout':
|
||||
default:
|
||||
// Classic: Sidebar + Content + TOC with zones
|
||||
return (
|
||||
<div className="flex">
|
||||
<Sidebar nav={nav} activeItem={activeItemPath} onItemClick={handleItemClick} />
|
||||
<Content config={config} routes={routes} selectedItem={selectedItem} />
|
||||
<Toc selectedItem={selectedItem} zones={zones} />
|
||||
{showToc && <Toc selectedItem={selectedItem} zones={zones} showToc={showToc} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -347,14 +380,13 @@ export default function App() {
|
||||
// Dashboard is handled entirely by plugin dashboard.js — React yields
|
||||
return null
|
||||
case 'custom':
|
||||
// For custom, show docs with copilot widget if chat enabled
|
||||
if (config.chat?.enabled) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<SkipLink enabled={config.a11y?.skipToContent} />
|
||||
<Header config={config} />
|
||||
{docsChrome}
|
||||
{renderLayout()}
|
||||
<Footer config={config} />
|
||||
{showGlobalFooter && <Footer config={config} templateKey={activeTemplateKey} />}
|
||||
<CopilotWidget config={config.chat} layout={config.layout} />
|
||||
</div>
|
||||
)
|
||||
@@ -362,23 +394,22 @@ export default function App() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<SkipLink enabled={config.a11y?.skipToContent} />
|
||||
<Header config={config} />
|
||||
{docsChrome}
|
||||
{renderLayout()}
|
||||
<Footer config={config} />
|
||||
{showGlobalFooter && <Footer config={config} templateKey={activeTemplateKey} />}
|
||||
</div>
|
||||
)
|
||||
case 'docs':
|
||||
default:
|
||||
// Docs mode - optionally with copilot widget
|
||||
if (config.chat?.enabled) {
|
||||
const layoutMode = config.layout?.mode
|
||||
if (layoutMode && ['bottom-right', 'bottom-left', 'top-right', 'top-left'].includes(layoutMode)) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<SkipLink enabled={config.a11y?.skipToContent} />
|
||||
<Header config={config} />
|
||||
{docsChrome}
|
||||
{renderLayout()}
|
||||
<Footer config={config} />
|
||||
{showGlobalFooter && <Footer config={config} templateKey={activeTemplateKey} />}
|
||||
<CopilotWidget config={config.chat} layout={config.layout} />
|
||||
</div>
|
||||
)
|
||||
@@ -387,9 +418,9 @@ export default function App() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<SkipLink enabled={config.a11y?.skipToContent} />
|
||||
<Header config={config} />
|
||||
{docsChrome}
|
||||
{renderLayout()}
|
||||
<Footer config={config} />
|
||||
{showGlobalFooter && <Footer config={config} templateKey={activeTemplateKey} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Content component — markdown rendering and landing page
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Markdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { SHADCN_THEMES } from './themes'
|
||||
import { enhanceMkdocsDom } from './markdown/mkdocsEnhance'
|
||||
import { preprocessMkdocsMarkdown } from './markdown/mkdocsPreprocess'
|
||||
import { slugify } from './markdown/slug'
|
||||
import { MobileToc } from './Toc'
|
||||
import type { UIConfig, NavItem, RouteManifest } from './types'
|
||||
|
||||
interface ContentProps {
|
||||
@@ -15,6 +19,7 @@ interface ContentProps {
|
||||
export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
const [markdown, setMarkdown] = useState<string>('')
|
||||
const [loadingContent, setLoadingContent] = useState(false)
|
||||
const articleRef = useRef<HTMLElement>(null)
|
||||
const theme = config.site?.theme
|
||||
|
||||
// Load markdown content when selected item changes
|
||||
@@ -27,12 +32,11 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
const loadContent = async () => {
|
||||
setLoadingContent(true)
|
||||
try {
|
||||
// Convert path like /docs/getting-started/installation to docs/getting-started/installation.md
|
||||
const docPath = (selectedItem.path ?? '').replace(/^\//, '') + '.md'
|
||||
const response = await fetch(`/${docPath}`)
|
||||
if (response.ok) {
|
||||
const content = await response.text()
|
||||
setMarkdown(content)
|
||||
setMarkdown(preprocessMkdocsMarkdown(content))
|
||||
} else {
|
||||
setMarkdown(`*Content for **${selectedItem.title}** not found.*`)
|
||||
}
|
||||
@@ -46,12 +50,19 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
loadContent()
|
||||
}, [selectedItem])
|
||||
|
||||
// Custom components for react-markdown with Tailwind styling
|
||||
useEffect(() => {
|
||||
if (!markdown || !articleRef.current) return
|
||||
enhanceMkdocsDom(articleRef.current)
|
||||
window.dispatchEvent(new CustomEvent('aiui:content-loaded', { detail: { root: articleRef.current } }))
|
||||
}, [markdown])
|
||||
|
||||
const headingId = (children?: React.ReactNode) => slugify(String(children ?? ''))
|
||||
|
||||
const markdownComponents = {
|
||||
h1: ({ children }: { children?: React.ReactNode }) => <h1 className="text-3xl font-bold mt-8 mb-4">{children}</h1>,
|
||||
h2: ({ children }: { children?: React.ReactNode }) => <h2 className="text-2xl font-semibold mt-8 mb-4 text-primary">{children}</h2>,
|
||||
h3: ({ children }: { children?: React.ReactNode }) => <h3 className="text-xl font-semibold mt-6 mb-3">{children}</h3>,
|
||||
h4: ({ children }: { children?: React.ReactNode }) => <h4 className="text-lg font-semibold mt-4 mb-2">{children}</h4>,
|
||||
h1: ({ children }: { children?: React.ReactNode }) => <h1 id={headingId(children)} className="text-3xl font-bold mt-8 mb-4 scroll-mt-20">{children}</h1>,
|
||||
h2: ({ children }: { children?: React.ReactNode }) => <h2 id={headingId(children)} className="text-2xl font-semibold mt-8 mb-4 text-primary scroll-mt-20">{children}</h2>,
|
||||
h3: ({ children }: { children?: React.ReactNode }) => <h3 id={headingId(children)} className="text-xl font-semibold mt-6 mb-3 scroll-mt-20">{children}</h3>,
|
||||
h4: ({ children }: { children?: React.ReactNode }) => <h4 id={headingId(children)} className="text-lg font-semibold mt-4 mb-2 scroll-mt-20">{children}</h4>,
|
||||
p: ({ children }: { children?: React.ReactNode }) => <p className="my-3 text-muted-foreground leading-relaxed">{children}</p>,
|
||||
a: ({ href, children }: { href?: string; children?: React.ReactNode }) => <a href={href} className="text-primary hover:underline">{children}</a>,
|
||||
ul: ({ children }: { children?: React.ReactNode }) => <ul className="list-disc pl-6 my-4 space-y-1">{children}</ul>,
|
||||
@@ -59,16 +70,14 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
li: ({ children }: { children?: React.ReactNode }) => <li className="text-muted-foreground">{children}</li>,
|
||||
blockquote: ({ children }: { children?: React.ReactNode }) => <blockquote className="border-l-4 border-primary pl-4 my-4 italic text-muted-foreground">{children}</blockquote>,
|
||||
code: ({ className, children }: { className?: string; children?: React.ReactNode }) => {
|
||||
// Check if this is inside a pre block (fenced code block)
|
||||
const isInline = !className && String(children).indexOf('\n') === -1
|
||||
if (isInline) {
|
||||
return <code className="bg-primary/10 text-primary px-1.5 py-0.5 rounded text-sm font-mono">{children}</code>
|
||||
}
|
||||
// Block code - rendered by pre wrapper
|
||||
return <code className="block font-mono">{children}</code>
|
||||
return <code className={`block font-mono text-foreground ${className ?? ''}`}>{children}</code>
|
||||
},
|
||||
pre: ({ children }: { children?: React.ReactNode }) => (
|
||||
<pre className="bg-muted p-4 rounded-lg text-sm overflow-auto my-4 font-mono whitespace-pre">{children}</pre>
|
||||
<pre className="bg-muted text-foreground border border-border p-4 rounded-lg text-sm overflow-auto my-4 font-mono whitespace-pre">{children}</pre>
|
||||
),
|
||||
table: ({ children }: { children?: React.ReactNode }) => <div className="overflow-auto my-4"><table className="w-full border-collapse text-sm">{children}</table></div>,
|
||||
thead: ({ children }: { children?: React.ReactNode }) => <thead className="bg-muted/50">{children}</thead>,
|
||||
@@ -83,10 +92,14 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
if (selectedItem) {
|
||||
return (
|
||||
<main id="main-content" className="flex-1 p-8 max-w-3xl">
|
||||
<MobileToc selectedItem={selectedItem} />
|
||||
{loadingContent ? (
|
||||
<div className="text-muted-foreground">Loading content...</div>
|
||||
) : markdown ? (
|
||||
<article className="prose max-w-none">
|
||||
<article
|
||||
ref={articleRef}
|
||||
className="prose prose-neutral dark:prose-invert max-w-none prose-pre:bg-muted prose-pre:text-foreground prose-code:text-foreground"
|
||||
>
|
||||
<Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{markdown}
|
||||
</Markdown>
|
||||
@@ -118,7 +131,7 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
<strong>Current theme from YAML:</strong>
|
||||
</p>
|
||||
<pre className="text-primary text-sm">
|
||||
<pre className="bg-muted text-foreground border border-border p-3 rounded-lg text-sm overflow-auto">
|
||||
{`site:
|
||||
theme:
|
||||
preset: "${theme?.preset || 'zinc'}"
|
||||
@@ -150,7 +163,7 @@ export function Content({ config, routes, selectedItem }: ContentProps) {
|
||||
<p className="text-muted-foreground mb-4">
|
||||
<span className="font-medium text-primary">{routes.routes?.length || 0}</span> routes configured.
|
||||
</p>
|
||||
<pre className="bg-muted p-4 rounded-lg text-sm overflow-auto">
|
||||
<pre className="bg-muted text-foreground border border-border p-4 rounded-lg text-sm overflow-auto">
|
||||
{JSON.stringify(routes.routes?.slice(0, 3), null, 2)}
|
||||
</pre>
|
||||
</main>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Footer component
|
||||
import type { UIConfig } from './types'
|
||||
|
||||
export function Footer({ config }: { config: UIConfig }) {
|
||||
// Resolve footer component via template slot ref (e.g., footer_main)
|
||||
const footerSlot = config.templates?.docs?.slots?.footer
|
||||
export function Footer({ config, templateKey = 'docs' }: { config: UIConfig; templateKey?: string }) {
|
||||
const footerSlot = config.templates?.[templateKey]?.slots?.footer
|
||||
const footerRef = footerSlot?.ref
|
||||
const footerComponent = footerRef
|
||||
? config.components?.[footerRef]
|
||||
|
||||
+32
-11
@@ -1,10 +1,19 @@
|
||||
// Header component
|
||||
import { Menu } from 'lucide-react'
|
||||
import type { UIConfig } from './types'
|
||||
import { LocaleSwitcher } from './i18n'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export function Header({ config }: { config: UIConfig }) {
|
||||
// Resolve header component via template slot ref (e.g., header_main)
|
||||
const headerSlot = config.templates?.docs?.slots?.header
|
||||
export function Header({
|
||||
config,
|
||||
templateKey = 'docs',
|
||||
onMenuClick,
|
||||
}: {
|
||||
config: UIConfig
|
||||
templateKey?: string
|
||||
onMenuClick?: () => void
|
||||
}) {
|
||||
const headerSlot = config.templates?.[templateKey]?.slots?.header
|
||||
const headerRef = headerSlot?.ref
|
||||
const headerComponent = headerRef
|
||||
? config.components?.[headerRef]
|
||||
@@ -16,7 +25,6 @@ export function Header({ config }: { config: UIConfig }) {
|
||||
cta?: { label: string; href: string }
|
||||
} | undefined
|
||||
|
||||
// Get logo configuration - support both header.logoImage and config.logo
|
||||
const logoConfig = config.logo
|
||||
const isDarkMode = document.documentElement.classList.contains('dark')
|
||||
const logoSrc = header?.logoImage || (logoConfig ? (isDarkMode ? (logoConfig.dark || logoConfig.light) : (logoConfig.light || logoConfig.dark)) : null)
|
||||
@@ -24,16 +32,29 @@ export function Header({ config }: { config: UIConfig }) {
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-border/50 bg-background/80 backdrop-blur-xl supports-[backdrop-filter]:bg-background/60">
|
||||
<div className="max-w-screen-2xl mx-auto flex h-16 items-center px-6 gap-8">
|
||||
<a href={logoHref} className="flex items-center gap-3 group cursor-pointer" onClick={(e) => {
|
||||
<div className="max-w-screen-2xl mx-auto flex h-16 items-center px-6 gap-4">
|
||||
{onMenuClick && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="md:hidden shrink-0"
|
||||
onClick={onMenuClick}
|
||||
aria-label="Open navigation menu"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<a href={logoHref} className="flex items-center gap-3 group cursor-pointer min-w-0" onClick={(e) => {
|
||||
e.preventDefault()
|
||||
window.history.pushState({}, '', logoHref)
|
||||
window.location.reload()
|
||||
}}>
|
||||
{logoSrc ? (
|
||||
<img
|
||||
src={logoSrc}
|
||||
alt="Logo"
|
||||
<img
|
||||
src={logoSrc}
|
||||
alt="Logo"
|
||||
className="w-8 h-8 rounded-lg shadow-lg shadow-primary/20 group-hover:shadow-primary/40 transition-shadow object-contain"
|
||||
/>
|
||||
) : (
|
||||
@@ -41,7 +62,7 @@ export function Header({ config }: { config: UIConfig }) {
|
||||
<span className="text-primary-foreground text-xs font-bold">AI</span>
|
||||
</div>
|
||||
)}
|
||||
<span className="font-semibold text-lg tracking-tight">{header?.logoText || config.site?.title || 'PraisonAIUI'}</span>
|
||||
<span className="font-semibold text-lg tracking-tight truncate">{header?.logoText || config.site?.title || 'PraisonAIUI'}</span>
|
||||
</a>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-1 flex-1">
|
||||
@@ -56,7 +77,7 @@ export function Header({ config }: { config: UIConfig }) {
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2 ml-auto">
|
||||
<LocaleSwitcher />
|
||||
{header?.cta && (
|
||||
<a
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
// Sidebar component — navigation tree
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import type { NavItem } from './types'
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet'
|
||||
import type { DocsNav, NavItem } from './types'
|
||||
|
||||
interface SidebarProps {
|
||||
nav: { items?: NavItem[] }
|
||||
interface NavTreeProps {
|
||||
nav: DocsNav
|
||||
activeItem: string
|
||||
onItemClick: (item: NavItem) => void
|
||||
}
|
||||
|
||||
export function Sidebar({ nav, activeItem, onItemClick }: SidebarProps) {
|
||||
export function NavTree({ nav, activeItem, onItemClick }: NavTreeProps) {
|
||||
const renderItem = (item: NavItem, depth = 0) => {
|
||||
const isActive = (item.path || item.title) === activeItem
|
||||
const hasChildren = item.children && item.children.length > 0
|
||||
@@ -30,10 +36,11 @@ export function Sidebar({ nav, activeItem, onItemClick }: SidebarProps) {
|
||||
return (
|
||||
<button
|
||||
key={item.title + (item.path || '')}
|
||||
type="button"
|
||||
onClick={() => onItemClick(item)}
|
||||
className={`w-full text-left px-3 py-1.5 text-sm rounded-md transition-all duration-150 ${isActive
|
||||
? 'bg-primary/10 text-primary font-medium border-l-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50'
|
||||
? 'bg-primary/10 text-primary font-medium border-l-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50'
|
||||
}`}
|
||||
style={{ paddingLeft: `${12 + depth * 12}px` }}
|
||||
>
|
||||
@@ -42,14 +49,52 @@ export function Sidebar({ nav, activeItem, onItemClick }: SidebarProps) {
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="space-y-0.5">
|
||||
{nav.items?.map((group) => renderItem(group))}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
interface SidebarProps extends NavTreeProps {
|
||||
nav: DocsNav
|
||||
}
|
||||
|
||||
export function Sidebar({ nav, activeItem, onItemClick }: SidebarProps) {
|
||||
return (
|
||||
<aside className="w-64 min-w-[16rem] border-r border-border/50 hidden md:block">
|
||||
<ScrollArea className="h-[calc(100vh-4rem)] py-4 px-2">
|
||||
<nav className="space-y-0.5">
|
||||
{nav.items?.map((group) => renderItem(group))}
|
||||
</nav>
|
||||
<NavTree nav={nav} activeItem={activeItem} onItemClick={onItemClick} />
|
||||
<Separator className="my-4" />
|
||||
</ScrollArea>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
||||
interface MobileNavSheetProps extends NavTreeProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
title?: string
|
||||
}
|
||||
|
||||
export function MobileNavSheet({ open, onOpenChange, nav, activeItem, onItemClick, title = 'Navigation' }: MobileNavSheetProps) {
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="left" className="w-72 p-0">
|
||||
<SheetHeader className="border-b px-4 py-3">
|
||||
<SheetTitle>{title}</SheetTitle>
|
||||
</SheetHeader>
|
||||
<ScrollArea className="h-[calc(100vh-4rem)] px-2 py-4">
|
||||
<NavTree
|
||||
nav={nav}
|
||||
activeItem={activeItem}
|
||||
onItemClick={(item) => {
|
||||
onItemClick(item)
|
||||
onOpenChange(false)
|
||||
}}
|
||||
/>
|
||||
</ScrollArea>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
+101
-37
@@ -1,51 +1,115 @@
|
||||
// Table of Contents & right sidebar component
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import type { NavItem, ZonesConfig } from './types'
|
||||
import { ZoneWidgets } from './Widgets'
|
||||
|
||||
export function Toc({ selectedItem, zones }: { selectedItem: NavItem | null; zones?: ZonesConfig }) {
|
||||
interface TocHeading {
|
||||
id: string
|
||||
text: string
|
||||
level: number
|
||||
}
|
||||
|
||||
function collectHeadings(): TocHeading[] {
|
||||
const article = document.querySelector('#main-content article') ?? document.querySelector('article.prose')
|
||||
if (!article) return []
|
||||
|
||||
return Array.from(article.querySelectorAll('h2[id], h3[id], h4[id]')).map((el) => ({
|
||||
id: el.id,
|
||||
text: el.textContent?.trim() ?? '',
|
||||
level: Number(el.tagName.slice(1)),
|
||||
}))
|
||||
}
|
||||
|
||||
function usePageHeadings(selectedItem: NavItem | null) {
|
||||
const [headings, setHeadings] = useState<TocHeading[]>([])
|
||||
|
||||
const refreshHeadings = useCallback(() => {
|
||||
setHeadings(collectHeadings())
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
refreshHeadings()
|
||||
const timer = window.setTimeout(refreshHeadings, 100)
|
||||
return () => window.clearTimeout(timer)
|
||||
}, [selectedItem, refreshHeadings])
|
||||
|
||||
useEffect(() => {
|
||||
const onContentLoaded = () => refreshHeadings()
|
||||
window.addEventListener('aiui:content-loaded', onContentLoaded)
|
||||
return () => window.removeEventListener('aiui:content-loaded', onContentLoaded)
|
||||
}, [refreshHeadings])
|
||||
|
||||
return headings
|
||||
}
|
||||
|
||||
function TocLinks({ headings }: { headings: TocHeading[] }) {
|
||||
if (headings.length === 0) {
|
||||
return <p className="text-sm text-muted-foreground">No sections on this page.</p>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{headings.map((heading) => (
|
||||
<a
|
||||
key={heading.id}
|
||||
href={`#${heading.id}`}
|
||||
className={`flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors ${heading.level === 3 ? 'pl-3 text-sm' : heading.level === 4 ? 'pl-6 text-sm' : 'font-medium'}`}
|
||||
>
|
||||
<span className="w-1 h-1 rounded-full bg-muted-foreground/30 shrink-0" />
|
||||
{heading.text}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function MobileToc({ selectedItem }: { selectedItem: NavItem | null }) {
|
||||
const headings = usePageHeadings(selectedItem)
|
||||
if (!selectedItem || headings.length === 0) return null
|
||||
|
||||
return (
|
||||
<details className="lg:hidden border border-border rounded-lg mb-6 px-4 py-3 bg-muted/20">
|
||||
<summary className="text-sm font-medium cursor-pointer">On this page</summary>
|
||||
<nav className="mt-3">
|
||||
<TocLinks headings={headings} />
|
||||
</nav>
|
||||
</details>
|
||||
)
|
||||
}
|
||||
|
||||
export function Toc({
|
||||
selectedItem,
|
||||
zones,
|
||||
showToc = true,
|
||||
}: {
|
||||
selectedItem: NavItem | null
|
||||
zones?: ZonesConfig
|
||||
showToc?: boolean
|
||||
}) {
|
||||
const headings = usePageHeadings(selectedItem)
|
||||
const rightSidebarWidgets = zones?.rightSidebar || []
|
||||
|
||||
if (!showToc && rightSidebarWidgets.filter((w) => w.type !== 'Toc').length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="w-64 hidden lg:block border-l border-border/50">
|
||||
<div className="sticky top-20 px-4 py-6 space-y-4">
|
||||
{/* Table of Contents */}
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-muted-foreground/70 uppercase tracking-widest mb-4">
|
||||
On this page
|
||||
</h4>
|
||||
<nav className="space-y-2 text-sm">
|
||||
{selectedItem ? (
|
||||
<div className="space-y-2">
|
||||
<a href="#" className="flex items-center gap-2 text-primary font-medium">
|
||||
<span className="w-1 h-1 rounded-full bg-primary" />
|
||||
{selectedItem.title}
|
||||
</a>
|
||||
<a href="#overview" className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors pl-3">
|
||||
Overview
|
||||
</a>
|
||||
<a href="#usage" className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors pl-3">
|
||||
Usage
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<a href="#theme" className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors">
|
||||
<span className="w-1 h-1 rounded-full bg-muted-foreground/30" />
|
||||
Theme Configuration
|
||||
</a>
|
||||
<a href="#presets" className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors">
|
||||
<span className="w-1 h-1 rounded-full bg-muted-foreground/30" />
|
||||
Available Presets
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
{showToc && (
|
||||
<div>
|
||||
<h4 className="text-xs font-semibold text-muted-foreground/70 uppercase tracking-widest mb-4">
|
||||
On this page
|
||||
</h4>
|
||||
<nav className="text-sm">
|
||||
<TocLinks headings={headings} />
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Zone Widgets (excluding Toc) */}
|
||||
{rightSidebarWidgets.filter(w => w.type !== 'Toc').length > 0 && (
|
||||
{rightSidebarWidgets.filter((w) => w.type !== 'Toc').length > 0 && (
|
||||
<div className="pt-4 border-t border-border/50">
|
||||
<ZoneWidgets widgets={rightSidebarWidgets.filter(w => w.type !== 'Toc')} />
|
||||
<ZoneWidgets widgets={rightSidebarWidgets.filter((w) => w.type !== 'Toc')} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -56,3 +56,27 @@
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Readable code blocks in light and dark prose */
|
||||
.prose pre {
|
||||
@apply bg-muted text-foreground border border-border;
|
||||
}
|
||||
|
||||
.prose pre code {
|
||||
@apply text-foreground bg-transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.prose :not(pre) > code {
|
||||
@apply bg-primary/10 text-primary;
|
||||
}
|
||||
|
||||
.prose .aiui-feature-grid > li {
|
||||
@apply list-none;
|
||||
}
|
||||
|
||||
.prose .aiui-tabs pre {
|
||||
@apply my-2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import { isMkdocsTabHeading, mkdocsTabTitle } from './mkdocsPreprocess'
|
||||
|
||||
/** Post-render DOM fixes for MkDocs-style content (tabs, feature cards). */
|
||||
export function enhanceMkdocsDom(root: HTMLElement): void {
|
||||
const articles = new Set<HTMLElement>()
|
||||
if (root.matches('article.prose, main .prose, .prose, main.flex-1, #main-content')) {
|
||||
articles.add(root)
|
||||
}
|
||||
root.querySelectorAll<HTMLElement>('article.prose, main .prose, .prose').forEach((el) => articles.add(el))
|
||||
|
||||
for (const article of articles) {
|
||||
removeRawDivParagraphs(article)
|
||||
enhanceFeatureGrid(article)
|
||||
enhanceTabs(article)
|
||||
}
|
||||
}
|
||||
|
||||
function removeRawDivParagraphs(article: HTMLElement): void {
|
||||
for (const p of article.querySelectorAll('p')) {
|
||||
const text = p.textContent?.trim() ?? ''
|
||||
if (/^<\/?div[\s>]/.test(text) || text === '</div>') {
|
||||
p.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enhanceFeatureGrid(article: HTMLElement): void {
|
||||
for (const ul of article.querySelectorAll('ul')) {
|
||||
if (ul.classList.contains('aiui-feature-grid')) continue
|
||||
|
||||
const firstLi = ul.querySelector('li')
|
||||
const looksLikeCards = Boolean(
|
||||
firstLi?.textContent?.includes('YAML-Driven')
|
||||
|| firstLi?.textContent?.includes('Component Slots')
|
||||
|| firstLi?.querySelector('strong'),
|
||||
)
|
||||
if (!looksLikeCards) continue
|
||||
|
||||
ul.classList.add(
|
||||
'aiui-feature-grid',
|
||||
'grid',
|
||||
'grid-cols-1',
|
||||
'md:grid-cols-2',
|
||||
'gap-4',
|
||||
'my-6',
|
||||
'list-none',
|
||||
'pl-0',
|
||||
)
|
||||
|
||||
for (const li of ul.querySelectorAll('li')) {
|
||||
li.classList.add('border', 'border-border', 'rounded-lg', 'p-4', 'bg-card')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface TabSection {
|
||||
heading: HTMLHeadingElement
|
||||
title: string
|
||||
nodes: Node[]
|
||||
}
|
||||
|
||||
function enhanceTabs(article: HTMLElement): void {
|
||||
if (article.querySelector('[data-aiui-tabs]')) return
|
||||
|
||||
const tabHeadings = Array.from(article.querySelectorAll<HTMLHeadingElement>('h3')).filter((h3) =>
|
||||
isMkdocsTabHeading(h3.textContent?.trim() ?? ''),
|
||||
)
|
||||
if (tabHeadings.length < 2) return
|
||||
|
||||
const sections: TabSection[] = tabHeadings.map((heading) => {
|
||||
const title = mkdocsTabTitle(heading.textContent?.trim() ?? '')
|
||||
const nodes: Node[] = []
|
||||
let sibling = heading.nextSibling
|
||||
|
||||
while (sibling) {
|
||||
if (sibling.nodeType === Node.ELEMENT_NODE) {
|
||||
const el = sibling as HTMLElement
|
||||
if (el.tagName === 'H3' && isMkdocsTabHeading(el.textContent?.trim() ?? '')) break
|
||||
if (['H1', 'H2'].includes(el.tagName)) break
|
||||
}
|
||||
const next = sibling.nextSibling
|
||||
nodes.push(sibling)
|
||||
sibling = next
|
||||
}
|
||||
|
||||
return { heading, title, nodes }
|
||||
})
|
||||
|
||||
const wrapper = document.createElement('div')
|
||||
wrapper.className = 'aiui-tabs my-6 border border-border rounded-lg overflow-hidden'
|
||||
wrapper.dataset.aiuiTabs = 'true'
|
||||
|
||||
const buttons = document.createElement('div')
|
||||
buttons.className = 'aiui-tab-buttons flex flex-wrap gap-0 border-b border-border bg-muted/30'
|
||||
wrapper.appendChild(buttons)
|
||||
|
||||
const panels = document.createElement('div')
|
||||
panels.className = 'aiui-tab-panels'
|
||||
wrapper.appendChild(panels)
|
||||
|
||||
sections.forEach((section, index) => {
|
||||
const btn = document.createElement('button')
|
||||
btn.type = 'button'
|
||||
btn.className = [
|
||||
'aiui-tab-button px-4 py-2 text-sm font-medium transition-colors',
|
||||
index === 0
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground',
|
||||
].join(' ')
|
||||
btn.textContent = section.title
|
||||
btn.dataset.tabIndex = String(index)
|
||||
buttons.appendChild(btn)
|
||||
|
||||
const panel = document.createElement('div')
|
||||
panel.className = index === 0 ? 'aiui-tab-panel p-4 block' : 'aiui-tab-panel p-4 hidden'
|
||||
panel.dataset.tabPanel = String(index)
|
||||
for (const node of section.nodes) {
|
||||
panel.appendChild(node)
|
||||
}
|
||||
panels.appendChild(panel)
|
||||
})
|
||||
|
||||
const insertPoint = sections[0].heading
|
||||
article.insertBefore(wrapper, insertPoint)
|
||||
|
||||
for (const section of sections) {
|
||||
section.heading.remove()
|
||||
}
|
||||
|
||||
buttons.addEventListener('click', (event) => {
|
||||
const target = (event.target as HTMLElement).closest<HTMLButtonElement>('.aiui-tab-button')
|
||||
if (!target) return
|
||||
const index = target.dataset.tabIndex ?? '0'
|
||||
|
||||
for (const btn of buttons.querySelectorAll<HTMLButtonElement>('.aiui-tab-button')) {
|
||||
const active = btn.dataset.tabIndex === index
|
||||
btn.classList.toggle('bg-background', active)
|
||||
btn.classList.toggle('text-foreground', active)
|
||||
btn.classList.toggle('border-b-2', active)
|
||||
btn.classList.toggle('border-primary', active)
|
||||
btn.classList.toggle('text-muted-foreground', !active)
|
||||
}
|
||||
|
||||
for (const panel of panels.querySelectorAll<HTMLElement>('.aiui-tab-panel')) {
|
||||
panel.classList.toggle('hidden', panel.dataset.tabPanel !== index)
|
||||
panel.classList.toggle('block', panel.dataset.tabPanel === index)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/** MkDocs Material syntax → standard markdown for react-markdown. */
|
||||
|
||||
export const MKDOCS_ICON_MAP: Record<string, string> = {
|
||||
':material-file-document:': '📄',
|
||||
':material-puzzle:': '🧩',
|
||||
':material-palette:': '🎨',
|
||||
':material-rocket-launch:': '🚀',
|
||||
':material-code-tags:': '💻',
|
||||
':material-cog:': '⚙️',
|
||||
':material-lightning-bolt:': '⚡',
|
||||
':material-shield:': '🛡️',
|
||||
':material-database:': '🗄️',
|
||||
':material-web:': '🌐',
|
||||
':material-book:': '📖',
|
||||
':material-star:': '⭐',
|
||||
':material-check:': '✅',
|
||||
':material-close:': '❌',
|
||||
':material-alert:': '⚠️',
|
||||
':material-information:': 'ℹ️',
|
||||
}
|
||||
|
||||
const TAB_HEADING_PREFIX = '\u200Btab:'
|
||||
|
||||
export function replaceMkdocsIcons(text: string): string {
|
||||
let result = text
|
||||
for (const [shortcode, emoji] of Object.entries(MKDOCS_ICON_MAP)) {
|
||||
result = result.replaceAll(shortcode, emoji)
|
||||
}
|
||||
return result
|
||||
.replace(/:material-[\w-]+:/g, '•')
|
||||
.replace(/:octicons-[\w-]+(?:-\d+)?:/g, '•')
|
||||
}
|
||||
|
||||
export function stripMkdocsGridWrappers(text: string): string {
|
||||
return text
|
||||
.replace(/<div[^>]*markdown[^>]*>\s*/gi, '')
|
||||
.replace(/<\/div>\s*/gi, '')
|
||||
}
|
||||
|
||||
/** Convert `=== "Title"` tab groups to headings the DOM enhancer recognises. */
|
||||
export function convertMkdocsTabs(md: string): string {
|
||||
const lines = md.split('\n')
|
||||
const out: string[] = []
|
||||
let i = 0
|
||||
|
||||
while (i < lines.length) {
|
||||
const tabMatch = lines[i].match(/^===\s+"([^"]+)"\s*$/)
|
||||
if (!tabMatch) {
|
||||
out.push(lines[i])
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
const title = tabMatch[1]
|
||||
i++
|
||||
while (i < lines.length && lines[i].trim() === '') i++
|
||||
|
||||
const block: string[] = []
|
||||
while (i < lines.length) {
|
||||
if (/^===\s+"[^"]+"\s*$/.test(lines[i])) break
|
||||
if (lines[i].trim() === '' && i + 1 < lines.length && /^===\s+"[^"]+"\s*$/.test(lines[i + 1])) break
|
||||
|
||||
if (lines[i].startsWith(' ') || lines[i].trimStart().startsWith('```')) {
|
||||
block.push(lines[i].replace(/^ /, ''))
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if (block.length > 0 && lines[i].trim() === '') {
|
||||
block.push('')
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
out.push(`### ${TAB_HEADING_PREFIX}${title}`)
|
||||
out.push('')
|
||||
out.push(...block)
|
||||
out.push('')
|
||||
}
|
||||
|
||||
return out.join('\n')
|
||||
}
|
||||
|
||||
export function preprocessMkdocsMarkdown(md: string): string {
|
||||
let result = stripMkdocsGridWrappers(md)
|
||||
result = replaceMkdocsIcons(result)
|
||||
result = convertMkdocsTabs(result)
|
||||
return result
|
||||
}
|
||||
|
||||
export function isMkdocsTabHeading(text: string): boolean {
|
||||
return text.startsWith(TAB_HEADING_PREFIX) || text.startsWith('tab:')
|
||||
}
|
||||
|
||||
export function mkdocsTabTitle(text: string): string {
|
||||
return text.replace(/^\u200Btab:/, '').replace(/^tab:/, '')
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export function slugify(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.replace(/^-|-$/g, '')
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { RouteManifest, SlotConfig, TemplateConfig } from './types'
|
||||
|
||||
export interface TemplateMatch {
|
||||
template: string
|
||||
layout: string
|
||||
slots: Record<string, SlotConfig | null | undefined>
|
||||
slotOverrides?: Record<string, SlotConfig | null>
|
||||
}
|
||||
|
||||
export function matchPattern(path: string, pattern: string): boolean {
|
||||
const normalizedPath = path.replace(/^\/+|\/+$/g, '')
|
||||
const normalizedPattern = pattern.replace(/^\/+|\/+$/g, '')
|
||||
|
||||
const regexPattern = normalizedPattern
|
||||
.replace(/\*\*/g, '{{GLOB}}')
|
||||
.replace(/\*/g, '[^/]+')
|
||||
.replace(/\{\{GLOB\}\}/g, '.*')
|
||||
|
||||
return new RegExp(`^${regexPattern}$`).test(normalizedPath)
|
||||
}
|
||||
|
||||
export function resolveTemplate(
|
||||
path: string,
|
||||
manifest: RouteManifest,
|
||||
templates: Record<string, TemplateConfig>,
|
||||
): TemplateMatch | null {
|
||||
const routes = manifest.routes ?? []
|
||||
const sortedRoutes = [...routes].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0))
|
||||
|
||||
for (const route of sortedRoutes) {
|
||||
if (!matchPattern(path, route.pattern)) continue
|
||||
|
||||
const template = templates[route.template]
|
||||
if (!template) {
|
||||
console.warn(`Template "${route.template}" not found`)
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
template: route.template,
|
||||
layout: template.layout ?? 'ThreeColumnLayout',
|
||||
slots: template.slots ?? {},
|
||||
slotOverrides: route.slotOverrides,
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function mergedSlot(
|
||||
slotName: string,
|
||||
match: TemplateMatch | null,
|
||||
): SlotConfig | null | undefined {
|
||||
if (!match) return undefined
|
||||
if (match.slotOverrides && slotName in match.slotOverrides) {
|
||||
return match.slotOverrides[slotName]
|
||||
}
|
||||
return match.slots[slotName]
|
||||
}
|
||||
|
||||
export function shouldShowToc(match: TemplateMatch | null): boolean {
|
||||
const right = mergedSlot('right', match)
|
||||
return right !== null
|
||||
}
|
||||
@@ -96,7 +96,12 @@ export interface DocsNav {
|
||||
}
|
||||
|
||||
export interface RouteManifest {
|
||||
routes?: { pattern: string; template: string }[]
|
||||
routes?: {
|
||||
pattern: string
|
||||
template: string
|
||||
priority?: number
|
||||
slotOverrides?: Record<string, SlotConfig | null>
|
||||
}[]
|
||||
}
|
||||
|
||||
// Message element types (matching Python schema)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""Version information."""
|
||||
|
||||
__version__ = "0.3.134"
|
||||
__version__ = "0.3.142"
|
||||
@@ -591,7 +591,7 @@ class SQLAlchemyDataStore(BaseDataStore):
|
||||
base = Path(os.environ.get("AIUI_DATA_DIR", str(Path.home() / ".praisonaiui")))
|
||||
base.mkdir(parents=True, exist_ok=True)
|
||||
db_path = base / "aiui.db"
|
||||
return f"sqlite+aiosqlite:///{db_path}"
|
||||
return f"sqlite+aiosqlite:///{db_path.as_posix()}"
|
||||
|
||||
async def _ensure_initialized(self) -> None:
|
||||
"""Lazy initialization of SQLAlchemy engine and tables."""
|
||||
|
||||
@@ -26,6 +26,24 @@ from ._base import BaseFeatureProtocol
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_EMBEDDING_ERROR_MARKERS = (
|
||||
"does not have access",
|
||||
"model_not_found",
|
||||
"does not exist or you do not have access",
|
||||
"notfounderror",
|
||||
)
|
||||
|
||||
|
||||
def _is_embedding_error(err_msg: str) -> bool:
|
||||
"""True when an error string indicates an unavailable embedding model."""
|
||||
lowered = err_msg.lower()
|
||||
return any(marker in lowered for marker in _EMBEDDING_ERROR_MARKERS)
|
||||
|
||||
|
||||
def _is_embedding_unavailable(err: BaseException) -> bool:
|
||||
"""Detect an embedding model access / not-found failure from an exception."""
|
||||
return _is_embedding_error(str(err))
|
||||
|
||||
|
||||
# ── Knowledge Protocol ───────────────────────────────────────────────
|
||||
|
||||
@@ -91,6 +109,10 @@ class KnowledgeProtocol(ABC):
|
||||
"""Get corpus statistics."""
|
||||
return {"total": 0}
|
||||
|
||||
def last_search_meta(self) -> Dict[str, Any]:
|
||||
"""Metadata for the most recent search: {"mode": ..., "error": ...}."""
|
||||
return {"mode": "vector", "error": None}
|
||||
|
||||
def health(self) -> Dict[str, Any]:
|
||||
"""Health check for this backend."""
|
||||
return {"status": "ok", "provider": self.__class__.__name__}
|
||||
@@ -171,8 +193,12 @@ class SimpleKnowledgeManager(KnowledgeProtocol):
|
||||
"status": "ok",
|
||||
"provider": "SimpleKnowledgeManager",
|
||||
"total_entries": len(self._entries),
|
||||
"embedding": {"available": False, "reason": "no_vector_backend"},
|
||||
}
|
||||
|
||||
def last_search_meta(self) -> Dict[str, Any]:
|
||||
return {"mode": "local", "error": None}
|
||||
|
||||
|
||||
# ── SDK Knowledge Manager (wraps praisonaiagents.knowledge) ──────────
|
||||
|
||||
@@ -191,6 +217,9 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
self._sdk_knowledge = None
|
||||
self._sdk_probed = False # True once we've verified SDK actually works
|
||||
self._local_index: Dict[str, Dict[str, Any]] = {}
|
||||
self._embedding_unavailable = False
|
||||
self._embedding_error: Optional[str] = None
|
||||
self._last_search_meta: Dict[str, Any] = {"mode": "vector", "error": None}
|
||||
|
||||
def _get_sdk_knowledge(self) -> Any:
|
||||
"""Lazy-init SDK Knowledge with health probe.
|
||||
@@ -242,6 +271,10 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
"using local in-memory index"
|
||||
)
|
||||
self._sdk_knowledge = None
|
||||
elif _is_embedding_error(err_msg):
|
||||
self._embedding_unavailable = True
|
||||
self._embedding_error = err_msg
|
||||
logger.warning("Embedding model unavailable: %s", e)
|
||||
else:
|
||||
# Other errors (e.g. empty DB) are fine — SDK works
|
||||
pass
|
||||
@@ -249,6 +282,10 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
self._sdk_probed = True
|
||||
return self._sdk_knowledge
|
||||
|
||||
def last_search_meta(self) -> Dict[str, Any]:
|
||||
"""Metadata for the most recent search: {"mode": ..., "error": ...}."""
|
||||
return dict(self._last_search_meta)
|
||||
|
||||
def store(
|
||||
self,
|
||||
text: str,
|
||||
@@ -294,6 +331,7 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
try:
|
||||
results = sdk.search(query, limit=limit, user_id="praisonaiui")
|
||||
if isinstance(results, list) and results:
|
||||
self._last_search_meta = {"mode": "vector", "error": None}
|
||||
return [
|
||||
{
|
||||
"id": str(r.get("id", i)),
|
||||
@@ -304,7 +342,11 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
for i, r in enumerate(results[:limit])
|
||||
]
|
||||
except BaseException as e:
|
||||
err_msg = str(e)
|
||||
logger.warning("SDK knowledge search failed: %s; falling back to local", e)
|
||||
if _is_embedding_error(err_msg):
|
||||
self._embedding_unavailable = True
|
||||
self._embedding_error = err_msg
|
||||
|
||||
# Fallback to local text search
|
||||
query_lower = query.lower()
|
||||
@@ -314,6 +356,21 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
results.append(e)
|
||||
if len(results) >= limit:
|
||||
break
|
||||
|
||||
if self._embedding_unavailable:
|
||||
self._last_search_meta = {
|
||||
"mode": "fallback",
|
||||
"error": (
|
||||
"Vector search unavailable: "
|
||||
f"{self._embedding_error or 'embedding model not accessible'}. "
|
||||
"Set AIUI_EMBEDDING_MODEL=text-embedding-ada-002 or enable embedding "
|
||||
"models in your provider project."
|
||||
),
|
||||
}
|
||||
elif sdk is not None:
|
||||
self._last_search_meta = {"mode": "vector", "error": None}
|
||||
else:
|
||||
self._last_search_meta = {"mode": "local", "error": None}
|
||||
return results
|
||||
|
||||
def add_file(
|
||||
@@ -453,11 +510,25 @@ class SDKKnowledgeManager(KnowledgeProtocol):
|
||||
|
||||
def health(self) -> Dict[str, Any]:
|
||||
sdk = self._get_sdk_knowledge()
|
||||
embedding_available = sdk is not None and not self._embedding_unavailable
|
||||
warnings: List[str] = []
|
||||
embedding: Dict[str, Any] = {"available": embedding_available}
|
||||
if self._embedding_unavailable:
|
||||
embedding["error"] = self._embedding_error
|
||||
warnings.append(
|
||||
"Embedding model unavailable; knowledge search degraded to local text "
|
||||
"matching. Set AIUI_EMBEDDING_MODEL to an accessible model."
|
||||
)
|
||||
status = "ok"
|
||||
if sdk is None or self._embedding_unavailable:
|
||||
status = "degraded"
|
||||
return {
|
||||
"status": "ok" if sdk is not None else "degraded",
|
||||
"status": status,
|
||||
"provider": "SDKKnowledgeManager",
|
||||
"sdk_available": sdk is not None,
|
||||
"total_indexed": len(self._local_index),
|
||||
"embedding": embedding,
|
||||
"warnings": warnings,
|
||||
}
|
||||
|
||||
|
||||
@@ -626,7 +697,15 @@ class KnowledgeFeature(BaseFeatureProtocol):
|
||||
query=query,
|
||||
limit=body.get("limit", 10),
|
||||
)
|
||||
return JSONResponse({"results": results, "count": len(results)})
|
||||
meta = mgr.last_search_meta()
|
||||
return JSONResponse(
|
||||
{
|
||||
"results": results,
|
||||
"count": len(results),
|
||||
"search_mode": meta.get("mode", "unknown"),
|
||||
"warning": meta.get("error"),
|
||||
}
|
||||
)
|
||||
|
||||
async def _add_file(self, request: Request) -> JSONResponse:
|
||||
"""POST /api/knowledge/add-file — ingest a file."""
|
||||
@@ -675,6 +754,8 @@ class KnowledgeFeature(BaseFeatureProtocol):
|
||||
"files": stats.get("files", 0),
|
||||
"backend": h.get("provider", "unknown"),
|
||||
"status": h.get("status", "ok"),
|
||||
"embedding": h.get("embedding", {"available": True}),
|
||||
"warnings": h.get("warnings", []),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ async def run_startup_hooks() -> None:
|
||||
return
|
||||
|
||||
_log.info(f"Running {len(_startup_hooks)} startup hooks")
|
||||
start_time = time.time()
|
||||
start_time = time.perf_counter()
|
||||
|
||||
for hook in _startup_hooks:
|
||||
try:
|
||||
@@ -187,7 +187,7 @@ async def run_startup_hooks() -> None:
|
||||
_log.error(f"Startup hook failed: {getattr(hook, '__name__', str(hook))}: {e}")
|
||||
# Continue with other hooks - individual failures shouldn't break startup
|
||||
|
||||
execution_time = time.time() - start_time
|
||||
execution_time = time.perf_counter() - start_time
|
||||
_lifecycle_state.update(
|
||||
{
|
||||
"startup_completed": True,
|
||||
@@ -209,7 +209,7 @@ async def run_shutdown_hooks() -> None:
|
||||
|
||||
_lifecycle_state["shutdown_initiated"] = True
|
||||
_log.info(f"Running {len(_shutdown_hooks)} shutdown hooks")
|
||||
start_time = time.time()
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# Get shutdown timeout from environment
|
||||
timeout = float(os.environ.get("AIUI_SHUTDOWN_TIMEOUT", "30.0"))
|
||||
@@ -227,7 +227,7 @@ async def run_shutdown_hooks() -> None:
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(_execute_hooks(), timeout=timeout)
|
||||
execution_time = time.time() - start_time
|
||||
execution_time = time.perf_counter() - start_time
|
||||
_lifecycle_state["shutdown_time"] = execution_time
|
||||
_log.info(f"Shutdown hooks completed in {execution_time:.3f}s")
|
||||
except asyncio.TimeoutError:
|
||||
|
||||
@@ -6,6 +6,7 @@ Wraps praisonai.capabilities functions as HTTP endpoints.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from dataclasses import asdict
|
||||
@@ -17,6 +18,35 @@ from starlette.routing import Route
|
||||
|
||||
from ._base import BaseFeatureProtocol
|
||||
|
||||
|
||||
def _default_embedding_model() -> str:
|
||||
"""Resolve the default embedding model from environment."""
|
||||
return os.environ.get(
|
||||
"AIUI_EMBEDDING_MODEL",
|
||||
os.environ.get("OPENAI_EMBEDDING_MODEL", "text-embedding-3-small"),
|
||||
)
|
||||
|
||||
|
||||
def _embedding_fallback_models() -> list[str]:
|
||||
"""Resolve the embedding fallback chain from environment."""
|
||||
raw = os.environ.get(
|
||||
"AIUI_EMBEDDING_FALLBACK_MODELS",
|
||||
"text-embedding-ada-002,text-embedding-3-small",
|
||||
)
|
||||
return [m.strip() for m in raw.split(",") if m.strip()]
|
||||
|
||||
|
||||
def _is_model_not_found(err: BaseException) -> bool:
|
||||
"""Heuristically detect embedding model access / not-found errors."""
|
||||
msg = str(err).lower()
|
||||
return (
|
||||
"does not have access" in msg
|
||||
or "model_not_found" in msg
|
||||
or "does not exist" in msg
|
||||
or "notfounderror" in msg
|
||||
)
|
||||
|
||||
|
||||
# Lazy import capabilities
|
||||
_capabilities = None
|
||||
|
||||
@@ -280,29 +310,57 @@ class OpenAIAPIFeature(BaseFeatureProtocol):
|
||||
|
||||
body = await request.json()
|
||||
input_text = body.get("input", "")
|
||||
model = body.get("model", "text-embedding-3-small")
|
||||
model = body.get("model") or _default_embedding_model()
|
||||
|
||||
try:
|
||||
result = await caps.aembed(
|
||||
input=input_text,
|
||||
model=model,
|
||||
)
|
||||
candidates = [model]
|
||||
for fallback in _embedding_fallback_models():
|
||||
if fallback not in candidates:
|
||||
candidates.append(fallback)
|
||||
|
||||
data = _result_to_dict(result)
|
||||
last_err: BaseException | None = None
|
||||
for candidate in candidates:
|
||||
try:
|
||||
result = await caps.aembed(
|
||||
input=input_text,
|
||||
model=candidate,
|
||||
)
|
||||
data = _result_to_dict(result)
|
||||
return JSONResponse(
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"object": "embedding",
|
||||
"index": 0,
|
||||
"embedding": data.get("embedding", []),
|
||||
}
|
||||
],
|
||||
"model": candidate,
|
||||
"usage": data.get("usage", {"prompt_tokens": 0, "total_tokens": 0}),
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
last_err = e
|
||||
if not _is_model_not_found(e):
|
||||
break
|
||||
|
||||
if last_err is not None and _is_model_not_found(last_err):
|
||||
return JSONResponse(
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{"object": "embedding", "index": 0, "embedding": data.get("embedding", [])}
|
||||
],
|
||||
"model": model,
|
||||
"usage": data.get("usage", {"prompt_tokens": 0, "total_tokens": 0}),
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
return JSONResponse(
|
||||
{"error": {"message": str(e), "type": "api_error"}}, status_code=500
|
||||
"error": {
|
||||
"message": (
|
||||
f"Embedding model unavailable: {last_err}. "
|
||||
"Set AIUI_EMBEDDING_MODEL to a model your project can access "
|
||||
"(e.g. text-embedding-ada-002)."
|
||||
),
|
||||
"type": "model_not_found",
|
||||
}
|
||||
},
|
||||
status_code=503,
|
||||
)
|
||||
return JSONResponse(
|
||||
{"error": {"message": str(last_err), "type": "api_error"}}, status_code=500
|
||||
)
|
||||
|
||||
async def _image_generate(self, request: Request) -> JSONResponse:
|
||||
"""POST /v1/images/generations — Generate images."""
|
||||
|
||||
@@ -2860,7 +2860,14 @@ def create_app(
|
||||
require_auth: bool = False,
|
||||
config_path: Optional[Path] = None,
|
||||
) -> Starlette:
|
||||
"""Create the Starlette application."""
|
||||
"""Create the Starlette application.
|
||||
|
||||
URL-token auth (``TokenQueryMiddleware``) is opt-in: it activates only
|
||||
when ``AIUI_REQUIRE_TOKEN`` is truthy, YAML ``auth.requireToken`` is set,
|
||||
or ``require_auth=True``. A bare ``GATEWAY_AUTH_TOKEN`` /
|
||||
``AIUI_URL_TOKEN`` env var (e.g. leaked into a developer shell after a
|
||||
gateway run) no longer forces 401 on standalone ``/api/*`` (#238, #251).
|
||||
"""
|
||||
from praisonaiui.auth import (
|
||||
AuthMiddleware,
|
||||
login_handler,
|
||||
@@ -2874,10 +2881,13 @@ def create_app(
|
||||
config = load_config_from_yaml(config_path)
|
||||
|
||||
# Extract auth settings from config
|
||||
_config_requires_token = False
|
||||
if config:
|
||||
auth_config = config.get("auth", {})
|
||||
if auth_config.get("requireAuth") or auth_config.get("require_auth"):
|
||||
require_auth = True
|
||||
if auth_config.get("requireToken") or auth_config.get("require_token"):
|
||||
_config_requires_token = True
|
||||
|
||||
middleware = [
|
||||
Middleware(
|
||||
@@ -2891,10 +2901,20 @@ def create_app(
|
||||
# Add optional AUTH_ENFORCE middleware (env var driven)
|
||||
middleware.append(Middleware(AuthEnforcementMiddleware))
|
||||
|
||||
# URL-token auth — opt-in via env. Enables query-param / cookie auth so
|
||||
# dashboards can be shared by URL (Jupyter model).
|
||||
# URL-token auth — opt-in. Enables query-param / cookie auth so dashboards
|
||||
# can be shared by URL (Jupyter model). The mere presence of a token env
|
||||
# var (e.g. GATEWAY_AUTH_TOKEN leaking into a developer shell after a
|
||||
# gateway run) must NOT force auth on standalone create_app() — that broke
|
||||
# ad-hoc TestClient scripts (#238, #251). Activation now requires an
|
||||
# explicit opt-in: AIUI_REQUIRE_TOKEN=1, YAML auth.requireToken, or the
|
||||
# require_auth flag.
|
||||
_tok = os.environ.get("AIUI_URL_TOKEN") or os.environ.get("GATEWAY_AUTH_TOKEN")
|
||||
if _tok:
|
||||
_token_auth_opt_in = (
|
||||
os.environ.get("AIUI_REQUIRE_TOKEN", "").lower() in ("true", "1", "yes")
|
||||
or _config_requires_token
|
||||
or require_auth
|
||||
)
|
||||
if _tok and _token_auth_opt_in:
|
||||
from praisonaiui.auth import TokenQueryMiddleware
|
||||
middleware.append(
|
||||
Middleware(TokenQueryMiddleware, expected_token=_tok)
|
||||
|
||||
@@ -1 +1 @@
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-800:oklch(47% .157 37.304);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-800:oklch(47.6% .114 61.907);--color-green-50:oklch(98.2% .018 155.826);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-emerald-400:oklch(76.5% .177 163.223);--color-cyan-400:oklch(78.9% .154 211.53);--color-blue-50:oklch(97% .014 254.604);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-violet-500:oklch(60.6% .25 292.717);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-pink-400:oklch(71.8% .202 349.761);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-2xl:96rem;--contLine truncated
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-orange-50:oklch(98% .016 73.684);--color-orange-100:oklch(95.4% .038 75.164);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-400:oklch(75% .183 55.934);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-800:oklch(47% .157 37.304);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-yellow-50:oklch(98.7% .026 102.212);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-800:oklch(47.6% .114 61.907);--color-green-50:oklch(98.2% .018 155.826);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-green-800:oklch(44.8% .119 151.328);--color-emerald-400:oklch(76.5% .177 163.223);--color-cyan-400:oklch(78.9% .154 211.53);--color-blue-50:oklch(97% .014 254.604);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-blue-800:oklch(42.4% .199 265.638);--color-blue-900:oklch(37.9% .146 265.522);--color-violet-500:oklch(60.6% .25 292.717);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-pink-400:oklch(71.8% .202 349.761);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-2xl:96rem;--contLine truncated
|
||||
@@ -1,65 +1,18 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index2.js","assets/common-types.js","assets/index3.js"])))=>i.map(i=>d[i]);
|
||||
function j1(e,t){for(var r=0;r<t.length;r++){const a=t[r];if(typeof a!="string"&&!Array.isArray(a)){for(const o in a)if(o!=="default"&&!(o in e)){const s=Object.getOwnPropertyDescriptor(a,o);s&&Object.defineProperty(e,o,s.get?s:{enumerable:!0,get:()=>a[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))a(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const u of s.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&a(u)}).observe(document,{childList:!0,subtree:!0});function r(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function a(o){if(o.ep)return;o.ep=!0;const s=r(o);fetch(o.href,s)}})();function Qu(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var of={exports:{}},al={};var LT;function U1(){if(LT)return al;LT=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function r(a,o,s){var u=null;if(s!==void 0&&(u=""+s),o.key!==void 0&&(u=""+o.key),"key"in o){s={};for(var d in o)d!=="key"&&(s[d]=o[d])}else s=o;return o=s.ref,{$$typeof:e,type:a,key:u,ref:o!==void 0?o:null,props:s}}return al.Fragment=t,al.jsx=r,al.jsxs=r,al}var DT;function F1(){return DT||(DT=1,of.exports=U1()),of.exports}var g=F1(),lf={exports:{}},Ne={};var MT;function B1(){if(MT)return Ne;MT=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),u=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),f=Symbol.for("react.memo"),h=Symbol.for("react.lazy"),b=Symbol.for("react.activity"),v=Symbol.iterator;function E(U){return U===null||typeof U!="object"?null:(U=v&&U[v]||U["@@iterator"],typeof U=="function"?U:null)}var w={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},T=Object.assign,k={};function A(U,J,I){this.props=U,this.context=J,this.refs=k,this.updater=I||w}A.prototype.isReactComponent={},A.prototype.setState=function(U,J){if(typeof U!="object"&&typeof U!="function"&&U!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,U,J,"setState")},A.prototype.forceUpdate=function(U){this.updater.enqueueForceUpdate(this,U,"forceUpdate")};function _(){}_.prototype=A.prototype;function R(U,J,I){this.props=U,this.context=J,this.refs=k,this.updater=I||w}var L=R.prototype=new _;L.constructor=R,T(L,A.prototype),L.isPureReactComponent=!0;var j=Array.isArray;function D(){}var z={H:null,A:null,T:null,S:null},F=Object.prototype.hasOwnProperty;function V(U,J,I){var ue=I.ref;return{$$typeof:e,type:U,key:J,ref:ue!==void 0?ue:null,props:I}}function M(U,J){return V(U.type,J,U.props)}function H(U){return typeof U=="object"&&U!==null&&U.$$typeof===e}function K(U){var J={"=":"=0",":":"=2"};return"$"+U.replace(/[=:]/g,function(I){return J[I]})}var Z=/\/+/g;function te(U,J){return typeof U=="object"&&U!==null&&U.key!=null?K(""+U.key):J.toString(36)}function Q(U){switch(U.status){case"fulfilled":return U.value;case"rejected":throw U.reason;default:switch(typeof U.status=="string"?U.then(D,D):(U.status="pending",U.then(function(J){U.status==="pending"&&(U.status="fulfilled",U.value=J)},function(J){U.status==="pending"&&(U.status="rejected",U.reason=J)})),U.status){case"fulfilled":return U.value;case"rejected":throw U.reason}}throw U}function P(U,J,I,ue,fe){var de=typeof U;(de==="undefined"||de==="boolean")&&(U=null);var le=!1;if(U===null)le=!0;else switch(de){case"bigint":case"string":case"number":le=!0;break;case"object":switch(U.$$typeof){case e:case t:le=!0;break;case h:return le=U._init,P(le(U._payload),J,I,ue,fe)}}if(le)return fe=fe(U),le=ue===""?"."+te(U,0):ue,j(fe)?(I="",le!=null&&(I=le.replace(Z,"$&/")+"/"),P(fe,J,I,"",function(Re){return Re})):fe!=null&&(H(fe)&&(fe=M(fe,I+(fe.key==null||U&&U.key===fe.key?"":(""+fe.key).replace(Z,"$&/")+"/")+le)),J.push(fe)),1;le=0;var we=ue===""?".":ue+":";if(j(U))for(var me=0;me<U.length;me++)ue=U[me],de=we+te(ue,me),le+=P(ue,J,I,de,fe);else if(me=E(U),typeof me=="function")for(U=me.call(U),me=0;!(ue=U.next()).done;)ue=ue.value,de=we+te(ue,me++),le+=P(ue,J,I,de,fe);else if(de==="object"){if(typeof U.then=="function")return P(Q(U),J,I,ue,fe);throw J=String(U),Error("Objects are not valid as a React child (found: "+(J==="[object Object]"?"object with keys {"+Object.keys(U).join(", ")+"}":J)+"). If you meant to render a collection of children, use an arLine truncated
|
||||
function NL(e,t){for(var r=0;r<t.length;r++){const a=t[r];if(typeof a!="string"&&!Array.isArray(a)){for(const o in a)if(o!=="default"&&!(o in e)){const s=Object.getOwnPropertyDescriptor(a,o);s&&Object.defineProperty(e,o,s.get?s:{enumerable:!0,get:()=>a[o]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))a(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const u of s.addedNodes)u.tagName==="LINK"&&u.rel==="modulepreload"&&a(u)}).observe(document,{childList:!0,subtree:!0});function r(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function a(o){if(o.ep)return;o.ep=!0;const s=r(o);fetch(o.href,s)}})();function ac(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ff={exports:{}},ul={};var XT;function RL(){if(XT)return ul;XT=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function r(a,o,s){var u=null;if(s!==void 0&&(u=""+s),o.key!==void 0&&(u=""+o.key),"key"in o){s={};for(var c in o)c!=="key"&&(s[c]=o[c])}else s=o;return o=s.ref,{$$typeof:e,type:a,key:u,ref:o!==void 0?o:null,props:s}}return ul.Fragment=t,ul.jsx=r,ul.jsxs=r,ul}var ZT;function CL(){return ZT||(ZT=1,ff.exports=RL()),ff.exports}var g=CL(),gf={exports:{}},Re={};var QT;function IL(){if(QT)return Re;QT=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),o=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),u=Symbol.for("react.context"),c=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),f=Symbol.for("react.memo"),h=Symbol.for("react.lazy"),m=Symbol.for("react.activity"),v=Symbol.iterator;function E(U){return U===null||typeof U!="object"?null:(U=v&&U[v]||U["@@iterator"],typeof U=="function"?U:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,k={};function A(U,B,C){this.props=U,this.context=B,this.refs=k,this.updater=C||x}A.prototype.isReactComponent={},A.prototype.setState=function(U,B){if(typeof U!="object"&&typeof U!="function"&&U!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,U,B,"setState")},A.prototype.forceUpdate=function(U){this.updater.enqueueForceUpdate(this,U,"forceUpdate")};function _(){}_.prototype=A.prototype;function N(U,B,C){this.props=U,this.context=B,this.refs=k,this.updater=C||x}var D=N.prototype=new _;D.constructor=N,w(D,A.prototype),D.isPureReactComponent=!0;var F=Array.isArray;function L(){}var z={H:null,A:null,T:null,S:null},$=Object.prototype.hasOwnProperty;function K(U,B,C){var J=C.ref;return{$$typeof:e,type:U,key:B,ref:J!==void 0?J:null,props:C}}function j(U,B){return K(U.type,B,U.props)}function q(U){return typeof U=="object"&&U!==null&&U.$$typeof===e}function X(U){var B={"=":"=0",":":"=2"};return"$"+U.replace(/[=:]/g,function(C){return B[C]})}var ne=/\/+/g;function re(U,B){return typeof U=="object"&&U!==null&&U.key!=null?X(""+U.key):B.toString(36)}function ee(U){switch(U.status){case"fulfilled":return U.value;case"rejected":throw U.reason;default:switch(typeof U.status=="string"?U.then(L,L):(U.status="pending",U.then(function(B){U.status==="pending"&&(U.status="fulfilled",U.value=B)},function(B){U.status==="pending"&&(U.status="rejected",U.reason=B)})),U.status){case"fulfilled":return U.value;case"rejected":throw U.reason}}throw U}function M(U,B,C,J,ae){var ue=typeof U;(ue==="undefined"||ue==="boolean")&&(U=null);var ie=!1;if(U===null)ie=!0;else switch(ue){case"bigint":case"string":case"number":ie=!0;break;case"object":switch(U.$$typeof){case e:case t:ie=!0;break;case h:return ie=U._init,M(ie(U._payload),B,C,J,ae)}}if(ie)return ae=ae(U),ie=J===""?"."+re(U,0):J,F(ae)?(C="",ie!=null&&(C=ie.replace(ne,"$&/")+"/"),M(ae,B,C,"",function(ke){return ke})):ae!=null&&(q(ae)&&(ae=j(ae,C+(ae.key==null||U&&U.key===ae.key?"":(""+ae.key).replace(ne,"$&/")+"/")+ie)),B.push(ae)),1;ie=0;var he=J===""?".":J+":";if(F(U))for(var fe=0;fe<U.length;fe++)J=U[fe],ue=he+re(J,fe),ie+=M(J,B,C,ue,ae);else if(fe=E(U),typeof fe=="function")for(U=fe.call(U),fe=0;!(J=U.next()).done;)J=J.value,ue=he+re(J,fe++),ie+=M(J,B,C,ue,ae);else if(ue==="object"){if(typeof U.then=="function")return M(ee(U),B,C,J,ae);throw B=String(U),Error("Objects are not valid as a React child (found: "+(B==="[object Object]"?"object with keys {"+Object.keys(U).join(", ")+"}":B)+"). If you meant to render a collection of children, use an array instead."Line truncated
|
||||
at`)?" (<anonymous>)":-1<l.stack.indexOf("@")?"@unknown:0:0":""}return`
|
||||
`+qe+n+dt}var qn=!1;function Yn(n,i){if(!n||qn)return"";qn=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var c={DetermineComponentFrameRoot:function(){try{if(i){var oe=function(){throw Error()};if(Object.defineProperty(oe.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(oe,[])}catch(ee){var X=ee}Reflect.construct(n,[],oe)}else{try{oe.call()}catch(ee){X=ee}n.call(oe.prototype)}}else{try{throw Error()}catch(ee){X=ee}(oe=n())&&typeof oe.catch=="function"&&oe.catch(function(){})}}catch(ee){if(ee&&X&&typeof ee.stack=="string")return[ee.stack,X.stack]}return[null,null]}};c.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var m=Object.getOwnPropertyDescriptor(c.DetermineComponentFrameRoot,"name");m&&m.configurable&&Object.defineProperty(c.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var y=c.DetermineComponentFrameRoot(),x=y[0],N=y[1];if(x&&N){var B=x.split(`
|
||||
`),Y=N.split(`
|
||||
`);for(m=c=0;c<B.length&&!B[c].includes("DetermineComponentFrameRoot");)c++;for(;m<Y.length&&!Y[m].includes("DetermineComponentFrameRoot");)m++;if(c===B.length||m===Y.length)for(c=B.length-1,m=Y.length-1;1<=c&&0<=m&&B[c]!==Y[m];)m--;for(;1<=c&&0<=m;c--,m--)if(B[c]!==Y[m]){if(c!==1||m!==1)do if(c--,m--,0>m||B[c]!==Y[m]){var re=`
|
||||
`+B[c].replace(" at new "," at ");return n.displayName&&re.includes("<anonymous>")&&(re=re.replace("<anonymous>",n.displayName)),re}while(1<=c&&0<=m);break}}}finally{qn=!1,Error.prepareStackTrace=l}return(l=n?n.displayName||n.name:"")?st(l):""}function Cr(n,i){switch(n.tag){case 26:case 27:case 5:return st(n.type);case 16:return st("Lazy");case 13:return n.child!==i&&i!==null?st("Suspense Fallback"):st("Suspense");case 19:return st("SuspenseList");case 0:case 15:return Yn(n.type,!1);case 11:return Yn(n.type.render,!1);case 1:return Yn(n.type,!0);case 31:return st("Activity");default:return""}}function ql(n){try{var i="",l=null;do i+=Cr(n,l),l=n,n=n.return;while(n);return i}catch(c){return`
|
||||
Error generating stack: `+c.message+`
|
||||
`+c.stack}}var Wa=Object.prototype.hasOwnProperty,Ka=e.unstable_scheduleCallback,po=e.unstable_cancelCallback,Hc=e.unstable_shouldYield,Vc=e.unstable_requestPaint,zt=e.unstable_now,qc=e.unstable_getCurrentPriorityLevel,ae=e.unstable_ImmediatePriority,pe=e.unstable_UserBlockingPriority,ke=e.unstable_NormalPriority,Me=e.unstable_LowPriority,We=e.unstable_IdlePriority,nn=e.log,or=e.unstable_setDisableYieldValue,Pt=null,vt=null;function Vt(n){if(typeof nn=="function"&&or(n),vt&&typeof vt.setStrictMode=="function")try{vt.setStrictMode(Pt,n)}catch{}}var tt=Math.clz32?Math.clz32:xO,Or=Math.log,In=Math.LN2;function xO(n){return n>>>=0,n===0?32:31-(Or(n)/In|0)|0}var Yl=256,Wl=262144,Kl=4194304;function ma(n){var i=n&42;if(i!==0)return i;switch(n&-n){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return n&261888;case 262144:case 524288:case 1048576:case 2097152:return n&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return n&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return n}}function Xl(n,i,l){var c=n.pendingLanes;if(c===0)return 0;var m=0,y=n.suspendedLanes,x=n.pingedLanes;n=n.warmLanes;var N=c&134217727;return N!==0?(c=N&~y,c!==0?m=ma(c):(x&=N,x!==0?m=ma(x):l||(l=N&~n,l!==0&&(m=ma(l))))):(N=c&~y,N!==0?m=ma(N):x!==0?m=ma(x):l||(l=c&~n,l!==0&&(m=ma(l)))),m===0?0:i!==0&&i!==m&&(i&y)===0&&(y=m&-m,l=i&-i,y>=l||y===32&&(l&4194048)!==0)?i:m}function fo(n,i){return(n.pendingLanes&~(n.suspendedLanes&~n.pingedLanes)&i)===0}function wO(n,i){switch(n){case 1:case 2:case 4:case 8:case 64:return i+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return i+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Uv(){var n=Kl;return Kl<<=1,(Kl&62914560)===0&&(Kl=4194304),n}function Yc(n){for(var i=[],l=0;31>l;l++)i.push(n);return i}function go(n,i){n.pendingLanes|=i,i!==268435456&&(n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0)}function TO(n,i,l,c,m,y){var x=n.pendingLanes;n.pendingLanes=l,n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0,n.expiredLanes&=l,n.entangledLanes&=l,n.errorRecoveryDisabledLanes&=l,n.shellSuspendCounter=0;var N=n.entanglements,B=n.expirationTimes,Y=n.hiddenUpdates;for(l=x&~l;0<l;){var re=31-tt(l),oe=1<<re;N[re]=0,B[re]=-1;var X=Y[re];if(X!==null)for(Y[re]=null,re=0;re<X.length;re++){var ee=X[re];ee!==null&&(ee.lane&=-536870913)}l&=~oe}c!==0&&Fv(n,c,0),y!==0&&m===0&&n.tag!==0&&(n.suspendedLanes|=y&~(x&~i))}function Fv(n,i,l){n.pendingLanes|=i,n.suspendedLanes&=~i;var c=31-tt(i);n.entangledLanes|=i,n.entanglements[c]=n.entanglements[c]|1073741824|l&261930}function Bv(n,i){var l=n.entangledLanes|=i;for(n=n.entanglements;l;){var c=31-tt(l),m=1<<c;m&i|n[c]&i&&(n[c]|=i),l&=~m}}function zv(n,i){var l=i&-i;return l=(l&42)!==0?1:Wc(l),(l&(n.suspendedLanes|i))!==0?0:l}function Wc(n){switch(n){case 2:n=1;break;case 8:n=4;break;case 32:n=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:n=128;break;case 268435456:n=134217728;break;default:n=0}return n}function Kc(n){return n&=-n,2<n?8<n?(n&134217727)!==0?32:268435456:8:2}function Pv(){var n=W.p;return n!==0?n:(n=window.event,n===void 0?32:kT(n.type))}function $v(n,i){var l=W.p;try{return W.p=n,i()}finally{W.p=l}}var Ir=Math.random().toString(36).slice(2),kt="__reactFiber$"+Ir,qt="__reactProps$"+Ir,Xa="__reactContainer$"+Ir,Xc="__reactEvents$"+Ir,AO="__reactListeners$"+Ir,kO="__reactHandles$"+Ir,Gv="__reactResources$"+Ir,mo="__reactMarker$"+Ir;function Zc(n){delete n[kt],delete n[qt],delete n[Xc],delete n[AO],delete n[kO]}function Za(n){var i=n[kt];if(i)return i;for(var l=n.parentNode;l;){if(i=l[Xa]||l[kt]){if(l=i.alternate,i.child!==null||l!==null&&l.child!==null)for(n=cT(n);n!==null;){if(l=n[kt])return l;n=cT(n)}return i}n=l,l=n.parentNode}return null}function Qa(n){if(n=n[kt]||n[Xa]){var i=n.tag;if(i===5||i===6||i===13||i===31||i===26||i===27||i===3)return n}return null}function ho(n){var i=n.tag;if(i===5||i===26||i===27||i===6)return n.stateNode;throw Error(a(33))}function Ja(n){var i=n[Gv];return i||(i=n[Gv]={hoistableStyles:new Map,hoistableScripts:new Map}),i}function wt(n){n[mo]=!0}var Hv=new Set,Vv={};function ha(n,i){ei(n,i),ei(n+"Capture",i)}function ei(n,i){for(Vv[n]=i,n=0;n<i.length;n++)Hv.add(i[n])}var _O=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u20
Line truncated
|
||||
`).replace(a1,"")}function eT(n,i){return i=Jw(i),Jw(n)===i}function Je(n,i,l,c,m,y){switch(l){case"children":typeof c=="string"?i==="body"||i==="textarea"&&c===""||ni(n,c):(typeof c=="number"||typeof c=="bigint")&&i!=="body"&&ni(n,""+c);break;case"className":Ql(n,"class",c);break;case"tabIndex":Ql(n,"tabindex",c);break;case"dir":case"role":case"viewBox":case"width":case"height":Ql(n,l,c);break;case"style":eS(n,c,y);break;case"data":if(i!=="object"){Ql(n,"data",c);break}case"src":case"href":if(c===""&&(i!=="a"||l!=="href")){n.removeAttribute(l);break}if(c==null||typeof c=="function"||typeof c=="symbol"||typeof c=="boolean"){n.removeAttribute(l);break}c=es(""+c),n.setAttribute(l,c);break;case"action":case"formAction":if(typeof c=="function"){n.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof y=="function"&&(l==="formAction"?(i!=="input"&&Je(n,i,"name",m.name,m,null),Je(n,i,"formEncType",m.formEncType,m,null),Je(n,i,"formMethod",m.formMethod,m,null),Je(n,i,"formTarget",m.formTarget,m,null)):(Je(n,i,"encType",m.encType,m,null),Je(n,i,"method",m.method,m,null),Je(n,i,"target",m.target,m,null)));if(c==null||typeof c=="symbol"||typeof c=="boolean"){n.removeAttribute(l);break}c=es(""+c),n.setAttribute(l,c);break;case"onClick":c!=null&&(n.onclick=sr);break;case"onScroll":c!=null&&Ue("scroll",n);break;case"onScrollEnd":c!=null&&Ue("scrollend",n);break;case"dangerouslySetInnerHTML":if(c!=null){if(typeof c!="object"||!("__html"in c))throw Error(a(61));if(l=c.__html,l!=null){if(m.children!=null)throw Error(a(60));n.innerHTML=l}}break;case"multiple":n.multiple=c&&typeof c!="function"&&typeof c!="symbol";break;case"muted":n.muted=c&&typeof c!="function"&&typeof c!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(c==null||typeof c=="function"||typeof c=="boolean"||typeof c=="symbol"){n.removeAttribute("xlink:href");break}l=es(""+c),n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":c!=null&&typeof c!="function"&&typeof c!="symbol"?n.setAttribute(l,""+c):n.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":c&&typeof c!="function"&&typeof c!="symbol"?n.setAttribute(l,""):n.removeAttribute(l);break;case"capture":case"download":c===!0?n.setAttribute(l,""):c!==!1&&c!=null&&typeof c!="function"&&typeof c!="symbol"?n.setAttribute(l,c):n.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":c!=null&&typeof c!="function"&&typeof c!="symbol"&&!isNaN(c)&&1<=c?n.setAttribute(l,c):n.removeAttribute(l);break;case"rowSpan":case"start":c==null||typeof c=="function"||typeof c=="symbol"||isNaN(c)?n.removeAttribute(l):n.setAttribute(l,c);break;case"popover":Ue("beforetoggle",n),Ue("toggle",n),Zl(n,"popover",c);break;case"xlinkActuate":lr(n,"http://www.w3.org/1999/xlink","xlink:actuate",c);break;case"xlinkArcrole":lr(n,"http://www.w3.org/1999/xlink","xlink:arcrole",c);break;case"xlinkRole":lr(n,"http://www.w3.org/1999/xlink","xlink:role",c);break;case"xlinkShow":lr(n,"http://www.w3.org/1999/xlink","xlink:show",c);break;case"xlinkTitle":lr(n,"http://www.w3.org/1999/xlink","xlink:title",c);break;case"xlinkType":lr(n,"http://www.w3.org/1999/xlink","xlink:type",c);break;case"xmlBase":lr(n,"http://www.w3.org/XML/1998/namespace","xml:base",c);break;case"xmlLang":lr(n,"http://www.w3.org/XML/1998/namespace","xml:lang",c);break;case"xmlSpace":lr(n,"http://www.w3.org/XML/1998/namespace","xml:space",c);break;case"is":Zl(n,"is",c);break;case"innerText":case"textContent":break;default:(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")&&(l=IO.get(l)||l,Zl(n,l,c))}}function Pp(n,i,l,c,m,y){switch(l){case"style":eS(n,c,y);break;case"dangerouslySetInnerHTML":if(c!=null){if(typeof c!="object"||!("__html"in c))throw Error(a(61));if(l=c.__html,l!=null){if(m.children!=null)throw Error(a(60));n.innerHTML=l}}break;case"children":typeof c=="string"?ni(n,c):(typeof c=="number"||typeof c=="bigint")&&ni(n,""+c);break;case"onScroll":c!=null&&Ue("scroll",n);break;case"onScrollEnd":c!=null&&Ue("scrollend",n);break;case"onClick":c!=null&&(n.onclick=sr);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"inne
Line truncated
|
||||
`,f="/",h="*",b="",v="comment",E="declaration";function w(k,A){if(typeof k!="string")throw new TypeError("First argument must be a string");if(!k)return[];A=A||{};var _=1,R=1;function L(te){var Q=te.match(t);Q&&(_+=Q.length);var P=te.lastIndexOf(p);R=~P?te.length-P:R+te.length}function j(){var te={line:_,column:R};return function(Q){return Q.position=new D(te),V(),Q}}function D(te){this.start=te,this.end={line:_,column:R},this.source=A.source}D.prototype.content=k;function z(te){var Q=new Error(A.source+":"+_+":"+R+": "+te);if(Q.reason=te,Q.filename=A.source,Q.line=_,Q.column=R,Q.source=k,!A.silent)throw Q}function F(te){var Q=te.exec(k);if(Q){var P=Q[0];return L(P),k=k.slice(P.length),Q}}function V(){F(r)}function M(te){var Q;for(te=te||[];Q=H();)Q!==!1&&te.push(Q);return te}function H(){var te=j();if(!(f!=k.charAt(0)||h!=k.charAt(1))){for(var Q=2;b!=k.charAt(Q)&&(h!=k.charAt(Q)||f!=k.charAt(Q+1));)++Q;if(Q+=2,b===k.charAt(Q-1))return z("End of comment missing");var P=k.slice(2,Q-2);return R+=2,L(P),k=k.slice(Q),R+=2,te({type:v,comment:P})}}function K(){var te=j(),Q=F(a);if(Q){if(H(),!F(o))return z("property missing ':'");var P=F(s),W=te({type:E,property:T(Q[0].replace(e,b)),value:P?T(P[0].replace(e,b)):b});return F(u),W}}function Z(){var te=[];M(te);for(var Q;Q=K();)Q!==!1&&(te.push(Q),M(te));return te}return V(),Z()}function T(k){return k?k.replace(d,b):b}return mf=w,mf}var iA;function VD(){if(iA)return Di;iA=1;var e=Di&&Di.__importDefault||function(a){return a&&a.__esModule?a:{default:a}};Object.defineProperty(Di,"__esModule",{value:!0}),Di.default=r;const t=e(HD());function r(a,o){let s=null;if(!a||typeof a!="string")return s;const u=(0,t.default)(a),d=typeof o=="function";return u.forEach(p=>{if(p.type!=="declaration")return;const{property:f,value:h}=p;d?o(f,h,p):h&&(s=s||{},s[f]=h)}),s}return Di}var ll={},oA;function qD(){if(oA)return ll;oA=1,Object.defineProperty(ll,"__esModule",{value:!0}),ll.camelCase=void 0;var e=/^--[a-zA-Z0-9_-]+$/,t=/-([a-z])/g,r=/^[^-]+$/,a=/^-(webkit|moz|ms|o|khtml)-/,o=/^-(ms)-/,s=function(f){return!f||r.test(f)||e.test(f)},u=function(f,h){return h.toUpperCase()},d=function(f,h){return"".concat(h,"-")},p=function(f,h){return h===void 0&&(h={}),s(f)?f:(f=f.toLowerCase(),h.reactCompat?f=f.replace(o,d):f=f.replace(a,d),f.replace(t,u))};return ll.camelCase=p,ll}var sl,lA;function YD(){if(lA)return sl;lA=1;var e=sl&&sl.__importDefault||function(o){return o&&o.__esModule?o:{default:o}},t=e(VD()),r=qD();function a(o,s){var u={};return!o||typeof o!="string"||(0,t.default)(o,function(d,p){d&&p&&(u[(0,r.camelCase)(d,s)]=p)}),u}return a.default=a,sl=a,sl}var WD=YD();const KD=Qu(WD),M_=j_("end"),jg=j_("start");function j_(e){return t;function t(r){const a=r&&r.position&&r.position[e]||{};if(typeof a.line=="number"&&a.line>0&&typeof a.column=="number"&&a.column>0)return{line:a.line,column:a.column,offset:typeof a.offset=="number"&&a.offset>-1?a.offset:void 0}}}function XD(e){const t=jg(e),r=M_(e);if(t&&r)return{start:t,end:r}}function gl(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?sA(e.position):"start"in e||"end"in e?sA(e):"line"in e||"column"in e?Kf(e):""}function Kf(e){return uA(e&&e.line)+":"+uA(e&&e.column)}function sA(e){return Kf(e&&e.start)+"-"+Kf(e&&e.end)}function uA(e){return e&&typeof e=="number"?e:1}class Ut extends Error{constructor(t,r,a){super(),typeof r=="string"&&(a=r,r=void 0);let o="",s={},u=!1;if(r&&("line"in r&&"column"in r?s={place:r}:"start"in r&&"end"in r?s={place:r}:"type"in r?s={ancestors:[r],place:r.position}:s={...r}),typeof t=="string"?o=t:!s.cause&&t&&(u=!0,o=t.message,s.cause=t),!s.ruleId&&!s.source&&typeof a=="string"){const p=a.indexOf(":");p===-1?s.ruleId=a:(s.source=a.slice(0,p),s.ruleId=a.slice(p+1))}if(!s.place&&s.ancestors&&s.ancestors){const p=s.ancestors[s.ancestors.length-1];p&&(s.place=p.position)}const d=s.place&&"start"in s.place?s.place.start:s.place;this.ancestors=s.ancestors||void 0,this.cause=s.cause||void 0,this.column=d?d.column:void 0,this.fatal=void 0,this.file="",this.message=o,this.line=d?d.line:void 0,this.name=gl(s.place)||"1:1",this.place=s.place||void 0,this.reason=this.message,this.ruleId=s.ruleId||void 0,this.source=s.source||void 0,this.stack=u&&s.cause&&typeof s.cause.stack=="string"?s.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}Ut.prototype.file="";Ut.prototype.name="";Ut.prototype.reason="";Ut.prototype.message="";Ut.prototype.stack="";Ut.prototype.column=void 0;Ut.prototype.line=void 0;Ut.prototype.ancestors=void 0;Ut.prototype.cause=void 0;Ut.prototype.fatal=void 0;Ut.prototype.place=void 0;Ut.prototype.ruleId=void 0;Ut.prototype.source=void 0;const Ug={}.hasOwnProperty,ZD=new Map,QD=/[A-Z]/g,JD=new Set(["table","tbody","thead","tfoot","tr"]),eM=new Set(["td","th"]),U_="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function tM(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expected `Fragment` in options");const r=t.filePath||void 0;let a;if(
Line truncated
|
||||
`;break}case-3:{u=`\r
|
||||
`;break}case-2:{u=t?" ":" ";break}case-1:{if(!t&&o)continue;u=" ";break}default:u=String.fromCharCode(s)}o=s===-2,a.push(u)}return a.join("")}function uj(e){const a={constructs:$_([ij,...(e||{}).extensions||[]]),content:o(TM),defined:[],document:o(kM),flow:o(H2),lazy:{},string:o(Y2),text:o(W2)};return a;function o(s){return u;function u(d){return oj(a,s,d)}}}function cj(e){for(;!Y_(e););return e}const EA=/[\0\t\n\r]/g;function dj(){let e=1,t="",r=!0,a;return o;function o(s,u,d){const p=[];let f,h,b,v,E;for(s=t+(typeof s=="string"?s.toString():new TextDecoder(u||void 0).decode(s)),b=0,t="",r&&(s.charCodeAt(0)===65279&&b++,r=void 0);b<s.length;){if(EA.lastIndex=b,f=EA.exec(s),v=f&&f.index!==void 0?f.index:s.length,E=s.charCodeAt(v),!f){t=s.slice(b);break}if(E===10&&b===v&&a)p.push(-3),a=void 0;else switch(a&&(p.push(-5),a=void 0),b<v&&(p.push(s.slice(b,v)),e+=v-b),E){case 0:{p.push(65533),e++;break}case 9:{for(h=Math.ceil(e/4)*4,p.push(-2);e++<h;)p.push(-1);break}case 10:{p.push(-4),e=1;break}default:a=!0,e=1}b=v+1}return d&&(a&&p.push(-5),t&&p.push(t),p.push(null)),p}}const pj=/\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;function fj(e){return e.replace(pj,gj)}function gj(e,t,r){if(t)return t;if(r.charCodeAt(0)===35){const o=r.charCodeAt(1),s=o===120||o===88;return G_(r.slice(s?2:1),s?16:10)}return xl(r)||e}const J_={}.hasOwnProperty;function mj(e,t,r){return t&&typeof t=="object"&&(r=t,t=void 0),hj(r)(cj(uj(r).document().write(dj()(e,t,!0))))}function hj(e){const t={transforms:[],canContainEols:["emphasis","fragment","heading","paragraph","strong"],enter:{autolink:s(Wa),autolinkProtocol:V,autolinkEmail:V,atxHeading:s(qn),blockQuote:s(Re),characterEscape:V,characterReference:V,codeFenced:s(Oe),codeFencedFenceInfo:u,codeFencedFenceMeta:u,codeIndented:s(Oe,u),codeText:s(qe,u),codeTextData:V,data:V,codeFlowValue:V,definition:s(dt),definitionDestinationString:u,definitionLabelString:u,definitionTitleString:u,emphasis:s(st),hardBreakEscape:s(Yn),hardBreakTrailing:s(Yn),htmlFlow:s(Cr,u),htmlFlowData:V,htmlText:s(Cr,u),htmlTextData:V,image:s(ql),label:u,link:s(Wa),listItem:s(po),listItemValue:v,listOrdered:s(Ka,b),listUnordered:s(Ka),paragraph:s(Hc),reference:I,referenceString:u,resourceDestinationString:u,resourceTitleString:u,setextHeading:s(qn),strong:s(Vc),thematicBreak:s(qc)},exit:{atxHeading:p(),atxHeadingSequence:j,autolink:p(),autolinkEmail:me,autolinkProtocol:we,blockQuote:p(),characterEscapeValue:M,characterReferenceMarkerHexadecimal:fe,characterReferenceMarkerNumeric:fe,characterReferenceValue:de,characterReference:le,codeFenced:p(k),codeFencedFence:T,codeFencedFenceInfo:E,codeFencedFenceMeta:w,codeFlowValue:M,codeIndented:p(A),codeText:p(Q),codeTextData:M,data:M,definition:p(),definitionDestinationString:L,definitionLabelString:_,definitionTitleString:R,emphasis:p(),hardBreakEscape:p(K),hardBreakTrailing:p(K),htmlFlow:p(Z),htmlFlowData:M,htmlText:p(te),htmlTextData:M,image:p(W),label:ce,labelText:ne,lineEnding:H,link:p(P),listItem:p(),listOrdered:p(),listUnordered:p(),paragraph:p(),referenceString:ue,resourceDestinationString:C,resourceTitleString:U,resource:J,setextHeading:p(F),setextHeadingLineSequence:z,setextHeadingText:D,strong:p(),thematicBreak:p()}};eN(t,(e||{}).mdastExtensions||[]);const r={};return a;function a(ae){let pe={type:"root",children:[]};const ke={stack:[pe],tokenStack:[],config:t,enter:d,exit:f,buffer:u,resume:h,data:r},Me=[];let We=-1;for(;++We<ae.length;)if(ae[We][1].type==="listOrdered"||ae[We][1].type==="listUnordered")if(ae[We][0]==="enter")Me.push(We);else{const nn=Me.pop();We=o(ae,nn,We)}for(We=-1;++We<ae.length;){const nn=t[ae[We][0]];J_.call(nn,ae[We][1].type)&&nn[ae[We][1].type].call(Object.assign({sliceSerialize:ae[We][2].sliceSerialize},ke),ae[We][1])}if(ke.tokenStack.length>0){const nn=ke.tokenStack[ke.tokenStack.length-1];(nn[1]||vA).call(ke,void 0,nn[0])}for(pe.position={start:aa(ae.length>0?ae[0][1].start:{line:1,column:1,offset:0}),end:aa(ae.length>0?ae[ae.length-2][1].end:{line:1,column:1,offset:0})},We=-1;++We<t.transforms.length;)pe=t.transforms[We](pe)||pe;return pe}function o(ae,pe,ke){let Me=pe-1,We=-1,nn=!1,or,Pt,vt,Vt;for(;++Me<=ke;){const tt=ae[Me];switch(tt[1].type){case"listUnordered":case"listOrdered":case"blockQuote":{tt[0]==="enter"?We++:We--,Vt=void 0;break}case"lineEndingBlank":{tt[0]==="enter"&&(or&&!Vt&&!We&&!vt&&(vt=Me),Vt=void 0);break}case"linePrefix":case"listItemValue":case"listItemMarker":case"listItemPrefix":case"listItemPrefixWhitespace":break;default:Vt=void 0}if(!We&&tt[0]==="enter"&&tt[1].type==="listItemPrefix"||We===-1&&tt[0]==="exit"&&(tt[1].type==="listUnordered"||tt[1].type==="listOrdered")){if(or){let Or=Me;for(Pt=void 0;Or--;){const In=ae[Or];if(In[1].type==="lineEnding"||In[1].type==="lineEndingBlank"){if(In[0]==="exit")continue;Pt&&(ae[Pt][1].type="lineEndingBlank",nn=!0),In[1].type="lineEnding",Pt=Or}else if(!(In[1].type==="linePrefix"||In[1].type==="blockQuotePrefix"||In[1].type==="blockQuotePrefixWhitespLine truncated
|
||||
`}]}function Sj(e,t){const r=t.value?t.value+`
|
||||
`:"",a={},o=t.lang?t.lang.split(/\s+/):[];o.length>0&&(a.className=["language-"+o[0]]);let s={type:"element",tagName:"code",properties:a,children:[{type:"text",value:r}]};return t.meta&&(s.data={meta:t.meta}),e.patch(t,s),s=e.applyData(t,s),s={type:"element",tagName:"pre",properties:{},children:[s]},e.patch(t,s),s}function xj(e,t){const r={type:"element",tagName:"del",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function wj(e,t){const r={type:"element",tagName:"em",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function Tj(e,t){const r=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",a=String(t.identifier).toUpperCase(),o=Ji(a.toLowerCase()),s=e.footnoteOrder.indexOf(a);let u,d=e.footnoteCounts.get(a);d===void 0?(d=0,e.footnoteOrder.push(a),u=e.footnoteOrder.length):u=s+1,d+=1,e.footnoteCounts.set(a,d);const p={type:"element",tagName:"a",properties:{href:"#"+r+"fn-"+o,id:r+"fnref-"+o+(d>1?"-"+d:""),dataFootnoteRef:!0,ariaDescribedBy:["footnote-label"]},children:[{type:"text",value:String(u)}]};e.patch(t,p);const f={type:"element",tagName:"sup",properties:{},children:[p]};return e.patch(t,f),e.applyData(t,f)}function Aj(e,t){const r={type:"element",tagName:"h"+t.depth,properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function kj(e,t){if(e.options.allowDangerousHtml){const r={type:"raw",value:t.value};return e.patch(t,r),e.applyData(t,r)}}function tN(e,t){const r=t.referenceType;let a="]";if(r==="collapsed"?a+="[]":r==="full"&&(a+="["+(t.label||t.identifier)+"]"),t.type==="imageReference")return[{type:"text",value:"!["+t.alt+a}];const o=e.all(t),s=o[0];s&&s.type==="text"?s.value="["+s.value:o.unshift({type:"text",value:"["});const u=o[o.length-1];return u&&u.type==="text"?u.value+=a:o.push({type:"text",value:a}),o}function _j(e,t){const r=String(t.identifier).toUpperCase(),a=e.definitionById.get(r);if(!a)return tN(e,t);const o={src:Ji(a.url||""),alt:t.alt};a.title!==null&&a.title!==void 0&&(o.title=a.title);const s={type:"element",tagName:"img",properties:o,children:[]};return e.patch(t,s),e.applyData(t,s)}function Nj(e,t){const r={src:Ji(t.url)};t.alt!==null&&t.alt!==void 0&&(r.alt=t.alt),t.title!==null&&t.title!==void 0&&(r.title=t.title);const a={type:"element",tagName:"img",properties:r,children:[]};return e.patch(t,a),e.applyData(t,a)}function Rj(e,t){const r={type:"text",value:t.value.replace(/\r?\n|\r/g," ")};e.patch(t,r);const a={type:"element",tagName:"code",properties:{},children:[r]};return e.patch(t,a),e.applyData(t,a)}function Cj(e,t){const r=String(t.identifier).toUpperCase(),a=e.definitionById.get(r);if(!a)return tN(e,t);const o={href:Ji(a.url||"")};a.title!==null&&a.title!==void 0&&(o.title=a.title);const s={type:"element",tagName:"a",properties:o,children:e.all(t)};return e.patch(t,s),e.applyData(t,s)}function Oj(e,t){const r={href:Ji(t.url)};t.title!==null&&t.title!==void 0&&(r.title=t.title);const a={type:"element",tagName:"a",properties:r,children:e.all(t)};return e.patch(t,a),e.applyData(t,a)}function Ij(e,t,r){const a=e.all(t),o=r?Lj(r):nN(t),s={},u=[];if(typeof t.checked=="boolean"){const h=a[0];let b;h&&h.type==="element"&&h.tagName==="p"?b=h:(b={type:"element",tagName:"p",properties:{},children:[]},a.unshift(b)),b.children.length>0&&b.children.unshift({type:"text",value:" "}),b.children.unshift({type:"element",tagName:"input",properties:{type:"checkbox",checked:t.checked,disabled:!0},children:[]}),s.className=["task-list-item"]}let d=-1;for(;++d<a.length;){const h=a[d];(o||d!==0||h.type!=="element"||h.tagName!=="p")&&u.push({type:"text",value:`
|
||||
`}),h.type==="element"&&h.tagName==="p"&&!o?u.push(...h.children):u.push(h)}const p=a[a.length-1];p&&(o||p.type!=="element"||p.tagName!=="p")&&u.push({type:"text",value:`
|
||||
`});const f={type:"element",tagName:"li",properties:s,children:u};return e.patch(t,f),e.applyData(t,f)}function Lj(e){let t=!1;if(e.type==="list"){t=e.spread||!1;const r=e.children;let a=-1;for(;!t&&++a<r.length;)t=nN(r[a])}return t}function nN(e){const t=e.spread;return t??e.children.length>1}function Dj(e,t){const r={},a=e.all(t);let o=-1;for(typeof t.start=="number"&&t.start!==1&&(r.start=t.start);++o<a.length;){const u=a[o];if(u.type==="element"&&u.tagName==="li"&&u.properties&&Array.isArray(u.properties.className)&&u.properties.className.includes("task-list-item")){r.className=["contains-task-list"];break}}const s={type:"element",tagName:t.ordered?"ol":"ul",properties:r,children:e.wrap(a,!0)};return e.patch(t,s),e.applyData(t,s)}function Mj(e,t){const r={type:"element",tagName:"p",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function jj(e,t){const r={type:"root",children:e.wrap(e.all(t))};return e.patch(t,r),e.applyData(t,r)}function Uj(e,t){const r={type:"element",tagName:"strong",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function Fj(e,t){const r=e.all(t),a=r.shift(),o=[];if(a){const u={type:"element",tagName:"thead",properties:{},children:e.wrap([a],!0)};e.patch(t.children[0],u),o.push(u)}if(r.length>0){const u={type:"element",tagName:"tbody",properties:{},children:e.wrap(r,!0)},d=jg(t.children[1]),p=M_(t.children[t.children.length-1]);d&&p&&(u.position={start:d,end:p}),o.push(u)}const s={type:"element",tagName:"table",properties:{},children:e.wrap(o,!0)};return e.patch(t,s),e.applyData(t,s)}function Bj(e,t,r){const a=r?r.children:void 0,s=(a?a.indexOf(t):1)===0?"th":"td",u=r&&r.type==="table"?r.align:void 0,d=u?u.length:t.children.length;let p=-1;const f=[];for(;++p<d;){const b=t.children[p],v={},E=u?u[p]:void 0;E&&(v.align=E);let w={type:"element",tagName:s,properties:v,children:[]};b&&(w.children=e.all(b),e.patch(b,w),w=e.applyData(b,w)),f.push(w)}const h={type:"element",tagName:"tr",properties:{},children:e.wrap(f,!0)};return e.patch(t,h),e.applyData(t,h)}function zj(e,t){const r={type:"element",tagName:"td",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}const SA=9,xA=32;function Pj(e){const t=String(e),r=/\r?\n|\r/g;let a=r.exec(t),o=0;const s=[];for(;a;)s.push(wA(t.slice(o,a.index),o>0,!0),a[0]),o=a.index+a[0].length,a=r.exec(t);return s.push(wA(t.slice(o),o>0,!1)),s.join("")}function wA(e,t,r){let a=0,o=e.length;if(t){let s=e.codePointAt(a);for(;s===SA||s===xA;)a++,s=e.codePointAt(a)}if(r){let s=e.codePointAt(o-1);for(;s===SA||s===xA;)o--,s=e.codePointAt(o-1)}return o>a?e.slice(a,o):""}function $j(e,t){const r={type:"text",value:Pj(String(t.value))};return e.patch(t,r),e.applyData(t,r)}function Gj(e,t){const r={type:"element",tagName:"hr",properties:{},children:[]};return e.patch(t,r),e.applyData(t,r)}const Hj={blockquote:Ej,break:vj,code:Sj,delete:xj,emphasis:wj,footnoteReference:Tj,heading:Aj,html:kj,imageReference:_j,image:Nj,inlineCode:Rj,linkReference:Cj,link:Oj,listItem:Ij,list:Dj,paragraph:Mj,root:jj,strong:Uj,table:Fj,tableCell:zj,tableRow:Bj,text:$j,thematicBreak:Gj,toml:fu,yaml:fu,definition:fu,footnoteDefinition:fu};function fu(){}const rN=-1,ic=0,hl=1,zu=2,$g=3,Gg=4,Hg=5,Vg=6,aN=7,iN=8,TA=typeof self=="object"?self:globalThis,Vj=(e,t)=>{const r=(o,s)=>(e.set(s,o),o),a=o=>{if(e.has(o))return e.get(o);const[s,u]=t[o];switch(s){case ic:case rN:return r(u,o);case hl:{const d=r([],o);for(const p of u)d.push(a(p));return d}case zu:{const d=r({},o);for(const[p,f]of u)d[a(p)]=a(f);return d}case $g:return r(new Date(u),o);case Gg:{const{source:d,flags:p}=u;return r(new RegExp(d,p),o)}case Hg:{const d=r(new Map,o);for(const[p,f]of u)d.set(a(p),a(f));return d}case Vg:{const d=r(new Set,o);for(const p of u)d.add(a(p));return d}case aN:{const{name:d,message:p}=u;return r(new TA[d](p),o)}case iN:return r(BigInt(u),o);case"BigInt":return r(Object(BigInt(u)),o);case"ArrayBuffer":return r(new Uint8Array(u).buffer,u);case"DataView":{const{buffer:d}=new Uint8Array(u);return r(new DataView(d),u)}}return r(new TA[s](u),o)};return a},AA=e=>Vj(new Map,e)(0),Mi="",{toString:qj}={},{keys:Yj}=Object,cl=e=>{const t=typeof e;if(t!=="object"||!e)return[ic,t];const r=qj.call(e).slice(8,-1);switch(r){case"Array":return[hl,Mi];case"Object":return[zu,Mi];case"Date":return[$g,Mi];case"RegExp":return[Gg,Mi];case"Map":return[Hg,Mi];case"Set":return[Vg,Mi];case"DataView":return[hl,r]}return r.includes("Array")?[hl,r]:r.includes("Error")?[aN,r]:[zu,r]},gu=([e,t])=>e===ic&&(t==="function"||t==="symbol"),Wj=(e,t,r,a)=>{const o=(u,d)=>{const p=a.push(u)-1;return r.set(d,p),p},s=u=>{if(r.has(u))return r.get(u);let[d,p]=cl(u);switch(d){case ic:{let h=u;switch(p){case"bigint":d=iN,h=u.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+p);h=null;break;case"undefined":return o([rN],u)}return o([d,h],u)}case hl:{if(p){let v=u;return p==="DataView"?v=new Uint8Array(u.buffer):p==="ArrayBuffer"&&(v=new Uint8Array(
Line truncated
|
||||
`},{type:"element",tagName:"ol",properties:{},children:e.wrap(d,!0)},{type:"text",value:`
|
||||
`}]}}const oc=(function(e){if(e==null)return tU;if(typeof e=="function")return lc(e);if(typeof e=="object")return Array.isArray(e)?Qj(e):Jj(e);if(typeof e=="string")return eU(e);throw new Error("Expected function, string, or object as test")});function Qj(e){const t=[];let r=-1;for(;++r<e.length;)t[r]=oc(e[r]);return lc(a);function a(...o){let s=-1;for(;++s<t.length;)if(t[s].apply(this,o))return!0;return!1}}function Jj(e){const t=e;return lc(r);function r(a){const o=a;let s;for(s in e)if(o[s]!==t[s])return!1;return!0}}function eU(e){return lc(t);function t(r){return r&&r.type===e}}function lc(e){return t;function t(r,a,o){return!!(nU(r)&&e.call(this,r,typeof a=="number"?a:void 0,o||void 0))}}function tU(){return!0}function nU(e){return e!==null&&typeof e=="object"&&"type"in e}const oN=[],rU=!0,Qf=!1,aU="skip";function lN(e,t,r,a){let o;typeof t=="function"&&typeof r!="function"?(a=r,r=t):o=t;const s=oc(o),u=a?-1:1;d(e,void 0,[])();function d(p,f,h){const b=p&&typeof p=="object"?p:{};if(typeof b.type=="string"){const E=typeof b.tagName=="string"?b.tagName:typeof b.name=="string"?b.name:void 0;Object.defineProperty(v,"name",{value:"node ("+(p.type+(E?"<"+E+">":""))+")"})}return v;function v(){let E=oN,w,T,k;if((!t||s(p,f,h[h.length-1]||void 0))&&(E=iU(r(p,h)),E[0]===Qf))return E;if("children"in p&&p.children){const A=p;if(A.children&&E[0]!==aU)for(T=(a?A.children.length:-1)+u,k=h.concat(A);T>-1&&T<A.children.length;){const _=A.children[T];if(w=d(_,T,k)(),w[0]===Qf)return w;T=typeof w[1]=="number"?w[1]:T+u}}return E}}}function iU(e){return Array.isArray(e)?e:typeof e=="number"?[rU,e]:e==null?oN:[e]}function qg(e,t,r,a){let o,s,u;typeof t=="function"&&typeof r!="function"?(s=void 0,u=t,o=r):(s=t,u=r,o=a),lN(e,s,d,o);function d(p,f){const h=f[f.length-1],b=h?h.children.indexOf(p):void 0;return u(p,b,h)}}const Jf={}.hasOwnProperty,oU={};function lU(e,t){const r=t||oU,a=new Map,o=new Map,s=new Map,u={...Hj,...r.handlers},d={all:f,applyData:uU,definitionById:a,footnoteById:o,footnoteCounts:s,footnoteOrder:[],handlers:u,one:p,options:r,patch:sU,wrap:dU};return qg(e,function(h){if(h.type==="definition"||h.type==="footnoteDefinition"){const b=h.type==="definition"?a:o,v=String(h.identifier).toUpperCase();b.has(v)||b.set(v,h)}}),d;function p(h,b){const v=h.type,E=d.handlers[v];if(Jf.call(d.handlers,v)&&E)return E(d,h,b);if(d.options.passThrough&&d.options.passThrough.includes(v)){if("children"in h){const{children:T,...k}=h,A=Pu(k);return A.children=d.all(h),A}return Pu(h)}return(d.options.unknownHandler||cU)(d,h,b)}function f(h){const b=[];if("children"in h){const v=h.children;let E=-1;for(;++E<v.length;){const w=d.one(v[E],h);if(w){if(E&&v[E-1].type==="break"&&(!Array.isArray(w)&&w.type==="text"&&(w.value=_A(w.value)),!Array.isArray(w)&&w.type==="element")){const T=w.children[0];T&&T.type==="text"&&(T.value=_A(T.value))}Array.isArray(w)?b.push(...w):b.push(w)}}}return b}}function sU(e,t){e.position&&(t.position=XD(e))}function uU(e,t){let r=t;if(e&&e.data){const a=e.data.hName,o=e.data.hChildren,s=e.data.hProperties;if(typeof a=="string")if(r.type==="element")r.tagName=a;else{const u="children"in r?r.children:[r];r={type:"element",tagName:a,properties:{},children:u}}r.type==="element"&&s&&Object.assign(r.properties,Pu(s)),"children"in r&&r.children&&o!==null&&o!==void 0&&(r.children=o)}return r}function cU(e,t){const r=t.data||{},a="value"in t&&!(Jf.call(r,"hProperties")||Jf.call(r,"hChildren"))?{type:"text",value:t.value}:{type:"element",tagName:"div",properties:{},children:e.all(t)};return e.patch(t,a),e.applyData(t,a)}function dU(e,t){const r=[];let a=-1;for(t&&r.push({type:"text",value:`
|
||||
`});++a<e.length;)a&&r.push({type:"text",value:`
|
||||
`}),r.push(e[a]);return t&&e.length>0&&r.push({type:"text",value:`
|
||||
`}),r}function _A(e){let t=0,r=e.charCodeAt(t);for(;r===9||r===32;)t++,r=e.charCodeAt(t);return e.slice(t)}function NA(e,t){const r=lU(e,t),a=r.one(e,void 0),o=Zj(r),s=Array.isArray(a)?{type:"root",children:a}:a||{type:"root",children:[]};return o&&s.children.push({type:"text",value:`
|
||||
`},o),s}function pU(e,t){return e&&"run"in e?async function(r,a){const o=NA(r,{file:a,...t});await e.run(o,a)}:function(r,a){return NA(r,{file:a,...e||t})}}function RA(e){if(e)throw e}var Ef,CA;function fU(){if(CA)return Ef;CA=1;var e=Object.prototype.hasOwnProperty,t=Object.prototype.toString,r=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=function(f){return typeof Array.isArray=="function"?Array.isArray(f):t.call(f)==="[object Array]"},s=function(f){if(!f||t.call(f)!=="[object Object]")return!1;var h=e.call(f,"constructor"),b=f.constructor&&f.constructor.prototype&&e.call(f.constructor.prototype,"isPrototypeOf");if(f.constructor&&!h&&!b)return!1;var v;for(v in f);return typeof v>"u"||e.call(f,v)},u=function(f,h){r&&h.name==="__proto__"?r(f,h.name,{enumerable:!0,configurable:!0,value:h.newValue,writable:!0}):f[h.name]=h.newValue},d=function(f,h){if(h==="__proto__")if(e.call(f,h)){if(a)return a(f,h).value}else return;return f[h]};return Ef=function p(){var f,h,b,v,E,w,T=arguments[0],k=1,A=arguments.length,_=!1;for(typeof T=="boolean"&&(_=T,T=arguments[1]||{},k=2),(T==null||typeof T!="object"&&typeof T!="function")&&(T={});k<A;++k)if(f=arguments[k],f!=null)for(h in f)b=d(T,h),v=d(f,h),T!==v&&(_&&v&&(s(v)||(E=o(v)))?(E?(E=!1,w=b&&o(b)?b:[]):w=b&&s(b)?b:{},u(T,{name:h,newValue:p(_,w,v)})):typeof v<"u"&&u(T,{name:h,newValue:v}));return T},Ef}var gU=fU();const vf=Qu(gU);function eg(e){if(typeof e!="object"||e===null)return!1;const t=Object.getPrototypeOf(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)}function mU(){const e=[],t={run:r,use:a};return t;function r(...o){let s=-1;const u=o.pop();if(typeof u!="function")throw new TypeError("Expected function as last argument, not "+u);d(null,...o);function d(p,...f){const h=e[++s];let b=-1;if(p){u(p);return}for(;++b<o.length;)(f[b]===null||f[b]===void 0)&&(f[b]=o[b]);o=f,h?hU(h,d)(...f):u(null,...f)}}function a(o){if(typeof o!="function")throw new TypeError("Expected `middelware` to be a function, not "+o);return e.push(o),t}}function hU(e,t){let r;return a;function a(...u){const d=e.length>u.length;let p;d&&u.push(o);try{p=e.apply(this,u)}catch(f){const h=f;if(d&&r)throw h;return o(h)}d||(p&&p.then&&typeof p.then=="function"?p.then(s,o):p instanceof Error?o(p):s(p))}function o(u,...d){r||(r=!0,t(u,...d))}function s(u){o(null,u)}}const Qn={basename:bU,dirname:yU,extname:EU,join:vU,sep:"/"};function bU(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');Ol(e);let r=0,a=-1,o=e.length,s;if(t===void 0||t.length===0||t.length>e.length){for(;o--;)if(e.codePointAt(o)===47){if(s){r=o+1;break}}else a<0&&(s=!0,a=o+1);return a<0?"":e.slice(r,a)}if(t===e)return"";let u=-1,d=t.length-1;for(;o--;)if(e.codePointAt(o)===47){if(s){r=o+1;break}}else u<0&&(s=!0,u=o+1),d>-1&&(e.codePointAt(o)===t.codePointAt(d--)?d<0&&(a=o):(d=-1,a=u));return r===a?a=u:a<0&&(a=e.length),e.slice(r,a)}function yU(e){if(Ol(e),e.length===0)return".";let t=-1,r=e.length,a;for(;--r;)if(e.codePointAt(r)===47){if(a){t=r;break}}else a||(a=!0);return t<0?e.codePointAt(0)===47?"/":".":t===1&&e.codePointAt(0)===47?"//":e.slice(0,t)}function EU(e){Ol(e);let t=e.length,r=-1,a=0,o=-1,s=0,u;for(;t--;){const d=e.codePointAt(t);if(d===47){if(u){a=t+1;break}continue}r<0&&(u=!0,r=t+1),d===46?o<0?o=t:s!==1&&(s=1):o>-1&&(s=-1)}return o<0||r<0||s===0||s===1&&o===r-1&&o===a+1?"":e.slice(o,r)}function vU(...e){let t=-1,r;for(;++t<e.length;)Ol(e[t]),e[t]&&(r=r===void 0?e[t]:r+"/"+e[t]);return r===void 0?".":SU(r)}function SU(e){Ol(e);const t=e.codePointAt(0)===47;let r=xU(e,!t);return r.length===0&&!t&&(r="."),r.length>0&&e.codePointAt(e.length-1)===47&&(r+="/"),t?"/"+r:r}function xU(e,t){let r="",a=0,o=-1,s=0,u=-1,d,p;for(;++u<=e.length;){if(u<e.length)d=e.codePointAt(u);else{if(d===47)break;d=47}if(d===47){if(!(o===u-1||s===1))if(o!==u-1&&s===2){if(r.length<2||a!==2||r.codePointAt(r.length-1)!==46||r.codePointAt(r.length-2)!==46){if(r.length>2){if(p=r.lastIndexOf("/"),p!==r.length-1){p<0?(r="",a=0):(r=r.slice(0,p),a=r.length-1-r.lastIndexOf("/")),o=u,s=0;continue}}else if(r.length>0){r="",a=0,o=u,s=0;continue}}t&&(r=r.length>0?r+"/..":"..",a=2)}else r.length>0?r+="/"+e.slice(o+1,u):r=e.slice(o+1,u),a=u-o-1;o=u,s=0}else d===46&&s>-1?s++:s=-1}return r}function Ol(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const wU={cwd:TU};function TU(){return"/"}function tg(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href&&"protocol"in e&&e.protocol&&e.auth===void 0)}function AU(e){if(typeof e=="string")e=new URL(e);else if(!tg(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return kU(e)}function kU(e){if(e.hostname!==""){consLine truncated
|
||||
`:" ")+s.indentLines(s.containerFlow(a,d.current()),t?pN:mF))),f(),p}}function mF(e,t,r){return t===0?e:pN(e,t,r)}function pN(e,t,r){return(r?"":" ")+e}const hF=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];fN.peek=SF;function bF(){return{canContainEols:["delete"],enter:{strikethrough:EF},exit:{strikethrough:vF}}}function yF(){return{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:hF}],handlers:{delete:fN}}}function EF(e){this.enter({type:"delete",children:[]},e)}function vF(e){this.exit(e)}function fN(e,t,r,a){const o=r.createTracker(a),s=r.enter("strikethrough");let u=o.move("~~");return u+=r.containerPhrasing(e,{...o.current(),before:u,after:"~"}),u+=o.move("~~"),s(),u}function SF(){return"~"}function xF(e){return e.length}function wF(e,t){const r=t||{},a=(r.align||[]).concat(),o=r.stringLength||xF,s=[],u=[],d=[],p=[];let f=0,h=-1;for(;++h<e.length;){const T=[],k=[];let A=-1;for(e[h].length>f&&(f=e[h].length);++A<e[h].length;){const _=TF(e[h][A]);if(r.alignDelimiters!==!1){const R=o(_);k[A]=R,(p[A]===void 0||R>p[A])&&(p[A]=R)}T.push(_)}u[h]=T,d[h]=k}let b=-1;if(typeof a=="object"&&"length"in a)for(;++b<f;)s[b]=UA(a[b]);else{const T=UA(a);for(;++b<f;)s[b]=T}b=-1;const v=[],E=[];for(;++b<f;){const T=s[b];let k="",A="";T===99?(k=":",A=":"):T===108?k=":":T===114&&(A=":");let _=r.alignDelimiters===!1?1:Math.max(1,p[b]-k.length-A.length);const R=k+"-".repeat(_)+A;r.alignDelimiters!==!1&&(_=k.length+_+A.length,_>p[b]&&(p[b]=_),E[b]=_),v[b]=R}u.splice(1,0,v),d.splice(1,0,E),h=-1;const w=[];for(;++h<u.length;){const T=u[h],k=d[h];b=-1;const A=[];for(;++b<f;){const _=T[b]||"";let R="",L="";if(r.alignDelimiters!==!1){const j=p[b]-(k[b]||0),D=s[b];D===114?R=" ".repeat(j):D===99?j%2?(R=" ".repeat(j/2+.5),L=" ".repeat(j/2-.5)):(R=" ".repeat(j/2),L=R):L=" ".repeat(j)}r.delimiterStart!==!1&&!b&&A.push("|"),r.padding!==!1&&!(r.alignDelimiters===!1&&_==="")&&(r.delimiterStart!==!1||b)&&A.push(" "),r.alignDelimiters!==!1&&A.push(R),A.push(_),r.alignDelimiters!==!1&&A.push(L),r.padding!==!1&&A.push(" "),(r.delimiterEnd!==!1||b!==f-1)&&A.push("|")}w.push(r.delimiterEnd===!1?A.join("").replace(/ +$/,""):A.join(""))}return w.join(`
|
||||
`)}function TF(e){return e==null?"":String(e)}function UA(e){const t=typeof e=="string"?e.codePointAt(0):0;return t===67||t===99?99:t===76||t===108?108:t===82||t===114?114:0}function AF(e,t,r,a){const o=r.enter("blockquote"),s=r.createTracker(a);s.move("> "),s.shift(2);const u=r.indentLines(r.containerFlow(e,s.current()),kF);return o(),u}function kF(e,t,r){return">"+(r?"":" ")+e}function _F(e,t){return FA(e,t.inConstruct,!0)&&!FA(e,t.notInConstruct,!1)}function FA(e,t,r){if(typeof t=="string"&&(t=[t]),!t||t.length===0)return r;let a=-1;for(;++a<t.length;)if(e.includes(t[a]))return!0;return!1}function BA(e,t,r,a){let o=-1;for(;++o<r.unsafe.length;)if(r.unsafe[o].character===`
|
||||
`&&_F(r.stack,r.unsafe[o]))return/[ \t]/.test(a.before)?"":" ";return`\\
|
||||
`}function NF(e,t){const r=String(e);let a=r.indexOf(t),o=a,s=0,u=0;if(typeof t!="string")throw new TypeError("Expected substring");for(;a!==-1;)a===o?++s>u&&(u=s):s=1,o=a+t.length,a=r.indexOf(t,o);return u}function RF(e,t){return!!(t.options.fences===!1&&e.value&&!e.lang&&/[^ \r\n]/.test(e.value)&&!/^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$/.test(e.value))}function CF(e){const t=e.options.fence||"`";if(t!=="`"&&t!=="~")throw new Error("Cannot serialize code with `"+t+"` for `options.fence`, expected `` ` `` or `~`");return t}function OF(e,t,r,a){const o=CF(r),s=e.value||"",u=o==="`"?"GraveAccent":"Tilde";if(RF(e,r)){const b=r.enter("codeIndented"),v=r.indentLines(s,IF);return b(),v}const d=r.createTracker(a),p=o.repeat(Math.max(NF(s,o)+1,3)),f=r.enter("codeFenced");let h=d.move(p);if(e.lang){const b=r.enter(`codeFencedLang${u}`);h+=d.move(r.safe(e.lang,{before:h,after:" ",encode:["`"],...d.current()})),b()}if(e.lang&&e.meta){const b=r.enter(`codeFencedMeta${u}`);h+=d.move(" "),h+=d.move(r.safe(e.meta,{before:h,after:`
|
||||
`,encode:["`"],...d.current()})),b()}return h+=d.move(`
|
||||
`),s&&(h+=d.move(s+`
|
||||
`)),h+=d.move(p),f(),h}function IF(e,t,r){return(r?"":" ")+e}function Wg(e){const t=e.options.quote||'"';if(t!=='"'&&t!=="'")throw new Error("Cannot serialize title with `"+t+"` for `options.quote`, expected `\"`, or `'`");return t}function LF(e,t,r,a){const o=Wg(r),s=o==='"'?"Quote":"Apostrophe",u=r.enter("definition");let d=r.enter("label");const p=r.createTracker(a);let f=p.move("[");return f+=p.move(r.safe(r.associationId(e),{before:f,after:"]",...p.current()})),f+=p.move("]: "),d(),!e.url||/[\0- \u007F]/.test(e.url)?(d=r.enter("destinationLiteral"),f+=p.move("<"),f+=p.move(r.safe(e.url,{before:f,after:">",...p.current()})),f+=p.move(">")):(d=r.enter("destinationRaw"),f+=p.move(r.safe(e.url,{before:f,after:e.title?" ":`
|
||||
`,...p.current()}))),d(),e.title&&(d=r.enter(`title${s}`),f+=p.move(" "+o),f+=p.move(r.safe(e.title,{before:f,after:o,...p.current()})),f+=p.move(o),d()),u(),f}function DF(e){const t=e.options.emphasis||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize emphasis with `"+t+"` for `options.emphasis`, expected `*`, or `_`");return t}function wl(e){return"&#x"+e.toString(16).toUpperCase()+";"}function $u(e,t,r){const a=Wi(e),o=Wi(t);return a===void 0?o===void 0?r==="_"?{inside:!0,outside:!0}:{inside:!1,outside:!1}:o===1?{inside:!0,outside:!0}:{inside:!1,outside:!0}:a===1?o===void 0?{inside:!1,outside:!1}:o===1?{inside:!0,outside:!0}:{inside:!1,outside:!1}:o===void 0?{inside:!1,outside:!1}:o===1?{inside:!0,outside:!1}:{inside:!1,outside:!1}}gN.peek=MF;function gN(e,t,r,a){const o=DF(r),s=r.enter("emphasis"),u=r.createTracker(a),d=u.move(o);let p=u.move(r.containerPhrasing(e,{after:o,before:d,...u.current()}));const f=p.charCodeAt(0),h=$u(a.before.charCodeAt(a.before.length-1),f,o);h.inside&&(p=wl(f)+p.slice(1));const b=p.charCodeAt(p.length-1),v=$u(a.after.charCodeAt(0),b,o);v.inside&&(p=p.slice(0,-1)+wl(b));const E=u.move(o);return s(),r.attentionEncodeSurroundingInfo={after:v.outside,before:h.outside},d+p+E}function MF(e,t,r){return r.options.emphasis||"*"}function jF(e,t){let r=!1;return qg(e,function(a){if("value"in a&&/\r?\n|\r/.test(a.value)||a.type==="break")return r=!0,Qf}),!!((!e.depth||e.depth<3)&&zg(e)&&(t.options.setext||r))}function UF(e,t,r,a){const o=Math.max(Math.min(6,e.depth||1),1),s=r.createTracker(a);if(jF(e,r)){const h=r.enter("headingSetext"),b=r.enter("phrasing"),v=r.containerPhrasing(e,{...s.current(),before:`
|
||||
`,after:`
|
||||
`});return b(),h(),v+`
|
||||
`+(o===1?"=":"-").repeat(v.length-(Math.max(v.lastIndexOf("\r"),v.lastIndexOf(`
|
||||
`))+1))}const u="#".repeat(o),d=r.enter("headingAtx"),p=r.enter("phrasing");s.move(u+" ");let f=r.containerPhrasing(e,{before:"# ",after:`
|
||||
`,...s.current()});return/^[\t ]/.test(f)&&(f=wl(f.charCodeAt(0))+f.slice(1)),f=f?u+" "+f:u,r.options.closeAtx&&(f+=" "+u),p(),d(),f}mN.peek=FF;function mN(e){return e.value||""}function FF(){return"<"}hN.peek=BF;function hN(e,t,r,a){const o=Wg(r),s=o==='"'?"Quote":"Apostrophe",u=r.enter("image");let d=r.enter("label");const p=r.createTracker(a);let f=p.move("![");return f+=p.move(r.safe(e.alt,{before:f,after:"]",...p.current()})),f+=p.move("]("),d(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(d=r.enter("destinationLiteral"),f+=p.move("<"),f+=p.move(r.safe(e.url,{before:f,after:">",...p.current()})),f+=p.move(">")):(d=r.enter("destinationRaw"),f+=p.move(r.safe(e.url,{before:f,after:e.title?" ":")",...p.current()}))),d(),e.title&&(d=r.enter(`title${s}`),f+=p.move(" "+o),f+=p.move(r.safe(e.title,{before:f,after:o,...p.current()})),f+=p.move(o),d()),f+=p.move(")"),u(),f}function BF(){return"!"}bN.peek=zF;function bN(e,t,r,a){const o=e.referenceType,s=r.enter("imageReference");let u=r.enter("label");const d=r.createTracker(a);let p=d.move("![");const f=r.safe(e.alt,{before:p,after:"]",...d.current()});p+=d.move(f+"]["),u();const h=r.stack;r.stack=[],u=r.enter("reference");const b=r.safe(r.associationId(e),{before:p,after:"]",...d.current()});return u(),r.stack=h,s(),o==="full"||!f||f!==b?p+=d.move(b+"]"):o==="shortcut"?p=p.slice(0,-1):p+=d.move("]"),p}function zF(){return"!"}yN.peek=PF;function yN(e,t,r){let a=e.value||"",o="`",s=-1;for(;new RegExp("(^|[^`])"+o+"([^`]|$)").test(a);)o+="`";for(/[^ \r\n]/.test(a)&&(/^[ \r\n]/.test(a)&&/[ \r\n]$/.test(a)||/^`|`$/.test(a))&&(a=" "+a+" ");++s<r.unsafe.length;){const u=r.unsafe[s],d=r.compilePattern(u);let p;if(u.atBreak)for(;p=d.exec(a);){let f=p.index;a.charCodeAt(f)===10&&a.charCodeAt(f-1)===13&&f--,a=a.slice(0,f)+" "+a.slice(p.index+1)}}return o+a+o}function PF(){return"`"}function EN(e,t){const r=zg(e);return!!(!t.options.resourceLink&&e.url&&!e.title&&e.children&&e.children.length===1&&e.children[0].type==="text"&&(r===e.url||"mailto:"+r===e.url)&&/^[a-z][a-z+.-]+:/i.test(e.url)&&!/[\0- <>\u007F]/.test(e.url))}vN.peek=$F;function vN(e,t,r,a){const o=Wg(r),s=o==='"'?"Quote":"Apostrophe",u=r.createTracker(a);let d,p;if(EN(e,r)){const h=r.stack;r.stack=[],d=r.enter("autolink");let b=u.move("<");return b+=u.move(r.containerPhrasing(e,{before:b,after:">",...u.current()})),b+=u.move(">"),d(),r.stack=h,b}d=r.enter("link"),p=r.enter("label");let f=u.move("[");return f+=u.move(r.containerPhrasing(e,{before:f,after:"](",...u.current()})),f+=u.move("]("),p(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(p=r.enter("destinationLiteral"),f+=u.move("<"),f+=u.move(r.safe(e.url,{before:f,after:">",...u.current()})),f+=u.move(">")):(p=r.enter("destinationRaw"),f+=u.move(r.safe(e.url,{before:f,after:e.title?" ":")",...u.current()}))),p(),e.title&&(p=r.enter(`title${s}`),f+=u.move(" "+o),f+=u.move(r.safe(e.title,{before:f,after:o,...u.current()})),f+=u.move(o),p()),f+=u.move(")"),d(),f}function $F(e,t,r){return EN(e,r)?"<":"["}SN.peek=GF;function SN(e,t,r,a){const o=e.referenceType,s=r.enter("linkReference");let u=r.enter("label");const d=r.createTracker(a);let p=d.move("[");const f=r.containerPhrasing(e,{before:p,after:"]",...d.current()});p+=d.move(f+"]["),u();const h=r.stack;r.stack=[],u=r.enter("reference");const b=r.safe(r.associationId(e),{before:p,after:"]",...d.current()});return u(),r.stack=h,s(),o==="full"||!f||f!==b?p+=d.move(b+"]"):o==="shortcut"?p=p.slice(0,-1):p+=d.move("]"),p}function GF(){return"["}function Kg(e){const t=e.options.bullet||"*";if(t!=="*"&&t!=="+"&&t!=="-")throw new Error("Cannot serialize items with `"+t+"` for `options.bullet`, expected `*`, `+`, or `-`");return t}function HF(e){const t=Kg(e),r=e.options.bulletOther;if(!r)return t==="*"?"-":"*";if(r!=="*"&&r!=="+"&&r!=="-")throw new Error("Cannot serialize items with `"+r+"` for `options.bulletOther`, expected `*`, `+`, or `-`");if(r===t)throw new Error("Expected `bullet` (`"+t+"`) and `bulletOther` (`"+r+"`) to be different");return r}function VF(e){const t=e.options.bulletOrdered||".";if(t!=="."&&t!==")")throw new Error("Cannot serialize items with `"+t+"` for `options.bulletOrdered`, expected `.` or `)`");return t}function xN(e){const t=e.options.rule||"*";if(t!=="*"&&t!=="-"&&t!=="_")throw new Error("Cannot serialize rules with `"+t+"` for `options.rule`, expected `*`, `-`, or `_`");return t}function qF(e,t,r,a){const o=r.enter("list"),s=r.bulletCurrent;let u=e.ordered?VF(r):Kg(r);const d=e.ordered?u==="."?")":".":HF(r);let p=t&&r.bulletLastUsed?u===r.bulletLastUsed:!1;if(!e.ordered){const h=e.children?e.children[0]:void 0;if((u==="*"||u==="-")&&h&&(!h.children||!h.children[0])&&r.stack[r.stack.length-1]==="list"&&r.stack[r.stack.length-2]==="listItem"&&r.stack[r.stack.length-3]==="list"&&r.stack[r.stack.length-4]==="listItem"&&r.indexStack[r.indexStack.length-1]===0&&r.indexStack[r.indexStack.length-2]===0&&r.indexStack[r.indexStack.length-3]===0&&(p=!0),xN(r)===u&&h){let b=-1;for(;++b<e.chilLine truncated
|
||||
`,inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:v,table:u,tableCell:p,tableRow:d}};function u(E,w,T,k){return f(h(E,T,k),E.align)}function d(E,w,T,k){const A=b(E,T,k),_=f([A]);return _.slice(0,_.indexOf(`
|
||||
`))}function p(E,w,T,k){const A=T.enter("tableCell"),_=T.enter("phrasing"),R=T.containerPhrasing(E,{...k,before:s,after:s});return _(),A(),R}function f(E,w){return wF(E,{align:w,alignDelimiters:a,padding:r,stringLength:o})}function h(E,w,T){const k=E.children;let A=-1;const _=[],R=w.enter("table");for(;++A<k.length;)_[A]=b(k[A],w,T);return R(),_}function b(E,w,T){const k=E.children;let A=-1;const _=[],R=w.enter("tableRow");for(;++A<k.length;)_[A]=p(k[A],E,w,T);return R(),_}function v(E,w,T){let k=TN.inlineCode(E,w,T);return T.stack.includes("tableCell")&&(k=k.replace(/\|/g,"\\$&")),k}}function cB(){return{exit:{taskListCheckValueChecked:PA,taskListCheckValueUnchecked:PA,paragraph:pB}}}function dB(){return{unsafe:[{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{listItem:fB}}}function PA(e){const t=this.stack[this.stack.length-2];t.type,t.checked=e.type==="taskListCheckValueChecked"}function pB(e){const t=this.stack[this.stack.length-2];if(t&&t.type==="listItem"&&typeof t.checked=="boolean"){const r=this.stack[this.stack.length-1];r.type;const a=r.children[0];if(a&&a.type==="text"){const o=t.children;let s=-1,u;for(;++s<o.length;){const d=o[s];if(d.type==="paragraph"){u=d;break}}u===r&&(a.value=a.value.slice(1),a.value.length===0?r.children.shift():r.position&&a.position&&typeof a.position.start.offset=="number"&&(a.position.start.column++,a.position.start.offset++,r.position.start=Object.assign({},a.position.start)))}}this.exit(e)}function fB(e,t,r,a){const o=e.children[0],s=typeof e.checked=="boolean"&&o&&o.type==="paragraph",u="["+(e.checked?"x":" ")+"] ",d=r.createTracker(a);s&&d.move(u);let p=TN.listItem(e,t,r,{...a,...d.current()});return s&&(p=p.replace(/^(?:[*+-]|\d+\.)([\r\n]| {1,3})/,f)),p;function f(h){return h+u}}function gB(){return[qU(),fF(),bF(),rB(),cB()]}function mB(e){return{extensions:[YU(),gF(e),yF(),uB(e),dB()]}}const hB={tokenize:xB,partial:!0},AN={tokenize:wB,partial:!0},kN={tokenize:TB,partial:!0},_N={tokenize:AB,partial:!0},bB={tokenize:kB,partial:!0},NN={name:"wwwAutolink",tokenize:vB,previous:CN},RN={name:"protocolAutolink",tokenize:SB,previous:ON},Nr={name:"emailAutolink",tokenize:EB,previous:IN},rr={};function yB(){return{text:rr}}let Da=48;for(;Da<123;)rr[Da]=Nr,Da++,Da===58?Da=65:Da===91&&(Da=97);rr[43]=Nr;rr[45]=Nr;rr[46]=Nr;rr[95]=Nr;rr[72]=[Nr,RN];rr[104]=[Nr,RN];rr[87]=[Nr,NN];rr[119]=[Nr,NN];function EB(e,t,r){const a=this;let o,s;return u;function u(b){return!ng(b)||!IN.call(a,a.previous)||Xg(a.events)?r(b):(e.enter("literalAutolink"),e.enter("literalAutolinkEmail"),d(b))}function d(b){return ng(b)?(e.consume(b),d):b===64?(e.consume(b),p):r(b)}function p(b){return b===46?e.check(bB,h,f)(b):b===45||b===95||Dt(b)?(s=!0,e.consume(b),p):h(b)}function f(b){return e.consume(b),o=!0,p}function h(b){return s&&o&&Ht(a.previous)?(e.exit("literalAutolinkEmail"),e.exit("literalAutolink"),t(b)):r(b)}}function vB(e,t,r){const a=this;return o;function o(u){return u!==87&&u!==119||!CN.call(a,a.previous)||Xg(a.events)?r(u):(e.enter("literalAutolink"),e.enter("literalAutolinkWww"),e.check(hB,e.attempt(AN,e.attempt(kN,s),r),r)(u))}function s(u){return e.exit("literalAutolinkWww"),e.exit("literalAutolink"),t(u)}}function SB(e,t,r){const a=this;let o="",s=!1;return u;function u(b){return(b===72||b===104)&&ON.call(a,a.previous)&&!Xg(a.events)?(e.enter("literalAutolink"),e.enter("literalAutolinkHttp"),o+=String.fromCodePoint(b),e.consume(b),d):r(b)}function d(b){if(Ht(b)&&o.length<5)return o+=String.fromCodePoint(b),e.consume(b),d;if(b===58){const v=o.toLowerCase();if(v==="http"||v==="https")return e.consume(b),p}return r(b)}function p(b){return b===47?(e.consume(b),s?f:(s=!0,p)):r(b)}function f(b){return b===null||Bu(b)||rt(b)||Fa(b)||rc(b)?r(b):e.attempt(AN,e.attempt(kN,h),r)(b)}function h(b){return e.exit("literalAutolinkHttp"),e.exit("literalAutolink"),t(b)}}function xB(e,t,r){let a=0;return o;function o(u){return(u===87||u===119)&&a<3?(a++,e.consume(u),o):u===46&&a===3?(e.consume(u),s):r(u)}function s(u){return u===null?r(u):t(u)}}function wB(e,t,r){let a,o,s;return u;function u(f){return f===46||f===95?e.check(_N,p,d)(f):f===null||rt(f)||Fa(f)||f!==45&&rc(f)?p(f):(s=!0,e.consume(f),u)}function d(f){return f===95?a=!0:(o=a,a=void 0),e.consume(f),u}function p(f){return o||a||!s?r(f):t(f)}}function TB(e,t){let r=0,a=0;return o;function o(u){return u===40?(r++,e.consume(u),o):u===41&&a<r?s(u):u===33||u===34||u===38||u===39||u===41||u===42||u===44||u===46||u===58||u===59||u===60||u===63||u===93||u===95||u===126?e.check(_N,t,s)(u):u===null||rt(u)||Fa(u)?t(u):(e.consume(u),o)}function s(u){return u===41&&a++,e.consume(u),o}}function AB(e,t,r){return a;function a(d){return d===33||d===34||d===39||d===41||d===42||d===44||d===46||d===58||d===59||d===63||d===95||d===126?(e.consume(d),a):d===38?(e.consume(d),s):d===93?(e.consume(d),o):d===60||d===null||rt(d)||Fa(d)?t(d):r(d)}function o(d){return d===null||d===40||d===91||rt(d)||Fa(d)?t(d):a(d)}function s(d){return Ht(d)?u(d):r(d)}function u(d){re
Line truncated
|
||||
`)===-1?g.jsx("code",{className:"bg-primary/10 text-primary px-1.5 py-0.5 rounded text-sm font-mono",children:h}):g.jsx("code",{className:"block font-mono",children:h}),pre:({children:f})=>g.jsx("pre",{className:"bg-muted p-4 rounded-lg text-sm overflow-auto my-4 font-mono whitespace-pre",children:f}),table:({children:f})=>g.jsx("div",{className:"overflow-auto my-4",children:g.jsx("table",{className:"w-full border-collapse text-sm",children:f})}),thead:({children:f})=>g.jsx("thead",{className:"bg-muted/50",children:f}),tr:({children:f})=>g.jsx("tr",{className:"border-b",children:f}),th:({children:f})=>g.jsx("th",{className:"px-4 py-2 text-left font-medium",children:f}),td:({children:f})=>g.jsx("td",{className:"px-4 py-2 text-muted-foreground",children:f}),hr:()=>g.jsx("hr",{className:"my-6 border-border"}),strong:({children:f})=>g.jsx("strong",{className:"font-semibold text-foreground",children:f}),em:({children:f})=>g.jsx("em",{children:f})};return r?g.jsx("main",{id:"main-content",className:"flex-1 p-8 max-w-3xl",children:s?g.jsx("div",{className:"text-muted-foreground",children:"Loading content..."}):a?g.jsx("article",{className:"prose max-w-none",children:g.jsx(uN,{remarkPlugins:[LN],components:p,children:a})}):g.jsx("div",{className:"bg-muted/50 border rounded-lg p-6",children:g.jsxs("p",{className:"text-muted-foreground",children:["Content for ",g.jsx("strong",{className:"text-primary",children:r.title})," would be displayed here."]})})}):g.jsxs("main",{className:"flex-1 p-8 max-w-3xl",children:[g.jsx("h1",{className:"text-4xl font-bold tracking-tight mb-4",children:e.site?.title||"Documentation"}),g.jsx("p",{className:"text-muted-foreground text-lg mb-8",children:e.site?.description||"Welcome to the documentation."}),g.jsx(Pi,{className:"my-8"}),g.jsx("h2",{className:"text-2xl font-semibold mb-4 text-primary",children:"Theme Configuration"}),g.jsxs("div",{className:"bg-muted/50 border rounded-lg p-4 mb-6",children:[g.jsx("p",{className:"text-sm text-muted-foreground mb-2",children:g.jsx("strong",{children:"Current theme from YAML:"})}),g.jsx("pre",{className:"text-primary text-sm",children:`site:
|
||||
theme:
|
||||
preset: "${d?.preset||"zinc"}"
|
||||
radius: "${d?.radius||"md"}"
|
||||
darkMode: ${d?.darkMode!==!1}`})]}),g.jsx("h3",{className:"text-lg font-medium mb-3",children:"Available Presets"}),g.jsx("div",{className:"flex flex-wrap gap-2 mb-6",children:Object.keys(Y1).map(f=>g.jsx("span",{className:`px-3 py-1 rounded-full text-sm ${f===(d?.preset||"zinc")?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground"}`,children:f},f))}),g.jsxs("p",{className:"text-muted-foreground mb-4",children:[g.jsx("strong",{children:"Click any item in the sidebar"})," to navigate."]}),g.jsx("h2",{className:"text-2xl font-semibold mt-8 mb-4",children:"Routes"}),g.jsxs("p",{className:"text-muted-foreground mb-4",children:[g.jsx("span",{className:"font-medium text-primary",children:t.routes?.length||0})," routes configured."]}),g.jsx("pre",{className:"bg-muted p-4 rounded-lg text-sm overflow-auto",children:JSON.stringify(t.routes?.slice(0,3),null,2)})]})}const GA=e=>typeof e=="boolean"?`${e}`:e===0?"0":e,HA=u_,Zg=(e,t)=>r=>{var a;if(t?.variants==null)return HA(e,r?.class,r?.className);const{variants:o,defaultVariants:s}=t,u=Object.keys(o).map(f=>{const h=r?.[f],b=s?.[f];if(h===null)return null;const v=GA(h)||GA(b);return o[f][v]}),d=r&&Object.entries(r).reduce((f,h)=>{let[b,v]=h;return v===void 0||(f[b]=v),f},{}),p=t==null||(a=t.compoundVariants)===null||a===void 0?void 0:a.reduce((f,h)=>{let{class:b,className:v,...E}=h;return Object.entries(E).every(w=>{let[T,k]=w;return Array.isArray(k)?k.includes({...s,...d}[T]):{...s,...d}[T]===k})?[...f,b,v]:f},[]);return HA(e,u,p,r?.class,r?.className)},DN=Zg("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2 has-[>svg]:px-3",xs:"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",sm:"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg:"h-10 rounded-md px-6 has-[>svg]:px-4",icon:"size-9","icon-xs":"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3","icon-sm":"size-8","icon-lg":"size-10"}},defaultVariants:{variant:"default",size:"default"}});function $i({className:e,variant:t="default",size:r="default",asChild:a=!1,...o}){const s=a?Dg:"button";return g.jsx(s,{"data-slot":"button","data-variant":t,"data-size":r,className:be(DN({variant:t,size:r,className:e})),...o})}function _n({className:e,...t}){return g.jsx("div",{"data-slot":"card",className:be("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",e),...t})}function Bn({className:e,...t}){return g.jsx("div",{"data-slot":"card-header",className:be("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",e),...t})}function zn({className:e,...t}){return g.jsx("div",{"data-slot":"card-title",className:be("leading-none font-semibold",e),...t})}function Qg({className:e,...t}){return g.jsx("div",{"data-slot":"card-description",className:be("text-muted-foreground text-sm",e),...t})}function Nn({className:e,...t}){return g.jsx("div",{"data-slot":"card-content",className:be("px-6",e),...t})}function KB({className:e,...t}){return g.jsx("div",{"data-slot":"card-footer",className:be("flex items-center px-6 [.border-t]:pt-6",e),...t})}function XB({props:e}){const t=e?.title||"Statistics",r=e?.value||"0",a=e?.change||"",o=e?.changeType||"positive";return g.jsxs(_n,{className:"mb-4",children:[g.jsxs(Bn,{className:"pb-2",children:[g.jsx(Qg,{children:t}),g.jsx(zn,{className:"text-2xl",children:r})]}),a&&g.jsx(Nn,{className:"pt-0",children:g.jsx("span",{className:o==="positive"?"text-green-500 text-sm":"text-red-500 text-sm",children:a})})]})}function ZB({props:e}){const t=e?.title||"Quick Links",r=e?.links||[];return g.jsxs(_n,{className:"mb-4",children:[g.jsx(Bn,{className:"pb-2",children:g.jsx(zn,{className:"text-sm font-medium",children:t})}),g.jsx(Nn,{className:"space-y-1",children:r.map((a,o)=>g.jsx("a",{href:a.href,className:"block text-sm text-muted-foreground hover:text-foreground transition-colors",children:a.label},o))})]})}function QB({
Line truncated
|
||||
`);p=b.pop()||"";for(const v of b)if(v.startsWith("data: "))try{const E=JSON.parse(v.slice(6));Ez(e,E,o)}catch{}}}catch(s){if(s.name!=="AbortError"){const u=Ua.get(e);u&&An(e,{thinkingSteps:[...u.state.thinkingSteps,`Error: ${s.message}`]})}}finally{const s=Ua.get(e);s&&(s.abortController=null),An(e,{isStreaming:!1}),o?.onEnd?.()}}function Ez(e,t,r){const a=Ua.get(e);if(!a)return;const o=a.toolCallMap;switch(t.type){case"session":t.session_id&&typeof t.session_id=="string"&&r?.onSessionId?.(t.session_id);break;case"token":case"run_content":case"team_run_content":{const u=t.token??t.content??"";u&&An(e,{currentResponse:a.state.currentResponse+u});break}case"message":case"run_completed":case"team_run_completed":{const u=t.content;u&&An(e,{currentResponse:u});break}case"thinking":case"reasoning_step":case"team_reasoning_step":{const u=t.step;u&&An(e,{thinkingSteps:[...a.state.thinkingSteps,u]});break}case"reasoning_started":case"team_reasoning_started":An(e,{thinkingSteps:[...a.state.thinkingSteps,"Starting reasoning..."]});break;case"tool_call":case"tool_call_started":case"team_tool_call_started":{const u={name:t.name,description:t.description,icon:t.icon,step_number:t.step_number,status:"running",tool_call_id:t.tool_call_id,args:t.args};u.tool_call_id&&o.set(u.tool_call_id,u);const d=qA(a.state.toolCalls,u);An(e,{toolCalls:d});break}case"tool_call_completed":case"team_tool_call_completed":{const u=t.extra_data,d=t.a2ui??u?.a2ui,p=t.elements??u?.elements,f={name:t.name,description:t.description,icon:t.icon,step_number:t.step_number,status:"done",tool_call_id:t.tool_call_id,args:t.args,formatted_result:t.formatted_result,result:t.result,surface_id:t.surface_id??u?.surface_id,a2ui:d?.length?{messages:d,surface_id:t.surface_id??u?.surface_id}:void 0,elements:p};f.tool_call_id&&o.set(f.tool_call_id,f);const h=qA(a.state.toolCalls,f),b=p?.length?[...a.state.pendingElements,...p]:a.state.pendingElements;An(e,{toolCalls:h,pendingElements:b});break}case"message_element":{const u=t.extra_data,d=t.element??u?.element;d&&An(e,{pendingElements:[...a.state.pendingElements,d]});break}case"error":case"run_error":case"team_run_error":{const u=t.error;u&&An(e,{thinkingSteps:[...a.state.thinkingSteps,`Error: ${u}`]});break}case"end":case"done":case"run_cancelled":case"team_run_cancelled":break;default:gz(e,t);break}}function qA(e,t){if(t.tool_call_id){const r=e.findIndex(a=>a.tool_call_id===t.tool_call_id);if(r>=0){const a=[...e];return a[r]={...a[r],...t},a}}return[...e,t]}function UN(e={}){const{externalSessionId:t}=e,[r,a]=S.useState(()=>yu(t));S.useEffect(()=>{if(!t){a(yu(null));return}return a(yu(t)),mz(t,()=>{a(yu(t))})},[t]);const o=S.useCallback((u,d)=>{const p=t??`new-${Date.now()}`;bz(p,u,d,{onSessionId:f=>{e.onSessionId?.(f)},onEnd:e.onEnd})},[t,e.onSessionId,e.onEnd]),s=S.useCallback(()=>{t&&hz(t)},[t]);return{isStreaming:r.isStreaming,currentResponse:r.currentResponse,toolCalls:r.toolCalls,thinkingSteps:r.thinkingSteps,pendingElements:r.pendingElements,sendMessage:o,cancel:s}}function vz(e,t){if(e==null)return{};var r={};for(var a in e)if({}.hasOwnProperty.call(e,a)){if(t.indexOf(a)!==-1)continue;r[a]=e[a]}return r}function Sz(e,t){if(e==null)return{};var r,a,o=vz(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(a=0;a<s.length;a++)r=s[a],t.indexOf(r)===-1&&{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}function ig(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,a=Array(t);r<t;r++)a[r]=e[r];return a}function xz(e){if(Array.isArray(e))return ig(e)}function wz(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function Tz(e,t){if(e){if(typeof e=="string")return ig(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?ig(e,t):void 0}}function Az(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function og(e){return xz(e)||wz(e)||Tz(e)||Az()}function Tl(e){"@babel/helpers - typeof";return Tl=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Tl(e)}function kz(e,t){if(Tl(e)!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var a=r.call(e,t);if(Tl(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function _z(e){var t=kz(e,"string");return Tl(t)=="symbol"?t:t+""}function FN(e,t,r){return(t=_z(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function lg(){return lg=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)({}).hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},lg.apply(null,arguments)}function YA(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),r.push.apply(r,a)}return r}function Gi(e){for(var t=1;t<arguments.length;t++){var r=arguments[t]!=null?arguments[t]:{};t%2?YA(Object(r),!0).forEach(function(a){FN(e,a,r[a])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):YA(Object(r)).forEach(function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(r,a))})}return e}function Nz(e){var t=e.length;if(t===0||t===1)return e;if(t===2)return[e[0],e[1],"".concat(e[0],".").concat(e[1]),"".concat(e[1],".").concat(e[0])];if(t===3)return[e[0],e[1],e[2],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0])];if(t>=4)return[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[
Line truncated
|
||||
`))})}function Uz(e){var t=e.codeString,r=e.codeStyle,a=e.containerStyle,o=a===void 0?{float:"left",paddingRight:"10px"}:a,s=e.numberStyle,u=s===void 0?{}:s,d=e.startingLineNumber;return Ke.createElement("code",{style:Object.assign({},r,o)},jz({lines:t.replace(/\n$/,"").split(`
|
||||
`),style:u,startingLineNumber:d}))}function Fz(e){return"".concat(e.toString().length,".25em")}function zN(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function PN(e,t,r){var a={display:"inline-block",minWidth:Fz(r),paddingRight:"1em",textAlign:"right",userSelect:"none"},o=typeof e=="function"?e(t):e,s=oa(oa({},a),o);return s}function Ru(e){var t=e.children,r=e.lineNumber,a=e.lineNumberStyle,o=e.largestLineNumber,s=e.showInlineLineNumbers,u=e.lineProps,d=u===void 0?{}:u,p=e.className,f=p===void 0?[]:p,h=e.showLineNumbers,b=e.wrapLongLines,v=e.wrapLines,E=v===void 0?!1:v,w=E?oa({},typeof d=="function"?d(r):d):{};if(w.className=w.className?[].concat(og(w.className.trim().split(/\s+/)),og(f)):f,r&&s){var T=PN(a,r,o);t.unshift(zN(r,T))}return b&h&&(w.style=oa({display:"flex"},w.style)),{type:"element",tagName:"span",properties:w,children:t}}function $N(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[],r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];e.length===void 0&&(e=[e]);for(var a=0;a<e.length;a++){var o=e[a];if(o.type==="text")r.push(Ru({children:[o],className:og(new Set(t))}));else if(o.children){var s,u=t.concat(((s=o.properties)===null||s===void 0?void 0:s.className)||[]);$N(o.children,u).forEach(function(d){return r.push(d)})}}return r}function Bz(e,t,r,a,o,s,u,d,p){var f,h=$N(e.value),b=[],v=-1,E=0;function w(j,D){var z=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return Ru({children:j,lineNumber:D,lineNumberStyle:d,largestLineNumber:u,showInlineLineNumbers:o,lineProps:r,className:z,showLineNumbers:a,wrapLongLines:p,wrapLines:t})}function T(j,D){if(a&&D&&o){var z=PN(d,D,u);j.unshift(zN(D,z))}return j}function k(j,D){var z=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return t||z.length>0?w(j,D,z):T(j,D)}for(var A=function(){var D=h[E],z=D.children[0].value,F=Mz(z);if(F){var V=z.split(`
|
||||
`);V.forEach(function(M,H){var K=a&&b.length+s,Z={type:"text",value:"".concat(M,`
|
||||
`)};if(H===0){var te=h.slice(v+1,E).concat(Ru({children:[Z],className:D.properties.className})),Q=k(te,K);b.push(Q)}else if(H===V.length-1){var P=h[E+1]&&h[E+1].children&&h[E+1].children[0],W={type:"text",value:"".concat(M)};if(P){var ne=Ru({children:[W],className:D.properties.className});h.splice(E+1,0,ne)}else{var ce=[W],C=k(ce,K,D.properties.className);b.push(C)}}else{var U=[Z],J=k(U,K,D.properties.className);b.push(J)}}),v=E}E++};E<h.length;)A();if(v!==h.length-1){var _=h.slice(v+1,h.length);if(_&&_.length){var R=a&&b.length+s,L=k(_,R);b.push(L)}}return t?b:(f=[]).concat.apply(f,b)}function zz(e){var t=e.rows,r=e.stylesheet,a=e.useInlineStyles;return t.map(function(o,s){return BN({node:o,stylesheet:r,useInlineStyles:a,key:"code-segment-".concat(s)})})}function GN(e){return e&&typeof e.highlightAuto<"u"}function Pz(e){var t=e.astGenerator,r=e.language,a=e.code,o=e.defaultCodeValue;if(GN(t)){var s=Iz(t,r);return r==="text"?{value:o,language:"text"}:s?t.highlight(r,a):t.highlightAuto(a)}try{return r&&r!=="text"?{value:t.highlight(a,r)}:{value:o}}catch{return{value:o}}}function $z(e,t){return function(a){var o,s,u=a.language,d=a.children,p=a.style,f=p===void 0?t:p,h=a.customStyle,b=h===void 0?{}:h,v=a.codeTagProps,E=v===void 0?{className:u?"language-".concat(u):void 0,style:oa(oa({},f['code[class*="language-"]']),f['code[class*="language-'.concat(u,'"]')])}:v,w=a.useInlineStyles,T=w===void 0?!0:w,k=a.showLineNumbers,A=k===void 0?!1:k,_=a.showInlineLineNumbers,R=_===void 0?!0:_,L=a.startingLineNumber,j=L===void 0?1:L,D=a.lineNumberContainerStyle,z=a.lineNumberStyle,F=z===void 0?{}:z,V=a.wrapLines,M=a.wrapLongLines,H=M===void 0?!1:M,K=a.lineProps,Z=K===void 0?{}:K,te=a.renderer,Q=a.PreTag,P=Q===void 0?"pre":Q,W=a.CodeTag,ne=W===void 0?"code":W,ce=a.code,C=ce===void 0?(Array.isArray(d)?d[0]:d)||"":ce,U=a.astGenerator,J=Sz(a,Lz);U=U||e;var I=A?Ke.createElement(Uz,{containerStyle:D,codeStyle:E.style||{},numberStyle:F,startingLineNumber:j,codeString:C}):null,ue=f.hljs||f['pre[class*="language-"]']||{backgroundColor:"#fff"},fe=GN(U)?"hljs":"prismjs",de=T?Object.assign({},J,{style:Object.assign({},ue,b)}):Object.assign({},J,{className:J.className?"".concat(fe," ").concat(J.className):fe,style:Object.assign({},b)});if(H?E.style=oa({whiteSpace:"pre-wrap"},E.style):E.style=oa({whiteSpace:"pre"},E.style),!U)return Ke.createElement(P,de,I,Ke.createElement(ne,E,C));(V===void 0&&te||H)&&(V=!0),te=te||zz;var le=[{type:"text",value:C}],we=Pz({astGenerator:U,language:u,code:C,defaultCodeValue:le});we.language===null&&(we.value=le);var me=(o=(s=C.match(/\n/g))===null||s===void 0?void 0:s.length)!==null&&o!==void 0?o:0,Re=j+me,Oe=Bz(we,V,Z,A,R,j,Re,F,H);return Ke.createElement(P,de,Ke.createElement(ne,E,!R&&I,te({rows:Oe,stylesheet:f,useInlineStyles:T})))}}const Gz="modulepreload",Hz=function(e){return"/"+e},XA={},ZA=function(t,r,a){let o=Promise.resolve();if(r&&r.length>0){let p=function(f){return Promise.all(f.map(h=>Promise.resolve(h).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};document.getElementsByTagName("link");const u=document.querySelector("meta[property=csp-nonce]"),d=u?.nonce||u?.getAttribute("nonce");o=p(r.map(f=>{if(f=Hz(f),f in XA)return;XA[f]=!0;const h=f.endsWith(".css"),b=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${f}"]${b}`))return;const v=document.createElement("link");if(v.rel=h?"stylesheet":Gz,h||(v.as="script"),v.crossOrigin="",v.href=f,d&&v.setAttribute("nonce",d),document.head.appendChild(v),h)return new Promise((E,w)=>{v.addEventListener("load",E),v.addEventListener("error",()=>w(new Error(`Unable to preload CSS for ${f}`)))})}))}function s(u){const d=new Event("vite:preloadError",{cancelable:!0});if(d.payload=u,window.dispatchEvent(d),!d.defaultPrevented)throw u}return o.then(u=>{for(const d of u||[])d.status==="rejected"&&s(d.reason);return t().catch(s)})},Vz=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apex","apl","applescript","aql","arduino","arff","armasm","arturo","asciidoc","asm6502","asmatmel","aspnet","autohotkey","autoit","avisynth","avro-idl","awk","bash","basic","batch","bbcode","bbj","bicep","birb","bison","bnf","bqn","brainfuck","brightscript","bro","bsl","c","cfscript","chaiscript","cil","cilkc","cilkcpp","clike","clojure","cmake","cobol","coffeescript","concurnas","cooklang","coq","cpp","crystal","csharp","cshtml","csp","css-extras","css","csv","cue","cypher","d","dart","dataweave","dax","dhall","diff","django","dns-zone-file","docker","dot","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","false","firestore-security-rules","flow","fortran","fsharp","ftl","gap","gcode","gdscript","gedcom","gettext","gherkin","git","glsl","gml","gn","go-module","go","gradle","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hoon","hpkp","hsts","http","ichigojam","icon","icu-message-format","idris","iecst","ignore","inform7","ini","io","j","java","javado
Line truncated
|
||||
?|
|
||||
|(?![\\s\\S])))+`,"m"),alias:s,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}}),Object.defineProperty(t.languages.diff,"PREFIXES",{value:r})})(e)}Bt.displayName="markup-templating";Bt.aliases=[];function Bt(e){e.register(Ot),(function(t){function r(a,o){return"___"+a.toUpperCase()+o+"___"}Object.defineProperties(t.languages["markup-templating"]={},{buildPlaceholders:{value:function(a,o,s,u){if(a.language===o){var d=a.tokenStack=[];a.code=a.code.replace(s,function(p){if(typeof u=="function"&&!u(p))return p;for(var f=d.length,h;a.code.indexOf(h=r(o,f))!==-1;)++f;return d[f]=p,h}),a.grammar=t.languages.markup}}},tokenizePlaceholders:{value:function(a,o){if(a.language!==o||!a.tokenStack)return;a.grammar=t.languages[o];var s=0,u=Object.keys(a.tokenStack);function d(p){for(var f=0;f<p.length&&!(s>=u.length);f++){var h=p[f];if(typeof h=="string"||h.content&&typeof h.content=="string"){var b=u[s],v=a.tokenStack[b],E=typeof h=="string"?h:h.content,w=r(o,b),T=E.indexOf(w);if(T>-1){++s;var k=E.substring(0,T),A=new t.Token(o,t.tokenize(v,a.grammar),"language-"+o,v),_=E.substring(T+w.length),R=[];k&&R.push.apply(R,d([k])),R.push(A),_&&R.push.apply(R,d([_])),typeof h=="string"?p.splice.apply(p,[f,1].concat(R)):h.content=R}}else h.content&&d(h.content)}return p}d(a.tokens)}}})})(e)}uh.displayName="django";uh.aliases=["jinja2"];function uh(e){e.register(Bt),(function(t){t.languages.django={comment:/^\{#[\s\S]*?#\}$/,tag:{pattern:/(^\{%[+-]?\s*)\w+/,lookbehind:!0,alias:"keyword"},delimiter:{pattern:/^\{[{%][+-]?|[+-]?[}%]\}$/,alias:"punctuation"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},filter:{pattern:/(\|)\w+/,lookbehind:!0,alias:"function"},test:{pattern:/(\bis\s+(?:not\s+)?)(?!not\b)\w+/,lookbehind:!0,alias:"function"},function:/\b[a-z_]\w+(?=\s*\()/i,keyword:/\b(?:and|as|by|else|for|if|import|in|is|loop|not|or|recursive|with|without)\b/,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,number:/\b\d+(?:\.\d+)?\b/,boolean:/[Ff]alse|[Nn]one|[Tt]rue/,variable:/\b\w+\b/,punctuation:/[{}[\](),.:;]/};var r=/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/g,a=t.languages["markup-templating"];t.hooks.add("before-tokenize",function(o){a.buildPlaceholders(o,"django",r)}),t.hooks.add("after-tokenize",function(o){a.tokenizePlaceholders(o,"django")}),t.languages.jinja2=t.languages.django,t.hooks.add("before-tokenize",function(o){a.buildPlaceholders(o,"jinja2",r)}),t.hooks.add("after-tokenize",function(o){a.tokenizePlaceholders(o,"jinja2")})})(e)}ch.displayName="dns-zone-file";ch.aliases=["dns-zone"];function ch(e){e.languages["dns-zone-file"]={comment:/;.*/,string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},variable:[{pattern:/(^\$ORIGIN[ \t]+)\S+/m,lookbehind:!0},{pattern:/(^|\s)@(?=\s|$)/,lookbehind:!0}],keyword:/^\$(?:INCLUDE|ORIGIN|TTL)(?=\s|$)/m,class:{pattern:/(^|\s)(?:CH|CS|HS|IN)(?=\s|$)/,lookbehind:!0,alias:"keyword"},type:{pattern:/(^|\s)(?:A|A6|AAAA|AFSDB|APL|ATMA|CAA|CDNSKEY|CDS|CERT|CNAME|DHCID|DLV|DNAME|DNSKEY|DS|EID|GID|GPOS|HINFO|HIP|IPSECKEY|ISDN|KEY|KX|LOC|MAILA|MAILB|MB|MD|MF|MG|MINFO|MR|MX|NAPTR|NB|NBSTAT|NIMLOC|NINFO|NS|NSAP|NSAP-PTR|NSEC|NSEC3|NSEC3PARAM|NULL|NXT|OPENPGPKEY|PTR|PX|RKEY|RP|RRSIG|RT|SIG|SINK|SMIMEA|SOA|SPF|SRV|SSHFP|TA|TKEY|TLSA|TSIG|TXT|UID|UINFO|UNSPEC|URI|WKS|X25)(?=\s|$)/,lookbehind:!0,alias:"keyword"},punctuation:/[()]/},e.languages["dns-zone"]=e.languages["dns-zone-file"]}dh.displayName="docker";dh.aliases=["dockerfile"];function dh(e){(function(t){var r=/\\[\r\n](?:\s|\\[\r\n]|#.*(?!.))*(?![\s#]|\\[\r\n])/.source,a=/(?:[ \t]+(?![ \t])(?:<SP_BS>)?|<SP_BS>)/.source.replace(/<SP_BS>/g,function(){return r}),o=/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"|'(?:[^'\\\r\n]|\\(?:\r\n|[\s\S]))*'/.source,s=/--[\w-]+=(?:<STR>|(?!["'])(?:[^\s\\]|\\.)+)/.source.replace(/<STR>/g,function(){return o}),u={pattern:RegExp(o),greedy:!0},d={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function p(f,h){return f=f.replace(/<OPT>/g,function(){return s}).replace(/<SP>/g,function(){return a}),RegExp(f,h)}t.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:p(/(^(?:ONBUILD<SP>)?\w+<SP>)<OPT>(?:<SP><OPT>)*/.source,"i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[u,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:p(/(^(?:ONBUILD<SP>)?HEALTHCHECK<SP>(?:<OPT><SP>)*)(?:CMD|NONE)\b/.source,"i"),lookbehind:!0,greedy:!0},{pattern:p(/(^(?:ONBUILD<SP>)?FROM<SP>(?:<OPT><SP>)*(?!--)[^ \t\\]+<SP>)AS/.source,"i"),lookbehind:!0,greedy:!0},{pattern:p(/(^ONBUILD<SP>)\w+/.source,"i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:d,string:u,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,oper
Line truncated
|
||||
]))(?:`+(/[^\s\\]/.source+'|[ ]+(?:(?![ "])|'+a+")|"+/\\[\r\n]+(?:[#;].*[\r\n]+)*(?![#;])/.source)+")*"),lookbehind:!0,greedy:!0,alias:"attr-value",inside:{comment:r,quoted:{pattern:RegExp(/(^|\s)/.source+a),lookbehind:!0,greedy:!0},punctuation:/\\$/m,boolean:{pattern:/^(?:false|no|off|on|true|yes)$/,greedy:!0}}},punctuation:/=/}})(e)}Ul.displayName="t4-templating";Ul.aliases=[];function Ul(e){(function(t){function r(o,s,u){return{pattern:RegExp("<#"+o+"[\\s\\S]*?#>"),alias:"block",inside:{delimiter:{pattern:RegExp("^<#"+o+"|#>$"),alias:"important"},content:{pattern:/[\s\S]+/,inside:s,alias:u}}}}function a(o){var s=t.languages[o],u="language-"+o;return{block:{pattern:/<#[\s\S]+?#>/,inside:{directive:r("@",{"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/,inside:{punctuation:/^=|^["']|["']$/}},keyword:/\b\w+(?=\s)/,"attr-name":/\b\w+/}),expression:r("=",s,u),"class-feature":r("\\+",s,u),standard:r("",s,u)}}}}t.languages["t4-templating"]=Object.defineProperty({},"createT4",{value:a})})(e)}TE.displayName="t4-cs";TE.aliases=["t4"];function TE(e){e.register(to),e.register(Ul),e.languages.t4=e.languages["t4-cs"]=e.languages["t4-templating"].createT4("csharp")}bc.displayName="vbnet";bc.aliases=[];function bc(e){e.register(dc),e.languages.vbnet=e.languages.extend("basic",{comment:[{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},{pattern:/(^|[^\\:])'.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(^|[^"])"(?:""|[^"])*"(?!")/,lookbehind:!0,greedy:!0},keyword:/(?:\b(?:ADDHANDLER|ADDRESSOF|ALIAS|AND|ANDALSO|AS|BEEP|BLOAD|BOOLEAN|BSAVE|BYREF|BYTE|BYVAL|CALL(?: ABSOLUTE)?|CASE|CATCH|CBOOL|CBYTE|CCHAR|CDATE|CDBL|CDEC|CHAIN|CHAR|CHDIR|CINT|CLASS|CLEAR|CLNG|CLOSE|CLS|COBJ|COM|COMMON|CONST|CONTINUE|CSBYTE|CSHORT|CSNG|CSTR|CTYPE|CUINT|CULNG|CUSHORT|DATA|DATE|DECIMAL|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DEFAULT|DELEGATE|DIM|DIRECTCAST|DO|DOUBLE|ELSE|ELSEIF|END|ENUM|ENVIRON|ERASE|ERROR|EVENT|EXIT|FALSE|FIELD|FILES|FINALLY|FOR(?: EACH)?|FRIEND|FUNCTION|GET|GETTYPE|GETXMLNAMESPACE|GLOBAL|GOSUB|GOTO|HANDLES|IF|IMPLEMENTS|IMPORTS|IN|INHERITS|INPUT|INTEGER|INTERFACE|IOCTL|IS|ISNOT|KEY|KILL|LET|LIB|LIKE|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|ME|MKDIR|MOD|MODULE|MUSTINHERIT|MUSTOVERRIDE|MYBASE|MYCLASS|NAME|NAMESPACE|NARROWING|NEW|NEXT|NOT|NOTHING|NOTINHERITABLE|NOTOVERRIDABLE|OBJECT|OF|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPERATOR|OPTION(?: BASE)?|OPTIONAL|OR|ORELSE|OUT|OVERLOADS|OVERRIDABLE|OVERRIDES|PARAMARRAY|PARTIAL|POKE|PRIVATE|PROPERTY|PROTECTED|PUBLIC|PUT|RAISEEVENT|READ|READONLY|REDIM|REM|REMOVEHANDLER|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SBYTE|SELECT(?: CASE)?|SET|SHADOWS|SHARED|SHELL|SHORT|SINGLE|SLEEP|STATIC|STEP|STOP|STRING|STRUCTURE|SUB|SWAP|SYNCLOCK|SYSTEM|THEN|THROW|TIMER|TO|TROFF|TRON|TRUE|TRY|TRYCAST|TYPE|TYPEOF|UINTEGER|ULONG|UNLOCK|UNTIL|USHORT|USING|VIEW PRINT|WAIT|WEND|WHEN|WHILE|WIDENING|WITH|WITHEVENTS|WRITE|WRITEONLY|XOR)|\B(?:#CONST|#ELSE|#ELSEIF|#END|#IF))(?:\$|\b)/i,punctuation:/[,;:(){}]/})}AE.displayName="t4-vb";AE.aliases=[];function AE(e){e.register(Ul),e.register(bc),e.languages["t4-vb"]=e.languages["t4-templating"].createT4("vbnet")}kE.displayName="tap";kE.aliases=[];function kE(e){e.register(cc),e.languages.tap={fail:/not ok[^#{\n\r]*/,pass:/ok[^#{\n\r]*/,pragma:/pragma [+-][a-z]+/,bailout:/bail out!.*/i,version:/TAP version \d+/i,plan:/\b\d+\.\.\d+(?: +#.*)?/,subtest:{pattern:/# Subtest(?:: .*)?/,greedy:!0},punctuation:/[{}]/,directive:/#.*/,yamlish:{pattern:/(^[ \t]*)---[\s\S]*?[\r\n][ \t]*\.\.\.$/m,lookbehind:!0,inside:e.languages.yaml,alias:"language-yaml"}}}_E.displayName="tcl";_E.aliases=[];function _E(e){e.languages.tcl={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:{pattern:/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"/,greedy:!0},variable:[{pattern:/(\$)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/,lookbehind:!0},{pattern:/(\$)\{[^}]+\}/,lookbehind:!0},{pattern:/(^[\t ]*set[ \t]+)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/m,lookbehind:!0}],function:{pattern:/(^[\t ]*proc[ \t]+)\S+/m,lookbehind:!0},builtin:[{pattern:/(^[\t ]*)(?:break|class|continue|error|eval|exit|for|foreach|if|proc|return|switch|while)\b/m,lookbehind:!0},/\b(?:else|elseif)\b/],scope:{pattern:/(^[\t ]*)(?:global|upvar|variable)\b/m,lookbehind:!0,alias:"constant"},keyword:{pattern:/(^[\t ]*|\[)(?:Safe_Base|Tcl|after|append|apply|array|auto_(?:execok|import|load|mkindex|qualify|reset)|automkindex_old|bgerror|binary|catch|cd|chan|clock|close|concat|dde|dict|encoding|eof|exec|expr|fblocked|fconfigure|fcopy|file(?:event|name)?|flush|gets|glob|history|http|incr|info|interp|join|lappend|lassign|lindex|linsert|list|llength|load|lrange|lrepeat|lreplace|lreverse|lsearch|lset|lsort|math(?:func|op)|memory|msgcat|namespace|open|package|parray|pid|pkg_mkIndex|platform|puts|pwd|re_syntax|read|refchan|regexp|registry|regsub|rename|scan|seek|set|socket|source|split|string|subst|tcl(?:_endOfWord|_findLibrary|startOf(?:Next|Previous)Word|test|vars|wordBreak(?:After|Before))|tell|time|tm|trace|unknown|unload|unset|update|uplevel|vwait
Line truncated
|
||||
- a positive number
|
||||
- less than the value passed to \`max\` (or ${dv} if no \`max\` prop is set)
|
||||
- \`null\` or \`undefined\` if the progress is indeterminate.
|
||||
|
||||
Defaulting to \`null\`.`}var b3=PR,y3=GR;function E3({className:e,value:t,...r}){return g.jsx(b3,{"data-slot":"progress",className:be("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",e),...r,children:g.jsx(y3,{"data-slot":"progress-indicator",className:"bg-primary h-full w-full flex-1 transition-all",style:{transform:`translateX(-${100-(t||0)}%)`}})})}function Bl(e){const t=S.useRef({value:e,previous:e});return S.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}function zl(e){const[t,r]=S.useState(void 0);return gt(()=>{if(e){r({width:e.offsetWidth,height:e.offsetHeight});const a=new ResizeObserver(o=>{if(!Array.isArray(o)||!o.length)return;const s=o[0];let u,d;if("borderBoxSize"in s){const p=s.borderBoxSize,f=Array.isArray(p)?p[0]:p;u=f.inlineSize,d=f.blockSize}else u=e.offsetWidth,d=e.offsetHeight;r({width:u,height:d})});return a.observe(e,{box:"border-box"}),()=>a.unobserve(e)}else r(void 0)},[e]),t}var Ac="Switch",[v3]=jt(Ac),[S3,x3]=v3(Ac),VR=S.forwardRef((e,t)=>{const{__scopeSwitch:r,name:a,checked:o,defaultChecked:s,required:u,disabled:d,value:p="on",onCheckedChange:f,form:h,...b}=e,[v,E]=S.useState(null),w=_e(t,R=>E(R)),T=S.useRef(!1),k=v?h||!!v.closest("form"):!0,[A,_]=en({prop:o,defaultProp:s??!1,onChange:f,caller:Ac});return g.jsxs(S3,{scope:r,checked:A,disabled:d,children:[g.jsx(Se.button,{type:"button",role:"switch","aria-checked":A,"aria-required":u,"data-state":KR(A),"data-disabled":d?"":void 0,disabled:d,value:p,...b,ref:w,onClick:ge(e.onClick,R=>{_(L=>!L),k&&(T.current=R.isPropagationStopped(),T.current||R.stopPropagation())})}),k&&g.jsx(WR,{control:v,bubbles:!T.current,name:a,value:p,checked:A,required:u,disabled:d,form:h,style:{transform:"translateX(-100%)"}})]})});VR.displayName=Ac;var qR="SwitchThumb",YR=S.forwardRef((e,t)=>{const{__scopeSwitch:r,...a}=e,o=x3(qR,r);return g.jsx(Se.span,{"data-state":KR(o.checked),"data-disabled":o.disabled?"":void 0,...a,ref:t})});YR.displayName=qR;var w3="SwitchBubbleInput",WR=S.forwardRef(({__scopeSwitch:e,control:t,checked:r,bubbles:a=!0,...o},s)=>{const u=S.useRef(null),d=_e(u,s),p=Bl(r),f=zl(t);return S.useEffect(()=>{const h=u.current;if(!h)return;const b=window.HTMLInputElement.prototype,E=Object.getOwnPropertyDescriptor(b,"checked").set;if(p!==r&&E){const w=new Event("click",{bubbles:a});E.call(h,r),h.dispatchEvent(w)}},[p,r,a]),g.jsx("input",{type:"checkbox","aria-hidden":!0,defaultChecked:r,...o,tabIndex:-1,ref:d,style:{...o.style,...f,position:"absolute",pointerEvents:"none",opacity:0,margin:0}})});WR.displayName=w3;function KR(e){return e?"checked":"unchecked"}var T3=VR,A3=YR;function k3({className:e,size:t="default",...r}){return g.jsx(T3,{"data-slot":"switch","data-size":t,className:be("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6",e),...r,children:g.jsx(A3,{"data-slot":"switch-thumb",className:be("bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")})})}var kc="Checkbox",[_3]=jt(kc),[N3,pv]=_3(kc);function R3(e){const{__scopeCheckbox:t,checked:r,children:a,defaultChecked:o,disabled:s,form:u,name:d,onCheckedChange:p,required:f,value:h="on",internal_do_not_use_render:b}=e,[v,E]=en({prop:r,defaultProp:o??!1,onChange:p,caller:kc}),[w,T]=S.useState(null),[k,A]=S.useState(null),_=S.useRef(!1),R=w?!!u||!!w.closest("form"):!0,L={checked:v,disabled:s,setChecked:E,control:w,setControl:T,name:d,form:u,value:h,hasConsumerStoppedPropagationRef:_,required:f,defaultChecked:la(o)?!1:o,isFormControl:R,bubbleInput:k,setBubbleInput:A};return g.jsx(N3,{scope:t,...L,children:C3(b)?b(L):a})}var XR="CheckboxTrigger",ZR=S.forwardRef(({__scopeCheckbox:e,onKeyDown:t,onClick:r,...a},o)=>{const{control:s,value:u,disabled:d,checked:p,required:f,setControl:h,setChecked:b,hasConsumerStoppedPropagationRef:v,isFormControl:E,bubbleInput:w}=pv(XR,e),T=_e(o,h),k=S.useRef(p);return S.useEffect(()=>{const A=s?.form;if(A){const _=()=>b(k.current);return A.addEventListener("reset",_),()=>A.removeEventListener("reset",_)}},[s,b]),g.jsx(Se.button,{type:"button",role:"checkbox","aria-checked":la(p)?"mixed":p,"aria-required":f,"data-state":rC(p),"data-disabled":d?"":void 0,disabled:d,value:u,...a,ref:T,onKeyDown:ge(t,A=>{A.key==="Enter"&&A.preventDefault()}),onClick:ge(r,A=>{b(_=>la(_)?!0:!_),w&&E&&(v.current=A.isPropagatiLine truncated
|
||||
.`.concat(OG,` {
|
||||
`+qe+n+dt}var Yn=!1;function Wn(n,i){if(!n||Yn)return"";Yn=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var d={DetermineComponentFrameRoot:function(){try{if(i){var ce=function(){throw Error()};if(Object.defineProperty(ce.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(ce,[])}catch(te){var Z=te}Reflect.construct(n,[],ce)}else{try{ce.call()}catch(te){Z=te}n.call(ce.prototype)}}else{try{throw Error()}catch(te){Z=te}(ce=n())&&typeof ce.catch=="function"&&ce.catch(function(){})}}catch(te){if(te&&Z&&typeof te.stack=="string")return[te.stack,Z.stack]}return[null,null]}};d.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var b=Object.getOwnPropertyDescriptor(d.DetermineComponentFrameRoot,"name");b&&b.configurable&&Object.defineProperty(d.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var y=d.DetermineComponentFrameRoot(),T=y[0],R=y[1];if(T&&R){var P=T.split(`
|
||||
`),W=R.split(`
|
||||
`);for(b=d=0;d<P.length&&!P[d].includes("DetermineComponentFrameRoot");)d++;for(;b<W.length&&!W[b].includes("DetermineComponentFrameRoot");)b++;if(d===P.length||b===W.length)for(d=P.length-1,b=W.length-1;1<=d&&0<=b&&P[d]!==W[b];)b--;for(;1<=d&&0<=b;d--,b--)if(P[d]!==W[b]){if(d!==1||b!==1)do if(d--,b--,0>b||P[d]!==W[b]){var oe=`
|
||||
`+P[d].replace(" at new "," at ");return n.displayName&&oe.includes("<anonymous>")&&(oe=oe.replace("<anonymous>",n.displayName)),oe}while(1<=d&&0<=b);break}}}finally{Yn=!1,Error.prepareStackTrace=l}return(l=n?n.displayName||n.name:"")?st(l):""}function Ir(n,i){switch(n.tag){case 26:case 27:case 5:return st(n.type);case 16:return st("Lazy");case 13:return n.child!==i&&i!==null?st("Suspense Fallback"):st("Suspense");case 19:return st("SuspenseList");case 0:case 15:return Wn(n.type,!1);case 11:return Wn(n.type.render,!1);case 1:return Wn(n.type,!0);case 31:return st("Activity");default:return""}}function Jl(n){try{var i="",l=null;do i+=Ir(n,l),l=n,n=n.return;while(n);return i}catch(d){return`
|
||||
Error generating stack: `+d.message+`
|
||||
`+d.stack}}var Za=Object.prototype.hasOwnProperty,Qa=e.unstable_scheduleCallback,bo=e.unstable_cancelCallback,Zc=e.unstable_shouldYield,Qc=e.unstable_requestPaint,Pt=e.unstable_now,Jc=e.unstable_getCurrentPriorityLevel,le=e.unstable_ImmediatePriority,ge=e.unstable_UserBlockingPriority,_e=e.unstable_NormalPriority,Me=e.unstable_LowPriority,We=e.unstable_IdlePriority,an=e.log,lr=e.unstable_setDisableYieldValue,$t=null,Et=null;function qt(n){if(typeof an=="function"&&lr(n),Et&&typeof Et.setStrictMode=="function")try{Et.setStrictMode($t,n)}catch{}}var tt=Math.clz32?Math.clz32:gO,Or=Math.log,Ln=Math.LN2;function gO(n){return n>>>=0,n===0?32:31-(Or(n)/Ln|0)|0}var es=256,ts=262144,ns=4194304;function ba(n){var i=n&42;if(i!==0)return i;switch(n&-n){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return n&261888;case 262144:case 524288:case 1048576:case 2097152:return n&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return n&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return n}}function rs(n,i,l){var d=n.pendingLanes;if(d===0)return 0;var b=0,y=n.suspendedLanes,T=n.pingedLanes;n=n.warmLanes;var R=d&134217727;return R!==0?(d=R&~y,d!==0?b=ba(d):(T&=R,T!==0?b=ba(T):l||(l=R&~n,l!==0&&(b=ba(l))))):(R=d&~y,R!==0?b=ba(R):T!==0?b=ba(T):l||(l=d&~n,l!==0&&(b=ba(l)))),b===0?0:i!==0&&i!==b&&(i&y)===0&&(y=b&-b,l=i&-i,y>=l||y===32&&(l&4194048)!==0)?i:b}function yo(n,i){return(n.pendingLanes&~(n.suspendedLanes&~n.pingedLanes)&i)===0}function mO(n,i){switch(n){case 1:case 2:case 4:case 8:case 64:return i+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return i+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function eS(){var n=ns;return ns<<=1,(ns&62914560)===0&&(ns=4194304),n}function ed(n){for(var i=[],l=0;31>l;l++)i.push(n);return i}function vo(n,i){n.pendingLanes|=i,i!==268435456&&(n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0)}function hO(n,i,l,d,b,y){var T=n.pendingLanes;n.pendingLanes=l,n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0,n.expiredLanes&=l,n.entangledLanes&=l,n.errorRecoveryDisabledLanes&=l,n.shellSuspendCounter=0;var R=n.entanglements,P=n.expirationTimes,W=n.hiddenUpdates;for(l=T&~l;0<l;){var oe=31-tt(l),ce=1<<oe;R[oe]=0,P[oe]=-1;var Z=W[oe];if(Z!==null)for(W[oe]=null,oe=0;oe<Z.length;oe++){var te=Z[oe];te!==null&&(te.lane&=-536870913)}l&=~ce}d!==0&&tS(n,d,0),y!==0&&b===0&&n.tag!==0&&(n.suspendedLanes|=y&~(T&~i))}function tS(n,i,l){n.pendingLanes|=i,n.suspendedLanes&=~i;var d=31-tt(i);n.entangledLanes|=i,n.entanglements[d]=n.entanglements[d]|1073741824|l&261930}function nS(n,i){var l=n.entangledLanes|=i;for(n=n.entanglements;l;){var d=31-tt(l),b=1<<d;b&i|n[d]&i&&(n[d]|=i),l&=~b}}function rS(n,i){var l=i&-i;return l=(l&42)!==0?1:td(l),(l&(n.suspendedLanes|i))!==0?0:l}function td(n){switch(n){case 2:n=1;break;case 8:n=4;break;case 32:n=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:n=128;break;case 268435456:n=134217728;break;default:n=0}return n}function nd(n){return n&=-n,2<n?8<n?(n&134217727)!==0?32:268435456:8:2}function aS(){var n=H.p;return n!==0?n:(n=window.event,n===void 0?32:GT(n.type))}function iS(n,i){var l=H.p;try{return H.p=n,i()}finally{H.p=l}}var Lr=Math.random().toString(36).slice(2),_t="__reactFiber$"+Lr,Yt="__reactProps$"+Lr,Ja="__reactContainer$"+Lr,rd="__reactEvents$"+Lr,bO="__reactListeners$"+Lr,yO="__reactHandles$"+Lr,oS="__reactResources$"+Lr,Eo="__reactMarker$"+Lr;function ad(n){delete n[_t],delete n[Yt],delete n[rd],delete n[bO],delete n[yO]}function ei(n){var i=n[_t];if(i)return i;for(var l=n.parentNode;l;){if(i=l[Ja]||l[_t]){if(l=i.alternate,i.child!==null||l!==null&&l.child!==null)for(n=_T(n);n!==null;){if(l=n[_t])return l;n=_T(n)}return i}n=l,l=n.parentNode}return null}function ti(n){if(n=n[_t]||n[Ja]){var i=n.tag;if(i===5||i===6||i===13||i===31||i===26||i===27||i===3)return n}return null}function So(n){var i=n.tag;if(i===5||i===26||i===27||i===6)return n.stateNode;throw Error(a(33))}function ni(n){var i=n[oS];return i||(i=n[oS]={hoistableStyles:new Map,hoistableScripts:new Map}),i}function wt(n){n[Eo]=!0}var lS=new Set,sS={};function ya(n,i){ri(n,i),ri(n+"Capture",i)}function ri(n,i){for(sS[n]=i,n=0;n<i.length;n++)lS.add(i[n])}var vO=RegExp("^[:A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u20
Line truncated
|
||||
`).replace(X1,"")}function bT(n,i){return i=hT(i),hT(n)===i}function Je(n,i,l,d,b,y){switch(l){case"children":typeof d=="string"?i==="body"||i==="textarea"&&d===""||ii(n,d):(typeof d=="number"||typeof d=="bigint")&&i!=="body"&&ii(n,""+d);break;case"className":is(n,"class",d);break;case"tabIndex":is(n,"tabindex",d);break;case"dir":case"role":case"viewBox":case"width":case"height":is(n,l,d);break;case"style":bS(n,d,y);break;case"data":if(i!=="object"){is(n,"data",d);break}case"src":case"href":if(d===""&&(i!=="a"||l!=="href")){n.removeAttribute(l);break}if(d==null||typeof d=="function"||typeof d=="symbol"||typeof d=="boolean"){n.removeAttribute(l);break}d=ls(""+d),n.setAttribute(l,d);break;case"action":case"formAction":if(typeof d=="function"){n.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof y=="function"&&(l==="formAction"?(i!=="input"&&Je(n,i,"name",b.name,b,null),Je(n,i,"formEncType",b.formEncType,b,null),Je(n,i,"formMethod",b.formMethod,b,null),Je(n,i,"formTarget",b.formTarget,b,null)):(Je(n,i,"encType",b.encType,b,null),Je(n,i,"method",b.method,b,null),Je(n,i,"target",b.target,b,null)));if(d==null||typeof d=="symbol"||typeof d=="boolean"){n.removeAttribute(l);break}d=ls(""+d),n.setAttribute(l,d);break;case"onClick":d!=null&&(n.onclick=ur);break;case"onScroll":d!=null&&Ue("scroll",n);break;case"onScrollEnd":d!=null&&Ue("scrollend",n);break;case"dangerouslySetInnerHTML":if(d!=null){if(typeof d!="object"||!("__html"in d))throw Error(a(61));if(l=d.__html,l!=null){if(b.children!=null)throw Error(a(60));n.innerHTML=l}}break;case"multiple":n.multiple=d&&typeof d!="function"&&typeof d!="symbol";break;case"muted":n.muted=d&&typeof d!="function"&&typeof d!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(d==null||typeof d=="function"||typeof d=="boolean"||typeof d=="symbol"){n.removeAttribute("xlink:href");break}l=ls(""+d),n.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":d!=null&&typeof d!="function"&&typeof d!="symbol"?n.setAttribute(l,""+d):n.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":d&&typeof d!="function"&&typeof d!="symbol"?n.setAttribute(l,""):n.removeAttribute(l);break;case"capture":case"download":d===!0?n.setAttribute(l,""):d!==!1&&d!=null&&typeof d!="function"&&typeof d!="symbol"?n.setAttribute(l,d):n.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":d!=null&&typeof d!="function"&&typeof d!="symbol"&&!isNaN(d)&&1<=d?n.setAttribute(l,d):n.removeAttribute(l);break;case"rowSpan":case"start":d==null||typeof d=="function"||typeof d=="symbol"||isNaN(d)?n.removeAttribute(l):n.setAttribute(l,d);break;case"popover":Ue("beforetoggle",n),Ue("toggle",n),as(n,"popover",d);break;case"xlinkActuate":sr(n,"http://www.w3.org/1999/xlink","xlink:actuate",d);break;case"xlinkArcrole":sr(n,"http://www.w3.org/1999/xlink","xlink:arcrole",d);break;case"xlinkRole":sr(n,"http://www.w3.org/1999/xlink","xlink:role",d);break;case"xlinkShow":sr(n,"http://www.w3.org/1999/xlink","xlink:show",d);break;case"xlinkTitle":sr(n,"http://www.w3.org/1999/xlink","xlink:title",d);break;case"xlinkType":sr(n,"http://www.w3.org/1999/xlink","xlink:type",d);break;case"xmlBase":sr(n,"http://www.w3.org/XML/1998/namespace","xml:base",d);break;case"xmlLang":sr(n,"http://www.w3.org/XML/1998/namespace","xml:lang",d);break;case"xmlSpace":sr(n,"http://www.w3.org/XML/1998/namespace","xml:space",d);break;case"is":as(n,"is",d);break;case"innerText":case"textContent":break;default:(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")&&(l=TO.get(l)||l,as(n,l,d))}}function Wp(n,i,l,d,b,y){switch(l){case"style":bS(n,d,y);break;case"dangerouslySetInnerHTML":if(d!=null){if(typeof d!="object"||!("__html"in d))throw Error(a(61));if(l=d.__html,l!=null){if(b.children!=null)throw Error(a(60));n.innerHTML=l}}break;case"children":typeof d=="string"?ii(n,d):(typeof d=="number"||typeof d=="bigint")&&ii(n,""+d);break;case"onScroll":d!=null&&Ue("scroll",n);break;case"onScrollEnd":d!=null&&Ue("scrollend",n);break;case"onClick":d!=null&&(n.onclick=ur);break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"inne
Line truncated
|
||||
.`.concat(y2,` {
|
||||
overflow: hidden `).concat(a,`;
|
||||
padding-right: `).concat(d,"px ").concat(a,`;
|
||||
padding-right: `).concat(c,"px ").concat(a,`;
|
||||
}
|
||||
body[`).concat(qi,`] {
|
||||
body[`).concat(Wi,`] {
|
||||
overflow: hidden `).concat(a,`;
|
||||
overscroll-behavior: contain;
|
||||
`).concat([t&&"position: relative ".concat(a,";"),r==="margin"&&`
|
||||
@@ -68,41 +21,90 @@ Defaulting to \`null\`.`}var b3=PR,y3=GR;function E3({className:e,value:t,...r})
|
||||
padding-right: `).concat(u,`px;
|
||||
margin-left:0;
|
||||
margin-top:0;
|
||||
margin-right: `).concat(d,"px ").concat(a,`;
|
||||
`),r==="padding"&&"padding-right: ".concat(d,"px ").concat(a,";")].filter(Boolean).join(""),`
|
||||
margin-right: `).concat(c,"px ").concat(a,`;
|
||||
`),r==="padding"&&"padding-right: ".concat(c,"px ").concat(a,";")].filter(Boolean).join(""),`
|
||||
}
|
||||
|
||||
.`).concat(Iu,` {
|
||||
right: `).concat(d,"px ").concat(a,`;
|
||||
.`).concat(Du,` {
|
||||
right: `).concat(c,"px ").concat(a,`;
|
||||
}
|
||||
|
||||
.`).concat(Lu,` {
|
||||
margin-right: `).concat(d,"px ").concat(a,`;
|
||||
.`).concat(Mu,` {
|
||||
margin-right: `).concat(c,"px ").concat(a,`;
|
||||
}
|
||||
|
||||
.`).concat(Iu," .").concat(Iu,` {
|
||||
.`).concat(Du," .").concat(Du,` {
|
||||
right: 0 `).concat(a,`;
|
||||
}
|
||||
|
||||
.`).concat(Lu," .").concat(Lu,` {
|
||||
.`).concat(Mu," .").concat(Mu,` {
|
||||
margin-right: 0 `).concat(a,`;
|
||||
}
|
||||
|
||||
body[`).concat(qi,`] {
|
||||
`).concat(IG,": ").concat(d,`px;
|
||||
body[`).concat(Wi,`] {
|
||||
`).concat(v2,": ").concat(c,`px;
|
||||
}
|
||||
`)},Dk=function(){var e=parseInt(document.body.getAttribute(qi)||"0",10);return isFinite(e)?e:0},ZG=function(){S.useEffect(function(){return document.body.setAttribute(qi,(Dk()+1).toString()),function(){var e=Dk()-1;e<=0?document.body.removeAttribute(qi):document.body.setAttribute(qi,e.toString())}},[])},QG=function(e){var t=e.noRelative,r=e.noImportant,a=e.gapMode,o=a===void 0?"margin":a;ZG();var s=S.useMemo(function(){return WG(o)},[o]);return S.createElement(KG,{styles:XG(s,!t,o,r?"":"!important")})},bg=!1;if(typeof window<"u")try{var Tu=Object.defineProperty({},"passive",{get:function(){return bg=!0,!0}});window.addEventListener("test",Tu,Tu),window.removeEventListener("test",Tu,Tu)}catch{bg=!1}var Ui=bg?{passive:!1}:!1,JG=function(e){return e.tagName==="TEXTAREA"},YC=function(e,t){if(!(e instanceof Element))return!1;var r=window.getComputedStyle(e);return r[t]!=="hidden"&&!(r.overflowY===r.overflowX&&!JG(e)&&r[t]==="visible")},eH=function(e){return YC(e,"overflowY")},tH=function(e){return YC(e,"overflowX")},Mk=function(e,t){var r=t.ownerDocument,a=t;do{typeof ShadowRoot<"u"&&a instanceof ShadowRoot&&(a=a.host);var o=WC(e,a);if(o){var s=KC(e,a),u=s[1],d=s[2];if(u>d)return!0}a=a.parentNode}while(a&&a!==r.body);return!1},nH=function(e){var t=e.scrollTop,r=e.scrollHeight,a=e.clientHeight;return[t,r,a]},rH=function(e){var t=e.scrollLeft,r=e.scrollWidth,a=e.clientWidth;return[t,r,a]},WC=function(e,t){return e==="v"?eH(t):tH(t)},KC=function(e,t){return e==="v"?nH(t):rH(t)},aH=function(e,t){return e==="h"&&t==="rtl"?-1:1},iH=function(e,t,r,a,o){var s=aH(e,window.getComputedStyle(t).direction),u=s*a,d=r.target,p=t.contains(d),f=!1,h=u>0,b=0,v=0;do{if(!d)break;var E=KC(e,d),w=E[0],T=E[1],k=E[2],A=T-k-s*w;(w||A)&&WC(e,d)&&(b+=A,v+=w);var _=d.parentNode;d=_&&_.nodeType===Node.DOCUMENT_FRAGMENT_NODE?_.host:_}while(!p&&d!==document.body||p&&(t.contains(d)||t===d));return(h&&Math.abs(b)<1||!h&&Math.abs(v)<1)&&(f=!0),f},Au=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},jk=function(e){return[e.deltaX,e.deltaY]},Uk=function(e){return e&&"current"in e?e.current:e},oH=function(e,t){return e[0]===t[0]&&e[1]===t[1]},lH=function(e){return`
|
||||
`)},kA=function(){var e=parseInt(document.body.getAttribute(Wi)||"0",10);return isFinite(e)?e:0},F2=function(){S.useEffect(function(){return document.body.setAttribute(Wi,(kA()+1).toString()),function(){var e=kA()-1;e<=0?document.body.removeAttribute(Wi):document.body.setAttribute(Wi,e.toString())}},[])},B2=function(e){var t=e.noRelative,r=e.noImportant,a=e.gapMode,o=a===void 0?"margin":a;F2();var s=S.useMemo(function(){return M2(o)},[o]);return S.createElement(j2,{styles:U2(s,!t,o,r?"":"!important")})},ig=!1;if(typeof window<"u")try{var vu=Object.defineProperty({},"passive",{get:function(){return ig=!0,!0}});window.addEventListener("test",vu,vu),window.removeEventListener("test",vu,vu)}catch{ig=!1}var Ui=ig?{passive:!1}:!1,z2=function(e){return e.tagName==="TEXTAREA"},uN=function(e,t){if(!(e instanceof Element))return!1;var r=window.getComputedStyle(e);return r[t]!=="hidden"&&!(r.overflowY===r.overflowX&&!z2(e)&&r[t]==="visible")},P2=function(e){return uN(e,"overflowY")},$2=function(e){return uN(e,"overflowX")},_A=function(e,t){var r=t.ownerDocument,a=t;do{typeof ShadowRoot<"u"&&a instanceof ShadowRoot&&(a=a.host);var o=cN(e,a);if(o){var s=dN(e,a),u=s[1],c=s[2];if(u>c)return!0}a=a.parentNode}while(a&&a!==r.body);return!1},G2=function(e){var t=e.scrollTop,r=e.scrollHeight,a=e.clientHeight;return[t,r,a]},H2=function(e){var t=e.scrollLeft,r=e.scrollWidth,a=e.clientWidth;return[t,r,a]},cN=function(e,t){return e==="v"?P2(t):$2(t)},dN=function(e,t){return e==="v"?G2(t):H2(t)},V2=function(e,t){return e==="h"&&t==="rtl"?-1:1},q2=function(e,t,r,a,o){var s=V2(e,window.getComputedStyle(t).direction),u=s*a,c=r.target,p=t.contains(c),f=!1,h=u>0,m=0,v=0;do{if(!c)break;var E=dN(e,c),x=E[0],w=E[1],k=E[2],A=w-k-s*x;(x||A)&&cN(e,c)&&(m+=A,v+=x);var _=c.parentNode;c=_&&_.nodeType===Node.DOCUMENT_FRAGMENT_NODE?_.host:_}while(!p&&c!==document.body||p&&(t.contains(c)||t===c));return(h&&Math.abs(m)<1||!h&&Math.abs(v)<1)&&(f=!0),f},Eu=function(e){return"changedTouches"in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},NA=function(e){return[e.deltaX,e.deltaY]},RA=function(e){return e&&"current"in e?e.current:e},Y2=function(e,t){return e[0]===t[0]&&e[1]===t[1]},W2=function(e){return`
|
||||
.block-interactivity-`.concat(e,` {pointer-events: none;}
|
||||
.allow-interactivity-`).concat(e,` {pointer-events: all;}
|
||||
`)},sH=0,Fi=[];function uH(e){var t=S.useRef([]),r=S.useRef([0,0]),a=S.useRef(),o=S.useState(sH++)[0],s=S.useState(qC)[0],u=S.useRef(e);S.useEffect(function(){u.current=e},[e]),S.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var T=CG([e.lockRef.current],(e.shards||[]).map(Uk),!0).filter(Boolean);return T.forEach(function(k){return k.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),T.forEach(function(k){return k.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var d=S.useCallback(function(T,k){if("touches"in T&&T.touches.length===2||T.type==="wheel"&&T.ctrlKey)return!u.current.allowPinchZoom;var A=Au(T),_=r.current,R="deltaX"in T?T.deltaX:_[0]-A[0],L="deltaY"in T?T.deltaY:_[1]-A[1],j,D=T.target,z=Math.abs(R)>Math.abs(L)?"h":"v";if("touches"in T&&z==="h"&&D.type==="range")return!1;var F=window.getSelection(),V=F&&F.anchorNode,M=V?V===D||V.contains(D):!1;if(M)return!1;var H=Mk(z,D);if(!H)return!0;if(H?j=z:(j=z==="v"?"h":"v",H=Mk(z,D)),!H)return!1;if(!a.current&&"changedTouches"in T&&(R||L)&&(a.current=j),!j)return!0;var K=a.current||j;return iH(K,k,T,K==="h"?R:L)},[]),p=S.useCallback(function(T){var k=T;if(!(!Fi.length||Fi[Fi.length-1]!==s)){var A="deltaY"in k?jk(k):Au(k),_=t.current.filter(function(j){return j.name===k.type&&(j.target===k.target||k.target===j.shadowParent)&&oH(j.delta,A)})[0];if(_&&_.should){k.cancelable&&k.preventDefault();return}if(!_){var R=(u.current.shards||[]).map(Uk).filter(Boolean).filter(function(j){return j.contains(k.target)}),L=R.length>0?d(k,R[0]):!u.current.noIsolation;L&&k.cancelable&&k.preventDefault()}}},[]),f=S.useCallback(function(T,k,A,_){var R={name:T,delta:k,target:A,should:_,shadowParent:cH(A)};t.current.push(R),setTimeout(function(){t.current=t.current.filter(function(L){return L!==R})},1)},[]),h=S.useCallback(function(T){r.current=Au(T),a.current=void 0},[]),b=S.useCallback(function(T){f(T.type,jk(T),T.target,d(T,e.lockRef.current))},[]),v=S.useCallback(function(T){f(T.type,Au(T),T.target,d(T,e.lockRef.current))},[]);S.useEffect(function(){return Fi.push(s),e.setCallbacks({onScrollCapture:b,onWheelCapture:b,onTouchMoveCapture:v}),document.addEventListener("wheel",p,Ui),document.addEventListener("touchmove",p,Ui),document.addEventListener("touchstart",h,Ui),function(){Fi=Fi.filter(function(T){return T!==s}),document.removeEventListener("wheel",p,Ui),document.removeEventListener("touchmove",p,Ui),document.removeEventListener("touchstart",h,Ui)}},[]);var E=e.removeScrollBar,w=e.inert;return S.createElement(S.Fragment,null,w?S.createElement(s,{styles:lH(o)}):null,E?S.createElement(QG,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function cH(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const dH=BG(VC,uH);var Mc=S.forwardRef(function(e,t){return S.createElement(Dc,Jn({},e,{ref:t,sideCar:dH}))});Mc.classNames=Dc.classNames;var pH=[" ","Enter","ArrowUp","ArrowDown"],fH=[" ","Enter"],za="Select",[jc,Uc,gH]=yc(za),[uo]=jt(za,[gH,so]),Fc=so(),[mH,pa]=uo(za),[hH,bH]=uo(za),XC=e=>{const{__scopeSelect:t,children:r,open:a,defaultOpen:o,onOpenChange:s,value:u,defaultValue:d,onValueChange:p,dir:f,name:h,autoComplete:b,disabled:v,required:E,form:w}=e,T=Fc(t),[k,A]=S.useState(null),[_,R]=S.useState(null),[L,j]=S.useState(!1),D=Ha(f),[z,F]=en({prop:a,defaultProp:o??!1,onChange:s,caller:za}),[V,M]=en({prop:u,defaultProp:d,onChange:p,caller:za}),H=S.useRef(null),K=k?w||!!k.closest("form"):!0,[Z,te]=S.useState(new Set),Q=Array.from(Z).map(P=>P.props.value).join(";");return g.jsx(wv,{...T,children:g.jsxs(mH,{required:E,scope:t,trigger:k,onTriggerChange:A,valueNode:_,onValueNodeChange:R,valueNodeHasChildren:L,onValueNodeHasChildrenChange:j,contentId:Rn(),value:V,onValueChange:M,open:z,onOpenChange:F,dir:D,triggerPointerDownPosRef:H,disabled:v,children:[g.jsx(jc.Provider,{scope:t,children:g.jsx(hH,{scope:e.__scopeSelect,onNativeOptionAdd:S.useCallback(P=>{te(W=>new Set(W).add(P))},[]),onNativeOptionRemove:S.useCallback(P=>{te(W=>{const ne=new Set(W);return ne.delete(P),ne})},[]),children:r})}),K?g.jsxs(y0,{"aria-hidden":!0,required:E,tabIndex:-1,name:h,autoComplete:b,value:V,onChange:P=>M(P.target.value),disabled:v,form:w,children:[V===void 0?g.jsx("option",{value:""}):null,Array.from(Z)]},Q):null]})})};XC.displayName=za;var ZC="SelectTrigger",QC=S.forwardRef((e,t)=>{const{__scopeSelect:r,disabled:a=!1,...o}=e,s=Fc(r),u=pa(ZC,r),d=u.disabled||a,p=_e(t,u.onTriggerChange),f=Uc(r),h=S.useRef("touch"),[b,v,E]=v0(T=>{const k=f().filter(R=>!R.disabled),A=k.find(R=>R.value===u.value),_=S0(k,T,A);_!==void 0&&u.onValueChange(_.value)}),w=T=>{d||(u.onOpenChange(!0),E()),T&&(u.triggerPointerDownPosRef.current={x:Math.round(T.pageX),y:Math.round(T.pageY)})};return g.jsx(Lc,{asChild:!0,...s,children:g.jsx(Se.button,{type:"button",role:"combobox","aria-controls":u.contentId,"aria-ex
Line truncated
|
||||
`)},K2=0,Fi=[];function X2(e){var t=S.useRef([]),r=S.useRef([0,0]),a=S.useRef(),o=S.useState(K2++)[0],s=S.useState(sN)[0],u=S.useRef(e);S.useEffect(function(){u.current=e},[e]),S.useEffect(function(){if(e.inert){document.body.classList.add("block-interactivity-".concat(o));var w=b2([e.lockRef.current],(e.shards||[]).map(RA),!0).filter(Boolean);return w.forEach(function(k){return k.classList.add("allow-interactivity-".concat(o))}),function(){document.body.classList.remove("block-interactivity-".concat(o)),w.forEach(function(k){return k.classList.remove("allow-interactivity-".concat(o))})}}},[e.inert,e.lockRef.current,e.shards]);var c=S.useCallback(function(w,k){if("touches"in w&&w.touches.length===2||w.type==="wheel"&&w.ctrlKey)return!u.current.allowPinchZoom;var A=Eu(w),_=r.current,N="deltaX"in w?w.deltaX:_[0]-A[0],D="deltaY"in w?w.deltaY:_[1]-A[1],F,L=w.target,z=Math.abs(N)>Math.abs(D)?"h":"v";if("touches"in w&&z==="h"&&L.type==="range")return!1;var $=window.getSelection(),K=$&&$.anchorNode,j=K?K===L||K.contains(L):!1;if(j)return!1;var q=_A(z,L);if(!q)return!0;if(q?F=z:(F=z==="v"?"h":"v",q=_A(z,L)),!q)return!1;if(!a.current&&"changedTouches"in w&&(N||D)&&(a.current=F),!F)return!0;var X=a.current||F;return q2(X,k,w,X==="h"?N:D)},[]),p=S.useCallback(function(w){var k=w;if(!(!Fi.length||Fi[Fi.length-1]!==s)){var A="deltaY"in k?NA(k):Eu(k),_=t.current.filter(function(F){return F.name===k.type&&(F.target===k.target||k.target===F.shadowParent)&&Y2(F.delta,A)})[0];if(_&&_.should){k.cancelable&&k.preventDefault();return}if(!_){var N=(u.current.shards||[]).map(RA).filter(Boolean).filter(function(F){return F.contains(k.target)}),D=N.length>0?c(k,N[0]):!u.current.noIsolation;D&&k.cancelable&&k.preventDefault()}}},[]),f=S.useCallback(function(w,k,A,_){var N={name:w,delta:k,target:A,should:_,shadowParent:Z2(A)};t.current.push(N),setTimeout(function(){t.current=t.current.filter(function(D){return D!==N})},1)},[]),h=S.useCallback(function(w){r.current=Eu(w),a.current=void 0},[]),m=S.useCallback(function(w){f(w.type,NA(w),w.target,c(w,e.lockRef.current))},[]),v=S.useCallback(function(w){f(w.type,Eu(w),w.target,c(w,e.lockRef.current))},[]);S.useEffect(function(){return Fi.push(s),e.setCallbacks({onScrollCapture:m,onWheelCapture:m,onTouchMoveCapture:v}),document.addEventListener("wheel",p,Ui),document.addEventListener("touchmove",p,Ui),document.addEventListener("touchstart",h,Ui),function(){Fi=Fi.filter(function(w){return w!==s}),document.removeEventListener("wheel",p,Ui),document.removeEventListener("touchmove",p,Ui),document.removeEventListener("touchstart",h,Ui)}},[]);var E=e.removeScrollBar,x=e.inert;return S.createElement(S.Fragment,null,x?S.createElement(s,{styles:W2(o)}):null,E?S.createElement(B2,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function Z2(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}const Q2=k2(lN,X2);var dc=S.forwardRef(function(e,t){return S.createElement(cc,er({},e,{ref:t,sideCar:Q2}))});dc.classNames=cc.classNames;var J2=function(e){if(typeof document>"u")return null;var t=Array.isArray(e)?e[0]:e;return t.ownerDocument.body},Bi=new WeakMap,Su=new WeakMap,xu={},kf=0,pN=function(e){return e&&(e.host||pN(e.parentNode))},ej=function(e,t){return t.map(function(r){if(e.contains(r))return r;var a=pN(r);return a&&e.contains(a)?a:(console.error("aria-hidden",r,"in not contained inside",e,". Doing nothing"),null)}).filter(function(r){return!!r})},tj=function(e,t,r,a){var o=ej(t,Array.isArray(e)?e:[e]);xu[r]||(xu[r]=new WeakMap);var s=xu[r],u=[],c=new Set,p=new Set(o),f=function(m){!m||c.has(m)||(c.add(m),f(m.parentNode))};o.forEach(f);var h=function(m){!m||p.has(m)||Array.prototype.forEach.call(m.children,function(v){if(c.has(v))h(v);else try{var E=v.getAttribute(a),x=E!==null&&E!=="false",w=(Bi.get(v)||0)+1,k=(s.get(v)||0)+1;Bi.set(v,w),s.set(v,k),u.push(v),w===1&&x&&Su.set(v,!0),k===1&&v.setAttribute(r,"true"),x||v.setAttribute(a,"true")}catch(A){console.error("aria-hidden: cannot operate on ",v,A)}})};return h(t),c.clear(),kf++,function(){u.forEach(function(m){var v=Bi.get(m)-1,E=s.get(m)-1;Bi.set(m,v),s.set(m,E),v||(Su.has(m)||m.removeAttribute(a),Su.delete(m)),E||m.removeAttribute(r)}),kf--,kf||(Bi=new WeakMap,Bi=new WeakMap,Su=new WeakMap,xu={})}},Xg=function(e,t,r){r===void 0&&(r="data-aria-hidden");var a=Array.from(Array.isArray(e)?e:[e]),o=J2(e);return o?(a.push.apply(a,Array.from(o.querySelectorAll("[aria-live], script"))),tj(a,o,r,"aria-hidden")):function(){return null}};function nj(e){const t=rj(e),r=S.forwardRef((a,o)=>{const{children:s,...u}=a,c=S.Children.toArray(s),p=c.find(ij);if(p){const f=p.props.children,h=c.map(m=>m===p?S.Children.count(f)>1?S.Children.only(null):S.isValidElement(f)?f.props.children:null:m);return g.jsx(t,{...u,ref:o,children:S.isValidElement(f)?S.cloneElement(f,void 0,h):null})}return g.jsx(t,{...u,ref:o,children:s})});return r.displayName=`${e}.Slot`,r}function rj(e){const t=S.forwardRef((r,a)=>{const{children:o
Line truncated
|
||||
|
||||
If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
|
||||
|
||||
For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return S.useEffect(()=>{e&&(document.getElementById(e)||console.error(r))},[r,e]),null},L5="DialogDescriptionWarning",D5=({contentRef:e,descriptionId:t})=>{const a=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${Z0(L5).contentName}}.`;return S.useEffect(()=>{const o=e.current?.getAttribute("aria-describedby");t&&o&&(document.getElementById(t)||console.warn(a))},[a,e,t]),null},M5=U0,j5=B0,U5=P0,F5=$0,B5=G0,z5=V0,P5=Y0,$5=K0;function G5({...e}){return g.jsx(M5,{"data-slot":"dialog",...e})}function H5({...e}){return g.jsx(j5,{"data-slot":"dialog-trigger",...e})}function V5({...e}){return g.jsx(U5,{"data-slot":"dialog-portal",...e})}function q5({className:e,...t}){return g.jsx(F5,{"data-slot":"dialog-overlay",className:be("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",e),...t})}function Y5({className:e,children:t,showCloseButton:r=!0,...a}){return g.jsxs(V5,{"data-slot":"dialog-portal",children:[g.jsx(q5,{}),g.jsxs(B5,{"data-slot":"dialog-content",className:be("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",e),...a,children:[t,r&&g.jsxs($5,{"data-slot":"dialog-close",className:"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",children:[g.jsx(z$,{}),g.jsx("span",{className:"sr-only",children:"Close"})]})]})]})}function W5({className:e,...t}){return g.jsx("div",{"data-slot":"dialog-header",className:be("flex flex-col gap-2 text-center sm:text-left",e),...t})}function K5({className:e,...t}){return g.jsx(z5,{"data-slot":"dialog-title",className:be("text-lg leading-none font-semibold",e),...t})}function X5({className:e,...t}){return g.jsx(P5,{"data-slot":"dialog-description",className:be("text-muted-foreground text-sm",e),...t})}var Z5=Symbol("radix.slottable");function Q5(e){const t=({children:r})=>g.jsx(g.Fragment,{children:r});return t.displayName=`${e}.Slottable`,t.__radixId=Z5,t}var[Pc]=jt("Tooltip",[so]),$c=so(),Q0="TooltipProvider",J5=700,kg="tooltip.open",[e6,Iv]=Pc(Q0),J0=e=>{const{__scopeTooltip:t,delayDuration:r=J5,skipDelayDuration:a=300,disableHoverableContent:o=!1,children:s}=e,u=S.useRef(!0),d=S.useRef(!1),p=S.useRef(0);return S.useEffect(()=>{const f=p.current;return()=>window.clearTimeout(f)},[]),g.jsx(e6,{scope:t,isOpenDelayedRef:u,delayDuration:r,onOpen:S.useCallback(()=>{window.clearTimeout(p.current),u.current=!1},[]),onClose:S.useCallback(()=>{window.clearTimeout(p.current),p.current=window.setTimeout(()=>u.current=!0,a)},[a]),isPointerInTransitRef:d,onPointerInTransitChange:S.useCallback(f=>{d.current=f},[]),disableHoverableContent:o,children:s})};J0.displayName=Q0;var kl="Tooltip",[t6,Hl]=Pc(kl),eO=e=>{const{__scopeTooltip:t,children:r,open:a,defaultOpen:o,onOpenChange:s,disableHoverableContent:u,delayDuration:d}=e,p=Iv(kl,e.__scopeTooltip),f=$c(t),[h,b]=S.useState(null),v=Rn(),E=S.useRef(0),w=u??p.disableHoverableContent,T=d??p.delayDuration,k=S.useRef(!1),[A,_]=en({prop:a,defaultProp:o??!1,onChange:z=>{z?(p.onOpen(),document.dispatchEvent(new CustomEvent(kg))):p.onClose(),s?.(z)},caller:kl}),R=S.useMemo(()=>A?k.current?"delayed-open":"instant-open":"closed",[A]),L=S.useCallback(()=>{window.clearTimeout(E.current),E.current=0,k.current=!1,_(!0)},[_]),j=S.useCallback(()=>{window.clearTimeout(E.current),E.current=0,_(!1)},[_]),D=S.useCallback(()=>{window.clearTimeout(E.current),E.current=window.setTimeout(()=>{k.current=!0,_(!0),E.current=0},T)},[T,_]);return S.useEffect(()=>()=>{E.current&&(window.clearTimeout(E.current),E.current=0)},[]),g.jsx(wv,{...f,children:g.jsx(t6,{scope:t,contentId:v,open:A,stateAttribute:R,trigger:h,onTriggerChange:b,onTriggerEnter:S.useCallback(()=>{p.isOpenDelayedRef.current?D():L()},[p.isOpenDelayedRef,D,L]),onTriggerLeave:S.useCallback(()=>{w?j():(window.clearTimeout(E.current),E.current=0)},[j,w]),onOpen:L,onClose:j,disableHoverableContent:w,children:r})})};eO.displayName=kl;var _g="TooltipTrigger",tO=S.forwardRef((e,t)=>{const{__scopeTooltip:r,...a}=e,o=Hl(_g,r),s=Iv(_g,r),u=$c(r),d=S.useRef(null),p=_e(t,d,o.onTriggerChange),f=S.useRef(!1),h=S.useRef(!1),b=S.useCallback(()=>f.current=!1,[]);return S.useEffect(()=>()=>document.removeEventListener("pointerup",b),[b]),g.jsx(Lc,{asChild:!0,...u,children:g.jsx(Se.button,
Line truncated
|
||||
For more information, see https://radix-ui.com/primitives/docs/components/${t.docsSlug}`;return S.useEffect(()=>{e&&(document.getElementById(e)||console.error(r))},[r,e]),null},mj="DialogDescriptionWarning",hj=({contentRef:e,descriptionId:t})=>{const a=`Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${NN(mj).contentName}}.`;return S.useEffect(()=>{const o=e.current?.getAttribute("aria-describedby");t&&o&&(document.getElementById(t)||console.warn(a))},[a,e,t]),null},em=gN,bj=hN,tm=yN,nm=vN,rm=EN,RN=xN,yj=TN,CN=kN;function vj({...e}){return g.jsx(em,{"data-slot":"sheet",...e})}function Ej({...e}){return g.jsx(tm,{"data-slot":"sheet-portal",...e})}function Sj({className:e,...t}){return g.jsx(nm,{"data-slot":"sheet-overlay",className:me("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",e),...t})}function xj({className:e,children:t,side:r="right",showCloseButton:a=!0,...o}){return g.jsxs(Ej,{children:[g.jsx(Sj,{}),g.jsxs(rm,{"data-slot":"sheet-content",className:me("bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",r==="right"&&"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",r==="left"&&"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",r==="top"&&"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",r==="bottom"&&"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",e),...o,children:[t,a&&g.jsxs(CN,{className:"ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none",children:[g.jsx(S_,{className:"size-4"}),g.jsx("span",{className:"sr-only",children:"Close"})]})]})]})}function wj({className:e,...t}){return g.jsx("div",{"data-slot":"sheet-header",className:me("flex flex-col gap-1.5 p-4",e),...t})}function Tj({className:e,...t}){return g.jsx(RN,{"data-slot":"sheet-title",className:me("text-foreground font-semibold",e),...t})}function IN({nav:e,activeItem:t,onItemClick:r}){const a=(o,s=0)=>{const u=(o.path||o.title)===t;return o.children&&o.children.length>0?g.jsxs("div",{children:[g.jsx("div",{className:"flex items-center gap-2 px-3 py-1.5 text-xs font-semibold text-muted-foreground/70 uppercase tracking-widest mt-4 first:mt-0",children:o.title}),g.jsx("div",{className:"space-y-0.5",children:o.children.map(p=>a(p,s+1))})]},o.title+(o.path||"")):g.jsx("button",{type:"button",onClick:()=>r(o),className:`w-full text-left px-3 py-1.5 text-sm rounded-md transition-all duration-150 ${u?"bg-primary/10 text-primary font-medium border-l-2 border-primary":"text-muted-foreground hover:text-foreground hover:bg-muted/50"}`,style:{paddingLeft:`${12+s*12}px`},children:o.title},o.title+(o.path||""))};return g.jsx("nav",{className:"space-y-0.5",children:e.items?.map(o=>a(o))})}function _f({nav:e,activeItem:t,onItemClick:r}){return g.jsx("aside",{className:"w-64 min-w-[16rem] border-r border-border/50 hidden md:block",children:g.jsxs(eN,{className:"h-[calc(100vh-4rem)] py-4 px-2",children:[g.jsx(IN,{nav:e,activeItem:t,onItemClick:r}),g.jsx(qi,{className:"my-4"})]})})}function Aj({open:e,onOpenChange:t,nav:r,activeItem:a,onItemClick:o,title:s="Navigation"}){return g.jsx(vj,{open:e,onOpenChange:t,children:g.jsxs(xj,{side:"left",className:"w-72 p-0",children:[g.jsx(wj,{className:"border-b px-4 py-3",children:g.jsx(Tj,{children:s})}),g.jsx(eN,{className:"h-[calc(100vh-4rem)] px-2 py-4",children:g.jsx(IN,{nav:r,activeItem:a,onItemClick:u=>{o(u),t(!1)}})})]})})}function CA(e){const t=[],r=String(e||"");let a=r.indexOf(","),o=0,s=!1;for(;!s;){a===-1&&(a=r.length,s=!0);const u=r.slice(o,a).trim();(u||!s)&&t.push(u),o=a+1,a=r.indexOf(",",o)}return t}function kj(e,t){const r={};return(e[e.length-1]===""?[...e,""]:e).join((r.padRight?" ":"")+","+(r.padLeft===!1?"":" ")).trim()}const _j=/^[$_\p{ID_Start}][$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,Nj=/^[$_\p{ID_Start}][-$_\u{200C}\u{200D}\p{ID_Continue}]*$/u,Rj={};function IA(e,t){return(Rj.jsx?Nj:_j).test(e)}const Cj=/[ \t\n\f\r]/g;function Ij(e){return typeof e=="object"?e.type==="text"?OA(e.value):!1:OA(e)}function OA(e){return e.replace(Cj,"")===""}class Ul{constructor(t,r,a){this.normal=r,this.property=t,a&&(this.space=a)}}Ul.prototype.normal={};Ul.prototype.property={};Ul.prototype.space=void 0;function ON(e,t){const r={},a={};for(const o of e)Object.assign(r,o.property),Object.assign(a,o.normal);return new Ul(r,a,t)}function kl(e){return e.toLowerCase()}class rn{con
Line truncated
|
||||
`,f="/",h="*",m="",v="comment",E="declaration";function x(k,A){if(typeof k!="string")throw new TypeError("First argument must be a string");if(!k)return[];A=A||{};var _=1,N=1;function D(re){var ee=re.match(t);ee&&(_+=ee.length);var M=re.lastIndexOf(p);N=~M?re.length-M:N+re.length}function F(){var re={line:_,column:N};return function(ee){return ee.position=new L(re),K(),ee}}function L(re){this.start=re,this.end={line:_,column:N},this.source=A.source}L.prototype.content=k;function z(re){var ee=new Error(A.source+":"+_+":"+N+": "+re);if(ee.reason=re,ee.filename=A.source,ee.line=_,ee.column=N,ee.source=k,!A.silent)throw ee}function $(re){var ee=re.exec(k);if(ee){var M=ee[0];return D(M),k=k.slice(M.length),ee}}function K(){$(r)}function j(re){var ee;for(re=re||[];ee=q();)ee!==!1&&re.push(ee);return re}function q(){var re=F();if(!(f!=k.charAt(0)||h!=k.charAt(1))){for(var ee=2;m!=k.charAt(ee)&&(h!=k.charAt(ee)||f!=k.charAt(ee+1));)++ee;if(ee+=2,m===k.charAt(ee-1))return z("End of comment missing");var M=k.slice(2,ee-2);return N+=2,D(M),k=k.slice(ee),N+=2,re({type:v,comment:M})}}function X(){var re=F(),ee=$(a);if(ee){if(q(),!$(o))return z("property missing ':'");var M=$(s),H=re({type:E,property:w(ee[0].replace(e,m)),value:M?w(M[0].replace(e,m)):m});return $(u),H}}function ne(){var re=[];j(re);for(var ee;ee=X();)ee!==!1&&(re.push(ee),j(re));return re}return K(),ne()}function w(k){return k?k.replace(c,m):m}return Rf=x,Rf}var UA;function $j(){if(UA)return zi;UA=1;var e=zi&&zi.__importDefault||function(a){return a&&a.__esModule?a:{default:a}};Object.defineProperty(zi,"__esModule",{value:!0}),zi.default=r;const t=e(Pj());function r(a,o){let s=null;if(!a||typeof a!="string")return s;const u=(0,t.default)(a),c=typeof o=="function";return u.forEach(p=>{if(p.type!=="declaration")return;const{property:f,value:h}=p;c?o(f,h,p):h&&(s=s||{},s[f]=h)}),s}return zi}var pl={},FA;function Gj(){if(FA)return pl;FA=1,Object.defineProperty(pl,"__esModule",{value:!0}),pl.camelCase=void 0;var e=/^--[a-zA-Z0-9_-]+$/,t=/-([a-z])/g,r=/^[^-]+$/,a=/^-(webkit|moz|ms|o|khtml)-/,o=/^-(ms)-/,s=function(f){return!f||r.test(f)||e.test(f)},u=function(f,h){return h.toUpperCase()},c=function(f,h){return"".concat(h,"-")},p=function(f,h){return h===void 0&&(h={}),s(f)?f:(f=f.toLowerCase(),h.reactCompat?f=f.replace(o,c):f=f.replace(a,c),f.replace(t,u))};return pl.camelCase=p,pl}var fl,BA;function Hj(){if(BA)return fl;BA=1;var e=fl&&fl.__importDefault||function(o){return o&&o.__esModule?o:{default:o}},t=e($j()),r=Gj();function a(o,s){var u={};return!o||typeof o!="string"||(0,t.default)(o,function(c,p){c&&p&&(u[(0,r.camelCase)(c,s)]=p)}),u}return a.default=a,fl=a,fl}var Vj=Hj();const qj=ac(Vj),PN=$N("end"),im=$N("start");function $N(e){return t;function t(r){const a=r&&r.position&&r.position[e]||{};if(typeof a.line=="number"&&a.line>0&&typeof a.column=="number"&&a.column>0)return{line:a.line,column:a.column,offset:typeof a.offset=="number"&&a.offset>-1?a.offset:void 0}}}function Yj(e){const t=im(e),r=PN(e);if(t&&r)return{start:t,end:r}}function El(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?zA(e.position):"start"in e||"end"in e?zA(e):"line"in e||"column"in e?sg(e):""}function sg(e){return PA(e&&e.line)+":"+PA(e&&e.column)}function zA(e){return sg(e&&e.start)+"-"+sg(e&&e.end)}function PA(e){return e&&typeof e=="number"?e:1}class Ft extends Error{constructor(t,r,a){super(),typeof r=="string"&&(a=r,r=void 0);let o="",s={},u=!1;if(r&&("line"in r&&"column"in r?s={place:r}:"start"in r&&"end"in r?s={place:r}:"type"in r?s={ancestors:[r],place:r.position}:s={...r}),typeof t=="string"?o=t:!s.cause&&t&&(u=!0,o=t.message,s.cause=t),!s.ruleId&&!s.source&&typeof a=="string"){const p=a.indexOf(":");p===-1?s.ruleId=a:(s.source=a.slice(0,p),s.ruleId=a.slice(p+1))}if(!s.place&&s.ancestors&&s.ancestors){const p=s.ancestors[s.ancestors.length-1];p&&(s.place=p.position)}const c=s.place&&"start"in s.place?s.place.start:s.place;this.ancestors=s.ancestors||void 0,this.cause=s.cause||void 0,this.column=c?c.column:void 0,this.fatal=void 0,this.file="",this.message=o,this.line=c?c.line:void 0,this.name=El(s.place)||"1:1",this.place=s.place||void 0,this.reason=this.message,this.ruleId=s.ruleId||void 0,this.source=s.source||void 0,this.stack=u&&s.cause&&typeof s.cause.stack=="string"?s.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}}Ft.prototype.file="";Ft.prototype.name="";Ft.prototype.reason="";Ft.prototype.message="";Ft.prototype.stack="";Ft.prototype.column=void 0;Ft.prototype.line=void 0;Ft.prototype.ancestors=void 0;Ft.prototype.cause=void 0;Ft.prototype.fatal=void 0;Ft.prototype.place=void 0;Ft.prototype.ruleId=void 0;Ft.prototype.source=void 0;const om={}.hasOwnProperty,Wj=new Map,Kj=/[A-Z]/g,Xj=new Set(["table","tbody","thead","tfoot","tr"]),Zj=new Set(["td","th"]),GN="https://github.com/syntax-tree/hast-util-to-jsx-runtime";function Qj(e,t){if(!t||t.Fragment===void 0)throw new TypeError("Expected `Fragment` in options
Line truncated
|
||||
`;break}case-3:{u=`\r
|
||||
`;break}case-2:{u=t?" ":" ";break}case-1:{if(!t&&o)continue;u=" ";break}default:u=String.fromCharCode(s)}o=s===-2,a.push(u)}return a.join("")}function oB(e){const a={constructs:WN([nB,...(e||{}).extensions||[]]),content:o(SU),defined:[],document:o(wU),flow:o(PF),lazy:{},string:o(HF),text:o(VF)};return a;function o(s){return u;function u(c){return rB(a,s,c)}}}function lB(e){for(;!JN(e););return e}const ZA=/[\0\t\n\r]/g;function sB(){let e=1,t="",r=!0,a;return o;function o(s,u,c){const p=[];let f,h,m,v,E;for(s=t+(typeof s=="string"?s.toString():new TextDecoder(u||void 0).decode(s)),m=0,t="",r&&(s.charCodeAt(0)===65279&&m++,r=void 0);m<s.length;){if(ZA.lastIndex=m,f=ZA.exec(s),v=f&&f.index!==void 0?f.index:s.length,E=s.charCodeAt(v),!f){t=s.slice(m);break}if(E===10&&m===v&&a)p.push(-3),a=void 0;else switch(a&&(p.push(-5),a=void 0),m<v&&(p.push(s.slice(m,v)),e+=v-m),E){case 0:{p.push(65533),e++;break}case 9:{for(h=Math.ceil(e/4)*4,p.push(-2);e++<h;)p.push(-1);break}case 10:{p.push(-4),e=1;break}default:a=!0,e=1}m=v+1}return c&&(a&&p.push(-5),t&&p.push(t),p.push(null)),p}}const uB=/\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;function cB(e){return e.replace(uB,dB)}function dB(e,t,r){if(t)return t;if(r.charCodeAt(0)===35){const o=r.charCodeAt(1),s=o===120||o===88;return KN(r.slice(s?2:1),s?16:10)}return Nl(r)||e}const iR={}.hasOwnProperty;function pB(e,t,r){return t&&typeof t=="object"&&(r=t,t=void 0),fB(r)(lB(oB(r).document().write(sB()(e,t,!0))))}function fB(e){const t={transforms:[],canContainEols:["emphasis","fragment","heading","paragraph","strong"],enter:{autolink:s(Za),autolinkProtocol:K,autolinkEmail:K,atxHeading:s(Yn),blockQuote:s(ke),characterEscape:K,characterReference:K,codeFenced:s(Ie),codeFencedFenceInfo:u,codeFencedFenceMeta:u,codeIndented:s(Ie,u),codeText:s(qe,u),codeTextData:K,data:K,codeFlowValue:K,definition:s(dt),definitionDestinationString:u,definitionLabelString:u,definitionTitleString:u,emphasis:s(st),hardBreakEscape:s(Wn),hardBreakTrailing:s(Wn),htmlFlow:s(Ir,u),htmlFlowData:K,htmlText:s(Ir,u),htmlTextData:K,image:s(Jl),label:u,link:s(Za),listItem:s(bo),listItemValue:v,listOrdered:s(Qa,m),listUnordered:s(Qa),paragraph:s(Zc),reference:C,referenceString:u,resourceDestinationString:u,resourceTitleString:u,setextHeading:s(Yn),strong:s(Qc),thematicBreak:s(Jc)},exit:{atxHeading:p(),atxHeadingSequence:F,autolink:p(),autolinkEmail:fe,autolinkProtocol:he,blockQuote:p(),characterEscapeValue:j,characterReferenceMarkerHexadecimal:ae,characterReferenceMarkerNumeric:ae,characterReferenceValue:ue,characterReference:ie,codeFenced:p(k),codeFencedFence:w,codeFencedFenceInfo:E,codeFencedFenceMeta:x,codeFlowValue:j,codeIndented:p(A),codeText:p(ee),codeTextData:j,data:j,definition:p(),definitionDestinationString:D,definitionLabelString:_,definitionTitleString:N,emphasis:p(),hardBreakEscape:p(X),hardBreakTrailing:p(X),htmlFlow:p(ne),htmlFlowData:j,htmlText:p(re),htmlTextData:j,image:p(H),label:de,labelText:Q,lineEnding:q,link:p(M),listItem:p(),listOrdered:p(),listUnordered:p(),paragraph:p(),referenceString:J,resourceDestinationString:I,resourceTitleString:U,resource:B,setextHeading:p($),setextHeadingLineSequence:z,setextHeadingText:L,strong:p(),thematicBreak:p()}};oR(t,(e||{}).mdastExtensions||[]);const r={};return a;function a(le){let ge={type:"root",children:[]};const _e={stack:[ge],tokenStack:[],config:t,enter:c,exit:f,buffer:u,resume:h,data:r},Me=[];let We=-1;for(;++We<le.length;)if(le[We][1].type==="listOrdered"||le[We][1].type==="listUnordered")if(le[We][0]==="enter")Me.push(We);else{const an=Me.pop();We=o(le,an,We)}for(We=-1;++We<le.length;){const an=t[le[We][0]];iR.call(an,le[We][1].type)&&an[le[We][1].type].call(Object.assign({sliceSerialize:le[We][2].sliceSerialize},_e),le[We][1])}if(_e.tokenStack.length>0){const an=_e.tokenStack[_e.tokenStack.length-1];(an[1]||QA).call(_e,void 0,an[0])}for(ge.position={start:ia(le.length>0?le[0][1].start:{line:1,column:1,offset:0}),end:ia(le.length>0?le[le.length-2][1].end:{line:1,column:1,offset:0})},We=-1;++We<t.transforms.length;)ge=t.transforms[We](ge)||ge;return ge}function o(le,ge,_e){let Me=ge-1,We=-1,an=!1,lr,$t,Et,qt;for(;++Me<=_e;){const tt=le[Me];switch(tt[1].type){case"listUnordered":case"listOrdered":case"blockQuote":{tt[0]==="enter"?We++:We--,qt=void 0;break}case"lineEndingBlank":{tt[0]==="enter"&&(lr&&!qt&&!We&&!Et&&(Et=Me),qt=void 0);break}case"linePrefix":case"listItemValue":case"listItemMarker":case"listItemPrefix":case"listItemPrefixWhitespace":break;default:qt=void 0}if(!We&&tt[0]==="enter"&&tt[1].type==="listItemPrefix"||We===-1&&tt[0]==="exit"&&(tt[1].type==="listUnordered"||tt[1].type==="listOrdered")){if(lr){let Or=Me;for($t=void 0;Or--;){const Ln=le[Or];if(Ln[1].type==="lineEnding"||Ln[1].type==="lineEndingBlank"){if(Ln[0]==="exit")continue;$t&&(le[$t][1].type="lineEndingBlank",an=!0),Ln[1].type="lineEnding",$t=Or}else if(!(Ln[1].type==="linePrefix"||Ln[1].type==="blockQuotePrefix"||Ln[1].type==="blockQuotePrefixWhitespLine truncated
|
||||
`}]}function yB(e,t){const r=t.value?t.value+`
|
||||
`:"",a={},o=t.lang?t.lang.split(/\s+/):[];o.length>0&&(a.className=["language-"+o[0]]);let s={type:"element",tagName:"code",properties:a,children:[{type:"text",value:r}]};return t.meta&&(s.data={meta:t.meta}),e.patch(t,s),s=e.applyData(t,s),s={type:"element",tagName:"pre",properties:{},children:[s]},e.patch(t,s),s}function vB(e,t){const r={type:"element",tagName:"del",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function EB(e,t){const r={type:"element",tagName:"em",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function SB(e,t){const r=typeof e.options.clobberPrefix=="string"?e.options.clobberPrefix:"user-content-",a=String(t.identifier).toUpperCase(),o=ro(a.toLowerCase()),s=e.footnoteOrder.indexOf(a);let u,c=e.footnoteCounts.get(a);c===void 0?(c=0,e.footnoteOrder.push(a),u=e.footnoteOrder.length):u=s+1,c+=1,e.footnoteCounts.set(a,c);const p={type:"element",tagName:"a",properties:{href:"#"+r+"fn-"+o,id:r+"fnref-"+o+(c>1?"-"+c:""),dataFootnoteRef:!0,ariaDescribedBy:["footnote-label"]},children:[{type:"text",value:String(u)}]};e.patch(t,p);const f={type:"element",tagName:"sup",properties:{},children:[p]};return e.patch(t,f),e.applyData(t,f)}function xB(e,t){const r={type:"element",tagName:"h"+t.depth,properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function wB(e,t){if(e.options.allowDangerousHtml){const r={type:"raw",value:t.value};return e.patch(t,r),e.applyData(t,r)}}function lR(e,t){const r=t.referenceType;let a="]";if(r==="collapsed"?a+="[]":r==="full"&&(a+="["+(t.label||t.identifier)+"]"),t.type==="imageReference")return[{type:"text",value:"!["+t.alt+a}];const o=e.all(t),s=o[0];s&&s.type==="text"?s.value="["+s.value:o.unshift({type:"text",value:"["});const u=o[o.length-1];return u&&u.type==="text"?u.value+=a:o.push({type:"text",value:a}),o}function TB(e,t){const r=String(t.identifier).toUpperCase(),a=e.definitionById.get(r);if(!a)return lR(e,t);const o={src:ro(a.url||""),alt:t.alt};a.title!==null&&a.title!==void 0&&(o.title=a.title);const s={type:"element",tagName:"img",properties:o,children:[]};return e.patch(t,s),e.applyData(t,s)}function AB(e,t){const r={src:ro(t.url)};t.alt!==null&&t.alt!==void 0&&(r.alt=t.alt),t.title!==null&&t.title!==void 0&&(r.title=t.title);const a={type:"element",tagName:"img",properties:r,children:[]};return e.patch(t,a),e.applyData(t,a)}function kB(e,t){const r={type:"text",value:t.value.replace(/\r?\n|\r/g," ")};e.patch(t,r);const a={type:"element",tagName:"code",properties:{},children:[r]};return e.patch(t,a),e.applyData(t,a)}function _B(e,t){const r=String(t.identifier).toUpperCase(),a=e.definitionById.get(r);if(!a)return lR(e,t);const o={href:ro(a.url||"")};a.title!==null&&a.title!==void 0&&(o.title=a.title);const s={type:"element",tagName:"a",properties:o,children:e.all(t)};return e.patch(t,s),e.applyData(t,s)}function NB(e,t){const r={href:ro(t.url)};t.title!==null&&t.title!==void 0&&(r.title=t.title);const a={type:"element",tagName:"a",properties:r,children:e.all(t)};return e.patch(t,a),e.applyData(t,a)}function RB(e,t,r){const a=e.all(t),o=r?CB(r):sR(t),s={},u=[];if(typeof t.checked=="boolean"){const h=a[0];let m;h&&h.type==="element"&&h.tagName==="p"?m=h:(m={type:"element",tagName:"p",properties:{},children:[]},a.unshift(m)),m.children.length>0&&m.children.unshift({type:"text",value:" "}),m.children.unshift({type:"element",tagName:"input",properties:{type:"checkbox",checked:t.checked,disabled:!0},children:[]}),s.className=["task-list-item"]}let c=-1;for(;++c<a.length;){const h=a[c];(o||c!==0||h.type!=="element"||h.tagName!=="p")&&u.push({type:"text",value:`
|
||||
`}),h.type==="element"&&h.tagName==="p"&&!o?u.push(...h.children):u.push(h)}const p=a[a.length-1];p&&(o||p.type!=="element"||p.tagName!=="p")&&u.push({type:"text",value:`
|
||||
`});const f={type:"element",tagName:"li",properties:s,children:u};return e.patch(t,f),e.applyData(t,f)}function CB(e){let t=!1;if(e.type==="list"){t=e.spread||!1;const r=e.children;let a=-1;for(;!t&&++a<r.length;)t=sR(r[a])}return t}function sR(e){const t=e.spread;return t??e.children.length>1}function IB(e,t){const r={},a=e.all(t);let o=-1;for(typeof t.start=="number"&&t.start!==1&&(r.start=t.start);++o<a.length;){const u=a[o];if(u.type==="element"&&u.tagName==="li"&&u.properties&&Array.isArray(u.properties.className)&&u.properties.className.includes("task-list-item")){r.className=["contains-task-list"];break}}const s={type:"element",tagName:t.ordered?"ol":"ul",properties:r,children:e.wrap(a,!0)};return e.patch(t,s),e.applyData(t,s)}function OB(e,t){const r={type:"element",tagName:"p",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function LB(e,t){const r={type:"root",children:e.wrap(e.all(t))};return e.patch(t,r),e.applyData(t,r)}function DB(e,t){const r={type:"element",tagName:"strong",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}function MB(e,t){const r=e.all(t),a=r.shift(),o=[];if(a){const u={type:"element",tagName:"thead",properties:{},children:e.wrap([a],!0)};e.patch(t.children[0],u),o.push(u)}if(r.length>0){const u={type:"element",tagName:"tbody",properties:{},children:e.wrap(r,!0)},c=im(t.children[1]),p=PN(t.children[t.children.length-1]);c&&p&&(u.position={start:c,end:p}),o.push(u)}const s={type:"element",tagName:"table",properties:{},children:e.wrap(o,!0)};return e.patch(t,s),e.applyData(t,s)}function jB(e,t,r){const a=r?r.children:void 0,s=(a?a.indexOf(t):1)===0?"th":"td",u=r&&r.type==="table"?r.align:void 0,c=u?u.length:t.children.length;let p=-1;const f=[];for(;++p<c;){const m=t.children[p],v={},E=u?u[p]:void 0;E&&(v.align=E);let x={type:"element",tagName:s,properties:v,children:[]};m&&(x.children=e.all(m),e.patch(m,x),x=e.applyData(m,x)),f.push(x)}const h={type:"element",tagName:"tr",properties:{},children:e.wrap(f,!0)};return e.patch(t,h),e.applyData(t,h)}function UB(e,t){const r={type:"element",tagName:"td",properties:{},children:e.all(t)};return e.patch(t,r),e.applyData(t,r)}const JA=9,ek=32;function FB(e){const t=String(e),r=/\r?\n|\r/g;let a=r.exec(t),o=0;const s=[];for(;a;)s.push(tk(t.slice(o,a.index),o>0,!0),a[0]),o=a.index+a[0].length,a=r.exec(t);return s.push(tk(t.slice(o),o>0,!1)),s.join("")}function tk(e,t,r){let a=0,o=e.length;if(t){let s=e.codePointAt(a);for(;s===JA||s===ek;)a++,s=e.codePointAt(a)}if(r){let s=e.codePointAt(o-1);for(;s===JA||s===ek;)o--,s=e.codePointAt(o-1)}return o>a?e.slice(a,o):""}function BB(e,t){const r={type:"text",value:FB(String(t.value))};return e.patch(t,r),e.applyData(t,r)}function zB(e,t){const r={type:"element",tagName:"hr",properties:{},children:[]};return e.patch(t,r),e.applyData(t,r)}const PB={blockquote:hB,break:bB,code:yB,delete:vB,emphasis:EB,footnoteReference:SB,heading:xB,html:wB,imageReference:TB,image:AB,inlineCode:kB,linkReference:_B,link:NB,listItem:RB,list:IB,paragraph:OB,root:LB,strong:DB,table:MB,tableCell:UB,tableRow:jB,text:BB,thematicBreak:zB,toml:wu,yaml:wu,definition:wu,footnoteDefinition:wu};function wu(){}const uR=-1,hc=0,xl=1,Yu=2,dm=3,pm=4,fm=5,gm=6,cR=7,dR=8,nk=typeof self=="object"?self:globalThis,$B=(e,t)=>{const r=(o,s)=>(e.set(s,o),o),a=o=>{if(e.has(o))return e.get(o);const[s,u]=t[o];switch(s){case hc:case uR:return r(u,o);case xl:{const c=r([],o);for(const p of u)c.push(a(p));return c}case Yu:{const c=r({},o);for(const[p,f]of u)c[a(p)]=a(f);return c}case dm:return r(new Date(u),o);case pm:{const{source:c,flags:p}=u;return r(new RegExp(c,p),o)}case fm:{const c=r(new Map,o);for(const[p,f]of u)c.set(a(p),a(f));return c}case gm:{const c=r(new Set,o);for(const p of u)c.add(a(p));return c}case cR:{const{name:c,message:p}=u;return r(new nk[c](p),o)}case dR:return r(BigInt(u),o);case"BigInt":return r(Object(BigInt(u)),o);case"ArrayBuffer":return r(new Uint8Array(u).buffer,u);case"DataView":{const{buffer:c}=new Uint8Array(u);return r(new DataView(c),u)}}return r(new nk[s](u),o)};return a},rk=e=>$B(new Map,e)(0),Pi="",{toString:GB}={},{keys:HB}=Object,ml=e=>{const t=typeof e;if(t!=="object"||!e)return[hc,t];const r=GB.call(e).slice(8,-1);switch(r){case"Array":return[xl,Pi];case"Object":return[Yu,Pi];case"Date":return[dm,Pi];case"RegExp":return[pm,Pi];case"Map":return[fm,Pi];case"Set":return[gm,Pi];case"DataView":return[xl,r]}return r.includes("Array")?[xl,r]:r.includes("Error")?[cR,r]:[Yu,r]},Tu=([e,t])=>e===hc&&(t==="function"||t==="symbol"),VB=(e,t,r,a)=>{const o=(u,c)=>{const p=a.push(u)-1;return r.set(c,p),p},s=u=>{if(r.has(u))return r.get(u);let[c,p]=ml(u);switch(c){case hc:{let h=u;switch(p){case"bigint":c=dR,h=u.toString();break;case"function":case"symbol":if(e)throw new TypeError("unable to serialize "+p);h=null;break;case"undefined":return o([uR],u)}return o([c,h],u)}case xl:{if(p){let v=u;return p==="DataView"?v=new Uint8Array(u.buffer):p==="ArrayBuffer"&&(v=new Uint8Array(
Line truncated
|
||||
`},{type:"element",tagName:"ol",properties:{},children:e.wrap(c,!0)},{type:"text",value:`
|
||||
`}]}}const bc=(function(e){if(e==null)return QB;if(typeof e=="function")return yc(e);if(typeof e=="object")return Array.isArray(e)?KB(e):XB(e);if(typeof e=="string")return ZB(e);throw new Error("Expected function, string, or object as test")});function KB(e){const t=[];let r=-1;for(;++r<e.length;)t[r]=bc(e[r]);return yc(a);function a(...o){let s=-1;for(;++s<t.length;)if(t[s].apply(this,o))return!0;return!1}}function XB(e){const t=e;return yc(r);function r(a){const o=a;let s;for(s in e)if(o[s]!==t[s])return!1;return!0}}function ZB(e){return yc(t);function t(r){return r&&r.type===e}}function yc(e){return t;function t(r,a,o){return!!(JB(r)&&e.call(this,r,typeof a=="number"?a:void 0,o||void 0))}}function QB(){return!0}function JB(e){return e!==null&&typeof e=="object"&&"type"in e}const pR=[],ez=!0,dg=!1,tz="skip";function fR(e,t,r,a){let o;typeof t=="function"&&typeof r!="function"?(a=r,r=t):o=t;const s=bc(o),u=a?-1:1;c(e,void 0,[])();function c(p,f,h){const m=p&&typeof p=="object"?p:{};if(typeof m.type=="string"){const E=typeof m.tagName=="string"?m.tagName:typeof m.name=="string"?m.name:void 0;Object.defineProperty(v,"name",{value:"node ("+(p.type+(E?"<"+E+">":""))+")"})}return v;function v(){let E=pR,x,w,k;if((!t||s(p,f,h[h.length-1]||void 0))&&(E=nz(r(p,h)),E[0]===dg))return E;if("children"in p&&p.children){const A=p;if(A.children&&E[0]!==tz)for(w=(a?A.children.length:-1)+u,k=h.concat(A);w>-1&&w<A.children.length;){const _=A.children[w];if(x=c(_,w,k)(),x[0]===dg)return x;w=typeof x[1]=="number"?x[1]:w+u}}return E}}}function nz(e){return Array.isArray(e)?e:typeof e=="number"?[ez,e]:e==null?pR:[e]}function mm(e,t,r,a){let o,s,u;typeof t=="function"&&typeof r!="function"?(s=void 0,u=t,o=r):(s=t,u=r,o=a),fR(e,s,c,o);function c(p,f){const h=f[f.length-1],m=h?h.children.indexOf(p):void 0;return u(p,m,h)}}const pg={}.hasOwnProperty,rz={};function az(e,t){const r=t||rz,a=new Map,o=new Map,s=new Map,u={...PB,...r.handlers},c={all:f,applyData:oz,definitionById:a,footnoteById:o,footnoteCounts:s,footnoteOrder:[],handlers:u,one:p,options:r,patch:iz,wrap:sz};return mm(e,function(h){if(h.type==="definition"||h.type==="footnoteDefinition"){const m=h.type==="definition"?a:o,v=String(h.identifier).toUpperCase();m.has(v)||m.set(v,h)}}),c;function p(h,m){const v=h.type,E=c.handlers[v];if(pg.call(c.handlers,v)&&E)return E(c,h,m);if(c.options.passThrough&&c.options.passThrough.includes(v)){if("children"in h){const{children:w,...k}=h,A=Wu(k);return A.children=c.all(h),A}return Wu(h)}return(c.options.unknownHandler||lz)(c,h,m)}function f(h){const m=[];if("children"in h){const v=h.children;let E=-1;for(;++E<v.length;){const x=c.one(v[E],h);if(x){if(E&&v[E-1].type==="break"&&(!Array.isArray(x)&&x.type==="text"&&(x.value=ik(x.value)),!Array.isArray(x)&&x.type==="element")){const w=x.children[0];w&&w.type==="text"&&(w.value=ik(w.value))}Array.isArray(x)?m.push(...x):m.push(x)}}}return m}}function iz(e,t){e.position&&(t.position=Yj(e))}function oz(e,t){let r=t;if(e&&e.data){const a=e.data.hName,o=e.data.hChildren,s=e.data.hProperties;if(typeof a=="string")if(r.type==="element")r.tagName=a;else{const u="children"in r?r.children:[r];r={type:"element",tagName:a,properties:{},children:u}}r.type==="element"&&s&&Object.assign(r.properties,Wu(s)),"children"in r&&r.children&&o!==null&&o!==void 0&&(r.children=o)}return r}function lz(e,t){const r=t.data||{},a="value"in t&&!(pg.call(r,"hProperties")||pg.call(r,"hChildren"))?{type:"text",value:t.value}:{type:"element",tagName:"div",properties:{},children:e.all(t)};return e.patch(t,a),e.applyData(t,a)}function sz(e,t){const r=[];let a=-1;for(t&&r.push({type:"text",value:`
|
||||
`});++a<e.length;)a&&r.push({type:"text",value:`
|
||||
`}),r.push(e[a]);return t&&e.length>0&&r.push({type:"text",value:`
|
||||
`}),r}function ik(e){let t=0,r=e.charCodeAt(t);for(;r===9||r===32;)t++,r=e.charCodeAt(t);return e.slice(t)}function ok(e,t){const r=az(e,t),a=r.one(e,void 0),o=WB(r),s=Array.isArray(a)?{type:"root",children:a}:a||{type:"root",children:[]};return o&&s.children.push({type:"text",value:`
|
||||
`},o),s}function uz(e,t){return e&&"run"in e?async function(r,a){const o=ok(r,{file:a,...t});await e.run(o,a)}:function(r,a){return ok(r,{file:a,...e||t})}}function lk(e){if(e)throw e}var Lf,sk;function cz(){if(sk)return Lf;sk=1;var e=Object.prototype.hasOwnProperty,t=Object.prototype.toString,r=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=function(f){return typeof Array.isArray=="function"?Array.isArray(f):t.call(f)==="[object Array]"},s=function(f){if(!f||t.call(f)!=="[object Object]")return!1;var h=e.call(f,"constructor"),m=f.constructor&&f.constructor.prototype&&e.call(f.constructor.prototype,"isPrototypeOf");if(f.constructor&&!h&&!m)return!1;var v;for(v in f);return typeof v>"u"||e.call(f,v)},u=function(f,h){r&&h.name==="__proto__"?r(f,h.name,{enumerable:!0,configurable:!0,value:h.newValue,writable:!0}):f[h.name]=h.newValue},c=function(f,h){if(h==="__proto__")if(e.call(f,h)){if(a)return a(f,h).value}else return;return f[h]};return Lf=function p(){var f,h,m,v,E,x,w=arguments[0],k=1,A=arguments.length,_=!1;for(typeof w=="boolean"&&(_=w,w=arguments[1]||{},k=2),(w==null||typeof w!="object"&&typeof w!="function")&&(w={});k<A;++k)if(f=arguments[k],f!=null)for(h in f)m=c(w,h),v=c(f,h),w!==v&&(_&&v&&(s(v)||(E=o(v)))?(E?(E=!1,x=m&&o(m)?m:[]):x=m&&s(m)?m:{},u(w,{name:h,newValue:p(_,x,v)})):typeof v<"u"&&u(w,{name:h,newValue:v}));return w},Lf}var dz=cz();const Df=ac(dz);function fg(e){if(typeof e!="object"||e===null)return!1;const t=Object.getPrototypeOf(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Symbol.toStringTag in e)&&!(Symbol.iterator in e)}function pz(){const e=[],t={run:r,use:a};return t;function r(...o){let s=-1;const u=o.pop();if(typeof u!="function")throw new TypeError("Expected function as last argument, not "+u);c(null,...o);function c(p,...f){const h=e[++s];let m=-1;if(p){u(p);return}for(;++m<o.length;)(f[m]===null||f[m]===void 0)&&(f[m]=o[m]);o=f,h?fz(h,c)(...f):u(null,...f)}}function a(o){if(typeof o!="function")throw new TypeError("Expected `middelware` to be a function, not "+o);return e.push(o),t}}function fz(e,t){let r;return a;function a(...u){const c=e.length>u.length;let p;c&&u.push(o);try{p=e.apply(this,u)}catch(f){const h=f;if(c&&r)throw h;return o(h)}c||(p&&p.then&&typeof p.then=="function"?p.then(s,o):p instanceof Error?o(p):s(p))}function o(u,...c){r||(r=!0,t(u,...c))}function s(u){o(null,u)}}const Jn={basename:gz,dirname:mz,extname:hz,join:bz,sep:"/"};function gz(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');Bl(e);let r=0,a=-1,o=e.length,s;if(t===void 0||t.length===0||t.length>e.length){for(;o--;)if(e.codePointAt(o)===47){if(s){r=o+1;break}}else a<0&&(s=!0,a=o+1);return a<0?"":e.slice(r,a)}if(t===e)return"";let u=-1,c=t.length-1;for(;o--;)if(e.codePointAt(o)===47){if(s){r=o+1;break}}else u<0&&(s=!0,u=o+1),c>-1&&(e.codePointAt(o)===t.codePointAt(c--)?c<0&&(a=o):(c=-1,a=u));return r===a?a=u:a<0&&(a=e.length),e.slice(r,a)}function mz(e){if(Bl(e),e.length===0)return".";let t=-1,r=e.length,a;for(;--r;)if(e.codePointAt(r)===47){if(a){t=r;break}}else a||(a=!0);return t<0?e.codePointAt(0)===47?"/":".":t===1&&e.codePointAt(0)===47?"//":e.slice(0,t)}function hz(e){Bl(e);let t=e.length,r=-1,a=0,o=-1,s=0,u;for(;t--;){const c=e.codePointAt(t);if(c===47){if(u){a=t+1;break}continue}r<0&&(u=!0,r=t+1),c===46?o<0?o=t:s!==1&&(s=1):o>-1&&(s=-1)}return o<0||r<0||s===0||s===1&&o===r-1&&o===a+1?"":e.slice(o,r)}function bz(...e){let t=-1,r;for(;++t<e.length;)Bl(e[t]),e[t]&&(r=r===void 0?e[t]:r+"/"+e[t]);return r===void 0?".":yz(r)}function yz(e){Bl(e);const t=e.codePointAt(0)===47;let r=vz(e,!t);return r.length===0&&!t&&(r="."),r.length>0&&e.codePointAt(e.length-1)===47&&(r+="/"),t?"/"+r:r}function vz(e,t){let r="",a=0,o=-1,s=0,u=-1,c,p;for(;++u<=e.length;){if(u<e.length)c=e.codePointAt(u);else{if(c===47)break;c=47}if(c===47){if(!(o===u-1||s===1))if(o!==u-1&&s===2){if(r.length<2||a!==2||r.codePointAt(r.length-1)!==46||r.codePointAt(r.length-2)!==46){if(r.length>2){if(p=r.lastIndexOf("/"),p!==r.length-1){p<0?(r="",a=0):(r=r.slice(0,p),a=r.length-1-r.lastIndexOf("/")),o=u,s=0;continue}}else if(r.length>0){r="",a=0,o=u,s=0;continue}}t&&(r=r.length>0?r+"/..":"..",a=2)}else r.length>0?r+="/"+e.slice(o+1,u):r=e.slice(o+1,u),a=u-o-1;o=u,s=0}else c===46&&s>-1?s++:s=-1}return r}function Bl(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const Ez={cwd:Sz};function Sz(){return"/"}function gg(e){return!!(e!==null&&typeof e=="object"&&"href"in e&&e.href&&"protocol"in e&&e.protocol&&e.auth===void 0)}function xz(e){if(typeof e=="string")e=new URL(e);else if(!gg(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return wz(e)}function wz(e){if(e.hostname!==""){consLine truncated
|
||||
`:" ")+s.indentLines(s.containerFlow(a,c.current()),t?yR:pP))),f(),p}}function pP(e,t,r){return t===0?e:yR(e,t,r)}function yR(e,t,r){return(r?"":" ")+e}const fP=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];vR.peek=yP;function gP(){return{canContainEols:["delete"],enter:{strikethrough:hP},exit:{strikethrough:bP}}}function mP(){return{unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:fP}],handlers:{delete:vR}}}function hP(e){this.enter({type:"delete",children:[]},e)}function bP(e){this.exit(e)}function vR(e,t,r,a){const o=r.createTracker(a),s=r.enter("strikethrough");let u=o.move("~~");return u+=r.containerPhrasing(e,{...o.current(),before:u,after:"~"}),u+=o.move("~~"),s(),u}function yP(){return"~"}function vP(e){return e.length}function EP(e,t){const r=t||{},a=(r.align||[]).concat(),o=r.stringLength||vP,s=[],u=[],c=[],p=[];let f=0,h=-1;for(;++h<e.length;){const w=[],k=[];let A=-1;for(e[h].length>f&&(f=e[h].length);++A<e[h].length;){const _=SP(e[h][A]);if(r.alignDelimiters!==!1){const N=o(_);k[A]=N,(p[A]===void 0||N>p[A])&&(p[A]=N)}w.push(_)}u[h]=w,c[h]=k}let m=-1;if(typeof a=="object"&&"length"in a)for(;++m<f;)s[m]=mk(a[m]);else{const w=mk(a);for(;++m<f;)s[m]=w}m=-1;const v=[],E=[];for(;++m<f;){const w=s[m];let k="",A="";w===99?(k=":",A=":"):w===108?k=":":w===114&&(A=":");let _=r.alignDelimiters===!1?1:Math.max(1,p[m]-k.length-A.length);const N=k+"-".repeat(_)+A;r.alignDelimiters!==!1&&(_=k.length+_+A.length,_>p[m]&&(p[m]=_),E[m]=_),v[m]=N}u.splice(1,0,v),c.splice(1,0,E),h=-1;const x=[];for(;++h<u.length;){const w=u[h],k=c[h];m=-1;const A=[];for(;++m<f;){const _=w[m]||"";let N="",D="";if(r.alignDelimiters!==!1){const F=p[m]-(k[m]||0),L=s[m];L===114?N=" ".repeat(F):L===99?F%2?(N=" ".repeat(F/2+.5),D=" ".repeat(F/2-.5)):(N=" ".repeat(F/2),D=N):D=" ".repeat(F)}r.delimiterStart!==!1&&!m&&A.push("|"),r.padding!==!1&&!(r.alignDelimiters===!1&&_==="")&&(r.delimiterStart!==!1||m)&&A.push(" "),r.alignDelimiters!==!1&&A.push(N),A.push(_),r.alignDelimiters!==!1&&A.push(D),r.padding!==!1&&A.push(" "),(r.delimiterEnd!==!1||m!==f-1)&&A.push("|")}x.push(r.delimiterEnd===!1?A.join("").replace(/ +$/,""):A.join(""))}return x.join(`
|
||||
`)}function SP(e){return e==null?"":String(e)}function mk(e){const t=typeof e=="string"?e.codePointAt(0):0;return t===67||t===99?99:t===76||t===108?108:t===82||t===114?114:0}function xP(e,t,r,a){const o=r.enter("blockquote"),s=r.createTracker(a);s.move("> "),s.shift(2);const u=r.indentLines(r.containerFlow(e,s.current()),wP);return o(),u}function wP(e,t,r){return">"+(r?"":" ")+e}function TP(e,t){return hk(e,t.inConstruct,!0)&&!hk(e,t.notInConstruct,!1)}function hk(e,t,r){if(typeof t=="string"&&(t=[t]),!t||t.length===0)return r;let a=-1;for(;++a<t.length;)if(e.includes(t[a]))return!0;return!1}function bk(e,t,r,a){let o=-1;for(;++o<r.unsafe.length;)if(r.unsafe[o].character===`
|
||||
`&&TP(r.stack,r.unsafe[o]))return/[ \t]/.test(a.before)?"":" ";return`\\
|
||||
`}function AP(e,t){const r=String(e);let a=r.indexOf(t),o=a,s=0,u=0;if(typeof t!="string")throw new TypeError("Expected substring");for(;a!==-1;)a===o?++s>u&&(u=s):s=1,o=a+t.length,a=r.indexOf(t,o);return u}function kP(e,t){return!!(t.options.fences===!1&&e.value&&!e.lang&&/[^ \r\n]/.test(e.value)&&!/^[\t ]*(?:[\r\n]|$)|(?:^|[\r\n])[\t ]*$/.test(e.value))}function _P(e){const t=e.options.fence||"`";if(t!=="`"&&t!=="~")throw new Error("Cannot serialize code with `"+t+"` for `options.fence`, expected `` ` `` or `~`");return t}function NP(e,t,r,a){const o=_P(r),s=e.value||"",u=o==="`"?"GraveAccent":"Tilde";if(kP(e,r)){const m=r.enter("codeIndented"),v=r.indentLines(s,RP);return m(),v}const c=r.createTracker(a),p=o.repeat(Math.max(AP(s,o)+1,3)),f=r.enter("codeFenced");let h=c.move(p);if(e.lang){const m=r.enter(`codeFencedLang${u}`);h+=c.move(r.safe(e.lang,{before:h,after:" ",encode:["`"],...c.current()})),m()}if(e.lang&&e.meta){const m=r.enter(`codeFencedMeta${u}`);h+=c.move(" "),h+=c.move(r.safe(e.meta,{before:h,after:`
|
||||
`,encode:["`"],...c.current()})),m()}return h+=c.move(`
|
||||
`),s&&(h+=c.move(s+`
|
||||
`)),h+=c.move(p),f(),h}function RP(e,t,r){return(r?"":" ")+e}function bm(e){const t=e.options.quote||'"';if(t!=='"'&&t!=="'")throw new Error("Cannot serialize title with `"+t+"` for `options.quote`, expected `\"`, or `'`");return t}function CP(e,t,r,a){const o=bm(r),s=o==='"'?"Quote":"Apostrophe",u=r.enter("definition");let c=r.enter("label");const p=r.createTracker(a);let f=p.move("[");return f+=p.move(r.safe(r.associationId(e),{before:f,after:"]",...p.current()})),f+=p.move("]: "),c(),!e.url||/[\0- \u007F]/.test(e.url)?(c=r.enter("destinationLiteral"),f+=p.move("<"),f+=p.move(r.safe(e.url,{before:f,after:">",...p.current()})),f+=p.move(">")):(c=r.enter("destinationRaw"),f+=p.move(r.safe(e.url,{before:f,after:e.title?" ":`
|
||||
`,...p.current()}))),c(),e.title&&(c=r.enter(`title${s}`),f+=p.move(" "+o),f+=p.move(r.safe(e.title,{before:f,after:o,...p.current()})),f+=p.move(o),c()),u(),f}function IP(e){const t=e.options.emphasis||"*";if(t!=="*"&&t!=="_")throw new Error("Cannot serialize emphasis with `"+t+"` for `options.emphasis`, expected `*`, or `_`");return t}function Rl(e){return"&#x"+e.toString(16).toUpperCase()+";"}function Ku(e,t,r){const a=Qi(e),o=Qi(t);return a===void 0?o===void 0?r==="_"?{inside:!0,outside:!0}:{inside:!1,outside:!1}:o===1?{inside:!0,outside:!0}:{inside:!1,outside:!0}:a===1?o===void 0?{inside:!1,outside:!1}:o===1?{inside:!0,outside:!0}:{inside:!1,outside:!1}:o===void 0?{inside:!1,outside:!1}:o===1?{inside:!0,outside:!1}:{inside:!1,outside:!1}}ER.peek=OP;function ER(e,t,r,a){const o=IP(r),s=r.enter("emphasis"),u=r.createTracker(a),c=u.move(o);let p=u.move(r.containerPhrasing(e,{after:o,before:c,...u.current()}));const f=p.charCodeAt(0),h=Ku(a.before.charCodeAt(a.before.length-1),f,o);h.inside&&(p=Rl(f)+p.slice(1));const m=p.charCodeAt(p.length-1),v=Ku(a.after.charCodeAt(0),m,o);v.inside&&(p=p.slice(0,-1)+Rl(m));const E=u.move(o);return s(),r.attentionEncodeSurroundingInfo={after:v.outside,before:h.outside},c+p+E}function OP(e,t,r){return r.options.emphasis||"*"}function LP(e,t){let r=!1;return mm(e,function(a){if("value"in a&&/\r?\n|\r/.test(a.value)||a.type==="break")return r=!0,dg}),!!((!e.depth||e.depth<3)&&um(e)&&(t.options.setext||r))}function DP(e,t,r,a){const o=Math.max(Math.min(6,e.depth||1),1),s=r.createTracker(a);if(LP(e,r)){const h=r.enter("headingSetext"),m=r.enter("phrasing"),v=r.containerPhrasing(e,{...s.current(),before:`
|
||||
`,after:`
|
||||
`});return m(),h(),v+`
|
||||
`+(o===1?"=":"-").repeat(v.length-(Math.max(v.lastIndexOf("\r"),v.lastIndexOf(`
|
||||
`))+1))}const u="#".repeat(o),c=r.enter("headingAtx"),p=r.enter("phrasing");s.move(u+" ");let f=r.containerPhrasing(e,{before:"# ",after:`
|
||||
`,...s.current()});return/^[\t ]/.test(f)&&(f=Rl(f.charCodeAt(0))+f.slice(1)),f=f?u+" "+f:u,r.options.closeAtx&&(f+=" "+u),p(),c(),f}SR.peek=MP;function SR(e){return e.value||""}function MP(){return"<"}xR.peek=jP;function xR(e,t,r,a){const o=bm(r),s=o==='"'?"Quote":"Apostrophe",u=r.enter("image");let c=r.enter("label");const p=r.createTracker(a);let f=p.move("![");return f+=p.move(r.safe(e.alt,{before:f,after:"]",...p.current()})),f+=p.move("]("),c(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(c=r.enter("destinationLiteral"),f+=p.move("<"),f+=p.move(r.safe(e.url,{before:f,after:">",...p.current()})),f+=p.move(">")):(c=r.enter("destinationRaw"),f+=p.move(r.safe(e.url,{before:f,after:e.title?" ":")",...p.current()}))),c(),e.title&&(c=r.enter(`title${s}`),f+=p.move(" "+o),f+=p.move(r.safe(e.title,{before:f,after:o,...p.current()})),f+=p.move(o),c()),f+=p.move(")"),u(),f}function jP(){return"!"}wR.peek=UP;function wR(e,t,r,a){const o=e.referenceType,s=r.enter("imageReference");let u=r.enter("label");const c=r.createTracker(a);let p=c.move("![");const f=r.safe(e.alt,{before:p,after:"]",...c.current()});p+=c.move(f+"]["),u();const h=r.stack;r.stack=[],u=r.enter("reference");const m=r.safe(r.associationId(e),{before:p,after:"]",...c.current()});return u(),r.stack=h,s(),o==="full"||!f||f!==m?p+=c.move(m+"]"):o==="shortcut"?p=p.slice(0,-1):p+=c.move("]"),p}function UP(){return"!"}TR.peek=FP;function TR(e,t,r){let a=e.value||"",o="`",s=-1;for(;new RegExp("(^|[^`])"+o+"([^`]|$)").test(a);)o+="`";for(/[^ \r\n]/.test(a)&&(/^[ \r\n]/.test(a)&&/[ \r\n]$/.test(a)||/^`|`$/.test(a))&&(a=" "+a+" ");++s<r.unsafe.length;){const u=r.unsafe[s],c=r.compilePattern(u);let p;if(u.atBreak)for(;p=c.exec(a);){let f=p.index;a.charCodeAt(f)===10&&a.charCodeAt(f-1)===13&&f--,a=a.slice(0,f)+" "+a.slice(p.index+1)}}return o+a+o}function FP(){return"`"}function AR(e,t){const r=um(e);return!!(!t.options.resourceLink&&e.url&&!e.title&&e.children&&e.children.length===1&&e.children[0].type==="text"&&(r===e.url||"mailto:"+r===e.url)&&/^[a-z][a-z+.-]+:/i.test(e.url)&&!/[\0- <>\u007F]/.test(e.url))}kR.peek=BP;function kR(e,t,r,a){const o=bm(r),s=o==='"'?"Quote":"Apostrophe",u=r.createTracker(a);let c,p;if(AR(e,r)){const h=r.stack;r.stack=[],c=r.enter("autolink");let m=u.move("<");return m+=u.move(r.containerPhrasing(e,{before:m,after:">",...u.current()})),m+=u.move(">"),c(),r.stack=h,m}c=r.enter("link"),p=r.enter("label");let f=u.move("[");return f+=u.move(r.containerPhrasing(e,{before:f,after:"](",...u.current()})),f+=u.move("]("),p(),!e.url&&e.title||/[\0- \u007F]/.test(e.url)?(p=r.enter("destinationLiteral"),f+=u.move("<"),f+=u.move(r.safe(e.url,{before:f,after:">",...u.current()})),f+=u.move(">")):(p=r.enter("destinationRaw"),f+=u.move(r.safe(e.url,{before:f,after:e.title?" ":")",...u.current()}))),p(),e.title&&(p=r.enter(`title${s}`),f+=u.move(" "+o),f+=u.move(r.safe(e.title,{before:f,after:o,...u.current()})),f+=u.move(o),p()),f+=u.move(")"),c(),f}function BP(e,t,r){return AR(e,r)?"<":"["}_R.peek=zP;function _R(e,t,r,a){const o=e.referenceType,s=r.enter("linkReference");let u=r.enter("label");const c=r.createTracker(a);let p=c.move("[");const f=r.containerPhrasing(e,{before:p,after:"]",...c.current()});p+=c.move(f+"]["),u();const h=r.stack;r.stack=[],u=r.enter("reference");const m=r.safe(r.associationId(e),{before:p,after:"]",...c.current()});return u(),r.stack=h,s(),o==="full"||!f||f!==m?p+=c.move(m+"]"):o==="shortcut"?p=p.slice(0,-1):p+=c.move("]"),p}function zP(){return"["}function ym(e){const t=e.options.bullet||"*";if(t!=="*"&&t!=="+"&&t!=="-")throw new Error("Cannot serialize items with `"+t+"` for `options.bullet`, expected `*`, `+`, or `-`");return t}function PP(e){const t=ym(e),r=e.options.bulletOther;if(!r)return t==="*"?"-":"*";if(r!=="*"&&r!=="+"&&r!=="-")throw new Error("Cannot serialize items with `"+r+"` for `options.bulletOther`, expected `*`, `+`, or `-`");if(r===t)throw new Error("Expected `bullet` (`"+t+"`) and `bulletOther` (`"+r+"`) to be different");return r}function $P(e){const t=e.options.bulletOrdered||".";if(t!=="."&&t!==")")throw new Error("Cannot serialize items with `"+t+"` for `options.bulletOrdered`, expected `.` or `)`");return t}function NR(e){const t=e.options.rule||"*";if(t!=="*"&&t!=="-"&&t!=="_")throw new Error("Cannot serialize rules with `"+t+"` for `options.rule`, expected `*`, `-`, or `_`");return t}function GP(e,t,r,a){const o=r.enter("list"),s=r.bulletCurrent;let u=e.ordered?$P(r):ym(r);const c=e.ordered?u==="."?")":".":PP(r);let p=t&&r.bulletLastUsed?u===r.bulletLastUsed:!1;if(!e.ordered){const h=e.children?e.children[0]:void 0;if((u==="*"||u==="-")&&h&&(!h.children||!h.children[0])&&r.stack[r.stack.length-1]==="list"&&r.stack[r.stack.length-2]==="listItem"&&r.stack[r.stack.length-3]==="list"&&r.stack[r.stack.length-4]==="listItem"&&r.indexStack[r.indexStack.length-1]===0&&r.indexStack[r.indexStack.length-2]===0&&r.indexStack[r.indexStack.length-3]===0&&(p=!0),NR(r)===u&&h){let m=-1;for(;++m<e.chilLine truncated
|
||||
`,inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{inlineCode:v,table:u,tableCell:p,tableRow:c}};function u(E,x,w,k){return f(h(E,w,k),E.align)}function c(E,x,w,k){const A=m(E,w,k),_=f([A]);return _.slice(0,_.indexOf(`
|
||||
`))}function p(E,x,w,k){const A=w.enter("tableCell"),_=w.enter("phrasing"),N=w.containerPhrasing(E,{...k,before:s,after:s});return _(),A(),N}function f(E,x){return EP(E,{align:x,alignDelimiters:a,padding:r,stringLength:o})}function h(E,x,w){const k=E.children;let A=-1;const _=[],N=x.enter("table");for(;++A<k.length;)_[A]=m(k[A],x,w);return N(),_}function m(E,x,w){const k=E.children;let A=-1;const _=[],N=x.enter("tableRow");for(;++A<k.length;)_[A]=p(k[A],E,x,w);return N(),_}function v(E,x,w){let k=CR.inlineCode(E,x,w);return w.stack.includes("tableCell")&&(k=k.replace(/\|/g,"\\$&")),k}}function l$(){return{exit:{taskListCheckValueChecked:vk,taskListCheckValueUnchecked:vk,paragraph:u$}}}function s$(){return{unsafe:[{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{listItem:c$}}}function vk(e){const t=this.stack[this.stack.length-2];t.type,t.checked=e.type==="taskListCheckValueChecked"}function u$(e){const t=this.stack[this.stack.length-2];if(t&&t.type==="listItem"&&typeof t.checked=="boolean"){const r=this.stack[this.stack.length-1];r.type;const a=r.children[0];if(a&&a.type==="text"){const o=t.children;let s=-1,u;for(;++s<o.length;){const c=o[s];if(c.type==="paragraph"){u=c;break}}u===r&&(a.value=a.value.slice(1),a.value.length===0?r.children.shift():r.position&&a.position&&typeof a.position.start.offset=="number"&&(a.position.start.column++,a.position.start.offset++,r.position.start=Object.assign({},a.position.start)))}}this.exit(e)}function c$(e,t,r,a){const o=e.children[0],s=typeof e.checked=="boolean"&&o&&o.type==="paragraph",u="["+(e.checked?"x":" ")+"] ",c=r.createTracker(a);s&&c.move(u);let p=CR.listItem(e,t,r,{...a,...c.current()});return s&&(p=p.replace(/^(?:[*+-]|\d+\.)([\r\n]| {1,3})/,f)),p;function f(h){return h+u}}function d$(){return[Gz(),cP(),gP(),e$(),l$()]}function p$(e){return{extensions:[Hz(),dP(e),mP(),o$(e),s$()]}}const f$={tokenize:v$,partial:!0},IR={tokenize:E$,partial:!0},OR={tokenize:S$,partial:!0},LR={tokenize:x$,partial:!0},g$={tokenize:w$,partial:!0},DR={name:"wwwAutolink",tokenize:b$,previous:jR},MR={name:"protocolAutolink",tokenize:y$,previous:UR},Rr={name:"emailAutolink",tokenize:h$,previous:FR},ar={};function m$(){return{text:ar}}let ja=48;for(;ja<123;)ar[ja]=Rr,ja++,ja===58?ja=65:ja===91&&(ja=97);ar[43]=Rr;ar[45]=Rr;ar[46]=Rr;ar[95]=Rr;ar[72]=[Rr,MR];ar[104]=[Rr,MR];ar[87]=[Rr,DR];ar[119]=[Rr,DR];function h$(e,t,r){const a=this;let o,s;return u;function u(m){return!mg(m)||!FR.call(a,a.previous)||vm(a.events)?r(m):(e.enter("literalAutolink"),e.enter("literalAutolinkEmail"),c(m))}function c(m){return mg(m)?(e.consume(m),c):m===64?(e.consume(m),p):r(m)}function p(m){return m===46?e.check(g$,h,f)(m):m===45||m===95||Mt(m)?(s=!0,e.consume(m),p):h(m)}function f(m){return e.consume(m),o=!0,p}function h(m){return s&&o&&Vt(a.previous)?(e.exit("literalAutolinkEmail"),e.exit("literalAutolink"),t(m)):r(m)}}function b$(e,t,r){const a=this;return o;function o(u){return u!==87&&u!==119||!jR.call(a,a.previous)||vm(a.events)?r(u):(e.enter("literalAutolink"),e.enter("literalAutolinkWww"),e.check(f$,e.attempt(IR,e.attempt(OR,s),r),r)(u))}function s(u){return e.exit("literalAutolinkWww"),e.exit("literalAutolink"),t(u)}}function y$(e,t,r){const a=this;let o="",s=!1;return u;function u(m){return(m===72||m===104)&&UR.call(a,a.previous)&&!vm(a.events)?(e.enter("literalAutolink"),e.enter("literalAutolinkHttp"),o+=String.fromCodePoint(m),e.consume(m),c):r(m)}function c(m){if(Vt(m)&&o.length<5)return o+=String.fromCodePoint(m),e.consume(m),c;if(m===58){const v=o.toLowerCase();if(v==="http"||v==="https")return e.consume(m),p}return r(m)}function p(m){return m===47?(e.consume(m),s?f:(s=!0,p)):r(m)}function f(m){return m===null||qu(m)||rt(m)||$a(m)||gc(m)?r(m):e.attempt(IR,e.attempt(OR,h),r)(m)}function h(m){return e.exit("literalAutolinkHttp"),e.exit("literalAutolink"),t(m)}}function v$(e,t,r){let a=0;return o;function o(u){return(u===87||u===119)&&a<3?(a++,e.consume(u),o):u===46&&a===3?(e.consume(u),s):r(u)}function s(u){return u===null?r(u):t(u)}}function E$(e,t,r){let a,o,s;return u;function u(f){return f===46||f===95?e.check(LR,p,c)(f):f===null||rt(f)||$a(f)||f!==45&&gc(f)?p(f):(s=!0,e.consume(f),u)}function c(f){return f===95?a=!0:(o=a,a=void 0),e.consume(f),u}function p(f){return o||a||!s?r(f):t(f)}}function S$(e,t){let r=0,a=0;return o;function o(u){return u===40?(r++,e.consume(u),o):u===41&&a<r?s(u):u===33||u===34||u===38||u===39||u===41||u===42||u===44||u===46||u===58||u===59||u===60||u===63||u===93||u===95||u===126?e.check(LR,t,s)(u):u===null||rt(u)||$a(u)?t(u):(e.consume(u),o)}function s(u){return u===41&&a++,e.consume(u),o}}function x$(e,t,r){return a;function a(c){return c===33||c===34||c===39||c===41||c===42||c===44||c===46||c===58||c===59||c===63||c===95||c===126?(e.consume(c),a):c===38?(e.consume(c),s):c===93?(e.consume(c),o):c===60||c===null||rt(c)||$a(c)?t(c):r(c)}function o(c){return c===null||c===40||c===91||rt(c)||$a(c)?t(c):a(c)}function s(c){return Vt(c)?u(c):r(c)}function u(c){re
Line truncated
|
||||
`),r=[];let a=0;for(;a<t.length;){const o=t[a].match(/^===\s+"([^"]+)"\s*$/);if(!o){r.push(t[a]),a++;continue}const s=o[1];for(a++;a<t.length&&t[a].trim()==="";)a++;const u=[];for(;a<t.length&&!(/^===\s+"[^"]+"\s*$/.test(t[a])||t[a].trim()===""&&a+1<t.length&&/^===\s+"[^"]+"\s*$/.test(t[a+1]));){if(t[a].startsWith(" ")||t[a].trimStart().startsWith("```")){u.push(t[a].replace(/^ /,"")),a++;continue}if(u.length>0&&t[a].trim()===""){u.push(""),a++;continue}break}r.push(`### ${zR}${s}`),r.push(""),r.push(...u),r.push("")}return r.join(`
|
||||
`)}function X$(e){let t=W$(e);return t=Y$(t),t=K$(t),t}function Sk(e){return e.startsWith(zR)||e.startsWith("tab:")}function Z$(e){return e.replace(/^\u200Btab:/,"").replace(/^tab:/,"")}function Q$(e){const t=new Set;e.matches("article.prose, main .prose, .prose, main.flex-1, #main-content")&&t.add(e),e.querySelectorAll("article.prose, main .prose, .prose").forEach(r=>t.add(r));for(const r of t)J$(r),e3(r),t3(r)}function J$(e){for(const t of e.querySelectorAll("p")){const r=t.textContent?.trim()??"";(/^<\/?div[\s>]/.test(r)||r==="</div>")&&t.remove()}}function e3(e){for(const t of e.querySelectorAll("ul")){if(t.classList.contains("aiui-feature-grid"))continue;const r=t.querySelector("li");if(r?.textContent?.includes("YAML-Driven")||r?.textContent?.includes("Component Slots")||r?.querySelector("strong")){t.classList.add("aiui-feature-grid","grid","grid-cols-1","md:grid-cols-2","gap-4","my-6","list-none","pl-0");for(const o of t.querySelectorAll("li"))o.classList.add("border","border-border","rounded-lg","p-4","bg-card")}}}function t3(e){if(e.querySelector("[data-aiui-tabs]"))return;const t=Array.from(e.querySelectorAll("h3")).filter(c=>Sk(c.textContent?.trim()??""));if(t.length<2)return;const r=t.map(c=>{const p=Z$(c.textContent?.trim()??""),f=[];let h=c.nextSibling;for(;h;){if(h.nodeType===Node.ELEMENT_NODE){const v=h;if(v.tagName==="H3"&&Sk(v.textContent?.trim()??"")||["H1","H2"].includes(v.tagName))break}const m=h.nextSibling;f.push(h),h=m}return{heading:c,title:p,nodes:f}}),a=document.createElement("div");a.className="aiui-tabs my-6 border border-border rounded-lg overflow-hidden",a.dataset.aiuiTabs="true";const o=document.createElement("div");o.className="aiui-tab-buttons flex flex-wrap gap-0 border-b border-border bg-muted/30",a.appendChild(o);const s=document.createElement("div");s.className="aiui-tab-panels",a.appendChild(s),r.forEach((c,p)=>{const f=document.createElement("button");f.type="button",f.className=["aiui-tab-button px-4 py-2 text-sm font-medium transition-colors",p===0?"bg-background text-foreground border-b-2 border-primary":"text-muted-foreground hover:text-foreground"].join(" "),f.textContent=c.title,f.dataset.tabIndex=String(p),o.appendChild(f);const h=document.createElement("div");h.className=p===0?"aiui-tab-panel p-4 block":"aiui-tab-panel p-4 hidden",h.dataset.tabPanel=String(p);for(const m of c.nodes)h.appendChild(m);s.appendChild(h)});const u=r[0].heading;e.insertBefore(a,u);for(const c of r)c.heading.remove();o.addEventListener("click",c=>{const p=c.target.closest(".aiui-tab-button");if(!p)return;const f=p.dataset.tabIndex??"0";for(const h of o.querySelectorAll(".aiui-tab-button")){const m=h.dataset.tabIndex===f;h.classList.toggle("bg-background",m),h.classList.toggle("text-foreground",m),h.classList.toggle("border-b-2",m),h.classList.toggle("border-primary",m),h.classList.toggle("text-muted-foreground",!m)}for(const h of s.querySelectorAll(".aiui-tab-panel"))h.classList.toggle("hidden",h.dataset.tabPanel!==f),h.classList.toggle("block",h.dataset.tabPanel===f)})}function n3(e){return e.toLowerCase().replace(/[^\w\s-]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}function Rn({className:e,...t}){return g.jsx("div",{"data-slot":"card",className:me("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",e),...t})}function zn({className:e,...t}){return g.jsx("div",{"data-slot":"card-header",className:me("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",e),...t})}function Pn({className:e,...t}){return g.jsx("div",{"data-slot":"card-title",className:me("leading-none font-semibold",e),...t})}function Em({className:e,...t}){return g.jsx("div",{"data-slot":"card-description",className:me("text-muted-foreground text-sm",e),...t})}function Cn({className:e,...t}){return g.jsx("div",{"data-slot":"card-content",className:me("px-6",e),...t})}function r3({className:e,...t}){return g.jsx("div",{"data-slot":"card-footer",className:me("flex items-center px-6 [.border-t]:pt-6",e),...t})}function a3({props:e}){const t=e?.title||"Statistics",r=e?.value||"0",a=e?.change||"",o=e?.changeType||"positive";return g.jsxs(Rn,{className:"mb-4",children:[g.jsxs(zn,{className:"pb-2",children:[g.jsx(Em,{children:t}),g.jsx(Pn,{className:"text-2xl",children:r})]}),a&&g.jsx(Cn,{className:"pt-0",children:g.jsx("span",{className:o==="positive"?"text-green-500 text-sm":"text-red-500 text-sm",children:a})})]})}function i3({props:e}){const t=e?.title||"Quick Links",r=e?.links||[];return g.jsxs(Rn,{className:"mb-4",children:[g.jsx(zn,{className:"pb-2",children:g.jsx(Pn,{className:"text-sm font-medium",children:t})}),g.jsx(Cn,{className:"space-y-1",children:r.map((a,o)=>g.jsx("a",{href:a.href,className:"block text-sm text-muted-foreground hover:text-foreground transition-colors",children:a.label},o))})]})}function o3({props:e}){const t=e?.title||"Newsletter",r=e?.buttonT
Line truncated
|
||||
`)===-1?g.jsx("code",{className:"bg-primary/10 text-primary px-1.5 py-0.5 rounded text-sm font-mono",children:v}):g.jsx("code",{className:`block font-mono text-foreground ${m??""}`,children:v}),pre:({children:m})=>g.jsx("pre",{className:"bg-muted text-foreground border border-border p-4 rounded-lg text-sm overflow-auto my-4 font-mono whitespace-pre",children:m}),table:({children:m})=>g.jsx("div",{className:"overflow-auto my-4",children:g.jsx("table",{className:"w-full border-collapse text-sm",children:m})}),thead:({children:m})=>g.jsx("thead",{className:"bg-muted/50",children:m}),tr:({children:m})=>g.jsx("tr",{className:"border-b",children:m}),th:({children:m})=>g.jsx("th",{className:"px-4 py-2 text-left font-medium",children:m}),td:({children:m})=>g.jsx("td",{className:"px-4 py-2 text-muted-foreground",children:m}),hr:()=>g.jsx("hr",{className:"my-6 border-border"}),strong:({children:m})=>g.jsx("strong",{className:"font-semibold text-foreground",children:m}),em:({children:m})=>g.jsx("em",{children:m})};return r?g.jsxs("main",{id:"main-content",className:"flex-1 p-8 max-w-3xl",children:[g.jsx(h3,{selectedItem:r}),s?g.jsx("div",{className:"text-muted-foreground",children:"Loading content..."}):a?g.jsx("article",{ref:c,className:"prose prose-neutral dark:prose-invert max-w-none prose-pre:bg-muted prose-pre:text-foreground prose-code:text-foreground",children:g.jsx(mR,{remarkPlugins:[BR],components:h,children:a})}):g.jsx("div",{className:"bg-muted/50 border rounded-lg p-6",children:g.jsxs("p",{className:"text-muted-foreground",children:["Content for ",g.jsx("strong",{className:"text-primary",children:r.title})," would be displayed here."]})})]}):g.jsxs("main",{className:"flex-1 p-8 max-w-3xl",children:[g.jsx("h1",{className:"text-4xl font-bold tracking-tight mb-4",children:e.site?.title||"Documentation"}),g.jsx("p",{className:"text-muted-foreground text-lg mb-8",children:e.site?.description||"Welcome to the documentation."}),g.jsx(qi,{className:"my-8"}),g.jsx("h2",{className:"text-2xl font-semibold mb-4 text-primary",children:"Theme Configuration"}),g.jsxs("div",{className:"bg-muted/50 border rounded-lg p-4 mb-6",children:[g.jsx("p",{className:"text-sm text-muted-foreground mb-2",children:g.jsx("strong",{children:"Current theme from YAML:"})}),g.jsx("pre",{className:"bg-muted text-foreground border border-border p-3 rounded-lg text-sm overflow-auto",children:`site:
|
||||
theme:
|
||||
preset: "${p?.preset||"zinc"}"
|
||||
radius: "${p?.radius||"md"}"
|
||||
darkMode: ${p?.darkMode!==!1}`})]}),g.jsx("h3",{className:"text-lg font-medium mb-3",children:"Available Presets"}),g.jsx("div",{className:"flex flex-wrap gap-2 mb-6",children:Object.keys(BL).map(m=>g.jsx("span",{className:`px-3 py-1 rounded-full text-sm ${m===(p?.preset||"zinc")?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground"}`,children:m},m))}),g.jsxs("p",{className:"text-muted-foreground mb-4",children:[g.jsx("strong",{children:"Click any item in the sidebar"})," to navigate."]}),g.jsx("h2",{className:"text-2xl font-semibold mt-8 mb-4",children:"Routes"}),g.jsxs("p",{className:"text-muted-foreground mb-4",children:[g.jsx("span",{className:"font-medium text-primary",children:t.routes?.length||0})," routes configured."]}),g.jsx("pre",{className:"bg-muted text-foreground border border-border p-4 rounded-lg text-sm overflow-auto",children:JSON.stringify(t.routes?.slice(0,3),null,2)})]})}function _u({config:e,templateKey:t="docs"}){const r=e.templates?.[t]?.slots?.footer,a=r?.ref,s=(a?e.components?.[a]:e.components?.footer)?.props||r?.props;return g.jsx("footer",{className:"border-t border-border/50 py-8 px-6 bg-muted/20",children:g.jsxs("div",{className:"max-w-screen-2xl mx-auto flex flex-col sm:flex-row justify-between items-center gap-4 text-sm text-muted-foreground",children:[g.jsxs("div",{className:"flex items-center gap-2",children:[g.jsx("div",{className:"w-5 h-5 rounded bg-gradient-to-br from-primary/60 to-primary flex items-center justify-center",children:g.jsx("span",{className:"text-primary-foreground text-[8px] font-bold",children:"AI"})}),g.jsx("span",{children:s?.text||`© ${new Date().getFullYear()} PraisonAIUI`})]}),g.jsx("nav",{className:"flex items-center gap-6",children:s?.links?.map(u=>g.jsx("a",{href:u.href,className:"hover:text-foreground transition-colors",children:u.label},u.href))})]})})}const b3=()=>({taskLists:[],collapsed:!1}),Bn=new Map,hg=100,y3=1440*60*1e3;function v3(){const e=Date.now(),t=Array.from(Bn.entries());for(const[r,a]of t)e-a.lastAccessed>y3&&Bn.delete(r);if(Bn.size>hg){const r=Array.from(Bn.entries()).sort(([,o],[,s])=>o.lastAccessed-s.lastAccessed),a=Bn.size-hg;for(let o=0;o<a;o++)Bn.delete(r[o][0])}}function bg(e){if(!Bn.has(e))Bn.size>=hg&&v3(),Bn.set(e,{state:b3(),listeners:new Set,lastAccessed:Date.now()});else{const t=Bn.get(e);t.lastAccessed=Date.now()}return Bn.get(e)}function Sm(e,t){const r=bg(e),a=t(r.state);r.state=a,r.listeners.forEach(o=>o())}function E3(e,t){Sm(e,r=>{const a=r.taskLists.filter(s=>s.id!==t.task_list_id),o={id:t.task_list_id,name:t.name,tasks:t.tasks,stats:GR(t.tasks),sequence:t.sequence,created_at:t.timestamp,updated_at:t.timestamp};return{...r,taskLists:[...a,o],collapsed:r.taskLists.length===0?t.tasks.length<=1:r.collapsed}})}function S3(e,t){Sm(e,r=>{const a=r.taskLists.map(o=>o.id===t.task_list_id?{...o,tasks:t.tasks,stats:GR(t.tasks),sequence:t.sequence,updated_at:t.timestamp}:o);return{...r,taskLists:a}})}function GR(e){return e.reduce((t,r)=>(t.total+=1,t[r.status.toLowerCase()]+=1,t),{total:0,ready:0,running:0,done:0,failed:0})}function x3(e,t){Sm(e,r=>({...r,collapsed:t}))}function w3(e){const[,t]=S.useState({});return S.useEffect(()=>{const r=bg(e),a=()=>t({});return r.listeners.add(a),()=>{r.listeners.delete(a)}},[e]),bg(e).state}function T3(e,t){return t.type==="task_list.init"?(E3(e,t),!0):t.type==="task_list.update"?(S3(e,t),!0):!1}const HR=()=>({isStreaming:!1,currentResponse:"",toolCalls:[],thinkingSteps:[],pendingElements:[]}),za=new Map;function vc(e){return za.has(e)||za.set(e,{state:HR(),listeners:new Set,abortController:null,toolCallMap:new Map}),za.get(e)}function Nu(e){return e?vc(e).state:HR()}function A3(e,t){const r=vc(e);return r.listeners.add(t),()=>r.listeners.delete(t)}function _n(e,t){const r=vc(e);r.state={...r.state,...t},r.listeners.forEach(a=>a())}async function k3(e){const t=za.get(e);if(t){t.abortController?.abort(),t.abortController=null,_n(e,{isStreaming:!1});try{await fetch("/cancel",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({session_id:e})})}catch{}}}function _3(e,t,r,a){const o=vc(e);if(o.state.isStreaming)return;_n(e,{isStreaming:!0,currentResponse:"",toolCalls:[],thinkingSteps:[],pendingElements:[]}),o.toolCallMap=new Map;const s=new AbortController;o.abortController=s,N3(e,t,r,s,a)}async function N3(e,t,r,a,o){try{const s=await fetch("/run",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,session_id:e,agent:r}),signal:a.signal});if(!s.ok)throw new Error(`HTTP ${s.status}`);const u=s.body?.getReader();if(!u)throw new Error("No response body");const c=new TextDecoder;let p="";for(;;){const{done:f,value:h}=await u.read();if(f)break;p+=c.decode(h,{stream:!0});const m=p.split(`
|
||||
`);p=m.pop()||"";for(const v of m)if(v.startsWith("data: "))try{const E=JSON.parse(v.slice(6));R3(e,E,o)}catch{}}}catch(s){if(s.name!=="AbortError"){const u=za.get(e);u&&_n(e,{thinkingSteps:[...u.state.thinkingSteps,`Error: ${s.message}`]})}}finally{const s=za.get(e);s&&(s.abortController=null),_n(e,{isStreaming:!1}),o?.onEnd?.()}}function R3(e,t,r){const a=za.get(e);if(!a)return;const o=a.toolCallMap;switch(t.type){case"session":t.session_id&&typeof t.session_id=="string"&&r?.onSessionId?.(t.session_id);break;case"token":case"run_content":case"team_run_content":{const u=t.token??t.content??"";u&&_n(e,{currentResponse:a.state.currentResponse+u});break}case"message":case"run_completed":case"team_run_completed":{const u=t.content;u&&_n(e,{currentResponse:u});break}case"thinking":case"reasoning_step":case"team_reasoning_step":{const u=t.step;u&&_n(e,{thinkingSteps:[...a.state.thinkingSteps,u]});break}case"reasoning_started":case"team_reasoning_started":_n(e,{thinkingSteps:[...a.state.thinkingSteps,"Starting reasoning..."]});break;case"tool_call":case"tool_call_started":case"team_tool_call_started":{const u={name:t.name,description:t.description,icon:t.icon,step_number:t.step_number,status:"running",tool_call_id:t.tool_call_id,args:t.args};u.tool_call_id&&o.set(u.tool_call_id,u);const c=wk(a.state.toolCalls,u);_n(e,{toolCalls:c});break}case"tool_call_completed":case"team_tool_call_completed":{const u=t.extra_data,c=t.a2ui??u?.a2ui,p=t.elements??u?.elements,f={name:t.name,description:t.description,icon:t.icon,step_number:t.step_number,status:"done",tool_call_id:t.tool_call_id,args:t.args,formatted_result:t.formatted_result,result:t.result,surface_id:t.surface_id??u?.surface_id,a2ui:c?.length?{messages:c,surface_id:t.surface_id??u?.surface_id}:void 0,elements:p};f.tool_call_id&&o.set(f.tool_call_id,f);const h=wk(a.state.toolCalls,f),m=p?.length?[...a.state.pendingElements,...p]:a.state.pendingElements;_n(e,{toolCalls:h,pendingElements:m});break}case"message_element":{const u=t.extra_data,c=t.element??u?.element;c&&_n(e,{pendingElements:[...a.state.pendingElements,c]});break}case"error":case"run_error":case"team_run_error":{const u=t.error;u&&_n(e,{thinkingSteps:[...a.state.thinkingSteps,`Error: ${u}`]});break}case"approval_required":case"approval_pending":case"run_paused":{const u=t.extra_data;if(u?.type==="approval_required"){const c=u;window.dispatchEvent(new CustomEvent("aiui:approval-required",{detail:c}))}break}case"end":case"done":case"run_cancelled":case"team_run_cancelled":break;default:T3(e,t);break}}function wk(e,t){if(t.tool_call_id){const r=e.findIndex(a=>a.tool_call_id===t.tool_call_id);if(r>=0){const a=[...e];return a[r]={...a[r],...t},a}}return[...e,t]}function VR(e={}){const{externalSessionId:t}=e,[r,a]=S.useState(()=>Nu(t));S.useEffect(()=>{if(!t){a(Nu(null));return}return a(Nu(t)),A3(t,()=>{a(Nu(t))})},[t]);const o=S.useCallback((u,c)=>{const p=t??`new-${Date.now()}`;_3(p,u,c,{onSessionId:f=>{e.onSessionId?.(f)},onEnd:e.onEnd})},[t,e.onSessionId,e.onEnd]),s=S.useCallback(()=>{t&&k3(t)},[t]);return{isStreaming:r.isStreaming,currentResponse:r.currentResponse,toolCalls:r.toolCalls,thinkingSteps:r.thinkingSteps,pendingElements:r.pendingElements,sendMessage:o,cancel:s}}function C3(e,t){if(e==null)return{};var r={};for(var a in e)if({}.hasOwnProperty.call(e,a)){if(t.indexOf(a)!==-1)continue;r[a]=e[a]}return r}function I3(e,t){if(e==null)return{};var r,a,o=C3(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(a=0;a<s.length;a++)r=s[a],t.indexOf(r)===-1&&{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}function yg(e,t){(t==null||t>e.length)&&(t=e.length);for(var r=0,a=Array(t);r<t;r++)a[r]=e[r];return a}function O3(e){if(Array.isArray(e))return yg(e)}function L3(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function D3(e,t){if(e){if(typeof e=="string")return yg(e,t);var r={}.toString.call(e).slice(8,-1);return r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set"?Array.from(e):r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?yg(e,t):void 0}}function M3(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
|
||||
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function vg(e){return O3(e)||L3(e)||D3(e)||M3()}function Cl(e){"@babel/helpers - typeof";return Cl=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Cl(e)}function j3(e,t){if(Cl(e)!="object"||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var a=r.call(e,t);if(Cl(a)!="object")return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return(t==="string"?String:Number)(e)}function U3(e){var t=j3(e,"string");return Cl(t)=="symbol"?t:t+""}function qR(e,t,r){return(t=U3(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function Eg(){return Eg=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var a in r)({}).hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},Eg.apply(null,arguments)}function Tk(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(o){return Object.getOwnPropertyDescriptor(e,o).enumerable})),r.push.apply(r,a)}return r}function Yi(e){for(var t=1;t<arguments.length;t++){var r=arguments[t]!=null?arguments[t]:{};t%2?Tk(Object(r),!0).forEach(function(a){qR(e,a,r[a])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):Tk(Object(r)).forEach(function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(r,a))})}return e}function F3(e){var t=e.length;if(t===0||t===1)return e;if(t===2)return[e[0],e[1],"".concat(e[0],".").concat(e[1]),"".concat(e[1],".").concat(e[0])];if(t===3)return[e[0],e[1],e[2],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0])];if(t>=4)return[e[0],e[1],e[2],e[3],"".concat(e[0],".").concat(e[1]),"".concat(e[0],".").concat(e[2]),"".concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[0]),"".concat(e[1],".").concat(e[2]),"".concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[1]),"".concat(e[2],".").concat(e[3]),"".concat(e[3],".").concat(e[0]),"".concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[0]),"".concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[2],".").concat(e[0],".").concat(e[1]),"".concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[0]),"".concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[1]),"".concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[3],".").concat(e[1],".").concat(e[0]),"".concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[0],".").concat(e[1],".").concat(e[2],".").concat(e[3]),"".concat(e[0],".").concat(e[1],".").concat(e[3],".").concat(e[2]),"".concat(e[0],".").concat(e[2],".").concat(e[1],".").concat(e[3]),"".concat(e[0],".").concat(e[2],".").concat(e[3],".").concat(e[1]),"".concat(e[0],".").concat(e[3],".").concat(e[1],".").concat(e[2]),"".concat(e[0],".").concat(e[3],".").concat(e[2],".").concat(e[1]),"".concat(e[1],".").concat(e[0],".").concat(e[2],".").concat(e[3]),"".concat(e[1],".").concat(e[0],".").concat(e[3],".").concat(e[2]),"".concat(e[1],".").concat(e[2],".").concat(e[0],".").concat(e[3]),"".concat(e[1],".").concat(e[2],".").concat(e[3],".").concat(e[0]),"".concat(e[1],".").concat(e[3],".").concat(e[0],".").concat(e[2]),"".concat(e[1],".").concat(e[3],".").concat(e[2],".").concat(e[0]),"".concat(e[2],".").concat(e[0],".").concat(e[1],".").concat(e[3]),"".concat(e[2],".").concat(e[0],".").concat(e[3],".").concat(e[1]),"".concat(e[2],".").concat(e[1],".").concat(e[0],".").concat(e[3]),"".concat(e[2],".").concat(e[1],".").concat(e[3],".").concat(e[
Line truncated
|
||||
`))})}function Y3(e){var t=e.codeString,r=e.codeStyle,a=e.containerStyle,o=a===void 0?{float:"left",paddingRight:"10px"}:a,s=e.numberStyle,u=s===void 0?{}:s,c=e.startingLineNumber;return Ke.createElement("code",{style:Object.assign({},r,o)},q3({lines:t.replace(/\n$/,"").split(`
|
||||
`),style:u,startingLineNumber:c}))}function W3(e){return"".concat(e.toString().length,".25em")}function WR(e,t){return{type:"element",tagName:"span",properties:{key:"line-number--".concat(e),className:["comment","linenumber","react-syntax-highlighter-line-number"],style:t},children:[{type:"text",value:e}]}}function KR(e,t,r){var a={display:"inline-block",minWidth:W3(r),paddingRight:"1em",textAlign:"right",userSelect:"none"},o=typeof e=="function"?e(t):e,s=la(la({},a),o);return s}function Uu(e){var t=e.children,r=e.lineNumber,a=e.lineNumberStyle,o=e.largestLineNumber,s=e.showInlineLineNumbers,u=e.lineProps,c=u===void 0?{}:u,p=e.className,f=p===void 0?[]:p,h=e.showLineNumbers,m=e.wrapLongLines,v=e.wrapLines,E=v===void 0?!1:v,x=E?la({},typeof c=="function"?c(r):c):{};if(x.className=x.className?[].concat(vg(x.className.trim().split(/\s+/)),vg(f)):f,r&&s){var w=KR(a,r,o);t.unshift(WR(r,w))}return m&h&&(x.style=la({display:"flex"},x.style)),{type:"element",tagName:"span",properties:x,children:t}}function XR(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:[],r=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];e.length===void 0&&(e=[e]);for(var a=0;a<e.length;a++){var o=e[a];if(o.type==="text")r.push(Uu({children:[o],className:vg(new Set(t))}));else if(o.children){var s,u=t.concat(((s=o.properties)===null||s===void 0?void 0:s.className)||[]);XR(o.children,u).forEach(function(c){return r.push(c)})}}return r}function K3(e,t,r,a,o,s,u,c,p){var f,h=XR(e.value),m=[],v=-1,E=0;function x(F,L){var z=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return Uu({children:F,lineNumber:L,lineNumberStyle:c,largestLineNumber:u,showInlineLineNumbers:o,lineProps:r,className:z,showLineNumbers:a,wrapLongLines:p,wrapLines:t})}function w(F,L){if(a&&L&&o){var z=KR(c,L,u);F.unshift(WR(L,z))}return F}function k(F,L){var z=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return t||z.length>0?x(F,L,z):w(F,L)}for(var A=function(){var L=h[E],z=L.children[0].value,$=V3(z);if($){var K=z.split(`
|
||||
`);K.forEach(function(j,q){var X=a&&m.length+s,ne={type:"text",value:"".concat(j,`
|
||||
`)};if(q===0){var re=h.slice(v+1,E).concat(Uu({children:[ne],className:L.properties.className})),ee=k(re,X);m.push(ee)}else if(q===K.length-1){var M=h[E+1]&&h[E+1].children&&h[E+1].children[0],H={type:"text",value:"".concat(j)};if(M){var Q=Uu({children:[H],className:L.properties.className});h.splice(E+1,0,Q)}else{var de=[H],I=k(de,X,L.properties.className);m.push(I)}}else{var U=[ne],B=k(U,X,L.properties.className);m.push(B)}}),v=E}E++};E<h.length;)A();if(v!==h.length-1){var _=h.slice(v+1,h.length);if(_&&_.length){var N=a&&m.length+s,D=k(_,N);m.push(D)}}return t?m:(f=[]).concat.apply(f,m)}function X3(e){var t=e.rows,r=e.stylesheet,a=e.useInlineStyles;return t.map(function(o,s){return YR({node:o,stylesheet:r,useInlineStyles:a,key:"code-segment-".concat(s)})})}function ZR(e){return e&&typeof e.highlightAuto<"u"}function Z3(e){var t=e.astGenerator,r=e.language,a=e.code,o=e.defaultCodeValue;if(ZR(t)){var s=$3(t,r);return r==="text"?{value:o,language:"text"}:s?t.highlight(r,a):t.highlightAuto(a)}try{return r&&r!=="text"?{value:t.highlight(a,r)}:{value:o}}catch{return{value:o}}}function Q3(e,t){return function(a){var o,s,u=a.language,c=a.children,p=a.style,f=p===void 0?t:p,h=a.customStyle,m=h===void 0?{}:h,v=a.codeTagProps,E=v===void 0?{className:u?"language-".concat(u):void 0,style:la(la({},f['code[class*="language-"]']),f['code[class*="language-'.concat(u,'"]')])}:v,x=a.useInlineStyles,w=x===void 0?!0:x,k=a.showLineNumbers,A=k===void 0?!1:k,_=a.showInlineLineNumbers,N=_===void 0?!0:_,D=a.startingLineNumber,F=D===void 0?1:D,L=a.lineNumberContainerStyle,z=a.lineNumberStyle,$=z===void 0?{}:z,K=a.wrapLines,j=a.wrapLongLines,q=j===void 0?!1:j,X=a.lineProps,ne=X===void 0?{}:X,re=a.renderer,ee=a.PreTag,M=ee===void 0?"pre":ee,H=a.CodeTag,Q=H===void 0?"code":H,de=a.code,I=de===void 0?(Array.isArray(c)?c[0]:c)||"":de,U=a.astGenerator,B=I3(a,G3);U=U||e;var C=A?Ke.createElement(Y3,{containerStyle:L,codeStyle:E.style||{},numberStyle:$,startingLineNumber:F,codeString:I}):null,J=f.hljs||f['pre[class*="language-"]']||{backgroundColor:"#fff"},ae=ZR(U)?"hljs":"prismjs",ue=w?Object.assign({},B,{style:Object.assign({},J,m)}):Object.assign({},B,{className:B.className?"".concat(ae," ").concat(B.className):ae,style:Object.assign({},m)});if(q?E.style=la({whiteSpace:"pre-wrap"},E.style):E.style=la({whiteSpace:"pre"},E.style),!U)return Ke.createElement(M,ue,C,Ke.createElement(Q,E,I));(K===void 0&&re||q)&&(K=!0),re=re||X3;var ie=[{type:"text",value:I}],he=Z3({astGenerator:U,language:u,code:I,defaultCodeValue:ie});he.language===null&&(he.value=ie);var fe=(o=(s=I.match(/\n/g))===null||s===void 0?void 0:s.length)!==null&&o!==void 0?o:0,ke=F+fe,Ie=K3(he,K,ne,A,N,F,ke,$,q);return Ke.createElement(M,ue,Ke.createElement(Q,E,!N&&C,re({rows:Ie,stylesheet:f,useInlineStyles:w})))}}const J3="modulepreload",e4=function(e){return"/"+e},_k={},Nk=function(t,r,a){let o=Promise.resolve();if(r&&r.length>0){let p=function(f){return Promise.all(f.map(h=>Promise.resolve(h).then(m=>({status:"fulfilled",value:m}),m=>({status:"rejected",reason:m}))))};document.getElementsByTagName("link");const u=document.querySelector("meta[property=csp-nonce]"),c=u?.nonce||u?.getAttribute("nonce");o=p(r.map(f=>{if(f=e4(f),f in _k)return;_k[f]=!0;const h=f.endsWith(".css"),m=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${f}"]${m}`))return;const v=document.createElement("link");if(v.rel=h?"stylesheet":J3,h||(v.as="script"),v.crossOrigin="",v.href=f,c&&v.setAttribute("nonce",c),document.head.appendChild(v),h)return new Promise((E,x)=>{v.addEventListener("load",E),v.addEventListener("error",()=>x(new Error(`Unable to preload CSS for ${f}`)))})}))}function s(u){const c=new Event("vite:preloadError",{cancelable:!0});if(c.payload=u,window.dispatchEvent(c),!c.defaultPrevented)throw u}return o.then(u=>{for(const c of u||[])c.status==="rejected"&&s(c.reason);return t().catch(s)})},t4=["abap","abnf","actionscript","ada","agda","al","antlr4","apacheconf","apex","apl","applescript","aql","arduino","arff","armasm","arturo","asciidoc","asm6502","asmatmel","aspnet","autohotkey","autoit","avisynth","avro-idl","awk","bash","basic","batch","bbcode","bbj","bicep","birb","bison","bnf","bqn","brainfuck","brightscript","bro","bsl","c","cfscript","chaiscript","cil","cilkc","cilkcpp","clike","clojure","cmake","cobol","coffeescript","concurnas","cooklang","coq","cpp","crystal","csharp","cshtml","csp","css-extras","css","csv","cue","cypher","d","dart","dataweave","dax","dhall","diff","django","dns-zone-file","docker","dot","ebnf","editorconfig","eiffel","ejs","elixir","elm","erb","erlang","etlua","excel-formula","factor","false","firestore-security-rules","flow","fortran","fsharp","ftl","gap","gcode","gdscript","gedcom","gettext","gherkin","git","glsl","gml","gn","go-module","go","gradle","graphql","groovy","haml","handlebars","haskell","haxe","hcl","hlsl","hoon","hpkp","hsts","http","ichigojam","icon","icu-message-format","idris","iecst","ignore","inform7","ini","io","j","java","java
Line truncated
|
||||
?|
|
||||
|(?![\\s\\S])))+`,"m"),alias:s,inside:{line:{pattern:/(.)(?=[\s\S]).*(?:\r\n?|\n)?/,lookbehind:!0},prefix:{pattern:/[\s\S]/,alias:/\w+/.exec(a)[0]}}}}),Object.defineProperty(t.languages.diff,"PREFIXES",{value:r})})(e)}zt.displayName="markup-templating";zt.aliases=[];function zt(e){e.register(Ot),(function(t){function r(a,o){return"___"+a.toUpperCase()+o+"___"}Object.defineProperties(t.languages["markup-templating"]={},{buildPlaceholders:{value:function(a,o,s,u){if(a.language===o){var c=a.tokenStack=[];a.code=a.code.replace(s,function(p){if(typeof u=="function"&&!u(p))return p;for(var f=c.length,h;a.code.indexOf(h=r(o,f))!==-1;)++f;return c[f]=p,h}),a.grammar=t.languages.markup}}},tokenizePlaceholders:{value:function(a,o){if(a.language!==o||!a.tokenStack)return;a.grammar=t.languages[o];var s=0,u=Object.keys(a.tokenStack);function c(p){for(var f=0;f<p.length&&!(s>=u.length);f++){var h=p[f];if(typeof h=="string"||h.content&&typeof h.content=="string"){var m=u[s],v=a.tokenStack[m],E=typeof h=="string"?h:h.content,x=r(o,m),w=E.indexOf(x);if(w>-1){++s;var k=E.substring(0,w),A=new t.Token(o,t.tokenize(v,a.grammar),"language-"+o,v),_=E.substring(w+x.length),N=[];k&&N.push.apply(N,c([k])),N.push(A),_&&N.push.apply(N,c([_])),typeof h=="string"?p.splice.apply(p,[f,1].concat(N)):h.content=N}}else h.content&&c(h.content)}return p}c(a.tokens)}}})})(e)}Ih.displayName="django";Ih.aliases=["jinja2"];function Ih(e){e.register(zt),(function(t){t.languages.django={comment:/^\{#[\s\S]*?#\}$/,tag:{pattern:/(^\{%[+-]?\s*)\w+/,lookbehind:!0,alias:"keyword"},delimiter:{pattern:/^\{[{%][+-]?|[+-]?[}%]\}$/,alias:"punctuation"},string:{pattern:/("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,greedy:!0},filter:{pattern:/(\|)\w+/,lookbehind:!0,alias:"function"},test:{pattern:/(\bis\s+(?:not\s+)?)(?!not\b)\w+/,lookbehind:!0,alias:"function"},function:/\b[a-z_]\w+(?=\s*\()/i,keyword:/\b(?:and|as|by|else|for|if|import|in|is|loop|not|or|recursive|with|without)\b/,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,number:/\b\d+(?:\.\d+)?\b/,boolean:/[Ff]alse|[Nn]one|[Tt]rue/,variable:/\b\w+\b/,punctuation:/[{}[\](),.:;]/};var r=/\{\{[\s\S]*?\}\}|\{%[\s\S]*?%\}|\{#[\s\S]*?#\}/g,a=t.languages["markup-templating"];t.hooks.add("before-tokenize",function(o){a.buildPlaceholders(o,"django",r)}),t.hooks.add("after-tokenize",function(o){a.tokenizePlaceholders(o,"django")}),t.languages.jinja2=t.languages.django,t.hooks.add("before-tokenize",function(o){a.buildPlaceholders(o,"jinja2",r)}),t.hooks.add("after-tokenize",function(o){a.tokenizePlaceholders(o,"jinja2")})})(e)}Oh.displayName="dns-zone-file";Oh.aliases=["dns-zone"];function Oh(e){e.languages["dns-zone-file"]={comment:/;.*/,string:{pattern:/"(?:\\.|[^"\\\r\n])*"/,greedy:!0},variable:[{pattern:/(^\$ORIGIN[ \t]+)\S+/m,lookbehind:!0},{pattern:/(^|\s)@(?=\s|$)/,lookbehind:!0}],keyword:/^\$(?:INCLUDE|ORIGIN|TTL)(?=\s|$)/m,class:{pattern:/(^|\s)(?:CH|CS|HS|IN)(?=\s|$)/,lookbehind:!0,alias:"keyword"},type:{pattern:/(^|\s)(?:A|A6|AAAA|AFSDB|APL|ATMA|CAA|CDNSKEY|CDS|CERT|CNAME|DHCID|DLV|DNAME|DNSKEY|DS|EID|GID|GPOS|HINFO|HIP|IPSECKEY|ISDN|KEY|KX|LOC|MAILA|MAILB|MB|MD|MF|MG|MINFO|MR|MX|NAPTR|NB|NBSTAT|NIMLOC|NINFO|NS|NSAP|NSAP-PTR|NSEC|NSEC3|NSEC3PARAM|NULL|NXT|OPENPGPKEY|PTR|PX|RKEY|RP|RRSIG|RT|SIG|SINK|SMIMEA|SOA|SPF|SRV|SSHFP|TA|TKEY|TLSA|TSIG|TXT|UID|UINFO|UNSPEC|URI|WKS|X25)(?=\s|$)/,lookbehind:!0,alias:"keyword"},punctuation:/[()]/},e.languages["dns-zone"]=e.languages["dns-zone-file"]}Lh.displayName="docker";Lh.aliases=["dockerfile"];function Lh(e){(function(t){var r=/\\[\r\n](?:\s|\\[\r\n]|#.*(?!.))*(?![\s#]|\\[\r\n])/.source,a=/(?:[ \t]+(?![ \t])(?:<SP_BS>)?|<SP_BS>)/.source.replace(/<SP_BS>/g,function(){return r}),o=/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"|'(?:[^'\\\r\n]|\\(?:\r\n|[\s\S]))*'/.source,s=/--[\w-]+=(?:<STR>|(?!["'])(?:[^\s\\]|\\.)+)/.source.replace(/<STR>/g,function(){return o}),u={pattern:RegExp(o),greedy:!0},c={pattern:/(^[ \t]*)#.*/m,lookbehind:!0,greedy:!0};function p(f,h){return f=f.replace(/<OPT>/g,function(){return s}).replace(/<SP>/g,function(){return a}),RegExp(f,h)}t.languages.docker={instruction:{pattern:/(^[ \t]*)(?:ADD|ARG|CMD|COPY|ENTRYPOINT|ENV|EXPOSE|FROM|HEALTHCHECK|LABEL|MAINTAINER|ONBUILD|RUN|SHELL|STOPSIGNAL|USER|VOLUME|WORKDIR)(?=\s)(?:\\.|[^\r\n\\])*(?:\\$(?:\s|#.*$)*(?![\s#])(?:\\.|[^\r\n\\])*)*/im,lookbehind:!0,greedy:!0,inside:{options:{pattern:p(/(^(?:ONBUILD<SP>)?\w+<SP>)<OPT>(?:<SP><OPT>)*/.source,"i"),lookbehind:!0,greedy:!0,inside:{property:{pattern:/(^|\s)--[\w-]+/,lookbehind:!0},string:[u,{pattern:/(=)(?!["'])(?:[^\s\\]|\\.)+/,lookbehind:!0}],operator:/\\$/m,punctuation:/=/}},keyword:[{pattern:p(/(^(?:ONBUILD<SP>)?HEALTHCHECK<SP>(?:<OPT><SP>)*)(?:CMD|NONE)\b/.source,"i"),lookbehind:!0,greedy:!0},{pattern:p(/(^(?:ONBUILD<SP>)?FROM<SP>(?:<OPT><SP>)*(?!--)[^ \t\\]+<SP>)AS/.source,"i"),lookbehind:!0,greedy:!0},{pattern:p(/(^ONBUILD<SP>)\w+/.source,"i"),lookbehind:!0,greedy:!0},{pattern:/^\w+/,greedy:!0}],comment:c,string:u,variable:/\$(?:\w+|\{[^{}"'\\]*\})/,oper
Line truncated
|
||||
]))(?:`+(/[^\s\\]/.source+'|[ ]+(?:(?![ "])|'+a+")|"+/\\[\r\n]+(?:[#;].*[\r\n]+)*(?![#;])/.source)+")*"),lookbehind:!0,greedy:!0,alias:"attr-value",inside:{comment:r,quoted:{pattern:RegExp(/(^|\s)/.source+a),lookbehind:!0,greedy:!0},punctuation:/\\$/m,boolean:{pattern:/^(?:false|no|off|on|true|yes)$/,greedy:!0}}},punctuation:/=/}})(e)}Vl.displayName="t4-templating";Vl.aliases=[];function Vl(e){(function(t){function r(o,s,u){return{pattern:RegExp("<#"+o+"[\\s\\S]*?#>"),alias:"block",inside:{delimiter:{pattern:RegExp("^<#"+o+"|#>$"),alias:"important"},content:{pattern:/[\s\S]+/,inside:s,alias:u}}}}function a(o){var s=t.languages[o],u="language-"+o;return{block:{pattern:/<#[\s\S]+?#>/,inside:{directive:r("@",{"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/,inside:{punctuation:/^=|^["']|["']$/}},keyword:/\b\w+(?=\s)/,"attr-name":/\b\w+/}),expression:r("=",s,u),"class-feature":r("\\+",s,u),standard:r("",s,u)}}}}t.languages["t4-templating"]=Object.defineProperty({},"createT4",{value:a})})(e)}qv.displayName="t4-cs";qv.aliases=["t4"];function qv(e){e.register(io),e.register(Vl),e.languages.t4=e.languages["t4-cs"]=e.languages["t4-templating"].createT4("csharp")}Nc.displayName="vbnet";Nc.aliases=[];function Nc(e){e.register(xc),e.languages.vbnet=e.languages.extend("basic",{comment:[{pattern:/(?:!|REM\b).+/i,inside:{keyword:/^REM/i}},{pattern:/(^|[^\\:])'.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(^|[^"])"(?:""|[^"])*"(?!")/,lookbehind:!0,greedy:!0},keyword:/(?:\b(?:ADDHANDLER|ADDRESSOF|ALIAS|AND|ANDALSO|AS|BEEP|BLOAD|BOOLEAN|BSAVE|BYREF|BYTE|BYVAL|CALL(?: ABSOLUTE)?|CASE|CATCH|CBOOL|CBYTE|CCHAR|CDATE|CDBL|CDEC|CHAIN|CHAR|CHDIR|CINT|CLASS|CLEAR|CLNG|CLOSE|CLS|COBJ|COM|COMMON|CONST|CONTINUE|CSBYTE|CSHORT|CSNG|CSTR|CTYPE|CUINT|CULNG|CUSHORT|DATA|DATE|DECIMAL|DECLARE|DEF(?: FN| SEG|DBL|INT|LNG|SNG|STR)|DEFAULT|DELEGATE|DIM|DIRECTCAST|DO|DOUBLE|ELSE|ELSEIF|END|ENUM|ENVIRON|ERASE|ERROR|EVENT|EXIT|FALSE|FIELD|FILES|FINALLY|FOR(?: EACH)?|FRIEND|FUNCTION|GET|GETTYPE|GETXMLNAMESPACE|GLOBAL|GOSUB|GOTO|HANDLES|IF|IMPLEMENTS|IMPORTS|IN|INHERITS|INPUT|INTEGER|INTERFACE|IOCTL|IS|ISNOT|KEY|KILL|LET|LIB|LIKE|LINE INPUT|LOCATE|LOCK|LONG|LOOP|LSET|ME|MKDIR|MOD|MODULE|MUSTINHERIT|MUSTOVERRIDE|MYBASE|MYCLASS|NAME|NAMESPACE|NARROWING|NEW|NEXT|NOT|NOTHING|NOTINHERITABLE|NOTOVERRIDABLE|OBJECT|OF|OFF|ON(?: COM| ERROR| KEY| TIMER)?|OPEN|OPERATOR|OPTION(?: BASE)?|OPTIONAL|OR|ORELSE|OUT|OVERLOADS|OVERRIDABLE|OVERRIDES|PARAMARRAY|PARTIAL|POKE|PRIVATE|PROPERTY|PROTECTED|PUBLIC|PUT|RAISEEVENT|READ|READONLY|REDIM|REM|REMOVEHANDLER|RESTORE|RESUME|RETURN|RMDIR|RSET|RUN|SBYTE|SELECT(?: CASE)?|SET|SHADOWS|SHARED|SHELL|SHORT|SINGLE|SLEEP|STATIC|STEP|STOP|STRING|STRUCTURE|SUB|SWAP|SYNCLOCK|SYSTEM|THEN|THROW|TIMER|TO|TROFF|TRON|TRUE|TRY|TRYCAST|TYPE|TYPEOF|UINTEGER|ULONG|UNLOCK|UNTIL|USHORT|USING|VIEW PRINT|WAIT|WEND|WHEN|WHILE|WIDENING|WITH|WITHEVENTS|WRITE|WRITEONLY|XOR)|\B(?:#CONST|#ELSE|#ELSEIF|#END|#IF))(?:\$|\b)/i,punctuation:/[,;:(){}]/})}Yv.displayName="t4-vb";Yv.aliases=[];function Yv(e){e.register(Vl),e.register(Nc),e.languages["t4-vb"]=e.languages["t4-templating"].createT4("vbnet")}Wv.displayName="tap";Wv.aliases=[];function Wv(e){e.register(Sc),e.languages.tap={fail:/not ok[^#{\n\r]*/,pass:/ok[^#{\n\r]*/,pragma:/pragma [+-][a-z]+/,bailout:/bail out!.*/i,version:/TAP version \d+/i,plan:/\b\d+\.\.\d+(?: +#.*)?/,subtest:{pattern:/# Subtest(?:: .*)?/,greedy:!0},punctuation:/[{}]/,directive:/#.*/,yamlish:{pattern:/(^[ \t]*)---[\s\S]*?[\r\n][ \t]*\.\.\.$/m,lookbehind:!0,inside:e.languages.yaml,alias:"language-yaml"}}}Kv.displayName="tcl";Kv.aliases=[];function Kv(e){e.languages.tcl={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:{pattern:/"(?:[^"\\\r\n]|\\(?:\r\n|[\s\S]))*"/,greedy:!0},variable:[{pattern:/(\$)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/,lookbehind:!0},{pattern:/(\$)\{[^}]+\}/,lookbehind:!0},{pattern:/(^[\t ]*set[ \t]+)(?:::)?(?:[a-zA-Z0-9]+::)*\w+/m,lookbehind:!0}],function:{pattern:/(^[\t ]*proc[ \t]+)\S+/m,lookbehind:!0},builtin:[{pattern:/(^[\t ]*)(?:break|class|continue|error|eval|exit|for|foreach|if|proc|return|switch|while)\b/m,lookbehind:!0},/\b(?:else|elseif)\b/],scope:{pattern:/(^[\t ]*)(?:global|upvar|variable)\b/m,lookbehind:!0,alias:"constant"},keyword:{pattern:/(^[\t ]*|\[)(?:Safe_Base|Tcl|after|append|apply|array|auto_(?:execok|import|load|mkindex|qualify|reset)|automkindex_old|bgerror|binary|catch|cd|chan|clock|close|concat|dde|dict|encoding|eof|exec|expr|fblocked|fconfigure|fcopy|file(?:event|name)?|flush|gets|glob|history|http|incr|info|interp|join|lappend|lassign|lindex|linsert|list|llength|load|lrange|lrepeat|lreplace|lreverse|lsearch|lset|lsort|math(?:func|op)|memory|msgcat|namespace|open|package|parray|pid|pkg_mkIndex|platform|puts|pwd|re_syntax|read|refchan|regexp|registry|regsub|rename|scan|seek|set|socket|source|split|string|subst|tcl(?:_endOfWord|_findLibrary|startOf(?:Next|Previous)Word|test|vars|wordBreak(?:After|Before))|tell|time|tm|trace|unknown|unload|unset|update|uplevel|vwait
Line truncated
|
||||
- a positive number
|
||||
- less than the value passed to \`max\` (or ${OE} if no \`max\` prop is set)
|
||||
- \`null\` or \`undefined\` if the progress is indeterminate.
|
||||
|
||||
Defaulting to \`null\`.`}var JG=VC,eH=YC;function tH({className:e,value:t,...r}){return g.jsx(JG,{"data-slot":"progress",className:me("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",e),...r,children:g.jsx(eH,{"data-slot":"progress-indicator",className:"bg-primary h-full w-full flex-1 transition-all",style:{transform:`translateX(-${100-(t||0)}%)`}})})}function Yl(e){const t=S.useRef({value:e,previous:e});return S.useMemo(()=>(t.current.value!==e&&(t.current.previous=t.current.value,t.current.value=e),t.current.previous),[e])}function Wl(e){const[t,r]=S.useState(void 0);return gt(()=>{if(e){r({width:e.offsetWidth,height:e.offsetHeight});const a=new ResizeObserver(o=>{if(!Array.isArray(o)||!o.length)return;const s=o[0];let u,c;if("borderBoxSize"in s){const p=s.borderBoxSize,f=Array.isArray(p)?p[0]:p;u=f.inlineSize,c=f.blockSize}else u=e.offsetWidth,c=e.offsetHeight;r({width:u,height:c})});return a.observe(e,{box:"border-box"}),()=>a.unobserve(e)}else r(void 0)},[e]),t}var Mc="Switch",[nH]=Ut(Mc),[rH,aH]=nH(Mc),KC=S.forwardRef((e,t)=>{const{__scopeSwitch:r,name:a,checked:o,defaultChecked:s,required:u,disabled:c,value:p="on",onCheckedChange:f,form:h,...m}=e,[v,E]=S.useState(null),x=Ne(t,N=>E(N)),w=S.useRef(!1),k=v?h||!!v.closest("form"):!0,[A,_]=tn({prop:o,defaultProp:s??!1,onChange:f,caller:Mc});return g.jsxs(rH,{scope:r,checked:A,disabled:c,children:[g.jsx(be.button,{type:"button",role:"switch","aria-checked":A,"aria-required":u,"data-state":JC(A),"data-disabled":c?"":void 0,disabled:c,value:p,...m,ref:x,onClick:ye(e.onClick,N=>{_(D=>!D),k&&(w.current=N.isPropagationStopped(),w.current||N.stopPropagation())})}),k&&g.jsx(QC,{control:v,bubbles:!w.current,name:a,value:p,checked:A,required:u,disabled:c,form:h,style:{transform:"translateX(-100%)"}})]})});KC.displayName=Mc;var XC="SwitchThumb",ZC=S.forwardRef((e,t)=>{const{__scopeSwitch:r,...a}=e,o=aH(XC,r);return g.jsx(be.span,{"data-state":JC(o.checked),"data-disabled":o.disabled?"":void 0,...a,ref:t})});ZC.displayName=XC;var iH="SwitchBubbleInput",QC=S.forwardRef(({__scopeSwitch:e,control:t,checked:r,bubbles:a=!0,...o},s)=>{const u=S.useRef(null),c=Ne(u,s),p=Yl(r),f=Wl(t);return S.useEffect(()=>{const h=u.current;if(!h)return;const m=window.HTMLInputElement.prototype,E=Object.getOwnPropertyDescriptor(m,"checked").set;if(p!==r&&E){const x=new Event("click",{bubbles:a});E.call(h,r),h.dispatchEvent(x)}},[p,r,a]),g.jsx("input",{type:"checkbox","aria-hidden":!0,defaultChecked:r,...o,tabIndex:-1,ref:c,style:{...o.style,...f,position:"absolute",pointerEvents:"none",opacity:0,margin:0}})});QC.displayName=iH;function JC(e){return e?"checked":"unchecked"}var oH=KC,lH=ZC;function sH({className:e,size:t="default",...r}){return g.jsx(oH,{"data-slot":"switch","data-size":t,className:me("peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6",e),...r,children:g.jsx(lH,{"data-slot":"switch-thumb",className:me("bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")})})}var jc="Checkbox",[uH]=Ut(jc),[cH,LE]=uH(jc);function dH(e){const{__scopeCheckbox:t,checked:r,children:a,defaultChecked:o,disabled:s,form:u,name:c,onCheckedChange:p,required:f,value:h="on",internal_do_not_use_render:m}=e,[v,E]=tn({prop:r,defaultProp:o??!1,onChange:p,caller:jc}),[x,w]=S.useState(null),[k,A]=S.useState(null),_=S.useRef(!1),N=x?!!u||!!x.closest("form"):!0,D={checked:v,disabled:s,setChecked:E,control:x,setControl:w,name:c,form:u,value:h,hasConsumerStoppedPropagationRef:_,required:f,defaultChecked:sa(o)?!1:o,isFormControl:N,bubbleInput:k,setBubbleInput:A};return g.jsx(cH,{scope:t,...D,children:pH(m)?m(D):a})}var e0="CheckboxTrigger",t0=S.forwardRef(({__scopeCheckbox:e,onKeyDown:t,onClick:r,...a},o)=>{const{control:s,value:u,disabled:c,checked:p,required:f,setControl:h,setChecked:m,hasConsumerStoppedPropagationRef:v,isFormControl:E,bubbleInput:x}=LE(e0,e),w=Ne(o,h),k=S.useRef(p);return S.useEffect(()=>{const A=s?.form;if(A){const _=()=>m(k.current);return A.addEventListener("reset",_),()=>A.removeEventListener("reset",_)}},[s,m]),g.jsx(be.button,{type:"button",role:"checkbox","aria-checked":sa(p)?"mixed":p,"aria-required":f,"data-state":l0(p),"data-disabled":c?"":void 0,disabled:c,value:u,...a,ref:w,onKeyDown:ye(t,A=>{A.key==="Enter"&&A.preventDefault()}),onClick:ye(r,A=>{m(_=>sa(_)?!0:!_),x&&E&&(v.current=A.isPropagatiLine truncated
|
||||
`);for(;t.length&&!t[0].trim();)t.shift();for(;t.length&&!t[t.length-1].trim();)t.pop();if(!t.length)return"";const r=Math.min(...t.filter(a=>a.trim().length>0).map(a=>a.match(/^ */)?.[0].length??0));return r===0?t.join(`
|
||||
`):t.map(a=>a.slice(r)).join(`
|
||||
`)}function wV({language:e,children:t}){const[r,a]=S.useState(!1),o=S.useCallback(async()=>{await navigator.clipboard.writeText(t),a(!0),setTimeout(()=>a(!1),2e3)},[t]);return g.jsxs("div",{className:"relative group/code my-3 rounded-lg overflow-hidden",children:[g.jsxs("div",{className:"flex items-center justify-between px-4 py-1.5 bg-[#1e1e2e] text-xs text-gray-400",children:[g.jsx("span",{children:e||"code"}),g.jsx("button",{onClick:o,className:"flex items-center gap-1 hover:text-white transition-colors",children:r?"✓ Copied":"Copy"})]}),g.jsx(KN,{style:yP,language:e||"text",PreTag:"div",customStyle:{margin:0,borderRadius:0,fontSize:"13px"},children:t})]})}function TV(e){const t=e.split(`
|
||||
`);let r=!1,a=!1,o="",s=0;for(let u=0;u<t.length;u++){const d=t[u];if(/^[ \t]*```/.test(d)){r=!r;continue}if(r)continue;const p=d.match(/^([ \t]*)(\d+)\.[ \t]/);if(p){const f=p[1];!a||f!==o?(a=!0,o=f,s=1):s++,t[u]=d.replace(/^([ \t]*)\d+\./,`${f}${s}.`)}else d.trim()===""&&a||a&&/^[ \t]+\S/.test(d)||a&&(a=!1,s=0)}return t.join(`
|
||||
`)}function vO({content:e}){const t=TV(e);return g.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none prose-p:leading-relaxed prose-pre:p-0 prose-pre:bg-transparent",children:g.jsx(uN,{remarkPlugins:[LN],components:{ul({children:r,ordered:a,start:o}){if(a){const s=o!=null?Number(o):1;return g.jsx("ol",{start:s,style:{listStyleType:"decimal",paddingLeft:"1.5rem",margin:"0.5rem 0"},children:r})}return g.jsx("ul",{style:{listStyleType:"disc",paddingLeft:"1.5rem",margin:"0.5rem 0"},children:r})},li({children:r}){return g.jsx("li",{style:{display:"list-item",marginBottom:"0.25rem"},children:r})},code({className:r,children:a,...o}){const s=/language-(\w+)/.exec(r||""),u=xV(String(a));return!s&&!u.includes(`
|
||||
`)?g.jsx("code",{className:"px-1.5 py-0.5 rounded bg-muted text-sm font-mono",...o,children:a}):g.jsx(wV,{language:s?.[1],children:u})},a({href:r,children:a}){return g.jsx("a",{href:r,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:a})},table({children:r}){return g.jsx("div",{className:"overflow-x-auto my-3",children:g.jsx("table",{className:"min-w-full text-sm",children:r})})}},children:t})})}function AV({message:e,sessionId:t}){return e.role==="user"?g.jsx("div",{className:"flex justify-end",children:g.jsxs("div",{className:"flex gap-3 max-w-[80%]",children:[g.jsx("div",{className:"rounded-2xl px-4 py-2.5 bg-primary text-primary-foreground",children:g.jsx("p",{className:"text-sm whitespace-pre-wrap",children:e.content})}),g.jsx(Zu,{role:"user"})]})}):g.jsxs("div",{className:"flex gap-3 group",children:[g.jsx(Zu,{role:"assistant"}),g.jsxs("div",{className:"flex-1 min-w-0",children:[e.toolCalls&&e.toolCalls.length>0&&g.jsx("div",{className:"mb-2 space-y-1",children:e.toolCalls.map((a,o)=>g.jsx(EO,{toolCall:a},o))}),g.jsx(vO,{content:e.content}),e.elements&&e.elements.length>0&&g.jsx("div",{className:"mt-3 space-y-3",children:e.elements.map((a,o)=>g.jsx(jv,{element:a,className:"w-full"},o))}),e.images&&e.images.length>0&&g.jsx("div",{className:"mt-2 flex flex-wrap gap-2",children:e.images.map((a,o)=>g.jsx("img",{src:a,alt:"",className:"max-w-full rounded-md max-h-64 object-contain"},o))}),e.actions&&e.actions.length>0&&g.jsx(mV,{actions:e.actions,messageId:e.id,sessionId:t}),g.jsxs("div",{className:"mt-1 flex items-center gap-1 relative",children:[g.jsx(vV,{text:e.content}),g.jsx(SV,{messageId:e.id})]})]})]})}function kV({onSend:e,onCancel:t,isStreaming:r=!1,placeholder:a="Type a message...",enableFileUpload:o=!1}){const[s,u]=S.useState(""),[d,p]=S.useState([]),f=S.useRef(null),h=S.useRef(null),b=S.useCallback(()=>{const A=s.trim();(A||d.length>0)&&!r&&(e(A,d.length>0?d:void 0),u(""),p([]),f.current&&(f.current.style.height="auto"))},[s,d,r,e]),v=S.useCallback(A=>{const _=Array.from(A.target.files||[]);p(R=>[...R,..._]),h.current&&(h.current.value="")},[]),E=S.useCallback(()=>{h.current?.click()},[]),w=S.useCallback(A=>{p(_=>_.filter((R,L)=>L!==A))},[]),T=S.useCallback(A=>{A.key==="Enter"&&!A.shiftKey&&(A.preventDefault(),b())},[b]),k=S.useCallback(()=>{const A=f.current;A&&(A.style.height="auto",A.style.height=`${Math.min(A.scrollHeight,200)}px`)},[]);return g.jsx("div",{className:"p-4 pb-6",children:g.jsxs("div",{className:"max-w-3xl mx-auto",children:[g.jsxs("div",{className:"rounded-2xl border bg-background/80 backdrop-blur-sm shadow-lg",children:[d.length>0&&g.jsx("div",{className:"flex flex-wrap gap-1 px-4 pt-3",children:d.map((A,_)=>g.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-muted rounded-lg text-xs",children:[g.jsx("span",{className:"truncate max-w-[100px]",children:A.name}),g.jsx("button",{type:"button",onClick:()=>w(_),className:"hover:text-destructive",children:"×"})]},`${A.name}-${_}`))}),g.jsxs("div",{className:"flex items-end gap-2 p-2",children:[o&&g.jsxs(g.Fragment,{children:[g.jsx("input",{ref:h,type:"file",multiple:!0,onChange:v,className:"hidden",accept:"image/*,audio/*,video/*,.pdf,.doc,.docx,.txt,.csv,.json"}),g.jsx("button",{type:"button",onClick:E,className:"p-2 rounded-xl hover:bg-accent text-muted-foreground transition-colors",title:"Attach file",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:g.jsx("path",{d:"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"})})})]}),g.jsx("textarea",{ref:f,value:s,onChange:A=>u(A.target.value),onKeyDown:T,onInput:k,placeholder:a,disabled:r,rows:1,className:"flex-1 resize-none bg-transparent px-3 py-2.5 text-sm focus:outline-none disabled:opacity-50 placeholder:text-muted-foreground/60",style:{maxHeight:"200px"}}),r?g.jsx("button",{type:"button",onClick:t,className:"p-2 rounded-xl bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors",title:"Stop",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"currentColor",children:g.jsx("rect",{x:"6",y:"6",width:"12",height:"12",rx:"2"})})}):g.jsx("button",{type:"button",onClick:b,disabled:!s.trim(),className:"p-2 rounded-xl bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",title:"Send",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:g.jsx("path",{d:"M5 12h14M12 5l7 7-7 7"})})})]})]}),g.jsx("p",{className:"text-[10px] text-center text-muted-foreground/50 mt-2",children:"Press Enter to send, Shift+Enter for new line"
Line truncated
|
||||
`),b=S.useCallback(async()=>{!r.trim()||s||p(r)},[r,s,p]),v=S.useCallback(()=>{a("")},[]);return g.jsxs("div",{className:"min-h-screen bg-background text-foreground flex flex-col",children:[g.jsxs("header",{className:"border-b px-6 py-4 flex items-center justify-between",children:[g.jsx("h1",{className:"text-xl font-semibold",children:t||"Playground"}),g.jsx("div",{className:"flex items-center gap-2",children:g.jsx("button",{onClick:v,className:"px-3 py-1.5 text-sm rounded-md border hover:bg-accent",children:"Clear"})})]}),g.jsxs("div",{className:"flex-1 flex",children:[g.jsxs("div",{className:"flex-1 flex flex-col border-r",children:[g.jsx("div",{className:"px-4 py-2 border-b bg-muted/30",children:g.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"Input"})}),g.jsx("div",{className:"flex-1 p-4",children:g.jsx("textarea",{value:r,onChange:E=>a(E.target.value),placeholder:"Enter your prompt here...",className:"w-full h-full resize-none bg-transparent border rounded-md p-3 focus:outline-none focus:ring-2 focus:ring-primary"})}),g.jsx("div",{className:"px-4 py-3 border-t flex justify-end gap-2",children:s?g.jsx("button",{onClick:f,className:"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90",children:"Stop"}):g.jsx("button",{onClick:b,disabled:!r.trim(),className:"px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",children:"Submit"})})]}),g.jsxs("div",{className:"flex-1 flex flex-col",children:[g.jsxs("div",{className:"px-4 py-2 border-b bg-muted/30 flex items-center justify-between",children:[g.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"Output"}),s&&g.jsx("span",{className:"text-xs text-muted-foreground animate-pulse",children:"Processing..."})]}),g.jsx("div",{className:"flex-1 p-4 overflow-auto",children:h?g.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none whitespace-pre-wrap",children:h}):g.jsx("div",{className:"h-full flex items-center justify-center text-muted-foreground text-sm",children:"Output will appear here"})})]})]}),e?.features&&g.jsxs("div",{className:"border-t px-6 py-3 bg-muted/20 flex items-center gap-4 text-sm text-muted-foreground",children:[e.features.streaming&&g.jsx("span",{children:"✓ Streaming"}),e.features.reasoning&&g.jsx("span",{children:"✓ Reasoning"}),e.features.tools&&g.jsx("span",{children:"✓ Tools"})]})]})}function _u({enabled:e}){return e?g.jsx("a",{href:"#main-content",className:"sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 z-50 bg-primary text-primary-foreground px-4 py-2 rounded-md text-sm font-medium",children:"Skip to main content"}):null}function jV(){const[e,t]=S.useState({}),[r,a]=S.useState({}),[o,s]=S.useState({}),[u,d]=S.useState(!0),[p,f]=S.useState(null),[h,b]=S.useState(null),[v,E]=S.useState("");S.useEffect(()=>{async function L(){try{const[j,D,z]=await Promise.all([fetch("/ui-config.json"),fetch("/docs-nav.json"),fetch("/route-manifest.json")]);if(!j.ok||!D.ok||!z.ok)throw new Error("Failed to load manifests");const F=await j.json(),V=await D.json();t(F),a(V),s(await z.json());const M=F.site?.theme;K1(M?.preset||"zinc",M?.darkMode!==!1,M?.radius||"md");const H=window.location.pathname;if(H&&H!=="/"){const K=Z=>{for(const te of Z){if(te.path===H||te.path===H.replace(/^\//,""))return te;if(te.children){const Q=K(te.children);if(Q)return Q}}return null};if(V.items){for(const Z of V.items)if(Z.children){const te=K(Z.children);if(te){b(te),E(te.path||te.title);break}}}}}catch(j){f(j instanceof Error?j.message:"Unknown error")}finally{d(!1)}}L()},[]),S.useEffect(()=>{const L=()=>{const j=window.location.pathname,D=F=>{for(const V of F){if(V.path===j||V.path===j.replace(/^\//,""))return V;if(V.children){const M=D(V.children);if(M)return M}}return null};let z=null;if(r.items){for(const F of r.items)if(F.children&&(z=D(F.children),z))break}if(z){b(z),E(z.path||z.title);const F=e.site?.title||"Documentation",V=e.seo?.titleTemplate||"%s | %s";if(V.includes("%s")){let M=V.replace("%s",z.title);M.includes("%s")&&(M=M.replace("%s",F)),document.title=M}else document.title=`${z.title} | ${F}`}else j==="/"&&(b(null),E(""),document.title=e.site?.title||"Documentation")};return window.addEventListener("popstate",L),()=>window.removeEventListener("popstate",L)},[r,e]);const w=(L,j,D)=>{const z=e.seo?.titleTemplate||"%s | %s",F=e.site?.title||"Documentation";if(z.includes("%s")){let Z=z.replace("%s",L);Z.includes("%s")&&(Z=Z.replace("%s",F)),document.title=Z}else document.title=`${L} | ${F}`;let V=document.querySelector('link[rel="canonical"]');V||(V=document.createElement("link"),V.rel="canonical",document.head.appendChild(V)),V.href=window.location.origin+j;let M=document.querySelector('meta[name="description"]');M||(M=document.createElement("meta"),M.name="description",document.head.appendChild(M)),M.content=`${L} - ${e.site?.description||""}`;let H=document.querySele
Line truncated
|
||||
`)}function Cq({language:e,children:t}){const[r,a]=S.useState(!1),o=S.useCallback(async()=>{await navigator.clipboard.writeText(t),a(!0),setTimeout(()=>a(!1),2e3)},[t]);return g.jsxs("div",{className:"relative group/code my-3 rounded-lg overflow-hidden",children:[g.jsxs("div",{className:"flex items-center justify-between px-4 py-1.5 bg-[#1e1e2e] text-xs text-gray-400",children:[g.jsx("span",{children:e||"code"}),g.jsx("button",{onClick:o,className:"flex items-center gap-1 hover:text-white transition-colors",children:r?"✓ Copied":"Copy"})]}),g.jsx(rC,{style:N4,language:e||"text",PreTag:"div",customStyle:{margin:0,borderRadius:0,fontSize:"13px"},children:t})]})}function Iq(e){const t=e.split(`
|
||||
`);let r=!1,a=!1,o="",s=0;for(let u=0;u<t.length;u++){const c=t[u];if(/^[ \t]*```/.test(c)){r=!r;continue}if(r)continue;const p=c.match(/^([ \t]*)(\d+)\.[ \t]/);if(p){const f=p[1];!a||f!==o?(a=!0,o=f,s=1):s++,t[u]=c.replace(/^([ \t]*)\d+\./,`${f}${s}.`)}else c.trim()===""&&a||a&&/^[ \t]+\S/.test(c)||a&&(a=!1,s=0)}return t.join(`
|
||||
`)}function nO({content:e}){const t=Iq(e);return g.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none prose-p:leading-relaxed prose-pre:p-0 prose-pre:bg-transparent",children:g.jsx(mR,{remarkPlugins:[BR],components:{ul({children:r,ordered:a,start:o}){if(a){const s=o!=null?Number(o):1;return g.jsx("ol",{start:s,style:{listStyleType:"decimal",paddingLeft:"1.5rem",margin:"0.5rem 0"},children:r})}return g.jsx("ul",{style:{listStyleType:"disc",paddingLeft:"1.5rem",margin:"0.5rem 0"},children:r})},li({children:r}){return g.jsx("li",{style:{display:"list-item",marginBottom:"0.25rem"},children:r})},code({className:r,children:a,...o}){const s=/language-(\w+)/.exec(r||""),u=Rq(String(a));return!s&&!u.includes(`
|
||||
`)?g.jsx("code",{className:"px-1.5 py-0.5 rounded bg-muted text-sm font-mono",...o,children:a}):g.jsx(Cq,{language:s?.[1],children:u})},a({href:r,children:a}){return g.jsx("a",{href:r,target:"_blank",rel:"noopener noreferrer",className:"text-primary hover:underline",children:a})},table({children:r}){return g.jsx("div",{className:"overflow-x-auto my-3",children:g.jsx("table",{className:"min-w-full text-sm",children:r})})}},children:t})})}function Oq({message:e,sessionId:t}){return e.role==="user"?g.jsx("div",{className:"flex justify-end",children:g.jsxs("div",{className:"flex gap-3 max-w-[80%]",children:[g.jsx("div",{className:"rounded-2xl px-4 py-2.5 bg-primary text-primary-foreground",children:g.jsx("p",{className:"text-sm whitespace-pre-wrap",children:e.content})}),g.jsx(rc,{role:"user"})]})}):g.jsxs("div",{className:"flex gap-3 group",children:[g.jsx(rc,{role:"assistant"}),g.jsxs("div",{className:"flex-1 min-w-0",children:[e.toolCalls&&e.toolCalls.length>0&&g.jsx("div",{className:"mb-2 space-y-1",children:e.toolCalls.map((a,o)=>g.jsx(tO,{toolCall:a},o))}),g.jsx(nO,{content:e.content}),e.elements&&e.elements.length>0&&g.jsx("div",{className:"mt-3 space-y-3",children:e.elements.map((a,o)=>g.jsx(QE,{element:a,className:"w-full"},o))}),e.images&&e.images.length>0&&g.jsx("div",{className:"mt-2 flex flex-wrap gap-2",children:e.images.map((a,o)=>g.jsx("img",{src:a,alt:"",className:"max-w-full rounded-md max-h-64 object-contain"},o))}),e.actions&&e.actions.length>0&&g.jsx(xq,{actions:e.actions,messageId:e.id,sessionId:t}),g.jsxs("div",{className:"mt-1 flex items-center gap-1 relative",children:[g.jsx(_q,{text:e.content}),g.jsx(Nq,{messageId:e.id})]})]})]})}function Lq({onSend:e,onCancel:t,isStreaming:r=!1,placeholder:a="Type a message...",enableFileUpload:o=!1}){const[s,u]=S.useState(""),[c,p]=S.useState([]),f=S.useRef(null),h=S.useRef(null),m=S.useCallback(()=>{const A=s.trim();(A||c.length>0)&&!r&&(e(A,c.length>0?c:void 0),u(""),p([]),f.current&&(f.current.style.height="auto"))},[s,c,r,e]),v=S.useCallback(A=>{const _=Array.from(A.target.files||[]);p(N=>[...N,..._]),h.current&&(h.current.value="")},[]),E=S.useCallback(()=>{h.current?.click()},[]),x=S.useCallback(A=>{p(_=>_.filter((N,D)=>D!==A))},[]),w=S.useCallback(A=>{A.key==="Enter"&&!A.shiftKey&&(A.preventDefault(),m())},[m]),k=S.useCallback(()=>{const A=f.current;A&&(A.style.height="auto",A.style.height=`${Math.min(A.scrollHeight,200)}px`)},[]);return g.jsx("div",{className:"p-4 pb-6",children:g.jsxs("div",{className:"max-w-3xl mx-auto",children:[g.jsxs("div",{className:"rounded-2xl border bg-background/80 backdrop-blur-sm shadow-lg",children:[c.length>0&&g.jsx("div",{className:"flex flex-wrap gap-1 px-4 pt-3",children:c.map((A,_)=>g.jsxs("span",{className:"inline-flex items-center gap-1 px-2 py-1 bg-muted rounded-lg text-xs",children:[g.jsx("span",{className:"truncate max-w-[100px]",children:A.name}),g.jsx("button",{type:"button",onClick:()=>x(_),className:"hover:text-destructive",children:"×"})]},`${A.name}-${_}`))}),g.jsxs("div",{className:"flex items-end gap-2 p-2",children:[o&&g.jsxs(g.Fragment,{children:[g.jsx("input",{ref:h,type:"file",multiple:!0,onChange:v,className:"hidden",accept:"image/*,audio/*,video/*,.pdf,.doc,.docx,.txt,.csv,.json"}),g.jsx("button",{type:"button",onClick:E,className:"p-2 rounded-xl hover:bg-accent text-muted-foreground transition-colors",title:"Attach file",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:g.jsx("path",{d:"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"})})})]}),g.jsx("textarea",{ref:f,value:s,onChange:A=>u(A.target.value),onKeyDown:w,onInput:k,placeholder:a,disabled:r,rows:1,className:"flex-1 resize-none bg-transparent px-3 py-2.5 text-sm focus:outline-none disabled:opacity-50 placeholder:text-muted-foreground/60",style:{maxHeight:"200px"}}),r?g.jsx("button",{type:"button",onClick:t,className:"p-2 rounded-xl bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors",title:"Stop",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"currentColor",children:g.jsx("rect",{x:"6",y:"6",width:"12",height:"12",rx:"2"})})}):g.jsx("button",{type:"button",onClick:m,disabled:!s.trim(),className:"p-2 rounded-xl bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",title:"Send",children:g.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:g.jsx("path",{d:"M5 12h14M12 5l7 7-7 7"})})})]})]}),g.jsx("p",{className:"text-[10px] text-center text-muted-foreground/50 mt-2",children:"Press Enter to send, Shift+Enter for new line"
Line truncated
|
||||
`),m=S.useCallback(async()=>{!r.trim()||s||p(r)},[r,s,p]),v=S.useCallback(()=>{a("")},[]);return g.jsxs("div",{className:"min-h-screen bg-background text-foreground flex flex-col",children:[g.jsxs("header",{className:"border-b px-6 py-4 flex items-center justify-between",children:[g.jsx("h1",{className:"text-xl font-semibold",children:t||"Playground"}),g.jsx("div",{className:"flex items-center gap-2",children:g.jsx("button",{onClick:v,className:"px-3 py-1.5 text-sm rounded-md border hover:bg-accent",children:"Clear"})})]}),g.jsxs("div",{className:"flex-1 flex",children:[g.jsxs("div",{className:"flex-1 flex flex-col border-r",children:[g.jsx("div",{className:"px-4 py-2 border-b bg-muted/30",children:g.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"Input"})}),g.jsx("div",{className:"flex-1 p-4",children:g.jsx("textarea",{value:r,onChange:E=>a(E.target.value),placeholder:"Enter your prompt here...",className:"w-full h-full resize-none bg-transparent border rounded-md p-3 focus:outline-none focus:ring-2 focus:ring-primary"})}),g.jsx("div",{className:"px-4 py-3 border-t flex justify-end gap-2",children:s?g.jsx("button",{onClick:f,className:"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90",children:"Stop"}):g.jsx("button",{onClick:m,disabled:!r.trim(),className:"px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",children:"Submit"})})]}),g.jsxs("div",{className:"flex-1 flex flex-col",children:[g.jsxs("div",{className:"px-4 py-2 border-b bg-muted/30 flex items-center justify-between",children:[g.jsx("span",{className:"text-sm font-medium text-muted-foreground",children:"Output"}),s&&g.jsx("span",{className:"text-xs text-muted-foreground animate-pulse",children:"Processing..."})]}),g.jsx("div",{className:"flex-1 p-4 overflow-auto",children:h?g.jsx("div",{className:"prose prose-sm dark:prose-invert max-w-none whitespace-pre-wrap",children:h}):g.jsx("div",{className:"h-full flex items-center justify-center text-muted-foreground text-sm",children:"Output will appear here"})})]})]}),e?.features&&g.jsxs("div",{className:"border-t px-6 py-3 bg-muted/20 flex items-center gap-4 text-sm text-muted-foreground",children:[e.features.streaming&&g.jsx("span",{children:"✓ Streaming"}),e.features.reasoning&&g.jsx("span",{children:"✓ Reasoning"}),e.features.tools&&g.jsx("span",{children:"✓ Tools"})]})]})}function v9(e,t){const r=e.replace(/^\/+|\/+$/g,""),o=t.replace(/^\/+|\/+$/g,"").replace(/\*\*/g,"{{GLOB}}").replace(/\*/g,"[^/]+").replace(/\{\{GLOB\}\}/g,".*");return new RegExp(`^${o}$`).test(r)}function E9(e,t,r){const o=[...t.routes??[]].sort((s,u)=>(u.priority??0)-(s.priority??0));for(const s of o){if(!v9(e,s.pattern))continue;const u=r[s.template];return u?{template:s.template,layout:u.layout??"ThreeColumnLayout",slots:u.slots??{},slotOverrides:s.slotOverrides}:(console.warn(`Template "${s.template}" not found`),null)}return null}function S9(e,t){if(t)return t.slotOverrides&&e in t.slotOverrides?t.slotOverrides[e]:t.slots[e]}function x9(e){return S9("right",e)!==null}function Lu({enabled:e}){return e?g.jsx("a",{href:"#main-content",className:"sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 z-50 bg-primary text-primary-foreground px-4 py-2 rounded-md text-sm font-medium",children:"Skip to main content"}):null}function w9(){const[e,t]=S.useState({}),[r,a]=S.useState({}),[o,s]=S.useState({}),[u,c]=S.useState(!0),[p,f]=S.useState(null),[h,m]=S.useState(null),[v,E]=S.useState(""),[x,w]=S.useState(()=>window.location.pathname.replace(/\/$/,"")||"/"),[k,A]=S.useState(!1),_=(M,H)=>{const Q=H.replace(/\/$/,"")||"/",de=I=>{for(const U of I){const B=(U.path??"").replace(/\/$/,"");if(B===Q||B===Q.replace(/^\//,""))return U;if(U.children){const C=de(U.children);if(C)return C}}return null};return M.items?de(M.items):null},N=(M,H,Q,de)=>{const I=de??e,U=I.seo?.titleTemplate||"%s | %s",B=I.site?.title||"Documentation";if(U.includes("%s")){let ie=U.replace("%s",M);ie.includes("%s")&&(ie=ie.replace("%s",B)),document.title=ie}else document.title=`${M} | ${B}`;let C=document.querySelector('link[rel="canonical"]');C||(C=document.createElement("link"),C.rel="canonical",document.head.appendChild(C)),C.href=window.location.origin+H;let J=document.querySelector('meta[name="description"]');J||(J=document.createElement("meta"),J.name="description",document.head.appendChild(J)),J.content=`${M} - ${I.site?.description||""}`;let ae=document.querySelector('meta[property="og:title"]');ae||(ae=document.createElement("meta"),ae.setAttribute("property","og:title"),document.head.appendChild(ae)),ae.content=M;let ue=document.querySelector('meta[property="og:url"]');if(ue||(ue=document.createElement("meta"),ue.setAttribute("property","og:url"),document.head.appendChild(ue)),ue.content=window.location.origin+H,I.seo?.twitter&&Object.entries(I.seo.twitter).forEach(([ie,he])=>{let fe=document.querySel
Line truncated
|
||||
@@ -475,6 +475,7 @@
|
||||
}
|
||||
|
||||
/* ── Light Theme (Gap 19) ─────────────────────────────────────────*/
|
||||
:root:not(.dark) .chat-container,
|
||||
[data-theme="light"] .chat-container,
|
||||
.chat-container.light {
|
||||
--chat-bg: #fff;
|
||||
|
||||
@@ -82,6 +82,7 @@ function addCopyButtons(root) {
|
||||
if (pre.dataset.copyProcessed) return;
|
||||
if (pre.closest('.mermaid-diagram')) return;
|
||||
if (pre.querySelector('.mermaid')) return;
|
||||
if (pre.querySelector('code.language-mermaid')) return;
|
||||
|
||||
pre.dataset.copyProcessed = 'true';
|
||||
|
||||
|
||||
@@ -122,6 +122,8 @@ async function loadContent(root) {
|
||||
|
||||
// Update the "On This Page" ToC if toc plugin is active
|
||||
updateTocSidebar(article);
|
||||
|
||||
window.dispatchEvent(new CustomEvent('aiui:content-loaded', { detail: { root: article } }));
|
||||
|
||||
console.debug('[AIUI:content-loader] Loaded content from', mdPath);
|
||||
} catch (err) {
|
||||
@@ -163,15 +165,73 @@ function updateTocSidebar(article) {
|
||||
|
||||
/* ───────── Markdown → HTML Converter ───────── */
|
||||
|
||||
function convertMkdocsTabs(md) {
|
||||
const lines = md.split('\n');
|
||||
const out = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < lines.length) {
|
||||
const tabMatch = lines[i].match(/^===\s+"([^"]+)"\s*$/);
|
||||
if (!tabMatch) {
|
||||
out.push(lines[i]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const title = tabMatch[1];
|
||||
i++;
|
||||
while (i < lines.length && lines[i].trim() === '') i++;
|
||||
|
||||
const block = [];
|
||||
while (i < lines.length) {
|
||||
if (/^===\s+"[^"]+"\s*$/.test(lines[i])) break;
|
||||
if (lines[i].trim() === '' && i + 1 < lines.length && /^===\s+"[^"]+"\s*$/.test(lines[i + 1])) break;
|
||||
|
||||
if (lines[i].startsWith(' ') || lines[i].trimStart().startsWith('```')) {
|
||||
block.push(lines[i].replace(/^ /, ''));
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.length > 0 && lines[i].trim() === '') {
|
||||
block.push('');
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out.push(`### \u200Btab:${title}`);
|
||||
out.push('');
|
||||
out.push(...block);
|
||||
out.push('');
|
||||
}
|
||||
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
function markdownToHtml(md) {
|
||||
// Clean up MkDocs-specific syntax
|
||||
// Clean up MkDocs-specific syntax (mirrors src/frontend/src/markdown/mkdocsPreprocess.ts)
|
||||
let html = md
|
||||
.replace(/<div[^>]*markdown[^>]*>/gi, '')
|
||||
.replace(/<\/div>/gi, '')
|
||||
.replace(/===\s+"([^"]+)"/g, '**$1**')
|
||||
.replace(/<div[^>]*markdown[^>]*>\s*/gi, '')
|
||||
.replace(/<\/div>\s*/gi, '');
|
||||
|
||||
const iconMap = {
|
||||
':material-file-document:': '📄',
|
||||
':material-puzzle:': '🧩',
|
||||
':material-palette:': '🎨',
|
||||
':material-rocket-launch:': '🚀',
|
||||
};
|
||||
for (const [shortcode, emoji] of Object.entries(iconMap)) {
|
||||
html = html.replaceAll(shortcode, emoji);
|
||||
}
|
||||
html = html
|
||||
.replace(/:material-[\w-]+:/g, '•')
|
||||
.replace(/:octicons-[\w-]+(?:-\d+)?:/g, '•');
|
||||
|
||||
html = convertMkdocsTabs(html);
|
||||
|
||||
const lines = html.split('\n');
|
||||
const result = [];
|
||||
let inCodeBlock = false, codeContent = '', codeLang = '';
|
||||
@@ -207,7 +267,7 @@ function markdownToHtml(md) {
|
||||
// Code blocks
|
||||
if (line.trimStart().startsWith('```')) {
|
||||
if (inCodeBlock) {
|
||||
result.push(`<pre class="bg-gray-900 rounded-lg p-4 my-4 overflow-x-auto"><code class="language-${codeLang}">${escapeHtml(codeContent.trim())}</code></pre>`);
|
||||
result.push(`<pre class="bg-muted text-foreground border border-border rounded-lg p-4 my-4 overflow-x-auto"><code class="language-${codeLang}">${escapeHtml(codeContent.trim())}</code></pre>`);
|
||||
inCodeBlock = false; codeContent = '';
|
||||
} else {
|
||||
flushList(); flushTable(); inCodeBlock = true;
|
||||
@@ -292,8 +352,8 @@ function inlineMarkdown(text) {
|
||||
return text
|
||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||
.replace(/`([^`]+)`/g, '<code class="bg-gray-800 px-1.5 py-0.5 rounded text-sm">$1</code>')
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="text-blue-400 hover:text-blue-300 underline">$1</a>');
|
||||
.replace(/`([^`]+)`/g, '<code class="bg-primary/10 text-primary px-1.5 py-0.5 rounded text-sm font-mono">$1</code>')
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="text-primary hover:underline">$1</a>');
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
@@ -376,6 +436,8 @@ async function navigateToContent(targetPath) {
|
||||
// Update the "On This Page" ToC
|
||||
updateTocSidebar(article);
|
||||
|
||||
window.dispatchEvent(new CustomEvent('aiui:content-loaded', { detail: { root: article } }));
|
||||
|
||||
// Remove anti-flicker CSS (critical: ensures content is visible)
|
||||
const af = document.getElementById('aiui-anti-flicker');
|
||||
if (af) af.remove();
|
||||
|
||||
@@ -1364,6 +1364,46 @@ async function consumeToken(token) {
|
||||
}
|
||||
}
|
||||
|
||||
async function probeGatewayReachable(urlString) {
|
||||
let base;
|
||||
try {
|
||||
base = new URL(urlString);
|
||||
} catch (e) {
|
||||
return { ok: false, reason: 'invalid_url' };
|
||||
}
|
||||
if (base.protocol !== 'http:' && base.protocol !== 'https:') {
|
||||
return { ok: false, reason: 'invalid_url' };
|
||||
}
|
||||
|
||||
const probe = new URL('/api/auth/status', base);
|
||||
const sameOrigin = base.origin === window.location.origin;
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||
|
||||
try {
|
||||
const res = await fetch(probe.toString(), {
|
||||
method: 'GET',
|
||||
credentials: 'omit',
|
||||
signal: controller.signal,
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
if (res.type === 'opaque' || res.status === 0) {
|
||||
return { ok: false, reason: 'opaque' };
|
||||
}
|
||||
return { ok: true, status: res.status };
|
||||
} catch (e) {
|
||||
clearTimeout(timeout);
|
||||
if (e.name === 'AbortError') {
|
||||
return { ok: false, reason: 'timeout' };
|
||||
}
|
||||
// A rejected fetch on a cross-origin URL is ambiguous: the gateway may be
|
||||
// up but blocking CORS, or genuinely unreachable. The browser cannot tell
|
||||
// these apart, so surface the "Continue anyway" opt-in rather than a
|
||||
// definitive unreachable error. Same-origin rejections are genuine.
|
||||
return { ok: false, reason: sameOrigin ? 'network' : 'opaque' };
|
||||
}
|
||||
}
|
||||
|
||||
async function maybeShowOnboarding() {
|
||||
let params;
|
||||
try {
|
||||
@@ -1449,18 +1489,36 @@ function renderOnboarding(status) {
|
||||
<button id="ob-continue" style="width:100%;padding:11px;border:none;border-radius:8px;background:var(--db-accent,#4f8cff);color:#fff;font-size:14px;font-weight:600;cursor:pointer">Continue</button>
|
||||
<button id="ob-standalone" style="width:100%;padding:8px;margin-top:10px;border:none;background:none;color:var(--db-text-muted,#9aa4b2);font-size:13px;cursor:pointer;text-decoration:underline">Use standalone mode</button>
|
||||
`);
|
||||
overlay.querySelector('#ob-continue').addEventListener('click', async () => {
|
||||
const continueBtn = overlay.querySelector('#ob-continue');
|
||||
continueBtn.addEventListener('click', async () => {
|
||||
const url = overlay.querySelector('#ob-url').value.trim();
|
||||
const errEl = overlay.querySelector('#ob-url-err');
|
||||
const override = overlay.querySelector('#ob-url-override');
|
||||
errEl.textContent = '';
|
||||
try {
|
||||
await fetch(url, { method: 'HEAD', mode: 'no-cors' });
|
||||
} catch (e) {
|
||||
errEl.textContent = `Cannot reach gateway at ${url}`;
|
||||
if (override && override.checked) {
|
||||
state.step = 2;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
state.step = 2;
|
||||
render();
|
||||
continueBtn.disabled = true;
|
||||
continueBtn.textContent = 'Checking…';
|
||||
const result = await probeGatewayReachable(url);
|
||||
continueBtn.disabled = false;
|
||||
continueBtn.textContent = 'Continue';
|
||||
if (result.ok) {
|
||||
state.step = 2;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
if (result.reason === 'invalid_url') {
|
||||
errEl.textContent = `Enter a valid http(s) URL`;
|
||||
} else if (result.reason === 'opaque') {
|
||||
errEl.innerHTML = `Cannot verify gateway from browser. <label style="cursor:pointer"><input id="ob-url-override" type="checkbox" style="vertical-align:middle"> Continue anyway</label>`;
|
||||
} else if (result.reason === 'timeout') {
|
||||
errEl.textContent = `Gateway did not respond at ${url}`;
|
||||
} else {
|
||||
errEl.textContent = `Cannot reach gateway at ${url}`;
|
||||
}
|
||||
});
|
||||
overlay.querySelector('#ob-standalone').addEventListener('click', () => {
|
||||
try { window.localStorage.setItem(ONBOARD_SKIP_KEY, '1'); } catch (e) { /* ignore */ }
|
||||
|
||||
@@ -300,7 +300,13 @@ let lastUrl = '';
|
||||
|
||||
export default {
|
||||
name: 'mermaid',
|
||||
async init() { await loadMermaidLib(); },
|
||||
async init() {
|
||||
await loadMermaidLib();
|
||||
window.addEventListener('aiui:content-loaded', (event) => {
|
||||
const root = event.detail?.root || document;
|
||||
renderMermaidBlocks(root);
|
||||
});
|
||||
},
|
||||
onContentChange(root) {
|
||||
const currentUrl = location.pathname + location.hash;
|
||||
|
||||
|
||||
@@ -1,74 +1,247 @@
|
||||
/**
|
||||
* AIUI MkDocs Compatibility Plugin
|
||||
*
|
||||
*
|
||||
* Strips or transforms MkDocs Material-specific syntax that renders
|
||||
* as raw text in standard markdown renderers:
|
||||
*
|
||||
* - :material-*: icon shortcodes → emoji or removes them
|
||||
* - <div class="grid cards" markdown> → removes wrapper
|
||||
* - === "Tab Title" → transforms to heading
|
||||
* - !!! note/warning → transforms to blockquote
|
||||
* - </div> visible tags → removes them
|
||||
* as raw text in standard markdown renderers.
|
||||
*/
|
||||
|
||||
const ICON_MAP = {
|
||||
':material-file-document:': '📄',
|
||||
':material-puzzle:': '🧩',
|
||||
':material-palette:': '🎨',
|
||||
':material-rocket-launch:': '🚀',
|
||||
':material-code-tags:': '💻',
|
||||
':material-cog:': '⚙️',
|
||||
':material-lightning-bolt:': '⚡',
|
||||
':material-shield:': '🛡️',
|
||||
':material-database:': '🗄️',
|
||||
':material-web:': '🌐',
|
||||
':material-book:': '📖',
|
||||
':material-star:': '⭐',
|
||||
':material-check:': '✅',
|
||||
':material-close:': '❌',
|
||||
':material-alert:': '⚠️',
|
||||
':material-information:': 'ℹ️',
|
||||
};
|
||||
|
||||
const TAB_HEADING_PREFIX = '\u200Btab:';
|
||||
|
||||
function replaceIcons(text) {
|
||||
let result = text;
|
||||
for (const [shortcode, emoji] of Object.entries(ICON_MAP)) {
|
||||
result = result.replaceAll(shortcode, emoji);
|
||||
}
|
||||
return result
|
||||
.replace(/:material-[\w-]+:/g, '•')
|
||||
.replace(/:octicons-[\w-]+(?:-\d+)?:/g, '•');
|
||||
}
|
||||
|
||||
function convertMkdocsTabs(md) {
|
||||
const lines = md.split('\n');
|
||||
const out = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < lines.length) {
|
||||
const tabMatch = lines[i].match(/^===\s+"([^"]+)"\s*$/);
|
||||
if (!tabMatch) {
|
||||
out.push(lines[i]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const title = tabMatch[1];
|
||||
i++;
|
||||
while (i < lines.length && lines[i].trim() === '') i++;
|
||||
|
||||
const block = [];
|
||||
while (i < lines.length) {
|
||||
if (/^===\s+"[^"]+"\s*$/.test(lines[i])) break;
|
||||
if (lines[i].trim() === '' && i + 1 < lines.length && /^===\s+"[^"]+"\s*$/.test(lines[i + 1])) break;
|
||||
|
||||
if (lines[i].startsWith(' ') || lines[i].trimStart().startsWith('```')) {
|
||||
block.push(lines[i].replace(/^ /, ''));
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.length > 0 && lines[i].trim() === '') {
|
||||
block.push('');
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out.push(`### ${TAB_HEADING_PREFIX}${title}`);
|
||||
out.push('');
|
||||
out.push(...block);
|
||||
out.push('');
|
||||
}
|
||||
|
||||
return out.join('\n');
|
||||
}
|
||||
|
||||
function isTabHeading(text) {
|
||||
return text.startsWith(TAB_HEADING_PREFIX) || text.startsWith('tab:');
|
||||
}
|
||||
|
||||
function tabTitle(text) {
|
||||
return text.replace(/^\u200Btab:/, '').replace(/^tab:/, '');
|
||||
}
|
||||
|
||||
function enhanceFeatureGrid(article) {
|
||||
for (const ul of article.querySelectorAll('ul')) {
|
||||
if (ul.classList.contains('aiui-feature-grid')) continue;
|
||||
|
||||
const firstLi = ul.querySelector('li');
|
||||
const looksLikeCards = Boolean(
|
||||
firstLi?.textContent?.includes('YAML-Driven')
|
||||
|| firstLi?.textContent?.includes('Component Slots')
|
||||
|| firstLi?.querySelector('strong'),
|
||||
);
|
||||
if (!looksLikeCards) continue;
|
||||
|
||||
ul.classList.add(
|
||||
'aiui-feature-grid',
|
||||
'grid',
|
||||
'grid-cols-1',
|
||||
'md:grid-cols-2',
|
||||
'gap-4',
|
||||
'my-6',
|
||||
'list-none',
|
||||
'pl-0',
|
||||
);
|
||||
|
||||
for (const li of ul.querySelectorAll('li')) {
|
||||
li.classList.add('border', 'border-border', 'rounded-lg', 'p-4', 'bg-card');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enhanceTabs(article) {
|
||||
if (article.querySelector('[data-aiui-tabs]')) return;
|
||||
|
||||
const tabHeadings = Array.from(article.querySelectorAll('h3')).filter((h3) =>
|
||||
isTabHeading(h3.textContent?.trim() ?? ''),
|
||||
);
|
||||
if (tabHeadings.length < 2) return;
|
||||
|
||||
const sections = tabHeadings.map((heading) => {
|
||||
const title = tabTitle(heading.textContent?.trim() ?? '');
|
||||
const nodes = [];
|
||||
let sibling = heading.nextSibling;
|
||||
|
||||
while (sibling) {
|
||||
if (sibling.nodeType === Node.ELEMENT_NODE) {
|
||||
const el = sibling;
|
||||
if (el.tagName === 'H3' && isTabHeading(el.textContent?.trim() ?? '')) break;
|
||||
if (['H1', 'H2'].includes(el.tagName)) break;
|
||||
}
|
||||
const next = sibling.nextSibling;
|
||||
nodes.push(sibling);
|
||||
sibling = next;
|
||||
}
|
||||
|
||||
return { heading, title, nodes };
|
||||
});
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'aiui-tabs my-6 border border-border rounded-lg overflow-hidden';
|
||||
wrapper.dataset.aiuiTabs = 'true';
|
||||
|
||||
const buttons = document.createElement('div');
|
||||
buttons.className = 'aiui-tab-buttons flex flex-wrap gap-0 border-b border-border bg-muted/30';
|
||||
wrapper.appendChild(buttons);
|
||||
|
||||
const panels = document.createElement('div');
|
||||
panels.className = 'aiui-tab-panels';
|
||||
wrapper.appendChild(panels);
|
||||
|
||||
sections.forEach((section, index) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = [
|
||||
'aiui-tab-button px-4 py-2 text-sm font-medium transition-colors',
|
||||
index === 0
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground',
|
||||
].join(' ');
|
||||
btn.textContent = section.title;
|
||||
btn.dataset.tabIndex = String(index);
|
||||
buttons.appendChild(btn);
|
||||
|
||||
const panel = document.createElement('div');
|
||||
panel.className = index === 0 ? 'aiui-tab-panel p-4 block' : 'aiui-tab-panel p-4 hidden';
|
||||
panel.dataset.tabPanel = String(index);
|
||||
for (const node of section.nodes) {
|
||||
panel.appendChild(node);
|
||||
}
|
||||
panels.appendChild(panel);
|
||||
});
|
||||
|
||||
article.insertBefore(wrapper, sections[0].heading);
|
||||
for (const section of sections) {
|
||||
section.heading.remove();
|
||||
}
|
||||
|
||||
buttons.addEventListener('click', (event) => {
|
||||
const target = event.target.closest('.aiui-tab-button');
|
||||
if (!target) return;
|
||||
const index = target.dataset.tabIndex ?? '0';
|
||||
|
||||
for (const btn of buttons.querySelectorAll('.aiui-tab-button')) {
|
||||
const active = btn.dataset.tabIndex === index;
|
||||
btn.classList.toggle('bg-background', active);
|
||||
btn.classList.toggle('text-foreground', active);
|
||||
btn.classList.toggle('border-b-2', active);
|
||||
btn.classList.toggle('border-primary', active);
|
||||
btn.classList.toggle('text-muted-foreground', !active);
|
||||
}
|
||||
|
||||
for (const panel of panels.querySelectorAll('.aiui-tab-panel')) {
|
||||
panel.classList.toggle('hidden', panel.dataset.tabPanel !== index);
|
||||
panel.classList.toggle('block', panel.dataset.tabPanel === index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean MkDocs-specific patterns from rendered HTML.
|
||||
*/
|
||||
function cleanMkDocsContent(root) {
|
||||
// Find all text nodes and elements in the content area
|
||||
const articles = root.querySelectorAll('article.prose, main .prose');
|
||||
if (articles.length === 0) return;
|
||||
const articles = new Set();
|
||||
if (root.matches?.('article.prose, main .prose, .prose, main.flex-1, #main-content')) {
|
||||
articles.add(root);
|
||||
}
|
||||
root.querySelectorAll('article.prose, main .prose, .prose, main.flex-1, #main-content').forEach((el) => articles.add(el));
|
||||
if (articles.size === 0) return;
|
||||
|
||||
for (const article of articles) {
|
||||
if (article.dataset.mkdocsClean) continue;
|
||||
delete article.dataset.mkdocsClean;
|
||||
|
||||
let html = article.innerHTML;
|
||||
let changed = false;
|
||||
|
||||
// 1. Remove :material-*: icon shortcodes (replace with relevant emoji or empty)
|
||||
const iconMap = {
|
||||
':material-file-document:': '📄',
|
||||
':material-puzzle:': '🧩',
|
||||
':material-palette:': '🎨',
|
||||
':material-rocket-launch:': '🚀',
|
||||
':material-code-tags:': '💻',
|
||||
':material-cog:': '⚙️',
|
||||
':material-lightning-bolt:': '⚡',
|
||||
':material-shield:': '🛡️',
|
||||
':material-database:': '🗄️',
|
||||
':material-web:': '🌐',
|
||||
':material-book:': '📖',
|
||||
':material-star:': '⭐',
|
||||
':material-check:': '✅',
|
||||
':material-close:': '❌',
|
||||
':material-alert:': '⚠️',
|
||||
':material-information:': 'ℹ️',
|
||||
};
|
||||
|
||||
// Replace known icons
|
||||
for (const [shortcode, emoji] of Object.entries(iconMap)) {
|
||||
for (const [shortcode, emoji] of Object.entries(ICON_MAP)) {
|
||||
if (html.includes(shortcode)) {
|
||||
html = html.replaceAll(shortcode, emoji);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace any remaining :material-*: with generic icon
|
||||
const materialPattern = /:material-[\w-]+:/g;
|
||||
if (materialPattern.test(html)) {
|
||||
html = html.replace(materialPattern, '•');
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Replace :octicons-*: shortcodes
|
||||
const octiconsPattern = /:octicons-[\w-]+(?:-\d+)?:/g;
|
||||
if (octiconsPattern.test(html)) {
|
||||
html = html.replace(octiconsPattern, '•');
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// 2. Remove <div class="grid cards" markdown> and </div> tags
|
||||
// These render as visible text in react-markdown
|
||||
const gridCardPattern = /<div\s+class="[^"]*"\s*(?:markdown)?>/gi;
|
||||
if (gridCardPattern.test(html)) {
|
||||
html = html.replace(gridCardPattern, '');
|
||||
@@ -81,29 +254,20 @@ function cleanMkDocsContent(root) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Also handle cases where react-markdown renders them as actual elements
|
||||
const rawDivNodes = article.querySelectorAll('p');
|
||||
for (const p of rawDivNodes) {
|
||||
if (changed) {
|
||||
article.innerHTML = html;
|
||||
}
|
||||
|
||||
for (const p of article.querySelectorAll('p')) {
|
||||
const text = p.textContent.trim();
|
||||
if (/^<\/?div[\s>]/.test(text) || text === '</div>') {
|
||||
// Clear text instead of hiding — safer for React reconciliation
|
||||
p.textContent = '';
|
||||
changed = true;
|
||||
p.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Transform === "Tab Title" content tabs to headings
|
||||
// These appear as literal text like: === "Python"
|
||||
const tabPattern = /===\s+"([^"]+)"/g;
|
||||
if (tabPattern.test(html)) {
|
||||
html = html.replace(tabPattern, '<strong>$1</strong>');
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
article.innerHTML = html;
|
||||
article.dataset.mkdocsClean = 'true';
|
||||
}
|
||||
enhanceFeatureGrid(article);
|
||||
enhanceTabs(article);
|
||||
article.dataset.mkdocsClean = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,3 +282,5 @@ export default {
|
||||
cleanMkDocsContent(root);
|
||||
},
|
||||
};
|
||||
|
||||
export { replaceIcons, convertMkdocsTabs, ICON_MAP };
|
||||
@@ -1,54 +1,73 @@
|
||||
/**
|
||||
* AIUI Syntax Highlighting Plugin
|
||||
*
|
||||
* Adds colorful syntax highlighting to code blocks using highlight.js.
|
||||
* Adds colourful syntax highlighting to code blocks using highlight.js.
|
||||
* Loads from CDN – no build step required.
|
||||
*/
|
||||
|
||||
let hljsReady = false;
|
||||
let pendingHighlight = false;
|
||||
let currentTheme = null;
|
||||
|
||||
const HLJS_VERSION = '11.9.0';
|
||||
const HLJS_THEME = 'github-dark';
|
||||
const HLJS_CDN = `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/${HLJS_VERSION}`;
|
||||
|
||||
function isDarkMode() {
|
||||
return document.documentElement.classList.contains('dark');
|
||||
}
|
||||
|
||||
function getHljsTheme() {
|
||||
return isDarkMode() ? 'github-dark' : 'github';
|
||||
}
|
||||
|
||||
function applyPreStyles() {
|
||||
const dark = isDarkMode();
|
||||
let override = document.getElementById('aiui-hljs-overrides');
|
||||
if (!override) {
|
||||
override = document.createElement('style');
|
||||
override.id = 'aiui-hljs-overrides';
|
||||
document.head.appendChild(override);
|
||||
}
|
||||
|
||||
override.textContent = `
|
||||
pre code.hljs {
|
||||
background: transparent !important;
|
||||
padding: 0 !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
pre {
|
||||
background: ${dark ? 'rgba(15, 23, 42, 0.6)' : 'var(--muted, #f4f4f5)'} !important;
|
||||
color: ${dark ? 'inherit' : 'var(--foreground, #18181b)'} !important;
|
||||
border: 1px solid ${dark ? 'rgba(148, 163, 184, 0.1)' : 'var(--border, #e4e4e7)'};
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem !important;
|
||||
overflow-x: auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load highlight.js CSS theme and core script from CDN.
|
||||
*/
|
||||
function loadHljs() {
|
||||
return new Promise((resolve) => {
|
||||
// Load theme CSS
|
||||
const theme = getHljsTheme();
|
||||
currentTheme = theme;
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = `${HLJS_CDN}/styles/${HLJS_THEME}.min.css`;
|
||||
link.id = 'aiui-hljs-theme';
|
||||
link.href = `${HLJS_CDN}/styles/${theme}.min.css`;
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Override styles for better integration
|
||||
const override = document.createElement('style');
|
||||
override.id = 'aiui-hljs-overrides';
|
||||
override.textContent = `
|
||||
pre code.hljs {
|
||||
background: transparent !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
pre {
|
||||
background: rgba(15, 23, 42, 0.6) !important;
|
||||
border: 1px solid rgba(148, 163, 184, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem !important;
|
||||
overflow-x: auto;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(override);
|
||||
applyPreStyles();
|
||||
|
||||
// Load highlight.js core
|
||||
const script = document.createElement('script');
|
||||
script.src = `${HLJS_CDN}/highlight.min.js`;
|
||||
script.onload = () => {
|
||||
hljsReady = true;
|
||||
console.debug('[AIUI:syntax] highlight.js core loaded');
|
||||
|
||||
// Load additional language packs
|
||||
|
||||
const langs = ['python', 'yaml', 'bash', 'javascript', 'typescript', 'json', 'css', 'html', 'xml'];
|
||||
let loaded = 0;
|
||||
for (const lang of langs) {
|
||||
@@ -58,7 +77,6 @@ function loadHljs() {
|
||||
loaded++;
|
||||
if (loaded >= langs.length) {
|
||||
console.debug('[AIUI:syntax] All languages loaded');
|
||||
// Highlight any pending content
|
||||
if (pendingHighlight) {
|
||||
pendingHighlight = false;
|
||||
highlightCodeBlocks();
|
||||
@@ -68,7 +86,6 @@ function loadHljs() {
|
||||
document.head.appendChild(ls);
|
||||
}
|
||||
|
||||
// Also highlight immediately with core languages
|
||||
setTimeout(() => highlightCodeBlocks(), 200);
|
||||
resolve();
|
||||
};
|
||||
@@ -80,6 +97,28 @@ function loadHljs() {
|
||||
});
|
||||
}
|
||||
|
||||
function swapThemeIfNeeded() {
|
||||
const theme = getHljsTheme();
|
||||
if (theme === currentTheme) {
|
||||
applyPreStyles();
|
||||
return;
|
||||
}
|
||||
|
||||
currentTheme = theme;
|
||||
const link = document.getElementById('aiui-hljs-theme');
|
||||
if (link) {
|
||||
link.href = `${HLJS_CDN}/styles/${theme}.min.css`;
|
||||
}
|
||||
applyPreStyles();
|
||||
|
||||
for (const block of document.querySelectorAll('pre code[data-hljs-highlighted]')) {
|
||||
delete block.dataset.hljsHighlighted;
|
||||
block.removeAttribute('data-highlighted');
|
||||
block.classList.remove('hljs');
|
||||
}
|
||||
highlightCodeBlocks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight all unprocessed code blocks.
|
||||
*/
|
||||
@@ -89,9 +128,10 @@ function highlightCodeBlocks() {
|
||||
return;
|
||||
}
|
||||
|
||||
swapThemeIfNeeded();
|
||||
|
||||
const blocks = document.querySelectorAll('pre code:not([data-hljs-highlighted])');
|
||||
for (const block of blocks) {
|
||||
// Skip mermaid blocks
|
||||
if (block.classList.contains('language-mermaid') ||
|
||||
block.closest('.mermaid') ||
|
||||
block.closest('[data-aiui-plugin="mermaid"]')) {
|
||||
@@ -111,10 +151,13 @@ export default {
|
||||
name: 'syntax-highlight',
|
||||
async init() {
|
||||
await loadHljs();
|
||||
|
||||
const observer = new MutationObserver(() => swapThemeIfNeeded());
|
||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
|
||||
|
||||
console.debug('[AIUI:syntax] Plugin loaded.');
|
||||
},
|
||||
onContentChange() {
|
||||
// Try immediately and with delay (for dynamically inserted content)
|
||||
highlightCodeBlocks();
|
||||
setTimeout(highlightCodeBlocks, 300);
|
||||
setTimeout(highlightCodeBlocks, 800);
|
||||
|
||||
@@ -22,6 +22,7 @@ let agents = [];
|
||||
let messageQueue = [];
|
||||
let focusMode = false;
|
||||
let sessionSelectHandler = null;
|
||||
let prefillHandler = null;
|
||||
let containerRef = null;
|
||||
let currentDeltaEl = null;
|
||||
let currentDeltaBodyEl = null;
|
||||
@@ -36,6 +37,50 @@ let contextRefreshDebounce = null;
|
||||
let lastContextPct = 0;
|
||||
let finopsConfig = null;
|
||||
let sessionCostState = { tokens: 0, model: '', estimate: false };
|
||||
let lastUserMessage = ''; // last sent user text, drives per-turn memory search
|
||||
|
||||
// ── i18n State ───────────────────────────────────────────────────
|
||||
let i18nStrings = {};
|
||||
const I18N_FALLBACK = {
|
||||
'chat.context.healthy': 'Context healthy',
|
||||
'chat.context.warning': 'Approaching context limit',
|
||||
'chat.context.critical': 'Context nearly full',
|
||||
'chat.context.estimate': 'Estimate',
|
||||
'chat.compaction.banner': 'Context nearly full — older messages may be summarized on next send',
|
||||
'chat.compaction.compacted': 'Context compacted',
|
||||
'chat.memory.chip': '{count} memories',
|
||||
'chat.memory.empty_turn': 'No memories for this turn',
|
||||
};
|
||||
|
||||
function t(key, params) {
|
||||
let text = i18nStrings[key] != null ? i18nStrings[key] : (I18N_FALLBACK[key] != null ? I18N_FALLBACK[key] : key);
|
||||
if (params) {
|
||||
for (const k in params) {
|
||||
text = text.split('{' + k + '}').join(String(params[k]));
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
async function loadI18nStrings() {
|
||||
let locale = 'en';
|
||||
try {
|
||||
const stored = (typeof localStorage !== 'undefined') ? localStorage.getItem('aiui-locale') : null;
|
||||
if (stored) {
|
||||
locale = stored;
|
||||
} else {
|
||||
const lr = await fetch('/api/i18n/locale');
|
||||
if (lr.ok) { const ld = await lr.json(); if (ld && ld.locale) locale = ld.locale; }
|
||||
}
|
||||
} catch (e) { /* fall back to English */ }
|
||||
try {
|
||||
const resp = await fetch('/api/i18n/strings/' + encodeURIComponent(locale));
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
i18nStrings = (data && data.strings) || {};
|
||||
}
|
||||
} catch (e) { /* keep English fallback */ }
|
||||
}
|
||||
|
||||
// ── Floating Chat State ──────────────────────────────────────────
|
||||
let chatModeConfig = { mode: 'fullpage' };
|
||||
@@ -695,11 +740,34 @@ function setupResizeHandles(wrapper) {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Prefill Composer Listener (singleton) ───────────────────────
|
||||
// Code Studio's "Send to Agent" dispatches a window-level
|
||||
// 'aiui:prefill-composer' event. This SPA calls render() on every
|
||||
// visit to /chat, so the listener is installed once at module scope
|
||||
// and guarded to avoid stacking duplicate handlers across navigations.
|
||||
function ensurePrefillListener() {
|
||||
if (prefillHandler) return;
|
||||
prefillHandler = (e) => {
|
||||
const text = e.detail && e.detail.text;
|
||||
if (!text) return;
|
||||
const el = document.getElementById('chat-input');
|
||||
if (!el) return;
|
||||
el.value = text;
|
||||
el.style.height = 'auto';
|
||||
el.style.height = Math.min(el.scrollHeight, 150) + 'px';
|
||||
el.focus();
|
||||
};
|
||||
window.addEventListener('aiui:prefill-composer', prefillHandler);
|
||||
}
|
||||
|
||||
// ── Main render (ES module entry point) ─────────────────────────
|
||||
export async function render(container) {
|
||||
injectStyles();
|
||||
containerRef = container;
|
||||
|
||||
|
||||
// Load translations for ring/banner/chip (English fallback on failure)
|
||||
await loadI18nStrings();
|
||||
|
||||
// Fetch chat mode config
|
||||
try {
|
||||
const configRes = await fetch('/ui-config.json');
|
||||
@@ -790,15 +858,16 @@ export async function render(container) {
|
||||
<textarea id="chat-input" placeholder="Type a message…" rows="1"></textarea>
|
||||
<div class="chat-compose-actions">
|
||||
<div class="chat-session-cost-chip" id="chat-session-cost-chip" tabindex="0"></div>
|
||||
<div class="chat-ring-wrap chat-ring-healthy" id="chat-ring-wrap">
|
||||
<div class="chat-ring-wrap chat-ring-healthy" id="chat-ring-wrap"
|
||||
role="progressbar" aria-label="Context usage"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
|
||||
<svg viewBox="0 0 48 48" aria-hidden="true">
|
||||
<circle class="chat-ring-track" cx="24" cy="24" r="20"></circle>
|
||||
<circle class="chat-ring-arc" id="chat-ring-arc" cx="24" cy="24" r="20"
|
||||
stroke-dasharray="125.66" stroke-dashoffset="125.66"></circle>
|
||||
</svg>
|
||||
<button class="chat-ring-send" id="chat-send-btn" title="Send"
|
||||
role="progressbar" aria-label="Send message"
|
||||
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">➤</button>
|
||||
type="button" aria-label="Send message">➤</button>
|
||||
<div class="chat-ring-tooltip" id="chat-ring-tooltip"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -833,16 +902,7 @@ export async function render(container) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
|
||||
});
|
||||
|
||||
window.addEventListener('aiui:prefill-composer', (e) => {
|
||||
const text = e.detail && e.detail.text;
|
||||
if (!text) return;
|
||||
const el = document.getElementById('chat-input');
|
||||
if (!el) return;
|
||||
el.value = text;
|
||||
el.style.height = 'auto';
|
||||
el.style.height = Math.min(el.scrollHeight, 150) + 'px';
|
||||
el.focus();
|
||||
});
|
||||
ensurePrefillListener();
|
||||
input.addEventListener('input', () => {
|
||||
input.style.height = 'auto';
|
||||
input.style.height = Math.min(input.scrollHeight, 150) + 'px';
|
||||
@@ -919,6 +979,10 @@ export function cleanup() {
|
||||
window.removeEventListener('aiui:session-select', sessionSelectHandler);
|
||||
sessionSelectHandler = null;
|
||||
}
|
||||
if (prefillHandler) {
|
||||
window.removeEventListener('aiui:prefill-composer', prefillHandler);
|
||||
prefillHandler = null;
|
||||
}
|
||||
currentSessionId = null;
|
||||
currentAgentName = null;
|
||||
isStreaming = false;
|
||||
@@ -1864,7 +1928,6 @@ function ringStateFor(pct) {
|
||||
function updateContextRing(stats) {
|
||||
const wrap = document.getElementById('chat-ring-wrap');
|
||||
const arc = document.getElementById('chat-ring-arc');
|
||||
const sendBtn = document.getElementById('chat-send-btn');
|
||||
if (!wrap || !arc) return;
|
||||
|
||||
const used = Number(stats.used) || 0;
|
||||
@@ -1888,10 +1951,8 @@ function updateContextRing(stats) {
|
||||
arc.setAttribute('stroke-dasharray', RING_CIRCUMFERENCE.toFixed(2));
|
||||
arc.setAttribute('stroke-dashoffset', (RING_CIRCUMFERENCE * (1 - clamped / 100)).toFixed(2));
|
||||
|
||||
if (sendBtn) {
|
||||
sendBtn.setAttribute('aria-valuenow', String(Math.round(pct)));
|
||||
sendBtn.setAttribute('aria-valuemax', '100');
|
||||
}
|
||||
wrap.setAttribute('aria-valuenow', String(Math.round(pct)));
|
||||
wrap.setAttribute('aria-valuemax', '100');
|
||||
|
||||
updateRingTooltip(stats, pct);
|
||||
updateCompactionBanner(pct, estimate);
|
||||
@@ -1912,9 +1973,11 @@ function updateRingTooltip(stats, pct) {
|
||||
['Memory', b.memory || 0],
|
||||
];
|
||||
const maxVal = Math.max(1, ...rows.map((r) => r[1]));
|
||||
const title = stats.estimate ? 'Context breakdown (estimate)' : 'Context breakdown';
|
||||
const statusKey = pct >= 85 ? 'chat.context.critical' : (pct >= 60 ? 'chat.context.warning' : 'chat.context.healthy');
|
||||
const title = stats.estimate ? escapeHtml(t('chat.context.estimate')) : escapeHtml(t(statusKey));
|
||||
tip.innerHTML =
|
||||
'<h5>' + title + '</h5>' +
|
||||
'<div class="chat-ring-tt-status">' + escapeHtml(t(statusKey)) + '</div>' +
|
||||
rows.map(([lbl, val]) =>
|
||||
'<div class="chat-ring-tt-row">' +
|
||||
'<span class="lbl">' + lbl + '</span>' +
|
||||
@@ -1942,9 +2005,7 @@ function updateCompactionBanner(pct, estimate) {
|
||||
banner.classList.add('open');
|
||||
banner.classList.toggle('critical', pct >= 95);
|
||||
banner.setAttribute('role', pct >= 95 ? 'alert' : 'status');
|
||||
textEl.textContent = pct >= 95
|
||||
? 'Context ' + Math.round(pct) + '% — older messages will be summarized on next send'
|
||||
: 'Context ' + Math.round(pct) + '% — older messages may be summarized on next send';
|
||||
textEl.textContent = t('chat.compaction.banner');
|
||||
}
|
||||
|
||||
function dismissCompactionBanner() {
|
||||
@@ -1962,7 +2023,7 @@ function flashCompactionCompacted(data) {
|
||||
banner.classList.remove('critical');
|
||||
banner.setAttribute('role', 'status');
|
||||
const removed = data && data.removed_tokens ? ' (' + data.removed_tokens + ' tokens freed)' : '';
|
||||
textEl.textContent = 'Context compacted' + removed;
|
||||
textEl.textContent = t('chat.compaction.compacted') + removed;
|
||||
sessionStorage.removeItem('aiui-compaction-dismissed');
|
||||
setTimeout(() => {
|
||||
if (lastContextPct < 85) banner.classList.remove('open');
|
||||
@@ -2009,32 +2070,46 @@ function updateMemoryChip(count, loading) {
|
||||
chip.classList.remove('loading');
|
||||
const n = Number(count) || 0;
|
||||
if (n <= 0) {
|
||||
// E5: hide chip when no memories retrieved this turn
|
||||
chip.classList.remove('visible');
|
||||
chip.innerHTML = '';
|
||||
// E5: no memories retrieved this turn — show muted empty-turn label
|
||||
chip.classList.add('visible');
|
||||
chip.title = t('chat.memory.empty_turn');
|
||||
chip.innerHTML = '🧠 0';
|
||||
return;
|
||||
}
|
||||
chip.classList.add('visible');
|
||||
chip.innerHTML = '🧠 ' + n + ' ' + (n === 1 ? 'memory' : 'memories');
|
||||
chip.title = '';
|
||||
chip.innerHTML = '🧠 ' + escapeHtml(t('chat.memory.chip', { count: n }));
|
||||
}
|
||||
|
||||
// Per-turn memory search: count hits for the last user message, not total store.
|
||||
async function refreshMemoryChip() {
|
||||
const chip = document.getElementById('chat-memory-chip');
|
||||
if (!chip) return;
|
||||
const query = (lastUserMessage || '').trim();
|
||||
if (!query) {
|
||||
updateMemoryChip(0, false);
|
||||
return;
|
||||
}
|
||||
updateMemoryChip(0, true);
|
||||
try {
|
||||
const endpoint = currentSessionId
|
||||
? '/api/memory/session/' + encodeURIComponent(currentSessionId)
|
||||
: '/api/memory';
|
||||
let resp = await fetch(endpoint);
|
||||
if (!resp.ok) resp = await fetch('/api/memory');
|
||||
if (!resp.ok) { chip.classList.remove('loading'); chip.innerHTML = '🧠 —'; chip.classList.add('visible'); return; }
|
||||
const resp = await fetch('/api/memory/search', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
limit: 10,
|
||||
session_id: currentSessionId || undefined,
|
||||
}),
|
||||
});
|
||||
if (!resp.ok) { chip.classList.remove('loading'); chip.classList.add('visible'); chip.title = ''; chip.innerHTML = '🧠 —'; return; }
|
||||
const data = await resp.json();
|
||||
const items = data.memories || [];
|
||||
updateMemoryChip(items.length, false);
|
||||
const results = data.results || [];
|
||||
updateMemoryChip(results.length, false);
|
||||
} catch (e) {
|
||||
// E6: memory search failed — muted dash
|
||||
chip.classList.remove('loading');
|
||||
chip.classList.add('visible');
|
||||
chip.title = '';
|
||||
chip.innerHTML = '🧠 —';
|
||||
}
|
||||
}
|
||||
@@ -2335,6 +2410,9 @@ function sendMessage() {
|
||||
const content = input.value.trim();
|
||||
if (!content && pendingAttachments.length === 0) return;
|
||||
|
||||
// Track last user text so the memory chip searches this turn's query
|
||||
if (content) lastUserMessage = content;
|
||||
|
||||
// Generate session ID if needed
|
||||
if (!currentSessionId) {
|
||||
currentSessionId = crypto.randomUUID ? crypto.randomUUID() :
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Mini-chat transport — shared agent WebSocket bridge for embedded chat panes.
|
||||
*
|
||||
* Reuses the same wire protocol as the full Chat view (chat.js): it connects
|
||||
* to /api/chat/ws, sends { type:'chat', content, session_id, agent_name },
|
||||
* and streams run_content / run_completed / run_error events back to the caller.
|
||||
*
|
||||
* The full Chat view keeps its own rich rendering; this module only owns the
|
||||
* transport so embedded surfaces (Work Hub Chat tab, etc.) can send to and
|
||||
* stream from the same agent without rebuilding chat from scratch.
|
||||
*
|
||||
* Usage:
|
||||
* import { createMiniChat } from '/plugins/views/mini-chat.js';
|
||||
* const chat = createMiniChat({ agentId, sessionId });
|
||||
* chat.send({ text, onDelta, onComplete, onError });
|
||||
* chat.disconnect(); // on card change / teardown
|
||||
*/
|
||||
|
||||
function wsUrl() {
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
return protocol + '//' + location.host + '/api/chat/ws';
|
||||
}
|
||||
|
||||
function genSessionId() {
|
||||
if (typeof crypto !== 'undefined' && crypto.randomUUID) return crypto.randomUUID();
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a mini-chat transport bound to one agent + session.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @param {string} [opts.agentId] - Agent name to route the message to.
|
||||
* @param {string} [opts.sessionId] - Session id (generated if omitted).
|
||||
* @returns {{ send: Function, disconnect: Function, sessionId: string }}
|
||||
*/
|
||||
export function createMiniChat({ agentId = '', sessionId = '' } = {}) {
|
||||
let ws = null;
|
||||
let closed = false;
|
||||
const session = sessionId || genSessionId();
|
||||
let active = null; // { onDelta, onComplete, onError, done }
|
||||
|
||||
function finish(handler, arg) {
|
||||
if (!active || active.done) return;
|
||||
active.done = true;
|
||||
const cb = active[handler];
|
||||
active = null;
|
||||
if (typeof cb === 'function') {
|
||||
try { cb(arg); } catch (_) { /* caller handler error */ }
|
||||
}
|
||||
}
|
||||
|
||||
function handle(data) {
|
||||
const type = data && data.type;
|
||||
switch (type) {
|
||||
case 'run_content':
|
||||
case 'team_run_content':
|
||||
if (data.token && active && typeof active.onDelta === 'function') {
|
||||
try { active.onDelta(data.token); } catch (_) { /* handler error */ }
|
||||
}
|
||||
break;
|
||||
case 'run_completed':
|
||||
case 'team_run_completed':
|
||||
finish('onComplete', data.content || '');
|
||||
break;
|
||||
case 'run_error':
|
||||
case 'team_run_error':
|
||||
finish('onError', new Error(data.error || 'Run error'));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function ensureSocket() {
|
||||
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return ws;
|
||||
ws = new WebSocket(wsUrl());
|
||||
ws.onmessage = (event) => {
|
||||
let data;
|
||||
try { data = JSON.parse(event.data); } catch (_) { return; }
|
||||
if (data.session_id && data.session_id !== session) return;
|
||||
handle(data);
|
||||
};
|
||||
ws.onerror = () => {
|
||||
finish('onError', new Error('Connection error'));
|
||||
};
|
||||
ws.onclose = () => {
|
||||
if (!closed) finish('onError', new Error('Connection closed'));
|
||||
};
|
||||
return ws;
|
||||
}
|
||||
|
||||
function transmit(text) {
|
||||
const sock = ensureSocket();
|
||||
const payload = JSON.stringify({
|
||||
type: 'chat',
|
||||
content: text,
|
||||
session_id: session,
|
||||
agent_name: agentId || null,
|
||||
});
|
||||
if (sock.readyState === WebSocket.OPEN) {
|
||||
sock.send(payload);
|
||||
} else if (sock.readyState === WebSocket.CONNECTING) {
|
||||
sock.addEventListener('open', () => {
|
||||
if (!closed) {
|
||||
try { sock.send(payload); } catch (_) { finish('onError', new Error('Connection error')); }
|
||||
}
|
||||
}, { once: true });
|
||||
} else {
|
||||
finish('onError', new Error('Connection unavailable'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a user message and stream the agent reply.
|
||||
*
|
||||
* @param {Object} req
|
||||
* @param {string} req.text - Message text.
|
||||
* @param {Function} [req.onDelta] - Called with each streamed token.
|
||||
* @param {Function} [req.onComplete] - Called with the final content string.
|
||||
* @param {Function} [req.onError] - Called with an Error on failure.
|
||||
*/
|
||||
function send({ text, onDelta, onComplete, onError }) {
|
||||
if (closed) {
|
||||
if (typeof onError === 'function') onError(new Error('Disconnected'));
|
||||
return;
|
||||
}
|
||||
if (active && !active.done) {
|
||||
finish('onError', new Error('Superseded'));
|
||||
}
|
||||
active = { onDelta, onComplete, onError, done: false };
|
||||
if (typeof navigator !== 'undefined' && navigator.onLine === false) {
|
||||
finish('onError', new Error('You are offline.'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
transmit(String(text == null ? '' : text));
|
||||
} catch (_) {
|
||||
finish('onError', new Error('Connection error'));
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
closed = true;
|
||||
active = null;
|
||||
if (ws) {
|
||||
try { ws.onclose = null; ws.onerror = null; ws.onmessage = null; ws.close(); } catch (_) { /* noop */ }
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
return { send, disconnect, sessionId: session };
|
||||
}
|
||||
@@ -449,9 +449,14 @@ async function refreshApprovals() {
|
||||
data.pending = pending;
|
||||
const metricsEl = _container.querySelector('#ov-metrics');
|
||||
const attnEl = _container.querySelector('#ov-attention');
|
||||
if (metricsEl) metricsEl.innerHTML = renderMetrics(data);
|
||||
if (attnEl) attnEl.innerHTML = renderAttention(data);
|
||||
bindEvents(_container, data);
|
||||
if (metricsEl) {
|
||||
metricsEl.innerHTML = renderMetrics(data);
|
||||
bindEvents(metricsEl, data);
|
||||
}
|
||||
if (attnEl) {
|
||||
attnEl.innerHTML = renderAttention(data);
|
||||
bindEvents(attnEl, data);
|
||||
}
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
* (?board=&card=&tab=) is kept in sync for deep links.
|
||||
*/
|
||||
import { createInteractiveBoard, relativeTime, escapeHtml } from '../board.js';
|
||||
import { createMiniChat } from './mini-chat.js';
|
||||
|
||||
const STYLE_ID = 'work-hub-styles';
|
||||
|
||||
@@ -35,6 +36,11 @@ function injectStyles() {
|
||||
.work-hub-chat-msg { padding:8px 10px; border-radius:8px; font-size:13px; line-height:1.5;
|
||||
background:var(--db-card-bg,#27272a); border:1px solid var(--db-border,#3f3f46); }
|
||||
.work-hub-chat-msg .role { font-size:11px; font-weight:600; color:var(--db-accent,#6366f1); }
|
||||
.work-hub-chat-msg .work-hub-chat-body { white-space:pre-wrap; }
|
||||
.work-hub-chat-streaming .work-hub-chat-body::after { content:'▌'; animation:work-hub-blink 1s infinite; }
|
||||
.work-hub-chat-error { border-color:#ef4444; }
|
||||
.work-hub-chat-error .work-hub-chat-body { color:#ef4444; }
|
||||
@keyframes work-hub-blink { 50% { opacity:0; } }
|
||||
.work-hub-composer { display:flex; gap:8px; align-items:flex-end; }
|
||||
.work-hub-composer textarea { flex:1; resize:none; min-height:40px; max-height:64px; padding:8px 10px;
|
||||
background:var(--db-card-bg,#27272a); border:1px solid var(--db-border,#3f3f46); border-radius:8px;
|
||||
@@ -71,6 +77,14 @@ export async function render(container) {
|
||||
let selectedCard = null;
|
||||
let activeTab = params.get('tab') || 'description';
|
||||
const pendingCardId = params.get('card');
|
||||
let miniChat = null;
|
||||
|
||||
function teardownChat() {
|
||||
if (miniChat) {
|
||||
try { miniChat.disconnect(); } catch (_) { /* noop */ }
|
||||
miniChat = null;
|
||||
}
|
||||
}
|
||||
|
||||
const hub = document.createElement('div');
|
||||
hub.className = 'work-hub';
|
||||
@@ -99,6 +113,7 @@ export async function render(container) {
|
||||
}
|
||||
|
||||
function renderDetail() {
|
||||
teardownChat();
|
||||
if (!selectedCard) {
|
||||
p2.innerHTML = '<div class="work-hub-empty">Select a card to see its details.</div>';
|
||||
return;
|
||||
@@ -153,7 +168,9 @@ export async function render(container) {
|
||||
}
|
||||
|
||||
function renderChat(body, card) {
|
||||
const agentId = card.assignee || (card.meta || {}).agent_id || '';
|
||||
const meta = card.meta || {};
|
||||
const agentId = card.assignee || meta.agent_id || '';
|
||||
const sessionId = meta.session_id || '';
|
||||
body.innerHTML = `
|
||||
<div class="work-hub-chat-log" id="work-hub-chat-log">
|
||||
<div class="work-hub-empty">Ask the assigned agent (${escapeHtml(agentId)}) about this card.</div>
|
||||
@@ -162,21 +179,74 @@ export async function render(container) {
|
||||
<textarea id="work-hub-composer" rows="2" placeholder="Message ${escapeHtml(agentId)}…"></textarea>
|
||||
<button class="db-btn db-btn-sm" id="work-hub-send">Send</button>
|
||||
</div>
|
||||
<div style="margin-top:8px;font-size:12px"><a href="/chat" style="color:var(--db-accent,#6366f1)">Open full chat →</a></div>`;
|
||||
<div style="margin-top:8px;font-size:12px"><a href="${sessionId ? '/chat?session_id=' + encodeURIComponent(sessionId) : '/chat'}" style="color:var(--db-accent,#6366f1)">Open full chat →</a></div>`;
|
||||
|
||||
const log = body.querySelector('#work-hub-chat-log');
|
||||
const ta = body.querySelector('#work-hub-composer');
|
||||
const send = body.querySelector('#work-hub-send');
|
||||
send.addEventListener('click', () => {
|
||||
const text = ta.value.trim();
|
||||
if (!text) return;
|
||||
const msg = document.createElement('div');
|
||||
msg.className = 'work-hub-chat-msg';
|
||||
msg.innerHTML = `<div class="role">You</div>${escapeHtml(text)}`;
|
||||
|
||||
teardownChat();
|
||||
miniChat = createMiniChat({ agentId, sessionId });
|
||||
|
||||
function appendMsg(role, text) {
|
||||
if (log.querySelector('.work-hub-empty')) log.innerHTML = '';
|
||||
log.appendChild(msg);
|
||||
ta.value = '';
|
||||
const el = document.createElement('div');
|
||||
el.className = 'work-hub-chat-msg';
|
||||
el.innerHTML = `<div class="role">${escapeHtml(role)}</div>${escapeHtml(text)}`;
|
||||
log.appendChild(el);
|
||||
log.scrollTop = log.scrollHeight;
|
||||
return el;
|
||||
}
|
||||
|
||||
function submit() {
|
||||
const text = ta.value.trim();
|
||||
if (!text || !miniChat) return;
|
||||
appendMsg('You', text);
|
||||
ta.value = '';
|
||||
|
||||
if (log.querySelector('.work-hub-empty')) log.innerHTML = '';
|
||||
const reply = document.createElement('div');
|
||||
reply.className = 'work-hub-chat-msg work-hub-chat-streaming';
|
||||
const roleEl = document.createElement('div');
|
||||
roleEl.className = 'role';
|
||||
roleEl.textContent = agentId || 'Agent';
|
||||
const textEl = document.createElement('div');
|
||||
textEl.className = 'work-hub-chat-body';
|
||||
textEl.textContent = '…';
|
||||
reply.appendChild(roleEl);
|
||||
reply.appendChild(textEl);
|
||||
log.appendChild(reply);
|
||||
log.scrollTop = log.scrollHeight;
|
||||
|
||||
let acc = '';
|
||||
send.disabled = true;
|
||||
miniChat.send({
|
||||
text,
|
||||
onDelta: (token) => {
|
||||
acc += token;
|
||||
textEl.textContent = acc;
|
||||
log.scrollTop = log.scrollHeight;
|
||||
},
|
||||
onComplete: (content) => {
|
||||
reply.classList.remove('work-hub-chat-streaming');
|
||||
if (content && content.trim()) textEl.textContent = content;
|
||||
else if (!acc) textEl.textContent = '(no response)';
|
||||
send.disabled = false;
|
||||
log.scrollTop = log.scrollHeight;
|
||||
},
|
||||
onError: (err) => {
|
||||
reply.classList.remove('work-hub-chat-streaming');
|
||||
reply.classList.add('work-hub-chat-error');
|
||||
textEl.textContent = '⚠ ' + ((err && err.message) || 'Could not reach agent.');
|
||||
send.disabled = false;
|
||||
log.scrollTop = log.scrollHeight;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
send.addEventListener('click', submit);
|
||||
ta.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); submit(); }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -265,5 +335,5 @@ export async function render(container) {
|
||||
renderDetail();
|
||||
renderActivity();
|
||||
|
||||
return () => board.destroy();
|
||||
return () => { teardownChat(); board.destroy(); };
|
||||
}
|
||||
@@ -49,13 +49,16 @@ templates:
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
"""
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
docs_dir = tmp_path / "docs"
|
||||
docs_dir.mkdir()
|
||||
(docs_dir / "index.md").write_text("# Welcome\n\nHello from integration test.")
|
||||
(docs_dir / "guide.md").write_text("# Guide\n\nA test guide.")
|
||||
(docs_dir / "index.md").write_text(
|
||||
"# Welcome\n\nHello from integration test.", encoding="utf-8"
|
||||
)
|
||||
(docs_dir / "guide.md").write_text("# Guide\n\nA test guide.", encoding="utf-8")
|
||||
|
||||
return tmp_path
|
||||
|
||||
@@ -94,7 +97,7 @@ class TestBuildCommand:
|
||||
["build", "--config", str(project_dir / "aiui.template.yaml"), "--output", str(output)],
|
||||
)
|
||||
|
||||
ui_config = json.loads((output / "ui-config.json").read_text())
|
||||
ui_config = json.loads((output / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["site"]["title"] == "Integration Test Site"
|
||||
assert "header_main" in ui_config["components"]
|
||||
assert "footer_main" in ui_config["components"]
|
||||
@@ -111,12 +114,12 @@ class TestBuildCommand:
|
||||
],
|
||||
)
|
||||
|
||||
content = (output / "ui-config.json").read_text()
|
||||
content = (output / "ui-config.json").read_text(encoding="utf-8")
|
||||
assert "\n" not in content # Minified = no newlines
|
||||
|
||||
def test_build_invalid_config(self, tmp_path: Path):
|
||||
config = tmp_path / "bad.yaml"
|
||||
config.write_text("not: valid: yaml: config")
|
||||
config.write_text("not: valid: yaml: config", encoding="utf-8")
|
||||
result = runner.invoke(
|
||||
app,
|
||||
["build", "--config", str(config), "--output", str(tmp_path / "out")],
|
||||
@@ -141,7 +144,8 @@ templates:
|
||||
routes:
|
||||
- match: "/docs/**"
|
||||
template: docs
|
||||
"""
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
result = runner.invoke(
|
||||
app,
|
||||
@@ -215,8 +219,8 @@ class TestFullPipeline:
|
||||
assert (output / "index.html").exists()
|
||||
|
||||
# Verify content
|
||||
ui_config = json.loads((output / "ui-config.json").read_text())
|
||||
ui_config = json.loads((output / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["site"]["title"] == "Integration Test Site"
|
||||
|
||||
routes = json.loads((output / "route-manifest.json").read_text())
|
||||
routes = json.loads((output / "route-manifest.json").read_text(encoding="utf-8"))
|
||||
assert len(routes["routes"]) >= 1
|
||||
@@ -0,0 +1,87 @@
|
||||
"""Unit tests for ADP JSON Schema validation."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
from jsonschema import Draft202012Validator, ValidationError
|
||||
|
||||
ADP_ROOT = Path(__file__).resolve().parents[2] / "docs" / "protocols" / "adp"
|
||||
SCHEMA_DIR = ADP_ROOT / "schema"
|
||||
EXAMPLES_DIR = ADP_ROOT / "examples"
|
||||
CATALOGS_DIR = ADP_ROOT / "catalogs"
|
||||
|
||||
|
||||
def _load_json(path: Path) -> dict:
|
||||
return json.loads(path.read_text())
|
||||
|
||||
|
||||
def _load_yaml(path: Path) -> dict:
|
||||
return yaml.safe_load(path.read_text())
|
||||
|
||||
|
||||
def _application_validator() -> Draft202012Validator:
|
||||
schema = _load_json(SCHEMA_DIR / "application.schema.json")
|
||||
return Draft202012Validator(schema)
|
||||
|
||||
|
||||
def _catalog_validator() -> Draft202012Validator:
|
||||
schema = _load_json(SCHEMA_DIR / "component-catalog.schema.json")
|
||||
return Draft202012Validator(schema)
|
||||
|
||||
|
||||
def _theme_validator() -> Draft202012Validator:
|
||||
schema = _load_json(SCHEMA_DIR / "theme.schema.json")
|
||||
return Draft202012Validator(schema)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"schema_path",
|
||||
[
|
||||
SCHEMA_DIR / "application.schema.json",
|
||||
SCHEMA_DIR / "component-catalog.schema.json",
|
||||
SCHEMA_DIR / "theme.schema.json",
|
||||
],
|
||||
)
|
||||
def test_schema_files_are_valid_meta_schema(schema_path: Path) -> None:
|
||||
schema = _load_json(schema_path)
|
||||
Draft202012Validator.check_schema(schema)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"example_name",
|
||||
["minimal.application.yaml", "docs-site.application.yaml", "landing-page.application.yaml"],
|
||||
)
|
||||
def test_examples_validate_against_application_schema(example_name: str) -> None:
|
||||
doc = _load_yaml(EXAMPLES_DIR / example_name)
|
||||
_application_validator().validate(doc)
|
||||
|
||||
|
||||
def test_default_catalog_validates_against_catalog_schema() -> None:
|
||||
catalog = _load_json(CATALOGS_DIR / "default.catalog.json")
|
||||
_catalog_validator().validate(catalog)
|
||||
|
||||
|
||||
def test_theme_mutual_exclusion_r27() -> None:
|
||||
with pytest.raises(ValidationError):
|
||||
_theme_validator().validate({"preset": "zinc", "tokens": "./tokens.json"})
|
||||
|
||||
|
||||
def test_theme_preset_only_valid() -> None:
|
||||
_theme_validator().validate({"preset": "zinc", "radius": "md", "darkMode": True})
|
||||
|
||||
|
||||
def test_x_adp_extension_accepted_at_root() -> None:
|
||||
doc = _load_yaml(EXAMPLES_DIR / "minimal.application.yaml")
|
||||
doc["x-adp-custom"] = True
|
||||
_application_validator().validate(doc)
|
||||
|
||||
|
||||
def test_template_requires_slots_or_zones() -> None:
|
||||
doc = _load_yaml(EXAMPLES_DIR / "minimal.application.yaml")
|
||||
doc["spec"]["templates"]["docs"] = {"layout": "TwoColumnLayout"}
|
||||
with pytest.raises(ValidationError):
|
||||
_application_validator().validate(doc)
|
||||
@@ -305,7 +305,7 @@ class TestAudioSessionManagement:
|
||||
session = _audio_sessions[session_id]
|
||||
assert session["chunks_received"] == 1
|
||||
assert session["bytes_received"] == len(test_data)
|
||||
assert session["last_chunk_at"] > session["started_at"]
|
||||
assert session["last_chunk_at"] >= session["started_at"]
|
||||
|
||||
# Check global stats were updated
|
||||
assert _audio_stats["total_chunks"] == 1
|
||||
|
||||
@@ -224,25 +224,37 @@ def test_constant_time_comparison():
|
||||
|
||||
|
||||
def test_timing_attack_resistance():
|
||||
"""Test timing attack resistance - all incorrect tokens take similar time."""
|
||||
"""Test timing attack resistance - all incorrect tokens take similar time.
|
||||
|
||||
Timing is inherently noisy: a single sample per token trips on scheduler
|
||||
spikes when the full suite runs under CPU contention (issue #256). We take
|
||||
several samples per token and compare the *median* time, which is resistant
|
||||
to transient spikes, so the test measures the constant-time property rather
|
||||
than system load.
|
||||
"""
|
||||
correct = "secret123456789012345678901234567890"
|
||||
wrong_same_length = "wrongg123456789012345678901234567890"
|
||||
wrong_diff_length = "wrong"
|
||||
tokens = [wrong_same_length, wrong_diff_length, ""]
|
||||
|
||||
# Measure times for different wrong tokens
|
||||
times = []
|
||||
for wrong_token in [wrong_same_length, wrong_diff_length, ""]:
|
||||
start = time.perf_counter()
|
||||
for _ in range(1000): # Multiple iterations for better timing measurement
|
||||
_constant_time_eq(correct, wrong_token)
|
||||
end = time.perf_counter()
|
||||
times.append(end - start)
|
||||
def _median_time(wrong_token: str) -> float:
|
||||
samples = []
|
||||
for _ in range(7):
|
||||
start = time.perf_counter()
|
||||
for _ in range(1000):
|
||||
_constant_time_eq(correct, wrong_token)
|
||||
samples.append(time.perf_counter() - start)
|
||||
samples.sort()
|
||||
return samples[len(samples) // 2]
|
||||
|
||||
# Times should be similar (within reasonable variance)
|
||||
# This is a best-effort test - timing can be affected by system load
|
||||
# Warm up so JIT/cache effects do not skew the first token's samples.
|
||||
_median_time(correct)
|
||||
|
||||
times = [_median_time(token) for token in tokens]
|
||||
|
||||
# Median times should be similar (all wrong tokens take comparable time).
|
||||
avg_time = sum(times) / len(times)
|
||||
for t in times:
|
||||
# Allow up to 50% variance due to system noise
|
||||
assert abs(t - avg_time) / avg_time < 0.5, f"Timing variance too high: {times}"
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ EXPLORER_VIEW = _FRONTEND / "views" / "explorer.js"
|
||||
|
||||
|
||||
def _view() -> str:
|
||||
return CHANNELS_VIEW.read_text()
|
||||
return CHANNELS_VIEW.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
class TestAddChannelControls:
|
||||
@@ -91,6 +91,6 @@ class TestEmptyState:
|
||||
|
||||
class TestExplorerPreset:
|
||||
def test_explorer_has_add_channel_post(self):
|
||||
src = EXPLORER_VIEW.read_text()
|
||||
src = EXPLORER_VIEW.read_text(encoding="utf-8")
|
||||
assert "Add Channel" in src
|
||||
assert "env:TELEGRAM_BOT_TOKEN" in src
|
||||
@@ -25,13 +25,13 @@ CHAT_JS = FRONTEND / "views" / "chat.js"
|
||||
|
||||
class TestSelectPageShellError:
|
||||
def test_shell_error_helper_defined(self):
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
assert "function showDashboardInitError(" in src
|
||||
assert "'alert'" in src
|
||||
assert "db-error" in src
|
||||
|
||||
def test_select_page_calls_shell_error_on_missing_main(self):
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
idx = src.find("const main = document.getElementById('db-main-content')")
|
||||
assert idx != -1
|
||||
region = src[idx:idx + 400]
|
||||
@@ -41,16 +41,16 @@ class TestSelectPageShellError:
|
||||
|
||||
class TestChatOfflineMessaging:
|
||||
def test_toast_imported(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "import { showToast } from '../toast.js';" in src
|
||||
|
||||
def test_offline_hint_in_fallback(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "navigator.onLine === false" in src
|
||||
assert "You are offline. " in src
|
||||
|
||||
def test_fallback_catch_shows_toast(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
idx = src.find("fetch('/api/chat/send'")
|
||||
assert idx != -1
|
||||
region = src[idx:idx + 900]
|
||||
@@ -62,7 +62,7 @@ class TestChatOfflineMessaging:
|
||||
|
||||
class TestSystemErrorRoleAlert:
|
||||
def test_append_message_sets_role_alert(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
idx = src.find("function appendMessage(")
|
||||
assert idx != -1
|
||||
region = src[idx:idx + 700]
|
||||
@@ -72,7 +72,7 @@ class TestSystemErrorRoleAlert:
|
||||
|
||||
class TestWebSocketSendGuard:
|
||||
def test_ws_send_wrapped_in_try_catch(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
idx = src.find("ws.send(JSON.stringify(payload))")
|
||||
assert idx != -1
|
||||
before = src[max(0, idx - 120):idx]
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Tests for the chat prefill-composer listener lifecycle (issue #209).
|
||||
|
||||
The 'aiui:prefill-composer' listener must be registered as a module-scope
|
||||
singleton and torn down in cleanup(), so repeated SPA navigations between
|
||||
/code-studio and /chat do not stack duplicate handlers on window.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
VIEWS = Path("src/praisonaiui/templates/frontend/plugins/views")
|
||||
|
||||
|
||||
class TestChatPrefillListenerLifecycle:
|
||||
def test_module_scope_handler_declared(self):
|
||||
src = (VIEWS / "chat.js").read_text()
|
||||
assert "let prefillHandler = null;" in src
|
||||
|
||||
def test_ensure_prefill_listener_guard(self):
|
||||
src = (VIEWS / "chat.js").read_text()
|
||||
assert "function ensurePrefillListener()" in src
|
||||
assert "if (prefillHandler) return;" in src
|
||||
|
||||
def test_render_uses_singleton_not_inline_addlistener(self):
|
||||
src = (VIEWS / "chat.js").read_text()
|
||||
assert "ensurePrefillListener();" in src
|
||||
# The listener is added exactly once, inside the guard helper.
|
||||
assert src.count("window.addEventListener('aiui:prefill-composer'") == 1
|
||||
|
||||
def test_cleanup_removes_listener(self):
|
||||
src = (VIEWS / "chat.js").read_text()
|
||||
assert "window.removeEventListener('aiui:prefill-composer', prefillHandler)" in src
|
||||
assert src.count("prefillHandler = null;") >= 2
|
||||
@@ -50,28 +50,28 @@ class TestCodeStudioFrontend:
|
||||
assert (VIEWS / "code-studio.js").is_file()
|
||||
|
||||
def test_view_exports_render(self):
|
||||
src = (VIEWS / "code-studio.js").read_text()
|
||||
src = (VIEWS / "code-studio.js").read_text(encoding="utf-8")
|
||||
assert "export async function render" in src
|
||||
assert "export function cleanup" in src
|
||||
|
||||
def test_view_calls_execute_api(self):
|
||||
src = (VIEWS / "code-studio.js").read_text()
|
||||
src = (VIEWS / "code-studio.js").read_text(encoding="utf-8")
|
||||
assert "/api/code/execute" in src
|
||||
assert "/api/code/languages" in src
|
||||
|
||||
def test_no_browser_eval(self):
|
||||
src = (VIEWS / "code-studio.js").read_text()
|
||||
src = (VIEWS / "code-studio.js").read_text(encoding="utf-8")
|
||||
assert "eval(" not in src
|
||||
|
||||
def test_registered_in_dashboard(self):
|
||||
src = (PLUGINS / "dashboard.js").read_text()
|
||||
src = (PLUGINS / "dashboard.js").read_text(encoding="utf-8")
|
||||
assert "'code-studio'" in src
|
||||
assert "code-studio.js" in src
|
||||
|
||||
def test_send_to_agent_event(self):
|
||||
src = (VIEWS / "code-studio.js").read_text()
|
||||
src = (VIEWS / "code-studio.js").read_text(encoding="utf-8")
|
||||
assert "aiui:prefill-composer" in src
|
||||
|
||||
def test_chat_has_prefill_listener(self):
|
||||
src = (VIEWS / "chat.js").read_text()
|
||||
src = (VIEWS / "chat.js").read_text(encoding="utf-8")
|
||||
assert "aiui:prefill-composer" in src
|
||||
@@ -22,7 +22,7 @@ DASHBOARD_JS = FRONTEND / "dashboard.js"
|
||||
|
||||
class TestCommandPalette:
|
||||
def _src(self) -> str:
|
||||
return DASHBOARD_JS.read_text()
|
||||
return DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
|
||||
def test_open_command_palette_exposed_globally(self):
|
||||
src = self._src()
|
||||
|
||||
+19
-19
@@ -86,7 +86,7 @@ class TestCompilerUiConfig:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["site"]["title"] == "Test Site"
|
||||
assert "templates" in ui_config
|
||||
assert "docs" in ui_config["templates"]
|
||||
@@ -96,7 +96,7 @@ class TestCompilerUiConfig:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert "header_main" in ui_config["components"]
|
||||
assert ui_config["components"]["header_main"]["props"]["logoText"] == "MySite"
|
||||
assert "footer_main" in ui_config["components"]
|
||||
@@ -107,7 +107,7 @@ class TestCompilerUiConfig:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8"))
|
||||
docs_template = ui_config["templates"]["docs"]
|
||||
assert docs_template["slots"]["header"]["ref"] == "header_main"
|
||||
assert docs_template["slots"]["footer"]["ref"] == "footer_main"
|
||||
@@ -123,7 +123,7 @@ class TestCompilerRouteManifest:
|
||||
assert "route-manifest.json" in result.files
|
||||
|
||||
manifest = json.loads(
|
||||
(tmp_path / "output" / "route-manifest.json").read_text()
|
||||
(tmp_path / "output" / "route-manifest.json").read_text(encoding="utf-8")
|
||||
)
|
||||
assert len(manifest["routes"]) == 1
|
||||
assert manifest["routes"][0]["pattern"] == "/docs/**"
|
||||
@@ -147,7 +147,7 @@ class TestCompilerRouteManifest:
|
||||
assert result.success is True
|
||||
|
||||
manifest = json.loads(
|
||||
(tmp_path / "output" / "route-manifest.json").read_text()
|
||||
(tmp_path / "output" / "route-manifest.json").read_text(encoding="utf-8")
|
||||
)
|
||||
# First route in list gets highest priority
|
||||
assert manifest["routes"][0]["priority"] > manifest["routes"][1]["priority"]
|
||||
@@ -200,7 +200,7 @@ class TestCompilerOutputFiles:
|
||||
compiler = Compiler(minimal_config, base_path=tmp_path)
|
||||
compiler.compile(tmp_path / "output", minify=True)
|
||||
|
||||
content = (tmp_path / "output" / "ui-config.json").read_text()
|
||||
content = (tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8")
|
||||
# Minified JSON should not have newlines
|
||||
assert "\n" not in content
|
||||
|
||||
@@ -208,7 +208,7 @@ class TestCompilerOutputFiles:
|
||||
compiler = Compiler(minimal_config, base_path=tmp_path)
|
||||
compiler.compile(tmp_path / "output", minify=False)
|
||||
|
||||
content = (tmp_path / "output" / "ui-config.json").read_text()
|
||||
content = (tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8")
|
||||
# Non-minified JSON should have newlines
|
||||
assert "\n" in content
|
||||
|
||||
@@ -228,7 +228,7 @@ class TestCompilerDocsContent:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert "docs-nav.json" in result.files
|
||||
|
||||
nav = json.loads((tmp_path / "output" / "docs-nav.json").read_text())
|
||||
nav = json.loads((tmp_path / "output" / "docs-nav.json").read_text(encoding="utf-8"))
|
||||
assert "items" in nav
|
||||
|
||||
def test_no_docs_nav_without_content(self, minimal_config: Config, tmp_path: Path):
|
||||
@@ -291,7 +291,7 @@ class TestCompilerThemeCss:
|
||||
assert result.success is True
|
||||
theme_css = tmp_path / "output" / "assets" / "theme.css"
|
||||
assert theme_css.exists(), "assets/theme.css must be generated"
|
||||
content = theme_css.read_text()
|
||||
content = theme_css.read_text(encoding="utf-8")
|
||||
assert ":root {" in content
|
||||
assert "--radius:" in content
|
||||
|
||||
@@ -340,7 +340,7 @@ class TestInjectThemeCss:
|
||||
inject_theme_css(tmp_path, preset="zinc", dark_mode=True, radius="0.5rem")
|
||||
theme_file = tmp_path / "assets" / "theme.css"
|
||||
assert theme_file.exists()
|
||||
content = theme_file.read_text()
|
||||
content = theme_file.read_text(encoding="utf-8")
|
||||
assert ":root {" in content
|
||||
|
||||
def test_creates_assets_dir(self, tmp_path: Path):
|
||||
@@ -367,7 +367,7 @@ class TestThemePipelineIntegration:
|
||||
assert result.success is True
|
||||
assert "assets/theme.css" in result.files
|
||||
|
||||
theme_css = (tmp_path / "output" / "assets" / "theme.css").read_text()
|
||||
theme_css = (tmp_path / "output" / "assets" / "theme.css").read_text(encoding="utf-8")
|
||||
assert ":root {" in theme_css
|
||||
assert ".dark {" in theme_css
|
||||
assert "--radius: 1rem" in theme_css # xl = 1rem
|
||||
@@ -386,7 +386,7 @@ class TestThemePipelineIntegration:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
theme_css = (tmp_path / "output" / "assets" / "theme.css").read_text()
|
||||
theme_css = (tmp_path / "output" / "assets" / "theme.css").read_text(encoding="utf-8")
|
||||
assert ":root {" in theme_css
|
||||
assert ".dark" not in theme_css
|
||||
|
||||
@@ -405,7 +405,7 @@ class TestThemePipelineIntegration:
|
||||
assert result.success is True
|
||||
|
||||
ui_config = json.loads(
|
||||
(tmp_path / "output" / "ui-config.json").read_text()
|
||||
(tmp_path / "output" / "ui-config.json").read_text(encoding="utf-8")
|
||||
)
|
||||
theme = ui_config["site"]["theme"]
|
||||
assert theme["preset"] == "blue"
|
||||
@@ -426,7 +426,7 @@ class TestThemePipelineIntegration:
|
||||
|
||||
theme_css_path = tmp_path / "output" / "assets" / "theme.css"
|
||||
assert theme_css_path.exists(), "theme.css must always be generated"
|
||||
content = theme_css_path.read_text()
|
||||
content = theme_css_path.read_text(encoding="utf-8")
|
||||
assert ":root {" in content
|
||||
assert "--radius:" in content
|
||||
|
||||
@@ -444,7 +444,7 @@ class TestThemePipelineIntegration:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
index_html = (tmp_path / "output" / "index.html").read_text()
|
||||
index_html = (tmp_path / "output" / "index.html").read_text(encoding="utf-8")
|
||||
assert "classList.add('dark')" not in index_html
|
||||
|
||||
def test_dark_mode_antiflicker_has_dark_class(self, tmp_path: Path):
|
||||
@@ -461,7 +461,7 @@ class TestThemePipelineIntegration:
|
||||
result = compiler.compile(tmp_path / "output")
|
||||
assert result.success is True
|
||||
|
||||
index_html = (tmp_path / "output" / "index.html").read_text()
|
||||
index_html = (tmp_path / "output" / "index.html").read_text(encoding="utf-8")
|
||||
assert "classList.add('dark')" in index_html
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ class TestCompositionResolver:
|
||||
|
||||
# Load ui-config.json and verify auto-bridging
|
||||
ui_config_path = tmp_path / "output" / "ui-config.json"
|
||||
ui_config = json.loads(ui_config_path.read_text())
|
||||
ui_config = json.loads(ui_config_path.read_text(encoding="utf-8"))
|
||||
|
||||
docs_template = ui_config["templates"]["docs"]
|
||||
assert "zones" in docs_template
|
||||
@@ -556,7 +556,7 @@ class TestCompositionResolver:
|
||||
|
||||
# Load ui-config.json and verify no auto-bridging occurred
|
||||
ui_config_path = tmp_path / "output" / "ui-config.json"
|
||||
ui_config = json.loads(ui_config_path.read_text())
|
||||
ui_config = json.loads(ui_config_path.read_text(encoding="utf-8"))
|
||||
|
||||
docs_template = ui_config["templates"]["docs"]
|
||||
# Should not have zones for non-FlexibleLayout
|
||||
@@ -589,7 +589,7 @@ class TestCompositionResolver:
|
||||
|
||||
# Load ui-config.json and verify no duplicate components
|
||||
ui_config_path = tmp_path / "output" / "ui-config.json"
|
||||
ui_config = json.loads(ui_config_path.read_text())
|
||||
ui_config = json.loads(ui_config_path.read_text(encoding="utf-8"))
|
||||
|
||||
docs_template = ui_config["templates"]["docs"]
|
||||
zones = docs_template["zones"]
|
||||
|
||||
@@ -30,7 +30,7 @@ class TestCompilerChatOutput:
|
||||
result = compiler.compile(tmp_path)
|
||||
|
||||
assert result.success
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["style"] == "chat"
|
||||
|
||||
def test_layout_in_ui_config(self, tmp_path: Path):
|
||||
@@ -43,7 +43,7 @@ class TestCompilerChatOutput:
|
||||
result = compiler.compile(tmp_path)
|
||||
|
||||
assert result.success
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["layout"]["mode"] == "bottom-right"
|
||||
assert ui_config["layout"]["width"] == "400px"
|
||||
assert ui_config["layout"]["height"] == "600px"
|
||||
@@ -73,7 +73,7 @@ class TestCompilerChatOutput:
|
||||
result = compiler.compile(tmp_path)
|
||||
|
||||
assert result.success
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["chat"]["enabled"] is True
|
||||
assert ui_config["chat"]["name"] == "AI Assistant"
|
||||
assert len(ui_config["chat"]["starters"]) == 1
|
||||
@@ -96,7 +96,7 @@ class TestCompilerChatOutput:
|
||||
result = compiler.compile(tmp_path)
|
||||
|
||||
assert result.success
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert ui_config["auth"]["enabled"] is True
|
||||
assert "password" in ui_config["auth"]["providers"]
|
||||
assert "google" in ui_config["auth"]["providers"]
|
||||
@@ -128,7 +128,7 @@ class TestCompilerChatOutput:
|
||||
result = compiler.compile(tmp_path)
|
||||
|
||||
assert result.success
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text())
|
||||
ui_config = json.loads((tmp_path / "ui-config.json").read_text(encoding="utf-8"))
|
||||
assert len(ui_config["widgets"]) == 2
|
||||
assert ui_config["widgets"][0]["type"] == "slider"
|
||||
assert ui_config["widgets"][0]["name"] == "temperature"
|
||||
|
||||
@@ -24,7 +24,7 @@ DASHBOARD_JS = (
|
||||
@pytest.fixture
|
||||
def dashboard_source():
|
||||
"""Read dashboard.js source."""
|
||||
return DASHBOARD_JS.read_text()
|
||||
return DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
class TestComponentRegistry:
|
||||
@@ -68,7 +68,7 @@ class TestPythonJSComponentParity:
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "src" / "praisonaiui" / "ui.py"
|
||||
)
|
||||
source = ui_py.read_text()
|
||||
source = ui_py.read_text(encoding="utf-8")
|
||||
|
||||
# Extract all "type": "xxx" patterns from ui.py
|
||||
py_types = set(re.findall(r'"type":\s*"(\w+)"', source))
|
||||
|
||||
@@ -111,7 +111,7 @@ class TestBuiltinSchemaCoverage:
|
||||
ui_src = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "src" / "praisonaiui" / "ui.py"
|
||||
).read_text()
|
||||
).read_text(encoding="utf-8")
|
||||
py_types = set(re.findall(r'"type":\s*"(\w+)"', ui_src))
|
||||
schemas = srv.get_component_schemas()
|
||||
missing = py_types - set(schemas)
|
||||
|
||||
@@ -136,47 +136,47 @@ class TestRingThresholds:
|
||||
|
||||
class TestChatJsComponents:
|
||||
def test_ring_dom_present(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-ring-wrap"' in src
|
||||
assert 'id="chat-ring-arc"' in src
|
||||
assert 'id="chat-ring-tooltip"' in src
|
||||
|
||||
def test_send_button_inside_ring_preserved_id(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'class="chat-ring-send" id="chat-send-btn"' in src
|
||||
assert 'aria-valuenow' in src
|
||||
assert 'aria-valuemax' in src
|
||||
|
||||
def test_compaction_banner_present(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-compaction-banner"' in src
|
||||
assert "function updateCompactionBanner(" in src
|
||||
assert "function dismissCompactionBanner(" in src
|
||||
|
||||
def test_memory_chip_present(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-memory-chip"' in src
|
||||
assert "function refreshMemoryChip(" in src
|
||||
|
||||
def test_ring_state_machine_thresholds(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "function ringStateFor(" in src
|
||||
assert "pct >= 95" in src
|
||||
assert "pct >= 85" in src
|
||||
assert "pct >= 60" in src
|
||||
|
||||
def test_ws_context_events_handled(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "case 'context_update':" in src
|
||||
assert "case 'context_compacted':" in src
|
||||
|
||||
def test_poll_fallback_and_debounce(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "function startContextPoll(" in src
|
||||
assert "function scheduleContextRefresh(" in src
|
||||
|
||||
def test_existing_memory_panel_and_metrics_preserved(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
# Regression: do not remove existing shipped elements
|
||||
assert 'id="chat-memory-panel"' in src
|
||||
assert 'id="chat-memory-btn"' in src
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
"""Unit tests for issue #208 — i18n wiring, per-turn memory search, and a11y.
|
||||
|
||||
Covers post-merge gaps left by PR #206 on the chat context ring UI:
|
||||
* Gap A — ring/banner/chip strings consume i18n via a JS ``t()`` helper.
|
||||
* Gap B — memory chip uses ``POST /api/memory/search`` (per-turn hits),
|
||||
not ``GET /api/memory`` (total store).
|
||||
* Gap C — ``role="progressbar"`` lives on the ring wrapper, not the send button.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from praisonaiui.features.i18n import JSONLocaleManager
|
||||
|
||||
FRONTEND = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "src" / "praisonaiui" / "templates" / "frontend" / "plugins"
|
||||
)
|
||||
CHAT_JS = FRONTEND / "views" / "chat.js"
|
||||
|
||||
|
||||
def _src() -> str:
|
||||
return CHAT_JS.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
class TestGapAI18nWiring:
|
||||
def test_i18n_loader_present(self):
|
||||
src = _src()
|
||||
assert "async function loadI18nStrings(" in src
|
||||
assert "/api/i18n/strings/" in src
|
||||
assert "await loadI18nStrings()" in src
|
||||
|
||||
def test_t_helper_with_fallback(self):
|
||||
src = _src()
|
||||
assert "function t(key, params)" in src
|
||||
assert "I18N_FALLBACK" in src
|
||||
|
||||
def test_i18n_used_at_least_four_times(self):
|
||||
# E2E regression bar from the issue: i18n key lookups >= 4
|
||||
src = _src()
|
||||
assert src.count("t('chat.") >= 4
|
||||
|
||||
def test_banner_uses_i18n_key(self):
|
||||
src = _src()
|
||||
assert "t('chat.compaction.banner')" in src
|
||||
|
||||
def test_compacted_flash_uses_i18n_key(self):
|
||||
src = _src()
|
||||
assert "t('chat.compaction.compacted')" in src
|
||||
|
||||
def test_memory_chip_uses_i18n_key(self):
|
||||
src = _src()
|
||||
assert "t('chat.memory.chip'" in src
|
||||
assert "t('chat.memory.empty_turn')" in src
|
||||
|
||||
def test_tooltip_uses_context_status_keys(self):
|
||||
src = _src()
|
||||
assert "chat.context.critical" in src
|
||||
assert "chat.context.warning" in src
|
||||
assert "chat.context.healthy" in src
|
||||
assert "t('chat.context.estimate')" in src
|
||||
|
||||
|
||||
class TestGapBMemorySearch:
|
||||
def test_search_endpoint_used(self):
|
||||
# E2E regression bar: memory/search present >= 1
|
||||
src = _src()
|
||||
assert "/api/memory/search" in src
|
||||
|
||||
def test_search_is_post_with_query(self):
|
||||
src = _src()
|
||||
assert "method: 'POST'" in src
|
||||
assert "lastUserMessage" in src
|
||||
assert "query," in src
|
||||
|
||||
def test_chip_counts_results_not_total(self):
|
||||
src = _src()
|
||||
assert "data.results || []" in src
|
||||
assert "results.length" in src
|
||||
|
||||
def test_last_user_message_tracked_on_send(self):
|
||||
src = _src()
|
||||
assert "lastUserMessage = content" in src
|
||||
|
||||
|
||||
class TestGapCAccessibility:
|
||||
def test_progressbar_role_on_wrapper(self):
|
||||
src = _src()
|
||||
# Wrapper div carries the progressbar role + aria values
|
||||
assert 'id="chat-ring-wrap"' in src
|
||||
wrap_idx = src.index('id="chat-ring-wrap"')
|
||||
wrap_slice = src[wrap_idx:wrap_idx + 260]
|
||||
assert 'role="progressbar"' in wrap_slice
|
||||
assert "aria-valuenow" in wrap_slice
|
||||
assert "aria-valuemax" in wrap_slice
|
||||
|
||||
def test_send_button_is_button_not_progressbar(self):
|
||||
src = _src()
|
||||
btn_idx = src.index('id="chat-send-btn"')
|
||||
btn_slice = src[btn_idx:btn_idx + 160]
|
||||
assert 'type="button"' in btn_slice
|
||||
assert 'role="progressbar"' not in btn_slice
|
||||
assert 'aria-label="Send message"' in btn_slice
|
||||
|
||||
def test_aria_updates_target_wrapper(self):
|
||||
src = _src()
|
||||
assert "wrap.setAttribute('aria-valuenow'" in src
|
||||
|
||||
|
||||
class TestI18nMemoryChipLocales:
|
||||
def test_chip_translation_interpolates_count_all_locales(self):
|
||||
mgr = JSONLocaleManager()
|
||||
for locale, word in (("en", "memories"), ("es", "recuerdos"), ("fr", "souvenirs")):
|
||||
out = mgr.t("chat.memory.chip", locale=locale, count=2)
|
||||
assert "2" in out
|
||||
assert word in out
|
||||
|
||||
def test_banner_localised(self):
|
||||
mgr = JSONLocaleManager()
|
||||
assert mgr.t("chat.compaction.banner", locale="es") != mgr.t(
|
||||
"chat.compaction.banner", locale="en"
|
||||
)
|
||||
assert mgr.t("chat.compaction.banner", locale="fr") != mgr.t(
|
||||
"chat.compaction.banner", locale="en"
|
||||
)
|
||||
@@ -27,7 +27,7 @@ DASHBOARD_JS = (
|
||||
|
||||
|
||||
def _style() -> str:
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
m = re.search(r"const DASHBOARD_STYLE = `(.*?)`;", src, re.DOTALL)
|
||||
assert m, "DASHBOARD_STYLE template literal not found"
|
||||
return m.group(1)
|
||||
|
||||
@@ -21,7 +21,7 @@ DASHBOARD_JS = FRONTEND / "dashboard.js"
|
||||
|
||||
class TestSelectPageGuard:
|
||||
def _select_page_body(self) -> str:
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r"async function selectPage\(pageId\)\s*\{(.*?)^}",
|
||||
src, re.DOTALL | re.MULTILINE,
|
||||
@@ -56,7 +56,7 @@ class TestSelectPageGuard:
|
||||
assert "console.error" in m.group(1)
|
||||
|
||||
def test_show_dashboard_init_error_helper_present(self):
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r"function showDashboardInitError\(message\)\s*\{(.*?)^}",
|
||||
src, re.DOTALL | re.MULTILINE,
|
||||
|
||||
@@ -23,7 +23,7 @@ GENERATOR = ROOT / "scripts" / "patch_dashboard_modular.py"
|
||||
|
||||
class TestDashboardShellOrder:
|
||||
def _build_body(self) -> str:
|
||||
src = DASHBOARD_JS.read_text()
|
||||
src = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r"function buildDashboard\([^)]*\)\s*\{(.*?)^}",
|
||||
src, re.DOTALL | re.MULTILINE,
|
||||
@@ -51,7 +51,7 @@ class TestDashboardShellOrder:
|
||||
assert m, "shellHeader insert must be guarded by children.length"
|
||||
|
||||
def test_generator_emits_main_before_insert_before(self):
|
||||
src = GENERATOR.read_text()
|
||||
src = GENERATOR.read_text(encoding="utf-8")
|
||||
append_idx = src.find("root.appendChild(main);\n const shellHeader")
|
||||
insert_idx = src.find("root.insertBefore(shellHeader, main)")
|
||||
assert append_idx != -1, (
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Tests for the shared examples console helper.
|
||||
|
||||
These verify that examples degrade emoji to ASCII on legacy Windows code pages
|
||||
(cp1252) instead of raising ``UnicodeEncodeError`` while preserving emoji on
|
||||
UTF-aware terminals, mirroring the CLI ``_icon``/``_supports_unicode`` pattern.
|
||||
"""
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "examples", "python", "_shared"))
|
||||
from console import icon, safe_print, supports_unicode
|
||||
|
||||
|
||||
class TestSupportsUnicode:
|
||||
def test_utf8_encoding_is_supported(self):
|
||||
mock_stdout = mock.MagicMock()
|
||||
mock_stdout.encoding = "utf-8"
|
||||
with mock.patch("sys.stdout", mock_stdout):
|
||||
assert supports_unicode() is True
|
||||
|
||||
def test_cp1252_encoding_is_not_supported(self):
|
||||
mock_stdout = mock.MagicMock()
|
||||
mock_stdout.encoding = "cp1252"
|
||||
with mock.patch("sys.stdout", mock_stdout):
|
||||
assert supports_unicode() is False
|
||||
|
||||
def test_missing_encoding_falls_back_to_ascii(self):
|
||||
mock_stdout = mock.MagicMock()
|
||||
mock_stdout.encoding = None
|
||||
with mock.patch("sys.stdout", mock_stdout):
|
||||
assert supports_unicode() is False
|
||||
|
||||
|
||||
class TestIcon:
|
||||
def test_returns_symbol_on_utf8(self):
|
||||
mock_stdout = mock.MagicMock()
|
||||
mock_stdout.encoding = "utf-8"
|
||||
with mock.patch("sys.stdout", mock_stdout):
|
||||
assert icon("🚀", "[START]") == "🚀"
|
||||
|
||||
def test_returns_fallback_on_cp1252(self):
|
||||
mock_stdout = mock.MagicMock()
|
||||
mock_stdout.encoding = "cp1252"
|
||||
with mock.patch("sys.stdout", mock_stdout):
|
||||
assert icon("🚀", "[START]") == "[START]"
|
||||
|
||||
|
||||
class TestSafePrint:
|
||||
def test_never_raises_on_cp1252(self):
|
||||
buffer = io.BytesIO()
|
||||
stream = io.TextIOWrapper(buffer, encoding="cp1252", errors="strict")
|
||||
with mock.patch("sys.stdout", stream):
|
||||
safe_print("🚀 Starting")
|
||||
stream.flush()
|
||||
assert buffer.getvalue()
|
||||
|
||||
def test_preserves_unicode_on_utf8(self):
|
||||
buffer = io.StringIO()
|
||||
with mock.patch("sys.stdout", buffer):
|
||||
safe_print("🚀 Starting")
|
||||
assert "🚀" in buffer.getvalue()
|
||||
|
||||
def test_respects_sep(self):
|
||||
buffer = io.StringIO()
|
||||
with mock.patch("sys.stdout", buffer):
|
||||
safe_print("a", "b", sep="-")
|
||||
assert buffer.getvalue().strip() == "a-b"
|
||||
@@ -154,13 +154,13 @@ class TestFinOpsValidation:
|
||||
|
||||
class TestHelpersJs:
|
||||
def test_donut_and_strip_helpers_present(self):
|
||||
src = HELPERS_JS.read_text()
|
||||
src = HELPERS_JS.read_text(encoding="utf-8")
|
||||
assert "export function MiniDonutSVG(" in src
|
||||
assert "export function costStripHTML(" in src
|
||||
assert "export function budgetBannerHTML(" in src
|
||||
|
||||
def test_math_helpers_present(self):
|
||||
src = HELPERS_JS.read_text()
|
||||
src = HELPERS_JS.read_text(encoding="utf-8")
|
||||
assert "export function computeBudgetPct(" in src
|
||||
assert "export function budgetLevelFor(" in src
|
||||
assert "export function sumTodayTokens(" in src
|
||||
@@ -168,53 +168,53 @@ class TestHelpersJs:
|
||||
assert "export function formatCost(" in src
|
||||
|
||||
def test_donut_empty_state_accessible(self):
|
||||
src = HELPERS_JS.read_text()
|
||||
src = HELPERS_JS.read_text(encoding="utf-8")
|
||||
assert 'aria-label="No model data"' in src
|
||||
|
||||
def test_strip_navigates_and_is_region(self):
|
||||
src = HELPERS_JS.read_text()
|
||||
src = HELPERS_JS.read_text(encoding="utf-8")
|
||||
assert 'data-nav="usage"' in src
|
||||
assert 'role="region"' in src
|
||||
|
||||
|
||||
class TestOverviewJs:
|
||||
def test_finops_imports_and_render(self):
|
||||
src = OVERVIEW_JS.read_text()
|
||||
src = OVERVIEW_JS.read_text(encoding="utf-8")
|
||||
assert "costStripHTML" in src
|
||||
assert "budgetBannerHTML" in src
|
||||
assert "function renderFinOps(" in src
|
||||
assert 'id="ov-finops"' in src
|
||||
|
||||
def test_fetches_models_and_config(self):
|
||||
src = OVERVIEW_JS.read_text()
|
||||
src = OVERVIEW_JS.read_text(encoding="utf-8")
|
||||
assert "/api/usage/models" in src
|
||||
assert "/api/config/runtime" in src
|
||||
|
||||
def test_graceful_degrade_state_machine(self):
|
||||
src = OVERVIEW_JS.read_text()
|
||||
src = OVERVIEW_JS.read_text(encoding="utf-8")
|
||||
assert "function finopsState(" in src
|
||||
assert "finops.enabled === false" in src
|
||||
|
||||
|
||||
class TestChatJs:
|
||||
def test_session_cost_chip_present(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-session-cost-chip"' in src
|
||||
assert "function updateSessionCostChip(" in src
|
||||
|
||||
def test_finops_banner_present(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-finops-banner"' in src
|
||||
assert "function updateFinopsBanner(" in src
|
||||
assert "function loadFinopsConfig(" in src
|
||||
|
||||
def test_chip_updates_on_run_completed(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert "updateFinopsBanner()" in src
|
||||
assert "updateSessionCostChip(stats)" in src
|
||||
|
||||
def test_context_ring_regression_preserved(self):
|
||||
src = CHAT_JS.read_text()
|
||||
src = CHAT_JS.read_text(encoding="utf-8")
|
||||
assert 'id="chat-ring-wrap"' in src
|
||||
assert "function ringStateFor(" in src
|
||||
assert 'class="chat-ring-send" id="chat-send-btn"' in src
|
||||
|
||||
@@ -108,7 +108,7 @@ class TestFormInputDataLabel:
|
||||
/ "src" / "praisonaiui" / "templates" / "frontend"
|
||||
/ "plugins" / "dashboard.js"
|
||||
)
|
||||
return p.read_text()
|
||||
return p.read_text(encoding="utf-8")
|
||||
|
||||
@pytest.mark.parametrize("renderer", [
|
||||
"renderTextInput",
|
||||
|
||||
@@ -84,7 +84,7 @@ class TestRenderersEmitName:
|
||||
via `el.name` (which has priority over `el.dataset.label`)."""
|
||||
|
||||
def _src(self):
|
||||
return DASHBOARD_JS.read_text()
|
||||
return DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
|
||||
@pytest.mark.parametrize("renderer", [
|
||||
"renderTextInput",
|
||||
@@ -117,7 +117,7 @@ class TestSubmitCollectorPriority:
|
||||
"""renderFormAction submit handler must prefer `name` over `data-label`."""
|
||||
|
||||
def test_name_has_priority_over_label(self):
|
||||
source = DASHBOARD_JS.read_text()
|
||||
source = DASHBOARD_JS.read_text(encoding="utf-8")
|
||||
m = re.search(
|
||||
r"function renderFormAction\(comp\)\s*\{(.*?)^}",
|
||||
source, re.DOTALL | re.MULTILINE,
|
||||
|
||||
@@ -16,7 +16,7 @@ class TestFrontendNavigation:
|
||||
index_path = Path("src/praisonaiui/templates/frontend/index.html")
|
||||
assert index_path.exists(), "index.html should exist"
|
||||
|
||||
content = index_path.read_text()
|
||||
content = index_path.read_text(encoding="utf-8")
|
||||
assert "/assets/index.js" in content, "Should reference index.js"
|
||||
assert "/assets/index.css" in content, "Should reference index.css"
|
||||
|
||||
@@ -26,7 +26,7 @@ class TestFrontendNavigation:
|
||||
if bundle_path.exists():
|
||||
# Check if the bundle contains popstate handling code
|
||||
# Note: The bundle is minified, so we check for the event name
|
||||
content = bundle_path.read_text()
|
||||
content = bundle_path.read_text(encoding="utf-8")
|
||||
assert "popstate" in content, "Bundle should contain popstate event handler"
|
||||
|
||||
def test_title_template_handling(self):
|
||||
|
||||
@@ -23,7 +23,7 @@ SERVER = _ROOT / "src" / "praisonaiui" / "server.py"
|
||||
|
||||
|
||||
def _inbox() -> str:
|
||||
return INBOX_VIEW.read_text()
|
||||
return INBOX_VIEW.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
class TestThreePaneShell:
|
||||
@@ -109,21 +109,21 @@ class TestEmptyState:
|
||||
|
||||
class TestRegistration:
|
||||
def test_route_registered_in_dashboard(self):
|
||||
src = DASHBOARD.read_text()
|
||||
src = DASHBOARD.read_text(encoding="utf-8")
|
||||
assert "inbox:" in src
|
||||
assert "/plugins/views/inbox.js" in src
|
||||
|
||||
def test_page_registered_in_server(self):
|
||||
src = SERVER.read_text()
|
||||
src = SERVER.read_text(encoding="utf-8")
|
||||
assert '"id": "inbox"' in src
|
||||
assert "Channel Inbox" in src
|
||||
|
||||
def test_channels_links_to_inbox(self):
|
||||
assert 'href="/inbox"' in CHANNELS_VIEW.read_text()
|
||||
assert 'href="/inbox"' in CHANNELS_VIEW.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
class TestSessionsPlatformField:
|
||||
def test_sessions_list_surfaces_platform(self):
|
||||
src = SESSIONS_EXT.read_text()
|
||||
src = SESSIONS_EXT.read_text(encoding="utf-8")
|
||||
assert '"platform"' in src
|
||||
assert '"channel_id"' in src
|
||||
@@ -0,0 +1,173 @@
|
||||
"""Tests for embedding-model fallback and degraded-mode reporting.
|
||||
|
||||
Covers issue #240: knowledge search degrades silently when the default
|
||||
embedding model is unavailable (403 model_not_found).
|
||||
|
||||
Verifies:
|
||||
- SDKKnowledgeManager marks embedding unavailable on model_not_found search failure
|
||||
- last_search_meta() reports mode="fallback" with an actionable warning
|
||||
- health() exposes embedding.available=false + warnings
|
||||
- non-embedding SDK errors do NOT flag embedding as unavailable
|
||||
- /v1/embeddings resolves env-configured default + fallback chain
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from praisonaiui.features.knowledge import (
|
||||
SDKKnowledgeManager,
|
||||
SimpleKnowledgeManager,
|
||||
_is_embedding_error,
|
||||
)
|
||||
|
||||
|
||||
class _FakeSDK:
|
||||
"""Minimal fake SDK Knowledge that raises a chosen error on search."""
|
||||
|
||||
def __init__(self, error: Exception | None = None, probe_error: Exception | None = None):
|
||||
self._error = error
|
||||
self._probe_error = probe_error
|
||||
self._probe_pending = probe_error is not None
|
||||
|
||||
def search(self, query, limit=10, user_id=None):
|
||||
if self._probe_pending:
|
||||
self._probe_pending = False
|
||||
raise self._probe_error
|
||||
if self._error is not None:
|
||||
raise self._error
|
||||
return []
|
||||
|
||||
|
||||
def _manager_with_sdk(sdk) -> SDKKnowledgeManager:
|
||||
"""Wire a fake SDK as an already-probed working backend."""
|
||||
mgr = SDKKnowledgeManager()
|
||||
mgr._sdk_knowledge = sdk
|
||||
mgr._sdk_probed = True
|
||||
return mgr
|
||||
|
||||
|
||||
# ── error detection ──────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"msg",
|
||||
[
|
||||
"Project does not have access to model `text-embedding-3-small`",
|
||||
"The model does not exist or you do not have access to it",
|
||||
"litellm.NotFoundError: model_not_found",
|
||||
],
|
||||
)
|
||||
def test_is_embedding_error_true(msg):
|
||||
assert _is_embedding_error(msg) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("msg", ["empty db", "connection refused", "timeout"])
|
||||
def test_is_embedding_error_false(msg):
|
||||
assert _is_embedding_error(msg) is False
|
||||
|
||||
|
||||
# ── degraded-mode reporting ──────────────────────────────────────────
|
||||
|
||||
|
||||
def test_search_reports_fallback_on_embedding_error():
|
||||
err = Exception("Project does not have access to model `text-embedding-3-small`")
|
||||
mgr = _manager_with_sdk(_FakeSDK(error=err))
|
||||
mgr.store("PraisonAIUI is a YAML documentation generator")
|
||||
|
||||
results = mgr.search("PraisonAIUI")
|
||||
|
||||
meta = mgr.last_search_meta()
|
||||
assert meta["mode"] == "fallback"
|
||||
assert meta["error"]
|
||||
assert "AIUI_EMBEDDING_MODEL" in meta["error"]
|
||||
assert results
|
||||
|
||||
|
||||
def test_health_reports_embedding_unavailable():
|
||||
err = Exception("model_not_found: text-embedding-3-small")
|
||||
mgr = _manager_with_sdk(_FakeSDK(error=err))
|
||||
mgr.search("anything")
|
||||
|
||||
h = mgr.health()
|
||||
assert h["embedding"]["available"] is False
|
||||
assert h["status"] == "degraded"
|
||||
assert h["warnings"]
|
||||
|
||||
|
||||
def test_non_embedding_error_does_not_flag_embedding():
|
||||
err = Exception("some transient database hiccup")
|
||||
mgr = _manager_with_sdk(_FakeSDK(error=err))
|
||||
mgr.search("anything")
|
||||
|
||||
h = mgr.health()
|
||||
assert h["embedding"]["available"] is True
|
||||
assert mgr.last_search_meta()["error"] is None
|
||||
|
||||
|
||||
def test_vector_mode_reported_on_success():
|
||||
class _OkSDK:
|
||||
def search(self, query, limit=10, user_id=None):
|
||||
return [{"id": "1", "memory": "hit", "score": 0.9}]
|
||||
|
||||
mgr = _manager_with_sdk(_OkSDK())
|
||||
results = mgr.search("q")
|
||||
assert results
|
||||
assert mgr.last_search_meta()["mode"] == "vector"
|
||||
|
||||
|
||||
def test_probe_flags_embedding_unavailable(monkeypatch):
|
||||
probe_err = Exception("Project does not have access to model `text-embedding-3-small`")
|
||||
|
||||
class _KnowledgeStub:
|
||||
def __init__(self, *a, **k):
|
||||
self._raised = False
|
||||
|
||||
def search(self, *a, **k):
|
||||
if not self._raised:
|
||||
self._raised = True
|
||||
raise probe_err
|
||||
return []
|
||||
|
||||
import sys
|
||||
import types
|
||||
|
||||
mod = types.ModuleType("praisonaiagents.knowledge")
|
||||
mod.Knowledge = _KnowledgeStub
|
||||
monkeypatch.setitem(sys.modules, "praisonaiagents.knowledge", mod)
|
||||
|
||||
mgr = SDKKnowledgeManager()
|
||||
mgr._get_sdk_knowledge()
|
||||
assert mgr._embedding_unavailable is True
|
||||
assert mgr._sdk_knowledge is not None # SDK stays active, embeddings flagged
|
||||
|
||||
|
||||
def test_simple_manager_default_meta():
|
||||
mgr = SimpleKnowledgeManager()
|
||||
assert mgr.last_search_meta()["mode"] == "local"
|
||||
|
||||
|
||||
# ── embedding-model resolution ───────────────────────────────────────
|
||||
|
||||
|
||||
def test_default_embedding_model_env(monkeypatch):
|
||||
from praisonaiui.features.openai_api import _default_embedding_model
|
||||
|
||||
monkeypatch.delenv("OPENAI_EMBEDDING_MODEL", raising=False)
|
||||
monkeypatch.setenv("AIUI_EMBEDDING_MODEL", "text-embedding-ada-002")
|
||||
assert _default_embedding_model() == "text-embedding-ada-002"
|
||||
|
||||
|
||||
def test_default_embedding_model_fallback_default(monkeypatch):
|
||||
from praisonaiui.features.openai_api import _default_embedding_model
|
||||
|
||||
monkeypatch.delenv("AIUI_EMBEDDING_MODEL", raising=False)
|
||||
monkeypatch.delenv("OPENAI_EMBEDDING_MODEL", raising=False)
|
||||
assert _default_embedding_model() == "text-embedding-3-small"
|
||||
|
||||
|
||||
def test_embedding_fallback_models_env(monkeypatch):
|
||||
from praisonaiui.features.openai_api import _embedding_fallback_models
|
||||
|
||||
monkeypatch.setenv("AIUI_EMBEDDING_FALLBACK_MODELS", "a, b ,, c")
|
||||
assert _embedding_fallback_models() == ["a", "b", "c"]
|
||||
@@ -0,0 +1,194 @@
|
||||
"""Unit tests for knowledge embedding fallback + degraded-mode reporting."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from praisonaiui.features import _features, register_feature
|
||||
from praisonaiui.features import knowledge as knowledge_mod
|
||||
from praisonaiui.features.knowledge import (
|
||||
KnowledgeFeature,
|
||||
SDKKnowledgeManager,
|
||||
SimpleKnowledgeManager,
|
||||
_is_embedding_unavailable,
|
||||
)
|
||||
from praisonaiui.features.openai_api import (
|
||||
OpenAIAPIFeature,
|
||||
_default_embedding_model,
|
||||
_embedding_fallback_models,
|
||||
_is_model_not_found,
|
||||
)
|
||||
from praisonaiui.server import create_app
|
||||
|
||||
|
||||
class _FakeSDK:
|
||||
def __init__(self, search_error: BaseException | None = None) -> None:
|
||||
self._search_error = search_error
|
||||
|
||||
def search(self, query, limit=1, user_id=None):
|
||||
if query == "__probe__":
|
||||
return []
|
||||
if self._search_error is not None:
|
||||
raise self._search_error
|
||||
return []
|
||||
|
||||
|
||||
def test_is_model_not_found_detects_403():
|
||||
assert _is_model_not_found(Exception("Project does not have access to model x"))
|
||||
assert _is_model_not_found(Exception("litellm.NotFoundError: nope"))
|
||||
assert not _is_model_not_found(Exception("timeout"))
|
||||
|
||||
|
||||
def test_is_embedding_unavailable_detects_403():
|
||||
assert _is_embedding_unavailable(Exception("model_not_found"))
|
||||
assert not _is_embedding_unavailable(Exception("connection reset"))
|
||||
|
||||
|
||||
def test_default_embedding_model_env(monkeypatch):
|
||||
monkeypatch.delenv("AIUI_EMBEDDING_MODEL", raising=False)
|
||||
monkeypatch.delenv("OPENAI_EMBEDDING_MODEL", raising=False)
|
||||
assert _default_embedding_model() == "text-embedding-3-small"
|
||||
monkeypatch.setenv("AIUI_EMBEDDING_MODEL", "text-embedding-ada-002")
|
||||
assert _default_embedding_model() == "text-embedding-ada-002"
|
||||
|
||||
|
||||
def test_fallback_models_env(monkeypatch):
|
||||
monkeypatch.setenv("AIUI_EMBEDDING_FALLBACK_MODELS", "a, b ,,c")
|
||||
assert _embedding_fallback_models() == ["a", "b", "c"]
|
||||
|
||||
|
||||
def test_simple_manager_reports_local_mode():
|
||||
mgr = SimpleKnowledgeManager()
|
||||
mgr.store("hello world")
|
||||
mgr.search("hello")
|
||||
assert mgr.last_search_meta()["mode"] == "local"
|
||||
assert mgr.health()["embedding"]["available"] is False
|
||||
|
||||
|
||||
def test_sdk_search_reports_fallback_on_embedding_error():
|
||||
mgr = SDKKnowledgeManager()
|
||||
err = Exception("Project does not have access to model text-embedding-3-small")
|
||||
mgr._sdk_knowledge = _FakeSDK(search_error=err)
|
||||
mgr._sdk_probed = True
|
||||
|
||||
mgr.store("PraisonAIUI is a YAML-driven UI generator.")
|
||||
mgr.search("documentation generator")
|
||||
|
||||
meta = mgr.last_search_meta()
|
||||
assert meta["mode"] == "fallback"
|
||||
assert "text-embedding" in meta["error"].lower()
|
||||
|
||||
health = mgr.health()
|
||||
assert health["embedding"]["available"] is False
|
||||
assert health["status"] == "degraded"
|
||||
assert health["warnings"]
|
||||
|
||||
|
||||
def test_sdk_search_vector_mode_when_healthy():
|
||||
class _OkSDK:
|
||||
def search(self, query, limit=1, user_id=None):
|
||||
if query == "__probe__":
|
||||
return []
|
||||
return [{"id": "1", "memory": "hit", "score": 0.9}]
|
||||
|
||||
mgr = SDKKnowledgeManager()
|
||||
mgr._sdk_knowledge = _OkSDK()
|
||||
mgr._sdk_probed = True
|
||||
results = mgr.search("anything")
|
||||
assert results
|
||||
assert mgr.last_search_meta()["mode"] == "vector"
|
||||
|
||||
|
||||
def test_probe_flags_embedding_unavailable():
|
||||
mgr = SDKKnowledgeManager()
|
||||
|
||||
class _Kn:
|
||||
def search(self, query, limit=1):
|
||||
raise Exception("model_not_found: no access")
|
||||
|
||||
with patch("praisonaiagents.knowledge.Knowledge", return_value=_Kn(), create=True):
|
||||
mgr._get_sdk_knowledge()
|
||||
assert mgr._embedding_unavailable is True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
saved = dict(_features)
|
||||
saved_mgr = knowledge_mod._knowledge_manager
|
||||
_features.clear()
|
||||
register_feature(KnowledgeFeature())
|
||||
register_feature(OpenAIAPIFeature())
|
||||
yield TestClient(create_app())
|
||||
_features.clear()
|
||||
_features.update(saved)
|
||||
knowledge_mod._knowledge_manager = saved_mgr
|
||||
|
||||
|
||||
def test_knowledge_search_reports_fallback(client):
|
||||
mgr = SDKKnowledgeManager()
|
||||
err = Exception("Project does not have access to model text-embedding-3-small")
|
||||
mgr._sdk_knowledge = _FakeSDK(search_error=err)
|
||||
mgr._sdk_probed = True
|
||||
mgr.store("PraisonAIUI is a YAML-driven documentation site generator.")
|
||||
knowledge_mod._knowledge_manager = mgr
|
||||
|
||||
r = client.post("/api/knowledge/search", json={"query": "documentation generator YAML"})
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["search_mode"] == "fallback"
|
||||
assert body["warning"]
|
||||
assert "embedding" in body["warning"].lower()
|
||||
|
||||
|
||||
def test_knowledge_status_reports_embedding(client):
|
||||
mgr = SDKKnowledgeManager()
|
||||
mgr._sdk_knowledge = _FakeSDK()
|
||||
mgr._sdk_probed = True
|
||||
mgr._embedding_unavailable = True
|
||||
mgr._embedding_error = "no access"
|
||||
knowledge_mod._knowledge_manager = mgr
|
||||
|
||||
r = client.get("/api/knowledge/status")
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["embedding"]["available"] is False
|
||||
assert body["warnings"]
|
||||
|
||||
|
||||
def test_embeddings_endpoint_fallback_models(client):
|
||||
call_models: list[str] = []
|
||||
|
||||
async def _aembed(input, model):
|
||||
call_models.append(model)
|
||||
if model == "text-embedding-3-small":
|
||||
raise Exception("Project does not have access to model text-embedding-3-small")
|
||||
return {"embedding": [0.1, 0.2], "usage": {"prompt_tokens": 1, "total_tokens": 1}}
|
||||
|
||||
fake_caps = type("Caps", (), {"aembed": staticmethod(AsyncMock(side_effect=_aembed))})
|
||||
with patch(
|
||||
"praisonaiui.features.openai_api._get_capabilities", return_value=fake_caps
|
||||
):
|
||||
r = client.post(
|
||||
"/v1/embeddings",
|
||||
json={"input": "test", "model": "text-embedding-3-small"},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["model"] == "text-embedding-ada-002"
|
||||
assert call_models[0] == "text-embedding-3-small"
|
||||
|
||||
|
||||
def test_embeddings_endpoint_all_fail_returns_503(client):
|
||||
async def _aembed(input, model):
|
||||
raise Exception("model_not_found: no access to any embedding model")
|
||||
|
||||
fake_caps = type("Caps", (), {"aembed": staticmethod(AsyncMock(side_effect=_aembed))})
|
||||
with patch(
|
||||
"praisonaiui.features.openai_api._get_capabilities", return_value=fake_caps
|
||||
):
|
||||
r = client.post("/v1/embeddings", json={"input": "test"})
|
||||
assert r.status_code == 503
|
||||
assert r.json()["error"]["type"] == "model_not_found"
|
||||
@@ -0,0 +1,121 @@
|
||||
"""Unit tests for MkDocs Material markdown preprocessing (mirrors frontend logic)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
MKDOCS_ICON_MAP = {
|
||||
":material-file-document:": "📄",
|
||||
":material-puzzle:": "🧩",
|
||||
":material-palette:": "🎨",
|
||||
}
|
||||
|
||||
TAB_HEADING_PREFIX = "\u200btab:"
|
||||
|
||||
|
||||
def replace_mkdocs_icons(text: str) -> str:
|
||||
result = text
|
||||
for shortcode, emoji in MKDOCS_ICON_MAP.items():
|
||||
result = result.replace(shortcode, emoji)
|
||||
result = re.sub(r":material-[\w-]+:", "•", result)
|
||||
result = re.sub(r":octicons-[\w-]+(?:-\d+)?:", "•", result)
|
||||
return result
|
||||
|
||||
|
||||
def strip_mkdocs_grid_wrappers(text: str) -> str:
|
||||
text = re.sub(r"<div[^>]*markdown[^>]*>\s*", "", text, flags=re.IGNORECASE)
|
||||
return re.sub(r"</div>\s*", "", text, flags=re.IGNORECASE)
|
||||
|
||||
|
||||
def convert_mkdocs_tabs(md: str) -> str:
|
||||
lines = md.split("\n")
|
||||
out: list[str] = []
|
||||
i = 0
|
||||
|
||||
while i < len(lines):
|
||||
tab_match = re.match(r'^===\s+"([^"]+)"\s*$', lines[i])
|
||||
if not tab_match:
|
||||
out.append(lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
title = tab_match.group(1)
|
||||
i += 1
|
||||
while i < len(lines) and lines[i].strip() == "":
|
||||
i += 1
|
||||
|
||||
block: list[str] = []
|
||||
while i < len(lines):
|
||||
if re.match(r'^===\s+"[^"]+"\s*$', lines[i]):
|
||||
break
|
||||
if (
|
||||
lines[i].strip() == ""
|
||||
and i + 1 < len(lines)
|
||||
and re.match(r'^===\s+"[^"]+"\s*$', lines[i + 1])
|
||||
):
|
||||
break
|
||||
|
||||
if lines[i].startswith(" ") or lines[i].lstrip().startswith("```"):
|
||||
block.append(lines[i][4:] if lines[i].startswith(" ") else lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if block and lines[i].strip() == "":
|
||||
block.append("")
|
||||
i += 1
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
out.append(f"### {TAB_HEADING_PREFIX}{title}")
|
||||
out.append("")
|
||||
out.extend(block)
|
||||
out.append("")
|
||||
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def preprocess_mkdocs_markdown(md: str) -> str:
|
||||
result = strip_mkdocs_grid_wrappers(md)
|
||||
result = replace_mkdocs_icons(result)
|
||||
return convert_mkdocs_tabs(result)
|
||||
|
||||
|
||||
class TestMkdocsPreprocess:
|
||||
def test_strip_grid_wrappers(self):
|
||||
md = '<div class="grid cards" markdown>\n\n- item\n\n</div>'
|
||||
assert "<div" not in preprocess_mkdocs_markdown(md)
|
||||
assert "</div>" not in preprocess_mkdocs_markdown(md)
|
||||
assert "- item" in preprocess_mkdocs_markdown(md)
|
||||
|
||||
def test_replace_material_icons(self):
|
||||
md = "- :material-file-document: **YAML-Driven**"
|
||||
out = preprocess_mkdocs_markdown(md)
|
||||
assert "📄" in out
|
||||
assert ":material-file-document:" not in out
|
||||
|
||||
def test_convert_tabs_with_indented_code(self):
|
||||
md = '''=== "Python"
|
||||
|
||||
```bash
|
||||
pip install praisonaiui
|
||||
```
|
||||
|
||||
=== "Node.js"
|
||||
|
||||
```bash
|
||||
npm install praisonaiui
|
||||
```
|
||||
'''
|
||||
out = preprocess_mkdocs_markdown(md)
|
||||
assert '=== "Python"' not in out
|
||||
assert f"### {TAB_HEADING_PREFIX}Python" in out
|
||||
assert f"### {TAB_HEADING_PREFIX}Node.js" in out
|
||||
assert "pip install praisonaiui" in out
|
||||
assert "npm install praisonaiui" in out
|
||||
assert "```bash" in out
|
||||
|
||||
def test_unknown_material_icon_becomes_bullet(self):
|
||||
md = ":material-unknown-icon:"
|
||||
out = preprocess_mkdocs_markdown(md)
|
||||
assert out == "•"
|
||||
@@ -0,0 +1,56 @@
|
||||
"""Regression tests for issue #207.
|
||||
|
||||
The Agent Command Center overview polled pending approvals every 10s and
|
||||
re-rendered only ``#ov-metrics`` and ``#ov-attention`` but then called
|
||||
``bindEvents(_container, data)`` over the whole overview root. Interactive
|
||||
elements in the page shell that were not re-rendered (agent rows, omnibar)
|
||||
accumulated stacked click/keydown listeners.
|
||||
|
||||
These tests assert the committed frontend view source
|
||||
``src/praisonaiui/templates/frontend/plugins/views/overview.js`` scopes the
|
||||
re-bind to only the subtrees that were re-rendered, so stale shell nodes no
|
||||
longer stack listeners.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
OVERVIEW_VIEW = (
|
||||
Path(__file__).resolve().parents[2]
|
||||
/ "src" / "praisonaiui" / "templates" / "frontend" / "plugins" / "views"
|
||||
/ "overview.js"
|
||||
)
|
||||
|
||||
|
||||
def _view() -> str:
|
||||
return OVERVIEW_VIEW.read_text()
|
||||
|
||||
|
||||
def _refresh_approvals_body() -> str:
|
||||
src = _view()
|
||||
start = src.index("async function refreshApprovals()")
|
||||
end = src.index("function startPolling()", start)
|
||||
return src[start:end]
|
||||
|
||||
|
||||
class TestScopedBind:
|
||||
def test_metrics_subtree_rebound(self):
|
||||
assert "bindEvents(metricsEl, data)" in _refresh_approvals_body()
|
||||
|
||||
def test_attention_subtree_rebound(self):
|
||||
assert "bindEvents(attnEl, data)" in _refresh_approvals_body()
|
||||
|
||||
def test_no_full_container_rebind_in_refresh(self):
|
||||
assert "bindEvents(_container, data)" not in _refresh_approvals_body()
|
||||
|
||||
def test_paint_still_binds_full_container(self):
|
||||
src = _view()
|
||||
start = src.index("async function paint()")
|
||||
end = src.index("async function refreshApprovals()", start)
|
||||
assert "bindEvents(_container, data)" in src[start:end]
|
||||
|
||||
def test_full_container_bind_only_in_paint(self):
|
||||
# Exactly one full-tree bind remains (paint's initial render).
|
||||
assert len(re.findall(r"bindEvents\(_container, data\)", _view())) == 1
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Unit tests for route template resolver (mirrors src/frontend/src/resolver.ts)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def match_pattern(path: str, pattern: str) -> bool:
|
||||
normalized_path = path.strip("/")
|
||||
normalized_pattern = pattern.strip("/")
|
||||
regex_pattern = (
|
||||
normalized_pattern.replace("**", "{{GLOB}}")
|
||||
.replace("*", "[^/]+")
|
||||
.replace("{{GLOB}}", ".*")
|
||||
)
|
||||
return re.match(f"^{regex_pattern}$", normalized_path) is not None
|
||||
|
||||
|
||||
def resolve_template(path: str, manifest: dict, templates: dict) -> dict | None:
|
||||
routes = sorted(manifest.get("routes", []), key=lambda r: r.get("priority", 0), reverse=True)
|
||||
for route in routes:
|
||||
if not match_pattern(path, route["pattern"]):
|
||||
continue
|
||||
template_name = route["template"]
|
||||
template = templates.get(template_name)
|
||||
if not template:
|
||||
return None
|
||||
return {
|
||||
"template": template_name,
|
||||
"layout": template.get("layout", "ThreeColumnLayout"),
|
||||
"slots": template.get("slots", {}),
|
||||
"slotOverrides": route.get("slotOverrides"),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def merged_slot(slot_name: str, match: dict | None):
|
||||
if not match:
|
||||
return None
|
||||
overrides = match.get("slotOverrides") or {}
|
||||
if slot_name in overrides:
|
||||
return overrides[slot_name]
|
||||
return match.get("slots", {}).get(slot_name)
|
||||
|
||||
|
||||
def should_show_toc(match: dict | None) -> bool:
|
||||
return merged_slot("right", match) is not None
|
||||
|
||||
|
||||
class TestRouteResolver:
|
||||
def test_match_docs_glob(self):
|
||||
assert match_pattern("docs/index", "/docs/**")
|
||||
assert match_pattern("docs/getting-started/installation", "/docs/**")
|
||||
assert not match_pattern("blog/post", "/docs/**")
|
||||
|
||||
def test_specific_route_wins_by_priority(self):
|
||||
manifest = {
|
||||
"routes": [
|
||||
{"pattern": "/docs/changelog", "template": "changelog", "priority": 2},
|
||||
{"pattern": "/docs/**", "template": "docs", "priority": 1},
|
||||
]
|
||||
}
|
||||
templates = {
|
||||
"docs": {"layout": "ThreeColumnLayout"},
|
||||
"changelog": {"layout": "TwoColumnLayout", "slots": {"right": None}},
|
||||
}
|
||||
match = resolve_template("docs/changelog", manifest, templates)
|
||||
assert match is not None
|
||||
assert match["template"] == "changelog"
|
||||
assert match["layout"] == "TwoColumnLayout"
|
||||
|
||||
def test_changelog_hides_toc_when_right_null(self):
|
||||
manifest = {
|
||||
"routes": [
|
||||
{"pattern": "/docs/changelog", "template": "changelog", "priority": 2},
|
||||
]
|
||||
}
|
||||
templates = {"changelog": {"layout": "TwoColumnLayout", "slots": {"right": None}}}
|
||||
match = resolve_template("docs/changelog", manifest, templates)
|
||||
assert should_show_toc(match) is False
|
||||
|
||||
def test_docs_shows_toc_by_default(self):
|
||||
manifest = {"routes": [{"pattern": "/docs/**", "template": "docs", "priority": 1}]}
|
||||
templates = {"docs": {"layout": "ThreeColumnLayout", "slots": {"right": {"ref": "toc"}}}}
|
||||
match = resolve_template("docs/index", manifest, templates)
|
||||
assert should_show_toc(match) is True
|
||||
@@ -21,12 +21,23 @@ def client():
|
||||
_agents.clear()
|
||||
_callbacks.clear()
|
||||
import praisonaiui.server as _srv
|
||||
from praisonaiui.features import _features
|
||||
|
||||
_srv._DEEP_HEALTH_CACHE = {}
|
||||
_srv._DEEP_HEALTH_CACHE_TIME = 0.0
|
||||
# Snapshot and clear the global feature registry so a feature leaked by an
|
||||
# earlier test (e.g. one that crashed before its cleanup) cannot slow down
|
||||
# /health and flake the deep-health timing/cache assertions under the full
|
||||
# suite. See issue #256.
|
||||
_features_snapshot = dict(_features)
|
||||
_features.clear()
|
||||
set_datastore(MemoryDataStore())
|
||||
app = create_app()
|
||||
return TestClient(app)
|
||||
try:
|
||||
yield TestClient(app)
|
||||
finally:
|
||||
_features.clear()
|
||||
_features.update(_features_snapshot)
|
||||
|
||||
|
||||
class TestHealthEndpoint:
|
||||
|
||||
Loaded 100 of 107 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user